feat: update feishu intake
This commit is contained in:
@@ -26,6 +26,9 @@ from typing import Any
|
||||
from yaml_subset import DuplicateKeyError, YamlSubsetError, load_json_unique, load_yaml_subset, make_unique_pyyaml_loader
|
||||
|
||||
REQUIRED_FIELDS = ("title", "actual", "expected", "stepsToReproduce", "acceptance", "priority", "attachments", "updatedAt")
|
||||
SOURCE_FACT_FIELDS = ("title", "actual", "expected", "updatedAt")
|
||||
COORDINATOR_FIELDS = ("steps", "acceptance", "priority")
|
||||
BUG_CONTENT_FIELDS = ("title", "actual", "expected", *COORDINATOR_FIELDS)
|
||||
MAX_PAGES = 100
|
||||
MAX_RECORDS = 10_000
|
||||
PAGE_SIZE = 100
|
||||
@@ -444,6 +447,7 @@ def source_ref(config: dict[str, Any], record_id: str) -> str:
|
||||
def fetch(config: dict[str, Any], output_dir: Path | None) -> dict[str, Any]:
|
||||
profile_check(config)
|
||||
prepared: list[tuple[dict[str, Any], list[tuple[dict[str, Any], str]]]] = []
|
||||
batch_warnings: list[dict[str, str]] = []
|
||||
total_attachments = 0
|
||||
total_attachment_bytes = 0
|
||||
for record_id, row in fetch_pages(config):
|
||||
@@ -456,9 +460,15 @@ def fetch(config: dict[str, Any], output_dir: Path | None) -> dict[str, Any]:
|
||||
if total_attachment_bytes > MAX_TOTAL_ATTACHMENT_BYTES:
|
||||
raise IntakeError("batch exceeded the attachment byte limit")
|
||||
record = {"sourceRef": source_ref(config, record_id), "recordId": record_id, "updatedAt": text(cells["updatedAt"]), "title": text(cells["title"]), "actual": text(cells["actual"]), "expected": text(cells["expected"]), "steps": text(cells["stepsToReproduce"]), "acceptance": text(cells["acceptance"]), "priority": text(cells["priority"]), "attachments": [metadata for metadata, _ in attachment_data], "warnings": []}
|
||||
for field in ("title", "actual", "expected", "steps", "acceptance", "priority", "updatedAt"):
|
||||
if not attachment_data and not any(record[field] for field in BUG_CONTENT_FIELDS):
|
||||
batch_warnings.append({"recordId": record_id, "code": "blank_record_skipped"})
|
||||
continue
|
||||
for field in SOURCE_FACT_FIELDS:
|
||||
if not record[field]:
|
||||
raise IntakeError(f"record {field} must not be empty")
|
||||
record["enrichmentRequired"] = [
|
||||
field for field in COORDINATOR_FIELDS if not record[field]
|
||||
]
|
||||
prepared.append((record, attachment_data))
|
||||
download_root: Path | None = None
|
||||
if output_dir is not None:
|
||||
@@ -483,10 +493,10 @@ def fetch(config: dict[str, Any], output_dir: Path | None) -> dict[str, Any]:
|
||||
attachment["size"], min(60, remaining),
|
||||
)
|
||||
records.append(record)
|
||||
return {"provider": "feishu-base", "profile": config["profile"], "tableId": config["tableId"], "viewId": config["viewId"], "records": records}
|
||||
return {"provider": "feishu-base", "profile": config["profile"], "tableId": config["tableId"], "viewId": config["viewId"], "records": records, "warnings": batch_warnings}
|
||||
|
||||
|
||||
def plan_actions(board: dict[str, Any], records: list[dict[str, Any]]) -> list[dict[str, str]]:
|
||||
def plan_actions(board: dict[str, Any], records: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""Plan idempotent Coordinator actions without mutating the task board."""
|
||||
tasks = board.get("tasks")
|
||||
if not isinstance(tasks, list):
|
||||
@@ -513,7 +523,7 @@ def plan_actions(board: dict[str, Any], records: list[dict[str, Any]]) -> list[d
|
||||
raise IntakeError("task board contains duplicate Feishu source references")
|
||||
existing[ref] = task
|
||||
|
||||
actions: list[dict[str, str]] = []
|
||||
actions: list[dict[str, Any]] = []
|
||||
seen_records: set[str] = set()
|
||||
for record in records:
|
||||
ref = record.get("sourceRef")
|
||||
@@ -530,22 +540,30 @@ def plan_actions(board: dict[str, Any], records: list[dict[str, Any]]) -> list[d
|
||||
seen_records.add(ref)
|
||||
task = existing.get(ref)
|
||||
if task is None:
|
||||
actions.append({"sourceRef": ref, "recordId": record_id, "action": "create"})
|
||||
planned_action: dict[str, Any] = {"sourceRef": ref, "recordId": record_id, "action": "create"}
|
||||
enrichment_required = record.get("enrichmentRequired")
|
||||
if enrichment_required:
|
||||
planned_action["enrichmentRequired"] = enrichment_required
|
||||
actions.append(planned_action)
|
||||
continue
|
||||
source = task["source"]
|
||||
if source["updatedAt"] == updated_at:
|
||||
action = "unchanged"
|
||||
action_name = "unchanged"
|
||||
elif task["status"] == "open":
|
||||
action = "refresh"
|
||||
action_name = "refresh"
|
||||
else:
|
||||
action = "drift"
|
||||
actions.append({
|
||||
action_name = "drift"
|
||||
planned_action = {
|
||||
"sourceRef": ref,
|
||||
"recordId": record_id,
|
||||
"taskId": task["id"],
|
||||
"status": task["status"],
|
||||
"action": action,
|
||||
})
|
||||
"action": action_name,
|
||||
}
|
||||
enrichment_required = record.get("enrichmentRequired")
|
||||
if enrichment_required and action_name == "refresh":
|
||||
planned_action["enrichmentRequired"] = enrichment_required
|
||||
actions.append(planned_action)
|
||||
return actions
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user