diff --git a/labs/__init__.py b/labs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/labs/admin.py b/labs/admin.py new file mode 100644 index 0000000..07b420b --- /dev/null +++ b/labs/admin.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin +from .models import * + +# Register your models here. + +admin.site.register(ContinuousMeasurement) +admin.site.register(DiscreteMeasurement) +admin.site.register(Lab) +admin.site.register(LabType) +admin.site.register(MeasurementType) +admin.site.register(DiscreteResultType) \ No newline at end of file diff --git a/labs/apps.py b/labs/apps.py new file mode 100644 index 0000000..1ce5de5 --- /dev/null +++ b/labs/apps.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class LabsConfig(AppConfig): + name = 'labs' diff --git a/labs/fixtures/labs.json b/labs/fixtures/labs.json new file mode 100644 index 0000000..4746579 --- /dev/null +++ b/labs/fixtures/labs.json @@ -0,0 +1 @@ +[{"model": "labs.labtype", "pk": "A1c", "fields": {}}, {"model": "labs.labtype", "pk": "BMP", "fields": {}}, {"model": "labs.labtype", "pk": "HIV", "fields": {}}, {"model": "labs.labtype", "pk": "Urinalysis", "fields": {}}, {"model": "labs.measurementtype", "pk": "A1c", "fields": {"short_name": "A1c", "unit": "%", "lab_type": "A1c"}}, {"model": "labs.measurementtype", "pk": "HIV Test", "fields": {"short_name": "HIV", "unit": "", "lab_type": "HIV"}}, {"model": "labs.measurementtype", "pk": "Nitrite", "fields": {"short_name": "Nitrite", "unit": "", "lab_type": "Urinalysis"}}, {"model": "labs.measurementtype", "pk": "Potassium, serum", "fields": {"short_name": "K+", "unit": "mmol/L", "lab_type": "BMP"}}, {"model": "labs.measurementtype", "pk": "Sodium, serum", "fields": {"short_name": "Na+", "unit": "mmol/L", "lab_type": "BMP"}}, {"model": "labs.discreteresulttype", "pk": "Negative", "fields": {"measurement_type": ["HIV Test", "Nitrite"]}}, {"model": "labs.discreteresulttype", "pk": "Positive", "fields": {"measurement_type": ["HIV Test", "Nitrite"]}}] \ No newline at end of file diff --git a/labs/forms.py b/labs/forms.py new file mode 100644 index 0000000..0529095 --- /dev/null +++ b/labs/forms.py @@ -0,0 +1,94 @@ +from django.forms import ( + fields, ModelForm, ModelChoiceField, ModelMultipleChoiceField, DecimalField, RadioSelect,Form +) +from django.shortcuts import render, redirect, get_object_or_404 +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit, Layout, Div, Row, HTML, Field +from crispy_forms.bootstrap import ( + InlineCheckboxes, AppendedText, PrependedText) +from pttrack.models import Patient +from . import models +from django.db.models import DateTimeField, ForeignKey +import django.db +import decimal + + +# Create a lab object to a patient without any measurements +class LabCreationForm(ModelForm): + class Meta: + model = models.Lab + exclude = ['patient','written_datetime'] + + def __init__(self, *args, **kwargs): + patient_obj = kwargs.pop('pt') + patient_name = patient_obj.name() + super(LabCreationForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + + self.helper.layout = Layout( + Row(HTML('
Patient name: %s
' %patient_name), + Div('lab_type', css_class='col-sm-6')), + + Submit('choose-lab', 'Choose Lab', css_class='btn btn-success') + ) + + +# Fill in corresponding measurements in a lab object +class MeasurementsCreationForm(Form): + def __init__(self, *args, **kwargs): + self.qs_fields = kwargs.pop('qs_mt') + self.new_lab = kwargs.pop('new_lab') + + # Should check if the lab is already filled with measurments + # How to check? + + super(MeasurementsCreationForm, self).__init__(*args, **kwargs) + + pt = self.new_lab.patient + + pt_info = Row(HTML('Patient name: %s
' %pt.name()), + HTML('Lab type: %s
' %self.new_lab.lab_type)) + + self.fieldsss = [pt_info] + for measurement_type in self.qs_fields: + str_name = measurement_type.short_name + unit = measurement_type.unit + self.fieldsss.append(Field(str_name)) + self.fieldsss[-1] = AppendedText(str_name,unit) + + value_qs=models.DiscreteResultType.objects.filter(measurement_type=measurement_type) + if len(value_qs)==0: + self.fields[str_name] = DecimalField() + else: + self.fields[str_name]=ModelChoiceField(queryset=value_qs) + + + self.helper = FormHelper() + self.helper.form_method = 'post' + + if len(self.fieldsss)==0: + button = [] + else: + button = [Submit('save-lab', 'Save Lab', css_class='btn btn-success')] + + self.helper.layout = Layout( + *(self.fieldsss + button) + ) + + def save(self): + for field in self.qs_fields: + if type(self.cleaned_data[field.short_name])==decimal.Decimal: + models.ContinuousMeasurement.objects.create( + measurement_type = field, + lab = self.new_lab, + value = self.cleaned_data[field.short_name] + ) + else: + models.DiscreteMeasurement.objects.create( + measurement_type = field, + lab = self.new_lab, + value = self.cleaned_data[field.short_name] + ) diff --git a/labs/migrations/0001_initial.py b/labs/migrations/0001_initial.py new file mode 100644 index 0000000..07c5916 --- /dev/null +++ b/labs/migrations/0001_initial.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-05-24 18:40 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('pttrack', '0010_auto_20200524_1340'), + ] + + operations = [ + migrations.CreateModel( + name='DiscreteResultType', + fields=[ + ('name', models.CharField(max_length=30, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='Lab', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('written_datetime', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.CreateModel( + name='LabType', + fields=[ + ('name', models.CharField(max_length=30, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name='Measurement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='MeasurementType', + fields=[ + ('long_name', models.CharField(max_length=30, primary_key=True, serialize=False)), + ('short_name', models.CharField(max_length=15)), + ('unit', models.CharField(blank=True, max_length=15)), + ('lab_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labs.LabType')), + ], + ), + migrations.CreateModel( + name='ContinuousMeasurement', + fields=[ + ('measurement_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='labs.Measurement')), + ('value', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True)), + ], + bases=('labs.measurement',), + ), + migrations.CreateModel( + name='DiscreteMeasurement', + fields=[ + ('measurement_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='labs.Measurement')), + ], + bases=('labs.measurement',), + ), + migrations.AddField( + model_name='measurement', + name='lab', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labs.Lab'), + ), + migrations.AddField( + model_name='measurement', + name='measurement_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labs.MeasurementType'), + ), + migrations.AddField( + model_name='lab', + name='lab_type', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labs.LabType'), + ), + migrations.AddField( + model_name='lab', + name='patient', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pttrack.Patient'), + ), + migrations.AddField( + model_name='discreteresulttype', + name='measurement_type', + field=models.ManyToManyField(to='labs.MeasurementType'), + ), + migrations.AddField( + model_name='discretemeasurement', + name='value', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labs.DiscreteResultType'), + ), + ] diff --git a/labs/migrations/__init__.py b/labs/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/labs/models.py b/labs/models.py new file mode 100644 index 0000000..6457db6 --- /dev/null +++ b/labs/models.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import datetime + +from django.db import models +from django.utils import timezone +from pttrack.models import Patient + +# type of lab panels +# e.g. BMP, A1c, CBC, etc. +class LabType(models.Model): + name = models.CharField(max_length=30, primary_key=True) + + def __unicode__(self): + return self.name + + +# object of a lab panel +class Lab(models.Model): + patient = models.ForeignKey(Patient) + + written_datetime = models.DateTimeField(auto_now_add=True) + + lab_type = models.ForeignKey(LabType) + + def __unicode__(self): + to_tz = timezone.get_default_timezone() + str_time = self.written_datetime.astimezone(to_tz).strftime("%B-%d-%Y, %H:%M") + return '%s | %s | %s ' %(str(self.patient),str(self.lab_type),str_time) + + +# type of measurements in a lab panel +# e.g. Na+, K+ in BMP, A1c in A1c, WBC in CBC, etc. +class MeasurementType(models.Model): + long_name = models.CharField(max_length=30, primary_key=True) + short_name = models.CharField(max_length=15) + unit = models.CharField(max_length=15, blank=True) + + lab_type = models.ForeignKey(LabType) + + def __unicode__(self): + return self.long_name + + +# parent class of measurements +class Measurement(models.Model): + measurement_type = models.ForeignKey(MeasurementType) + lab = models.ForeignKey(Lab) + + +# object of a continuous measurement +class ContinuousMeasurement(Measurement): + value = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True) + + def __unicode__(self): + return '%s: %2g' %(self.measurement_type, self.value) + + +# type of discrete results +# e.g. Positive, Negative, Trace, etc. +class DiscreteResultType(models.Model): + name = models.CharField(max_length=30, primary_key=True) + measurement_type = models.ManyToManyField(MeasurementType) + + def __unicode__(self): + return self.name + + +# object of a continuous measurement +class DiscreteMeasurement(Measurement): + value = models.ForeignKey(DiscreteResultType) + + def __unicode__(self): + value_name = DiscreteResultType.objects.get(pk=self.value) + return '%s: %s' %(self.measurement_type, value_name.name) diff --git a/labs/templates/labs/lab_all.html b/labs/templates/labs/lab_all.html new file mode 100644 index 0000000..f039e5a --- /dev/null +++ b/labs/templates/labs/lab_all.html @@ -0,0 +1,21 @@ +{% extends "pttrack/base.html" %} + +{% block title %} +All Labs +{% endblock %} + +{% block header%} +{{ error_message }}
{% endif %} + +{{ error_message }}
{% endif %} + +{{ error_message }}
{% endif %} + +