headless_lms_server/domain/csv_export/
submissions.rs

1use anyhow::Result;
2use bytes::Bytes;
3
4use futures::TryStreamExt;
5use headless_lms_models::exercise_task_submissions;
6
7use async_trait::async_trait;
8
9use crate::domain::csv_export::CsvWriter;
10
11use sqlx::PgConnection;
12use std::io::Write;
13use tokio::sync::mpsc::UnboundedSender;
14
15use uuid::Uuid;
16
17use crate::prelude::*;
18
19use super::{
20    super::authorization::{AuthorizationToken, AuthorizedResponse},
21    CSVExportAdapter, CsvExportDataLoader,
22};
23
24pub struct ExamSubmissionExportOperation {
25    pub exam_id: Uuid,
26}
27
28#[async_trait]
29impl CsvExportDataLoader for ExamSubmissionExportOperation {
30    async fn load_data(
31        &self,
32        sender: UnboundedSender<Result<AuthorizedResponse<Bytes>, ControllerError>>,
33        conn: &mut PgConnection,
34        token: AuthorizationToken,
35    ) -> anyhow::Result<CSVExportAdapter> {
36        export_exam_submissions(
37            &mut *conn,
38            self.exam_id,
39            CSVExportAdapter {
40                sender,
41                authorization_token: token,
42            },
43        )
44        .await
45    }
46}
47
48/// Writes the exam submissions as csv into the writer
49pub async fn export_exam_submissions<W>(
50    conn: &mut PgConnection,
51    exam_id: Uuid,
52    writer: W,
53) -> Result<W>
54where
55    W: Write + Send + 'static,
56{
57    let headers = IntoIterator::into_iter([
58        "id".to_string(),
59        "user_id".to_string(),
60        "created_at".to_string(),
61        "exercise_id".to_string(),
62        "exercise_task_id".to_string(),
63        "score_given".to_string(),
64        "data_json".to_string(),
65    ]);
66
67    let mut stream = exercise_task_submissions::stream_exam_submissions(conn, exam_id);
68
69    let writer = CsvWriter::new_with_initialized_headers(writer, headers).await?;
70    while let Some(next) = stream.try_next().await? {
71        let csv_row = vec![
72            next.id.to_string(),
73            next.user_id.to_string(),
74            next.created_at.to_rfc3339(),
75            next.exercise_id.to_string(),
76            next.exercise_task_id.to_string(),
77            next.score_given.unwrap_or(0.0).to_string(),
78            next.data_json
79                .map(|o| o.to_string())
80                .unwrap_or_else(|| "".to_string()),
81        ];
82        writer.write_record(csv_row);
83    }
84    let writer = writer.finish().await?;
85    Ok(writer)
86}
87
88pub struct CourseSubmissionExportOperation {
89    pub course_id: Uuid,
90}
91
92#[async_trait]
93impl CsvExportDataLoader for CourseSubmissionExportOperation {
94    async fn load_data(
95        &self,
96        sender: UnboundedSender<Result<AuthorizedResponse<Bytes>, ControllerError>>,
97        conn: &mut PgConnection,
98        token: AuthorizationToken,
99    ) -> anyhow::Result<CSVExportAdapter> {
100        export_course_exercise_task_submissions(
101            &mut *conn,
102            self.course_id,
103            CSVExportAdapter {
104                sender,
105                authorization_token: token,
106            },
107        )
108        .await
109    }
110}
111
112/// Writes the course submissions as csv into the writer
113pub async fn export_course_exercise_task_submissions<W>(
114    conn: &mut PgConnection,
115    course_id: Uuid,
116    writer: W,
117) -> Result<W>
118where
119    W: Write + Send + 'static,
120{
121    let headers = IntoIterator::into_iter([
122        "exercise_slide_submission_id".to_string(),
123        "exercise_task_submission_id".to_string(),
124        "user_id".to_string(),
125        "created_at".to_string(),
126        "course_instance_id".to_string(),
127        "exercise_id".to_string(),
128        "exercise_task_id".to_string(),
129        "score_given".to_string(),
130        "data_json".to_string(),
131    ]);
132
133    let mut stream = exercise_task_submissions::stream_course_submissions(conn, course_id);
134
135    let writer = CsvWriter::new_with_initialized_headers(writer, headers).await?;
136    while let Some(next) = stream.try_next().await? {
137        let csv_row = vec![
138            next.exercise_slide_submission_id.to_string(),
139            next.id.to_string(),
140            next.user_id.to_string(),
141            next.created_at.to_rfc3339(),
142            next.course_instance_id
143                .map(|o| o.to_string())
144                .unwrap_or_else(|| "".to_string()),
145            next.exercise_id.to_string(),
146            next.exercise_task_id.to_string(),
147            next.score_given.unwrap_or(0.0).to_string(),
148            next.data_json
149                .map(|o| o.to_string())
150                .unwrap_or_else(|| "".to_string()),
151        ];
152        writer.write_record(csv_row);
153    }
154    let writer = writer.finish().await?;
155    Ok(writer)
156}