Add barebone session resumption feature

This commit is contained in:
PhilipWafula 2021-03-09 19:05:01 +03:00
parent 15445b8d0f
commit 29da44bb9f
Signed by untrusted user: mango-habanero
GPG Key ID: B00CE9034DA19FB7
2 changed files with 58 additions and 14 deletions

View File

@ -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.ussd_session import UssdSession
from cic_ussd.db.models.task_tracker import TaskTracker from cic_ussd.db.models.task_tracker import TaskTracker
from cic_ussd.menu.ussd_menu import UssdMenu 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.redis import InMemoryStore
from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession
from cic_ussd.validator import check_known_user, validate_response_type from cic_ussd.validator import check_known_user, validate_response_type
@ -60,7 +60,8 @@ def create_ussd_session(
phone: str, phone: str,
service_code: str, service_code: str,
user_input: str, user_input: str,
current_menu: str) -> InMemoryUssdSession: current_menu: str,
session_data: Optional[dict] = None) -> InMemoryUssdSession:
""" """
Creates a new ussd session Creates a new ussd session
:param external_session_id: Session id value provided by AT :param external_session_id: Session id value provided by AT
@ -73,6 +74,8 @@ def create_ussd_session(
:type user_input: str :type user_input: str
:param current_menu: Menu name that is currently being displayed on the ussd session :param current_menu: Menu name that is currently being displayed on the ussd session
:type current_menu: str :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 :return: ussd session object
:rtype: Session :rtype: Session
""" """
@ -81,7 +84,8 @@ def create_ussd_session(
msisdn=phone, msisdn=phone,
user_input=user_input, user_input=user_input,
state=current_menu, state=current_menu,
service_code=service_code service_code=service_code,
session_data=session_data
) )
return session return session
@ -126,7 +130,9 @@ def create_or_update_session(
phone=phone, phone=phone,
service_code=service_code, service_code=service_code,
user_input=user_input, user_input=user_input,
current_menu=current_menu) current_menu=current_menu,
session_data=session_data
)
return ussd_session return ussd_session
@ -338,14 +344,26 @@ def process_menu_interaction_requests(chain_str: str,
user_input=user_input user_input=user_input
) )
# create or update the ussd session as appropriate last_ussd_session = retrieve_most_recent_ussd_session(phone_number=user.phone_number)
ussd_session = create_or_update_session(
external_session_id=external_session_id, if last_ussd_session:
phone=phone_number, # create or update the ussd session as appropriate
service_code=service_code, ussd_session = create_or_update_session(
user_input=user_input, external_session_id=external_session_id,
current_menu=current_menu.get('name') 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 # define appropriate response
response = custom_display_text( response = custom_display_text(

View File

@ -6,7 +6,7 @@ from typing import Optional
# third party imports # third party imports
import celery import celery
from cic_types.models.person import Person from sqlalchemy import desc
from tinydb.table import Document from tinydb.table import Document
# local imports # 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: 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 """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 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) return UssdMenu.find_by_name(name=successive_state)
else: else:
if user.has_valid_pin(): 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: else:
if user.failed_pin_attempts >= 3 and user.get_account_status() == AccountStatus.LOCKED.name: if user.failed_pin_attempts >= 3 and user.get_account_status() == AccountStatus.LOCKED.name:
return UssdMenu.find_by_name(name='exit_pin_blocked') return UssdMenu.find_by_name(name='exit_pin_blocked')