docuware_export.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. """Export a bounded selection into the offline DocuWare manifest format."""
  2. import argparse
  3. import hashlib
  4. import json
  5. import logging
  6. from pathlib import Path
  7. from .docuware_search import LiveAdapter
  8. from .logging_setup import run_cli
  9. logger = logging.getLogger(__name__)
  10. def export(adapter, tenant, searches, output):
  11. output = Path(output)
  12. output.mkdir(parents=True, exist_ok=True)
  13. if (output / "manifest.json").exists():
  14. raise ValueError("Ziel enthält bereits einen Export; bitte neuen Ausgabeordner verwenden")
  15. documents, errors = {}, []
  16. for search in searches:
  17. try:
  18. for doc in adapter.search(tenant, **search):
  19. documents[(doc["archive"], doc["id"])] = {**doc, "client_db": tenant}
  20. except Exception as exc:
  21. errors.append(dict(stage="search", error=type(exc).__name__))
  22. for (archive, id_), doc in documents.items():
  23. # Hash both identifiers so archive labels cannot become paths.
  24. stem = hashlib.sha256(f"{archive}\0{id_}".encode()).hexdigest()
  25. try:
  26. data, media, name = adapter.download(tenant, doc)
  27. if media.split(";", 1)[0].lower() != "application/pdf":
  28. raise ValueError("PDF erwartet")
  29. filename = stem + ".pdf"
  30. (output / filename).write_bytes(data)
  31. doc.update(file=filename, name=name)
  32. except Exception as exc:
  33. errors.append(dict(stage="download", archive=archive, id=id_, error=type(exc).__name__))
  34. manifest = dict(version=1, documents=list(documents.values()), errors=errors)
  35. (output / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
  36. return manifest
  37. def main():
  38. parser = argparse.ArgumentParser(description=__doc__)
  39. parser.add_argument("--client-db", required=True, choices=["1", "2", "3", "4"])
  40. parser.add_argument("--document-no", action="append", default=[])
  41. parser.add_argument("--customer-no")
  42. parser.add_argument("--vehicle-no")
  43. parser.add_argument("--output", required=True)
  44. args = parser.parse_args()
  45. searches = [dict(document_no=n) for n in args.document_no]
  46. if args.customer_no:
  47. searches.append(dict(customer_no=args.customer_no))
  48. if args.vehicle_no:
  49. searches.append(dict(vehicle_no=args.vehicle_no))
  50. if not searches:
  51. parser.error("Mindestens ein Beleg-, Kunden- oder Fahrzeugfilter ist erforderlich")
  52. result = export(LiveAdapter(), args.client_db, searches, args.output)
  53. logger.info(
  54. "Export abgeschlossen: %s Dokumente, %s Fehler. Bericht: manifest.json",
  55. len(result["documents"]),
  56. len(result["errors"]),
  57. )
  58. return 1 if result["errors"] else 0
  59. if __name__ == "__main__":
  60. def run():
  61. raise SystemExit(main())
  62. run_cli(run)