Andrey Epifancev a88bb1e6e8 refactor(sdlc-rename): пакет sdlc_hub, CLI sdlc-hub, каталог данных sdlc-hub
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 11:27:17 +04:00

89 lines
4.5 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_root, значения для GET /projects, хуки создания и правки проекта."""
import glob
import logging
import shutil
from ...core import db, queue
from ...core.modules import ProjectField
from . import commands, paths
from .roots import parse_root, parse_root_edit # noqa: F401 parse_root — прежнее имя для импортёров
log = logging.getLogger(__name__)
FIELDS = (ProjectField("wiki_root", parse_root, editable=True, validate_edit=parse_root_edit),)
def view(conn, labels: list[str]) -> dict[str, dict]:
"""Поля проекта для списка меток; без корня все значения None."""
if not labels:
return {}
marks = ", ".join("?" * len(labels))
rows = {r["label"]: r for r in conn.execute(f"SELECT * FROM wiki_project WHERE label IN ({marks})", labels)}
out = {}
for label in labels:
r = rows.get(label)
out[label] = {"wiki_root": r["root_code"] if r else None, "wiki_root_title": r["root_title"] if r else None,
"wiki_pages": r["pages_count"] if r else None, "wiki_synced_at": r["synced_at"] if r else None,
"wiki_sync_error": r["sync_error"] if r else None}
return out
def on_created(conn, project, fields, author) -> dict | None:
"""Проект создан (в его транзакции): с корнем — строка wiki_project и пакет синхронизации."""
root = fields.get("wiki_root")
if root is None:
return None
conn.execute("INSERT INTO wiki_project(label, root_code) VALUES (?, ?)", (project["label"], root))
return commands.enqueue_sync(conn, project["label"], root, author)
def _cancel_queued(conn, label: str) -> None:
"""Отменить пакеты wiki_sync проекта, ни одна команда которых ещё не выдана."""
ids = [r["id"] for r in conn.execute(
"SELECT b.id FROM batch b WHERE b.project = ? AND b.cancelled_at IS NULL "
"AND EXISTS (SELECT 1 FROM command c WHERE c.batch_id = b.id AND c.type = 'wiki_sync' AND c.status = 'queued') "
"AND NOT EXISTS (SELECT 1 FROM command c WHERE c.batch_id = b.id AND c.status NOT IN ('queued'))", (label,))]
for batch_id in ids:
queue.cancel_batch(conn, batch_id)
def _drop_copy(conn, label: str) -> None:
"""Сброс корня: строки проекта, индекс поиска (если он уже создан) и каталог копии."""
if db.has_table(conn, "wiki_page_fts"):
conn.execute("DELETE FROM wiki_page_fts WHERE label = ?", (label,))
conn.execute("DELETE FROM wiki_page WHERE label = ?", (label,))
conn.execute("DELETE FROM wiki_attachment WHERE label = ?", (label,))
conn.execute("DELETE FROM wiki_project WHERE label = ?", (label,))
try:
base = paths.project_dir(conn, label)
except RuntimeError: # база без файла (тесты в памяти): копии на диске нет
return
leftovers = sorted(base.parent.glob(glob.escape(f".{base.name}") + ".new*")) if base.parent.is_dir() else []
for d in (base, *leftovers, base.with_name(f".{base.name}.old")):
try:
shutil.rmtree(d)
except FileNotFoundError:
pass
except OSError as e:
log.warning("вики: каталог %s не удалён: %s", d, e)
def on_updated(conn, project, fields, author) -> list[dict]:
"""Корень изменён (в транзакции PATCH): невыданные пакеты проекта отменяются; новый корень — пакет синхронизации,
прежняя копия остаётся до успеха; None — копия, страницы, индекс и вложения проекта удаляются."""
if "wiki_root" not in fields:
return []
label, root = project["label"], fields["wiki_root"]
row = conn.execute("SELECT root_code FROM wiki_project WHERE label = ?", (label,)).fetchone()
if (row["root_code"] if row else None) == root:
return []
_cancel_queued(conn, label)
if root is None:
_drop_copy(conn, label)
return []
if row is None:
conn.execute("INSERT INTO wiki_project(label, root_code) VALUES (?, ?)", (label, root))
else:
conn.execute("UPDATE wiki_project SET root_code = ? WHERE label = ?", (root, label))
return [commands.enqueue_sync(conn, label, root, author)]