# standard imports import json import logging from typing import Tuple # third-party imports import celery import i18n from chainlib.hash import strip_0x from cic_types.models.person import get_contact_data_from_vcard, generate_vcard_from_contact_data, manage_identity_data # local imports from cic_ussd.account.chain import Chain from cic_ussd.account.maps import gender, language from cic_ussd.account.metadata import get_cached_preferred_language from cic_ussd.db.models.account import Account from cic_ussd.db.models.base import SessionBase from cic_ussd.error import MetadataNotFoundError from cic_ussd.metadata import PersonMetadata from cic_ussd.session.ussd_session import save_session_data from cic_ussd.translation import translation_for from sqlalchemy.orm.session import Session logg = logging.getLogger(__file__) def change_preferred_language(state_machine_data: Tuple[str, dict, Account, Session]): """ :param state_machine_data: :type state_machine_data: :return: :rtype: """ user_input, ussd_session, account, session = state_machine_data r_user_input = language().get(user_input) session = SessionBase.bind_session(session) account.preferred_language = r_user_input session.add(account) session.flush() SessionBase.release_session(session) preferences_data = { 'preferred_language': r_user_input } s = celery.signature( 'cic_ussd.tasks.metadata.add_preferences_metadata', [account.blockchain_address, preferences_data], queue='cic-ussd' ) return s.apply_async() def update_account_status_to_active(state_machine_data: Tuple[str, dict, Account, Session]): """This function sets user's account to active. :param state_machine_data: A tuple containing user input, a ussd session and user object. :type state_machine_data: tuple """ user_input, ussd_session, account, session = state_machine_data session = SessionBase.bind_session(session=session) account.activate_account() session.add(account) session.flush() SessionBase.release_session(session=session) def parse_gender(account: Account, user_input: str): """ :param account: :type account: :param user_input: :type user_input: :return: :rtype: """ preferred_language = get_cached_preferred_language(account.blockchain_address) if not preferred_language: preferred_language = i18n.config.get('fallback') r_user_input = gender().get(user_input) return translation_for(f'helpers.{r_user_input}', preferred_language) def save_metadata_attribute_to_session_data(state_machine_data: Tuple[str, dict, Account, Session]): """This function saves first name data to the ussd session in the redis cache. :param state_machine_data: A tuple containing user input, a ussd session and user object. :type state_machine_data: tuple """ user_input, ussd_session, account, session = state_machine_data session = SessionBase.bind_session(session=session) current_state = ussd_session.get('state') key = '' if 'given_name' in current_state: key = 'given_name' if 'date_of_birth' in current_state: key = 'date_of_birth' if 'family_name' in current_state: key = 'family_name' if 'gender' in current_state: key = 'gender' user_input = parse_gender(account, user_input) if 'location' in current_state: key = 'location' if 'products' in current_state: key = 'products' if ussd_session.get('data'): data = ussd_session.get('data') data[key] = user_input else: data = { key: user_input } save_session_data('cic-ussd', session, data, ussd_session) SessionBase.release_session(session) def parse_person_metadata(account: Account, metadata: dict): """ :param account: :type account: :param metadata: :type metadata: :return: :rtype: """ set_gender = metadata.get('gender') given_name = metadata.get('given_name') family_name = metadata.get('family_name') email = metadata.get('email') if isinstance(metadata.get('date_of_birth'), dict): date_of_birth = metadata.get('date_of_birth') else: date_of_birth = { "year": int(metadata.get('date_of_birth')[:4]) } if isinstance(metadata.get('location'), dict): location = metadata.get('location') else: location = { "area_name": metadata.get('location') } if isinstance(metadata.get('products'), list): products = metadata.get('products') else: products = metadata.get('products').split(',') phone_number = account.phone_number date_registered = int(account.created.replace().timestamp()) blockchain_address = account.blockchain_address chain_str = Chain.spec.__str__() if isinstance(metadata.get('identities'), dict): identities = metadata.get('identities') else: identities = manage_identity_data( blockchain_address=blockchain_address, chain_str=chain_str ) return { "date_registered": date_registered, "date_of_birth": date_of_birth, "gender": set_gender, "identities": identities, "location": location, "products": products, "vcard": generate_vcard_from_contact_data( email=email, family_name=family_name, given_name=given_name, tel=phone_number ) } def save_complete_person_metadata(state_machine_data: Tuple[str, dict, Account, Session]): """This function persists elements of the user metadata stored in session data :param state_machine_data: A tuple containing user input, a ussd session and user object. :type state_machine_data: tuple """ user_input, ussd_session, account, session = state_machine_data metadata = ussd_session.get('data') person_metadata = parse_person_metadata(account, metadata) blockchain_address = account.blockchain_address s_create_person_metadata = celery.signature( 'cic_ussd.tasks.metadata.create_person_metadata', [blockchain_address, person_metadata], queue='cic-ussd') s_create_person_metadata.apply_async() def edit_user_metadata_attribute(state_machine_data: Tuple[str, dict, Account, Session]): """ :param state_machine_data: :type state_machine_data: :return: :rtype: """ user_input, ussd_session, account, session = state_machine_data blockchain_address = account.blockchain_address identifier = bytes.fromhex(strip_0x(blockchain_address)) person_metadata = PersonMetadata(identifier) cached_person_metadata = person_metadata.get_cached_metadata() if not cached_person_metadata: raise MetadataNotFoundError(f'Expected user metadata but found none in cache for key: {blockchain_address}') person_metadata = json.loads(cached_person_metadata) data = ussd_session.get('data') contact_data = {} vcard = person_metadata.get('vcard') if vcard: contact_data = get_contact_data_from_vcard(vcard) person_metadata.pop('vcard') given_name = data.get('given_name') or contact_data.get('given') family_name = data.get('family_name') or contact_data.get('family') date_of_birth = data.get('date_of_birth') or person_metadata.get('date_of_birth') set_gender = data.get('gender') or person_metadata.get('gender') location = data.get('location') or person_metadata.get('location') products = data.get('products') or person_metadata.get('products') if isinstance(date_of_birth, str): year = int(date_of_birth) person_metadata['date_of_birth'] = {'year': year} person_metadata['gender'] = set_gender person_metadata['given_name'] = given_name person_metadata['family_name'] = family_name if isinstance(location, str): location_data = person_metadata.get('location') location_data['area_name'] = location person_metadata['location'] = location_data person_metadata['products'] = products if contact_data: contact_data.pop('given') contact_data.pop('family') contact_data.pop('tel') person_metadata = {**person_metadata, **contact_data} parsed_person_metadata = parse_person_metadata(account, person_metadata) s_edit_person_metadata = celery.signature( 'cic_ussd.tasks.metadata.create_person_metadata', [blockchain_address, parsed_person_metadata] ) s_edit_person_metadata.apply_async(queue='cic-ussd')