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

feat: Add service request history for asset #1277

Merged
merged 39 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6bae0be
feat: Add service history to assets
Ashesh3 Apr 27, 2023
9c4c3a7
Merge branch 'master' into asset-service-history
Ashesh3 Jul 14, 2023
c5a8a43
Make merge migration
Ashesh3 Jul 14, 2023
763db49
make requested changes
Ashesh3 Jul 21, 2023
ea4107a
Run pre-commit
Ashesh3 Jul 21, 2023
55c63ec
Merge branch 'master' into asset-service-history
Ashesh3 Jul 24, 2023
8339fe5
Squash migrations
Ashesh3 Jul 24, 2023
d3723ee
fix syntax error in squashed migration file
Ashesh3 Jul 25, 2023
69fc3a4
Merge branch 'master' into asset-service-history
Ashesh3 Jul 25, 2023
4590131
Merge branch 'master' into asset-service-history
vigneshhari Aug 8, 2023
0ae2f5d
Merge branch 'master' into asset-service-history
Ashesh3 Aug 9, 2023
9d0efdd
Delete squashed migrations
Ashesh3 Aug 9, 2023
ded7f01
merge migration
Ashesh3 Aug 9, 2023
87247f7
add missing migration
Ashesh3 Aug 9, 2023
a96e37b
squash migrations
Ashesh3 Aug 9, 2023
243b657
Update care/facility/models/asset.py
Ashesh3 Aug 9, 2023
646ae86
Update care/facility/api/serializers/asset.py
Ashesh3 Aug 9, 2023
8df1ab0
Rebase migrations
Ashesh3 Aug 9, 2023
b7324fd
Add tests for service history
Ashesh3 Aug 10, 2023
9c6259d
Add edit history
Ashesh3 Aug 23, 2023
e3cc7be
Update tests
Ashesh3 Aug 23, 2023
ee8008e
Merge branch 'master' into asset-service-history
Ashesh3 Aug 23, 2023
95ca3fe
Merge migrations
Ashesh3 Aug 23, 2023
4b884d4
Nest router and fix tests
Ashesh3 Aug 23, 2023
77b516d
fix condition
Ashesh3 Aug 23, 2023
f9a9d5e
rename endpoint
Ashesh3 Aug 23, 2023
8a79460
include nested route
Ashesh3 Aug 23, 2023
f6430b2
Add kwarg filter to queryset
Ashesh3 Aug 23, 2023
af8a37b
Fix tests
Ashesh3 Aug 23, 2023
0914071
Update care/facility/api/serializers/asset.py
Ashesh3 Aug 23, 2023
48fbe76
Update care/facility/api/serializers/asset.py
Ashesh3 Aug 23, 2023
b0b7a3b
Apply suggestions from code review
Ashesh3 Aug 24, 2023
30c539d
Update migrations
Ashesh3 Aug 24, 2023
0c8ac90
fix linting
Ashesh3 Aug 24, 2023
286ccc5
update migration
Ashesh3 Aug 24, 2023
3ef3025
Merge branch 'master' into asset-service-history
vigneshhari Aug 24, 2023
0b07b89
Update migration
Ashesh3 Aug 24, 2023
59cd30a
Merge branch 'master' into asset-service-history
Ashesh3 Aug 25, 2023
6be83bb
Update migrations
Ashesh3 Aug 25, 2023
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
105 changes: 94 additions & 11 deletions care/facility/api/serializers/asset.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import enum
from datetime import datetime

from django.core.cache import cache
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils.timezone import now
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.serializers import (
CharField,
Expand All @@ -20,6 +21,8 @@
Asset,
AssetAvailabilityRecord,
AssetLocation,
AssetService,
AssetServiceEdit,
AssetTransaction,
UserDefaultAssetLocation,
)
Expand Down Expand Up @@ -61,12 +64,62 @@ class Meta:
read_only_fields = TIMESTAMP_FIELDS


