Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Labs #2642

Draft
wants to merge 37 commits into
base: develop
Choose a base branch
from
Draft

Labs #2642

Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
332401e
added service request related valuesets, model and specs
khavinshankar Nov 28, 2024
3b388c3
added specimen related valuesets, model and specs
khavinshankar Nov 30, 2024
d4d57be
added signal to auto create specimen on creation of service request
khavinshankar Dec 4, 2024
5d2ae31
Merge branch 'vigneshhari/health-details' into khavinshankar/labs
khavinshankar Dec 8, 2024
a38f83d
resolved migration conflict and import path errors
khavinshankar Dec 8, 2024
ed50d6a
added diagnostic report related valuesets, model and specs
khavinshankar Dec 8, 2024
97a0fcd
added endpoints to facilitate the labs flow
khavinshankar Dec 9, 2024
96fbfd7
added status flow filter in specimen
khavinshankar Dec 12, 2024
9e57884
added verify and review apis in diagnostic report
khavinshankar Dec 15, 2024
6daf082
added phase filters for diagnostic report
khavinshankar Dec 15, 2024
9a831ca
Merge branch 'develop' into khavinshankar/labs
khavinshankar Jan 4, 2025
f46595c
fixed migration conflict
khavinshankar Jan 5, 2025
20514ae
updated the model references to emr models | created separate specs f…
khavinshankar Jan 5, 2025
8515c24
preserve default values if set manually
khavinshankar Jan 5, 2025
45cee9d
update condition type in specimen
khavinshankar Jan 5, 2025
ac0cbc4
fixed required field issues while using specs in other specs
khavinshankar Jan 5, 2025
86f215e
updated facility type filter to support multiple types
khavinshankar Jan 6, 2025
79db034
serialize subject, encounter and requester in service request list spec
khavinshankar Jan 6, 2025
a7e1bd0
Merge branch 'develop' into khavinshankar/labs
khavinshankar Jan 6, 2025
703d651
cleaned up migrations
khavinshankar Jan 6, 2025
9b6fb2c
Merge branch 'develop' into khavinshankar/labs
khavinshankar Jan 10, 2025
5f1e76e
fixed migration conflicts
khavinshankar Jan 10, 2025
fcbcf2e
fixed receive at lab endpoint
khavinshankar Jan 10, 2025
223f0c7
fixed len issue in diagnostic report retrieve serializer
khavinshankar Jan 10, 2025
c4e043b
added phase field to service request
khavinshankar Jan 10, 2025
8094d8c
use retrieve spec model in endpoints in specimen and diagnostic report
khavinshankar Jan 10, 2025
10273ea
fixed error while retrieving diagnostic report
khavinshankar Jan 10, 2025
e9f06c0
added phase field to service request spec
khavinshankar Jan 10, 2025
1b4a12a
added phase filter and removed flow status filter
khavinshankar Jan 10, 2025
7549d74
fixed typo caused while resolving merge conflict
khavinshankar Jan 10, 2025
5e23ee0
added code rabbit suggestions
khavinshankar Jan 10, 2025
bafcb93
use perform create instead of clean create data
khavinshankar Jan 10, 2025
48a43d9
moved all the request specs into the viewset
khavinshankar Jan 10, 2025
de165f9
added authz
khavinshankar Jan 10, 2025
c23ac0d
Merge branch 'develop' into khavinshankar/labs
khavinshankar Jan 10, 2025
4dbf130
fixed migration conflict
khavinshankar Jan 10, 2025
48f88e6
added based on and subject in diagnostic report list spec
khavinshankar Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions care/emr/api/viewsets/diagnostic_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from datetime import UTC, datetime

from django_filters import CharFilter, FilterSet, OrderingFilter, UUIDFilter
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.response import Response

from care.emr.api.viewsets.base import EMRModelViewSet
from care.emr.models.diagnostic_report import DiagnosticReport
from care.emr.models.observation import Observation
from care.emr.resources.diagnostic_report.spec import (
DiagnosticReportCreateSpec,
DiagnosticReportListSpec,
DiagnosticReportObservationRequest,
DiagnosticReportRetrieveSpec,
DiagnosticReportReviewRequest,
DiagnosticReportUpdateSpec,
DiagnosticReportVerifyRequest,
StatusChoices,
)
from care.emr.resources.observation.spec import Performer, PerformerType


class DiagnosticReportFilters(FilterSet):
phase = CharFilter(field_name="based_on__phase", lookup_expr="iexact")
status = CharFilter(field_name="status", lookup_expr="iexact")
specimen = UUIDFilter(field_name="specimen__external_id")
based_on = UUIDFilter(field_name="based_on__external_id")

