geforkt von antekov/mt5-manager
- оформить комментарии и заголовки функций по замечаниям модератора - обновить листинги и версию сервиса до 1.01 - подготовить MQL5.zip без EX5 и личных реквизитов - сохранить исправленную редакцию статьи и правила оформления
'''Собирает MQL5.zip для четвёртой части из тега p4.
| |
| |
Исходник сервиса берётся из рабочего дерева только после проверки, что от p4
| |
отличаются лишь комментарии, пробелы и версия. Личные файлы не читаются.
| |
Запуск: python docs/articles/part-04/publish/build_archive.py
| |
'''
| |
| |
import json
| |
import re
| |
import subprocess
| |
from pathlib import Path
| |
from zipfile import ZIP_DEFLATED, ZipFile
| |
| |
ROOT = Path(__file__).resolve().parents[4]
| |
OUTPUT = Path(__file__).resolve().parent / 'MQL5.zip'
| |
PREFIX = 'MQL5/Shared Projects/mt5-manager/'
| |
BASE = 'p4'
| |
| |
| |
def git(*args):
| |
return subprocess.run(['git', *args], cwd=ROOT, capture_output=True, check=True).stdout
| |
| |
| |
def executable(text):
| |
text = re.sub(r'^#property version[^\n]*', '', text, flags=re.M)
| |
parts = re.compile(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|//[^\n]*|/\*[\s\S]*?\*/')
| |
text = parts.sub(lambda m: '' if m.group().startswith(('//', '/*')) else m.group(), text)
| |
return re.findall(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|\w+|[^\s]', text)
| |
| |
| |
def build():
| |
baseline = git('show', f'{BASE}:ServiceStateWriter.mq5').decode('utf-8-sig')
| |
service = (ROOT / 'ServiceStateWriter.mq5').read_text(encoding='utf-8-sig')
| |
if executable(baseline) != executable(service):
| |
raise RuntimeError('Сервис отличается от p4 по логике. Для архива четвёртой части нужен её исходник.')
| |
paths = git('ls-tree', '-r', '--name-only', BASE).decode().splitlines()
| |
paths = [name for name in paths if name in {
| |
'LICENSE', 'README.md', 'ServiceStateWriter.mq5', 'config.json', 'config.py',
| |
'deploy_service.py', 'extracticon.py', 'main.py', 'mt5_control.py', 'run.py'}
| |
or name.startswith(('static/', 'templates/'))]
| |
files = {name: git('show', f'{BASE}:{name}') for name in paths}
| |
files['ServiceStateWriter.mq5'] = service.encode('utf-8')
| |
config = json.loads(files['config.json'].decode('utf-8-sig'))
| |
config['mt5_folder'] = 'C:/MT5'
| |
config['database'] = 'C:/Users/USERNAME/AppData/Roaming/MetaQuotes/Terminal/Common/Files/mt5-manager.sqlite'
| |
for name, record in config['terminals'].items():
| |
record['login'] = ''
| |
record['server'] = ''
| |
record.pop('password', None)
| |
record.pop('path', None)
| |
files['config.json'] = (json.dumps(config, ensure_ascii=False, indent=2) + '\n').encode('utf-8')
| |
note = '''# Архив к четвёртой части
| |
| |
Распакуйте MQL5.zip в папку данных терминала. Проект появится в
| |
MQL5/Shared Projects/mt5-manager. Для portable-режима это папка установки MT5.
| |
| |
Перед запуском исправьте database в config.json: замените USERNAME на имя
| |
пользователя Windows. Проверьте mt5_folder и имена уже установленных терминалов.
| |
Логины и серверы в примере оставлены пустыми; используются счета, сохранённые MT5.
| |
| |
Откройте ServiceStateWriter.mq5 в MetaEditor и скомпилируйте его. Полученный EX5
| |
будет расположен в MQL5/Services/Shared Projects/mt5-manager. Скопируйте EX5 по
| |
этому же пути в остальные терминалы и static/services.ini в их папки config.
| |
Либо используйте deploy_service.py, как описано в статье.
| |
| |
Архив содержит код версии p4 с исправленным оформлением сервиса 1.01.
| |
Возможности пятой части в него не включены. Исполняемые EX5 не распространяются.
| |
| |
---
| |
| |
'''
| |
files['README.md'] = note.encode('utf-8') + files['README.md']
| |
with ZipFile(OUTPUT, 'w', compression=ZIP_DEFLATED) as archive:
| |
for name, data in sorted(files.items()):
| |
if name.lower().endswith('.ex5'):
| |
raise RuntimeError('EX5 запрещён в архиве публикации.')
| |
archive.writestr(PREFIX + name, data)
| |
with ZipFile(OUTPUT) as archive:
| |
assert archive.testzip() is None
| |
assert all(name.startswith('MQL5/') for name in archive.namelist())
| |
assert not any(name.lower().endswith('.ex5') for name in archive.namelist())
| |
print(f'MQL5.zip: {len(files)} файлов, {OUTPUT.stat().st_size} байт; база p4, без EX5 и личных реквизитов.')
| |
| |
| |
if __name__ == '__main__':
| |
build()
|