class AssetBareMinimumSerializer(ModelSerializer):
id = UUIDField(source="external_id", read_only=True)

class Meta:
model = Asset
fields = ("name", "id")


class AssetServiceEditSerializer(ModelSerializer):
id = UUIDField(source="asset_service.external_id", read_only=True)
edited_by = UserBaseMinimumSerializer(read_only=True)

class Meta:
model = AssetServiceEdit
exclude = ("asset_service",)


class AssetServiceSerializer(ModelSerializer):
id = UUIDField(source="external_id", read_only=True)
edits = AssetServiceEditSerializer(many=True)

class Meta:
model = AssetService
exclude = ("deleted",)

def update(self, instance, validated_data):
user = self.context["request"].user
serviced_on = validated_data.get("serviced_on", instance.serviced_on)
note = validated_data.get("note", instance.note)
if serviced_on == instance.serviced_on and note == instance.note:
return instance

with transaction.atomic():
edit = AssetServiceEdit(
asset_service=instance,
edited_on=now(),
edited_by=user,
serviced_on=serviced_on,
note=note,
)
edit.save()

updated_instance = super().update(instance, validated_data)

return updated_instance


class AssetSerializer(ModelSerializer):
id = UUIDField(source="external_id", read_only=True)
status = ChoiceField(choices=Asset.StatusChoices, read_only=True)
asset_type = ChoiceField(choices=Asset.AssetTypeChoices)
location_object = AssetLocationSerializer(source="current_location", read_only=True)
location = UUIDField(write_only=True, required=True)
last_service = AssetServiceSerializer(read_only=True)
last_serviced_on = serializers.DateField(write_only=True, required=False)
note = serializers.CharField(write_only=True, required=False)

class Meta:
model = Asset
Expand Down Expand Up @@ -125,10 +178,48 @@ def validate(self, attrs):

return super().validate(attrs)

def create(self, validated_data):
last_serviced_on = validated_data.pop("last_serviced_on", None)
note = validated_data.pop("note", None)
with transaction.atomic():
asset_instance = super().create(validated_data)
if last_serviced_on or note:
asset_service = AssetService(
asset=asset_instance, serviced_on=last_serviced_on, note=note
)
asset_service.save()
asset_instance.last_service = asset_service
asset_instance.save(update_fields=["last_service"])
return asset_instance

def update(self, instance, validated_data):
user = self.context["request"].user

with transaction.atomic():
if validated_data.get("last_serviced_on") and (
not instance.last_service
or instance.last_service.serviced_on
!= validated_data.get(
"last_serviced_on", instance.last_service.serviced_on
)
or instance.last_service.note
!= validated_data.get("note", instance.last_service.note)
):
asset_service = AssetService(
asset=instance,
serviced_on=validated_data.get("last_serviced_on"),
note=validated_data.get("note"),
)
asset_service_initial_edit = AssetServiceEdit(
asset_service=asset_service,
edited_on=now(),
edited_by=user,
serviced_on=asset_service.serviced_on,
note=asset_service.note,
)
asset_service.save()
asset_service_initial_edit.save()
instance.last_service = asset_service

