Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
artyl committed Oct 24, 2022
2 parents 5c0daae + e225d16 commit 7713dd6
Show file tree
Hide file tree
Showing 16 changed files with 440 additions and 221 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[run]
omit = *tests*,python/*
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.pyc
.cache
.coverage
htmlcov/*
.pytest_cache
.mypy_cache/
__pycache__
Expand All @@ -24,7 +25,7 @@ webdriver/*
.idea/*
venv/*
tests/data/mbplugin/*
BalanceHistory.sqlite
*.sqlite
balance.html
plugin/debug.log
mbplugin*.zip
Expand Down
13 changes: 13 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@
%~d0
@REM clean:
@REM git clean -fXd
set "ptime= "
where ptime
if %errorlevel%==0 set ptime=ptime

if NOT "%1"=="" goto %1
ECHO RUN build clean/test/build/fixup

goto :EOF
@REM @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ test
:coverage
%~d0
cd "%~dp0"
call python\python -m coverage run -m pytest tests %2 %3 %4 %5 %6 %7 %8 %9
echo coverage html
call python\python -m coverage html
@rem call start htmlcov\index.html
echo %errorlevel%
goto :EOF
@REM @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ test
:test
Expand Down
4 changes: 4 additions & 0 deletions changelist.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,3 +429,7 @@ FIX: Tele2 исправлена работа через Chrome (напомню
FIX: Beeline подправил расчет интернета
FIX: Sipnet в процессе получения ругается на неправильный сертификат, пришлось отключить проверку сертификатов.
FIX: Похоже поменялась страница входа в beeline_uz поправил, но проверить не могу, т.к. не имею телефонов

## mbplugin v1.00.38 (24.10.22) refactoring и консольные команды
REFACTORING: Переработано большое количество кода, с целью большей управляемости, но без изменения функционала, pytest+coverage
ADD: Диагностические сообщения и коды возврата для консольной команды mbp get-balance и check-plugin
1 change: 1 addition & 0 deletions docker/requirements_pytest.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
atomicwrites==1.4.0
attrs==21.4.0
coverage==6.5.0
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
Expand Down
2 changes: 1 addition & 1 deletion plugin/beeline_uz.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def data_collector(self):
{'name': 'TarifPlan', 'url_tag': ['/dashboard$'], 'jsformula': "data.subUsers[0].pricePlan.ru"},
{'name': 'UserName', 'url_tag': ['/dashboard$'], 'jsformula': "data.subUsers[0].fio"},
{'name': 'Internet', 'url_tag': ['/dashboard$'], 'jsformula': "parseFloat(data.usages.filter(el => el.belongsTo=='GPRS_PACK_2')[0].leftover/1024/1024).toFixed(2)"},
{'name': 'SMS', 'url_tag': ['/dashboard$'], 'jsformula': "parseFloat(data.usages.filter(el => el.belongsTo=='SMS_ACTIVE')[0].leftover).toFixed(0)"},
{'name': 'SMS', 'url_tag': ['/dashboard$'], 'jsformula': "parseFloat(data.usages.filter(el => el.belongsTo.startsWith('SMS'))[0].leftover).toFixed(0)"},
{'name': 'Min', 'url_tag': ['/dashboard$'], 'jsformula': "parseFloat(data.usages.filter(el => el.belongsTo=='CBM_ON_MINUTE_BALANCE')[0].leftover/60).toFixed(0)"},
{'name': 'LicSchet', 'url_tag': ['/dashboard$'], 'jsformula': "data.subUsers[0].subAccount"},
{'name': 'BlockStatus', 'url_tag': ['/dashboard$'], 'jsformula': "data.state"},
Expand Down
321 changes: 170 additions & 151 deletions plugin/dbengine.py

Large diffs are not rendered by default.

47 changes: 27 additions & 20 deletions plugin/httpserver_mobile.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
После включения, запустите mbplugin\\setup_and_check.bat
'''

# TODO в командах для traymeny используется os.system(f'start ... это будет работать только в windows, но пока пофигу, т.к. сам pystrayработает только в windows
# TODO в командах для traymeny используется os.system(f'start ... это будет работать только в windows, но пока пофигу, т.к. сам pystray работает только в windows
TRAY_MENU = (
{'text': "Main page", 'cmd': lambda: os.system(f'start http://localhost:{store.options("port", section="HttpServer")}/main'), 'show': True},
{'text': "View report", 'cmd': lambda: os.system(f'start http://localhost:{store.options("port", section="HttpServer")}/report'), 'show': True},
Expand All @@ -58,7 +58,7 @@
)


def getbalance_standalone_one_pass(filter: list = [], only_failed: bool = False) -> None:
def getbalance_standalone_one_pass(filter: list = [], only_failed: bool = False):
''' Получаем балансы самостоятельно без mobilebalance ОДИН ПРОХОД
Если filter пустой то по всем номерам из phones.ini
Если не пустой - то логин/алиас/оператор или его часть
Expand All @@ -68,6 +68,7 @@ def getbalance_standalone_one_pass(filter: list = [], only_failed: bool = False)
для Standalone версии в файле phones_add.ini
only_failed=True - делать запросы только по тем номерам, по которым прошлый запрос был неудачный
'''
result: typing.Dict = {}
phones = store.ini('phones.ini').phones()
queue_balance = [] # Очередь телефонов на получение баланса
for val in phones.values():
Expand All @@ -84,22 +85,26 @@ def getbalance_standalone_one_pass(filter: list = [], only_failed: bool = False)
dbengine.flags('set', f'{keypair}', 'queue') # выставляем флаг о постановке в очередь
store.feedback.text(f'Queued {len(queue_balance)} numbers')
for val in queue_balance:
# TODO пока дергаем метод от вебсервера там уже все есть, потом может вынесем отдельно
# TODO пока дергаем метод от веб сервера там уже все есть, потом может вынесем отдельно
keypair = f"{val['Region']}_{val['Number']}"
try:
# проверяем на сигнал Q_CMD_CANCEL, все остальное - кладем обратно
if Q_CMD_CANCEL in cmdqueue.queue:
qu = [cmdqueue.get(block=False) for el in range(cmdqueue.qsize())]
[cmdqueue.put(el) for el in qu if el != Q_CMD_CANCEL] # type: ignore
logging.info(f'Receive cancel signal to query')
store.feedback.text(f"Receive cancel signal")
return
return result
store.feedback.text(f"Receive {val['Alias']}:{val['Region']}_{val['Number']}")
getbalance_plugin('get', {'plugin': [val['Region']], 'login': [val['Number']], 'password': [val['Password2']], 'date': ['date']})
r1 = getbalance_plugin('get', {'plugin': [val['Region']], 'login': [val['Number']], 'password': [val['Password2']], 'date': ['date']})
result[keypair] = 'Balance' in repr(r1)
except Exception:
result[keypair] = False
logging.error(f"Unsuccessful check {val['Region']} {val['Number']} {store.exception_text()}")
return result


def getbalance_standalone(filter: list = [], only_failed: bool = False, retry: int = -1, params=None) -> None:
def getbalance_standalone(filter: list = [], only_failed: bool = False, retry: int = -1, params=None):
''' Получаем балансы делая несколько проходов по неудачным
retry=N количество повторов по неудачным попыткам, после запроса по всем (повторы только при only_failed=False)
params добавлен чтобы унифицировать вызовы
Expand All @@ -108,12 +113,14 @@ def getbalance_standalone(filter: list = [], only_failed: bool = False, retry: i
logging.info(f'getbalance_standalone: filter={filter}')
if retry == -1:
retry = int(store.options('retry_failed', flush=True))
result = {}
if only_failed:
getbalance_standalone_one_pass(filter=filter, only_failed=True)
result.update(getbalance_standalone_one_pass(filter=filter, only_failed=True))
else:
getbalance_standalone_one_pass(filter=filter, only_failed=False)
result.update(getbalance_standalone_one_pass(filter=filter, only_failed=False))
for i in range(retry):
getbalance_standalone_one_pass(filter=filter, only_failed=True)
result.update(getbalance_standalone_one_pass(filter=filter, only_failed=True))
return result


def get_full_info_one_number(keypair: str, check: bool = False) -> str:
Expand Down Expand Up @@ -246,9 +253,9 @@ def view_log(param):
def prepare_loglist_personal():
'Делает список пар по которым есть скриншоты'
ss = glob.glob(store.abspath_join(store.options('loggingfolder'), '*.png'))
allmatch = [re.search(r'(.*)_\d+\.png', os.path.split(fn)[-1]) for fn in ss]
allgroups = sorted(set([m.groups()[0] for m in allmatch if m]))
return allgroups
all_match = [re.search(r'(.*)_\d+\.png', os.path.split(fn)[-1]) for fn in ss]
all_groups = sorted(set([m.groups()[0] for m in all_match if m]))
return all_groups

def prepare_log_personal(prefix):
'Готовит html лог со скриншотами начинающимися на prefix'
Expand Down Expand Up @@ -293,7 +300,7 @@ def pp_field(pkey, he, el, hover, unwanted=False):
store.options('logginglevel', flush=True) # Запускаем, чтобы сбросить кэш и перечитать ini
template_page = settings.table_template['page']
template_history = settings.table_template['history']
temlate_style = settings.table_template['style']
template_style = settings.table_template['style']
html_script = settings.table_template['script']
db = dbengine.Dbengine()
flags = dbengine.flags('getall') # берем все флаги словарем
Expand Down Expand Up @@ -353,8 +360,8 @@ def pp_field(pkey, he, el, hover, unwanted=False):
if flags.get(f"{line['Operator']}_{line['PhoneNumber']}", '').startswith('queue'):
classflag = 'n_us'
html_table.append(f'<tr id="row" class="order {classflag}">{"".join(html_line)}</tr>')
temlate_style = temlate_style.replace('{HoverCss}', store.options('HoverCss')) # HoverCss общий на всю страницу, поэтому берем без pkey
res = template_page.format(style=temlate_style, html_header=html_header, html_table='\n'.join(html_table), title=store.version(), html_script=html_script)
template_style = template_style.replace('{HoverCss}', store.options('HoverCss')) # HoverCss общий на всю страницу, поэтому берем без pkey
res = template_page.format(style=template_style, html_header=html_header, html_table='\n'.join(html_table), title=store.version(), html_script=html_script)
return 'text/html', [res]


Expand Down Expand Up @@ -532,7 +539,7 @@ def restart_program(reason='', exit_only=False, delay=0):
if pid_from_file == os.getpid():
os.remove(filename_pid)
if not exit_only:
subprocess.Popen(cmd) # Crossplatform run process
subprocess.Popen(cmd) # Cross platform run process
psutil.Process().kill()
if Q_CMD_EXIT not in cmdqueue.queue: # Если есть то второй раз не кладем
cmdqueue.put(Q_CMD_EXIT) # Если kill не сработал (для pid=1 не сработает) - шлем сигнал
Expand All @@ -556,7 +563,7 @@ def send_http_signal(cmd, force=True):
except Exception:
pass
# То что дальше - это вышибание процесса если web сервер не остановился
if not(cmd == 'exit' and force):
if not (cmd == 'exit' and force):
return
for i in range(50): # Подождем пока сервер остановится
if os.path.exists(filename_pid):
Expand Down Expand Up @@ -778,7 +785,7 @@ def stop(self):


def auth_decorator(errmsg=None, nonauth: typing.Callable = None):
'Если хотим незалогиненому выдать сообщение об ошибке - указываем его в errmsg, если без авторизации хотим вызвать другой метод - указываем его в nonauth'
'Если хотим не залогиненому выдать сообщение об ошибке - указываем его в errmsg, если без авторизации хотим вызвать другой метод - указываем его в nonauth'
def decorator(func): # pylint: disable=no-self-argument
def wrapper(self, update: telegram.update.Update, context):
# update.message.chat_id отсутствует у CallbackQueryHandler пробуем через update.effective_chat.id:
Expand Down Expand Up @@ -998,7 +1005,7 @@ def get_one(self, update, context: callbackcontext.CallbackContext):
"""Receive one balance with inline keyboard/args, only auth user.
/checkone - получаем баланс
/getone - показываем"""
# Заданы агрументы? Тогда спросим по ним.
# Заданы аргументы? Тогда спросим по ним.
args = ' '.join(context.args if context.args is not None else []).lower()
if args != '' and update is not None: # context.args
cmd = (update.effective_message.text[1:]).split(' ')[0]
Expand Down Expand Up @@ -1047,7 +1054,7 @@ def reply(edit_text, message, keypair):
self.put_text(edit_text, 'This is log')
res = prepare_log_personal(keypair)
message.reply_document(filename=f'{keypair}_log.htm', document=io.BytesIO(res.strip().encode('cp1251')))
# Заданы агрументы? Тогда спросим по ним.
# Заданы аргументы? Тогда спросим по ним.
args = ' '.join(context.args if context.args is not None else []).lower()
# запрашиваем по заданному аргументу
if args != '' and update is not None: # context.args
Expand Down
12 changes: 6 additions & 6 deletions plugin/mbplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ def main():
logging.error(exception_text)
sys.stdout.write(exception_text)
return -1
if len(sys.argv) == 4: # plugin login password
if len(sys.argv) == 4: # plugin login password
login = sys.argv[2]
password = sys.argv[3]
else: # request указан в переменной RequestVariable ?
else: # request указан в переменной RequestVariable ?
try:
RequestVariable = os.environ['RequestVariable'].strip(' "')
root = etree.fromstring(RequestVariable)
Expand All @@ -46,7 +46,7 @@ def main():

# Запуск плагина
logging.info(f'Start {lang} {plugin} {login}')
dbengine.flags('setunic',f'{lang}_{plugin}_{login}','start') # выставляем флаг о начале запроса
dbengine.flags('setunic', f'{lang}_{plugin}_{login}', 'start') # выставляем флаг о начале запроса
try:
storename = re.sub(r'\W', '_', f'{lang}_{plugin}_{login}')
pkey = (login, f'{lang}_{plugin}') # Пара (номер, оператор)
Expand All @@ -57,7 +57,7 @@ def main():
exception_text = f'Ошибка при вызове модуля \n{plugin}: {store.exception_text()}'
logging.error(exception_text)
sys.stdout.write(exception_text)
dbengine.flags('set',f'{lang}_{plugin}_{login}','error call') # выставляем флаг о ошибке вызова
dbengine.flags('set', f'{lang}_{plugin}_{login}', 'error call') # выставляем флаг о ошибке вызова
return -1
# Готовим результат
try:
Expand All @@ -66,9 +66,9 @@ def main():
exception_text = f'Ошибка при подготовке результата: {store.exception_text()}'
logging.error(exception_text)
sys.stdout.write(exception_text)
dbengine.flags('set',f'{lang}_{plugin}_{login}','error result') # выставляем флаг о плохом результате]
dbengine.flags('set', f'{lang}_{plugin}_{login}', 'error result') # выставляем флаг о плохом результате]
return -1
dbengine.flags('delete',f'{lang}_{plugin}_{login}','start') # запрос завершился успешно - сбрасываем флаг
dbengine.flags('delete', f'{lang}_{plugin}_{login}', 'start') # запрос завершился успешно - сбрасываем флаг
try:
# пишем в базу
dbengine.write_result_to_db(f'{lang}_{plugin}', login, result)
Expand Down
Loading

0 comments on commit 7713dd6

Please sign in to comment.