Skip to content

Commit

Permalink
MDE/PKFE-46 fixed pylint errors and minor structure changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mantvydasdeltuva committed Sep 10, 2024
1 parent 3abbb2c commit d52efa6
Show file tree
Hide file tree
Showing 5 changed files with 387 additions and 184 deletions.
100 changes: 97 additions & 3 deletions app/back-end/src/routes/workspace_aggregate_route.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,6 +38,39 @@
f"{WORKSPACE_AGGREGATE_ROUTE}/all/<path:relative_path>", 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")

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -203,6 +257,45 @@ def get_workspace_aggregate_all(relative_path):
f"{WORKSPACE_AGGREGATE_ROUTE}/<path:relative_path>", 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")

Expand Down Expand Up @@ -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,
Expand Down
117 changes: 117 additions & 0 deletions app/back-end/src/routes/workspace_export_route.py
Original file line number Diff line number Diff line change
@@ -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/<path:relative_path>: 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}/<path:relative_path>", 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
Loading

0 comments on commit d52efa6

Please sign in to comment.