"""Заполнить зеркало тестового хаба фикстурой доски и записать токены рядом с базой (для e2e и npm run dev). Пишет в базу напрямую (mirror.apply_state, set_note), до запуска serve: гонки с первым запросом нет. Дополнения только для тестового хаба (описание, комментарии, команды) — из seed-extras.json рядом со скриптом.""" import json import sys from datetime import date, timedelta from pathlib import Path from sdlc_hub.core import db, paths, settings from sdlc_hub.modules.jira import mirror, projects as jira_projects from sdlc_hub.modules.wiki import paths as wiki_paths, search as wiki_search, sync as wiki_sync snap = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) extras_path = Path(__file__).with_name("seed-extras.json") extras = json.loads(extras_path.read_text(encoding="utf-8")) if extras_path.exists() else {} extra_issues = extras.get("issues", {}) conn = db.connect(paths.db_path()) settings.ensure_defaults(conn) def own_labels(i): # свои задачи (не соседи) принадлежат проекту AWPCA: метка нужна для разреза GET /board?project=AWPCA labels = list(i.get("labels") or []) return labels if i["neighbor"] or "AWPCA" in labels else ["AWPCA", *labels] issues = [{k: v for k, v in i.items() if k not in ("work_kind", "neighbor")} | {"id": i["key"]} | {"labels": own_labels(i)} | extra_issues.get(i["key"], {}) for i in snap["issues"]] links = [{"source_key": l["source"], "target_key": l["target"], "link_type": l["link_type"]} for l in snap["links"]] # доски для экранов «Бэклог», «Спринт», «Kanban» (R-27): AWPCA — scrum 4216, KANBAN_E2E — kanban 5120 SPRINTS = {"AWPCA_Спринт 1": 101, "AWPCA_Спринт 2": 102, "AWPCA_Спринт 3": 103} for n, i in enumerate(issues, 1): i["rank"] = f"0|i{n:04d}:" sid = SPRINTS.get(i.get("sprint")) i["sprint_ids"] = [] if sid is None else [101, 102] if i["key"] == "AURORA-9001" else [sid] if n % 2: i["story_points"] = float(n % 5 + 1) today = date.today() iso = lambda d, hhmm: f"{d.isoformat()}T{hhmm}:00.000+03:00" now_iso = iso(today, "12:00") issues += [{"key": k, "id": k, "summary": f"Задача канбана {k[-4:]}", "issue_type": "Задача", "status": st, "priority": "Средний", "labels": ["KANBAN_E2E"], "rank": f"0|k{n:04d}:", "sprint_ids": [], **({"jira_updated": now_iso} if st == "Готово" else {})} for n, (k, st) in enumerate([("AURORA-9101", "Backlog"), ("AURORA-9102", "В работе"), ("AURORA-9103", "Готово")], 1)] jira_projects.create(conn, "KANBAN_E2E", "Канбан e2e", 5120) # WIDE_E2E: широкая scrum-доска (11 колонок, 40 задач в одной, статус вне колонок) — воспроизводит баг прокрутки (#112, T10.3) WIDE_COLS = [f"Колонка {n}" for n in range(1, 12)] wide_statuses = [f"Статус {n}" for n in range(1, 12)] wide = [] for n in range(1, 41): # 40 задач в первой колонке wide.append((f"AURORA-92{n:02d}", wide_statuses[0])) for c in range(1, 11): # по 1-3 в остальных for j in range(1, c % 3 + 2): wide.append((f"AURORA-93{c:02d}{j}", wide_statuses[c])) wide.append(("AURORA-9400", "Чужой статус")) # вне колонок -> «Без колонки» issues += [{"key": k, "id": k, "summary": f"Задача широкой доски {k[-4:]}", "issue_type": "Задача", "status": st, "priority": "Средний", "labels": ["WIDE_E2E"], "rank": f"0|w{n:04d}:", "sprint_ids": [201]} for n, (k, st) in enumerate(wide, 1)] jira_projects.create(conn, "WIDE_E2E", "Широкая доска e2e", 6100) boards = [ {"id": 4216, "type": "scrum", "kanban_backlog": None, "columns": [{"name": "К выполнению", "statuses": ["Backlog", "К выполнению", "Ожидание", "Ожидание разработки"]}, {"name": "В работе", "statuses": ["В работе"]}, {"name": "Готово", "statuses": ["Готово"]}], "sprints": [{"id": 101, "name": "AWPCA_Спринт 1", "state": "closed", "start_date": iso(today - timedelta(days=21), "09:00"), "end_date": iso(today - timedelta(days=8), "09:00"), "complete_date": iso(today - timedelta(days=8), "18:00"), "goal": None}, {"id": 102, "name": "AWPCA_Спринт 2", "state": "active", "start_date": iso(today - timedelta(days=7), "09:00"), "end_date": iso(today + timedelta(days=7), "12:00"), "complete_date": None, "goal": "Тестовая цель спринта"}, {"id": 103, "name": "AWPCA_Спринт 3", "state": "future", "start_date": None, "end_date": None, "complete_date": None, "goal": None}]}, {"id": 5120, "type": "kanban", "kanban_backlog": True, "columns": [{"name": "Бэклог", "statuses": ["Backlog"]}, {"name": "В работе", "statuses": ["В работе"]}, {"name": "Готово", "statuses": ["Готово"]}]}, {"id": 6100, "type": "scrum", "kanban_backlog": None, "columns": [{"name": n, "statuses": [st]} for n, st in zip(WIDE_COLS, wide_statuses)], "sprints": [{"id": 201, "name": "WIDE_Спринт 1", "state": "active", "start_date": iso(today - timedelta(days=3), "09:00"), "end_date": iso(today + timedelta(days=11), "12:00"), "complete_date": None, "goal": None}]}, ] mirror.apply_state(conn, issues, links, neighbor_keys=[i["key"] for i in snap["issues"] if i["neighbor"]], boards=boards) for i in snap["issues"]: note = {k: i[k] for k in ("work_kind",) if i.get(k)} if note: mirror.set_note(conn, i["key"], note, "seed") if extras.get("commands"): # завершённые команды по задачам — для раздела «Команды моста» карточки with db.transaction(conn): batch = conn.execute("INSERT INTO batch(title, author, created_at) VALUES ('Тестовые команды', 'seed', ?)", (db.now(),)).lastrowid for seq, c in enumerate(extras["commands"], 1): conn.execute("INSERT INTO command(batch_id, seq, type, params, status, finished_at, error) " "VALUES (?, ?, ?, ?, ?, ?, ?)", (batch, seq, c["type"], json.dumps(c["params"], ensure_ascii=False), c["status"], db.now(), c.get("error"))) # копия вики проекта AWPCA для e2e (тексты синтетические): корень DOC-9001 и две страницы, у одной вложение. # Собирается тем же build_copy, что и настоящая синхронизация, — без команд и без агента WIKI_PAGES = [ {"code": "DOC-9001", "title": "Тестовая вики", "parent_code": None, "orderno": 1, "url": "https://eva.test/DOC-9001", "markdown": "Корневая страница тестовой вики.\n\n[Модель данных](page:DOC-9002)\n", "attachments": []}, {"code": "DOC-9002", "title": "Модель данных", "parent_code": "DOC-9001", "orderno": 1, "url": "https://eva.test/DOC-9002", "markdown": "# Таблицы\n\n| Поле | Смысл |\n|---|---|\n| id | ключ |\n\nСогласование схемы проводится раз в квартал.\n", "attachments": []}, {"code": "DOC-9003", "title": "Регламент согласования", "parent_code": "DOC-9001", "orderno": 2, "url": "https://eva.test/DOC-9003", "markdown": "Порядок согласования описан в файле.\n\n[📎 схема.txt](attachment:@0)\n\n[К модели](page:DOC-9002)\n", "attachments": [{"url": "https://eva.test/files/schema.txt", "name": "схема.txt"}]}, ] conn.execute("INSERT OR REPLACE INTO wiki_project(label, root_code, root_title, synced_at, pages_count) VALUES ('AWPCA', 'DOC-9001', 'Тестовая вики', ?, ?)", (db.now(), len(WIKI_PAGES))) att_id = conn.execute("INSERT INTO wiki_attachment(label, page_code, url, name) VALUES ('AWPCA', 'DOC-9003', ?, 'схема.txt')", ("https://eva.test/files/schema.txt",)).lastrowid wiki_rows = wiki_sync.build_copy(WIKI_PAGES, [p["code"] for p in WIKI_PAGES], wiki_paths.project_dir(conn, "AWPCA"), {("DOC-9003", "https://eva.test/files/schema.txt"): att_id}) for r in wiki_rows: conn.execute("INSERT INTO wiki_page(label, code, eva_id, title, parent_code, orderno, depth, path, url, size, synced_at) " "VALUES ('AWPCA', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (r["code"], r["eva_id"], r["title"], r["parent_code"], r["orderno"], r["depth"], r["path"], r["url"], r["size"], r["synced_at"])) wiki_search.replace(conn, "AWPCA", [(r["code"], r["title"], r["markdown"]) for r in wiki_rows]) (paths.home() / "tokens.json").write_text(json.dumps(settings.tokens(conn)), encoding="utf-8") conn.close() print("зеркало:", len(issues), "задач,", len(links), "связей; токены —", paths.home() / "tokens.json")