Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 74 additions & 3 deletions jupytercad_freecad/handlers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import json
import base64
from pathlib import Path

from jupyter_server.base.handlers import APIHandler
from jupyter_server.utils import url_path_join
from jupyter_server.utils import url_path_join, ApiPath, to_os_path
import tornado

from jupytercad_freecad.freecad.loader import FCStd


class BackendCheckHandler(APIHandler):
@tornado.web.authenticated
Expand All @@ -23,10 +27,77 @@ def post(self):
self.finish(json.dumps({"installed": False}))


class JCadExportHandler(APIHandler):
@tornado.web.authenticated
def post(self):
body = self.get_json_body()

# Works with both prefixed and unprefixed paths
path = body.get("path", "")
parts = path.split(":", 1)

if len(parts) == 2:
file_name = parts[1]
else:
file_name = parts[0] # fallback: treat whole thing as path

export_name = body["newName"]

root_dir = Path(self.contents_manager.root_dir).resolve()
file_path = Path(to_os_path(ApiPath(file_name), str(root_dir)))

try:
with open(file_path, "rb") as fobj:
base64_content = base64.b64encode(fobj.read()).decode("utf-8")
except Exception as e:
self.log.error(f"Error reading file {file_path}: {e}")
self.set_status(500)
self.finish(json.dumps({"error": f"Failed to read file: {str(e)}"}))
return

try:
fcstd = FCStd()
fcstd.load(base64_content=base64_content)
except Exception as e:
self.log.error(f"Error loading FCStd file: {e}")
self.set_status(500)
self.finish(json.dumps({"error": f"Failed to load FCStd file: {str(e)}"}))
return

jcad = dict(
schemaVersion="1.0",
objects=fcstd._objects,
metadata=fcstd._metadata,
options=fcstd._guidata,
outputs={},
)

export_path = file_path.parent / export_name
try:
with open(export_path, "w") as fobj:
fobj.write(json.dumps(jcad, indent=2))
except Exception as e:
self.log.error(f"Error writing JCAD file: {e}")
self.set_status(500)
self.finish(json.dumps({"error": f"Failed to write JCAD file: {str(e)}"}))
return

self.finish(json.dumps({"done": True, "exportedPath": str(export_path)}))


def setup_handlers(web_app):
host_pattern = ".*$"

base_url = web_app.settings["base_url"]
route_pattern = url_path_join(base_url, "jupytercad_freecad", "backend-check")
handlers = [(route_pattern, BackendCheckHandler)]

handlers = [
(
url_path_join(base_url, "jupytercad_freecad", "backend-check"),
BackendCheckHandler,
),
(
url_path_join(base_url, "jupytercad_freecad", "export-jcad"),
JCadExportHandler,
),
]
web_app.add_handlers(host_pattern, handlers)
6 changes: 5 additions & 1 deletion src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const freecadIcon = new LabIcon({

const FACTORY = 'Jupytercad Freecad Factory';

class JupyterCadFCstdDoc extends JupyterCadDoc {
toJcadEndpoint = 'jupytercad_freecad/export-jcad';
}

const activate = async (
app: JupyterFrontEnd,
tracker: WidgetTracker<IJupyterCadWidget>,
Expand Down Expand Up @@ -96,7 +100,7 @@ const activate = async (
});

const FCStdSharedModelFactory: SharedDocumentFactory = () => {
return new JupyterCadDoc();
return new JupyterCadFCstdDoc();
};
drive.sharedModelFactory.registerDocumentFactory(
'FCStd',
Expand Down
Loading