diff --git a/SQL/site_data.sql b/SQL/site_data.sql index 6c58c21..b2ee4c8 100644 --- a/SQL/site_data.sql +++ b/SQL/site_data.sql @@ -49,6 +49,7 @@ ALTER TABLE members ADD profile_completed timestamp without time zone DEFAULT NU ALTER TABLE members ADD option_name VARCHAR(100) REFERENCES subscription_options(option_name); ALTER TABLE members ADD next_billing timestamp without time zone; ALTER TABLE members ADD trial_end timestamp without time zone; +ALTER TABLE members ADD last_login timestamp without time zone DEFAULT now(); -- bALTER TABLE members ADD option_name VARCHAR(100) REFERENCES subscription_options(option_name); CREATE TABLE members_profile( @@ -111,6 +112,22 @@ CREATE TABLE products ( ALTER TABLE products ADD start_url VARCHAR(100); + ALTER TABLE products ADD list_order INT DEFAULT 0; + ALTER TABLE products ADD contacts INT DEFAULT 0; + ALTER TABLE products ADD comments INT DEFAULT 0; + + +UPDATE products SET contacts = 1 WHERE product_id IN ('A000001','A000002'); +UPDATE products SET comments = 1 WHERE product_id IN ('A000003','A000004'); + +ALTER TABLE products ADD blog_product_id VARCHAR(25); +UPDATE products SET blog_product_id='A000003' WHERE product_id = 'A000001'; +UPDATE products SET blog_product_id='A000004' WHERE product_id = 'A000002'; + +--- initial set +UPDATE products SET list_order = 10*id; +UPDATE products SET name='Open-EMR' WHERE product_id = 'A000005'; + --- DEVELOPMENT UPDATE products SET start_url = 'A1.devprov.mermsemr.com' WHERE product_id = 'A000001'; UPDATE products SET start_url = 'A2.devprov.mermsemr.com' WHERE product_id = 'A000002'; @@ -248,6 +265,53 @@ merms_panel=# select product_id,description from products; A000005 | Get Open EMR for practice management (5 rows) + +CREATE TABLE products_colorstyle ( + id SERIAL, + uid uuid DEFAULT uuid_generate_v4(), + product_id VARCHAR(25) REFERENCES products(product_id), + name VARCHAR(55) NOT NULL, + color_style VARCHAR(55) NOT NULL, + color_code VARCHAR(9) NOT NULL, + status INT DEFAULT 0, + added timestamp without time zone DEFAULT now() + ); + ALTER TABLE ONLY products_colorstyle + ADD CONSTRAINT products_colorstyle_id_key UNIQUE (id); + +ALTER TABLE products_colorstyle OWNER TO merms_panel; + +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Blue','blue-theme.css','0000FF'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Crocus','crocus-theme.css','9172EC'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Green','green-theme.css','00FF00'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Magenta','magenta-theme.css','FF00FF'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Pink','pink-theme.css','FFC0CB'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Purple','purple-theme.css','800080'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Skyblue','skyblue-theme.css','87CEEB'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Red','red-theme.css','FF0000'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000001','Violet','violet-theme.css','8F00FF'); + +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Blue','blue-theme.css','0000FF'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Crocus','crocus-theme.css','9172EC'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Green','green-theme.css','00FF00'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Magenta','magenta-theme.css','FF00FF'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Pink','pink-theme.css','FFC0CB'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Purple','purple-theme.css','800080'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Skyblue','skyblue-theme.css','87CEEB'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Red','red-theme.css','FF0000'); +INSERT INTO products_colorstyle (product_id, name, color_style, color_code ) VALUES ('A000002','Violet','violet-theme.css','8F00FF'); + + + +{`nav-link ${(activeTab == value.controls || (index == 0 & !activeTab)) && 'active show'}`} + + + + + + + + CREATE TABLE products_templates ( id SERIAL, uid uuid DEFAULT uuid_generate_v4(), @@ -264,6 +328,9 @@ merms_panel=# select product_id,description from products; ALTER TABLE products_templates OWNER TO merms_panel; + + + INSERT INTO products_templates(product_id,name,provision_name,status) VALUES( 'A000001', @@ -350,22 +417,52 @@ INSERT INTO products (name,description,status,product_id, banner) VALUES ('Perso INSERT INTO products (name,description,status,product_id, banner) VALUES ('Professional Blog','Booster your practice with engaging contents',1,'A000004', 'banner.jpg'); INSERT INTO products (name,description,status,product_id, banner) VALUES ('Practice EMR','Get Open EMR for practice management',1,'A000005', 'banner.jpg'); -INSERT INTO products (name,description,status,product_id, banner) VALUES ('Mers Practice','Get Open EMR for practice management',1,'A000006', 'banner.jpg'); -UPDATE products SET start_url = 'm.ehr.mermsemr.com' WHERE product_id = 'A000006'; +-- NEW PRODUCT------------------------------------------------------ + + + +INSERT INTO products (name,description,status,product_id, banner, start_url) +VALUES ('MERMs Practice', + 'Get Merms EMR for your Practice Management', + 5, + 'A000006', + 'p6.jpg', + 'm.ehr.mermsemr.com'); + +-- UPDATE products SET start_url = 'm.ehr.mermsemr.com' WHERE product_id = ''; INSERT INTO products_details (product_id,details) VALUES ('A000006', ' -

OpenEMR is the most popular open source electronic health records and medical practice management solution. OpenEMR is a community of passionate volunteers and contributors dedicated to guarding OpenEMR status as a free, open source software solution for medical practices with a commitment to openness, kindness and cooperation. +

OpenEMR is the most popular open source electronic health records and medical practice management solution. Merms EMR is a community of passionate volunteers and contributors dedicated to guarding OpenEMR status as a free, open source software solution for medical practices with a commitment to openness, kindness and cooperation.

-Activate to deploy OpenEMR for your practice -

-URL: https://www.open-emr.org/

'); +Activate to deploy MERMS Emr for your practice +

'); UPDATE products_details SET sale_text = '

Start free for 1 month, and customize your product with additional features as you see fit. You''re in control-you can cancel anytime.
-
' WHERE product_id= 'products_details'; +
' WHERE product_id= 'A000006'; + + +INSERT INTO products_templates(product_id,name,provision_name,status) +VALUES( + 'A000006', + 'Template F01', + 'registry.chiefsoft.com/products/merms-emr-001:latest', + 1); + +INSERT INTO products_templates(product_id,name,provision_name,status) +VALUES( + 'A000006', + 'Template F02', + 'registry.chiefsoft.com/products/merms-emr-002:latest', + 1); + + +-- UPDATE products SET start_url = 'm.ehr.mermsemr.com' WHERE product_id = 'A000006'; +-- UPDATE products SET status = 5 WHERE product_id = 'A000006'; +-- END 000 Product -------------------------------------------------- @@ -670,6 +767,8 @@ CREATE TABLE office_users( ALTER TABLE ONLY office_users ADD CONSTRAINT office_users_id_key UNIQUE (id); +ALTER TABLE office_users ALTER COLUMN password TYPE VARCHAR(250); + ALTER TABLE office_users OWNER TO merms_panel; INSERT INTO office_users (username, password, firstname, lastname, acc_level) VALUES ('mermsadmin','password','Merms','Admin',1000); diff --git a/app/api/enums/settings_items_data.py b/app/api/enums/settings_items_data.py index bf25a02..bbf160a 100644 --- a/app/api/enums/settings_items_data.py +++ b/app/api/enums/settings_items_data.py @@ -79,6 +79,6 @@ class SettingsItemsData: def get_site_blog_connect_settings(): data = { "boolean_blog_connect_show": {"name": 'Show Blog Section', "controls": 'SELECT_NO_YES', - "active": True, "list_order": 0}, + "active": True, "list_order": 0}, } return data \ No newline at end of file diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py index 9b7fec8..d3bbb77 100644 --- a/app/api/routes/routes.py +++ b/app/api/routes/routes.py @@ -115,6 +115,7 @@ def merms_account(): response = AccountService.process_request(data) return response + @api.route("/panel/account/profile", methods=["POST"]) @jwt_required() def merms_account_profile(): @@ -189,6 +190,16 @@ def get_myproduct_templates(): return response +@api.route("/panel/account/products/color-styles", methods=["POST"]) +# @token_required +def get_myproduct_colorstyles(): + # Call the ColorStyles service + data = request.get_json() + # logger.info(f"Route Product ColorStyles Data ==>>>> {data}") + response = MyProductsService.mpproduct_colostyles_data(data) + return response + + @api.route("/panel/account/template/activate", methods=["POST"]) # @token_required def get_myproduct_templates_activate(): @@ -205,6 +216,7 @@ def merms_contacts(): response = ContactService.process_request(data) return response + @api.route("/panel/comments", methods=["POST"]) def merms_site_comments(): data = request.get_json() @@ -228,6 +240,7 @@ def myproduct_dash(): response = MyProductsService.process_request(data) return response + @api.route("/panel/myproduct/external-url", methods=["POST"]) def set_external_url(): data = request.get_json() @@ -382,6 +395,7 @@ def get_dashboard(): result = OfficeDashboardService.get_dashboard_data() return jsonify(result) + @api.route('/office/country', methods=['GET']) # @token_required def get_office_country(): @@ -389,6 +403,7 @@ def get_office_country(): result = OfficeCountryService.get_office_country_list() return jsonify(result) + @api.route('/office/set-country', methods=['POST']) # @token_required def set_office_country(): @@ -427,6 +442,7 @@ def get_subscriptions_list_office(): response = SubscriptionsService.get_subscription_data(filters) return response + @api.route('/office/subscription-view', methods=['GET']) # @token_required def get_subcriptions_view_office(): @@ -438,6 +454,7 @@ def get_subcriptions_view_office(): response = OfficeDashboardService.get_subscription_view_data(filters) return response + @api.route('/office/template/set-custom', methods=['POST']) # @token_required def get_set_custom_template_office(): @@ -464,6 +481,7 @@ def get_create_custom_template_office(): response = OfficeTemplatesService.create_custom_template(data) return response + @api.route('/office/billings', methods=['GET']) # @token_required def get_subscription_billings_office(): @@ -490,6 +508,7 @@ def get_subscription_transaction_office(): result = OfficeDashboardService.get_payments_data(filters) return jsonify(result) + @api.route('/office/recent-signup', methods=['GET']) def get_recent_signup_office(): # Call the dashboard service @@ -497,6 +516,7 @@ def get_recent_signup_office(): result = OfficeDashboardService.get_payments_data(filters) return jsonify(result) + @api.route('/office/right-sidebar', methods=['GET']) def get_office_sidebar(): # Call the dashboard service @@ -504,6 +524,7 @@ def get_office_sidebar(): result = OfficeDashboardService.get_office_sidebar(filters) return jsonify(result) + @api.route('/office/users', methods=['GET']) def get_office_users(): logger.info('API::get office users') @@ -511,6 +532,7 @@ def get_office_users(): result = OfficeUsersService.get_office_users(filters) return jsonify(result) + @api.route('/office/products', methods=['GET']) def get_product_office(): # Call the dashboard service @@ -518,6 +540,7 @@ def get_product_office(): result = OfficeDashboardService.get_office_products(filters) return jsonify(result) + @api.route('/office/product-view', methods=['GET']) def get_product_view_office(): # Call the dashboard service @@ -527,6 +550,7 @@ def get_product_view_office(): result = OfficeDashboardService.get_office_product_detail(filters) return jsonify(result) + @api.route('/office/product-update', methods=['POST']) def get_product_update_office(): # Call the dashboard service @@ -556,6 +580,7 @@ def get_product_templates(): result = OfficeDashboardService.get_office_product_templates(filters) return jsonify(result) + @api.route('/office/account-view', methods=['GET']) def get_office_accoint_view(): # Call the office account viiew @@ -568,6 +593,7 @@ def get_office_accoint_view(): # return response return jsonify(response) + @api.route('/office/custom-templates', methods=['GET']) def get_custom_templates(): # Call the dashboard service @@ -581,6 +607,7 @@ def get_custom_templates(): result = OfficeDashboardService.get_office_custom_templates(filters) return jsonify(result) + # ===================================================== @api.route('/web/contents', methods=['GET']) # @token_required @@ -590,6 +617,7 @@ def get_web_contents(): result = WebContentsService.get_web_contents_data(provision_uid) return jsonify(result) + # ===================================================== @api.route('/web/contacts', methods=['POST']) # @token_required @@ -599,6 +627,7 @@ def get_web_receive_contacts(): result = ContactService.process_save_contacts(data) return jsonify(result) + # ===================================================== @api.route('/web/generatives', methods=['GET']) def get_refresh_generatives(): @@ -608,6 +637,7 @@ def get_refresh_generatives(): result = GenerativesService.process_generatives_list(data) return jsonify(result) + # ===================================================== @api.route('/web/traffic', methods=['POST']) # @token_required @@ -636,10 +666,11 @@ def common_practice(): # Health Check Endpoint @api.route("/testemail", methods=["GET"]) def email_check(): - data ={} + data = {} AccountService.process_test_email(data) return {"status": "ok"}, 200 + # Health Check Endpoint @api.route("/health", methods=["GET"]) def health_check(): diff --git a/app/api/services/myproduct.py b/app/api/services/myproduct.py index a7d1597..3e729b4 100644 --- a/app/api/services/myproduct.py +++ b/app/api/services/myproduct.py @@ -10,7 +10,7 @@ from app.api.services.base_service import BaseService from marshmallow import ValidationError from app.extensions import db from app.models import MembersProducts, Products, Members, ProductsDetails, ProductsDetails, ProvisionActions, \ - MembersProductsSettings, ProductsTemplates, MembersProfile, SubscriptionGenerative + MembersProductsSettings, ProductsTemplates, MembersProfile, SubscriptionGenerative, ProductsColorStyle from app.api.helpers.response_helper import ResponseHelper from app.api.schemas.myproduct import MyProductSchema from app.api.schemas.provision import ProvisionSchema @@ -124,6 +124,63 @@ class MyProductsService(BaseService): db.session.rollback() return ResponseHelper.internal_server_error() + @staticmethod + def mpproduct_colostyles_data(data): + try: + with db.session.begin(): + # logger.info(f"Incoming MyProduct data ==>>>> {data}") + validated_data = MyProductsService.validate_data(data, MyProductSchema()) + token = validated_data.get('token') + uid = validated_data.get('uid') + member_data = Members.get_member_by_uid(uid) + member_id = member_data.id + + product_id = validated_data.get('product_id') + color_style_data = [] + memberSubscription = MembersProducts.get_member_product_by_product_member_id(member_id, product_id) + if memberSubscription: + colorStyles = ProductsColorStyle.get_colorstyle_by_product_id(product_id) + # templates = ProductsTemplates.get_template_by_product_id(product_id) + for t in colorStyles: + color_style_data.append({ + 'id': t.id, + 'template_uid': t.uid, + 'color_style_uid': t.uid, + 'color_code': t.color_code, + 'title': t.name, + 'active': t.status, + 'added': t.added.isoformat() if t.added else None + }) + + response_data = { + "product_id": product_id, + "current_template_uid": memberSubscription.product_template if memberSubscription else None, + "custom_template_name": memberSubscription.custom_template if memberSubscription else '', + "templates": color_style_data, + "last_update": datetime.datetime.utcnow(), + } + + return ResponseHelper.success(data=response_data) + + + except ValidationError as err: + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() + return ResponseHelper.unprocessable_entity(result_description="Validation exception") + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() + return ResponseHelper.error(result_description=str(err)) + + except Exception as e: + logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() + return ResponseHelper.internal_server_error() + + + @staticmethod def process_provision(data): try: diff --git a/app/models/__init__.py b/app/models/__init__.py index 051c771..a5c793f 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -19,12 +19,14 @@ from .subscription_generative import SubscriptionGenerative from .generative_results import GenerativeResults from .office_users import OfficeUsers from .custom_templates import CustomTemplates +from .products_colorstyle import ProductsColorStyle from .country import Country __all__ = ['Members', 'Account', 'Products', 'MembersProducts', 'MembersActions', 'MembersPending', 'ProductsDetails', 'ProvisionActions', 'MembersProductsRefresh', 'MembersProductsSettings', 'PasswordReset', 'MembersProfile', 'SubscriptionOptions', 'SubscriptionOptionsItems', - 'ProductsTemplates', 'Payments', 'PaymentsSession', 'SubscriptionGenerative', 'GenerativeResults', - 'CustomTemplates','Country', + 'ProductsTemplates', 'ProductsColorStyle', 'Payments', 'PaymentsSession', + 'SubscriptionGenerative', 'GenerativeResults', + 'CustomTemplates', 'Country', 'OfficeUsers'] diff --git a/app/models/products_colorstyle.py b/app/models/products_colorstyle.py new file mode 100644 index 0000000..3958b04 --- /dev/null +++ b/app/models/products_colorstyle.py @@ -0,0 +1,59 @@ +# + +from datetime import datetime, timezone +from app.extensions import db +from app.models.charge import Charge +from sqlalchemy.orm import relationship +from sqlalchemy.sql import func + + +class ProductsColorStyle(db.Model): + __tablename__ = 'products_colorstyle' + + id = db.Column(db.String, primary_key=True) + uid = db.Column(db.String, nullable=False) + product_id = db.Column(db.String, nullable=False) + name = db.Column(db.String, nullable=False) + color_style = db.Column(db.String, nullable=False) + color_code = db.Column(db.String, nullable=False) + status = db.Column(db.Integer, nullable=True, default=1) + added = db.Column(db.DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) + + @classmethod + def get_colorstyle_by_product_id(cls, product_id): + templates = cls.query.filter_by(product_id=str(product_id)).all() + + if not templates: + # raise ValueError(f"Templates with Product ID {product_id} not found") + return None + return templates + + @classmethod + def get_colorstyle_for_office(cls, filters): + templates = cls.query.all() + + if not templates: + raise ValueError(f"Templates not found") + return templates + + # @classmethod + # def get_template_by_uid(cls, template_uid): + # selTemplate = cls.query.filter_by(uif=template_uid).first() + # if not selTemplate: + # return None + # return selTemplate + + def to_dict(self): + return { + "id": self.id, + "uid": self.uid, + "product_id": self.product_id, + "name": self.name, + "color_style": self.color_style, + "color_code": self.color_code, + "status": self.status, + "added": self.added.isoformat() if self.added else None + } + + def __repr__(self): + return f'' diff --git a/app/models/products_templates.py b/app/models/products_templates.py index f61d5bd..535a7a7 100644 --- a/app/models/products_templates.py +++ b/app/models/products_templates.py @@ -4,6 +4,7 @@ from app.models.charge import Charge from sqlalchemy.orm import relationship from sqlalchemy.sql import func + class ProductsTemplates(db.Model): __tablename__ = 'products_templates' @@ -16,7 +17,6 @@ class ProductsTemplates(db.Model): provision_name = db.Column(db.String, nullable=False) added = db.Column(db.DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) - @classmethod def get_template_by_product_id(cls, product_id): templates = cls.query.filter_by(product_id=str(product_id)).all() @@ -41,7 +41,6 @@ class ProductsTemplates(db.Model): return None return selTemplate - def to_dict(self): return { "id": self.id, @@ -51,8 +50,8 @@ class ProductsTemplates(db.Model): "status": self.status, "owner_uid": self.owner_uid, "provision_name": self.provision_name, - "provision_name": self.provision_name + "added": self.added.isoformat() if self.added else None } def __repr__(self): - return f'' \ No newline at end of file + return f''