Repository: USEPA/haztrak (python)

Issue:
PaperSignaturesInfo serializer
# 🚀 Feature Request

Add a model serializer for the `PaperSignature` model. Incorporate into the existing manifest related serializers.

depends on #374 

<!--
Please provide a clear and concise description of what you'd like to see. Things like...
1. what is the expected behavior?
2. your opinion on what the default(s) should be?
-->


Pull request:
375 papersignaturesinfo serializer
## Description

This PR adds the `PaperSignatureSerializer` class and incorperates it into the current `ManifestHandlerSerializer` and it's corresponding methods

## Issue ticket number and link
close #375 

<!-- Bonus points for using GitHub's keywords (e.g., closes #123) -->

## Checklist

<!-- We're so happy your contributing, before we do, there are some things we would like to know before merging your fabulous changes. -->

- [x] I have added tests that prove my fix is effective or that my feature works
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings

## Other Stuff


Patch:
diff --git a/server/apps/trak/models/handler_model.py b/server/apps/trak/models/handler_model.py
index 26f5470b..d0609257 100644
--- a/server/apps/trak/models/handler_model.py
+++ b/server/apps/trak/models/handler_model.py
@@ -6,7 +6,7 @@ from django.db import models
 
 from .address_model import Address
 from .contact_model import Contact, EpaPhone
-from .signature_model import ESignature
+from .signature_model import ESignature, PaperSignature
 
 logger = logging.getLogger(__name__)
 
@@ -172,9 +172,12 @@ class ManifestHandlerManager(models.Manager):
             handler (dict): handler data in (ordered)dict format
         """
         e_signatures = []
+        paper_signature = None
         if "e_signatures" in handler_data:
             e_signatures = handler_data.pop("e_signatures")
         logger.debug(f"e_signature data {e_signatures}")
+        if "paper_signature" in handler_data:
+            paper_signature = PaperSignature.objects.create(**handler_data.pop("paper_signature"))
         try:
             if Handler.objects.filter(epa_id=handler_data["handler"]["epa_id"]).exists():
                 handler = Handler.objects.get(epa_id=handler_data["handler"]["epa_id"])
@@ -182,7 +185,9 @@ class ManifestHandlerManager(models.Manager):
             else:
                 handler = Handler.objects.create_handler(**handler_data["handler"])
                 logger.debug(f"Handler created {handler}")
-            manifest_handler = ManifestHandler.objects.create(handler=handler)
+            manifest_handler = ManifestHandler.objects.create(
+                handler=handler, paper_signature=paper_signature
+            )
             logger.debug(f"ManifestHandler created {manifest_handler}")
             for e_signature_data in e_signatures:
                 e_sig = ESignature.objects.create_e_signature(
diff --git a/server/apps/trak/models/signature_model.py b/server/apps/trak/models/signature_model.py
index 42543dc5..a3169008 100644
--- a/server/apps/trak/models/signature_model.py
+++ b/server/apps/trak/models/signature_model.py
@@ -148,7 +148,8 @@ class ESignature(models.Model):
 
 class PaperSignature(models.Model):
     """
