"""DWG-to-DXF conversion microservice via ODA File Converter.""" import glob import os import shutil import subprocess import tempfile import uuid from flask import Flask, request, send_file, jsonify app = Flask(__name__) MAX_FILE_SIZE = 100 * 1024 * 1024 # 100 MB # ODA File Converter — try common install paths ODA_BIN = "/usr/local/bin/ODAFileConverter" for _p in ["/usr/local/bin/ODAFileConverter", "/usr/bin/ODAFileConverter", "/opt/ODAFileConverter/ODAFileConverter", "/opt/oda-file-converter/ODAFileConverter"]: if os.path.isfile(_p): ODA_BIN = _p break @app.route("/health", methods=["GET"]) def health(): """Health check — verifies ODA File Converter is installed.""" if os.path.isfile(ODA_BIN) and os.access(ODA_BIN, os.X_OK): return jsonify({"status": "ok", "converter": "ODAFileConverter"}), 200 return jsonify({"status": "error", "detail": "ODAFileConverter not found"}), 503 @app.route("/convert", methods=["POST"]) def convert(): """Accept a DWG file via multipart upload, return DXF.""" if "file" not in request.files: return jsonify({"error": "Missing 'file' field in upload."}), 400 uploaded = request.files["file"] original_name = uploaded.filename or "input.dwg" if not original_name.lower().endswith(".dwg"): return jsonify({"error": "File must have .dwg extension."}), 400 safe_name = "".join( c if c.isalnum() or c in "._-" else "_" for c in original_name ) work_dir = os.path.join(tempfile.gettempdir(), f"dwg-{uuid.uuid4().hex}") input_dir = os.path.join(work_dir, "in") output_dir = os.path.join(work_dir, "out") os.makedirs(input_dir, exist_ok=True) os.makedirs(output_dir, exist_ok=True) input_path = os.path.join(input_dir, safe_name) try: uploaded.save(input_path) file_size = os.path.getsize(input_path) if file_size > MAX_FILE_SIZE: return jsonify({"error": f"File too large ({file_size} bytes)."}), 413 # ODA File Converter: input_dir output_dir version type recurse audit # ACAD2018 = output DXF version, DXF = output format, 0 = no recurse, 1 = audit result = subprocess.run( [ "xvfb-run", "--auto-servernum", "--server-args=-screen 0 1x1x24", ODA_BIN, input_dir, output_dir, "ACAD2018", "DXF", "0", "1", ], capture_output=True, timeout=120, ) # Find the output DXF file dxf_files = glob.glob(os.path.join(output_dir, "*.dxf")) if not dxf_files: stderr = result.stderr.decode("utf-8", errors="replace") stdout = result.stdout.decode("utf-8", errors="replace") detail = stderr or stdout or "No output file produced" return jsonify({"error": f"Conversion failed: {detail}"}), 500 dxf_path = dxf_files[0] dxf_name = safe_name.rsplit(".", 1)[0] + ".dxf" return send_file( dxf_path, mimetype="application/dxf", as_attachment=True, download_name=dxf_name, ) except subprocess.TimeoutExpired: return jsonify({"error": "Conversion timed out (120s limit)."}), 504 except Exception as e: return jsonify({"error": f"Unexpected error: {str(e)}"}), 500 finally: shutil.rmtree(work_dir, ignore_errors=True) if __name__ == "__main__": app.run(host="0.0.0.0", port=5001)