Example #1
0
    def post(self):
        post_data = request.get_json() or request.form

        session_id = post_data.get('sessionId')
        phone_number = post_data.get('phoneNumber')
        user_input = post_data.get('text')
        service_code = post_data.get('serviceCode')

        if phone_number:
            user = get_user_by_phone(phone_number)
            # api chains all inputs that came through with *
            latest_input = user_input.split('*')[-1]
            if None in [user, session_id]:
                current_menu = UssdMenu.find_by_name('exit_not_registered')
                text = menu_display_text_in_lang(current_menu, user)
            else:
                current_menu = UssdProcessor.process_request(session_id, latest_input, user)
                ussd_session = create_or_update_session(session_id, user, current_menu, user_input, service_code)
                text = UssdProcessor.custom_display_text(current_menu, ussd_session)

                if "CON" not in text and "END" not in text:
                    raise Exception("no menu found. text={}, user={}, menu={}, session={}".format(text, user.id, current_menu.name, ussd_session.id))

                if len(text) > 164:
                    print(f"Warning, text has length {len(text)}, display may be truncated")

                db.session.commit()
        else:
            current_menu = UssdMenu.find_by_name('exit_invalid_request')
            text = menu_display_text_in_lang(current_menu, None)

        return make_response(text), 200
Example #2
0
def test_custom_display_text(test_client, init_database, menu_name, language, expecteds,
                             real_at_idx, length, unexpected, menu_nr):
    with db.session.no_autoflush:
        start_state = UssdSessionFactory(state="menu_name")

        mapping = fake_transfer_mapping(length)
        real_usage1 = TransferUsage.find_or_create('Education')
        real_usage1.translations = {'en': 'Education', 'sw': 'Elimu'}
        real_usage2 = TransferUsage.find_or_create('Health')
        real_usage2.translations = {'en': 'Health', 'sw': 'Afya'}
        mapping[real_at_idx] = KenyaUssdStateMachine.make_usage_mapping(real_usage1)
        mapping[real_at_idx + 1] = KenyaUssdStateMachine.make_usage_mapping(real_usage2)
        user = standard_user()
        user.preferred_language = language

        start_state.session_data = {
            'transfer_usage_mapping': mapping,
            'usage_menu': menu_nr,
            'usage_index_stack': [0, 8]
        }

        start_state.user = user

        menu = UssdMenu(name=menu_name, display_key="ussd.kenya.{}".format(menu_name))
        resulting_menu = KenyaUssdProcessor.custom_display_text(
            menu, start_state)

        for expected in expecteds:
            assert expected in resulting_menu
        if unexpected is not None:
            assert unexpected not in resulting_menu
Example #3
0
def test_menu_display_text_in_lang(test_client, init_database, user_factory,
                                   expected):
    user = user_factory() if user_factory else None

    menu = UssdMenu(display_key="ussd.sempo.exit_invalid_request")
    result = menu_display_text_in_lang(menu, user)
    assert result == expected
 def process_request(session_id: str, user_input: str,
                     user: User) -> UssdMenu:
     session: Optional[UssdSession] = UssdSession.query.filter_by(
         session_id=session_id).first()
     # returning session
     if session:
         if user_input == "":
             return UssdMenu.find_by_name('exit_invalid_input')
         elif user_input == '0':
             return UssdMenu.find_by_name(session.state).parent()
         else:
             new_state = KenyaUssdProcessor.next_state(
                 session, user_input, user)
             return UssdMenu.find_by_name(new_state)
     # new session
     else:
         if user.has_valid_pin():
             return UssdMenu.find_by_name('start')
         else:
             if user.failed_pin_attempts is not None and user.failed_pin_attempts >= 3:
                 return UssdMenu.find_by_name('exit_pin_blocked')
             elif user.preferred_language is None:
                 return UssdMenu.find_by_name('initial_language_selection')
             else:
                 return UssdMenu.find_by_name('initial_pin_entry')
