Andrey Epifancev 07357732da Хвост пакета со спринтом — refresh по JQL проекта (#108)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 19:23:46 +04:00

104 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Команды модуля wiki: wiki_sync (синхронизация дерева проекта) и wiki_fetch (файл вложения). Обе читают Eva."""
import re
from ...core import queue
from ...core.errors import CommandError
from ...core.modules import CommandHooks
from . import attachments, sync
TYPES = {"wiki_sync", "wiki_fetch"}
SPEC = {"wiki_sync": {"required": ["label", "root"], "optional": []},
"wiki_fetch": {"required": ["attachment_id", "url"], "optional": []}}
_ROOT = re.compile(r"^DOC-\d+$")
def validate(cmds) -> list[dict]:
if not isinstance(cmds, list) or not cmds:
raise CommandError("пакет без команд")
out = []
for i, cmd in enumerate(cmds, 1):
if not isinstance(cmd, dict) or "type" not in cmd:
raise CommandError(f"команда {i}: нет type")
ctype, params = cmd["type"], cmd.get("params") or {}
if ctype not in SPEC:
raise CommandError(f"команда {i}: неизвестный тип «{ctype}»")
if not isinstance(params, dict):
raise CommandError(f"команда {i}: params не объект")
spec = SPEC[ctype]
missing = [f for f in spec["required"] if params.get(f) in (None, "")]
if missing:
raise CommandError(f"команда {i} ({ctype}): нет {', '.join(missing)}; ожидаются {', '.join(spec['required'])}")
unknown = set(params) - set(spec["required"])
if unknown:
raise CommandError(f"команда {i} ({ctype}): лишние параметры {', '.join(sorted(unknown))}; "
f"ожидаются {', '.join(spec['required'])}")
if ctype == "wiki_sync":
if not isinstance(params["label"], str):
raise CommandError(f"команда {i}: label — строка")
if not isinstance(params["root"], str) or not _ROOT.match(params["root"]):
raise CommandError(f"команда {i}: root — код DOC-<цифры>")
else:
a = params["attachment_id"]
if not isinstance(a, int) or isinstance(a, bool):
raise CommandError(f"команда {i}: attachment_id — целое")
if not isinstance(params["url"], str):
raise CommandError(f"команда {i}: url — строка")
out.append({"type": ctype, "params": params})
return out
def check_batch(conn, cmds, project, files_dir) -> list[dict]:
"""wiki_sync живёт в пакете своего проекта: project пакета равен label команды."""
for i, c in enumerate(cmds, 1):
if c["type"] == "wiki_sync" and c["params"]["label"] != project:
raise CommandError(f"команда {i}: проект пакета ({project}) не равен label ({c['params']['label']})")
return cmds
def enqueue_sync(conn, label: str, root: str, author: str) -> dict:
"""Пакет «Вики <метка>» из одной команды wiki_sync."""
return queue.create_batch(conn, f"Вики {label}", author, [{"type": "wiki_sync", "params": {"label": label, "root": root}}],
project=label)
def active_sync(conn, label: str) -> dict | None:
"""Пакет wiki_sync проекта не в терминальном статусе (есть команда queued или claimed) или None."""
row = conn.execute(
"SELECT b.id FROM batch b WHERE b.project = ? AND EXISTS (SELECT 1 FROM command c WHERE c.batch_id = b.id "
"AND c.type = 'wiki_sync' AND c.status IN ('queued', 'claimed')) ORDER BY b.id DESC LIMIT 1", (label,)).fetchone()
return queue.batch_view(conn, row["id"], with_commands=False) if row else None
def on_failed(conn, cmd, now) -> None:
(attachments if cmd["type"] == "wiki_fetch" else sync).on_failed(conn, cmd, now)
def check_result(conn, cmd, status, result, files_dir) -> tuple[str, str | None]:
if cmd["type"] == "wiki_fetch":
return attachments.check_result(conn, cmd, status, result, files_dir)
return sync.check_result(conn, cmd, status, result, files_dir)
def on_result(conn, cmd, status, result, jira_calls, rate_limited, now, files_dir) -> None:
if cmd["type"] == "wiki_fetch":
attachments.on_result(conn, cmd, status, result, now)
else:
sync.on_result(conn, cmd, status, result, jira_calls, rate_limited, now, files_dir)
def after_result(conn, cmd, status) -> None:
if cmd["type"] == "wiki_sync":
sync.after_result(conn, cmd, status)
# wiki_sync — sync.py (хуки before/abort/after только у него), wiki_fetch — attachments.py
HOOKS = CommandHooks(
types=frozenset(TYPES), read_types=frozenset(TYPES), validate=validate, check_batch=check_batch,
tail=lambda conn, cmds, project: None, pause=lambda conn, ctype: 0, deliver_params=lambda conn, cmd, files_dir: cmd["params"],
temp_definer=None, temp_keys_used=lambda cmd: [], substitute=lambda ctype, params, mapping: params,
drop_unresolved=lambda cmd, unresolved: unresolved, retry_params=lambda cmd: cmd["params"],
check_confirmed=lambda conn, cmd: (False, None), on_claim=sync.on_claim, on_failed=on_failed,
check_result=check_result, on_result=on_result, after_result=after_result,
on_late_result=lambda *a: False, before_result=sync.before_result,
abort_result=sync.abort_result)