Skip to content
Draft
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
69 changes: 66 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,69 @@ def post(self):
self.finish(json.dumps({"installed": False}))


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

file_name = body["path"].split(":")[1]
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our jupytercad_freecad.freecad.loader module only exposes a load method that reads base 64 data, but it will create a temporary file under the hood before passing it to actual freecad. So we're reading into base 64, writing the file, and reading it again.

We could reduce the complexity of this by introducing e.g. a load_file method to the FCStd that loads from a file directly.

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