import logging
import traceback
from rest_framework import viewsets, permissions, status
from rest_framework.response import Response
from rest_framework.reverse import reverse
from ferrosoft.apps.befundung.api.serializers import (
ExaminationSerializer,
)
from ferrosoft.apps.befundung.models import (
Examination,
create_log,
ExaminationRequest,
ExaminationPhotoRequest,
)
from ferrosoft.apps.date_time import now_utc
from ferrosoft.apps.ferrobase.services.taskqueue import (
background_task_tenant,
TaskQueueError,
)
[docs]
class ExaminationViewSet(viewsets.ModelViewSet):
"""
API endpoint for examination reports of materials.
"""
queryset = Examination.objects.all()
serializer_class = ExaminationSerializer
permission_classes = [permissions.IsAuthenticated]
[docs]
def create(self, request, *args, **kwargs):
weighing_slip_id = ""
try:
payload, weighing_slip_id = self.save_request(request)
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
create_log(
"invalid examination data, weighing_slip_id={}; errors={}".format(
weighing_slip_id, serializer.errors
)
)
background_task_tenant(
"ferrosoft.apps.befundung.request.report_examination_errors",
str(payload.pk),
serializer.errors,
reverse(
"befundung:replay_request", args=[payload.pk], request=request
),
)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
exam = serializer.save()
weighing_slip_id = exam.weighing_slip_id
task_id = background_task_tenant(
"ferrosoft.apps.befundung.request.report_examination", str(exam.pk)
)
create_log(
"enqueued examination for processing, weighing_slip_id=%s; task_id=%s"
% (
weighing_slip_id,
task_id,
)
)
headers = self.get_success_headers(serializer.data)
return Response(
serializer.data, status=status.HTTP_201_CREATED, headers=headers
)
except TaskQueueError as e:
logging.exception("could not enqueue examination for processing")
create_log(
"could not enqueue examination for processing: weighing_slip_id=%s; error=%s\n%s"
% (weighing_slip_id, str(e), traceback.format_exc())
)
return Response(
{"error": "task queue unavailable"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
except Exception as e:
logging.exception("unexpected error during examination processing")
create_log(
"cannot create examination: weighing_slip_id=%s; error=%s\n%s"
% (weighing_slip_id, str(e), traceback.format_exc())
)
return Response(
{"error": "unexpected error"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
[docs]
def save_request(self, request) -> (ExaminationRequest, str):
# Save request for replay function. PowerApp sometimes sends unexpected data,
# breaking API contract, and it cannot be fixed on the other side.
#
# Photos are removed from ExaminationRequest, because it's a lot of data,
# which would render replay function unusable. Therefore, photos are stored
# separate.
data = request.data
if not isinstance(data, dict):
# Even though PowerApps sometimes sends garbage inside the payload,
# whole payload should never be garbage.
raise ValueError("Received garbage request")
photos = data.pop("photos", [])
weighing_slip_id = data.get("weighing_slip_id", "")
payload = ExaminationRequest.objects.create(
creation_date=now_utc(),
weighing_slip_id=weighing_slip_id,
payload=data,
)
if len(photos) > 0:
ExaminationPhotoRequest.objects.create(
request=payload,
payload=photos,
)
# Restore popped photos to request, for serializer to pick it up.
request.data["photos"] = photos
return payload, weighing_slip_id