Skip to content

Commit

Permalink
Improvements for events module (#1895)
Browse files Browse the repository at this point in the history
* enable latest events and minor optimizations

* serialize event actor
  • Loading branch information
sainak authored Feb 15, 2024
1 parent 1b55227 commit 3b91fd1
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 63 deletions.
2 changes: 2 additions & 0 deletions care/facility/api/serializers/events.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework.serializers import ModelSerializer, SerializerMethodField

from care.facility.models.events import EventType, PatientConsultationEvent
from care.users.api.serializers.user import UserBaseMinimumSerializer
from care.utils.ulid.serializers import ULIDField


Expand All @@ -24,6 +25,7 @@ def get_children(self, obj: EventType) -> list[EventType] | None:
class PatientConsultationEventDetailSerializer(ModelSerializer):
id = ULIDField(source="external_id", read_only=True)
event_type = EventTypeSerializer()
caused_by = UserBaseMinimumSerializer()

class Meta:
model = PatientConsultationEvent
Expand Down
4 changes: 3 additions & 1 deletion care/facility/api/viewsets/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class Meta:

class PatientConsultationEventViewSet(ReadOnlyModelViewSet):
serializer_class = PatientConsultationEventDetailSerializer
queryset = PatientConsultationEvent.objects.all()
queryset = PatientConsultationEvent.objects.all().select_related(
"event_type", "caused_by"
)
permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = PatientConsultationEventFilterSet
Expand Down
116 changes: 54 additions & 62 deletions care/facility/events/handler.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import json
from datetime import datetime

from celery import shared_task
from django.apps import apps
from django.core import serializers
from django.db import models, transaction
from django.db.models import Model
from django.db.models.query import QuerySet
from django.utils.timezone import now

from care.facility.models.events import ChangeType, EventType, PatientConsultationEvent
from care.utils.event_utils import CustomJSONEncoder, model_diff
from care.utils.event_utils import model_diff


def transform(obj, diff):
Expand All @@ -35,59 +33,54 @@ def transform(obj, diff):
@shared_task
def create_consultation_event_entry(
consultation_id: int,
object_model: str,
object_id: int,
object_instance: Model,
caused_by: int,
created_date: datetime,
diff: str = None,
diff: dict | None = None,
):
Model = apps.get_model("facility", object_model)

instance = Model.objects.get(id=object_id)

diff = json.loads(diff) if diff else None
change_type = ChangeType.UPDATED if diff else ChangeType.CREATED

data = transform(instance, diff)
data = transform(object_instance, diff)

object_fields = set(data.keys())
changed_fields = set(data.keys())

with transaction.atomic():
batch = []
groups = EventType.objects.filter(
model=object_model, fields__len__gt=0
).values_list("id", "fields")
for group_id, group_fields in groups:
if set(group_fields) & object_fields:
# PatientConsultationEvent.objects.select_for_update().filter(
# consultation_id=consultation_id,
# event_type=group_id,
# is_latest=True,
# created_date__lt=created_date,
# ).update(is_latest=False)
value = {}
for field in group_fields:
try:
value[field] = data[field]
except KeyError:
value[field] = getattr(instance, field, None)
batch.append(
PatientConsultationEvent(
consultation_id=consultation_id,
caused_by_id=caused_by,
event_type_id=group_id,
is_latest=True,
created_date=created_date,
object_model=object_model,
object_id=object_id,
value=value,
change_type=change_type,
meta={
"external_id": str(getattr(instance, "external_id", ""))
or None
},
)
batch = []
groups = EventType.objects.filter(
model=object_instance.__class__.__name__, fields__len__gt=0
).values_list("id", "fields")
for group_id, group_fields in groups:
if set(group_fields) & changed_fields:
PatientConsultationEvent.objects.select_for_update().filter(
consultation_id=consultation_id,
event_type=group_id,
is_latest=True,
object_model=object_instance.__class__.__name__,
object_id=object_instance.id,
created_date__lt=created_date,
).update(is_latest=False)
value = {}
for field in group_fields:
try:
value[field] = data[field]
except KeyError:
value[field] = getattr(object_instance, field, None)
batch.append(
PatientConsultationEvent(
consultation_id=consultation_id,
caused_by_id=caused_by,
event_type_id=group_id,
is_latest=True,
created_date=created_date,
object_model=object_instance.__class__.__name__,
object_id=object_instance.id,
value=value,
change_type=change_type,
meta={
"external_id": str(getattr(object_instance, "external_id", ""))
or None
},
)
)

PatientConsultationEvent.objects.bulk_create(batch)
return len(batch)
Expand All @@ -103,19 +96,18 @@ def create_consultation_events(
if created_date is None:
created_date = now()

if isinstance(objects, (QuerySet, list, tuple)):
if old is not None:
raise ValueError("diff is not available when objects is a list or queryset")
for obj in objects:
object_model = obj.__class__.__name__
with transaction.atomic():
if isinstance(objects, (QuerySet, list, tuple)):
if old is not None:
raise ValueError(
"diff is not available when objects is a list or queryset"
)
for obj in objects:
create_consultation_event_entry(
consultation_id, obj, caused_by, created_date
)
else:
diff = model_diff(old, objects) if old else None
create_consultation_event_entry(
consultation_id, object_model, obj.id, caused_by, created_date
consultation_id, objects, caused_by, created_date, diff
)
else:
diff = (
json.dumps(model_diff(old, objects), cls=CustomJSONEncoder) if old else None
)
object_model = objects.__class__.__name__
create_consultation_event_entry(
consultation_id, object_model, objects.id, caused_by, created_date, diff
)

0 comments on commit 3b91fd1

Please sign in to comment.