Cic notify build
This commit is contained in:
parent
85f06aa445
commit
cebdb24ed6
@ -3,6 +3,7 @@ include:
|
||||
- local: 'apps/contract-migration/.gitlab-ci.yml'
|
||||
- local: 'apps/cic-eth/.gitlab-ci.yml'
|
||||
- local: 'apps/cic-ussd/.gitlab-ci.yml'
|
||||
- local: 'apps/cic-notify/.gitlab-ci.yml'
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "apps/cic-notify"]
|
||||
path = apps/cic-notify
|
||||
url = git@gitlab.com:grassrootseconomics/cic-notify.git
|
||||
[submodule "apps/cic-cache"]
|
||||
path = apps/cic-cache
|
||||
url = git@gitlab.com:grassrootseconomics/cic-cache.git
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit ba8e5989fa369782ed086e8b10bdd3524e353fe4
|
4
apps/cic-notify/.config/africastalking.ini
Normal file
4
apps/cic-notify/.config/africastalking.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[AFRICASTALKING]
|
||||
api_username = foo
|
||||
api_key = bar
|
||||
api_sender_id = baz
|
3
apps/cic-notify/.config/celery.ini
Normal file
3
apps/cic-notify/.config/celery.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[celery]
|
||||
broker_url = redis://
|
||||
result_url = redis://
|
10
apps/cic-notify/.config/database.ini
Normal file
10
apps/cic-notify/.config/database.ini
Normal file
@ -0,0 +1,10 @@
|
||||
[DATABASE]
|
||||
user = postgres
|
||||
password =
|
||||
host = localhost
|
||||
port = 5432
|
||||
name = /tmp/cic-notify.db
|
||||
#engine = postgresql
|
||||
#driver = psycopg2
|
||||
engine = sqlite
|
||||
driver = pysqlite
|
4
apps/cic-notify/.config/tasks.ini
Normal file
4
apps/cic-notify/.config/tasks.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[TASKS]
|
||||
africastalking = cic_notify.tasks.sms.africastalking
|
||||
db = cic_notify.tasks.sms.db
|
||||
log = cic_notify.tasks.sms.log
|
4
apps/cic-notify/.config/test/africastalking.ini
Normal file
4
apps/cic-notify/.config/test/africastalking.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[AFRICASTALKING]
|
||||
api_username = foo
|
||||
api_key = bar
|
||||
api_sender_id = baz
|
3
apps/cic-notify/.config/test/celery.ini
Normal file
3
apps/cic-notify/.config/test/celery.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[celery]
|
||||
broker_url = filesystem://
|
||||
result_url = filesystem://
|
10
apps/cic-notify/.config/test/database.ini
Normal file
10
apps/cic-notify/.config/test/database.ini
Normal file
@ -0,0 +1,10 @@
|
||||
[DATABASE]
|
||||
user = postgres
|
||||
password =
|
||||
host = localhost
|
||||
port = 5432
|
||||
name = /tmp/cic-notify.db
|
||||
#engine = postgresql
|
||||
#driver = psycopg2
|
||||
engine = sqlite
|
||||
driver = pysqlite
|
4
apps/cic-notify/.config/test/tasks.ini
Normal file
4
apps/cic-notify/.config/test/tasks.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[TASKS]
|
||||
africastalking = cic_notify.tasks.sms.africastalking
|
||||
db = cic_notify.tasks.sms.db
|
||||
log = cic_notify.tasks.sms.log
|
4
apps/cic-notify/.gitignore
vendored
Normal file
4
apps/cic-notify/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
venv/
|
||||
.idea/
|
23
apps/cic-notify/.gitlab-ci.yml
Normal file
23
apps/cic-notify/.gitlab-ci.yml
Normal file
@ -0,0 +1,23 @@
|
||||
.cic_notify_variables:
|
||||
variables:
|
||||
APP_NAME: cic-notify
|
||||
DOCKERFILE_PATH: $APP_NAME/docker/Dockerfile
|
||||
|
||||
.this_changes_target:
|
||||
rules:
|
||||
- changes:
|
||||
- $CONTEXT/$APP_NAME/*
|
||||
|
||||
build-mr-cic-notify:
|
||||
extends:
|
||||
- .this_changes_target
|
||||
- .py_build_merge_request
|
||||
- .cic_notify_variables
|
||||
|
||||
build-push-cic-notify:
|
||||
extends:
|
||||
- .this_changes_target
|
||||
- .py_build_push
|
||||
- .cic_notify_variables
|
||||
|
||||
|
24
apps/cic-notify/CHANGELOG
Normal file
24
apps/cic-notify/CHANGELOG
Normal file
@ -0,0 +1,24 @@
|
||||
- 0.3.2
|
||||
* Relax dependencies to compatible
|
||||
- 0.3.1
|
||||
* Upgrade dependencies
|
||||
- 0.3.0
|
||||
* Rehabilitate africastalking script
|
||||
* Add queue identifiers
|
||||
- 0.2.0
|
||||
* Remove unnecessarily complicated code
|
||||
- 0.1.0
|
||||
* Simplify tasks
|
||||
- 0.0.4
|
||||
* Add python api
|
||||
* Add send sms tool
|
||||
* Add database to tasker script
|
||||
* Rename tasker script
|
||||
* Fix no-commit bug in db notification
|
||||
- 0.0.3
|
||||
* Add sms notify to db
|
||||
- 0.0.2
|
||||
* Re-introduce log sms notify handler
|
||||
- 0.0.1
|
||||
* Port code from cic-ussd
|
||||
* Make dynamic task module loader
|
1
apps/cic-notify/cic_notify/__init__.py
Normal file
1
apps/cic-notify/cic_notify/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .api import *
|
55
apps/cic-notify/cic_notify/api.py
Normal file
55
apps/cic-notify/cic_notify/api.py
Normal file
@ -0,0 +1,55 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import re
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
|
||||
# local imports
|
||||
from cic_notify.tasks import sms
|
||||
|
||||
app = celery.current_app
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
|
||||
sms_tasks_matcher = r"^(cic_notify.tasks.sms)(\.\w+)?"
|
||||
|
||||
|
||||
class Api:
|
||||
# TODO: Implement callback strategy
|
||||
def __init__(self, queue='cic-notify'):
|
||||
"""
|
||||
:param queue: The queue on which to execute notification tasks
|
||||
:type queue: str
|
||||
"""
|
||||
registered_tasks = app.tasks
|
||||
self.sms_tasks = []
|
||||
|
||||
for task in registered_tasks.keys():
|
||||
logg.debug(f'Found: {task} {registered_tasks[task]}')
|
||||
match = re.match(sms_tasks_matcher, task)
|
||||
if match:
|
||||
self.sms_tasks.append(task)
|
||||
|
||||
self.queue = queue
|
||||
logg.info(f'api using queue: {self.queue}')
|
||||
|
||||
def sms(self, message, recipient):
|
||||
"""This function chains all sms tasks in order to send a message, log and persist said data to disk
|
||||
:param message: The message to be sent to the recipient.
|
||||
:type message: str
|
||||
:param recipient: The phone number of the recipient.
|
||||
:type recipient: str
|
||||
:return: a celery Task
|
||||
:rtype: Celery.Task
|
||||
"""
|
||||
signatures = []
|
||||
for task in self.sms_tasks:
|
||||
signature = celery.signature(task)
|
||||
signatures.append(signature)
|
||||
signature_group = celery.group(signatures)
|
||||
result = signature_group.apply_async(
|
||||
args=[message, recipient],
|
||||
queue=self.queue
|
||||
)
|
||||
return result
|
34
apps/cic-notify/cic_notify/db/__init__.py
Normal file
34
apps/cic-notify/cic_notify/db/__init__.py
Normal file
@ -0,0 +1,34 @@
|
||||
# standard imports
|
||||
import os
|
||||
import logging
|
||||
|
||||
# local imports
|
||||
from cic_notify.db.models.base import SessionBase
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def dsn_from_config(config):
|
||||
scheme = config.get('DATABASE_ENGINE')
|
||||
if config.get('DATABASE_DRIVER') != None:
|
||||
scheme += '+{}'.format(config.get('DATABASE_DRIVER'))
|
||||
|
||||
dsn = ''
|
||||
if config.get('DATABASE_ENGINE') == 'sqlite':
|
||||
dsn = '{}:///{}'.format(
|
||||
scheme,
|
||||
config.get('DATABASE_NAME'),
|
||||
)
|
||||
|
||||
else:
|
||||
dsn = '{}://{}:{}@{}:{}/{}'.format(
|
||||
scheme,
|
||||
config.get('DATABASE_USER'),
|
||||
config.get('DATABASE_PASSWORD'),
|
||||
config.get('DATABASE_HOST'),
|
||||
config.get('DATABASE_PORT'),
|
||||
config.get('DATABASE_NAME'),
|
||||
)
|
||||
logg.debug('parsed dsn from config: {}'.format(dsn))
|
||||
return dsn
|
||||
|
7
apps/cic-notify/cic_notify/db/enum.py
Normal file
7
apps/cic-notify/cic_notify/db/enum.py
Normal file
@ -0,0 +1,7 @@
|
||||
import enum
|
||||
|
||||
class NotificationStatusEnum(enum.Enum):
|
||||
UNKNOWN = 'UNKNOWN'
|
||||
|
||||
class NotificationTransportEnum(enum.Enum):
|
||||
SMS = 'SMS'
|
1
apps/cic-notify/cic_notify/db/migrations/default/README
Normal file
1
apps/cic-notify/cic_notify/db/migrations/default/README
Normal file
@ -0,0 +1 @@
|
||||
Generic single-database configuration.
|
85
apps/cic-notify/cic_notify/db/migrations/default/alembic.ini
Normal file
85
apps/cic-notify/cic_notify/db/migrations/default/alembic.ini
Normal file
@ -0,0 +1,85 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = migrations
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# timezone to use when rendering the date
|
||||
# within the migration file as well as the filename.
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; this defaults
|
||||
# to migrations/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path
|
||||
# version_locations = %(here)s/bar %(here)s/bat migrations/versions
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url = postgres+psycopg2://postgres@localhost/cic-notify
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks=black
|
||||
# black.type=console_scripts
|
||||
# black.entrypoint=black
|
||||
# black.options=-l 79
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
77
apps/cic-notify/cic_notify/db/migrations/default/env.py
Normal file
77
apps/cic-notify/cic_notify/db/migrations/default/env.py
Normal file
@ -0,0 +1,77 @@
|
||||
from logging.config import fileConfig
|
||||
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
|
||||
from alembic import context
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = None
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection, target_metadata=target_metadata
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
@ -0,0 +1,24 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
@ -0,0 +1,48 @@
|
||||
"""Notify sms log
|
||||
|
||||
Revision ID: b2aedf79b0b2
|
||||
Revises:
|
||||
Create Date: 2020-10-11 15:59:02.765157
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b2aedf79b0b2'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
status_enum = sa.Enum(
|
||||
'UNKNOWN', # the state of the message is not known
|
||||
name='notification_status',
|
||||
)
|
||||
|
||||
transport_enum = sa.Enum(
|
||||
'SMS',
|
||||
name='notification_transport',
|
||||
)
|
||||
|
||||
def upgrade():
|
||||
op.create_table('notification',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('transport', transport_enum, nullable=False),
|
||||
sa.Column('status', status_enum, nullable=False),
|
||||
sa.Column('status_code', sa.String(), nullable=True),
|
||||
sa.Column('status_serial', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('recipient', sa.String(), nullable=False),
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated', sa.DateTime(), nullable=False),
|
||||
sa.Column('message', sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
)
|
||||
op.create_index('notification_recipient_transport_idx', 'notification', ['transport', 'recipient'], schema=None, unique=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index('notification_recipient_transport_idx')
|
||||
op.drop_table('notification')
|
||||
status_enum.drop(op.get_bind(), checkfirst=False)
|
||||
transport_enum.drop(op.get_bind(), checkfirst=False)
|
0
apps/cic-notify/cic_notify/db/models/__init__.py
Normal file
0
apps/cic-notify/cic_notify/db/models/__init__.py
Normal file
40
apps/cic-notify/cic_notify/db/models/base.py
Normal file
40
apps/cic-notify/cic_notify/db/models/base.py
Normal file
@ -0,0 +1,40 @@
|
||||
# third-party imports
|
||||
from sqlalchemy import Column, Integer
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
Model = declarative_base(name='Model')
|
||||
|
||||
|
||||
class SessionBase(Model):
|
||||
__abstract__ = True
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
engine = None
|
||||
session = None
|
||||
query = None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def create_session():
|
||||
session = sessionmaker(bind=SessionBase.engine)
|
||||
SessionBase.session = session()
|
||||
return SessionBase.session
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _set_engine(engine):
|
||||
SessionBase.engine = engine
|
||||
|
||||
|
||||
@staticmethod
|
||||
def build():
|
||||
Model.metadata.create_all(bind=SessionBase.engine)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def connect(dsn):
|
||||
e = create_engine(dsn)
|
||||
SessionBase._set_engine(e)
|
27
apps/cic-notify/cic_notify/db/models/notification.py
Normal file
27
apps/cic-notify/cic_notify/db/models/notification.py
Normal file
@ -0,0 +1,27 @@
|
||||
# standard imports
|
||||
import datetime
|
||||
|
||||
# third-party imports
|
||||
from sqlalchemy import Enum, Column, String, DateTime
|
||||
|
||||
# local imports
|
||||
from .base import SessionBase
|
||||
from ..enum import NotificationStatusEnum, NotificationTransportEnum
|
||||
|
||||
|
||||
class Notification(SessionBase):
|
||||
__tablename__ = 'notification'
|
||||
|
||||
transport = Column(Enum(NotificationTransportEnum))
|
||||
status = Column(Enum(NotificationStatusEnum))
|
||||
recipient = Column(String)
|
||||
message = Column(String)
|
||||
created = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
def __init__(self, transport, recipient, message, **kwargs):
|
||||
super(Notification, self).__init__(**kwargs)
|
||||
self.transport = transport
|
||||
self.recipient = recipient
|
||||
self.message = message
|
||||
self.status = NotificationStatusEnum.UNKNOWN
|
11
apps/cic-notify/cic_notify/error.py
Normal file
11
apps/cic-notify/cic_notify/error.py
Normal file
@ -0,0 +1,11 @@
|
||||
class NotInitializedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyInitializedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PleaseCommitFirstError(Exception):
|
||||
"""Raised when there exists uncommitted changes in the code while trying to build out the package."""
|
||||
pass
|
110
apps/cic-notify/cic_notify/runnable/tasker.py
Normal file
110
apps/cic-notify/cic_notify/runnable/tasker.py
Normal file
@ -0,0 +1,110 @@
|
||||
# standard imports
|
||||
import os
|
||||
import logging
|
||||
import importlib
|
||||
import argparse
|
||||
import tempfile
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
import confini
|
||||
|
||||
# local imports
|
||||
from cic_notify.db.models.base import SessionBase
|
||||
from cic_notify.db import dsn_from_config
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
config_dir = os.path.join('/usr/local/etc/cic-notify')
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-c', type=str, default=config_dir, help='config file')
|
||||
argparser.add_argument('-q', type=str, default='cic-notify', help='queue name for worker tasks')
|
||||
argparser.add_argument('-v', action='store_true', help='be verbose')
|
||||
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
|
||||
argparser.add_argument('-vv', action='store_true', help='be more verbose')
|
||||
args = argparser.parse_args()
|
||||
|
||||
if args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
config = confini.Config(args.c, args.env_prefix)
|
||||
config.process()
|
||||
config.censor('PASSWORD', 'DATABASE')
|
||||
|
||||
# connect to database
|
||||
dsn = dsn_from_config(config)
|
||||
SessionBase.connect(dsn)
|
||||
|
||||
# verify database connection with minimal sanity query
|
||||
session = SessionBase.create_session()
|
||||
session.execute('select version_num from alembic_version')
|
||||
session.close()
|
||||
|
||||
# set up celery
|
||||
app = celery.Celery(__name__)
|
||||
|
||||
broker = config.get('CELERY_BROKER_URL')
|
||||
if broker[:4] == 'file':
|
||||
bq = tempfile.mkdtemp()
|
||||
bp = tempfile.mkdtemp()
|
||||
app.conf.update({
|
||||
'broker_url': broker,
|
||||
'broker_transport_options': {
|
||||
'data_folder_in': bq,
|
||||
'data_folder_out': bq,
|
||||
'data_folder_processed': bp,
|
||||
},
|
||||
},
|
||||
)
|
||||
logg.warning('celery broker dirs queue i/o {} processed {}, will NOT be deleted on shutdown'.format(bq, bp))
|
||||
else:
|
||||
app.conf.update({
|
||||
'broker_url': broker,
|
||||
})
|
||||
|
||||
result = config.get('CELERY_RESULT_URL')
|
||||
if result[:4] == 'file':
|
||||
rq = tempfile.mkdtemp()
|
||||
app.conf.update({
|
||||
'result_backend': 'file://{}'.format(rq),
|
||||
})
|
||||
logg.warning('celery backend store dir {} created, will NOT be deleted on shutdown'.format(rq))
|
||||
else:
|
||||
app.conf.update({
|
||||
'result_backend': result,
|
||||
})
|
||||
|
||||
|
||||
for key in config.store.keys():
|
||||
if key[:5] == 'TASKS':
|
||||
logg.info(f'adding sms task from {key}')
|
||||
module = importlib.import_module(config.store[key])
|
||||
if key == 'TASKS_AFRICASTALKING':
|
||||
africastalking_notifier = module.AfricasTalkingNotifier
|
||||
africastalking_notifier.initialize(
|
||||
config.get('AFRICASTALKING_API_USERNAME'),
|
||||
config.get('AFRICASTALKING_API_KEY'),
|
||||
config.get('AFRICASTALKING_API_SENDER_ID')
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argv = ['worker']
|
||||
if args.vv:
|
||||
argv.append('--loglevel=DEBUG')
|
||||
elif args.v:
|
||||
argv.append('--loglevel=INFO')
|
||||
argv.append('-Q')
|
||||
argv.append(args.q)
|
||||
argv.append('-n')
|
||||
argv.append(args.q)
|
||||
|
||||
app.worker_main(argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1
apps/cic-notify/cic_notify/tasks/__init__.py
Normal file
1
apps/cic-notify/cic_notify/tasks/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import sms
|
12
apps/cic-notify/cic_notify/tasks/sms/__init__.py
Normal file
12
apps/cic-notify/cic_notify/tasks/sms/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
# standard imports
|
||||
|
||||
# third-party imports
|
||||
|
||||
# local imports
|
||||
import celery
|
||||
|
||||
celery_app = celery.current_app
|
||||
|
||||
from .africastalking import send
|
||||
from .db import persist_notification
|
||||
from .log import log
|
69
apps/cic-notify/cic_notify/tasks/sms/africastalking.py
Normal file
69
apps/cic-notify/cic_notify/tasks/sms/africastalking.py
Normal file
@ -0,0 +1,69 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# third party imports
|
||||
import celery
|
||||
import africastalking
|
||||
|
||||
# local imports
|
||||
from cic_notify.error import NotInitializedError, AlreadyInitializedError
|
||||
|
||||
logg = logging.getLogger()
|
||||
celery_app = celery.current_app
|
||||
|
||||
|
||||
class AfricasTalkingNotifier:
|
||||
initiated = None
|
||||
sender_id = None
|
||||
|
||||
def __init__(self):
|
||||
if not self.initiated:
|
||||
raise NotInitializedError()
|
||||
self.api_client = africastalking.SMS
|
||||
|
||||
@staticmethod
|
||||
def initialize(api_username, api_key, sender_id=None):
|
||||
"""
|
||||
:param api_username:
|
||||
:type api_username:
|
||||
:param api_key:
|
||||
:type api_key:
|
||||
:param sender_id:
|
||||
:type sender_id:
|
||||
"""
|
||||
if AfricasTalkingNotifier.initiated:
|
||||
raise AlreadyInitializedError()
|
||||
africastalking.initialize(username=api_username, api_key=api_key)
|
||||
|
||||
AfricasTalkingNotifier.sender_id = sender_id
|
||||
AfricasTalkingNotifier.initiated = True
|
||||
|
||||
def send(self, message, recipient):
|
||||
"""
|
||||
:param message:
|
||||
:type message:
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
if self.sender_id:
|
||||
response = self.api_client.send(message=message, recipients=[recipient], sender_id=self.sender_id)
|
||||
logg.debug(f'Africastalking response sender-id {response}')
|
||||
else:
|
||||
response = self.api_client.send(message=message, recipients=[recipient])
|
||||
logg.debug(f'africastalking response no-sender-id {response}')
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def send(message, recipient):
|
||||
"""
|
||||
:param message:
|
||||
:type message:
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
africastalking_notifier = AfricasTalkingNotifier()
|
||||
africastalking_notifier.send(message=message, recipient=recipient)
|
26
apps/cic-notify/cic_notify/tasks/sms/db.py
Normal file
26
apps/cic-notify/cic_notify/tasks/sms/db.py
Normal file
@ -0,0 +1,26 @@
|
||||
# standard imports
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
|
||||
# local imports
|
||||
from cic_notify.db.models.notification import Notification
|
||||
from cic_notify.db.enum import NotificationTransportEnum
|
||||
|
||||
celery_app = celery.current_app
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def persist_notification(recipient, message):
|
||||
"""
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:param message:
|
||||
:type message:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
Notification.create_session()
|
||||
notification = Notification(transport=NotificationTransportEnum.SMS, recipient=recipient, message=message)
|
||||
Notification.session.add(notification)
|
||||
Notification.session.commit()
|
26
apps/cic-notify/cic_notify/tasks/sms/log.py
Normal file
26
apps/cic-notify/cic_notify/tasks/sms/log.py
Normal file
@ -0,0 +1,26 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import time
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
|
||||
celery_app = celery.current_app
|
||||
logg = celery_app.log.get_default_logger()
|
||||
local_logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def log(recipient, message):
|
||||
"""
|
||||
:param recipient:
|
||||
:type recipient:
|
||||
:param message:
|
||||
:type message:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
timestamp = time.time()
|
||||
log_string = f'[{timestamp}] {__name__} message to {recipient}: {message}'
|
||||
logg.info(log_string)
|
||||
local_logg.info(log_string)
|
47
apps/cic-notify/cic_notify/version.py
Normal file
47
apps/cic-notify/cic_notify/version.py
Normal file
@ -0,0 +1,47 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import time
|
||||
|
||||
# third-party imports
|
||||
import semver
|
||||
|
||||
# local imports
|
||||
from cic_notify.error import PleaseCommitFirstError
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
version = (0, 4, 0, 'alpha.2')
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
major=version[0],
|
||||
minor=version[1],
|
||||
patch=version[2],
|
||||
prerelease=version[3],
|
||||
)
|
||||
|
||||
version_string = str(version_object)
|
||||
|
||||
|
||||
def git_hash():
|
||||
import subprocess
|
||||
git_diff = subprocess.run(['git', 'diff'], capture_output=True)
|
||||
if len(git_diff.stdout) > 0:
|
||||
raise PleaseCommitFirstError()
|
||||
|
||||
git_hash = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True)
|
||||
git_hash_brief = git_hash.stdout.decode('utf-8')[:8]
|
||||
return git_hash_brief
|
||||
|
||||
|
||||
try:
|
||||
version_git = git_hash()
|
||||
version_string += '.build.{}'.format(version_git)
|
||||
except FileNotFoundError:
|
||||
time_string_pair = str(time.time()).split('.')
|
||||
version_string += '+build.{}{:<09d}'.format(
|
||||
time_string_pair[0],
|
||||
int(time_string_pair[1]),
|
||||
)
|
||||
logg.info(f'Final version string will be {version_string}')
|
||||
|
||||
__version_string__ = version_string
|
5
apps/cic-notify/doc/texinfo/Makefile
Normal file
5
apps/cic-notify/doc/texinfo/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
all: html
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
makeinfo --html --output html index.texi
|
14
apps/cic-notify/doc/texinfo/api.texi
Normal file
14
apps/cic-notify/doc/texinfo/api.texi
Normal file
@ -0,0 +1,14 @@
|
||||
@node API
|
||||
@chapter API
|
||||
|
||||
A setuptools file is provided to install @code{cic-notify} as a normal python package.
|
||||
|
||||
A single Python API method is provided so far:
|
||||
|
||||
@itemize @code
|
||||
@item sms(recipient, content)
|
||||
@end itemize
|
||||
|
||||
@code{recipient} is a string with phone number in @code{msisdn} format with a @code{+} prefix.
|
||||
|
||||
@code{content} is a UTF-8 string containing the message to be sent.
|
61
apps/cic-notify/doc/texinfo/html/API.html
Normal file
61
apps/cic-notify/doc/texinfo/html/API.html
Normal file
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<!-- Released 2020 under GPL3 -->
|
||||
<!-- Created by GNU Texinfo 6.7, http://www.gnu.org/software/texinfo/ -->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>API (Grassroots Economics CIC notify 0.2.0)</title>
|
||||
|
||||
<meta name="description" content="API (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="keywords" content="API (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="resource-type" content="document">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="Generator" content="makeinfo">
|
||||
<link href="index.html" rel="start" title="Top">
|
||||
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
|
||||
<link href="index.html" rel="up" title="Top">
|
||||
<link href="Run.html" rel="prev" title="Run">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
a.summary-letter {text-decoration: none}
|
||||
blockquote.indentedblock {margin-right: 0em}
|
||||
div.display {margin-left: 3.2em}
|
||||
div.example {margin-left: 3.2em}
|
||||
div.lisp {margin-left: 3.2em}
|
||||
kbd {font-style: oblique}
|
||||
pre.display {font-family: inherit}
|
||||
pre.format {font-family: inherit}
|
||||
pre.menu-comment {font-family: serif}
|
||||
pre.menu-preformatted {font-family: serif}
|
||||
span.nolinebreak {white-space: nowrap}
|
||||
span.roman {font-family: initial; font-weight: normal}
|
||||
span.sansserif {font-family: sans-serif; font-weight: normal}
|
||||
ul.no-bullet {list-style: none}
|
||||
-->
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<span id="API"></span><div class="header">
|
||||
<p>
|
||||
Previous: <a href="Run.html" accesskey="p" rel="prev">Run</a>, Up: <a href="index.html" accesskey="u" rel="up">Top</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
|
||||
</div>
|
||||
<hr>
|
||||
<span id="API-1"></span><h2 class="chapter">4 API</h2>
|
||||
|
||||
<p>A single Python API method is provided so far:
|
||||
</p>
|
||||
<ul class="no-bullet">
|
||||
<li> sms(recipient, content)
|
||||
</li></ul>
|
||||
|
||||
<p><code>recipient</code> is a string with phone number in <code>msisdn</code> format with a <code>+</code> prefix.
|
||||
</p>
|
||||
<p><code>content</code> is a UTF-8 string containing the message to be sent.
|
||||
</p>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
72
apps/cic-notify/doc/texinfo/html/Overview.html
Normal file
72
apps/cic-notify/doc/texinfo/html/Overview.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<!-- Released 2020 under GPL3 -->
|
||||
<!-- Created by GNU Texinfo 6.7, http://www.gnu.org/software/texinfo/ -->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Overview (Grassroots Economics CIC notify 0.2.0)</title>
|
||||
|
||||
<meta name="description" content="Overview (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="keywords" content="Overview (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="resource-type" content="document">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="Generator" content="makeinfo">
|
||||
<link href="index.html" rel="start" title="Top">
|
||||
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
|
||||
<link href="index.html" rel="up" title="Top">
|
||||
<link href="Setup.html" rel="next" title="Setup">
|
||||
<link href="index.html" rel="prev" title="Top">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
a.summary-letter {text-decoration: none}
|
||||
blockquote.indentedblock {margin-right: 0em}
|
||||
div.display {margin-left: 3.2em}
|
||||
div.example {margin-left: 3.2em}
|
||||
div.lisp {margin-left: 3.2em}
|
||||
kbd {font-style: oblique}
|
||||
pre.display {font-family: inherit}
|
||||
pre.format {font-family: inherit}
|
||||
pre.menu-comment {font-family: serif}
|
||||
pre.menu-preformatted {font-family: serif}
|
||||
span.nolinebreak {white-space: nowrap}
|
||||
span.roman {font-family: initial; font-weight: normal}
|
||||
span.sansserif {font-family: sans-serif; font-weight: normal}
|
||||
ul.no-bullet {list-style: none}
|
||||
-->
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<span id="Overview"></span><div class="header">
|
||||
<p>
|
||||
Next: <a href="Setup.html" accesskey="n" rel="next">Setup</a>, Up: <a href="index.html" accesskey="u" rel="up">Top</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
|
||||
</div>
|
||||
<hr>
|
||||
<span id="Overview-1"></span><h2 class="chapter">1 Overview</h2>
|
||||
|
||||
<p>This system is in early stages of development. It is intended to be a flexible notification broker where additional notification targets easily can be plugged in.
|
||||
</p>
|
||||
<p>The framework is designed to asynchronously execute all tasks belonging to a specific context, based on the name of the task.
|
||||
</p>
|
||||
<p>Currently, only handlers for <strong>sms</strong> notifications are implemented. Any task with a <code>notify.sms.</code> prefix registered in the celery task worker pool will be executed upon the high-level "send sms" task.
|
||||
</p>
|
||||
<p>Similary, any other notification category can be implemented. e.g. <code>notify.email.</code>, <code>notify.telegram.</code>, <code>notify.mattermost.</code> etc.
|
||||
</p>
|
||||
<span id="Contents"></span><h3 class="section">1.1 Contents</h3>
|
||||
|
||||
<p>The only implementations so far are three <code>sms</code> notification tasks:
|
||||
</p>
|
||||
<ul>
|
||||
<li> log (as in python logger, thus more or less noop)
|
||||
</li><li> db (postgres)
|
||||
</li><li> Africas Talking API
|
||||
</li></ul>
|
||||
|
||||
<p><strong>NOTE</strong>: The Africas Talking API will be removed from the suite, and provided as an add-on package down the road. It will illustrate how to include arbitrary tasks to a asynchronous group of notification targets.
|
||||
</p>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
54
apps/cic-notify/doc/texinfo/html/Run.html
Normal file
54
apps/cic-notify/doc/texinfo/html/Run.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<!-- Released 2020 under GPL3 -->
|
||||
<!-- Created by GNU Texinfo 6.7, http://www.gnu.org/software/texinfo/ -->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Run (Grassroots Economics CIC notify 0.2.0)</title>
|
||||
|
||||
<meta name="description" content="Run (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="keywords" content="Run (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="resource-type" content="document">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="Generator" content="makeinfo">
|
||||
<link href="index.html" rel="start" title="Top">
|
||||
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
|
||||
<link href="index.html" rel="up" title="Top">
|
||||
<link href="API.html" rel="next" title="API">
|
||||
<link href="Setup.html" rel="prev" title="Setup">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
a.summary-letter {text-decoration: none}
|
||||
blockquote.indentedblock {margin-right: 0em}
|
||||
div.display {margin-left: 3.2em}
|
||||
div.example {margin-left: 3.2em}
|
||||
div.lisp {margin-left: 3.2em}
|
||||
kbd {font-style: oblique}
|
||||
pre.display {font-family: inherit}
|
||||
pre.format {font-family: inherit}
|
||||
pre.menu-comment {font-family: serif}
|
||||
pre.menu-preformatted {font-family: serif}
|
||||
span.nolinebreak {white-space: nowrap}
|
||||
span.roman {font-family: initial; font-weight: normal}
|
||||
span.sansserif {font-family: sans-serif; font-weight: normal}
|
||||
ul.no-bullet {list-style: none}
|
||||
-->
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<span id="Run"></span><div class="header">
|
||||
<p>
|
||||
Next: <a href="API.html" accesskey="n" rel="next">API</a>, Previous: <a href="Setup.html" accesskey="p" rel="prev">Setup</a>, Up: <a href="index.html" accesskey="u" rel="up">Top</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
|
||||
</div>
|
||||
<hr>
|
||||
<span id="Running-the-Application"></span><h2 class="chapter">3 Running the Application</h2>
|
||||
|
||||
<p>A convenience task runner is provided in <samp>scripts/cic-notify-tasker.py</samp>.
|
||||
</p>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
64
apps/cic-notify/doc/texinfo/html/Setup.html
Normal file
64
apps/cic-notify/doc/texinfo/html/Setup.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<!-- Released 2020 under GPL3 -->
|
||||
<!-- Created by GNU Texinfo 6.7, http://www.gnu.org/software/texinfo/ -->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Setup (Grassroots Economics CIC notify 0.2.0)</title>
|
||||
|
||||
<meta name="description" content="Setup (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="keywords" content="Setup (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="resource-type" content="document">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="Generator" content="makeinfo">
|
||||
<link href="index.html" rel="start" title="Top">
|
||||
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
|
||||
<link href="index.html" rel="up" title="Top">
|
||||
<link href="Run.html" rel="next" title="Run">
|
||||
<link href="Overview.html" rel="prev" title="Overview">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
a.summary-letter {text-decoration: none}
|
||||
blockquote.indentedblock {margin-right: 0em}
|
||||
div.display {margin-left: 3.2em}
|
||||
div.example {margin-left: 3.2em}
|
||||
div.lisp {margin-left: 3.2em}
|
||||
kbd {font-style: oblique}
|
||||
pre.display {font-family: inherit}
|
||||
pre.format {font-family: inherit}
|
||||
pre.menu-comment {font-family: serif}
|
||||
pre.menu-preformatted {font-family: serif}
|
||||
span.nolinebreak {white-space: nowrap}
|
||||
span.roman {font-family: initial; font-weight: normal}
|
||||
span.sansserif {font-family: sans-serif; font-weight: normal}
|
||||
ul.no-bullet {list-style: none}
|
||||
-->
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<span id="Setup"></span><div class="header">
|
||||
<p>
|
||||
Next: <a href="Run.html" accesskey="n" rel="next">Run</a>, Previous: <a href="Overview.html" accesskey="p" rel="prev">Overview</a>, Up: <a href="index.html" accesskey="u" rel="up">Top</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
|
||||
</div>
|
||||
<hr>
|
||||
<span id="Setup-1"></span><h2 class="chapter">2 Setup</h2>
|
||||
|
||||
|
||||
<span id="Handlers"></span><h3 class="section">2.1 Handlers</h3>
|
||||
|
||||
<p>Notification tasks in this package are intended to be loaded dynamically, making no assumptions on which handlers that should be connected to the different tasks. The tasks provided are:
|
||||
</p>
|
||||
<ul>
|
||||
<li> <strong>notify.sms.log</strong>: (none)
|
||||
</li><li> <strong>notify.sms.db</strong>: Postgresql, along with python packages alembic, SQLAlchemy and psycopg2.
|
||||
</li><li> <strong>notify.sms.africastalking</strong>: africastalking python package.
|
||||
</li></ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
94
apps/cic-notify/doc/texinfo/html/index.html
Normal file
94
apps/cic-notify/doc/texinfo/html/index.html
Normal file
@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<!-- Released 2020 under GPL3 -->
|
||||
<!-- Created by GNU Texinfo 6.7, http://www.gnu.org/software/texinfo/ -->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Top (Grassroots Economics CIC notify 0.2.0)</title>
|
||||
|
||||
<meta name="description" content="Top (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="keywords" content="Top (Grassroots Economics CIC notify 0.2.0)">
|
||||
<meta name="resource-type" content="document">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="Generator" content="makeinfo">
|
||||
<link href="#Top" rel="start" title="Top">
|
||||
<link href="#SEC_Contents" rel="contents" title="Table of Contents">
|
||||
<link href="Overview.html" rel="next" title="Overview">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
a.summary-letter {text-decoration: none}
|
||||
blockquote.indentedblock {margin-right: 0em}
|
||||
div.display {margin-left: 3.2em}
|
||||
div.example {margin-left: 3.2em}
|
||||
div.lisp {margin-left: 3.2em}
|
||||
kbd {font-style: oblique}
|
||||
pre.display {font-family: inherit}
|
||||
pre.format {font-family: inherit}
|
||||
pre.menu-comment {font-family: serif}
|
||||
pre.menu-preformatted {font-family: serif}
|
||||
span.nolinebreak {white-space: nowrap}
|
||||
span.roman {font-family: initial; font-weight: normal}
|
||||
span.sansserif {font-family: sans-serif; font-weight: normal}
|
||||
ul.no-bullet {list-style: none}
|
||||
-->
|
||||
</style>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 class="settitle" align="center">Grassroots Economics CIC notify 0.2.0</h1>
|
||||
|
||||
|
||||
|
||||
<span id="SEC_Contents"></span>
|
||||
<h2 class="contents-heading">Table of Contents</h2>
|
||||
|
||||
<div class="contents">
|
||||
|
||||
<ul class="no-bullet">
|
||||
<li><a id="toc-Overview-1" href="Overview.html#Overview">1 Overview</a>
|
||||
<ul class="no-bullet">
|
||||
<li><a id="toc-Contents" href="Overview.html#Contents">1.1 Contents</a></li>
|
||||
</ul></li>
|
||||
<li><a id="toc-Setup-1" href="Setup.html#Setup">2 Setup</a>
|
||||
<ul class="no-bullet">
|
||||
<li><a id="toc-Handlers" href="Setup.html#Handlers">2.1 Handlers</a></li>
|
||||
</ul></li>
|
||||
<li><a id="toc-Running-the-Application" href="Run.html#Run">3 Running the Application</a></li>
|
||||
<li><a id="toc-API-1" href="API.html#API">4 API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<span id="Top"></span><div class="header">
|
||||
<p>
|
||||
[<a href="#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
|
||||
</div>
|
||||
<hr>
|
||||
<span id="Grassroots-Economics-CIC-ETH"></span><h1 class="top">Grassroots Economics CIC ETH</h1>
|
||||
|
||||
<p>This document describes microservices that broker external notifications from the CIC network
|
||||
</p>
|
||||
|
||||
<table class="menu" border="0" cellspacing="0">
|
||||
<tr><td align="left" valign="top">• <a href="Overview.html" accesskey="1">Overview</a></td><td> </td><td align="left" valign="top">
|
||||
</td></tr>
|
||||
<tr><td align="left" valign="top">• <a href="Setup.html" accesskey="2">Setup</a></td><td> </td><td align="left" valign="top">
|
||||
</td></tr>
|
||||
<tr><td align="left" valign="top">• <a href="Run.html" accesskey="3">Running the Application</a></td><td> </td><td align="left" valign="top">
|
||||
</td></tr>
|
||||
<tr><td align="left" valign="top">• <a href="API.html" accesskey="4">API</a></td><td> </td><td align="left" valign="top">
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
<div class="header">
|
||||
<p>
|
||||
[<a href="#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
28
apps/cic-notify/doc/texinfo/index.texi
Normal file
28
apps/cic-notify/doc/texinfo/index.texi
Normal file
@ -0,0 +1,28 @@
|
||||
\input texinfo
|
||||
@settitle Grassroots Economics CIC notify 0.2.0
|
||||
|
||||
@copying
|
||||
Released 2020 under GPL3
|
||||
@end copying
|
||||
|
||||
@titlepage
|
||||
@title Community Inclusion Currency Network
|
||||
@author Louis Holbrook
|
||||
|
||||
@end titlepage
|
||||
|
||||
@c
|
||||
@contents
|
||||
|
||||
@ifnottex
|
||||
@node Top
|
||||
@top Grassroots Economics CIC ETH
|
||||
|
||||
This document describes microservices that broker external notifications from the CIC network
|
||||
@end ifnottex
|
||||
|
||||
|
||||
@include overview.texi
|
||||
@include setup.texi
|
||||
@include run.texi
|
||||
@include api.texi
|
22
apps/cic-notify/doc/texinfo/overview.texi
Normal file
22
apps/cic-notify/doc/texinfo/overview.texi
Normal file
@ -0,0 +1,22 @@
|
||||
@node Overview
|
||||
@chapter Overview
|
||||
|
||||
This system is in early stages of development. It is intended to be a flexible notification broker where additional notification targets easily can be plugged in.
|
||||
|
||||
The framework is designed to asynchronously execute all tasks belonging to a specific context, based on the name of the task.
|
||||
|
||||
Currently, only handlers for @strong{sms} notifications are implemented. Any task with a @code{notify.sms.} prefix registered in the celery task worker pool will be executed upon the high-level "send sms" task.
|
||||
|
||||
Similary, any other notification category can be implemented. e.g. @code{notify.email.}, @code{notify.telegram.}, @code{notify.mattermost.} etc.
|
||||
|
||||
@section Contents
|
||||
|
||||
The only implementations so far are three @code{sms} notification tasks:
|
||||
|
||||
@itemize
|
||||
@item log (as in python logger, thus more or less noop)
|
||||
@item db (postgres)
|
||||
@item Africas Talking API
|
||||
@end itemize
|
||||
|
||||
@strong{NOTE}: The Africas Talking API will be removed from the suite, and provided as an add-on package down the road. It will illustrate how to include arbitrary tasks to a asynchronous group of notification targets.
|
4
apps/cic-notify/doc/texinfo/run.texi
Normal file
4
apps/cic-notify/doc/texinfo/run.texi
Normal file
@ -0,0 +1,4 @@
|
||||
@node Run
|
||||
@chapter Running the Application
|
||||
|
||||
A convenience task runner is provided in @file{scripts/cic-notify-tasker.py}.
|
17
apps/cic-notify/doc/texinfo/setup.texi
Normal file
17
apps/cic-notify/doc/texinfo/setup.texi
Normal file
@ -0,0 +1,17 @@
|
||||
@node Setup
|
||||
@chapter Setup
|
||||
|
||||
|
||||
@section Handlers
|
||||
|
||||
Notification tasks in this package are intended to be loaded dynamically, making no assumptions on which handlers that should be connected to the different tasks. The tasks provided are:
|
||||
|
||||
@itemize
|
||||
@item
|
||||
@strong{notify.sms.log}: (none)
|
||||
@item
|
||||
@strong{notify.sms.db}: Postgresql, along with python packages alembic, SQLAlchemy and psycopg2.
|
||||
@item
|
||||
@strong{notify.sms.africastalking}: africastalking python package.
|
||||
@end itemize
|
||||
|
39
apps/cic-notify/docker/Dockerfile
Normal file
39
apps/cic-notify/docker/Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
FROM python:3.8.6
|
||||
|
||||
RUN apt-get update && \
|
||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps
|
||||
|
||||
WORKDIR /usr/src/cic-notify
|
||||
|
||||
ARG pip_extra_index_url_flag='--index https://pypi.org/simple --extra-index-url https://pip.grassrootseconomics.net:8433'
|
||||
ARG root_requirement_file='requirements.txt'
|
||||
RUN echo "copying root req file ${root_requirement_file}"
|
||||
COPY $root_requirement_file .
|
||||
RUN pip install -r $root_requirement_file $pip_extra_index_url_flag
|
||||
|
||||
COPY cic-notify/setup.cfg \
|
||||
cic-notify/setup.py \
|
||||
./
|
||||
COPY cic-notify/cic_notify/ ./cic_notify/
|
||||
COPY cic-notify/requirements.txt \
|
||||
cic-notify/test_requirements.txt \
|
||||
./
|
||||
|
||||
COPY cic-notify/scripts/ scripts/
|
||||
RUN pip install $pip_extra_index_url_flag .[africastalking,notifylog]
|
||||
|
||||
COPY cic-notify/tests/ tests/
|
||||
COPY cic-notify/docker/db.sh \
|
||||
cic-notify/docker/start_tasker.sh \
|
||||
/root/
|
||||
|
||||
#RUN apk add postgresql-client
|
||||
#RUN apk add bash
|
||||
|
||||
# ini files in config directory defines the configurable parameters for the application
|
||||
# they can all be overridden by environment variables
|
||||
# to generate a list of environment variables from configuration, use: confini-dump -z <dir> (executable provided by confini package)
|
||||
COPY cic-notify/.config/ /usr/local/etc/cic-notify/
|
||||
COPY cic-notify/cic_notify/db/migrations/ /usr/local/share/cic-notify/alembic/
|
||||
|
||||
WORKDIR /root
|
13
apps/cic-notify/docker/Dockerfile.service
Normal file
13
apps/cic-notify/docker/Dockerfile.service
Normal file
@ -0,0 +1,13 @@
|
||||
FROM grassrootseconomics:cic-notify
|
||||
|
||||
#FROM python:3.8.6-alpine
|
||||
|
||||
#RUN apk update && \
|
||||
# apk add gnupg libpq
|
||||
|
||||
#COPY --from=0 /usr/local/ /usr/local/
|
||||
#COPY --from=0 /root/ /root/
|
||||
|
||||
#RUN apk add bash
|
||||
|
||||
WORKDIR /root
|
3
apps/cic-notify/docker/db.sh
Normal file
3
apps/cic-notify/docker/db.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
migrate.py -c /usr/local/etc/cic-notify --migrations-dir /usr/local/share/cic-notify/alembic -vv
|
5
apps/cic-notify/docker/start_tasker.sh
Executable file
5
apps/cic-notify/docker/start_tasker.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./db.sh
|
||||
|
||||
/usr/local/bin/cic-notify-tasker -vv $@
|
5
apps/cic-notify/requirements.txt
Normal file
5
apps/cic-notify/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
celery~=4.4.7
|
||||
confini~=0.3.6a1
|
||||
alembic~=1.4.2
|
||||
redis~=3.5.3
|
||||
semver==2.13.0
|
57
apps/cic-notify/scripts/migrate.py
Normal file
57
apps/cic-notify/scripts/migrate.py
Normal file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
# standard imports
|
||||
import argparse
|
||||
import os
|
||||
import logging
|
||||
|
||||
# third party imports
|
||||
import alembic
|
||||
from alembic.config import Config as AlembicConfig
|
||||
import confini
|
||||
|
||||
from cic_notify.db import dsn_from_config
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
rootdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
dbdir = os.path.join(rootdir, 'cic_notify', 'db')
|
||||
migrationsdir = os.path.join(dbdir, 'migrations')
|
||||
|
||||
config_dir = os.path.join('/usr/local/etc/cic-notify')
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-c', type=str, default=config_dir, help='config file')
|
||||
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
|
||||
argparser.add_argument('--migrations-dir', dest='migrations_dir', default=migrationsdir, type=str, help='path to alembic migrations directory')
|
||||
argparser.add_argument('-v', action='store_true', help='be verbose')
|
||||
argparser.add_argument('-vv', action='store_true', help='be more verbose')
|
||||
args = argparser.parse_args()
|
||||
|
||||
if args.vv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
config = confini.Config(args.c, args.env_prefix)
|
||||
config.process()
|
||||
config.censor('PASSWORD', 'DATABASE')
|
||||
#config.censor('PASSWORD', 'SSL')
|
||||
logg.debug('config:\n{}'.format(config))
|
||||
|
||||
migrations_dir = os.path.join(args.migrations_dir, config.get('DATABASE_ENGINE'))
|
||||
if not os.path.isdir(migrations_dir):
|
||||
logg.debug('migrations dir for engine {} not found, reverting to default'.format(config.get('DATABASE_ENGINE')))
|
||||
migrations_dir = os.path.join(args.migrations_dir, 'default')
|
||||
|
||||
# connect to database
|
||||
dsn = dsn_from_config(config)
|
||||
|
||||
|
||||
logg.info('using migrations dir {}'.format(migrations_dir))
|
||||
logg.info('using db {}'.format(dsn))
|
||||
ac = AlembicConfig(os.path.join(migrations_dir, 'alembic.ini'))
|
||||
ac.set_main_option('sqlalchemy.url', dsn)
|
||||
ac.set_main_option('script_location', migrations_dir)
|
||||
|
||||
alembic.command.upgrade(ac, 'head')
|
12
apps/cic-notify/scripts/send_sms.py
Normal file
12
apps/cic-notify/scripts/send_sms.py
Normal file
@ -0,0 +1,12 @@
|
||||
# local imports
|
||||
import cic_notify
|
||||
|
||||
# third-part imports
|
||||
import celery
|
||||
|
||||
|
||||
# TODO: Add configurable backend
|
||||
celery_app = celery.Celery(broker='redis:///', backend='redis:///')
|
||||
|
||||
api = cic_notify.Api('cic-notify')
|
||||
print(api.sms('+25412121212', 'foo'))
|
47
apps/cic-notify/setup.cfg
Normal file
47
apps/cic-notify/setup.cfg
Normal file
@ -0,0 +1,47 @@
|
||||
[metadata]
|
||||
name = cic-notify
|
||||
version= attr: cic_notify.version.__version_string__
|
||||
description = CIC notifications service
|
||||
author = Louis Holbrook
|
||||
author_email = dev@holbrook.no
|
||||
url = https://gitlab.com/grassrootseconomics/cic-eth
|
||||
keywords =
|
||||
cic
|
||||
cryptocurrency
|
||||
ethereum
|
||||
sms
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
Operating System :: OS Independent
|
||||
Development Status :: 3 - Alpha
|
||||
Environment :: No Input/Output (Daemon)
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Topic :: Internet
|
||||
Topic :: Blockchain :: EVM
|
||||
license = GPL3
|
||||
licence_files =
|
||||
LICENSE.txt
|
||||
|
||||
[options]
|
||||
python_requires = >= 3.6
|
||||
packages =
|
||||
cic_notify
|
||||
cic_notify.db
|
||||
cic_notify.db.models
|
||||
cic_notify.tasks.sms
|
||||
cic_notify.runnable
|
||||
scripts =
|
||||
scripts/migrate.py
|
||||
[options.extras_require]
|
||||
africastalking = africastalking==1.2.3
|
||||
notifylog = psycopg2==2.8.6
|
||||
testing =
|
||||
pytest==6.0.1
|
||||
pytest-celery==0.0.0a1
|
||||
pytest-mock==3.3.1
|
||||
pysqlite3==0.4.3
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
cic-notify-tasker = cic_notify.runnable.tasker:main
|
30
apps/cic-notify/setup.py
Normal file
30
apps/cic-notify/setup.py
Normal file
@ -0,0 +1,30 @@
|
||||
# standard imports
|
||||
from setuptools import setup
|
||||
|
||||
# third-party imports
|
||||
|
||||
# local imports
|
||||
|
||||
|
||||
requirements = []
|
||||
requirements_file = open('requirements.txt', 'r')
|
||||
while True:
|
||||
requirement = requirements_file.readline()
|
||||
if requirement == '':
|
||||
break
|
||||
requirements.append(requirement.rstrip())
|
||||
requirements_file.close()
|
||||
|
||||
test_requirements = []
|
||||
test_requirements_file = open('test_requirements.txt', 'r')
|
||||
while True:
|
||||
test_requirement = test_requirements_file.readline()
|
||||
if test_requirement == '':
|
||||
break
|
||||
test_requirements.append(test_requirement.rstrip())
|
||||
test_requirements_file.close()
|
||||
|
||||
setup(
|
||||
install_requires=requirements,
|
||||
tests_require=test_requirements,
|
||||
)
|
5
apps/cic-notify/test_requirements.txt
Normal file
5
apps/cic-notify/test_requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
pytest~=6.0.1
|
||||
pytest-celery~=0.0.0a1
|
||||
pytest-mock~=3.3.1
|
||||
pysqlite3~=0.4.3
|
||||
|
31
apps/cic-notify/tests/conftest.py
Normal file
31
apps/cic-notify/tests/conftest.py
Normal file
@ -0,0 +1,31 @@
|
||||
# standard imports
|
||||
import sys
|
||||
import os
|
||||
import pytest
|
||||
import logging
|
||||
|
||||
# third party imports
|
||||
import confini
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
root_dir = os.path.dirname(script_dir)
|
||||
sys.path.insert(0, root_dir)
|
||||
|
||||
# local imports
|
||||
from cic_notify.db.models.base import SessionBase
|
||||
#from transport.notification import AfricastalkingNotification
|
||||
|
||||
# fixtures
|
||||
from tests.fixtures_config import *
|
||||
from tests.fixtures_celery import *
|
||||
from tests.fixtures_database import *
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
#@pytest.fixture(scope='session')
|
||||
#def africastalking_notification(
|
||||
# load_config,
|
||||
# ):
|
||||
# return AfricastalkingNotificationTransport(load_config)
|
||||
#
|
48
apps/cic-notify/tests/fixtures_celery.py
Normal file
48
apps/cic-notify/tests/fixtures_celery.py
Normal file
@ -0,0 +1,48 @@
|
||||
# third-party imports
|
||||
import pytest
|
||||
import tempfile
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
# celery fixtures
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_includes():
|
||||
return [
|
||||
'cic_notify.tasks.sms',
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_config():
|
||||
bq = tempfile.mkdtemp()
|
||||
bp = tempfile.mkdtemp()
|
||||
rq = tempfile.mkdtemp()
|
||||
logg.debug('celery broker queue {} processed {}'.format(bq, bp))
|
||||
logg.debug('celery backend store {}'.format(rq))
|
||||
yield {
|
||||
'broker_url': 'filesystem://',
|
||||
'broker_transport_options': {
|
||||
'data_folder_in': bq,
|
||||
'data_folder_out': bq,
|
||||
'data_folder_processed': bp,
|
||||
},
|
||||
'result_backend': 'file://{}'.format(rq),
|
||||
}
|
||||
logg.debug('cleaning up celery filesystem backend files {} {} {}'.format(bq, bp, rq))
|
||||
shutil.rmtree(bq)
|
||||
shutil.rmtree(bp)
|
||||
shutil.rmtree(rq)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_worker_parameters():
|
||||
return {
|
||||
# 'queues': ('cic-notify'),
|
||||
}
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def celery_enable_logging():
|
||||
return True
|
20
apps/cic-notify/tests/fixtures_config.py
Normal file
20
apps/cic-notify/tests/fixtures_config.py
Normal file
@ -0,0 +1,20 @@
|
||||
# standard imports
|
||||
import os
|
||||
import logging
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
import confini
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
root_dir = os.path.dirname(script_dir)
|
||||
logg = logging.getLogger(__file__)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def load_config():
|
||||
config_dir = os.path.join(root_dir, '.config/test')
|
||||
conf = confini.Config(config_dir, 'CICTEST')
|
||||
conf.process()
|
||||
logg.debug('config {}'.format(conf))
|
||||
return conf
|
48
apps/cic-notify/tests/fixtures_database.py
Normal file
48
apps/cic-notify/tests/fixtures_database.py
Normal file
@ -0,0 +1,48 @@
|
||||
# standard imports
|
||||
import os
|
||||
|
||||
# third-party imports
|
||||
import pytest
|
||||
import alembic
|
||||
from alembic.config import Config as AlembicConfig
|
||||
|
||||
# local imports
|
||||
from cic_notify.db import SessionBase
|
||||
from cic_notify.db import dsn_from_config
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def database_engine(
|
||||
load_config,
|
||||
):
|
||||
dsn = dsn_from_config(load_config)
|
||||
SessionBase.connect(dsn)
|
||||
return dsn
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def init_database(
|
||||
load_config,
|
||||
database_engine,
|
||||
):
|
||||
|
||||
rootdir = os.path.dirname(os.path.dirname(__file__))
|
||||
dbdir = os.path.join(rootdir, 'cic_notify', 'db')
|
||||
migrationsdir = os.path.join(dbdir, 'migrations', load_config.get('DATABASE_ENGINE'))
|
||||
if not os.path.isdir(migrationsdir):
|
||||
migrationsdir = os.path.join(dbdir, 'migrations', 'default')
|
||||
|
||||
session = SessionBase.create_session()
|
||||
|
||||
ac = AlembicConfig(os.path.join(migrationsdir, 'alembic.ini'))
|
||||
ac.set_main_option('sqlalchemy.url', database_engine)
|
||||
ac.set_main_option('script_location', migrationsdir)
|
||||
|
||||
alembic.command.downgrade(ac, 'base')
|
||||
alembic.command.upgrade(ac, 'head')
|
||||
|
||||
yield session
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
|
34
apps/cic-notify/tests/test_sms.py
Normal file
34
apps/cic-notify/tests/test_sms.py
Normal file
@ -0,0 +1,34 @@
|
||||
# standard imports
|
||||
import json
|
||||
|
||||
# third party imports
|
||||
import pytest
|
||||
import celery
|
||||
|
||||
# local imports
|
||||
from cic_notify.tasks.sms import db
|
||||
from cic_notify.tasks.sms import log
|
||||
|
||||
def test_log_notification(
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
recipient = '+25412121212'
|
||||
content = 'bar'
|
||||
s_log = celery.signature('cic_notify.tasks.sms.log.log')
|
||||
t = s_log.apply_async(args=[recipient, content])
|
||||
|
||||
r = t.get()
|
||||
|
||||
|
||||
def test_db_notification(
|
||||
init_database,
|
||||
celery_session_worker,
|
||||
):
|
||||
|
||||
recipient = '+25412121213'
|
||||
content = 'foo'
|
||||
s_db = celery.signature('cic_notify.tasks.sms.db.persist_notification')
|
||||
t = s_db.apply_async(args=[recipient, content])
|
||||
|
||||
r = t.get()
|
@ -404,29 +404,29 @@ services:
|
||||
|
||||
|
||||
|
||||
# cic-notify-tasker:
|
||||
# image: grassrootseconomics:cic-notify-service
|
||||
# environment:
|
||||
# DATABASE_USER: $DATABASE_USER
|
||||
# DATABASE_HOST: $DATABASE_HOST
|
||||
# DATABASE_PORT: $DATABASE_PORT
|
||||
# DATABASE_PASSWORD: $DATABASE_PASSWORD
|
||||
# DATABASE_NAME: $DATABASE_NAME_CIC_NOTIFY
|
||||
# DATABASE_ENGINE: $DATABASE_ENGINE
|
||||
# DATABASE_DRIVER: $DATABASE_DRIVER
|
||||
# PGPASSWORD: $DATABASE_PASSWORD
|
||||
# CELERY_BROKER_URL: $CELERY_BROKER_URL
|
||||
# CELERY_RESULT_URL: $CELERY_RESULT_URL
|
||||
# TASKS_AFRICASTALKING: $TASKS_AFRICASTALKING
|
||||
# TASKS_SMS_DB: $TASKS_SMS_DB
|
||||
# TASKS_LOG: $TASKS_LOG
|
||||
# depends_on:
|
||||
# - postgres
|
||||
# - redis
|
||||
# deploy:
|
||||
# restart_policy:
|
||||
# condition: on-failure
|
||||
# command: "/root/start_tasker.sh -q cic-notify"
|
||||
cic-notify-tasker:
|
||||
image: grassrootseconomics:cic-notify-service
|
||||
environment:
|
||||
DATABASE_USER: ${DATABASE_USER:-grassroots}
|
||||
DATABASE_HOST: ${DATABASE_HOST:-postgres}
|
||||
DATABASE_PORT: ${DATABASE_PORT:-5432}
|
||||
DATABASE_PASSWORD: ${DATABASE_PASSWORD:-tralala}
|
||||
DATABASE_NAME: ${DATABASE_NAME_CIC_NOTIFY:-cic_notify}
|
||||
DATABASE_ENGINE: ${DATABASE_ENGINE:-postgres}
|
||||
DATABASE_DRIVER: ${DATABASE_DRIVER:-psycopg2}
|
||||
PGPASSWORD: ${DATABASE_PASSWORD:-tralala}
|
||||
CELERY_BROKER_URL: ${CELERY_BROKER_URL:-redis://redis}
|
||||
CELERY_RESULT_URL: ${CELERY_BROKER_URL:-redis://redis}
|
||||
TASKS_AFRICASTALKING: $TASKS_AFRICASTALKING
|
||||
TASKS_SMS_DB: $TASKS_SMS_DB
|
||||
TASKS_LOG: $TASKS_LOG
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command: "/root/start_tasker.sh -q cic-notify"
|
||||
|
||||
|
||||
# cic-meta-server:
|
||||
|
Loading…
Reference in New Issue
Block a user