The great bump

This commit is contained in:
2021-08-06 16:29:01 +00:00
parent f764b73f66
commit 0672a17d2e
195 changed files with 5791 additions and 4983 deletions

View File

@@ -1,14 +1,13 @@
# standard import
# third-party imports
# this must be included for the package to be recognized as a tasks package
import celery
celery_app = celery.current_app
# export external celery task modules
from .logger import *
from .ussd_session import *
from .callback_handler import *
from .metadata import *
from .notifications import *
from .processor import *
from .ussd_session import *
celery_app = celery.current_app

View File

@@ -1,4 +1,5 @@
# standard imports
import logging
# third-party imports
import celery
@@ -8,6 +9,8 @@ import sqlalchemy
from cic_ussd.error import MetadataStoreError
from cic_ussd.db.models.base import SessionBase
logg = logging.getLogger(__name__)
class BaseTask(celery.Task):

View File

@@ -1,19 +1,22 @@
# standard imports
import json
import logging
from datetime import datetime, timedelta
from datetime import timedelta
# third-party imports
import celery
from chainlib.hash import strip_0x
# local imports
from cic_ussd.balance import compute_operational_balance, get_balances
from cic_ussd.chain import Chain
from cic_ussd.conversions import from_wei
from cic_ussd.account.balance import get_balances, calculate_available_balance
from cic_ussd.account.statement import generate
from cic_ussd.cache import Cache, cache_data, cache_data_key, get_cached_data
from cic_ussd.account.chain import Chain
from cic_ussd.db.models.base import SessionBase
from cic_ussd.db.models.account import Account
from cic_ussd.error import ActionDataNotFoundError
from cic_ussd.redis import InMemoryStore, cache_data, create_cached_data_key, get_cached_data
from cic_ussd.account.statement import filter_statement_transactions
from cic_ussd.account.transaction import transaction_actors
from cic_ussd.error import AccountCreationDataNotFound
from cic_ussd.tasks.base import CriticalSQLAlchemyTask
logg = logging.getLogger(__file__)
@@ -21,8 +24,10 @@ celery_app = celery.current_app
@celery_app.task(bind=True, base=CriticalSQLAlchemyTask)
def process_account_creation_callback(self, result: str, url: str, status_code: int):
def account_creation_callback(self, result: str, url: str, status_code: int):
"""This function defines a task that creates a user and
:param self: Reference providing access to the callback task instance.
:type self: celery.Task
:param result: The blockchain address for the created account
:type result: str
:param url: URL provided to callback task in cic-eth should http be used for callback.
@@ -30,266 +35,183 @@ def process_account_creation_callback(self, result: str, url: str, status_code:
:param status_code: The status of the task to create an account
:type status_code: int
"""
task_uuid = self.request.root_id
cached_account_creation_data = get_cached_data(task_uuid)
if not cached_account_creation_data:
raise AccountCreationDataNotFound(f'No account creation data found for task id: {task_uuid}')
if status_code != 0:
raise ValueError(f'Unexpected status code: {status_code}')
account_creation_data = json.loads(cached_account_creation_data)
account_creation_data['status'] = 'CREATED'
cache_data(task_uuid, json.dumps(account_creation_data))
phone_number = account_creation_data.get('phone_number')
session = SessionBase.create_session()
cache = InMemoryStore.cache
task_id = self.request.root_id
# get account creation status
account_creation_data = cache.get(task_id)
# check status
if account_creation_data:
account_creation_data = json.loads(account_creation_data)
if status_code == 0:
# update redis data
account_creation_data['status'] = 'CREATED'
cache.set(name=task_id, value=json.dumps(account_creation_data))
cache.persist(task_id)
phone_number = account_creation_data.get('phone_number')
# create user
user = Account(blockchain_address=result, phone_number=phone_number)
session.add(user)
session.commit()
session.close()
queue = self.request.delivery_info.get('routing_key')
# add phone number metadata lookup
s_phone_pointer = celery.signature(
'cic_ussd.tasks.metadata.add_phone_pointer',
[result, phone_number]
)
s_phone_pointer.apply_async(queue=queue)
# add custom metadata tags
custom_metadata = {
"tags": ["ussd", "individual"]
}
s_custom_metadata = celery.signature(
'cic_ussd.tasks.metadata.add_custom_metadata',
[result, custom_metadata]
)
s_custom_metadata.apply_async(queue=queue)
# expire cache
cache.expire(task_id, timedelta(seconds=180))
else:
session.close()
cache.expire(task_id, timedelta(seconds=180))
else:
session.close()
raise ActionDataNotFoundError(f'Account creation task: {task_id}, returned unexpected response: {status_code}')
account = Account(blockchain_address=result, phone_number=phone_number)
session.add(account)
session.commit()
session.close()
queue = self.request.delivery_info.get('routing_key')
s_phone_pointer = celery.signature(
'cic_ussd.tasks.metadata.add_phone_pointer', [result, phone_number], queue=queue
)
s_phone_pointer.apply_async()
custom_metadata = {"tags": ["ussd", "individual"]}
s_custom_metadata = celery.signature(
'cic_ussd.tasks.metadata.add_custom_metadata', [result, custom_metadata], queue=queue
)
s_custom_metadata.apply_async()
Cache.store.expire(task_uuid, timedelta(seconds=180))
@celery_app.task
def balances_callback(result: list, param: str, status_code: int):
"""
:param result:
:type result:
:param param:
:type param:
:param status_code:
:type status_code:
:return:
:rtype:
"""
if status_code != 0:
raise ValueError(f'Unexpected status code: {status_code}.')
balances = result[0]
identifier = bytes.fromhex(strip_0x(param))
key = cache_data_key(identifier, ':cic.balances')
cache_data(key, json.dumps(balances))
@celery_app.task(bind=True)
def process_transaction_callback(self, result: dict, param: str, status_code: int):
if status_code == 0:
chain_str = Chain.spec.__str__()
def statement_callback(self, result, param: str, status_code: int):
"""
:param self:
:type self:
:param result:
:type result:
:param param:
:type param:
:param status_code:
:type status_code:
:return:
:rtype:
"""
if status_code != 0:
raise ValueError(f'Unexpected status code: {status_code}.')
# collect transaction metadata
destination_token_symbol = result.get('destination_token_symbol')
destination_token_value = result.get('destination_token_value')
recipient_blockchain_address = result.get('recipient')
sender_blockchain_address = result.get('sender')
source_token_symbol = result.get('source_token_symbol')
source_token_value = result.get('source_token_value')
queue = self.request.delivery_info.get('routing_key')
statement_transactions = filter_statement_transactions(result)
for transaction in statement_transactions:
recipient_transaction, sender_transaction = transaction_actors(transaction)
if recipient_transaction.get('blockchain_address') == param:
generate(param, queue, recipient_transaction)
if sender_transaction.get('blockchain_address') == param:
generate(param, queue, sender_transaction)
# build stakeholder callback params
recipient_metadata = {
"token_symbol": destination_token_symbol,
"token_value": destination_token_value,
"blockchain_address": recipient_blockchain_address,
"tag": "recipient",
"tx_param": param
@celery_app.task(bind=True)
def transaction_balances_callback(self, result: list, param: dict, status_code: int):
"""
:param self:
:type self:
:param result:
:type result:
:param param:
:type param:
:param status_code:
:type status_code:
:return:
:rtype:
"""
if status_code != 0:
raise ValueError(f'Unexpected status code: {status_code}.')
balances_data = result[0]
available_balance = calculate_available_balance(balances_data)
transaction = param
blockchain_address = param.get('blockchain_address')
transaction['available_balance'] = available_balance
queue = self.request.delivery_info.get('routing_key')
s_preferences_metadata = celery.signature(
'cic_ussd.tasks.metadata.query_preferences_metadata', [blockchain_address], queue=queue
)
s_process_account_metadata = celery.signature(
'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue
)
s_notify_account = celery.signature('cic_ussd.tasks.notifications.transaction', queue=queue)
if param.get('transaction_type') == 'transfer':
celery.chain(s_preferences_metadata, s_process_account_metadata, s_notify_account).apply_async()
if param.get('transaction_type') == 'tokengift':
s_process_account_metadata = celery.signature(
'cic_ussd.tasks.processor.parse_transaction', [{}, transaction], queue=queue
)
celery.chain(s_process_account_metadata, s_notify_account).apply_async()
@celery_app.task
def transaction_callback(result: dict, param: str, status_code: int):
"""
:param result:
:type result:
:param param:
:type param:
:param status_code:
:type status_code:
:return:
:rtype:
"""
if status_code != 0:
raise ValueError(f'Unexpected status code: {status_code}.')
chain_str = Chain.spec.__str__()
destination_token_symbol = result.get('destination_token_symbol')
destination_token_value = result.get('destination_token_value')
recipient_blockchain_address = result.get('recipient')
sender_blockchain_address = result.get('sender')
source_token_symbol = result.get('source_token_symbol')
source_token_value = result.get('source_token_value')
recipient_metadata = {
"token_symbol": destination_token_symbol,
"token_value": destination_token_value,
"blockchain_address": recipient_blockchain_address,
"role": "recipient",
"transaction_type": param
}
get_balances(
address=recipient_blockchain_address,
callback_param=recipient_metadata,
chain_str=chain_str,
callback_task='cic_ussd.tasks.callback_handler.transaction_balances_callback',
token_symbol=destination_token_symbol,
asynchronous=True)
if param == 'transfer':
sender_metadata = {
"blockchain_address": sender_blockchain_address,
"token_symbol": source_token_symbol,
"token_value": source_token_value,
"role": "sender",
"transaction_type": param
}
# retrieve account balances
get_balances(
address=recipient_blockchain_address,
callback_param=recipient_metadata,
address=sender_blockchain_address,
callback_param=sender_metadata,
chain_str=chain_str,
callback_task='cic_ussd.tasks.callback_handler.process_transaction_balances_callback',
token_symbol=destination_token_symbol,
callback_task='cic_ussd.tasks.callback_handler.transaction_balances_callback',
token_symbol=source_token_symbol,
asynchronous=True)
# only retrieve sender if transaction is a transfer
if param == 'transfer':
sender_metadata = {
"blockchain_address": sender_blockchain_address,
"token_symbol": source_token_symbol,
"token_value": source_token_value,
"tag": "sender",
"tx_param": param
}
get_balances(
address=sender_blockchain_address,
callback_param=sender_metadata,
chain_str=chain_str,
callback_task='cic_ussd.tasks.callback_handler.process_transaction_balances_callback',
token_symbol=source_token_symbol,
asynchronous=True)
else:
raise ValueError(f'Unexpected status code: {status_code}.')
@celery_app.task(bind=True)
def process_transaction_balances_callback(self, result: list, param: dict, status_code: int):
queue = self.request.delivery_info.get('routing_key')
if status_code == 0:
# retrieve balance data
balances_data = result[0]
operational_balance = compute_operational_balance(balances=balances_data)
# retrieve account's address
blockchain_address = param.get('blockchain_address')
# append balance to transaction metadata
transaction_metadata = param
transaction_metadata['operational_balance'] = operational_balance
# retrieve account's preferences
s_preferences_metadata = celery.signature(
'cic_ussd.tasks.metadata.query_preferences_metadata',
[blockchain_address],
queue=queue
)
# parse metadata and run validations
s_process_account_metadata = celery.signature(
'cic_ussd.tasks.processor.process_tx_metadata_for_notification',
[transaction_metadata],
queue=queue
)
# issue notification of transaction
s_notify_account = celery.signature(
'cic_ussd.tasks.notifications.notify_account_of_transaction',
queue=queue
)
if param.get('tx_param') == 'transfer':
celery.chain(s_preferences_metadata, s_process_account_metadata, s_notify_account).apply_async()
if param.get('tx_param') == 'tokengift':
s_process_account_metadata = celery.signature(
'cic_ussd.tasks.processor.process_tx_metadata_for_notification',
[{}, transaction_metadata],
queue=queue
)
celery.chain(s_process_account_metadata, s_notify_account).apply_async()
else:
raise ValueError(f'Unexpected status code: {status_code}.')
@celery_app.task
def process_balances_callback(result: list, param: str, status_code: int):
if status_code == 0:
balances_data = result[0]
blockchain_address = balances_data.get('address')
key = create_cached_data_key(
identifier=bytes.fromhex(blockchain_address[2:]),
salt=':cic.balances_data'
)
cache_data(key=key, data=json.dumps(balances_data))
logg.debug(f'caching: {balances_data} with key: {key}')
else:
raise ValueError(f'Unexpected status code: {status_code}.')
# TODO: clean up this handler
def define_transaction_action_tag(
preferred_language: str,
sender_blockchain_address: str,
param: str):
# check if out going ot incoming transaction
if sender_blockchain_address == param:
# check preferred language
if preferred_language == 'en':
action_tag = 'SENT'
direction = 'TO'
else:
action_tag = 'ULITUMA'
direction = 'KWA'
else:
if preferred_language == 'en':
action_tag = 'RECEIVED'
direction = 'FROM'
else:
action_tag = 'ULIPOKEA'
direction = 'KUTOKA'
return action_tag, direction
@celery_app.task
def process_statement_callback(result, param: str, status_code: int):
if status_code == 0:
# create session
processed_transactions = []
# process transaction data to cache
for transaction in result:
sender_blockchain_address = transaction.get('sender')
recipient_address = transaction.get('recipient')
source_token = transaction.get('source_token')
# filter out any transactions that are "gassy"
if '0x0000000000000000000000000000000000000000' in source_token:
pass
else:
session = SessionBase.create_session()
# describe a processed transaction
processed_transaction = {}
# check if sender is in the system
sender: Account = session.query(Account).filter_by(blockchain_address=sender_blockchain_address).first()
owner: Account = session.query(Account).filter_by(blockchain_address=param).first()
if sender:
processed_transaction['sender_phone_number'] = sender.phone_number
action_tag, direction = define_transaction_action_tag(
preferred_language=owner.preferred_language,
sender_blockchain_address=sender_blockchain_address,
param=param
)
processed_transaction['action_tag'] = action_tag
processed_transaction['direction'] = direction
else:
processed_transaction['sender_phone_number'] = 'GRASSROOTS ECONOMICS'
# check if recipient is in the system
recipient: Account = session.query(Account).filter_by(blockchain_address=recipient_address).first()
if recipient:
processed_transaction['recipient_phone_number'] = recipient.phone_number
else:
logg.warning(f'Tx with recipient not found in cic-ussd')
session.close()
# add transaction values
processed_transaction['to_value'] = from_wei(value=transaction.get('to_value')).__str__()
processed_transaction['from_value'] = from_wei(value=transaction.get('from_value')).__str__()
raw_timestamp = transaction.get('timestamp')
timestamp = datetime.utcfromtimestamp(raw_timestamp).strftime('%d/%m/%y, %H:%M')
processed_transaction['timestamp'] = timestamp
processed_transactions.append(processed_transaction)
# cache account statement
identifier = bytes.fromhex(param[2:])
key = create_cached_data_key(identifier=identifier, salt=':cic.statement')
data = json.dumps(processed_transactions)
# cache statement data
cache_data(key=key, data=data)
else:
raise ValueError(f'Unexpected status code: {status_code}.')

View File

@@ -1,11 +0,0 @@
# third-party imports
import celery
import logging
celery_app = celery.current_app
logg = logging.getLogger()
@celery_app.task()
def log_it_plz(whatever):
logg.info('logged it plz: {}'.format(whatever))

View File

@@ -6,11 +6,7 @@ import celery
from hexathon import strip_0x
# local imports
from cic_ussd.metadata import blockchain_address_to_metadata_pointer
from cic_ussd.metadata.custom import CustomMetadata
from cic_ussd.metadata.person import PersonMetadata
from cic_ussd.metadata.phone import PhonePointerMetadata
from cic_ussd.metadata.preferences import PreferencesMetadata
from cic_ussd.metadata import CustomMetadata, PersonMetadata, PhonePointerMetadata, PreferencesMetadata
from cic_ussd.tasks.base import CriticalMetadataTask
celery_app = celery.current_app
@@ -25,8 +21,7 @@ def query_person_metadata(blockchain_address: str):
:return:
:rtype:
"""
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
logg.debug(f'Retrieving person metadata for address: {blockchain_address}.')
identifier = bytes.fromhex(strip_0x(blockchain_address))
person_metadata_client = PersonMetadata(identifier=identifier)
person_metadata_client.query()
@@ -41,14 +36,14 @@ def create_person_metadata(blockchain_address: str, data: dict):
:return:
:rtype:
"""
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
identifier = bytes.fromhex(strip_0x(blockchain_address))
person_metadata_client = PersonMetadata(identifier=identifier)
person_metadata_client.create(data=data)
@celery_app.task
def edit_person_metadata(blockchain_address: str, data: dict):
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
identifier = bytes.fromhex(strip_0x(blockchain_address))
person_metadata_client = PersonMetadata(identifier=identifier)
person_metadata_client.edit(data=data)
@@ -63,16 +58,16 @@ def add_phone_pointer(self, blockchain_address: str, phone_number: str):
@celery_app.task()
def add_custom_metadata(blockchain_address: str, data: dict):
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
identifier = bytes.fromhex(strip_0x(blockchain_address))
custom_metadata_client = CustomMetadata(identifier=identifier)
custom_metadata_client.create(data=data)
@celery_app.task()
def add_preferences_metadata(blockchain_address: str, data: dict):
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
custom_metadata_client = PreferencesMetadata(identifier=identifier)
custom_metadata_client.create(data=data)
identifier = bytes.fromhex(strip_0x(blockchain_address))
preferences_metadata_client = PreferencesMetadata(identifier=identifier)
preferences_metadata_client.create(data=data)
@celery_app.task()
@@ -81,7 +76,7 @@ def query_preferences_metadata(blockchain_address: str):
:param blockchain_address: Blockchain address of an account.
:type blockchain_address: str | Ox-hex
"""
identifier = blockchain_address_to_metadata_pointer(blockchain_address=blockchain_address)
identifier = bytes.fromhex(strip_0x(blockchain_address))
logg.debug(f'Retrieving preferences metadata for address: {blockchain_address}.')
person_metadata_client = PreferencesMetadata(identifier=identifier)
return person_metadata_client.query()

View File

@@ -6,6 +6,7 @@ import logging
import celery
# local imports
from cic_ussd.account.transaction import from_wei
from cic_ussd.notifications import Notifier
from cic_ussd.phone_number import Support
@@ -15,56 +16,46 @@ notifier = Notifier()
@celery_app.task
def notify_account_of_transaction(notification_data: dict):
def transaction(notification_data: dict):
"""
:param notification_data:
:type notification_data:
:return:
:rtype:
"""
account_tx_role = notification_data.get('account_tx_role')
amount = notification_data.get('amount')
balance = notification_data.get('balance')
role = notification_data.get('role')
amount = from_wei(notification_data.get('token_value'))
balance = notification_data.get('available_balance')
phone_number = notification_data.get('phone_number')
preferred_language = notification_data.get('preferred_language')
token_symbol = notification_data.get('token_symbol')
transaction_account_metadata = notification_data.get('transaction_account_metadata')
transaction_account_metadata = notification_data.get('metadata_id')
transaction_type = notification_data.get('transaction_type')
timestamp = datetime.datetime.now().strftime('%d-%m-%y, %H:%M %p')
if transaction_type == 'tokengift':
support_phone = Support.phone_number
notifier.send_sms_notification(
key='sms.account_successfully_created',
phone_number=phone_number,
preferred_language=preferred_language,
balance=balance,
support_phone=support_phone,
token_symbol=token_symbol
)
support_phone=Support.phone_number)
if transaction_type == 'transfer':
if account_tx_role == 'recipient':
notifier.send_sms_notification(
key='sms.received_tokens',
phone_number=phone_number,
preferred_language=preferred_language,
amount=amount,
token_symbol=token_symbol,
tx_sender_information=transaction_account_metadata,
timestamp=timestamp,
balance=balance
)
else:
notifier.send_sms_notification(
key='sms.sent_tokens',
phone_number=phone_number,
preferred_language=preferred_language,
amount=amount,
token_symbol=token_symbol,
tx_recipient_information=transaction_account_metadata,
timestamp=timestamp,
balance=balance
)
if role == 'recipient':
notifier.send_sms_notification('sms.received_tokens',
phone_number=phone_number,
preferred_language=preferred_language,
amount=amount,
token_symbol=token_symbol,
tx_sender_information=transaction_account_metadata,
timestamp=timestamp,
balance=balance)
if role == 'sender':
notifier.send_sms_notification('sms.sent_tokens',
phone_number=phone_number,
preferred_language=preferred_language,
amount=amount,
token_symbol=token_symbol,
tx_recipient_information=transaction_account_metadata,
timestamp=timestamp,
balance=balance)

View File

@@ -1,88 +1,84 @@
# standard imports
import json
import logging
# third-party imports
import celery
from i18n import config
import i18n
from chainlib.hash import strip_0x
# local imports
from cic_ussd.account import define_account_tx_metadata
from cic_ussd.db.models.account import Account
from cic_ussd.account.statement import get_cached_statement
from cic_ussd.account.transaction import aux_transaction_data, validate_transaction_account
from cic_ussd.cache import cache_data, cache_data_key
from cic_ussd.db.models.base import SessionBase
from cic_ussd.error import UnknownUssdRecipient
from cic_ussd.transactions import from_wei
celery_app = celery.current_app
logg = logging.getLogger(__file__)
@celery_app.task(bind=True)
def generate_statement(self, querying_party: str, transaction: dict):
""""""
queue = self.request.delivery_info.get('routing_key')
s_preferences = celery.signature(
'cic_ussd.tasks.metadata.query_preferences_metadata', [querying_party], queue=queue
)
s_parse_transaction = celery.signature(
'cic_ussd.tasks.processor.parse_transaction', [transaction], queue=queue
)
s_cache_statement = celery.signature(
'cic_ussd.tasks.processor.cache_statement', [querying_party], queue=queue
)
celery.chain(s_preferences, s_parse_transaction, s_cache_statement).apply_async()
@celery_app.task
def process_tx_metadata_for_notification(result: celery.Task, transaction_metadata: dict):
def cache_statement(parsed_transaction: dict, querying_party: str):
"""
:param result:
:type result:
:param transaction_metadata:
:type transaction_metadata:
:param parsed_transaction:
:type parsed_transaction:
:param querying_party:
:type querying_party:
:return:
:rtype:
"""
notification_data = {}
cached_statement = get_cached_statement(querying_party)
statement_transactions = []
if cached_statement:
statement_transactions = json.loads(cached_statement)
statement_transactions.append(parsed_transaction)
data = json.dumps(statement_transactions)
identifier = bytes.fromhex(strip_0x(querying_party))
key = cache_data_key(identifier, ':cic.statement')
cache_data(key, data)
# get preferred language
preferred_language = result.get('preferred_language')
@celery_app.task
def parse_transaction(preferences: dict, transaction: dict) -> dict:
"""This function parses transaction objects and collates all relevant data for system use i.e:
- An account's set preferred language.
- Account identifier that facilitates notification.
- Contextual tags i.e action and direction tags.
:param preferences: An account's set preferences.
:type preferences: dict
:param transaction: Transaction object.
:type transaction: dict
:return: Transaction object with contextual data for use in the system.
:rtype: dict
"""
preferred_language = preferences.get('preferred_language')
if not preferred_language:
preferred_language = config.get('fallback')
notification_data['preferred_language'] = preferred_language
preferred_language = i18n.config.get('fallback')
# validate account information against present ussd storage data.
transaction = aux_transaction_data(preferred_language, transaction)
session = SessionBase.create_session()
blockchain_address = transaction_metadata.get('blockchain_address')
tag = transaction_metadata.get('tag')
account = session.query(Account).filter_by(blockchain_address=blockchain_address).first()
if not account and tag == 'recipient':
session.close()
raise UnknownUssdRecipient(
f'Tx for recipient: {blockchain_address} was received but has no matching user in the system.'
)
# get phone number associated with account
phone_number = account.phone_number
notification_data['phone_number'] = phone_number
# get account's role in transaction i.e sender / recipient
tx_param = transaction_metadata.get('tx_param')
notification_data['transaction_type'] = tx_param
# get token amount and symbol
if tag == 'recipient':
account_tx_role = tag
amount = transaction_metadata.get('token_value')
amount = from_wei(value=amount)
token_symbol = transaction_metadata.get('token_symbol')
else:
account_tx_role = tag
amount = transaction_metadata.get('token_value')
amount = from_wei(value=amount)
token_symbol = transaction_metadata.get('token_symbol')
notification_data['account_tx_role'] = account_tx_role
notification_data['amount'] = amount
notification_data['token_symbol'] = token_symbol
# get account's standard ussd identification pattern
if tx_param == 'transfer':
tx_account_metadata = define_account_tx_metadata(user=account)
notification_data['transaction_account_metadata'] = tx_account_metadata
if tag == 'recipient':
notification_data['notification_key'] = 'sms.received_tokens'
else:
notification_data['notification_key'] = 'sms.sent_tokens'
if tx_param == 'tokengift':
notification_data['notification_key'] = 'sms.account_successfully_created'
# get account's balance
notification_data['balance'] = transaction_metadata.get('operational_balance')
return notification_data
account = validate_transaction_account(session, transaction)
metadata_id = account.standard_metadata_id()
transaction['metadata_id'] = metadata_id
transaction['phone_number'] = account.phone_number
session.commit()
session.close()
return transaction

View File

@@ -7,10 +7,10 @@ import celery
from celery.utils.log import get_logger
# local imports
from cic_ussd.cache import Cache, get_cached_data
from cic_ussd.db.models.base import SessionBase
from cic_ussd.db.models.ussd_session import UssdSession
from cic_ussd.error import SessionNotFoundError
from cic_ussd.session.ussd_session import UssdSession as InMemoryUssdSession
from cic_ussd.tasks.base import CriticalSQLAlchemyTask
celery_app = celery.current_app
@@ -28,46 +28,36 @@ def persist_session_to_db(external_session_id: str):
:raises SessionNotFoundError: If the session object is not found in memory.
:raises VersionTooLowError: If the session's version doesn't match the latest version.
"""
# create session
session = SessionBase.create_session()
# get ussd session in redis cache
in_memory_session = InMemoryUssdSession.redis_cache.get(external_session_id)
# process persistence to db
if in_memory_session:
in_memory_session = json.loads(in_memory_session)
in_db_ussd_session = session.query(UssdSession).filter_by(external_session_id=external_session_id).first()
if in_db_ussd_session:
in_db_ussd_session.update(
cached_ussd_session = get_cached_data(external_session_id)
if cached_ussd_session:
cached_ussd_session = json.loads(cached_ussd_session)
ussd_session = session.query(UssdSession).filter_by(external_session_id=external_session_id).first()
if ussd_session:
ussd_session.update(
session=session,
user_input=in_memory_session.get('user_input'),
state=in_memory_session.get('state'),
version=in_memory_session.get('version'),
user_input=cached_ussd_session.get('user_input'),
state=cached_ussd_session.get('state'),
version=cached_ussd_session.get('version'),
)
else:
in_db_ussd_session = UssdSession(
ussd_session = UssdSession(
external_session_id=external_session_id,
service_code=in_memory_session.get('service_code'),
msisdn=in_memory_session.get('msisdn'),
user_input=in_memory_session.get('user_input'),
state=in_memory_session.get('state'),
version=in_memory_session.get('version'),
service_code=cached_ussd_session.get('service_code'),
msisdn=cached_ussd_session.get('msisdn'),
user_input=cached_ussd_session.get('user_input'),
state=cached_ussd_session.get('state'),
version=cached_ussd_session.get('version'),
)
# handle the updating of session data for persistence to db
session_data = in_memory_session.get('session_data')
if session_data:
for key, value in session_data.items():
in_db_ussd_session.set_data(key=key, value=value, session=session)
session.add(in_db_ussd_session)
data = cached_ussd_session.get('data')
if data:
for key, value in data.items():
ussd_session.set_data(key=key, value=value, session=session)
session.add(ussd_session)
session.commit()
session.close()
InMemoryUssdSession.redis_cache.expire(external_session_id, timedelta(minutes=1))
Cache.store.expire(external_session_id, timedelta(minutes=1))
else:
session.close()
raise SessionNotFoundError('Session does not exist!')
session.close()