c94d2fb1a7
- 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.
117 lines
4.4 KiB
Python
117 lines
4.4 KiB
Python
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
from research.models import ResearchStudy, ResearchStudyArm, ResearchParticipant
|
|
|
|
|
|
@admin.register(ResearchStudy)
|
|
class ResearchStudyAdmin(admin.ModelAdmin):
|
|
list_display = ['name', 'slug', 'active', 'generation_mode', 'randomisation_mode', 'created_at']
|
|
list_filter = ['active', 'generation_mode', 'randomisation_mode', 'created_at']
|
|
search_fields = ['name', 'slug', 'description']
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
fieldsets = (
|
|
('Study Information', {
|
|
'fields': ('name', 'slug', 'description', 'active')
|
|
}),
|
|
('Participant Creation', {
|
|
'fields': ('generation_mode', 'collect_demographics'),
|
|
'description': 'Configure how participants are created and what data is collected.'
|
|
}),
|
|
('Randomisation Strategy', {
|
|
'fields': ('randomisation_mode', 'balance_within_candidate_group', 'allocation_targets'),
|
|
'description': 'Configure how participants are assigned to arms.'
|
|
}),
|
|
('Authors', {
|
|
'fields': ('author',),
|
|
}),
|
|
('Timestamps', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
|
|
@admin.register(ResearchStudyArm)
|
|
class ResearchStudyArmAdmin(admin.ModelAdmin):
|
|
list_display = ['name', 'study', 'packet', 'active', 'sort_order', 'order_sequence']
|
|
list_filter = ['study', 'active', 'order_sequence']
|
|
search_fields = ['name', 'study__name', 'packet__name']
|
|
fieldsets = (
|
|
('Arm Information', {
|
|
'fields': ('study', 'name', 'packet', 'active', 'sort_order')
|
|
}),
|
|
('Randomisation', {
|
|
'fields': ('allocation_weight',),
|
|
'description': 'Used for completely-random allocation.'
|
|
}),
|
|
('Ordering & Prerequisites', {
|
|
'fields': ('order_sequence', 'required_previous_arm'),
|
|
'description': 'Configure sequential completion requirements.'
|
|
}),
|
|
)
|
|
|
|
def get_form(self, request, obj=None, **kwargs):
|
|
"""Pre-filter related fields based on study."""
|
|
form = super().get_form(request, obj, **kwargs)
|
|
if obj and hasattr(obj, 'study'):
|
|
form.base_fields['required_previous_arm'].queryset = ResearchStudyArm.objects.filter(
|
|
study=obj.study
|
|
).exclude(pk=obj.pk)
|
|
return form
|
|
|
|
|
|
@admin.register(ResearchParticipant)
|
|
class ResearchParticipantAdmin(admin.ModelAdmin):
|
|
list_display = ['pseudo_id', 'study', 'assigned_arm', 'cid_user_display', 'consented', 'assigned_at']
|
|
list_filter = ['study', 'assigned_arm', 'consented', 'created_at']
|
|
search_fields = ['pseudo_id', 'study__slug', 'cid_user__cid', 'email']
|
|
readonly_fields = ['created_at', 'updated_at', 'cid_user_info']
|
|
fieldsets = (
|
|
('Study & ID', {
|
|
'fields': ('study', 'pseudo_id')
|
|
}),
|
|
('CID User', {
|
|
'fields': ('cid_user', 'cid_user_info'),
|
|
'description': 'Associated CID user for study access.'
|
|
}),
|
|
('Demographics', {
|
|
'fields': ('candidate_group', 'age_band', 'sex', 'training_grade', 'years_experience', 'email'),
|
|
}),
|
|
('Assignment', {
|
|
'fields': ('assigned_arm', 'assignment_method', 'assigned_at'),
|
|
}),
|
|
('Consent & Status', {
|
|
'fields': ('consented', 'completed_arms'),
|
|
}),
|
|
('User Link', {
|
|
'fields': ('user_user',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Timestamps', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def cid_user_display(self, obj):
|
|
"""Display CID number with link."""
|
|
if obj.cid_user:
|
|
return format_html(
|
|
'<code>{}</code>',
|
|
obj.cid_user.cid
|
|
)
|
|
return '-'
|
|
cid_user_display.short_description = 'CID'
|
|
|
|
def cid_user_info(self, obj):
|
|
"""Display full CID user info."""
|
|
if obj.cid_user:
|
|
return format_html(
|
|
'<strong>CID:</strong> {}<br><strong>Passcode:</strong> <code>{}</code><br><strong>Name:</strong> {}',
|
|
obj.cid_user.cid,
|
|
obj.cid_user.passcode,
|
|
obj.cid_user.name
|
|
)
|
|
return 'Not yet assigned'
|
|
cid_user_info.short_description = 'CID User Information'
|