diff --git a/.circleci/config.yml b/.circleci/config.yml index 8711a35..62eb76d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/python:2.7.15-jessie-browsers + - image: circleci/python:3.7.7-buster-browsers steps: - checkout - run: mkdir test-reports diff --git a/api/serializers.py b/api/serializers.py index 0fdf142..817ef9c 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -4,7 +4,7 @@ from pttrack import models from workup import models as workupModels from simple_history.models import HistoricalRecords -# from django.core.urlresolvers import reverse +# from django.urls import reverse class UrlReverser(object): diff --git a/api/test.py b/api/test.py index c5f8714..ddfb108 100644 --- a/api/test.py +++ b/api/test.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import datetime -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils.timezone import now from rest_framework.test import APITestCase from rest_framework import status @@ -45,7 +45,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=models.ContactMethod.objects.first(), ) @@ -61,7 +61,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=models.ContactMethod.objects.first(), ) @@ -77,7 +77,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=models.ContactMethod.objects.first(), ) @@ -326,7 +326,7 @@ def test_api_list_patients_with_pending_action_item_and_referral(self): FollowupRequest.objects.filter( patient=response.data[1]['pk']).first().due_date) - # Add one more AI whose due date is even farther away than the + # Add one more AI whose due date is even farther away than the # referral followup request pt5_ai = models.ActionItem.objects.create( due_date=now().date() + datetime.timedelta(days=1), diff --git a/api/urls.py b/api/urls.py index e84df31..2ec594a 100644 --- a/api/urls.py +++ b/api/urls.py @@ -8,11 +8,9 @@ # pylint: disable=I0011 unwrapped_urlpatterns = [ # pylint: disable=invalid-name - url(r'^pt_list/$', - views.PtList.as_view(), - name='pt_list_api'), + url(r'pt_list/', views.PtList.as_view(), name='pt_list_api'), ] wrap_config = {} -urlpatterns = [wrap_url(url, **wrap_config) for url in unwrapped_urlpatterns] +urlpatterns = [wrap_url(u, **wrap_config) for u in unwrapped_urlpatterns] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/appointment/forms.py b/appointment/forms.py index 40eaef5..9b517ce 100644 --- a/appointment/forms.py +++ b/appointment/forms.py @@ -4,7 +4,6 @@ from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit -from bootstrap3_datetime.widgets import DateTimePicker from .models import Appointment @@ -17,8 +16,9 @@ class Meta(object): 'patient'] widgets = { - 'clindate': DateTimePicker(options={"format": "YYYY-MM-DD"}), - 'clintime': TimeInput(format='%H:%M')} + # 'clindate': DateTimePicker(options={"format": "YYYY-MM-DD"}), + 'clintime': TimeInput(format='%H:%M') + } def __init__(self, *args, **kwargs): super(AppointmentForm, self).__init__(*args, **kwargs) diff --git a/appointment/migrations/0001_initial.py b/appointment/migrations/0001_initial.py index bb19ace..64d0e46 100644 --- a/appointment/migrations/0001_initial.py +++ b/appointment/migrations/0001_initial.py @@ -1,12 +1,10 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2018-08-17 03:20 -from __future__ import unicode_literals +# Generated by Django 2.2.12 on 2020-05-05 15:32 -import datetime +import appointment.models from django.conf import settings from django.db import migrations, models import django.db.models.deletion -from django.utils.timezone import utc +import simple_history.models class Migration(migrations.Migration): @@ -14,39 +12,22 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('pttrack', '0005_simplehistory_add_change_reason'), + ('pttrack', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ - migrations.CreateModel( - name='Appointment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('written_datetime', models.DateTimeField(auto_now_add=True)), - ('last_modified', models.DateTimeField(auto_now=True)), - ('clindate', models.DateField(verbose_name=b'Appointment Date')), - ('clintime', models.TimeField(default=datetime.datetime(2018, 8, 17, 9, 0, tzinfo=utc), verbose_name=b'Time of Appointment')), - ('appointment_type', models.CharField(choices=[(b'PSYCH_NIGHT', b'Psych Night'), (b'ACUTE_FOLLOWUP', b'Acute Followup'), (b'CHRONIC_CARE', b'Chronic Care')], default=b'CHRONIC_CARE', max_length=15, verbose_name=b'Appointment Type')), - ('comment', models.TextField(help_text=b'What should happen at this appointment?')), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Provider')), - ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.ProviderType')), - ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Patient')), - ], - options={ - 'abstract': False, - }, - ), migrations.CreateModel( name='HistoricalAppointment', fields=[ ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), ('written_datetime', models.DateTimeField(blank=True, editable=False)), ('last_modified', models.DateTimeField(blank=True, editable=False)), - ('clindate', models.DateField(verbose_name=b'Appointment Date')), - ('clintime', models.TimeField(default=datetime.datetime(2018, 8, 17, 9, 0, tzinfo=utc), verbose_name=b'Time of Appointment')), - ('appointment_type', models.CharField(choices=[(b'PSYCH_NIGHT', b'Psych Night'), (b'ACUTE_FOLLOWUP', b'Acute Followup'), (b'CHRONIC_CARE', b'Chronic Care')], default=b'CHRONIC_CARE', max_length=15, verbose_name=b'Appointment Type')), - ('comment', models.TextField(help_text=b'What should happen at this appointment?')), + ('clindate', models.DateField(verbose_name='Appointment Date')), + ('clintime', models.TimeField(default=appointment.models.generate_default_appointment_time, verbose_name='Time of Appointment')), + ('appointment_type', models.CharField(choices=[('PSYCH_NIGHT', 'Psych Night'), ('ACUTE_FOLLOWUP', 'Acute Followup'), ('CHRONIC_CARE', 'Chronic Care'), ('VACCINE', 'Vaccine Followup')], default='CHRONIC_CARE', max_length=15, verbose_name='Appointment Type')), + ('comment', models.TextField(help_text='What should happen at this appointment?')), + ('pt_showed', models.NullBooleanField(help_text='Did the patient come to this appointment?', verbose_name='Patient Showed')), ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField()), ('history_change_reason', models.CharField(max_length=100, null=True)), @@ -57,9 +38,29 @@ class Migration(migrations.Migration): ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), ], options={ + 'verbose_name': 'historical appointment', 'ordering': ('-history_date', '-history_id'), 'get_latest_by': 'history_date', - 'verbose_name': 'historical appointment', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='Appointment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ('clindate', models.DateField(verbose_name='Appointment Date')), + ('clintime', models.TimeField(default=appointment.models.generate_default_appointment_time, verbose_name='Time of Appointment')), + ('appointment_type', models.CharField(choices=[('PSYCH_NIGHT', 'Psych Night'), ('ACUTE_FOLLOWUP', 'Acute Followup'), ('CHRONIC_CARE', 'Chronic Care'), ('VACCINE', 'Vaccine Followup')], default='CHRONIC_CARE', max_length=15, verbose_name='Appointment Type')), + ('comment', models.TextField(help_text='What should happen at this appointment?')), + ('pt_showed', models.NullBooleanField(help_text='Did the patient come to this appointment?', verbose_name='Patient Showed')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ], + options={ + 'ordering': ['-clindate', '-clintime'], }, ), ] diff --git a/appointment/migrations/0002_vaccineappointment_20181031_1852.py b/appointment/migrations/0002_vaccineappointment_20181031_1852.py deleted file mode 100644 index 0bcabd7..0000000 --- a/appointment/migrations/0002_vaccineappointment_20181031_1852.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2018-10-31 23:52 -from __future__ import unicode_literals - -import datetime -from django.db import migrations, models -from django.utils.timezone import utc - - -class Migration(migrations.Migration): - - dependencies = [ - ('appointment', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='appointment', - name='appointment_type', - field=models.CharField(choices=[(b'PSYCH_NIGHT', b'Psych Night'), (b'ACUTE_FOLLOWUP', b'Acute Followup'), (b'CHRONIC_CARE', b'Chronic Care'), (b'VACCINE', b'Vaccine Followup')], default=b'CHRONIC_CARE', max_length=15, verbose_name=b'Appointment Type'), - ), - migrations.AlterField( - model_name='appointment', - name='clintime', - field=models.TimeField(default=datetime.datetime(2018, 10, 31, 9, 0, tzinfo=utc), verbose_name=b'Time of Appointment'), - ), - migrations.AlterField( - model_name='historicalappointment', - name='appointment_type', - field=models.CharField(choices=[(b'PSYCH_NIGHT', b'Psych Night'), (b'ACUTE_FOLLOWUP', b'Acute Followup'), (b'CHRONIC_CARE', b'Chronic Care'), (b'VACCINE', b'Vaccine Followup')], default=b'CHRONIC_CARE', max_length=15, verbose_name=b'Appointment Type'), - ), - migrations.AlterField( - model_name='historicalappointment', - name='clintime', - field=models.TimeField(default=datetime.datetime(2018, 10, 31, 9, 0, tzinfo=utc), verbose_name=b'Time of Appointment'), - ), - ] diff --git a/appointment/migrations/0003_pt_showed_and_default_time_function_20181103_1414.py b/appointment/migrations/0003_pt_showed_and_default_time_function_20181103_1414.py deleted file mode 100644 index db64fc0..0000000 --- a/appointment/migrations/0003_pt_showed_and_default_time_function_20181103_1414.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2018-11-03 19:14 -from __future__ import unicode_literals - -import appointment.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('appointment', '0002_vaccineappointment_20181031_1852'), - ] - - operations = [ - migrations.AddField( - model_name='appointment', - name='pt_showed', - field=models.NullBooleanField(help_text=b'Did the patient come to this appointment?', verbose_name=b'Patient Showed'), - ), - migrations.AddField( - model_name='historicalappointment', - name='pt_showed', - field=models.NullBooleanField(help_text=b'Did the patient come to this appointment?', verbose_name=b'Patient Showed'), - ), - migrations.AlterField( - model_name='appointment', - name='clintime', - field=models.TimeField(default=appointment.models.generate_default_appointment_time, verbose_name=b'Time of Appointment'), - ), - migrations.AlterField( - model_name='historicalappointment', - name='clintime', - field=models.TimeField(default=appointment.models.generate_default_appointment_time, verbose_name=b'Time of Appointment'), - ), - ] diff --git a/appointment/migrations/0004_update_appointment_ordering_20190902_2116.py b/appointment/migrations/0004_update_appointment_ordering_20190902_2116.py deleted file mode 100644 index dc52f42..0000000 --- a/appointment/migrations/0004_update_appointment_ordering_20190902_2116.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:16 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('appointment', '0003_pt_showed_and_default_time_function_20181103_1414'), - ] - - operations = [ - migrations.AlterModelOptions( - name='appointment', - options={'ordering': ['-clindate', '-clintime']}, - ), - ] diff --git a/appointment/test_views.py b/appointment/test_views.py index 2fc98a0..9f1d0d0 100644 --- a/appointment/test_views.py +++ b/appointment/test_views.py @@ -5,7 +5,7 @@ from django.test import TestCase from django.utils.timezone import now -from django.core.urlresolvers import reverse +from django.urls import reverse from pttrack.models import Provider, ProviderType, Patient from pttrack.test_views import log_in_provider, build_provider from .test_forms import apt_dict diff --git a/appointment/urls.py b/appointment/urls.py index cdb61a6..9add28b 100644 --- a/appointment/urls.py +++ b/appointment/urls.py @@ -1,25 +1,25 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import path from pttrack.urls import wrap_url from . import views unwrapped_urlconf = [ # pylint: disable=invalid-name - url(r'^new$', - views.AppointmentCreate.as_view(), - name='appointment-new'), - url(r'^(?P[0-9]+)/update$', - views.AppointmentUpdate.as_view(), - name='appointment-update'), - url(r'^list$', - views.list_view, - name='appointment-list'), - url(r'^(?P[0-9]+)/noshow$', - views.mark_no_show, - name='appointment-mark-no-show'), - url(r'^(?P[0-9]+)/arrived$', - views.mark_arrived, - name='appointment-mark-arrived'), + path(r'new', + views.AppointmentCreate.as_view(), + name='appointment-new'), + path(r'/update', + views.AppointmentUpdate.as_view(), + name='appointment-update'), + path(r'list', + views.list_view, + name='appointment-list'), + path(r'/noshow', + views.mark_no_show, + name='appointment-mark-no-show'), + path(r'/arrived', + views.mark_arrived, + name='appointment-mark-arrived'), ] wrap_config = {} diff --git a/appointment/views.py b/appointment/views.py index 5c62a4d..b5f483d 100644 --- a/appointment/views.py +++ b/appointment/views.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import collections -from django.core.urlresolvers import reverse +from django.urls import reverse from django.shortcuts import render, get_object_or_404, HttpResponseRedirect from django.utils.timezone import now diff --git a/audit/migrations/0001_initial.py b/audit/migrations/0001_initial.py index a933988..3b8a36c 100644 --- a/audit/migrations/0001_initial.py +++ b/audit/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2018-11-18 01:34 -from __future__ import unicode_literals +# Generated by Django 2.2.12 on 2020-05-05 15:32 from django.conf import settings from django.db import migrations, models @@ -12,7 +10,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('pttrack', '0006_referral_additional_fields_20180826'), + ('pttrack', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -27,8 +25,8 @@ class Migration(migrations.Migration): ('referrer', models.URLField(blank=True, max_length=256, null=True)), ('status_code', models.PositiveSmallIntegerField()), ('timestamp', models.DateTimeField(auto_now_add=True)), - ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pttrack.ProviderType')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='pttrack.ProviderType')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/audit/models.py b/audit/models.py index 305584d..b9880e8 100644 --- a/audit/models.py +++ b/audit/models.py @@ -11,10 +11,16 @@ class PageviewRecord(models.Model): HTTP_METHODS = ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE'] - user = models.ForeignKey(User, blank=True, null=True) + user = models.ForeignKey( + User, + blank=True, null=True, + on_delete=models.DO_NOTHING + ) user_ip = models.GenericIPAddressField() - role = models.ForeignKey(core_models.ProviderType, - blank=True, null=True) + role = models.ForeignKey( + core_models.ProviderType, + on_delete=models.DO_NOTHING, + blank=True, null=True) method = models.CharField( max_length=max(len(v) for v in HTTP_METHODS), diff --git a/audit/tests.py b/audit/tests.py index 1257634..03393b3 100644 --- a/audit/tests.py +++ b/audit/tests.py @@ -2,7 +2,7 @@ from builtins import str from django.test import TestCase, override_settings from django.test import Client -from django.core.urlresolvers import reverse +from django.urls import reverse from django.contrib.auth.models import User from pttrack.models import ProviderType diff --git a/dashboard/tests.py b/dashboard/tests.py index e1315b0..539a480 100644 --- a/dashboard/tests.py +++ b/dashboard/tests.py @@ -6,7 +6,7 @@ from django.utils.timezone import now from django.test import TestCase, override_settings -from django.core.urlresolvers import reverse +from django.urls import reverse from django.conf import settings from pttrack.test_views import log_in_provider, build_provider @@ -64,7 +64,7 @@ def setUp(self): phone='+49 178 236 5288', gender=Gender.objects.first(), address='Schulstrasse 9', city='Munich', state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1994, 0o1, 22), + date_of_birth=datetime.date(1994, 1, 22), patient_comfortable_with_english=False, preferred_contact_method=ContactMethod.objects.first(), ) @@ -103,7 +103,7 @@ def test_pt_without_note_and_pt_unsigned(self): phone='454545', gender=Gender.objects.first(), address='A', city='B', state='C', zip_code='12345', pcp_preferred_zip='12345', - date_of_birth=datetime.date(1992, 0o4, 22), + date_of_birth=datetime.date(1992, 4, 22), patient_comfortable_with_english=False, preferred_contact_method=ContactMethod.objects.first(), ) diff --git a/dashboard/urls.py b/dashboard/urls.py index ebb8bdd..65fa7ba 100644 --- a/dashboard/urls.py +++ b/dashboard/urls.py @@ -1,16 +1,16 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import path from pttrack.urls import wrap_url from . import views unwrapped_urlconf = [ - url(r'^dispatch/$', - views.dashboard_dispatch, - name='dashboard-dispatch'), - url(r'^attending/$', - views.dashboard_attending, - name='dashboard-attending'), + path(r'dispatch/', + views.dashboard_dispatch, + name='dashboard-dispatch'), + path(r'attending/', + views.dashboard_attending, + name='dashboard-attending'), ] urlpatterns = [wrap_url(u, **{}) for u in unwrapped_urlconf] diff --git a/demographics/forms.py b/demographics/forms.py index 4735fc8..52622f4 100644 --- a/demographics/forms.py +++ b/demographics/forms.py @@ -1,20 +1,17 @@ from __future__ import unicode_literals -from builtins import object from django.forms import ModelForm from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit, Layout, Fieldset -from bootstrap3_datetime.widgets import DateTimePicker from . import models class DemographicsForm(ModelForm): - class Meta(object): + class Meta: model = models.Demographics exclude = ['patient', 'creation_date'] - widgets = {'last_date_physician_visit': DateTimePicker(options={"format": "MM/DD/YYYY"})} def __init__(self, *args, **kwargs): super(DemographicsForm, self).__init__(*args, **kwargs) diff --git a/demographics/migrations/0001_initial.py b/demographics/migrations/0001_initial.py new file mode 100644 index 0000000..899d5c8 --- /dev/null +++ b/demographics/migrations/0001_initial.py @@ -0,0 +1,110 @@ +# Generated by Django 2.2.12 on 2020-05-05 15:32 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import simple_history.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('pttrack', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='ChronicCondition', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='EducationLevel', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='IncomeRange', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='ResourceAccess', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + options={ + 'verbose_name_plural': 'Resource accesses', + }, + ), + migrations.CreateModel( + name='TransportationOption', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='WorkStatus', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + options={ + 'verbose_name_plural': 'Work statuses', + }, + ), + migrations.CreateModel( + name='HistoricalDemographics', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('creation_date', models.DateField(blank=True, null=True)), + ('has_insurance', models.BooleanField(blank=True, null=True)), + ('ER_visit_last_year', models.BooleanField(blank=True, null=True, verbose_name='Visited ER in the Past Year')), + ('last_date_physician_visit', models.DateField(blank=True, null=True, verbose_name="Date of Patient's Last Visit to Physician or ER")), + ('lives_alone', models.BooleanField(blank=True, null=True)), + ('dependents', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Number of Dependents')), + ('currently_employed', models.BooleanField(blank=True, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('annual_income', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='demographics.IncomeRange')), + ('education_level', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='demographics.EducationLevel')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), + ('transportation', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='demographics.TransportationOption')), + ('work_status', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='demographics.WorkStatus')), + ], + options={ + 'verbose_name': 'historical demographics', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='Demographics', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('creation_date', models.DateField(blank=True, null=True)), + ('has_insurance', models.BooleanField(blank=True, null=True)), + ('ER_visit_last_year', models.BooleanField(blank=True, null=True, verbose_name='Visited ER in the Past Year')), + ('last_date_physician_visit', models.DateField(blank=True, null=True, verbose_name="Date of Patient's Last Visit to Physician or ER")), + ('lives_alone', models.BooleanField(blank=True, null=True)), + ('dependents', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Number of Dependents')), + ('currently_employed', models.BooleanField(blank=True, null=True)), + ('annual_income', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='demographics.IncomeRange')), + ('chronic_condition', models.ManyToManyField(blank=True, to='demographics.ChronicCondition')), + ('education_level', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='demographics.EducationLevel')), + ('patient', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='pttrack.Patient')), + ('resource_access', models.ManyToManyField(blank=True, to='demographics.ResourceAccess', verbose_name='Access to Resources')), + ('transportation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='demographics.TransportationOption')), + ('work_status', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='demographics.WorkStatus')), + ], + ), + ] diff --git a/demographics/migrations/0001_squashed_0003_auto_20170623_1048.py b/demographics/migrations/0001_squashed_0003_auto_20170623_1048.py deleted file mode 100644 index 8654e01..0000000 --- a/demographics/migrations/0001_squashed_0003_auto_20170623_1048.py +++ /dev/null @@ -1,130 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - replaces = [(b'demographics', '0001_initial'), (b'demographics', '0002_auto_20160328_1425'), (b'demographics', '0003_auto_20170623_1048')] - - dependencies = [ - ('pttrack', '0001_squashed_0010_auto_20170623_1300'), - ] - - operations = [ - migrations.CreateModel( - name='ChronicCondition', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='Demographics', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('creation_date', models.DateField(null=True, blank=True)), - ('has_insurance', models.BooleanField(default=False)), - ('ER_visit_last_year', models.BooleanField(default=False, verbose_name=b'Visited ER in the past year')), - ('last_date_physician_visit', models.DateField(null=True, verbose_name=b'Date Last Visited Patient', blank=True)), - ('lives_alone', models.BooleanField(default=False)), - ('dependents', models.PositiveSmallIntegerField(null=True, verbose_name=b'Number of Dependents', blank=True)), - ('currently_employed', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='EducationLevel', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='IncomeRange', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='ResourceAccess', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='TransportationOption', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='WorkStatus', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.AddField( - model_name='demographics', - name='annual_income', - field=models.ForeignKey(blank=True, to='demographics.IncomeRange', null=True), - ), - migrations.AddField( - model_name='demographics', - name='chronic_condition', - field=models.ManyToManyField(to='demographics.ChronicCondition', blank=True), - ), - migrations.AddField( - model_name='demographics', - name='education_level', - field=models.ForeignKey(blank=True, to='demographics.EducationLevel', null=True), - ), - migrations.AddField( - model_name='demographics', - name='patient', - field=models.OneToOneField(null=True, to='pttrack.Patient'), - ), - migrations.AddField( - model_name='demographics', - name='resource_access', - field=models.ManyToManyField(to='demographics.ResourceAccess', verbose_name=b'Access to Resources', blank=True), - ), - migrations.AddField( - model_name='demographics', - name='transportation', - field=models.ForeignKey(blank=True, to='demographics.TransportationOption', null=True), - ), - migrations.AddField( - model_name='demographics', - name='work_status', - field=models.ForeignKey(blank=True, to='demographics.WorkStatus', null=True), - ), - migrations.AlterField( - model_name='demographics', - name='ER_visit_last_year', - field=models.NullBooleanField(verbose_name=b'Visited ER in the past year'), - ), - migrations.AlterField( - model_name='demographics', - name='currently_employed', - field=models.NullBooleanField(), - ), - migrations.AlterField( - model_name='demographics', - name='has_insurance', - field=models.NullBooleanField(), - ), - migrations.AlterField( - model_name='demographics', - name='lives_alone', - field=models.NullBooleanField(), - ), - migrations.AlterField( - model_name='demographics', - name='ER_visit_last_year', - field=models.NullBooleanField(verbose_name=b'Visited ER in the Past Year'), - ), - migrations.AlterField( - model_name='demographics', - name='last_date_physician_visit', - field=models.DateField(null=True, verbose_name=b"Date of Patient's Last Visit to Physician or ER", blank=True), - ), - ] diff --git a/demographics/migrations/0002_historicalrecords_and_nullboolean_20171119_1818.py b/demographics/migrations/0002_historicalrecords_and_nullboolean_20171119_1818.py deleted file mode 100644 index 436a0cd..0000000 --- a/demographics/migrations/0002_historicalrecords_and_nullboolean_20171119_1818.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('pttrack', '0003_auto_20171014_1843'), - ('demographics', '0001_squashed_0003_auto_20170623_1048'), - ] - - operations = [ - migrations.CreateModel( - name='HistoricalDemographics', - fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('creation_date', models.DateField(null=True, blank=True)), - ('has_insurance', models.NullBooleanField(choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')])), - ('ER_visit_last_year', models.NullBooleanField(verbose_name=b'Visited ER in the Past Year', choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')])), - ('last_date_physician_visit', models.DateField(null=True, verbose_name=b"Date of Patient's Last Visit to Physician or ER", blank=True)), - ('lives_alone', models.NullBooleanField(choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')])), - ('dependents', models.PositiveSmallIntegerField(null=True, verbose_name=b'Number of Dependents', blank=True)), - ('currently_employed', models.NullBooleanField(choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')])), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('annual_income', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='demographics.IncomeRange', null=True)), - ('education_level', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='demographics.EducationLevel', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('patient', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True)), - ('transportation', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='demographics.TransportationOption', null=True)), - ('work_status', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='demographics.WorkStatus', null=True)), - ], - options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical demographics', - }, - ), - migrations.AlterField( - model_name='demographics', - name='ER_visit_last_year', - field=models.NullBooleanField(verbose_name=b'Visited ER in the Past Year', choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')]), - ), - migrations.AlterField( - model_name='demographics', - name='currently_employed', - field=models.NullBooleanField(choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')]), - ), - migrations.AlterField( - model_name='demographics', - name='has_insurance', - field=models.NullBooleanField(choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')]), - ), - migrations.AlterField( - model_name='demographics', - name='lives_alone', - field=models.NullBooleanField(choices=[(None, b'Not Answered'), (True, b'Yes'), (False, b'No')]), - ), - ] diff --git a/demographics/migrations/0003_simplehistory_add_change_reason.py b/demographics/migrations/0003_simplehistory_add_change_reason.py deleted file mode 100644 index 659c1b5..0000000 --- a/demographics/migrations/0003_simplehistory_add_change_reason.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2018-07-09 01:07 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('demographics', '0002_historicalrecords_and_nullboolean_20171119_1818'), - ] - - operations = [ - migrations.AddField( - model_name='historicaldemographics', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - ] diff --git a/demographics/migrations/0004_add_proper_plurals_20190902_2116.py b/demographics/migrations/0004_add_proper_plurals_20190902_2116.py deleted file mode 100644 index 88100c6..0000000 --- a/demographics/migrations/0004_add_proper_plurals_20190902_2116.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:16 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('demographics', '0003_simplehistory_add_change_reason'), - ] - - operations = [ - migrations.AlterModelOptions( - name='resourceaccess', - options={'verbose_name_plural': 'Resource accesses'}, - ), - migrations.AlterModelOptions( - name='workstatus', - options={'verbose_name_plural': 'Work statuses'}, - ), - ] diff --git a/demographics/models.py b/demographics/models.py index 299e7d2..7038b3d 100644 --- a/demographics/models.py +++ b/demographics/models.py @@ -60,23 +60,19 @@ def __str__(self): class Demographics(models.Model): - NULL_BOOLEAN_CHOICES = ( - (None, "Not Answered"), - (True, "Yes"), - (False, "No") - ) - - patient = models.OneToOneField(Patient, null=True) + patient = models.OneToOneField( + Patient, on_delete=models.CASCADE, null=True) creation_date = models.DateField(blank=True, null=True) chronic_condition = models.ManyToManyField(ChronicCondition, blank=True) - has_insurance = models.NullBooleanField(choices=NULL_BOOLEAN_CHOICES) + has_insurance = models.BooleanField(null=True, blank=True) - ER_visit_last_year = models.NullBooleanField( - verbose_name="Visited ER in the Past Year", - choices=NULL_BOOLEAN_CHOICES) + ER_visit_last_year = models.BooleanField( + null=True, blank=True, + verbose_name="Visited ER in the Past Year" + ) last_date_physician_visit = models.DateField( blank=True, null=True, @@ -86,20 +82,32 @@ class Demographics(models.Model): ResourceAccess, blank=True, verbose_name="Access to Resources") - lives_alone = models.NullBooleanField(choices=NULL_BOOLEAN_CHOICES) + lives_alone = models.BooleanField(null=True, blank=True) dependents = models.PositiveSmallIntegerField( blank=True, null=True, verbose_name="Number of Dependents") - currently_employed = models.NullBooleanField(choices=NULL_BOOLEAN_CHOICES) + currently_employed = models.BooleanField(null=True, blank=True) - work_status = models.ForeignKey(WorkStatus, blank=True, null=True) + work_status = models.ForeignKey( + WorkStatus, + on_delete=models.PROTECT, + blank=True, null=True + ) - education_level = models.ForeignKey(EducationLevel, blank=True, null=True) + education_level = models.ForeignKey( + EducationLevel, + on_delete=models.PROTECT, + blank=True, null=True) - annual_income = models.ForeignKey(IncomeRange, blank=True, null=True) + annual_income = models.ForeignKey( + IncomeRange, + on_delete=models.PROTECT, + blank=True, null=True) transportation = models.ForeignKey( - TransportationOption, blank=True, null=True) + TransportationOption, + on_delete=models.PROTECT, + blank=True, null=True) history = HistoricalRecords() diff --git a/demographics/tests.py b/demographics/tests.py index 6916dca..d3dedb5 100644 --- a/demographics/tests.py +++ b/demographics/tests.py @@ -2,15 +2,13 @@ from datetime import date from django.test import TestCase -from django.core.urlresolvers import reverse -from django.db import transaction +from django.urls import reverse from pttrack.test_views import build_provider, log_in_provider from pttrack.models import Patient, Gender, ContactMethod from . import models from . import forms -# Create your tests here. class ViewsExistTest(TestCase): @@ -104,7 +102,7 @@ def test_demographics_form_submission(self): Test submission of a demographics form ''' - for i in list(dict(models.Demographics.NULL_BOOLEAN_CHOICES).keys()): + for i in [True, False, None]: pt = self.make_patient() @@ -114,14 +112,16 @@ def test_demographics_form_submission(self): 'education_level': models.EducationLevel.objects.all()[0], 'transportation': models.TransportationOption.objects.all()[0], 'work_status': models.WorkStatus.objects.all()[0], - 'has_insurance': i, - 'ER_visit_last_year': i, 'last_date_physician_visit': date.today(), - 'lives_alone': i, 'dependents': 4, - 'currently_employed': i, } + if i is True or i is False: + valid_dg_dict['has_insurance'] = i + valid_dg_dict['ER_visit_last_year'] = i + valid_dg_dict['lives_alone'] = i + valid_dg_dict['currently_employed'] = i + final_url = reverse('demographics-create', args=(pt.id,)) form = forms.DemographicsForm(data=valid_dg_dict) @@ -162,12 +162,10 @@ def test_demographics_form_double_submission(self): 'education_level': models.EducationLevel.objects.all()[0], 'transportation': models.TransportationOption.objects.all()[0], 'work_status': models.WorkStatus.objects.all()[0], - 'has_insurance': None, 'ER_visit_last_year': True, 'last_date_physician_visit': date.today(), 'lives_alone': False, 'dependents': 4, - 'currently_employed': None, } # Submit demographics object twice @@ -178,11 +176,13 @@ def test_demographics_form_double_submission(self): response, 'pttrack/patient_detail.html') self.assertEqual(models.Demographics.objects.count(), 1) - # Send in submission with the same patient ID - response2 = self.client.post(dg_url, dg) + # Send in submission with the same patient ID; we should see no new + # Demographics objects, and a successful redirect to patient-detail + response2 = self.client.post(dg_url, dg, follow=True) self.assertEqual(response2.status_code, 200) + self.assertEqual(models.Demographics.objects.count(), 1) self.assertTemplateUsed( - response2, 'demographics/demographics-resolve.html') + response2, 'pttrack/patient_detail.html') def test_demographics_form_double_submission_errors(self): ''' @@ -198,18 +198,21 @@ def test_demographics_form_double_submission_errors(self): 'education_level': models.EducationLevel.objects.first(), 'transportation': models.TransportationOption.objects.first(), 'work_status': models.WorkStatus.objects.first(), - 'has_insurance': None, 'ER_visit_last_year': True, 'last_date_physician_visit': date.today(), 'lives_alone': False, 'dependents': 4, - 'currently_employed': None, } dg_url = reverse('demographics-create', args=(pt.pk,)) - response = self.client.post(dg_url, dg) + + response1 = self.client.post(dg_url, dg, follow=True) + self.assertTemplateUsed(response1, 'pttrack/patient_detail.html') + response2 = self.client.post(dg_url, dg, follow=True) + # success here means redirection to patient detail page self.assertNotContains(response2, 'Clash') + self.assertTemplateUsed(response2, 'pttrack/patient_detail.html') # Test case 2 - two different forms submitted # In this case, all fields should have errors @@ -227,19 +230,22 @@ def test_demographics_form_double_submission_errors(self): 'last_date_physician_visit': date.today(), 'lives_alone': False, 'dependents': 6, - 'currently_employed': None, } response3 = self.client.post(dg_url, dg2, follow=True) - self.assertFormError(response3, 'form_old', 'has_insurance', - "Clash in this field. Database entry is 'Not Answered'") - self.assertFormError(response3, 'form_new', 'ER_visit_last_year', - "Clash in this field. You entered 'No'") - self.assertFormError(response3, 'form_old', 'dependents', - "Clash in this field. Database entry is '4'") - self.assertFormError(response3, 'form_new','dependents', - "Clash in this field. You entered '6'") + self.assertFormError( + response3, 'form_old', 'has_insurance', + "Clash in this field. Database entry is 'None'") + self.assertFormError( + response3, 'form_new', 'ER_visit_last_year', + "Clash in this field. You entered 'False'") + self.assertFormError( + response3, 'form_old', 'dependents', + "Clash in this field. Database entry is '4'") + self.assertFormError( + response3, 'form_new', 'dependents', + "Clash in this field. You entered '6'") # Verify that there are 6 errors on page (3 fields x 2 messages) self.assertContains(response3, 'Clash', count=6) diff --git a/demographics/urls.py b/demographics/urls.py index 6536d5c..fc16342 100644 --- a/demographics/urls.py +++ b/demographics/urls.py @@ -1,5 +1,5 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import path from pttrack.urls import wrap_url from django.views.generic import DetailView @@ -7,16 +7,16 @@ from . import models as mymodels unwrapped_urlconf = [ # pylint: disable=invalid-name - url(r'^new/(?P[0-9]+)$', - views.DemographicsCreate.as_view(), - name='demographics-create'), - url(r'^(?P[0-9]+)/$', - DetailView.as_view(model=mymodels.Demographics), - name='demographics-detail'), - url(r'^(?P[0-9]+)/update/$', - views.DemographicsUpdate.as_view(), - name='demographics-update'), + path('new/', + views.DemographicsCreate.as_view(), + name='demographics-create'), + path('/', + DetailView.as_view(model=mymodels.Demographics), + name='demographics-detail'), + path('/update/', + views.DemographicsUpdate.as_view(), + name='demographics-update'), ] wrap_config = {} -urlpatterns = [wrap_url(url, **wrap_config) for url in unwrapped_urlconf] \ No newline at end of file +urlpatterns = [wrap_url(u, **wrap_config) for u in unwrapped_urlconf] diff --git a/demographics/views.py b/demographics/views.py index 7444ee7..d49967d 100644 --- a/demographics/views.py +++ b/demographics/views.py @@ -4,7 +4,7 @@ from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect from django.views.generic.edit import FormView, UpdateView -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import IntegrityError, transaction from django.forms.models import model_to_dict @@ -31,61 +31,66 @@ def get_context_data(self, **kwargs): return context - def form_valid(self, form): + def form_valid(self, form_new): pt = get_object_or_404(Patient, pk=self.kwargs['pt_id']) - dg = form.save(commit=False) - dg.creation_date = datetime.date.today() - dg.patient = pt + dg_new = form_new.save(commit=False) + dg_new.creation_date = datetime.date.today() + dg_new.patient = pt try: with transaction.atomic(): - dg.save() + dg_new.save() pt.save() - form.save_m2m() - form.save() + form_new.save_m2m() + form_new.save() return HttpResponseRedirect(reverse("patient-detail", args=(pt.id,))) except IntegrityError: + # Create form object containing data from current entry in database dg_old = Demographics.objects.get(patient=pt.id) - form_old = DemographicsForm(dg_old.__dict__) - # calling is_valid causes cleaned_data to be availiable - assert form_old.is_valid() - - NULL_BOOLEAN_CHOICES = dict(Demographics.NULL_BOOLEAN_CHOICES) + dgdict_new = model_to_dict(dg_new) + dgdict_old = model_to_dict(dg_old) - # Add errors to the forms to point user to fields to fix - dg_old_dict = model_to_dict(dg_old) - dg_new_dict = model_to_dict(dg) + differences = [k for k, v in dgdict_old.items() if + k not in ['_state', 'id'] and + dgdict_new[k] != v] - for field in form_old.base_fields: + # An integrity error will be thrown whether or not there is + # a difference between the models. + if not differences: + return HttpResponseRedirect(reverse("patient-detail", + args=(pt.id,))) - old_value = dg_old_dict.get(field) - new_value = dg_new_dict.get(field) + form_old = DemographicsForm(dg_old.__dict__) + assert form_old.is_valid() - if new_value != old_value: + for field in differences: - new_err_msg = "Clash in this field. You entered '%s'" - old_err_msg = "Clash in this field. Database entry is '%s'" + new_val = form_new[field].value() + old_val = form_old[field].value() - value_msg_form_tuples = [ - (new_value, new_err_msg, form), - (old_value, old_err_msg, form_old) - ] + if new_val != old_val: - for val, err_msg, f in value_msg_form_tuples: - if val in NULL_BOOLEAN_CHOICES: - err_msg = err_msg % NULL_BOOLEAN_CHOICES[val] - else: - err_msg = err_msg % val + form_old.add_error( + field, + "Clash in this field. Database entry is '%s'" % + old_val + # (old_val if old_val is not None else 'Not Answered') + ) - f.add_error(field, err_msg) + form_new.add_error( + field, + "Clash in this field. You entered '%s'" % + new_val + # (new_val if new_val is not None else 'Not Answered') + ) # Create context variable containing new and old forms context = {"form_old": form_old, - "form_new": form, + "form_new": form_new, "pt_id": pt.id, "pt_name": pt.name} return render(self.request, diff --git a/followup/forms.py b/followup/forms.py index a6f801a..280572f 100644 --- a/followup/forms.py +++ b/followup/forms.py @@ -2,13 +2,12 @@ from builtins import object from django.forms import ModelForm -from bootstrap3_datetime.widgets import DateTimePicker - from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit from . import models + class BaseFollowup(ModelForm): '''The base class for followup forms''' class Meta(object): @@ -99,7 +98,6 @@ class VaccineFollowup(BaseFollowup): class Meta(object): model = models.VaccineFollowup exclude = ['patient', 'author', 'author_type'] - widgets = {'dose_date': DateTimePicker(options={"format": "MM/DD/YYYY"})} def clean(self): '''VaccineFollowups require a next dose date iff there there is a next diff --git a/followup/migrations/0001_initial.py b/followup/migrations/0001_initial.py index 095a35d..051ac8e 100644 --- a/followup/migrations/0001_initial.py +++ b/followup/migrations/0001_initial.py @@ -1,13 +1,15 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals +# Generated by Django 2.2.12 on 2020-05-05 15:32 +from django.conf import settings from django.db import migrations, models import django.db.models.deletion -from django.conf import settings +import simple_history.models class Migration(migrations.Migration): + initial = True + dependencies = [ ('pttrack', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), @@ -17,215 +19,207 @@ class Migration(migrations.Migration): migrations.CreateModel( name='ContactResult', fields=[ - ('name', models.CharField(max_length=100, serialize=False, primary_key=True)), - ('attempt_again', models.BooleanField(default=False, help_text=b'True if outcome means the pt should be contacted again.')), - ('patient_reached', models.BooleanField(default=True, help_text=b'True if outcome means they reached the patient')), + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), + ('attempt_again', models.BooleanField(default=False, help_text='True if outcome means the pt should be contacted again.')), + ('patient_reached', models.BooleanField(default=True, help_text='True if outcome means they reached the patient')), ], ), migrations.CreateModel( - name='GeneralFollowup', + name='NoAptReason', + fields=[ + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='NoShowReason', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='VaccineFollowup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('written_datetime', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now=True)), - ('comments', models.TextField(null=True, blank=True)), - ('author', models.ForeignKey(to='pttrack.Provider')), - ('author_type', models.ForeignKey(to='pttrack.ProviderType')), - ('contact_method', models.ForeignKey(to='pttrack.ContactMethod')), - ('contact_resolution', models.ForeignKey(to='followup.ContactResult')), - ('patient', models.ForeignKey(to='pttrack.Patient')), + ('comments', models.TextField(blank=True, null=True)), + ('subsq_dose', models.BooleanField(verbose_name='Has the patient committed to coming back for another dose?')), + ('dose_date', models.DateField(blank=True, help_text='When does the patient want to get their next dose (if applicable)?', null=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='followup.ContactResult')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), ], options={ 'abstract': False, }, ), migrations.CreateModel( - name='HistoricalGeneralFollowup', + name='ReferralFollowup', fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('comments', models.TextField(null=True, blank=True)), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('author', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author_type', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True)), - ('contact_method', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ContactMethod', null=True)), - ('contact_resolution', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='followup.ContactResult', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('patient', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ('comments', models.TextField(blank=True, null=True)), + ('has_appointment', models.BooleanField(help_text='Does the patient have an appointment?')), + ('pt_showed', models.CharField(blank=True, choices=[('Yes', 'Yes'), ('No', 'No'), ('Not yet', 'Not yet')], help_text='Did the patient show up to the appointment?', max_length=7, null=True)), + ('apt_location', models.ForeignKey(blank=True, help_text='Where is the appointment?', null=True, on_delete=django.db.models.deletion.PROTECT, to='pttrack.ReferralLocation')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='followup.ContactResult')), + ('noapt_reason', models.ForeignKey(blank=True, help_text="If the patient didn't make an appointment, why not?", null=True, on_delete=django.db.models.deletion.PROTECT, to='followup.NoAptReason')), + ('noshow_reason', models.ForeignKey(blank=True, help_text="If the patient didn't go to appointment, why not?", null=True, on_delete=django.db.models.deletion.PROTECT, to='followup.NoShowReason')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ('referral_type', models.ForeignKey(blank=True, help_text='What kind of provider was the patient referred to?', null=True, on_delete=django.db.models.deletion.PROTECT, to='pttrack.ReferralType')), ], options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical general followup', + 'abstract': False, }, ), migrations.CreateModel( - name='HistoricalLabFollowup', + name='LabFollowup', fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('comments', models.TextField(null=True, blank=True)), - ('communication_success', models.BooleanField(help_text=b'Were you able to communicate the results?')), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('author', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author_type', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True)), - ('contact_method', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ContactMethod', null=True)), - ('contact_resolution', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='followup.ContactResult', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('patient', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ('comments', models.TextField(blank=True, null=True)), + ('communication_success', models.BooleanField(help_text='Were you able to communicate the results?')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='followup.ContactResult')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), ], options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical lab followup', + 'abstract': False, }, ), migrations.CreateModel( - name='HistoricalReferralFollowup', + name='HistoricalVaccineFollowup', fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('comments', models.TextField(null=True, blank=True)), - ('has_appointment', models.BooleanField(help_text=b'Does the patient have an appointment?')), - ('pt_showed', models.CharField(blank=True, max_length=7, null=True, help_text=b'Did the patient show up to the appointment?', choices=[(b'Yes', b'Yes'), (b'No', b'No'), (b'Not yet', b'Not yet')])), - ('history_id', models.AutoField(serialize=False, primary_key=True)), + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('comments', models.TextField(blank=True, null=True)), + ('subsq_dose', models.BooleanField(verbose_name='Has the patient committed to coming back for another dose?')), + ('dose_date', models.DateField(blank=True, help_text='When does the patient want to get their next dose (if applicable)?', null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('apt_location', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ReferralLocation', null=True)), - ('author', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author_type', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True)), - ('contact_method', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ContactMethod', null=True)), - ('contact_resolution', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='followup.ContactResult', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.ContactResult')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), ], options={ + 'verbose_name': 'historical vaccine followup', 'ordering': ('-history_date', '-history_id'), 'get_latest_by': 'history_date', - 'verbose_name': 'historical referral followup', }, + bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.CreateModel( - name='HistoricalVaccineFollowup', + name='HistoricalReferralFollowup', fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('comments', models.TextField(null=True, blank=True)), - ('subsq_dose', models.BooleanField(verbose_name=b'Has the patient committed to coming back for another dose?')), - ('dose_date', models.DateField(help_text=b'When does the patient want to get their next dose (if applicable)?', null=True, blank=True)), - ('history_id', models.AutoField(serialize=False, primary_key=True)), + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('comments', models.TextField(blank=True, null=True)), + ('has_appointment', models.BooleanField(help_text='Does the patient have an appointment?')), + ('pt_showed', models.CharField(blank=True, choices=[('Yes', 'Yes'), ('No', 'No'), ('Not yet', 'Not yet')], help_text='Did the patient show up to the appointment?', max_length=7, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('author', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author_type', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True)), - ('contact_method', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ContactMethod', null=True)), - ('contact_resolution', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='followup.ContactResult', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('patient', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('apt_location', models.ForeignKey(blank=True, db_constraint=False, help_text='Where is the appointment?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ReferralLocation')), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.ContactResult')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('noapt_reason', models.ForeignKey(blank=True, db_constraint=False, help_text="If the patient didn't make an appointment, why not?", null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.NoAptReason')), + ('noshow_reason', models.ForeignKey(blank=True, db_constraint=False, help_text="If the patient didn't go to appointment, why not?", null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.NoShowReason')), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), + ('referral_type', models.ForeignKey(blank=True, db_constraint=False, help_text='What kind of provider was the patient referred to?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ReferralType')), ], options={ + 'verbose_name': 'historical referral followup', 'ordering': ('-history_date', '-history_id'), 'get_latest_by': 'history_date', - 'verbose_name': 'historical vaccine followup', }, + bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.CreateModel( - name='LabFollowup', + name='HistoricalLabFollowup', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('written_datetime', models.DateTimeField(auto_now_add=True)), - ('last_modified', models.DateTimeField(auto_now=True)), - ('comments', models.TextField(null=True, blank=True)), - ('communication_success', models.BooleanField(help_text=b'Were you able to communicate the results?')), - ('author', models.ForeignKey(to='pttrack.Provider')), - ('author_type', models.ForeignKey(to='pttrack.ProviderType')), - ('contact_method', models.ForeignKey(to='pttrack.ContactMethod')), - ('contact_resolution', models.ForeignKey(to='followup.ContactResult')), - ('patient', models.ForeignKey(to='pttrack.Patient')), + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('comments', models.TextField(blank=True, null=True)), + ('communication_success', models.BooleanField(help_text='Were you able to communicate the results?')), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.ContactResult')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), ], options={ - 'abstract': False, + 'verbose_name': 'historical lab followup', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', }, + bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.CreateModel( - name='NoAptReason', - fields=[ - ('name', models.CharField(max_length=100, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='NoShowReason', - fields=[ - ('name', models.CharField(max_length=100, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='ReferralFollowup', + name='HistoricalGeneralFollowup', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('written_datetime', models.DateTimeField(auto_now_add=True)), - ('last_modified', models.DateTimeField(auto_now=True)), - ('comments', models.TextField(null=True, blank=True)), - ('has_appointment', models.BooleanField(help_text=b'Does the patient have an appointment?')), - ('pt_showed', models.CharField(blank=True, max_length=7, null=True, help_text=b'Did the patient show up to the appointment?', choices=[(b'Yes', b'Yes'), (b'No', b'No'), (b'Not yet', b'Not yet')])), - ('apt_location', models.ForeignKey(blank=True, to='pttrack.ReferralLocation', help_text=b'Where is the appointment?', null=True)), - ('author', models.ForeignKey(to='pttrack.Provider')), - ('author_type', models.ForeignKey(to='pttrack.ProviderType')), - ('contact_method', models.ForeignKey(to='pttrack.ContactMethod')), - ('contact_resolution', models.ForeignKey(to='followup.ContactResult')), - ('noapt_reason', models.ForeignKey(blank=True, to='followup.NoAptReason', help_text=b"If the patient didn't make an appointment, why not?", null=True)), - ('noshow_reason', models.ForeignKey(blank=True, to='followup.NoShowReason', help_text=b"If the patient didn't go to appointment, why not?", null=True)), - ('patient', models.ForeignKey(to='pttrack.Patient')), - ('referral_type', models.ForeignKey(blank=True, to='pttrack.ReferralType', help_text=b'What kind of provider was the patient referred to?', null=True)), + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('comments', models.TextField(blank=True, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.ContactResult')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), ], options={ - 'abstract': False, + 'verbose_name': 'historical general followup', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', }, + bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.CreateModel( - name='VaccineFollowup', + name='GeneralFollowup', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('written_datetime', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now=True)), - ('comments', models.TextField(null=True, blank=True)), - ('subsq_dose', models.BooleanField(verbose_name=b'Has the patient committed to coming back for another dose?')), - ('dose_date', models.DateField(help_text=b'When does the patient want to get their next dose (if applicable)?', null=True, blank=True)), - ('author', models.ForeignKey(to='pttrack.Provider')), - ('author_type', models.ForeignKey(to='pttrack.ProviderType')), - ('contact_method', models.ForeignKey(to='pttrack.ContactMethod')), - ('contact_resolution', models.ForeignKey(to='followup.ContactResult')), - ('patient', models.ForeignKey(to='pttrack.Patient')), + ('comments', models.TextField(blank=True, null=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ContactMethod')), + ('contact_resolution', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='followup.ContactResult')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), ], options={ 'abstract': False, }, ), - migrations.AddField( - model_name='historicalreferralfollowup', - name='noapt_reason', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='followup.NoAptReason', null=True), - ), - migrations.AddField( - model_name='historicalreferralfollowup', - name='noshow_reason', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='followup.NoShowReason', null=True), - ), - migrations.AddField( - model_name='historicalreferralfollowup', - name='patient', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True), - ), - migrations.AddField( - model_name='historicalreferralfollowup', - name='referral_type', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ReferralType', null=True), - ), ] diff --git a/followup/migrations/0002_simplehistory_add_change_reason.py b/followup/migrations/0002_simplehistory_add_change_reason.py deleted file mode 100644 index 1561470..0000000 --- a/followup/migrations/0002_simplehistory_add_change_reason.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2018-07-09 01:07 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('followup', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='historicalgeneralfollowup', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicallabfollowup', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicalreferralfollowup', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicalvaccinefollowup', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - ] diff --git a/followup/migrations/0003_simplehistory_add_v2_migrations_20190324_1133.py b/followup/migrations/0003_simplehistory_add_v2_migrations_20190324_1133.py deleted file mode 100644 index f42f7c4..0000000 --- a/followup/migrations/0003_simplehistory_add_v2_migrations_20190324_1133.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.20 on 2019-03-24 16:33 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('followup', '0002_simplehistory_add_change_reason'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalreferralfollowup', - name='apt_location', - field=models.ForeignKey( - blank=True, db_constraint=False, - help_text=b'Where is the appointment?', null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='pttrack.ReferralLocation'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='noapt_reason', - field=models.ForeignKey( - blank=True, db_constraint=False, - help_text=b"If the patient didn't make an appointment, " - b"why not?", - null=True, on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='followup.NoAptReason'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='noshow_reason', - field=models.ForeignKey( - blank=True, db_constraint=False, - help_text=b"If the patient didn't go to appointment, why not?", - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='followup.NoShowReason'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='referral_type', - field=models.ForeignKey( - blank=True, db_constraint=False, - help_text=b'What kind of provider was the patient ' - b'referred to?', - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='pttrack.ReferralType'), - ), - ] diff --git a/followup/migrations/0004_simplehistory_add_v2_migrations_2_20190813_2018.py b/followup/migrations/0004_simplehistory_add_v2_migrations_2_20190813_2018.py deleted file mode 100644 index ae7bfe8..0000000 --- a/followup/migrations/0004_simplehistory_add_v2_migrations_2_20190813_2018.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.20 on 2019-08-14 01:18 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('followup', '0003_simplehistory_add_v2_migrations_20190324_1133'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalreferralfollowup', - name='apt_location', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ReferralLocation'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='noapt_reason', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.NoAptReason'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='noshow_reason', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.NoShowReason'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='referral_type', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ReferralType'), - ), - ] diff --git a/followup/migrations/0005_db_do_nothings_20190902_2116.py b/followup/migrations/0005_db_do_nothings_20190902_2116.py deleted file mode 100644 index 740817d..0000000 --- a/followup/migrations/0005_db_do_nothings_20190902_2116.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:16 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('followup', '0004_simplehistory_add_v2_migrations_2_20190813_2018'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalreferralfollowup', - name='apt_location', - field=models.ForeignKey(blank=True, db_constraint=False, help_text=b'Where is the appointment?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ReferralLocation'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='noapt_reason', - field=models.ForeignKey(blank=True, db_constraint=False, help_text=b"If the patient didn't make an appointment, why not?", null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.NoAptReason'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='noshow_reason', - field=models.ForeignKey(blank=True, db_constraint=False, help_text=b"If the patient didn't go to appointment, why not?", null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='followup.NoShowReason'), - ), - migrations.AlterField( - model_name='historicalreferralfollowup', - name='referral_type', - field=models.ForeignKey(blank=True, db_constraint=False, help_text=b'What kind of provider was the patient referred to?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ReferralType'), - ), - ] diff --git a/followup/models.py b/followup/models.py index bb608ea..56d8422 100644 --- a/followup/models.py +++ b/followup/models.py @@ -7,7 +7,6 @@ from simple_history.models import HistoricalRecords -# pylint: disable=I0011,E1305 class NoShowReason(models.Model): '''Simple text-contiaining class for storing the different reasons a @@ -52,8 +51,10 @@ class Followup(Note): class Meta(object): abstract = True - contact_method = models.ForeignKey(ContactMethod) - contact_resolution = models.ForeignKey(ContactResult) + contact_method = models.ForeignKey( + ContactMethod, on_delete=models.PROTECT) + contact_resolution = models.ForeignKey( + ContactResult, on_delete=models.PROTECT) comments = models.TextField(blank=True, null=True) @@ -148,21 +149,25 @@ def short_text(self): class ReferralFollowup(Followup): '''Datamodel for a PCP referral followup.''' - # Template relies on following variable to render Admin Edit. If you change the variable here, you must edit patient_detail.html + # Template relies on following variable to render Admin Edit. + # If you change the variable here, you must edit patient_detail.html REFTYPE_HELP = "What kind of provider was the patient referred to?" - referral_type = models.ForeignKey(ReferralType, - help_text=REFTYPE_HELP, - blank=True, - null=True) + referral_type = models.ForeignKey( + ReferralType, + on_delete=models.PROTECT, + help_text=REFTYPE_HELP, + blank=True, + null=True) bREF_HELP = "Does the patient have an appointment?" has_appointment = models.BooleanField(help_text=bREF_HELP) APP_HELP = "Where is the appointment?" - apt_location = models.ForeignKey(ReferralLocation, - blank=True, - null=True, - help_text=APP_HELP) + apt_location = models.ForeignKey( + ReferralLocation, + blank=True, null=True, + on_delete=models.PROTECT, + help_text=APP_HELP) PTSHOW_OPTS = [("Yes", "Yes"), ("No", "No"), @@ -176,16 +181,20 @@ class ReferralFollowup(Followup): null=True) NOAPT_HELP = "If the patient didn't make an appointment, why not?" - noapt_reason = models.ForeignKey(NoAptReason, - help_text=NOAPT_HELP, - blank=True, - null=True) + noapt_reason = models.ForeignKey( + NoAptReason, + on_delete=models.PROTECT, + help_text=NOAPT_HELP, + blank=True, + null=True) NOSHOW_HELP = "If the patient didn't go to appointment, why not?" - noshow_reason = models.ForeignKey(NoShowReason, - help_text=NOSHOW_HELP, - blank=True, - null=True) + noshow_reason = models.ForeignKey( + NoShowReason, + help_text=NOSHOW_HELP, + on_delete=models.PROTECT, + blank=True, + null=True) history = HistoricalRecords() @@ -204,7 +213,7 @@ def short_text(self): out.append("but the patient didn't go because") out.append(str(self.noshow_reason).lower()) else: - out.append("No appointment made because") + out.append("No appointment made because ") out.append(str(self.noapt_reason).lower()) - return " ".join(out)+"." + return " ".join(out) + "." diff --git a/followup/tests.py b/followup/tests.py index 2b39122..96aa972 100644 --- a/followup/tests.py +++ b/followup/tests.py @@ -6,12 +6,7 @@ import datetime from django.test import TestCase -from django.core.urlresolvers import reverse - -# For live tests. -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.common.by import By +from django.urls import reverse from pttrack.models import Gender, Patient, Provider, ProviderType from pttrack.test import SeleniumLiveTestCase @@ -163,12 +158,11 @@ def test_referral_followup_js_and_submission(self): # and the submission with this comment should be in the db self.assertGreater( - models.ReferralFollowup.objects.\ - filter(comments=COMMENT).count(), + models.ReferralFollowup.objects.filter(comments=COMMENT).count(), 0) def test_followup_view_rendering(self): - from django.core.urlresolvers import NoReverseMatch + from django.urls import NoReverseMatch for url in urls.urlpatterns: if url.name in ['new-followup', 'followup']: @@ -211,7 +205,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -229,21 +223,22 @@ def setUp(self): self.noshow_reason = models.NoShowReason.objects.create( name="Hella busy.") + def build_form(self, contact_successful, has_appointment, apt_location, + noapt_reason, noshow_reason, pt_showed=None): + """Construct a ReferralFollowup form to suit the needs of the + testing subroutines based upon what is provided and not provided. + """ - def build_form(self, contact_successful, has_appointment, apt_location, noapt_reason, noshow_reason, pt_showed=None): - ''' - Construct a ReferralFollowup form to suit the needs of the testing - subroutines based upon what is provided and not provided. - ''' - - contact_resolution = self.successful_res if contact_successful else self.unsuccessful_res + contact_resolution = ( + self.successful_res if contact_successful + else self.unsuccessful_res) form_data = { 'contact_method': self.contact_method, 'contact_resolution': contact_resolution, 'patient': self.pt, 'referral_type': self.reftype, - } + } # Has appointment could (at least in principle) be True, False, or # unspecified. @@ -390,6 +385,12 @@ def setUp(self): from pttrack.test_views import log_in_provider, build_provider log_in_provider(self.client, build_provider()) + def tearDown(self): + models.GeneralFollowup.objects.all().delete() + models.LabFollowup.objects.all().delete() + models.VaccineFollowup.objects.all().delete() + models.ReferralFollowup.objects.all().delete() + def test_followup_view_urls(self): pt = Patient.objects.all()[0] diff --git a/followup/urls.py b/followup/urls.py index fbb18ec..2c8ea8d 100644 --- a/followup/urls.py +++ b/followup/urls.py @@ -1,36 +1,36 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import path from pttrack.urls import wrap_url from . import views unwrapped_urlconf = [ # pylint: disable=invalid-name - url(r'^(?P[0-9]+)/referral/$', - views.ReferralFollowupCreate.as_view(), - name='new-referral-followup'), - url(r'^(?P[0-9]+)/(?P[\w]+)/$', - views.FollowupCreate.as_view(), - name='new-followup'), - url(r'^(?P[0-9]+)/$', - views.followup_choice, - name='followup-choice'), - url(r'^referral/(?P[0-9]+)/$', - views.ReferralFollowupUpdate.as_view(), - {"model": "Referral"}, - name="followup"), # parameter 'model' to identify from others w/ name - url(r'^lab/(?P[0-9]+)/$', - views.LabFollowupUpdate.as_view(), - {"model": "Lab"}, - name="followup"), - url(r'^vaccine/(?P[0-9]+)/$', - views.VaccineFollowupUpdate.as_view(), - {"model": "Vaccine"}, - name="followup"), - url(r'^general/(?P[0-9]+)/$', - views.GeneralFollowupUpdate.as_view(), - {"model": "General"}, - name="followup"), + path('/referral/', + views.ReferralFollowupCreate.as_view(), + name='new-referral-followup'), + path('//', + views.FollowupCreate.as_view(), + name='new-followup'), + path('/', + views.followup_choice, + name='followup-choice'), + path('referral//', + views.ReferralFollowupUpdate.as_view(), + {"model": "Referral"}, + name="followup"), # parameter 'model' to identify from others w/ name + path('lab//', + views.LabFollowupUpdate.as_view(), + {"model": "Lab"}, + name="followup"), + path('vaccine//', + views.VaccineFollowupUpdate.as_view(), + {"model": "Vaccine"}, + name="followup"), + path('general//', + views.GeneralFollowupUpdate.as_view(), + {"model": "General"}, + name="followup"), ] wrap_config = {} -urlpatterns = [wrap_url(url, **wrap_config) for url in unwrapped_urlconf] \ No newline at end of file +urlpatterns = [wrap_url(u, **wrap_config) for u in unwrapped_urlconf] diff --git a/followup/views.py b/followup/views.py index a18d27a..4998cfe 100644 --- a/followup/views.py +++ b/followup/views.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from django.shortcuts import get_object_or_404, render -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponseRedirect from pttrack.models import Patient diff --git a/osler/base_settings.py b/osler/base_settings.py index c5d08e2..724bed3 100644 --- a/osler/base_settings.py +++ b/osler/base_settings.py @@ -33,7 +33,6 @@ 'api', 'crispy_forms', 'bootstrap3', - 'bootstrap3_datetime', 'simple_history', 'rest_framework', 'audit', @@ -44,7 +43,6 @@ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', diff --git a/osler/urls.py b/osler/urls.py index ef34bbb..3c86616 100644 --- a/osler/urls.py +++ b/osler/urls.py @@ -1,31 +1,34 @@ from __future__ import unicode_literals -from django.conf.urls import include, url + from django.contrib import admin + +# from django.conf.urls import include +from django.urls import path, include from django.views.generic.base import RedirectView from django.conf.urls.static import static from django.conf import settings urlpatterns = [ - url(r'^pttrack/', include('pttrack.urls')), - url(r'^accounts/', include('pttrack.auth_urls')), - url(r'^followup/', include('followup.urls')), - url(r'^workup/', include('workup.urls')), - url(r'^dashboard/', include('dashboard.urls')), - url(r'^demographics/', include('demographics.urls')), - url(r'^appointment/', include('appointment.urls')), - url(r'^admin/', include(admin.site.urls)), - url(r'^accounts/', include('django.contrib.auth.urls')), - url(r'^api/', include('api.urls')), - url(r'^referral/', include('referral.urls')), - url(r'^$', - RedirectView.as_view(pattern_name="dashboard-dispatch", - permanent=False), - name='root'), + path('pttrack/', include('pttrack.urls')), + path('accounts/', include('pttrack.auth_urls')), + path('followup/', include('followup.urls')), + path('workup/', include('workup.urls')), + path('dashboard/', include('dashboard.urls')), + path('demographics/', include('demographics.urls')), + path('appointment/', include('appointment.urls')), + path('admin/', admin.site.urls), + path('accounts/', include('django.contrib.auth.urls')), + path('api/', include('api.urls')), + path('referral/', include('referral.urls')), + path('', + RedirectView.as_view(pattern_name="dashboard-dispatch", + permanent=False), + name='root'), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: import debug_toolbar urlpatterns = [ - url(r'^__debug__/', include(debug_toolbar.urls)), + path('__debug__/', include(debug_toolbar.urls)), ] + urlpatterns diff --git a/pttrack/auth_urls.py b/pttrack/auth_urls.py index 3387b04..b50b40b 100644 --- a/pttrack/auth_urls.py +++ b/pttrack/auth_urls.py @@ -1,13 +1,13 @@ from __future__ import unicode_literals -from django.conf.urls import url -from django.contrib.auth.views import login +from django.urls import path +from django.contrib.auth.views import LoginView from . import forms urlpatterns = [ - url(r'^login/$', login, { - 'template_name': 'registration/login.html', - 'authentication_form': forms.CrispyAuthenticationForm - }), - ] + path('login/', LoginView.as_view( + template_name='registration/login.html', + authentication_form=forms.CrispyAuthenticationForm + )), +] diff --git a/pttrack/decorators.py b/pttrack/decorators.py index 3c75e6d..9b79926 100644 --- a/pttrack/decorators.py +++ b/pttrack/decorators.py @@ -1,11 +1,11 @@ from __future__ import unicode_literals from functools import wraps +from urllib.parse import urlparse + from django.contrib.auth.decorators import user_passes_test from django.contrib.auth import REDIRECT_FIELD_NAME -from django.core.urlresolvers import reverse_lazy +from django.urls import reverse_lazy from django.shortcuts import resolve_url -from django.utils.decorators import available_attrs -from django.utils.six.moves.urllib.parse import urlparse def provider_exists(user): @@ -32,7 +32,7 @@ def session_passes_test(test_func, fail_url, """ def decorator(view_func): - @wraps(view_func, assigned=available_attrs(view_func)) + @wraps(view_func) def _wrapped_view(request, *args, **kwargs): if test_func(request.session): return view_func(request, *args, **kwargs) diff --git a/pttrack/forms.py b/pttrack/forms.py index c54c4ad..1b95012 100644 --- a/pttrack/forms.py +++ b/pttrack/forms.py @@ -4,7 +4,7 @@ from builtins import str from builtins import range from builtins import object -from bootstrap3_datetime.widgets import DateTimePicker + from django.forms import (Form, CharField, ModelForm, EmailField, CheckboxSelectMultiple, ModelMultipleChoiceField) from django.contrib.auth.forms import AuthenticationForm @@ -95,7 +95,7 @@ class Meta(object): model = models.ActionItem exclude = ['completion_date', 'author', 'written_date', 'patient', 'completion_author', 'author_type'] - widgets = {'due_date': DateTimePicker(options={"format": "MM/DD/YYYY"})} + # widgets = {'due_date': DateTimePicker(options={"format": "MM/DD/YYYY"})} def __init__(self, *args, **kwargs): diff --git a/pttrack/migrations/0001_initial.py b/pttrack/migrations/0001_initial.py new file mode 100644 index 0000000..7ef7a72 --- /dev/null +++ b/pttrack/migrations/0001_initial.py @@ -0,0 +1,308 @@ +# Generated by Django 2.2.12 on 2020-05-05 15:32 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import pttrack.models +import pttrack.validators +import simple_history.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='ActionInstruction', + fields=[ + ('instruction', models.CharField(max_length=50, primary_key=True, serialize=False)), + ('active', models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name='ContactMethod', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='DocumentType', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='Ethnicity', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + options={ + 'verbose_name_plural': 'ethnicities', + }, + ), + migrations.CreateModel( + name='Gender', + fields=[ + ('long_name', models.CharField(max_length=30, primary_key=True, serialize=False)), + ('short_name', models.CharField(max_length=1)), + ], + ), + migrations.CreateModel( + name='Language', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='Outcome', + fields=[ + ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='ProviderType', + fields=[ + ('long_name', models.CharField(max_length=100)), + ('short_name', models.CharField(max_length=30, primary_key=True, serialize=False)), + ('signs_charts', models.BooleanField(default=False)), + ('staff_view', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='ReferralType', + fields=[ + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), + ('is_fqhc', models.BooleanField(default=False)), + ('is_active', models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name='ReferralLocation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=300)), + ('address', models.TextField()), + ('care_availiable', models.ManyToManyField(to='pttrack.ReferralType')), + ], + ), + migrations.CreateModel( + name='Provider', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), + ('phone', models.CharField(blank=True, max_length=40, null=True)), + ('needs_updating', models.BooleanField(default=False)), + ('associated_user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('clinical_roles', models.ManyToManyField(to='pttrack.ProviderType')), + ('gender', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Gender')), + ('languages', models.ManyToManyField(help_text='Specify here languages that are spoken at a level sufficient to be used for medical communication.', to='pttrack.Language')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Patient', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), + ('phone', models.CharField(blank=True, max_length=40, null=True)), + ('address', models.CharField(max_length=200)), + ('city', models.CharField(default='St. Louis', max_length=50)), + ('state', models.CharField(default='MO', max_length=2)), + ('zip_code', models.CharField(max_length=5, validators=[pttrack.validators.validate_zip])), + ('country', models.CharField(default='USA', max_length=100)), + ('pcp_preferred_zip', models.CharField(blank=True, max_length=5, null=True, validators=[pttrack.validators.validate_zip])), + ('date_of_birth', models.DateField(help_text='MM/DD/YYYY', validators=[pttrack.validators.validate_birth_date])), + ('patient_comfortable_with_english', models.BooleanField(default=True)), + ('alternate_phone_1_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_1', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_2_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_2', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_3_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_3', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_4_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_4', models.CharField(blank=True, max_length=40, null=True)), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('needs_workup', models.BooleanField(default=True)), + ('case_managers', models.ManyToManyField(to='pttrack.Provider')), + ('ethnicities', models.ManyToManyField(to='pttrack.Ethnicity')), + ('gender', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Gender')), + ('languages', models.ManyToManyField(help_text='Specify here languages that are spoken at a level sufficient to be used for medical communication.', to='pttrack.Language')), + ('outcome', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='pttrack.Outcome')), + ('preferred_contact_method', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='pttrack.ContactMethod')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HistoricalProvider', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), + ('phone', models.CharField(blank=True, max_length=40, null=True)), + ('needs_updating', models.BooleanField(default=False)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('associated_user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ('gender', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Gender')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'historical provider', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalPatient', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), + ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), + ('phone', models.CharField(blank=True, max_length=40, null=True)), + ('address', models.CharField(max_length=200)), + ('city', models.CharField(default='St. Louis', max_length=50)), + ('state', models.CharField(default='MO', max_length=2)), + ('zip_code', models.CharField(max_length=5, validators=[pttrack.validators.validate_zip])), + ('country', models.CharField(default='USA', max_length=100)), + ('pcp_preferred_zip', models.CharField(blank=True, max_length=5, null=True, validators=[pttrack.validators.validate_zip])), + ('date_of_birth', models.DateField(help_text='MM/DD/YYYY', validators=[pttrack.validators.validate_birth_date])), + ('patient_comfortable_with_english', models.BooleanField(default=True)), + ('alternate_phone_1_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_1', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_2_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_2', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_3_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_3', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_4_owner', models.CharField(blank=True, max_length=40, null=True)), + ('alternate_phone_4', models.CharField(blank=True, max_length=40, null=True)), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('needs_workup', models.BooleanField(default=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('gender', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Gender')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('outcome', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Outcome')), + ('preferred_contact_method', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ContactMethod')), + ], + options={ + 'verbose_name': 'historical patient', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalDocument', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('title', models.CharField(max_length=200)), + ('image', models.TextField(help_text='Please deidentify all file names before upload! Delete all files after upload!', max_length=100, verbose_name='PDF File or Image Upload')), + ('comments', models.TextField()), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('document_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.DocumentType')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), + ], + options={ + 'verbose_name': 'historical document', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalActionItem', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('completion_date', models.DateTimeField(blank=True, null=True)), + ('due_date', models.DateField(help_text='MM/DD/YYYY')), + ('priority', models.BooleanField(default=False, help_text='Check this box if this action item is high priority')), + ('comments', models.TextField()), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('completion_author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('instruction', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ActionInstruction')), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), + ], + options={ + 'verbose_name': 'historical action item', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='Document', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ('title', models.CharField(max_length=200)), + ('image', models.FileField(help_text='Please deidentify all file names before upload! Delete all files after upload!', upload_to=pttrack.models.make_filepath, verbose_name='PDF File or Image Upload')), + ('comments', models.TextField()), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('document_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.DocumentType')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ], + options={ + 'ordering': ['-written_datetime', '-last_modified'], + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ActionItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ('completion_date', models.DateTimeField(blank=True, null=True)), + ('due_date', models.DateField(help_text='MM/DD/YYYY')), + ('priority', models.BooleanField(default=False, help_text='Check this box if this action item is high priority')), + ('comments', models.TextField()), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('completion_author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pttrack_actionitem_completed', to='pttrack.Provider')), + ('instruction', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ActionInstruction')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ], + options={ + 'ordering': ['-written_datetime', '-last_modified'], + 'abstract': False, + }, + ), + ] diff --git a/pttrack/migrations/0001_squashed_0010_auto_20170623_1300.py b/pttrack/migrations/0001_squashed_0010_auto_20170623_1300.py deleted file mode 100644 index 2824da2..0000000 --- a/pttrack/migrations/0001_squashed_0010_auto_20170623_1300.py +++ /dev/null @@ -1,464 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - - -from django.db import migrations, models -import pttrack.validators -import pttrack.models -import django.db.models.deletion -from django.conf import settings - - -class Migration(migrations.Migration): - - replaces = [ - ('pttrack', '0001_initial'), - ('pttrack', '0002_providertype_is_staff'), - ('pttrack', '0003_auto_20160119_1459'), - ('pttrack', '0004_auto_20160328_1425'), - ('pttrack', '0005_auto_20160628_1852'), - ('pttrack', '0006_rm_ssn'), - ('pttrack', '0007_needs_workup_default_true'), - ('pttrack', '0008_add_case_manager'), - ('pttrack', '0009_auto_20170502_1103'), - ('pttrack', '0010_auto_20170623_1300')] - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='ActionInstruction', - fields=[ - ('instruction', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='ActionItem', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('written_datetime', models.DateTimeField(auto_now_add=True)), - ('last_modified', models.DateTimeField(auto_now=True)), - ('due_date', models.DateField(help_text=b'MM/DD/YYYY or YYYY-MM-DD')), - ('comments', models.CharField(max_length=300)), - ('completion_date', models.DateTimeField(null=True, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='ContactMethod', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='Document', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('written_datetime', models.DateTimeField(auto_now_add=True)), - ('last_modified', models.DateTimeField(auto_now=True)), - ('title', models.CharField(max_length=200)), - ('image', models.FileField(help_text=b'Please deidentify all file names before upload! Delete all files after upload!', upload_to=pttrack.models.make_filepath, verbose_name=b'PDF File or Image Upload')), - ('comments', models.TextField()), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='DocumentType', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='Ethnicity', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='Gender', - fields=[ - ('long_name', models.CharField(max_length=30, serialize=False, primary_key=True)), - ('short_name', models.CharField(max_length=1)), - ], - ), - migrations.CreateModel( - name='HistoricalActionItem', - fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('due_date', models.DateField(help_text=b'MM/DD/YYYY or YYYY-MM-DD')), - ('comments', models.CharField(max_length=300)), - ('completion_date', models.DateTimeField(null=True, blank=True)), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ], - options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical action item', - }, - ), - migrations.CreateModel( - name='HistoricalDocument', - fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('title', models.CharField(max_length=200)), - ('image', models.TextField(help_text=b'Please deidentify all file names before upload! Delete all files after upload!', max_length=100, verbose_name=b'PDF File or Image Upload')), - ('comments', models.TextField()), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ], - options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical document', - }, - ), - migrations.CreateModel( - name='HistoricalPatient', - fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), - ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), - ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), - ('phone', models.CharField(max_length=40, null=True, blank=True)), - ('address', models.CharField(max_length=200)), - ('city', models.CharField(default=b'St. Louis', max_length=50)), - ('state', models.CharField(default=b'MO', max_length=2)), - ('zip_code', models.CharField(max_length=5, validators=[pttrack.validators.validate_zip])), - ('country', models.CharField(default=b'USA', max_length=100)), - ('pcp_preferred_zip', models.CharField(blank=True, max_length=5, null=True, validators=[pttrack.validators.validate_zip])), - ('date_of_birth', models.DateField(validators=[pttrack.validators.validate_birth_date])), - ('patient_comfortable_with_english', models.BooleanField(default=True)), - ('alternate_phone_1_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_1', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_2_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_2', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_3_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_3', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_4_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_4', models.CharField(max_length=40, null=True, blank=True)), - ('needs_workup', models.BooleanField(default=True)), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('gender', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Gender', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('preferred_contact_method', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ContactMethod', null=True)), - ('email', models.EmailField(max_length=254, null=True, blank=True)), - ], - options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical patient', - }, - ), - migrations.CreateModel( - name='HistoricalProvider', - fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), - ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), - ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), - ('phone', models.CharField(max_length=40, null=True, blank=True)), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('associated_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to=settings.AUTH_USER_MODEL, null=True)), - ('gender', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Gender', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('needs_updating', models.BooleanField(default=False)), - ], - options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical provider', - }, - ), - migrations.CreateModel( - name='Language', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='Patient', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('first_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), - ('last_name', models.CharField(max_length=100, validators=[pttrack.validators.validate_name])), - ('middle_name', models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name])), - ('phone', models.CharField(max_length=40, null=True, blank=True)), - ('address', models.CharField(max_length=200)), - ('city', models.CharField(default=b'St. Louis', max_length=50)), - ('state', models.CharField(default=b'MO', max_length=2)), - ('zip_code', models.CharField(max_length=5, validators=[pttrack.validators.validate_zip])), - ('country', models.CharField(default=b'USA', max_length=100)), - ('pcp_preferred_zip', models.CharField(blank=True, max_length=5, null=True, validators=[pttrack.validators.validate_zip])), - ('date_of_birth', models.DateField(validators=[pttrack.validators.validate_birth_date])), - ('patient_comfortable_with_english', models.BooleanField(default=True)), - ('alternate_phone_1_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_1', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_2_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_2', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_3_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_3', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_4_owner', models.CharField(max_length=40, null=True, blank=True)), - ('alternate_phone_4', models.CharField(max_length=40, null=True, blank=True)), - ('needs_workup', models.BooleanField(default=True)), - ('ethnicities', models.ManyToManyField(to='pttrack.Ethnicity')), - ('gender', models.ForeignKey(to='pttrack.Gender')), - ('languages', models.ManyToManyField(help_text=b'Specify here languages that are spoken at a level sufficient to be used for medical communication.', to='pttrack.Language')), - ('preferred_contact_method', models.ForeignKey(blank=True, to='pttrack.ContactMethod', null=True)), - ('email', models.EmailField(max_length=254, null=True, blank=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='Provider', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('first_name', models.CharField(max_length=100)), - ('last_name', models.CharField(max_length=100)), - ('middle_name', models.CharField(max_length=100, blank=True)), - ('phone', models.CharField(max_length=40)), - ('associated_user', models.OneToOneField(null=True, blank=True, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='ProviderType', - fields=[ - ('long_name', models.CharField(max_length=100)), - ('short_name', models.CharField(max_length=30, serialize=False, primary_key=True)), - ('signs_charts', models.BooleanField(default=False)), - ('staff_view', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='ReferralLocation', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=300)), - ('address', models.TextField()), - ], - ), - migrations.CreateModel( - name='ReferralType', - fields=[ - ('name', models.CharField(max_length=100, serialize=False, primary_key=True)), - ], - ), - migrations.AddField( - model_name='provider', - name='clinical_roles', - field=models.ManyToManyField(to='pttrack.ProviderType'), - ), - migrations.AddField( - model_name='provider', - name='gender', - field=models.ForeignKey(to='pttrack.Gender'), - ), - migrations.AddField( - model_name='provider', - name='languages', - field=models.ManyToManyField(help_text=b'Specify here languages that are spoken at a level sufficient to be used for medical communication.', to='pttrack.Language'), - ), - migrations.AddField( - model_name='historicaldocument', - name='author', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True), - ), - migrations.AddField( - model_name='historicaldocument', - name='author_type', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True), - ), - migrations.AddField( - model_name='historicaldocument', - name='document_type', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.DocumentType', null=True), - ), - migrations.AddField( - model_name='historicaldocument', - name='history_user', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True), - ), - migrations.AddField( - model_name='historicaldocument', - name='patient', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True), - ), - migrations.AddField( - model_name='historicalactionitem', - name='author', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True), - ), - migrations.AddField( - model_name='historicalactionitem', - name='author_type', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True), - ), - migrations.AddField( - model_name='historicalactionitem', - name='completion_author', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True), - ), - migrations.AddField( - model_name='historicalactionitem', - name='history_user', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True), - ), - migrations.AddField( - model_name='historicalactionitem', - name='instruction', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ActionInstruction', null=True), - ), - migrations.AddField( - model_name='historicalactionitem', - name='patient', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True), - ), - migrations.AddField( - model_name='document', - name='author', - field=models.ForeignKey(to='pttrack.Provider'), - ), - migrations.AddField( - model_name='document', - name='author_type', - field=models.ForeignKey(to='pttrack.ProviderType'), - ), - migrations.AddField( - model_name='document', - name='document_type', - field=models.ForeignKey(to='pttrack.DocumentType'), - ), - migrations.AddField( - model_name='document', - name='patient', - field=models.ForeignKey(to='pttrack.Patient'), - ), - migrations.AddField( - model_name='actionitem', - name='author', - field=models.ForeignKey(to='pttrack.Provider'), - ), - migrations.AddField( - model_name='actionitem', - name='author_type', - field=models.ForeignKey(to='pttrack.ProviderType'), - ), - migrations.AddField( - model_name='actionitem', - name='completion_author', - field=models.ForeignKey(related_name='action_items_completed', blank=True, to='pttrack.Provider', null=True), - ), - migrations.AddField( - model_name='actionitem', - name='instruction', - field=models.ForeignKey(to='pttrack.ActionInstruction'), - ), - migrations.AddField( - model_name='actionitem', - name='patient', - field=models.ForeignKey(to='pttrack.Patient'), - ), - migrations.AlterField( - model_name='actionitem', - name='comments', - field=models.TextField(max_length=300), - ), - migrations.AlterField( - model_name='historicalactionitem', - name='comments', - field=models.TextField(max_length=300), - ), - migrations.AlterField( - model_name='actionitem', - name='comments', - field=models.TextField(), - ), - migrations.AlterField( - model_name='historicalactionitem', - name='comments', - field=models.TextField(), - ), - migrations.AlterField( - model_name='provider', - name='phone', - field=models.CharField(max_length=40, null=True, blank=True), - ), - migrations.AddField( - model_name='provider', - name='needs_updating', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='provider', - name='first_name', - field=models.CharField(max_length=100, validators=[pttrack.validators.validate_name]), - ), - migrations.AlterField( - model_name='provider', - name='last_name', - field=models.CharField(max_length=100, validators=[pttrack.validators.validate_name]), - ), - migrations.AlterField( - model_name='provider', - name='middle_name', - field=models.CharField(blank=True, max_length=100, validators=[pttrack.validators.validate_name]), - ), - migrations.AddField( - model_name='historicalpatient', - name='case_manager', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True), - ), - migrations.AddField( - model_name='patient', - name='case_manager', - field=models.ForeignKey(blank=True, to='pttrack.Provider', null=True), - ), - migrations.AlterField( - model_name='historicalpatient', - name='date_of_birth', - field=models.DateField(help_text=b'MM/DD/YYYY', validators=[pttrack.validators.validate_birth_date]), - ), - migrations.AlterField( - model_name='patient', - name='date_of_birth', - field=models.DateField(help_text=b'MM/DD/YYYY', validators=[pttrack.validators.validate_birth_date]), - ), - migrations.CreateModel( - name='Outcome', - fields=[ - ('name', models.CharField(max_length=50, serialize=False, primary_key=True)), - ], - ), - migrations.AddField( - model_name='historicalpatient', - name='outcome', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Outcome', null=True), - ), - migrations.AddField( - model_name='patient', - name='outcome', - field=models.ForeignKey(default='NA', blank=True, to='pttrack.Outcome', null=True), - preserve_default=False, - ), - ] diff --git a/pttrack/migrations/0002_auto_20171007_1533.py b/pttrack/migrations/0002_auto_20171007_1533.py deleted file mode 100644 index f6c20dd..0000000 --- a/pttrack/migrations/0002_auto_20171007_1533.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0001_squashed_0010_auto_20170623_1300'), - ] - - operations = [ - migrations.AddField( - model_name='actionitem', - name='priority', - field=models.BooleanField(default=False, help_text=b'Check this box if this action item is high priority'), - ), - migrations.AddField( - model_name='historicalactionitem', - name='priority', - field=models.BooleanField(default=False, help_text=b'Check this box if this action item is high priority'), - ), - ] diff --git a/pttrack/migrations/0003_auto_20171014_1843.py b/pttrack/migrations/0003_auto_20171014_1843.py deleted file mode 100644 index 2a21a2c..0000000 --- a/pttrack/migrations/0003_auto_20171014_1843.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0002_auto_20171007_1533'), - ] - - operations = [ - migrations.AddField( - model_name='historicalpatient', - name='case_manager_2', - field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True), - ), - migrations.AddField( - model_name='patient', - name='case_manager_2', - field=models.ForeignKey(related_name='secondary_case_manager', blank=True, to='pttrack.Provider', null=True), - ), - ] diff --git a/pttrack/migrations/0004_auto_20171016_1646.py b/pttrack/migrations/0004_auto_20171016_1646.py deleted file mode 100644 index b662fe9..0000000 --- a/pttrack/migrations/0004_auto_20171016_1646.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -def move_case_manager(apps, schema_editor): - Patient = apps.get_model('pttrack','Patient') - for patient_person in Patient.objects.all(): - try: - if patient_person.case_manager != None: - patient_person.case_managers.add(patient_person.case_manager) - if patient_person.case_manager_2 != None: - patient_person.case_managers.add(patient_person.case_manager_2) - except AttributeError: - pass - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0003_auto_20171014_1843'), - ] - - operations = [ - migrations.AddField( - model_name='patient', - name='case_managers', - field=models.ManyToManyField(to='pttrack.Provider'), - ), - migrations.RunPython(move_case_manager), - migrations.RemoveField( - model_name='historicalpatient', - name='case_manager', - ), - migrations.RemoveField( - model_name='historicalpatient', - name='case_manager_2', - ), - migrations.RemoveField( - model_name='patient', - name='case_manager_2', - ), - migrations.RemoveField( - model_name='patient', - name='case_manager', - ), - ] diff --git a/pttrack/migrations/0005_simplehistory_add_change_reason.py b/pttrack/migrations/0005_simplehistory_add_change_reason.py deleted file mode 100644 index 2e28a92..0000000 --- a/pttrack/migrations/0005_simplehistory_add_change_reason.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2018-07-09 01:07 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0004_auto_20171016_1646'), - ] - - operations = [ - migrations.AddField( - model_name='historicalactionitem', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicaldocument', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicalpatient', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicalprovider', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - ] diff --git a/pttrack/migrations/0006_referral_additional_fields_20180826.py b/pttrack/migrations/0006_referral_additional_fields_20180826.py deleted file mode 100644 index 79a0f2f..0000000 --- a/pttrack/migrations/0006_referral_additional_fields_20180826.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2018-08-27 00:31 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0005_simplehistory_add_change_reason'), - ] - - operations = [ - migrations.AddField( - model_name='referrallocation', - name='care_availiable', - field=models.ManyToManyField(to='pttrack.ReferralType'), - ), - migrations.AddField( - model_name='referraltype', - name='is_fqhc', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='actionitem', - name='completion_author', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pttrack_actionitem_completed', to='pttrack.Provider'), - ), - ] diff --git a/pttrack/migrations/0007_referraltype_is_active.py b/pttrack/migrations/0007_referraltype_is_active.py deleted file mode 100644 index 6b86acf..0000000 --- a/pttrack/migrations/0007_referraltype_is_active.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.3 on 2018-12-01 20:26 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0006_referral_additional_fields_20180826'), - ] - - operations = [ - migrations.AddField( - model_name='referraltype', - name='is_active', - field=models.BooleanField(default=True), - ), - ] diff --git a/pttrack/migrations/0008_actioninstruction_active.py b/pttrack/migrations/0008_actioninstruction_active.py deleted file mode 100644 index d648b13..0000000 --- a/pttrack/migrations/0008_actioninstruction_active.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.20 on 2019-08-14 01:52 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0007_referraltype_is_active'), - ] - - operations = [ - migrations.AddField( - model_name='actioninstruction', - name='active', - field=models.BooleanField(default=True), - ), - ] diff --git a/pttrack/migrations/0009_set_orderings_20190902_2116.py b/pttrack/migrations/0009_set_orderings_20190902_2116.py deleted file mode 100644 index 10fdd1b..0000000 --- a/pttrack/migrations/0009_set_orderings_20190902_2116.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:16 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0008_actioninstruction_active'), - ] - - operations = [ - migrations.AlterModelOptions( - name='actionitem', - options={'ordering': ['-written_datetime', '-last_modified']}, - ), - migrations.AlterModelOptions( - name='document', - options={'ordering': ['-written_datetime', '-last_modified']}, - ), - migrations.AlterModelOptions( - name='ethnicity', - options={'verbose_name_plural': 'ethnicities'}, - ), - ] diff --git a/pttrack/models.py b/pttrack/models.py index d7c37c7..6a1f677 100644 --- a/pttrack/models.py +++ b/pttrack/models.py @@ -1,5 +1,7 @@ '''The datamodels for the Osler core''' from __future__ import unicode_literals +import os + from builtins import str from builtins import range from builtins import object @@ -10,8 +12,7 @@ from django.conf import settings from django.utils.timezone import now from django.utils.text import slugify -import os -from django.core.urlresolvers import reverse +from django.urls import reverse from simple_history.models import HistoricalRecords @@ -149,14 +150,17 @@ class Meta(object): max_length=100, blank=True, validators=[validators.validate_name]) phone = models.CharField(max_length=40, null=True, blank=True) - languages = models.ManyToManyField(Language, help_text="Specify here languages that are spoken at a level sufficient to be used for medical communication.") + languages = models.ManyToManyField( + Language, help_text="Specify here languages that are spoken at a " + "level sufficient to be used for medical " + "communication.") - gender = models.ForeignKey(Gender) + gender = models.ForeignKey(Gender, on_delete=models.PROTECT) def name(self, reverse=True, middle_short=True): if self.middle_name: if middle_short: - middle = "".join([mname[0]+"." for mname + middle = "".join([mname[0] + "." for mname in self.middle_name.split()]) else: middle = self.middle_name @@ -175,8 +179,10 @@ def name(self, reverse=True, middle_short=True): class Provider(Person): - associated_user = models.OneToOneField(settings.AUTH_USER_MODEL, - blank=True, null=True) + associated_user = models.OneToOneField( + settings.AUTH_USER_MODEL, + blank=True, null=True, + on_delete=models.CASCADE) clinical_roles = models.ManyToManyField(ProviderType) @@ -196,7 +202,8 @@ class Patient(Person): case_managers = models.ManyToManyField(Provider) - outcome = models.ForeignKey(Outcome, null=True, blank=True) + outcome = models.ForeignKey(Outcome, null=True, blank=True, + on_delete=models.PROTECT) address = models.CharField(max_length=200) @@ -209,13 +216,13 @@ class Patient(Person): country = models.CharField(max_length=100, default="USA") - pcp_preferred_zip = models.CharField(max_length=5, validators=[validators.validate_zip], blank=True, null=True) - date_of_birth = models.DateField(help_text='MM/DD/YYYY', + date_of_birth = models.DateField( + help_text='MM/DD/YYYY', validators=[validators.validate_birth_date]) patient_comfortable_with_english = models.BooleanField(default=True) @@ -240,7 +247,8 @@ class Patient(Person): alternate_phone_4_owner = models.CharField(max_length=40, blank=True, null=True) alternate_phone_4 = models.CharField(max_length=40, blank=True, null=True) - preferred_contact_method = models.ForeignKey(ContactMethod, blank=True, null=True) + preferred_contact_method = models.ForeignKey( + ContactMethod, blank=True, null=True, on_delete=models.PROTECT) email = models.EmailField(blank=True, null=True) @@ -252,7 +260,7 @@ class Patient(Person): history = HistoricalRecords() def age(self): - return (now().date() - self.date_of_birth).days//365 + return (now().date() - self.date_of_birth).days // 365 def __str__(self): return self.name() @@ -262,8 +270,8 @@ def active_action_items(self): 2) due today or before. The list is sorted by due_date''' return sorted( - ActionItem.objects.filter(patient=self.pk)\ - .filter(completion_author=None)\ + ActionItem.objects.filter(patient=self.pk) \ + .filter(completion_author=None) \ .filter(due_date__lte=now().date()), key=lambda ai: ai.due_date) @@ -389,9 +397,9 @@ class Meta(object): abstract = True ordering = ["-written_datetime", "-last_modified"] - author = models.ForeignKey(Provider) - author_type = models.ForeignKey(ProviderType) - patient = models.ForeignKey(Patient) + author = models.ForeignKey(Provider, on_delete=models.PROTECT) + author_type = models.ForeignKey(ProviderType, on_delete=models.PROTECT) + patient = models.ForeignKey(Patient, on_delete=models.PROTECT) written_datetime = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) @@ -411,7 +419,7 @@ class Document(Note): upload_to=make_filepath, verbose_name="PDF File or Image Upload") comments = models.TextField() - document_type = models.ForeignKey(DocumentType) + document_type = models.ForeignKey(DocumentType, on_delete=models.PROTECT) history = HistoricalRecords() @@ -461,7 +469,8 @@ class Meta(object): completion_author = models.ForeignKey( Provider, blank=True, null=True, - related_name="%(app_label)s_%(class)s_completed") + related_name="%(app_label)s_%(class)s_completed", + on_delete=models.PROTECT) due_date = models.DateField(help_text="MM/DD/YYYY") def done(self): @@ -498,7 +507,8 @@ def summary(self): class ActionItem(Note, CompletableMixin): - instruction = models.ForeignKey(ActionInstruction) + instruction = models.ForeignKey(ActionInstruction, + on_delete=models.PROTECT) priority = models.BooleanField( default=False, help_text='Check this box if this action item is high priority') diff --git a/pttrack/test_forms.py b/pttrack/test_forms.py index d50fb40..4e3b81b 100644 --- a/pttrack/test_forms.py +++ b/pttrack/test_forms.py @@ -9,14 +9,15 @@ Provider, ActionInstruction, Patient from . import forms + class TestActionItemCreateForms(TestCase): '''Tests for form used to create new Action Items''' def setUp(self): ai_data = { - 'author': Provider.objects.first(), - 'author_type': ProviderType.objects.first(), - 'patient': Patient.objects.first() + 'author': Provider.objects.first(), + 'author_type': ProviderType.objects.first(), + 'patient': Patient.objects.first() } self.ai_data = ai_data @@ -27,14 +28,14 @@ def test_instruction_inactive(self): ai_data = self.ai_data PCP_followup = ActionInstruction.objects.create( - instruction='PCP Followup',active=False) + instruction='PCP Followup', active=False) Lab_Followup = ActionInstruction.objects.create( - instruction='Lab Followup',active=True) + instruction='Lab Followup', active=True) Vaccine_Followup = ActionInstruction.objects.create( - instruction='Vaccine Followup',active=True) + instruction='Vaccine Followup', active=True) ai_qs = ActionInstruction.objects.filter( - active=True).values_list('instruction',flat=True) + active=True).values_list('instruction', flat=True) form = forms.ActionItemForm(data=ai_data) @@ -42,15 +43,15 @@ def test_instruction_inactive(self): self.assertEqual(set(ai_qs), set(form_list)) - #Accept active Action Instructions + # Accept active Action Instructions ai_data['instruction'] = Lab_Followup.pk form = forms.ActionItemForm(data=ai_data) - self.assertEqual(form['instruction'].errors, []) + self.assertEqual(len(form['instruction'].errors), 0) - #Reject inactive Action Instructions + # Reject inactive Action Instructions ai_data['instruction'] = PCP_followup.pk form = forms.ActionItemForm(data=ai_data) - self.assertNotEqual(form['instruction'].errors, []) + self.assertNotEqual(len(form['instruction'].errors), 0) class TestPatientCreateForms(TestCase): @@ -72,7 +73,7 @@ def setUp(self): 'country': 'Germany', 'zip_code': '63108', 'pcp_preferred_zip': '63018', - 'date_of_birth': datetime.date(1990, 0o1, 0o1), + 'date_of_birth': datetime.date(1990, 1, 1), 'patient_comfortable_with_english': False, 'ethnicities': [Ethnicity.objects.create(name="Klingon")], 'preferred_contact_method': @@ -122,26 +123,26 @@ def test_form_casemanager_options(self): # Make sure we reject non-case manager providers form_data = self.valid_pt_dict.copy() - form_data['case_managers'] = [pvds[0].pk] + form_data['case_managers'] = [pvds[0]] form = forms.PatientForm(data=form_data) - self.assertNotEqual(form['case_managers'].errors, []) + self.assertNotEqual(len(form['case_managers'].errors), 0) form_data = self.valid_pt_dict.copy() form_data['case_managers'] = [pvds[1].pk] form = forms.PatientForm(data=form_data) - self.assertNotEqual(form['case_managers'].errors, []) + self.assertNotEqual(len(form['case_managers'].errors), 0) # Make sure we accept case manager providers form_data = self.valid_pt_dict.copy() form_data['case_managers'] = [pvds[2].pk] form = forms.PatientForm(data=form_data) - self.assertEqual(form['case_managers'].errors, []) + self.assertEqual(len(form['case_managers'].errors), 0) form_data = self.valid_pt_dict.copy() form_data['case_managers'] = [pvds[3].pk] form = forms.PatientForm(data=form_data) - self.assertEqual(form['case_managers'].errors, []) + self.assertEqual(len(form['case_managers'].errors), 0) def test_missing_alt_phone(self): '''Missing the alternative phone w/o alt phone owner should fail.''' @@ -153,7 +154,7 @@ def test_missing_alt_phone(self): form = forms.PatientForm(data=form_data) # and expect an error to be on the empty altphone field - self.assertNotEqual(form['alternate_phone_1'].errors, []) + self.assertNotEqual(len(form['alternate_phone_1'].errors), 0) def test_missing_alt_phone_owner(self): '''Missing the alt phone owner w/o alt phone should fail.''' @@ -164,4 +165,4 @@ def test_missing_alt_phone_owner(self): form = forms.PatientForm(data=form_data) # we expect errors on the empty alternate_phone_1_owner field - self.assertNotEqual(form['alternate_phone_1_owner'].errors, []) + self.assertNotEqual(len(form['alternate_phone_1_owner'].errors), 0) diff --git a/pttrack/test_utils.py b/pttrack/test_utils.py index c309077..fc2e6ad 100644 --- a/pttrack/test_utils.py +++ b/pttrack/test_utils.py @@ -84,7 +84,7 @@ def create_pts(): 'zip_code': '63108', 'gender': models.Gender(long_name="Male", short_name="m"), 'pcp_preferred_zip': '63018', - 'date_of_birth': datetime.date(1990, 0o1, 0o1), + 'date_of_birth': datetime.date(1990, 1, 1), 'patient_comfortable_with_english': False, 'preferred_contact_method': models.ContactMethod.objects.first(), } @@ -132,6 +132,10 @@ class return_duplicatesTests(TestCase): ben and benjamin) """ + def tearDown(self): + # necessary because of the ForeignKey on_delete=PROTECT directives + models.Patient.objects.all().delete() + def test_empty_first_name(self): """If first and last name is empty string should return None """ diff --git a/pttrack/test_validators.py b/pttrack/test_validators.py index 8b59aeb..a30e170 100644 --- a/pttrack/test_validators.py +++ b/pttrack/test_validators.py @@ -67,4 +67,4 @@ def test_validate_name(self): with self.assertRaises(ValidationError): validators.validate_name(" Name ") # tab then space with self.assertRaises(ValidationError): - validators.validate_name(" Name ") # space then tab \ No newline at end of file + validators.validate_name(" Name ") # space then tab diff --git a/pttrack/test_views.py b/pttrack/test_views.py index 115772d..b7a5bd1 100644 --- a/pttrack/test_views.py +++ b/pttrack/test_views.py @@ -8,7 +8,7 @@ import json from django.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from django.contrib.auth.models import User from django.utils.timezone import now from django.core.files import File @@ -311,7 +311,7 @@ def test_pttrack_view_rendering(self): existance of a jumbotron at the top. ''' from . import urls - from django.core.urlresolvers import NoReverseMatch + from django.urls import NoReverseMatch # build a provider and log in. build_provider(username='timmy', password='password', @@ -411,7 +411,7 @@ def setUp(self): 'state': 'BA', 'zip_code': '63108', 'pcp_preferred_zip': '63018', - 'date_of_birth': datetime.date(1990, 0o1, 0o1), + 'date_of_birth': datetime.date(1990, 1, 1), 'patient_comfortable_with_english': False, 'preferred_contact_method': models.ContactMethod.objects.first(), } @@ -946,7 +946,7 @@ def setUp(self): 'country': 'Germany', 'zip_code': '63108', 'pcp_preferred_zip': '63018', - 'date_of_birth': datetime.date(1990, 0o1, 0o1), + 'date_of_birth': datetime.date(1990, 1, 1), 'patient_comfortable_with_english': False, 'ethnicities': [models.Ethnicity.objects.first()], 'preferred_contact_method': @@ -1219,7 +1219,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) diff --git a/pttrack/urls.py b/pttrack/urls.py index 92bee1e..9f445a9 100644 --- a/pttrack/urls.py +++ b/pttrack/urls.py @@ -1,5 +1,5 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import re_path from django.views.generic import DetailView from django.views.generic.base import TemplateView @@ -12,72 +12,92 @@ # pylint: disable=I0011 unwrapped_urlpatterns = [ # pylint: disable=invalid-name - url(r'^$', + re_path( + r'^$', views.home_page, name="home"), - url(r'^all/$', + re_path( + r'^all/$', views.all_patients, name="all-patients"), - url(r'^preintake-select/$', + re_path( + r'^preintake-select/$', views.PreIntakeSelect.as_view(), name="preintake-select"), - url(r'^preintake/$', + re_path( + r'^preintake/$', views.PreIntake.as_view(), name="preintake"), - url(r'^intake/$', + re_path( + r'^intake/$', views.PatientCreate.as_view(), name="intake"), - url(r'^(?P[0-9]+)/$', + re_path( + r'^(?P[0-9]+)/$', views.patient_detail, name='patient-detail'), - url(r'^patient/update/(?P[0-9]+)$', + re_path( + r'^patient/update/(?P[0-9]+)$', views.PatientUpdate.as_view(), name='patient-update'), - url(r'^patient/activate_detail/(?P[0-9]+)$', + re_path( + r'^patient/activate_detail/(?P[0-9]+)$', views.patient_activate_detail, name='patient-activate-detail'), - url(r'^patient/activate_home/(?P[0-9]+)$', + re_path( + r'^patient/activate_home/(?P[0-9]+)$', views.patient_activate_home, name='patient-activate-home'), # PROVIDERS - url(r'^new-provider/$', + re_path( + r'^new-provider/$', views.ProviderCreate.as_view(), name='new-provider'), - url(r'^choose-role/$', + re_path( + r'^choose-role/$', views.choose_clintype, name='choose-clintype'), - url(r'^provider-update/$', + re_path( + r'^provider-update/$', views.ProviderUpdate.as_view(), name='provider-update'), # ACTION ITEMS - url(r'^(?P[0-9]+)/action-item/$', + re_path( + r'^(?P[0-9]+)/action-item/$', views.ActionItemCreate.as_view(), name='new-action-item'), - url(r'^action-item/(?P[0-9]+)/update$', + re_path( + r'^action-item/(?P[0-9]+)/update$', views.ActionItemUpdate.as_view(), name="update-action-item"), - url(r'^action-item/(?P[0-9]+)/done$', + re_path( + r'^action-item/(?P[0-9]+)/done$', views.done_action_item, name=models.ActionItem.MARK_DONE_URL_NAME), - url(r'^action-item/(?P[0-9]+)/reset$', + re_path( + r'^action-item/(?P[0-9]+)/reset$', views.reset_action_item, name='reset-action-item'), # DOCUMENTS - url(r'^(?P[0-9]+)/document/$', + re_path( + r'^(?P[0-9]+)/document/$', views.DocumentCreate.as_view(), name="new-document"), - url(r'^document/(?P[0-9]+)$', + re_path( + r'^document/(?P[0-9]+)$', DetailView.as_view(model=models.Document), name="document-detail"), - url(r'^document/update/(?P[0-9]+)$', + re_path( + r'^document/update/(?P[0-9]+)$', views.DocumentUpdate.as_view(), name="document-update"), # MISC - url(r'^about/', + re_path( + r'^about/', TemplateView.as_view(template_name='pttrack/about.html'), name='about'), ] diff --git a/pttrack/views.py b/pttrack/views.py index 505c996..d784b4b 100644 --- a/pttrack/views.py +++ b/pttrack/views.py @@ -10,7 +10,7 @@ from django.http import HttpResponseRedirect, HttpResponseServerError from django.views.generic.edit import FormView, UpdateView from django.views.generic.list import ListView -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import ImproperlyConfigured from django.db.models import Prefetch from django.utils.http import is_safe_url @@ -305,7 +305,7 @@ def choose_clintype(request): RADIO_CHOICE_KEY = 'radio-roles' redirect_to = request.GET['next'] - if not is_safe_url(url=redirect_to, host=request.get_host()): + if not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()): redirect_to = reverse("home") if request.POST: diff --git a/referral/forms.py b/referral/forms.py index 13f6b1f..ad0bd89 100644 --- a/referral/forms.py +++ b/referral/forms.py @@ -7,7 +7,6 @@ from . import models from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit -from bootstrap3_datetime.widgets import DateTimePicker class ReferralForm(ModelForm): @@ -27,8 +26,8 @@ class FollowupRequestForm(ModelForm): class Meta(object): model = models.FollowupRequest fields = ['due_date', 'contact_instructions'] - widgets = {'due_date': DateTimePicker( - options={"format": "MM/DD/YYYY"})} + # widgets = {'due_date': DateTimePicker( + # options={"format": "MM/DD/YYYY"})} def __init__(self, *args, **kwargs): super(FollowupRequestForm, self).__init__(*args, **kwargs) diff --git a/referral/migrations/0001_initial.py b/referral/migrations/0001_initial.py index 47316a5..1b30d87 100644 --- a/referral/migrations/0001_initial.py +++ b/referral/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2018-10-26 01:35 -from __future__ import unicode_literals +# Generated by Django 2.2.12 on 2020-05-05 15:32 from django.db import migrations, models import django.db.models.deletion @@ -11,8 +9,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('pttrack', '0006_referral_additional_fields_20180826'), - ('followup', '0002_simplehistory_add_change_reason'), + ('followup', '0001_initial'), + ('pttrack', '0001_initial'), ] operations = [ @@ -23,62 +21,61 @@ class Migration(migrations.Migration): ('written_datetime', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now=True)), ('completion_date', models.DateTimeField(blank=True, null=True)), - ('due_date', models.DateField(help_text=b'MM/DD/YYYY or YYYY-MM-DD')), + ('due_date', models.DateField(help_text='MM/DD/YYYY')), ('contact_instructions', models.TextField()), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Provider')), - ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.ProviderType')), - ('completion_author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='referral_followuprequest_completed', to='pttrack.Provider')), - ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Patient')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('completion_author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='referral_followuprequest_completed', to='pttrack.Provider')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), ], options={ + 'ordering': ['-written_datetime', '-last_modified'], 'abstract': False, }, ), migrations.CreateModel( - name='PatientContact', + name='Referral', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('written_datetime', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now=True)), - ('has_appointment', models.CharField(blank=True, choices=[(b'Y', b'Yes'), (b'N', b'No')], help_text=b'Did the patient make an appointment?', max_length=1, verbose_name=b'Appointment scheduled?')), - ('pt_showed', models.CharField(blank=True, choices=[(b'Y', b'Yes'), (b'N', b'No')], help_text=b'Did the patient show up to the appointment?', max_length=1, null=True, verbose_name=b'Appointment attended?')), - ('appointment_location', models.ManyToManyField(blank=True, help_text=b'Where did the patient make an appointment?', to='pttrack.ReferralLocation')), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Provider')), - ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.ProviderType')), - ('contact_method', models.ForeignKey(help_text=b'What was the method of contact?', on_delete=django.db.models.deletion.CASCADE, to='pttrack.ContactMethod')), - ('contact_status', models.ForeignKey(help_text=b'Did you make contact with the patient about this referral?', on_delete=django.db.models.deletion.CASCADE, to='followup.ContactResult')), - ('followup_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='referral.FollowupRequest')), - ('no_apt_reason', models.ForeignKey(blank=True, help_text=b"If the patient didn't make an appointment, why not?", null=True, on_delete=django.db.models.deletion.CASCADE, to='followup.NoAptReason', verbose_name=b'No appointment reason')), - ('no_show_reason', models.ForeignKey(blank=True, help_text=b"If the patient didn't go to the appointment, why not?", null=True, on_delete=django.db.models.deletion.CASCADE, to='followup.NoShowReason')), - ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Patient')), + ('comments', models.TextField(blank=True)), + ('status', models.CharField(choices=[('S', 'Successful'), ('P', 'Pending'), ('U', 'Unsuccessful')], default='P', max_length=50)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('kind', models.ForeignKey(help_text='The kind of care the patient should recieve at the referral location.', on_delete=django.db.models.deletion.PROTECT, to='pttrack.ReferralType')), + ('location', models.ManyToManyField(to='pttrack.ReferralLocation')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), ], options={ + 'ordering': ['-written_datetime', '-last_modified'], 'abstract': False, }, ), migrations.CreateModel( - name='Referral', + name='PatientContact', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('written_datetime', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now=True)), - ('comments', models.TextField(blank=True)), - ('status', models.CharField(choices=[(b'S', b'Successful'), (b'P', b'Pending'), (b'U', b'Unsuccessful')], default=b'P', max_length=50)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Provider')), - ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.ProviderType')), - ('kind', models.ForeignKey(help_text=b'The kind of care the patient should recieve at the referral location.', on_delete=django.db.models.deletion.CASCADE, to='pttrack.ReferralType')), - ('location', models.ManyToManyField(to='pttrack.ReferralLocation')), - ('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Patient')), + ('has_appointment', models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], help_text='Did the patient make an appointment?', max_length=1, verbose_name='Appointment scheduled?')), + ('pt_showed', models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], help_text='Did the patient show up to the appointment?', max_length=1, null=True, verbose_name='Appointment attended?')), + ('appointment_location', models.ManyToManyField(blank=True, help_text='Where did the patient make an appointment?', to='pttrack.ReferralLocation')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('contact_method', models.ForeignKey(help_text='What was the method of contact?', on_delete=django.db.models.deletion.PROTECT, to='pttrack.ContactMethod')), + ('contact_status', models.ForeignKey(help_text='Did you make contact with the patient about this referral?', on_delete=django.db.models.deletion.PROTECT, to='followup.ContactResult')), + ('followup_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='referral.FollowupRequest')), + ('no_apt_reason', models.ForeignKey(blank=True, help_text="If the patient didn't make an appointment, why not?", null=True, on_delete=django.db.models.deletion.PROTECT, to='followup.NoAptReason', verbose_name='No appointment reason')), + ('no_show_reason', models.ForeignKey(blank=True, help_text="If the patient didn't go to the appointment, why not?", null=True, on_delete=django.db.models.deletion.PROTECT, to='followup.NoShowReason')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ('referral', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='referral.Referral')), ], options={ + 'ordering': ['-written_datetime', '-last_modified'], 'abstract': False, }, ), - migrations.AddField( - model_name='patientcontact', - name='referral', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='referral.Referral'), - ), migrations.AddField( model_name='followuprequest', name='referral', diff --git a/referral/migrations/0002_auto_20190902_2116.py b/referral/migrations/0002_auto_20190902_2116.py deleted file mode 100644 index a1e11bc..0000000 --- a/referral/migrations/0002_auto_20190902_2116.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:16 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('referral', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='followuprequest', - options={'ordering': ['-written_datetime', '-last_modified']}, - ), - migrations.AlterModelOptions( - name='patientcontact', - options={'ordering': ['-written_datetime', '-last_modified']}, - ), - migrations.AlterModelOptions( - name='referral', - options={'ordering': ['-written_datetime', '-last_modified']}, - ), - ] diff --git a/referral/models.py b/referral/models.py index 5d286a8..aa884e3 100644 --- a/referral/models.py +++ b/referral/models.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from builtins import map from django.db import models -from django.core.urlresolvers import reverse +from django.urls import reverse from pttrack.models import (ReferralType, ReferralLocation, Note, ContactMethod, CompletableMixin,) @@ -32,6 +32,7 @@ class Referral(Note): max_length=50, choices=REFERRAL_STATUSES, default=STATUS_PENDING) kind = models.ForeignKey( ReferralType, + on_delete=models.PROTECT, help_text="The kind of care the patient should recieve at the " "referral location.") @@ -71,7 +72,9 @@ def aggregate_referral_status(referrals): class FollowupRequest(Note, CompletableMixin): - referral = models.ForeignKey(Referral) + referral = models.ForeignKey( + Referral, + on_delete=models.CASCADE) contact_instructions = models.TextField() MARK_DONE_URL_NAME = 'new-patient-contact' @@ -107,19 +110,24 @@ def __str__(self): class PatientContact(Note): - followup_request = models.ForeignKey(FollowupRequest) - referral = models.ForeignKey(Referral) + followup_request = models.ForeignKey( + FollowupRequest, + on_delete=models.CASCADE) + referral = models.ForeignKey( + Referral, + on_delete=models.PROTECT + ) contact_method = models.ForeignKey( ContactMethod, - null=False, - blank=False, + null=False, blank=False, + on_delete=models.PROTECT, help_text="What was the method of contact?") contact_status = models.ForeignKey( ContactResult, - blank=False, - null=False, + blank=False, null=False, + on_delete=models.PROTECT, help_text="Did you make contact with the patient about this referral?") PTSHOW_YES = "Y" @@ -135,8 +143,8 @@ class PatientContact(Note): no_apt_reason = models.ForeignKey( NoAptReason, - blank=True, - null=True, + blank=True, null=True, + on_delete=models.PROTECT, verbose_name="No appointment reason", help_text="If the patient didn't make an appointment, why not?") @@ -155,8 +163,8 @@ class PatientContact(Note): no_show_reason = models.ForeignKey( NoShowReason, - blank=True, - null=True, + blank=True, null=True, + on_delete=models.PROTECT, help_text="If the patient didn't go to the appointment, why not?") def short_text(self): diff --git a/referral/tests.py b/referral/tests.py index 9a831eb..5706860 100644 --- a/referral/tests.py +++ b/referral/tests.py @@ -5,7 +5,7 @@ from django.test import TestCase from itertools import * -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils.timezone import now from followup.models import ( @@ -44,7 +44,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -68,7 +68,7 @@ def setUp(self): self.followup_request = models.FollowupRequest.objects.create( referral=self.referral, contact_instructions="Call him", - due_date=datetime.date(2018, 9, 0o1), + due_date=datetime.date(2018, 9, 1), author=Provider.objects.first(), author_type=ProviderType.objects.first(), patient=self.pt @@ -335,7 +335,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -398,7 +398,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -496,7 +496,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -526,7 +526,7 @@ def test_referral_list(self): models.FollowupRequest.objects.create( referral=referral1, contact_instructions="Call him", - due_date=datetime.date(2018, 11, 0o1), + due_date=datetime.date(2018, 11, 1), author=Provider.objects.first(), author_type=ProviderType.objects.first(), patient=self.pt @@ -560,7 +560,7 @@ def test_referral_list(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1994, 0o1, 22), + date_of_birth=datetime.date(1994, 1, 22), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -584,7 +584,7 @@ def test_referral_list(self): models.FollowupRequest.objects.create( referral=referral3, contact_instructions="Call him", - due_date=datetime.date(2018, 11, 0o1), + due_date=datetime.date(2018, 11, 1), author=Provider.objects.first(), author_type=ProviderType.objects.first(), patient=pt2 @@ -696,7 +696,7 @@ def setUp(self): state='BA', zip_code='63108', pcp_preferred_zip='63018', - date_of_birth=datetime.date(1990, 0o1, 0o1), + date_of_birth=datetime.date(1990, 1, 1), patient_comfortable_with_english=False, preferred_contact_method=self.contact_method, ) @@ -726,7 +726,7 @@ def test_valid_input(self): followup_request1 = models.FollowupRequest.objects.create( referral=referral1, contact_instructions="Call him", - due_date=datetime.date(2018, 11, 0o1), + due_date=datetime.date(2018, 11, 1), author=Provider.objects.first(), author_type=ProviderType.objects.first(), patient=self.pt diff --git a/referral/urls.py b/referral/urls.py index ef3ec27..8fe8439 100644 --- a/referral/urls.py +++ b/referral/urls.py @@ -1,25 +1,30 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import re_path from pttrack.urls import wrap_url from . import views from . import models unwrapped_urlconf = [ # pylint: disable=invalid-name - url(r'^new-referral/(?P[0-9]+)/(?P[-a-z]+)$', + re_path( + r'^new-referral/(?P[0-9]+)/(?P[-a-z]+)$', views.ReferralCreate.as_view(), name='new-referral'), - url(r'^followup-request/(?P[0-9]+)/(?P[0-9]+)$', + re_path( + r'^followup-request/(?P[0-9]+)/(?P[0-9]+)$', views.FollowupRequestCreate.as_view(), name='new-followup-request'), - url(r'^patient-contact/(?P[0-9]+)/(?P[0-9]+)/' + re_path( + r'^patient-contact/(?P[0-9]+)/(?P[0-9]+)/' r'(?P[0-9]+)$', views.PatientContactCreate.as_view(), name=models.FollowupRequest.MARK_DONE_URL_NAME), - url(r'^select-referral/(?P[0-9]+)$', + re_path( + r'^select-referral/(?P[0-9]+)$', views.select_referral, name='select-referral'), - url(r'^select-referral-type/(?P[0-9]+)$', + re_path( + r'^select-referral-type/(?P[0-9]+)$', views.select_referral_type, name='select-referral-type') ] diff --git a/referral/views.py b/referral/views.py index 4b50489..174be01 100644 --- a/referral/views.py +++ b/referral/views.py @@ -1,7 +1,7 @@ from __future__ import print_function from __future__ import unicode_literals from django.shortcuts import get_object_or_404, render -from django.core.urlresolvers import reverse +from django.urls import reverse from django.views.generic.edit import FormView from django.http import HttpResponseRedirect from django.contrib import messages diff --git a/requirements-dev.txt b/requirements-dev.txt index 4e0f8c7..acc6396 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ -r requirements.txt -django-debug-toolbar==1.9.1 +django-debug-toolbar==2.2 codecov==2.0.15 coverage==4.5.1 selenium==3.12.0 diff --git a/requirements.txt b/requirements.txt index ad8272e..5f36fca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,23 @@ -Django==1.11.28 -django-bootstrap3==11.0.0 -git+https://github.com/inducer/django-bootstrap3-datetimepicker.git -django-crispy-forms==1.7.2 -django-simple-history==2.7.0 -djangorestframework==3.4.0 +asgiref==3.2.7 +certifi==2020.4.5.1 +chardet==3.0.4 +Django==3.0.6 +django-bootstrap3==12.1.0 +django-crispy-forms==1.8.1 +django-simple-history==2.9.0 +djangorestframework==3.11.0 ecdsa==0.13.3 future==0.18.2 html5lib==1.0.1 httplib2==0.11.3 -Pillow==6.2.0 +idna==2.9 +Pillow==7.1.2 PyPDF2==1.26.0 +pytz==2020.1 reportlab==3.4.0 +requests==2.23.0 six==1.11.0 +sqlparse==0.2.4 +urllib3==1.25.9 webencodings==0.5.1 xhtml2pdf==0.2.2 diff --git a/workup/admin.py b/workup/admin.py index f25b679..42ffab1 100644 --- a/workup/admin.py +++ b/workup/admin.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from django.contrib import admin -from django.core.urlresolvers import reverse +from django.urls import reverse from simple_history.admin import SimpleHistoryAdmin from pttrack.admin import NoteAdmin diff --git a/workup/management/commands/unsigned_wu_notify.py b/workup/management/commands/unsigned_wu_notify.py index 0e0a6f7..8e26759 100644 --- a/workup/management/commands/unsigned_wu_notify.py +++ b/workup/management/commands/unsigned_wu_notify.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from builtins import str -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.management.base import BaseCommand from django.core.mail import send_mail @@ -19,7 +19,7 @@ def handle(self, *args, **options): unsigned_wu2providers = { wu: Provider.objects.filter( - signed_workups=wu.clinic_day.workup_set.all()).first() + signed_workups__in=wu.clinic_day.workup_set.all()).first() for wu in unsigned_wus} provider2unsigned = {} @@ -41,10 +41,16 @@ def handle(self, *args, **options): else provider.associated_user.last_name) message_lines = [ - ("Hi there Dr. %s," % last_name), '', - "We've noticed something of a backlog of unattested notes, and so we've cooked up something to try to identify--wherever possible--which unattested notes should have been attested by which physicians. From here, it looks like the following note(s) should have been signed by you:", + ("Hi there Dr. %s," % last_name), + '', + "We've noticed something of a backlog of unattested notes, " + "and so we've cooked up something to try to " + "identify--wherever possible--which unattested notes " + "should have been attested by which physicians. From here, " + "it looks like the following note(s) should have been " + "signed by you:", "" - ] + ] for wu in inferred_wus: message_lines.append(" ".join([ diff --git a/workup/migrations/0001_initial.py b/workup/migrations/0001_initial.py index 705bc06..b5b1303 100644 --- a/workup/migrations/0001_initial.py +++ b/workup/migrations/0001_initial.py @@ -1,29 +1,20 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals +# Generated by Django 2.2.12 on 2020-05-05 15:32 +from django.conf import settings +import django.core.validators from django.db import migrations, models +import django.db.models.deletion import pttrack.validators +import simple_history.models import workup.validators -import django.db.models.deletion -from django.conf import settings -import django.core.validators class Migration(migrations.Migration): - replaces = [ - (b'workup', '0002_add_verbose_names'), - (b'workup', '0003_auto_20160122_1631'), - (b'workup', '0004_auto_20160328_1425'), - (b'workup', '0005_auto_20160826_0620'), - (b'workup', '0007_auto_20170502_1107'), - (b'workup', '0008_auto_20170623_1048'), - (b'workup', '0009_vitals_to_numeric_type'), - (b'workup', '0010_decimal_fields_and_validation'), - (b'workup', '0011_historicalprogressnote_progressnote')] + initial = True dependencies = [ - ('pttrack', '0001_squashed_0010_auto_20170623_1300'), + ('pttrack', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -31,167 +22,183 @@ class Migration(migrations.Migration): migrations.CreateModel( name='ClinicDate', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('clinic_date', models.DateField()), - ('gcal_id', models.CharField(max_length=50)), ], + options={ + 'ordering': ['-clinic_date'], + }, ), migrations.CreateModel( name='ClinicType', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=50)), ], + options={ + 'ordering': ['name'], + }, ), migrations.CreateModel( name='DiagnosisType', fields=[ - ('name', models.CharField(max_length=100, serialize=False, primary_key=True)), - ], - ), - migrations.CreateModel( - name='HistoricalProgressNote', - fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('title', models.CharField(max_length=200)), - ('text', models.TextField()), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('author', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author_type', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('patient', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True)), + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), ], options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical progress note', + 'ordering': ['name'], }, ), migrations.CreateModel( - name='HistoricalWorkup', + name='Workup', fields=[ - ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), - ('written_datetime', models.DateTimeField(editable=False, blank=True)), - ('last_modified', models.DateTimeField(editable=False, blank=True)), - ('chief_complaint', models.CharField(max_length=1000, verbose_name=b'CC')), - ('diagnosis', models.CharField(max_length=1000, verbose_name=b'Dx')), - ('HPI', models.TextField(verbose_name=b'HPI')), - ('PMH_PSH', models.TextField(verbose_name=b'PMH/PSH')), - ('meds', models.TextField(verbose_name=b'Medications')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ('chief_complaint', models.CharField(max_length=1000, verbose_name='CC')), + ('diagnosis', models.CharField(max_length=1000, verbose_name='Dx')), + ('HPI', models.TextField(verbose_name='HPI')), + ('PMH_PSH', models.TextField(verbose_name='PMH/PSH')), + ('meds', models.TextField(verbose_name='Medications')), ('allergies', models.TextField()), - ('fam_hx', models.TextField(verbose_name=b'Family History')), - ('soc_hx', models.TextField(verbose_name=b'Social History')), - ('ros', models.TextField(verbose_name=b'ROS')), - ('hr', models.PositiveSmallIntegerField(null=True, verbose_name=b'Heart Rate', blank=True)), - ('bp_sys', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name=b'Systolic', validators=[workup.validators.validate_bp_systolic])), - ('bp_dia', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name=b'Diastolic', validators=[workup.validators.validate_bp_diastolic])), - ('rr', models.PositiveSmallIntegerField(null=True, verbose_name=b'Respiratory Rate', blank=True)), - ('t', models.DecimalField(null=True, verbose_name=b'Temperature', max_digits=4, decimal_places=1, blank=True)), - ('height', models.PositiveSmallIntegerField(null=True, blank=True)), - ('weight', models.DecimalField(null=True, max_digits=5, decimal_places=1, blank=True)), - ('pe', models.TextField(verbose_name=b'Physical Examination')), - ('labs_ordered_quest', models.TextField(null=True, verbose_name=b'Labs Ordered from Quest', blank=True)), - ('labs_ordered_internal', models.TextField(null=True, verbose_name=b'Labs Ordered Internally', blank=True)), - ('rx', models.TextField(null=True, verbose_name=b'Prescription Orders', blank=True)), + ('fam_hx', models.TextField(verbose_name='Family History')), + ('soc_hx', models.TextField(verbose_name='Social History')), + ('ros', models.TextField(verbose_name='ROS')), + ('hr', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Heart Rate')), + ('bp_sys', models.PositiveSmallIntegerField(blank=True, null=True, validators=[workup.validators.validate_bp_systolic], verbose_name='Systolic')), + ('bp_dia', models.PositiveSmallIntegerField(blank=True, null=True, validators=[workup.validators.validate_bp_diastolic], verbose_name='Diastolic')), + ('rr', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Respiratory Rate')), + ('t', models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, verbose_name='Temperature')), + ('height', models.PositiveSmallIntegerField(blank=True, null=True)), + ('weight', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True)), + ('pe', models.TextField(verbose_name='Physical Examination')), + ('labs_ordered_quest', models.TextField(blank=True, null=True, verbose_name='Labs Ordered from Quest')), + ('labs_ordered_internal', models.TextField(blank=True, null=True, verbose_name='Labs Ordered Internally')), + ('rx', models.TextField(blank=True, null=True, verbose_name='Prescription Orders')), ('got_voucher', models.BooleanField(default=False)), - ('voucher_amount', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), - ('patient_pays', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), + ('voucher_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), + ('patient_pays', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), ('got_imaging_voucher', models.BooleanField(default=False)), - ('imaging_voucher_amount', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), - ('patient_pays_imaging', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), - ('will_return', models.BooleanField(default=False, help_text=b'Will the pt. return to SNHC?')), + ('imaging_voucher_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), + ('patient_pays_imaging', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), + ('will_return', models.BooleanField(default=False, help_text='Will the pt. return to SNHC?')), ('A_and_P', models.TextField()), - ('signed_date', models.DateTimeField(null=True, blank=True)), - ('history_id', models.AutoField(serialize=False, primary_key=True)), - ('history_date', models.DateTimeField()), - ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), - ('attending', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), - ('author_type', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.ProviderType', null=True)), - ('clinic_day', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='workup.ClinicDate', null=True)), - ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), - ('patient', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Patient', null=True)), - ('signer', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='pttrack.Provider', null=True)), + ('signed_date', models.DateTimeField(blank=True, null=True)), + ('attending', models.ForeignKey(blank=True, help_text='Which attending saw the patient?', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='attending_physician', to='pttrack.Provider', validators=[pttrack.validators.validate_attending])), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('clinic_day', models.ForeignKey(help_text='When was the patient seen?', on_delete=django.db.models.deletion.PROTECT, to='workup.ClinicDate')), + ('diagnosis_categories', models.ManyToManyField(to='workup.DiagnosisType')), + ('other_volunteer', models.ManyToManyField(blank=True, help_text='Which other volunteer(s) did you work with (if any)?', related_name='other_volunteer', to='pttrack.Provider')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ('referral_location', models.ManyToManyField(blank=True, to='pttrack.ReferralLocation')), + ('referral_type', models.ManyToManyField(blank=True, to='pttrack.ReferralType')), + ('signer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='signed_workups', to='pttrack.Provider', validators=[pttrack.validators.validate_attending])), ], options={ - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': 'history_date', - 'verbose_name': 'historical workup', + 'abstract': False, }, ), migrations.CreateModel( name='ProgressNote', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('written_datetime', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now=True)), ('title', models.CharField(max_length=200)), ('text', models.TextField()), - ('author', models.ForeignKey(to='pttrack.Provider')), - ('author_type', models.ForeignKey(to='pttrack.ProviderType')), - ('patient', models.ForeignKey(to='pttrack.Patient')), + ('signed_date', models.DateTimeField(blank=True, null=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Provider')), + ('author_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.ProviderType')), + ('patient', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pttrack.Patient')), + ('signer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='signed_progress_notes', to='pttrack.Provider', validators=[pttrack.validators.validate_attending])), ], options={ 'abstract': False, }, ), migrations.CreateModel( - name='Workup', + name='HistoricalWorkup', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('written_datetime', models.DateTimeField(auto_now_add=True)), - ('last_modified', models.DateTimeField(auto_now=True)), - ('chief_complaint', models.CharField(max_length=1000, verbose_name=b'CC')), - ('diagnosis', models.CharField(max_length=1000, verbose_name=b'Dx')), - ('HPI', models.TextField(verbose_name=b'HPI')), - ('PMH_PSH', models.TextField(verbose_name=b'PMH/PSH')), - ('meds', models.TextField(verbose_name=b'Medications')), + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('chief_complaint', models.CharField(max_length=1000, verbose_name='CC')), + ('diagnosis', models.CharField(max_length=1000, verbose_name='Dx')), + ('HPI', models.TextField(verbose_name='HPI')), + ('PMH_PSH', models.TextField(verbose_name='PMH/PSH')), + ('meds', models.TextField(verbose_name='Medications')), ('allergies', models.TextField()), - ('fam_hx', models.TextField(verbose_name=b'Family History')), - ('soc_hx', models.TextField(verbose_name=b'Social History')), - ('ros', models.TextField(verbose_name=b'ROS')), - ('hr', models.PositiveSmallIntegerField(null=True, verbose_name=b'Heart Rate', blank=True)), - ('bp_sys', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name=b'Systolic', validators=[workup.validators.validate_bp_systolic])), - ('bp_dia', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name=b'Diastolic', validators=[workup.validators.validate_bp_diastolic])), - ('rr', models.PositiveSmallIntegerField(null=True, verbose_name=b'Respiratory Rate', blank=True)), - ('t', models.DecimalField(null=True, verbose_name=b'Temperature', max_digits=4, decimal_places=1, blank=True)), - ('height', models.PositiveSmallIntegerField(null=True, blank=True)), - ('weight', models.DecimalField(null=True, max_digits=5, decimal_places=1, blank=True)), - ('pe', models.TextField(verbose_name=b'Physical Examination')), - ('labs_ordered_quest', models.TextField(null=True, verbose_name=b'Labs Ordered from Quest', blank=True)), - ('labs_ordered_internal', models.TextField(null=True, verbose_name=b'Labs Ordered Internally', blank=True)), - ('rx', models.TextField(null=True, verbose_name=b'Prescription Orders', blank=True)), + ('fam_hx', models.TextField(verbose_name='Family History')), + ('soc_hx', models.TextField(verbose_name='Social History')), + ('ros', models.TextField(verbose_name='ROS')), + ('hr', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Heart Rate')), + ('bp_sys', models.PositiveSmallIntegerField(blank=True, null=True, validators=[workup.validators.validate_bp_systolic], verbose_name='Systolic')), + ('bp_dia', models.PositiveSmallIntegerField(blank=True, null=True, validators=[workup.validators.validate_bp_diastolic], verbose_name='Diastolic')), + ('rr', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Respiratory Rate')), + ('t', models.DecimalField(blank=True, decimal_places=1, max_digits=4, null=True, verbose_name='Temperature')), + ('height', models.PositiveSmallIntegerField(blank=True, null=True)), + ('weight', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True)), + ('pe', models.TextField(verbose_name='Physical Examination')), + ('labs_ordered_quest', models.TextField(blank=True, null=True, verbose_name='Labs Ordered from Quest')), + ('labs_ordered_internal', models.TextField(blank=True, null=True, verbose_name='Labs Ordered Internally')), + ('rx', models.TextField(blank=True, null=True, verbose_name='Prescription Orders')), ('got_voucher', models.BooleanField(default=False)), - ('voucher_amount', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), - ('patient_pays', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), + ('voucher_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), + ('patient_pays', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), ('got_imaging_voucher', models.BooleanField(default=False)), - ('imaging_voucher_amount', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), - ('patient_pays_imaging', models.DecimalField(blank=True, null=True, max_digits=6, decimal_places=2, validators=[django.core.validators.MinValueValidator(0)])), - ('will_return', models.BooleanField(default=False, help_text=b'Will the pt. return to SNHC?')), + ('imaging_voucher_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), + ('patient_pays_imaging', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(0)])), + ('will_return', models.BooleanField(default=False, help_text='Will the pt. return to SNHC?')), ('A_and_P', models.TextField()), - ('signed_date', models.DateTimeField(null=True, blank=True)), - ('attending', models.ForeignKey(related_name='attending_physician', validators=[pttrack.validators.validate_attending], to='pttrack.Provider', blank=True, help_text=b'Which attending saw the patient?', null=True)), - ('author', models.ForeignKey(to='pttrack.Provider')), - ('author_type', models.ForeignKey(to='pttrack.ProviderType')), - ('clinic_day', models.ForeignKey(to='workup.ClinicDate')), - ('diagnosis_categories', models.ManyToManyField(to='workup.DiagnosisType')), - ('other_volunteer', models.ManyToManyField(help_text=b'Which other volunteer(s) did you work with (if any)?', related_name='other_volunteer', to='pttrack.Provider', blank=True)), - ('patient', models.ForeignKey(to='pttrack.Patient')), - ('referral_location', models.ManyToManyField(to='pttrack.ReferralLocation', blank=True)), - ('referral_type', models.ManyToManyField(to='pttrack.ReferralType', blank=True)), - ('signer', models.ForeignKey(related_name='signed_workups', validators=[pttrack.validators.validate_attending], to='pttrack.Provider', blank=True, null=True)), + ('signed_date', models.DateTimeField(blank=True, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('attending', models.ForeignKey(blank=True, db_constraint=False, help_text='Which attending saw the patient?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider', validators=[pttrack.validators.validate_attending])), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('clinic_day', models.ForeignKey(blank=True, db_constraint=False, help_text='When was the patient seen?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='workup.ClinicDate')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), + ('signer', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider', validators=[pttrack.validators.validate_attending])), ], options={ - 'abstract': False, + 'verbose_name': 'historical workup', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='HistoricalProgressNote', + fields=[ + ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('written_datetime', models.DateTimeField(blank=True, editable=False)), + ('last_modified', models.DateTimeField(blank=True, editable=False)), + ('title', models.CharField(max_length=200)), + ('text', models.TextField()), + ('signed_date', models.DateTimeField(blank=True, null=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('author', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider')), + ('author_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.ProviderType')), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('patient', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Patient')), + ('signer', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider', validators=[pttrack.validators.validate_attending])), + ], + options={ + 'verbose_name': 'historical progress note', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', }, + bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.AddField( model_name='clinicdate', name='clinic_type', - field=models.ForeignKey(to='workup.ClinicType'), + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='workup.ClinicType'), ), ] diff --git a/workup/migrations/0002_simplehistory_add_change_reason.py b/workup/migrations/0002_simplehistory_add_change_reason.py deleted file mode 100644 index 7292eec..0000000 --- a/workup/migrations/0002_simplehistory_add_change_reason.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2018-07-09 01:07 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workup', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='clinicdate', - options={'ordering': ['-clinic_date']}, - ), - migrations.AddField( - model_name='historicalprogressnote', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - migrations.AddField( - model_name='historicalworkup', - name='history_change_reason', - field=models.CharField(max_length=100, null=True), - ), - ] diff --git a/workup/migrations/0003_historicalworkup_add_attending_validator_20190324_1133.py b/workup/migrations/0003_historicalworkup_add_attending_validator_20190324_1133.py deleted file mode 100644 index ee4e8fa..0000000 --- a/workup/migrations/0003_historicalworkup_add_attending_validator_20190324_1133.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.20 on 2019-03-24 16:33 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion -import pttrack.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('workup', '0002_simplehistory_add_change_reason'), - ] - - operations = [ - migrations.AlterField( - model_name='historicalworkup', - name='attending', - field=models.ForeignKey( - blank=True, db_constraint=False, - help_text=b'Which attending saw the patient?', - null=True, on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='pttrack.Provider', - validators=[pttrack.validators.validate_attending]), - ), - migrations.AlterField( - model_name='historicalworkup', - name='clinic_day', - field=models.ForeignKey( - blank=True, db_constraint=False, - help_text=b'When was the patient seen?', null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='workup.ClinicDate'), - ), - migrations.AlterField( - model_name='historicalworkup', - name='signer', - field=models.ForeignKey( - blank=True, db_constraint=False, null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name='+', to='pttrack.Provider', - validators=[pttrack.validators.validate_attending]), - ), - migrations.AlterField( - model_name='workup', - name='clinic_day', - field=models.ForeignKey( - help_text=b'When was the patient seen?', - on_delete=django.db.models.deletion.PROTECT, - to='workup.ClinicDate'), - ), - ] diff --git a/workup/migrations/0004_add_progressnote_signing_20190813_2018.py b/workup/migrations/0004_add_progressnote_signing_20190813_2018.py deleted file mode 100644 index 1b8077a..0000000 --- a/workup/migrations/0004_add_progressnote_signing_20190813_2018.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.20 on 2019-08-14 01:18 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion -import pttrack.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('pttrack', '0007_referraltype_is_active'), - ('workup', '0003_historicalworkup_add_attending_validator_20190324_1133'), - ] - - operations = [ - migrations.AddField( - model_name='historicalprogressnote', - name='signed_date', - field=models.DateTimeField(blank=True, null=True), - ), - migrations.AddField( - model_name='historicalprogressnote', - name='signer', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider'), - ), - migrations.AddField( - model_name='progressnote', - name='signed_date', - field=models.DateTimeField(blank=True, null=True), - ), - migrations.AddField( - model_name='progressnote', - name='signer', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='signed_progress_notes', to='pttrack.Provider', validators=[pttrack.validators.validate_attending]), - ), - migrations.AlterField( - model_name='historicalworkup', - name='attending', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider'), - ), - migrations.AlterField( - model_name='historicalworkup', - name='clinic_day', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='workup.ClinicDate'), - ), - migrations.AlterField( - model_name='historicalworkup', - name='signer', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider'), - ), - migrations.AlterField( - model_name='workup', - name='clinic_day', - field=models.ForeignKey(help_text=b'When was the patient seen?', on_delete=django.db.models.deletion.CASCADE, to='workup.ClinicDate'), - ), - ] diff --git a/workup/migrations/0005_set_orderings_and_db_do_nothing_20190902_2116.py b/workup/migrations/0005_set_orderings_and_db_do_nothing_20190902_2116.py deleted file mode 100644 index 39bff79..0000000 --- a/workup/migrations/0005_set_orderings_and_db_do_nothing_20190902_2116.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:16 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion -import pttrack.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('workup', '0004_add_progressnote_signing_20190813_2018'), - ] - - operations = [ - migrations.AlterModelOptions( - name='clinictype', - options={'ordering': ['name']}, - ), - migrations.AlterModelOptions( - name='diagnosistype', - options={'ordering': ['name']}, - ), - migrations.AlterField( - model_name='historicalprogressnote', - name='signer', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider', validators=[pttrack.validators.validate_attending]), - ), - migrations.AlterField( - model_name='historicalworkup', - name='attending', - field=models.ForeignKey(blank=True, db_constraint=False, help_text=b'Which attending saw the patient?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider', validators=[pttrack.validators.validate_attending]), - ), - migrations.AlterField( - model_name='historicalworkup', - name='clinic_day', - field=models.ForeignKey(blank=True, db_constraint=False, help_text=b'When was the patient seen?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='workup.ClinicDate'), - ), - migrations.AlterField( - model_name='historicalworkup', - name='signer', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pttrack.Provider', validators=[pttrack.validators.validate_attending]), - ), - ] diff --git a/workup/migrations/0006_remove_clinicdate_gcal_id.py b/workup/migrations/0006_remove_clinicdate_gcal_id.py deleted file mode 100644 index ecb7751..0000000 --- a/workup/migrations/0006_remove_clinicdate_gcal_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.23 on 2019-09-03 02:30 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workup', '0005_set_orderings_and_db_do_nothing_20190902_2116'), - ] - - operations = [ - migrations.RemoveField( - model_name='clinicdate', - name='gcal_id', - ), - ] diff --git a/workup/models.py b/workup/models.py index 1dd6064..7ffb992 100644 --- a/workup/models.py +++ b/workup/models.py @@ -8,7 +8,7 @@ from django.utils.timezone import now from simple_history.models import HistoricalRecords -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.validators import MinValueValidator from pttrack.models import Note, Provider, ReferralLocation, ReferralType @@ -46,7 +46,8 @@ class ClinicDate(models.Model): class Meta(object): ordering = ["-clinic_date"] - clinic_type = models.ForeignKey(ClinicType) + clinic_type = models.ForeignKey( + ClinicType, on_delete=models.PROTECT) clinic_date = models.DateField() @@ -135,10 +136,12 @@ class ProgressNote(AttestableNote): history = HistoricalRecords() - signer = models.ForeignKey(Provider, - blank=True, null=True, - related_name="signed_progress_notes", - validators=[validate_attending]) + signer = models.ForeignKey( + Provider, + blank=True, null=True, + on_delete=models.PROTECT, + related_name="signed_progress_notes", + validators=[validate_attending]) signed_date = models.DateTimeField(blank=True, null=True) def __str__(self): @@ -158,15 +161,23 @@ class Workup(AttestableNote): continuity care.''' attending = models.ForeignKey( - Provider, null=True, blank=True, related_name="attending_physician", + Provider, + null=True, blank=True, + related_name="attending_physician", + on_delete=models.PROTECT, validators=[validate_attending], help_text="Which attending saw the patient?") + other_volunteer = models.ManyToManyField( - Provider, blank=True, related_name="other_volunteer", + Provider, + blank=True, + related_name="other_volunteer", help_text="Which other volunteer(s) did you work with (if any)?") clinic_day = models.ForeignKey( - ClinicDate, help_text="When was the patient seen?") + ClinicDate, + on_delete=models.PROTECT, + help_text="When was the patient seen?") chief_complaint = models.CharField(max_length=1000, verbose_name="CC") diagnosis = models.CharField(max_length=1000, verbose_name="Dx") @@ -246,10 +257,12 @@ class Workup(AttestableNote): A_and_P = models.TextField() - signer = models.ForeignKey(Provider, - blank=True, null=True, - related_name="signed_workups", - validators=[validate_attending]) + signer = models.ForeignKey( + Provider, + blank=True, null=True, + on_delete=models.PROTECT, + related_name="signed_workups", + validators=[validate_attending]) signed_date = models.DateTimeField(blank=True, null=True) history = HistoricalRecords() diff --git a/workup/test_forms.py b/workup/test_forms.py index aa139d0..683d31f 100644 --- a/workup/test_forms.py +++ b/workup/test_forms.py @@ -62,7 +62,7 @@ def test_vitals_no_units_error(self): self.assertFalse( form.is_valid(), msg='Failed to raise error if only %s specified' % field) - self.assertNotEqual(form[field].errors, []) + self.assertNotEqual(len(form[field].errors), 0) def test_vitals_no_value_no_units_ok(self): """Units are required only when vitals are provided.""" @@ -174,24 +174,24 @@ def test_blood_pressure(self): form_data = self.valid_wu_dict form = WorkupForm(data=form_data) - self.assertEqual(form['bp_sys'].errors, []) - self.assertEqual(form['bp_dia'].errors, []) + self.assertEqual(len(form['bp_sys'].errors), 0) + self.assertEqual(len(form['bp_dia'].errors), 0) form_data['bp_sys'] = '800' form = WorkupForm(data=form_data) - self.assertNotEqual(form['bp_sys'].errors, []) + self.assertNotEqual(len(form['bp_sys'].errors), 0) form_data['bp_sys'] = '120' form_data['bp_dia'] = '30' form = WorkupForm(data=form_data) - self.assertNotEqual(form['bp_dia'].errors, []) + self.assertNotEqual(len(form['bp_dia'].errors), 0) # the systolic < diastolic error is a bp_sys error not bp_dia form_data['bp_dia'] = '130' form = WorkupForm(data=form_data) - self.assertNotEqual(form['bp_sys'].errors, []) + self.assertNotEqual(len(form['bp_sys'].errors), 0) def test_missing_voucher_amount(self): @@ -201,25 +201,25 @@ def test_missing_voucher_amount(self): form = WorkupForm(data=form_data) - self.assertNotEqual(form['voucher_amount'].errors, []) - self.assertNotEqual(form['patient_pays'].errors, []) + self.assertNotEqual(len(form['voucher_amount'].errors), 0) + self.assertNotEqual(len(form['patient_pays'].errors), 0) form_data['voucher_amount'] = '40' form_data['patient_pays'] = '40' form = WorkupForm(data=form_data) - self.assertEqual(form['voucher_amount'].errors, []) - self.assertEqual(form['patient_pays'].errors, []) + self.assertEqual(len(form['voucher_amount'].errors), 0) + self.assertEqual(len(form['patient_pays'].errors), 0) - self.assertNotEqual(form['imaging_voucher_amount'].errors, []) - self.assertNotEqual(form['patient_pays_imaging'].errors, []) + self.assertNotEqual(len(form['imaging_voucher_amount'].errors), 0) + self.assertNotEqual(len(form['patient_pays_imaging'].errors), 0) form_data['imaging_voucher_amount'] = '40' form_data['patient_pays_imaging'] = '40' form = WorkupForm(data=form_data) - self.assertEqual(form['imaging_voucher_amount'].errors, []) - self.assertEqual(form['patient_pays_imaging'].errors, []) + self.assertEqual(len(form['imaging_voucher_amount'].errors), 0) + self.assertEqual(len(form['patient_pays_imaging'].errors), 0) class TestWorkupFormProviderChoices(TestCase): @@ -274,16 +274,16 @@ def test_form_attending_options(self): form_data = wu_dict() form_data['attending'] = self.pvds[2].pk form = WorkupForm(data=form_data) - self.assertNotEqual(form['attending'].errors, []) + self.assertNotEqual(len(form['attending'].errors), 0) form_data['attending'] = self.pvds[3].pk form = WorkupForm(data=form_data) - self.assertNotEqual(form['attending'].errors, []) + self.assertNotEqual(len(form['attending'].errors), 0) # Make sure we accept attending providers form_data['attending'] = self.pvds[1].pk form = WorkupForm(data=form_data) - self.assertEqual(form['attending'].errors, []) + self.assertEqual(len(form['attending'].errors), 0) def test_form_other_volunteer_options(self): """WorkupForm offers only non-attendings for 'other volunteers'""" @@ -299,13 +299,13 @@ def test_form_other_volunteer_options(self): form_data = wu_dict() form_data['other_volunteer'] = self.pvds[1].pk form = WorkupForm(data=form_data) - self.assertNotEqual(form['other_volunteer'].errors, []) + self.assertNotEqual(len(form['other_volunteer'].errors), 0) # Accept non-attending providers form_data['other_volunteer'] = [self.pvds[2].pk] form = WorkupForm(data=form_data) - self.assertEqual(form['other_volunteer'].errors, []) + self.assertEqual(len(form['other_volunteer'].errors), 0) form_data['other_volunteer'] = [self.pvds[3].pk] form = WorkupForm(data=form_data) - self.assertEqual(form['other_volunteer'].errors, []) + self.assertEqual(len(form['other_volunteer'].errors), 0) diff --git a/workup/test_views.py b/workup/test_views.py index 15534a5..37469d6 100644 --- a/workup/test_views.py +++ b/workup/test_views.py @@ -4,7 +4,7 @@ from builtins import range from django.test import TestCase from django.utils.timezone import now -from django.core.urlresolvers import reverse +from django.urls import reverse from pttrack.models import Patient, ProviderType from pttrack.test_views import build_provider, log_in_provider @@ -43,6 +43,7 @@ def test_clindate_create_redirect(self): clindate create page.''' # First delete clindate that's created in setUp. + models.Workup.objects.all().delete() models.ClinicDate.objects.all().delete() pt = Patient.objects.first() @@ -234,11 +235,14 @@ class TestProgressNoteViews(TestCase): def setUp(self): + provider = build_provider() + log_in_provider(self.client, provider) + self.formdata = { 'title': 'Depression', 'text': 'so sad does testing work???', 'patient': Patient.objects.first(), - 'author': models.Provider.objects.first(), + 'author': provider, 'author_type': ProviderType.objects.first() } @@ -246,9 +250,6 @@ def setUp(self): clinic_type=models.ClinicType.objects.first(), clinic_date=now().date()) - provider = build_provider() - log_in_provider(self.client, provider) - def test_progressnote_urls(self): url = reverse('new-progress-note', args=(1,)) response = self.client.get(url) diff --git a/workup/tests.py b/workup/tests.py index f666397..856f54c 100644 --- a/workup/tests.py +++ b/workup/tests.py @@ -3,7 +3,7 @@ from django.test import TestCase from django.core import mail from django.core.exceptions import ValidationError -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.management import call_command from django.utils.timezone import now @@ -54,7 +54,6 @@ def setUp(self): clinic_type=models.ClinicType.objects.first(), clinic_date=now().date()) - def test_unsigned_email(self): pt = Patient.objects.first() diff --git a/workup/urls.py b/workup/urls.py index 88e77fc..59b1130 100644 --- a/workup/urls.py +++ b/workup/urls.py @@ -1,5 +1,5 @@ from __future__ import unicode_literals -from django.conf.urls import url +from django.urls import re_path from pttrack.urls import wrap_url from django.views.generic import DetailView @@ -7,47 +7,60 @@ from . import views unwrapped_urlconf = [ - url(r'^new-note/(?P[0-9]+)/$', + re_path( + r'^new-note/(?P[0-9]+)/$', views.new_note_dispatch, name='new-note-dispatch'), - url(r'^new/(?P[0-9]+)/$', + re_path( + r'^new/(?P[0-9]+)/$', views.WorkupCreate.as_view(), name='new-workup'), - url(r'^(?P[0-9]+)/$', + re_path( + r'^(?P[0-9]+)/$', DetailView.as_view(model=models.Workup), name='workup'), - url(r'^(?P[0-9]+)/update/$', + re_path( + r'^(?P[0-9]+)/update/$', views.WorkupUpdate.as_view(), name='workup-update'), - url(r'^(?P[0-9]+)/sign/$', + re_path( + r'^(?P[0-9]+)/sign/$', views.sign_workup, name='workup-sign'), - url(r'^(?P[0-9]+)/error/$', + re_path( + r'^(?P[0-9]+)/error/$', views.error_workup, name="workup-error"), - url(r'^(?P[0-9]+)/pdf/$', + re_path( + r'^(?P[0-9]+)/pdf/$', views.pdf_workup, name="workup-pdf"), # PROGRESS NOTES - url(r'^(?P[0-9]+)/psychnote/$', + re_path( + r'^(?P[0-9]+)/psychnote/$', views.ProgressNoteCreate.as_view(), name="new-progress-note"), - url(r'^psychnote/update/(?P[0-9]+)$', + re_path( + r'^psychnote/update/(?P[0-9]+)$', views.ProgressNoteUpdate.as_view(), name="progress-note-update"), - url(r'^psychnote/sign/(?P[0-9]+)$', + re_path( + r'^psychnote/sign/(?P[0-9]+)$', views.sign_progress_note, name='progress-note-sign'), - url(r'^psychnote/(?P[0-9]+)$', + re_path( + r'^psychnote/(?P[0-9]+)$', DetailView.as_view(model=models.ProgressNote), name="progress-note-detail"), - url(r'^(?P[0-9]+)/clindate/$', + re_path( + r'^(?P[0-9]+)/clindate/$', views.ClinicDateCreate.as_view(), name="new-clindate"), - url(r'^clindates/$', + re_path( + r'^clindates/$', views.clinic_date_list, name="clindate-list") ] diff --git a/workup/views.py b/workup/views.py index dc7cbca..e25d36a 100644 --- a/workup/views.py +++ b/workup/views.py @@ -4,7 +4,7 @@ from django.http import (HttpResponseRedirect, HttpResponseServerError, HttpResponse) -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.template.loader import get_template