api/routes/modules/assignments/memo_output/
get.rs1use crate::response::ApiResponse;
2use axum::{extract::{Path, State}, http::StatusCode, response::IntoResponse, Json};
3use serde::Serialize;
4use tokio::fs as tokio_fs;
5use util::{execution_config::ExecutionConfig, state::AppState};
6use db::models::{assignment_memo_output, assignment_task};
7use sea_orm::{EntityTrait, ColumnTrait, QueryFilter};
8
9#[derive(Serialize)]
10struct MemoSubsection {
11 label: String,
12 output: String,
13}
14
15#[derive(Serialize)]
16struct MemoTaskOutput {
17 task_number: i64,
18 name: String,
19 subsections: Vec<MemoSubsection>,
20 raw: String,
21}
22
23pub async fn get_all_memo_outputs(
57 State(app_state): State<AppState>,
58 Path((module_id, assignment_id)): Path<(i64, i64)>,
59) -> impl IntoResponse {
60 let db = app_state.db();
61
62 let memo_outputs = match assignment_memo_output::Entity::find()
64 .filter(assignment_memo_output::Column::AssignmentId.eq(assignment_id))
65 .all(db)
66 .await
67 {
68 Ok(models) if !models.is_empty() => models,
69 Ok(_) => {
70 return (
71 StatusCode::NOT_FOUND,
72 Json(ApiResponse::<Vec<MemoTaskOutput>>::error("No memo output records found")),
73 );
74 }
75 Err(_) => {
76 return (
77 StatusCode::INTERNAL_SERVER_ERROR,
78 Json(ApiResponse::<Vec<MemoTaskOutput>>::error("Failed to query memo outputs")),
79 );
80 }
81 };
82
83 let separator = match ExecutionConfig::get_execution_config(module_id, assignment_id) {
85 Ok(config) => config.marking.deliminator,
86 Err(_) => "&-=-&".to_string(),
87 };
88
89 let mut results = Vec::new();
90
91 for memo in memo_outputs {
92 let full_path = memo.full_path();
93 if !full_path.is_file() {
94 continue;
95 }
96
97 let raw_content = match tokio_fs::read_to_string(&full_path).await {
98 Ok(c) => c,
99 Err(_) => continue,
100 };
101
102 let sections = raw_content
104 .split(&separator)
105 .filter(|s| !s.trim().is_empty());
106
107 let subsections = sections
108 .map(|s| {
109 let mut lines = s.lines();
110 let label = lines.next().unwrap_or("").trim().to_string();
111 let output = lines.collect::<Vec<_>>().join("\n");
112 MemoSubsection { label, output }
113 })
114 .collect::<Vec<_>>();
115
116 let Some(task) = assignment_task::Entity::find_by_id(memo.task_id)
118 .filter(assignment_task::Column::AssignmentId.eq(assignment_id))
119 .one(db)
120 .await
121 .ok()
122 .flatten()
123 else {
124 continue;
125 };
126
127 results.push(MemoTaskOutput {
128 task_number: task.task_number,
129 name: task.name,
130 subsections,
131 raw: raw_content,
132 });
133 }
134
135 if results.is_empty() {
136 return (
137 StatusCode::NOT_FOUND,
138 Json(ApiResponse::<Vec<MemoTaskOutput>>::error(
139 "No valid memo output files found",
140 )),
141 );
142 }
143
144 (
145 StatusCode::OK,
146 Json(ApiResponse::success(results, "Fetched memo output successfully")),
147 )
148}