/
kochenov
/
uni
Обзор
Документация
Войти
/
kochenov
/
uni
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
scripts/gen_progress.py
154 строки
6 KB
Dmitry Kochenov
Initial commit: UMP base structure
16 июл 2026, 21:14
16 июл 2026, 21:14
86b4d0e
Код
Авторство
О чём код?
#!/usr/bin/env python3 """ Генератор прогресс-дашборда (I10). Запуск: uv run python scripts/gen_progress.py Читает STEP_STATE.md, генерирует docs/progress.md. """ import os import re from datetime import datetime BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STEP_STATE = os.path.join(BASE, 'STEP_STATE.md') OUTPUT = os.path.join(BASE, 'docs', 'progress.md') # Метаданные этапов (номер, название, шагов, тег) STAGES = [ (1, 'Foundation', 12, None), (2, 'Kernel', 11, 'v0.1.0'), (3, 'Platform Services', 5, None), (4, 'Infrastructure', 6, None), (5, 'Module SDK', 4, None), (6, 'Auth + Users', 15, None), (7, 'Frontend Core', 8, 'v0.5.0'), (8, 'Growth', 9, None), (9, 'Scaling', 13, 'v1.0.0'), (10, 'Enterprise Modules', 9, None), (11, 'Billing', 8, None), (12, 'Business Modules', 7, None), (13, 'SRE & Governance', 22, None), (14, 'Finalization', 7, 'v2.0.0'), ] def parse_step_state(): """Парсит STEP_STATE.md и возвращает (current_step, completed_steps_list, stage_archives).""" if not os.path.exists(STEP_STATE): return None, [], [] with open(STEP_STATE, encoding='utf-8') as f: content = f.read() # CURRENT_STEP — поддерживает оба формата: CURRENT_STEP=1.0 и CURRENT_STEP: 1.0 m = re.search(r'CURRENT_STEP[=:]\s*(\S+)', content) current_step = m.group(1) if m else None # COMPLETED_STEPS — извлекаем все step-id вида '1.0', '9.4a' и т.д. completed = [] in_completed = False for line in content.split('\n'): if line.startswith('## COMPLETED_STEPS'): in_completed = True continue if in_completed: if line.startswith('## ') or line.startswith('# '): in_completed = False continue # Строка таблицы: | step | name | date | docs | m = re.match(r'\|\s*([\d.]+[a-z]?)\s*\|', line) if m: completed.append(m.group(1)) # STAGE_ARCHIVES archives = [] in_archives = False for line in content.split('\n'): if line.startswith('## STAGE_ARCHIVES'): in_archives = True continue if in_archives: if line.startswith('## ') or line.startswith('# '): in_archives = False continue m = re.match(r'\|\s*(\d+)\s*\|', line) if m: archives.append(int(m.group(1))) return current_step, completed, archives def determine_stage(step_id): """По step_id вида '1.0' или '9.4a' возвращает номер этапа.""" if not step_id: return None m = re.match(r'^(\d+)\.', step_id) if m: return int(m.group(1)) return None def generate(): current_step, completed, archives = parse_step_state() now = datetime.now().strftime('%Y-%m-%d %H:%M') lines = [ '# Прогресс проекта — Universal Modular Platform', '', f'> Автогенерация: `scripts/gen_progress.py` • Обновлено: {now}', '', '## Текущий статус', '', ] if current_step: stage = determine_stage(current_step) stage_name = next((s[1] for s in STAGES if s[0] == stage), '?') lines.append(f'- **Текущий шаг:** `{current_step}`') lines.append(f'- **Этап:** {stage} — {stage_name}') lines.append(f'- **Завершено шагов:** {len(completed)} из 140') lines.append('') else: lines.append('- **Текущий шаг:** не определён (`STEP_STATE.md` отсутствует)') lines.append('') # Таблица этапов lines.append('## Этапы') lines.append('') lines.append('| # | Этап | Шагов | Завершено | Тег | Архив |') lines.append('|---|------|-------|-----------|-----|-------|') for stage_num, name, total, tag in STAGES: done = sum(1 for s in completed if determine_stage(s) == stage_num) archive_mark = '✅' if stage_num in archives else '' tag_str = tag or '—' lines.append(f'| {stage_num} | {name} | {total} | {done}/{total} | {tag_str} | {archive_mark} |') lines.append('') # Завершённые шаги (последние 10) if completed: lines.append('## Последние завершённые шаги') lines.append('') for s in completed[-10:]: lines.append(f'- `{s}`') lines.append('') # Вехи lines.append('## Вехи') lines.append('') lines.append('| Веха | Тег | Этапы | Статус |') lines.append('|------|-----|-------|--------|') milestones = [ ('v0.1 — Скелет', 'v0.1.0', '1–2', 'После этапа 02'), ('v0.5 — MVP', 'v0.5.0', '1–7', 'После этапа 07'), ('v1.0 — Production', 'v1.0.0', '1–9', 'После этапа 09'), ('v2.0 — Enterprise', 'v2.0.0', '1–14', 'После этапа 14'), ] for name, tag, stages, status in milestones: lines.append(f'| {name} | `{tag}` | {stages} | {status} |') lines.append('') os.makedirs(os.path.dirname(OUTPUT), exist_ok=True) with open(OUTPUT, 'w', encoding='utf-8') as f: f.write('\n'.join(lines)) print(f'✅ {OUTPUT} сгенерирован') if __name__ == '__main__': generate()