You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
12 KiB

# -*- coding:utf-8 -*-
"""
@Created on : 2023/7/5 16:04
@Author: hxl
@Des:
"""
from datetime import datetime
from fastapi import APIRouter, Request, Depends
from tortoise import Tortoise
from tortoise.expressions import Q
from tortoise.functions import Count
from tortoise.queryset import QuerySet
from helper import respond_to, login_required
from helper.drug import drug_near_already_expired
from helper.utils import timezone_now
from models import Drug, DrugStateEnum, DrugUseStateEnum, DrugUseLog, Dictionary, Template, Cabinet, \
DrawerBoard, User
router = APIRouter(prefix='/home', dependencies=[Depends(login_required)])
@router.get('/overview', summary='概览数据')
async def index(request: Request):
"""
获取首页概览数据 \n
return: \n
- 'storage_count': 今日入库,
- 'expire_count': 未归还,
- 'receive_count': 今日领用,
- 'return_count': 今日归还,
- 'total_inventory': 库存总量,
- 'inventory_warning_count': 库存预警,
- 'near_warning_count': 试剂临期,
- 'already_expired_count':试剂过期
"""
stime = datetime.combine(timezone_now(), datetime.min.time())
etime = datetime.combine(timezone_now(), datetime.max.time())
archive_id = request.state.archive_id
template_obj = await Template.get(archive_id=request.state.archive_id)
if not template_obj:
return respond_to()
# 今日入库
storage_count = await Drug.filter(storage_at__range=[stime, etime], template_id=template_obj.id).count()
# 截止目前在库已过期
expire_count = await Drug.filter(state=DrugStateEnum.IN,
expired_at__lt=timezone_now().strftime('%Y-%m-%d %H:%M:%S'),
template_id=template_obj.id).count()
# 今日领用
# receive_count_1 = await (DrugUseLog.filter(state=DrugUseStateEnum.TAKE, created_at__range=[stime, etime])
# .filter(drug__template_id=template_obj.id)
# .filter(Q(" and JSON_EXTRACT(json_data, '$.first_will_empty') = true ")).count())
receive_count = await (DrugUseLog.filter(state=DrugUseStateEnum.TAKE, created_at__range=[stime, etime])
.filter(drug__template_id=template_obj.id).count())
# 构建自定义SQL语句
sql = f"""SELECT COUNT(*) count FROM drug_use_logs a
left join drugs b on a.drug_id=b.id
left join templates c on c.id = b.template_id
left join archives d on d.id = c.archive_id
WHERE a.state = '{DrugUseStateEnum.EMPTY}'
AND a.created_at BETWEEN '{stime}' AND '{etime}'
AND b.template_id = '{template_obj.id}'
AND JSON_EXTRACT(d.params, '$.first_will_empty') = true"""
conn = Tortoise.get_connection(connection_name='default')
receive_count_first_empty = await conn.execute_query(sql)
receive_count = receive_count + receive_count_first_empty[1][0].get("count")
# 今日归还
return_count = await (DrugUseLog.filter(state=DrugUseStateEnum.PUT, created_at__range=[stime, etime])
.filter(drug__template_id=template_obj.id).count())
# 试剂库存总量
total_inventory = await Drug.filter(state__in=[1, 2], template_id=template_obj.id).count()
# 库存预警
inventory_warning_count, _ = await get_lack_stock(archive_id)
# 临期试剂
near_warning_count, _ = await get_near_expired(archive_id)
# 试剂过期
already_expired_count = await get_already_expired(archive_id)
data = {
'storage_count': storage_count,
'expire_count': expire_count,
'receive_count': receive_count,
'return_count': return_count,
'total_inventory': total_inventory,
'inventory_warning_count': inventory_warning_count,
'near_warning_count': near_warning_count,
'already_expired_count': len(already_expired_count)
}
# await conn.close()
return respond_to(data=data)
@router.get('/overview/item/{key}', summary='概览数据明细')
async def index(request: Request, key: str):
"""
获取首页概览数据明细
return:
key: storage expire receive return
"""
stime = datetime.combine(timezone_now(), datetime.min.time())
etime = datetime.combine(timezone_now(), datetime.max.time())
archive_id = request.state.archive_id
template_obj = await Template.get(archive_id=archive_id)
if not template_obj:
return respond_to()
drugs, logs = [], []
# 今日入库
if key == "storage":
drugs = await Drug.filter(storage_at__range=[stime, etime], template_id=template_obj.id).all()
# 截止目前在库已过期
if key == "expire":
drugs = await Drug.filter(state=DrugStateEnum.IN,
expired_at__lt=timezone_now().strftime('%Y-%m-%d %H:%M:%S'),
template_id=template_obj.id).all()
# 今日领用
if key == "receive":
logs = await (DrugUseLog.filter(state=DrugUseStateEnum.TAKE, created_at__range=[stime, etime])
.filter(drug__template_id=template_obj.id).prefetch_related("drug").all())
# 今日归还
if key == "return":
logs = await (DrugUseLog.filter(state=DrugUseStateEnum.PUT, created_at__range=[stime, etime])
.filter(drug__template_id=template_obj.id).prefetch_related("drug").all())
result = []
if key in ["storage", "expire"]:
if not drugs:
return respond_to(data=[])
for drug_obj in drugs:
data = {
"drug_id": drug_obj.id,
"rfid": drug_obj.rfid,
"position": drug_obj.position,
"expired_at": drug_obj.expired_at,
"drug_info": await drug_obj.parse_fill_json_content()
}
result.append(data)
else:
if not logs:
return respond_to(data=[])
for log in logs:
if not log.drug:
continue
result.append({
"drug_id": log.drug.id,
"rfid": log.drug.rfid,
"position": log.drug.position,
"expired_at": log.drug.expired_at,
"drug_info": await log.drug.attribute_drug_info(),
})
return respond_to(data=result)
@router.post('/remaining_stock', summary='剩余库存表')
async def get_remaining_stock(request: Request, cabinets: list):
"""
获取剩余库存表
:return:
"""
data = {}
cabinets = await Cabinet.filter(id__in=cabinets).values("id", "label")
query = QuerySet(Dictionary).filter(archive_id=request.state.archive_id)
dictionary_ids = await query.filter().values_list("id", flat=True)
drug_query = Drug.filter(~Q(state__in=[DrugStateEnum.EMPTY, DrugStateEnum.INITIAL]),
dictionary_id__in=dictionary_ids, cabinet_id__in=[obj.get("id") for obj in cabinets]).prefetch_related('dictionary').annotate(
count=Count('id')).group_by("dictionary_id").values("dictionary_id", "count", "template_id")
drug_counts = await drug_query
for c in cabinets:
count = []
drug_list = []
for i in drug_counts:
template_obj = await Template.get(id=i.get("template_id"))
dictionary_obj = await Dictionary.get(id=i.get("dictionary_id"))
drug_dict = await dictionary_obj.attribute_dictionary_info(template_obj)
drug_list.append(",".join(map(str, drug_dict.values())))
count.append(i.get("count"))
data['drug_dict'] = drug_list
data[c["label"]] = count
return respond_to(data=data)
@router.get('/use_number', summary='试剂使用次数')
async def get_drug_count(request: Request, page_on: int = 1, page_size: int = 10):
"""
获取试剂使用次数
:param request: 请求
:param page_on: 当前页码
:param page_size: 每页显示数量
:return:
"""
dictionaries = await Dictionary.filter(archive_id=request.state.archive_id).all().values()
total_count = 0
drug_use_counts = []
start_index = (page_on - 1) * page_size
end_index = page_on * page_size
conn = Tortoise.get_connection(connection_name='default')
for dictionary in dictionaries:
drug_use_count = await DrugUseLog.filter(drug__dictionary=dictionary['id'], state=2).count()
sql = f"""SELECT COUNT(*) count FROM drug_use_logs a
left join drugs b on a.drug_id=b.id
left join templates c on c.id = b.template_id
left join archives d on d.id = c.archive_id
WHERE a.state = '{DrugUseStateEnum.EMPTY}'
AND b.dictionary_id = '{dictionary['id']}'
AND JSON_EXTRACT(d.params, '$.first_will_empty') = true"""
receive_count_first_empty = await conn.execute_query(sql)
drug_use_count = drug_use_count + receive_count_first_empty[1][0].get("count")
if(drug_use_count==0):
continue
drug_info = ",".join([value for key, value in dictionary.items() if key.startswith('k') and value is not None])
drug_use_counts.append({'drug_info': drug_info, 'drug_use_count': drug_use_count})
total_count += drug_use_count
paginated_drug_use_counts = drug_use_counts[start_index:end_index]
# conn.close()
return respond_to(
data={'drug_use_counts': paginated_drug_use_counts, 'total_count': len(paginated_drug_use_counts)})
@router.get('/near_warning', summary='近期预警列表')
async def get_warning(request: Request):
archive_id = request.state.archive_id
_, near_expired = await get_near_expired(archive_id)
already_expired = await get_already_expired(archive_id)
_, lack_stock = await get_lack_stock(archive_id)
return respond_to(data={'near_expired': near_expired, 'already_expired': already_expired, 'lack_stock': lack_stock})
async def get_near_expired(archive_id: str, page_no: int = 1, page_size: int = 10):
drug_objs = await Drug.filter(state__in=[DrugStateEnum.IN, DrugStateEnum.OUT]).prefetch_related(
'dictionary', 'template').filter(Q(dictionary__archive_id=archive_id)).all()
result = await drug_near_already_expired(drug_objs, "near")
count = len(result)
# offset = (page_no - 1) * page_size
# drug_result = result[offset: offset + page_size]
return count, result
async def get_already_expired(archive_id: str):
drug_objs = await (Drug.filter(state__in=[DrugStateEnum.IN, DrugStateEnum.OUT]).prefetch_related('dictionary', 'template')
.filter(Q(dictionary__archive_id=archive_id)).all())
result = await drug_near_already_expired(drug_objs, "already")
return result
async def get_lack_stock(archive_id: str, page_no: int = 1, page_size: int = 10):
query = QuerySet(Dictionary).filter(archive_id=archive_id)
dictionary_ids = await query.filter().all().values_list("id", flat=True)
drug_counts = await Drug.filter(~Q(state=DrugStateEnum.EMPTY),
dictionary_id__in=dictionary_ids
).prefetch_related('dictionary').annotate(count=Count('id')).group_by(
"dictionary_id").order_by("count").values("dictionary_id",
"count",
"template_id")
data = list()
for i in drug_counts:
template_obj = await Template.get(id=i.get("template_id"))
dictionary_obj = await Dictionary.get(id=i.get("dictionary_id"))
lack_stock_count = dictionary_obj.params.get("lack_stock_count") if dictionary_obj.params.get(
"lack_stock_count") else 20
if i.get("count") < lack_stock_count:
drug_info = await dictionary_obj.attribute_dictionary_info(template_obj)
data.append({
"drug_info": ",".join(list(map(lambda x:str(x), drug_info.values()))),
"count": i.get("count"),
"dictionary_id": i.get("dictionary_id")
})
count = len(data)
# offset = (page_no - 1) * page_size
# result = data[offset: offset + page_size]
return count, data
@router.get('/lock', summary='修改账号锁定状态')
async def destroy(request: Request, id: str,locked:bool):
"""锁定账号"""
user = await User.get(id=id)
user.locked = locked
await user.save()
return respond_to(200)