Compare commits
10 Commits
cic-eth-se
...
master
Author | SHA1 | Date | |
---|---|---|---|
3dcfbe59fe | |||
0dd21f3970 | |||
6ca8632cde | |||
b7dc290992 | |||
e69801ea08 | |||
50a596e707 | |||
cb61e45e4c | |||
e5b06b18d7 | |||
|
5d1a30021a | ||
|
ade3f4e917 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,4 +15,3 @@ build/
|
|||||||
.idea
|
.idea
|
||||||
**/.vim
|
**/.vim
|
||||||
**/*secret.yaml
|
**/*secret.yaml
|
||||||
.envrc
|
|
@ -15,5 +15,5 @@ To get started see [./apps/contract-migration/README.md](./apps/contract-migrati
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
[https://docs.grassecon.org/cic_stack/](https://docs.grassecon.org/cic_stack/)
|
[https://docs.grassecon.org/software/](https://docs.grassecon.org/software/)
|
||||||
|
|
||||||
|
@ -1,36 +1 @@
|
|||||||
# CIC-ETH
|
# CIC-ETH
|
||||||
|
|
||||||
## Testing CIC-ETH locally.
|
|
||||||
|
|
||||||
### Setup a Virtual Env
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -m venv ./venv # Python 3.9
|
|
||||||
source ./venv/activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running All Unit Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash ./tests/run_tests.sh # This will also install required dependencies
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running Specific Unit Tests
|
|
||||||
|
|
||||||
Ensure that:
|
|
||||||
|
|
||||||
- You have called `bash ./tests/run_tests.sh` at least once or run the following to install required dependencies
|
|
||||||
- You have activated the virtual environment
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install --extra-index-url https://pip.grassrootseconomics.net --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \
|
|
||||||
-r admin_requirements.txt \
|
|
||||||
-r services_requirements.txt \
|
|
||||||
-r test_requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Then here is an example that only runs tests with the keyword(-k) `test_server`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pytest -s -v --log-cli-level DEBUG --log-level DEBUG -k test_server
|
|
||||||
```
|
|
||||||
|
@ -8,6 +8,9 @@ import logging
|
|||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
import celery
|
import celery
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
|
from hexathon import strip_0x
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from cic_eth.api.base import ApiBase
|
from cic_eth.api.base import ApiBase
|
||||||
from cic_eth.enum import LockEnum
|
from cic_eth.enum import LockEnum
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def blockchain_address() -> str:
|
|
||||||
return os.urandom(20).hex().lower()
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
import os
|
|
||||||
|
|
||||||
# external imports
|
|
||||||
import pytest
|
|
||||||
from celery import uuid
|
|
||||||
# test imports
|
|
||||||
from cic_eth.pytest.helpers.accounts import blockchain_address
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def task_uuid():
|
|
||||||
return uuid()
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def default_token_data(foo_token_symbol, foo_token):
|
|
||||||
return {
|
|
||||||
'symbol': foo_token_symbol,
|
|
||||||
'address': foo_token,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'decimals': 6,
|
|
||||||
"converters": []
|
|
||||||
}
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_account_creation_task_request(mocker, task_uuid):
|
|
||||||
def mock_request(self):
|
|
||||||
mocked_task_request = mocker.patch('celery.app.task.Task.request')
|
|
||||||
mocked_task_request.id = task_uuid
|
|
||||||
return mocked_task_request
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.create_account', mock_request)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_account_creation_task_result(mocker, task_uuid):
|
|
||||||
def task_result(self):
|
|
||||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
|
||||||
sync_res.id = task_uuid
|
|
||||||
sync_res.get.return_value = blockchain_address()
|
|
||||||
return sync_res
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.create_account', task_result)
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_token_api_query(foo_token_symbol, foo_token, mocker, task_uuid):
|
|
||||||
def mock_query(self, token_symbol, proof=None):
|
|
||||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
|
||||||
sync_res.id = task_uuid
|
|
||||||
sync_res.get.return_value = [
|
|
||||||
{
|
|
||||||
'address': foo_token,
|
|
||||||
'converters': [],
|
|
||||||
'decimals': 6,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'proofs': ['5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3'],
|
|
||||||
'symbol': foo_token_symbol,
|
|
||||||
},{'5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3': ['Eb3907eCad74a0013c259D5874AE7f22DcBcC95C','Eb3907eCad74a0013c259D5874AE7f22DcBcC95C']}
|
|
||||||
]
|
|
||||||
return sync_res
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.token', mock_query)
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_tokens_api_query(foo_token_symbol, foo_token, mocker, task_uuid):
|
|
||||||
def mock_query(self, token_symbol, proof=None):
|
|
||||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
|
||||||
sync_res.id = task_uuid
|
|
||||||
sync_res.get.return_value = [[
|
|
||||||
{
|
|
||||||
'address': foo_token,
|
|
||||||
'converters': [],
|
|
||||||
'decimals': 6,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'proofs': ['5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3'],
|
|
||||||
'symbol': foo_token_symbol,
|
|
||||||
},{'5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3': ['Eb3907eCad74a0013c259D5874AE7f22DcBcC95C','Eb3907eCad74a0013c259D5874AE7f22DcBcC95C']}
|
|
||||||
], [
|
|
||||||
{
|
|
||||||
'address': foo_token,
|
|
||||||
'converters': [],
|
|
||||||
'decimals': 6,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'proofs': ['5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3'],
|
|
||||||
'symbol': foo_token_symbol,
|
|
||||||
},{'5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3': ['Eb3907eCad74a0013c259D5874AE7f22DcBcC95C','Eb3907eCad74a0013c259D5874AE7f22DcBcC95C']}
|
|
||||||
]]
|
|
||||||
return sync_res
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.tokens', mock_query)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_sync_balance_api_query(balances, mocker, task_uuid):
|
|
||||||
def sync_api_query(self, address: str, token_symbol: str):
|
|
||||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
|
||||||
sync_res.id = task_uuid
|
|
||||||
sync_res.get.return_value = balances
|
|
||||||
return sync_res
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.balance', sync_api_query)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_sync_default_token_api_query(default_token_data, mocker, task_uuid):
|
|
||||||
def mock_query(self):
|
|
||||||
sync_res = mocker.patch('celery.result.AsyncResult')
|
|
||||||
sync_res.id = task_uuid
|
|
||||||
sync_res.get.return_value = default_token_data
|
|
||||||
return sync_res
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.default_token', mock_query)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_transaction_list_query(mocker):
|
|
||||||
query_args = {}
|
|
||||||
|
|
||||||
def mock_query(self, address: str, limit: int):
|
|
||||||
query_args['address'] = address
|
|
||||||
query_args['limit'] = limit
|
|
||||||
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.list', mock_query)
|
|
||||||
return query_args
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def mock_transfer_api(mocker):
|
|
||||||
transfer_args = {}
|
|
||||||
|
|
||||||
def mock_transfer(self, from_address: str, to_address: str, value: int, token_symbol: str):
|
|
||||||
transfer_args['from_address'] = from_address
|
|
||||||
transfer_args['to_address'] = to_address
|
|
||||||
transfer_args['value'] = value
|
|
||||||
transfer_args['token_symbol'] = token_symbol
|
|
||||||
|
|
||||||
mocker.patch('cic_eth.api.api_task.Api.transfer', mock_transfer)
|
|
||||||
return transfer_args
|
|
@ -1,11 +1,15 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
import json
|
import json
|
||||||
|
import argparse
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
import redis
|
import redis
|
||||||
|
from xdg.BaseDirectory import xdg_config_home
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
import cic_eth.cli
|
import cic_eth.cli
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
import cic_eth.cli
|
|
||||||
from cic_eth.server.app import create_app
|
|
||||||
from cic_eth.server.celery import create_celery_wrapper
|
|
||||||
|
|
||||||
arg_flags = cic_eth.cli.argflag_std_base
|
|
||||||
local_arg_flags = cic_eth.cli.argflag_local_taskcallback
|
|
||||||
argparser = cic_eth.cli.ArgumentParser(arg_flags)
|
|
||||||
argparser.process_local_flags(local_arg_flags)
|
|
||||||
args = argparser.parse_args()
|
|
||||||
config = cic_eth.cli.Config.from_args(args, arg_flags, local_arg_flags)
|
|
||||||
# Define log levels
|
|
||||||
if args.vv:
|
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
|
||||||
elif args.v:
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
|
||||||
|
|
||||||
|
|
||||||
# Setup Celery App
|
|
||||||
celery_app = cic_eth.cli.CeleryApp.from_config(config)
|
|
||||||
celery_app.set_default()
|
|
||||||
|
|
||||||
|
|
||||||
chain_spec = config.get('CHAIN_SPEC')
|
|
||||||
celery_queue = config.get('CELERY_QUEUE')
|
|
||||||
redis_host = config.get('REDIS_HOST')
|
|
||||||
redis_port = config.get('REDIS_PORT')
|
|
||||||
redis_db = config.get('REDIS_DB')
|
|
||||||
redis_timeout = config.get('REDIS_TIMEOUT')
|
|
||||||
|
|
||||||
celery_wrapper = create_celery_wrapper(celery_queue=celery_queue, chain_spec=chain_spec,
|
|
||||||
redis_db=redis_db, redis_host=redis_host, redis_port=redis_port, redis_timeout=redis_timeout)
|
|
||||||
app = create_app(celery_wrapper)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=5000, log_level="info")
|
|
@ -1,3 +0,0 @@
|
|||||||
from . import converters
|
|
||||||
from . import cache
|
|
||||||
from . import celery
|
|
@ -1,119 +0,0 @@
|
|||||||
import logging
|
|
||||||
import sys
|
|
||||||
from typing import List, Optional, Union
|
|
||||||
|
|
||||||
from cic_eth.server import cache, converters
|
|
||||||
from cic_eth.server.cache import setup_cache
|
|
||||||
from cic_eth.server.celery import create_celery_wrapper
|
|
||||||
from cic_eth.server.models import (DefaultToken, Token, TokenBalance,
|
|
||||||
Transaction)
|
|
||||||
from fastapi import FastAPI, Query
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def create_app(celery_wrapper):
|
|
||||||
|
|
||||||
app = FastAPI(debug=True,
|
|
||||||
title="Grassroots Economics",
|
|
||||||
description="CIC ETH API",
|
|
||||||
version="0.0.1",
|
|
||||||
terms_of_service="https://www.grassrootseconomics.org/pages/terms-and-conditions.html",
|
|
||||||
contact={
|
|
||||||
"name": "Grassroots Economics",
|
|
||||||
"url": "https://www.grassrootseconomics.org",
|
|
||||||
"email": "will@grassecon.org"
|
|
||||||
},
|
|
||||||
license_info={
|
|
||||||
"name": "GPLv3",
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.get("/transactions", response_model=List[Transaction])
|
|
||||||
def transactions(address: str, limit: Optional[str] = 10):
|
|
||||||
return celery_wrapper('list', address, limit=limit)
|
|
||||||
|
|
||||||
@app.get("/balance", response_model=List[TokenBalance])
|
|
||||||
def balance(token_symbol: str, address: str = Query(..., title="Address", min_length=40, max_length=42), include_pending: bool = True):
|
|
||||||
log.info(f"address: {address}")
|
|
||||||
log.info(f"token_symbol: {token_symbol}")
|
|
||||||
data = celery_wrapper('balance', address, token_symbol,
|
|
||||||
include_pending=include_pending)
|
|
||||||
for b in data:
|
|
||||||
token = get_token(token_symbol)
|
|
||||||
b['balance_network'] = converters.from_wei(
|
|
||||||
token.decimals, int(b['balance_network']))
|
|
||||||
b['balance_incoming'] = converters.from_wei(
|
|
||||||
token.decimals, int(b['balance_incoming']))
|
|
||||||
b['balance_outgoing'] = converters.from_wei(
|
|
||||||
token.decimals, int(b['balance_outgoing']))
|
|
||||||
|
|
||||||
b.update({
|
|
||||||
"balance_available": int(b['balance_network']) + int(b['balance_incoming']) - int(b['balance_outgoing'])
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
|
|
||||||
@app.post("/create_account")
|
|
||||||
def create_account(password: Optional[str] = None, register: bool = True):
|
|
||||||
data = celery_wrapper(
|
|
||||||
'create_account', password=password, register=register)
|
|
||||||
return data
|
|
||||||
|
|
||||||
# def refill_gas(start_response, query: dict):
|
|
||||||
# address = query.pop('address')
|
|
||||||
# data = celery_wrapper('refill_gas', address)
|
|
||||||
# return data
|
|
||||||
|
|
||||||
# def ping(start_response, query: dict):
|
|
||||||
# data = celery_wrapper('ping', **query)
|
|
||||||
# return data
|
|
||||||
|
|
||||||
@app.post("/transfer")
|
|
||||||
def transfer(from_address: str, to_address: str, value: int, token_symbol: str):
|
|
||||||
token = get_token(
|
|
||||||
token_symbol)
|
|
||||||
wei_value = converters.to_wei(token.decimals, int(value))
|
|
||||||
data = celery_wrapper('transfer', from_address,
|
|
||||||
to_address, wei_value, token_symbol)
|
|
||||||
return data
|
|
||||||
|
|
||||||
@app.post("/transfer_from")
|
|
||||||
def transfer_from(from_address: str, to_address: str, value: int, token_symbol: str, spender_address: str):
|
|
||||||
token = get_token(
|
|
||||||
token_symbol)
|
|
||||||
wei_value = converters.to_wei(token.decimals, int(value))
|
|
||||||
data = celery_wrapper('transfer_from', from_address, to_address,
|
|
||||||
wei_value, token_symbol, spender_address)
|
|
||||||
return data
|
|
||||||
|
|
||||||
@app.get("/token", response_model=Token)
|
|
||||||
def token(token_symbol: str, proof: Optional[str] = None):
|
|
||||||
token = get_token(token_symbol)
|
|
||||||
if token == None:
|
|
||||||
sys.stderr.write(f"Cached Token {token_symbol} not found")
|
|
||||||
data = celery_wrapper('token', token_symbol, proof=proof)
|
|
||||||
token = Token.new(data)
|
|
||||||
sys.stderr.write(f"Token {token}")
|
|
||||||
|
|
||||||
return token
|
|
||||||
|
|
||||||
@app.get("/tokens", response_model=List[Token])
|
|
||||||
def tokens(token_symbols: Optional[List[str]] = Query(None), proof: Optional[Union[str, List[str], List[List[str]]]] = None):
|
|
||||||
data = celery_wrapper('tokens', token_symbols,
|
|
||||||
catch=len(token_symbols), proof=proof)
|
|
||||||
if data:
|
|
||||||
tokens = []
|
|
||||||
for token in data:
|
|
||||||
print(f"Token: {token}")
|
|
||||||
tokens.append(Token.new(token))
|
|
||||||
return tokens
|
|
||||||
return None
|
|
||||||
|
|
||||||
@app.get("/default_token", response_model=DefaultToken)
|
|
||||||
def default_token():
|
|
||||||
data = celery_wrapper('default_token')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_token(token_symbol: str):
|
|
||||||
data = celery_wrapper('token', token_symbol)
|
|
||||||
return Token.new(data)
|
|
||||||
return app
|
|
@ -1,130 +0,0 @@
|
|||||||
# standard imports
|
|
||||||
import hashlib
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
from typing import Optional, Union
|
|
||||||
|
|
||||||
from cic_eth.server.models import Token
|
|
||||||
from cic_types.condiments import MetadataPointer
|
|
||||||
from redis import Redis, StrictRedis
|
|
||||||
|
|
||||||
logg = logging.getLogger(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
class Cache:
|
|
||||||
store: Redis = None
|
|
||||||
|
|
||||||
|
|
||||||
def setup_cache(redis_host, redis_port, redis_db):
|
|
||||||
# Define universal redis cache access
|
|
||||||
Cache.store = StrictRedis(
|
|
||||||
host=redis_host, port=redis_port, db=redis_db, decode_responses=True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_token_data(token_symbol: str):
|
|
||||||
"""
|
|
||||||
:param token_symbol:
|
|
||||||
:type token_symbol:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
identifier = [token_symbol.encode('utf-8')]
|
|
||||||
key = cache_data_key(identifier, MetadataPointer.TOKEN_DATA)
|
|
||||||
logg.debug(f'Retrieving token data for: {token_symbol} at: {key}')
|
|
||||||
token_data_str = get_cached_data(key=key)
|
|
||||||
if(token_data_str is None):
|
|
||||||
logg.debug(f'No token data found for: {token_symbol}')
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
token_data = json.loads(token_data_str)
|
|
||||||
logg.debug(f'Retrieved token data: {token_data}')
|
|
||||||
return token_data
|
|
||||||
|
|
||||||
|
|
||||||
def set_token_data(token_symbol: str, token: dict):
|
|
||||||
"""
|
|
||||||
:param token_symbol:
|
|
||||||
:type token_symbol:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
identifier = [token_symbol.encode('utf-8')]
|
|
||||||
key = cache_data_key(identifier, MetadataPointer.TOKEN_DATA)
|
|
||||||
cache_data(key, json.dumps(token))
|
|
||||||
logg.debug(f'Cached token data for: {token_symbol} at: {key}')
|
|
||||||
|
|
||||||
|
|
||||||
def get_default_token() -> Optional[str]:
|
|
||||||
"""This function attempts to retrieve the default token's data from the redis cache.
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
logg.debug(f'Retrieving default token from cache')
|
|
||||||
# TODO: What should the identifier be?
|
|
||||||
key = cache_data_key(identifier="ff".encode('utf-8'),
|
|
||||||
salt=MetadataPointer.TOKEN_DEFAULT)
|
|
||||||
default_token_str = get_cached_data(key=key)
|
|
||||||
if default_token_str is None:
|
|
||||||
logg.debug(f'No cached default token found: {key}')
|
|
||||||
return None
|
|
||||||
default_token = json.loads(default_token_str)
|
|
||||||
logg.debug(f'Retrieved default token data: {default_token}')
|
|
||||||
return default_token
|
|
||||||
|
|
||||||
|
|
||||||
def set_default_token(default_token: dict):
|
|
||||||
"""
|
|
||||||
:param default_token:
|
|
||||||
:type default_token:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
logg.debug(f'Setting default token in cache')
|
|
||||||
key = cache_data_key(identifier="ff".encode('utf-8'),
|
|
||||||
salt=MetadataPointer.TOKEN_DEFAULT)
|
|
||||||
cache_data(key, json.dumps(default_token))
|
|
||||||
|
|
||||||
|
|
||||||
def cache_data(key: str, data: str):
|
|
||||||
"""
|
|
||||||
:param key:
|
|
||||||
:type key:
|
|
||||||
:param data:
|
|
||||||
:type data:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
cache = Cache.store
|
|
||||||
cache.set(name=key, value=data)
|
|
||||||
cache.persist(name=key)
|
|
||||||
logg.debug(f'caching: {data} with key: {key}.')
|
|
||||||
|
|
||||||
|
|
||||||
def get_cached_data(key: str):
|
|
||||||
"""
|
|
||||||
:param key:
|
|
||||||
:type key:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
cache = Cache.store
|
|
||||||
return cache.get(name=key)
|
|
||||||
|
|
||||||
|
|
||||||
def cache_data_key(identifier: Union[list, bytes], salt: MetadataPointer):
|
|
||||||
"""
|
|
||||||
:param identifier:
|
|
||||||
:type identifier:
|
|
||||||
:param salt:
|
|
||||||
:type salt:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
hash_object = hashlib.new("sha256")
|
|
||||||
if isinstance(identifier, list):
|
|
||||||
for identity in identifier:
|
|
||||||
hash_object.update(identity)
|
|
||||||
else:
|
|
||||||
hash_object.update(identifier)
|
|
||||||
hash_object.update(salt.value.encode(encoding="utf-8"))
|
|
||||||
return hash_object.digest().hex()
|
|
@ -1,64 +0,0 @@
|
|||||||
import json
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import redis
|
|
||||||
from cic_eth.api.api_task import Api
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def create_celery_wrapper(chain_spec,
|
|
||||||
celery_queue,
|
|
||||||
redis_host,
|
|
||||||
redis_port,
|
|
||||||
redis_db,
|
|
||||||
redis_timeout):
|
|
||||||
def call(method, *args, catch=1, **kwargs):
|
|
||||||
""" Creates a redis channel and calls `cic_eth.api` with the provided `method` and `*args`. Returns the result of the api call. Catch allows you to specify how many messages to catch before returning.
|
|
||||||
"""
|
|
||||||
log.debug(f"Using redis: {redis_host}, {redis_port}, {redis_db}")
|
|
||||||
redis_channel = str(uuid.uuid4())
|
|
||||||
r = redis.Redis(redis_host, redis_port, redis_db)
|
|
||||||
ps = r.pubsub()
|
|
||||||
ps.subscribe(redis_channel)
|
|
||||||
api = Api(
|
|
||||||
chain_spec,
|
|
||||||
queue=celery_queue,
|
|
||||||
callback_param='{}:{}:{}:{}'.format(
|
|
||||||
redis_host, redis_port, redis_db, redis_channel),
|
|
||||||
callback_task='cic_eth.callbacks.redis.redis',
|
|
||||||
callback_queue=celery_queue,
|
|
||||||
)
|
|
||||||
getattr(api, method)(*args, **kwargs)
|
|
||||||
|
|
||||||
ps.get_message()
|
|
||||||
try:
|
|
||||||
data = []
|
|
||||||
if catch == 1:
|
|
||||||
message = ps.get_message(timeout=redis_timeout)
|
|
||||||
data = json.loads(message['data'])["result"]
|
|
||||||
raise data
|
|
||||||
else:
|
|
||||||
for _i in range(catch):
|
|
||||||
message = ps.get_message(
|
|
||||||
timeout=redis_timeout)
|
|
||||||
result = json.loads(message['data'])["result"]
|
|
||||||
data.append(result)
|
|
||||||
|
|
||||||
except TimeoutError as e:
|
|
||||||
sys.stderr.write(
|
|
||||||
f"cic_eth.api.{method}({args}, {kwargs}) timed out:\n {e}")
|
|
||||||
raise e
|
|
||||||
except Exception as e:
|
|
||||||
sys.stderr.write(
|
|
||||||
f'Unable to parse Data:\n{data}\n Error:\n{e}')
|
|
||||||
raise e
|
|
||||||
|
|
||||||
log.debug(
|
|
||||||
f"cic_eth.api.{method}(args={args}, kwargs={kwargs})\n {data}")
|
|
||||||
|
|
||||||
ps.unsubscribe()
|
|
||||||
return data
|
|
||||||
return call
|
|
@ -1,39 +0,0 @@
|
|||||||
# Stolen from ussd
|
|
||||||
|
|
||||||
from math import trunc
|
|
||||||
|
|
||||||
def from_wei(decimals: int, value: int) -> float:
|
|
||||||
"""This function converts values in Wei to a token in the cic network.
|
|
||||||
:param decimals: The decimals required for wei values.
|
|
||||||
:type decimals: int
|
|
||||||
:param value: Value in Wei
|
|
||||||
:type value: int
|
|
||||||
:return: SRF equivalent of value in Wei
|
|
||||||
:rtype: float
|
|
||||||
"""
|
|
||||||
value = float(value) / (10**decimals)
|
|
||||||
return truncate(value=value, decimals=2)
|
|
||||||
|
|
||||||
|
|
||||||
def to_wei(decimals: int, value: int) -> int:
|
|
||||||
"""This functions converts values from a token in the cic network to Wei.
|
|
||||||
:param decimals: The decimals required for wei values.
|
|
||||||
:type decimals: int
|
|
||||||
:param value: Value in SRF
|
|
||||||
:type value: int
|
|
||||||
:return: Wei equivalent of value in SRF
|
|
||||||
:rtype: int
|
|
||||||
"""
|
|
||||||
return int(value * (10**decimals))
|
|
||||||
|
|
||||||
def truncate(value: float, decimals: int) -> float:
|
|
||||||
"""This function truncates a value to a specified number of decimals places.
|
|
||||||
:param value: The value to be truncated.
|
|
||||||
:type value: float
|
|
||||||
:param decimals: The number of decimals for the value to be truncated to
|
|
||||||
:type decimals: int
|
|
||||||
:return: The truncated value.
|
|
||||||
:rtype: int
|
|
||||||
"""
|
|
||||||
stepper = 10.0**decimals
|
|
||||||
return trunc(stepper*value) / stepper
|
|
@ -1,92 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
|
|
||||||
|
|
||||||
class Transaction(BaseModel):
|
|
||||||
block_number: Optional[int] = Field(None, example=24531)
|
|
||||||
date_checked: Optional[str] = Field(
|
|
||||||
None, example='2021-11-12T09:36:40.725296')
|
|
||||||
date_created: Optional[str] = Field(
|
|
||||||
None, example='2021-11-12T09:36:40.131292')
|
|
||||||
date_updated: Optional[str] = Field(
|
|
||||||
None, example='2021-11-12T09:36:40.131292')
|
|
||||||
destination_token: Optional[str] = Field(
|
|
||||||
None, example=365185044137427460620354810422988491181438940190
|
|
||||||
)
|
|
||||||
destination_token_decimals: Optional[int] = Field(None, example=6)
|
|
||||||
destination_token_symbol: Optional[str] = Field(None, example='COFE')
|
|
||||||
from_value: Optional[int] = Field(None, example=100000000)
|
|
||||||
hash: Optional[str] = Field(
|
|
||||||
None,
|
|
||||||
example=90380195350511178677041624165156640995490505896556680958001954705731707874291,
|
|
||||||
)
|
|
||||||
nonce: Optional[int] = Field(None, example=1)
|
|
||||||
recipient: Optional[str] = Field(
|
|
||||||
None, example='872e1ec9d499b242ebfcfd0a279a4c3e0cd472c0'
|
|
||||||
)
|
|
||||||
sender: Optional[str] = Field(
|
|
||||||
None, example='1a92b05e0b880127a4c26ac0f68a52df3ac6b89d'
|
|
||||||
)
|
|
||||||
signed_tx: Optional[str] = Field(
|
|
||||||
None,
|
|
||||||
example=1601943273486236942256143665779318355236220334071247753507187634376562549990085710958441113013370129915441072693447256942510246386178938683325073160349857879326297351587330623503997011254644396580777843154770873208185332563272343361515226115860084201932230246018679661802320007832375955345977725551120479084062615799940692628221555193198194825737613358738414884130187144700126061702642574663703095161159219410608270,
|
|
||||||
)
|
|
||||||
source_token: Optional[str] = Field(
|
|
||||||
None, example=365185044137427460620354810422988491181438940190
|
|
||||||
)
|
|
||||||
source_token_decimals: Optional[int] = Field(None, example=6)
|
|
||||||
source_token_symbol: Optional[str] = Field(None, example='COFE')
|
|
||||||
status: Optional[str] = Field(None, example='SUCCESS')
|
|
||||||
status_code: Optional[int] = Field(None, example=4104)
|
|
||||||
timestamp: Optional[int] = Field(None, example=1636709800)
|
|
||||||
to_value: Optional[int] = Field(None, example=100000000)
|
|
||||||
tx_hash: Optional[str] = Field(
|
|
||||||
None,
|
|
||||||
example=90380195350511178677041624165156640995490505896556680958001954705731707874291,
|
|
||||||
)
|
|
||||||
tx_index: Optional[int] = Field(None, example=0)
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultToken(BaseModel):
|
|
||||||
symbol: Optional[str] = Field(None, description='Token Symbol')
|
|
||||||
address: Optional[str] = Field(None, description='Token Address')
|
|
||||||
name: Optional[str] = Field(None, description='Token Name')
|
|
||||||
decimals: Optional[int] = Field(None, description='Decimals')
|
|
||||||
|
|
||||||
|
|
||||||
class TokenBalance(BaseModel):
|
|
||||||
address: Optional[str] = None
|
|
||||||
converters: Optional[List[str]] = None
|
|
||||||
balance_network: Optional[int] = None
|
|
||||||
balance_incoming: Optional[int] = None
|
|
||||||
balance_outgoing: Optional[int] = None
|
|
||||||
balance_available: Optional[int] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Token(BaseModel):
|
|
||||||
decimals: Optional[int] = None
|
|
||||||
name: Optional[str] = None
|
|
||||||
address: Optional[str] = None
|
|
||||||
symbol: Optional[str] = None
|
|
||||||
proofs: Optional[List[str]] = None
|
|
||||||
converters: Optional[List[str]] = None
|
|
||||||
proofs_with_signers: Optional[List[Proof]] = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def new(data: List[dict]) -> Token:
|
|
||||||
proofs_with_signers = [{"proof": proof, "signers": signers}
|
|
||||||
for (proof, signers) in data[1].items()]
|
|
||||||
return Token(**data[0],
|
|
||||||
proofs_with_signers=proofs_with_signers,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Proof(BaseModel):
|
|
||||||
proof: Optional[str] = None
|
|
||||||
signers: Optional[List[str]] = None
|
|
||||||
|
|
||||||
|
|
||||||
Token.update_forward_refs()
|
|
@ -1,5 +0,0 @@
|
|||||||
[redis]
|
|
||||||
host=redis
|
|
||||||
database=0
|
|
||||||
password=
|
|
||||||
port=6379
|
|
@ -5,5 +5,3 @@ urlybird~=0.0.1
|
|||||||
cic-eth-registry~=0.6.6
|
cic-eth-registry~=0.6.6
|
||||||
cic-types~=0.2.1a8
|
cic-types~=0.2.1a8
|
||||||
cic-eth-aux-erc20-demurrage-token~=0.0.3
|
cic-eth-aux-erc20-demurrage-token~=0.0.3
|
||||||
fastapi[all]==0.70.1
|
|
||||||
uvicorn[standard]<0.16.0
|
|
@ -36,7 +36,6 @@ packages =
|
|||||||
cic_eth.db.models
|
cic_eth.db.models
|
||||||
cic_eth.queue
|
cic_eth.queue
|
||||||
cic_eth.ext
|
cic_eth.ext
|
||||||
cic_eth.server
|
|
||||||
cic_eth.runnable
|
cic_eth.runnable
|
||||||
cic_eth.runnable.daemons
|
cic_eth.runnable.daemons
|
||||||
cic_eth.runnable.daemons.filters
|
cic_eth.runnable.daemons.filters
|
||||||
|
@ -24,8 +24,6 @@ from cic_eth.pytest.fixtures_database import *
|
|||||||
from cic_eth.pytest.fixtures_role import *
|
from cic_eth.pytest.fixtures_role import *
|
||||||
from cic_eth.pytest.fixtures_contract import *
|
from cic_eth.pytest.fixtures_contract import *
|
||||||
from cic_eth.pytest.fixtures_token import *
|
from cic_eth.pytest.fixtures_token import *
|
||||||
from cic_eth.pytest.patches.account import *
|
|
||||||
|
|
||||||
from chainlib.eth.pytest import *
|
from chainlib.eth.pytest import *
|
||||||
from eth_contract_registry.pytest import *
|
from eth_contract_registry.pytest import *
|
||||||
from cic_eth_registry.pytest.fixtures_contracts import *
|
from cic_eth_registry.pytest.fixtures_contracts import *
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
pip install --extra-index-url https://pip.grassrootseconomics.net --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \
|
pip install --extra-index-url https://pip.grassrootseconomics.net --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple
|
||||||
-r admin_requirements.txt \
|
-r admin_requirements.txt
|
||||||
-r services_requirements.txt \
|
-r services_requirements.txt
|
||||||
-r test_requirements.txt
|
-r test_requirements.txt
|
||||||
|
|
||||||
export PYTHONPATH=. && pytest -x --cov=cic_eth --cov-fail-under=90 --cov-report term-missing tests
|
export PYTHONPATH=. && pytest -x --cov=cic_eth --cov-fail-under=90 --cov-report term-missing tests
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
# coding: utf-8
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
|
|
||||||
import hexathon
|
|
||||||
import pytest
|
|
||||||
from cic_eth.server.app import create_app
|
|
||||||
from cic_eth.server.celery import create_celery_wrapper
|
|
||||||
from fastapi.testclient import TestClient
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def celery_wrapper(api):
|
|
||||||
""" Creates a redis channel and calls `cic_eth.api` with the provided `method` and `*args`. Returns the result of the api call. Catch allows you to specify how many messages to catch before returning.
|
|
||||||
"""
|
|
||||||
def wrapper(method, *args, catch=1, **kwargs):
|
|
||||||
t = getattr(api, method)(*args, **kwargs)
|
|
||||||
return t.get()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def client(celery_wrapper):
|
|
||||||
app = create_app(celery_wrapper)
|
|
||||||
return TestClient(app)
|
|
||||||
|
|
||||||
|
|
||||||
def test_default_token(client,mock_sync_default_token_api_query):
|
|
||||||
# Default Token
|
|
||||||
response = client.get('/default_token')
|
|
||||||
log.debug(f"balance response {response}")
|
|
||||||
default_token = response.json()
|
|
||||||
assert default_token == {'symbol': 'FOO', 'address': '0xe7c559c40B297d7f039767A2c3677E20B24F1385', 'name': 'Giftable Token', 'decimals': 6}
|
|
||||||
|
|
||||||
def test_token(client, mock_token_api_query):
|
|
||||||
# Default Token
|
|
||||||
response = client.get('/token?token_symbol=FOO')
|
|
||||||
log.debug(f"token response {response}")
|
|
||||||
token = response.json()
|
|
||||||
assert token == {
|
|
||||||
'address': '0xe7c559c40B297d7f039767A2c3677E20B24F1385',
|
|
||||||
'converters': [],
|
|
||||||
'decimals': 6,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'proofs': ['5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3'],
|
|
||||||
'proofs_with_signers': [{'proof': '5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3',
|
|
||||||
'signers': ['Eb3907eCad74a0013c259D5874AE7f22DcBcC95C', 'Eb3907eCad74a0013c259D5874AE7f22DcBcC95C']}],
|
|
||||||
'symbol': 'FOO',
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_tokens(client, mock_tokens_api_query):
|
|
||||||
# Default Token
|
|
||||||
response = client.get(
|
|
||||||
'/tokens', params={'token_symbols': ['FOO', 'FOO']})
|
|
||||||
|
|
||||||
log.debug(f"tokens response {response}")
|
|
||||||
tokens = response.json()
|
|
||||||
assert tokens == [
|
|
||||||
{
|
|
||||||
'address': '0xe7c559c40B297d7f039767A2c3677E20B24F1385',
|
|
||||||
'converters': [],
|
|
||||||
'decimals': 6,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'proofs': ['5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3'],
|
|
||||||
'proofs_with_signers': [{'proof': '5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3',
|
|
||||||
'signers': ['Eb3907eCad74a0013c259D5874AE7f22DcBcC95C', 'Eb3907eCad74a0013c259D5874AE7f22DcBcC95C']}],
|
|
||||||
'symbol': 'FOO',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'address': '0xe7c559c40B297d7f039767A2c3677E20B24F1385',
|
|
||||||
'converters': [],
|
|
||||||
'decimals': 6,
|
|
||||||
'name': 'Giftable Token',
|
|
||||||
'proofs': ['5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3'],
|
|
||||||
'proofs_with_signers': [{'proof': '5b1549818725ca07c19fc47fda5d8d85bbebb1283855d5ab99785dcb7d9051d3',
|
|
||||||
'signers': ['Eb3907eCad74a0013c259D5874AE7f22DcBcC95C', 'Eb3907eCad74a0013c259D5874AE7f22DcBcC95C']}],
|
|
||||||
'symbol': 'FOO',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
@pytest.mark.skip("Not implemented")
|
|
||||||
def test_account(client):
|
|
||||||
# Default Token
|
|
||||||
response = client.get('/default_token')
|
|
||||||
log.debug(f"balance response {response}")
|
|
||||||
default_token = response.json()
|
|
||||||
|
|
||||||
# Create Account 1
|
|
||||||
params = {
|
|
||||||
'password': '',
|
|
||||||
'register': True
|
|
||||||
}
|
|
||||||
response = client.post(
|
|
||||||
'/create_account',
|
|
||||||
params=params)
|
|
||||||
address_1 = hexathon.valid(response.json())
|
|
||||||
|
|
||||||
# Create Account 2
|
|
||||||
params = {
|
|
||||||
'password': '',
|
|
||||||
'register': True
|
|
||||||
}
|
|
||||||
response = client.post('/create_account',
|
|
||||||
params=params)
|
|
||||||
address_2 = hexathon.valid(response.json())
|
|
||||||
time.sleep(30) # Required to allow balance to show
|
|
||||||
|
|
||||||
# Balance Account 1
|
|
||||||
params = {
|
|
||||||
'address': address_1,
|
|
||||||
'token_symbol': 'COFE',
|
|
||||||
'include_pending': True
|
|
||||||
}
|
|
||||||
response = client.get('/balance',
|
|
||||||
params=params)
|
|
||||||
balance = response.json()
|
|
||||||
|
|
||||||
assert (balance[0] == {
|
|
||||||
"address": default_token.get('address').lower(),
|
|
||||||
"balance_available": 30000,
|
|
||||||
"balance_incoming": 0,
|
|
||||||
"balance_network": 30000,
|
|
||||||
"balance_outgoing": 0,
|
|
||||||
"converters": []
|
|
||||||
})
|
|
||||||
|
|
||||||
# Transfer
|
|
||||||
params = {
|
|
||||||
'from_address': address_1,
|
|
||||||
'to_address': address_2,
|
|
||||||
'value': 100,
|
|
||||||
'token_symbol': 'COFE'
|
|
||||||
}
|
|
||||||
response = client.post('/transfer',
|
|
||||||
params=params)
|
|
||||||
transfer = response.json()
|
|
||||||
|
|
||||||
# Balance Account 1
|
|
||||||
params = {
|
|
||||||
'address': address_1,
|
|
||||||
'token_symbol': 'COFE',
|
|
||||||
'include_pending': True
|
|
||||||
}
|
|
||||||
response = client.get('/balance',
|
|
||||||
params=params)
|
|
||||||
balance_after_transfer = response.json()
|
|
||||||
assert (balance_after_transfer[0] == {
|
|
||||||
"address": default_token.get('address').lower(),
|
|
||||||
"balance_available": 29900,
|
|
||||||
"balance_incoming": 0,
|
|
||||||
"balance_network": 30000,
|
|
||||||
"balance_outgoing": 100,
|
|
||||||
"converters": []
|
|
||||||
})
|
|
||||||
|
|
||||||
# Transactions Account 1
|
|
||||||
params = {
|
|
||||||
'address': address_1,
|
|
||||||
'limit': 10
|
|
||||||
}
|
|
||||||
response = client.get('/transactions',
|
|
||||||
params=params)
|
|
||||||
transactions = response.json()
|
|
||||||
# TODO: What are the other 2 transactions
|
|
||||||
assert len(transactions) == 3
|
|
||||||
# Check the transaction is correct
|
|
||||||
# TODO wtf is READSEND (Ready to send? Or already sent)
|
|
||||||
assert transactions[0].status == 'READYSEND'
|
|
@ -14,7 +14,7 @@ from cic_ussd.account.chain import Chain
|
|||||||
from cic_ussd.cache import cache_data, cache_data_key, get_cached_data
|
from cic_ussd.cache import cache_data, cache_data_key, get_cached_data
|
||||||
from cic_ussd.error import CachedDataNotFoundError, SeppukuError
|
from cic_ussd.error import CachedDataNotFoundError, SeppukuError
|
||||||
from cic_ussd.metadata.tokens import query_token_info, query_token_metadata
|
from cic_ussd.metadata.tokens import query_token_info, query_token_metadata
|
||||||
from cic_ussd.processor.util import wait_for_cache
|
from cic_ussd.processor.poller import wait_for_cache
|
||||||
|
|
||||||
logg = logging.getLogger(__file__)
|
logg = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
9
apps/cic-ussd/cic_ussd/data/sys/guardians.txt
Normal file
9
apps/cic-ussd/cic_ussd/data/sys/guardians.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
+254707628499
|
||||||
|
+254757628885
|
||||||
|
+254757628900
|
||||||
|
+254792048646
|
||||||
|
+254792048228
|
||||||
|
+254792048490
|
||||||
|
+254792048902
|
||||||
|
+254727806655
|
||||||
|
+254790079966
|
19
apps/cic-ussd/cic_ussd/data/sys/helpers.csv
Normal file
19
apps/cic-ussd/cic_ussd/data/sys/helpers.csv
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
keys,en,sw,kam,kik,miji,luo,bor
|
||||||
|
female,Female,Mwanamke,Mundumuka,Mutumia,Muche,Dhako,Uwole
|
||||||
|
from,From,Kutoka kwa,Kuma kwa,Kuuma kwa,Ulaako,Kowuok kuom,ira
|
||||||
|
male,Male,Mwanaume,Mundume,Mundurume,Mulume,Dichuo,Dir
|
||||||
|
not_provided,Not provided,Haijawekwa,Inenganitwe,Ndiikiritwo,Kaphana,Okoketi,Kes inkan
|
||||||
|
no_language_list,No language list,Hamna lugha ya kuchagua,Vai luka ya kusakwa,Hatire ruthiomi rwakucagurwo,Kahana luga irio orodeshwa,Onge dhok miyiero,Afaan chaguad injirt
|
||||||
|
no_transaction_history,No transaction history,Hamna ripoti ya matumizi,Vai livoti ya utumii,Hatire riboti ya mahuthira,Kahana repoti ya mahumizi,Onge ripot mar tiyo,Odhuu jalkaban injirt
|
||||||
|
no_tokens_list,No more Sarafu,Hamna sarafu zingine,Vai Sarafu ingi,Hatire Sarafu inge,Kahana Sarafu zaidi,Onge Sarafu moko,Sarafu dibii injirt
|
||||||
|
other,Other,Nyingine,Ingi,Inge,Nyinjine,Moko,Ta dibii
|
||||||
|
received,Received,Ulipokea,Niwakwatie,Niuramukirire ,Hokera,Niyudo,Argat
|
||||||
|
sent,Sent,Ulituma,Niwatumie,Niuratumire,Humwa,Nioro,Ergan
|
||||||
|
to,To,Kwa,Kwa,Hare,Kwa,Ne,Es
|
||||||
|
guardians_list_header,Your PIN guards are:,PIN Walinzi uliowaongeza ni:,PIN Atetheesya ala wongelile ni:,Agiteri a PIN yaku ni:,PIN Aimirizi urioika ni:,PIN Jorit magi gin:,PIN Naam at korkorad:
|
||||||
|
no_guardians_list,No PIN guardians set,Hamna PIN walinzi walioongezwa,Vai atetheesya mongelwa,Hartire agiteri meekeretwo,Kahana aimirizi adzoikwa,Onge jorit moketi,Nam an korkorad injirt
|
||||||
|
error.no_phone_number_provided,No phone number was provided,Nambari ya simu haijawekwa,Namba ya simu inaikiwa,Namba ya thimu ndihianetwo,Kahana namba ya simu idzopewa,Namba mar simu okoketi,Namba simu kees inkaan
|
||||||
|
error.no_matching_account,The number provided is not registered,Nambari uliyoweka haijasajiliwa,Namba ya simu ila wekiya ti mbandikithye,Namba iria wekera ndiandekithetwo,Namba idzopewa kaidzagwe kusajiliwa,Namba mar simu miketo pok ondiki,Namba ka at kekeet sajiil incab
|
||||||
|
error.is_initiator,Phone number cannot be your own,Nambari yafaa kuwa tofauti na yako,Namba ya simu yaile ithiwa itavwanene na yaku,Namba ifatie gukorwo ina utiganu na yaku,Namba yasimu kaidima kukala niyako,Namba onego obed mopogre gimari,Namba simu tete tau mal
|
||||||
|
error.is_existent_guardian,This phone number is already added as a PIN guardian,Nambari hii tayari imeongezwa kama mlinzi wa nambari ya siri,Namba ii niyongeletwe tayari ta mutethesya wa kusovya pin,Namba ino niyongereirwo ta murugamereri ya namba ya thiri,Nambari ii yasimu yaikwa kare Muimirizi,Nambani oseketi kaka jarit,Namba tana yayu nam korkoradi taat
|
||||||
|
error.is_not_existent_guardian,Phone number not set as PIN guardian,Nambari hii haijaongezwa kama mlinzi wa nambari ya PIN,Namba ii iyongeletwe ta mutethesya wa kusovya PIN,Namba ino ndiongereirwo ta mugiteri wa PIN,Nambari ii yasimu kaiikika kugaluza PIN zda mwimirizi,Nambani pok omed kaka jarit,Namba simu ta nam korkorad indharan
|
|
7
apps/cic-ussd/cic_ussd/data/sys/sms.csv
Normal file
7
apps/cic-ussd/cic_ussd/data/sys/sms.csv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
keys,en,sw,kam,kik,miji,luo,bor
|
||||||
|
account_successfully_created,You have been registered on Sarafu Network! To use dial *384*96# on Safaricom and *483*96# on other networks. For help %{support_phone},Umesajiliwa kwa Sarafu Network! Kutumia bonyeza *384*96# Safaricom ama *483*46# kwa utandao tofauti. Kwa Usaidizi %{support_phone},Niwayandikithya na Sarafu Network! Safaricom kuna namba ii *384*96# mitandao ingi *483*96#. Utethyo ungi kuna %{support_phone},Niweyandekithia kwe Sarafu Network! Kuhuthira hihinya *384*96# he Safaricom na *483*46# he mitambo ingi Uteithio %{support_phone},Usajiliwa Sarafu Network! kuhumira hopya *384*96# Saf *483*96# mtandao mnjine. Kuvizwa %{support_phone},Osendiki e Sarafu Network! Kidwatiyogo to dii *384*96# Safaricom kata *483*46# e netwak mamoko. Kuom kony %{support_phone},Yaayu sirejestan Sarafu Network! Kuches *384*96# Safaricom *483*46# Airtel
|
||||||
|
received_tokens,Successfully received %{amount} %{token_symbol} from %{tx_sender_information} %{timestamp} to %{tx_recipient_information} Balance %{balance} %{token_symbol},Umepokea %{amount} %{token_symbol} kutoka kwa %{tx_sender_information} %{timestamp} kuendea %{tx_recipient_information} Salio %{balance} %{token_symbol},Niwakwata %{amount} %{token_symbol} kuma %{tx_sender_information} %{timestamp} kuvikia %{tx_recipient_information} Mbalansi %{balance} %{token_symbol},Wamukira %{amount} %{token_symbol} kuuma kwa %{tx_sender_information} %{timestamp} to %{tx_recipient_information} Watigaria %{balance} %{token_symbol},Uphokera %{amount} %{token_symbol} kula %{tx_sender_information} %{timestamp} Kwenda %{tx_recipient_information}. Sazoro %{balance} %{token_symbol},Iyudo %{amount} %{token_symbol} kowuok kuom %{tx_sender_information} %{timestamp} odhi ne %{tx_recipient_information}. Dong mari en %{balance} %{token_symbol},Yaargat %{amount} %{token_symbol} ira %{tx_sender_information} %{timestamp} Es %{tx_recipient_information} Balansi %{balance} %{token_symbol}
|
||||||
|
sent_tokens,Successfully sent %{amount} %{token_symbol} to %{tx_recipient_information} %{timestamp} from %{tx_sender_information} Balance %{balance} %{token_symbol},Umetuma %{amount} %{token_symbol} kwa %{tx_recipient_information} %{timestamp} kutoka kwa %{tx_sender_information} Salio %{balance} %{token_symbol},Niwatuma %{amount} %{token_symbol} kwa %{tx_recipient_information} %{timestamp} kuma %{tx_sender_information} Mbalansi %{balance} %{token_symbol}.,Watuma %{amount} %{token_symbol} kwe %{tx_recipient_information} %{timestamp} kuuma %{tx_sender_information} Watigaria %{balance} %{token_symbol},Uhuma %{amount} %{token_symbol} kwa %{tx_recipient_information} %{timestamp} kula %{tx_sender_information} Sazoro %{balance} %{token_symbol},Ioro %{amount} %{token_symbol} ne %{tx_recipient_information} %{timestamp} kowuok kuom %{tx_sender_information}. Dong mari en %{balance} %{token_symbol},yaergat %{amount} %{token_symbol} Es %{tx_recipient_information} %{timestamp} ira %{tx_sender_information} Balansi hareetin %{balance} %{token_symbol}
|
||||||
|
terms,By using the service you agree to the terms and conditions at http://grassecon.org/tos,Kwa kutumia hii huduma umekubali sheria na masharti yafuatayo http://grassecon.org/tos,Kwa kutumia mutandao uu niwetikilana na miyao na masharti ma http://grassecon.org/tos,"Kuhuthira mitambo ino , niuraetekania na mawatho na mutaratara wa http://grassecon.org/tos","Kuhumira huduma,Ukubali sheria na malagizo http://grassecon.org/tos ",Kuom tiyo gi huduma ni iyie chike kod weche mantie http://grassecon.org/tos,Oja service tun tumiith yaayuu kubalt one chuf at http://grassecon.org/tos
|
||||||
|
upsell_unregistered_recipient,%{tx_sender_information} tried to send you %{token_symbol}. Dial *384*96# on Safaricom and *483*96# on others For help %{support_phone},%{tx_sender_information} amejaribu kutuma %{token_symbol} na hujasajili. Bonyeza*384*96# Saf au*483*46# kwa mitandao tofauti. Usaidizi %{support_phone},%{tx_sender_information} niwatata kuutumia %{token_symbol} lakini ndwimwandikithye. Safaricom kuna *384*96# laini ingi *483*96# Utethyo %{support_phone},%{tx_sender_information} ekugeretie gugutumira %{token_symbol} no ndeyandikithetie. Hihinya *384*96# he Safaricom na *483*96# mitambo ingi. Uteithio %{support_phone},%{tx_sender_information} Yuhuma %{token_symbol} Kudzasajiliwa. Humira hopya *384*96# Safaricom au *483*96# mtandao mnjine. Kuvizwa %{support_phone},%{tx_sender_information} otemo oro ni %{token_symbol} to pok ondiki. Tiyo go dii *384*96# Safaricom gi *483*96# e netwak mamoko. E kony %{support_phone},%{tx_sender_information} yaa si ergu jariib %{token_symbol} ammo atin insajilan.Tumiitu kuches *384*96# Safaricom *483*96# dibii Qarqars %{support_phone}
|
||||||
|
pin_reset_initiated,%{pin_initiator} has sent a request to initiate your PIN reset,%{pin_initiator} ametuma ombi la kubadilisha PIN yako,%{pin_initiator} niwatuma wendi waku wa kwambiisya kusovya PIN yaku,%{pin_initiator} Niatuma ihoya ria guchengia PIN yaku,%{pin_initiator} yuhuma voyo kurekebisha piniyo.,%{pin_initiator} ooro kwayo mar loko nambani mopondo,%{pin_initiator} pin Tate badilishadu feet
|
|
1098
apps/cic-ussd/cic_ussd/data/sys/ussd.csv
Normal file
1098
apps/cic-ussd/cic_ussd/data/sys/ussd.csv
Normal file
File diff suppressed because it is too large
Load Diff
@ -52,4 +52,5 @@ class UnknownUssdRecipient(Exception):
|
|||||||
"""Raised when a recipient of a transaction is not known to the ussd application."""
|
"""Raised when a recipient of a transaction is not known to the ussd application."""
|
||||||
|
|
||||||
|
|
||||||
|
class MaxRetryReached(Exception):
|
||||||
|
"""Raised when the maximum number of retries defined for polling for the availability of a resource."""
|
||||||
|
@ -31,7 +31,8 @@ from cic_ussd.cache import cache_data_key, cache_data, get_cached_data
|
|||||||
from cic_ussd.db.models.account import Account
|
from cic_ussd.db.models.account import Account
|
||||||
from cic_ussd.metadata import PersonMetadata
|
from cic_ussd.metadata import PersonMetadata
|
||||||
from cic_ussd.phone_number import Support
|
from cic_ussd.phone_number import Support
|
||||||
from cic_ussd.processor.util import parse_person_metadata, ussd_menu_list, wait_for_session_data
|
from cic_ussd.processor.poller import wait_for_session_data
|
||||||
|
from cic_ussd.processor.util import parse_person_metadata, ussd_menu_list
|
||||||
from cic_ussd.session.ussd_session import save_session_data
|
from cic_ussd.session.ussd_session import save_session_data
|
||||||
from cic_ussd.state_machine.logic.language import preferred_langauge_from_selection
|
from cic_ussd.state_machine.logic.language import preferred_langauge_from_selection
|
||||||
from cic_ussd.translation import translation_for
|
from cic_ussd.translation import translation_for
|
||||||
|
104
apps/cic-ussd/cic_ussd/processor/poller.py
Normal file
104
apps/cic-ussd/cic_ussd/processor/poller.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
from queue import Queue
|
||||||
|
from typing import Callable, Dict, Optional, Tuple, Union
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
from cic_types.condiments import MetadataPointer
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from cic_ussd.cache import cache_data_key, get_cached_data
|
||||||
|
from cic_ussd.error import MaxRetryReached
|
||||||
|
|
||||||
|
|
||||||
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
# adapted from https://github.com/justiniso/polling/blob/master/polling.py
|
||||||
|
# opted not to use the package to reduce dependency
|
||||||
|
def poller(args: Optional[Tuple],
|
||||||
|
interval: int,
|
||||||
|
kwargs: Optional[Dict],
|
||||||
|
max_retry: int,
|
||||||
|
target: Callable[..., Union[Dict, str]]):
|
||||||
|
""""""
|
||||||
|
collected_values: list = []
|
||||||
|
expected_value = None
|
||||||
|
tries = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if tries >= max_retry:
|
||||||
|
raise MaxRetryReached(collected_values, expected_value)
|
||||||
|
try:
|
||||||
|
if args:
|
||||||
|
value = target(*args)
|
||||||
|
elif kwargs:
|
||||||
|
value = target(**kwargs)
|
||||||
|
else:
|
||||||
|
value = target()
|
||||||
|
expected_value = value
|
||||||
|
except () as error:
|
||||||
|
expected_value = error
|
||||||
|
else:
|
||||||
|
if bool(value) or value == {}:
|
||||||
|
logg.debug(f'Resource: {expected_value} now available.')
|
||||||
|
break
|
||||||
|
collected_values.append(expected_value)
|
||||||
|
logg.debug(f'Collected values are: {collected_values}')
|
||||||
|
tries += 1
|
||||||
|
time.sleep(interval)
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_cache(identifier: Union[list, bytes],
|
||||||
|
resource_name: str,
|
||||||
|
salt: MetadataPointer,
|
||||||
|
interval: int = 1,
|
||||||
|
max_retry: int = 5):
|
||||||
|
"""
|
||||||
|
:param identifier:
|
||||||
|
:type identifier:
|
||||||
|
:param interval:
|
||||||
|
:type interval:
|
||||||
|
:param resource_name:
|
||||||
|
:type resource_name:
|
||||||
|
:param salt:
|
||||||
|
:type salt:
|
||||||
|
:param max_retry:
|
||||||
|
:type max_retry:
|
||||||
|
:return:
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
|
key: str = cache_data_key(identifier=identifier, salt=salt)
|
||||||
|
logg.debug(f'Polling for resource: {resource_name} at: {key} every: {interval} second(s) for {max_retry} seconds.')
|
||||||
|
poller(args=(key,), interval=interval, kwargs=None, max_retry=max_retry, target=get_cached_data)
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_session_data(resource_name: str,
|
||||||
|
session_data_key: str,
|
||||||
|
ussd_session: dict,
|
||||||
|
interval: int = 1,
|
||||||
|
max_retry: int = 5):
|
||||||
|
"""
|
||||||
|
:param interval:
|
||||||
|
:type interval:
|
||||||
|
:param resource_name:
|
||||||
|
:type resource_name:
|
||||||
|
:param session_data_key:
|
||||||
|
:type session_data_key:
|
||||||
|
:param ussd_session:
|
||||||
|
:type ussd_session:
|
||||||
|
:param max_retry:
|
||||||
|
:type max_retry:
|
||||||
|
:return:
|
||||||
|
:rtype:
|
||||||
|
"""
|
||||||
|
# poll for data element first
|
||||||
|
logg.debug(f'Data poller with max retry at: {max_retry}. Checking for every: {interval} seconds.')
|
||||||
|
poller(args=('data',), interval=interval, kwargs=None, max_retry=max_retry, target=ussd_session.get)
|
||||||
|
|
||||||
|
# poll for session data element
|
||||||
|
get_session_data = ussd_session.get('data').get
|
||||||
|
logg.debug(f'Session data poller for: {resource_name} with max retry at: {max_retry}. Checking for every: {interval} seconds.')
|
||||||
|
poller(args=(session_data_key,), interval=interval, kwargs=None, max_retry=max_retry, target=get_session_data)
|
||||||
|
|
@ -102,77 +102,3 @@ def ussd_menu_list(fallback: str, menu_list: list, split: int = 3) -> List[str]:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
menu_list_reprs.append(fallback)
|
menu_list_reprs.append(fallback)
|
||||||
return menu_list_reprs
|
return menu_list_reprs
|
||||||
|
|
||||||
|
|
||||||
def wait_for_cache(identifier: Union[list, bytes], resource_name: str, salt: MetadataPointer, interval: int = 1, max_retry: int = 5):
|
|
||||||
"""
|
|
||||||
:param identifier:
|
|
||||||
:type identifier:
|
|
||||||
:param interval:
|
|
||||||
:type interval:
|
|
||||||
:param resource_name:
|
|
||||||
:type resource_name:
|
|
||||||
:param salt:
|
|
||||||
:type salt:
|
|
||||||
:param max_retry:
|
|
||||||
:type max_retry:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
key = cache_data_key(identifier=identifier, salt=salt)
|
|
||||||
resource = get_cached_data(key)
|
|
||||||
counter = 0
|
|
||||||
while resource is None:
|
|
||||||
logg.debug(f'Waiting for: {resource_name} at: {key}. Checking after: {interval} ...')
|
|
||||||
time.sleep(interval)
|
|
||||||
counter += 1
|
|
||||||
resource = get_cached_data(key)
|
|
||||||
if resource is not None:
|
|
||||||
logg.debug(f'{resource_name} now available.')
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if counter == max_retry:
|
|
||||||
logg.debug(f'Could not find: {resource_name} within: {max_retry}')
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
def wait_for_session_data(resource_name: str, session_data_key: str, ussd_session: dict, interval: int = 1, max_retry: int = 5):
|
|
||||||
"""
|
|
||||||
:param interval:
|
|
||||||
:type interval:
|
|
||||||
:param resource_name:
|
|
||||||
:type resource_name:
|
|
||||||
:param session_data_key:
|
|
||||||
:type session_data_key:
|
|
||||||
:param ussd_session:
|
|
||||||
:type ussd_session:
|
|
||||||
:param max_retry:
|
|
||||||
:type max_retry:
|
|
||||||
:return:
|
|
||||||
:rtype:
|
|
||||||
"""
|
|
||||||
data = ussd_session.get('data')
|
|
||||||
data_poller = 0
|
|
||||||
while not data:
|
|
||||||
logg.debug(f'Waiting for data object on ussd session: {ussd_session.get("external_session_id")}')
|
|
||||||
logg.debug(f'Data poller at: {data_poller}. Checking again after: {interval} secs...')
|
|
||||||
time.sleep(interval)
|
|
||||||
data_poller += 1
|
|
||||||
if data:
|
|
||||||
logg.debug(f'Data object found, proceeding to poll for: {session_data_key}')
|
|
||||||
break
|
|
||||||
if data:
|
|
||||||
session_data_poller = 0
|
|
||||||
session_data = data.get(session_data_key)
|
|
||||||
while not session_data_key:
|
|
||||||
logg.debug(
|
|
||||||
f'Session data poller at: {data_poller} with max retry at: {max_retry}. Checking again after: {interval} secs...')
|
|
||||||
time.sleep(interval)
|
|
||||||
session_data_poller += 1
|
|
||||||
|
|
||||||
if session_data:
|
|
||||||
logg.debug(f'{resource_name} now available.')
|
|
||||||
break
|
|
||||||
|
|
||||||
elif session_data_poller >= max_retry:
|
|
||||||
logg.debug(f'Could not find data object within: {max_retry}')
|
|
||||||
|
@ -11,7 +11,7 @@ from sqlalchemy.orm.session import Session
|
|||||||
# local imports
|
# local imports
|
||||||
from cic_ussd.cache import cache_data_key, get_cached_data
|
from cic_ussd.cache import cache_data_key, get_cached_data
|
||||||
from cic_ussd.db.models.account import Account
|
from cic_ussd.db.models.account import Account
|
||||||
from cic_ussd.processor.util import wait_for_cache, wait_for_session_data
|
from cic_ussd.processor.poller import wait_for_cache, wait_for_session_data
|
||||||
from cic_ussd.session.ussd_session import save_session_data
|
from cic_ussd.session.ussd_session import save_session_data
|
||||||
from cic_ussd.translation import Languages
|
from cic_ussd.translation import Languages
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from cic_ussd.db.models.account import Account
|
|||||||
from cic_ussd.db.models.base import SessionBase
|
from cic_ussd.db.models.base import SessionBase
|
||||||
from cic_ussd.db.enum import AccountStatus
|
from cic_ussd.db.enum import AccountStatus
|
||||||
from cic_ussd.encoder import create_password_hash, check_password_hash
|
from cic_ussd.encoder import create_password_hash, check_password_hash
|
||||||
from cic_ussd.processor.util import wait_for_session_data
|
from cic_ussd.processor.poller import wait_for_session_data
|
||||||
from cic_ussd.session.ussd_session import create_or_update_session, persist_ussd_session
|
from cic_ussd.session.ussd_session import create_or_update_session, persist_ussd_session
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from sqlalchemy.orm.session import Session
|
|||||||
# local imports
|
# local imports
|
||||||
from cic_ussd.account.tokens import set_active_token
|
from cic_ussd.account.tokens import set_active_token
|
||||||
from cic_ussd.db.models.account import Account
|
from cic_ussd.db.models.account import Account
|
||||||
from cic_ussd.processor.util import wait_for_session_data
|
from cic_ussd.processor.poller import wait_for_session_data
|
||||||
from cic_ussd.session.ussd_session import save_session_data
|
from cic_ussd.session.ussd_session import save_session_data
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from cic_ussd.cache import Cache, cache_data, cache_data_key, get_cached_data
|
|||||||
from cic_ussd.account.chain import Chain
|
from cic_ussd.account.chain import Chain
|
||||||
from cic_ussd.db.models.base import SessionBase
|
from cic_ussd.db.models.base import SessionBase
|
||||||
from cic_ussd.db.models.account import Account
|
from cic_ussd.db.models.account import Account
|
||||||
from cic_ussd.processor.util import wait_for_cache
|
from cic_ussd.processor.poller import wait_for_cache
|
||||||
from cic_ussd.account.statement import filter_statement_transactions
|
from cic_ussd.account.statement import filter_statement_transactions
|
||||||
from cic_ussd.account.transaction import transaction_actors
|
from cic_ussd.account.transaction import transaction_actors
|
||||||
from cic_ussd.account.tokens import (collate_token_metadata,
|
from cic_ussd.account.tokens import (collate_token_metadata,
|
||||||
|
@ -13,4 +13,4 @@ port =
|
|||||||
ssl =
|
ssl =
|
||||||
|
|
||||||
[system]
|
[system]
|
||||||
guardians_file = var/lib/sys/guardians.txt
|
guardians_file = cic_ussd/data/sys/guardians.txt
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[PIP]
|
[PIP]
|
||||||
extra_index_host = pip.grassrootseconomics.net
|
extra_index_host = pip.grassrootseconomics.net
|
||||||
extra_index_port =
|
extra_index_port = 8433
|
||||||
extra_index_path = /
|
extra_index_path = /
|
||||||
extra_index_proto = https
|
extra_index_proto = https
|
||||||
|
@ -8,4 +8,4 @@ states=states/
|
|||||||
transitions=transitions/
|
transitions=transitions/
|
||||||
|
|
||||||
[system]
|
[system]
|
||||||
guardians_file = var/lib/sys/guardians.txt
|
guardians_file = cic_ussd/data/sys/guardians.txt
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[PIP]
|
[PIP]
|
||||||
extra_index_host = pip.grassrootseconomics.net
|
extra_index_host = pip.grassrootseconomics.net
|
||||||
extra_index_port =
|
extra_index_port = 8433
|
||||||
extra_index_path = /
|
extra_index_path = /
|
||||||
extra_index_proto = https
|
extra_index_proto = https
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
[locale]
|
[locale]
|
||||||
fallback=sw
|
fallback=sw
|
||||||
path=
|
path=cic_ussd/data/locale/
|
||||||
file_builders=var/lib/sys/
|
file_builders=cic_ussd/data/sys/
|
||||||
|
|
||||||
[schema]
|
[schema]
|
||||||
file_path = data/schema
|
file_path = data/schema
|
||||||
|
|
||||||
[languages]
|
[languages]
|
||||||
file = var/lib/sys/languages.json
|
file = cic_ussd/data/sys/languages.json
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[ussd]
|
[ussd]
|
||||||
menu_file=cic_ussd/db/ussd_menu.json
|
menu_file=cic_ussd/data/sys/ussd_menu.json
|
||||||
service_code=*483*46#,*483*061#,*384*96#
|
service_code=*483*46#,*483*061#,*384*96#
|
||||||
user =
|
user =
|
||||||
pass =
|
pass =
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
[locale]
|
[locale]
|
||||||
fallback=sw
|
fallback=sw
|
||||||
path=var/lib/locale/
|
path=cic_ussd/data/locale/
|
||||||
file_builders=var/lib/sys/
|
file_builders=cic_ussd/data/sys/
|
||||||
|
|
||||||
[schema]
|
[schema]
|
||||||
file_path = /usr/local/lib/python3.8/site-packages/cic_translations/data/schema
|
file_path = /usr/local/lib/python3.8/site-packages/cic_translations/data/schema
|
||||||
|
|
||||||
[languages]
|
[languages]
|
||||||
file = var/lib/sys/languages.json
|
file = cic_ussd/data/sys/languages.json
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[ussd]
|
[ussd]
|
||||||
menu_file=data/ussd_menu.json
|
menu_file=cic_ussd/data/sys/ussd_menu.json
|
||||||
service_code=*483*46#,*483*061#,*384*96#
|
service_code=*483*46#,*483*061#,*384*96#
|
||||||
user =
|
user =
|
||||||
pass =
|
pass =
|
||||||
|
@ -3,12 +3,10 @@ ARG DOCKER_REGISTRY="registry.gitlab.com/grassrootseconomics"
|
|||||||
FROM $DOCKER_REGISTRY/cic-base-images:python-3.8.6-dev-e8eb2ee2
|
FROM $DOCKER_REGISTRY/cic-base-images:python-3.8.6-dev-e8eb2ee2
|
||||||
|
|
||||||
RUN apt-get install -y redis-server
|
RUN apt-get install -y redis-server
|
||||||
|
|
||||||
# create secrets directory
|
# create secrets directory
|
||||||
RUN mkdir -vp pgp/keys
|
RUN mkdir -vp pgp/keys
|
||||||
|
|
||||||
# create application directory
|
|
||||||
RUN mkdir -vp cic-ussd
|
|
||||||
RUN mkdir -vp data
|
|
||||||
|
|
||||||
ARG EXTRA_PIP_INDEX_URL=https://pip.grassrootseconomics.net
|
ARG EXTRA_PIP_INDEX_URL=https://pip.grassrootseconomics.net
|
||||||
ARG EXTRA_PIP_ARGS=""
|
ARG EXTRA_PIP_ARGS=""
|
||||||
@ -25,7 +23,8 @@ RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN python setup.py install
|
RUN python setup.py install
|
||||||
|
|
||||||
COPY cic_ussd/db/ussd_menu.json data/
|
# create local files directory
|
||||||
|
RUN mkdir -vp cic_ussd/data/locale
|
||||||
|
|
||||||
COPY docker/*.sh ./
|
COPY docker/*.sh ./
|
||||||
RUN chmod +x /root/*.sh
|
RUN chmod +x /root/*.sh
|
||||||
|
69
apps/cic-ussd/tests/cic_ussd/processor/test_poller.py
Normal file
69
apps/cic-ussd/tests/cic_ussd/processor/test_poller.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
import pytest
|
||||||
|
from cic_types.condiments import MetadataPointer
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from cic_ussd.cache import cache_data, cache_data_key, get_cached_data
|
||||||
|
from cic_ussd.error import MaxRetryReached
|
||||||
|
from cic_ussd.processor.poller import poller, wait_for_cache, wait_for_session_data
|
||||||
|
|
||||||
|
# test imports
|
||||||
|
|
||||||
|
|
||||||
|
def test_poller(activated_account, caplog, init_cache, token_symbol):
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||||
|
key = cache_data_key(identifier, MetadataPointer.TOKEN_ACTIVE)
|
||||||
|
with pytest.raises(MaxRetryReached) as error:
|
||||||
|
interval = 1
|
||||||
|
max_retry = 3
|
||||||
|
collected_values = [None, None, None]
|
||||||
|
poller(args=(key,), interval=interval, kwargs=None, max_retry=max_retry, target=get_cached_data)
|
||||||
|
assert str(error.value) == str(MaxRetryReached(collected_values, None))
|
||||||
|
cache_data(key, token_symbol)
|
||||||
|
poller(args=(key,), interval=interval, kwargs=None, max_retry=max_retry, target=get_cached_data)
|
||||||
|
assert f'Resource: {token_symbol} now available.' in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_wait_for_cache(activated_account, caplog, init_cache, token_symbol):
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
identifier = bytes.fromhex(activated_account.blockchain_address)
|
||||||
|
key = cache_data_key(identifier, MetadataPointer.TOKEN_ACTIVE)
|
||||||
|
cache_data(key, token_symbol)
|
||||||
|
interval = 1
|
||||||
|
max_retry = 3
|
||||||
|
resource_name = 'Active Token'
|
||||||
|
wait_for_cache(identifier, resource_name, MetadataPointer.TOKEN_ACTIVE, interval, max_retry)
|
||||||
|
assert f'Polling for resource: {resource_name} at: {key} every: {interval} second(s) for {max_retry} seconds.' in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_wait_for_session_data(activated_account, caplog, generic_ussd_session):
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
generic_ussd_session.__delitem__('data')
|
||||||
|
interval = 1
|
||||||
|
max_retry = 3
|
||||||
|
collected_values = [None, None, None]
|
||||||
|
resource_name = 'Foo Data'
|
||||||
|
session_data_key = 'foo'
|
||||||
|
with pytest.raises(MaxRetryReached) as error:
|
||||||
|
wait_for_session_data(resource_name, session_data_key, generic_ussd_session, interval, max_retry)
|
||||||
|
assert str(error.value) == str(MaxRetryReached(collected_values, None))
|
||||||
|
assert f'Data poller with max retry at: {max_retry}. Checking for every: {interval} seconds.' in caplog.text
|
||||||
|
generic_ussd_session['data'] = {}
|
||||||
|
with pytest.raises(MaxRetryReached) as error:
|
||||||
|
collected_values = [None, None, None]
|
||||||
|
wait_for_session_data(resource_name, session_data_key, generic_ussd_session, interval, max_retry)
|
||||||
|
assert f'Data poller with max retry at: {max_retry}. Checking for every: {interval} seconds.' in caplog.text
|
||||||
|
assert f'Session data poller for: {resource_name} with max retry at: {max_retry}. Checking for every: {interval} seconds.' in caplog.text
|
||||||
|
assert str(error.value) == str(MaxRetryReached(collected_values, None))
|
||||||
|
expected_value = 'bar'
|
||||||
|
generic_ussd_session['data'] = {'foo': expected_value}
|
||||||
|
wait_for_session_data(resource_name, session_data_key, generic_ussd_session, interval, max_retry)
|
||||||
|
assert f'Data poller with max retry at: {max_retry}. Checking for every: {interval} seconds.' in caplog.text
|
||||||
|
assert f'Session data poller for: {resource_name} with max retry at: {max_retry}. Checking for every: {interval} seconds.' in caplog.text
|
||||||
|
assert f'Resource: {expected_value} now available.' in caplog.text
|
@ -9,7 +9,7 @@ from cic_ussd.notifications import Notifier
|
|||||||
|
|
||||||
@pytest.mark.parametrize("key, preferred_language, recipient, expected_message", [
|
@pytest.mark.parametrize("key, preferred_language, recipient, expected_message", [
|
||||||
("ussd.exit", "en", "+254712345678", "END Thank you for using the service."),
|
("ussd.exit", "en", "+254712345678", "END Thank you for using the service."),
|
||||||
("ussd.exit", "sw", "+254712345678", "END Asante kwa kutumia huduma.")
|
("ussd.exit", "sw", "+254712345678", "END Asante kwa kutumia huduma")
|
||||||
])
|
])
|
||||||
def test_send_sms_notification(celery_session_worker,
|
def test_send_sms_notification(celery_session_worker,
|
||||||
expected_message,
|
expected_message,
|
||||||
|
@ -17,5 +17,5 @@ def test_translation_for(set_locale_files):
|
|||||||
key='ussd.exit_invalid_request',
|
key='ussd.exit_invalid_request',
|
||||||
preferred_language='sw'
|
preferred_language='sw'
|
||||||
)
|
)
|
||||||
assert swahili_translation == 'END Chaguo si sahihi.'
|
assert swahili_translation == 'END Chaguo si sahihi'
|
||||||
assert english_translation == 'END Invalid request.'
|
assert english_translation == 'END Invalid request.'
|
||||||
|
@ -1 +0,0 @@
|
|||||||
+254700000000
|
|
@ -1,19 +0,0 @@
|
|||||||
keys,en,sw
|
|
||||||
female,Female,Mwanamke
|
|
||||||
from,From,Kutoka kwa
|
|
||||||
male,Male,Mwanaume
|
|
||||||
not_provided,Not provided,Haijawekwa
|
|
||||||
no_language_list,No language list,Hamna lugha ya kuchagua
|
|
||||||
no_transaction_history,No transaction history,Hamna ripoti ya matumizi
|
|
||||||
no_tokens_list,No tokens to list,Hamna sarafu nyingine
|
|
||||||
other,Other,Nyingine
|
|
||||||
received,Received,Ulipokea
|
|
||||||
sent,Sent,Ulituma
|
|
||||||
to,To,Kwa
|
|
||||||
guardians_list_header,Your set guardians are:,Walinzi uliowaongeza ni:
|
|
||||||
no_guardians_list,No guardians set,Hamna walinzi walioongezwa
|
|
||||||
error.no_phone_number_provided,No phone number was provided.,Namabari ya simu haijawekwa.
|
|
||||||
error.no_matching_account,The number provided is not registered.,Nambari uliyoweka haijasajiliwa.
|
|
||||||
error.is_initiator,Phone number cannot be your own.,Nambari yafaa kuwa tofauti na yako.
|
|
||||||
error.is_existent_guardian,This phone number is is already added as a guardian.,Namabari hii tayari imeongezwa kama mlinzi wa nambari ya siri.
|
|
||||||
error.is_not_existent_guardian,Phone number not set as PIN reset guardian.,Nambari hii haijaongezwa kama mlinzi wa nambari ya siri.
|
|
|
@ -1,7 +0,0 @@
|
|||||||
keys,en,sw
|
|
||||||
account_successfully_created,You have been registered on Sarafu Network! To use dial *384*96# on Safaricom and *483*96# on other networks. For help %{support_phone}.,Umesajiliwa kwa huduma ya Sarafu! Kutumia bonyeza *384*96# Safaricom ama *483*46# kwa utandao tofauti. Kwa Usaidizi %{support_phone}.
|
|
||||||
received_tokens,Successfully received %{amount} %{token_symbol} from %{tx_sender_information} %{timestamp} to %{tx_recipient_information}. New balance is %{balance} %{token_symbol}.,Umepokea %{amount} %{token_symbol} kutoka kwa %{tx_sender_information} %{timestamp} ikapokewa na %{tx_recipient_information}. Salio lako ni %{balance} %{token_symbol}.
|
|
||||||
sent_tokens,Successfully sent %{amount} %{token_symbol} to %{tx_recipient_information} %{timestamp} from %{tx_sender_information}. New balance is %{balance} %{token_symbol}.,Umetuma %{amount} %{token_symbol} kwa %{tx_recipient_information} %{timestamp} kutoka kwa %{tx_sender_information}. Salio lako ni %{balance} %{token_symbol}.
|
|
||||||
terms,"By using the service, you agree to the terms and conditions at http://grassecon.org/tos","Kwa kutumia hii huduma, umekubali sheria na masharti yafuatayo http://grassecon.org/tos"
|
|
||||||
upsell_unregistered_recipient,%{tx_sender_information} tried to send you %{token_symbol} but you are not registered. To use dial *384*96# on Safaricom and *483*96# on other networks. For help %{support_phone}.,%{tx_sender_information} amejaribu kukutumia %{token_symbol} lakini hujasajili. Kutumia bonyeza *384*96# Safaricom ama *483*46# kwa utandao tofauti. Kwa Usaidizi %{support_phone}.
|
|
||||||
pin_reset_initiated,%{pin_initiator} has sent a request to initiate your PIN reset.,%{pin_initiator} ametuma ombi la kubadilisha PIN yako.
|
|
|
@ -1,866 +0,0 @@
|
|||||||
keys,en,sw,kam,kik,miji,luo,bor
|
|
||||||
initial_language_selection,"CON Welcome to Sarafu Network
|
|
||||||
%{first_language_set}
|
|
||||||
|
|
||||||
11. Next
|
|
||||||
00. Exit","CON Karibu Sarafu Network
|
|
||||||
%{first_language_set}
|
|
||||||
|
|
||||||
11. Mbele
|
|
||||||
00. Ondoka","CON Kalivu Network ya Sarafu
|
|
||||||
1. English
|
|
||||||
2. Kiswahili
|
|
||||||
3. Kikamba
|
|
||||||
3. Help","CON Karibu Sarafu Network
|
|
||||||
1. Githungu
|
|
||||||
2. Githweri
|
|
||||||
3. Uteithio","CON Karibu Sarafu Network
|
|
||||||
1. Chizungu
|
|
||||||
2. Chiswahili
|
|
||||||
3. Avizwa","CON Machiegni e network mar Sarafu
|
|
||||||
1. Dho Ngere
|
|
||||||
2. Dho oswayo
|
|
||||||
3. Kony","CON Karibu Sarafu Network
|
|
||||||
1. Afaan ferenji
|
|
||||||
2. Afaan kiswahili
|
|
||||||
3. Qarqars"
|
|
||||||
initial_pin_entry,CON Please enter a new four number PIN for your account.,CON Tafadhali weka pin mpya yenye nambari nne kwa akaunti yako,CON Tafadhali ikia pin yumbya ila ina namba inya kinanduni chaku,CON Ekera namba yaku ya thiri njeru ena numba enna.,CON Ika piniyo ya namba Ne kwa akaunti Yakwako.,CON Kiyie to ket namba ni mopondo e akaont ni.,CON Tafadhal pin hareti kekhae ka namba afuri fulaa akaunti kake
|
|
||||||
initial_pin_confirmation,CON Enter your four number PIN again,CON Weka PIN yako tena,CON Ikia PIN yaku ingi,CON Ekera namba yaku ya thiri ringi,CON Uyira Kwika pin kaheri.,CON Ket namba ni mopondo kendo,CON Mar dibii pin kekhae
|
|
||||||
enter_given_name,"CON Enter first name
|
|
||||||
0. Back",CON Weka jina lako la kwanza,CON Ikia isyitwa yaku ya mbee,CON Ekera retwa rwaku ria mbere,CON Ika dzinaro rakwanza.,CON Ket nyingi mokwongo,CON Makhaa kake ka karaa kor
|
|
||||||
enter_family_name,"CON Enter family name
|
|
||||||
0. Back","CON Weka jina lako la mwisho
|
|
||||||
0. Rudi","CON Ikia isyitwa yaku ya muthya
|
|
||||||
0. Syoka itina","CON Ekera ritwa rwaku ria mwisho
|
|
||||||
0. Coka thutha","CON Ika dzinaro ra mwisho
|
|
||||||
0. Uya Nyuma","CON Ket nyingi mogik.
|
|
||||||
0. Dog chien","CON Makhaa kake ka egee
|
|
||||||
0. Dhebii"
|
|
||||||
enter_date_of_birth,"CON Enter year of birth
|
|
||||||
0. Back","CON Weka mwaka wa kuzaliwa
|
|
||||||
0. Rudi","CON Ikia mwaka wa kusyawa
|
|
||||||
0. Syoka itina","CON Ekera mwaka waku wa guciarwo
|
|
||||||
0. Coka thutha","CON Ika mwaka wakuvyalwa
|
|
||||||
0. Uya Nyuma","CON Ket iki mar nyuol
|
|
||||||
0. Dog chien","CON Gan kake ka athdalat kor
|
|
||||||
0. Dheebi"
|
|
||||||
enter_gender,"CON Enter gender
|
|
||||||
1. Male
|
|
||||||
2. Female
|
|
||||||
3. Other
|
|
||||||
0. Back","CON Weka jinsia yako
|
|
||||||
1. Mwanaume
|
|
||||||
2. Mwanamke
|
|
||||||
3. Nyngine
|
|
||||||
0. Rudi","CON Ikia gender yaku
|
|
||||||
1. Mundume
|
|
||||||
2. Mundumuka
|
|
||||||
3. Ingi
|
|
||||||
0. Syoka itina","CON We mudurume kana mutumia
|
|
||||||
1.Mudurume
|
|
||||||
2. Mutumia
|
|
||||||
3. Ingi
|
|
||||||
0. Coka thutha","CON Ika kala Umulume ama Umuche au vingine.
|
|
||||||
1. Mulume
|
|
||||||
2. Muche
|
|
||||||
3. Vinjine
|
|
||||||
0. Uya Nyuma","CON Ket kit chwech mari
|
|
||||||
1. Dichuo
|
|
||||||
2. Dhako
|
|
||||||
3. Moko
|
|
||||||
0. Dog chien","CON Athin Dir mo Dubr
|
|
||||||
1. Dir
|
|
||||||
2. Dubr
|
|
||||||
3. Ka dibii
|
|
||||||
0. Dheebi"
|
|
||||||
enter_location,"CON Enter your location,
|
|
||||||
0. Back","CON Weka eneo lako
|
|
||||||
0. Rudi","CON Ikia utui waku kana location
|
|
||||||
0. Syoka itina","CON Ekera kuria uumete
|
|
||||||
0. Coka thutha","CON Ika enero wombolako.
|
|
||||||
0. Uya nyuma","CON Ket kumaidake
|
|
||||||
0. Dog chien","CON Fulaa athin kubat kor
|
|
||||||
0. Dhebii"
|
|
||||||
enter_products,"CON Please enter a product or service you offer
|
|
||||||
0. Back","CON Weka bidhaa ama huduma unauza
|
|
||||||
0. Rudi","CON Ikia syindu kana huduma ila unenganae
|
|
||||||
0. Syoka itina","CON Ekera indo kana wira uria urendia
|
|
||||||
0. Coka thutha","CON Ika Viya ama utu uhendao
|
|
||||||
0. Uya Nyuma","CON Ket gima iuso kata tich mitimo
|
|
||||||
0. Dog chien","CON Waan gurgurt okan namaa kenit khes khae
|
|
||||||
0. Dheebi"
|
|
||||||
start,"CON Balance %{account_balance} %{account_token_name}
|
|
||||||
1. Send
|
|
||||||
2. My Sarafu
|
|
||||||
3. My Account
|
|
||||||
4. Help","CON Salio %{account_balance} %{account_token_name}
|
|
||||||
1. Tuma
|
|
||||||
2. Sarafu yangu
|
|
||||||
3. Akaunti yangu
|
|
||||||
4. Usaidizi","CON Mbalansi kana utyalyo %{account_balance} %{account_token_name}
|
|
||||||
1. Tuma
|
|
||||||
2. Kinandu chakwa
|
|
||||||
3. Utethyo","CON Matigari %{account_balance} %{account_token_name}
|
|
||||||
1. Tuma
|
|
||||||
2. Akaunti yaku
|
|
||||||
3. Uteithio","CON Sazo %{account_balance} %{account_token_name}
|
|
||||||
1. Huma
|
|
||||||
2. Akaunti yangu
|
|
||||||
3. Avizwa","CON Ma Odong' %{account_balance} %{account_token_name}
|
|
||||||
1. Or
|
|
||||||
2. Akaont na
|
|
||||||
3. Kony","CON Salio %{account_balance} %{account_token_name}
|
|
||||||
1. Erg
|
|
||||||
2. Akaunti khiy
|
|
||||||
3. Qarqars"
|
|
||||||
enter_transaction_recipient,"CON Enter phone number
|
|
||||||
0. Back","CON Weka nambari ya simu
|
|
||||||
0. Rudi","CON Ikia namba ya simu
|
|
||||||
0. Syoka itina","CON Ikira namba ya thimu
|
|
||||||
0. Coka thutha","CON Ika namba yasimu.
|
|
||||||
0. Uya Nyuma","CON Ket nambani mar simu
|
|
||||||
0. Dog chien","CON Namba ta simuu kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
enter_transaction_amount,"CON Enter amount
|
|
||||||
0. Back","CON Weka kiwango
|
|
||||||
0. Rudi","CON Ikia kiwango
|
|
||||||
0. Syoka itina","CON Ikira muigana
|
|
||||||
0. Coka thutha","CON Ika chaasi.
|
|
||||||
0. Uya nyuma","CON Ket giko mari
|
|
||||||
0. Dog chien","CON kiwango kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
first_account_tokens_set,"CON Choose a number or symbol from your balances:
|
|
||||||
%{first_account_tokens_set}
|
|
||||||
|
|
||||||
0. Back
|
|
||||||
11. Next
|
|
||||||
00. Exit","CON Chagua nambari au ishara kutoka kwa salio zako:
|
|
||||||
%{first_account_tokens_set}
|
|
||||||
|
|
||||||
0. Rudi
|
|
||||||
11. Mbele
|
|
||||||
00. Ondoka","Sakua Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Thi mbee
|
|
||||||
00. Syoka itina","Shaghura Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Mbere
|
|
||||||
00. Coka thutha","Tsagula Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Enderera
|
|
||||||
00. Uya Nyuma","Yier Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Nyime
|
|
||||||
00. Dog chien","Chaqui Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Dhuur
|
|
||||||
00. Dheebi"
|
|
||||||
middle_account_tokens_set,"CON Choose a number or symbol from your balances:
|
|
||||||
%{middle_account_tokens_set}
|
|
||||||
11. Next
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON Chagua nambari au ishara kutoka kwa salio zako:
|
|
||||||
%{middle_account_tokens_set}
|
|
||||||
11. Mbele
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka","Sakura Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Thi mbee
|
|
||||||
00. Syoka itina","Shaghura Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Mbere
|
|
||||||
00. Cooka thutha","Tsagula Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Enderera
|
|
||||||
00. Uya Nyuma","Yier Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Nyime
|
|
||||||
00. Dog chien","Chaqui Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
99. Dhuur
|
|
||||||
00. Dheebi"
|
|
||||||
last_account_tokens_set,"CON Choose a number or symbol from your balances:
|
|
||||||
%{last_account_tokens_set}
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON Chagua nambari au ishara kutoka kwa salio zako:
|
|
||||||
%{last_account_tokens_set}
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka","Sakura sarafu:
|
|
||||||
%{token_list}
|
|
||||||
00. Syoka itina","Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
00. Cooka thutha","Tsagula Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
00. Uya Nyuma","Yier Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
00. Dog chien","Chagua Sarafu:
|
|
||||||
%{token_list}
|
|
||||||
00. Dheebi"
|
|
||||||
token_selection_pin_authorization.first,"CON %{token_data}
|
|
||||||
Enter pin to select:","CON %{token_data}
|
|
||||||
Weka nambari ya siri kuchagua:
|
|
||||||
0. Back","%{token_info}
|
|
||||||
Sakua kwa kwikia pin yaku:
|
|
||||||
0. Syoka itina","%{token_info}
|
|
||||||
Ekera pin yaku gushaghura:
|
|
||||||
0. Cooka thutha","%{token_info}
|
|
||||||
Ika piniyo kutsagula Sarafu:
|
|
||||||
0. Uya Nyuma","%{token_info}
|
|
||||||
Ket pin ni iyier:
|
|
||||||
0. Dog chien","%{token_info}
|
|
||||||
Pin kake khai akh dibii chaguat
|
|
||||||
0. Dheebi"
|
|
||||||
token_selection_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
account_management,"CON My account
|
|
||||||
1. My profile
|
|
||||||
2. Change language
|
|
||||||
3. Check balance
|
|
||||||
4. Check statement
|
|
||||||
5. PIN options
|
|
||||||
0. Back","CON Akaunti yangu
|
|
||||||
1. Wasifu wangu
|
|
||||||
2. Chagua lugha utakayotumia
|
|
||||||
3. Angalia salio
|
|
||||||
4. Angalia taarifa ya matumizi
|
|
||||||
5. Mipangilio ya nambari ya siri
|
|
||||||
0. Rudi","CON Kinandu Chakwa
|
|
||||||
1. Profile/wasifu wakwa
|
|
||||||
2. Sakua luka ila ukatumiaa
|
|
||||||
3. Syisya Mbalansi yaku
|
|
||||||
4. Syisya welesyo wa utumii
|
|
||||||
5. Chenja namba yaku ya siri
|
|
||||||
0. Syoka itina","CON Akaunti yakwa
|
|
||||||
1. Maritwa makwa
|
|
||||||
2. Shaghura rothiumu ukuhuthira
|
|
||||||
3. Rora matigari
|
|
||||||
4. Rora rugano rwa mahuthira
|
|
||||||
5. Chengia namba ya thiri
|
|
||||||
0. Coka","CON Akaunti yangu
|
|
||||||
1. Malagizo Gangu
|
|
||||||
2. Tsagula Luga Undohumira
|
|
||||||
3. Lola Sazo
|
|
||||||
4. Lola tarifa Za Mahumizi
|
|
||||||
5. Galuza Namba Fitse
|
|
||||||
0. Uya Nyuma","CON Akaont na
|
|
||||||
1. Nyanonro mara
|
|
||||||
2. Yier dhok midwatiyogo
|
|
||||||
3. Ngi midong go
|
|
||||||
4. N'gi chal mar akaont
|
|
||||||
5. Lok nambani mopondo
|
|
||||||
0. Dog chien","CON Akaunti khiy
|
|
||||||
1. Wasifu wangu
|
|
||||||
2. Afaan dubaad chaqui
|
|
||||||
3. laali balansi
|
|
||||||
4. Angalia taarifa ya matumizi
|
|
||||||
5. Gargarch namba
|
|
||||||
0. Dheebi"
|
|
||||||
metadata_management,"CON My profile
|
|
||||||
1. Edit name
|
|
||||||
2. Edit gender
|
|
||||||
3. Edit age
|
|
||||||
4. Edit location
|
|
||||||
5. Edit products
|
|
||||||
6. View my profile
|
|
||||||
0. Back","CON Wasifu wangu
|
|
||||||
1. Weka jina
|
|
||||||
2. Weka jinsia
|
|
||||||
3. Weka umri
|
|
||||||
4. Weka eneo
|
|
||||||
5. Weka bidhaa
|
|
||||||
6. Angalia wasifu wako
|
|
||||||
0. Rudi","CON Profile/Wasifu wakwa
|
|
||||||
1. Ikia isyitwa
|
|
||||||
2. Ikia jinsia/gender yaku
|
|
||||||
3. Ikia miaka yaku
|
|
||||||
4. Ikia utui waku
|
|
||||||
5. Ikia syindu ila utesaa
|
|
||||||
6. Sisya profile/wasifu waku
|
|
||||||
0. Syoka itina","CON Maondu maku
|
|
||||||
1. Ekera ritwa
|
|
||||||
2. Ekera kana we mundurume kana mutumia
|
|
||||||
3. Ekera miaka yaku
|
|
||||||
4. Ekera kuria uikaraga
|
|
||||||
5. Ikira kiria uendagia
|
|
||||||
6. Rora maundu maku
|
|
||||||
0. Coka thutha","CON Malagizo Gangu
|
|
||||||
1. Ika dzinaro
|
|
||||||
2. Ika kala umulume ama Umuche
|
|
||||||
3. Ika umuri
|
|
||||||
4. Ika eneo
|
|
||||||
5. Ika Miyo ama viya uguzavyo
|
|
||||||
6. Lola malagizo Gangu
|
|
||||||
0. Uya nyuma","CON Wasifu wangu
|
|
||||||
1. Ket nyingi
|
|
||||||
2. Ket kit chuech mari
|
|
||||||
3. Ket iki
|
|
||||||
4. Ket kumaidake
|
|
||||||
5. Ket gikmaiuso
|
|
||||||
6. Ng'i nyanonro mara
|
|
||||||
0. Dog chien","CON Wasifu wangu
|
|
||||||
1. Maqa kekhai
|
|
||||||
2. Naam dira mo dubr
|
|
||||||
3. Gan kekhai
|
|
||||||
4. Fulaa itgalt kai
|
|
||||||
5. Mih kai
|
|
||||||
6. Angalia wasifu wako
|
|
||||||
0. Dheebi"
|
|
||||||
display_user_metadata,"CON Your details are:
|
|
||||||
Name: %{full_name}
|
|
||||||
Gender: %{gender}
|
|
||||||
Age: %{age}
|
|
||||||
Location: %{location}
|
|
||||||
You sell: %{products}
|
|
||||||
0. Back","CON Wasifu wako una maelezo yafuatayo:
|
|
||||||
Jina: %{full_name}
|
|
||||||
Jinsia: %{gender}
|
|
||||||
Umri: %{age}
|
|
||||||
Eneo: %{location}
|
|
||||||
Unauza: %{products}
|
|
||||||
0. Rudi","CON Profile/Wasifu waku wina maelesyo aa:
|
|
||||||
Isyitwa: %{full_name}
|
|
||||||
Jinsia yaku/gender: %{gender}
|
|
||||||
Miaka yaku: %{age}
|
|
||||||
Utui/location yaku: %{location}
|
|
||||||
Syindu ila uta: %{products}
|
|
||||||
0. Syoka itina","CON Maundu maku mena rugano ruru:
|
|
||||||
Maretwa: %{full_name}
|
|
||||||
Mutumia kana muthuri: %{gender}
|
|
||||||
Miaka : %{age}
|
|
||||||
Kuria uikaraga : %{location}
|
|
||||||
Kiria uendagia : %{products}
|
|
||||||
0. Coka thutha","CON Malagizo gako gana moro uthuwizirao:
|
|
||||||
Dzina: %{full_name}
|
|
||||||
Umuche ama Mulume: %{gender}
|
|
||||||
Umuri: %{age}
|
|
||||||
Umbolako: %{location}
|
|
||||||
Miyo uguzayo: %{products}
|
|
||||||
0. Uya nyuma","CON Nyanonro mari en:
|
|
||||||
Nying: %{full_name}
|
|
||||||
Kit chuech: %{gender}
|
|
||||||
Iga: %{age}
|
|
||||||
Kumidake: %{location}
|
|
||||||
Gima iuso: %{products}
|
|
||||||
0. Dog chien","CON Wasifu wako una maelezo yafuatayo:
|
|
||||||
JinaMakha: %{full_name}
|
|
||||||
Jinsia: %{gender}
|
|
||||||
Gan: %{age}
|
|
||||||
Fulaa : %{location}
|
|
||||||
Maan gurgurt: %{products}
|
|
||||||
0. Dheebi"
|
|
||||||
select_preferred_language,"CON Choose language:
|
|
||||||
%{first_language_set}
|
|
||||||
|
|
||||||
0. Back
|
|
||||||
11. Next
|
|
||||||
00. Exit","CON Chagua lugha:
|
|
||||||
%{first_language_set}
|
|
||||||
|
|
||||||
0. Rudi
|
|
||||||
11. Mbele
|
|
||||||
00. Ondoka","CON Sakua luka
|
|
||||||
1. Kisungu
|
|
||||||
2. Kiswahili
|
|
||||||
3. Kikamba
|
|
||||||
0. Syoka itina","CON Caghura ruthiomi
|
|
||||||
1. Githungu
|
|
||||||
2. Githweri
|
|
||||||
0. Coka","CON Tsagula Luga
|
|
||||||
1. Kizungu
|
|
||||||
2. Kiswahili
|
|
||||||
0. Uya nyuma","CON Yier dhok
|
|
||||||
1. Dho Ngere
|
|
||||||
2. Dho Oswayo
|
|
||||||
0. Dog chien","CON Chagua lugha
|
|
||||||
1. Afaan ferenji
|
|
||||||
2. Afaan kiswahili
|
|
||||||
0. Dheebi"
|
|
||||||
retry_pin_entry,"CON Incorrect PIN entered,please try again. You have %{remaining_attempts} attempts remaining.
|
|
||||||
0. Back","CON Nambari uliyoweka si sahihi, jaribu tena. Una majaribio %{remaining_attempts} yaliyobaki.
|
|
||||||
0. Rudi","CON Namba ila wekiya iyaile, tata kwikia ingi. Tata mala %{remaining_attempts} nimo matyele.
|
|
||||||
0. Itina","CON Namba uikirite ti njega, geria ringi.Ni maita %{remaining_attempts} matigarete.
|
|
||||||
0. Gucoka thutha","CON Nambari fitse urioika seyo, jeza kaheri. Usere Majezo %{remaining_attempts} Gaserego.
|
|
||||||
0. Uya nyuma","CON Namba miketo oknikare, tem kendo. Idong gi temo di %{remaining_attempts} modong.
|
|
||||||
0. Chien","CON Namba at keket suninit,laal amalle.Nafaas kaitdheebit %tanaataf
|
|
||||||
0. Dheebi"
|
|
||||||
pin_management,"CON Pin options
|
|
||||||
1. Change PIN
|
|
||||||
2. Reset PIN
|
|
||||||
3. Guard PIN
|
|
||||||
0. Back","CON Pin options
|
|
||||||
1. Badilisha nambari yangu ya siri
|
|
||||||
2. Tuma ombili la kubadilisha nambari ya siri
|
|
||||||
3. Linda nambari ya siri
|
|
||||||
0. Rudi",,,,,
|
|
||||||
enter_current_pin.first,"CON Enter current PIN.
|
|
||||||
0. Back","CON Weka nambari ya siri.
|
|
||||||
0. Rudi","CON Ikia namba yaku ya siri.
|
|
||||||
0. Syoka itina","CON Ekera namba ya thiri
|
|
||||||
0. Coka thutha","CON Ika namba fitse.
|
|
||||||
0. Uya Nyuma","CON Ket nambani mopondo.
|
|
||||||
0. Dog chien","CON Namba ka namii imben kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
enter_current_pin.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
enter_new_pin,"CON Enter your new four number PIN
|
|
||||||
0. Back","CON Weka nambari ya siri mpya
|
|
||||||
0. Rudi","CON Ikia namba yaku ya siri ila yumbya
|
|
||||||
0. Syoka itina","CON Ekera namba njeru ya thiri
|
|
||||||
0. coka thutha","CON Ika namba fitse mbisha
|
|
||||||
0. Uya nyuma","CON Ket namba mopondo maanyien.
|
|
||||||
0. Dog chien","CON Namba hareti ka namii imben kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
new_pin_confirmation,"CON Enter your new four number PIN again
|
|
||||||
0. Back","CON Weka nambari yako ya siri tena
|
|
||||||
0. Rudi","CON Ikia namba yaku ya siri ingi
|
|
||||||
0. Syoka itina","CON Ekera namba yaku ya thiri renge
|
|
||||||
0. Coka thutha","CON Uyira kuika lwaphiri
|
|
||||||
0. Uya Nyuma","CON Ket nambani mopondo kendo
|
|
||||||
0. Dog chien","CON Namba hareti ka namii imben kekhai amalle
|
|
||||||
0. Dheebi"
|
|
||||||
reset_guarded_pin,"CON Enter phone number you are the guardian to reset their pin
|
|
||||||
0. Back","CON Weka nambari ya simu ili kutuma ombi la kubalisha nambari ya siri.
|
|
||||||
0. Rudi",,,,,
|
|
||||||
reset_guarded_pin_authorization.first,"CON Enter YOUR pin to confirm %{guarded_account_information}'s reset
|
|
||||||
0. Back","CON Weka nambari YAKO ya siri ili kudhibitisha ombi la kubadilisha nambari ya siri ya %{guarded_account_information}.
|
|
||||||
0. Rudi",,,,,
|
|
||||||
reset_guarded_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},,,,,
|
|
||||||
exit_pin_reset_initiated_success,"CON Success: You have initiated a PIN reset for %{guarded_account_information}
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON Ombi lako la kubadili nambari ya siri ya %{guarded_account_information} limetumwa.
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
exit_not_authorized_for_pin_reset,"CON Failure: You are not authorized to reset that PIN. You must be a guardian!
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON Huruhusiwi kutuma ombi la kubadilisha nambari ya siri.
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
guard_pin,"CON Pin guard
|
|
||||||
1. View guardians
|
|
||||||
2. Add guardian
|
|
||||||
3. Remove guardian
|
|
||||||
0. Back","CON Linda nambari ya siri
|
|
||||||
1. Walinzi wa namabari ya siri
|
|
||||||
2. Ongeza mlinzi
|
|
||||||
3. Ondoa mlinzi
|
|
||||||
0. Rudi",,,,,
|
|
||||||
guardian_list_pin_authorization.first,"CON Enter your pin to view set guardians
|
|
||||||
0. Back","CON Weka nambari yako ya siri ili kuona walinzi uliowaongeza
|
|
||||||
0. Rudi",,,,,
|
|
||||||
guardian_list_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},,,,,
|
|
||||||
guardian_list,"CON %{guardians_list}
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON %{guardians_list}
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
add_guardian,"CON Enter phone number to add as pin reset guardian
|
|
||||||
0. Back","CON Weka nambari ya simu ili kuongeza mlinzi
|
|
||||||
0. Rudi",,,,,
|
|
||||||
add_guardian_pin_authorization.first,"CON Enter your pin to add %{guardian_information} as your PIN reset guardian
|
|
||||||
0. Back","CON Weka nambari YAKO ya siri ili kumwongeza %{guardian_information} kama mlinzi
|
|
||||||
0. Rudi",,,,,
|
|
||||||
add_guardian_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},,,,,
|
|
||||||
exit_guardian_addition_success,"CON Success: %{guardian_information} can now reset your PIN
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON Ombi lako la kumwongeza: %{guardian_information} kama mlinzi limefanikiwa
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
exit_invalid_guardian_addition,"CON %{error_exit}
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON %{error_exit}
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
remove_guardian,"CON Enter phone number to revoke guardianship:
|
|
||||||
0. Back","CON Weka nambari ya simu ili kuondoa mlinzi
|
|
||||||
0. Rudi",,,,,
|
|
||||||
remove_guardian_pin_authorization.first,"CON Enter your pin to remove %{guardian_information} as your PIN reset guardian
|
|
||||||
0. Back","CON Weka nambari YAKO ya siri ili kumwondoa %{guardian_information} kama mlinzi
|
|
||||||
0. Rudi",,,,,
|
|
||||||
remove_guardian_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},,,,,
|
|
||||||
exit_guardian_removal_success,"CON Success: %{guardian_information} PIN reset guardianship is revoked
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON Ombi lako la kumwondoa: %{guardian_information} kama mlinzi limefanikiwa
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
exit_invalid_guardian_removal,"CON %{error_exit}
|
|
||||||
0. Back
|
|
||||||
9. Exit","CON %{error_exit}
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka",,,,,
|
|
||||||
transaction_pin_authorization.first,"CON %{recipient_information} will receive %{transaction_amount} %{token_symbol} from %{sender_information}.
|
|
||||||
Please enter your PIN to confirm.
|
|
||||||
0. Back","CON %{recipient_information} atapokea %{transaction_amount} %{token_symbol} kutoka kwa %{sender_information}.
|
|
||||||
Tafadhali weka nambari yako ya siri kudhibitisha.
|
|
||||||
0. Rudi","CON %{recipient_information} nukwata %{transaction_amount} %{token_symbol} kuma kwa %{sender_information}.
|
|
||||||
Tafadhali ikia namba yaku ya siri kuvitukithya.
|
|
||||||
0. Syoka itina","CON %{recipient_information} akuamukira %{transaction_amount} %{token_symbol} kuuma kwa %{sender_information}.
|
|
||||||
Ekera namba yaku ya thiri kuetekeria.
|
|
||||||
0. Coka thutha","CON %{recipient_information} atapokea %{transaction_amount} %{token_symbol} kutoka kwa %{sender_information}.
|
|
||||||
Unavoywa kuika nambayo fitse kugeluza.
|
|
||||||
0. Uya nyuma.","CON %{recipient_information} dhiyudo %{transaction_amount} %{token_symbol} kowuok kuom %{sender_information}.
|
|
||||||
Kiyie to ket nambani mopondo mondo iyie:
|
|
||||||
0. Dog chien","CON %{recipient_information} in argad%{transaction_amount} %{token_symbol} ir %{sender_information}.
|
|
||||||
Namba ka namii imben kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
transaction_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
display_metadata_pin_authorization.first,"CON Please enter your PIN
|
|
||||||
0. Back","CON Tafadhali weka PIN yako
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku
|
|
||||||
0. Syoka itina","CON Ekera pin yaku
|
|
||||||
0. coka thutha","CON Unavoywa kuika namayo fitswe
|
|
||||||
0. Uya Nyuma","CON Kiyie to ket nambani mopondo
|
|
||||||
0. Dog chien","CON Namba ka namii imben kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
display_metadata_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
account_balances_pin_authorization.first,"CON Please enter your PIN to view balances
|
|
||||||
0. Back","CON Tafadhali weka PIN yako kuona salio.
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku kwona utyalo.
|
|
||||||
0. Syoka itina","CON Ekera pin yaku kuona matigari maku
|
|
||||||
0. Coka ","CON Unavoywa namba fitswe kulola Sazo.
|
|
||||||
0. Uya nyuma","CON Kiyie to ket nambani mopondo mondo ine modong'
|
|
||||||
0. Dog chien","CON Namba ka namii imbeen kekhai ak balansi kake lalt
|
|
||||||
0. Dheebi"
|
|
||||||
account_balances_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
account_statement_pin_authorization.first,"CON Please enter your PIN to view statement
|
|
||||||
0. Back","CON Tafadhali weka PIN yako kuona taarifa ya matumizi.
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku kwona welesyo wa utumii.
|
|
||||||
0. Syoka itina","CON Ekera pin yaku kuona rugano rwa mahuthira maku
|
|
||||||
0. coka thutha","CON Unavoywa namba fitswe kupata maerezo ga mahumizi Gako.
|
|
||||||
0. Uya Nyuma","CON Kiyie to ket nambani mar siri mondo ine chenro mar tiyo.
|
|
||||||
0. Dog chien","CON Tafadhali weka PIN yako kuona taarifa ya matumizi.
|
|
||||||
0. Dheebi"
|
|
||||||
account_statement_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
name_edit_pin_authorization.first,"CON Please enter your PIN
|
|
||||||
0. Back","CON Tafadhali weka PIN yako
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku
|
|
||||||
0. Syoka itina","CON Ekera pin yaku
|
|
||||||
0. coka thutha","CON Unavoywa namba fitse
|
|
||||||
0. Uya Nyuma","CON Ket nambani mopondo
|
|
||||||
0. Dog chien","CON Namba ka namii imben kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
name_edit_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
dob_edit_pin_authorization.first,"CON Please enter your PIN
|
|
||||||
0. Back","CON Tafadhali weka PIN yako
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku
|
|
||||||
0. Syoka itina","CON Ekera namba yaku ya thiri
|
|
||||||
0. Rudi","CON Unavoywa namba fitswe
|
|
||||||
0. Uya nyuma","CON Kiyie to ket nambani mopondo
|
|
||||||
0. Dog chien","CON Namba kake ka namii imbeen kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
dob_edit_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
gender_edit_pin_authorization.first,"CON Please enter your PIN
|
|
||||||
0. Back","CON Tafadhali weka PIN yako
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku
|
|
||||||
0. Syoka itina","CON Ekera namba yaku ya thiri
|
|
||||||
0. coka thutha","CON Unavoywa namba fitswe
|
|
||||||
0. Uya nyuma","CON Kiyie to ket nambani mopondo
|
|
||||||
0. Dog chien","CON Namba kake ka namii imbeen kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
gender_edit_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
location_edit_pin_authorization.first,"CON Please enter your PIN
|
|
||||||
0. Back","CON Tafadhali weka PIN yako
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku
|
|
||||||
0. Syoka itina","CON Ekera namba yaku ya thiri
|
|
||||||
0. Coka thutha","CON Unavoywa namba fitswe
|
|
||||||
0. Uya nyuma","CON Kiyie to ket nambani mopondo
|
|
||||||
0. Dog chien","CON Namba kake ka namii imbeen kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
location_edit_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
products_edit_pin_authorization.first,"CON Please enter your PIN
|
|
||||||
0. Back","CON Tafadhali weka PIN yako
|
|
||||||
0. Rudi","CON Tafadhali ikia PIN yaku
|
|
||||||
0. Syoka itina","CON Ekera namba yaku ya thiri
|
|
||||||
0. Coka thutha","CON Unavoywa namba fitswe
|
|
||||||
0. Uya nyuma","CON Kiyie to ket nambani mopondo
|
|
||||||
0. Dog chien","CON Namba kake ka namii imbeen kekhai
|
|
||||||
0. Dheebi"
|
|
||||||
products_edit_pin_authorization.retry,%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry},%{retry_pin_entry}
|
|
||||||
account_balances.available_balance,"CON Your balances are as follows:
|
|
||||||
%{available_balance} %{token_symbol}
|
|
||||||
0. Back","CON Salio zako ni zifuatazo:
|
|
||||||
%{available_balance} %{token_symbol}
|
|
||||||
0. Rudi","CON Utyalo waku ni uu:
|
|
||||||
utyalo: %{available_balance} %{token_symbol}
|
|
||||||
0. Syoka itina","CON Matigari maku ni maya:
|
|
||||||
matigari: %{available_balance} %{token_symbol}
|
|
||||||
0. coka","CON Masazogo nidzavivyo gatuwiravyo:
|
|
||||||
Sazo: %{available_balance} %{token_symbol}
|
|
||||||
0. Uya Nyuma","CON Dong mari en:
|
|
||||||
Dong: %{available_balance} %{token_symbol}
|
|
||||||
0. Dog chien","CON Balansi kake akan
|
|
||||||
salio: %{available_balance} %{token_symbol}
|
|
||||||
0. Dheebi"
|
|
||||||
account_balances.with_fees,"CON Your balances are as follows:
|
|
||||||
balances: %{available_balance} %{token_symbol}
|
|
||||||
fees: %{tax} %{token_symbol}
|
|
||||||
0. Back","CON Salio zako ni zifuatazo:
|
|
||||||
salio: %{available_balance} %{token_symbol}
|
|
||||||
ushuru: %{tax} %{token_symbol}
|
|
||||||
0. Rudi","CON Utyalo waku ni uu:
|
|
||||||
utyalo: %{available_balance} %{token_symbol}
|
|
||||||
tax/ushuru: %{tax} %{token_symbol}
|
|
||||||
0. Syoka itina","CON Matigari maku ni maya:
|
|
||||||
matigari: %{available_balance} %{token_symbol}
|
|
||||||
ushuru: %{tax} %{token_symbol}
|
|
||||||
0. coka thutha","CON Masazogo ni gatuwirago:
|
|
||||||
Masazo: %{available_balance} %{token_symbol}
|
|
||||||
Ushuuru: %{tax} %{token_symbol}
|
|
||||||
0. Uya nyuma","CON Dong mari en:
|
|
||||||
Dong: %{available_balance} %{token_symbol}
|
|
||||||
osuru: %{tax} %{token_symbol}
|
|
||||||
0. Dog chien","CON Balansi kake akan
|
|
||||||
salio: %{available_balance} %{token_symbol}
|
|
||||||
ushuru: %{tax} %{token_symbol}
|
|
||||||
0. Dheebi"
|
|
||||||
account_balances.with_rewards,"CON Your balances are as follows:
|
|
||||||
balance: %{available_balance} %{token_symbol}
|
|
||||||
fees: %{tax} %{token_symbol}
|
|
||||||
rewards: %{bonus} %{token_symbol}
|
|
||||||
0. Back","CON Salio zako ni zifuatazo:
|
|
||||||
salio: %{available_balance} %{token_symbol}
|
|
||||||
ushuru: %{tax} %{token_symbol}
|
|
||||||
tuzo: %{bonus} %{token_symbol}
|
|
||||||
0. Rudi","CON Utyalo waku ni uu:
|
|
||||||
Utyalo: %{available_balance} %{token_symbol}
|
|
||||||
Tax/ushuru: %{tax} %{token_symbol}
|
|
||||||
muthinzio: %{bonus} %{token_symbol}
|
|
||||||
0. Syoka itina","CON Salio zako ni zifuatazo:
|
|
||||||
salio: %{available_balance} %{token_symbol}
|
|
||||||
ushuru: %{tax} %{token_symbol}
|
|
||||||
tuzo: %{bonus} %{token_symbol}
|
|
||||||
0. coka thutha","CON Sazoro ni rituwiraro:
|
|
||||||
Sazo: %{available_balance} %{token_symbol}
|
|
||||||
Ushuuru: %{tax} %{token_symbol}
|
|
||||||
Zawadi: %{bonus} %{token_symbol}
|
|
||||||
0. Uya Nyuma","CON Dong mari en:
|
|
||||||
Dong: %{available_balance} %{token_symbol}
|
|
||||||
osuru: %{tax} %{token_symbol}
|
|
||||||
mich: %{bonus} %{token_symbol}
|
|
||||||
0. Dog chien","CON Balansi kake akan
|
|
||||||
salio: %{available_balance} %{token_symbol}
|
|
||||||
ushuru: %{tax} %{token_symbol}
|
|
||||||
tuzo: %{bonus} %{token_symbol}
|
|
||||||
0. Dheebi"
|
|
||||||
first_transaction_set,"CON %{first_transaction_set}
|
|
||||||
|
|
||||||
0. Back
|
|
||||||
11. Next
|
|
||||||
00. Exit","CON %{first_transaction_set}
|
|
||||||
|
|
||||||
0. Rudi
|
|
||||||
11. Mbele
|
|
||||||
00. Ondoka","CON %{first_transaction_set}
|
|
||||||
1. Mbee
|
|
||||||
00. Ondoka","CON %{first_transaction_set}
|
|
||||||
1. Mbere
|
|
||||||
00. uma","CON %{first_transaction_set}
|
|
||||||
1. Mbere
|
|
||||||
00. Uka","CON %{first_transaction_set}
|
|
||||||
1. Nyime
|
|
||||||
00. Wuogi","CON %{first_transaction_set}
|
|
||||||
1. Dhuur
|
|
||||||
00. Bai"
|
|
||||||
middle_transaction_set,"CON %{middle_transaction_set}
|
|
||||||
|
|
||||||
11. Next
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON %{middle_transaction_set}
|
|
||||||
|
|
||||||
11. Mbele
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka","CON %{middle_transaction_set}
|
|
||||||
1. Mbee
|
|
||||||
2. Itina
|
|
||||||
00. Ondoka","CON %{middle_transaction_set}
|
|
||||||
1. Mbere
|
|
||||||
2. coka thutha
|
|
||||||
00. Uma","CON %{middle_transaction_set}
|
|
||||||
1. Mbere
|
|
||||||
2. Uya nyuma
|
|
||||||
00. Uka","CON %{middle_transaction_set}
|
|
||||||
1. Nyime
|
|
||||||
2. Dog chien
|
|
||||||
00. Wuogi","CON %{middle_transaction_set}
|
|
||||||
1. Dhuur
|
|
||||||
2. Dheebi
|
|
||||||
00. Bai"
|
|
||||||
last_transaction_set,"CON %{last_transaction_set}
|
|
||||||
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON %{last_transaction_set}
|
|
||||||
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka","CON %{last_transaction_set}
|
|
||||||
2. Itina
|
|
||||||
00. Ondoka","CON %{last_transaction_set}
|
|
||||||
2. Coka thutha
|
|
||||||
00. Uma","CON %{last_transaction_set}
|
|
||||||
2. Uya Nyuma
|
|
||||||
00. Uka","CON %{last_transaction_set}
|
|
||||||
2. Dog chien
|
|
||||||
00. Wuogi","CON %{last_transaction_set}
|
|
||||||
2. Dhuur
|
|
||||||
00. Bai"
|
|
||||||
exit,END Thank you for using the service.,END Asante kwa kutumia huduma.,END Ni muvea kwa kutumia huduma ii.,END Thegio ni kuhuthira mutabo,END. Namvera kwa mahumizi ga ii huduma.,END Erokamano kuom tiyo kodwa.,END Asante kwa kutumia huduma.
|
|
||||||
exit_invalid_request,END Invalid request.,END Chaguo si sahihi.,END Usakuo waku uyaile.,END shaguro riaku ti riega,END. Tsagulo karisawa sawa,END Yiero okni kare,END Ka at chaquad suninit
|
|
||||||
exit_invalid_menu_option,"CON Invalid menu option. For help,call %{support_phone}.
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Chaguo lako sio sahihi. Kwa usaidizi piga simu %{support_phone}
|
|
||||||
00. Rudi
|
|
||||||
99. Ondoka","CON Usakuo waku uyaile. Kwa utethyo kuna simu %{support_phone}
|
|
||||||
00. Itina
|
|
||||||
99. Ondoka","CON Shaguro riaku ti riega.Kwa uteithio hura %{support_phone}
|
|
||||||
00. Coka thutha
|
|
||||||
99. Uma","CON Tsaguloro karisawa sawa. Kuavizwa piga %{support_phone}
|
|
||||||
00. Uya Nyuma
|
|
||||||
99. Uka","CON Yiero ni oknikare. Kuom kony go simu e %{support_phone}
|
|
||||||
00. Dog chien
|
|
||||||
99. Wuogi","CON Ka at chaqui suninit.qarqarsa simu dai%{support_phone}
|
|
||||||
00. Dheebi
|
|
||||||
99. Bai"
|
|
||||||
exit_invalid_input,"CON Invalid input. Nothing selected
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Chaguo lako halipatikani. Hakuna kilichochaguliwa.
|
|
||||||
00. Rudi
|
|
||||||
99. Ondoka","CON Usakuo waku wikwonekana.Vaii kindu kisakue.
|
|
||||||
00. Itina
|
|
||||||
99. Ondoka","CON Shaguro riaku ritironekana. Hatiri kindu washaghura.
|
|
||||||
00. Coka thutha
|
|
||||||
99. Uma","CON Tsaguloro karipatikana. Kakuna Kutsagurire chochosi.
|
|
||||||
00. Uya nyuma
|
|
||||||
99. Uka","CON Yiero ni okyudre. Onge gima iyiero.
|
|
||||||
00. Dog chien
|
|
||||||
99. Wuogi","CON Ka at chaguad injirt. oo
|
|
||||||
00. dheebi
|
|
||||||
99. Bai"
|
|
||||||
exit_pin_blocked,"END Your PIN has been blocked. For help, please call %{support_phone}.",END PIN yako imefungwa. Kwa usaidizi tafadhali piga simu %{support_phone}.,END PIN yaku niyavingwa. Kutethwa kuna simu ino %{support_phone}.,END PIN yaku niyahingwo. Kwa uteithio hura thimu %{support_phone}.,END. Namba fitse yakwako ifungwa. Kwa kuavizwa unaangwa upige simu %{support_phone}.,END Nambani mopondo olor. Kuom kony go simu e %{support_phone}.,END Pin kake yahidat. Qarqarsa simu dai %{support_phone}.
|
|
||||||
exit_invalid_pin,"END The PIN you have entered is invalid. PIN must consist of 4 digits. For help, call %{support_phone}.",END PIN uliyobonyeza sio sahihi. PIN lazima iwe na nambari nne. Kwa usaidizi piga simu %{support_phone}.,END PIN ila wekia iyaile. Ni lasima PIN ithiwe na namba inya. Kutethwa kuna namba ii %{support_phone}.,END PIN iria wekera tii njega. PIN nomoka ikorwo na namba inya. Kwa uteithio hura thimu %{support_phone}.,"END Namba fitse urohofya seyo, kaisawa. Namba fitswe inamalwa ikale na namba nee. Kwa kuvizwa, piga simu%{support_phone}.",END. Namba mopondo miketo oknikare. Nyaka obed gi nembni ang'wen. Kuom kony go simu e %{support_phone}.,END PIN ka at keket suninit. PIN Pin namba afuuri tatatu. Qarqarsa simu dai %{support_phone}.
|
|
||||||
exit_invalid_new_pin,"END The PIN you have entered is invalid. PIN must be different from your current PIN. For help, call %{support_phone}.",END PIN uliyobonyeza sio sahihi. PIN lazima iwe tofauti na pin yako ya sasa. Kwa usaidizi piga simu %{support_phone}.,END PIN ila wekia iyaile. PIN ni lasima ithiwe tofauti na pin yaku ya oyu. Kutethwa kuna namba ii %{support_phone}.,END PIN uria wekera ti njega. PIN nomohaka ikorwo na namba ndiganu na ya riu . Kwa uteithio hora thimu %{support_phone}.,END Namba fitswe uriohopya siyo ya karakara. Namba fitswe inahenzekana ikale itofauti na uhumirayo vivi. Kwa maavizo piga simu %{support_phone}.,END Namba mopondo miketo oknikare. Nyaka obed mopogore gi nambani mopondo masani. Kuom kony gochi e %{support_phone}.,END PIN ka at keket suninit.Pin kake walinfakaatin.Qarqars simu dai %{support_phone}.
|
|
||||||
exit_pin_mismatch,"END The new PIN does not match the one you entered. Please try again. For help, call %{support_phone}.",END PIN mpya na udhibitisho wa pin mpya hazilingani. Tafadhali jaribu tena. Kwa usaidizi piga simu %{support_phone}.,END PIN yumbya na uhakikisho wa pin yumbya syivwanene. Tafadhali tata ingi. Kutethwa kuna simu %{support_phone}.,END PIN njeru na pin ya guetekeria shitira hianana . Geria ringi. Kwa uteithio hora thimu %{support_phone}.,END Namba fitse uzdoinjiza kaikara kara na uriyohopya laphiri. Unavoywa ujeze kaheri. Kwa kuavizwa piga simu %{support_phone}.,GIKO. Namba mopondo miketo opogore gi manikuongo keto. Kiyie to ket kendo. Kuom kony gochi e %{support_phone}.,END PIN mpya na udhibitisho wa pin mpya hazilingani. Pin hareti ka at kekeet walinfakan. it dheebi amaale.Qarqars simu dai%{support_phone}.
|
|
||||||
exit_invalid_recipient,"CON Recipient's phone number is not registered or is invalid:
|
|
||||||
00. Retry
|
|
||||||
99. Exit","CON Mpokeaji wa nambari hapatikani au sio sahihi.
|
|
||||||
00. Jaribu tena
|
|
||||||
99. Ondoka","CON Mukwati wa namba ndokwatikana kana ii namba iyaile kana ti sahihi.
|
|
||||||
00. Tata ingi
|
|
||||||
99. Ondoka","CON Mpokeaji wa nambari hapatikani au sio sahihi.Namba ya mutumirwo ndiranyitikana kana ti njega
|
|
||||||
00. Geria ringi
|
|
||||||
99. Uma","CON Muphokezi wa namba kapatikana ama namba kai karakara.
|
|
||||||
00. Jeza Kaheri
|
|
||||||
99. Uka","CON Jayuto mar nambani okyudre kata oknikare.
|
|
||||||
00. Tem kendo
|
|
||||||
99. Wuogi","CON Mpokeaji wa nambari hapatikani au sio sahihi.
|
|
||||||
00.
|
|
||||||
99. Ondoka"
|
|
||||||
exit_successful_transaction,"CON Your request has been sent. %{recipient_information} will receive %{transaction_amount} %{token_symbol} from %{sender_information}.
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Ombi lako limetumwa. %{recipient_information} atapokea %{transaction_amount} %{token_symbol} kutoka kwa %{sender_information}.
|
|
||||||
00. Rudi
|
|
||||||
99. Ondoka","CON Woni waku niwatumwa. %{recipient_information} nukupokea %{transaction_amount} %{token_symbol} kuma kwa %{sender_information}.
|
|
||||||
00. Itina
|
|
||||||
99. Ondoka","CON Mahoya maku nimatomwo. %{recipient_information} akuamukira%{transaction_amount} %{token_symbol} kuma kwa %{sender_information}.
|
|
||||||
00. Coka
|
|
||||||
99. Uma","CON Mavoyogo gahumwa. %{recipient_information} undaphokera %{transaction_amount} %{token_symbol} kuombola kwa %{sender_information}.
|
|
||||||
00. Uya Nyuma
|
|
||||||
99. Uka","CON Kwayo ni oseor. %{recipient_information} oboyudo %{transaction_amount} %{token_symbol} kowuok kuom %{sender_information}.
|
|
||||||
00. Dog chien
|
|
||||||
99. Wuogi","CON Qarqar kake yaergad. %{recipient_information} inargat %{transaction_amount} %{token_symbol} kutoka kwa %{sender_information}.
|
|
||||||
00. Dheebi
|
|
||||||
99. Bai"
|
|
||||||
exit_insufficient_balance,"CON Payment of %{amount} %{token_symbol} to %{recipient_information} has failed due to insufficient balance.
|
|
||||||
Your Sarafu-Network balances is: %{token_balance}
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Malipo ya %{amount} %{token_symbol} kwa %{recipient_information} halijakamilika kwa sababu salio lako haitoshi.
|
|
||||||
Akaunti yako ya Sarafu ina salio ifuatayo: %{token_balance}
|
|
||||||
00. Rudi
|
|
||||||
99. Ondoka","CON Ndivi ya %{amount} %{token_symbol} kwa %{recipient_information} inavitukithwa nundu utyalyo waku ni munini.
|
|
||||||
Kinandu chaku cha Sarafu kina utyalo uu: %{token_balance}
|
|
||||||
00. Itina
|
|
||||||
99. Ondoka","CON Marehi ma %{amount} %{token_symbol} kwa %{recipient_information} matinarekereka tondu matigari maku matiraigana.
|
|
||||||
Akaunti yako ya Sarafu ina salio ifuatayo: %{token_balance}
|
|
||||||
00. Coka
|
|
||||||
99. Uma","CON Maripho ga %{amount} %{token_symbol} kwa %{recipient_information} Karidzangwe kukamirika Kwaukala sazoro Karitosha.
|
|
||||||
Akauntiyo vivi ina sazo dza rituwiranavyo: %{token_balance}
|
|
||||||
00. Uya nyuma
|
|
||||||
99. Uka","CON Chudo mar %{amount} %{token_symbol} kuom %{recipient_information} okotieki nikech dong ni okrom.
|
|
||||||
Akaont ni mar Sarafu ni gi dong mar: %{token_balance}
|
|
||||||
00. Dog chien
|
|
||||||
99. Wuogi","CON Malipo ka%{amount} %{token_symbol} kwa %{recipient_information} Inkamilikee balansi kake ingau
|
|
||||||
Akaunti kake balansi akan kabd: %{token_balance}
|
|
||||||
00. Dheebi
|
|
||||||
99. Bai"
|
|
||||||
exit_successful_token_selection,"CON Success! %{token_symbol} is your active Sarafu.
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Chaguo lako limekamilika, %{token_symbol} ni sarafu itakayotumika.
|
|
||||||
00. Rudi
|
|
||||||
99. Ondoka",,,,,
|
|
||||||
invalid_service_code,Please dial %{valid_service_code} to access Sarafu Network,Bonyeza %{valid_service_code} kutumia mtandao wa Sarafu,Vinyia %{valid_service_code} kutumia mutandao wa Sarafu,Hihinya%{valid_service_code} kuhudhira mutabo wa Sarafu,Hofya %{valid_service_code} Kuhumira Mutandao wa sarafu,Dii %{valid_service_code} mondo iti gi Sarafu,Bonyeza %{valid_service_code} kutumia mtandao wa Sarafu
|
|
||||||
help,"CON For assistance call %{support_phone}
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Kwa usaidizi piga simu %{support_phone}
|
|
||||||
0. Rudi
|
|
||||||
9. Ondoka","CON Kwa utethyo kuna simu %{support_phone}
|
|
||||||
0. Itina
|
|
||||||
9. Ondoka","CON Kwa uteithio hora thimu %{support_phone}
|
|
||||||
0. Coka
|
|
||||||
9. Uma","CON Kwa Kuavizwa piga simu %{support_phone}
|
|
||||||
0. Uya nyuma
|
|
||||||
9. Uka","CON Kuom kony go simu e %{support_phone}
|
|
||||||
0. Dog chien
|
|
||||||
9. Wuogi","CON Qarqars simu dai%{support_phone}
|
|
||||||
0. Dheebi
|
|
||||||
9. Bai"
|
|
||||||
complete,"CON Your request has been sent. You will receive an SMS shortly.
|
|
||||||
00. Back
|
|
||||||
99. Exit","CON Ombi lako limetumwa. Utapokea uthibitishaji wa SMS kwa muda mfupi.
|
|
||||||
00. Rudi
|
|
||||||
99. Ondoka","CON Woni waku niwatumwa. Nukwata SMS ya kwonya ivinda ite yasa.
|
|
||||||
00. Itina
|
|
||||||
99. Ondoka","CON Mahoya maku nimatomwo. Niukuamukira SMS ya guitikirika ihinda ikuhi .
|
|
||||||
00. Coka
|
|
||||||
99. Uma","CON Vyoyoro rihumwa. Undaphokera Uthibitishaji wa SMS kwa muda mufuhi.
|
|
||||||
00. Uya nyuma
|
|
||||||
99. Uka","CON Kwayo ni oseor. Iboyudo mesej mar ote ni bang' saa matin.
|
|
||||||
00. Dog chien
|
|
||||||
99. Wuogi","CON Qarqars kake yaergad. Utapokea uthibitishaji wa SMS kwa muda mfupi.
|
|
||||||
00. Dheebi
|
|
||||||
99. Bai"
|
|
||||||
account_creation_prompt,END Your account is being created. You will receive an SMS when your account is ready.,END Akaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.,END Akaunti yako ya Sarafu yendeye usovwa. Nukwata SMS akaunti yaku yasovwa.,END Akaunti yaku ya Sarafu niiraharirio.Niugutumirwo SMS akauti yaku ya rikio kuharirio,END Akauntiyo ya sarafu idzikoni. Undaphokera ujumbe wa SMS ichikala tayari.,END Akaont ni mar Sarafu iloso. Iboyudo mesej ka akaont ni otieki.,END Akaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.
|
|
||||||
initial_middle_language_set,"CON Choose language:
|
|
||||||
%{middle_language_set}
|
|
||||||
|
|
||||||
11. Next
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON Chagua lugha:
|
|
||||||
%{middle_language_set}
|
|
||||||
|
|
||||||
11. Mbele
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka",,,,,
|
|
||||||
initial_last_language_set,"CON Choose language:
|
|
||||||
%{last_language_set}
|
|
||||||
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON Choose language:
|
|
||||||
%{last_language_set}
|
|
||||||
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka",,,,,
|
|
||||||
middle_language_set,"CON Choose language:
|
|
||||||
%{middle_language_set}
|
|
||||||
|
|
||||||
11. Next
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON Chagua lugha:
|
|
||||||
%{middle_language_set}
|
|
||||||
|
|
||||||
11. Mbele
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka",,,,,
|
|
||||||
last_language_set,"CON Choose language:
|
|
||||||
|
|
||||||
%{last_language_set}
|
|
||||||
22. Previous
|
|
||||||
00. Exit","CON Choose language:
|
|
||||||
%{last_language_set}
|
|
||||||
|
|
||||||
22. Rudi
|
|
||||||
00. Ondoka",,,,,
|
|
|
@ -45,10 +45,10 @@ services:
|
|||||||
context: apps/contract-migration
|
context: apps/contract-migration
|
||||||
dockerfile: docker/Dockerfile
|
dockerfile: docker/Dockerfile
|
||||||
args:
|
args:
|
||||||
DOCKER_REGISTRY: ${DEV_DOCKER_REGISTRY:-registry.gitlab.com/grassrootseconomics}
|
DOCKER_REGISTRY: ${DEV_DOCKER_REGISTRY:-registry.gitlab.com/grassrootseconomics}
|
||||||
PIP_INDEX_URL: ${PIP_INDEX_URL:-https://pypi.org/simple}
|
PIP_INDEX_URL: ${PIP_INDEX_URL:-https://pypi.org/simple}
|
||||||
EXTRA_PIP_INDEX_URL: ${EXTRA_PIP_INDEX_URL:-https://pip.grassrootseconomics.net}
|
EXTRA_PIP_INDEX_URL: ${EXTRA_PIP_INDEX_URL:-https://pip.grassrootseconomics.net}
|
||||||
EXTRA_PIP_ARGS: $EXTRA_PIP_ARGS
|
EXTRA_PIP_ARGS: $EXTRA_PIP_ARGS
|
||||||
environment:
|
environment:
|
||||||
DEV_DATA_DIR: ${DEV_DATA_DIR:-/tmp/cic/config}
|
DEV_DATA_DIR: ${DEV_DATA_DIR:-/tmp/cic/config}
|
||||||
DEV_CONFIG_RESET: $DEV_CONFIG_RESET
|
DEV_CONFIG_RESET: $DEV_CONFIG_RESET
|
||||||
@ -172,42 +172,6 @@ services:
|
|||||||
set +a
|
set +a
|
||||||
./start_tasker.sh --aux-all -q cic-eth -vv
|
./start_tasker.sh --aux-all -q cic-eth -vv
|
||||||
|
|
||||||
cic-eth-server:
|
|
||||||
image: ${DEV_DOCKER_REGISTRY:-registry.gitlab.com/grassrootseconomics}/cic-eth:${TAG:-latest}
|
|
||||||
ports:
|
|
||||||
- 5000:5000
|
|
||||||
build:
|
|
||||||
context: apps/cic-eth
|
|
||||||
dockerfile: docker/Dockerfile
|
|
||||||
args:
|
|
||||||
DOCKER_REGISTRY: ${DEV_DOCKER_REGISTRY:-registry.gitlab.com/grassrootseconomics}
|
|
||||||
PIP_INDEX_URL: ${PIP_INDEX_URL:-https://pypi.org/simple}
|
|
||||||
EXTRA_PIP_INDEX_URL: ${EXTRA_PIP_INDEX_URL:-https://pip.grassrootseconomics.net}
|
|
||||||
EXTRA_PIP_ARGS: $EXTRA_PIP_ARGS
|
|
||||||
environment:
|
|
||||||
REDIS_PORT: 6379
|
|
||||||
REDIS_HOST: redis
|
|
||||||
CHAIN_SPEC: ${CHAIN_SPEC:-evm:byzantium:8996:bloxberg}
|
|
||||||
CELERY_BROKER_URL: ${CELERY_BROKER_URL:-redis://redis}
|
|
||||||
CELERY_RESULT_URL: ${CELERY_RESULT_URL:-redis://redis}
|
|
||||||
CELERY_DEBUG: ${CELERY_DEBUG:-1}
|
|
||||||
restart: unless-stopped
|
|
||||||
depends_on:
|
|
||||||
- cic-eth-tasker
|
|
||||||
- cic-eth-tracker
|
|
||||||
- cic-eth-dispatcher
|
|
||||||
volumes:
|
|
||||||
# - ./apps/cic-eth/:/root
|
|
||||||
- signer-data:/run/crypto-dev-signer
|
|
||||||
- contract-config:/tmp/cic/config/:ro
|
|
||||||
command:
|
|
||||||
- /bin/bash
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
set -a
|
|
||||||
if [[ -f /tmp/cic/config/env_reset ]]; then source /tmp/cic/config/env_reset; fi
|
|
||||||
set +a
|
|
||||||
python -m cic_eth.runnable.daemons.server
|
|
||||||
|
|
||||||
cic-eth-tracker:
|
cic-eth-tracker:
|
||||||
image: ${IMAGE_BASE_URL:-registry.gitlab.com/grassrootseconomics/cic-internal-integration}/cic-eth:${TAG:-latest}
|
image: ${IMAGE_BASE_URL:-registry.gitlab.com/grassrootseconomics/cic-internal-integration}/cic-eth:${TAG:-latest}
|
||||||
|
Loading…
Reference in New Issue
Block a user