felicity-lims/felicity/api/gql/shipment/mutations.py

322 lines
11 KiB
Python
Raw Normal View History

2023-05-17 16:54:05 +08:00
import logging
from typing import List, Optional
import strawberry # noqa
from sqlalchemy import or_
2023-08-04 13:12:43 +08:00
from api.gql.types import OperationError
from api.gql.auth import auth_from_info, verify_user_auth
2023-07-26 15:23:28 +08:00
from api.gql.permissions import IsAuthenticated
2023-05-17 16:54:05 +08:00
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
2023-05-17 16:54:05 +08:00
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@strawberry.input
class ShipmentInputType:
laboratory_uid: str | None
2023-05-17 16:54:05 +08:00
courier: str
comment: str | None
count: int | None = 1
@strawberry.input
class ShipmentUpdateInputType:
laboratory_uid: str | None
2023-05-17 16:54:05 +08:00
courier: str
comment: str | None = ""
2023-05-17 16:54:05 +08:00
@strawberry.input
class ReferenceSampleInput:
sample_uid: str | None = None
shiped_sample_uid: str | None = None
2023-09-11 13:02:05 +08:00
analyses: list[str] # list of analysis uids being assignes/unassigned
2023-05-17 16:54:05 +08:00
@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="",
)
2023-05-17 16:54:05 +08:00
ShipmentsResponse = strawberry.union(
"ShipmentsResponse", (ShipmentListingType, OperationError), description="" # noqa
)
ShipmentResponse = strawberry.union(
"ShipmentResponse", (ShipmentType, OperationError), description="" # noqa
)
@strawberry.type
class ShipmentMutations:
2023-07-26 15:23:28 +08:00
@strawberry.mutation(permission_classes=[IsAuthenticated])
2023-05-17 16:54:05 +08:00
async def create_shipment(
2023-09-11 13:02:05 +08:00
self, info, payload: ShipmentInputType
2023-05-17 16:54:05 +08:00
) -> 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")
2023-09-11 13:02:05 +08:00
2023-05-17 16:54:05 +08:00
incoming = {
"incoming": False,
2023-05-17 16:54:05 +08:00
"comment": payload.comment,
"courier": payload.courier,
"laboratory_uid": payload.laboratory_uid,
2023-05-17 16:54:05 +08:00
"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]}
2023-05-17 16:54:05 +08:00
)
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]
)
2023-07-26 15:23:28 +08:00
@strawberry.mutation(permission_classes=[IsAuthenticated])
2023-05-17 16:54:05 +08:00
async def update_shipment(
2023-09-11 13:02:05 +08:00
self, info, uid: str, payload: ShipmentUpdateInputType
2023-05-17 16:54:05 +08:00
) -> 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:
2023-09-11 13:02:05 +08:00
return OperationError(
error="The uid for the update shipment was not provided"
)
2023-05-17 16:54:05 +08:00
# if not all(not getattr(payload, attr) for attr in dir(payload)):
# return OperationError(error="Either comment, courier or samples must be provided.")
2023-05-17 16:54:05 +08:00
2023-09-11 13:02:05 +08:00
shipment: Optional[models.Shipment] = await models.Shipment.get(uid=uid)
2023-05-17 16:54:05 +08:00
if not shipment:
2023-09-11 13:02:05 +08:00
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)
2023-09-11 13:02:05 +08:00
shipment_in = schemas.ShipmentUpdate(**shipment.to_dict())
shipment = await shipment.update(shipment_in)
2023-05-17 16:54:05 +08:00
return ShipmentType(**shipment.marshal_simple())
2023-05-17 16:54:05 +08:00
2023-07-26 15:23:28 +08:00
@strawberry.mutation(permission_classes=[IsAuthenticated])
async def action_shipment(
2023-09-11 13:02:05 +08:00
self, info, uid: str, action: str
) -> ShipmentResponse: # noqa
2023-05-17 16:54:05 +08:00
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(
2023-09-11 13:02:05 +08:00
error="The uid/action for the update shipment was not provided"
)
2023-09-11 13:02:05 +08:00
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":
2023-09-11 13:02:05 +08:00
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)
2023-09-11 13:02:05 +08:00
else:
shipment = await action_shipment(uid, action, felicity_user)
2023-05-17 16:54:05 +08:00
return ShipmentType(**shipment.marshal_simple())
2023-07-26 15:23:28 +08:00
@strawberry.mutation(permission_classes=[IsAuthenticated])
2023-05-17 16:54:05 +08:00
async def shipment_manage_samples(
2023-09-11 13:02:05 +08:00
self, info, uid: str, payload: ShipmentManageSamplesInput
2023-05-17 16:54:05 +08:00
) -> 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))
2023-05-17 16:54:05 +08:00
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,
2023-05-17 16:54:05 +08:00
)
await job_models.Job.create(job_schema)
elif payload.action == "recover":
2023-09-11 13:02:05 +08:00
await shipment_recover(shipment.uid, data, felicity_user.uid)
elif payload.action == "recall":
await shipment_recall(shipment.uid, data, felicity_user.uid)
2023-05-17 16:54:05 +08:00
else:
pass
2023-05-17 16:54:05 +08:00
shipment = await models.Shipment.get(uid=uid)
2023-05-17 16:54:05 +08:00
return ShipmentType(**shipment.marshal_simple())
2023-07-26 15:23:28 +08:00
@strawberry.mutation(permission_classes=[IsAuthenticated])
2023-09-11 13:02:05 +08:00
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")
2023-09-11 13:02:05 +08:00
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:
2023-09-11 13:02:05 +08:00
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)
2023-09-11 13:02:05 +08:00
referral_laboratory: models.ReferralLaboratory = (
await models.ReferralLaboratory.create(obj_in)
)
return types.ReferralLaboratoryType(**referral_laboratory.marshal_simple())
2023-07-26 15:23:28 +08:00
@strawberry.mutation(permission_classes=[IsAuthenticated])
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:
2023-09-11 13:02:05 +08:00
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)
2023-09-11 13:02:05 +08:00
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())