diff --git a/generic/templatetags/help_tags.py b/generic/templatetags/help_tags.py index b20fafa4..bbdb2d10 100644 --- a/generic/templatetags/help_tags.py +++ b/generic/templatetags/help_tags.py @@ -3,6 +3,27 @@ from django.template import Node, TemplateSyntaxError, Variable register = template.Library() + +@register.simple_tag(takes_context=True) +def update_query_param(context, key, value): + """Return a querystring with a dynamic key updated. + + Django's built-in querystring tag does not allow dynamic keyword names, + so this helper keeps compatibility for django-tables2 prefixed fields. + """ + request = context.get("request") + if request is None: + return "" + + params = request.GET.copy() + if value in (None, ""): + params.pop(key, None) + else: + params[key] = value + + encoded = params.urlencode() + return f"?{encoded}" if encoded else "" + class HelpNode(Node): def __init__(self, nodelist, title, icon, extra_class): self.nodelist = nodelist diff --git a/research/admin.py b/research/admin.py index 535d9de9..dbe88ff8 100644 --- a/research/admin.py +++ b/research/admin.py @@ -5,13 +5,13 @@ 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_display = ['id', 'name', 'active', 'generation_mode', 'randomisation_mode', 'created_at'] list_filter = ['active', 'generation_mode', 'randomisation_mode', 'created_at'] - search_fields = ['name', 'slug', 'description'] + search_fields = ['name', 'description'] readonly_fields = ['created_at', 'updated_at'] fieldsets = ( ('Study Information', { - 'fields': ('name', 'slug', 'description', 'active') + 'fields': ('name', 'description', 'active') }), ('Participant Creation', { 'fields': ('generation_mode', 'collect_demographics'), @@ -64,7 +64,7 @@ class ResearchStudyArmAdmin(admin.ModelAdmin): 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'] + search_fields = ['pseudo_id', 'study__name', 'cid_user__cid', 'email'] readonly_fields = ['created_at', 'updated_at', 'cid_user_info'] fieldsets = ( ('Study & ID', { diff --git a/research/forms.py b/research/forms.py index cd352af8..5b60fb81 100644 --- a/research/forms.py +++ b/research/forms.py @@ -26,7 +26,6 @@ class ResearchStudyForm(ModelForm): model = ResearchStudy fields = [ "name", - "slug", "description", "active", "randomisation_mode", @@ -48,7 +47,6 @@ class ResearchStudyForm(ModelForm): Fieldset( 'Study Details', 'name', - 'slug', 'description', 'active', ), @@ -85,7 +83,7 @@ class ResearchStudyArmForm(ModelForm): "required_previous_arm", ] - def __init__(self, study=None, *args, **kwargs): + def __init__(self, *args, study=None, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' diff --git a/research/migrations/0002_remove_researchstudy_slug.py b/research/migrations/0002_remove_researchstudy_slug.py new file mode 100644 index 00000000..d42a6cce --- /dev/null +++ b/research/migrations/0002_remove_researchstudy_slug.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.4 on 2026-05-12 20:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('research', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='researchstudy', + name='slug', + ), + ] diff --git a/research/models.py b/research/models.py index 70691f72..25977bc6 100644 --- a/research/models.py +++ b/research/models.py @@ -1,13 +1,10 @@ from django.db import models from django.conf import settings from django.urls import reverse -from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from django.core.exceptions import ValidationError from generic.mixins import AuthorMixin from generic.models import CidUser from atlas.models import CaseCollection -from loguru import logger class ResearchStudy(models.Model, AuthorMixin): @@ -24,7 +21,6 @@ class ResearchStudy(models.Model, AuthorMixin): BULK_ONLY = "BULK", _("Bulk generation only") name = models.CharField(max_length=255) - slug = models.SlugField(max_length=100, unique=True) description = models.TextField(blank=True) active = models.BooleanField(default=True) @@ -222,7 +218,7 @@ class ResearchParticipant(models.Model): verbose_name_plural = "Research Participants" def __str__(self): - return f"{self.study.slug}:{self.pseudo_id}" + return f"{self.study.pk}:{self.pseudo_id}" def is_prerequisite_satisfied(self): """Check if participant has completed prerequisite arm(s) for current assignment.""" diff --git a/research/templates/research/study_detail.html b/research/templates/research/study_detail.html index c1f61ac5..c7ef871a 100644 --- a/research/templates/research/study_detail.html +++ b/research/templates/research/study_detail.html @@ -6,7 +6,7 @@
Slug: {{ study.slug }} •
+
Study ID: {{ study.pk }} •
{{ study.get_generation_mode_display }} •
{{ study.get_randomisation_mode_display }}
Append the pseudo-anonymised ID to this base URL to share with participants:
-{{ request.scheme }}://{{ request.get_host }}{% url 'research:participant_entry' study.slug 'PSEUDO_ID' %}
+ {{ request.scheme }}://{{ request.get_host }}{% url 'research:participant_entry' study.pk 'PSEUDO_ID' %}
{{ study.slug }}{{ study.pk }}