Example #5
0
def test_create_or_update_session(test_client, init_database):
    from flask import g
    g.active_organisation = OrganisationFactory(country_code='AU')

    user = UserFactory(phone="123")

    # create a session in db

    menu3 = UssdMenu(id=3, name='foo', display_key='foo')
    db.session.add(menu3)

    session = UssdSession(session_id="1",
                          user_id=user.id,
                          msisdn="123",
                          ussd_menu=menu3,
                          state="foo",
                          service_code="*123#")
    db.session.add(session)
    db.session.commit()

    # test updating existing
    create_or_update_session("1", user,
                             UssdMenu(id=4, name="bar", display_key='bar'),
                             "input", "*123#")
    sessions = UssdSession.query.filter_by(session_id="1")
    assert sessions.count() == 1
    session = sessions.first()
    assert session.state == "bar"
    assert session.user_input == "input"
    assert session.ussd_menu_id == 4

    # test creating a new one
    sessions = UssdSession.query.filter_by(session_id="2")
    assert sessions.count() == 0
    create_or_update_session("2", user,
                             UssdMenu(id=5, name="bat", display_key='bat'), "",
                             "*123#")
    sessions = UssdSession.query.filter_by(session_id="2")
    assert sessions.count() == 1
    session = sessions.first()
    assert session.state == "bat"
    assert session.user_input == ""
    assert session.ussd_menu_id == 5
def update_or_create_menu(name, description, parent_id=None):
    instance = UssdMenu.query.filter_by(name=name).first()
    if instance:
        instance.name = name
        instance.description = description
        instance.display_key = "ussd.kenya.{}".format(name)
        instance.parent_id = parent_id
    else:
        instance = UssdMenu(name=name,
                            description=description,
                            display_key="ussd.kenya.{}".format(name),
                            parent_id=parent_id)
        db.session.add(instance)

    db.session.commit()
    return instance
    def post(self):
        post_data = request.get_json() or request.form

        session_id = post_data.get('sessionId')
        phone_number = post_data.get('phoneNumber')
        user_input = post_data.get('text')
        service_code = post_data.get('serviceCode')

        # enforce only one single service code that can access the ussd state machine
        # through the endpoint
        if config.USSD_VALID_SERVICE_CODE != service_code:
            response = 'END '
            response += i18n.t(
                'ussd.kenya.invalid_service_code',
                valid_service_code=config.USSD_VALID_SERVICE_CODE,
                locale='sw')
            response += "\n"
            response += i18n.t(
                'ussd.kenya.invalid_service_code',
                valid_service_code=config.USSD_VALID_SERVICE_CODE,
                locale='en')
            return make_response(response, 200)

        elif phone_number:
            user = get_user_by_phone(phone_number, 'KE')
            # api chains all inputs that came through with *
            latest_input = user_input.split('*')[-1]
            if None in [user, session_id]:
                user_without_transfer_account = create_user_without_transfer_account(
                    phone_number)
                current_menu = UssdMenu.find_by_name(
                    'initial_language_selection')
                ussd_session = create_or_update_session(
                    session_id=session_id,
                    user=user_without_transfer_account,
                    user_input=latest_input,
                    service_code=service_code,
                    current_menu=current_menu)
                text = KenyaUssdProcessor.custom_display_text(
                    current_menu, ussd_session)
            else:
                current_menu = KenyaUssdProcessor.process_request(
                    session_id, latest_input, user)
                ussd_session = create_or_update_session(
                    session_id, user, current_menu, user_input, service_code)
                text = KenyaUssdProcessor.custom_display_text(
                    current_menu, ussd_session)

                if "CON" not in text and "END" not in text:
                    raise Exception(
                        "no menu found. text={}, user={}, menu={}, session={}".
                        format(text, user.id, current_menu.name,
                               ussd_session.id))

                if len(text) > 164:
                    print(
                        f"Warning, text has length {len(text)}, display may be truncated"
                    )

                db.session.commit()

        else:
            current_menu = UssdMenu.find_by_name('exit_invalid_request')
            text = menu_display_text_in_lang(current_menu, None)

        return make_response(text), 200