Skip to content

Commit

Permalink
Create e manifest (#581)
Browse files Browse the repository at this point in the history
* scaffold CreateRcraManifestView and url routing

* new endpoint routing (and placeholder) for for create manifest service

* scaffold create_rcra_manifest celery task

* add create_rcra_manifest service

* use module local logger instead of class logger

* semi working validation before creating electronic manifest through rcrainfo

* adjust additional info serializer adjustments so null values are not passed to RCRAInfo services
  • Loading branch information
dpgraham4401 authored Aug 28, 2023
1 parent 1e51324 commit 283b408
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 23 deletions.
5 changes: 5 additions & 0 deletions client/src/components/Manifest/ManifestForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React, { createContext, useEffect, useState } from 'react';
import { Alert, Button, Col, Form, Row } from 'react-bootstrap';
import { FormProvider, SubmitHandler, useFieldArray, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { htApi } from 'services';
import { ContactForm, PhoneForm } from './Contact';
import { AddHandler, GeneratorForm, Handler } from './Handler';
import { Manifest, manifestSchema, ManifestStatus } from './manifestSchema';
Expand Down Expand Up @@ -85,6 +86,10 @@ export function ManifestForm({
// Function to handle form submission
const onSubmit: SubmitHandler<Manifest> = (data: Manifest) => {
console.log('Manifest Submitted', data);
htApi
.post('/rcra/manifest/create', data)
.then((response) => console.log(response))
.catch((r) => console.error(r));
};

// Generator state and methods
Expand Down
7 changes: 7 additions & 0 deletions client/src/features/manifest/NewManifest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import { useParams } from 'react-router-dom';
import { useAppSelector } from 'store';
import { siteByEpaIdSelector } from 'store/rcraProfileSlice/rcraProfile.slice';

/**
* NewManifest component allows a user to create a new electronic manifest.
* It requires that the site the user is drafting the manifest for is specified,
* if haztrak cannot determine the site, the user will be prompted to select the site before
* presenting the manifest form.
* @constructor
*/
export function NewManifest() {
useTitle('New Manifest');
const { siteId } = useParams();
Expand Down
1 change: 1 addition & 0 deletions server/apps/sites/serializers/contact_ser.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class RcraPhoneSerializer(SitesBaseSerializer):
number = serializers.CharField()
extension = serializers.CharField(
required=False,
allow_blank=True,
)

class Meta:
Expand Down
30 changes: 19 additions & 11 deletions server/apps/trak/serializers/manifest_ser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

from apps.sites.models import RcraStates, Role
from apps.trak.models import Manifest
from apps.trak.models.manifest_models import AdditionalInfo, ImportInfo, PortOfEntry, CorrectionInfo
from apps.trak.models.manifest_models import (
AdditionalInfo,
CorrectionInfo,
ImportInfo,
PortOfEntry,
)
from apps.trak.serializers.handler_ser import HandlerSerializer
from apps.trak.serializers.signature_ser import ESignatureSerializer

Expand All @@ -18,26 +23,28 @@

class AdditionalInfoSerializer(serializers.ModelSerializer):
originalManifestTrackingNumbers = serializers.JSONField(
allow_null=True,
allow_null=False,
required=False,
source="original_mtn",
)
newManifestDestination = serializers.CharField(
allow_null=True,
allow_null=False,
required=False,
allow_blank=True,
source="new_destination",
)
consentNumber = serializers.CharField(
allow_null=True,
allow_null=False,
required=False,
allow_blank=True,
source="consent_number",
)
comments = serializers.JSONField(
allow_null=True,
allow_null=False,
required=False,
)
handlingInstructions = serializers.CharField(
allow_null=True,
allow_null=False,
allow_blank=True,
required=False,
source="handling_instructions",
Expand Down Expand Up @@ -100,6 +107,7 @@ class ManifestSerializer(TrakBaseSerializer):
manifestTrackingNumber = serializers.CharField(
source="mtn",
required=False,
allow_blank=True,
)
# status
submissionType = serializers.CharField(
Expand Down Expand Up @@ -320,7 +328,7 @@ class CorrectionInfoSerializer(TrakBaseSerializer):
"""
Serializer for Correction Info
"""

versionNumber = serializers.CharField(
source="version_number",
required=False,
Expand All @@ -343,18 +351,18 @@ class CorrectionInfoSerializer(TrakBaseSerializer):
allow_null=True,
)
epaSiteId = serializers.CharField(
source = "epa_site_id",
source="epa_site_id",
required=False,
allow_null=True,
)
initiatorRole = serializers.ChoiceField(
source = "initiator_role",
source="initiator_role",
choices=Role.choices,
required=False,
allow_null=True,
)
updateRole = serializers.ChoiceField(
source = "update_role",
source="update_role",
choices=Role.choices,
required=False,
allow_null=True,
Expand All @@ -370,4 +378,4 @@ class Meta:
"epaSiteId",
"initiatorRole",
"updateRole",
]
]
32 changes: 22 additions & 10 deletions server/apps/trak/services/manifest_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from apps.trak.serializers import ManifestSerializer, QuickerSignSerializer
from apps.trak.tasks import pull_manifest

logger = logging.getLogger(__name__)


class ManifestService:
"""
Expand All @@ -20,7 +22,6 @@ class ManifestService:
def __init__(self, *, username: str, rcrainfo: RcrainfoService = None):
self.username = username
self.rcrainfo = rcrainfo or RcrainfoService(api_username=self.username)
self.logger = logging.getLogger(__name__)

def __repr__(self):
return (
Expand All @@ -30,22 +31,22 @@ def __repr__(self):
def _retrieve_manifest(self, mtn: str):
response = self.rcrainfo.get_manifest(mtn)
if response.ok:
self.logger.debug(f"manifest pulled {mtn}")
logger.debug(f"manifest pulled {mtn}")
return response.json()
else:
self.logger.warning(f"error retrieving manifest {mtn}")
logger.warning(f"error retrieving manifest {mtn}")
raise RequestException(response.json())

@transaction.atomic
def _save_manifest(self, manifest_json: dict) -> Manifest:
serializer = ManifestSerializer(data=manifest_json)
if serializer.is_valid():
self.logger.debug("manifest serializer is valid")
logger.debug("manifest serializer is valid")
manifest = serializer.save()
self.logger.info(f"saved manifest {manifest.mtn}")
logger.info(f"saved manifest {manifest.mtn}")
return manifest
else:
self.logger.warning(f"malformed serializer data: {serializer.errors}")
logger.warning(f"malformed serializer data: {serializer.errors}")
raise Exception(serializer.errors)

def search_rcra_mtn(
Expand Down Expand Up @@ -117,7 +118,7 @@ def pull_manifests(self, tracking_numbers: List[str]) -> Dict[str, List[str]]:
manifest = self._save_manifest(manifest_json)
results["success"].append(manifest.mtn)
except Exception as exc:
self.logger.warning(f"error pulling manifest {mtn}: {exc}")
logger.warning(f"error pulling manifest {mtn}: {exc}")
results["error"].append(mtn)
return results

Expand All @@ -143,20 +144,31 @@ def sign_manifest(self, signature: QuickerSign) -> dict[str, list[str]]:
mtn=[manifest["manifestTrackingNumber"]], username=self.username
)
else:
self.logger.warning(
logger.warning(
f"Error Quicker signing manifests, "
f"response: {response.status_code} {response.json()}"
)
results["error"].extend(results["success"]) # Temporary
return results

def _filter_mtn(self, signature: QuickerSign) -> dict[str, list[str]]:
def create_rcra_manifest(self, *, manifest: Dict):
"""
Create a manifest in RCRAInfo through the RESTful API.
:param manifest: Dict
:return:
"""
logger.info("create rcra manifest with arguments: ", manifest)
resp = self.rcrainfo.save_manifest(manifest)
print("resp: ", resp.json())

@staticmethod
def _filter_mtn(signature: QuickerSign) -> dict[str, list[str]]:
results = {"success": [], "error": []}
site_filter = Manifest.objects.get_handler_query(signature.site_id, signature.site_type)
existing_mtn = Manifest.objects.existing_mtn(site_filter, mtn=signature.mtn)
# get our list of valid MTN
results["success"] = [manifest.mtn for manifest in existing_mtn]
# append any MTN, passed as an argument, not found in the DB to the error results
results["error"].extend(list(set(signature.mtn).difference(set(results["success"]))))
self.logger.warning(f"MTN not found or site not listed as site type {results['error']}")
logger.warning(f"MTN not found or site not listed as site type {results['error']}")
return results
2 changes: 1 addition & 1 deletion server/apps/trak/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .lookup_task import pull_federal_codes
from .manifest_task import pull_manifest, sign_manifest, sync_site_manifests
from .manifest_task import create_rcra_manifest, pull_manifest, sign_manifest, sync_site_manifests
19 changes: 19 additions & 0 deletions server/apps/trak/tasks/manifest_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,22 @@ def sync_site_manifests(self, *, site_id: str, username: str):
logger.error(f"failed to sync {site_id} manifest")
self.update_state(state=states.FAILURE, meta=f"Internal Error {exc}")
raise Ignore()


# create_rcra_manifest
@shared_task(name="create rcra manifests", bind=True)
def create_rcra_manifest(self, *, manifest: dict, username: str):
"""
asynchronous task to use the RCRAInfo web services to create an electronic (RCRA) manifest
it accepts a Python dict of the manifest data to be submitted as JSON, and the username of the
user who is creating the manifest
"""
from apps.trak.services import ManifestService

logger.info(f"start task: {self.name}")
try:
logger.debug(f"creating manifest: {manifest}")
manifest_service = ManifestService(username=username)
manifest_service.create_rcra_manifest(manifest=manifest)
except Exception as exc:
logger.error("error: ", exc)
2 changes: 2 additions & 0 deletions server/apps/trak/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from rest_framework import routers

from apps.trak.views import (
CreateRcraManifestView,
FederalWasteCodesView,
ManifestView,
MtnList,
Expand All @@ -17,6 +18,7 @@
urlpatterns = [
# Manifest
path("", include(manifest_router.urls)),
path("rcra/manifest/create", CreateRcraManifestView.as_view()),
path("manifest/pull", PullManifestView.as_view()),
path("manifest/sign", SignManifestView.as_view()),
path("manifest/sync", SyncSiteManifestView.as_view()),
Expand Down
1 change: 1 addition & 0 deletions server/apps/trak/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .lookup_views import FederalWasteCodesView, StateWasteCodesView
from .manifest_view import (
CreateRcraManifestView,
ManifestView,
MtnList,
PullManifestView,
Expand Down
32 changes: 31 additions & 1 deletion server/apps/trak/views/manifest_view.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import logging

from celery.exceptions import TaskError
Expand All @@ -12,7 +13,7 @@
from apps.trak.models import Manifest
from apps.trak.serializers import ManifestSerializer, MtnSerializer
from apps.trak.serializers.signature_ser import QuickerSignSerializer
from apps.trak.tasks import pull_manifest, sign_manifest, sync_site_manifests
from apps.trak.tasks import create_rcra_manifest, pull_manifest, sign_manifest, sync_site_manifests

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -120,3 +121,32 @@ def post(self, request: Request) -> Response:
return self.response(
data={"error": "malformed payload"}, status=status.HTTP_400_BAD_REQUEST
)


class CreateRcraManifestView(GenericAPIView):
"""
This is a proxy endpoint used to create electronic manifest(s) in RCRAInfo/e-Manifest
"""

queryset = None
response = Response
serializer_class = ManifestSerializer
http_method_names = ["post"]

def post(self, request: Request) -> Response:
"""The Body of the POST request should contain the complete and valid manifest object"""
manifest_serializer = self.serializer_class(data=request.data)
if manifest_serializer.is_valid():
logger.info(
f"valid manifest data submitted for creation in RCRAInfo: "
f"{datetime.datetime.utcnow()}"
)
task = create_rcra_manifest.delay(
manifest=manifest_serializer.data, username=str(request.user)
)
return self.response(data={"task": task.id}, status=status.HTTP_201_CREATED)
else:
logger.error("manifest_serializer errors: ", manifest_serializer.errors)
return self.response(
exception=manifest_serializer.errors, status=status.HTTP_400_BAD_REQUEST
)

0 comments on commit 283b408

Please sign in to comment.