api/routes/modules/assignments/mark_allocator/
get.rs

1use axum::{extract::Path, http::StatusCode, response::IntoResponse, Json};
2use serde_json::json;
3use util::mark_allocator::mark_allocator::{load_allocator, SaveError};
4use crate::response::ApiResponse;
5
6/// GET /api/modules/{module_id}/assignments/{assignment_id}/mark_allocator
7///
8/// Load the mark allocator JSON configuration for a specific assignment. Accessible to users with
9/// appropriate permissions assigned to the module.
10///
11/// The mark allocator configuration defines how marks are distributed across different tasks and
12/// criteria within an assignment. This configuration is used by the grading system to determine
13/// the weight and allocation of marks for various assessment components.
14///
15/// ### Path Parameters
16/// - `module_id` (i64): The ID of the module containing the assignment
17/// - `assignment_id` (i64): The ID of the assignment to load mark allocator for
18///
19/// ### Example Request
20/// ```bash
21/// curl -X GET http://localhost:3000/api/modules/1/assignments/2/mark_allocator \
22///   -H "Authorization: Bearer <token>"
23/// ```
24///
25/// ### Success Response (200 OK)
26/// ```json
27/// {
28///   "success": true,
29///   "message": "Mark allocator successfully loaded.",
30///   "data": {
31///     "tasks": [
32///       {
33///         "task_number": 1,
34///         "weight": 0.4,
35///         "criteria": [
36///           {
37///             "name": "Correctness",
38///             "weight": 0.7
39///           },
40///           {
41///             "name": "Code Quality",
42///             "weight": 0.3
43///           }
44///         ]
45///       },
46///       {
47///         "task_number": 2,
48///         "weight": 0.6,
49///         "criteria": [
50///           {
51///             "name": "Functionality",
52///             "weight": 0.8
53///           },
54///           {
55///             "name": "Documentation",
56///             "weight": 0.2
57///           }
58///         ]
59///       }
60///     ],
61///     "total_weight": 1.0
62///   }
63/// }
64/// ```
65///
66/// ### Error Responses
67///
68/// **404 Not Found** - Module or assignment folder does not exist
69/// ```json
70/// {
71///   "error": "Module or assignment folder does not exist"
72/// }
73/// ```
74///
75/// **500 Internal Server Error** - Failed to load allocator
76/// ```json
77/// {
78///   "error": "Failed to load allocator"
79/// }
80/// ```
81///
82/// ### Mark Allocator Structure
83/// The mark allocator configuration typically contains:
84/// - `tasks`: Array of task configurations with mark allocations
85///   - `task_number`: Sequential number of the task
86///   - `weight`: Relative weight of this task in the overall assignment (0.0 to 1.0)
87///   - `criteria`: Array of grading criteria for this task
88///     - `name`: Name of the grading criterion
89///     - `weight`: Weight of this criterion within the task (0.0 to 1.0)
90/// - `total_weight`: Sum of all task weights (should equal 1.0)
91///
92/// ### Notes
93/// - The mark allocator configuration is stored as a JSON file in the module/assignment directory
94/// - This configuration is used by the grading system to calculate final marks
95/// - Task weights should sum to 1.0 across all tasks in the assignment
96/// - Criteria weights within each task should also sum to 1.0
97/// - Mark allocator loading is restricted to users with appropriate module permissions
98pub async fn load(
99    Path((module_id, assignment_id)): Path<(i64, i64)>
100) -> impl IntoResponse {
101    match load_allocator(module_id, assignment_id).await {
102        Ok(json) => (
103            StatusCode::OK,
104            Json(ApiResponse::success(
105                json,
106                "Mark allocator successfully loaded.",
107            )),
108        )
109            .into_response(),
110
111        Err(SaveError::DirectoryNotFound) => (
112            StatusCode::NOT_FOUND,
113            Json(json!({ "error": "Module or assignment folder does not exist" })),
114        )
115            .into_response(),
116
117        Err(_) => (
118            StatusCode::INTERNAL_SERVER_ERROR,
119            Json(json!({ "error": "Failed to load allocator" })),
120        )
121            .into_response(),
122    }
123}