Skip to main content

For building ussd applications faster, with navigation management out of the box

Project description

AnySD

AnySD (Any USSD) is a package with classes to help you quickly write ussd (or ussd-like) applications.

Understanding Anysd ussd


Anysd

Anysd uses anytree to build a tree navigation, and redis for tracking navigation and session data (variables) Therefore, you need redis to continue

ussds

Ussd applications have 2 main components

  1. A Form - for taking input from the user
  2. Navigation - How to reach the beginning of a form, by selecting options

Getting started


Install anysd in virtual environment

Create and activate a virtualenv, then Install anysd if you have not:

>>> mkdir anysdtest && cd anysdtest

>>> virtualenv venv
created virtual environment CPython3.8.10.final.0-64 in 4591ms
  creator CPython3Posix(dest=/home/steven/workspace/anysdtest/venv, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/steven/.local/share/virtualenv)
    added seed packages: pip==22.0.4, setuptools==62.1.0, wheel==0.37.1
  activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

>>> source venv/bin/activate
(venv) >>>
(venv) >>> pip install anysd

...

(venv) >>> pip install Flask

Building the ussd

To build a ussd application, we first need to define the menus, first a home screen (or starting point), then the other menus descending from the home menu

In this example, we want a ussd to buy airtime and bundles. Then for each selection, we'll need to get the phone number of the receiver. For airtime, we need amount, and for bundles, we'll select a bundle option

step 1: Build the questions

# validator.py

from anysd import FormFlow, ListInput
airtime_questions = {
    "1": {'name': 'AIRTIME_RECEIVER', 'menu': ListInput(items=['Buy for myself', 'Buy for other number'], title='Select Option')},
    '2': {'name': 'AIRTIME_AMOUNT', 'menu': 'Enter amount'},
    '3': {'name': 'CONFIRMATION', 'menu': 'You are about to buy {amount} airtime for {receiver}\n1. Confirm\n0. Cancel'}
}

bundle_packages = [
    '100mb @15 valid 24 hours',
    '1GB @40 valid 24 hours',
    '5GB @100 valid 7 days'
]
bundle_questions = {
    "1": {'name': 'BUNDLE_RECEIVER', 'menu': ListInput(items=['Buy for myself', 'Buy for other number'], title='Select Option')},
    '2': {'name': 'BUNDLE_PACKAGE', 'menu': ListInput(items=bundle_packages, title='Select package')},
    '3': {'name': 'CONFIRMATION', 'menu': 'You are about to buy {package} for {receiver}\n1. Confirm\n0. Cancel'}
}

step 2: create form validators

By default, list inputs will be validated, but it's good you write another validator. In validation, you specify what conditions make a user input invalid, and return either true or false on the user input. Also, you can modify the input, if you need to Note: the validators should accept extra kwargs that may be passed

# validator.py

def airtime_validator(current_step, last_input: str, **kwargs):
    valid = True
    validated = None  # in case we want to modify user input, 
    
    if current_step == 2:
        if not last_input.isnumeric():  # checking if amount is a numeric value
            valid = False
        elif int(last_input) < 5:  # checking if is less than minimum 
            valid = False
    
    elif current_step == 3:
        if last_input not in ['1', '0']:
            valid = False
    
    return valid, validated

def bundle_validator(current_step, last_input: str, **kwargs):
    valid = True
    validated = None  # in case we want to modify user input, 
    
    if current_step == 3:
        if last_input not in ['1', '0']:
            valid = False
    
    return valid, validated

Note: Without step validators, all inputs except for List inputs(so far), will be assumed to be valid

step 3: Build the navigation

Link the menu, with the forms

# menu.py

from anysd import NavigationMenu, FormFlow
from validator import *


# forms
airtime_form = FormFlow(form_questions=airtime_questions, step_validator=airtime_validator)
bundle_form = FormFlow(form_questions=bundle_questions, step_validator=bundle_validator)

# menus
home = NavigationMenu(name="Home", title="Main Menu", show_title=True)

buy_airtime = NavigationMenu(name="airtime_home", title="Buy Airtime", parent=home, next_form=airtime_form)
buy_bundles = NavigationMenu(name="bundles_home", title="Buy Bundles", parent=home, next_form=bundle_form)

step 4: Navigation controller

The NavigationController object will be used to bind things together. It takes in msisdn, session_id and ussd_string plus your navigation home, then responds with an appropriate response.

We will call this inside a simple flask application.

If you haven't, install flask: pip install flask in your virtualenv

from flask import Flask, request
from anysd import NavigationController
from menu import home

app = Flask(__name__)

@app.get('/ussd')
def ussd_app():

    msisdn = request.args.get('msisdn')
    session_id = request.args.get('session_id')
    ussd_string = request.args.get('ussd_string')

    print(f"{msisdn} :: {session_id} ::: {ussd_string}")
    navigator = NavigationController(home, msisdn, session_id, ussd_string)
    msg = navigator.navigate()

    return msg


if __name__ == '__main__':
    app.run()

BEFORE WE RUN OUR BEAUTIFUL USSD, Anysd uses redis to store session data. We therefore need to specify the connection to redis in a config.yaml file

# config.yaml

redis:
    host: localhost
    port: 6379
    db: 4

Now we are ready to run the application:

(venv) >>> flask run 
* Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
2022-05-27 11:52:03,350 INFO   _log (on line 224 ) :  * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)

Our flask application will receive GET requests in this format: http://host:port/ussd?msisdn=XXXXXXXXXXXX&session_id=XXXXXXXXXXXXX&ussd_string=XXX*XXX*XXX

Now Let's use postman to hit the endpoint

img.png

Now let's select option 1 and see what happens:

img_1.png

Congratulations. you have built a basic ussd application, with one level of navigation and a form. We'll make more lessons on how to use anysd.

This is a new project, so many features are going to be added, progressively

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anysd-1.2.6.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anysd-1.2.6-py3-none-any.whl (13.9 kB view details)

Uploaded Python 3

File details

Details for the file anysd-1.2.6.tar.gz.

File metadata

  • Download URL: anysd-1.2.6.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.4 CPython/3.11.5 Linux/6.8.0-90-generic

File hashes

Hashes for anysd-1.2.6.tar.gz
Algorithm Hash digest
SHA256 7af2386da4d066e82a4bdd487fca2c78896aeabb1b9de089bacf1cba21f4d2fc
MD5 89064ca6be4edce8b3366103318a9ac5
BLAKE2b-256 4f1f5807534fd50436855872be55204de15e12a24c9faf6144e3ae76ab859cae

See more details on using hashes here.

File details

Details for the file anysd-1.2.6-py3-none-any.whl.

File metadata

  • Download URL: anysd-1.2.6-py3-none-any.whl
  • Upload date:
  • Size: 13.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.4 CPython/3.11.5 Linux/6.8.0-90-generic

File hashes

Hashes for anysd-1.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 d6afeb128c2ae3c04c5c320c0ab344f9a16eed345a4724e9ed8cb614762aea11
MD5 55c3673d6fc8fe1478aa990fe6ff4602
BLAKE2b-256 0b8095743d069e1c540e6d20a78f92afb91e3100796db92f52f24926251747f7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page