import json
from pprint import pprint
from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from ferrosoft.apps.befundung.models import (
Reduction,
Refusal,
Photo,
Examination,
Settings,
ExaminationRequest,
ExaminationPhotoRequest,
)
[docs]
class SettingsSerializer(serializers.ModelSerializer):
[docs]
@classmethod
def get_instance_dict(cls) -> dict:
instance = Settings.get_instance()
return cls(instance).data
[docs]
class ReductionSerializer(serializers.ModelSerializer):
[docs]
class RefusalSerializer(serializers.ModelSerializer):
[docs]
class PhotoSerializer(serializers.ModelSerializer):
image_file = Base64ImageField()
[docs]
class ExaminationSerializer(serializers.ModelSerializer):
reductions = ReductionSerializer(many=True)
refusals = RefusalSerializer(many=True)
photos = PhotoSerializer(many=True)
[docs]
def create(self, validated_data):
reductions_data = validated_data.pop("reductions")
refusals_data = validated_data.pop("refusals")
photos_data = validated_data.pop("photos")
exam = Examination.objects.create(**validated_data)
Reduction.objects.bulk_create(
[Reduction(examination=exam, **data) for data in reductions_data]
)
Refusal.objects.bulk_create(
[Refusal(examination=exam, **data) for data in refusals_data]
)
Photo.objects.bulk_create(
[Photo(examination=exam, **data) for data in photos_data]
)
return exam
[docs]
def to_internal_value(self, data):
# Before introduction of DRF, a legacy format was in use, where reductions
# and refusals are dictionaries. They need to be transformed transparently
# to the current format.
reductions = data.get("reductions", {})
if isinstance(reductions, dict):
data["reductions"] = [
{
"reduction_type": rtype,
"value": value or 0,
}
for rtype, value in reductions.items()
]
elif isinstance(reductions, list):
# Need to filter garbage data out, i.e. custom reductions without a label.
data["reductions"] = list(
filter(
lambda r: r.get("reduction_type", "") != "custom"
or r.get("label") != "",
reductions,
)
)
refusals = data.get("refusals", {})
if isinstance(refusals, dict):
data["refusals"] = [
{
"material_name": name,
"value": value or 0,
}
for name, value in refusals.items()
]
power_photos = self._get_power_photos(
data.get("photos", None), data.get("weighing_slip_id", None)
)
data["photos"] = [
{
"image_file": photo.get("Value", ""),
}
for photo in power_photos
]
# Comment may come in as array, must be transformed to delimiter separated value.
comment = data.get("comment", None)
if isinstance(comment, list):
data["comment"] = "\n".join(comment)
return super().to_internal_value(data)
def _get_power_photos(self, unvalidated_photos, weighing_slip_id: str | None):
if len(unvalidated_photos) > 0:
return self._parse_power_photos(unvalidated_photos)
# If photos are missing, this is likely a replay request.
# Load photos from ExaminationPhotoRequest.
if weighing_slip_id is None:
return []
photo_request = ExaminationPhotoRequest.objects.filter(
request__weighing_slip_id=weighing_slip_id
).first()
if photo_request is None:
return []
return self._parse_power_photos(photo_request.payload)
def _parse_power_photos(self, unvalidated_photos):
# PowerApps may send photos either as a normal list of objects
# or as a JSON-encoded list. Support both cases.
if isinstance(unvalidated_photos, list):
return self._ensure_power_photos(unvalidated_photos)
elif isinstance(unvalidated_photos, str):
return self._ensure_power_photos(json.loads(unvalidated_photos))
else:
raise ValueError("Photos must be a list or a string")
@staticmethod
def _ensure_power_photos(unvalidated_photos: list) -> list:
photos = []
for photo in unvalidated_photos:
if isinstance(photo, dict):
photos.append(photo)
return photos