Skip to content
Extraits de code Groupes Projets
Valider 20feb55f rédigé par Brieuc Dubois's avatar Brieuc Dubois
Parcourir les fichiers

End of study questions

parent b948a056
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
"""Study info
Revision ID: cef049467e32
Revises: c4ff1cfa66b7
Create Date: 2025-03-13 00:19:21.655377
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "cef049467e32"
down_revision: Union[str, None] = "c4ff1cfa66b7"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
pass
def downgrade() -> None:
op.drop_table("study_infos")
......@@ -242,3 +242,15 @@ def download_study_wide(db: Session, study_id: int):
"Content-Disposition": f"attachment; filename={study_id}-surveys-wide.csv"
},
)
def create_study_info(
db: Session, study_id: int, study_info: schemas.StudyInfoCreate
) -> models.StudyInfo:
db_study_info = models.StudyInfo(
study_id=study_id, **study_info.dict(exclude_unset=True)
)
db.add(db_study_info)
db.commit()
db.refresh(db_study_info)
return db_study_info
......@@ -42,3 +42,15 @@ class StudyTest(Base):
study_id = Column(Integer, ForeignKey("studies.id"), primary_key=True)
test_id = Column(Integer, ForeignKey("tests.id"), primary_key=True)
class StudyInfo(Base):
__tablename__ = "study_infos"
id = Column(Integer, primary_key=True, index=True)
study_id = Column(Integer, ForeignKey("studies.id"))
rid = Column(String)
birthyear = Column(Integer)
gender = Column(String)
primary_language = Column(String)
other_languages = Column(String)
education = Column(String)
......@@ -75,7 +75,7 @@ def download_study(
@require_admin("You do not have permission to download this study.")
@studiesRouter.get("/{study_id}/download/surveys-wide")
def download_study(
def download_study_wide(
study_id: int,
db: Session = Depends(get_db),
):
......@@ -83,3 +83,12 @@ def download_study(
if study is None:
raise HTTPException(status_code=404, detail="Study not found")
return crud.download_study_wide(db, study_id)
@studiesRouter.post("/{study_id}/info", status_code=status.HTTP_201_CREATED)
def create_study_info(
study_id: int,
study_info: schemas.StudyInfoCreate,
db: Session = Depends(get_db),
):
return crud.create_study_info(db, study_id, study_info)
......@@ -39,3 +39,12 @@ class Study(BaseModel):
users: list[User] = []
tests: list[Test] = []
class StudyInfoCreate(BaseModel):
rid: str
birthyear: int
gender: str
primary_language: str
other_languages: str
education: str
......@@ -386,6 +386,7 @@
"study": "Étude",
"code": "Code",
"tests": "Tests",
"infos": "Informations",
"end": "Fin"
},
"complete": "Merci pour votre participation !",
......
......@@ -111,3 +111,29 @@ export async function createTestTypingAPI(
return parseInt(await response.text());
}
export async function sendStudyResponseInfoAPI(
fetch: fetchType,
survey_id: number,
rid: string,
birthyear: number,
gender: string,
primary_language: string,
other_languages: string,
education: string
) {
const response = await fetch(`/api/studies/${survey_id}/info`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
rid,
birthyear,
gender,
primary_language,
other_languages,
education
})
});
return response.ok;
}
......@@ -50,29 +50,3 @@ export async function getSurveyScoreAPI(fetch: fetchType, survey_id: number, sid
return await response.json();
}
export async function sendSurveyResponseInfoAPI(
fetch: fetchType,
survey_id: number,
sid: string,
birthyear: number,
gender: string,
primary_language: string,
other_language: string,
education: string
) {
const response = await fetch(`/api/tests/info/${survey_id}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sid,
birthyear,
gender,
primary_language,
other_language,
education
})
});
return response.ok;
}
<script lang="ts">
import { sendStudyResponseInfoAPI } from '$lib/api/studies';
import config from '$lib/config';
import { t } from '$lib/services/i18n';
import { toastAlert } from '$lib/utils/toasts';
import Dropdown from './dropdown.svelte';
let {
study_id,
rid,
onFinish = () => {}
}: { study_id: number; rid: string; onFinish: Function } = $props();
let step = $state(0);
const genderOptions = [
{ value: 'male', label: $t('surveys.genders.male') },
{ value: 'female', label: $t('surveys.genders.female') },
{ value: 'other', label: $t('surveys.genders.other') },
{ value: 'na', label: $t('surveys.genders.na') }
];
let birthYear = '';
let gender = '';
let primaryLanguage = '';
let other_language = '';
let education = '';
let selectedOption: any = $state();
async function send() {
if (
!(await sendStudyResponseInfoAPI(
fetch,
study_id,
rid,
parseInt(birthYear),
gender,
primaryLanguage,
other_language,
education
))
) {
toastAlert($t('surveys.info.error'));
} else {
onFinish();
}
}
</script>
{#if step === 0}
<div class="mx-auto mt-16 text-center px-4">
<p class="text-center font-bold py-4 px-6 m-auto">{$t('surveys.birthYear')}</p>
<Dropdown
values={Array.from({ length: 82 }, (_, i) => {
const year = 1931 + i;
return { value: year, display: year };
}).reverse()}
bind:option={selectedOption}
placeholder={$t('surveys.birthYear')}
funct={() => {
birthYear = selectedOption;
step++;
}}
></Dropdown>
</div>
{:else if step === 1}
<div class="mx-auto mt-16 text-center px-4">
<p class="text-center font-bold py-4 px-6 m-auto">{$t('surveys.gender')}</p>
<div class="flex flex-col items-center space-y-4">
{#each genderOptions as { value, label }}
<label class="radio-label flex items-center space-x-2">
<input
type="radio"
name="gender"
{value}
onchange={() => {
gender = value;
step++;
}}
required
class="radio-button"
/>
<span>{label}</span>
</label>
{/each}
</div>
</div>
{:else if step === 2}
<div class="mx-auto mt-16 text-center px-4">
<p class="text-center font-bold py-4 px-6 m-auto">{$t('surveys.homeLanguage')}</p>
<Dropdown
values={Object.entries(config.PRIMARY_LANGUAGE).map(([code, name]) => ({
value: code,
display: name
}))}
bind:option={selectedOption}
placeholder={$t('surveys.homeLanguage')}
funct={() => {
primaryLanguage = selectedOption;
step++;
}}
></Dropdown>
</div>
{:else if step === 3}
<div class="mx-auto mt-16 text-center px-4">
<p class="text-center font-bold py-4 px-6 m-auto">{$t('surveys.otherLanguage')}</p>
<p class="mb-6 text-sm text-gray-600 text-center">{$t('surveys.otherLanguageNote')}</p>
<Dropdown
values={[
{ value: 'none', display: '/' },
...Object.entries(config.PRIMARY_LANGUAGE).map(([code, name]) => ({
value: code,
display: name
}))
]}
bind:option={selectedOption}
placeholder={$t('surveys.otherLanguage')}
funct={() => {
other_language = selectedOption;
step++;
}}
></Dropdown>
</div>
{:else if step === 4}
<div class="mx-auto mt-16 text-center px-4">
<p class="text-center font-bold py-4 px-6 m-auto">{$t('surveys.education.title')}</p>
<Dropdown
values={[
{ value: 'NoEducation', display: $t('surveys.education.NoEducation') },
{ value: 'PrimarySchool', display: $t('surveys.education.PrimarySchool') },
{ value: 'SecondarySchool', display: $t('surveys.education.SecondarySchool') },
{ value: 'NonUni', display: $t('surveys.education.NonUni') },
{ value: 'Bachelor', display: $t('surveys.education.Bachelor') },
{ value: 'Master', display: $t('surveys.education.Master') }
]}
bind:option={selectedOption}
placeholder={$t('surveys.education.title')}
funct={() => {
education = selectedOption;
send();
}}
></Dropdown>
</div>
{/if}
......@@ -9,6 +9,7 @@
import { TestTask, TestTyping } from '$lib/types/tests';
import Typingbox from '$lib/components/tests/typingbox.svelte';
import { getTestEntriesScoreAPI } from '$lib/api/tests';
import EndQuestions from '$lib/components/surveys/endQuestions.svelte';
let { data, form }: { data: PageData; form: FormData } = $props();
let study: Study | undefined = $state(data.study);
......@@ -63,6 +64,9 @@
</li>
{/if}
<li class="step" class:step-primary={study && current_step >= study.tests.length + 2}>
{$t('studies.tab.infos')}
</li>
<li class="step" class:step-primary={study && current_step >= study.tests.length + 3}>
{$t('studies.tab.end')}
</li>
</ul>
......@@ -158,6 +162,10 @@
{/if}
{/key}
{:else if current_step == study.tests.length + 2}
<div class="flex flex-col h-full">
<EndQuestions study_id={study.id} {rid} onFinish={() => current_step++} />
</div>
{:else if current_step == study.tests.length + 3}
<div class="flex flex-col h-full">
<div class="flex-grow text-center mt-16">
<span>{$t('studies.complete')}</span>
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter