Skip to content
Extraits de code Groupes Projets
Valider 47f90f56 rédigé par Delphine van Rossum's avatar Delphine van Rossum Validation de Brieuc Dubois
Parcourir les fichiers

Resolve "Studies frontend for creation and edition"

parent 549fe1d9
Aucune branche associée trouvée
Aucune étiquette associée trouvée
2 requêtes de fusion!43Merge dev into main,!41Resolve "Studies frontend for creation and edition"
"""study_data_divided_in_more_field
Revision ID: c4ff1cfa66b7
Revises: 344d94d32fa1
Create Date: 2025-03-11 15:37:02.225207
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "c4ff1cfa66b7"
down_revision: Union[str, None] = "344d94d32fa1"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.drop_column("studies", "consent_study_data")
op.add_column(
"studies", sa.Column("study_data_organisation", sa.String, nullable=False)
)
op.add_column("studies", sa.Column("study_data_address", sa.String, nullable=False))
op.add_column("studies", sa.Column("study_data_contact", sa.String, nullable=False))
op.add_column("studies", sa.Column("study_data_email", sa.String, nullable=False))
def downgrade() -> None:
op.add_column("studies", sa.Column("consent_study_data", sa.String, nullable=False))
op.drop_column("studies", "study_data_organisation")
op.drop_column("studies", "study_data_address")
op.drop_column("studies", "study_data_contact")
op.drop_column("studies", "study_data_email")
...@@ -21,7 +21,10 @@ class Study(Base): ...@@ -21,7 +21,10 @@ class Study(Base):
consent_participation = Column(String) consent_participation = Column(String)
consent_privacy = Column(String) consent_privacy = Column(String)
consent_rights = Column(String) consent_rights = Column(String)
consent_study_data = Column(String) study_data_organisation = Column(String)
study_data_address = Column(String)
study_data_contact = Column(String)
study_data_email = Column(String)
users = relationship("User", secondary="study_users") users = relationship("User", secondary="study_users")
tests = relationship("Test", secondary="study_tests") tests = relationship("Test", secondary="study_tests")
......
...@@ -13,7 +13,10 @@ class StudyCreate(BaseModel): ...@@ -13,7 +13,10 @@ class StudyCreate(BaseModel):
consent_participation: str consent_participation: str
consent_privacy: str consent_privacy: str
consent_rights: str consent_rights: str
consent_study_data: str study_data_organisation: str
study_data_address: str
study_data_contact: str
study_data_email: str
user_ids: list[int] = [] user_ids: list[int] = []
test_ids: list[int] = [] test_ids: list[int] = []
...@@ -29,7 +32,10 @@ class Study(BaseModel): ...@@ -29,7 +32,10 @@ class Study(BaseModel):
consent_participation: str consent_participation: str
consent_privacy: str consent_privacy: str
consent_rights: str consent_rights: str
consent_study_data: str study_data_organisation: str
study_data_address: str
study_data_contact: str
study_data_email: str
users: list[User] = [] users: list[User] = []
tests: list[Test] = [] tests: list[Test] = []
...@@ -145,7 +145,10 @@ ...@@ -145,7 +145,10 @@
"programed": "Scheduled", "programed": "Scheduled",
"status": "Status", "status": "Status",
"topics": "Topics", "topics": "Topics",
"toggle": "Participants" "toggle": "Participants",
"title": "Study Title",
"OrganisationUni": "Organization/University",
"Address": "Address"
} }
}, },
"button": { "button": {
...@@ -430,5 +433,11 @@ ...@@ -430,5 +433,11 @@
"tutor": "Tutor", "tutor": "Tutor",
"tutors": "Tutor(s)" "tutors": "Tutor(s)"
} }
},
"studies": {
"studyDescription": "Research Project (title and/or funding of the project this study is part of)",
"addressD": "Place Cardinal Mercier 14, 1348 Louvain-la-Neuve",
"contact": "Contact Person/Principal Investigator",
"email": "Email of PI/Contact Person"
} }
} }
...@@ -378,6 +378,10 @@ ...@@ -378,6 +378,10 @@
"consentPrivacy": "Les données collectées (par exemple, les transcriptions des conversations, les résultats de tests, les mesures de frappe, les informations sur les participants comme l'age ou le genre) seront traitées de manière confidentielle et anonyme. Elles seront conservées après leur anonymisation intégrale et ne pourront être utilisées qu'à des fins scientifiques ou pédagogiques. Elles pourront éventuellement être partagées avec d'autres chercheurs ou enseignants, mais toujours dans ce cadre strictement de recherche ou d'enseignement.", "consentPrivacy": "Les données collectées (par exemple, les transcriptions des conversations, les résultats de tests, les mesures de frappe, les informations sur les participants comme l'age ou le genre) seront traitées de manière confidentielle et anonyme. Elles seront conservées après leur anonymisation intégrale et ne pourront être utilisées qu'à des fins scientifiques ou pédagogiques. Elles pourront éventuellement être partagées avec d'autres chercheurs ou enseignants, mais toujours dans ce cadre strictement de recherche ou d'enseignement.",
"consentRights": "Votre participation à cette étude est volontaire. Vous pouvez à tout moment décider de ne plus participer à l'étude sans avoir à vous justifier. Vous pouvez également demander à ce que vos données soient supprimées à tout moment. Si vous avez des questions ou des préoccupations concernant cette étude, vous pouvez contacter le responsable de l'étude.", "consentRights": "Votre participation à cette étude est volontaire. Vous pouvez à tout moment décider de ne plus participer à l'étude sans avoir à vous justifier. Vous pouvez également demander à ce que vos données soient supprimées à tout moment. Si vous avez des questions ou des préoccupations concernant cette étude, vous pouvez contacter le responsable de l'étude.",
"consentStudyData": "Informations sur l'étude.", "consentStudyData": "Informations sur l'étude.",
"studyDescription": "Projet de recherche (titre et/ou financement du projet dans lequel s'inscrit cette étude)",
"addressD": "Place Cardinal Mercier 14, 1348 Louvain-la-Neuve",
"contact": "Personne de contact/chercheur principal",
"email": "Email du PI/de la personne de contact",
"tab": { "tab": {
"study": "Étude", "study": "Étude",
"code": "Code", "code": "Code",
...@@ -487,14 +491,16 @@ ...@@ -487,14 +491,16 @@
"inProgress": "En cours", "inProgress": "En cours",
"finished": "Terminée", "finished": "Terminée",
"topics": "Topics", "topics": "Topics",
"title": "Titre", "title": "Titre de l'étude",
"users": "Utilisateurs", "users": "Utilisateurs",
"description": "Description", "description": "Description",
"email": "E-mail", "email": "E-mail",
"toggle": "Participants", "toggle": "Participants",
"groups": "groupes", "groups": "groupes",
"questions": "questions", "questions": "questions",
"tests": "tests" "tests": "tests",
"OrganisationUni": "Organisation/Université",
"Address": "Adresse"
} }
}, },
"inputs": { "inputs": {
......
...@@ -31,7 +31,10 @@ export async function createStudyAPI( ...@@ -31,7 +31,10 @@ export async function createStudyAPI(
consentParticipation: string, consentParticipation: string,
consentPrivacy: string, consentPrivacy: string,
consentRights: string, consentRights: string,
consentStudyData: string, studyOrganisation: string,
studyAddress: string,
studyContact: string,
studyPIemail: string,
user_ids: number[] user_ids: number[]
): Promise<number | null> { ): Promise<number | null> {
const response = await fetch('/api/studies', { const response = await fetch('/api/studies', {
...@@ -47,7 +50,10 @@ export async function createStudyAPI( ...@@ -47,7 +50,10 @@ export async function createStudyAPI(
consent_participation: consentParticipation, consent_participation: consentParticipation,
consent_privacy: consentPrivacy, consent_privacy: consentPrivacy,
consent_rights: consentRights, consent_rights: consentRights,
consent_study_data: consentStudyData, study_data_organisation: studyOrganisation,
study_data_address: studyAddress,
study_data_contact: studyContact,
study_data_email: studyPIemail,
user_ids user_ids
}) })
}); });
......
...@@ -40,8 +40,10 @@ ...@@ -40,8 +40,10 @@
form?.consentPrivacy ?? (study ? study.consentPrivacy : $t('studies.consentPrivacy')); form?.consentPrivacy ?? (study ? study.consentPrivacy : $t('studies.consentPrivacy'));
let consentRights = let consentRights =
form?.consentRights ?? (study ? study.consentRights : $t('studies.consentRights')); form?.consentRights ?? (study ? study.consentRights : $t('studies.consentRights'));
let consentStudyData = let studyOrganisation = study ? study.studyOrganisation : '';
form?.consentStudyData ?? (study ? study.consentStudyData : $t('studies.consentStudyData')); let studyAddress = form?.studyAddress ?? (study ? study.studyAddress : $t('studies.addressD'));
let studyContact = study ? study.studyContact : '';
let studyPIemail = study ? study.studyPIemail : '';
let newUsername: string = $state(''); let newUsername: string = $state('');
let newUserModal = $state(false); let newUserModal = $state(false);
...@@ -128,7 +130,7 @@ ...@@ -128,7 +130,7 @@
<!-- Title & description --> <!-- Title & description -->
<label class="label" for="title">{$t('utils.words.title')} *</label> <label class="label" for="title">{$t('utils.words.title')} *</label>
<input class="input w-full" type="text" id="title" name="title" required bind:value={title} /> <input class="input w-full" type="text" id="title" name="title" required bind:value={title} />
<label class="label" for="description">{$t('utils.words.description')}</label> <label class="label" for="description">{$t('studies.studyDescription')}</label>
<textarea <textarea
use:autosize use:autosize
class="input w-full max-h-52" class="input w-full max-h-52"
...@@ -268,17 +270,47 @@ ...@@ -268,17 +270,47 @@
value={consentRights} value={consentRights}
required required
></textarea> ></textarea>
<label class="label text-sm" for="consentStudyData" <h3 class="py-2 px-1">{$t('register.consent.studyData.title')}</h3>
>{$t('register.consent.studyData.title')} *</label <label class="label text-sm" for="StudyOrganisation"
>{$t('utils.words.OrganisationUni')} *</label
> >
<textarea <textarea
use:autosize use:autosize
class="input w-full max-h-52" class="input w-full max-h-52"
id="consentStudyData" id="StudyOrganisation"
name="consentStudyData" name="StudyOrganisation"
value={consentStudyData} value={studyOrganisation}
required required
></textarea> ></textarea>
<label class="label text-sm" for="StudyAddress">{$t('utils.words.Address')} *</label>
<textarea
use:autosize
class="input w-full max-h-52"
id="StudyAddress"
name="StudyAddress"
value={studyAddress}
required
></textarea>
<label class="label text-sm" for="StudyContact">{$t('studies.contact')} *</label>
<textarea
use:autosize
class="input w-full max-h-52"
id="StudyContact"
name="StudyContact"
value={studyContact}
required
></textarea>
<label class="label text-sm" for="StudyPIemail">
{$t('studies.email')} *
</label>
<input
class="input w-full"
id="StudyPIemail"
name="StudyPIemail"
type="email"
bind:value={studyPIemail}
required
/>
<!-- submit, cancel and delete buttons --> <!-- submit, cancel and delete buttons -->
<div class="mt-4 mb-6"> <div class="mt-4 mb-6">
......
...@@ -22,8 +22,11 @@ export default class Study { ...@@ -22,8 +22,11 @@ export default class Study {
private _consentParticipation: string; private _consentParticipation: string;
private _consentPrivacy: string; private _consentPrivacy: string;
private _consentRights: string; private _consentRights: string;
private _consentStudyData: string;
private _tests: Test[]; private _tests: Test[];
private _studyOrganisation: string;
private _studyAddress: string;
private _studyContact: string;
private _studyPIemail: string;
private constructor( private constructor(
id: number, id: number,
...@@ -36,8 +39,11 @@ export default class Study { ...@@ -36,8 +39,11 @@ export default class Study {
consentParticipation: string, consentParticipation: string,
consentPrivacy: string, consentPrivacy: string,
consentRights: string, consentRights: string,
consentStudyData: string, tests: Test[],
tests: Test[] studyOrganisation: string,
studyAddress: string,
studyContact: string,
studyPIemail: string
) { ) {
this._id = id; this._id = id;
this._title = title; this._title = title;
...@@ -49,8 +55,11 @@ export default class Study { ...@@ -49,8 +55,11 @@ export default class Study {
this._consentParticipation = consentParticipation; this._consentParticipation = consentParticipation;
this._consentPrivacy = consentPrivacy; this._consentPrivacy = consentPrivacy;
this._consentRights = consentRights; this._consentRights = consentRights;
this._consentStudyData = consentStudyData;
this._tests = tests; this._tests = tests;
this._studyOrganisation = studyOrganisation;
this._studyAddress = studyAddress;
this._studyContact = studyContact;
this._studyPIemail = studyPIemail;
} }
get id(): number { get id(): number {
...@@ -133,14 +142,6 @@ export default class Study { ...@@ -133,14 +142,6 @@ export default class Study {
this._consentRights = value; this._consentRights = value;
} }
get consentStudyData(): string {
return this._consentStudyData;
}
set consentStudyData(value: string) {
this._consentStudyData = value;
}
get tests(): Test[] { get tests(): Test[] {
return this._tests; return this._tests;
} }
...@@ -149,6 +150,38 @@ export default class Study { ...@@ -149,6 +150,38 @@ export default class Study {
this._tests = value; this._tests = value;
} }
get studyOrganisation(): string {
return this._studyOrganisation;
}
set studyOrganisation(value: string) {
this._studyOrganisation = value;
}
get studyAddress(): string {
return this._studyAddress;
}
set studyAddress(value: string) {
this._studyAddress = value;
}
get studyContact(): string {
return this._studyContact;
}
set studyContact(value: string) {
this._studyContact = value;
}
get studyPIemail(): string {
return this._studyPIemail;
}
set studyPIemail(value: string) {
this._studyPIemail = value;
}
/** /**
* Creates a new Study and saves it in the database. * Creates a new Study and saves it in the database.
* @async * @async
...@@ -165,6 +198,10 @@ export default class Study { ...@@ -165,6 +198,10 @@ export default class Study {
consentRights: string, consentRights: string,
consentStudyData: string, consentStudyData: string,
tests: Test[], tests: Test[],
studyOrganisation: string,
studyAddress: string,
studyContact: string,
studyPIemail: string,
users: User[], users: User[],
f: fetchType = fetch f: fetchType = fetch
): Promise<Study | null> { ): Promise<Study | null> {
...@@ -179,7 +216,10 @@ export default class Study { ...@@ -179,7 +216,10 @@ export default class Study {
consentParticipation, consentParticipation,
consentPrivacy, consentPrivacy,
consentRights, consentRights,
consentStudyData, studyOrganisation,
studyAddress,
studyContact,
studyPIemail,
users.map((u) => u.id) users.map((u) => u.id)
); );
...@@ -195,8 +235,11 @@ export default class Study { ...@@ -195,8 +235,11 @@ export default class Study {
consentParticipation, consentParticipation,
consentPrivacy, consentPrivacy,
consentRights, consentRights,
consentStudyData, tests,
tests studyOrganisation,
studyAddress,
studyContact,
studyPIemail
); );
} }
return null; return null;
...@@ -228,7 +271,10 @@ export default class Study { ...@@ -228,7 +271,10 @@ export default class Study {
if (data.consent_participation) this._consentParticipation = data.consent_participation; if (data.consent_participation) this._consentParticipation = data.consent_participation;
if (data.consent_privacy) this._consentPrivacy = data.consent_privacy; if (data.consent_privacy) this._consentPrivacy = data.consent_privacy;
if (data.consent_rights) this._consentRights = data.consent_rights; if (data.consent_rights) this._consentRights = data.consent_rights;
if (data.consent_study_data) this._consentStudyData = data.consent_study_data; if (data.study_organisation) this._studyOrganisation = data.study_organisation;
if (data.study_address) this._studyAddress = data.study_address;
if (data.study_contact) this._studyContact = data.study_contact;
if (data.study_pi_email) this._studyPIemail = data.study_pi_email;
if (data.tests) this._tests = data.tests; if (data.tests) this._tests = data.tests;
return true; return true;
} }
...@@ -292,8 +338,11 @@ export default class Study { ...@@ -292,8 +338,11 @@ export default class Study {
json.consent_participation, json.consent_participation,
json.consent_privacy, json.consent_privacy,
json.consent_rights, json.consent_rights,
json.consent_study_data, [],
[] json.study_data_organisation,
json.study_data_address,
json.study_data_contact,
json.study_data_email
); );
study._users = User.parseAll(json.users); study._users = User.parseAll(json.users);
......
...@@ -14,7 +14,10 @@ export const actions: Actions = { ...@@ -14,7 +14,10 @@ export const actions: Actions = {
const consentParticipation = formData.get('consentParticipation')?.toString(); const consentParticipation = formData.get('consentParticipation')?.toString();
const consentPrivacy = formData.get('consentPrivacy')?.toString(); const consentPrivacy = formData.get('consentPrivacy')?.toString();
const consentRights = formData.get('consentRights')?.toString(); const consentRights = formData.get('consentRights')?.toString();
const consentStudyData = formData.get('consentStudyData')?.toString(); const studyOrganisation = formData.get('StudyOrganisation')?.toString();
const studyAddress = formData.get('StudyAddress')?.toString();
const studyContact = formData.get('StudyContact')?.toString();
const studyPIemail = formData.get('StudyPIemail')?.toString();
if ( if (
!title || !title ||
...@@ -24,10 +27,13 @@ export const actions: Actions = { ...@@ -24,10 +27,13 @@ export const actions: Actions = {
!consentParticipation || !consentParticipation ||
!consentPrivacy || !consentPrivacy ||
!consentRights || !consentRights ||
!consentStudyData !studyOrganisation ||
!studyAddress ||
!studyContact ||
!studyPIemail
) { ) {
return { return {
message: 'Invalid request 1' message: 'Invalid request: Missing required fields'
}; };
} }
...@@ -40,20 +46,20 @@ export const actions: Actions = { ...@@ -40,20 +46,20 @@ export const actions: Actions = {
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
return { return {
message: 'Invalid request 2' message: 'Invalid request: Invalid dates'
}; };
} }
const nbSession = parseInt(nbSessionStr, 10); const nbSession = parseInt(nbSessionStr, 10);
if (isNaN(nbSession)) { if (isNaN(nbSession)) {
return { return {
message: 'Invalid request 3' message: 'Invalid request: Invalid number of sessions'
}; };
} }
if (startDate.getTime() > endDate.getTime()) { if (startDate.getTime() > endDate.getTime()) {
return { return {
message: 'End time cannot be before start time' message: 'End date cannot be before start date'
}; };
} }
...@@ -81,7 +87,10 @@ export const actions: Actions = { ...@@ -81,7 +87,10 @@ export const actions: Actions = {
consentParticipation, consentParticipation,
consentPrivacy, consentPrivacy,
consentRights, consentRights,
consentStudyData, studyOrganisation,
studyAddress,
studyContact,
studyPIemail,
user_ids user_ids
); );
...@@ -94,3 +103,4 @@ export const actions: Actions = { ...@@ -94,3 +103,4 @@ export const actions: Actions = {
return redirect(303, '/admin/studies'); return redirect(303, '/admin/studies');
} }
}; };
null as any as Actions;
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