Name | anysd JSON |
Version |
1.2.1
JSON |
| download |
home_page | None |
Summary | For building ussd applications faster, with navigation management out of the box |
upload_time | 2024-11-12 11:51:49 |
maintainer | None |
docs_url | None |
author | Somwaki |
requires_python | >=3.7 |
license | MIT |
keywords |
ussd
navigation
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# 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](https://pypi.org/project/anytree/) to build a tree navigation,
and [redis](https://pypi.org/project/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**
```python
# 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
```python
# 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
```python
# 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
```python
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**
```yaml
# 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](img.png)
Now let's select option 1 and see what happens:
![img_1.png](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
Raw data
{
"_id": null,
"home_page": null,
"name": "anysd",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "ussd, navigation",
"author": "Somwaki",
"author_email": "somwaki@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/d7/ac/774981c80fcfd558b15081df7832c95457b136327be6ee7e753aba09f75a/anysd-1.2.1.tar.gz",
"platform": null,
"description": "# AnySD\n\n\nAnySD (Any USSD) is a package with classes to help you quickly write ussd (or ussd-like) applications.\n\n\n### Understanding Anysd ussd\n\n---\n#### Anysd\nAnysd uses [anytree](https://pypi.org/project/anytree/) to build a tree navigation, \nand [redis](https://pypi.org/project/redis/) for tracking navigation and session data (variables)\n*Therefore, you need redis to continue*\n\n#### ussds\nUssd applications have 2 main components\n\n1. **A Form** - for taking input from the user\n2. **Navigation** - How to reach the beginning of a form, by selecting options\n\n## Getting started\n\n---\n\n### Install anysd in virtual environment\nCreate and activate a virtualenv, then\nInstall `anysd` if you have not: \n```\n>>> mkdir anysdtest && cd anysdtest\n\n>>> virtualenv venv\ncreated virtual environment CPython3.8.10.final.0-64 in 4591ms\n creator CPython3Posix(dest=/home/steven/workspace/anysdtest/venv, clear=False, no_vcs_ignore=False, global=False)\n seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/steven/.local/share/virtualenv)\n added seed packages: pip==22.0.4, setuptools==62.1.0, wheel==0.37.1\n activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator\n\n>>> source venv/bin/activate\n(venv) >>>\n(venv) >>> pip install anysd\n\n...\n\n(venv) >>> pip install Flask\n\n```\n\n### Building the ussd\n\nTo build a ussd application, we first need to define the menus, first a home screen (or starting point), \nthen the other menus descending from the home menu\n\nIn this example, we want a ussd to buy airtime and bundles. Then for each selection, we'll need to get the phone number\nof the receiver. For airtime, we need amount, and for bundles, we'll select a bundle option\n\n**step 1: Build the questions**\n\n```python\n# validator.py\n\nfrom anysd import FormFlow, ListInput\nairtime_questions = {\n \"1\": {'name': 'AIRTIME_RECEIVER', 'menu': ListInput(items=['Buy for myself', 'Buy for other number'], title='Select Option')},\n '2': {'name': 'AIRTIME_AMOUNT', 'menu': 'Enter amount'},\n '3': {'name': 'CONFIRMATION', 'menu': 'You are about to buy {amount} airtime for {receiver}\\n1. Confirm\\n0. Cancel'}\n}\n\nbundle_packages = [\n '100mb @15 valid 24 hours',\n '1GB @40 valid 24 hours',\n '5GB @100 valid 7 days'\n]\nbundle_questions = {\n \"1\": {'name': 'BUNDLE_RECEIVER', 'menu': ListInput(items=['Buy for myself', 'Buy for other number'], title='Select Option')},\n '2': {'name': 'BUNDLE_PACKAGE', 'menu': ListInput(items=bundle_packages, title='Select package')},\n '3': {'name': 'CONFIRMATION', 'menu': 'You are about to buy {package} for {receiver}\\n1. Confirm\\n0. Cancel'}\n}\n\n\n```\n\n**step 2: create form validators**\n\nBy default, list inputs will be validated, but it's good you write another validator.\nIn validation, you specify what conditions make a user input invalid, and return either true or false on the user input.\nAlso, you can modify the input, if you need to\nNote: the validators should accept extra `kwargs` that may be passed \n\n```python\n# validator.py\n\ndef airtime_validator(current_step, last_input: str, **kwargs):\n valid = True\n validated = None # in case we want to modify user input, \n \n if current_step == 2:\n if not last_input.isnumeric(): # checking if amount is a numeric value\n valid = False\n elif int(last_input) < 5: # checking if is less than minimum \n valid = False\n \n elif current_step == 3:\n if last_input not in ['1', '0']:\n valid = False\n \n return valid, validated\n\ndef bundle_validator(current_step, last_input: str, **kwargs):\n valid = True\n validated = None # in case we want to modify user input, \n \n if current_step == 3:\n if last_input not in ['1', '0']:\n valid = False\n \n return valid, validated\n```\n*Note: Without step validators, all inputs except for List inputs(so far), will be assumed to be valid*\n\n**step 3: Build the navigation**\n\nLink the menu, with the forms\n```python\n# menu.py\n\nfrom anysd import NavigationMenu, FormFlow\nfrom validator import *\n\n\n# forms\nairtime_form = FormFlow(form_questions=airtime_questions, step_validator=airtime_validator)\nbundle_form = FormFlow(form_questions=bundle_questions, step_validator=bundle_validator)\n\n# menus\nhome = NavigationMenu(name=\"Home\", title=\"Main Menu\", show_title=True)\n\nbuy_airtime = NavigationMenu(name=\"airtime_home\", title=\"Buy Airtime\", parent=home, next_form=airtime_form)\nbuy_bundles = NavigationMenu(name=\"bundles_home\", title=\"Buy Bundles\", parent=home, next_form=bundle_form)\n```\n\n**step 4: Navigation controller**\n\nThe `NavigationController` object will be used to bind things together. It takes in `msisdn`, `session_id` and `ussd_string` plus your navigation `home`, then responds\nwith an appropriate response.\n\nWe will call this inside a simple flask application.\n\nIf you haven't, install flask: `pip install flask` in your virtualenv\n\n```python\nfrom flask import Flask, request\nfrom anysd import NavigationController\nfrom menu import home\n\napp = Flask(__name__)\n\n@app.get('/ussd')\ndef ussd_app():\n\n msisdn = request.args.get('msisdn')\n session_id = request.args.get('session_id')\n ussd_string = request.args.get('ussd_string')\n\n print(f\"{msisdn} :: {session_id} ::: {ussd_string}\")\n navigator = NavigationController(home, msisdn, session_id, ussd_string)\n msg = navigator.navigate()\n\n return msg\n\n\nif __name__ == '__main__':\n app.run()\n```\n\n**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**\n\n```yaml\n# config.yaml\n\nredis:\n host: localhost\n port: 6379\n db: 4\n```\n\nNow we are ready to run the application:\n\n```\n(venv) >>> flask run \n* Environment: production\n WARNING: This is a development server. Do not use it in a production deployment.\n Use a production WSGI server instead.\n * Debug mode: off\n2022-05-27 11:52:03,350 INFO _log (on line 224 ) : * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)\n\n```\n\nOur flask application will receive `GET` requests in this format: `http://host:port/ussd?msisdn=XXXXXXXXXXXX&session_id=XXXXXXXXXXXXX&ussd_string=XXX*XXX*XXX`\n\nNow Let's use postman to hit the endpoint\n\n![img.png](img.png)\n\nNow let's select option 1 and see what happens:\n\n![img_1.png](img_1.png)\n\n\n**Congratulations**. you have built a basic ussd application, with one level of navigation and a form.\nWe'll make more lessons on how to use anysd.\n\n### This is a new project, so many features are going to be added, progressively\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "For building ussd applications faster, with navigation management out of the box",
"version": "1.2.1",
"project_urls": {
"Homepage": "https://github.com/somwaki/anysd"
},
"split_keywords": [
"ussd",
" navigation"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0a189d9270349fe04dc2c3bead17bb218444e73e400584be991829f278ca9263",
"md5": "1a5d8dd1ff4ac99bc71a38ee4ee01dc7",
"sha256": "0a8214ed533b3316d5bff6c99017c475d1430736df5781e97285b5ac9c87b08d"
},
"downloads": -1,
"filename": "anysd-1.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1a5d8dd1ff4ac99bc71a38ee4ee01dc7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 13680,
"upload_time": "2024-11-12T11:51:47",
"upload_time_iso_8601": "2024-11-12T11:51:47.764834Z",
"url": "https://files.pythonhosted.org/packages/0a/18/9d9270349fe04dc2c3bead17bb218444e73e400584be991829f278ca9263/anysd-1.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d7ac774981c80fcfd558b15081df7832c95457b136327be6ee7e753aba09f75a",
"md5": "5bfe9062ffdf381b495e3075ee1d314b",
"sha256": "7735559606034dc0674ca94e2a6e2ba5bdacfb382da140394563ccbbe3c70a9a"
},
"downloads": -1,
"filename": "anysd-1.2.1.tar.gz",
"has_sig": false,
"md5_digest": "5bfe9062ffdf381b495e3075ee1d314b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 15247,
"upload_time": "2024-11-12T11:51:49",
"upload_time_iso_8601": "2024-11-12T11:51:49.970914Z",
"url": "https://files.pythonhosted.org/packages/d7/ac/774981c80fcfd558b15081df7832c95457b136327be6ee7e753aba09f75a/anysd-1.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-12 11:51:49",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "somwaki",
"github_project": "anysd",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "anysd"
}