Merge branch 'cic-notify-build' into 'master'

Cic notify build

See merge request grassrootseconomics/cic-internal-integration!10
This commit is contained in:
Blair Vanderlugt 2021-02-07 02:29:24 +00:00
commit 5bfc3e5796
61 changed files with 1650 additions and 27 deletions

View File

@ -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
View File

@ -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

View File

@ -0,0 +1,4 @@
[AFRICASTALKING]
api_username = foo
api_key = bar
api_sender_id = baz

View File

@ -0,0 +1,3 @@
[celery]
broker_url = redis://
result_url = redis://

View 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

View File

@ -0,0 +1,4 @@
[TASKS]
africastalking = cic_notify.tasks.sms.africastalking
db = cic_notify.tasks.sms.db
log = cic_notify.tasks.sms.log

View File

@ -0,0 +1,4 @@
[AFRICASTALKING]
api_username = foo
api_key = bar
api_sender_id = baz

View File

@ -0,0 +1,3 @@
[celery]
broker_url = filesystem://
result_url = filesystem://

View 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

View 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
View File

@ -0,0 +1,4 @@
__pycache__
*.pyc
venv/
.idea/

View 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
View 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

View File

@ -0,0 +1 @@
from .api import *

View 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

View 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

View File

@ -0,0 +1,7 @@
import enum
class NotificationStatusEnum(enum.Enum):
UNKNOWN = 'UNKNOWN'
class NotificationTransportEnum(enum.Enum):
SMS = 'SMS'

View File

@ -0,0 +1 @@
Generic single-database configuration.

View 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

View 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()

View File

@ -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"}

View File

@ -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)

View 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)

View 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

View 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

View 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()

View File

@ -0,0 +1 @@
from . import sms

View 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

View 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)

View 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()

View 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)

View 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

View File

@ -0,0 +1,5 @@
all: html
.PHONY: html
html:
makeinfo --html --output html index.texi

View 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.

View 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> &nbsp; [<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>

View 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> &nbsp; [<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 &quot;send sms&quot; 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>

View 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> &nbsp; [<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>

View 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> &nbsp; [<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>

View 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>
&nbsp; [<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">&bull; <a href="Overview.html" accesskey="1">Overview</a></td><td>&nbsp;&nbsp;</td><td align="left" valign="top">
</td></tr>
<tr><td align="left" valign="top">&bull; <a href="Setup.html" accesskey="2">Setup</a></td><td>&nbsp;&nbsp;</td><td align="left" valign="top">
</td></tr>
<tr><td align="left" valign="top">&bull; <a href="Run.html" accesskey="3">Running the Application</a></td><td>&nbsp;&nbsp;</td><td align="left" valign="top">
</td></tr>
<tr><td align="left" valign="top">&bull; <a href="API.html" accesskey="4">API</a></td><td>&nbsp;&nbsp;</td><td align="left" valign="top">
</td></tr>
</table>
<hr>
<div class="header">
<p>
&nbsp; [<a href="#SEC_Contents" title="Table of contents" rel="contents">Contents</a>]</p>
</div>
</body>
</html>

View 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

View 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.

View File

@ -0,0 +1,4 @@
@node Run
@chapter Running the Application
A convenience task runner is provided in @file{scripts/cic-notify-tasker.py}.

View 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

View 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

View 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

View File

@ -0,0 +1,3 @@
#!/bin/bash
migrate.py -c /usr/local/etc/cic-notify --migrations-dir /usr/local/share/cic-notify/alembic -vv

View File

@ -0,0 +1,5 @@
#!/bin/bash
. ./db.sh
/usr/local/bin/cic-notify-tasker -vv $@

View 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

View 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')

View 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
View 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
View 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,
)

View File

@ -0,0 +1,5 @@
pytest~=6.0.1
pytest-celery~=0.0.0a1
pytest-mock~=3.3.1
pysqlite3~=0.4.3

View 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)
#

View 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

View 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

View 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()

View 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()

View File

@ -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: