From d6a2008efa3c57cc09cf699a6caa871bb4ea8c77 Mon Sep 17 00:00:00 2001
From: delphvr <delphine.vanrossum@student.uclouvain.be>
Date: Tue, 11 Mar 2025 09:45:33 +0100
Subject: [PATCH 1/2] csv wide

---
 backend/app/crud/studies.py   | 58 +++++++++++++++++++++++++++++++++++
 backend/app/routes/studies.py | 12 ++++++++
 2 files changed, 70 insertions(+)

diff --git a/backend/app/crud/studies.py b/backend/app/crud/studies.py
index 561fa1de..3377ac32 100644
--- a/backend/app/crud/studies.py
+++ b/backend/app/crud/studies.py
@@ -164,3 +164,61 @@ def download_study(db: Session, study_id: int):
         media_type="text/csv",
         headers={"Content-Disposition": f"attachment; filename={study_id}-surveys.csv"},
     )
+
+
+def download_study_wide(db: Session, study_id: int):
+    output = StringIO()
+    writer = csv.writer(output)
+
+    data = {}
+    question_ids = set()
+
+    db_entries = (
+        db.query(models.TestEntry).filter(models.TestEntry.study_id == study_id).all()
+    )
+
+    for entry in db_entries:
+        if entry.entry_task is None:
+            continue
+
+        user_id = entry.user_id
+        code = entry.code
+        item_id = entry.entry_task.test_question_id
+        key = (user_id, code)
+
+        if key not in data:
+            data[key] = {"study_id": study_id, "user_id": user_id, "code": code}
+
+        if entry.entry_task.entry_task_qcm:
+            selected_id = entry.entry_task.entry_task_qcm.selected_id
+            correct_id = entry.entry_task.test_question.question_qcm.correct
+            correct_answer = int(selected_id == correct_id)
+            data[key][item_id] = correct_answer
+            question_ids.add(item_id)
+
+        if entry.entry_task.entry_task_gapfill:
+            answer = entry.entry_task.entry_task_gapfill.text
+            correct = extract_text_between_angle_bracket(
+                entry.entry_task.test_question.question
+            )
+            correct_answer = int(answer == correct)
+            data[key][item_id] = correct_answer
+            question_ids.add(item_id)
+
+    # Sort question IDs for consistent column order
+    question_ids = sorted(question_ids)
+    header = ["study_id", "user_id", "code"] + question_ids
+    writer.writerow(header)
+    for (user_id, code), values in data.items():
+        row = [values.get(col, "") for col in header]
+        writer.writerow(row)
+
+    output.seek(0)
+
+    return StreamingResponse(
+        output,
+        media_type="text/csv",
+        headers={
+            "Content-Disposition": f"attachment; filename={study_id}-surveys-wide.csv"
+        },
+    )
diff --git a/backend/app/routes/studies.py b/backend/app/routes/studies.py
index 0cba51e4..04411b25 100644
--- a/backend/app/routes/studies.py
+++ b/backend/app/routes/studies.py
@@ -71,3 +71,15 @@ def download_study(
     if study is None:
         raise HTTPException(status_code=404, detail="Study not found")
     return crud.download_study(db, study_id)
+
+
+@require_admin("You do not have permission to download this study.")
+@studiesRouter.get("/{study_id}/download/surveys-wide")
+def download_study(
+    study_id: int,
+    db: Session = Depends(get_db),
+):
+    study = crud.get_study(db, study_id)
+    if study is None:
+        raise HTTPException(status_code=404, detail="Study not found")
+    return crud.download_study_wide(db, study_id)
-- 
GitLab


From bbd289ed4c20c6416b8a22d30a8bcd6964e46376 Mon Sep 17 00:00:00 2001
From: delphvr <delphine.vanrossum@student.uclouvain.be>
Date: Tue, 11 Mar 2025 09:57:49 +0100
Subject: [PATCH 2/2] csv wide added meta data

---
 backend/app/crud/studies.py                   | 24 +++++++++++++++++--
 .../src/routes/admin/studies/+page.svelte     | 10 ++++++--
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/backend/app/crud/studies.py b/backend/app/crud/studies.py
index 3377ac32..5848676a 100644
--- a/backend/app/crud/studies.py
+++ b/backend/app/crud/studies.py
@@ -187,7 +187,19 @@ def download_study_wide(db: Session, study_id: int):
         key = (user_id, code)
 
         if key not in data:
-            data[key] = {"study_id": study_id, "user_id": user_id, "code": code}
+            if user_id is not None:
+                user = crud.get_user(db, user_id)
+                data[key] = {
+                    "study_id": study_id,
+                    "user_id": user_id,
+                    "code": code,
+                    "home_language": user.home_language,
+                    "target_language": user.target_language,
+                    "gender": user.gender,
+                    "birthdate": user.birthdate,
+                }
+            else:
+                data[key] = {"study_id": study_id, "user_id": user_id, "code": code}
 
         if entry.entry_task.entry_task_qcm:
             selected_id = entry.entry_task.entry_task_qcm.selected_id
@@ -207,7 +219,15 @@ def download_study_wide(db: Session, study_id: int):
 
     # Sort question IDs for consistent column order
     question_ids = sorted(question_ids)
-    header = ["study_id", "user_id", "code"] + question_ids
+    header = [
+        "study_id",
+        "user_id",
+        "code",
+        "home_language",
+        "target_language",
+        "gender",
+        "birthdate",
+    ] + question_ids
     writer.writerow(header)
     for (user_id, code), values in data.items():
         row = [values.get(col, "") for col in header]
diff --git a/frontend/src/routes/admin/studies/+page.svelte b/frontend/src/routes/admin/studies/+page.svelte
index 5ca59fd3..254ee148 100644
--- a/frontend/src/routes/admin/studies/+page.svelte
+++ b/frontend/src/routes/admin/studies/+page.svelte
@@ -38,10 +38,16 @@
 						title="Download"
 						href={`${config.API_URL}/v1/studies/${study.id}/download/surveys`}
 					>
-						<Icon src={ArrowDownTray} size="16" />
+						<Icon src={ArrowDownTray} size="16" /> CSV long
+					</a>
+					<a
+						class="btn btn-primary btn-sm"
+						title="Download"
+						href={`${config.API_URL}/v1/studies/${study.id}/download/surveys-wide`}
+					>
+						<Icon src={ArrowDownTray} size="16" /> CSV wide
 					</a></td
 				>
-				<td></td>
 			</tr>
 		{/each}
 	</tbody>
-- 
GitLab