diff --git a/api/helpers/db_helper.py b/api/helpers/db_helper.py index 69ab2e33..33ca3d46 100644 --- a/api/helpers/db_helper.py +++ b/api/helpers/db_helper.py @@ -20,7 +20,7 @@ def get_class_by_tablename(tablename): :param tablename: String with name of table. :return: Class reference or None. """ - for classObject in db.Model._decl_class_registry.values(): + for classObject in db.Model.registry._class_registry.values(): if ( hasattr(classObject, "__tablename__") and classObject.__tablename__ == tablename diff --git a/api/models/call_log.py b/api/models/call_log.py index b1a5ee0a..c7a9607d 100644 --- a/api/models/call_log.py +++ b/api/models/call_log.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class CallLogQuery(BaseQuery): diff --git a/api/models/contact_fields_mapping.py b/api/models/contact_fields_mapping.py index d588d46a..24d32744 100644 --- a/api/models/contact_fields_mapping.py +++ b/api/models/contact_fields_mapping.py @@ -1,5 +1,5 @@ from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery from utils.loggingutils import logger diff --git a/api/models/content.py b/api/models/content.py index 59aef5bf..5fd0f77f 100644 --- a/api/models/content.py +++ b/api/models/content.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class ContentQuery(BaseQuery): diff --git a/api/models/content_version.py b/api/models/content_version.py index 17f334b2..55c82112 100644 --- a/api/models/content_version.py +++ b/api/models/content_version.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class ContentVersionQuery(BaseQuery): diff --git a/api/models/ivr_prompt.py b/api/models/ivr_prompt.py index ef4de91a..a35831bd 100644 --- a/api/models/ivr_prompt.py +++ b/api/models/ivr_prompt.py @@ -1,7 +1,7 @@ from api.mixins import TimestampMixin from api import db from sqlalchemy import desc, and_, func -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class IvrPromptQuery(BaseQuery): diff --git a/api/models/ivr_prompt_mapping.py b/api/models/ivr_prompt_mapping.py index e5420778..a00af706 100644 --- a/api/models/ivr_prompt_mapping.py +++ b/api/models/ivr_prompt_mapping.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class IvrPromptMappingQuery(BaseQuery): diff --git a/api/models/ivr_prompt_response.py b/api/models/ivr_prompt_response.py index cf33b87d..253ca8b1 100644 --- a/api/models/ivr_prompt_response.py +++ b/api/models/ivr_prompt_response.py @@ -1,12 +1,15 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class IvrPromptResponseQuery(BaseQuery): def get_by_call_log_id(self, call_log_id): return self.filter(IvrPromptResponse.call_log_id == call_log_id).all() + def get_latest_prompt(self): + return self.filter().order_by(IvrPromptResponse.id.desc()).first() + class IvrPromptResponse(TimestampMixin, db.Model): query_class = IvrPromptResponseQuery diff --git a/api/models/language.py b/api/models/language.py index 1eb6ff6e..c73424ed 100644 --- a/api/models/language.py +++ b/api/models/language.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class LanguageQuery(BaseQuery): diff --git a/api/models/module_content.py b/api/models/module_content.py index 8df91327..2e975eaf 100644 --- a/api/models/module_content.py +++ b/api/models/module_content.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class ModuleContentQuery(BaseQuery): diff --git a/api/models/partner_system_phone.py b/api/models/partner_system_phone.py index 87005b15..40adbf42 100644 --- a/api/models/partner_system_phone.py +++ b/api/models/partner_system_phone.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class PartnerSystemPhoneQuery(BaseQuery): diff --git a/api/models/program_module.py b/api/models/program_module.py index 103be070..a9b7372a 100644 --- a/api/models/program_module.py +++ b/api/models/program_module.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class ProgramModuleQuery(BaseQuery): diff --git a/api/models/program_sequence.py b/api/models/program_sequence.py index 1913d560..b186bafc 100644 --- a/api/models/program_sequence.py +++ b/api/models/program_sequence.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class ProgramSequenceQuery(BaseQuery): diff --git a/api/models/registration.py b/api/models/registration.py index 2baf6083..8b6639cc 100644 --- a/api/models/registration.py +++ b/api/models/registration.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class RegistrationQuery(BaseQuery): @@ -24,6 +24,9 @@ def get_latest_by_phone(self, phone): .first() ) + def get_latest_record(self): + return self.filter().order_by(Registration.id.desc()).first() + class Registration(TimestampMixin, db.Model): query_class = RegistrationQuery diff --git a/api/models/system_phone.py b/api/models/system_phone.py index fdb5d783..d34da195 100644 --- a/api/models/system_phone.py +++ b/api/models/system_phone.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class SystemPhoneQuery(BaseQuery): diff --git a/api/models/user.py b/api/models/user.py index f28cd168..6f855a8d 100644 --- a/api/models/user.py +++ b/api/models/user.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class UserQuery(BaseQuery): diff --git a/api/models/user_custom_fields.py b/api/models/user_custom_fields.py index 633d0195..19087501 100644 --- a/api/models/user_custom_fields.py +++ b/api/models/user_custom_fields.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery from sqlalchemy import and_ diff --git a/api/models/user_group.py b/api/models/user_group.py index 4fbfa0ea..13c618e7 100644 --- a/api/models/user_group.py +++ b/api/models/user_group.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery from sqlalchemy import desc, and_ diff --git a/api/models/user_program.py b/api/models/user_program.py index d34b0a71..beac746f 100644 --- a/api/models/user_program.py +++ b/api/models/user_program.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db, app -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery from api.helpers import common_helper, db_helper from utils.loggingutils import logger diff --git a/api/models/webhook_transaction_log.py b/api/models/webhook_transaction_log.py index be7492ad..4bc61962 100644 --- a/api/models/webhook_transaction_log.py +++ b/api/models/webhook_transaction_log.py @@ -1,6 +1,6 @@ from api.mixins import TimestampMixin from api import db -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query as BaseQuery class WebhookTransactionLogQuery(BaseQuery): @@ -9,6 +9,9 @@ def get_by_id(self, webhook_transaction_log_id): WebhookTransactionLog.id == webhook_transaction_log_id ).first() + def get_last_record(self): + return self.filter().order_by(WebhookTransactionLog.id.desc()).first() + class WebhookTransactionLog(TimestampMixin, db.Model): query_class = WebhookTransactionLogQuery diff --git a/api/services/prompt_service.py b/api/services/prompt_service.py index 218f80d5..71a733e7 100644 --- a/api/services/prompt_service.py +++ b/api/services/prompt_service.py @@ -1,8 +1,9 @@ # This file is treated as service layer -from api import models, db +from api import db from datetime import datetime from api.helpers import prompt_helper, common_helper, db_helper from utils.loggingutils import logger +from api import models class PromptService: diff --git a/config.py b/config.py index 0c522952..92bbdc76 100644 --- a/config.py +++ b/config.py @@ -48,7 +48,6 @@ if github_action_db_url: SQLALCHEMY_DATABASE_URI = github_action_db_url -SQLALCHEMY_TRACK_MODIFICATIONS = True WTF_CSRF_ENABLED = True SECRET_KEY = os.environ.get("SECRET_KEY") diff --git a/main.py b/main.py index fbbb9daa..9ef50137 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,5 @@ -from api import services, models from flask import jsonify -from api.helpers import db_helper +from api import app, helpers, services, models from utils.loggingutils import logger import json @@ -8,30 +7,31 @@ ### Endpoint for Cloud function def webhook(request): - try: - if request.method == "POST": - try: - jsonData = request.get_json() - except Exception as e: - logger.warning( - f"[WARN] Could not retrieve JSON data from the request. Error:{e}" - ) - return jsonify(message="Something went wrong!"), 400 - - if jsonData.get("flow_category", None) == "dry_flow": - handle_dry_flow(jsonData) - else: - handle_regular_flow(jsonData) - return jsonify(message="Success"), 200 - - logger.warning("[WARN] Received a GET request instead of POST") - return ( - jsonify(message="Currently, the system does not accept a GET request"), - 405, - ) - except Exception as e: - logger.error(f"An unexpected error occurred. Error message: {e}") - return jsonify(message="Internal server error"), 500 + with app.app_context(): + try: + if request.method == "POST": + try: + jsonData = request.get_json() + except Exception as e: + logger.warning( + f"[WARN] Could not retrieve JSON data from the request. Error:{e}" + ) + return jsonify(message="Something went wrong!"), 400 + + if jsonData.get("flow_category", None) == "dry_flow": + handle_dry_flow(jsonData) + else: + handle_regular_flow(jsonData) + return jsonify(message="Success"), 200 + + logger.warning("[WARN] Received a GET request instead of POST") + return ( + jsonify(message="Currently, the system does not accept a GET request"), + 405, + ) + except Exception as e: + logger.error(f"An unexpected error occurred. Error message: {e}") + return jsonify(message="Internal server error"), 500 def handle_dry_flow(jsonData): @@ -77,7 +77,7 @@ def retry_failed_webhook(transaction_log_service): for log in failed_webhook_logs: log.attempts += 1 - db_helper.save(log) + helpers.db_helper.save(log) json_data = json.loads(log.payload) json_data["log_created_on"] = log.created_on @@ -86,7 +86,7 @@ def retry_failed_webhook(transaction_log_service): continue log.processed = True - db_helper.save(log) + helpers.db_helper.save(log) def handle_payload(jsonData): diff --git a/manage.py b/manage.py index 1783bd53..52ebebb3 100644 --- a/manage.py +++ b/manage.py @@ -1,12 +1,11 @@ from __future__ import absolute_import from api import db, app -from api.models import * from flask_migrate import Migrate -from flask_script import Manager +from flask.cli import FlaskGroup migrate = Migrate(app, db) -manager = Manager(app) +cli = FlaskGroup(app) if __name__ == "__main__": - manager.run() + cli() diff --git a/migrations/seeders/__init__.py b/migrations/seeders/__init__.py new file mode 100644 index 00000000..ed1485d8 --- /dev/null +++ b/migrations/seeders/__init__.py @@ -0,0 +1,14 @@ +from migrations.seeders.user import * +from migrations.seeders.system_phone import * +from migrations.seeders.partner import * +from migrations.seeders.registration import * +from migrations.seeders.program import * +from migrations.seeders.language import * +from migrations.seeders.content_version import * +from migrations.seeders.content import * +from migrations.seeders.user_program import * +from migrations.seeders.module import * +from migrations.seeders.module_content import * +from migrations.seeders.program_module import * +from migrations.seeders.program_sequence import * +from migrations.seeders.call_log import * diff --git a/migrations/seeders/call_log.py b/migrations/seeders/call_log.py new file mode 100644 index 00000000..8ab50bab --- /dev/null +++ b/migrations/seeders/call_log.py @@ -0,0 +1,25 @@ +from faker import Faker +from uuid import uuid4 +from api.models import CallLog as CallLogInstance +from api import db + + +class CallLog: + faker = Faker() # Kept for future use + + def create_call_log(self, phone_number, content_id, content_version_id): + call_log_record = CallLogInstance() + call_log_record.user_phone_number = phone_number + call_log_record.content_id = content_id + call_log_record.content_version_id = content_version_id + call_log_record.flow_run_uuid = str(uuid4()) + + db.session.add(call_log_record) + db.session.commit() + + +if __name__ == "__main__": + seeder = CallLog() + seeder.create_call_log( + phone_number="1234567890", content_id=1, content_version_id=1 + ) diff --git a/migrations/seeders/content_version.py b/migrations/seeders/content_version.py index 22fbb738..c14fdafa 100644 --- a/migrations/seeders/content_version.py +++ b/migrations/seeders/content_version.py @@ -22,6 +22,8 @@ def create_content_version(self, language_id: int, contents: List[ContentModel]) db.session.add_all(content_version_instances) db.session.commit() + return content_version_instances + if __name__ == "__main__": seeder = ContentVersion() diff --git a/migrations/seeders/module.py b/migrations/seeders/module.py new file mode 100644 index 00000000..acd6495f --- /dev/null +++ b/migrations/seeders/module.py @@ -0,0 +1,23 @@ +import datetime +from faker import Faker +from api.models import Module as ModuleInstance +from api import db + + +class Module: + faker = Faker() # Kept for future use + + def create_module(self, program_id): + module_instance = ModuleInstance() + module_instance.name = "Test Module" + module_instance.program_id = program_id + + db.session.add(module_instance) + db.session.commit() + + return module_instance + + +if __name__ == "__main__": + seeder = Module() + seeder.create_module(program_id=1) diff --git a/migrations/seeders/module_content.py b/migrations/seeders/module_content.py new file mode 100644 index 00000000..ead1e9aa --- /dev/null +++ b/migrations/seeders/module_content.py @@ -0,0 +1,23 @@ +import datetime +from faker import Faker +from api.models import ModuleContent as ModuleContentInstance +from api import db + + +class ModuleContent: + faker = Faker() # Kept for future use + + def create_module_content(self, module_id, content_id): + module_instance = ModuleContentInstance() + module_instance.module_id = module_id + module_instance.content_id = content_id + + db.session.add(module_instance) + db.session.commit() + + return module_instance + + +if __name__ == "__main__": + seeder = ModuleContent() + seeder.create_module_content(module_id=1, content_id=1) diff --git a/migrations/seeders/program_module.py b/migrations/seeders/program_module.py new file mode 100644 index 00000000..826504f7 --- /dev/null +++ b/migrations/seeders/program_module.py @@ -0,0 +1,24 @@ +import datetime +from faker import Faker +from api.models import ProgramModule as ProgramModuleInstance +from api import db + + +class ProgramModule: + faker = Faker() # Kept for future use + + def create_program_module(self, program_id, module_id, sequence_id=1): + program_module_instance = ProgramModuleInstance() + program_module_instance.program_id = program_id + program_module_instance.module_id = module_id + program_module_instance.sequence = sequence_id + + db.session.add(program_module_instance) + db.session.commit() + + return program_module_instance + + +if __name__ == "__main__": + seeder = ProgramModule() + seeder.create_program_module(program_id=1, module_id=1) diff --git a/migrations/seeders/program_sequence.py b/migrations/seeders/program_sequence.py new file mode 100644 index 00000000..9074dd2b --- /dev/null +++ b/migrations/seeders/program_sequence.py @@ -0,0 +1,24 @@ +import datetime +from faker import Faker +from api.models import ProgramSequence as ProgramSequenceInstance +from api import db + + +class ProgramSequence: + faker = Faker() # Kept for future use + + def create_program_sequence(self, content_id, program_id, module_id): + program_sequence_instance = ProgramSequenceInstance() + program_sequence_instance.program_id = program_id + program_sequence_instance.content_id = content_id + program_sequence_instance.module_id = module_id + + db.session.add(program_sequence_instance) + db.session.commit() + + return program_sequence_instance + + +if __name__ == "__main__": + seeder = ProgramSequence() + seeder.create_program_sequence(program_id=1, content_id=1, module_id=1) diff --git a/migrations/seeders/testing_seeder.py b/migrations/seeders/testing_seeder.py index 27f89647..c4c7ea3b 100644 --- a/migrations/seeders/testing_seeder.py +++ b/migrations/seeders/testing_seeder.py @@ -1,14 +1,6 @@ from faker import Faker from typing import List -from migrations.seeders.user import User -from migrations.seeders.system_phone import SystemPhone -from migrations.seeders.partner import Partner -from migrations.seeders.registration import Registration -from migrations.seeders.program import Program -from migrations.seeders.language import Language -from migrations.seeders.content_version import ContentVersion -from migrations.seeders.content import Content -from migrations.seeders.user_program import UserProgram +from migrations.seeders import * def main(): @@ -21,7 +13,7 @@ def main(): created_contents = content.create_content() content_version = ContentVersion() - content_version.create_content_version( + created_content_versions = content_version.create_content_version( language_id=language_instance.id, contents=created_contents ) @@ -49,3 +41,30 @@ def main(): created_user_programs: List = user_program.create_user_program( created_programs, [user.user_id for user in created_user_instances] ) + + module = Module() + created_module = module.create_module(program_id=created_programs[0].id) + + module_content = ModuleContent() + created_module_content = module_content.create_module_content( + module_id=created_module.id, content_id=created_contents[0].id + ) + + program_module = ProgramModule() + create_program_module = program_module.create_program_module( + program_id=created_programs[0].id, module_id=created_module.id + ) + + program_sequence = ProgramSequence() + created_program_sequence = program_sequence.create_program_sequence( + program_id=created_programs[0].id, + content_id=created_contents[0].id, + module_id=created_module.id, + ) + + call_log = CallLog() + call_log.create_call_log( + phone_number=created_user_instances[0].phone, + content_id=created_content_versions[0].content_id, + content_version_id=created_content_versions[0].id, + ) diff --git a/requirements-dev.txt b/requirements-dev.txt index 95c84a52..b3a30d94 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,5 @@ # Includes dev dependencies on top of requirements.txt -r requirements.txt -functions-framework==2.1.2 flake8==3.8.4 flake8-polyfill==1.0.2 pep8-naming==0.10.0 diff --git a/requirements.txt b/requirements.txt index d6b6e9b0..00d48fb4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,27 @@ certifi==2020.12.5 chardet==4.0.0 -click==7.1.2 -Flask==1.1.2 +click==8.1.3 +Flask==3.0.0 requests==2.25.1 python-dotenv==0.15.0 -Flask-SQLAlchemy==2.1 +Flask-SQLAlchemy==3.1.1 +functions-framework==3.5.0 +greenlet==3.0.3 idna==2.10 -itsdangerous==1.1.0 -Jinja2==2.11.3 -MarkupSafe==1.1.1 -psycopg2==2.8.6 +itsdangerous==2.1.2 +Jinja2==3.1.2 +MarkupSafe==2.1.1 +psycopg2==2.9.9 python-dotenv==0.15.0 requests==2.25.1 -SQLAlchemy==1.3.23 +SQLAlchemy==2.0.23 urllib3==1.26.3 -Werkzeug==1.0.1 +Werkzeug==3.0.0 google-cloud-logging==3.5.0 -Flask-Migrate==2.1.1 +Flask-Migrate==4.0.5 Flask-Script==2.0.6 pytest==7.4.3 faker==20.1.0 setuptools==68.2.2 pytest-cov==4.0.0 +watchdog==3.0.0 diff --git a/tests/conftest.py b/tests/conftest.py index 64f9aede..32eac7a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,8 +27,7 @@ def client(app): @pytest.fixture(scope="session") def db(): - db = SQLAlchemy(flask_app) - db.init_app(flask_app) + db = SQLAlchemy() return db @@ -36,12 +35,13 @@ def db(): @pytest.fixture(scope="session") def setup_test_environment(db): # Creating tables. - try: - os.system("flask db downgrade 31955a9b7348") - os.system("flask db downgrade") - os.system("flask db upgrade") - except: - os.system("flask db upgrade") - - # Loading pre-filled data for running tests. - testing_seeder.main() + with flask_app.app_context(): + try: + os.system("flask db downgrade 31955a9b7348") + os.system("flask db downgrade") + os.system("flask db upgrade") + except: + os.system("flask db upgrade") + + # Loading pre-filled data for running tests. + testing_seeder.main() diff --git a/tests/payloads/registration_payload.json b/tests/payloads/registration_payload.json new file mode 100644 index 00000000..741c05fb --- /dev/null +++ b/tests/payloads/registration_payload.json @@ -0,0 +1,189 @@ +{ + "channel": { + "address": "", + "name": "", + "uuid": "a9efa603-dabe-4259-b449-f1de8dc878d9" + }, + "contact": { + "fields": { + "activities_week_6": null, + "address": null, + "age": null, + "agency": null, + "alumni_programme": "Yes", + "awcname": null, + "block": null, + "blocknumber": null, + "boygirl": null, + "case_classification": null, + "cg_a1_results": null, + "cg_a2_results": null, + "cg_a3_results": null, + "cg_a4_results": null, + "cg_a5_results": null, + "concerncoc": null, + "count_polls_completed": null, + "count_polls_responded": null, + "cv_opt_in": null, + "cvb_first_engagement": null, + "cvb_first_reach": null, + "cvb_first_start": null, + "cvb_hac_1_reach_def_2020_06_10": null, + "cvb_last_start": null, + "cvb_max_version": null, + "cvb_menu_setting": null, + "cvb_menus_visited": null, + "cvb_num_info": null, + "cvb_num_input": null, + "cvb_num_other_in_message": null, + "cvb_num_runs": null, + "cvb_num_starts": null, + "cvb_other": null, + "d_c_c": null, + "dat": null, + "date": "2024-01-01", + "day": null, + "deliveryedd": null, + "designation": null, + "district": null, + "district_audio": null, + "dost_cc": null, + "dost_flow_status": "Introdone", + "dost_module10_streak": null, + "dost_module5_streak": null, + "dost_module7_streak": null, + "dost_nudge_group": null, + "dost_program": "Program1", + "dost_program_completion": null, + "dost_program_status": null, + "dost_signup_status": "Yes", + "dost_time": "1", + "dost_ttr_group": null, + "dutystation": null, + "edulanguage": null, + "email": null, + "engagementcounter": null, + "feedback": null, + "fl_a10_results": null, + "fl_a11_results": null, + "fl_a12_results": null, + "fl_a1_results": null, + "fl_a2_results": null, + "fl_a3_results": null, + "fl_a4_results": null, + "fl_a5_results": null, + "fl_a6_results": null, + "fl_a7_results": null, + "fl_a8_results": null, + "fl_a9_results": null, + "gender": null, + "gram_panchayat": null, + "jharkhand_number_switch_impacteduser": null, + "language_id": null, + "last_incoming_call": null, + "lastactivedate": null, + "level": null, + "lname": null, + "location_coordinates": null, + "mobiletype": null, + "mothername": null, + "pin": null, + "pincode": null, + "polls_completed": null, + "polls_responded": null, + "pregnantmothername": null, + "program": null, + "programme_status": null, + "projectname": null, + "registration_date": "2023-04-04", + "role": null, + "sector": null, + "sm_contractual_status": null, + "sm_current_condition": null, + "sm_date_of_reporting": null, + "sm_dependent_current_condition": null, + "sm_email": null, + "sm_gender": null, + "sm_mobile_number": null, + "sm_name": null, + "sm_no_of_dependent_affected": null, + "sm_office": null, + "sm_patient_state": null, + "sm_section": null, + "sm_title": null, + "staff_type": null, + "stafftype": null, + "state": "India > Uttar Pradesh", + "stream": null, + "team": null, + "test_response_result": null, + "time": null, + "title": null, + "totalnochild": null, + "type_of_participant": null, + "updnumber": null, + "ups": null, + "upweek1": null, + "ward": null, + "week": null, + "whohavephone": null, + "whose_affected": null + }, + "groups": [ + { + "name": "Dost-Pilot-Intro", + "uuid": "4bf04450-cf03-4eed-8e25-f7e6f0dd3301" + } + ], + "name": "", + "urn": "", + "uuid": "1e745892-f454-46cd-9b68-d9348e7a270a" + }, + "content_id": "1", + "flow": { + "name": "Devteam-DOST-Pilot-OnboardingCall1.2", + "uuid": "bf63ea9f-a7c9-4ea5-bb1e-b632c8f54dbb" + }, + "flow_category": "registration", + "flow_run_details": { + "created_on": "2024-01-01T12:30:47.977099Z", + "uuid": "" + }, + "parent": null, + "program_details": { + "categories": [ + "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN_1" + ], + "categories_localized": [ + "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN_1" + ], + "category": "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN_1", + "category_localized": "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN_1", + "created_on": "2024-01-01T12:31:06.070339Z", + "extra": null, + "input": "1", + "name": "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN", + "node_uuid": "2676658b-853e-4de1-b395-6dbf327c8025", + "value": "1", + "values": [ + "1" + ] + }, + "results": { + "intro_1_signup_1_selection_program_optin": { + "category": "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN_1", + "name": "INTRO_1-SIGNUP_1-SELECTION_PROGRAM-OPTIN", + "value": "1" + }, + "intro_1_signup_1_selection_time_optin": { + "category": "INTRO_1-SIGNUP_1-SELECTION_TIME-OPTIN_MORNING", + "name": "INTRO_1-SIGNUP_1-SELECTION_TIME-OPTIN", + "value": "1" + }, + "result": { + "category": "Success", + "name": "Result", + "value": "200" + } + } +} diff --git a/tests/test_call_log.py b/tests/test_call_log.py index 239ae74b..d174be4b 100644 --- a/tests/test_call_log.py +++ b/tests/test_call_log.py @@ -25,56 +25,59 @@ def pytest_namespace(): def test_create_call_log(app, db, setup_test_environment): - # Get a specific user - user: User = User.query.get_by_id(1) + with app.app_context(): + # Get a specific user + user: User = User.query.get_by_id(1) - # Update user phone number to payload. - payload["contact"]["urn"] = f"tel:{user.phone}" + # Update user phone number to payload. + payload["contact"]["urn"] = f"tel:{user.phone}" - # Get a specific system phone number - system_phone: SystemPhone = SystemPhone.query.get_by_id(1) + # Get a specific system phone number + system_phone: SystemPhone = SystemPhone.query.get_by_id(1) - # Update system phone number to payload. - payload["channel"]["address"] = f"+91{system_phone.phone}" + # Update system phone number to payload. + payload["channel"]["address"] = f"+91{system_phone.phone}" - # Get a specific content - content_version: ContentVersion = ContentVersion.query.get_by_id(1) + # Get a specific content + content_version: ContentVersion = ContentVersion.query.get_by_id(1) - payload["content_id"] = content_version.content_id - payload["language_id"] = content_version.language_id + payload["content_id"] = content_version.content_id + payload["language_id"] = content_version.language_id - # Initialize the call log service. - call_log_service_instance = CallLogService() - call_log_service_instance.handle_call_log(payload) + # Initialize the call log service. + call_log_service_instance = CallLogService() + call_log_service_instance.handle_call_log(payload) - call_log_model = CallLog.query.get_by_flow_run_uuid(new_run_uuid) - pytest.initial_content_version_id = call_log_model.content_version_id - assert call_log_model != None + call_log_model = CallLog.query.get_by_flow_run_uuid(new_run_uuid) + pytest.initial_content_version_id = call_log_model.content_version_id + assert call_log_model != None def test_update_call_log(app, db, setup_test_environment): - # Get all content version records - all_content_version_records = ContentVersion.query.all() - - stop_iteration = False - new_content_id = None - new_language_id = None - while not stop_iteration: - random_index = randint(0, len(all_content_version_records) - 1) - if ( - all_content_version_records[random_index].id - != pytest.initial_content_version_id - ): - new_content_id = all_content_version_records[random_index].content_id - new_language_id = all_content_version_records[random_index].language_id - stop_iteration = True - - payload["content_id"] = new_content_id - payload["contact"]["fields"]["language_id"] = new_language_id - - # Initialize the call log service. - call_log_service_instance = CallLogService() - call_log_service_instance.handle_call_log(payload) - - call_log_model = CallLog.query.get_by_flow_run_uuid(new_run_uuid) - assert call_log_model.content_version_id != pytest.initial_content_version_id + with app.app_context(): + + # Get all content version records + all_content_version_records = ContentVersion.query.all() + + stop_iteration = False + new_content_id = None + new_language_id = None + while not stop_iteration: + random_index = randint(0, len(all_content_version_records) - 1) + if ( + all_content_version_records[random_index].id + != pytest.initial_content_version_id + ): + new_content_id = all_content_version_records[random_index].content_id + new_language_id = all_content_version_records[random_index].language_id + stop_iteration = True + + payload["content_id"] = new_content_id + payload["contact"]["fields"]["language_id"] = new_language_id + + # Initialize the call log service. + call_log_service_instance = CallLogService() + call_log_service_instance.handle_call_log(payload) + + call_log_model = CallLog.query.get_by_flow_run_uuid(new_run_uuid) + assert call_log_model.content_version_id != pytest.initial_content_version_id diff --git a/tests/test_program_sequence_service.py b/tests/test_program_sequence_service.py new file mode 100644 index 00000000..08f6a5a0 --- /dev/null +++ b/tests/test_program_sequence_service.py @@ -0,0 +1,48 @@ +from __future__ import absolute_import +import json +import pytest +from uuid import uuid4 +from random import randint +from faker import Faker +from api.services.program_sequence_service import * +from api.models import * + +faker = Faker() + +# Load the json payload +payload = json.load(open("tests/payloads/call_log_payload.json")) + +# Generate a unique flow_run_uuid +new_run_uuid = str(uuid4()) + +# Insert into the payload +payload["flow_run_details"]["uuid"] = new_run_uuid + + +def test_get_program_sequence(app, db, setup_test_environment): + with app.app_context(): + # Get a user from User Program + user_program: UserProgram = UserProgram.query.all()[0] + + # Get a specific user + user: User = User.query.get_by_id(user_program.user_id) + + # Update user phone number to payload. + payload["contact"]["urn"] = f"tel:{user.phone}" + + # Get a specific system phone number + system_phone: SystemPhone = SystemPhone.query.get_by_id(1) + + # Update system phone number to payload. + payload["channel"]["address"] = f"+91{system_phone.phone}" + + # Get Content Module + module_content: ModuleContent = ModuleContent.query.all()[0] + + payload["content_id"] = module_content.content_id + + # Initialize the Program Sequence service. + program_sequence_service = ProgramSequenceService() + sequence_id = program_sequence_service.get_program_sequence_id(payload) + + assert sequence_id != None diff --git a/tests/test_prompt_service.py b/tests/test_prompt_service.py new file mode 100644 index 00000000..bad178bd --- /dev/null +++ b/tests/test_prompt_service.py @@ -0,0 +1,56 @@ +from __future__ import absolute_import +import json +import pytest +from faker import Faker +from api.services.prompt_service import * +from api.models import * +from api import db + +faker = Faker() + +# Load the json payload +payload = json.load(open("tests/payloads/registration_payload.json")) + + +def pytest_namespace(): + # We will record the content version from the create operation for future reference + return {"initial_content_version_id": None} + + +def test_prompt_response(app, db, setup_test_environment): + with app.app_context(): + # Get call log record + call_log: CallLog = CallLog.query.all()[0] + + # Get a specific user + user: User = User.query.get_by_phone(call_log.user_phone_number) + + # Insert into the payload + payload["flow_run_details"]["uuid"] = call_log.flow_run_uuid + + # Update user phone number to payload. + payload["contact"]["urn"] = f"tel:{user.phone}" + + # Get a specific system phone number + system_phone: SystemPhone = SystemPhone.query.get_by_id(1) + + # Update system phone number to payload. + payload["channel"]["address"] = f"+91{system_phone.phone}" + + # Get a specific content + content_version: ContentVersion = ContentVersion.query.get_by_id( + call_log.content_version_id + ) + + payload["content_id"] = content_version.content_id + payload["language_id"] = content_version.language_id + + # Initialize the Prompt service. + prompt_service_instance = PromptService() + prompt_service_instance.handle_prompt_response(payload) + + user_phone_number = ( + payload["contact"]["urn"].replace("tel:", "").replace("+", "") + ) + ivr_prompt_response = IvrPromptResponse.query.get_latest_prompt() + assert ivr_prompt_response.user_phone == user_phone_number diff --git a/tests/test_registration.py b/tests/test_registration.py new file mode 100644 index 00000000..db7ca70e --- /dev/null +++ b/tests/test_registration.py @@ -0,0 +1,60 @@ +from __future__ import absolute_import +import json +import pytest +from uuid import uuid4 +from random import randint +from faker import Faker +from api.services.registration_service import * +from api.models import * + +faker = Faker() + +# Load the json payload +payload = json.load(open("tests/payloads/registration_payload.json")) + +# Generate a unique flow_run_uuid +new_run_uuid = str(uuid4()) + +# Insert into the payload +payload["flow_run_details"]["uuid"] = new_run_uuid + + +def test_create_registration(app, db, setup_test_environment): + with app.app_context(): + # Get a specific user + user: User = User.query.get_by_id(1) + + # Update user phone number to payload. + payload["contact"]["urn"] = f"tel:{user.phone}" + + # Get a specific system phone number + system_phone: SystemPhone = SystemPhone.query.get_by_id(1) + + # Update system phone number to payload. + payload["channel"]["address"] = f"+91{system_phone.phone}" + + # Get a specific content + content_version: ContentVersion = ContentVersion.query.get_by_id(1) + + payload["content_id"] = content_version.content_id + payload["language_id"] = content_version.language_id + + # Initialize the registration service. + registration_service_instance = RegistrationService() + registration_service_instance.handle_registration(payload) + + user_phone_number = payload["contact"]["urn"].replace("tel:", "") + registration_model = Registration.query.get_latest_record() + assert registration_model.user_phone == user_phone_number + + +def test_update_registration(app, db, setup_test_environment): + with app.app_context(): + # Initialize the registration service. + registration_service_instance = RegistrationService() + registration_service_instance.handle_registration(payload) + + user_phone_number = payload["contact"]["urn"].replace("tel:", "") + registration_model = Registration.query.get_latest_record() + assert registration_model.user_phone == user_phone_number + assert registration_model.has_received_callback == True diff --git a/tests/test_transaction_log_service.py b/tests/test_transaction_log_service.py new file mode 100644 index 00000000..3c65e57d --- /dev/null +++ b/tests/test_transaction_log_service.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import +import json +import pytest +from uuid import uuid4 +from random import randint +from faker import Faker +from api.services.transaction_log_service import * +from api.models import * + +faker = Faker() + +# Load the json payload +payload = json.load(open("tests/payloads/registration_payload.json")) + +# Generate a unique flow_run_uuid +new_run_uuid = str(uuid4()) + +# Insert into the payload +payload["flow_run_details"]["uuid"] = new_run_uuid + + +def test_create_transaction_log(app, db, setup_test_environment): + with app.app_context(): + # Get a specific user + user: User = User.query.get_by_id(1) + + # Update user phone number to payload. + payload["contact"]["urn"] = f"tel:{user.phone}" + + # Get a specific system phone number + system_phone: SystemPhone = SystemPhone.query.get_by_id(1) + + # Update system phone number to payload. + payload["channel"]["address"] = f"+91{system_phone.phone}" + + # Get a specific content version. + content_version: ContentVersion = ContentVersion.query.get_by_id(1) + + payload["content_id"] = content_version.content_id + payload["language_id"] = content_version.language_id + + # Initialize the Transaction Log Service. + transaction_log_instance = TransactionLogService() + transaction_log_instance.create_new_webhook_log(payload) + + last_transaction_log_record = WebhookTransactionLog.query.get_last_record() + assert last_transaction_log_record.payload == json.dumps(payload)