diff --git a/castellum_core/castellum_core/recruitment/admin.py b/castellum_core/castellum_core/recruitment/admin.py index 87e5ca31072c83e0c4a5d149f9fe9829b8a0a417..627d221ae3d5734b03602f4b3aa35a5ecee780da 100644 --- a/castellum_core/castellum_core/recruitment/admin.py +++ b/castellum_core/castellum_core/recruitment/admin.py @@ -22,5 +22,7 @@ from django.contrib import admin from .models import AttributeSet +from .models import Participation admin.site.register(AttributeSet) +admin.site.register(Participation) diff --git a/castellum_core/castellum_core/recruitment/migrations/0002_participation.py b/castellum_core/castellum_core/recruitment/migrations/0002_participation.py new file mode 100644 index 0000000000000000000000000000000000000000..61c6dc3379d83b81cd605c4a88c335997bfc1857 --- /dev/null +++ b/castellum_core/castellum_core/recruitment/migrations/0002_participation.py @@ -0,0 +1,25 @@ +# Generated by Django 2.0.4 on 2018-06-25 14:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('studies', '0001_initial'), + ('recruitment', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Participation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject_pk', models.CharField(max_length=128)), + ('status', models.IntegerField(choices=[(0, 'missed call'), (1, 'unsuitable'), (2, 'invited')], default=1, verbose_name='status of participation')), + ('updated_at', models.DateTimeField(blank=True, null=True, verbose_name='last updated at')), + ('study', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='studies.Study', verbose_name='study')), + ], + ), + ] diff --git a/castellum_core/castellum_core/recruitment/models.py b/castellum_core/castellum_core/recruitment/models.py index f0f96706912d9f14e519785231526807f405ca06..bc6e3fc87ee0ebfb96cf157202fe6fd287b12456 100644 --- a/castellum_core/castellum_core/recruitment/models.py +++ b/castellum_core/castellum_core/recruitment/models.py @@ -22,6 +22,8 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from studies.models import Study + class AttributeSet(models.Model): handedness = models.CharField(_('Handedness'), blank=True, max_length=10, choices=( @@ -30,3 +32,23 @@ class AttributeSet(models.Model): )) first_language = models.CharField(_('First Language'), blank=True, max_length=5) date_of_birth = models.DateField(_('Date of birth'), null=True, blank=True) + + +class Participation(models.Model): + MISSED_CALL = 0 + UNSUITABLE = 1 + INVITED = 2 + STATUS_OPTIONS = ( + (MISSED_CALL, _('missed call')), + (UNSUITABLE, _('unsuitable')), + (INVITED, _('invited')), + ) + + study = models.ForeignKey(Study, verbose_name=_('study'), on_delete=models.CASCADE) + + # just a placeholder until proper pseudonyms are available + subject_pk = models.CharField(max_length=128) + + status = models.IntegerField( + _('status of participation'), choices=STATUS_OPTIONS, default=UNSUITABLE) + updated_at = models.DateTimeField(_('last updated at'), blank=True, null=True) diff --git a/castellum_core/castellum_core/recruitment/templates/recruitment/contact.html b/castellum_core/castellum_core/recruitment/templates/recruitment/contact.html new file mode 100644 index 0000000000000000000000000000000000000000..21e2f5042bf46714414fe4cbc3cbf3abba673bed --- /dev/null +++ b/castellum_core/castellum_core/recruitment/templates/recruitment/contact.html @@ -0,0 +1,51 @@ +{% extends "base.html" %} +{% load i18n bootstrap4 %} + +{% block content %} + + + +

{{ subject.first_name|default:_('not provided') }} {{ subject.last_name|default:_('not provided') }}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans 'Gender:' %}{{ subject.gender|default:_('not provided') }}
{% trans 'Birthday:' %}{{ subject.birthday|default:_('not provided') }}
{% trans 'Address:' %}{{ subject.address|default:_('not provided') }}
{% trans 'Email:' %}{{ subject.email|default:_('not provided') }}
{% trans 'Phone number:' %}{{ subject.phone_number|default:_('not provided') }}
{% trans 'Second phone number:' %}{{ subject.phone_number_alternative|default:_('not provided') }}
+ +
+ {% for error in form.non_field_errors %} + + {% endfor %} + + {% csrf_token %} + {% bootstrap_field form.status %} + + {% trans "Back" %} + +
+ +{% endblock %} diff --git a/castellum_core/castellum_core/recruitment/templates/recruitment/subject_detail_view.html b/castellum_core/castellum_core/recruitment/templates/recruitment/subject_detail_view.html deleted file mode 100644 index 687f0481225c5fad5cc3741356ade5639f6cacfe..0000000000000000000000000000000000000000 --- a/castellum_core/castellum_core/recruitment/templates/recruitment/subject_detail_view.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} - -{% block content %} - - - -