ordering = OrderingFilter(
fields=(
("created_date", "created_date"),
("modified_date", "modified_date"),
)
)


class DiagnosticReportViewSet(EMRModelViewSet):
database_model = DiagnosticReport
pydantic_model = DiagnosticReportCreateSpec
pydantic_update_model = DiagnosticReportUpdateSpec
pydantic_read_model = DiagnosticReportListSpec
pydantic_retrieve_model = DiagnosticReportRetrieveSpec
filter_backends = [DjangoFilterBackend]
filterset_class = DiagnosticReportFilters

def clean_create_data(self, request, *args, **kwargs):
clean_data = super().clean_create_data(request, *args, **kwargs)

clean_data["performer"] = self.request.user.external_id
khavinshankar marked this conversation as resolved.
Show resolved Hide resolved
return clean_data

@extend_schema(
request=DiagnosticReportObservationRequest,
responses={200: DiagnosticReportRetrieveSpec},
tags=["diagnostic_report"],
)
@action(detail=True, methods=["POST"])
def observations(self, request, *args, **kwargs):
data = DiagnosticReportObservationRequest(**request.data)
khavinshankar marked this conversation as resolved.
Show resolved Hide resolved
report: DiagnosticReport = self.get_object()

observations = []
for observation in data.observations:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is observation valueset validation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The observations are already validated by the specs
image

if not observation.performer:
observation.performer = Performer(
type=PerformerType.user,
id=str(request.user.external_id),
)

observation_instance = observation.de_serialize()
observation_instance.subject_id = report.subject.id
observation_instance.encounter = report.encounter
observation_instance.patient = report.subject

observations.append(observation_instance)

observation_instances = Observation.objects.bulk_create(observations)
report.result.set(observation_instances)
report.status = StatusChoices.partial
report.save()

return Response(
self.get_retrieve_pydantic_model().serialize(report).to_json(),
)

@extend_schema(
request=DiagnosticReportVerifyRequest,
responses={200: DiagnosticReportRetrieveSpec},
tags=["diagnostic_report"],
)
@action(detail=True, methods=["POST"])
def verify(self, request, *args, **kwargs):
data = DiagnosticReportVerifyRequest(**request.data)
report: DiagnosticReport = self.get_object()

if data.is_approved:
report.status = StatusChoices.preliminary
else:
report.status = StatusChoices.cancelled

report.issued = datetime.now(UTC)
report.save()

return Response(
self.get_retrieve_pydantic_model().serialize(report).to_json(),
)

@extend_schema(
request=DiagnosticReportReviewRequest,
responses={200: DiagnosticReportRetrieveSpec},
tags=["diagnostic_report"],
)
@action(detail=True, methods=["POST"])
def review(self, request, *args, **kwargs):
data = DiagnosticReportReviewRequest(**request.data)
report: DiagnosticReport = self.get_object()

if (
report.results_interpreter
and report.results_interpreter.external_id != request.user.external_id
):
return Response(
{"detail": "This report is assigned to a different user for review."},
status=403,
)

if data.is_approved:
report.status = StatusChoices.final
else:
report.status = StatusChoices.cancelled

report.conclusion = data.conclusion
report.results_interpreter = request.user
report.save()

return Response(
self.get_retrieve_pydantic_model().serialize(report).to_json(),
)
40 changes: 40 additions & 0 deletions care/emr/api/viewsets/service_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django_filters import CharFilter, FilterSet, OrderingFilter, UUIDFilter
from django_filters.rest_framework import DjangoFilterBackend

from care.emr.api.viewsets.base import EMRModelViewSet
from care.emr.models.service_request import ServiceRequest
from care.emr.resources.service_request.spec import (
ServiceRequestCreateSpec,
ServiceRequestListSpec,
ServiceRequestRetrieveSpec,
ServiceRequestUpdateSpec,
)


class ServiceRequestFilters(FilterSet):
phase = CharFilter(field_name="phase", lookup_expr="iexact")
subject = UUIDFilter(field_name="subject__external_id")
encounter = UUIDFilter(field_name="encounter__external_id")

ordering = OrderingFilter(
fields=(
("created_date", "created_date"),
("modified_date", "modified_date"),
)
)


class ServiceRequestViewSet(EMRModelViewSet):
database_model = ServiceRequest
pydantic_model = ServiceRequestCreateSpec
pydantic_update_model = ServiceRequestUpdateSpec
pydantic_read_model = ServiceRequestListSpec
pydantic_retrieve_model = ServiceRequestRetrieveSpec
filter_backends = [DjangoFilterBackend]
filterset_class = ServiceRequestFilters