if (
"current_location" in validated_data
and instance.current_location != validated_data["current_location"]
Expand All @@ -151,14 +242,6 @@ def update(self, instance, validated_data):
return updated_instance


class AssetBareMinimumSerializer(ModelSerializer):
id = UUIDField(source="external_id", read_only=True)

class Meta:
model = Asset
fields = ("name", "id")


class AssetTransactionSerializer(ModelSerializer):
id = UUIDField(source="external_id", read_only=True)
asset = AssetBareMinimumSerializer(read_only=True)
Expand Down Expand Up @@ -190,7 +273,7 @@ class Meta:

class AssetActionSerializer(Serializer):
def actionChoices():
actions: list[enum.Enum] = [
actions = [
OnvifAsset.OnvifActions,
HL7MonitorAsset.HL7MonitorActions,
VentilatorAsset.VentilatorActions,
Expand Down
53 changes: 53 additions & 0 deletions care/facility/api/viewsets/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
AssetAvailabilitySerializer,
AssetLocationSerializer,
AssetSerializer,
AssetServiceSerializer,
AssetTransactionSerializer,
DummyAssetOperateResponseSerializer,
DummyAssetOperateSerializer,
Expand All @@ -35,6 +36,7 @@
Asset,
AssetAvailabilityRecord,
AssetLocation,
AssetService,
AssetTransaction,
UserDefaultAssetLocation,
)
Expand Down Expand Up @@ -338,3 +340,54 @@ def get_queryset(self):
| Q(to_location__facility__id__in=allowed_facilities)
)
return queryset


class AssetServiceFilter(filters.FilterSet):
qr_code_id = filters.CharFilter(field_name="asset__qr_code_id")
external_id = filters.CharFilter(field_name="asset__external_id")


class AssetServiceViewSet(
ListModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
GenericViewSet,
):
queryset = (
Ashesh3 marked this conversation as resolved.
Show resolved Hide resolved
AssetService.objects.all()
.select_related(
"asset",
)
.prefetch_related("edits")
.order_by("-created_date")
)
serializer_class = AssetServiceSerializer

permission_classes = (IsAuthenticated,)

lookup_field = "external_id"

filter_backends = (filters.DjangoFilterBackend,)
filterset_class = AssetServiceFilter

def get_queryset(self):
user = self.request.user
queryset = self.queryset.filter(
asset__external_id=self.kwargs.get("asset_external_id")
)
if user.is_superuser:
pass
elif user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]:
queryset = queryset.filter(
asset__current_location__facility__state=user.state
)
elif user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]:
queryset = queryset.filter(
asset__current_location__facility__district=user.district
)
else:
allowed_facilities = get_accessible_facilities(user)
queryset = queryset.filter(
asset__current_location__facility__id__in=allowed_facilities
)
return queryset
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Generated by Django 4.2.2 on 2023-08-24 06:32

import uuid

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


def move_last_serviced_on_and_notes(apps, schema_editor):
Asset = apps.get_model("facility", "Asset")
AssetService = apps.get_model("facility", "AssetService")

asset_services = []
asset_linked_last_services = []
for asset in Asset.objects.all():
if asset.last_serviced_on or asset.notes:
service_record = AssetService(
asset=asset, serviced_on=asset.last_serviced_on, note=asset.notes
)
asset_services.append(service_record)
asset.last_service = service_record
asset_linked_last_services.append(asset)

AssetService.objects.bulk_create(asset_services)
Asset.objects.bulk_update(asset_linked_last_services, ["last_service"])


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
(
"facility",
"0379_rename_prescribed_medication_patientconsultation_treatment_plan",
),
]

operations = [
migrations.CreateModel(
name="AssetService",
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)),
("serviced_on", models.DateField(default=None, null=True)),
("note", models.TextField(blank=True, default="", null=True)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="AssetServiceEdit",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("edited_on", models.DateTimeField(auto_now_add=True)),
("serviced_on", models.DateField()),
("note", models.TextField()),
(
"asset_service",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="edits",
to="facility.assetservice",
),
),
(
"edited_by",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["-edited_on"],
},
),
migrations.AddField(
model_name="assetservice",
name="asset",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT, to="facility.asset"
),
),
migrations.AddField(
model_name="asset",
name="last_service",
field=models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="last_service",
to="facility.assetservice",
),
),
migrations.RunPython(
code=move_last_serviced_on_and_notes,
reverse_code=django.db.migrations.operations.special.RunPython.noop,
),
migrations.RemoveField(
model_name="asset",
name="last_serviced_on",
),
migrations.RemoveField(
model_name="asset",
name="notes",
),
]
Loading
Loading