From 29da44bb9f016355a451d31396425ceacc19fccb Mon Sep 17 00:00:00 2001 From: PhilipWafula Date: Tue, 9 Mar 2021 19:05:01 +0300 Subject: [PATCH] Add barebone session resumption feature --- apps/cic-ussd/cic_ussd/operations.py | 42 ++++++++++++++++++++-------- apps/cic-ussd/cic_ussd/processor.py | 30 ++++++++++++++++++-- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/apps/cic-ussd/cic_ussd/operations.py b/apps/cic-ussd/cic_ussd/operations.py index f02b460b..b7b7e0ef 100644 --- a/apps/cic-ussd/cic_ussd/operations.py +++ b/apps/cic-ussd/cic_ussd/operations.py @@ -14,7 +14,7 @@ from cic_ussd.db.models.user import User from cic_ussd.db.models.ussd_session import UssdSession from cic_ussd.db.models.task_tracker import TaskTracker from cic_ussd.menu.ussd_menu import UssdMenu -from cic_ussd.processor import custom_display_text, process_request +from cic_ussd.processor import custom_display_text, process_request, retrieve_most_recent_ussd_session from cic_ussd.redis import InMemoryStore from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession from cic_ussd.validator import check_known_user, validate_response_type @@ -60,7 +60,8 @@ def create_ussd_session( phone: str, service_code: str, user_input: str, - current_menu: str) -> InMemoryUssdSession: + current_menu: str, + session_data: Optional[dict] = None) -> InMemoryUssdSession: """ Creates a new ussd session :param external_session_id: Session id value provided by AT @@ -73,6 +74,8 @@ def create_ussd_session( :type user_input: str :param current_menu: Menu name that is currently being displayed on the ussd session :type current_menu: str + :param session_data: Any additional data that was persisted during the user's interaction with the system. + :type session_data: dict. :return: ussd session object :rtype: Session """ @@ -81,7 +84,8 @@ def create_ussd_session( msisdn=phone, user_input=user_input, state=current_menu, - service_code=service_code + service_code=service_code, + session_data=session_data ) return session @@ -126,7 +130,9 @@ def create_or_update_session( phone=phone, service_code=service_code, user_input=user_input, - current_menu=current_menu) + current_menu=current_menu, + session_data=session_data + ) return ussd_session @@ -338,14 +344,26 @@ def process_menu_interaction_requests(chain_str: str, user_input=user_input ) - # create or update the ussd session as appropriate - ussd_session = create_or_update_session( - external_session_id=external_session_id, - phone=phone_number, - service_code=service_code, - user_input=user_input, - current_menu=current_menu.get('name') - ) + last_ussd_session = retrieve_most_recent_ussd_session(phone_number=user.phone_number) + + if last_ussd_session: + # create or update the ussd session as appropriate + ussd_session = create_or_update_session( + external_session_id=external_session_id, + phone=phone_number, + service_code=service_code, + user_input=user_input, + current_menu=current_menu.get('name'), + session_data=last_ussd_session.session_data + ) + else: + ussd_session = create_or_update_session( + external_session_id=external_session_id, + phone=phone_number, + service_code=service_code, + user_input=user_input, + current_menu=current_menu.get('name') + ) # define appropriate response response = custom_display_text( diff --git a/apps/cic-ussd/cic_ussd/processor.py b/apps/cic-ussd/cic_ussd/processor.py index 5087fb18..32e69a3e 100644 --- a/apps/cic-ussd/cic_ussd/processor.py +++ b/apps/cic-ussd/cic_ussd/processor.py @@ -6,7 +6,7 @@ from typing import Optional # third party imports import celery -from cic_types.models.person import Person +from sqlalchemy import desc from tinydb.table import Document # local imports @@ -315,6 +315,16 @@ def process_start_menu(display_key: str, user: User): ) +def retrieve_most_recent_ussd_session(phone_number: str) -> UssdSession: + # get last ussd session based on user phone number + last_ussd_session = UssdSession.session\ + .query(UssdSession)\ + .filter_by(msisdn=phone_number)\ + .order_by(desc(UssdSession.created))\ + .first() + return last_ussd_session + + def process_request(user_input: str, user: User, ussd_session: Optional[dict] = None) -> Document: """This function assesses a request based on the user from the request comes, the session_id and the user's input. It determines whether the request translates to a return to an existing session by checking whether the @@ -337,7 +347,23 @@ def process_request(user_input: str, user: User, ussd_session: Optional[dict] = return UssdMenu.find_by_name(name=successive_state) else: if user.has_valid_pin(): - return UssdMenu.find_by_name(name='start') + last_ussd_session = retrieve_most_recent_ussd_session(phone_number=user.phone_number) + + key = create_cached_data_key( + identifier=blockchain_address_to_metadata_pointer(blockchain_address=user.blockchain_address), + salt='cic.person' + ) + user_metadata = get_cached_data(key=key) + + if last_ussd_session: + # get last state + last_state = last_ussd_session.state + logg.debug(f'LAST USSD SESSION STATE: {last_state}') + # if last state is account_creation_prompt and metadata exists, show start menu + if last_state == 'account_creation_prompt' and user_metadata is not None: + return UssdMenu.find_by_name(name='start') + else: + return UssdMenu.find_by_name(name=last_state) else: if user.failed_pin_attempts >= 3 and user.get_account_status() == AccountStatus.LOCKED.name: return UssdMenu.find_by_name(name='exit_pin_blocked')