feat(research): add research study management functionality
- Implemented models for ResearchStudy, ResearchStudyArm, and ResearchParticipant. - Created views for listing, creating, updating, and exporting research studies. - Added bulk participant generation feature with CSV and JSON support. - Developed templates for study management, participant entry, and bulk generation. - Introduced URL routing for research study operations. - Added utility functions for randomisation and participant assignment. - Implemented participant intake process with demographic data collection. - Ensured proper handling of participant CIDs and assignment methods.
This commit is contained in:
@@ -1458,6 +1458,47 @@ class CaseCollection(ExamOrCollectionGenericBase):
|
||||
default=VIEWER_MODE_CHOICES.BUILT_IN,
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Marking configuration
|
||||
# These fields can be defined here so that the same CaseCollection can be
|
||||
# reused in multiple research studies or standalone with consistent marking.
|
||||
# -----------------------------------------------------------------------
|
||||
marking_scheme_name = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
help_text="Human-readable mark scheme name shown to markers and in exports.",
|
||||
)
|
||||
marking_guidance = models.TextField(
|
||||
blank=True,
|
||||
help_text="Plain-language marking guidance displayed to markers.",
|
||||
)
|
||||
case_question_max_score = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Maximum score per case used for normalisation. Defaults to 10 when not set.",
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Case question template
|
||||
# When set, this JSON schema is used as a default for all CaseDetail entries
|
||||
# in the collection that do not have their own question_schema.
|
||||
# -----------------------------------------------------------------------
|
||||
case_question_template = models.JSONField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=(
|
||||
"JSON schema applied as a default question template for all cases in this collection. "
|
||||
"Individual cases can override via CaseDetail.question_schema."
|
||||
),
|
||||
)
|
||||
auto_apply_question_template = models.BooleanField(
|
||||
default=False,
|
||||
help_text=(
|
||||
"If enabled, the case_question_template is automatically applied to new cases "
|
||||
"added to this collection that do not have an existing question_schema."
|
||||
),
|
||||
)
|
||||
|
||||
def get_app_name(self):
|
||||
return "atlas"
|
||||
|
||||
@@ -1546,6 +1587,38 @@ class CaseCollection(ExamOrCollectionGenericBase):
|
||||
else:
|
||||
return cases[new_index]
|
||||
|
||||
def apply_question_template_to_cases(self, overwrite_existing=False):
|
||||
"""
|
||||
Apply case_question_template to all CaseDetail entries in this collection.
|
||||
|
||||
Args:
|
||||
overwrite_existing: If True, overwrite existing question_schema on CaseDetails.
|
||||
If False (default), only apply to cases without a schema.
|
||||
|
||||
Returns:
|
||||
int: Number of CaseDetail entries updated.
|
||||
"""
|
||||
if not self.case_question_template:
|
||||
return 0
|
||||
|
||||
casedetails = self.casedetail_set.all()
|
||||
updated = 0
|
||||
for cd in casedetails:
|
||||
if overwrite_existing or not cd.question_schema:
|
||||
cd.question_schema = self.case_question_template
|
||||
cd.save(update_fields=["question_schema"])
|
||||
updated += 1
|
||||
return updated
|
||||
|
||||
def get_effective_question_schema(self, casedetail):
|
||||
"""
|
||||
Return the effective question schema for a CaseDetail.
|
||||
Uses the individual CaseDetail schema if set, otherwise falls back to the collection template.
|
||||
"""
|
||||
if casedetail.question_schema:
|
||||
return casedetail.question_schema
|
||||
return self.case_question_template
|
||||
|
||||
|
||||
def get_index_of_case(self, case, case_count=False):
|
||||
cases = list(self.get_cases())
|
||||
|
||||
Reference in New Issue
Block a user