felicity-lims/felicity/api/gql/shipment/mutations.py
2023-07-09 17:10:41 +02:00

332 lines
11 KiB
Python

import logging
from typing import List, Optional
import strawberry # noqa
from sqlalchemy import or_
from api.gql import OperationError, auth_from_info, verify_user_auth
from api.gql.shipment.types import ShipmentType
from apps.common.models import IdSequence
from apps.job import models as job_models
from apps.job import schemas as job_schemas
from apps.job.conf import actions, categories, priorities, states
from apps.shipment import conf, models, schemas
from apps.shipment.utils import shipment_recover, shipment_recall, action_shipment
from api.gql.shipment import types
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@strawberry.input
class ShipmentInputType:
laboratory_uid: str | None
courier: str
comment: str | None
count: int | None = 1
@strawberry.input
class ShipmentUpdateInputType:
laboratory_uid: str | None
courier: str
comment: str | None = ""
@strawberry.input
class ReferenceSampleInput:
sample_uid: str | None = None
shiped_sample_uid: str | None = None
analyses: list[str] # list of analysis uids being assignes/unassigned
@strawberry.input
class ShipmentManageSamplesInput:
samples: List[ReferenceSampleInput]
action: str
@strawberry.type
class ShipmentListingType:
shipments: Optional[List[ShipmentType]]
@strawberry.input
class ReferralLaboratoryInputType:
name: str
code: str
url: str
username: str
password: str
is_reference: bool = False
is_referral: bool = False
ReferralLaboratoryResponse = strawberry.union(
"ReferralLaboratoryResponse",
(types.ReferralLaboratoryType, OperationError), # noqa
description="",
)
ShipmentsResponse = strawberry.union(
"ShipmentsResponse", (ShipmentListingType, OperationError), description="" # noqa
)
ShipmentResponse = strawberry.union(
"ShipmentResponse", (ShipmentType, OperationError), description="" # noqa
)
@strawberry.type
class ShipmentMutations:
@strawberry.mutation
async def create_shipment(
self,
info,
payload: ShipmentInputType
) -> ShipmentsResponse:
is_authenticated, felicity_user = await auth_from_info(info)
verify_user_auth(
is_authenticated,
felicity_user,
"Only Authenticated user can create shipments",
)
if not payload.courier:
return OperationError(error="Courier Details are required")
incoming = {
"incoming": False,
"comment": payload.comment,
"courier": payload.courier,
"laboratory_uid": payload.laboratory_uid,
"shipment_id": None,
"state": conf.shipment_states.EMPTY,
"created_by_uid": felicity_user.uid,
"updated_by_uid": felicity_user.uid,
}
sh_schema = schemas.ShipmentCreate(**incoming)
shipment_schemas = [
sh_schema.copy(
update={"shipment_id": (await IdSequence.get_next_number("SH"))[1]}
)
for i in list(range(payload.count))
]
shipments = await models.Shipment.bulk_create(shipment_schemas)
logger.info(f"Bulk create: {shipments}")
# to get lazy loads working otherwise return WorksheetListingType(shipments)
return ShipmentListingType(
shipments=[(await models.Shipment.get(uid=sh.uid)) for sh in shipments]
)
@strawberry.mutation
async def update_shipment(
self,
info,
uid: str,
payload: ShipmentUpdateInputType
) -> ShipmentResponse: # noqa
is_authenticated, felicity_user = await auth_from_info(info)
verify_user_auth(
is_authenticated,
felicity_user,
"Only Authenticated user can update shipments",
)
if not uid:
return OperationError(error="The uid for the update shipment was not provided")
# if not all(not getattr(payload, attr) for attr in dir(payload)):
# return OperationError(error="Either comment, courier or samples must be provided.")
shipment: Optional[models.Shipment] = await models.Shipment.get(
uid=uid
)
if not shipment:
return OperationError(
error=f"Shipment {uid} does not exist"
)
shipment_data = shipment.to_dict()
for field in shipment_data:
if field in payload.__dict__:
try:
setattr(shipment, field, payload.__dict__[field])
except Exception as e:
logger.warning(e)
shipment_in = schemas.ShipmentUpdate(**shipment.to_dict())
shipment = await shipment.update(shipment_in)
return ShipmentType(**shipment.marshal_simple())
@strawberry.mutation
async def action_shipment(
self,
info,
uid: str,
action: str
) -> ShipmentResponse: # noqa
is_authenticated, felicity_user = await auth_from_info(info)
verify_user_auth(
is_authenticated,
felicity_user,
"Only Authenticated user can action shipments",
)
if not uid or not action:
return OperationError(error="The uid/action for the update shipment was not provided")
shipment: Optional[models.Shipment] = await models.Shipment.get(
uid=uid
)
if not shipment:
return OperationError(
error=f"Shipment {uid} does not exist"
)
if action == "dispatch":
shipment = await shipment.change_state(conf.shipment_states.AWAITING, felicity_user.uid)
job_schema = job_schemas.JobCreate(
action=actions.SH_DISPATCH,
category=categories.SHIPMENT,
priority=priorities.MEDIUM,
creator_uid=felicity_user.uid,
job_id=shipment.uid,
status=states.PENDING,
data=None,
)
await job_models.Job.create(job_schema)
else:
shipment = await action_shipment(uid, action, felicity_user)
return ShipmentType(**shipment.marshal_simple())
@strawberry.mutation
async def shipment_manage_samples(
self,
info,
uid: str,
payload: ShipmentManageSamplesInput
) -> ShipmentResponse:
is_authenticated, felicity_user = await auth_from_info(info)
verify_user_auth(
is_authenticated,
felicity_user,
"Only Authenticated user can update shipments",
)
if not len(payload.samples) > 0:
return OperationError(error="Samples for assignment are required")
if not uid:
return OperationError(error="Shipment uid is required")
shipment = await models.Shipment.get(uid=uid)
if not shipment:
return OperationError(error=f"Shipment {uid} does not exist")
data = list(map(lambda s: s.__dict__, payload.samples))
if payload.action == "assign":
# Add a job
job_schema = job_schemas.JobCreate(
action=actions.SH_MANUAL_ASSIGN,
category=categories.SHIPMENT,
priority=priorities.MEDIUM,
creator_uid=felicity_user.uid,
job_id=shipment.uid,
status=states.PENDING,
data=data,
)
await job_models.Job.create(job_schema)
elif payload.action == "recover":
await shipment_recover(shipment.uid, data, felicity_user.uid)
elif payload.action == "recall":
await shipment_recall(shipment.uid, data, felicity_user.uid)
else:
pass
shipment = await models.Shipment.get(uid=uid)
return ShipmentType(**shipment.marshal_simple())
@strawberry.mutation
async def create_referral_laboratory(info, payload: ReferralLaboratoryInputType) -> ReferralLaboratoryResponse:
is_authenticated, felicity_user = await auth_from_info(info)
verify_user_auth(
is_authenticated,
felicity_user,
"Only Authenticated user can create referral labs",
)
if not payload.name or not payload.code:
return OperationError(error="Name and Code are mandatory")
stmt = models.ReferralLaboratory.smart_query(filters={ or_: {
'name__exact': payload.name,
'code__exact': payload.code
}})
exists = await models.ReferralLaboratory.from_smart_query(stmt)
if exists:
return OperationError(error=f"ReferralLaboratory: {payload.name}, {payload.code} already exists")
incoming = {
"created_by_uid": felicity_user.uid,
"updated_by_uid": felicity_user.uid,
}
for k, v in payload.__dict__.items():
incoming[k] = v
obj_in = schemas.ReferralLaboratoryCreate(**incoming)
referral_laboratory: models.ReferralLaboratory = await models.ReferralLaboratory.create(
obj_in
)
return types.ReferralLaboratoryType(**referral_laboratory.marshal_simple())
@strawberry.mutation
async def update_referral_laboratory(
info, uid: str, payload: ReferralLaboratoryInputType
) -> ReferralLaboratoryResponse:
is_authenticated, felicity_user = await auth_from_info(info)
verify_user_auth(
is_authenticated,
felicity_user,
"Only Authenticated user can update referral labs",
)
referral_laboratory = await models.ReferralLaboratory.get(uid=uid)
if not referral_laboratory:
return OperationError(error=f"ReferralLaboratory with uid {uid} does not exist")
st_data = referral_laboratory.to_dict()
for field in st_data:
if field in payload.__dict__:
try:
setattr(referral_laboratory, field, payload.__dict__[field])
except Exception as e:
logger.warning(e)
referral_laboratory_in = schemas.ReferralLaboratoryUpdate(**referral_laboratory.to_dict())
referral_laboratory = await referral_laboratory.update(referral_laboratory_in)
return types.ReferralLaboratoryType(**referral_laboratory.marshal_simple())