From 1975b1bce744efc38f7f78ffa80ff91acda0a73b Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 13:24:29 +0100 Subject: [PATCH 01/60] chore: setup django and ready for USSD implementation --- .../__pycache__/views.cpython-36.pyc | Bin 0 -> 398 bytes africaTalkingUSSD/config/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 199 bytes .../__pycache__/settings.cpython-36.pyc | Bin 0 -> 2310 bytes .../config/__pycache__/urls.cpython-36.pyc | Bin 0 -> 1054 bytes .../config/__pycache__/wsgi.cpython-36.pyc | Bin 0 -> 611 bytes africaTalkingUSSD/config/settings.py | 120 ++++++++++++++++++ africaTalkingUSSD/config/urls.py | 23 ++++ africaTalkingUSSD/config/wsgi.py | 16 +++ africaTalkingUSSD/db.sqlite3 | Bin 0 -> 131072 bytes africaTalkingUSSD/manage.py | 15 +++ africaTalkingUSSD/test.py | 0 africaTalkingUSSD/views.py | 5 + requirements/base.txt | 2 + startup.sh | 4 + 15 files changed, 185 insertions(+) create mode 100644 africaTalkingUSSD/__pycache__/views.cpython-36.pyc create mode 100644 africaTalkingUSSD/config/__init__.py create mode 100644 africaTalkingUSSD/config/__pycache__/__init__.cpython-36.pyc create mode 100644 africaTalkingUSSD/config/__pycache__/settings.cpython-36.pyc create mode 100644 africaTalkingUSSD/config/__pycache__/urls.cpython-36.pyc create mode 100644 africaTalkingUSSD/config/__pycache__/wsgi.cpython-36.pyc create mode 100644 africaTalkingUSSD/config/settings.py create mode 100644 africaTalkingUSSD/config/urls.py create mode 100644 africaTalkingUSSD/config/wsgi.py create mode 100644 africaTalkingUSSD/db.sqlite3 create mode 100755 africaTalkingUSSD/manage.py create mode 100644 africaTalkingUSSD/test.py create mode 100644 africaTalkingUSSD/views.py create mode 100644 requirements/base.txt create mode 100644 startup.sh diff --git a/africaTalkingUSSD/__pycache__/views.cpython-36.pyc b/africaTalkingUSSD/__pycache__/views.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b352dfbe21d5ab091ae10e4c88f4fd8839aa4236 GIT binary patch literal 398 zcmZ8du};G<5VeyQDrh$b#(-ENp?Y9N2-FT`sStssNK{sQP0~7cu$`i^!q31bWo627)4BxZEaN84i#4!? z0gzm!z<9f8yNBH;$#|p%jB+j|sO%r_X|^ul*$%5sb5I$bg0=J7S~1@m3DJf1<_I-A nh|@T1_QjH`Ob>IsMr*ufTEl;!i``A{ywswWaN&>C>hyyjfi`9~ literal 0 HcmV?d00001 diff --git a/africaTalkingUSSD/config/__init__.py b/africaTalkingUSSD/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/africaTalkingUSSD/config/__pycache__/__init__.cpython-36.pyc b/africaTalkingUSSD/config/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0662ee493107c0fba766e4891bfc0011e42203b5 GIT binary patch literal 199 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 literal 0 HcmV?d00001 diff --git a/africaTalkingUSSD/config/settings.py b/africaTalkingUSSD/config/settings.py new file mode 100644 index 0000000..6380953 --- /dev/null +++ b/africaTalkingUSSD/config/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for africaTalkingUSSD project. + +Generated by 'django-admin startproject' using Django 2.0.6. + +For more information on this file, see +https://docs.djangoproject.com/en/2.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.0/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ.get('SECRET_KEY', 'something-secretive') + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/2.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.0/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/africaTalkingUSSD/config/urls.py b/africaTalkingUSSD/config/urls.py new file mode 100644 index 0000000..03d5265 --- /dev/null +++ b/africaTalkingUSSD/config/urls.py @@ -0,0 +1,23 @@ +"""africaTalkingUSSD URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path +import views + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', views.process_ussd_request, name="process_ussd_request") +] diff --git a/africaTalkingUSSD/config/wsgi.py b/africaTalkingUSSD/config/wsgi.py new file mode 100644 index 0000000..b27afab --- /dev/null +++ b/africaTalkingUSSD/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for africaTalkingUSSD project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_wsgi_application() diff --git a/africaTalkingUSSD/db.sqlite3 b/africaTalkingUSSD/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..c60262000d8da0fb9df3bb22135c5495d17f733a GIT binary patch literal 131072 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 S009U<00Izz00j2Fz<&W(9u~#` literal 0 HcmV?d00001 diff --git a/africaTalkingUSSD/manage.py b/africaTalkingUSSD/manage.py new file mode 100755 index 0000000..312127d --- /dev/null +++ b/africaTalkingUSSD/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) diff --git a/africaTalkingUSSD/test.py b/africaTalkingUSSD/test.py new file mode 100644 index 0000000..e69de29 diff --git a/africaTalkingUSSD/views.py b/africaTalkingUSSD/views.py new file mode 100644 index 0000000..171b20c --- /dev/null +++ b/africaTalkingUSSD/views.py @@ -0,0 +1,5 @@ +from django.http import HttpResponse + +def process_ussd_request(request): + return HttpResponse('Hello Africa Talking, It Peter Here!') + # import pdb; pdb.set_trace() \ No newline at end of file diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..696bcc2 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,2 @@ +Django==2.0.6 +pytz==2018.4 diff --git a/startup.sh b/startup.sh new file mode 100644 index 0000000..13e83b2 --- /dev/null +++ b/startup.sh @@ -0,0 +1,4 @@ +source ../venv/bin/activate +# 'qtoye7os0$^-05i+2&w#_t_4c_a0o%nmp!r0c3s(te0kx81t27' +SECRET_KEY='something-very-secretive' +PWD=root \ No newline at end of file From b8707bf59f80404c2710836fc6f9cbfa0445087d Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 14:10:26 +0100 Subject: [PATCH 02/60] removed python3 related script from codebase --- .../__pycache__/views.cpython-36.pyc | Bin 398 -> 0 bytes africaTalkingUSSD/config/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 199 -> 0 bytes .../__pycache__/settings.cpython-36.pyc | Bin 2310 -> 0 bytes .../config/__pycache__/urls.cpython-36.pyc | Bin 1054 -> 0 bytes .../config/__pycache__/wsgi.cpython-36.pyc | Bin 611 -> 0 bytes africaTalkingUSSD/config/settings.py | 120 ------------------ africaTalkingUSSD/config/urls.py | 23 ---- africaTalkingUSSD/config/wsgi.py | 16 --- africaTalkingUSSD/db.sqlite3 | Bin 131072 -> 0 bytes africaTalkingUSSD/manage.py | 15 --- africaTalkingUSSD/test.py | 0 africaTalkingUSSD/views.py | 5 - startup.sh | 2 +- 14 files changed, 1 insertion(+), 180 deletions(-) delete mode 100644 africaTalkingUSSD/__pycache__/views.cpython-36.pyc delete mode 100644 africaTalkingUSSD/config/__init__.py delete mode 100644 africaTalkingUSSD/config/__pycache__/__init__.cpython-36.pyc delete mode 100644 africaTalkingUSSD/config/__pycache__/settings.cpython-36.pyc delete mode 100644 africaTalkingUSSD/config/__pycache__/urls.cpython-36.pyc delete mode 100644 africaTalkingUSSD/config/__pycache__/wsgi.cpython-36.pyc delete mode 100644 africaTalkingUSSD/config/settings.py delete mode 100644 africaTalkingUSSD/config/urls.py delete mode 100644 africaTalkingUSSD/config/wsgi.py delete mode 100644 africaTalkingUSSD/db.sqlite3 delete mode 100755 africaTalkingUSSD/manage.py delete mode 100644 africaTalkingUSSD/test.py delete mode 100644 africaTalkingUSSD/views.py diff --git a/africaTalkingUSSD/__pycache__/views.cpython-36.pyc b/africaTalkingUSSD/__pycache__/views.cpython-36.pyc deleted file mode 100644 index b352dfbe21d5ab091ae10e4c88f4fd8839aa4236..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398 zcmZ8du};G<5VeyQDrh$b#(-ENp?Y9N2-FT`sStssNK{sQP0~7cu$`i^!q31bWo627)4BxZEaN84i#4!? z0gzm!z<9f8yNBH;$#|p%jB+j|sO%r_X|^ul*$%5sb5I$bg0=J7S~1@m3DJf1<_I-A nh|@T1_QjH`Ob>IsMr*ufTEl;!i``A{ywswWaN&>C>hyyjfi`9~ diff --git a/africaTalkingUSSD/config/__init__.py b/africaTalkingUSSD/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/africaTalkingUSSD/config/__pycache__/__init__.cpython-36.pyc b/africaTalkingUSSD/config/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 0662ee493107c0fba766e4891bfc0011e42203b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 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 diff --git a/africaTalkingUSSD/config/settings.py b/africaTalkingUSSD/config/settings.py deleted file mode 100644 index 6380953..0000000 --- a/africaTalkingUSSD/config/settings.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -Django settings for africaTalkingUSSD project. - -Generated by 'django-admin startproject' using Django 2.0.6. - -For more information on this file, see -https://docs.djangoproject.com/en/2.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.0/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get('SECRET_KEY', 'something-secretive') - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'config.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'config.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/2.0/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/2.0/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.0/howto/static-files/ - -STATIC_URL = '/static/' diff --git a/africaTalkingUSSD/config/urls.py b/africaTalkingUSSD/config/urls.py deleted file mode 100644 index 03d5265..0000000 --- a/africaTalkingUSSD/config/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -"""africaTalkingUSSD URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/2.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" -from django.contrib import admin -from django.urls import path -import views - -urlpatterns = [ - path('admin/', admin.site.urls), - path('', views.process_ussd_request, name="process_ussd_request") -] diff --git a/africaTalkingUSSD/config/wsgi.py b/africaTalkingUSSD/config/wsgi.py deleted file mode 100644 index b27afab..0000000 --- a/africaTalkingUSSD/config/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for africaTalkingUSSD project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") - -application = get_wsgi_application() diff --git a/africaTalkingUSSD/db.sqlite3 b/africaTalkingUSSD/db.sqlite3 deleted file mode 100644 index c60262000d8da0fb9df3bb22135c5495d17f733a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 S009U<00Izz00j2Fz<&W(9u~#` diff --git a/africaTalkingUSSD/manage.py b/africaTalkingUSSD/manage.py deleted file mode 100755 index 312127d..0000000 --- a/africaTalkingUSSD/manage.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) diff --git a/africaTalkingUSSD/test.py b/africaTalkingUSSD/test.py deleted file mode 100644 index e69de29..0000000 diff --git a/africaTalkingUSSD/views.py b/africaTalkingUSSD/views.py deleted file mode 100644 index 171b20c..0000000 --- a/africaTalkingUSSD/views.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.http import HttpResponse - -def process_ussd_request(request): - return HttpResponse('Hello Africa Talking, It Peter Here!') - # import pdb; pdb.set_trace() \ No newline at end of file diff --git a/startup.sh b/startup.sh index 13e83b2..a9276a4 100644 --- a/startup.sh +++ b/startup.sh @@ -1,4 +1,4 @@ -source ../venv/bin/activate +source venv/bin/activate # 'qtoye7os0$^-05i+2&w#_t_4c_a0o%nmp!r0c3s(te0kx81t27' SECRET_KEY='something-very-secretive' PWD=root \ No newline at end of file From 13a4ddc16bc5eb7ef249e0189dd852ecb28b200b Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 14:11:58 +0100 Subject: [PATCH 03/60] chore: started app with python2 --- .../AfricasTalkingGateway.py | 692 ++++++++++++++++++ .../AfricasTalkingGateway.pyc | Bin 0 -> 26792 bytes py_2_africaTalkingUSSD/config/__init__.py | 0 py_2_africaTalkingUSSD/config/__init__.pyc | Bin 0 -> 208 bytes py_2_africaTalkingUSSD/config/settings.py | 120 +++ py_2_africaTalkingUSSD/config/settings.pyc | Bin 0 -> 2645 bytes py_2_africaTalkingUSSD/config/urls.py | 23 + py_2_africaTalkingUSSD/config/urls.pyc | Bin 0 -> 1150 bytes py_2_africaTalkingUSSD/config/wsgi.py | 16 + py_2_africaTalkingUSSD/config/wsgi.pyc | Bin 0 -> 680 bytes py_2_africaTalkingUSSD/db.sqlite3 | Bin 0 -> 131072 bytes py_2_africaTalkingUSSD/manage.py | 22 + py_2_africaTalkingUSSD/test.py | 0 py_2_africaTalkingUSSD/utils.py | 36 + py_2_africaTalkingUSSD/views.py | 28 + py_2_africaTalkingUSSD/views.pyc | Bin 0 -> 1002 bytes 16 files changed, 937 insertions(+) create mode 100644 py_2_africaTalkingUSSD/AfricasTalkingGateway.py create mode 100644 py_2_africaTalkingUSSD/AfricasTalkingGateway.pyc create mode 100644 py_2_africaTalkingUSSD/config/__init__.py create mode 100644 py_2_africaTalkingUSSD/config/__init__.pyc create mode 100644 py_2_africaTalkingUSSD/config/settings.py create mode 100644 py_2_africaTalkingUSSD/config/settings.pyc create mode 100644 py_2_africaTalkingUSSD/config/urls.py create mode 100644 py_2_africaTalkingUSSD/config/urls.pyc create mode 100644 py_2_africaTalkingUSSD/config/wsgi.py create mode 100644 py_2_africaTalkingUSSD/config/wsgi.pyc create mode 100644 py_2_africaTalkingUSSD/db.sqlite3 create mode 100755 py_2_africaTalkingUSSD/manage.py create mode 100644 py_2_africaTalkingUSSD/test.py create mode 100644 py_2_africaTalkingUSSD/utils.py create mode 100644 py_2_africaTalkingUSSD/views.py create mode 100644 py_2_africaTalkingUSSD/views.pyc diff --git a/py_2_africaTalkingUSSD/AfricasTalkingGateway.py b/py_2_africaTalkingUSSD/AfricasTalkingGateway.py new file mode 100644 index 0000000..ec48322 --- /dev/null +++ b/py_2_africaTalkingUSSD/AfricasTalkingGateway.py @@ -0,0 +1,692 @@ +""" + COPYRIGHT (C) 2014 AFRICASTALKING LTD # + + AFRICAStALKING SMS GATEWAY CLASS IS A FREE SOFTWARE IE. CAN BE MODIFIED AND/OR REDISTRIBUTED + UNDER THER TERMS OF GNU GENERAL PUBLIC LICENCES AS PUBLISHED BY THE + FREE SOFTWARE FOUNDATION VERSION 3 OR ANY LATER VERSION + + THE CLASS IS DISTRIBUTED ON 'AS IS' BASIS WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO + THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import urllib +import urllib2 +import json + +class AfricasTalkingGatewayException(Exception): + pass + +class AfricasTalkingGateway: + + def __init__(self, username_, apiKey_): + self.username = username_ + self.apiKey = apiKey_ + self.environment = 'sandbox' if username_ is 'sandbox' else 'prod' + + self.HTTP_RESPONSE_OK = 200 + self.HTTP_RESPONSE_CREATED = 201 + + # Turn this on if you run into problems. It will print the raw HTTP response from our server + self.Debug = True + + def generateAuthToken(self): + parameters = {'username': self.username} + url = self.getGenerateAuthTokenUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + # Messaging methods + def sendMessage(self, to_, message_, from_ = None, bulkSMSMode_ = 1, enqueue_ = 0, keyword_ = None, linkId_ = None, retryDurationInHours_ = None, authToken_ = None): + if len(to_) == 0 or len(message_) == 0: + raise AfricasTalkingGatewayException("Please provide both to_ and message_ parameters") + + parameters = {'username' : self.username, + 'to': to_, + 'message': message_, + 'bulkSMSMode':bulkSMSMode_} + + if not from_ is None : + parameters["from"] = from_ + + if enqueue_ > 0: + parameters["enqueue"] = enqueue_ + + if not keyword_ is None: + parameters["keyword"] = keyword_ + + if not linkId_ is None: + parameters["linkId"] = linkId_ + + if not retryDurationInHours_ is None: + parameters["retryDurationInHours"] = retryDurationInHours_ + + response = self.sendRequest(self.getSmsUrl(), parameters, authToken_) + + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + recipients = decoded['SMSMessageData']['Recipients'] + + if len(recipients) > 0: + return recipients + + raise AfricasTalkingGatewayException(decoded['SMSMessageData']['Message']) + + raise AfricasTalkingGatewayException(response) + + + def fetchMessages(self, lastReceivedId_ = 0): + url = "%s?username=%s&lastReceivedId=%s" % (self.getSmsUrl(), self.username, lastReceivedId_) + response = self.sendRequest(url) + + if self.responseCode == self.HTTP_RESPONSE_OK: + decoded = json.loads(response) + return decoded['SMSMessageData']['Messages'] + raise AfricasTalkingGatewayException(response) + + + # Subscription methods + def createSubscription(self, phoneNumber_, shortCode_, keyword_, checkoutToken_): + if len(phoneNumber_) == 0 or len(shortCode_) == 0 or len(keyword_) == 0: + raise AfricasTalkingGatewayException("Please supply phone number, short code and keyword") + + url = "%s/create" %(self.getSmsSubscriptionUrl()) + parameters = { + 'username' : self.username, + 'phoneNumber' : phoneNumber_, + 'shortCode' : shortCode_, + 'keyword' : keyword_, + "checkoutToken" : checkoutToken_ + } + + response = self.sendRequest (url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + return decoded + raise AfricasTalkingGatewayException(response) + + + def deleteSubscription(self, phoneNumber_, shortCode_, keyword_): + if len(phoneNumber_) == 0 or len(shortCode_) == 0 or len(keyword_) == 0: + raise AfricasTalkingGatewayException("Please supply phone number, short code and keyword") + + url = "%s/delete" %(self.getSmsSubscriptionUrl()) + parameters = { + 'username' :self.username, + 'phoneNumber' :phoneNumber_, + 'shortCode' :shortCode_, + 'keyword' :keyword_ + } + response = self.sendRequest(url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + return decoded + raise AfricasTalkingGatewayException(response) + + + def fetchPremiumSubscriptions(self,shortCode_, keyword_, lastReceivedId_ = 0): + if len(shortCode_) == 0 or len(keyword_) == 0: + raise AfricasTalkingGatewayException("Please supply the short code and keyword") + + url = "%s?username=%s&shortCode=%s&keyword=%s&lastReceivedId=%s" % (self.getSmsSubscriptionUrl(), + self.username, + shortCode_, + keyword_, + lastReceivedId_) + result = self.sendRequest(url) + if self.responseCode == self.HTTP_RESPONSE_OK: + decoded = json.loads(result) + return decoded['responses'] + + raise AfricasTalkingGatewayException(response) + + + # Voice methods + def call(self, from_, to_): + parameters = { + 'username' : self.username, + 'from' : from_, + 'to': to_ + } + + url = "%s/call" %(self.getVoiceUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + if decoded['errorMessage'] == "None": + return decoded['entries']; + raise AfricasTalkingGatewayException(decoded['errorMessage']) + + def getNumQueuedCalls(self, phoneNumber_, queueName_ = None): + parameters = { + 'username' :self.username, + 'phoneNumbers' :phoneNumber_ + } + + if queueName_ is not None: + parameters['queueName'] = queueName_ + + url = "%s/queueStatus" %(self.getVoiceUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + if decoded['errorMessage'] == "None": + return decoded['entries'] + + raise AfricasTalkingGatewayException(decoded['errorMessage']) + + def uploadMediaFile(self, urlString_): + parameters = { + 'username' :self.username, + 'url' :urlString_ + } + url = "%s/mediaUpload" %(self.getVoiceUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + if decoded['errorMessage'] != "None": + raise AfricasTalkingGatewayException(decoded['errorMessage']) + + #Airtime method + def sendAirtime(self, recipients_): + parameters = { + 'username' : self.username, + 'recipients' : json.dumps(recipients_) + } + + url = "%s/send" %(self.getAirtimeUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + responses = decoded['responses'] + if self.responseCode == self.HTTP_RESPONSE_CREATED: + if len(responses) > 0: + return responses + raise AfricasTalkingGatewayException(decoded["errorMessage"]) + raise AfricasTalkingGatewayException(response) + + #USSD Push method + def sendUssdPush(self, phoneNumber_, menu_, checkoutToken_): + parameters = { + 'username' : self.username, + 'phoneNumber' : phoneNumber_, + 'menu' : menu_, + 'checkoutToken' : checkoutToken_ + } + + url = self.getUssdPushUrl() + response = self.sendRequest(url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if decoded['status'] == 'Queued': + return decoded['sessionId'] + raise AfricasTalkingGatewayException(decoded["errorMessage"]) + raise AfricasTalkingGatewayException(response) + + #Checkout Token Request + def createCheckoutToken(self, phoneNumber_): + parameters = { + 'phoneNumber' : phoneNumber_ + } + + url = "%s/checkout/token/create" %(self.getApiHost()) + response = self.sendRequest(url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if decoded['token'] == 'None': + raise AfricasTalkingGatewayException(decoded['token']) + return decoded['description'] + raise AfricasTalkingGatewayException(response) + + #Payment Methods + def bankPaymentCheckoutCharge(self, + productName_, + bankAccount_, + currencyCode_, + amount_, + narration_, + metadata_ = None): + + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'bankAccount' : bankAccount_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'narration' : narration_ + } + + if metadata_: + parameters['metadata'] = metadata_ + + url = self.getBankPaymentCheckoutChargeUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'PendingValidation': + return responseObj['transactionId'] + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def bankPaymentCheckoutValidation(self, + transactionId_, + otp_): + + parameters = { + 'username' : self.username, + 'transactionId' : transactionId_, + 'otp' : otp_ + } + + url = self.getBankPaymentCheckoutValidationUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'Success': return + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def bankPaymentTransfer(self, + productName_, + recipients_): + + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'recipients' : recipients_ + } + + url = self.getBankPaymentTransferUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if len(responseObj['entries']): + return responseObj['entries'] + raise AfricasTalkingGatewayException(responseObj['errorMessage']) + raise AfricasTalkingGatewayException(response) + + def cardPaymentCheckoutCharge(self, + productName_, + paymentCard_, + currencyCode_, + amount_, + narration_, + metadata_ = None): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'paymentCard' : paymentCard_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'narration' : narration_ + } + + if metadata_: + parameters['metadata'] = metadata_ + + url = self.getCardPaymentCheckoutChargeUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'PendingValidation': + return responseObj['transactionId'] + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def cardPaymentCheckoutChargeWithToken(self, + productName_, + checkoutToken_, + currencyCode_, + amount_, + narration_, + metadata_ = None): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'checkoutToken' : checkoutToken_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'narration' : narration_ + } + + if metadata_: + parameters['metadata'] = metadata_ + + url = self.getCardPaymentCheckoutChargeUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'Success': return + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def cardPaymentCheckoutValidation(self, + transactionId_, + otp_): + + parameters = { + 'username' : self.username, + 'transactionId' : transactionId_, + 'otp' : otp_ + } + + url = self.getCardPaymentCheckoutValidationUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'Success': + return responseObj['checkoutToken'] + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def paymentStashTopup(self, + productName_, + currencyCode_, + amount_, + metadata_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + url = self.getPaymentStashTopupUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentWalletTransfer(self, + productName_, + targetUsername_, + targetProductName_, + currencyCode_, + amount_, + metadata_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'targetUsername' : targetUsername_, + 'targetProductName' : targetProductName_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + url = self.getPaymentWalletTransferUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentWalletBalanceQuery(self): + url = self.getPaymentWalletBalanceQueryUrl() + response = self.sendRequest(url + "?username=%s" % self.username) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentTransactionFindQuery(self, + transactionId_): + url = self.getPaymentTransactionFindQueryUrl() + response = self.sendRequest(url + "?username=%s&transactionId=%s" % (self.username, transactionId_)) + if self.responseCode == self.HTTP_RESPONSE_OK: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def initiateMobilePaymentCheckout(self, + productName_, + phoneNumber_, + currencyCode_, + amount_, + metadata_, + providerChannel_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'phoneNumber' : phoneNumber_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + + if providerChannel_ is not None: + parameters['providerChannel'] = providerChannel_ + + url = self.getMobilePaymentCheckoutUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if decoded['status'] == 'PendingConfirmation': + return decoded['transactionId'] + raise AfricasTalkingGatewayException(decoded['description']) + raise AfricasTalkingGatewayException(response) + + def mobilePaymentB2CRequest(self, productName_, recipients_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'recipients' : recipients_ + } + url = self.getMobilePaymentB2CUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if len(decoded['entries']) > 0: + return decoded['entries'] + raise AfricasTalkingGatewayException(decoded['errorMessage']) + raise AfricasTalkingGatewayException(response) + + def mobilePaymentB2BRequest(self, productName_, providerData_, currencyCode_, amount_, metadata_): + if "provider" not in providerData_: + raise AfricasTalkingGatewayException("Missing field provider") + + if "destinationChannel" not in providerData_: + raise AfricasTalkingGatewayException("Missing field destinationChannel") + + if "transferType" not in providerData_: + raise AfricasTalkingGatewayException("Missing field transferType") + + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'provider' : providerData_['provider'], + 'destinationChannel' : providerData_['destinationChannel'], + 'transferType' : providerData_['transferType'], + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + if "destinationAccount" in providerData_: + parameters['destinationAccount'] = providerData_['destinationAccount'] + + url = self.getMobilePaymentB2BUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + return decoded + raise AfricasTalkingGatewayException(response) + + def mobilePaymentB2BRequest(self, + productName_, + provider_, + transferType_, + currencyCode_, + amount_, + metadata_, + destinationChannel_, + destinationAccount_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'provider' : provider_, + 'transferType' : transferType_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'destinationChannel' : destinationChannel_, + } + if metadata_ is not None: + parameters['metadata'] = metadata_ + + if destinationAccount_ is not None: + parameters['destinationAccount'] = destinationAccount_ + + url = self.getMobilePaymentB2BUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentBankWithdrawalRequest(self, + productName_, + bankAccountName_, + currencyCode_, + amount_, + metadata_): + parameters = { + 'username' : self.username, + 'bankAccountName' : bankAccountName_, + 'productName' : productName_, + 'currencyCode' : currencyCode_, + 'amount' : amount_ + } + if metadata_ is not None: + parameters['metadata'] = metadata_ + + url = self.getPaymentBankWithdrawalUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + # Userdata method + def getUserData(self): + url = "%s?username=%s" %(self.getUserDataUrl(), self.username) + result = self.sendRequest(url) + if self.responseCode == self.HTTP_RESPONSE_OK: + decoded = json.loads(result) + return decoded['UserData'] + raise AfricasTalkingGatewayException(response) + + # HTTP access method + def sendRequest(self, urlString, data_ = None, authToken_ = None): + try: + headers = {'Accept' : 'application/json'} + if authToken_ is None: + headers['apikey'] = self.apiKey + else: + headers['authToken'] = authToken_ + + if data_ is not None: + data = urllib.urlencode(data_) + request = urllib2.Request(urlString, data, headers = headers) + else: + request = urllib2.Request(urlString, headers = headers) + response = urllib2.urlopen(request) + except urllib2.HTTPError as e: + raise AfricasTalkingGatewayException(e.read()) + else: + self.responseCode = response.getcode() + response = ''.join(response.readlines()) + if self.Debug: + print "Raw response: " + response + + return response + + def sendJSONRequest(self, urlString, data_): + try: + headers = {'Accept' : 'application/json', + 'Content-Type' : 'application/json', + 'apikey' : self.apiKey} + request = urllib2.Request(urlString, + data_, + headers = headers) + response = urllib2.urlopen(request) + except urllib2.HTTPError as e: + raise AfricasTalkingGatewayException(e.read()) + else: + self.responseCode = response.getcode() + response = ''.join(response.readlines()) + if self.Debug: + print "Raw response: " + response + + return response + + def getApiHost(self): + if self.environment == 'sandbox': + return 'https://api.sandbox.africastalking.com' + else: + return 'https://api.africastalking.com' + + def getPaymentHost(self): + if self.environment == 'sandbox': + return 'https://payments.sandbox.africastalking.com' + else: + return 'https://payments.africastalking.com' + + def getVoiceHost(self): + if self.environment == 'sandbox': + return 'https://voice.sandbox.africastalking.com' + else: + return 'https://voice.africastalking.com' + + def getGenerateAuthTokenUrl(self): + return self.getApiHost() + "/auth-token/generate" + + def getSmsUrl(self): + return self.getApiHost() + "/version1/messaging" + + def getVoiceUrl(self): + return self.getVoiceHost() + + def getSmsSubscriptionUrl(self): + return self.getApiHost() + "/version1/subscription" + + def getUserDataUrl(self): + return self.getApiHost() + "/version1/user" + + def getAirtimeUrl(self): + return self.getApiHost() + "/version1/airtime" + + def getUssdPushUrl(self): + return self.getApiHost() + "/ussd/push/request" + + def getMobilePaymentCheckoutUrl(self): + return self.getPaymentHost() + "/mobile/checkout/request" + + def getMobilePaymentB2CUrl(self): + return self.getPaymentHost() + "/mobile/b2c/request" + + def getMobilePaymentB2BUrl(self): + return self.getPaymentHost() + "/mobile/b2b/request" + + def getPaymentBankWithdrawalUrl(self): + return self.getPaymentHost() + "/bank-withdrawal" + + def getBankPaymentCheckoutChargeUrl(self): + return self.getPaymentHost() + "/bank/checkout/charge" + + def getBankPaymentCheckoutValidationUrl(self): + return self.getPaymentHost() + "/bank/checkout/validate" + + def getBankPaymentTransferUrl(self): + return self.getPaymentHost() + "/bank/transfer" + + def getCardPaymentCheckoutChargeUrl(self): + return self.getPaymentHost() + "/card/checkout/charge" + + def getCardPaymentCheckoutValidationUrl(self): + return self.getPaymentHost() + "/card/checkout/validate" + + def getPaymentStashTopupUrl(self): + return self.getPaymentHost() + "/topup/stash" + + def getPaymentWalletTransferUrl(self): + return self.getPaymentHost() + "/transfer/wallet" + + def getPaymentWalletBalanceQueryUrl(self): + return self.getPaymentHost() + "/query/wallet/balance" + + def getPaymentTransactionFindQueryUrl(self): + return self.getPaymentHost() + "/query/transaction/find" + diff --git a/py_2_africaTalkingUSSD/AfricasTalkingGateway.pyc b/py_2_africaTalkingUSSD/AfricasTalkingGateway.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64f80a7fd55c6d3a9de2b953c4bdf03caa635483 GIT binary patch literal 26792 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 literal 0 HcmV?d00001 diff --git a/py_2_africaTalkingUSSD/config/__init__.py b/py_2_africaTalkingUSSD/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py_2_africaTalkingUSSD/config/__init__.pyc b/py_2_africaTalkingUSSD/config/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96cb7e37e6c3ebbf5e30caebcb4c1cdabb54a6ca GIT binary patch literal 208 zcmZ9GF$=;l5QQT;h~S@a>g0j5h|tb1)yW|o$)ycVlai!Dez$+XE4b*v`wkwDd$)e? zcSawcN_=O<`^riih8Ci&7Uxzm#2->&5Be|?Oq3}GhsF8#(ZRc~JeCK5UH ziU3Wt3h(F!>pOU~ZBqwLb&t-G@Ay)YC;1avGN6JtwgV}xw^_>LRPvVF&tuTDqs#JR GVu=rdbvL{K literal 0 HcmV?d00001 diff --git a/py_2_africaTalkingUSSD/config/settings.py b/py_2_africaTalkingUSSD/config/settings.py new file mode 100644 index 0000000..2116d8f --- /dev/null +++ b/py_2_africaTalkingUSSD/config/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for py_2_africaTalkingUSSD. + +Generated by 'django-admin startproject' using Django 1.11.13. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'u2q$$b@zu9hi3=##rq*($pm70$$pl&z-o*bgfi+hslz_si2=ax' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/py_2_africaTalkingUSSD/config/settings.pyc b/py_2_africaTalkingUSSD/config/settings.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92dc84e7957193f295a8c09d44fa33f4012d97d9 GIT binary patch literal 2645 zcmb7Gdvnu95MSHDP7L80-leFJLK+&0B%RQ93Pso=c<>_=$)r_hs*!bP`+{E4oe1Gy zK1n}gKR~-D`NhODZ7lh;w~ycM?d_ga`g^(h7kTi92cusFpD*#xjRF7xuyHH^DS}@B zy8yHRhebFnogx;1o`cVY(QTK3l)$b4T?Bgx28dGyvH;{95ENB_ECNA(8SL{wP=w3q-51&z>pvgC|V_%V)2IMi=E7>xyXXA}*#E{m&#;A&5u6-xd`3%!ptslDKAW}FLf`tRF^rJ+HPn<2s z?I+B0E!TgK{H|%%)oOK@hBR>nB}(r^c}z0X4VMHgRJd>x5hu|h^~7T(^ajFC%2tivo>?nuOFu z6f=)&(w~NG`}xC%$%BprJsEQUlf&88bN3@(L9={WxSmiX ztfyv49Lzol~XTGm^e|*5z(5+_zzQy@yf=5s51~|9^8zfEXYwxX7q5kgD6yM+2g;pdD2&F zIJ{%@ct-o#6nh+$i&>owNXI#56ANCJ?!rP*NGd@PApyW8PoGR zduz1oVgc!TW8V~uNa%Zej-2qdIjC7teH0FMn};ik62zyybimjDpK?Nvu&)GvGFAT~ z<8$!kPVnv1`jafru^G$6RkS-T&lca=vl^YYnY9M0$YdV#0aM*L#y!ZkdA9sXZ<y2WiPNr@Wfr@fA4dss@YG=> z^Q?9`O>`kUKNZwu0rOqiQtxsN%=?QofVEi2f;4E-a3F?Lh+oeDk$2eMDAFm$%QIlK z(}3bD<21-Q1fk(Z%0&sOu2ti*5H);bV8dx^w{ems(sA&>cN|efM#QtSja@O!lgyG3 znDF~imob{+7V7F<>y5Lgo901hzwW%#n+=qAuuWxb zO})L_)prf2)~Or0qejbc>`vRrmfFRYHa353PbSUHr|qm_rNpxH6j&HUO@_OMkT4zi z2~M?P6i_Xuf+msgp0Mz}tJNb9cQF)Pa}#gKK2WWnQ0lv(N3}0L2sHUcP>Tqy4PD>I z%0r~NUkzH`e3N&l8lPk_{}Mzb_2~Jl3)+^;wxmYfj3gc^OVNA*AQmI@j d6)M?PD&~@va;{$~+*?8!a>xJWMa0Ul^1rvv3i<#5 literal 0 HcmV?d00001 diff --git a/py_2_africaTalkingUSSD/config/urls.py b/py_2_africaTalkingUSSD/config/urls.py new file mode 100644 index 0000000..8048842 --- /dev/null +++ b/py_2_africaTalkingUSSD/config/urls.py @@ -0,0 +1,23 @@ +"""py_2_africaTalkingUSSD URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url +from django.contrib import admin +import views + +urlpatterns = [ + url(r'', views.process_ussd, name="process_ussd"), + url(r'^admin/', admin.site.urls), +] diff --git a/py_2_africaTalkingUSSD/config/urls.pyc b/py_2_africaTalkingUSSD/config/urls.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8aebbd511a78ea65e839207e4b4b647e6f711b5e GIT binary patch literal 1150 zcma)5OK%e~5T3mHSPJyQ-9ku7q_S3ejGzLgR8&G7()JRhZr$}Haq6{KwzrToCw?zK z0LGgH+6tsPyWW|}c;=gLJguKklOufjC5QFu(Dxm^K3XKeg!Tr=Cg=tfO_(&H5%m`6 z78Grmv{!l?bO$CKz$QrAJFwUSNzXQXZ#JTF2e1u`ZCLD(1!fd!7i1gGdb=^%k0wC5 z%nma#bt)ILW*6g6Y-G(;%_=8?vL;E+=g6)qr%Mq6I^(aHRz5IitAIWp z@GMw%tMIE&*@<TPkIW+u6Gm$uLg|N`OPl*tE`*uc(%A*(AU z(eOb!81P_Am3tn&Ipy~}`S48?6wD8klgi`~-YRb$b&#_6QZiwfDoX3Zkg=(=1uK@B zD9g3+j{o7>zpj}oxKkn>C9UVWm#?}9>(b`7zyoGPfp5D}?)Hp0DYubHU3BAi6>Iwvlyn=e}L)KAq!YTz6<3Q`ULC3Rnw~jPQiaBy8rPM=Jc?d_^jcH-wi+17o#VDsvOV literal 0 HcmV?d00001 diff --git a/py_2_africaTalkingUSSD/config/wsgi.py b/py_2_africaTalkingUSSD/config/wsgi.py new file mode 100644 index 0000000..1d014de --- /dev/null +++ b/py_2_africaTalkingUSSD/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for py_2_africaTalkingUSSD project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_wsgi_application() diff --git a/py_2_africaTalkingUSSD/config/wsgi.pyc b/py_2_africaTalkingUSSD/config/wsgi.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c943442331fb20ece97d7f42e9db84d3d06a4753 GIT binary patch literal 680 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 literal 0 HcmV?d00001 diff --git a/py_2_africaTalkingUSSD/manage.py b/py_2_africaTalkingUSSD/manage.py new file mode 100755 index 0000000..68141ee --- /dev/null +++ b/py_2_africaTalkingUSSD/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) + raise + execute_from_command_line(sys.argv) diff --git a/py_2_africaTalkingUSSD/test.py b/py_2_africaTalkingUSSD/test.py new file mode 100644 index 0000000..e69de29 diff --git a/py_2_africaTalkingUSSD/utils.py b/py_2_africaTalkingUSSD/utils.py new file mode 100644 index 0000000..ac9473d --- /dev/null +++ b/py_2_africaTalkingUSSD/utils.py @@ -0,0 +1,36 @@ +from AfricasTalkingGateway import AfricasTalkingGateway, AfricasTalkingGatewayException + +class AfricasTalkingUtils: + def __init__(self): + #Specify your credentials + self.username = "sandbox" + self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" + #Create an instance of our awesome gateway class and pass your credentials + self.gateway = AfricasTalkingGateway(self.username, self.apiKey) + + def bank_checkout(self): + try: + # Send the request to the gateway. If successful, we will respond with + # a transactionId that you can then use to validate the transaction + transactionId = self.gateway.bankPaymentCheckoutCharge( + productName_ = 'Airtime Distribution', + currencyCode_ = 'NGN', + amount_ = 100, + narration_ = 'Airtime Purchase Request', + bankAccount_ = { + 'accountName' : 'Fela Kuti', + 'accountNumber' : '123456789', + 'bankCode' : 234001 + }, + metadata_ = { + 'Reason' : 'To Test The Gateways' + } + ) + print transactionId + except AfricasTalkingGatewayException, e: + print 'Encountered an error while sending: %s' % str(e) + +africa_talking = AfricasTalkingUtils() +fff=africa_talking.bank_checkout() +import pdb; pdb.set_trace() +pass \ No newline at end of file diff --git a/py_2_africaTalkingUSSD/views.py b/py_2_africaTalkingUSSD/views.py new file mode 100644 index 0000000..02e7a51 --- /dev/null +++ b/py_2_africaTalkingUSSD/views.py @@ -0,0 +1,28 @@ +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt + +@csrf_exempt +def process_ussd(request): + ## MENU + MY_COPERATIVE = 1 + WAZOBIA_LOANS = 2 + JOIN_AGBETUNTU = 3 + REQUEST_A_CALL = 4 + + ## SUB MENU + # MY_COPERATIVE SUBMENU + CHECK_BALANCE = 1*1 + REQUEST_LOAN = 1*2 + MAKE_DEPOSIT = 1*3 + # WAZOBIA SUBMENU + REGISTER = 2*1 + REQUEST_LOAN = 2*2 + MAKE_DEPOSIT = 2*3 + REQUEST_LOAN = 2*4 + REQUEST_A_CALL = 2*5 + + if request.POST: + + import pdb; pdb.set_trace() + return HttpResponse('Hello Africa Talking, It Peter Here!') + # import pdb; pdb.set_trace() \ No newline at end of file diff --git a/py_2_africaTalkingUSSD/views.pyc b/py_2_africaTalkingUSSD/views.pyc new file mode 100644 index 0000000000000000000000000000000000000000..092a19803c5f05be824c772a6b42dc269147fd97 GIT binary patch literal 1002 zcmc&y&2G~`5FXoU+N4d}2jCD8ha6BW!4)A~*J?u3B()t7tK%1S)s|Z11--pJ(T9$NJG}U*fY*32Yye_P4a;D*{2E0U5y3fGYzG)=kJvSXyvp zfq@nX3%&z9(EkRA1_ZqwI+y;Wr)Q9)4hRNHl|IU{$R%#rePRL!Rb)Vm<|q22>=pqY+l4z+y5_V)k_fNBAP z>;W=z+k*QREKGO+FmlS0T<>xbLGPFXk5kSkL;%4zR+f|GLo*wXY^S++*OXd(0_@=_Cj#8GurJRxR2ksz_CFf<7tG zV{5fbP~n#qN~Jm^Uwn+*$<*_maQNQS%m?Rcax!$H(Zm@C>X2;SO@`yhIXm&f**KhO z%J(j3UJyo3 Date: Thu, 14 Jun 2018 15:14:29 +0100 Subject: [PATCH 04/60] chore: set base for USSD --- py_2_africaTalkingUSSD/utils.py | 7 ++- py_2_africaTalkingUSSD/views.py | 81 +++++++++++++++++++++++++------ py_2_africaTalkingUSSD/views.pyc | Bin 1002 -> 2499 bytes requirements/base.txt | 2 - 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/py_2_africaTalkingUSSD/utils.py b/py_2_africaTalkingUSSD/utils.py index ac9473d..486b311 100644 --- a/py_2_africaTalkingUSSD/utils.py +++ b/py_2_africaTalkingUSSD/utils.py @@ -30,7 +30,6 @@ def bank_checkout(self): except AfricasTalkingGatewayException, e: print 'Encountered an error while sending: %s' % str(e) -africa_talking = AfricasTalkingUtils() -fff=africa_talking.bank_checkout() -import pdb; pdb.set_trace() -pass \ No newline at end of file + def ussd(self, service_code, session_id, phone_number, text): + import pdb; pdb.set_trace() + \ No newline at end of file diff --git a/py_2_africaTalkingUSSD/views.py b/py_2_africaTalkingUSSD/views.py index 02e7a51..cf9e383 100644 --- a/py_2_africaTalkingUSSD/views.py +++ b/py_2_africaTalkingUSSD/views.py @@ -1,28 +1,79 @@ from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt +from utils import AfricasTalkingUtils @csrf_exempt def process_ussd(request): ## MENU - MY_COPERATIVE = 1 - WAZOBIA_LOANS = 2 - JOIN_AGBETUNTU = 3 - REQUEST_A_CALL = 4 + MY_COPERATIVE = '1' + WAZOBIA_LOANS = '2' + JOIN_AGBETUNTU = '3' + REQUEST_A_CALL = '4' ## SUB MENU # MY_COPERATIVE SUBMENU - CHECK_BALANCE = 1*1 - REQUEST_LOAN = 1*2 - MAKE_DEPOSIT = 1*3 + CHECK_BALANCE = '1*1' + REQUEST_LOAN = '1*2' + MAKE_DEPOSIT = '1*3' # WAZOBIA SUBMENU - REGISTER = 2*1 - REQUEST_LOAN = 2*2 - MAKE_DEPOSIT = 2*3 - REQUEST_LOAN = 2*4 - REQUEST_A_CALL = 2*5 + REGISTER = '2*1' + REPAY_LOAN = '2*2' + MAKE_DEPOSIT = '2*3' + REQUEST_LOAN_2 = '2*4' + REQUEST_A_CALL_2 = '2*5' - if request.POST: + if request.method == 'POST': + session_id = request.POST.get('sessionId') + service_code = request.POST.get('serviceCode') + phone_number = request.POST.get('phoneNumber') + text = request.POST.get('text') + + # main menu + if text == "": + response = "CON WELCOME, What would you want to check. \n" + response += "1. My Cooperative \n" + response += "2. Wazobia Loans \n" + elif text == MY_COPERATIVE: + response = "CON What would you like to do in You Cooperative account. \n" + response += "1. Check Balance \n" + response += "2. Request Loan \n" + response += "3. Make Deposit \n" + elif text == WAZOBIA_LOANS: + response = "CON WELCOME to Wazobia Loans \n" + response += "1. Register \n" + response += "2. Repay Loan \n" + response += "3. Make Deposit \n" + response += "4. Request Loan \n" + response += "5. Request a call \n" + elif text == JOIN_AGBETUNTU: + response = "CON Welcome to Agbetuntu" + + # multiple options + elif text == REQUEST_A_CALL or text == REQUEST_A_CALL_2: + response = "Your Request has been recorded, An agent will call soon! #CHEERS " + elif text == REQUEST_LOAN_2 or text == REQUEST_LOAN: + balance = 50 + response = "CON Loan Repaid, \n" + response += "New Balance:", balance + + # sub menu + elif text == CHECK_BALANCE : + balance = 200 + response = "CON Balance:", balance, "\n" + elif text == MAKE_DEPOSIT: + balance = 1000 + response = "CON Deposit was successful,\n" + response += "New Balance:", balance + + elif text == REGISTER: + response = "CON Your Name" + else: + response = "CON You selected a wrong option, please try again\n" + response += "1. My Account \n" + response += "2. My Phone Number \n" + africa_talking = AfricasTalkingUtils() + else: + response = "Ooops, Sorry..." - import pdb; pdb.set_trace() - return HttpResponse('Hello Africa Talking, It Peter Here!') + return HttpResponse(response) # import pdb; pdb.set_trace() \ No newline at end of file diff --git a/py_2_africaTalkingUSSD/views.pyc b/py_2_africaTalkingUSSD/views.pyc index 092a19803c5f05be824c772a6b42dc269147fd97..d70ba7c6dbb7157c333cea7b1d66fe7659b9241b 100644 GIT binary patch literal 2499 zcmc&$-E!MR6h4v@CvlSIuRtiJY+HUz2=>HnhoLYGisNZR6Fb-rnT89EqTSePWXWjP zP0YmHln3BVxZ)jp&kWDN43}Io1Dtatr)h_KEPJ*4={w&!yL*mTxj*L%<+EGg`jCBW zynl&5UByP?*MbnBXThNb7L9EPZRq9TFqe*V5KcgE5)LQ9QaO+l@FT!8k0Y&8bQV!xC&#WjQ@y@P!4(9Z<0SAs-S)zhAnNR!Ak;qT z(6b!=C`!GJL1UaFi=rFZAagL{NJbMB!)Ov@9!40?6pW@oPQz##OS7=hBIVPmY0weR6kVQtWTtb!@S%C8ekjt$39OMd& z7U6sm%GRkNLffcx0v`B$k)fjH!g^)7ri%AlM0*IY_e&|Yh#nQE{NAJdZ`I{ ze)|Huk;?o*A)e=0=D?EgkWBO&gA)YmnElK$;mneP3BNF*z=WTekY~crmb?k0C6Kqq z;oOGvB{;TIXFssnBvXI23+?K<~N4xRywmLbBV_?TE`f;I$qxK%7+O4SV|*#@^2Ztrk=S5wk$tZiiX zN_M})_ezFW)>6E(HpWxC%Gy2j1F+XYQGCpORyKEzok z7h+vcizB5Xktjb-q>}5xiG^t5EqyN#TjpB#t^F?iU3iAi^ zYLAjtq}iJ7L<3VzuZvckB*RjvRGdVzq2r*ZOgEOzDH>Bfb(Cq+xzofS6lrS=JxkI& zF*JkS$8NQ;S8F=$?Qd#~95{!K&27isX*l(kxq_Up8ryZ(d9Yb)@7LS=3^i+C@7G#w z*Kw=P&JNq*HdG(Eo6e3?uh#fD8k0zgfnDcO&E2YDnQphuG%7c154KzFTGJGGu;)Bx zw=cOMYqw$+nQ_@Gf@GG}x*JILkrlh^Q?=6ubG5q>mwS``4%l;&9h1BuM`xuiP9m3f8=p-Y@qG89tL+wPvhUR$5{HGPCp6ialq|peixv?7Uqsa#GRq$tPaa ziA!C2*PuuYRZ0e?flDe{&%}u?(Yr^Js>)F41A5I!Gx8=5Uo7tbx5CpH$LGCR4np+> N#i^-|7A?H&e*s5wMC||o delta 417 zcmZ8c!AiqG5PjJsZ8o+wAfAelsz?sK^)8Aa^-^owlL}I^tE7f1$sQXD)*mSGApVYj z;AeRC19T>Ma$w%f&c63{_G9a--nPG+{)OJXit!hM>Jeq$TetVNwGCzC1z1_Qbf6rJ zBX=G5ma#<~pOX0}%r^weLSaD(6oPdHsT~xg^H8`@5=9NlM^T5`K;c0(s!6k&w5ooS z`3qnMWHTt@11Z@`EF@+EE5Mp#2}9m>UTYi$^ztC#g%aK-u^Ai&m*IyZ->0sx)ATVs zzRQw$8cwF^Lo&bbhG!-mYNNBTuQT2GZ&MjoUN!de80gzwL_9PM!}IZkwgH~ht&vo7 zZEj39jrE8Qi2U-k_AW+tc_5$5Q!l8oC=bhPZ%0beK+_sTZu#jQiczcV$TyGg11k`} D=^IIM diff --git a/requirements/base.txt b/requirements/base.txt index 696bcc2..e69de29 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,2 +0,0 @@ -Django==2.0.6 -pytz==2018.4 From d18a9852036cf101d4e8f07ad8fd3b99555650b0 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:06:06 +0100 Subject: [PATCH 05/60] chore:setting up for heroku --- requirements/base.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 requirements/base.txt diff --git a/requirements/base.txt b/requirements/base.txt deleted file mode 100644 index e69de29..0000000 From a0ccf4b9834443b8fcab3d97144e050ff45171be Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:09:40 +0100 Subject: [PATCH 06/60] fix: heroku build pack --- .gitignore | 3 +++ Procfile | 0 requirements.txt | 0 runtime.txt | 1 + 4 files changed, 4 insertions(+) create mode 100644 .gitignore create mode 100644 Procfile create mode 100644 requirements.txt create mode 100644 runtime.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f77aa6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pyc +*.pyc +venv \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..ba85ab9 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-2.7.13 \ No newline at end of file From 7e57a3c96ac786f6d7eadb091375e48558d622ef Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:30:16 +0100 Subject: [PATCH 07/60] fix: heroku build pack error --- Pipfile | 2 ++ Procfile | 1 + .../config => config}/__init__.py | 0 .../config => config}/settings.py | 0 .../config => config}/urls.py | 2 +- .../config => config}/wsgi.py | 0 .../db.sqlite3 => db.sqlite3 | Bin 131072 -> 131072 bytes py_2_africaTalkingUSSD/manage.py => manage.py | 0 .../AfricasTalkingGateway.pyc | Bin 26792 -> 0 bytes py_2_africaTalkingUSSD/config/__init__.pyc | Bin 208 -> 0 bytes py_2_africaTalkingUSSD/config/settings.pyc | Bin 2645 -> 0 bytes py_2_africaTalkingUSSD/config/urls.pyc | Bin 1150 -> 0 bytes py_2_africaTalkingUSSD/config/wsgi.pyc | Bin 680 -> 0 bytes py_2_africaTalkingUSSD/views.pyc | Bin 2499 -> 0 bytes runtime.txt | 2 +- .../AfricasTalkingGateway.py | 0 .../test.py => ussd_app/__init__.py | 0 ussd_app/test.py | 0 {py_2_africaTalkingUSSD => ussd_app}/utils.py | 0 {py_2_africaTalkingUSSD => ussd_app}/views.py | 0 20 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 Pipfile rename {py_2_africaTalkingUSSD/config => config}/__init__.py (100%) rename {py_2_africaTalkingUSSD/config => config}/settings.py (100%) rename {py_2_africaTalkingUSSD/config => config}/urls.py (96%) rename {py_2_africaTalkingUSSD/config => config}/wsgi.py (100%) rename py_2_africaTalkingUSSD/db.sqlite3 => db.sqlite3 (99%) rename py_2_africaTalkingUSSD/manage.py => manage.py (100%) delete mode 100644 py_2_africaTalkingUSSD/AfricasTalkingGateway.pyc delete mode 100644 py_2_africaTalkingUSSD/config/__init__.pyc delete mode 100644 py_2_africaTalkingUSSD/config/settings.pyc delete mode 100644 py_2_africaTalkingUSSD/config/urls.pyc delete mode 100644 py_2_africaTalkingUSSD/config/wsgi.pyc delete mode 100644 py_2_africaTalkingUSSD/views.pyc rename {py_2_africaTalkingUSSD => ussd_app}/AfricasTalkingGateway.py (100%) rename py_2_africaTalkingUSSD/test.py => ussd_app/__init__.py (100%) create mode 100644 ussd_app/test.py rename {py_2_africaTalkingUSSD => ussd_app}/utils.py (100%) rename {py_2_africaTalkingUSSD => ussd_app}/views.py (100%) diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..cb5c30d --- /dev/null +++ b/Pipfile @@ -0,0 +1,2 @@ +[requires] +python_version = "2.7" \ No newline at end of file diff --git a/Procfile b/Procfile index e69de29..2a05d17 100644 --- a/Procfile +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn config.wsgi --log-file - \ No newline at end of file diff --git a/py_2_africaTalkingUSSD/config/__init__.py b/config/__init__.py similarity index 100% rename from py_2_africaTalkingUSSD/config/__init__.py rename to config/__init__.py diff --git a/py_2_africaTalkingUSSD/config/settings.py b/config/settings.py similarity index 100% rename from py_2_africaTalkingUSSD/config/settings.py rename to config/settings.py diff --git a/py_2_africaTalkingUSSD/config/urls.py b/config/urls.py similarity index 96% rename from py_2_africaTalkingUSSD/config/urls.py rename to config/urls.py index 8048842..56673f7 100644 --- a/py_2_africaTalkingUSSD/config/urls.py +++ b/config/urls.py @@ -15,7 +15,7 @@ """ from django.conf.urls import url from django.contrib import admin -import views +from ussd_app import views urlpatterns = [ url(r'', views.process_ussd, name="process_ussd"), diff --git a/py_2_africaTalkingUSSD/config/wsgi.py b/config/wsgi.py similarity index 100% rename from py_2_africaTalkingUSSD/config/wsgi.py rename to config/wsgi.py diff --git a/py_2_africaTalkingUSSD/db.sqlite3 b/db.sqlite3 similarity index 99% rename from py_2_africaTalkingUSSD/db.sqlite3 rename to db.sqlite3 index 538963cb0425ea6cc7700a8a80de9bf320291efd..5d1dc83a90221f0478847d22af67f8a6670fc658 100644 GIT binary patch delta 283 zcmYMuJ!(Qh5C?FBB)rb@6?tCYO2sN3ckf z+Ecio&uxG3|LFUo?~9+mYJ#vpULYDEhT!#F$zWh2yGW>5ud;b|B&7S=WkbtAQGaWn z&8DmbeQIbhYXYSe*0+YUk+|rhel*K$DH|f_>24q<0E>@0ZQb+rb26K*1Z&5SqjL88 ozTR$j`qh@V9*Bm6=hXArzGNd5@}S3dK3Fi~#0nm|@9yFM0`3DsAOHXW delta 283 zcmYMuFKz-s6o7FW(gufIQFa%aU@9hW-rpHWNN~Ld2?o(#p&LBvCG|DcJp+a#ATZTE z1qe1{f8xv6b)&8u{az;Hhzp5}m;&XP68Zlm7mKV&=2)+KvyhQEiGS8y%vc%CkF^w2 z(TvHT8Y;%3O4;8UErt>)nje~FF#x#T__>&d1f+&OYx(*9tbsFt01O`o)qK6#zVAN# iyRG_;1~ijQ{?)z|1B!{zPwi`O5-giC?597|hrcWORYDB_ diff --git a/py_2_africaTalkingUSSD/manage.py b/manage.py similarity index 100% rename from py_2_africaTalkingUSSD/manage.py rename to manage.py diff --git a/py_2_africaTalkingUSSD/AfricasTalkingGateway.pyc b/py_2_africaTalkingUSSD/AfricasTalkingGateway.pyc deleted file mode 100644 index 64f80a7fd55c6d3a9de2b953c4bdf03caa635483..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26792 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 diff --git a/py_2_africaTalkingUSSD/config/__init__.pyc b/py_2_africaTalkingUSSD/config/__init__.pyc deleted file mode 100644 index 96cb7e37e6c3ebbf5e30caebcb4c1cdabb54a6ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmZ9GF$=;l5QQT;h~S@a>g0j5h|tb1)yW|o$)ycVlai!Dez$+XE4b*v`wkwDd$)e? zcSawcN_=O<`^riih8Ci&7Uxzm#2->&5Be|?Oq3}GhsF8#(ZRc~JeCK5UH ziU3Wt3h(F!>pOU~ZBqwLb&t-G@Ay)YC;1avGN6JtwgV}xw^_>LRPvVF&tuTDqs#JR GVu=rdbvL{K diff --git a/py_2_africaTalkingUSSD/config/settings.pyc b/py_2_africaTalkingUSSD/config/settings.pyc deleted file mode 100644 index 92dc84e7957193f295a8c09d44fa33f4012d97d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2645 zcmb7Gdvnu95MSHDP7L80-leFJLK+&0B%RQ93Pso=c<>_=$)r_hs*!bP`+{E4oe1Gy zK1n}gKR~-D`NhODZ7lh;w~ycM?d_ga`g^(h7kTi92cusFpD*#xjRF7xuyHH^DS}@B zy8yHRhebFnogx;1o`cVY(QTK3l)$b4T?Bgx28dGyvH;{95ENB_ECNA(8SL{wP=w3q-51&z>pvgC|V_%V)2IMi=E7>xyXXA}*#E{m&#;A&5u6-xd`3%!ptslDKAW}FLf`tRF^rJ+HPn<2s z?I+B0E!TgK{H|%%)oOK@hBR>nB}(r^c}z0X4VMHgRJd>x5hu|h^~7T(^ajFC%2tivo>?nuOFu z6f=)&(w~NG`}xC%$%BprJsEQUlf&88bN3@(L9={WxSmiX ztfyv49Lzol~XTGm^e|*5z(5+_zzQy@yf=5s51~|9^8zfEXYwxX7q5kgD6yM+2g;pdD2&F zIJ{%@ct-o#6nh+$i&>owNXI#56ANCJ?!rP*NGd@PApyW8PoGR zduz1oVgc!TW8V~uNa%Zej-2qdIjC7teH0FMn};ik62zyybimjDpK?Nvu&)GvGFAT~ z<8$!kPVnv1`jafru^G$6RkS-T&lca=vl^YYnY9M0$YdV#0aM*L#y!ZkdA9sXZ<y2WiPNr@Wfr@fA4dss@YG=> z^Q?9`O>`kUKNZwu0rOqiQtxsN%=?QofVEi2f;4E-a3F?Lh+oeDk$2eMDAFm$%QIlK z(}3bD<21-Q1fk(Z%0&sOu2ti*5H);bV8dx^w{ems(sA&>cN|efM#QtSja@O!lgyG3 znDF~imob{+7V7F<>y5Lgo901hzwW%#n+=qAuuWxb zO})L_)prf2)~Or0qejbc>`vRrmfFRYHa353PbSUHr|qm_rNpxH6j&HUO@_OMkT4zi z2~M?P6i_Xuf+msgp0Mz}tJNb9cQF)Pa}#gKK2WWnQ0lv(N3}0L2sHUcP>Tqy4PD>I z%0r~NUkzH`e3N&l8lPk_{}Mzb_2~Jl3)+^;wxmYfj3gc^OVNA*AQmI@j d6)M?PD&~@va;{$~+*?8!a>xJWMa0Ul^1rvv3i<#5 diff --git a/py_2_africaTalkingUSSD/config/urls.pyc b/py_2_africaTalkingUSSD/config/urls.pyc deleted file mode 100644 index 8aebbd511a78ea65e839207e4b4b647e6f711b5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcma)5OK%e~5T3mHSPJyQ-9ku7q_S3ejGzLgR8&G7()JRhZr$}Haq6{KwzrToCw?zK z0LGgH+6tsPyWW|}c;=gLJguKklOufjC5QFu(Dxm^K3XKeg!Tr=Cg=tfO_(&H5%m`6 z78Grmv{!l?bO$CKz$QrAJFwUSNzXQXZ#JTF2e1u`ZCLD(1!fd!7i1gGdb=^%k0wC5 z%nma#bt)ILW*6g6Y-G(;%_=8?vL;E+=g6)qr%Mq6I^(aHRz5IitAIWp z@GMw%tMIE&*@<TPkIW+u6Gm$uLg|N`OPl*tE`*uc(%A*(AU z(eOb!81P_Am3tn&Ipy~}`S48?6wD8klgi`~-YRb$b&#_6QZiwfDoX3Zkg=(=1uK@B zD9g3+j{o7>zpj}oxKkn>C9UVWm#?}9>(b`7zyoGPfp5D}?)Hp0DYubHU3BAi6>Iwvlyn=e}L)KAq!YTz6<3Q`ULC3Rnw~jPQiaBy8rPM=Jc?d_^jcH-wi+17o#VDsvOV diff --git a/py_2_africaTalkingUSSD/config/wsgi.pyc b/py_2_africaTalkingUSSD/config/wsgi.pyc deleted file mode 100644 index c943442331fb20ece97d7f42e9db84d3d06a4753..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 680 zcmZuu&5qMB5O$K?f&z$@K%6a3JwU8-L#9haZVz*1r`#3xp z55NP!xB;<9SoY`fjOUxb?8m+Qn|l0NlHV)C`xPGl20;6}iDQPpZ z%18nU*&LD0F;PcE9h2)6RT;TPzfp85$D_#g9U=cz&gN7)Tj`osuBY9eKjWh6wG>NX zF14*M%5qA(-fcjJBG2c6!q?7u@H8|)2Y+FV_+o$xPX%qA+8TIjUFABBQ#9kyk6Byg&2z(KIxv@DKua#gA`AvIv}GQk;hrW#_{QUPFR~#JY+Y=OgNd&tP}D zcQ)jxKq>{5*qRXT;PXZ<H&HnhoLYGisNZR6Fb-rnT89EqTSePWXWjP zP0YmHln3BVxZ)jp&kWDN43}Io1Dtatr)h_KEPJ*4={w&!yL*mTxj*L%<+EGg`jCBW zynl&5UByP?*MbnBXThNb7L9EPZRq9TFqe*V5KcgE5)LQ9QaO+l@FT!8k0Y&8bQV!xC&#WjQ@y@P!4(9Z<0SAs-S)zhAnNR!Ak;qT z(6b!=C`!GJL1UaFi=rFZAagL{NJbMB!)Ov@9!40?6pW@oPQz##OS7=hBIVPmY0weR6kVQtWTtb!@S%C8ekjt$39OMd& z7U6sm%GRkNLffcx0v`B$k)fjH!g^)7ri%AlM0*IY_e&|Yh#nQE{NAJdZ`I{ ze)|Huk;?o*A)e=0=D?EgkWBO&gA)YmnElK$;mneP3BNF*z=WTekY~crmb?k0C6Kqq z;oOGvB{;TIXFssnBvXI23+?K<~N4xRywmLbBV_?TE`f;I$qxK%7+O4SV|*#@^2Ztrk=S5wk$tZiiX zN_M})_ezFW)>6E(HpWxC%Gy2j1F+XYQGCpORyKEzok z7h+vcizB5Xktjb-q>}5xiG^t5EqyN#TjpB#t^F?iU3iAi^ zYLAjtq}iJ7L<3VzuZvckB*RjvRGdVzq2r*ZOgEOzDH>Bfb(Cq+xzofS6lrS=JxkI& zF*JkS$8NQ;S8F=$?Qd#~95{!K&27isX*l(kxq_Up8ryZ(d9Yb)@7LS=3^i+C@7G#w z*Kw=P&JNq*HdG(Eo6e3?uh#fD8k0zgfnDcO&E2YDnQphuG%7c154KzFTGJGGu;)Bx zw=cOMYqw$+nQ_@Gf@GG}x*JILkrlh^Q?=6ubG5q>mwS``4%l;&9h1BuM`xuiP9m3f8=p-Y@qG89tL+wPvhUR$5{HGPCp6ialq|peixv?7Uqsa#GRq$tPaa ziA!C2*PuuYRZ0e?flDe{&%}u?(Yr^Js>)F41A5I!Gx8=5Uo7tbx5CpH$LGCR4np+> N#i^-|7A?H&e*s5wMC||o diff --git a/runtime.txt b/runtime.txt index ba85ab9..5b66454 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-2.7.13 \ No newline at end of file +python-2.7.15 \ No newline at end of file diff --git a/py_2_africaTalkingUSSD/AfricasTalkingGateway.py b/ussd_app/AfricasTalkingGateway.py similarity index 100% rename from py_2_africaTalkingUSSD/AfricasTalkingGateway.py rename to ussd_app/AfricasTalkingGateway.py diff --git a/py_2_africaTalkingUSSD/test.py b/ussd_app/__init__.py similarity index 100% rename from py_2_africaTalkingUSSD/test.py rename to ussd_app/__init__.py diff --git a/ussd_app/test.py b/ussd_app/test.py new file mode 100644 index 0000000..e69de29 diff --git a/py_2_africaTalkingUSSD/utils.py b/ussd_app/utils.py similarity index 100% rename from py_2_africaTalkingUSSD/utils.py rename to ussd_app/utils.py diff --git a/py_2_africaTalkingUSSD/views.py b/ussd_app/views.py similarity index 100% rename from py_2_africaTalkingUSSD/views.py rename to ussd_app/views.py From a7ba63b333d2e9a9be468e8e3168c7aa5b4cbc2f Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:31:52 +0100 Subject: [PATCH 08/60] fix: heroku build pack error --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index e69de29..c265858 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,3 @@ +Django==1.11.13 +gunicorn==19.8.1 +pytz==2018.4 From 4fc3e414eb625013836e1248e148343e4b10274d Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:37:57 +0100 Subject: [PATCH 09/60] fix: heroku build pack error --- Pipfile | 2 -- Procfile | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 Pipfile diff --git a/Pipfile b/Pipfile deleted file mode 100644 index cb5c30d..0000000 --- a/Pipfile +++ /dev/null @@ -1,2 +0,0 @@ -[requires] -python_version = "2.7" \ No newline at end of file diff --git a/Procfile b/Procfile index 2a05d17..1002183 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn config.wsgi --log-file - \ No newline at end of file +web: gunicorn config.wsgi \ No newline at end of file From 178c7635858277aa97192f7cf7384fbc22fc96d5 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:42:23 +0100 Subject: [PATCH 10/60] fix: heroku build pack error --- Pipfile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Pipfile diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..51b1e49 --- /dev/null +++ b/Pipfile @@ -0,0 +1,9 @@ +[packages] + +django = "*" +gunicorn = "*" + + +[requires] + +python_version = "3.6" \ No newline at end of file From 5fa621be3a333e79091287e28665c1a1769a4eec Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:44:16 +0100 Subject: [PATCH 11/60] fix: heroku build pack error --- Pipfile | 2 +- runtime.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 runtime.txt diff --git a/Pipfile b/Pipfile index 51b1e49..40480da 100644 --- a/Pipfile +++ b/Pipfile @@ -6,4 +6,4 @@ gunicorn = "*" [requires] -python_version = "3.6" \ No newline at end of file +python_version = "2.7" \ No newline at end of file diff --git a/runtime.txt b/runtime.txt deleted file mode 100644 index 5b66454..0000000 --- a/runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-2.7.15 \ No newline at end of file From ae6ba86912b995b5ac5d93eceecafad039e76fa3 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 22:56:31 +0100 Subject: [PATCH 12/60] fix: heroku build pack error --- Pipfile | 9 +-------- Pipfile.lock | 20 ++++++++++++++++++++ Procfile | 2 +- __runtime.txt | 1 + 4 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 Pipfile.lock create mode 100644 __runtime.txt diff --git a/Pipfile b/Pipfile index 40480da..6c720c6 100644 --- a/Pipfile +++ b/Pipfile @@ -1,9 +1,2 @@ -[packages] - -django = "*" -gunicorn = "*" - - [requires] - -python_version = "2.7" \ No newline at end of file +python_full_version = "2.7.15" \ No newline at end of file diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..565df73 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,20 @@ +{ + "_meta": { + "hash": { + "sha256": "073a83b7e278f1d87b7560ec97d04dd29ad3e7c612e25362f4d53a5e73a8bc58" + }, + "pipfile-spec": 6, + "requires": { + "python_full_version": "2.7.15" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": {} +} diff --git a/Procfile b/Procfile index 1002183..2a05d17 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn config.wsgi \ No newline at end of file +web: gunicorn config.wsgi --log-file - \ No newline at end of file diff --git a/__runtime.txt b/__runtime.txt new file mode 100644 index 0000000..08a5809 --- /dev/null +++ b/__runtime.txt @@ -0,0 +1 @@ +python-2.7.12 \ No newline at end of file From 5be4a8a75179904b916cd1d7b683c64007114b49 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 23:10:33 +0100 Subject: [PATCH 13/60] refactor: build pack still fails despite correct config --- .gitignore | 3 +- Procfile | 2 +- __runtime.txt | 1 - config/__init__.py | 0 config/settings.py | 120 ------ config/urls.py | 23 - config/wsgi.py | 16 - db.sqlite3 | Bin 131072 -> 0 bytes manage.py | 2 +- ussd_app/AfricasTalkingGateway.py | 692 ------------------------------ ussd_app/__init__.py | 0 ussd_app/test.py | 0 ussd_app/utils.py | 35 -- ussd_app/views.py | 79 ---- 14 files changed, 4 insertions(+), 969 deletions(-) delete mode 100644 __runtime.txt delete mode 100644 config/__init__.py delete mode 100644 config/settings.py delete mode 100644 config/urls.py delete mode 100644 config/wsgi.py delete mode 100644 db.sqlite3 delete mode 100644 ussd_app/AfricasTalkingGateway.py delete mode 100644 ussd_app/__init__.py delete mode 100644 ussd_app/test.py delete mode 100644 ussd_app/utils.py delete mode 100644 ussd_app/views.py diff --git a/.gitignore b/.gitignore index 5f77aa6..70beab6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .pyc *.pyc -venv \ No newline at end of file +venv +backup \ No newline at end of file diff --git a/Procfile b/Procfile index 2a05d17..ab90914 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn config.wsgi --log-file - \ No newline at end of file +web: gunicorn ussd_project.wsgi \ No newline at end of file diff --git a/__runtime.txt b/__runtime.txt deleted file mode 100644 index 08a5809..0000000 --- a/__runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-2.7.12 \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/config/settings.py b/config/settings.py deleted file mode 100644 index 2116d8f..0000000 --- a/config/settings.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -Django settings for py_2_africaTalkingUSSD. - -Generated by 'django-admin startproject' using Django 1.11.13. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.11/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'u2q$$b@zu9hi3=##rq*($pm70$$pl&z-o*bgfi+hslz_si2=ax' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'config.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'config.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/1.11/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/1.11/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.11/howto/static-files/ - -STATIC_URL = '/static/' diff --git a/config/urls.py b/config/urls.py deleted file mode 100644 index 56673f7..0000000 --- a/config/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -"""py_2_africaTalkingUSSD URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.11/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -from django.conf.urls import url -from django.contrib import admin -from ussd_app import views - -urlpatterns = [ - url(r'', views.process_ussd, name="process_ussd"), - url(r'^admin/', admin.site.urls), -] diff --git a/config/wsgi.py b/config/wsgi.py deleted file mode 100644 index 1d014de..0000000 --- a/config/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for py_2_africaTalkingUSSD project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") - -application = get_wsgi_application() diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index 5d1dc83a90221f0478847d22af67f8a6670fc658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131072 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 diff --git a/manage.py b/manage.py index 68141ee..cf2e3b1 100755 --- a/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ussd_project.settings") try: from django.core.management import execute_from_command_line except ImportError: diff --git a/ussd_app/AfricasTalkingGateway.py b/ussd_app/AfricasTalkingGateway.py deleted file mode 100644 index ec48322..0000000 --- a/ussd_app/AfricasTalkingGateway.py +++ /dev/null @@ -1,692 +0,0 @@ -""" - COPYRIGHT (C) 2014 AFRICASTALKING LTD # - - AFRICAStALKING SMS GATEWAY CLASS IS A FREE SOFTWARE IE. CAN BE MODIFIED AND/OR REDISTRIBUTED - UNDER THER TERMS OF GNU GENERAL PUBLIC LICENCES AS PUBLISHED BY THE - FREE SOFTWARE FOUNDATION VERSION 3 OR ANY LATER VERSION - - THE CLASS IS DISTRIBUTED ON 'AS IS' BASIS WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO - THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" - -import urllib -import urllib2 -import json - -class AfricasTalkingGatewayException(Exception): - pass - -class AfricasTalkingGateway: - - def __init__(self, username_, apiKey_): - self.username = username_ - self.apiKey = apiKey_ - self.environment = 'sandbox' if username_ is 'sandbox' else 'prod' - - self.HTTP_RESPONSE_OK = 200 - self.HTTP_RESPONSE_CREATED = 201 - - # Turn this on if you run into problems. It will print the raw HTTP response from our server - self.Debug = True - - def generateAuthToken(self): - parameters = {'username': self.username} - url = self.getGenerateAuthTokenUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - # Messaging methods - def sendMessage(self, to_, message_, from_ = None, bulkSMSMode_ = 1, enqueue_ = 0, keyword_ = None, linkId_ = None, retryDurationInHours_ = None, authToken_ = None): - if len(to_) == 0 or len(message_) == 0: - raise AfricasTalkingGatewayException("Please provide both to_ and message_ parameters") - - parameters = {'username' : self.username, - 'to': to_, - 'message': message_, - 'bulkSMSMode':bulkSMSMode_} - - if not from_ is None : - parameters["from"] = from_ - - if enqueue_ > 0: - parameters["enqueue"] = enqueue_ - - if not keyword_ is None: - parameters["keyword"] = keyword_ - - if not linkId_ is None: - parameters["linkId"] = linkId_ - - if not retryDurationInHours_ is None: - parameters["retryDurationInHours"] = retryDurationInHours_ - - response = self.sendRequest(self.getSmsUrl(), parameters, authToken_) - - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - recipients = decoded['SMSMessageData']['Recipients'] - - if len(recipients) > 0: - return recipients - - raise AfricasTalkingGatewayException(decoded['SMSMessageData']['Message']) - - raise AfricasTalkingGatewayException(response) - - - def fetchMessages(self, lastReceivedId_ = 0): - url = "%s?username=%s&lastReceivedId=%s" % (self.getSmsUrl(), self.username, lastReceivedId_) - response = self.sendRequest(url) - - if self.responseCode == self.HTTP_RESPONSE_OK: - decoded = json.loads(response) - return decoded['SMSMessageData']['Messages'] - raise AfricasTalkingGatewayException(response) - - - # Subscription methods - def createSubscription(self, phoneNumber_, shortCode_, keyword_, checkoutToken_): - if len(phoneNumber_) == 0 or len(shortCode_) == 0 or len(keyword_) == 0: - raise AfricasTalkingGatewayException("Please supply phone number, short code and keyword") - - url = "%s/create" %(self.getSmsSubscriptionUrl()) - parameters = { - 'username' : self.username, - 'phoneNumber' : phoneNumber_, - 'shortCode' : shortCode_, - 'keyword' : keyword_, - "checkoutToken" : checkoutToken_ - } - - response = self.sendRequest (url, parameters) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - return decoded - raise AfricasTalkingGatewayException(response) - - - def deleteSubscription(self, phoneNumber_, shortCode_, keyword_): - if len(phoneNumber_) == 0 or len(shortCode_) == 0 or len(keyword_) == 0: - raise AfricasTalkingGatewayException("Please supply phone number, short code and keyword") - - url = "%s/delete" %(self.getSmsSubscriptionUrl()) - parameters = { - 'username' :self.username, - 'phoneNumber' :phoneNumber_, - 'shortCode' :shortCode_, - 'keyword' :keyword_ - } - response = self.sendRequest(url, parameters) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - return decoded - raise AfricasTalkingGatewayException(response) - - - def fetchPremiumSubscriptions(self,shortCode_, keyword_, lastReceivedId_ = 0): - if len(shortCode_) == 0 or len(keyword_) == 0: - raise AfricasTalkingGatewayException("Please supply the short code and keyword") - - url = "%s?username=%s&shortCode=%s&keyword=%s&lastReceivedId=%s" % (self.getSmsSubscriptionUrl(), - self.username, - shortCode_, - keyword_, - lastReceivedId_) - result = self.sendRequest(url) - if self.responseCode == self.HTTP_RESPONSE_OK: - decoded = json.loads(result) - return decoded['responses'] - - raise AfricasTalkingGatewayException(response) - - - # Voice methods - def call(self, from_, to_): - parameters = { - 'username' : self.username, - 'from' : from_, - 'to': to_ - } - - url = "%s/call" %(self.getVoiceUrl()) - response = self.sendRequest(url, parameters) - decoded = json.loads(response) - if decoded['errorMessage'] == "None": - return decoded['entries']; - raise AfricasTalkingGatewayException(decoded['errorMessage']) - - def getNumQueuedCalls(self, phoneNumber_, queueName_ = None): - parameters = { - 'username' :self.username, - 'phoneNumbers' :phoneNumber_ - } - - if queueName_ is not None: - parameters['queueName'] = queueName_ - - url = "%s/queueStatus" %(self.getVoiceUrl()) - response = self.sendRequest(url, parameters) - decoded = json.loads(response) - if decoded['errorMessage'] == "None": - return decoded['entries'] - - raise AfricasTalkingGatewayException(decoded['errorMessage']) - - def uploadMediaFile(self, urlString_): - parameters = { - 'username' :self.username, - 'url' :urlString_ - } - url = "%s/mediaUpload" %(self.getVoiceUrl()) - response = self.sendRequest(url, parameters) - decoded = json.loads(response) - if decoded['errorMessage'] != "None": - raise AfricasTalkingGatewayException(decoded['errorMessage']) - - #Airtime method - def sendAirtime(self, recipients_): - parameters = { - 'username' : self.username, - 'recipients' : json.dumps(recipients_) - } - - url = "%s/send" %(self.getAirtimeUrl()) - response = self.sendRequest(url, parameters) - decoded = json.loads(response) - responses = decoded['responses'] - if self.responseCode == self.HTTP_RESPONSE_CREATED: - if len(responses) > 0: - return responses - raise AfricasTalkingGatewayException(decoded["errorMessage"]) - raise AfricasTalkingGatewayException(response) - - #USSD Push method - def sendUssdPush(self, phoneNumber_, menu_, checkoutToken_): - parameters = { - 'username' : self.username, - 'phoneNumber' : phoneNumber_, - 'menu' : menu_, - 'checkoutToken' : checkoutToken_ - } - - url = self.getUssdPushUrl() - response = self.sendRequest(url, parameters) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - if decoded['status'] == 'Queued': - return decoded['sessionId'] - raise AfricasTalkingGatewayException(decoded["errorMessage"]) - raise AfricasTalkingGatewayException(response) - - #Checkout Token Request - def createCheckoutToken(self, phoneNumber_): - parameters = { - 'phoneNumber' : phoneNumber_ - } - - url = "%s/checkout/token/create" %(self.getApiHost()) - response = self.sendRequest(url, parameters) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - if decoded['token'] == 'None': - raise AfricasTalkingGatewayException(decoded['token']) - return decoded['description'] - raise AfricasTalkingGatewayException(response) - - #Payment Methods - def bankPaymentCheckoutCharge(self, - productName_, - bankAccount_, - currencyCode_, - amount_, - narration_, - metadata_ = None): - - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'bankAccount' : bankAccount_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'narration' : narration_ - } - - if metadata_: - parameters['metadata'] = metadata_ - - url = self.getBankPaymentCheckoutChargeUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - responseObj = json.loads(response) - if responseObj['status'] == 'PendingValidation': - return responseObj['transactionId'] - raise AfricasTalkingGatewayException(responseObj['description']) - raise AfricasTalkingGatewayException(response) - - def bankPaymentCheckoutValidation(self, - transactionId_, - otp_): - - parameters = { - 'username' : self.username, - 'transactionId' : transactionId_, - 'otp' : otp_ - } - - url = self.getBankPaymentCheckoutValidationUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - responseObj = json.loads(response) - if responseObj['status'] == 'Success': return - raise AfricasTalkingGatewayException(responseObj['description']) - raise AfricasTalkingGatewayException(response) - - def bankPaymentTransfer(self, - productName_, - recipients_): - - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'recipients' : recipients_ - } - - url = self.getBankPaymentTransferUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - responseObj = json.loads(response) - if len(responseObj['entries']): - return responseObj['entries'] - raise AfricasTalkingGatewayException(responseObj['errorMessage']) - raise AfricasTalkingGatewayException(response) - - def cardPaymentCheckoutCharge(self, - productName_, - paymentCard_, - currencyCode_, - amount_, - narration_, - metadata_ = None): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'paymentCard' : paymentCard_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'narration' : narration_ - } - - if metadata_: - parameters['metadata'] = metadata_ - - url = self.getCardPaymentCheckoutChargeUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - responseObj = json.loads(response) - if responseObj['status'] == 'PendingValidation': - return responseObj['transactionId'] - raise AfricasTalkingGatewayException(responseObj['description']) - raise AfricasTalkingGatewayException(response) - - def cardPaymentCheckoutChargeWithToken(self, - productName_, - checkoutToken_, - currencyCode_, - amount_, - narration_, - metadata_ = None): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'checkoutToken' : checkoutToken_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'narration' : narration_ - } - - if metadata_: - parameters['metadata'] = metadata_ - - url = self.getCardPaymentCheckoutChargeUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - responseObj = json.loads(response) - if responseObj['status'] == 'Success': return - raise AfricasTalkingGatewayException(responseObj['description']) - raise AfricasTalkingGatewayException(response) - - def cardPaymentCheckoutValidation(self, - transactionId_, - otp_): - - parameters = { - 'username' : self.username, - 'transactionId' : transactionId_, - 'otp' : otp_ - } - - url = self.getCardPaymentCheckoutValidationUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - responseObj = json.loads(response) - if responseObj['status'] == 'Success': - return responseObj['checkoutToken'] - raise AfricasTalkingGatewayException(responseObj['description']) - raise AfricasTalkingGatewayException(response) - - def paymentStashTopup(self, - productName_, - currencyCode_, - amount_, - metadata_): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'metadata' : metadata_ - } - url = self.getPaymentStashTopupUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - def paymentWalletTransfer(self, - productName_, - targetUsername_, - targetProductName_, - currencyCode_, - amount_, - metadata_): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'targetUsername' : targetUsername_, - 'targetProductName' : targetProductName_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'metadata' : metadata_ - } - url = self.getPaymentWalletTransferUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - def paymentWalletBalanceQuery(self): - url = self.getPaymentWalletBalanceQueryUrl() - response = self.sendRequest(url + "?username=%s" % self.username) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - def paymentTransactionFindQuery(self, - transactionId_): - url = self.getPaymentTransactionFindQueryUrl() - response = self.sendRequest(url + "?username=%s&transactionId=%s" % (self.username, transactionId_)) - if self.responseCode == self.HTTP_RESPONSE_OK: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - def initiateMobilePaymentCheckout(self, - productName_, - phoneNumber_, - currencyCode_, - amount_, - metadata_, - providerChannel_): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'phoneNumber' : phoneNumber_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'metadata' : metadata_ - } - - if providerChannel_ is not None: - parameters['providerChannel'] = providerChannel_ - - url = self.getMobilePaymentCheckoutUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - if decoded['status'] == 'PendingConfirmation': - return decoded['transactionId'] - raise AfricasTalkingGatewayException(decoded['description']) - raise AfricasTalkingGatewayException(response) - - def mobilePaymentB2CRequest(self, productName_, recipients_): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'recipients' : recipients_ - } - url = self.getMobilePaymentB2CUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - if len(decoded['entries']) > 0: - return decoded['entries'] - raise AfricasTalkingGatewayException(decoded['errorMessage']) - raise AfricasTalkingGatewayException(response) - - def mobilePaymentB2BRequest(self, productName_, providerData_, currencyCode_, amount_, metadata_): - if "provider" not in providerData_: - raise AfricasTalkingGatewayException("Missing field provider") - - if "destinationChannel" not in providerData_: - raise AfricasTalkingGatewayException("Missing field destinationChannel") - - if "transferType" not in providerData_: - raise AfricasTalkingGatewayException("Missing field transferType") - - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'provider' : providerData_['provider'], - 'destinationChannel' : providerData_['destinationChannel'], - 'transferType' : providerData_['transferType'], - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'metadata' : metadata_ - } - if "destinationAccount" in providerData_: - parameters['destinationAccount'] = providerData_['destinationAccount'] - - url = self.getMobilePaymentB2BUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - decoded = json.loads(response) - return decoded - raise AfricasTalkingGatewayException(response) - - def mobilePaymentB2BRequest(self, - productName_, - provider_, - transferType_, - currencyCode_, - amount_, - metadata_, - destinationChannel_, - destinationAccount_): - parameters = { - 'username' : self.username, - 'productName' : productName_, - 'provider' : provider_, - 'transferType' : transferType_, - 'currencyCode' : currencyCode_, - 'amount' : amount_, - 'destinationChannel' : destinationChannel_, - } - if metadata_ is not None: - parameters['metadata'] = metadata_ - - if destinationAccount_ is not None: - parameters['destinationAccount'] = destinationAccount_ - - url = self.getMobilePaymentB2BUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - def paymentBankWithdrawalRequest(self, - productName_, - bankAccountName_, - currencyCode_, - amount_, - metadata_): - parameters = { - 'username' : self.username, - 'bankAccountName' : bankAccountName_, - 'productName' : productName_, - 'currencyCode' : currencyCode_, - 'amount' : amount_ - } - if metadata_ is not None: - parameters['metadata'] = metadata_ - - url = self.getPaymentBankWithdrawalUrl() - response = self.sendJSONRequest(url, json.dumps(parameters)) - if self.responseCode == self.HTTP_RESPONSE_CREATED: - return json.loads(response) - raise AfricasTalkingGatewayException(response) - - # Userdata method - def getUserData(self): - url = "%s?username=%s" %(self.getUserDataUrl(), self.username) - result = self.sendRequest(url) - if self.responseCode == self.HTTP_RESPONSE_OK: - decoded = json.loads(result) - return decoded['UserData'] - raise AfricasTalkingGatewayException(response) - - # HTTP access method - def sendRequest(self, urlString, data_ = None, authToken_ = None): - try: - headers = {'Accept' : 'application/json'} - if authToken_ is None: - headers['apikey'] = self.apiKey - else: - headers['authToken'] = authToken_ - - if data_ is not None: - data = urllib.urlencode(data_) - request = urllib2.Request(urlString, data, headers = headers) - else: - request = urllib2.Request(urlString, headers = headers) - response = urllib2.urlopen(request) - except urllib2.HTTPError as e: - raise AfricasTalkingGatewayException(e.read()) - else: - self.responseCode = response.getcode() - response = ''.join(response.readlines()) - if self.Debug: - print "Raw response: " + response - - return response - - def sendJSONRequest(self, urlString, data_): - try: - headers = {'Accept' : 'application/json', - 'Content-Type' : 'application/json', - 'apikey' : self.apiKey} - request = urllib2.Request(urlString, - data_, - headers = headers) - response = urllib2.urlopen(request) - except urllib2.HTTPError as e: - raise AfricasTalkingGatewayException(e.read()) - else: - self.responseCode = response.getcode() - response = ''.join(response.readlines()) - if self.Debug: - print "Raw response: " + response - - return response - - def getApiHost(self): - if self.environment == 'sandbox': - return 'https://api.sandbox.africastalking.com' - else: - return 'https://api.africastalking.com' - - def getPaymentHost(self): - if self.environment == 'sandbox': - return 'https://payments.sandbox.africastalking.com' - else: - return 'https://payments.africastalking.com' - - def getVoiceHost(self): - if self.environment == 'sandbox': - return 'https://voice.sandbox.africastalking.com' - else: - return 'https://voice.africastalking.com' - - def getGenerateAuthTokenUrl(self): - return self.getApiHost() + "/auth-token/generate" - - def getSmsUrl(self): - return self.getApiHost() + "/version1/messaging" - - def getVoiceUrl(self): - return self.getVoiceHost() - - def getSmsSubscriptionUrl(self): - return self.getApiHost() + "/version1/subscription" - - def getUserDataUrl(self): - return self.getApiHost() + "/version1/user" - - def getAirtimeUrl(self): - return self.getApiHost() + "/version1/airtime" - - def getUssdPushUrl(self): - return self.getApiHost() + "/ussd/push/request" - - def getMobilePaymentCheckoutUrl(self): - return self.getPaymentHost() + "/mobile/checkout/request" - - def getMobilePaymentB2CUrl(self): - return self.getPaymentHost() + "/mobile/b2c/request" - - def getMobilePaymentB2BUrl(self): - return self.getPaymentHost() + "/mobile/b2b/request" - - def getPaymentBankWithdrawalUrl(self): - return self.getPaymentHost() + "/bank-withdrawal" - - def getBankPaymentCheckoutChargeUrl(self): - return self.getPaymentHost() + "/bank/checkout/charge" - - def getBankPaymentCheckoutValidationUrl(self): - return self.getPaymentHost() + "/bank/checkout/validate" - - def getBankPaymentTransferUrl(self): - return self.getPaymentHost() + "/bank/transfer" - - def getCardPaymentCheckoutChargeUrl(self): - return self.getPaymentHost() + "/card/checkout/charge" - - def getCardPaymentCheckoutValidationUrl(self): - return self.getPaymentHost() + "/card/checkout/validate" - - def getPaymentStashTopupUrl(self): - return self.getPaymentHost() + "/topup/stash" - - def getPaymentWalletTransferUrl(self): - return self.getPaymentHost() + "/transfer/wallet" - - def getPaymentWalletBalanceQueryUrl(self): - return self.getPaymentHost() + "/query/wallet/balance" - - def getPaymentTransactionFindQueryUrl(self): - return self.getPaymentHost() + "/query/transaction/find" - diff --git a/ussd_app/__init__.py b/ussd_app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/ussd_app/test.py b/ussd_app/test.py deleted file mode 100644 index e69de29..0000000 diff --git a/ussd_app/utils.py b/ussd_app/utils.py deleted file mode 100644 index 486b311..0000000 --- a/ussd_app/utils.py +++ /dev/null @@ -1,35 +0,0 @@ -from AfricasTalkingGateway import AfricasTalkingGateway, AfricasTalkingGatewayException - -class AfricasTalkingUtils: - def __init__(self): - #Specify your credentials - self.username = "sandbox" - self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" - #Create an instance of our awesome gateway class and pass your credentials - self.gateway = AfricasTalkingGateway(self.username, self.apiKey) - - def bank_checkout(self): - try: - # Send the request to the gateway. If successful, we will respond with - # a transactionId that you can then use to validate the transaction - transactionId = self.gateway.bankPaymentCheckoutCharge( - productName_ = 'Airtime Distribution', - currencyCode_ = 'NGN', - amount_ = 100, - narration_ = 'Airtime Purchase Request', - bankAccount_ = { - 'accountName' : 'Fela Kuti', - 'accountNumber' : '123456789', - 'bankCode' : 234001 - }, - metadata_ = { - 'Reason' : 'To Test The Gateways' - } - ) - print transactionId - except AfricasTalkingGatewayException, e: - print 'Encountered an error while sending: %s' % str(e) - - def ussd(self, service_code, session_id, phone_number, text): - import pdb; pdb.set_trace() - \ No newline at end of file diff --git a/ussd_app/views.py b/ussd_app/views.py deleted file mode 100644 index cf9e383..0000000 --- a/ussd_app/views.py +++ /dev/null @@ -1,79 +0,0 @@ -from django.http import HttpResponse -from django.views.decorators.csrf import csrf_exempt -from utils import AfricasTalkingUtils - -@csrf_exempt -def process_ussd(request): - ## MENU - MY_COPERATIVE = '1' - WAZOBIA_LOANS = '2' - JOIN_AGBETUNTU = '3' - REQUEST_A_CALL = '4' - - ## SUB MENU - # MY_COPERATIVE SUBMENU - CHECK_BALANCE = '1*1' - REQUEST_LOAN = '1*2' - MAKE_DEPOSIT = '1*3' - # WAZOBIA SUBMENU - REGISTER = '2*1' - REPAY_LOAN = '2*2' - MAKE_DEPOSIT = '2*3' - REQUEST_LOAN_2 = '2*4' - REQUEST_A_CALL_2 = '2*5' - - if request.method == 'POST': - session_id = request.POST.get('sessionId') - service_code = request.POST.get('serviceCode') - phone_number = request.POST.get('phoneNumber') - text = request.POST.get('text') - - # main menu - if text == "": - response = "CON WELCOME, What would you want to check. \n" - response += "1. My Cooperative \n" - response += "2. Wazobia Loans \n" - elif text == MY_COPERATIVE: - response = "CON What would you like to do in You Cooperative account. \n" - response += "1. Check Balance \n" - response += "2. Request Loan \n" - response += "3. Make Deposit \n" - elif text == WAZOBIA_LOANS: - response = "CON WELCOME to Wazobia Loans \n" - response += "1. Register \n" - response += "2. Repay Loan \n" - response += "3. Make Deposit \n" - response += "4. Request Loan \n" - response += "5. Request a call \n" - elif text == JOIN_AGBETUNTU: - response = "CON Welcome to Agbetuntu" - - # multiple options - elif text == REQUEST_A_CALL or text == REQUEST_A_CALL_2: - response = "Your Request has been recorded, An agent will call soon! #CHEERS " - elif text == REQUEST_LOAN_2 or text == REQUEST_LOAN: - balance = 50 - response = "CON Loan Repaid, \n" - response += "New Balance:", balance - - # sub menu - elif text == CHECK_BALANCE : - balance = 200 - response = "CON Balance:", balance, "\n" - elif text == MAKE_DEPOSIT: - balance = 1000 - response = "CON Deposit was successful,\n" - response += "New Balance:", balance - - elif text == REGISTER: - response = "CON Your Name" - else: - response = "CON You selected a wrong option, please try again\n" - response += "1. My Account \n" - response += "2. My Phone Number \n" - africa_talking = AfricasTalkingUtils() - else: - response = "Ooops, Sorry..." - - return HttpResponse(response) - # import pdb; pdb.set_trace() \ No newline at end of file From c1a5b7b69859bea674abb32361a43f04981964e8 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 23:10:49 +0100 Subject: [PATCH 14/60] refactor: build pack still fails despite correct config --- ussd_project/__init__.py | 0 ussd_project/settings.py | 120 +++++++++++++++++++++++++++++++++++++++ ussd_project/urls.py | 21 +++++++ ussd_project/wsgi.py | 16 ++++++ 4 files changed, 157 insertions(+) create mode 100644 ussd_project/__init__.py create mode 100644 ussd_project/settings.py create mode 100644 ussd_project/urls.py create mode 100644 ussd_project/wsgi.py diff --git a/ussd_project/__init__.py b/ussd_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ussd_project/settings.py b/ussd_project/settings.py new file mode 100644 index 0000000..1dc3c93 --- /dev/null +++ b/ussd_project/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for ussd_project project. + +Generated by 'django-admin startproject' using Django 1.11.13. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'n$d=7^2g3k*teavwc7b+@o68)391+==bh+%6348xuz0yqhck6p' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'ussd_project.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'ussd_project.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/ussd_project/urls.py b/ussd_project/urls.py new file mode 100644 index 0000000..ef60669 --- /dev/null +++ b/ussd_project/urls.py @@ -0,0 +1,21 @@ +"""ussd_project URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url +from django.contrib import admin + +urlpatterns = [ + url(r'^admin/', admin.site.urls), +] diff --git a/ussd_project/wsgi.py b/ussd_project/wsgi.py new file mode 100644 index 0000000..6fceac5 --- /dev/null +++ b/ussd_project/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for ussd_project project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ussd_project.settings") + +application = get_wsgi_application() From 93005402644474b4fe4e4a2902118814c9b5aa67 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Thu, 14 Jun 2018 23:17:02 +0100 Subject: [PATCH 15/60] refactor: build pack still fails despite correct config --- runtime.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 runtime.txt diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..5b66454 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-2.7.15 \ No newline at end of file From b1a7f7f951557a22a9476b00fd56c5a258c4b275 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 09:20:22 +0100 Subject: [PATCH 16/60] heroku: build now --- Procfile | 2 +- __runtime.txt | 1 + {ussd_project => config}/__init__.py | 0 {ussd_project => config}/settings.py | 8 +- {ussd_project => config}/urls.py | 4 +- {ussd_project => config}/wsgi.py | 4 +- db.sqlite3 | Bin 0 -> 131072 bytes manage.py | 2 +- runtime.txt | 1 - ussd_app/AfricasTalkingGateway.py | 692 +++++++++++++++++++++++++++ ussd_app/__init__.py | 0 ussd_app/test.py | 0 ussd_app/utils.py | 35 ++ ussd_app/views.py | 79 +++ 14 files changed, 818 insertions(+), 10 deletions(-) create mode 100644 __runtime.txt rename {ussd_project => config}/__init__.py (100%) rename {ussd_project => config}/settings.py (93%) rename {ussd_project => config}/urls.py (85%) rename {ussd_project => config}/wsgi.py (71%) create mode 100644 db.sqlite3 delete mode 100644 runtime.txt create mode 100644 ussd_app/AfricasTalkingGateway.py create mode 100644 ussd_app/__init__.py create mode 100644 ussd_app/test.py create mode 100644 ussd_app/utils.py create mode 100644 ussd_app/views.py diff --git a/Procfile b/Procfile index ab90914..2a05d17 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn ussd_project.wsgi \ No newline at end of file +web: gunicorn config.wsgi --log-file - \ No newline at end of file diff --git a/__runtime.txt b/__runtime.txt new file mode 100644 index 0000000..08a5809 --- /dev/null +++ b/__runtime.txt @@ -0,0 +1 @@ +python-2.7.12 \ No newline at end of file diff --git a/ussd_project/__init__.py b/config/__init__.py similarity index 100% rename from ussd_project/__init__.py rename to config/__init__.py diff --git a/ussd_project/settings.py b/config/settings.py similarity index 93% rename from ussd_project/settings.py rename to config/settings.py index 1dc3c93..2116d8f 100644 --- a/ussd_project/settings.py +++ b/config/settings.py @@ -1,5 +1,5 @@ """ -Django settings for ussd_project project. +Django settings for py_2_africaTalkingUSSD. Generated by 'django-admin startproject' using Django 1.11.13. @@ -20,7 +20,7 @@ # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'n$d=7^2g3k*teavwc7b+@o68)391+==bh+%6348xuz0yqhck6p' +SECRET_KEY = 'u2q$$b@zu9hi3=##rq*($pm70$$pl&z-o*bgfi+hslz_si2=ax' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -49,7 +49,7 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = 'ussd_project.urls' +ROOT_URLCONF = 'config.urls' TEMPLATES = [ { @@ -67,7 +67,7 @@ }, ] -WSGI_APPLICATION = 'ussd_project.wsgi.application' +WSGI_APPLICATION = 'config.wsgi.application' # Database diff --git a/ussd_project/urls.py b/config/urls.py similarity index 85% rename from ussd_project/urls.py rename to config/urls.py index ef60669..56673f7 100644 --- a/ussd_project/urls.py +++ b/config/urls.py @@ -1,4 +1,4 @@ -"""ussd_project URL Configuration +"""py_2_africaTalkingUSSD URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ @@ -15,7 +15,9 @@ """ from django.conf.urls import url from django.contrib import admin +from ussd_app import views urlpatterns = [ + url(r'', views.process_ussd, name="process_ussd"), url(r'^admin/', admin.site.urls), ] diff --git a/ussd_project/wsgi.py b/config/wsgi.py similarity index 71% rename from ussd_project/wsgi.py rename to config/wsgi.py index 6fceac5..1d014de 100644 --- a/ussd_project/wsgi.py +++ b/config/wsgi.py @@ -1,5 +1,5 @@ """ -WSGI config for ussd_project project. +WSGI config for py_2_africaTalkingUSSD project. It exposes the WSGI callable as a module-level variable named ``application``. @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ussd_project.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") application = get_wsgi_application() diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..5d1dc83a90221f0478847d22af67f8a6670fc658 GIT binary patch literal 131072 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 literal 0 HcmV?d00001 diff --git a/manage.py b/manage.py index cf2e3b1..68141ee 100755 --- a/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ussd_project.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError: diff --git a/runtime.txt b/runtime.txt deleted file mode 100644 index 5b66454..0000000 --- a/runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-2.7.15 \ No newline at end of file diff --git a/ussd_app/AfricasTalkingGateway.py b/ussd_app/AfricasTalkingGateway.py new file mode 100644 index 0000000..ec48322 --- /dev/null +++ b/ussd_app/AfricasTalkingGateway.py @@ -0,0 +1,692 @@ +""" + COPYRIGHT (C) 2014 AFRICASTALKING LTD # + + AFRICAStALKING SMS GATEWAY CLASS IS A FREE SOFTWARE IE. CAN BE MODIFIED AND/OR REDISTRIBUTED + UNDER THER TERMS OF GNU GENERAL PUBLIC LICENCES AS PUBLISHED BY THE + FREE SOFTWARE FOUNDATION VERSION 3 OR ANY LATER VERSION + + THE CLASS IS DISTRIBUTED ON 'AS IS' BASIS WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO + THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import urllib +import urllib2 +import json + +class AfricasTalkingGatewayException(Exception): + pass + +class AfricasTalkingGateway: + + def __init__(self, username_, apiKey_): + self.username = username_ + self.apiKey = apiKey_ + self.environment = 'sandbox' if username_ is 'sandbox' else 'prod' + + self.HTTP_RESPONSE_OK = 200 + self.HTTP_RESPONSE_CREATED = 201 + + # Turn this on if you run into problems. It will print the raw HTTP response from our server + self.Debug = True + + def generateAuthToken(self): + parameters = {'username': self.username} + url = self.getGenerateAuthTokenUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + # Messaging methods + def sendMessage(self, to_, message_, from_ = None, bulkSMSMode_ = 1, enqueue_ = 0, keyword_ = None, linkId_ = None, retryDurationInHours_ = None, authToken_ = None): + if len(to_) == 0 or len(message_) == 0: + raise AfricasTalkingGatewayException("Please provide both to_ and message_ parameters") + + parameters = {'username' : self.username, + 'to': to_, + 'message': message_, + 'bulkSMSMode':bulkSMSMode_} + + if not from_ is None : + parameters["from"] = from_ + + if enqueue_ > 0: + parameters["enqueue"] = enqueue_ + + if not keyword_ is None: + parameters["keyword"] = keyword_ + + if not linkId_ is None: + parameters["linkId"] = linkId_ + + if not retryDurationInHours_ is None: + parameters["retryDurationInHours"] = retryDurationInHours_ + + response = self.sendRequest(self.getSmsUrl(), parameters, authToken_) + + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + recipients = decoded['SMSMessageData']['Recipients'] + + if len(recipients) > 0: + return recipients + + raise AfricasTalkingGatewayException(decoded['SMSMessageData']['Message']) + + raise AfricasTalkingGatewayException(response) + + + def fetchMessages(self, lastReceivedId_ = 0): + url = "%s?username=%s&lastReceivedId=%s" % (self.getSmsUrl(), self.username, lastReceivedId_) + response = self.sendRequest(url) + + if self.responseCode == self.HTTP_RESPONSE_OK: + decoded = json.loads(response) + return decoded['SMSMessageData']['Messages'] + raise AfricasTalkingGatewayException(response) + + + # Subscription methods + def createSubscription(self, phoneNumber_, shortCode_, keyword_, checkoutToken_): + if len(phoneNumber_) == 0 or len(shortCode_) == 0 or len(keyword_) == 0: + raise AfricasTalkingGatewayException("Please supply phone number, short code and keyword") + + url = "%s/create" %(self.getSmsSubscriptionUrl()) + parameters = { + 'username' : self.username, + 'phoneNumber' : phoneNumber_, + 'shortCode' : shortCode_, + 'keyword' : keyword_, + "checkoutToken" : checkoutToken_ + } + + response = self.sendRequest (url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + return decoded + raise AfricasTalkingGatewayException(response) + + + def deleteSubscription(self, phoneNumber_, shortCode_, keyword_): + if len(phoneNumber_) == 0 or len(shortCode_) == 0 or len(keyword_) == 0: + raise AfricasTalkingGatewayException("Please supply phone number, short code and keyword") + + url = "%s/delete" %(self.getSmsSubscriptionUrl()) + parameters = { + 'username' :self.username, + 'phoneNumber' :phoneNumber_, + 'shortCode' :shortCode_, + 'keyword' :keyword_ + } + response = self.sendRequest(url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + return decoded + raise AfricasTalkingGatewayException(response) + + + def fetchPremiumSubscriptions(self,shortCode_, keyword_, lastReceivedId_ = 0): + if len(shortCode_) == 0 or len(keyword_) == 0: + raise AfricasTalkingGatewayException("Please supply the short code and keyword") + + url = "%s?username=%s&shortCode=%s&keyword=%s&lastReceivedId=%s" % (self.getSmsSubscriptionUrl(), + self.username, + shortCode_, + keyword_, + lastReceivedId_) + result = self.sendRequest(url) + if self.responseCode == self.HTTP_RESPONSE_OK: + decoded = json.loads(result) + return decoded['responses'] + + raise AfricasTalkingGatewayException(response) + + + # Voice methods + def call(self, from_, to_): + parameters = { + 'username' : self.username, + 'from' : from_, + 'to': to_ + } + + url = "%s/call" %(self.getVoiceUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + if decoded['errorMessage'] == "None": + return decoded['entries']; + raise AfricasTalkingGatewayException(decoded['errorMessage']) + + def getNumQueuedCalls(self, phoneNumber_, queueName_ = None): + parameters = { + 'username' :self.username, + 'phoneNumbers' :phoneNumber_ + } + + if queueName_ is not None: + parameters['queueName'] = queueName_ + + url = "%s/queueStatus" %(self.getVoiceUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + if decoded['errorMessage'] == "None": + return decoded['entries'] + + raise AfricasTalkingGatewayException(decoded['errorMessage']) + + def uploadMediaFile(self, urlString_): + parameters = { + 'username' :self.username, + 'url' :urlString_ + } + url = "%s/mediaUpload" %(self.getVoiceUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + if decoded['errorMessage'] != "None": + raise AfricasTalkingGatewayException(decoded['errorMessage']) + + #Airtime method + def sendAirtime(self, recipients_): + parameters = { + 'username' : self.username, + 'recipients' : json.dumps(recipients_) + } + + url = "%s/send" %(self.getAirtimeUrl()) + response = self.sendRequest(url, parameters) + decoded = json.loads(response) + responses = decoded['responses'] + if self.responseCode == self.HTTP_RESPONSE_CREATED: + if len(responses) > 0: + return responses + raise AfricasTalkingGatewayException(decoded["errorMessage"]) + raise AfricasTalkingGatewayException(response) + + #USSD Push method + def sendUssdPush(self, phoneNumber_, menu_, checkoutToken_): + parameters = { + 'username' : self.username, + 'phoneNumber' : phoneNumber_, + 'menu' : menu_, + 'checkoutToken' : checkoutToken_ + } + + url = self.getUssdPushUrl() + response = self.sendRequest(url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if decoded['status'] == 'Queued': + return decoded['sessionId'] + raise AfricasTalkingGatewayException(decoded["errorMessage"]) + raise AfricasTalkingGatewayException(response) + + #Checkout Token Request + def createCheckoutToken(self, phoneNumber_): + parameters = { + 'phoneNumber' : phoneNumber_ + } + + url = "%s/checkout/token/create" %(self.getApiHost()) + response = self.sendRequest(url, parameters) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if decoded['token'] == 'None': + raise AfricasTalkingGatewayException(decoded['token']) + return decoded['description'] + raise AfricasTalkingGatewayException(response) + + #Payment Methods + def bankPaymentCheckoutCharge(self, + productName_, + bankAccount_, + currencyCode_, + amount_, + narration_, + metadata_ = None): + + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'bankAccount' : bankAccount_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'narration' : narration_ + } + + if metadata_: + parameters['metadata'] = metadata_ + + url = self.getBankPaymentCheckoutChargeUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'PendingValidation': + return responseObj['transactionId'] + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def bankPaymentCheckoutValidation(self, + transactionId_, + otp_): + + parameters = { + 'username' : self.username, + 'transactionId' : transactionId_, + 'otp' : otp_ + } + + url = self.getBankPaymentCheckoutValidationUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'Success': return + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def bankPaymentTransfer(self, + productName_, + recipients_): + + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'recipients' : recipients_ + } + + url = self.getBankPaymentTransferUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if len(responseObj['entries']): + return responseObj['entries'] + raise AfricasTalkingGatewayException(responseObj['errorMessage']) + raise AfricasTalkingGatewayException(response) + + def cardPaymentCheckoutCharge(self, + productName_, + paymentCard_, + currencyCode_, + amount_, + narration_, + metadata_ = None): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'paymentCard' : paymentCard_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'narration' : narration_ + } + + if metadata_: + parameters['metadata'] = metadata_ + + url = self.getCardPaymentCheckoutChargeUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'PendingValidation': + return responseObj['transactionId'] + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def cardPaymentCheckoutChargeWithToken(self, + productName_, + checkoutToken_, + currencyCode_, + amount_, + narration_, + metadata_ = None): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'checkoutToken' : checkoutToken_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'narration' : narration_ + } + + if metadata_: + parameters['metadata'] = metadata_ + + url = self.getCardPaymentCheckoutChargeUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'Success': return + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def cardPaymentCheckoutValidation(self, + transactionId_, + otp_): + + parameters = { + 'username' : self.username, + 'transactionId' : transactionId_, + 'otp' : otp_ + } + + url = self.getCardPaymentCheckoutValidationUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + responseObj = json.loads(response) + if responseObj['status'] == 'Success': + return responseObj['checkoutToken'] + raise AfricasTalkingGatewayException(responseObj['description']) + raise AfricasTalkingGatewayException(response) + + def paymentStashTopup(self, + productName_, + currencyCode_, + amount_, + metadata_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + url = self.getPaymentStashTopupUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentWalletTransfer(self, + productName_, + targetUsername_, + targetProductName_, + currencyCode_, + amount_, + metadata_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'targetUsername' : targetUsername_, + 'targetProductName' : targetProductName_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + url = self.getPaymentWalletTransferUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentWalletBalanceQuery(self): + url = self.getPaymentWalletBalanceQueryUrl() + response = self.sendRequest(url + "?username=%s" % self.username) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentTransactionFindQuery(self, + transactionId_): + url = self.getPaymentTransactionFindQueryUrl() + response = self.sendRequest(url + "?username=%s&transactionId=%s" % (self.username, transactionId_)) + if self.responseCode == self.HTTP_RESPONSE_OK: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def initiateMobilePaymentCheckout(self, + productName_, + phoneNumber_, + currencyCode_, + amount_, + metadata_, + providerChannel_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'phoneNumber' : phoneNumber_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + + if providerChannel_ is not None: + parameters['providerChannel'] = providerChannel_ + + url = self.getMobilePaymentCheckoutUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if decoded['status'] == 'PendingConfirmation': + return decoded['transactionId'] + raise AfricasTalkingGatewayException(decoded['description']) + raise AfricasTalkingGatewayException(response) + + def mobilePaymentB2CRequest(self, productName_, recipients_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'recipients' : recipients_ + } + url = self.getMobilePaymentB2CUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + if len(decoded['entries']) > 0: + return decoded['entries'] + raise AfricasTalkingGatewayException(decoded['errorMessage']) + raise AfricasTalkingGatewayException(response) + + def mobilePaymentB2BRequest(self, productName_, providerData_, currencyCode_, amount_, metadata_): + if "provider" not in providerData_: + raise AfricasTalkingGatewayException("Missing field provider") + + if "destinationChannel" not in providerData_: + raise AfricasTalkingGatewayException("Missing field destinationChannel") + + if "transferType" not in providerData_: + raise AfricasTalkingGatewayException("Missing field transferType") + + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'provider' : providerData_['provider'], + 'destinationChannel' : providerData_['destinationChannel'], + 'transferType' : providerData_['transferType'], + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'metadata' : metadata_ + } + if "destinationAccount" in providerData_: + parameters['destinationAccount'] = providerData_['destinationAccount'] + + url = self.getMobilePaymentB2BUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + decoded = json.loads(response) + return decoded + raise AfricasTalkingGatewayException(response) + + def mobilePaymentB2BRequest(self, + productName_, + provider_, + transferType_, + currencyCode_, + amount_, + metadata_, + destinationChannel_, + destinationAccount_): + parameters = { + 'username' : self.username, + 'productName' : productName_, + 'provider' : provider_, + 'transferType' : transferType_, + 'currencyCode' : currencyCode_, + 'amount' : amount_, + 'destinationChannel' : destinationChannel_, + } + if metadata_ is not None: + parameters['metadata'] = metadata_ + + if destinationAccount_ is not None: + parameters['destinationAccount'] = destinationAccount_ + + url = self.getMobilePaymentB2BUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + def paymentBankWithdrawalRequest(self, + productName_, + bankAccountName_, + currencyCode_, + amount_, + metadata_): + parameters = { + 'username' : self.username, + 'bankAccountName' : bankAccountName_, + 'productName' : productName_, + 'currencyCode' : currencyCode_, + 'amount' : amount_ + } + if metadata_ is not None: + parameters['metadata'] = metadata_ + + url = self.getPaymentBankWithdrawalUrl() + response = self.sendJSONRequest(url, json.dumps(parameters)) + if self.responseCode == self.HTTP_RESPONSE_CREATED: + return json.loads(response) + raise AfricasTalkingGatewayException(response) + + # Userdata method + def getUserData(self): + url = "%s?username=%s" %(self.getUserDataUrl(), self.username) + result = self.sendRequest(url) + if self.responseCode == self.HTTP_RESPONSE_OK: + decoded = json.loads(result) + return decoded['UserData'] + raise AfricasTalkingGatewayException(response) + + # HTTP access method + def sendRequest(self, urlString, data_ = None, authToken_ = None): + try: + headers = {'Accept' : 'application/json'} + if authToken_ is None: + headers['apikey'] = self.apiKey + else: + headers['authToken'] = authToken_ + + if data_ is not None: + data = urllib.urlencode(data_) + request = urllib2.Request(urlString, data, headers = headers) + else: + request = urllib2.Request(urlString, headers = headers) + response = urllib2.urlopen(request) + except urllib2.HTTPError as e: + raise AfricasTalkingGatewayException(e.read()) + else: + self.responseCode = response.getcode() + response = ''.join(response.readlines()) + if self.Debug: + print "Raw response: " + response + + return response + + def sendJSONRequest(self, urlString, data_): + try: + headers = {'Accept' : 'application/json', + 'Content-Type' : 'application/json', + 'apikey' : self.apiKey} + request = urllib2.Request(urlString, + data_, + headers = headers) + response = urllib2.urlopen(request) + except urllib2.HTTPError as e: + raise AfricasTalkingGatewayException(e.read()) + else: + self.responseCode = response.getcode() + response = ''.join(response.readlines()) + if self.Debug: + print "Raw response: " + response + + return response + + def getApiHost(self): + if self.environment == 'sandbox': + return 'https://api.sandbox.africastalking.com' + else: + return 'https://api.africastalking.com' + + def getPaymentHost(self): + if self.environment == 'sandbox': + return 'https://payments.sandbox.africastalking.com' + else: + return 'https://payments.africastalking.com' + + def getVoiceHost(self): + if self.environment == 'sandbox': + return 'https://voice.sandbox.africastalking.com' + else: + return 'https://voice.africastalking.com' + + def getGenerateAuthTokenUrl(self): + return self.getApiHost() + "/auth-token/generate" + + def getSmsUrl(self): + return self.getApiHost() + "/version1/messaging" + + def getVoiceUrl(self): + return self.getVoiceHost() + + def getSmsSubscriptionUrl(self): + return self.getApiHost() + "/version1/subscription" + + def getUserDataUrl(self): + return self.getApiHost() + "/version1/user" + + def getAirtimeUrl(self): + return self.getApiHost() + "/version1/airtime" + + def getUssdPushUrl(self): + return self.getApiHost() + "/ussd/push/request" + + def getMobilePaymentCheckoutUrl(self): + return self.getPaymentHost() + "/mobile/checkout/request" + + def getMobilePaymentB2CUrl(self): + return self.getPaymentHost() + "/mobile/b2c/request" + + def getMobilePaymentB2BUrl(self): + return self.getPaymentHost() + "/mobile/b2b/request" + + def getPaymentBankWithdrawalUrl(self): + return self.getPaymentHost() + "/bank-withdrawal" + + def getBankPaymentCheckoutChargeUrl(self): + return self.getPaymentHost() + "/bank/checkout/charge" + + def getBankPaymentCheckoutValidationUrl(self): + return self.getPaymentHost() + "/bank/checkout/validate" + + def getBankPaymentTransferUrl(self): + return self.getPaymentHost() + "/bank/transfer" + + def getCardPaymentCheckoutChargeUrl(self): + return self.getPaymentHost() + "/card/checkout/charge" + + def getCardPaymentCheckoutValidationUrl(self): + return self.getPaymentHost() + "/card/checkout/validate" + + def getPaymentStashTopupUrl(self): + return self.getPaymentHost() + "/topup/stash" + + def getPaymentWalletTransferUrl(self): + return self.getPaymentHost() + "/transfer/wallet" + + def getPaymentWalletBalanceQueryUrl(self): + return self.getPaymentHost() + "/query/wallet/balance" + + def getPaymentTransactionFindQueryUrl(self): + return self.getPaymentHost() + "/query/transaction/find" + diff --git a/ussd_app/__init__.py b/ussd_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ussd_app/test.py b/ussd_app/test.py new file mode 100644 index 0000000..e69de29 diff --git a/ussd_app/utils.py b/ussd_app/utils.py new file mode 100644 index 0000000..486b311 --- /dev/null +++ b/ussd_app/utils.py @@ -0,0 +1,35 @@ +from AfricasTalkingGateway import AfricasTalkingGateway, AfricasTalkingGatewayException + +class AfricasTalkingUtils: + def __init__(self): + #Specify your credentials + self.username = "sandbox" + self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" + #Create an instance of our awesome gateway class and pass your credentials + self.gateway = AfricasTalkingGateway(self.username, self.apiKey) + + def bank_checkout(self): + try: + # Send the request to the gateway. If successful, we will respond with + # a transactionId that you can then use to validate the transaction + transactionId = self.gateway.bankPaymentCheckoutCharge( + productName_ = 'Airtime Distribution', + currencyCode_ = 'NGN', + amount_ = 100, + narration_ = 'Airtime Purchase Request', + bankAccount_ = { + 'accountName' : 'Fela Kuti', + 'accountNumber' : '123456789', + 'bankCode' : 234001 + }, + metadata_ = { + 'Reason' : 'To Test The Gateways' + } + ) + print transactionId + except AfricasTalkingGatewayException, e: + print 'Encountered an error while sending: %s' % str(e) + + def ussd(self, service_code, session_id, phone_number, text): + import pdb; pdb.set_trace() + \ No newline at end of file diff --git a/ussd_app/views.py b/ussd_app/views.py new file mode 100644 index 0000000..cf9e383 --- /dev/null +++ b/ussd_app/views.py @@ -0,0 +1,79 @@ +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt +from utils import AfricasTalkingUtils + +@csrf_exempt +def process_ussd(request): + ## MENU + MY_COPERATIVE = '1' + WAZOBIA_LOANS = '2' + JOIN_AGBETUNTU = '3' + REQUEST_A_CALL = '4' + + ## SUB MENU + # MY_COPERATIVE SUBMENU + CHECK_BALANCE = '1*1' + REQUEST_LOAN = '1*2' + MAKE_DEPOSIT = '1*3' + # WAZOBIA SUBMENU + REGISTER = '2*1' + REPAY_LOAN = '2*2' + MAKE_DEPOSIT = '2*3' + REQUEST_LOAN_2 = '2*4' + REQUEST_A_CALL_2 = '2*5' + + if request.method == 'POST': + session_id = request.POST.get('sessionId') + service_code = request.POST.get('serviceCode') + phone_number = request.POST.get('phoneNumber') + text = request.POST.get('text') + + # main menu + if text == "": + response = "CON WELCOME, What would you want to check. \n" + response += "1. My Cooperative \n" + response += "2. Wazobia Loans \n" + elif text == MY_COPERATIVE: + response = "CON What would you like to do in You Cooperative account. \n" + response += "1. Check Balance \n" + response += "2. Request Loan \n" + response += "3. Make Deposit \n" + elif text == WAZOBIA_LOANS: + response = "CON WELCOME to Wazobia Loans \n" + response += "1. Register \n" + response += "2. Repay Loan \n" + response += "3. Make Deposit \n" + response += "4. Request Loan \n" + response += "5. Request a call \n" + elif text == JOIN_AGBETUNTU: + response = "CON Welcome to Agbetuntu" + + # multiple options + elif text == REQUEST_A_CALL or text == REQUEST_A_CALL_2: + response = "Your Request has been recorded, An agent will call soon! #CHEERS " + elif text == REQUEST_LOAN_2 or text == REQUEST_LOAN: + balance = 50 + response = "CON Loan Repaid, \n" + response += "New Balance:", balance + + # sub menu + elif text == CHECK_BALANCE : + balance = 200 + response = "CON Balance:", balance, "\n" + elif text == MAKE_DEPOSIT: + balance = 1000 + response = "CON Deposit was successful,\n" + response += "New Balance:", balance + + elif text == REGISTER: + response = "CON Your Name" + else: + response = "CON You selected a wrong option, please try again\n" + response += "1. My Account \n" + response += "2. My Phone Number \n" + africa_talking = AfricasTalkingUtils() + else: + response = "Ooops, Sorry..." + + return HttpResponse(response) + # import pdb; pdb.set_trace() \ No newline at end of file From 3d8e79289c5dc7c240b3209954664944b166287a Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 09:33:56 +0100 Subject: [PATCH 17/60] . --- __runtime.txt | 1 - requirements.txt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 __runtime.txt diff --git a/__runtime.txt b/__runtime.txt deleted file mode 100644 index 08a5809..0000000 --- a/__runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-2.7.12 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index c265858..64d6c49 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ Django==1.11.13 gunicorn==19.8.1 -pytz==2018.4 +pytz==2018.4 \ No newline at end of file From a1010a1efc0b990a98e04d402e53e37fada25f8f Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 09:34:25 +0100 Subject: [PATCH 18/60] . --- runtime.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 runtime.txt diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..5b66454 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-2.7.15 \ No newline at end of file From 8914d0ed941e3e1e771d3132a1f34e2c6892f1e2 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 09:58:33 +0100 Subject: [PATCH 19/60] . --- Pipfile | 2 -- Pipfile.lock | 20 -------------------- Procfile | 2 +- {config => app_config}/__init__.py | 0 {config => app_config}/settings.py | 4 ++-- {config => app_config}/urls.py | 0 {config => app_config}/wsgi.py | 2 +- manage.py | 2 +- 8 files changed, 5 insertions(+), 27 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock rename {config => app_config}/__init__.py (100%) rename {config => app_config}/settings.py (97%) rename {config => app_config}/urls.py (100%) rename {config => app_config}/wsgi.py (82%) diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 6c720c6..0000000 --- a/Pipfile +++ /dev/null @@ -1,2 +0,0 @@ -[requires] -python_full_version = "2.7.15" \ No newline at end of file diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 565df73..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,20 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "073a83b7e278f1d87b7560ec97d04dd29ad3e7c612e25362f4d53a5e73a8bc58" - }, - "pipfile-spec": 6, - "requires": { - "python_full_version": "2.7.15" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": {}, - "develop": {} -} diff --git a/Procfile b/Procfile index 2a05d17..67067cb 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn config.wsgi --log-file - \ No newline at end of file +web: gunicorn app_config.wsgi --log-file - \ No newline at end of file diff --git a/config/__init__.py b/app_config/__init__.py similarity index 100% rename from config/__init__.py rename to app_config/__init__.py diff --git a/config/settings.py b/app_config/settings.py similarity index 97% rename from config/settings.py rename to app_config/settings.py index 2116d8f..385a661 100644 --- a/config/settings.py +++ b/app_config/settings.py @@ -49,7 +49,7 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = 'config.urls' +ROOT_URLCONF = 'app_config.urls' TEMPLATES = [ { @@ -67,7 +67,7 @@ }, ] -WSGI_APPLICATION = 'config.wsgi.application' +WSGI_APPLICATION = 'app_config.wsgi.application' # Database diff --git a/config/urls.py b/app_config/urls.py similarity index 100% rename from config/urls.py rename to app_config/urls.py diff --git a/config/wsgi.py b/app_config/wsgi.py similarity index 82% rename from config/wsgi.py rename to app_config/wsgi.py index 1d014de..460f5ac 100644 --- a/config/wsgi.py +++ b/app_config/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app_config.settings") application = get_wsgi_application() diff --git a/manage.py b/manage.py index 68141ee..67c2e6a 100755 --- a/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app_config.settings") try: from django.core.management import execute_from_command_line except ImportError: From a5a7f1a96fa6bac3f040118283617954a62c2d12 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:04:03 +0100 Subject: [PATCH 20/60] . --- app.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 app.json diff --git a/app.json b/app.json new file mode 100644 index 0000000..c67b390 --- /dev/null +++ b/app.json @@ -0,0 +1,22 @@ +{ + "name": "Ussd App", + "description": "A barebones Python app, which can easily be deployed to Heroku.", + "image": "heroku/python", + "repository": "https://github.com/peterolayinka/USSDCodeChallenge", + "keywords": ["python", "django"], + // "addons": ["heroku-postgresql"], + "env": { + "SECRET_KEY": { + "description": "The secret key for the Django application.", + "generator": "secret" + } + }, + "environments": { + "test": { + "scripts": { + "test-setup": "python manage.py collectstatic --noinput", + "test": "python manage.py test" + } + } + } +} \ No newline at end of file From 29f49dd1684939629b80364063228c6b5876b2ce Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:06:09 +0100 Subject: [PATCH 21/60] . --- .gitignore | 3 ++- startup.sh => app_startup.sh | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename startup.sh => app_startup.sh (100%) diff --git a/.gitignore b/.gitignore index 70beab6..e40ad29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .pyc *.pyc venv -backup \ No newline at end of file +backup +db.sqlite3 \ No newline at end of file diff --git a/startup.sh b/app_startup.sh similarity index 100% rename from startup.sh rename to app_startup.sh From 3b91e078aae04d88fc9d936f87e45e4549615803 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:07:09 +0100 Subject: [PATCH 22/60] . --- app.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app.json b/app.json index c67b390..cdcae6a 100644 --- a/app.json +++ b/app.json @@ -4,7 +4,6 @@ "image": "heroku/python", "repository": "https://github.com/peterolayinka/USSDCodeChallenge", "keywords": ["python", "django"], - // "addons": ["heroku-postgresql"], "env": { "SECRET_KEY": { "description": "The secret key for the Django application.", From a6773ae56afc590a3a00f1b217297ae32fe3c2d1 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:10:54 +0100 Subject: [PATCH 23/60] ...d --- requirements.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 64d6c49..4682192 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,7 @@ +certifi==2018.4.16 Django==1.11.13 gunicorn==19.8.1 -pytz==2018.4 \ No newline at end of file +pipenv==2018.5.18 +pytz==2018.4 +virtualenv==16.0.0 +virtualenv-clone==0.3.0 From eeaf9b8b86ad8bc53f3e012f1b2c346851aa8ef2 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:19:43 +0100 Subject: [PATCH 24/60] reverted back to amster branch --- app_startup.sh | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 app_startup.sh diff --git a/app_startup.sh b/app_startup.sh deleted file mode 100644 index a9276a4..0000000 --- a/app_startup.sh +++ /dev/null @@ -1,4 +0,0 @@ -source venv/bin/activate -# 'qtoye7os0$^-05i+2&w#_t_4c_a0o%nmp!r0c3s(te0kx81t27' -SECRET_KEY='something-very-secretive' -PWD=root \ No newline at end of file From 00c84787b43635540d46cca91f0e8a9fca5708e9 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:19:53 +0100 Subject: [PATCH 25/60] reverted back to amster branch --- startup.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 startup.sh diff --git a/startup.sh b/startup.sh new file mode 100644 index 0000000..a9276a4 --- /dev/null +++ b/startup.sh @@ -0,0 +1,4 @@ +source venv/bin/activate +# 'qtoye7os0$^-05i+2&w#_t_4c_a0o%nmp!r0c3s(te0kx81t27' +SECRET_KEY='something-very-secretive' +PWD=root \ No newline at end of file From 09ffcfe9b870fae0f7185348bb4120ba89966d37 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:25:35 +0100 Subject: [PATCH 26/60] got app on heroku --- app_config/__init__.py | 0 app_config/settings.py | 120 ----------------------------------------- app_config/urls.py | 23 -------- app_config/wsgi.py | 16 ------ 4 files changed, 159 deletions(-) delete mode 100644 app_config/__init__.py delete mode 100644 app_config/settings.py delete mode 100644 app_config/urls.py delete mode 100644 app_config/wsgi.py diff --git a/app_config/__init__.py b/app_config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app_config/settings.py b/app_config/settings.py deleted file mode 100644 index 385a661..0000000 --- a/app_config/settings.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -Django settings for py_2_africaTalkingUSSD. - -Generated by 'django-admin startproject' using Django 1.11.13. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.11/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'u2q$$b@zu9hi3=##rq*($pm70$$pl&z-o*bgfi+hslz_si2=ax' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'app_config.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'app_config.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/1.11/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/1.11/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.11/howto/static-files/ - -STATIC_URL = '/static/' diff --git a/app_config/urls.py b/app_config/urls.py deleted file mode 100644 index 56673f7..0000000 --- a/app_config/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -"""py_2_africaTalkingUSSD URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.11/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -from django.conf.urls import url -from django.contrib import admin -from ussd_app import views - -urlpatterns = [ - url(r'', views.process_ussd, name="process_ussd"), - url(r'^admin/', admin.site.urls), -] diff --git a/app_config/wsgi.py b/app_config/wsgi.py deleted file mode 100644 index 460f5ac..0000000 --- a/app_config/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for py_2_africaTalkingUSSD project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app_config.settings") - -application = get_wsgi_application() From dd074e999c3a4bcb5dfae06f29acb0fefbb0331b Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 10:30:56 +0100 Subject: [PATCH 27/60] got app on heroku --- Procfile | 2 +- config/__init__.py | 0 config/settings.py | 120 +++++++++++++++++++++++++++++++++++++++++++++ config/urls.py | 23 +++++++++ config/wsgi.py | 16 ++++++ manage.py | 2 +- 6 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 config/__init__.py create mode 100644 config/settings.py create mode 100644 config/urls.py create mode 100644 config/wsgi.py diff --git a/Procfile b/Procfile index 67067cb..2a05d17 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app_config.wsgi --log-file - \ No newline at end of file +web: gunicorn config.wsgi --log-file - \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 0000000..a43092c --- /dev/null +++ b/config/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for py_2_africaTalkingUSSD. + +Generated by 'django-admin startproject' using Django 1.11.13. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'u2q$$b@zu9hi3=##rq*($pm70$$pl&z-o*bgfi+hslz_si2=ax' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/config/urls.py b/config/urls.py new file mode 100644 index 0000000..56673f7 --- /dev/null +++ b/config/urls.py @@ -0,0 +1,23 @@ +"""py_2_africaTalkingUSSD URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url +from django.contrib import admin +from ussd_app import views + +urlpatterns = [ + url(r'', views.process_ussd, name="process_ussd"), + url(r'^admin/', admin.site.urls), +] diff --git a/config/wsgi.py b/config/wsgi.py new file mode 100644 index 0000000..1d014de --- /dev/null +++ b/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for py_2_africaTalkingUSSD project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_wsgi_application() diff --git a/manage.py b/manage.py index 67c2e6a..68141ee 100755 --- a/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app_config.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError: From 13209cda208b21ec6266843601d771f64590edff Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 13:46:03 +0100 Subject: [PATCH 28/60] setup app models, and working structure --- ____ussd_app/__init__.py | 1 + ____ussd_app/apps.py | 6 + {ussd_app => ____ussd_app}/test.py | 0 config/settings.py | 1 + config/urls.py | 2 +- db.sqlite3 | Bin 131072 -> 167936 bytes ussd_app/admin.py | 14 +++ ussd_app/apps.py | 8 ++ ussd_app/migrations/0001_initial.py | 67 +++++++++++ ussd_app/migrations/__init__.py | 0 ussd_app/models.py | 31 +++++ ussd_app/tests.py | 6 + ussd_app/utils.py | 177 ++++++++++++++++++++++++++-- ussd_app/views.py | 72 ++--------- 14 files changed, 310 insertions(+), 75 deletions(-) create mode 100644 ____ussd_app/__init__.py create mode 100644 ____ussd_app/apps.py rename {ussd_app => ____ussd_app}/test.py (100%) create mode 100644 ussd_app/admin.py create mode 100644 ussd_app/apps.py create mode 100644 ussd_app/migrations/0001_initial.py create mode 100644 ussd_app/migrations/__init__.py create mode 100644 ussd_app/models.py create mode 100644 ussd_app/tests.py diff --git a/____ussd_app/__init__.py b/____ussd_app/__init__.py new file mode 100644 index 0000000..16d6fd1 --- /dev/null +++ b/____ussd_app/__init__.py @@ -0,0 +1 @@ +default_app_config = 'ussd_app.apps.UssdApp' \ No newline at end of file diff --git a/____ussd_app/apps.py b/____ussd_app/apps.py new file mode 100644 index 0000000..2ef00fc --- /dev/null +++ b/____ussd_app/apps.py @@ -0,0 +1,6 @@ + +from django.apps import AppConfig + +class UssdApp(AppConfig): + name = 'ussd_app' + verbose_name = "Ussd App" \ No newline at end of file diff --git a/ussd_app/test.py b/____ussd_app/test.py similarity index 100% rename from ussd_app/test.py rename to ____ussd_app/test.py diff --git a/config/settings.py b/config/settings.py index a43092c..56089fb 100644 --- a/config/settings.py +++ b/config/settings.py @@ -37,6 +37,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'ussd_app' ] MIDDLEWARE = [ diff --git a/config/urls.py b/config/urls.py index 56673f7..aeef142 100644 --- a/config/urls.py +++ b/config/urls.py @@ -18,6 +18,6 @@ from ussd_app import views urlpatterns = [ - url(r'', views.process_ussd, name="process_ussd"), + url(r'^$', views.process_ussd, name="process_ussd"), url(r'^admin/', admin.site.urls), ] diff --git a/db.sqlite3 b/db.sqlite3 index 5d1dc83a90221f0478847d22af67f8a6670fc658..6fe22b87af99fd9bfe829749eea7b0a3ce95d2d0 100644 GIT binary patch delta 4852 zcmb7Hdu&^06~ABX8d#@jH&{~{F6Q{Ma^pS_t)xEKu+Nodo zk;dsd=XTRTJT$rp%pXV_g(3vv5vnPGKupyN5@XU-9z!tEY1)LwYX}i0U}Ajt`rhk% z6PH1io+#)1zVrRgx!-rbbJo^0YtQLE-L{*;FwBnri~ojh9HjxZaq(H$MHrFNWLU%C zkKt!w6k1KMnm%P3H#HgW7|TY=@O#7ahP4g$yaCdtMy$QdSIp<5VJ4eh|3asEw~HXD zLDD@)di$J|$K&?ie*VCuY7Y)4G59u|gkOd~gpb1;Ft)*ZVY#J2)eE9aO!`tL%tTX( zbU2y0)L=2FTw2jKA>a5|?reM<8#~ zA%Gq>WAM-LK70xO06qsl0hi$gI1C?x%^U2Nsk~R+-U&qd%b7$Z7GaV}{XTX3K}jtO zO4q7x@0JYt7|$sgTaG33T8p~fDw!e~L>kdpj%ixd?QN1?2pZ>=`Plk1$8)WExCeuO zg1?17f!~ME!jHoud=w5rJKVFu{>@Z2>(y9$=MIh*9a0|@)iRFzwaCyd8WbG&YLLk) znra-Iainh(^#aG{CQg}OzcHCtS&f71_#<5%Cf1ZR&6tKv51IBD|7pB$e9`y~<7bSI z8RNz&}vRe%f0-ReeevsSi7d}EZ0n;7K zbZ3M8j;Vb8L!dG6WTP+XBy$YyLRV)olU-RYC0r|%GvUcDr%I6|#^a$> zz!ex9T?tU3RB+}*Vvd?y38g~G$+H1+a^`d*kQ%)bJUeuGZfPhnx0LnxliqlA_H<^J zo?Z>jPAoEGCsvtZzuTWW=LjzO%BA3p-w~vyR)b3cIy4e-p^ZwRu{nBfbvhLo@i~J_ z{!(D|L_D6ua-i4Y z?xnnZy&D*G(tV`c;daqnfdKF|48C@I_r-g#>!2#2{h(YLZ-eanozG;|Dl0s;j=$2? z0iS`NgipaWxC#p}4dZYDo`=)$5jX)y;4$tFhZC24Q^WjS=CXwumWKv@G zr8N}Q=XKCDCVN?m#A zJow}0&t^dQFTadZ1@d$5$wXE@ca#Spv>C6xaCn7U;~MCG`1pE zQ~8@H-sEs18eVX_U0$~%S`T+*`1Gjn>?q+6j*QL{wtA2quFA-ljhGD9huerFHld85 z&24s;9KbD%7qI7)WECY(zK17@_&VGkiHic+J$$-cm$3p>&)+u63jB7`*IPWeg(Sff zX9~sUl|Yga4S`u=CF{;v%aBIqq5vcFH>c09=Oir3rwMyw-L)s4wd2D5!zuV zg6B@1I!f4>6xXKMhy?0QF2!=9Iv2YXjxvRqjo>5^oIER9ggjU7&j4FlVkBs}n79vM zzuxc0EiM;$5-5X_y=&51nXc;r@yH;;7Chlf-{Zg@|1Z&Tt=9h&_6LV7wJPH0mO?)1 zYO;jA{AH`H)0B$;#so!)IXGnrx1JCqNI$IiBpxaEu;uwK3p3iAzj5-Q)k3a(Yg zOW@(k(<|V+O=Mq_Wwf*URD7`n%$r9a178D`?-jsTDnt(aV)NB&KnE&YIq;GiwH1pS z>;fpW)O|mfQaMg=NYq42uyQil*MV@Px6X(m z;~*=jeeG4eKtg7b(U57696=agjz;Wj%_Y+77B!ZXt9A_!Mto0*lop^@>`pippc4b+vRPvvX|1F6l2io@j4L#@afXydVTNDB4?^I^$^z z+NHRx6e122{DjLmj}LJShYi+)`f#YvBjOOjReX=na27{#$Y7^YAFAmWvQ=Go)NVCc zlRBkGPgUHa4ZAJ(2-!HII~J^uHq=L!+oov458rso^Y7kEfhQo<5LJ)}`qf4E0r!$C z;HZ;c$~(kK`>MT^j!>H*tsh$g#sNv6!QR^3tj8a2EO-`E0W zLSue2bp$4X(#xb+3m5T2XW$JlJqPprWGy_W(l%1Av7LOPAMTiyham`NegrN7b Date: Fri, 15 Jun 2018 13:49:19 +0100 Subject: [PATCH 29/60] removed breakpoint --- ussd_app/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ussd_app/views.py b/ussd_app/views.py index 0ac9d21..3e623d1 100644 --- a/ussd_app/views.py +++ b/ussd_app/views.py @@ -17,8 +17,7 @@ def process_ussd(request): text = request.POST.get('text') africa_talking = AfricasTalkingUtils() - import pdb; pdb.set_trace() - ussd_response = africa_talking.get_ussd_response(text) + response = africa_talking.get_ussd_response(text) else: response = "Ooops, Sorry..." From fc6f652f09112f2911fe3da5c0d348cf8d2ee098 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 13:53:02 +0100 Subject: [PATCH 30/60] debugging app from simulator --- ussd_app/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index f091cdd..45853fa 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -179,6 +179,7 @@ def get_ussd_response(self, text): response = "CON You selected a wrong option, please try again\n" response += "1. My Account \n" response += "2. My Phone Number \n" + response += "Your Last Input was {} \n".format(text) return response ussd=AfricasTalkingUtils() From c765c22282eb0d10f5da69017f4cb69108d1b46d Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:28:00 +0100 Subject: [PATCH 31/60] conclude ussd and voice app --- ____ussd_app/__init__.py | 1 - ____ussd_app/apps.py | 6 -- ____ussd_app/test.py | 0 db.sqlite3 | Bin 167936 -> 172032 bytes ussd_app/admin.py | 2 +- ussd_app/models.py | 111 ++++++++++++++++++-- ussd_app/utils.py | 216 ++++++++++++++++++++++++++------------- ussd_app/views.py | 20 ++-- 8 files changed, 267 insertions(+), 89 deletions(-) delete mode 100644 ____ussd_app/__init__.py delete mode 100644 ____ussd_app/apps.py delete mode 100644 ____ussd_app/test.py diff --git a/____ussd_app/__init__.py b/____ussd_app/__init__.py deleted file mode 100644 index 16d6fd1..0000000 --- a/____ussd_app/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = 'ussd_app.apps.UssdApp' \ No newline at end of file diff --git a/____ussd_app/apps.py b/____ussd_app/apps.py deleted file mode 100644 index 2ef00fc..0000000 --- a/____ussd_app/apps.py +++ /dev/null @@ -1,6 +0,0 @@ - -from django.apps import AppConfig - -class UssdApp(AppConfig): - name = 'ussd_app' - verbose_name = "Ussd App" \ No newline at end of file diff --git a/____ussd_app/test.py b/____ussd_app/test.py deleted file mode 100644 index e69de29..0000000 diff --git a/db.sqlite3 b/db.sqlite3 index 6fe22b87af99fd9bfe829749eea7b0a3ce95d2d0..a4ee23ba46d61eb0b283e19209b5501f7f1df484 100644 GIT binary patch delta 3879 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 Date: Fri, 15 Jun 2018 23:28:13 +0100 Subject: [PATCH 32/60] conclude ussd and voice app --- .../migrations/0002_auto_20180615_1306.py | 47 +++++++++++++++++++ .../migrations/0003_auto_20180615_1517.py | 35 ++++++++++++++ .../migrations/0004_auto_20180615_1518.py | 20 ++++++++ .../migrations/0005_auto_20180615_1640.py | 31 ++++++++++++ .../migrations/0006_auto_20180615_1658.py | 21 +++++++++ .../migrations/0007_auto_20180615_1751.py | 25 ++++++++++ 6 files changed, 179 insertions(+) create mode 100644 ussd_app/migrations/0002_auto_20180615_1306.py create mode 100644 ussd_app/migrations/0003_auto_20180615_1517.py create mode 100644 ussd_app/migrations/0004_auto_20180615_1518.py create mode 100644 ussd_app/migrations/0005_auto_20180615_1640.py create mode 100644 ussd_app/migrations/0006_auto_20180615_1658.py create mode 100644 ussd_app/migrations/0007_auto_20180615_1751.py diff --git a/ussd_app/migrations/0002_auto_20180615_1306.py b/ussd_app/migrations/0002_auto_20180615_1306.py new file mode 100644 index 0000000..bcd062b --- /dev/null +++ b/ussd_app/migrations/0002_auto_20180615_1306.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-15 13:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ussd_app', '0001_initial'), + ] + + operations = [ + migrations.DeleteModel( + name='Coperative', + ), + migrations.RemoveField( + model_name='sessionlevels', + name='account', + ), + migrations.RemoveField( + model_name='voicecall', + name='account', + ), + migrations.AlterField( + model_name='account', + name='balance', + field=models.DecimalField(decimal_places=2, default=0, max_digits=10, null=True), + ), + migrations.AlterField( + model_name='account', + name='loan', + field=models.DecimalField(decimal_places=2, default=0, max_digits=10, null=True), + ), + migrations.AlterField( + model_name='transaction', + name='amount', + field=models.DecimalField(decimal_places=2, max_digits=10, null=True), + ), + migrations.DeleteModel( + name='SessionLevels', + ), + migrations.DeleteModel( + name='VoiceCall', + ), + ] diff --git a/ussd_app/migrations/0003_auto_20180615_1517.py b/ussd_app/migrations/0003_auto_20180615_1517.py new file mode 100644 index 0000000..af7ee29 --- /dev/null +++ b/ussd_app/migrations/0003_auto_20180615_1517.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-15 15:17 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ussd_app', '0002_auto_20180615_1306'), + ] + + operations = [ + migrations.AddField( + model_name='account', + name='account_name', + field=models.CharField(max_length=25, null=True), + ), + migrations.AddField( + model_name='account', + name='account_number', + field=models.CharField(max_length=25, null=True), + ), + migrations.AddField( + model_name='account', + name='bank', + field=models.CharField(max_length=25, null=True), + ), + migrations.AddField( + model_name='account', + name='bank_code', + field=models.IntegerField(max_length=25, null=True), + ), + ] diff --git a/ussd_app/migrations/0004_auto_20180615_1518.py b/ussd_app/migrations/0004_auto_20180615_1518.py new file mode 100644 index 0000000..771da46 --- /dev/null +++ b/ussd_app/migrations/0004_auto_20180615_1518.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-15 15:18 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ussd_app', '0003_auto_20180615_1517'), + ] + + operations = [ + migrations.AlterField( + model_name='account', + name='bank_code', + field=models.IntegerField(null=True), + ), + ] diff --git a/ussd_app/migrations/0005_auto_20180615_1640.py b/ussd_app/migrations/0005_auto_20180615_1640.py new file mode 100644 index 0000000..c8b88ea --- /dev/null +++ b/ussd_app/migrations/0005_auto_20180615_1640.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-15 16:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ussd_app', '0004_auto_20180615_1518'), + ] + + operations = [ + migrations.AddField( + model_name='transaction', + name='trans_id', + field=models.CharField(default=1234, max_length=255), + preserve_default=False, + ), + migrations.AlterField( + model_name='transaction', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('concluded', 'Concluded')], max_length=30, null=True), + ), + migrations.AlterField( + model_name='transaction', + name='type', + field=models.CharField(choices=[('loan', 'Loan'), ('deposit', 'Deposit')], max_length=30, null=True), + ), + ] diff --git a/ussd_app/migrations/0006_auto_20180615_1658.py b/ussd_app/migrations/0006_auto_20180615_1658.py new file mode 100644 index 0000000..973b84c --- /dev/null +++ b/ussd_app/migrations/0006_auto_20180615_1658.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-15 16:58 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ussd_app', '0005_auto_20180615_1640'), + ] + + operations = [ + migrations.AlterField( + model_name='transaction', + name='account', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transaction', to='ussd_app.Account'), + ), + ] diff --git a/ussd_app/migrations/0007_auto_20180615_1751.py b/ussd_app/migrations/0007_auto_20180615_1751.py new file mode 100644 index 0000000..ed4daf3 --- /dev/null +++ b/ussd_app/migrations/0007_auto_20180615_1751.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-15 17:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ussd_app', '0006_auto_20180615_1658'), + ] + + operations = [ + migrations.AlterField( + model_name='transaction', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('concluded', 'Concluded'), ('declined', 'Payed')], max_length=30, null=True), + ), + migrations.AlterField( + model_name='transaction', + name='trans_id', + field=models.CharField(max_length=255, null=True), + ), + ] From 4be7969f611e7fe17c8adf9769e217f1adfbdc46 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:32:00 +0100 Subject: [PATCH 33/60] refactor: approval --- ussd_app/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ussd_app/models.py b/ussd_app/models.py index ffbd99d..903b5b8 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -74,8 +74,9 @@ class Transaction(models.Model): (PAYED, 'Payed'), (NOT_PAYED, 'Not Payed'), (DECLINED, 'Declined') + (APPROVED, 'Approved') ) - trans_id = models.CharField(max_length=255, null=True) + trans_id = models.CharField(max_length=255, null=True, default='12345') status=models.CharField(max_length=30,null=True, choices=STATUS_TYPE) account = models.ForeignKey(Account, related_name="transaction") type = models.CharField(max_length=30, null=True, choices=TRANSACTION_TYPES) From 358bd72ad059dea30dcb04dcb2d38fd6d4f341c9 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:34:36 +0100 Subject: [PATCH 34/60] refactor: tuple error on heroku --- ussd_app/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ussd_app/models.py b/ussd_app/models.py index 903b5b8..60fbc01 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -73,7 +73,7 @@ class Transaction(models.Model): (CONCLUDED, 'Concluded'), (PAYED, 'Payed'), (NOT_PAYED, 'Not Payed'), - (DECLINED, 'Declined') + (DECLINED, 'Declined'), (APPROVED, 'Approved') ) trans_id = models.CharField(max_length=255, null=True, default='12345') From 49c81baba369e81713dfb8ef869ade3876cfe253 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:39:09 +0100 Subject: [PATCH 35/60] fix: key_error on initiation --- ussd_app/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index ad8e517..3ed6b6b 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -32,14 +32,14 @@ def __init__(self, **kwargs): #Specify your credentials self.username = "sandbox" self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" - self.phonenumber = kwargs['phonenumber'] - self.callerNumber = kwargs['callerNumber'] - self.is_active = kwargs['isActive'] - self.duration_in_seconds = kwargs['durationInSeconds'] + self.phonenumber = kwargs.get('phonenumber') + self.callerNumber = kwargs.get('callerNumber') + self.is_active = kwargs.get('isActive') + self.duration_in_seconds = kwargs.get('durationInSeconds') self.currency_code = kwargs.get('currencyCode') self.amount = kwargs.get('amount') - self.customer = models.Account.get_current_customer(kwargs['phonenumber']) + self.customer = models.Account.get_current_customer(kwargs.get('phonenumber')) #Create an instance of our awesome gateway class and pass your credentials self.gateway = AfricasTalkingGateway(self.username, self.apiKey) From deb577bf604899014a050937c171455306f6659d Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:45:20 +0100 Subject: [PATCH 36/60] chore: added customer authentication --- ussd_app/models.py | 5 +- ussd_app/utils.py | 163 ++++++++++++++++++++++++--------------------- 2 files changed, 92 insertions(+), 76 deletions(-) diff --git a/ussd_app/models.py b/ussd_app/models.py index 60fbc01..22d9d0e 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -27,7 +27,10 @@ def check_balance(cls, phonenumber): @classmethod def get_current_customer(cls, phonenumber): - customer = cls.objects.get(phonenumber=phonenumber) + try: + customer = cls.objects.get(phonenumber=phonenumber) + except: + customer = None return customer @classmethod diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 3ed6b6b..2175a24 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -154,100 +154,113 @@ def get_ussd_response(self, **kwargs): elif kwargs['text'] == JOIN_AGBETUNTU: response = "CON Welcome to Agbetuntu" - elif kwargs['text'] == REQUEST_A_CALL or kwargs['text'] == REQUEST_A_CALL_2: - # trigger call api - response = "Your Request has been recorded, An agent will call soon! #CHEERS " + # elif kwargs['text'] == REQUEST_A_CALL or kwargs['text'] == REQUEST_A_CALL_2: + # # trigger call api + # response = "Your Request has been recorded, An agent will call soon! #CHEERS " elif kwargs['text'].startswith(REQUEST_LOAN): # request a loan - if len(kwargs['text'].split('*')) == 3: - amount=kwargs['text'].split('*')[2] - response = "END An Agent will process your request and get back to you by text, \n" - response += "Enjoy Yourself!" - models.Transaction.record( - account=self.customer, - type=models.Transaction.LOAN, - status=models.Transaction.PENDING, - amount=amount - ) + if self.customer: + if len(kwargs['text'].split('*')) == 3: + amount=kwargs['text'].split('*')[2] + response = "END An Agent will process your request and get back to you by text, \n" + response += "Enjoy Yourself!" + models.Transaction.record( + account=self.customer, + type=models.Transaction.LOAN, + status=models.Transaction.PENDING, + amount=amount + ) + else: + response = "CON Please specify the amount for the loan: \n" else: - response = "CON Please specify the amount for the loan: \n" + response = "END You are not a registered user, please register and try again.\n" elif kwargs['text'].startswith(REQUEST_LOAN_2): - if len(kwargs['text'].split('*')) == 3: - narration = 'Loan from Wazobia group' - access_code=kwargs['text'].split('*')[2] - trans = models.Transaction.check_loan_validity( - access_code=access_code, - phonenumber=self.phonenumber) - if trans: - recipients_list = [ - dict(account_name=self.customer.account_name, account_num=self.customer.account_number, - bank_code=self.customer.bank_code, amount=int(trans.amount), narration=narration, reference_id=access_code, office_branch='201'), - ] - pay_status = self.pay_customers(recipients_list=recipients_list) - if pay_status: - balance, loan = trans.received_loan() - response = "END Your Loan Deposit is being processed. You will receive it shortly. \n" - response += "Balance: {} \n".format(balance) - response += "Loan: {} \n".format(loan) - response += "Enjoy Yourself!" + if self.customer: + if len(kwargs['text'].split('*')) == 3: + narration = 'Loan from Wazobia group' + access_code=kwargs['text'].split('*')[2] + trans = models.Transaction.check_loan_validity( + access_code=access_code, + phonenumber=self.phonenumber) + if trans: + recipients_list = [ + dict(account_name=self.customer.account_name, account_num=self.customer.account_number, + bank_code=self.customer.bank_code, amount=int(trans.amount), narration=narration, reference_id=access_code, office_branch='201'), + ] + pay_status = self.pay_customers(recipients_list=recipients_list) + if pay_status: + balance, loan = trans.received_loan() + response = "END Your Loan Deposit is being processed. You will receive it shortly. \n" + response += "Balance: {} \n".format(balance) + response += "Loan: {} \n".format(loan) + response += "Enjoy Yourself!" + else: + response = "END Your Request was not successful \n" + response += "Please try again later {} \n".format(balance) else: - response = "END Your Request was not successful \n" - response += "Please try again later {} \n".format(balance) + response = "END Your Access Code is invalid.\n" + response += "Please try again.\n" + else: - response = "END Your Access Code is invalid.\n" - response += "Please try again.\n" - + response = "CON Please enter your loan access code to accept the loan requested.\n" else: - response = "CON Please enter your loan access code to accept the loan requested.\n" - + response = "END You are not a registered user, please register and try again.\n" + # sub menu ## check balance Done elif kwargs['text'] == CHECK_BALANCE : # return user balance - response = "CON Balance: {}\n".format(self.customer.balance) - response = "Loan: {}\n".format(self.customer.balance) + if self.customer: + response = "CON Balance: {}\n".format(self.customer.balance) + response = "Loan: {}\n".format(self.customer.balance) + else: + response = "END You are not a registered user, please register and try again.\n" + ## make deposit Done elif kwargs['text'].startswith(MAKE_DEPOSIT): - if len(kwargs['text'].split('*')) == 3: - amount=kwargs['text'].split('*')[2] - if self.customer: - narration = 'Deposit by {}'.format(self.customer.phonenumber) - deposit = self.bank_checkout( - amount=amount, - narration=narration, - account_name=self.customer.account_name, - account_num=self.customer.account_number, - bank_code=self.customer.bank_code) - if deposit: - models.Transaction.record( - account=self.customer, - type=models.Transaction.DEPOSIT, - trans_id=deposit, - status=models.Transaction.PENDING, - amount=amount - ) - response = "CON Please enter OTP sent to your phone,\n"; + if self.customer: + if len(kwargs['text'].split('*')) == 3: + amount=kwargs['text'].split('*')[2] + if self.customer: + narration = 'Deposit by {}'.format(self.customer.phonenumber) + deposit = self.bank_checkout( + amount=amount, + narration=narration, + account_name=self.customer.account_name, + account_num=self.customer.account_number, + bank_code=self.customer.bank_code) + if deposit: + models.Transaction.record( + account=self.customer, + type=models.Transaction.DEPOSIT, + trans_id=deposit, + status=models.Transaction.PENDING, + amount=amount + ) + response = "CON Please enter OTP sent to your phone,\n"; + else: + response = "END Your Deposite was not successful, please try again" + + elif len(kwargs['text'].split('*')) == 4: + otp = kwargs['text'].split('*')[3] + amount = kwargs['text'].split('*')[2] + trans = self.customer.get_last_trans( + status=models.Transaction.PENDING) + validate = self.validate_payment(otp=otp, trans_id=trans.trans_id) + if validate: + balance, loan = trans.mark_as_paid(amount=amount) + response = "CON Deposit was successful,\n" + response += "New Balance: {}".format(balance) + response += "Loan: {}".format(loan) else: response = "END Your Deposite was not successful, please try again" - - elif len(kwargs['text'].split('*')) == 4: - otp = kwargs['text'].split('*')[3] - amount = kwargs['text'].split('*')[2] - trans = self.customer.get_last_trans( - status=models.Transaction.PENDING) - validate = self.validate_payment(otp=otp, trans_id=trans.trans_id) - if validate: - balance, loan = trans.mark_as_paid(amount=amount) - response = "CON Deposit was successful,\n" - response += "New Balance: {}".format(balance) - response += "Loan: {}".format(loan) else: - response = "END Your Deposite was not successful, please try again" + response = "CON Please enter amount: " else: - response = "CON Please enter amount: " - + response = "END You are not a registered user, please register and try again.\n" + ## register user Done elif kwargs['text'] == REGISTER: resp = models.Account.create_account(phonenumber=self.phonenumber) From e7b6cc1a1963da54d75cc80e4e370fcf274027f7 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:48:13 +0100 Subject: [PATCH 37/60] chore: added customer authentication --- ussd_app/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ussd_app/models.py b/ussd_app/models.py index 22d9d0e..05be565 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -35,11 +35,11 @@ def get_current_customer(cls, phonenumber): @classmethod def create_account(cls, phonenumber): - check = cls.objects.exists(phonenumber=kwargs['phonenumber']) + check = cls.objects.exists(phonenumber=phonenumber) if check: return 'Account already exist' else: - cls.objects.create(phonenumber=kwargs['phonenumber']) + cls.objects.create(phonenumber=phonenumber) return 'Your phone number number has been successfully registerd' pass From 418dd0cadc676b7ce440d4fff49b3855ba4cb8fe Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Fri, 15 Jun 2018 23:55:15 +0100 Subject: [PATCH 38/60] corrected typos --- ussd_app/models.py | 5 ++--- ussd_app/utils.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ussd_app/models.py b/ussd_app/models.py index 05be565..d8c73e8 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -35,13 +35,12 @@ def get_current_customer(cls, phonenumber): @classmethod def create_account(cls, phonenumber): - check = cls.objects.exists(phonenumber=phonenumber) + check = cls.objects.filter(phonenumber=phonenumber).exists() if check: - return 'Account already exist' + return 'Account already exist. \n' else: cls.objects.create(phonenumber=phonenumber) return 'Your phone number number has been successfully registerd' - pass def get_last_trans(self, **kwargs): # import pdb; pdb.set_trace() diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 2175a24..042c28d 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -213,7 +213,7 @@ def get_ussd_response(self, **kwargs): # return user balance if self.customer: response = "CON Balance: {}\n".format(self.customer.balance) - response = "Loan: {}\n".format(self.customer.balance) + response += "Loan: {}\n".format(self.customer.balance) else: response = "END You are not a registered user, please register and try again.\n" @@ -264,8 +264,8 @@ def get_ussd_response(self, **kwargs): ## register user Done elif kwargs['text'] == REGISTER: resp = models.Account.create_account(phonenumber=self.phonenumber) - response = "END \n".format(resp) - response = "Balance: {} 0.00".format(CURRENCY) + response = "END {}\n".format(resp) + response += "Balance: {} 0.00\n".format(CURRENCY) else: response = "CON You selected a wrong option, please try again\n" response += "Your Last Input was {} \n".format(kwargs['text']) From a9ab2f0dc381c81d7b08dc8d3c177b5301709ef4 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 00:36:18 +0100 Subject: [PATCH 39/60] optimized --- db.sqlite3 | Bin 172032 -> 172032 bytes ussd_app/models.py | 14 -------- ussd_app/utils.py | 82 +++++++++++++++++++++++++-------------------- ussd_app/views.py | 16 ++++----- 4 files changed, 53 insertions(+), 59 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index a4ee23ba46d61eb0b283e19209b5501f7f1df484..3859a800868f1d86068a1034b4c7d09f24430b73 100644 GIT binary patch delta 845 zcmaiwOHUJF7=^!Yrd(RAUmZh~K)48sgh1!gnYN%N4dqh8Ol@f>GX$m30-foFcG}QR zg0zO6E+h?h;R0C*3rM7r*rh}h<4WAPQC#33@Gm%tK^89Dy~%mcIXO?^*i<++J!^(* zn=M0dtulAg4~Y_Kwbc;lDQMbsHI^P(yCHtGYn_>ExcnYK^9C?)m^UiLocY1yL9B+~ zsBy*VltQKLWHKLOIdyS$Nlj%EY+SW7(P%oOIlMt1&1+Ij(1s~q_A$K1+r*%MSqu(S z0xeQr_Yxz-hx7W)@PT2PAC+W5E46R8uwq`&6q*l+tRTB`UbjLDZiSHo9zA4P7G**9 zj{0(5Jxq{C6d}NCUbm-|+P=CTQ#^7q%1?S!Pt3U-p7tlFnJG=09$ysWBXWqI%1T2X z&J*{i#2}lP&$IG8&5{f8Sac#jLWM_{vVZ@^_-r8PRHR^TNDK;TDab0G1bLSxDMug4 z_0i6Dni*hh12(eXPBAQJ0|3H%mEr)rWJ0F^qAEaDR6V*fw~uP0_~OS4q+#T_Nvq&Tt|?o9=wCo z7Mw7img~Ogit)5kJTx{*gb|=_I0KC?Y9*41q?55wKAKQM<&*EQ`SwakRacYg@QvBE zwOPYC8H&9i!`L{AW2%mC@N;AB89-;~gAU{?y48xi;JM*nl$Z&<1s1fwRfo0X`{+RT z6#s0%h;jvC_su#h92FocppWQBrI<$LLK`;Q*@2rtcYS?_E(4me*-6f3w>v4P@@6FimMGhH;H=C+=z9+Yb%jNIGR|BB|{1D>B5UqdMGq(6u#2%I4CR6&7G)ZH#Qh z#DgAOi0;6}i=387UE;h+I2cb#JQ>4@FW^VeVdBX|@6XHcd1{BA+M(z96}Zy48igy) z(w7O?zl#N^9zS>nU2ZV_yN?1lV5GetxVzK~K@jXaf_z6#{k?B{XA17&ANMdt3SU>r z;Dgo8!dj_nTY9icPFa_!e(#nqu-nJ{hm_gklQWgf0_uRWX%fdV$B& z*?FBe<6FYQ^ondu>#|V_v&D#(m*&?bQnVF`E2znYtwz}pYc67f!E9SXmc;}jV+#gP zDzRL!TVyFonUrnO(2HUH@5?b#K)q9nzQjL9Vol@QBHl->A?G}ST-3d-0aMJcKeFdY#DBt_PFE6y=($B%S$L1g% z52rYCx}1^~QXI+8#G_-mz}ULRrKm8C6)FWoqmuzjPo+q6<_^nAGvuP4U(WIE*FWwb zxcp!b>UdtkPeEhuTyyUYtfPL=YPt{L;V#V%=(Ik-QXlH~&`tL-JnYnco(}yT{@m|6 zg}`Z7@(qk$Lz&)x%meQr0Gtqmok9lkzW_C9**X9K diff --git a/ussd_app/models.py b/ussd_app/models.py index d8c73e8..3adc030 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -17,14 +17,6 @@ class Account(models.Model): def __unicode__(self): return "{} with balance {}".format(self.phonenumber, self.balance) - @classmethod - def check_balance(cls, phonenumber): - try: - resp = cls.objects.filter(phonenumber=phonenumber).first().balance - except: - resp = '' - return - @classmethod def get_current_customer(cls, phonenumber): try: @@ -49,12 +41,6 @@ def get_last_trans(self, **kwargs): else: trans = self.transaction.last() return trans -# class Coperative(models.Model): -# name = models.CharField(max_length=30,null=True) -# phonenumber = models.CharField(max_length=20,null=True) -# city = models.CharField(max_length=30,null=True) -# reg_date=models.DateField(auto_now_add=True) -# level = models.IntegerField(null=True) class Transaction(models.Model): LOAN = 'loan' diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 042c28d..a36dfd0 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -32,12 +32,15 @@ def __init__(self, **kwargs): #Specify your credentials self.username = "sandbox" self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" - self.phonenumber = kwargs.get('phonenumber') - self.callerNumber = kwargs.get('callerNumber') - self.is_active = kwargs.get('isActive') - self.duration_in_seconds = kwargs.get('durationInSeconds') - self.currency_code = kwargs.get('currencyCode') - self.amount = kwargs.get('amount') + self.phonenumber = kwargs.get('phoneNumber', [None])[0] + self.callerNumber = kwargs.get('callerNumber', [None])[0] + self.is_active = kwargs.get('isActive', [None])[0] + self.duration_in_seconds = kwargs.get('du=rationInSeconds', [None])[0] + self.currency_code = kwargs.get('currencyCode', [None])[0] + self.amount = kwargs.get('amount', [None])[0] + self.session_id = kwargs.get('sessionId', [None])[0] + self.service_code = kwargs.get('serviceCode', [None])[0] + self.text = kwargs.get('text', [None])[0] self.customer = models.Account.get_current_customer(kwargs.get('phonenumber')) #Create an instance of our awesome gateway class and pass your credentials @@ -106,6 +109,7 @@ def pay_customers(self, **kwargs): pass def handle_calls(self, **kwargs): + import pdb; pdb.set_trace() duration = self.duration_in_seconds try: if self.is_active == '1': #make the call when isActive is 1 @@ -128,40 +132,35 @@ def handle_calls(self, **kwargs): except: print 'exception',duration - - def ussd(self, **kwargs): - pass - # import pdb; pdb.set_trace() - def get_ussd_response(self, **kwargs): # main menu - if kwargs['text'] == "": + if self.text == "": response = "CON WELCOME, What would you want to check. \n" response += "1. My Cooperative \n" response += "2. Wazobia Loans \n" - elif kwargs['text'] == MY_COPERATIVE: + response += "3. Join Agbetuntun \n" + response += "4. Request a Call \n" + elif self.text == MY_COPERATIVE: response = "CON What would you like to do in You Cooperative account. \n" response += "1. Check Balance \n" response += "2. Accept Loan \n" response += "3. Make Deposit \n" - elif kwargs['text'] == WAZOBIA_LOANS: + elif self.text == WAZOBIA_LOANS: response = "CON WELCOME to Wazobia Loans \n" response += "1. Register \n" response += "2. Repay Loan \n" response += "3. Make Deposit \n" response += "4. Request Loan \n" response += "5. Request a call \n" - elif kwargs['text'] == JOIN_AGBETUNTU: - response = "CON Welcome to Agbetuntu" - # elif kwargs['text'] == REQUEST_A_CALL or kwargs['text'] == REQUEST_A_CALL_2: + # elif self.text == REQUEST_A_CALL or self.text == REQUEST_A_CALL_2: # # trigger call api # response = "Your Request has been recorded, An agent will call soon! #CHEERS " - elif kwargs['text'].startswith(REQUEST_LOAN): + elif self.text.startswith(REQUEST_LOAN): # request a loan if self.customer: - if len(kwargs['text'].split('*')) == 3: - amount=kwargs['text'].split('*')[2] + if len(self.text.split('*')) == 3: + amount=self.text.split('*')[2] response = "END An Agent will process your request and get back to you by text, \n" response += "Enjoy Yourself!" models.Transaction.record( @@ -175,11 +174,11 @@ def get_ussd_response(self, **kwargs): else: response = "END You are not a registered user, please register and try again.\n" - elif kwargs['text'].startswith(REQUEST_LOAN_2): + elif self.text.startswith(REQUEST_LOAN_2): if self.customer: - if len(kwargs['text'].split('*')) == 3: + if len(self.text.split('*')) == 3: narration = 'Loan from Wazobia group' - access_code=kwargs['text'].split('*')[2] + access_code=self.text.split('*')[2] trans = models.Transaction.check_loan_validity( access_code=access_code, phonenumber=self.phonenumber) @@ -209,7 +208,7 @@ def get_ussd_response(self, **kwargs): # sub menu ## check balance Done - elif kwargs['text'] == CHECK_BALANCE : + elif self.text == CHECK_BALANCE : # return user balance if self.customer: response = "CON Balance: {}\n".format(self.customer.balance) @@ -219,10 +218,10 @@ def get_ussd_response(self, **kwargs): ## make deposit Done - elif kwargs['text'].startswith(MAKE_DEPOSIT): + elif self.text.startswith(MAKE_DEPOSIT): if self.customer: - if len(kwargs['text'].split('*')) == 3: - amount=kwargs['text'].split('*')[2] + if len(self.text.split('*')) == 3: + amount=self.text.split('*')[2] if self.customer: narration = 'Deposit by {}'.format(self.customer.phonenumber) deposit = self.bank_checkout( @@ -243,9 +242,9 @@ def get_ussd_response(self, **kwargs): else: response = "END Your Deposite was not successful, please try again" - elif len(kwargs['text'].split('*')) == 4: - otp = kwargs['text'].split('*')[3] - amount = kwargs['text'].split('*')[2] + elif len(self.text.split('*')) == 4: + otp = self.text.split('*')[3] + amount = self.text.split('*')[2] trans = self.customer.get_last_trans( status=models.Transaction.PENDING) validate = self.validate_payment(otp=otp, trans_id=trans.trans_id) @@ -260,19 +259,28 @@ def get_ussd_response(self, **kwargs): response = "CON Please enter amount: " else: response = "END You are not a registered user, please register and try again.\n" - ## register user Done - elif kwargs['text'] == REGISTER: + elif self.text == REGISTER or self.text == JOIN_AGBETUNTU: + suffix = '' + balance = '0.00' + response = '' resp = models.Account.create_account(phonenumber=self.phonenumber) - response = "END {}\n".format(resp) - response += "Balance: {} 0.00\n".format(CURRENCY) + if 'exist' in resp: + suffix = 'END ' + balance = self.customer.balance + else: + response += "END Welcome to Agbetuntu \n" + response += "{}{} \n".format(suffix, resp) + response += "Balance: {} {}\n".format(CURRENCY, balance) + elif self.text == REQUEST_A_CALL or self.text == REQUEST_A_CALL_2: + response = self.handle_calls() else: response = "CON You selected a wrong option, please try again\n" - response += "Your Last Input was {} \n".format(kwargs['text']) + response += "Your Last Input was {} \n".format(self.text) return response - def get_voice_response(self, **kwargs): - response = self.handle_calls() + # def get_voice_response(self, **kwargs): + # ussd=AfricasTalkingUtils() # ussd.handle_calls() pass \ No newline at end of file diff --git a/ussd_app/views.py b/ussd_app/views.py index e4a9bd2..3b3cfc5 100644 --- a/ussd_app/views.py +++ b/ussd_app/views.py @@ -11,13 +11,13 @@ @csrf_exempt def process_ussd(request): if request.method == 'POST': - session_id = request.POST.get('sessionId') - service_code = request.POST.get('serviceCode') - phonenumber = request.POST.get('phoneNumber') - text = request.POST.get('text') + # session_id = request.POST.get('sessionId') + # service_code = request.POST.get('serviceCode') + # phonenumber = request.POST.get('phoneNumber') + # text = request.POST.get('text') - africa_talking = AfricasTalkingUtils(phonenumber=phonenumber) - response = africa_talking.get_ussd_response(text=text) + africa_talking = AfricasTalkingUtils(**request.POST) + response = africa_talking.get_ussd_response() else: response = "Ooops, Sorry... #wink" @@ -26,9 +26,9 @@ def process_ussd(request): @csrf_exempt def process_voice(request): if request.method == 'POST': - caller_number = request.POST.get('callerNumber') + # caller_number = request.POST.get('callerNumber') - africa_talking = AfricasTalkingUtils(phonenumber=caller_number) + africa_talking = AfricasTalkingUtils(**request.POST) response = africa_talking.get_voice_response() else: response = 'Ooops, sorry... #wink' From c323238e4d28e9e0e54a0e5d0284fd5057cdfeb2 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 00:46:51 +0100 Subject: [PATCH 40/60] fix: customer phone number bug --- db.sqlite3 | Bin 172032 -> 172032 bytes ussd_app/models.py | 1 + ussd_app/utils.py | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/db.sqlite3 b/db.sqlite3 index 3859a800868f1d86068a1034b4c7d09f24430b73..d56c2c4a914622d286eb8943e80fc6f68f6fb7c0 100644 GIT binary patch delta 90 zcmZoTz}0YoYl1Xm-9#B@#=4COuKJ8D&CB$+FVkm?d%(lOAH&Gsz<-SYE`Q9%Mjw6^ tB^E9QO%4u41|ZNjGBz Date: Sat, 16 Jun 2018 00:48:28 +0100 Subject: [PATCH 41/60] fix: removed breakpoint --- ussd_app/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ussd_app/models.py b/ussd_app/models.py index a9d4e2b..3adc030 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -19,7 +19,6 @@ def __unicode__(self): @classmethod def get_current_customer(cls, phonenumber): - import pdb; pdb.set_trace() try: customer = cls.objects.get(phonenumber=phonenumber) except: From 89c8cf65ea4a852dd927c5c73f69a32018a52669 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:02:36 +0100 Subject: [PATCH 42/60] chore: completed... --- db.sqlite3 | Bin 172032 -> 172032 bytes ussd_app/models.py | 38 +++++++++++++++++++++++----- ussd_app/utils.py | 61 ++++++++++++++++++++++++++++----------------- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index d56c2c4a914622d286eb8943e80fc6f68f6fb7c0..208a9ae65dbdc19edd62445030107a9b1e17ad7d 100644 GIT binary patch delta 1634 zcma)+&2Jk;6u@`ZwbyprOprg~kHl-IH6evMGrKc8JElSeltxX|3RF<3L@2g*BiclX zL4`_4sbdumNI+|au(?!1Mrtld)T)#msAx}!TRDOXMdBae&WFU#B#KKygk|aX){@_E z-g}<6%lD(p_oFwmpxqjL5wtsRJ}80Y3K}Cp9e%8&TjPn9t79V|dhOQO=B+XCMtxX^ zlL}mgf5PA4b@&my2AAL!_%@t_F1!HG!qafl>-z%aRCvDA1n^8S0Dz`T<|Ky%ql5A} zE18i@OQs|TB$JW}$+%?9Tm2d=26Q;T!Sm{Q@@fAU<1An--UI*mf21- z562G#B0v|&2@DAg3SrJ-Fd%KltZGK8s9-{|Mg+ z&xecJFWTqYYoWhGH$q2(8=Yo77@pH33Va!y^?rN=@*##$2_f&nLy&!DezCGJJhQYk zqZtU>$B}g$TX}@b6qk`*VkTymJ(ufd>l#Hpq!V5yxMU(q3^X^iUpm_Fbi&Gnm5^l{ z7CJu|QJxq9=RjoK@CuK>sCWG#NHk=X1}__|L=my6bt3B@!@*(kyEBUmZvM)}%WvhY zmHEm-&CQFB1G{>}ylfgJ#BD_FiHx5;EwfF-Bn(-EF^;*tkEZe1tb_-bxXlnZPNsd& zY3U(P_afTku^Htyok;D}Lm&5;+x0a5r!QP$)Z~Ou4D5KUZaFq*`}DP^9E%tn8xzSL z4U?LNoR3X=_E7&SF*=>_J?G?jZ1b3JLdZOZu>U|U#(I_!qk$}BPsev`e2wJ zFgh9Y@7dN3z^FSa^S?)x`S-ePT>)$%YLU&Ke*(oPLyDrrV0%?l^V$ad3a-O;r+FF5 zz23FW{c@$fZ|;FpDFE*(@NVDL-JeeL!@dLXJLT6_vnb-mcKoE()W!Ep+i`L`9&I&W z&%OYn%1Br(94TZ2-HtG4;lI3_x;nZJo>%+BhYxbiz&kV>`che~ZK?k%5C)8_YG%~oR7Bo*wsR@JVV75|1ZhdQ&A*04cr6Fc@)OeV5D wi7q!CF?iSVUsj}Mnsk<6hf>$F{T;CJq4=S=QfxH~>Qui_q&9zhR?Rv8040ZOE&u=k delta 381 zcmZoTz}0YoYl1Xm-9#B@#=4CO=k*wcCL8FRunO__Fi1{x6rEhBug=KQyi9-lGJVFl z0xlu`sSNy6`G51jpRTrmQEp>nCja!Q>lyQzh4`0GXWzi6y0NjEfBW+NjIjdDLY#M| zvtIxSZQ$H~=O&}78XNyM1_A!nQ`wmmm{;?!*({jQ$S=Uk%*eM*ar=WBhmdV>UMW@H;B8a4~3da3})RFlZYYo0uDznV6ef8d_Qy85mmV z8kp%Cnz}G@F{m>!DJm;UGb1Z87o5U*Wcqb^CQU05CN2gBs4)nQ42sM^oiJhShA(Gy zU=rZqLoC}!G=j<`y*>6xfB4(`dRe= diff --git a/ussd_app/models.py b/ussd_app/models.py index 3adc030..efd77c9 100644 --- a/ussd_app/models.py +++ b/ussd_app/models.py @@ -26,21 +26,48 @@ def get_current_customer(cls, phonenumber): return customer @classmethod - def create_account(cls, phonenumber): + def create_account(cls, phonenumber, sort_code=None, account_number=None, check_status=True): check = cls.objects.filter(phonenumber=phonenumber).exists() if check: - return 'Account already exist. \n' + return 'Account already exist. \n', False else: - cls.objects.create(phonenumber=phonenumber) - return 'Your phone number number has been successfully registerd' + if not check_status: + cls.objects.create(phonenumber=phonenumber) + return 'Your phone number number has been successfully registerd', True + return 'Does not exist', True def get_last_trans(self, **kwargs): - # import pdb; pdb.set_trace() if kwargs['status'] == Transaction.PENDING: trans = self.transaction.filter(status=Transaction.PENDING).last() else: trans = self.transaction.last() return trans + + def settle_loan(self): + if self.balance > self.loan and self.loan > 0: + self.balance -= self.loan + self.loan = 0 + self.save() + response = "END Loan Repaid was successful,\n" + response += "New Balance: {}\n".format(self.balance) + response += "Loan: {}".format(self.loan) + elif self.loan == 0: + response = "END You have no loan to pay back,\n" + response += "Balance: {}\n".format(self.balance) + response += "Loan: {}".format(self.loan) + elif self.balance > 0 and self.loan > self.balance: + self.loan -= self.balance + self.balance = 0 + self.save() + response = "END Loan Repaid was successful,\n" + response += "New Balance: {}\n".format(self.balance) + response += "Loan: {}".format(self.loan) + else: + response = "END You current balance can not settle your debt,\n" + response += "Please Deposit in your account \n" + response += "New Balance: {}\n".format(self.balance) + response += "Loan: {}".format(self.loan) + return response class Transaction(models.Model): LOAN = 'loan' @@ -111,7 +138,6 @@ def mark_as_paid(self, **kwargs): return self.account.balance, self.account.loan def received_loan(self, **kwargs): - import pdb; pdb.set_trace() self.status = self.APPROVED self.save() self.account.loan += self.amount diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 75f03dc..957b0b6 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -33,7 +33,7 @@ def __init__(self, **kwargs): self.username = "sandbox" self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" self.phonenumber = kwargs.get('phoneNumber', [None])[0] - self.callerNumber = kwargs.get('callerNumber', [None])[0] + self.caller_number = kwargs.get('callerNumber', [None])[0] self.is_active = kwargs.get('isActive', [None])[0] self.duration_in_seconds = kwargs.get('du=rationInSeconds', [None])[0] self.currency_code = kwargs.get('currencyCode', [None])[0] @@ -109,12 +109,11 @@ def pay_customers(self, **kwargs): pass def handle_calls(self, **kwargs): - import pdb; pdb.set_trace() duration = self.duration_in_seconds try: if self.is_active == '1': #make the call when isActive is 1 - callerNumber = self.caller_number + caller_number = self.caller_number or self.phonenumber # Compose the response response = '' @@ -153,9 +152,6 @@ def get_ussd_response(self, **kwargs): response += "4. Request Loan \n" response += "5. Request a call \n" - # elif self.text == REQUEST_A_CALL or self.text == REQUEST_A_CALL_2: - # # trigger call api - # response = "Your Request has been recorded, An agent will call soon! #CHEERS " elif self.text.startswith(REQUEST_LOAN): # request a loan if self.customer: @@ -207,18 +203,15 @@ def get_ussd_response(self, **kwargs): response = "END You are not a registered user, please register and try again.\n" # sub menu - ## check balance Done elif self.text == CHECK_BALANCE : # return user balance if self.customer: - response = "CON Balance: {}\n".format(self.customer.balance) + response = "END Balance: {}\n".format(self.customer.balance) response += "Loan: {}\n".format(self.customer.balance) else: response = "END You are not a registered user, please register and try again.\n" - - ## make deposit Done - elif self.text.startswith(MAKE_DEPOSIT): + elif self.text.startswith(MAKE_DEPOSIT) or self.text.startswith(MAKE_DEPOSIT_2): if self.customer: if len(self.text.split('*')) == 3: amount=self.text.split('*')[2] @@ -250,8 +243,8 @@ def get_ussd_response(self, **kwargs): validate = self.validate_payment(otp=otp, trans_id=trans.trans_id) if validate: balance, loan = trans.mark_as_paid(amount=amount) - response = "CON Deposit was successful,\n" - response += "New Balance: {}".format(balance) + response = "END Deposit was successful,\n" + response += "New Balance: {}\n".format(balance) response += "Loan: {}".format(loan) else: response = "END Your Deposite was not successful, please try again" @@ -259,21 +252,43 @@ def get_ussd_response(self, **kwargs): response = "CON Please enter amount: " else: response = "END You are not a registered user, please register and try again.\n" - ## register user Done - elif self.text == REGISTER or self.text == JOIN_AGBETUNTU: - suffix = '' + + elif self.text.startswith(REGISTER) or self.text.startswith(JOIN_AGBETUNTU): balance = '0.00' - response = '' - resp = models.Account.create_account(phonenumber=self.phonenumber) - if 'exist' in resp: - suffix = 'END ' + resp, check_status = models.Account.create_account(phonenumber=self.phonenumber, check_status=True) + if check_status == False: balance = self.customer.balance + response = "END {} \n".format(resp) + response += "Balance: {} {}\n".format(CURRENCY, self.customer.balance) + response += "Loan: {} {}\n".format(CURRENCY, self.customer.loan) else: - response += "END Welcome to Agbetuntu \n" - response += "{}{} \n".format(suffix, resp) - response += "Balance: {} {}\n".format(CURRENCY, balance) + if(len(self.text.split('*')) == 3) and self.text[0] == JOIN_AGBETUNTU: + sort_code = self.text.split('*')[1] + account_number = self.text.split('*')[2] + resp, check_status = models.Account.create_account(self.phonenumber, + sort_code, account_number, check_status=False) + response = "END Welcome to Agbetuntu \n" + response += "{}\n".format(resp) + response += "Balance: 0.00\n".format(CURRENCY) + response += "Loan: 0.00\n".format(CURRENCY) + elif(len(self.text.split('*')) == 2) and self.text[0] == REGISTER.split('*')[0]: + response += "Please enter your account number 1 \n" + elif (len(self.text.split('*')) == 4) and self.text[0] == REGISTER.split('*')[0]: + sort_code = self.text.split('*')[2] + account_number = self.text.split('*')[3] + resp, check_status = models.Account.create_account(self.phonenumber, + sort_code, account_number, check_status=False) + response = "END Welcome to Agbetuntu \n" + response += "{}\n".format(resp) + response += "Balance: 0.00\n".format(CURRENCY) + response += "Loan: 0.00\n".format(CURRENCY) + else: + response = "Please enter your bank sort code \n" + elif self.text == REQUEST_A_CALL or self.text == REQUEST_A_CALL_2: response = self.handle_calls() + elif self.text == REPAY_LOAN: + response = self.customer.settle_loan() else: response = "CON You selected a wrong option, please try again\n" response += "Your Last Input was {} \n".format(self.text) From 223cee48ce27673f7b18a189bb0cf29135696ea1 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:17:06 +0100 Subject: [PATCH 43/60] heroku: app failed on heroku for api specific parameter --- ussd_app/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 957b0b6..aaf3eba 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -42,6 +42,8 @@ def __init__(self, **kwargs): self.service_code = kwargs.get('serviceCode', [None])[0] self.text = kwargs.get('text', [None])[0] + print(kwargs) + self.customer = models.Account.get_current_customer(self.phonenumber) #Create an instance of our awesome gateway class and pass your credentials self.gateway = AfricasTalkingGateway(self.username, self.apiKey) From 13ef1ecc0939fce260295ae90e49b99166d6db35 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:20:33 +0100 Subject: [PATCH 44/60] heroku: app failed on heroku for api specific parameter --- ussd_app/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index aaf3eba..37a583c 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -40,7 +40,7 @@ def __init__(self, **kwargs): self.amount = kwargs.get('amount', [None])[0] self.session_id = kwargs.get('sessionId', [None])[0] self.service_code = kwargs.get('serviceCode', [None])[0] - self.text = kwargs.get('text', [None])[0] + self.text = kwargs.get('text')[0] print(kwargs) From eeeb9bedab959c78085b8aa54267659c3d450468 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:28:13 +0100 Subject: [PATCH 45/60] heroku: app failed on heroku for api specific parameter --- ussd_app/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 37a583c..efd9fbd 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -40,9 +40,10 @@ def __init__(self, **kwargs): self.amount = kwargs.get('amount', [None])[0] self.session_id = kwargs.get('sessionId', [None])[0] self.service_code = kwargs.get('serviceCode', [None])[0] - self.text = kwargs.get('text')[0] - + self.text = kwargs['text'][0] + # import pdb; pdb.set_trace() print(kwargs) + print(self.__dict__) self.customer = models.Account.get_current_customer(self.phonenumber) #Create an instance of our awesome gateway class and pass your credentials From c32b656a34256df4b5ee54459c39c300c1af9bcc Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:31:41 +0100 Subject: [PATCH 46/60] heroku: app failed on heroku for api specific parameter --- ussd_app/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ussd_app/views.py b/ussd_app/views.py index 3b3cfc5..0e9fcad 100644 --- a/ussd_app/views.py +++ b/ussd_app/views.py @@ -17,6 +17,7 @@ def process_ussd(request): # text = request.POST.get('text') africa_talking = AfricasTalkingUtils(**request.POST) + print(request.POST) response = africa_talking.get_ussd_response() else: response = "Ooops, Sorry... #wink" From 8e370354b03358515310a89a6b601b7893008a1e Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:37:43 +0100 Subject: [PATCH 47/60] simulator stops sending text parameter at some point --- ussd_app/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index efd9fbd..ed52a2e 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -42,8 +42,6 @@ def __init__(self, **kwargs): self.service_code = kwargs.get('serviceCode', [None])[0] self.text = kwargs['text'][0] # import pdb; pdb.set_trace() - print(kwargs) - print(self.__dict__) self.customer = models.Account.get_current_customer(self.phonenumber) #Create an instance of our awesome gateway class and pass your credentials From 81a7f35a38cb8e8e79f78f291788d666ec50af31 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:40:55 +0100 Subject: [PATCH 48/60] simulator stops sending text parameter at some point --- ussd_app/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ussd_app/views.py b/ussd_app/views.py index 0e9fcad..c809c7b 100644 --- a/ussd_app/views.py +++ b/ussd_app/views.py @@ -16,8 +16,8 @@ def process_ussd(request): # phonenumber = request.POST.get('phoneNumber') # text = request.POST.get('text') - africa_talking = AfricasTalkingUtils(**request.POST) print(request.POST) + africa_talking = AfricasTalkingUtils(**request.POST) response = africa_talking.get_ussd_response() else: response = "Ooops, Sorry... #wink" From 7b65bd47d839a35dac2956680c93b718b31c17bd Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 03:50:06 +0100 Subject: [PATCH 49/60] heroku eb workers failed due to '\n' newline in the request --- ussd_app/utils.py | 100 +++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index ed52a2e..b570929 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -40,7 +40,7 @@ def __init__(self, **kwargs): self.amount = kwargs.get('amount', [None])[0] self.session_id = kwargs.get('sessionId', [None])[0] self.service_code = kwargs.get('serviceCode', [None])[0] - self.text = kwargs['text'][0] + self.text = kwargs.get('text')[0] # import pdb; pdb.set_trace() self.customer = models.Account.get_current_customer(self.phonenumber) @@ -135,30 +135,30 @@ def handle_calls(self, **kwargs): def get_ussd_response(self, **kwargs): # main menu if self.text == "": - response = "CON WELCOME, What would you want to check. \n" - response += "1. My Cooperative \n" - response += "2. Wazobia Loans \n" - response += "3. Join Agbetuntun \n" - response += "4. Request a Call \n" + response = "CON WELCOME, What would you want to check. \r\n" + response += "1. My Cooperative \r\n" + response += "2. Wazobia Loans \r\n" + response += "3. Join Agbetuntun \r\n" + response += "4. Request a Call \r\n" elif self.text == MY_COPERATIVE: - response = "CON What would you like to do in You Cooperative account. \n" - response += "1. Check Balance \n" - response += "2. Accept Loan \n" - response += "3. Make Deposit \n" + response = "CON What would you like to do in You Cooperative account. \r\n" + response += "1. Check Balance \r\n" + response += "2. Accept Loan \r\n" + response += "3. Make Deposit \r\n" elif self.text == WAZOBIA_LOANS: - response = "CON WELCOME to Wazobia Loans \n" - response += "1. Register \n" - response += "2. Repay Loan \n" - response += "3. Make Deposit \n" - response += "4. Request Loan \n" - response += "5. Request a call \n" + response = "CON WELCOME to Wazobia Loans \r\n" + response += "1. Register \r\n" + response += "2. Repay Loan \r\n" + response += "3. Make Deposit \r\n" + response += "4. Request Loan \r\n" + response += "5. Request a call \r\n" elif self.text.startswith(REQUEST_LOAN): # request a loan if self.customer: if len(self.text.split('*')) == 3: amount=self.text.split('*')[2] - response = "END An Agent will process your request and get back to you by text, \n" + response = "END An Agent will process your request and get back to you by text, \r\n" response += "Enjoy Yourself!" models.Transaction.record( account=self.customer, @@ -167,9 +167,9 @@ def get_ussd_response(self, **kwargs): amount=amount ) else: - response = "CON Please specify the amount for the loan: \n" + response = "CON Please specify the amount for the loan: \r\n" else: - response = "END You are not a registered user, please register and try again.\n" + response = "END You are not a registered user, please register and try again.\r\n" elif self.text.startswith(REQUEST_LOAN_2): if self.customer: @@ -187,30 +187,30 @@ def get_ussd_response(self, **kwargs): pay_status = self.pay_customers(recipients_list=recipients_list) if pay_status: balance, loan = trans.received_loan() - response = "END Your Loan Deposit is being processed. You will receive it shortly. \n" - response += "Balance: {} \n".format(balance) - response += "Loan: {} \n".format(loan) + response = "END Your Loan Deposit is being processed. You will receive it shortly. \r\n" + response += "Balance: {} \r\n".format(balance) + response += "Loan: {} \r\n".format(loan) response += "Enjoy Yourself!" else: - response = "END Your Request was not successful \n" - response += "Please try again later {} \n".format(balance) + response = "END Your Request was not successful \r\n" + response += "Please try again later {} \r\n".format(balance) else: - response = "END Your Access Code is invalid.\n" - response += "Please try again.\n" + response = "END Your Access Code is invalid.\r\n" + response += "Please try again.\r\n" else: - response = "CON Please enter your loan access code to accept the loan requested.\n" + response = "CON Please enter your loan access code to accept the loan requested.\r\n" else: - response = "END You are not a registered user, please register and try again.\n" + response = "END You are not a registered user, please register and try again.\r\n" # sub menu elif self.text == CHECK_BALANCE : # return user balance if self.customer: - response = "END Balance: {}\n".format(self.customer.balance) - response += "Loan: {}\n".format(self.customer.balance) + response = "END Balance: {}\r\n".format(self.customer.balance) + response += "Loan: {}\r\n".format(self.customer.balance) else: - response = "END You are not a registered user, please register and try again.\n" + response = "END You are not a registered user, please register and try again.\r\n" elif self.text.startswith(MAKE_DEPOSIT) or self.text.startswith(MAKE_DEPOSIT_2): if self.customer: @@ -232,7 +232,7 @@ def get_ussd_response(self, **kwargs): status=models.Transaction.PENDING, amount=amount ) - response = "CON Please enter OTP sent to your phone,\n"; + response = "CON Please enter OTP sent to your phone,\r\n"; else: response = "END Your Deposite was not successful, please try again" @@ -244,55 +244,55 @@ def get_ussd_response(self, **kwargs): validate = self.validate_payment(otp=otp, trans_id=trans.trans_id) if validate: balance, loan = trans.mark_as_paid(amount=amount) - response = "END Deposit was successful,\n" - response += "New Balance: {}\n".format(balance) + response = "END Deposit was successful,\r\n" + response += "New Balance: {}\r\n".format(balance) response += "Loan: {}".format(loan) else: response = "END Your Deposite was not successful, please try again" else: response = "CON Please enter amount: " else: - response = "END You are not a registered user, please register and try again.\n" + response = "END You are not a registered user, please register and try again.\r\n" elif self.text.startswith(REGISTER) or self.text.startswith(JOIN_AGBETUNTU): balance = '0.00' resp, check_status = models.Account.create_account(phonenumber=self.phonenumber, check_status=True) if check_status == False: balance = self.customer.balance - response = "END {} \n".format(resp) - response += "Balance: {} {}\n".format(CURRENCY, self.customer.balance) - response += "Loan: {} {}\n".format(CURRENCY, self.customer.loan) + response = "END {} \r\n".format(resp) + response += "Balance: {} {}\r\n".format(CURRENCY, self.customer.balance) + response += "Loan: {} {}\r\n".format(CURRENCY, self.customer.loan) else: if(len(self.text.split('*')) == 3) and self.text[0] == JOIN_AGBETUNTU: sort_code = self.text.split('*')[1] account_number = self.text.split('*')[2] resp, check_status = models.Account.create_account(self.phonenumber, sort_code, account_number, check_status=False) - response = "END Welcome to Agbetuntu \n" - response += "{}\n".format(resp) - response += "Balance: 0.00\n".format(CURRENCY) - response += "Loan: 0.00\n".format(CURRENCY) + response = "END Welcome to Agbetuntu \r\n" + response += "{}\r\n".format(resp) + response += "Balance: 0.00\r\n".format(CURRENCY) + response += "Loan: 0.00\r\n".format(CURRENCY) elif(len(self.text.split('*')) == 2) and self.text[0] == REGISTER.split('*')[0]: - response += "Please enter your account number 1 \n" + response += "Please enter your account number 1 \r\n" elif (len(self.text.split('*')) == 4) and self.text[0] == REGISTER.split('*')[0]: sort_code = self.text.split('*')[2] account_number = self.text.split('*')[3] resp, check_status = models.Account.create_account(self.phonenumber, sort_code, account_number, check_status=False) - response = "END Welcome to Agbetuntu \n" - response += "{}\n".format(resp) - response += "Balance: 0.00\n".format(CURRENCY) - response += "Loan: 0.00\n".format(CURRENCY) + response = "END Welcome to Agbetuntu \r\n" + response += "{}\r\n".format(resp) + response += "Balance: 0.00\r\n".format(CURRENCY) + response += "Loan: 0.00\r\n".format(CURRENCY) else: - response = "Please enter your bank sort code \n" + response = "Please enter your bank sort code \r\n" elif self.text == REQUEST_A_CALL or self.text == REQUEST_A_CALL_2: response = self.handle_calls() elif self.text == REPAY_LOAN: response = self.customer.settle_loan() else: - response = "CON You selected a wrong option, please try again\n" - response += "Your Last Input was {} \n".format(self.text) + response = "CON You selected a wrong option, please try again\r\n" + response += "Your Last Input was {} \r\n".format(self.text) return response # def get_voice_response(self, **kwargs): From 2c9cb4fce0570aeb8b5c4dbf8868c4d2d8b137a5 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 04:08:28 +0100 Subject: [PATCH 50/60] post data doesnt come in from simulator --- db.sqlite3 | Bin 172032 -> 172032 bytes ussd_app/utils.py | 5 ++++- ussd_app/views.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index 208a9ae65dbdc19edd62445030107a9b1e17ad7d..fd74d8fcae1ab4cd316a6287828b0b64461c6fc4 100644 GIT binary patch delta 87 zcmZoTz}0YoYl1Z6;)ycOjEgrWxau= Date: Sat, 16 Jun 2018 04:11:36 +0100 Subject: [PATCH 51/60] removed break points --- ussd_app/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 04bd711..df9e145 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -263,7 +263,6 @@ def get_ussd_response(self, **kwargs): response += "Balance: {} {}\r\n".format(CURRENCY, self.customer.balance) response += "Loan: {} {}\r\n".format(CURRENCY, self.customer.loan) else: - import pdb; pdb.set_trace() if (len(self.text.split('*')) == 2) and self.text[0] == JOIN_AGBETUNTU: response += "Please enter your account number 1 \r\n" elif(len(self.text.split('*')) == 3) and self.text[0] == JOIN_AGBETUNTU: From ce29e3607b3ee8398395aefd12c16e3a76763232 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 04:23:35 +0100 Subject: [PATCH 52/60] refactor: modified some fields --- db.sqlite3 | Bin 172032 -> 172032 bytes ussd_app/utils.py | 13 +++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index fd74d8fcae1ab4cd316a6287828b0b64461c6fc4..031acb905a3761df45d04c60cbcc4aac96ed480a 100644 GIT binary patch delta 89 zcmZoTz}0YoYl1Z6l8G|Tj7v5qxau?VG%wTNzD%Dn?g1Awe;EV+9{!p9W!w1#m@@d4 s6?wQA)Hyg58GyjZ*u>nx%*4#x(#+D_$iUD-*T78I&}{omd8YIR0D6)Zxc~qF delta 56 zcmV-80LTA;zzTrC3XmHCqmdj#0i&^CMK1vhgQhRHrY`|r;0gi{nE(&G519^rw+;{j OXb+K)Be$6!0%w3J7!t_< diff --git a/ussd_app/utils.py b/ussd_app/utils.py index df9e145..20f0fa2 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -263,8 +263,10 @@ def get_ussd_response(self, **kwargs): response += "Balance: {} {}\r\n".format(CURRENCY, self.customer.balance) response += "Loan: {} {}\r\n".format(CURRENCY, self.customer.loan) else: - if (len(self.text.split('*')) == 2) and self.text[0] == JOIN_AGBETUNTU: - response += "Please enter your account number 1 \r\n" + if (len(self.text.split('*')) == 1) and self.text[0] == JOIN_AGBETUNTU: + response = "CON Please enter your bank sort code \r\n" + elif (len(self.text.split('*')) == 2) and self.text[0] == JOIN_AGBETUNTU: + response = "CON Please enter your account number: \r\n" elif(len(self.text.split('*')) == 3) and self.text[0] == JOIN_AGBETUNTU: sort_code = self.text.split('*')[1] account_number = self.text.split('*')[2] @@ -274,8 +276,10 @@ def get_ussd_response(self, **kwargs): response += "{}\r\n".format(resp) response += "Balance: 0.00\r\n".format(CURRENCY) response += "Loan: 0.00\r\n".format(CURRENCY) + elif(len(self.text.split('*')) == 3) and self.text[0] == REGISTER.split('*')[0]: + response = "CON Please enter your account number: \r\n" elif(len(self.text.split('*')) == 2) and self.text[0] == REGISTER.split('*')[0]: - response += "Please enter your account number 1 \r\n" + response = "CON Please enter your bank sort code \r\n" elif (len(self.text.split('*')) == 4) and self.text[0] == REGISTER.split('*')[0]: sort_code = self.text.split('*')[2] account_number = self.text.split('*')[3] @@ -286,7 +290,8 @@ def get_ussd_response(self, **kwargs): response += "Balance: 0.00\r\n".format(CURRENCY) response += "Loan: 0.00\r\n".format(CURRENCY) else: - response = "Please enter your bank sort code \r\n" + response = "END Registration failed, \r\n" + response += "Try again later \r\n" elif self.text == REQUEST_A_CALL or self.text == REQUEST_A_CALL_2: response = self.handle_calls() From 099b1d1d2ab92689c12251a72eb24ca5658d9c9e Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 04:35:51 +0100 Subject: [PATCH 53/60] app working fine --- db.sqlite3 | Bin 172032 -> 172032 bytes ussd_app/models.py | 2 +- ussd_app/utils.py | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/db.sqlite3 b/db.sqlite3 index 031acb905a3761df45d04c60cbcc4aac96ed480a..70ac2bc396adab6d1896a5aed41f5f36e29d4a75 100644 GIT binary patch delta 785 zcma*h&ui0Q7zgk?Z<1vxE^l=T4&9cfp>soQ+Wcs<9oDMroU#>UAcD%g=*=HD4>J4e zO+@-`V0MzKu#1zKi#b*2f8b^8r2YYVmmNg&1kxUM+4I1MCvU#*%hTG)w01J>Q?SKP zUx2M}MJmcBeIbnS`ib?`;sj{VHjB|_5pF#YO*$DU9njS}ouMu*(#fdMfqiXE76BA9 zF`sB8<`VT_ejB>+<;6hx2(uY_o8F+;=vBHHvcKAhB&uK{dBi|+h#uYf0>oT&=*3Je9GcVRr}?!mFVJ#D+wW#^n*EtPGzkynMIaRAhM zDgJgUmgjiE*Pk$)q|c;j$Ej3&zhU&#fYI5i4zNHEsOWtMKI`EqF6VHh6m!K5%BhbL1{^F@hSYW!GAR zh0MlbI%I1ZBA@4Mfk=NiyG*3_oLw3?ay;JXCTI7F_?xqNBEI8noQO{`A$pi5zY;rm kLY9+R#TmMZ5Ag&F@mugc9$_6>!0iTf-fl$73fZ6i4=^*a9{>OV delta 1768 zcma)+OK)366vywi6XN)FUF88)AdYFPBBYR+xifQTRw3=ejTfku%7O)ud7?mZEK(v? zaRp+B1R)X*8x%^_ZYn_)sb27C*`SCO8#a6bJ^&IRvEW?CB*r8awlyA&&i&0f|NlAn z#>4!Lhxw0Y!dqKUUJP&DTNzmxtwtXNr?-}-wl^2fhsF19FLZA&gs-%+)u`SX+YRTs zcsKl`y#0LTnNI!7@LM?dow2XN#l2H2b5Ykn2tTW2tE2ZjV?V(euCzwG_kRvwcs4r` z-AvRggv(E4qm3Z?Ci*nmC~ppa z_iA(Rmph-&W_CuRAA_*{!ANcCDBX&gXKq06rw93F4F|lFNi3EanlP$GlcJ zE(8yTTnN*UOzlih>Q{Rw*^ejgd|%B} zo+}l;2qyA7nW!7R8J#KrQvR@fvGjZCZfT`7Rs6BIS$w(33;Tr^3*-6k@;ji~>-qNC z>0I#CR9G8-T(w$nt!+4aZA~@p;nlo%-||#0sLnvtOJ=QUuDxxYwXU_czIuHia_Q>j zRr{(`CP`fEq$5^QX{dINI6{@y@TW{;GDT(jsP-T$*gv}V0H$NAuyb4s6DuK;#4_s{ zGnh~jV=EdHgRn=loQy4EoI0LxK}{SpPw^?snB#)QQsClbaWXgu7S}E}DkpKH-NPK& zL5@s08fzM37hCKSO}I`p7leC8C`J^zW|X%w*nODEc%A!%fc7_y4FM8a14Sdyh^+;(iq#?xkgn&6HR?GR5P|Xz8?oC8rGmo(Yl_ Date: Sat, 16 Jun 2018 04:38:02 +0100 Subject: [PATCH 54/60] app working fine --- ussd_app/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 87e63e2..e1958f1 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -41,7 +41,6 @@ def __init__(self, **kwargs): self.session_id = kwargs.get('sessionId', [None])[0] self.service_code = kwargs.get('serviceCode', [None])[0] self.text = kwargs.get('text')[0] - # import pdb; pdb.set_trace() self.customer = models.Account.get_current_customer(self.phonenumber) #Create an instance of our awesome gateway class and pass your credentials @@ -218,7 +217,6 @@ def get_ussd_response(self, **kwargs): amount=self.text.split('*')[2] if self.customer: narration = 'Deposit by {}'.format(self.customer.phonenumber) - import pdb; pdb.set_trace() deposit = self.bank_checkout( amount=amount, narration=narration, From fd823ff36f095509db19a083d1dbf83e7de3bfab Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 04:42:09 +0100 Subject: [PATCH 55/60] app working fine --- db.sqlite3 | Bin 172032 -> 172032 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index 70ac2bc396adab6d1896a5aed41f5f36e29d4a75..3fc482879d2922d83b3a66c67a77ad42dda5e706 100644 GIT binary patch delta 256 zcmZoTz}0YoYl1Z6hKVxHj2kv4oY!L%pKPFC%E;fmOn>__ea5&8Y>fQB82Eop6=0H^ zs=&myRgdWmzW^&UBO|{!F9QPuBmX%D{&U;81elKS8yWC(F{m?fC^9HAFdG@0m>Zax zn44P~TADMQFfuT-&^0jAH8e8>%9xs&TUZ(}PZC~!P@YL(QUeRnmdyTnv#-QkYAixf@~zeV@N<|O1yERagwot vg=wN;qD7*miAhqLajHd9Vv4b4qFIWGS;}^xW%dG_6%tPHZ)Z__ea5&8EDQn+3{wS|Rr($p8c_ydV-N$8WHmOMvMJ|8{-{CL@7K4II-244CAmD;O|wY}YejYUQ68 Xz|QEooza1bO=02$x9#i+Oh4@b(RLoU From 4071b0ebc083bbd1eb078102651af96d0dab487b Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 04:44:10 +0100 Subject: [PATCH 56/60] app working fine --- ussd_app/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index e1958f1..4c45e0f 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -189,7 +189,7 @@ def get_ussd_response(self, **kwargs): response = "END Your Loan Deposit is being processed. You will receive it shortly. \r\n" response += "Balance: {} \r\n".format(balance) response += "Loan: {} \r\n".format(loan) - response += "Enjoy Yourself!" + response += "Enjoy Yourself! \r\n" else: response = "END Your Request was not successful \r\n" response += "Please try again later {} \r\n".format(balance) @@ -233,7 +233,7 @@ def get_ussd_response(self, **kwargs): ) response = "CON Please enter OTP sent to your phone,\r\n"; else: - response = "END Your Deposite was not successful, please try again" + response = "END Your Deposite was not successful, please try again \r\n" elif len(self.text.split('*')) == 4: otp = self.text.split('*')[3] @@ -245,11 +245,11 @@ def get_ussd_response(self, **kwargs): balance, loan = trans.mark_as_paid(amount=amount) response = "END Deposit was successful,\r\n" response += "New Balance: {}\r\n".format(balance) - response += "Loan: {}".format(loan) + response += "Loan: {} \r\n".format(loan) else: - response = "END Your Deposite was not successful, please try again" + response = "END Your Deposite was not successful, please try again \r\n" else: - response = "CON Please enter amount: " + response = "CON Please enter amount: \r\n" else: response = "END You are not a registered user, please register and try again.\r\n" From 84a0123978e8f7cf0c3855d774e2c57bea9d7cb6 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 04:48:02 +0100 Subject: [PATCH 57/60] app working fine --- ussd_app/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 4c45e0f..71bf868 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -207,7 +207,7 @@ def get_ussd_response(self, **kwargs): # return user balance if self.customer: response = "END Balance: {}\r\n".format(self.customer.balance) - response += "Loan: {}\r\n".format(self.customer.balance) + response += "Loan: {}\r\n".format(self.customer.loan) else: response = "END You are not a registered user, please register and try again.\r\n" From b3a5489a79846306088b1de38da8b151cabad3cf Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 05:18:43 +0100 Subject: [PATCH 58/60] removed environmental variables --- .gitignore | 2 +- config/settings.py | 2 +- config/urls.py | 1 + startup.sh | 4 ---- ussd_app/utils.py | 13 +++++-------- ussd_app/views.py | 10 +--------- 6 files changed, 9 insertions(+), 23 deletions(-) delete mode 100644 startup.sh diff --git a/.gitignore b/.gitignore index e40ad29..903c4a3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ *.pyc venv backup -db.sqlite3 \ No newline at end of file +*db.sqlite3 diff --git a/config/settings.py b/config/settings.py index 56089fb..b847786 100644 --- a/config/settings.py +++ b/config/settings.py @@ -20,7 +20,7 @@ # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'u2q$$b@zu9hi3=##rq*($pm70$$pl&z-o*bgfi+hslz_si2=ax' +SECRET_KEY = os.environ.get('SECRET_KEY', 'something-secret') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True diff --git a/config/urls.py b/config/urls.py index aeef142..8de0110 100644 --- a/config/urls.py +++ b/config/urls.py @@ -19,5 +19,6 @@ urlpatterns = [ url(r'^$', views.process_ussd, name="process_ussd"), + url(r'^process-voice/$', views.process_voice, name="process_voice"), url(r'^admin/', admin.site.urls), ] diff --git a/startup.sh b/startup.sh deleted file mode 100644 index a9276a4..0000000 --- a/startup.sh +++ /dev/null @@ -1,4 +0,0 @@ -source venv/bin/activate -# 'qtoye7os0$^-05i+2&w#_t_4c_a0o%nmp!r0c3s(te0kx81t27' -SECRET_KEY='something-very-secretive' -PWD=root \ No newline at end of file diff --git a/ussd_app/utils.py b/ussd_app/utils.py index 71bf868..7bab52a 100644 --- a/ussd_app/utils.py +++ b/ussd_app/utils.py @@ -1,6 +1,9 @@ -from AfricasTalkingGateway import AfricasTalkingGateway, AfricasTalkingGatewayException +import os + from . import models +from AfricasTalkingGateway import AfricasTalkingGateway, AfricasTalkingGatewayException + ## CONSTANT PRODUCT_NAME = 'wazobia' CURRENCY = 'NGN' @@ -31,7 +34,7 @@ class AfricasTalkingUtils: def __init__(self, **kwargs): #Specify your credentials self.username = "sandbox" - self.apiKey = "93c1f491be8e3480265075a1b207cefc7601c36e06d66cc1a178aba7df633832" + self.apiKey = os.environ.get('API_KEY') self.phonenumber = kwargs.get('phoneNumber', [None])[0] self.caller_number = kwargs.get('callerNumber', [None])[0] self.is_active = kwargs.get('isActive', [None])[0] @@ -300,9 +303,3 @@ def get_ussd_response(self, **kwargs): response = "CON You selected a wrong option, please try again\r\n" response += "Your Last Input was {} \r\n".format(self.text) return response - - # def get_voice_response(self, **kwargs): - -# ussd=AfricasTalkingUtils() -# ussd.handle_calls() -pass \ No newline at end of file diff --git a/ussd_app/views.py b/ussd_app/views.py index 454a45b..4d87ad7 100644 --- a/ussd_app/views.py +++ b/ussd_app/views.py @@ -11,12 +11,6 @@ @csrf_exempt def process_ussd(request): if request.method == 'POST': - # session_id = request.POST.get('sessionId') - # service_code = request.POST.get('serviceCode') - # phonenumber = request.POST.get('phoneNumber') - # text = request.POST.get('text') - - print(request.POST.__dict__) africa_talking = AfricasTalkingUtils(**request.POST) response = africa_talking.get_ussd_response() else: @@ -27,10 +21,8 @@ def process_ussd(request): @csrf_exempt def process_voice(request): if request.method == 'POST': - # caller_number = request.POST.get('callerNumber') - africa_talking = AfricasTalkingUtils(**request.POST) - response = africa_talking.get_voice_response() + response = africa_talking.handle_calls() else: response = 'Ooops, sorry... #wink' From 99ba5d68de6f91f48b8d2da0363f1bf3737f753a Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 05:20:49 +0100 Subject: [PATCH 59/60] updated ignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 903c4a3..7fac4e2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ venv backup *db.sqlite3 +startup.sh \ No newline at end of file From f68ffbb36d27ced67e4adb1005ec0fbb856dbbe7 Mon Sep 17 00:00:00 2001 From: Peter Olayinka Date: Sat, 16 Jun 2018 05:24:03 +0100 Subject: [PATCH 60/60] updated ignore file --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7fac4e2..6da9ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ venv backup *db.sqlite3 -startup.sh \ No newline at end of file +start_app.sh \ No newline at end of file