import json import uuid from fastapi import APIRouter, File, Form, Response, UploadFile from app.api.deps import CurrentUser, SessionDep from app.schemas.gedcom import ImportPreview, ImportReport from app.services import gedcom, tree_service router = APIRouter(prefix="/trees", tags=["gedcom"]) @router.post("/{tree_id}/gedcom/preview", response_model=ImportPreview) async def preview_gedcom( tree_id: uuid.UUID, session: SessionDep, current: CurrentUser, file: UploadFile = File(...), ) -> ImportPreview: """Dry run: report counts and incoming people that look like duplicates of existing ones, so the user can choose how to resolve each before importing.""" tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id) text = (await file.read()).decode("utf-8", errors="replace") report = await gedcom.preview_gedcom(session, actor=current, tree=tree, text=text) return ImportPreview(**report) @router.post("/{tree_id}/gedcom/import", response_model=ImportReport) async def import_gedcom( tree_id: uuid.UUID, session: SessionDep, current: CurrentUser, file: UploadFile = File(...), default_action: str = Form("new"), resolutions: str = Form("{}"), ) -> ImportReport: """Import a GEDCOM. ``default_action`` (new|skip|merge|overwrite) applies to incoming people that match an existing one; ``resolutions`` is a JSON object {xref: {action, target_id}} overriding it per record.""" tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id) text = (await file.read()).decode("utf-8", errors="replace") try: parsed = json.loads(resolutions or "{}") except json.JSONDecodeError: parsed = {} report = await gedcom.import_gedcom( session, actor=current, tree=tree, text=text, default_action=default_action, resolutions=parsed, ) return ImportReport(**report) @router.get("/{tree_id}/gedcom/export") async def export_gedcom( tree_id: uuid.UUID, session: SessionDep, current: CurrentUser ) -> Response: tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id) text = await gedcom.export_gedcom(session, viewer_id=current.id, tree=tree) safe = "".join(c for c in tree.name if c.isalnum() or c in " -_").strip() or "tree" return Response( content=text, media_type="text/plain", headers={"Content-Disposition": f'attachment; filename="{safe}.ged"'}, )