155 lines
5.2 KiB
Python
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())
|