def clean_create_data(self, request, *args, **kwargs):
clean_data = super().clean_create_data(request, *args, **kwargs)

clean_data["requester"] = self.request.user.external_id
return clean_data
143 changes: 143 additions & 0 deletions care/emr/api/viewsets/specimen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
from datetime import UTC, datetime
khavinshankar marked this conversation as resolved.
Show resolved Hide resolved

from django.db.models import Q
from django_filters import CharFilter, FilterSet, OrderingFilter, UUIDFilter
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response

from care.emr.api.viewsets.base import EMRModelViewSet
from care.emr.models.specimen import Specimen
from care.emr.resources.specimen.spec import (
SpecimenCollectRequest,
SpecimenCreateSpec,
SpecimenListSpec,
SpecimenProcessRequest,
SpecimenReceiveAtLabRequest,
SpecimenRetrieveSpec,
SpecimenSendToLabRequest,
SpecimenUpdateSpec,
StatusChoices,
)


class SpecimenFilters(FilterSet):
phase = CharFilter(field_name="request__phase", lookup_expr="iexact")
request = UUIDFilter(field_name="request__external_id")
encounter = UUIDFilter(field_name="request__encounter__external_id")

ordering = OrderingFilter(
fields=(
("created_date", "created_date"),
("modified_date", "modified_date"),
)
)


class SpecimenViewSet(EMRModelViewSet):
database_model = Specimen
pydantic_model = SpecimenCreateSpec
pydantic_update_model = SpecimenUpdateSpec
pydantic_read_model = SpecimenListSpec
pydantic_retrieve_model = SpecimenRetrieveSpec
filter_backends = [DjangoFilterBackend]
filterset_class = SpecimenFilters

def get_object(self) -> Specimen:
return get_object_or_404(
self.get_queryset(),
Q(external_id__iexact=self.kwargs[self.lookup_field])
| Q(identifier=self.kwargs[self.lookup_field])
| Q(accession_identifier=self.kwargs[self.lookup_field]),
)

@extend_schema(
request=SpecimenCollectRequest,
responses={200: SpecimenRetrieveSpec},
tags=["specimen"],
)
@action(detail=True, methods=["POST"])
def collect(self, request, *args, **kwargs):
data = SpecimenCollectRequest(**request.data)
specimen = self.get_object()

specimen.identifier = data.identifier
specimen.status = StatusChoices.available
specimen.collected_at = datetime.now(UTC)
specimen.collected_by = request.user
specimen.save()

return Response(
self.get_retrieve_pydantic_model().serialize(specimen).to_json(),
)

@extend_schema(
request=SpecimenSendToLabRequest,
responses={200: SpecimenRetrieveSpec},
tags=["specimen"],
)
@action(detail=True, methods=["POST"])
def send_to_lab(self, request, *args, **kwargs):
data = SpecimenSendToLabRequest(**request.data)
specimen = self.get_object()
service_request = specimen.request

service_request.location = data.lab
specimen.dispatched_at = datetime.now(UTC)
specimen.dispatched_by = request.user
service_request.save()
specimen.save()

return Response(
self.get_retrieve_pydantic_model().serialize(specimen).to_json(),
)

@extend_schema(
request=SpecimenReceiveAtLabRequest,
responses={200: SpecimenRetrieveSpec},
tags=["specimen"],
)
@action(detail=True, methods=["POST"])
def receive_at_lab(self, request, *args, **kwargs):
data = SpecimenReceiveAtLabRequest(**request.data)
specimen = self.get_object()

specimen.accession_identifier = data.accession_identifier
specimen.condition = data.condition
specimen.received_at = datetime.now(UTC)
specimen.received_by = request.user
specimen.note = data.note
specimen.save()

return Response(
self.get_retrieve_pydantic_model().serialize(specimen).to_json(),
)

@extend_schema(
request=SpecimenProcessRequest,
responses={200: SpecimenRetrieveSpec},
tags=["specimen"],
)
@action(detail=True, methods=["POST"])
def process(self, request, *args, **kwargs):
data = SpecimenProcessRequest(**request.data)
specimen = self.get_object()

processes = []
for process in data.process:
if not process.time:
process.time = datetime.now(UTC)

if not process.performer:
process.performer = request.user.external_id

processes.append(process.model_dump(mode="json"))

specimen.processing.extend(processes)
specimen.save()

return Response(
self.get_retrieve_pydantic_model().serialize(specimen).to_json(),
)
3 changes: 3 additions & 0 deletions care/emr/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
class EMRConfig(AppConfig):
name = "care.emr"
verbose_name = _("Electronic Medical Record")

def ready(self):
import care.emr.signals # noqa
Loading
Loading