Skip to content

Commit 46ed81c

Browse files
committed
Added file export functionality
1 parent 3ac0ec3 commit 46ed81c

File tree

4 files changed

+109
-4
lines changed

4 files changed

+109
-4
lines changed

app/back-end/src/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
WORKSPACE_DELETE_ROUTE = "/workspace/delete"
3535
WORKSPACE_AGGREGATE_ROUTE = "/workspace/aggregate"
3636
WORKSPACE_IMPORT_ROUTE = "/workspace/import"
37+
WORKSPACE_EXPORT_ROUTE = "/workspace/export"
3738

3839
# Events
3940
CONSOLE_FEEDBACK_EVENT = "console_feedback"

app/back-end/src/routes/workspace_route.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
import os
8383
import shutil
8484
import csv
85-
from flask import Blueprint, request, jsonify
85+
from flask import Blueprint, request, jsonify, send_file
8686

8787
from src.setup.extensions import compress, logger
8888
from src.utils.helpers import socketio_emit_to_user_session, build_workspace_structure
@@ -99,6 +99,7 @@
9999
CONSOLE_FEEDBACK_EVENT,
100100
WORKSPACE_FILE_SAVE_FEEDBACK_EVENT,
101101
WORKSPACE_IMPORT_ROUTE,
102+
WORKSPACE_EXPORT_ROUTE,
102103
)
103104

104105
workspace_route_bp = Blueprint("workspace_route", __name__)
@@ -1168,3 +1169,83 @@ def import_file(relative_path=None):
11681169
return jsonify({"error": "An internal error occurred"}), 500
11691170

11701171
return jsonify({'message': 'File imported successfully'}), 200
1172+
1173+
@workspace_route_bp.route(f"{WORKSPACE_EXPORT_ROUTE}/<path:relative_path>", methods=["GET"])
1174+
def export_file(relative_path):
1175+
1176+
uuid = request.headers.get("uuid")
1177+
sid = request.headers.get("sid")
1178+
1179+
# Ensure the uuid header is present
1180+
if not uuid:
1181+
return jsonify({"error": "UUID header is missing"}), 400
1182+
1183+
# Ensure the sid header is present
1184+
if not sid:
1185+
return jsonify({"error": "SID header is missing"}), 400
1186+
1187+
try:
1188+
user_workspace_dir = os.path.join(WORKSPACE_DIR, uuid)
1189+
file_path = os.path.join(user_workspace_dir, relative_path)
1190+
1191+
#Emit a feedback to the user's console
1192+
socketio_emit_to_user_session(
1193+
CONSOLE_FEEDBACK_EVENT,
1194+
{"type": "info", "message": f"Exporting file '{relative_path}'..."},
1195+
uuid,
1196+
sid,
1197+
)
1198+
1199+
response = send_file(file_path, as_attachment=True)
1200+
1201+
# Emit success message after file export is successful
1202+
#socketio_emit_to_user_session(
1203+
# CONSOLE_FEEDBACK_EVENT,
1204+
# {"type": "succ", "message": f"File '{relative_path}' was exported successfully."},
1205+
# uuid,
1206+
# sid,
1207+
#)
1208+
1209+
except FileNotFoundError as e:
1210+
logger.error("FileNotFoundError: %s while accessing %s", e, user_workspace_dir)
1211+
# Emit a feedback to the user's console
1212+
socketio_emit_to_user_session(
1213+
CONSOLE_FEEDBACK_EVENT,
1214+
{
1215+
"type": "errr",
1216+
"message": f"FileNotFoundError: {e} while accessing {user_workspace_dir}",
1217+
},
1218+
uuid,
1219+
sid,
1220+
)
1221+
return jsonify({"error": "Requested file not found"}), 404
1222+
except PermissionError as e:
1223+
logger.error("PermissionError: %s while accessing %s", e, user_workspace_dir)
1224+
# Emit a feedback to the user's console
1225+
socketio_emit_to_user_session(
1226+
CONSOLE_FEEDBACK_EVENT,
1227+
{
1228+
"type": "errr",
1229+
"message": f"PermissionError: {e} while accessing {user_workspace_dir}",
1230+
},
1231+
uuid,
1232+
sid,
1233+
)
1234+
return jsonify({"error": "Permission denied"}), 403
1235+
except UnexpectedError as e:
1236+
logger.error("UnexpectedError: %s while accessing %s", e.message, user_workspace_dir)
1237+
# Emit a feedback to the user's console
1238+
socketio_emit_to_user_session(
1239+
CONSOLE_FEEDBACK_EVENT,
1240+
{
1241+
"type": "errr",
1242+
"message": f"UnexpectedError: {e.message} while accessing {user_workspace_dir}",
1243+
},
1244+
uuid,
1245+
sid,
1246+
)
1247+
return jsonify({"error": "An internal error occurred"}), 500
1248+
1249+
return response
1250+
1251+

app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const FileTreeItemContextMenu: React.FC<FileTreeItemContextMenuProps> = (
7777
);
7878
} else {
7979
menuItems.push(
80-
<MenuItem key='export' onClick={() => handleActionContextMenu('export')} disabled>
80+
<MenuItem key='export' onClick={() => handleActionContextMenu('export')}>
8181
Export...
8282
</MenuItem>,
8383
<Divider key='divider-export' />
@@ -112,8 +112,7 @@ export const FileTreeItemContextMenu: React.FC<FileTreeItemContextMenuProps> = (
112112
setFileImportDialogOpen(true);
113113
break;
114114
case 'export':
115-
// TODO: Implement file export
116-
console.log('export');
115+
handleExport();
117116
break;
118117
case 'rename':
119118
setRenameDialogOpen(true);
@@ -158,6 +157,29 @@ export const FileTreeItemContextMenu: React.FC<FileTreeItemContextMenuProps> = (
158157
filesHistoryStateUpdate(undefined, { id: item.id, label: item.label, type: item.fileType || FileTypes.FILE });
159158
};
160159

160+
const handleExport = async () => {
161+
try {
162+
const response = await axios.get(`${Endpoints.WORKSPACE_EXPORT}/${item.id}`, {
163+
responseType: 'blob',
164+
});
165+
const url = window.URL.createObjectURL(new Blob([response.data]));
166+
console.log(item.id);
167+
const fileName = item.id.match(/[^/\\]+$/)?.[0] || item.id; // Extracts only the file name, otherwise uses the full path
168+
const link = Object.assign(document.createElement('a'), {
169+
href: url,
170+
download: fileName,
171+
});
172+
173+
link.click();
174+
window.URL.revokeObjectURL(url);
175+
176+
// TODO: Implement socket console event for successful file export
177+
console.log('Exported:', fileName);
178+
} catch (error) {
179+
console.error('Error exporting the file:', error);
180+
}
181+
};
182+
161183
return (
162184
<>
163185
<Menu

app/front-end/src/types/constants/endpoints.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ export const Endpoints = {
3939
WORKSPACE_DELETE: `/workspace/delete`,
4040
WORKSPACE_AGGREGATE: `/workspace/aggregate`,
4141
WORKSPACE_IMPORT: `/workspace/import`,
42+
WORKSPACE_EXPORT: `/workspace/export`,
4243
};

0 commit comments

Comments
 (0)