Compare commits

...

3 Commits

Author SHA1 Message Date
PhilipWafula ff0ed7b99c
Refactors balance.py
- Moves it into account package
- Makes functions names cic-compliant.
- Adds docstrings.
2021-05-11 09:49:50 +03:00
PhilipWafula 8a6d332bca
Move account creation to package init file. 2021-05-11 09:45:19 +03:00
PhilipWafula 4d812a74e6
Defines central module to retrieve all account relevant data from external components. 2021-05-11 09:43:57 +03:00
3 changed files with 136 additions and 21 deletions

View File

@ -0,0 +1,62 @@
"""This package handles account operations."""
# external imports
from cic_eth.api import Api
# local imports
from cic_ussd.operations import (add_tasks_to_tracker,
cache_account_creation_task_id,
create_or_update_session,
define_multilingual_responses)
from cic_ussd.menu.ussd_menu import UssdMenu
def create(chain_str: str,
external_session_id: str,
phone_number: str,
service_code: str,
user_input: str) -> str:
"""This function issues a task to create a blockchain account on cic-eth. It then creates a record of the ussd
session corresponding to the creation of the account and returns a response denoting that the user's account is
being created.
:param chain_str: The chain name and network id.
:type chain_str: str
:param external_session_id: A unique ID from africastalking.
:type external_session_id: str
:param phone_number: The phone number for the account to be created.
:type phone_number: str
:param service_code: The service code dialed.
:type service_code: str
:param user_input: The input entered by the user.
:type user_input: str
:return: A response denoting that the account is being created.
:rtype: str
"""
# attempt to create a user
cic_eth_api = Api(callback_task='cic_ussd.tasks.callback_handler.process_account_creation_callback',
callback_queue='cic-ussd',
callback_param='',
chain_str=chain_str)
creation_task_id = cic_eth_api.create_account().id
# record task initiation time
add_tasks_to_tracker(task_uuid=creation_task_id)
# cache account creation data
cache_account_creation_task_id(phone_number=phone_number, task_id=creation_task_id)
# find menu to notify user account is being created
current_menu = UssdMenu.find_by_name(name='account_creation_prompt')
# create a ussd session session
create_or_update_session(
external_session_id=external_session_id,
phone=phone_number,
service_code=service_code,
current_menu=current_menu.get('name'),
user_input=user_input)
# define response to relay to user
response = define_multilingual_responses(
key='ussd.kenya.account_creation_prompt', locales=['en', 'sw'], prefix='END')
return response

View File

@ -33,12 +33,12 @@ class BalanceManager:
def get_balances(self, asynchronous: bool = False) -> Union[celery.Task, dict]:
"""
This function queries cic-eth for an account's balances, It provides a means to receive the balance either
asynchronously or synchronously depending on the provided value for teh asynchronous parameter. It returns a
dictionary containing network, outgoing and incoming balances.
asynchronously or synchronously depending on the provided asynchronous value from the function's caller. It
returns a dictionary containing network, outgoing and incoming balances when synchronously called.
:param asynchronous: Boolean value checking whether to return balances asynchronously
:type asynchronous: bool
:return:
:rtype:
:return: dict containing network, outgoing and incoming balances | an async result object from celery.
:rtype: dict|celery.Task
"""
if asynchronous:
cic_eth_api = Api(
@ -47,7 +47,7 @@ class BalanceManager:
callback_task='cic_ussd.tasks.callback_handler.process_balances_callback',
callback_param=''
)
cic_eth_api.balance(address=self.address, token_symbol=self.token_symbol)
return cic_eth_api.balance(address=self.address, token_symbol=self.token_symbol)
else:
cic_eth_api = Api(chain_str=self.chain_str)
balance_request_task = cic_eth_api.balance(
@ -56,27 +56,30 @@ class BalanceManager:
return balance_request_task.get()[0]
def compute_operational_balance(balances: dict) -> float:
"""This function calculates the right balance given incoming and outgoing
:param balances:
:type balances:
:return:
:rtype:
def operational_balance(balances: dict) -> float:
"""This function computes the operational balance at an instance in the system by providing the difference of the
outgoing balance from the sum of incoming and network balances.
:param balances: A dictionary containing incoming, outgoing and network balances.
:type balances: dict
:return: The operational balance of the account at the instance of querying.
:rtype: float
"""
incoming_balance = balances.get('balance_incoming')
outgoing_balance = balances.get('balance_outgoing')
network_balance = balances.get('balance_network')
operational_balance = (network_balance + incoming_balance) - outgoing_balance
return from_wei(value=operational_balance)
balance = (network_balance + incoming_balance) - outgoing_balance
return from_wei(value=balance)
def get_cached_operational_balance(blockchain_address: str):
"""
:param blockchain_address:
:type blockchain_address:
:return:
:rtype:
def cached_operational_balance(blockchain_address: str) -> float:
"""This function retrieves the cached balances data from redis cache and computes the operational balance from
the cached data.
:param blockchain_address: Ethereum address of the account whose balance data is being retrieved.
:type blockchain_address: str, 0x-hex
:return: The operational balance of the account as per cached balance data.
:rtype: float
:raises CachedDataNotFoundError
"""
key = create_cached_data_key(
identifier=bytes.fromhex(blockchain_address[2:]),
@ -84,7 +87,7 @@ def get_cached_operational_balance(blockchain_address: str):
)
cached_balance = get_cached_data(key=key)
if cached_balance:
operational_balance = compute_operational_balance(balances=json.loads(cached_balance))
return operational_balance
balance = operational_balance(balances=json.loads(cached_balance))
return balance
else:
raise CachedDataNotFoundError('Cached operational balance not found.')

View File

@ -0,0 +1,50 @@
"""This module defines functions required to query external components of the cic platform for data relevant to
accounts on the cic-ussd component.
"""
# external imports
import celery
from cic_eth.api import Api
# local imports
from cic_ussd.chain import Chain
def person_metadata(blockchain_address: str):
"""This function asynchronously queries the metadata server for metadata associated with the person data type and
a given blockchain address.
:param blockchain_address: Ethereum address of account whose metadata is being queried.
:type blockchain_address: str, 0x-hex
"""
s_query_person_metadata = celery.signature(
'cic_ussd.tasks.metadata.query_person_metadata',
[blockchain_address]
)
s_query_person_metadata.apply_async(queue='cic-ussd')
def default_token_data() -> dict:
"""This function queries for the default token's data from the cic_eth tasks exposed over its Api class.
:return: A dict containing the default token address and it's corresponding symbol.
:rtype: dict
"""
chain_str = Chain.spec.__str__()
cic_eth_api = Api(chain_str=chain_str)
default_token_request_task = cic_eth_api.default_token()
return default_token_request_task.get()
def transactions_statement(blockchain_address: str):
"""This function asynchronously queries the cic-eth server to retrieve a chronologically reversed list of
transactions for an account.
:param blockchain_address: Ethereum address of account whose transactions is being queried.
:type blockchain_address:
"""
chain_str = Chain.spec.__str__()
cic_eth_api = Api(
chain_str=chain_str,
callback_queue='cic-ussd',
callback_task='cic_ussd.tasks.callback_handler.process_statement_callback',
callback_param=blockchain_address
)
cic_eth_api.list(address=blockchain_address, limit=9)