refactor: remove XML export, JSON-only for .rfflow files
- Remove XML builder, format query param, and XML tests - Simplify ExportFlowModal (no format picker) - Simplify rfflowParser (JSON-only) - Remove format field from schemas and types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
"""Flow export/import endpoints (.rfflow files)."""
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from datetime import datetime, timezone
|
||||
from typing import Annotated, Optional
|
||||
from uuid import UUID
|
||||
@@ -43,45 +41,6 @@ def _slugify(name: str) -> str:
|
||||
return re.sub(r'[-\s]+', '-', slug)
|
||||
|
||||
|
||||
def _build_xml(envelope: FlowExportEnvelope) -> str:
|
||||
"""Build XML representation of an .rfflow export."""
|
||||
root = ET.Element("rfflow")
|
||||
root.set("version", envelope.rfflow_version)
|
||||
|
||||
ET.SubElement(root, "exported_at").text = envelope.exported_at.isoformat()
|
||||
ET.SubElement(root, "source_app").text = envelope.source_app
|
||||
ET.SubElement(root, "format").text = "xml"
|
||||
|
||||
flow_el = ET.SubElement(root, "flow")
|
||||
flow = envelope.flow
|
||||
|
||||
ET.SubElement(flow_el, "name").text = flow.name
|
||||
ET.SubElement(flow_el, "description").text = flow.description or ""
|
||||
ET.SubElement(flow_el, "tree_type").text = flow.tree_type
|
||||
ET.SubElement(flow_el, "version").text = str(flow.version)
|
||||
ET.SubElement(flow_el, "author_name").text = flow.author_name or ""
|
||||
|
||||
if flow.category:
|
||||
cat_el = ET.SubElement(flow_el, "category")
|
||||
ET.SubElement(cat_el, "name").text = flow.category.name
|
||||
ET.SubElement(cat_el, "slug").text = flow.category.slug
|
||||
|
||||
tags_el = ET.SubElement(flow_el, "tags")
|
||||
for tag in flow.tags:
|
||||
ET.SubElement(tags_el, "tag").text = tag
|
||||
|
||||
# Store tree_structure as JSON string in CDATA-safe text
|
||||
ts_el = ET.SubElement(flow_el, "tree_structure")
|
||||
ts_el.text = json.dumps(flow.tree_structure)
|
||||
|
||||
if flow.intake_form:
|
||||
if_el = ET.SubElement(flow_el, "intake_form")
|
||||
if_el.text = json.dumps(flow.intake_form)
|
||||
|
||||
ET.indent(root)
|
||||
return ET.tostring(root, encoding="unicode", xml_declaration=True)
|
||||
|
||||
|
||||
# --- Export ---
|
||||
|
||||
@router.get("/{tree_id}/export")
|
||||
@@ -89,9 +48,8 @@ async def export_tree(
|
||||
tree_id: UUID,
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
current_user: Annotated[User, Depends(get_current_active_user)],
|
||||
format: str = Query("json", pattern="^(json|xml)$"),
|
||||
):
|
||||
"""Export a tree as a downloadable .rfflow file (JSON or XML)."""
|
||||
"""Export a tree as a downloadable .rfflow JSON file."""
|
||||
# Load tree with relationships + author name
|
||||
result = await db.execute(
|
||||
select(Tree)
|
||||
@@ -139,25 +97,15 @@ async def export_tree(
|
||||
rfflow_version="1.0",
|
||||
exported_at=datetime.now(timezone.utc),
|
||||
source_app="ResolutionFlow",
|
||||
format=format,
|
||||
flow=flow_data,
|
||||
)
|
||||
|
||||
slug = _slugify(tree.name)
|
||||
|
||||
# Audit log
|
||||
await log_audit(db, current_user.id, "tree.export", "tree", tree.id, {"format": format})
|
||||
await log_audit(db, current_user.id, "tree.export", "tree", tree.id)
|
||||
await db.commit()
|
||||
|
||||
if format == "xml":
|
||||
content = _build_xml(envelope)
|
||||
return Response(
|
||||
content=content,
|
||||
media_type="application/xml",
|
||||
headers={"Content-Disposition": f'attachment; filename="{slug}.rfflow"'},
|
||||
)
|
||||
|
||||
# JSON
|
||||
content = envelope.model_dump_json(indent=2)
|
||||
return Response(
|
||||
content=content,
|
||||
|
||||
Reference in New Issue
Block a user