"""Add-project flow: name + optional description.""" import logging from app import drafts as draft_store from app import observability as obs from app.api.client import ApiError from app.drafts import ( KIND_PROJECT, merge_project_patch, new_project_payload, project_missing, ) from app.keyboards import simple_review_keyboard from app.texts import ERR_GENERIC, esc, project_committed_report, project_review_text logger = logging.getLogger("bot.project") async def handle_entities(ctx, chat_id, user, patch: dict, source: str | None, trace_id: str | None = None) -> None: draft = await draft_store.get_active(chat_id, KIND_PROJECT) if draft is not None: payload = draft.payload merge_project_patch(payload, patch) else: payload = new_project_payload() merge_project_patch(payload, patch) draft = await draft_store.create(chat_id, KIND_PROJECT, payload, source=source) await obs.link_draft(trace_id, draft.id, draft.kind) await draft_store.save_payload(draft, missing={"fields": project_missing(payload)}) await render(ctx, chat_id, draft, fresh=draft.review_message_id is None, trace_id=trace_id) def _keyboard(payload: dict, draft_id: str) -> dict: return simple_review_keyboard(draft_id, KIND_PROJECT, []) async def render(ctx, chat_id, draft, fresh: bool = True, trace_id: str | None = None) -> None: payload = draft.payload missing = project_missing(payload) text = project_review_text(payload, missing) keyboard = _keyboard(payload, draft.id) if fresh: message = await ctx.send(chat_id, text, reply_markup=keyboard) draft.review_message_id = (message or {}).get("message_id") await draft_store.save_payload(draft, missing={"fields": missing}, review_message_id=draft.review_message_id) else: await ctx.edit(chat_id, draft.review_message_id, text, reply_markup=keyboard) await draft_store.save_payload(draft, missing={"fields": missing}) await obs.event(trace_id or await obs.latest_trace_for_draft(draft.id), "draft.render", payload={"fresh": fresh, "missing": [m.get("key") for m in missing if not m.get("ok")]}) async def commit(ctx, chat_id, user, draft, trace_id: str | None = None) -> None: payload = draft.payload trace_id = trace_id or await obs.latest_trace_for_draft(draft.id) missing = {m["key"]: m["ok"] for m in project_missing(payload)} if not missing.get("name"): await ctx.send(chat_id, "ثبت ممکن نیست؛ نام پروژه مشخص نشده است.") return body = {"name": payload["name"]} if payload.get("description"): body["description"] = payload["description"] try: created = await ctx.api.create_project(body, actor_id=user.actor_id) except ApiError as exc: await ctx.send(chat_id, ERR_GENERIC.format(detail=esc(exc.message))) return except Exception: logger.exception("create_project failed") await ctx.send(chat_id, ERR_GENERIC.format(detail="خطای سرور")) return await draft_store.set_status(draft.id, "committed") await obs.event(trace_id, "draft.commit", payload={"kind": "project", "project_id": (created or {}).get("id")}) await obs.set_trace_status(trace_id, "committed") ctx.resolver.invalidate() await ctx.send(chat_id, project_committed_report(created))