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:
@@ -0,0 +1,32 @@
|
||||
{% extends "atlas/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ study.name }}</h2>
|
||||
<p class="text-muted">Participant ID: {{ participant.pseudo_id }}</p>
|
||||
|
||||
{% if assigned_packet and start_url %}
|
||||
<div class="card card-body bg-dark border-secondary mb-3">
|
||||
<h5>Assignment Complete</h5>
|
||||
<p class="mb-1">You have been assigned to packet: <strong>{{ participant.assigned_arm.name }}</strong>.</p>
|
||||
<p class="text-muted small mb-3">Mark scheme: {{ participant.assigned_arm.marking_scheme_name|default:'Default collection scoring' }}</p>
|
||||
<a class="btn btn-primary" href="{{ start_url }}">Start Packet</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card card-body bg-dark border-secondary mb-3">
|
||||
<h5>Consent and Details</h5>
|
||||
<p class="text-muted">Complete this form to begin. Assignment to packet is performed automatically when you submit.</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button class="btn btn-primary" type="submit">Submit and Start</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if study.description %}
|
||||
<div class="card card-body bg-dark border-secondary">
|
||||
{{ study.description|linebreaks }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
{% extends "atlas/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Update Packet: {{ arm.name }}</h2>
|
||||
<p class="text-muted">Study: {{ study.name }}</p>
|
||||
|
||||
<form method="post" class="card card-body bg-dark border-secondary">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<a class="btn btn-outline-secondary" href="{% url 'atlas:research_study_detail' study.pk %}">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,96 @@
|
||||
{% extends "atlas/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ study.name }}</h2>
|
||||
<p class="text-muted mb-3">Slug: <code>{{ study.slug }}</code></p>
|
||||
|
||||
<div class="mb-3 d-flex gap-2 flex-wrap">
|
||||
<a class="btn btn-outline-primary" href="{% url 'atlas:research_study_update' study.pk %}">Edit Study</a>
|
||||
<a class="btn btn-outline-secondary" href="{% url 'atlas:research_study_export_csv' study.pk %}">Export CSV</a>
|
||||
<a class="btn btn-outline-secondary" href="{% url 'atlas:research_study_export_json' study.pk %}">Export JSON</a>
|
||||
</div>
|
||||
|
||||
<div class="card card-body bg-dark border-secondary mb-4">
|
||||
<h5 class="mb-2">Participant URL</h5>
|
||||
<p class="small text-muted mb-2">Append the pseudo-anonymised ID to this base URL:</p>
|
||||
<code>{{ request.scheme }}://{{ request.get_host }}{% url 'atlas:research_participant_entry' study.slug 'PSEUDO_ID' %}</code>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card card-body bg-dark border-secondary h-100">
|
||||
<h5>Packets</h5>
|
||||
{% if arm_rows %}
|
||||
<table class="table table-dark table-striped table-sm align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Collection</th>
|
||||
<th>Assigned</th>
|
||||
<th>Mark Scheme</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for arm, assigned_count in arm_rows %}
|
||||
<tr>
|
||||
<td>{{ arm.name }}</td>
|
||||
<td>{{ arm.packet.name }}</td>
|
||||
<td>{{ assigned_count }}</td>
|
||||
<td>{{ arm.marking_scheme_name|default:'Default' }}</td>
|
||||
<td><a class="btn btn-sm btn-outline-primary" href="{% url 'atlas:research_study_arm_update' study.pk arm.pk %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">No packets configured. Add at least one packet before sharing participant links.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card card-body bg-dark border-secondary h-100">
|
||||
<h5>Add Packet</h5>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ arm_form|crispy }}
|
||||
<button class="btn btn-primary" type="submit">Add Packet</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-body bg-dark border-secondary mt-4">
|
||||
<h5>Participants ({{ participants|length }})</h5>
|
||||
{% if participants %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pseudo ID</th>
|
||||
<th>Group</th>
|
||||
<th>Assigned Packet</th>
|
||||
<th>CID</th>
|
||||
<th>Assigned At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for participant in participants %}
|
||||
<tr>
|
||||
<td>{{ participant.pseudo_id }}</td>
|
||||
<td>{{ participant.candidate_group|default:'-' }}</td>
|
||||
<td>{% if participant.assigned_arm %}{{ participant.assigned_arm.name }}{% else %}-{% endif %}</td>
|
||||
<td>{% if participant.cid_user %}{{ participant.cid_user.cid }}{% else %}-{% endif %}</td>
|
||||
<td>{{ participant.assigned_at|default:'-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info mb-0">No participants yet.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
{% extends "atlas/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% if is_create %}Create Research Study{% else %}Update Research Study{% endif %}</h2>
|
||||
<p class="text-muted">Configure randomisation and participant intake options. Packets are added on the study detail page.</p>
|
||||
|
||||
<form method="post" class="card card-body bg-dark border-secondary">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<a class="btn btn-outline-secondary" href="{% if study %}{% url 'atlas:research_study_detail' study.pk %}{% else %}{% url 'atlas:research_study_list' %}{% endif %}">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,43 @@
|
||||
{% extends "atlas/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Research Studies</h2>
|
||||
<p class="text-muted">Create and manage study packets that randomise participants to Atlas collections.</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<a class="btn btn-primary" href="{% url 'atlas:research_study_create' %}">Create Study</a>
|
||||
</div>
|
||||
|
||||
{% if studies %}
|
||||
<table class="table table-dark table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Slug</th>
|
||||
<th>Status</th>
|
||||
<th>Randomisation</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for study in studies %}
|
||||
<tr>
|
||||
<td>{{ study.name }}</td>
|
||||
<td>{{ study.slug }}</td>
|
||||
<td>
|
||||
{% if study.active %}
|
||||
<span class="badge bg-success">Active</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ study.get_randomisation_mode_display }}</td>
|
||||
<td><a class="btn btn-sm btn-outline-primary" href="{% url 'atlas:research_study_detail' study.pk %}">Open</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="alert alert-info">No studies have been created yet.</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user