Files
llm/scripts/render_1c_task_proposal_markdown.py

155 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Render a 1C task change proposal JSON as a compact Markdown report."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
def load_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8-sig"))
def line(text: str = "") -> str:
return text.rstrip() + "\n"
def bullet(text: str) -> str:
return f"- {text}\n"
def fmt_origin(origin: dict[str, Any] | None) -> str:
if not origin:
return ""
if origin.get("extension"):
return f"{origin.get('layer')}:{origin.get('extension')}"
return str(origin.get("layer") or "")
def fmt_path(path: str | None, line_no: Any = None) -> str:
if not path:
return ""
suffix = f":{line_no}" if line_no else ""
return f"`{path}{suffix}`"
def render_target(target: dict[str, Any]) -> str:
path = target.get("path") or target.get("module_path")
bits = [str(target.get("kind") or ""), str(target.get("name") or "")]
origin = fmt_origin(target.get("origin"))
if origin:
bits.append(f"({origin})")
rendered = " ".join(bit for bit in bits if bit)
if path:
rendered += f" -> {fmt_path(path, target.get('line'))}"
return rendered
def render_proposal(proposal: dict[str, Any], index: int) -> str:
out = ""
obj = proposal.get("object") or {}
out += line(f"## Proposal {index}: {obj.get('kind')}.{obj.get('name')}")
if obj.get("synonym"):
out += line(f"Synonym: {obj.get('synonym')}")
out += line()
strategy = proposal.get("write_strategy") or {}
out += line("### Strategy")
out += bullet(f"Mode: `{strategy.get('mode')}`")
out += bullet(f"Preferred extension: `{strategy.get('preferred_extension')}`")
out += bullet(f"Reason: {strategy.get('reason')}")
out += line()
state = proposal.get("existing_state") or {}
commands = state.get("found_form_commands_or_items") or []
attrs = state.get("found_metadata_attributes") or []
code_hits = state.get("found_code_hits") or []
if commands:
out += line("### Existing Form Commands/Items")
for row in commands[:10]:
out += bullet(
f"{row.get('area')} `{row.get('name')}` on `{row.get('form')}` "
f"({fmt_origin(row.get('origin'))}) {fmt_path(row.get('path'), row.get('line'))}"
)
out += line()
if attrs:
out += line("### Related Metadata Attributes")
for row in attrs[:10]:
out += bullet(f"`{row.get('name')}` ({fmt_origin(row.get('origin'))})")
out += line()
if code_hits:
out += line("### Code Hits")
for row in code_hits[:12]:
out += bullet(f"`{row.get('name')}` ({fmt_origin(row.get('origin'))}) {fmt_path(row.get('path'), row.get('line'))}")
out += line()
policy = proposal.get("target_policy") or {}
write_candidates = policy.get("write_candidates") or []
references = policy.get("read_only_reference_files") or []
out += line("### Write Candidates")
if write_candidates:
for target in write_candidates[:20]:
out += bullet(render_target(target))
else:
out += bullet("No write candidate selected.")
out += line()
out += line("### Read-Only References")
for target in references[:20]:
out += bullet(render_target(target))
if len(references) > 20:
out += bullet(f"... {len(references) - 20} more")
out += line()
out += line("### Implementation Steps")
for step in proposal.get("implementation_steps") or []:
out += bullet(f"{step.get('order')}. `{step.get('action')}` - {step.get('purpose') or ''}")
out += line()
out += line("### Validation")
for check in proposal.get("validation_checks") or []:
out += bullet(f"`{check.get('name')}` - {check.get('description')}")
questions = proposal.get("open_questions") or []
if questions:
out += line()
out += line("### Open Questions")
for question in questions:
out += bullet(question)
return out
def render(data: dict[str, Any]) -> str:
out = ""
out += line("# 1C Task Change Proposal")
out += line()
out += line(f"Task: {((data.get('task') or {}).get('text') or '').strip()}")
out += line(f"Schema: `{data.get('schema')}`")
out += line(f"View: `{data.get('view')}`")
safety = data.get("safety") or {}
out += line(f"Safety: `{safety.get('mode')}`, `{safety.get('write_status')}`")
out += line()
for idx, proposal in enumerate(data.get("proposals") or [], start=1):
out += render_proposal(proposal, idx)
out += line()
return out
def main() -> int:
parser = argparse.ArgumentParser(description="Render 1C task proposal Markdown.")
parser.add_argument("--proposal", type=Path, required=True)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
markdown = render(load_json(args.proposal))
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(markdown, encoding="utf-8")
print(json.dumps({"output": str(args.output)}, ensure_ascii=False))
else:
print(markdown)
return 0
if __name__ == "__main__":
raise SystemExit(main())