Skip to content

Commit

Permalink
Medibase Medicines Integration (#1369)
Browse files Browse the repository at this point in the history
* Adds medibase medicines data

* Adds MedibaseMedicine Table and migrations

* in-memory search and updated viewsetrs and serializers

* rebase migrations

* fix import preventing migration

* set batch_size on medibase population

* noop for medibase population migration

* minor fix

* cleanup

* sort by word matches first

* adds test for medibase

* fix test casing

* fix discharge summary

* merge migrations

* load medicines data management command

* update docs

* updated load_dummy_data to call load_medicines_data

* merge migrations

* fresh migrations, drop medibase_id, use name as unique

* remove redundant slice operator

* call load_medicines_data in migration

* merge migrations

---------

Co-authored-by: Vignesh Hari <vichuhari100@gmail.com>
Co-authored-by: Aakash Singh <mail@singhaakash.dev>
  • Loading branch information
3 people authored Jul 5, 2023
1 parent b0eefd1 commit 46d6b35
Show file tree
Hide file tree
Showing 15 changed files with 1,144,658 additions and 10 deletions.
27 changes: 23 additions & 4 deletions care/facility/api/serializers/prescription.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
from django.shortcuts import get_object_or_404
from rest_framework import serializers

from care.facility.models import MedicineAdministration, Prescription
from care.facility.models import MedibaseMedicine, MedicineAdministration, Prescription
from care.users.api.serializers.user import UserBaseMinimumSerializer


class PrescriptionSerializer(serializers.ModelSerializer):
class MedibaseMedicineSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source="external_id", read_only=True)

prescribed_by = UserBaseMinimumSerializer(read_only=True)
class Meta:
model = MedibaseMedicine
exclude = ("deleted",)
read_only_fields = (
"external_id",
"created_date",
"modified_date",
)


class PrescriptionSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source="external_id", read_only=True)
prescribed_by = UserBaseMinimumSerializer(read_only=True)
last_administered_on = serializers.SerializerMethodField()
medicine_object = MedibaseMedicineSerializer(read_only=True, source="medicine")
medicine = serializers.UUIDField(write_only=True)

def get_last_administered_on(self, obj):
last_administration = (
Expand All @@ -28,6 +42,7 @@ class Meta:
"deleted",
)
read_only_fields = (
"medicine_old",
"external_id",
"prescribed_by",
"created_date",
Expand All @@ -37,12 +52,16 @@ class Meta:
)

def validate(self, attrs):
if "medicine" in attrs:
attrs["medicine"] = get_object_or_404(
MedibaseMedicine, external_id=attrs["medicine"]
)

if attrs.get("is_prn"):
if not attrs.get("indicator"):
raise serializers.ValidationError(
{"indicator": "Indicator should be set for PRN prescriptions."}
)

else:
if not attrs.get("frequency"):
raise serializers.ValidationError(
Expand Down
55 changes: 54 additions & 1 deletion care/facility/api/viewsets/prescription.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from re import IGNORECASE

from django.shortcuts import get_object_or_404
from django_filters import rest_framework as filters
from drf_spectacular.utils import extend_schema
from rest_framework import mixins, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from rest_framework.viewsets import GenericViewSet, ViewSet

from care.facility.api.serializers.prescription import (
MedicineAdministrationSerializer,
Expand Down Expand Up @@ -132,3 +134,54 @@ def administer(self, request, *args, **kwargs):
# administered_obj = MedicineAdministration.objects.get(external_id=request.query_params.get("id", None))
# administered_obj.delete()
# return Response({"success": True}, status=status.HTTP_200_OK)


class MedibaseViewSet(ViewSet):
permission_classes = (IsAuthenticated,)

def serailize_data(self, objects):
result = []
for object in objects:
if type(object) == tuple:
object = object[0]
result.append(
{
"id": object.external_id,
"name": object.name,
"type": object.type,
"generic": object.generic,
"company": object.company,
"contents": object.contents,
"cims_class": object.cims_class,
"atc_classification": object.atc_classification,
}
)
return result

def sort(self, query, results):
exact_matches = []
partial_matches = []

for result in results:
if type(result) == tuple:
result = result[0]
words = result.searchable.lower().split()
if query in words:
exact_matches.append(result)
else:
partial_matches.append(result)

return exact_matches + partial_matches

def list(self, request):
from care.facility.static_data.medibase import MedibaseMedicineTable

queryset = MedibaseMedicineTable

if request.GET.get("query", False):
query = request.GET.get("query").strip().lower()
queryset = queryset.where(
searchable=queryset.re_match(r".*" + query + r".*", IGNORECASE)
)
queryset = self.sort(query, queryset)
return Response(self.serailize_data(queryset[:15]))
1 change: 1 addition & 0 deletions care/facility/management/commands/load_dummy_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def handle(self, *args, **options):
try:
management.call_command("loaddata", self.BASE_URL + "users.json")
management.call_command("load_data", "kerala")
management.call_command("load_medicines_data")
management.call_command("seed_data")
management.call_command("loaddata", self.BASE_URL + "facility.json")
management.call_command("loaddata", self.BASE_URL + "cypress_users.json")
Expand Down
39 changes: 39 additions & 0 deletions care/facility/management/commands/load_medicines_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import json

from django.core.management import BaseCommand

from care.facility.models import MedibaseMedicine


class Command(BaseCommand):
"""
Command to load medibase medicines
Usage: python manage.py load_medicines_data
"""

help = "Loads Medibase Medicines into the database from medibase.json"

def fetch_data(self):
with open("data/medibase.json", "r") as json_file:
return json.load(json_file)

def handle(self, *args, **options):
print("Loading Medibase Medicines into the database from medibase.json")

medibase_objects = self.fetch_data()
MedibaseMedicine.objects.bulk_create(
[
MedibaseMedicine(
name=medicine["name"],
type=medicine["type"],
company=medicine.get("company"),
contents=medicine.get("contents"),
cims_class=medicine.get("cims_class"),
atc_classification=medicine.get("atc_classification"),
generic=medicine.get("generic"),
)
for medicine in medibase_objects
],
batch_size=1000,
ignore_conflicts=True,
)
73 changes: 73 additions & 0 deletions care/facility/migrations/0366_medibasemedicine_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Generated by Django 4.2.2 on 2023-06-28 02:50

import uuid

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0365_merge_20230626_1834"),
]

operations = [
migrations.CreateModel(
name="MedibaseMedicine",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"external_id",
models.UUIDField(db_index=True, default=uuid.uuid4, unique=True),
),
(
"created_date",
models.DateTimeField(auto_now_add=True, db_index=True, null=True),
),
(
"modified_date",
models.DateTimeField(auto_now=True, db_index=True, null=True),
),
("deleted", models.BooleanField(db_index=True, default=False)),
("name", models.CharField(db_index=True, max_length=255, unique=True)),
(
"type",
models.CharField(
choices=[("BRAND", "brand"), ("GENERIC", "generic")],
db_index=True,
max_length=16,
),
),
(
"generic",
models.CharField(
blank=True, db_index=True, max_length=255, null=True
),
),
(
"company",
models.CharField(
blank=True, db_index=True, max_length=255, null=True
),
),
("contents", models.TextField(blank=True, null=True)),
("cims_class", models.CharField(blank=True, max_length=255, null=True)),
("atc_classification", models.TextField(blank=True, null=True)),
],
options={
"abstract": False,
},
),
migrations.RenameField(
model_name="prescription",
old_name="medicine",
new_name="medicine_old",
),
]
27 changes: 27 additions & 0 deletions care/facility/migrations/0367_prescription_medicine_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.2 on 2023-06-28 02:50

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0366_medibasemedicine_and_more"),
]

