diff --git a/castellum/recruitment/management/commands/create_demo_content.py b/castellum/recruitment/management/commands/create_demo_content.py
index 18aade902383f8576c29803071b6374b733c8408..67e5223321c5efe11f45318bdc122dc40a2bdc18 100644
--- a/castellum/recruitment/management/commands/create_demo_content.py
+++ b/castellum/recruitment/management/commands/create_demo_content.py
@@ -35,7 +35,6 @@ from castellum.recruitment.models import AttributeChoice
from castellum.recruitment.models import AttributeSet
from castellum.studies.models import Study
from castellum.studies.models import StudySession
-from castellum.studies.models import StudyType
from castellum.subjects.models import Consent
from castellum.subjects.models import ConsentDocument
from castellum.subjects.models import Subject
@@ -195,10 +194,6 @@ def generate_studies(count):
study.save()
StudySession.objects.bulk_create(fake_studysession(study) for study in studies)
- study_types = list(StudyType.objects.all())
- for study in studies:
- study.study_type.add(fake.random.choice(study_types))
-
def generate_subjects(count):
# NOTE: We have ForeignKeys to Address and Subject. Unfortunately, bulk_create does not set
diff --git a/castellum/recruitment/templates/recruitment/contact.html b/castellum/recruitment/templates/recruitment/contact.html
index 9bb41aa35c9d96071b48187ab0610ad7da668057..49df6240495bd7856426033ebb6fab9686f66dc6 100644
--- a/castellum/recruitment/templates/recruitment/contact.html
+++ b/castellum/recruitment/templates/recruitment/contact.html
@@ -102,7 +102,7 @@
{{ study|verbose_name:'recruitment_text' }}
{{ study|display:'recruitment_text' }}
- {{ study|verbose_name:'study_type' }}
+ {% trans 'Type of study' %}
{{ study|display:'study_type' }}
{{ study|verbose_name:'expense_allowance' }}
diff --git a/castellum/studies/forms.py b/castellum/studies/forms.py
index fac504437bfd2f7afe8678df53de855fb5f856e8..85046cfb778fcf0b38346f3cc7aea7935928ebc2 100644
--- a/castellum/studies/forms.py
+++ b/castellum/studies/forms.py
@@ -45,14 +45,19 @@ class StudyForm(ReadonlyModelForm):
'affiliated_research_assistants',
'description',
'keywords',
- 'study_type',
'data_sensitivity',
'consent',
'min_subject_count',
'expense_allowance',
]
+
+
+class SessionForm(ReadonlyModelForm):
+ class Meta:
+ model = models.StudySession
+ fields = ('name', 'duration', 'resource', 'type')
widgets = {
- 'study_type': forms.CheckboxSelectMultiple(),
+ 'type': forms.CheckboxSelectMultiple(),
}
diff --git a/castellum/studies/migrations/0009_studysession_type.py b/castellum/studies/migrations/0009_studysession_type.py
new file mode 100644
index 0000000000000000000000000000000000000000..90890ba08118e151b27f56b73adeefafe9d3c15b
--- /dev/null
+++ b/castellum/studies/migrations/0009_studysession_type.py
@@ -0,0 +1,35 @@
+from django.db import migrations, models
+
+
+def migrate_data(apps, schema_editor):
+ Study = apps.get_model('studies', 'Study')
+ StudySession = apps.get_model('studies', 'StudySession')
+
+ for study in Study.objects.all():
+ if not study.study_type.exists():
+ continue
+ try:
+ session = study.studysession_set.first()
+ except StudySession.DoesNotExist:
+ session = study.studysession_set.create(name='session', duration=0)
+ session.type.set(study.study_type.all())
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('studies', '0008_study_type_rm_3rd_party')
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='studysession',
+ name='type',
+ field=models.ManyToManyField(blank=True, to='studies.StudyType', verbose_name='Type'),
+ ),
+ migrations.RunPython(migrate_data),
+ migrations.RemoveField(
+ model_name='study',
+ name='study_type',
+ ),
+ ]
diff --git a/castellum/studies/models.py b/castellum/studies/models.py
index af26f318492013618a293e58561c9e173372d674..398b0c1b399bce90ab1668a35c5370d0f6bd04fa 100644
--- a/castellum/studies/models.py
+++ b/castellum/studies/models.py
@@ -78,7 +78,6 @@ class Study(models.Model):
)
description = models.TextField(_('Description'), blank=True)
keywords = models.CharField(_('Keywords'), max_length=254, blank=True)
- study_type = models.ManyToManyField(StudyType, verbose_name=_('Type of study'), blank=True)
previous_status = models.SmallIntegerField(
_('Previous status'), choices=STATUS, default=EDIT
)
@@ -203,6 +202,10 @@ class Study(models.Model):
result = self.subjectfiltergroup_set.aggregate(models.Max(key))
return result[key + '__max'] or 0
+ @property
+ def study_type(self):
+ return StudyType.objects.filter(studysession__study=self).distinct()
+
@property
def followup_urgent(self):
today = datetime.date.today()
@@ -266,6 +269,7 @@ class StudySession(models.Model):
study = models.ForeignKey(Study, on_delete=models.CASCADE)
name = models.CharField(_('Name'), max_length=128)
duration = models.PositiveIntegerField(_('Duration of a session in minutes'))
+ type = models.ManyToManyField(StudyType, verbose_name=_('Type'), blank=True)
resource = models.ForeignKey(
Resource, on_delete=models.SET_NULL, verbose_name=_('Resource'), blank=True, null=True
)
diff --git a/castellum/studies/templates/studies/study_detail.html b/castellum/studies/templates/studies/study_detail.html
index d45b1ff8d4b8f0fbe5014b5e1c687c8f693a843a..dabfc1617d4f58110be98809accc88a166ca3f1c 100644
--- a/castellum/studies/templates/studies/study_detail.html
+++ b/castellum/studies/templates/studies/study_detail.html
@@ -11,7 +11,7 @@
{{ object|verbose_name:'affiliated_scientists' }}
{{ object|display:'affiliated_scientists' }}
- {{ object|verbose_name:'study_type' }}
+ {% trans 'Type of study' %}
{{ object|display:'study_type' }}
{% trans 'Scheduled runtime' %}
diff --git a/castellum/studies/templates/studies/study_form.html b/castellum/studies/templates/studies/study_form.html
index d9173f0c9908a30702d34a3628887e405f76a444..9549ccb53e74e14a8396e1dce8af5b15d797a848 100644
--- a/castellum/studies/templates/studies/study_form.html
+++ b/castellum/studies/templates/studies/study_form.html
@@ -25,7 +25,6 @@
{% bootstrap_field form.affiliated_research_assistants %}
{% bootstrap_field form.description %}
{% bootstrap_field form.keywords %}
- {% bootstrap_field form.study_type %}
{% bootstrap_field form.data_sensitivity %}
{% include "file_field.html" with field=form.consent %}
{% bootstrap_field form.min_subject_count %}
diff --git a/castellum/studies/templates/studies/study_sessions.html b/castellum/studies/templates/studies/study_sessions.html
index eca14313e3d5bdfb38e05b190d613cb4963905ca..464f02377134c2568f4672c981754105a3ba438e 100644
--- a/castellum/studies/templates/studies/study_sessions.html
+++ b/castellum/studies/templates/studies/study_sessions.html
@@ -16,6 +16,7 @@
{% else %}
{% endif %}
+ {% bootstrap_field template_form.type %}
@@ -44,6 +45,7 @@
{% else %}
{% endif %}
+ {% bootstrap_field subform.type %}
{% endfor %}
diff --git a/castellum/studies/views/recruitment.py b/castellum/studies/views/recruitment.py
index 6bdb84ab51c121e6badb635d811c40f00a012a1a..035c9e6b3a47059995fa16ee21474de5b7789ace 100644
--- a/castellum/studies/views/recruitment.py
+++ b/castellum/studies/views/recruitment.py
@@ -34,10 +34,10 @@ from castellum.recruitment import filter_queries
from castellum.recruitment.models import ParticipationRequest
from castellum.recruitment.models import SubjectFilter
from castellum.subjects.models import Subject
-from castellum.utils.forms import ReadonlyModelForm
from castellum.utils.views import ReadonlyMixin
from ..forms import ExcludedStudiesForm
+from ..forms import SessionForm
from ..mixins import StudyMixin
from ..models import Resource
from ..models import Study
@@ -151,16 +151,11 @@ class StudySessionsView(
subtab = 'sessions'
def get_session_form(self):
- form_class = forms.modelform_factory(StudySession, fields=('name', 'duration', 'resource'))
- return form_class(prefix='{prefix}')
+ return SessionForm(prefix='{prefix}')
def get_session_formset(self):
formset_class = forms.modelformset_factory(
- StudySession,
- fields=('name', 'duration', 'resource'),
- form=ReadonlyModelForm,
- extra=0,
- can_delete=True,
+ StudySession, form=SessionForm, extra=0, can_delete=True
)
kwargs = self.get_form_kwargs()
study = kwargs.pop('instance', None)
diff --git a/castellum/studies/views/studies.py b/castellum/studies/views/studies.py
index e52399ed547d5f5913c24708860cafb5a967e65e..3c7286342de9f1bc4bf745795f1ed9d4a55c50df 100644
--- a/castellum/studies/views/studies.py
+++ b/castellum/studies/views/studies.py
@@ -74,7 +74,7 @@ class StudyIndexView(LoginRequiredMixin, ListView):
models.Q(contact_person__icontains=part) |
models.Q(description__icontains=part) |
models.Q(keywords__icontains=part) |
- models.Q(study_type__translations__label__iexact=part)
+ models.Q(studysession__study_type__translations__label__iexact=part)
)
if 'all' not in self.request.GET:
qs = qs.filter(studymembership__user=self.request.user)
@@ -134,11 +134,12 @@ class StudyCreateView(PermissionRequiredMixin, CreateView):
self.object.save()
for session in self.duplicate.studysession_set.all():
- self.object.studysession_set.create(
+ s = self.object.studysession_set.create(
name=session.name,
duration=session.duration,
resource_id=session.resource_id,
)
+ s.type.set(session.type.all())
return response
diff --git a/tests/recruitment/models/test_subjectfilters.py b/tests/recruitment/models/test_subjectfilters.py
index a99ae21d8cb08c39ddf41027de8af1a32c3d62b3..8f4bce00cd4e0c314131f178bfc7953f06e4c4a0 100644
--- a/tests/recruitment/models/test_subjectfilters.py
+++ b/tests/recruitment/models/test_subjectfilters.py
@@ -7,6 +7,7 @@ from castellum.recruitment.models import ParticipationRequest
from castellum.recruitment.models import SubjectFilter
from castellum.recruitment.models import SubjectFilterGroup
from castellum.studies.models import Study
+from castellum.studies.models import StudySession
from castellum.studies.models import StudyType
from castellum.subjects.models import Subject
@@ -102,7 +103,8 @@ def test_to_q_first_study_exclusive_second_study_unfinished(attributeset):
def test_disinterest_in_study_type(db):
study_type = StudyType.objects.first()
study = baker.make(Study)
- study.study_type.add(study_type)
+ session = baker.make(StudySession, study=study)
+ session.type.add(study_type)
baker.make(AttributeSet, study_type_disinterest=[study_type])
assert not Subject.objects.filter(filter_queries.study(study)).exists()