From 46ed81ceb15c44df2bfe386fb18e8f08cf88beca Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:26:03 +0300 Subject: [PATCH 01/11] Added file export functionality --- app/back-end/src/constants.py | 1 + app/back-end/src/routes/workspace_route.py | 83 ++++++++++++++++++- .../fileTreeItem/fileTreeItemContextMenu.tsx | 28 ++++++- .../src/types/constants/endpoints.ts | 1 + 4 files changed, 109 insertions(+), 4 deletions(-) diff --git a/app/back-end/src/constants.py b/app/back-end/src/constants.py index 5729675..f8ee6d1 100644 --- a/app/back-end/src/constants.py +++ b/app/back-end/src/constants.py @@ -34,6 +34,7 @@ WORKSPACE_DELETE_ROUTE = "/workspace/delete" WORKSPACE_AGGREGATE_ROUTE = "/workspace/aggregate" WORKSPACE_IMPORT_ROUTE = "/workspace/import" +WORKSPACE_EXPORT_ROUTE = "/workspace/export" # Events CONSOLE_FEEDBACK_EVENT = "console_feedback" diff --git a/app/back-end/src/routes/workspace_route.py b/app/back-end/src/routes/workspace_route.py index c02c003..25d6189 100644 --- a/app/back-end/src/routes/workspace_route.py +++ b/app/back-end/src/routes/workspace_route.py @@ -82,7 +82,7 @@ import os import shutil import csv -from flask import Blueprint, request, jsonify +from flask import Blueprint, request, jsonify, send_file from src.setup.extensions import compress, logger from src.utils.helpers import socketio_emit_to_user_session, build_workspace_structure @@ -99,6 +99,7 @@ CONSOLE_FEEDBACK_EVENT, WORKSPACE_FILE_SAVE_FEEDBACK_EVENT, WORKSPACE_IMPORT_ROUTE, + WORKSPACE_EXPORT_ROUTE, ) workspace_route_bp = Blueprint("workspace_route", __name__) @@ -1168,3 +1169,83 @@ def import_file(relative_path=None): return jsonify({"error": "An internal error occurred"}), 500 return jsonify({'message': 'File imported successfully'}), 200 + +@workspace_route_bp.route(f"{WORKSPACE_EXPORT_ROUTE}/", methods=["GET"]) +def export_file(relative_path): + + uuid = request.headers.get("uuid") + sid = request.headers.get("sid") + + # Ensure the uuid header is present + if not uuid: + return jsonify({"error": "UUID header is missing"}), 400 + + # Ensure the sid header is present + if not sid: + return jsonify({"error": "SID header is missing"}), 400 + + try: + user_workspace_dir = os.path.join(WORKSPACE_DIR, uuid) + file_path = os.path.join(user_workspace_dir, relative_path) + + #Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + {"type": "info", "message": f"Exporting file '{relative_path}'..."}, + uuid, + sid, + ) + + response = send_file(file_path, as_attachment=True) + + # Emit success message after file export is successful + #socketio_emit_to_user_session( + # CONSOLE_FEEDBACK_EVENT, + # {"type": "succ", "message": f"File '{relative_path}' was exported successfully."}, + # uuid, + # sid, + #) + + except FileNotFoundError as e: + logger.error("FileNotFoundError: %s while accessing %s", e, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"FileNotFoundError: {e} while accessing {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "Requested file not found"}), 404 + except PermissionError as e: + logger.error("PermissionError: %s while accessing %s", e, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"PermissionError: {e} while accessing {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "Permission denied"}), 403 + except UnexpectedError as e: + logger.error("UnexpectedError: %s while accessing %s", e.message, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"UnexpectedError: {e.message} while accessing {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "An internal error occurred"}), 500 + + return response + + diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 82f1321..c3a1cbd 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -77,7 +77,7 @@ export const FileTreeItemContextMenu: React.FC = ( ); } else { menuItems.push( - handleActionContextMenu('export')} disabled> + handleActionContextMenu('export')}> Export... , @@ -112,8 +112,7 @@ export const FileTreeItemContextMenu: React.FC = ( setFileImportDialogOpen(true); break; case 'export': - // TODO: Implement file export - console.log('export'); + handleExport(); break; case 'rename': setRenameDialogOpen(true); @@ -158,6 +157,29 @@ export const FileTreeItemContextMenu: React.FC = ( filesHistoryStateUpdate(undefined, { id: item.id, label: item.label, type: item.fileType || FileTypes.FILE }); }; + const handleExport = async () => { + try { + const response = await axios.get(`${Endpoints.WORKSPACE_EXPORT}/${item.id}`, { + responseType: 'blob', + }); + const url = window.URL.createObjectURL(new Blob([response.data])); + console.log(item.id); + const fileName = item.id.match(/[^/\\]+$/)?.[0] || item.id; // Extracts only the file name, otherwise uses the full path + const link = Object.assign(document.createElement('a'), { + href: url, + download: fileName, + }); + + link.click(); + window.URL.revokeObjectURL(url); + + // TODO: Implement socket console event for successful file export + console.log('Exported:', fileName); + } catch (error) { + console.error('Error exporting the file:', error); + } + }; + return ( <> Date: Tue, 10 Sep 2024 01:48:05 +0300 Subject: [PATCH 02/11] Small fixes --- app/back-end/src/routes/workspace_route.py | 8 -------- .../fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx | 5 +++-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/app/back-end/src/routes/workspace_route.py b/app/back-end/src/routes/workspace_route.py index 25d6189..ebfc4d9 100644 --- a/app/back-end/src/routes/workspace_route.py +++ b/app/back-end/src/routes/workspace_route.py @@ -1198,14 +1198,6 @@ def export_file(relative_path): response = send_file(file_path, as_attachment=True) - # Emit success message after file export is successful - #socketio_emit_to_user_session( - # CONSOLE_FEEDBACK_EVENT, - # {"type": "succ", "message": f"File '{relative_path}' was exported successfully."}, - # uuid, - # sid, - #) - except FileNotFoundError as e: logger.error("FileNotFoundError: %s while accessing %s", e, user_workspace_dir) # Emit a feedback to the user's console diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index c3a1cbd..948bcc8 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -163,8 +163,9 @@ export const FileTreeItemContextMenu: React.FC = ( responseType: 'blob', }); const url = window.URL.createObjectURL(new Blob([response.data])); - console.log(item.id); + const fileName = item.id.match(/[^/\\]+$/)?.[0] || item.id; // Extracts only the file name, otherwise uses the full path + const link = Object.assign(document.createElement('a'), { href: url, download: fileName, @@ -176,7 +177,7 @@ export const FileTreeItemContextMenu: React.FC = ( // TODO: Implement socket console event for successful file export console.log('Exported:', fileName); } catch (error) { - console.error('Error exporting the file:', error); + console.error('Error exporting file:', error); } }; From 94260ba7c625797966ec9a4f61f73a8e0a526595 Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Tue, 10 Sep 2024 01:51:29 +0300 Subject: [PATCH 03/11] More small fixes --- app/back-end/src/routes/workspace_route.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/back-end/src/routes/workspace_route.py b/app/back-end/src/routes/workspace_route.py index ebfc4d9..01a38b9 100644 --- a/app/back-end/src/routes/workspace_route.py +++ b/app/back-end/src/routes/workspace_route.py @@ -1197,6 +1197,8 @@ def export_file(relative_path): ) response = send_file(file_path, as_attachment=True) + + return response except FileNotFoundError as e: logger.error("FileNotFoundError: %s while accessing %s", e, user_workspace_dir) @@ -1238,6 +1240,4 @@ def export_file(relative_path): ) return jsonify({"error": "An internal error occurred"}), 500 - return response - From 3c097e73719993879c1b5d7e18772789e6f27ed1 Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Tue, 10 Sep 2024 02:17:27 +0300 Subject: [PATCH 04/11] Fixed issues --- app/back-end/src/routes/workspace_route.py | 14 +++++++------- .../fileTreeItem/fileTreeItemContextMenu.tsx | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/back-end/src/routes/workspace_route.py b/app/back-end/src/routes/workspace_route.py index 01a38b9..0e8843d 100644 --- a/app/back-end/src/routes/workspace_route.py +++ b/app/back-end/src/routes/workspace_route.py @@ -1197,43 +1197,43 @@ def export_file(relative_path): ) response = send_file(file_path, as_attachment=True) - + return response except FileNotFoundError as e: - logger.error("FileNotFoundError: %s while accessing %s", e, user_workspace_dir) + logger.error("FileNotFoundError: %s while exporting %s", e, user_workspace_dir) # Emit a feedback to the user's console socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, { "type": "errr", - "message": f"FileNotFoundError: {e} while accessing {user_workspace_dir}", + "message": f"FileNotFoundError: {e} while exporting {user_workspace_dir}", }, uuid, sid, ) return jsonify({"error": "Requested file not found"}), 404 except PermissionError as e: - logger.error("PermissionError: %s while accessing %s", e, user_workspace_dir) + logger.error("PermissionError: %s while exporting %s", e, user_workspace_dir) # Emit a feedback to the user's console socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, { "type": "errr", - "message": f"PermissionError: {e} while accessing {user_workspace_dir}", + "message": f"PermissionError: {e} while exporting {user_workspace_dir}", }, uuid, sid, ) return jsonify({"error": "Permission denied"}), 403 except UnexpectedError as e: - logger.error("UnexpectedError: %s while accessing %s", e.message, user_workspace_dir) + logger.error("UnexpectedError: %s while exporting %s", e.message, user_workspace_dir) # Emit a feedback to the user's console socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, { "type": "errr", - "message": f"UnexpectedError: {e.message} while accessing {user_workspace_dir}", + "message": f"UnexpectedError: {e.message} while exporting {user_workspace_dir}", }, uuid, sid, diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 948bcc8..024f796 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -8,7 +8,7 @@ import { FileTreeItemContextMenuActions, FileTreeViewItemProps, FileTypes } from import { axios } from '@/lib'; import { Endpoints } from '@/types'; import { Divider, Menu, MenuItem } from '@mui/material'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; export interface FileTreeItemContextMenuProps { item: FileTreeViewItemProps; @@ -157,7 +157,7 @@ export const FileTreeItemContextMenu: React.FC = ( filesHistoryStateUpdate(undefined, { id: item.id, label: item.label, type: item.fileType || FileTypes.FILE }); }; - const handleExport = async () => { + const handleExport = useCallback(async () => { try { const response = await axios.get(`${Endpoints.WORKSPACE_EXPORT}/${item.id}`, { responseType: 'blob', @@ -179,7 +179,7 @@ export const FileTreeItemContextMenu: React.FC = ( } catch (error) { console.error('Error exporting file:', error); } - }; + }, [item.id]); return ( <> From 9e0e201808c08d2a9470a3f4f25904e91bba1824 Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Tue, 10 Sep 2024 02:20:24 +0300 Subject: [PATCH 05/11] Minor fix --- .../fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 024f796..2b7518c 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -179,7 +179,7 @@ export const FileTreeItemContextMenu: React.FC = ( } catch (error) { console.error('Error exporting file:', error); } - }, [item.id]); + }, [item]); return ( <> From 845047ddc85fbc793481e2ed51fc0083c41a3f55 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Tue, 10 Sep 2024 19:16:27 +0300 Subject: [PATCH 06/11] MDE/PKFE-46 implemented socket emit in [FE] --- .../fileTreeItem/fileTreeItemContextMenu.tsx | 9 +++++---- app/front-end/src/types/constants/events.ts | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 2b7518c..5595cb1 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -5,8 +5,9 @@ import { } from '@/features/editor/components/fileTreeView/fileTreeItem'; import { useWorkspaceContext } from '@/features/editor/hooks'; import { FileTreeItemContextMenuActions, FileTreeViewItemProps, FileTypes } from '@/features/editor/types'; -import { axios } from '@/lib'; -import { Endpoints } from '@/types'; +import { axios, socket } from '@/lib'; +import { Endpoints, Events } from '@/types'; +import { getSID, getUUID } from '@/utils'; import { Divider, Menu, MenuItem } from '@mui/material'; import { useCallback, useState } from 'react'; @@ -174,9 +175,9 @@ export const FileTreeItemContextMenu: React.FC = ( link.click(); window.URL.revokeObjectURL(url); - // TODO: Implement socket console event for successful file export - console.log('Exported:', fileName); + socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { uuid: getUUID(), sid: getSID(), status: 'success' }); } catch (error) { + socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { uuid: getUUID(), sid: getSID(), status: 'failure' }); console.error('Error exporting file:', error); } }, [item]); diff --git a/app/front-end/src/types/constants/events.ts b/app/front-end/src/types/constants/events.ts index 33b7072..d4460e0 100644 --- a/app/front-end/src/types/constants/events.ts +++ b/app/front-end/src/types/constants/events.ts @@ -9,11 +9,13 @@ * - `CONSOLE_FEEDBACK_EVENT`: The event name for console feedback messages. * - `WORKSPACE_FILE_SAVE_FEEDBACK_EVENT`: The event name for feedback related to saving files in the workspace. * - `WORKSPACE_UPDATE_FEEDBACK_EVENT`: The event name for feedback related to updates in the workspace. + * - `WORKSPACE_EXPORT_FEEDBACK_EVENT`: The event name for feedback related to exporting the workspace. * * @constant {Object} Events - The object containing event names. * @property {string} CONSOLE_FEEDBACK_EVENT - The event name for receiving console feedback messages. * @property {string} WORKSPACE_FILE_SAVE_FEEDBACK_EVENT - The event name for receiving feedback on file save operations within the workspace. * @property {string} WORKSPACE_UPDATE_FEEDBACK_EVENT - The event name for receiving feedback on workspace updates. + * @property {string} WORKSPACE_EXPORT_FEEDBACK_EVENT - The event name for receiving feedback on exporting the workspace. * * @example * // Example usage of the Events object @@ -26,4 +28,5 @@ export const Events = { CONSOLE_FEEDBACK_EVENT: 'console_feedback', WORKSPACE_FILE_SAVE_FEEDBACK_EVENT: 'workspace_file_save_feedback', WORKSPACE_UPDATE_FEEDBACK_EVENT: 'workspace_update_feedback', + WORKSPACE_EXPORT_FEEDBACK_EVENT: 'workspace_export_feedback', }; From 3abbb2cba872b6b237930fa7eba88decfb75c9e7 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Tue, 10 Sep 2024 19:17:14 +0300 Subject: [PATCH 07/11] MDE/PKFE-46 implemented event handler for export feedback --- app/back-end/src/constants.py | 1 + app/back-end/src/events/workspace_event.py | 95 ------------------- .../src/events/workspace_export_event.py | 81 ++++++++++++++++ app/back-end/src/setup/eventer.py | 4 +- 4 files changed, 84 insertions(+), 97 deletions(-) delete mode 100644 app/back-end/src/events/workspace_event.py create mode 100644 app/back-end/src/events/workspace_export_event.py diff --git a/app/back-end/src/constants.py b/app/back-end/src/constants.py index f8ee6d1..20bf7f8 100644 --- a/app/back-end/src/constants.py +++ b/app/back-end/src/constants.py @@ -40,3 +40,4 @@ CONSOLE_FEEDBACK_EVENT = "console_feedback" WORKSPACE_FILE_SAVE_FEEDBACK_EVENT = "workspace_file_save_feedback" WORKSPACE_UPDATE_FEEDBACK_EVENT = "workspace_update_feedback" +WOKRSPACE_EXPORT_FEEDBACK_EVENT = "workspace_export_feedback" diff --git a/app/back-end/src/events/workspace_event.py b/app/back-end/src/events/workspace_event.py deleted file mode 100644 index d0b9d4e..0000000 --- a/app/back-end/src/events/workspace_event.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -This module contains functions for handling workspace-related WebSocket events. - -It includes functions that manage file operations within the workspace directory. The module -provides real-time feedback to clients through WebSocket events. - -Dependencies: -- os: Used for file and directory operations. -- src.setup.extensions.socketio: The Socket.IO instance used for handling real-time communication. -- src.setup.constants.WORKSPACE_DIR: The base directory for workspace files. -""" - -# pylint: disable=import-error - -import os -from flask_socketio import SocketIO - -from src.utils.exceptions import UnexpectedError -from src.constants import WORKSPACE_DIR - - -def workspace_event_handler(socketio: SocketIO): - """ - Registers WebSocket event handlers for workspace operations using the provided SocketIO - instance. - - This function sets up event handling for the 'workspace_file_update' event, which allows - clients to update files in the workspace directory. It verifies the presence of necessary - data (UUID and file ID), handles file writing, and provides status updates back to the client. - - Args: - socketio (SocketIO): The SocketIO instance used for managing WebSocket connections and - events. - - Events Handled: - - 'workspace_file_update': Processes file update requests and emits status updates. - - Emits: - - 'workspace_file_update_status': Indicates success or failure of the file update operation - with a status message. - """ - - @socketio.on("workspace_file_update") - def handle_workspace_file_update(data): - """ - Handle the 'workspace_file_update' WebSocket event. - - This function processes the data received from the client to update a file in the workspace - directory. It ensures that required fields are present, writes the content to the specified - file, and handles any errors by sending appropriate status updates back to the client. - - Args: - data (dict): The data received from the client, expected to contain 'uuid', 'fileId', - and 'content'. - - Emits: - 'workspace_file_update_status': Emits a status message indicating success or failure of - the file update operation. - """ - uuid = data.get("uuid") - file_id = data.get("fileId") - content = data.get("content") - - # Ensure the uuid is provided - if not uuid: - socketio.emit( - "workspace_file_update_status", - {"status": "error", "message": "UUID is missing"}, - ) - return - - # Ensure the fileId is provided - if not file_id: - socketio.emit( - "workspace_file_update_status", - {"status": "error", "message": "File ID is missing"}, - ) - return - - file_path = os.path.join(WORKSPACE_DIR, uuid, file_id) - - try: - # Ensure the directory exists - os.makedirs(os.path.dirname(file_path), exist_ok=True) - - # Write the content to the file - with open(file_path, "w", encoding="utf-8") as file: - file.write(content) - - # Notify the client of the successful update - socketio.emit("workspace_file_update_status", {"status": "success"}) - - except UnexpectedError as e: - # Notify the client of an error during the update - socketio.emit("workspace_file_update_status", {"status": "error", "message": str(e)}) diff --git a/app/back-end/src/events/workspace_export_event.py b/app/back-end/src/events/workspace_export_event.py new file mode 100644 index 0000000..846396d --- /dev/null +++ b/app/back-end/src/events/workspace_export_event.py @@ -0,0 +1,81 @@ +""" +Module for handling Socket.IO events related to workspace file exports. + +This module sets up the event handler for the `WOKRSPACE_EXPORT_FEEDBACK_EVENT` event using +Socket.IO. It processes feedback about file export operations and sends appropriate real-time +feedback messages to the user's console based on the status of the export operation. + +Imports: + - socketio: The Socket.IO instance for handling real-time communication. + - socketio_emit_to_user_session: Utility function for emitting messages to a user's session. + - WOKRSPACE_EXPORT_FEEDBACK_EVENT: Constant defining the event name for file export feedback. + - CONSOLE_FEEDBACK_EVENT: Constant defining the event name for console feedback. + +Functions: + - workspace_export_event_handler: Registers the Socket.IO event handler for file export + feedback. +""" + +# pylint: disable=import-error + +from src.setup.extensions import socketio +from src.utils.helpers import socketio_emit_to_user_session +from src.constants import WOKRSPACE_EXPORT_FEEDBACK_EVENT, CONSOLE_FEEDBACK_EVENT + + +def workspace_export_event_handler(): + """ + Sets up the event handler for the `WOKRSPACE_EXPORT_FEEDBACK_EVENT` event in Socket.IO. + + This function registers an event handler for the `WOKRSPACE_EXPORT_FEEDBACK_EVENT` event, + which is triggered during file export operations in the workspace. The event handler processes + the feedback based on the status of the file export operation and sends appropriate feedback + messages to the user's console. + + This function does not return any value. It directly interacts with the Socket.IO event system + to provide real-time feedback to users. + + Side Effects: + - Registers the `handle_workspace_export_feedback` function as an event handler for + `WOKRSPACE_EXPORT_FEEDBACK_EVENT` using Socket.IO. + """ + + @socketio.on(WOKRSPACE_EXPORT_FEEDBACK_EVENT) + def handle_workspace_export_feedback(data): + """ + Handles the `WOKRSPACE_EXPORT_FEEDBACK_EVENT` event by providing feedback about the + file export operation. + + This function listens for Socket.IO events related to workspace file exports and processes + the feedback based on the status provided in the event data. It then sends a message to the + user's console indicating whether the file export was successful or not. + + Args: + data (dict): The event data containing feedback about the file export operation. + It should include: + - `status` (str): The status of the file export operation ("success" or "failure"). + - `uuid` (str): The unique identifier for the user's session. + - `sid` (str): The session identifier used for emitting real-time feedback. + + Emits: + - Success message to the user's console if the status is "success". + - Error message to the user's console if the status is "failure". + + Side Effects: + - Sends real-time feedback to the user's console using `socketio_emit_to_user_session`. + """ + + if data["status"] == "success": + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + {"type": "succ", "message": "File export completed successfully."}, + data["uuid"], + data["sid"], + ) + else: + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + {"type": "errr", "message": "File export failed."}, + data["uuid"], + data["sid"], + ) diff --git a/app/back-end/src/setup/eventer.py b/app/back-end/src/setup/eventer.py index 4584f9b..2e5dff5 100644 --- a/app/back-end/src/setup/eventer.py +++ b/app/back-end/src/setup/eventer.py @@ -35,14 +35,14 @@ from src.setup.extensions import socketio, logger, socket_manager # Import all event modules here -from src.events.workspace_event import workspace_event_handler +from src.events.workspace_export_event import workspace_export_event_handler def eventer(): """ Register all event handlers including connect and disconnect events. """ - workspace_event_handler(socketio) + workspace_export_event_handler() @socketio.on("connect") def handle_connect(): From d52efa6fe7784986fa2d2e99ca7d81a65e417a07 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Tue, 10 Sep 2024 19:17:54 +0300 Subject: [PATCH 08/11] MDE/PKFE-46 fixed pylint errors and minor structure changes --- .../src/routes/workspace_aggregate_route.py | 100 +++++++++- .../src/routes/workspace_export_route.py | 117 +++++++++++ .../src/routes/workspace_import_route.py | 167 ++++++++++++++++ app/back-end/src/routes/workspace_route.py | 181 +----------------- app/back-end/src/setup/router.py | 6 +- 5 files changed, 387 insertions(+), 184 deletions(-) create mode 100644 app/back-end/src/routes/workspace_export_route.py create mode 100644 app/back-end/src/routes/workspace_import_route.py diff --git a/app/back-end/src/routes/workspace_aggregate_route.py b/app/back-end/src/routes/workspace_aggregate_route.py index 4db5cf6..8a4e7a7 100644 --- a/app/back-end/src/routes/workspace_aggregate_route.py +++ b/app/back-end/src/routes/workspace_aggregate_route.py @@ -1,9 +1,29 @@ +""" +This module defines the routes for aggregating data from user workspaces in a Flask application. +It provides two main routes for performing column-level calculations on CSV files stored in the +user's workspace. The supported operations include summing, averaging, counting, finding the +minimum, and finding the maximum values in specified columns. + +The module emits real-time feedback to the user’s session via Socket.IO, providing status updates +on the calculations, handling skipped cells due to invalid data, and notifying the user of errors +such as file not found, permission denied, or unexpected issues. + +Routes: + - get_workspace_aggregate_all(relative_path): + Calculates aggregate values (sum, avg, min, max, cnt) for multiple columns in a CSV file. + + - get_workspace_aggregate(relative_path): + Calculates an aggregate value (sum, avg, min, max, cnt) for a single column in a CSV file. + +Exceptions are handled to provide feedback through the user’s console using Socket.IO. +""" + # pylint: disable=import-error import os import csv -from flask import Blueprint, request, jsonify from ast import literal_eval +from flask import Blueprint, request, jsonify from src.setup.extensions import logger from src.utils.helpers import socketio_emit_to_user_session, is_number @@ -18,6 +38,39 @@ f"{WORKSPACE_AGGREGATE_ROUTE}/all/", methods=["GET"] ) def get_workspace_aggregate_all(relative_path): + """ + Route to calculate aggregate values (e.g., sum, avg, min, max, cnt) for multiple columns + in a CSV file located in the user's workspace. The columns and their aggregation actions + are specified in the request's query parameters. + + Args: + relative_path (str): The relative path to the CSV file inside the user's workspace. + + Request Headers: + - uuid: A unique identifier for the user's session. + - sid: A session identifier for emitting real-time console feedback via Socket.IO. + + Query Parameters: + - columnsAggregation (str): A stringified dictionary where the keys are column names and + the values are dictionaries with an "action" key specifying the aggregation operation + ('sum', 'avg', 'min', 'max', or 'cnt'). + + Returns: + Response (JSON): + - On success: A JSON object with aggregated results for each specified column. + - On error: A JSON object with an error message and appropriate HTTP status code. + + Emits: + - Real-time console feedback using Socket.IO via the `socketio_emit_to_user_session` + function. Feedback includes the start, completion, and any warnings or errors during + the aggregation process. + + Possible Errors: + - FileNotFoundError: The specified CSV file does not exist. + - PermissionError: Insufficient permissions to read the CSV file. + - UnexpectedError: Any other unexpected error during the aggregation process. + """ + uuid = request.headers.get("uuid") sid = request.headers.get("sid") @@ -140,7 +193,8 @@ def get_workspace_aggregate_all(relative_path): CONSOLE_FEEDBACK_EVENT, { "type": "warn", - "message": f"The following columns had cells skipped due to non-numeric values: {', '.join(skipped_columns_info)}", + "message": "The following columns had cells skipped due to non-numeric " + + f"values: {', '.join(skipped_columns_info)}", }, uuid, sid, @@ -203,6 +257,45 @@ def get_workspace_aggregate_all(relative_path): f"{WORKSPACE_AGGREGATE_ROUTE}/", methods=["GET"] ) def get_workspace_aggregate(relative_path): + """ + Route to calculate an aggregate value (e.g., sum, avg, min, max, cnt) for a single column + in a CSV file located in the user's workspace. The column and the aggregation action + are specified in the request's query parameters. + + Args: + relative_path (str): The relative path to the CSV file inside the user's workspace. + + Request Headers: + - uuid: A unique identifier for the user's session. + - sid: A session identifier for emitting real-time console feedback via Socket.IO. + + Query Parameters: + - field (str): The name of the column to perform the aggregation on. + - action (str): The type of aggregation action to perform + ('sum', 'avg', 'min', 'max', or 'cnt'). + + Returns: + Response (JSON): + - On success: A JSON object with the aggregated result for the specified column. + - On error: A JSON object with an error message and appropriate HTTP status code. + + Emits: + - Real-time console feedback using Socket.IO via the `socketio_emit_to_user_session` + function.Feedback includes the start, completion, and any warnings or errors during the + aggregation process. + + Possible Errors: + - FileNotFoundError: The specified CSV file does not exist. + - PermissionError: Insufficient permissions to read the CSV file. + - UnexpectedError: Any other unexpected error during the aggregation process. + + Notes: + - If the column contains non-numeric values, those cells are skipped and a warning is sent + via Socket.IO. + - The result is formatted as "N/A" if no valid numeric data is found or if the specified + action is invalid for the data present in the column. + """ + uuid = request.headers.get("uuid") sid = request.headers.get("sid") @@ -289,7 +382,8 @@ def get_workspace_aggregate(relative_path): CONSOLE_FEEDBACK_EVENT, { "type": "warn", - "message": f"At column '{field}' {skipped_count} cells were skipped because they contain non-numeric values.", + "message": f"At column '{field}' {skipped_count} cells " + + "were skipped because they contain non-numeric values.", }, uuid, sid, diff --git a/app/back-end/src/routes/workspace_export_route.py b/app/back-end/src/routes/workspace_export_route.py new file mode 100644 index 0000000..94b9dab --- /dev/null +++ b/app/back-end/src/routes/workspace_export_route.py @@ -0,0 +1,117 @@ +""" +Handle file export from the workspace. + +This module provides a Flask route for exporting files from a user's workspace. +It performs the following: +- Retrieves the file based on the user's workspace and the requested path. +- Emits feedback to the user's console via SocketIO during the export process. +- Handles exceptions like file not found, permission errors, or unexpected errors, + returning appropriate HTTP responses. + +Routes: + GET /workspace_export/: Exports the requested file. +""" + +# pylint: disable=import-error + +import os +from flask import Blueprint, request, jsonify, send_file + +from src.setup.extensions import compress, logger +from src.utils.helpers import socketio_emit_to_user_session +from src.utils.exceptions import UnexpectedError +from src.constants import ( + WORKSPACE_DIR, + CONSOLE_FEEDBACK_EVENT, + WORKSPACE_EXPORT_ROUTE, +) + +workspace_export_route_bp = Blueprint("workspace_export_route", __name__) + + +@workspace_export_route_bp.route(f"{WORKSPACE_EXPORT_ROUTE}/", methods=["GET"]) +@compress.compressed() +def get_workspace_export(relative_path): + """ + Export a file from the user's workspace. + + Handles file export requests by retrieving the file from the user's workspace and + sending it as a downloadable attachment. Emits real-time feedback during the process. + + Args: + relative_path (str): The path to the file to be exported within the user's workspace. + + Headers: + uuid: Unique identifier for the user's workspace. + sid: Session identifier for emitting real-time events. + + Returns: + A downloadable file or a JSON response with an error message. + """ + + uuid = request.headers.get("uuid") + sid = request.headers.get("sid") + + # Ensure the uuid header is present + if not uuid: + return jsonify({"error": "UUID header is missing"}), 400 + + # Ensure the sid header is present + if not sid: + return jsonify({"error": "SID header is missing"}), 400 + + try: + user_workspace_dir = os.path.join(WORKSPACE_DIR, uuid) + file_path = os.path.join(user_workspace_dir, relative_path) + + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + {"type": "info", "message": f"Exporting file '{relative_path}'..."}, + uuid, + sid, + ) + + response = send_file(file_path, as_attachment=True) + + return response + + except FileNotFoundError as e: + logger.error("FileNotFoundError: %s while exporting %s", e, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"FileNotFoundError: {e} while exporting {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "Requested file not found"}), 404 + except PermissionError as e: + logger.error("PermissionError: %s while exporting %s", e, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"PermissionError: {e} while exporting {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "Permission denied"}), 403 + except UnexpectedError as e: + logger.error("UnexpectedError: %s while exporting %s", e.message, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"UnexpectedError: {e.message} while exporting {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "An internal error occurred"}), 500 diff --git a/app/back-end/src/routes/workspace_import_route.py b/app/back-end/src/routes/workspace_import_route.py new file mode 100644 index 0000000..7bda8d3 --- /dev/null +++ b/app/back-end/src/routes/workspace_import_route.py @@ -0,0 +1,167 @@ +""" +Handle file import into the workspace. + +This module provides a Flask route for importing files into a user's workspace. +It performs the following: +- Accepts files of type 'csv' or 'txt' for upload. +- Saves the file to the specified workspace directory. +- Emits feedback to the user's console via SocketIO during the import process. +- Handles exceptions like file not found, permission errors, or unexpected errors, + returning appropriate HTTP responses. + +Routes: + POST /workspace_import: Imports a file to the root folder. + POST /workspace_import/: Imports a file to the specified folder. +""" + +# pylint: disable=import-error + +import os +from flask import Blueprint, request, jsonify + +from src.setup.extensions import compress, logger +from src.utils.helpers import socketio_emit_to_user_session +from src.utils.exceptions import UnexpectedError +from src.constants import ( + WORKSPACE_DIR, + WORKSPACE_UPDATE_FEEDBACK_EVENT, + CONSOLE_FEEDBACK_EVENT, + WORKSPACE_IMPORT_ROUTE, +) + +workspace_import_route_bp = Blueprint("workspace_import_route", __name__) + + +@workspace_import_route_bp.route(f"{WORKSPACE_IMPORT_ROUTE}", methods=["POST"]) +@workspace_import_route_bp.route(f"{WORKSPACE_IMPORT_ROUTE}/", methods=["POST"]) +@compress.compressed() +def post_workspace_import(relative_path=None): + """ + Import a file into the user's workspace. + + Handles file uploads to the workspace, ensuring the file is of type 'csv' or 'txt'. + Saves the file to the appropriate folder and sends real-time feedback to the user's console. + + Args: + relative_path (str, optional): The folder within the workspace where the file should + be saved. + + Headers: + uuid: Unique identifier for the user's workspace. + sid: Session identifier for emitting real-time events. + + Request: + A file part must be included in the request. + + Returns: + JSON response with a success or error message. + """ + + uuid = request.headers.get("uuid") + sid = request.headers.get("sid") + + # Ensure the uuid header is present + if not uuid: + return jsonify({"error": "UUID header is missing"}), 400 + + # Ensure the sid header is present + if not sid: + return jsonify({"error": "SID header is missing"}), 400 + + if "file" not in request.files: + return jsonify({"error": "No file part in the request"}), 400 + + file = request.files["file"] + + if file.filename == "": + return jsonify({"error": "No file selected for importing"}), 400 + + file_extension = file.filename.rsplit(".", 1)[1].lower() if "." in file.filename else None + if file_extension not in ["csv", "txt"]: + return ( + jsonify( + { + "error": f"FileImportError: Incorrect file type for '{file.filename}'. " + + "Accepted file types: 'csv', 'txt'." + } + ), + 400, + ) + + relative_path_title = relative_path + + if relative_path is None: + relative_path = "" + relative_path_title = "root folder" + + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "info", + "message": f"Importing file '{file.filename}' to '{relative_path_title}'...", + }, + uuid, + sid, + ) + + try: + user_workspace_dir = os.path.join(WORKSPACE_DIR, uuid) + folder_path = os.path.join(user_workspace_dir, relative_path) + destination_path = os.path.join(folder_path, file.filename) + file.save(destination_path) + + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + {"type": "succ", "message": f"File {file.filename} was imported successfully."}, + uuid, + sid, + ) + + socketio_emit_to_user_session( + WORKSPACE_UPDATE_FEEDBACK_EVENT, + {"status": "updated"}, + uuid, + sid, + ) + + return jsonify({"message": "File imported successfully"}), 200 + + except FileNotFoundError as e: + logger.error("FileNotFoundError: %s while importing %s", e, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"FileNotFoundError: {e} while importing {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "Requested file not found"}), 404 + except PermissionError as e: + logger.error("PermissionError: %s while importing %s", e, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"PermissionError: {e} while importing {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "Permission denied"}), 403 + except UnexpectedError as e: + logger.error("UnexpectedError: %s while importing %s", e.message, user_workspace_dir) + # Emit a feedback to the user's console + socketio_emit_to_user_session( + CONSOLE_FEEDBACK_EVENT, + { + "type": "errr", + "message": f"UnexpectedError: {e.message} while importing {user_workspace_dir}", + }, + uuid, + sid, + ) + return jsonify({"error": "An internal error occurred"}), 500 diff --git a/app/back-end/src/routes/workspace_route.py b/app/back-end/src/routes/workspace_route.py index 0e8843d..2a5347f 100644 --- a/app/back-end/src/routes/workspace_route.py +++ b/app/back-end/src/routes/workspace_route.py @@ -76,13 +76,12 @@ """ # pylint: disable=import-error -# pylint: disable=too-many-locals # pylint: disable=too-many-lines import os import shutil import csv -from flask import Blueprint, request, jsonify, send_file +from flask import Blueprint, request, jsonify from src.setup.extensions import compress, logger from src.utils.helpers import socketio_emit_to_user_session, build_workspace_structure @@ -98,8 +97,6 @@ WORKSPACE_UPDATE_FEEDBACK_EVENT, CONSOLE_FEEDBACK_EVENT, WORKSPACE_FILE_SAVE_FEEDBACK_EVENT, - WORKSPACE_IMPORT_ROUTE, - WORKSPACE_EXPORT_ROUTE, ) workspace_route_bp = Blueprint("workspace_route", __name__) @@ -1065,179 +1062,3 @@ def put_workspace_delete(relative_path): sid, ) return jsonify({"error": "An internal error occurred"}), 500 - -@workspace_route_bp.route(f"{WORKSPACE_IMPORT_ROUTE}", methods=["POST"]) -@workspace_route_bp.route(f"{WORKSPACE_IMPORT_ROUTE}/", methods=["POST"]) -@compress.compressed() -def import_file(relative_path=None): - - uuid = request.headers.get("uuid") - sid = request.headers.get("sid") - - # Ensure the uuid header is present - if not uuid: - return jsonify({"error": "UUID header is missing"}), 400 - - # Ensure the sid header is present - if not sid: - return jsonify({"error": "SID header is missing"}), 400 - - - if 'file' not in request.files: - return jsonify({'error': 'No file part in the request'}), 400 - - file = request.files['file'] - - if file.filename == '': - return jsonify({'error': 'No file selected for importing'}), 400 - - file_extension = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else None - if file_extension not in ['csv', 'txt']: - return jsonify({'error': f"FileImportError: Incorrect file type for '{file.filename}'. Accepted file types: 'csv', 'txt'."}), 400 - - relative_path_title = relative_path - - if relative_path is None: - relative_path = "" - relative_path_title = "root folder" - - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - {"type": "info", "message": f"Importing file '{file.filename}' to '{relative_path_title}'..."}, - uuid, - sid, - ) - - try: - user_workspace_dir = os.path.join(WORKSPACE_DIR, uuid) - folder_path = os.path.join(user_workspace_dir, relative_path) - destination_path = os.path.join(folder_path, file.filename) - file.save(destination_path) - - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - {"type": "succ", "message": f"File {file.filename} was imported successfully."}, - uuid, - sid, - ) - - socketio_emit_to_user_session( - WORKSPACE_UPDATE_FEEDBACK_EVENT, - {"status": "updated"}, - uuid, - sid, - ) - - except FileNotFoundError as e: - logger.error("FileNotFoundError: %s while accessing %s", e, user_workspace_dir) - # Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - { - "type": "errr", - "message": f"FileNotFoundError: {e} while accessing {user_workspace_dir}", - }, - uuid, - sid, - ) - return jsonify({"error": "Requested file not found"}), 404 - except PermissionError as e: - logger.error("PermissionError: %s while accessing %s", e, user_workspace_dir) - # Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - { - "type": "errr", - "message": f"PermissionError: {e} while accessing {user_workspace_dir}", - }, - uuid, - sid, - ) - return jsonify({"error": "Permission denied"}), 403 - except UnexpectedError as e: - logger.error("UnexpectedError: %s while accessing %s", e.message, user_workspace_dir) - # Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - { - "type": "errr", - "message": f"UnexpectedError: {e.message} while accessing {user_workspace_dir}", - }, - uuid, - sid, - ) - return jsonify({"error": "An internal error occurred"}), 500 - - return jsonify({'message': 'File imported successfully'}), 200 - -@workspace_route_bp.route(f"{WORKSPACE_EXPORT_ROUTE}/", methods=["GET"]) -def export_file(relative_path): - - uuid = request.headers.get("uuid") - sid = request.headers.get("sid") - - # Ensure the uuid header is present - if not uuid: - return jsonify({"error": "UUID header is missing"}), 400 - - # Ensure the sid header is present - if not sid: - return jsonify({"error": "SID header is missing"}), 400 - - try: - user_workspace_dir = os.path.join(WORKSPACE_DIR, uuid) - file_path = os.path.join(user_workspace_dir, relative_path) - - #Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - {"type": "info", "message": f"Exporting file '{relative_path}'..."}, - uuid, - sid, - ) - - response = send_file(file_path, as_attachment=True) - - return response - - except FileNotFoundError as e: - logger.error("FileNotFoundError: %s while exporting %s", e, user_workspace_dir) - # Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - { - "type": "errr", - "message": f"FileNotFoundError: {e} while exporting {user_workspace_dir}", - }, - uuid, - sid, - ) - return jsonify({"error": "Requested file not found"}), 404 - except PermissionError as e: - logger.error("PermissionError: %s while exporting %s", e, user_workspace_dir) - # Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - { - "type": "errr", - "message": f"PermissionError: {e} while exporting {user_workspace_dir}", - }, - uuid, - sid, - ) - return jsonify({"error": "Permission denied"}), 403 - except UnexpectedError as e: - logger.error("UnexpectedError: %s while exporting %s", e.message, user_workspace_dir) - # Emit a feedback to the user's console - socketio_emit_to_user_session( - CONSOLE_FEEDBACK_EVENT, - { - "type": "errr", - "message": f"UnexpectedError: {e.message} while exporting {user_workspace_dir}", - }, - uuid, - sid, - ) - return jsonify({"error": "An internal error occurred"}), 500 - - diff --git a/app/back-end/src/setup/router.py b/app/back-end/src/setup/router.py index 068a10f..102e080 100644 --- a/app/back-end/src/setup/router.py +++ b/app/back-end/src/setup/router.py @@ -17,6 +17,8 @@ from src.routes.workspace_route import workspace_route_bp from src.routes.workspace_aggregate_route import workspace_aggregate_route_bp +from src.routes.workspace_export_route import workspace_export_route_bp +from src.routes.workspace_import_route import workspace_import_route_bp def router(prefix): @@ -35,7 +37,9 @@ def router(prefix): router_bp = Blueprint("router", __name__, url_prefix=prefix) # Register API routes with the main router blueprint - router_bp.register_blueprint(workspace_route_bp) + router_bp.register_blueprint(workspace_export_route_bp) + router_bp.register_blueprint(workspace_import_route_bp) router_bp.register_blueprint(workspace_aggregate_route_bp) + router_bp.register_blueprint(workspace_route_bp) return router_bp From 126481dcdd52873565d4020b162b364431b69164 Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Wed, 11 Sep 2024 00:21:06 +0300 Subject: [PATCH 09/11] Added file names to success and fail messages --- app/back-end/src/events/workspace_export_event.py | 5 +++-- .../fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/back-end/src/events/workspace_export_event.py b/app/back-end/src/events/workspace_export_event.py index 846396d..bb48606 100644 --- a/app/back-end/src/events/workspace_export_event.py +++ b/app/back-end/src/events/workspace_export_event.py @@ -56,6 +56,7 @@ def handle_workspace_export_feedback(data): - `status` (str): The status of the file export operation ("success" or "failure"). - `uuid` (str): The unique identifier for the user's session. - `sid` (str): The session identifier used for emitting real-time feedback. + - `fileName` (str): The name of the file that is being exported. Emits: - Success message to the user's console if the status is "success". @@ -68,14 +69,14 @@ def handle_workspace_export_feedback(data): if data["status"] == "success": socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, - {"type": "succ", "message": "File export completed successfully."}, + {"type": "succ", "message": f"File '{data['fileName']}' export was completed successfully."}, data["uuid"], data["sid"], ) else: socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, - {"type": "errr", "message": "File export failed."}, + {"type": "errr", "message": f"File '{data['fileName']}' export failed."}, data["uuid"], data["sid"], ) diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 5595cb1..2b1375e 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -175,7 +175,12 @@ export const FileTreeItemContextMenu: React.FC = ( link.click(); window.URL.revokeObjectURL(url); - socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { uuid: getUUID(), sid: getSID(), status: 'success' }); + socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { + uuid: getUUID(), + sid: getSID(), + status: 'success', + fileName: item.id, + }); } catch (error) { socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { uuid: getUUID(), sid: getSID(), status: 'failure' }); console.error('Error exporting file:', error); From fcb4e2adcd1f383f3f744d71500fbd9599224717 Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Wed, 11 Sep 2024 00:24:08 +0300 Subject: [PATCH 10/11] Added filename in frontend export error --- .../fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 2b1375e..6433f9e 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -182,7 +182,12 @@ export const FileTreeItemContextMenu: React.FC = ( fileName: item.id, }); } catch (error) { - socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { uuid: getUUID(), sid: getSID(), status: 'failure' }); + socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { + uuid: getUUID(), + sid: getSID(), + status: 'failure', + fileName: item.id, + }); console.error('Error exporting file:', error); } }, [item]); From 7f112e89a403ce79ae14d974c33367b38cd7438d Mon Sep 17 00:00:00 2001 From: Justinas <156369263+justinnas@users.noreply.github.com> Date: Wed, 11 Sep 2024 00:30:49 +0300 Subject: [PATCH 11/11] Switched from fileName to filePath variable --- app/back-end/src/events/workspace_export_event.py | 4 ++-- .../fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/back-end/src/events/workspace_export_event.py b/app/back-end/src/events/workspace_export_event.py index bb48606..cd14f3d 100644 --- a/app/back-end/src/events/workspace_export_event.py +++ b/app/back-end/src/events/workspace_export_event.py @@ -69,14 +69,14 @@ def handle_workspace_export_feedback(data): if data["status"] == "success": socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, - {"type": "succ", "message": f"File '{data['fileName']}' export was completed successfully."}, + {"type": "succ", "message": f"File '{data['filePath']}' export was completed successfully."}, data["uuid"], data["sid"], ) else: socketio_emit_to_user_session( CONSOLE_FEEDBACK_EVENT, - {"type": "errr", "message": f"File '{data['fileName']}' export failed."}, + {"type": "errr", "message": f"File '{data['filePath']}' export failed."}, data["uuid"], data["sid"], ) diff --git a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx index 6433f9e..6ba3320 100644 --- a/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx +++ b/app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItemContextMenu.tsx @@ -179,14 +179,14 @@ export const FileTreeItemContextMenu: React.FC = ( uuid: getUUID(), sid: getSID(), status: 'success', - fileName: item.id, + filePath: item.id, }); } catch (error) { socket.emit(Events.WORKSPACE_EXPORT_FEEDBACK_EVENT, { uuid: getUUID(), sid: getSID(), status: 'failure', - fileName: item.id, + filePath: item.id, }); console.error('Error exporting file:', error); }