operations = [
migrations.AddField(
model_name="prescription",
name="medicine",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="facility.medibasemedicine",
),
),
migrations.AlterField(
model_name="prescription",
name="medicine_old",
field=models.CharField(max_length=1023, null=True),
),
]
17 changes: 17 additions & 0 deletions care/facility/migrations/0368_populate_medibase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2023-06-28 09:58

from django.core.management import call_command
from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("facility", "0367_prescription_medicine_and_more"),
]

operations = [
migrations.RunPython(
lambda apps, schema_editor: call_command("load_medicines_data"),
reverse_code=migrations.RunPython.noop,
)
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 4.2.2 on 2023-06-30 02:01

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("facility", "0366_auto_20230627_1806"),
("facility", "0368_populate_medibase"),
]

operations = []
42 changes: 41 additions & 1 deletion care/facility/models/prescription.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,36 @@ def generate_choices(enum_class):
return [(tag.name, tag.value) for tag in enum_class]


class MedibaseMedicineType(enum.Enum):
BRAND = "brand"
GENERIC = "generic"


class MedibaseMedicine(BaseModel):
name = models.CharField(
max_length=255,
blank=False,
null=False,
db_index=True,
unique=True,
)
type = models.CharField(
max_length=16,
choices=generate_choices(MedibaseMedicineType),
blank=False,
null=False,
db_index=True,
)
generic = models.CharField(max_length=255, null=True, blank=True, db_index=True)
company = models.CharField(max_length=255, blank=True, null=True, db_index=True)
contents = models.TextField(blank=True, null=True)
cims_class = models.CharField(max_length=255, blank=True, null=True)
atc_classification = models.TextField(blank=True, null=True)

def __str__(self):
return " - ".join([self.name, self.generic, self.company])


class Prescription(BaseModel):
consultation = models.ForeignKey(
PatientConsultation,
Expand All @@ -49,7 +79,13 @@ class Prescription(BaseModel):
choices=generate_choices(PrescriptionType),
)

medicine = models.CharField(max_length=1023, blank=False, null=False)
medicine = models.ForeignKey(
MedibaseMedicine,
on_delete=models.PROTECT,
null=True,
blank=False,
)
medicine_old = models.CharField(max_length=1023, blank=False, null=True)
route = models.CharField(
max_length=100,
choices=[(tag.name, tag.value) for tag in Routes],
Expand Down Expand Up @@ -98,6 +134,10 @@ def save(self, *args, **kwargs) -> None:
self.discontinued_date = None
return super().save(*args, **kwargs)

@property
def medicine_name(self):
return str(self.medicine) if self.medicine else self.medicine_old

def __str__(self):
return self.medicine + " - " + self.consultation.patient.name

Expand Down
Loading

0 comments on commit 46d6b35

Please sign in to comment.