{{ object.first_name|default:_('not provided') }} {{ object.last_name|default:_('not provided') }}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{% trans 'Gender:' %}{{ object.gender|default:_('not provided') }}
{% trans 'Birthday:' %}{{ object.birthday|default:_('not provided') }}
{% trans 'Address:' %}{{ object.address|default:_('not provided') }}
{% trans 'Email:' %}{{ object.email|default:_('not provided') }}
{% trans 'Phone number:' %}{{ object.phone_number|default:_('not provided') }}
{% trans 'Second phone number:' %}{{ object.phone_number_alternative|default:_('not provided') }}
- {% trans "Back" %} -{% endblock %} \ No newline at end of file diff --git a/castellum_core/castellum_core/recruitment/templates/recruitment/subjects_list.html b/castellum_core/castellum_core/recruitment/templates/recruitment/subjects_list.html index b5e3c631670c8e0f8b97c15319b18d97e215bdcd..29e8cabb100d786c6740a5f9b6c9aa580de5dee0 100644 --- a/castellum_core/castellum_core/recruitment/templates/recruitment/subjects_list.html +++ b/castellum_core/castellum_core/recruitment/templates/recruitment/subjects_list.html @@ -34,7 +34,7 @@ {{ subject.first_name|default:_('not provided') }} {{ subject.last_name|default:_('not provided') }} - {% trans 'Call' %} + {% trans 'Call' %} {% endfor %} diff --git a/castellum_core/castellum_core/recruitment/urls.py b/castellum_core/castellum_core/recruitment/urls.py index 293678129207d45bd6dfdf7a3ba947098162efc8..a310cd47bf48c92a466d5f449fc484e8f801ec4c 100644 --- a/castellum_core/castellum_core/recruitment/urls.py +++ b/castellum_core/castellum_core/recruitment/urls.py @@ -27,5 +27,5 @@ app_name = 'recruitment' urlpatterns = [ path('', views.StudyChoiceView.as_view(), name='select_study'), path('/', views.SubjectChoiceView.as_view(), name='select_subject'), - path('//', views.SubjectDetailsView.as_view(), name='detail_subject'), + path('//', views.ContactView.as_view(), name='contact'), ] diff --git a/castellum_core/castellum_core/recruitment/views.py b/castellum_core/castellum_core/recruitment/views.py index a8bb69621d99cf06ba60b6236b3226b3daaf1025..a54e7fd6e3818b2cb80957da378c1f3e45238a65 100644 --- a/castellum_core/castellum_core/recruitment/views.py +++ b/castellum_core/castellum_core/recruitment/views.py @@ -21,12 +21,16 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import get_object_or_404 -from django.views.generic import DetailView +from django.urls import reverse +from django.utils import timezone from django.views.generic import ListView +from django.views.generic import UpdateView from studies.models import Study from subject_management.models import Subject +from .models import Participation + class StudyChoiceView(LoginRequiredMixin, ListView): model = Study @@ -51,14 +55,31 @@ class SubjectChoiceView(LoginRequiredMixin, ListView): return context -class SubjectDetailsView(LoginRequiredMixin, DetailView): - model = Subject - slug_field = 'id' - slug_url_kwarg = 'subject_pk' - template_name = 'recruitment/subject_detail_view.html' +class ContactView(LoginRequiredMixin, UpdateView): + model = Participation + fields = ['status'] + template_name = 'recruitment/contact.html' + + def get_object(self, queryset=None): + if queryset is None: + queryset = self.get_queryset() - def get_context_data(self, **kwargs): - context = super(DetailView, self).get_context_data(**kwargs) study = get_object_or_404(Study, pk=self.kwargs['study_pk']) - context['study'] = study + subject_pk = self.kwargs['subject_pk'] + + participation, __ = queryset.get_or_create(study=study, subject_pk=subject_pk) + return participation + + def get_success_url(self): + return reverse('recruitment:select_subject', args=[self.object.study.pk]) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['study'] = self.object.study + context['subject'] = get_object_or_404(Subject, pk=self.object.subject_pk) return context + + def form_valid(self, form): + instance = form.save(commit=False) + instance.updated_at = timezone.now() + return super(ContactView, self).form_valid(form) diff --git a/castellum_core/tests/recruitment/views/test_contact_view.py b/castellum_core/tests/recruitment/views/test_contact_view.py new file mode 100644 index 0000000000000000000000000000000000000000..c55cb214e7d058a7af1cdd7545c1bb988edf97aa --- /dev/null +++ b/castellum_core/tests/recruitment/views/test_contact_view.py @@ -0,0 +1,34 @@ +from freezegun import freeze_time + +from recruitment.models import Participation + + +def test_404_page_returned_if_study_is_not_provided(client, user, subject): + client.force_login(user) + url = '/recruitment/3/{}/'.format(subject.pk) + response = client.get(url) + assert response.status_code == 404 + + +def test_200_page_returned_if_study_is_provided(client, user, study, subject): + client.force_login(user) + url = '/recruitment/{}/{}/'.format(study.pk, subject.pk) + response = client.get(url) + assert response.status_code == 200 + + +def test_post_redirect(client, user, study, subject): + client.force_login(user) + url = '/recruitment/{}/{}/'.format(study.pk, subject.pk) + response = client.post(url, {'status': 2}) + assert response.status_code == 302 + assert response.url == '/recruitment/{}/'.format(study.pk) + + +@freeze_time('2018-01-01') +def test_post_updated_at(client, user, study, subject): + client.force_login(user) + url = '/recruitment/{}/{}/'.format(study.pk, subject.pk) + client.post(url, {'status': 2}) + participation = Participation.objects.get() + assert participation.updated_at.strftime('%Y-%m-%d') == '2018-01-01' diff --git a/castellum_core/tests/recruitment/views/test_subject_details_view.py b/castellum_core/tests/recruitment/views/test_subject_details_view.py deleted file mode 100644 index 519456bc06d705cdc21e26b46635c3e283c0e4e4..0000000000000000000000000000000000000000 --- a/castellum_core/tests/recruitment/views/test_subject_details_view.py +++ /dev/null @@ -1,14 +0,0 @@ - - -def test_404_page_returned_if_study_is_not_provided(client, user, subject): - client.force_login(user) - url = '/recruitment/3/{}/'.format(subject.pk) - response = client.get(url) - assert response.status_code == 404 - - -def test_200_page_returned_if_study_is_provided(client, user, study, subject): - client.force_login(user) - url = '/recruitment/{}/{}/'.format(study.pk, subject.pk) - response = client.get(url) - assert response.status_code == 200