-    Contains printed name of the handler Signee and Date of Signature.
+    Contains printed name of the handler Signee and
+    Date of Signature for paper manifests.
     """
 
     printed_name = models.CharField(
diff --git a/server/apps/trak/serializers/handler_ser.py b/server/apps/trak/serializers/handler_ser.py
index 054b1933..9447483e 100644
--- a/server/apps/trak/serializers/handler_ser.py
+++ b/server/apps/trak/serializers/handler_ser.py
@@ -6,7 +6,7 @@ from apps.trak.models import ESignature, Handler, ManifestHandler
 from apps.trak.serializers import AddressSerializer
 
 from .contact_ser import ContactSerializer, EpaPhoneSerializer
-from .signature_ser import ESignatureSerializer
+from .signature_ser import ESignatureSerializer, PaperSignatureSerializer
 from .trak_ser import TrakBaseSerializer
 
 
@@ -97,6 +97,10 @@ class ManifestHandlerSerializer(HandlerSerializer):
         many=True,
         required=False,
     )
+    paperSignatureInfo = PaperSignatureSerializer(
+        source="paper_signature",
+        required=False,
+    )
 
     def create(self, validated_data: Dict):
         e_signatures_data = []
@@ -117,11 +121,12 @@ class ManifestHandlerSerializer(HandlerSerializer):
         return representation
 
     def to_internal_value(self, data: Dict):
+        instance = {}
         if "electronicSignaturesInfo" in data:
-            e_signature_data = data.pop("electronicSignaturesInfo")
-            instance = {"handler": data, "electronicSignaturesInfo": e_signature_data}
-        else:
-            instance = {"handler": data}
+            instance["electronicSignaturesInfo"] = data.pop("electronicSignaturesInfo")
+        if "paperSignatureInfo" in data:
+            instance["paperSignatureInfo"] = data.pop("paperSignatureInfo")
+        instance["handler"] = data
         return super().to_internal_value(instance)
 
     class Meta:
@@ -129,4 +134,5 @@ class ManifestHandlerSerializer(HandlerSerializer):
         fields = [
             "handler",
             "electronicSignaturesInfo",
+            "paperSignatureInfo",
         ]
diff --git a/server/apps/trak/serializers/signature_ser.py b/server/apps/trak/serializers/signature_ser.py
index 57f0d5d0..eb987d2d 100644
--- a/server/apps/trak/serializers/signature_ser.py
+++ b/server/apps/trak/serializers/signature_ser.py
@@ -2,7 +2,7 @@ from typing import Dict
 
 from rest_framework import serializers
 
-from apps.trak.models import ESignature, Signer
+from apps.trak.models import ESignature, PaperSignature, Signer
 from apps.trak.serializers.contact_ser import EpaPhoneSerializer
 from apps.trak.serializers.trak_ser import TrakBaseSerializer
 
@@ -109,3 +109,32 @@ class ESignatureSerializer(TrakBaseSerializer):
             "onBehalf",
             "order",
         ]
+
+
+class PaperSignatureSerializer(TrakBaseSerializer):
+    """
+    Serializer for Paper Signature on manifest which indicates the change
+    of custody with paper manifests
+    """
+
+    printedName = serializers.CharField(
+        source="printed_name",
+        required=False,
+    )
+    signatureDate = serializers.DateTimeField(
+        source="sign_date",
+        required=False,
+    )
+
+    def update(self, instance, validated_data):
+        return super().update(instance, validated_data)
+
+    def create(self, validated_data: Dict):
+        return super().create(validated_data)
+
+    class Meta:
+        model = PaperSignature
+        fields = [
+            "printedName",
+            "signatureDate",
+        ]


Test patch:
diff --git a/server/apps/trak/tests/conftest.py b/server/apps/trak/tests/conftest.py
index cc6cd40f..5509d738 100644
--- a/server/apps/trak/tests/conftest.py
+++ b/server/apps/trak/tests/conftest.py
@@ -42,6 +42,7 @@ TEST_MANIFEST_JSON = f"{JSON_DIR}/test_manifest_100033134ELC.json"
 TEST_SITE_PERM_JSON = f"{JSON_DIR}/site_permission.json"
 TEST_EPA_PERM_JSON = f"{JSON_DIR}/epa_permission.json"
 TEST_HANDLER_JSON = f"{JSON_DIR}/test_handler.json"
+TEST_PAPER_HANDLER_JSON = f"{JSON_DIR}/paper_manifest_handler.json"
 TEST_E_SIGNATURE_JSON = f"{JSON_DIR}/test_e_signature.json"
 
 
@@ -190,6 +191,12 @@ def wasteline_json() -> Dict:
         return json.load(f)
 
 
+@pytest.fixture
+def paper_manifest_handler_json() -> Dict:
+    with open(TEST_PAPER_HANDLER_JSON, "r") as f:
+        return json.load(f)
+
+
 @pytest.fixture
 def phone_json(db) -> Dict:
     with open(TEST_PHONE_JSON, "r") as f:
@@ -220,7 +227,16 @@ def handler_serializer(db, handler_json) -> HandlerSerializer:
 
 @pytest.fixture
 def manifest_handler_serializer(db, handler_json) -> ManifestHandlerSerializer:
-    return ManifestHandlerSerializer(data=handler_json)
+    manifest_handler_serializer = ManifestHandlerSerializer(data=handler_json)
+    manifest_handler_serializer.is_valid()
+    return manifest_handler_serializer
+
+
+@pytest.fixture
+def paper_handler_serializer(db, paper_manifest_handler_json) -> ManifestHandlerSerializer:
+    handler_serializer = ManifestHandlerSerializer(data=paper_manifest_handler_json)
+    handler_serializer.is_valid()
+    return handler_serializer
 
 
 @pytest.fixture
diff --git a/server/apps/trak/tests/resources/json/paper_manifest_handler.json b/server/apps/trak/tests/resources/json/paper_manifest_handler.json
new file mode 100644
index 00000000..f6613684
--- /dev/null
+++ b/server/apps/trak/tests/resources/json/paper_manifest_handler.json
@@ -0,0 +1,52 @@
+{
+  "epaSiteId": "VA988177803",
+  "name": "WORLD OIL RECYCLING",
+  "modified": false,
+  "registered": true,
+  "paperSignatureInfo": {
+    "printedName": "gen",
+    "signatureDate": "2022-06-07T01:00:00.000+00:00"
+  },
+  "mailingAddress": {
+    "address1": "15 CLOVERFIELD LANE",
+    "city": "LOS ANGELES",
+    "state": {
+      "code": "VA",
+      "name": "VIRGINIA"
+    },
+    "country": {
+      "code": "US",
+      "name": "UNITED STATES"
+    },
+    "zip": "12345"
+  },
+  "siteAddress": {
+    "address1": "15 CLOVERFIELD LANE",
+    "city": "LOS ANGELES",
+    "state": {
+      "code": "VA",
+      "name": "VIRGINIA"
+    },
+    "country": {
+      "code": "US",
+      "name": "UNITED STATES"
+    },
+    "zip": "12345"
+  },
+  "contact": {
+    "firstName": "JOHN",
+    "middleInitial": "Q",
+    "lastName": "TESTER",
+    "phone": {
+      "number": "703-308-0023"
+    },
+    "email": "EMANIFEST@EPA.GOV"
+  },
+  "emergencyPhone": {
+    "number": "911-911-0000"
+  },
+  "gisPrimary": false,
+  "canEsign": true,
+  "limitedEsign": true,
+  "hasRegisteredEmanifestUser": true
+}
diff --git a/server/apps/trak/tests/serializers/test_handler_serializer.py b/server/apps/trak/tests/serializers/test_handler_serializer.py
index 2c0ffe69..27b2c773 100644
--- a/server/apps/trak/tests/serializers/test_handler_serializer.py
+++ b/server/apps/trak/tests/serializers/test_handler_serializer.py
@@ -1,25 +1,29 @@
-import pytest
-
-from apps.trak.models import Handler, ManifestHandler
+from apps.trak.models import Handler, ManifestHandler, PaperSignature
+from apps.trak.serializers import ManifestHandlerSerializer
 
 
 class TestManifestHandlerSerializer:
-    def test_m_handler_serializes(self, manifest_handler_serializer) -> None:
-        assert manifest_handler_serializer.is_valid() is True
+    def test_m_handler_serializes(self, handler_json) -> None:
+        manifest_handler_serializer = ManifestHandlerSerializer(data=handler_json)
+        assert manifest_handler_serializer.is_valid()
 
     def test_serializer_saves_handler(self, db, manifest_handler_serializer) -> None:
-        if manifest_handler_serializer.is_valid():
-            manifest_handler = manifest_handler_serializer.save()
-            assert isinstance(manifest_handler, ManifestHandler)
-        else:
-            pytest.fail()
+        manifest_handler = manifest_handler_serializer.save()
+        assert isinstance(manifest_handler, ManifestHandler)
+
+    def test_paper_manifest_handler_serializes(self, paper_manifest_handler_json) -> None:
+        manifest_handler_serializer = ManifestHandlerSerializer(data=paper_manifest_handler_json)
+        assert manifest_handler_serializer.is_valid()
+
+    def test_creates_paper_signature(self, db, paper_handler_serializer) -> None:
+        manifest_handler: ManifestHandler = paper_handler_serializer.save()
+        assert isinstance(manifest_handler.paper_signature, PaperSignature)
 
     def test_serializer_flattens_foreign_keys(self, manifest_handler_serializer) -> None:
-        if manifest_handler_serializer.is_valid():
-            # The ManifestHandler holds a foreign key to a Handler instance
-            # however it should flatten that representation.
-            assert "epaSiteId" in manifest_handler_serializer.data
-            assert "handler" not in manifest_handler_serializer.data
+        # The ManifestHandler holds a foreign key to a Handler instance
+        # however it should flatten that representation.
+        assert "epaSiteId" in manifest_handler_serializer.data
+        assert "handler" not in manifest_handler_serializer.data
 
 
 class TestHandlerSerializer: