fix: 能管的工段异常修改

This commit is contained in:
caoqianming 2023-11-22 16:07:00 +08:00
parent 6f058dc757
commit 2030812c18
10 changed files with 305 additions and 106 deletions

View File

@ -127,7 +127,7 @@ class CorrectViewSet(CustomGenericViewSet):
矫正工单流水号 矫正工单流水号
""" """
data = request.data data = request.data
from apps.wf.models import Ticket from apps.wf.models import Ticket
from apps.wf.services import WfService from apps.wf.services import WfService
from django.utils.timezone import localtime from django.utils.timezone import localtime
@ -150,10 +150,11 @@ class CorrectViewSet(CustomGenericViewSet):
from apps.enm.models import Mpoint from apps.enm.models import Mpoint
for mpoint in Mpoint.objects.exclude(mgroup=None): for mpoint in Mpoint.objects.exclude(mgroup=None):
mgroup = mpoint.mgroup mgroup = mpoint.mgroup
mpoint.mgroups_allocate = [{'mgroup': mgroup.id, 'mgroup_name': mgroup.name, 'ratio': 1}] mpoint.mgroups_allocate = [
{'mgroup': mgroup.id, 'mgroup_name': mgroup.name, 'ratio': 1}]
mpoint.save() mpoint.save()
return Response() return Response()
@action(methods=['post'], detail=False, serializer_class=Serializer) @action(methods=['post'], detail=False, serializer_class=Serializer)
def mpointstat(self, request, pk=None): def mpointstat(self, request, pk=None):
"""矫正测点统计数据 """矫正测点统计数据
@ -194,7 +195,7 @@ class CorrectViewSet(CustomGenericViewSet):
item.shift = item.sflog.shift item.shift = item.sflog.shift
item.save() item.save()
return Response() return Response()
# @action(methods=['post'], detail=False, serializer_class=Serializer) # @action(methods=['post'], detail=False, serializer_class=Serializer)
# def global_img_compressed(self, request, pk=None): # def global_img_compressed(self, request, pk=None):
# """矫正全景压缩照片 # """矫正全景压缩照片
@ -209,6 +210,7 @@ class CorrectViewSet(CustomGenericViewSet):
# event.save() # event.save()
# return Response() # return Response()
class TestViewSet(CustomGenericViewSet): class TestViewSet(CustomGenericViewSet):
authentication_classes = () authentication_classes = ()
permission_classes = () permission_classes = ()
@ -261,7 +263,8 @@ class TestViewSet(CustomGenericViewSet):
m = importlib.import_module(module) m = importlib.import_module(module)
f = getattr(m, func) f = getattr(m, func)
if vdata['delay']: if vdata['delay']:
task = f.delay(*vdata.get('args', []), **vdata.get('kwargs', {})) # 同步执行 task = f.delay(*vdata.get('args', []), **
vdata.get('kwargs', {})) # 同步执行
return Response({'task_id': task.id}) return Response({'task_id': task.id})
else: else:
f(*vdata.get('args', []), **vdata.get('kwargs', {})) f(*vdata.get('args', []), **vdata.get('kwargs', {}))
@ -299,7 +302,8 @@ class TestViewSet(CustomGenericViewSet):
from apps.hrm.services import HrmService from apps.hrm.services import HrmService
# data = {'id': 25198, 'category': 'alarm', 'method': 'alarm.msg', 'info': {'orgName': '河北省', 'nodeCode': '1000096$7$0$0', 'deviceCode': '1000096', 'alarmCode': 'd38c98b842334581a8219b3125ca72d5', 'alarmPicture': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221123/1/dsf_43aee9e6-6ae2-11ed-9872-e4246c7d1635_42018473_42037732.jpg', 'nodeType': '2', 'alarmDate': 1669187805, 'alarmGrade': 2, 'isSave': False, 'unitType': 7, 'extend': {'deptName': '设备管理部', 'deptIdList': [25], 'acsChannelCode': '1000096$7$0$0', 'maskState': 0, 'enterOrExit': 1, 'openTypeStr': '人脸刷门', 'swingTime': '2022-11-23 15:16:45', 'deviceName': '厂区门入', 'personCode': '8jtfoa', 'openType': '61', 'isOverTemp': False, 'orgCode': '001', 'paperNumber': '61012419850304335X', 'errorDetail': '', 'from': 'evo-accesscontrol', 'id': 1044994923113353216, 'beginTime': 1669187805000, 'acsChannelName': '厂区门入_门禁通道_1', 'cardStatus': '0', 'faceImageUrl': ['http://10.99.5.24:8927/6ad010cf-ce45-11ec-9715-e4246c7d1635/20221123/1/dsf_43aee9e6-6ae2-11ed-9872-e4246c7d1635_42018473_42037732.jpg'], 'orgName': '河北省', 'openFailedCode': 0, 'sex': 1, 'deptId': '25', 'cardType': 0, 'curTemp': 35.5, 'deviceCode': '1000096', 'personName': '谭刚位', 'personImg': '6ae577ee-ce45-11ec-bb54-e4246c7d1635/20221110/1/3313679c-6092-11ed-b963-e4246c7d1635.jpg', 'openResult': 1, 'personId': 673, 'recordImage1': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221123/1/dsf_43aee9e6-6ae2-11ed-9872-e4246c7d1635_42018473_42037732.jpg', 'category': '0', 'cardNumber': '0525871590', 'rfidType': 0, 'age': 0}, 'alarmType': 61, 'channelSeq': 0, 'orgCode': '001', 'channelName': '厂区门入_门禁通道_1', 'alarmStat': 1, 'isEvent': True}, 'subsystem': 'evo-accesscontrol', 'userIds': None, 'sid': None, 'domainId': None, 'infoArray': None, 'protocol': None} # data = {'id': 25198, 'category': 'alarm', 'method': 'alarm.msg', 'info': {'orgName': '河北省', 'nodeCode': '1000096$7$0$0', 'deviceCode': '1000096', 'alarmCode': 'd38c98b842334581a8219b3125ca72d5', 'alarmPicture': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221123/1/dsf_43aee9e6-6ae2-11ed-9872-e4246c7d1635_42018473_42037732.jpg', 'nodeType': '2', 'alarmDate': 1669187805, 'alarmGrade': 2, 'isSave': False, 'unitType': 7, 'extend': {'deptName': '设备管理部', 'deptIdList': [25], 'acsChannelCode': '1000096$7$0$0', 'maskState': 0, 'enterOrExit': 1, 'openTypeStr': '人脸刷门', 'swingTime': '2022-11-23 15:16:45', 'deviceName': '厂区门入', 'personCode': '8jtfoa', 'openType': '61', 'isOverTemp': False, 'orgCode': '001', 'paperNumber': '61012419850304335X', 'errorDetail': '', 'from': 'evo-accesscontrol', 'id': 1044994923113353216, 'beginTime': 1669187805000, 'acsChannelName': '厂区门入_门禁通道_1', 'cardStatus': '0', 'faceImageUrl': ['http://10.99.5.24:8927/6ad010cf-ce45-11ec-9715-e4246c7d1635/20221123/1/dsf_43aee9e6-6ae2-11ed-9872-e4246c7d1635_42018473_42037732.jpg'], 'orgName': '河北省', 'openFailedCode': 0, 'sex': 1, 'deptId': '25', 'cardType': 0, 'curTemp': 35.5, 'deviceCode': '1000096', 'personName': '谭刚位', 'personImg': '6ae577ee-ce45-11ec-bb54-e4246c7d1635/20221110/1/3313679c-6092-11ed-b963-e4246c7d1635.jpg', 'openResult': 1, 'personId': 673, 'recordImage1': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221123/1/dsf_43aee9e6-6ae2-11ed-9872-e4246c7d1635_42018473_42037732.jpg', 'category': '0', 'cardNumber': '0525871590', 'rfidType': 0, 'age': 0}, 'alarmType': 61, 'channelSeq': 0, 'orgCode': '001', 'channelName': '厂区门入_门禁通道_1', 'alarmStat': 1, 'isEvent': True}, 'subsystem': 'evo-accesscontrol', 'userIds': None, 'sid': None, 'domainId': None, 'infoArray': None, 'protocol': None}
# data = {'id': 29870, 'category': 'alarm', 'method': 'alarm.msg', 'info': {'orgName': '河北省', 'nodeCode': '1002222$7$0$0', 'deviceCode': '1002222', 'alarmCode': '191f4a8d0a2c4b6282448af04df6ec11', 'alarmPicture': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221124/1/dsf_b8662018-6b9c-11ed-9872-e4246c7d1635_51799202_51816153.jpg', 'nodeType': '2', 'alarmDate': 1669263013, 'alarmGrade': 2, 'isSave': False, 'unitType': 7, 'extend': {'deptName': '安全生产部', 'deptIdList': [28], 'acsChannelCode': '1002222$7$0$0', 'maskState': 3, 'enterOrExit': 3, 'openTypeStr': '人脸刷门', 'swingTime': '2022-11-24 12:10:13', 'deviceName': '办公楼考勤面板', 'personCode': '13911097513', 'openType': '61', 'orgCode': '001', 'paperNumber': '110107196804040335', 'errorDetail': '', 'from': 'evo-accesscontrol', 'id': 1045310360703340544, 'beginTime': 1669263013000, 'acsChannelName': '办公楼考勤面板_门禁通道_1', 'cardStatus': '0', 'faceImageUrl': ['http://10.99.5.24:8927/6ad010cf-ce45-11ec-9715-e4246c7d1635/20221124/1/dsf_b8662018-6b9c-11ed-9872-e4246c7d1635_51799202_51816153.jpg'], 'orgName': '河北省', 'openFailedCode': 0, 'sex': 1, 'deptId': '28', 'cardType': 0, 'deviceCode': '1002222', 'personName': '刘静元', 'personImg': '6ae577ee-ce45-11ec-bb54-e4246c7d1635/20220907/1/d841d821-2e67-11ed-b073-e4246c7d1635.jpg', 'openResult': 1, 'personId': 237, 'recordImage1': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221124/1/dsf_b8662018-6b9c-11ed-9872-e4246c7d1635_51799202_51816153.jpg', 'category': '0', 'cardNumber': '5973291203', 'rfidType': 0, 'age': 0}, 'alarmType': 61, 'channelSeq': 0, 'orgCode': '001', 'channelName': '办公楼考勤面板_门禁通道_1', 'alarmStat': 1, 'isEvent': True}, 'subsystem': 'evo-accesscontrol', 'userIds': None, 'sid': None, 'domainId': None, 'infoArray': None, 'protocol': None} # data = {'id': 29870, 'category': 'alarm', 'method': 'alarm.msg', 'info': {'orgName': '河北省', 'nodeCode': '1002222$7$0$0', 'deviceCode': '1002222', 'alarmCode': '191f4a8d0a2c4b6282448af04df6ec11', 'alarmPicture': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221124/1/dsf_b8662018-6b9c-11ed-9872-e4246c7d1635_51799202_51816153.jpg', 'nodeType': '2', 'alarmDate': 1669263013, 'alarmGrade': 2, 'isSave': False, 'unitType': 7, 'extend': {'deptName': '安全生产部', 'deptIdList': [28], 'acsChannelCode': '1002222$7$0$0', 'maskState': 3, 'enterOrExit': 3, 'openTypeStr': '人脸刷门', 'swingTime': '2022-11-24 12:10:13', 'deviceName': '办公楼考勤面板', 'personCode': '13911097513', 'openType': '61', 'orgCode': '001', 'paperNumber': '110107196804040335', 'errorDetail': '', 'from': 'evo-accesscontrol', 'id': 1045310360703340544, 'beginTime': 1669263013000, 'acsChannelName': '办公楼考勤面板_门禁通道_1', 'cardStatus': '0', 'faceImageUrl': ['http://10.99.5.24:8927/6ad010cf-ce45-11ec-9715-e4246c7d1635/20221124/1/dsf_b8662018-6b9c-11ed-9872-e4246c7d1635_51799202_51816153.jpg'], 'orgName': '河北省', 'openFailedCode': 0, 'sex': 1, 'deptId': '28', 'cardType': 0, 'deviceCode': '1002222', 'personName': '刘静元', 'personImg': '6ae577ee-ce45-11ec-bb54-e4246c7d1635/20220907/1/d841d821-2e67-11ed-b073-e4246c7d1635.jpg', 'openResult': 1, 'personId': 237, 'recordImage1': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20221124/1/dsf_b8662018-6b9c-11ed-9872-e4246c7d1635_51799202_51816153.jpg', 'category': '0', 'cardNumber': '5973291203', 'rfidType': 0, 'age': 0}, 'alarmType': 61, 'channelSeq': 0, 'orgCode': '001', 'channelName': '办公楼考勤面板_门禁通道_1', 'alarmStat': 1, 'isEvent': True}, 'subsystem': 'evo-accesscontrol', 'userIds': None, 'sid': None, 'domainId': None, 'infoArray': None, 'protocol': None}
data = {'id': 70781, 'category': 'alarm', 'method': 'alarm.msg', 'info': {'orgName': '河北省', 'nodeCode': '1002222$7$0$0', 'deviceCode': '1002222', 'alarmCode': '74ac9b9511774388a975dc5daa6ad8b9', 'alarmPicture': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20230419/1/dsf_93e4c0ab-de83-11ed-af40-e4246c7d1635_45533104_45552146.jpg', 'nodeType': '2', 'alarmDate': 1681897834, 'alarmGrade': 2, 'isSave': False, 'unitType': 7, 'extend': {'deptName': '企业管理部', 'deptIdList': [21], 'acsChannelCode': '1002222$7$0$0', 'maskState': 0, 'enterOrExit': 3, 'openTypeStr': '人脸刷门', 'swingTime': '2023-04-19 17:50:34', 'deviceName': '办公楼考勤面板', 'personCode': '13731293508', 'openType': '61', 'orgCode': '001', 'paperNumber': '130634198612035821', 'errorDetail': '', 'from': 'evo-accesscontrol', 'id': 1098304215199125504, 'beginTime': 1681897834000, 'acsChannelName': '办公楼考勤面板_门禁通道_1', 'cardStatus': '0', 'faceImageUrl': ['http://10.99.5.24:8927/6ad010cf-ce45-11ec-9715-e4246c7d1635/20230419/1/dsf_93e4c0ab-de83-11ed-af40-e4246c7d1635_45533104_45552146.jpg'], 'orgName': '河北省', 'openFailedCode': 0, 'sex': 1, 'deptId': '21', 'cardType': 0, 'deviceCode': '1002222', 'personName': '李贝', 'personImg': '6ae577ee-ce45-11ec-bb54-e4246c7d1635/20220907/1/9ab9a1eb-2e74-11ed-b073-e4246c7d1635.jpg', 'openResult': 1, 'personId': 331, 'recordImage1': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20230419/1/dsf_93e4c0ab-de83-11ed-af40-e4246c7d1635_45533104_45552146.jpg', 'category': '0', 'cardNumber': '6705501569', 'rfidType': 0, 'age': 0}, 'alarmType': 61, 'channelSeq': 0, 'orgCode': '001', 'channelName': '办公楼考勤面板_门禁通道_1', 'alarmStat': 1, 'isEvent': True}, 'subsystem': 'evo-accesscontrol', 'userIds': None, 'sid': None, 'domainId': None, 'infoArray': None, 'protocol': None} data = {'id': 70781, 'category': 'alarm', 'method': 'alarm.msg', 'info': {'orgName': '河北省', 'nodeCode': '1002222$7$0$0', 'deviceCode': '1002222', 'alarmCode': '74ac9b9511774388a975dc5daa6ad8b9', 'alarmPicture': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20230419/1/dsf_93e4c0ab-de83-11ed-af40-e4246c7d1635_45533104_45552146.jpg', 'nodeType': '2', 'alarmDate': 1681897834, 'alarmGrade': 2, 'isSave': False, 'unitType': 7, 'extend': {'deptName': '企业管理部', 'deptIdList': [21], 'acsChannelCode': '1002222$7$0$0', 'maskState': 0, 'enterOrExit': 3, 'openTypeStr': '人脸刷门', 'swingTime': '2023-04-19 17:50:34', 'deviceName': '办公楼考勤面板', 'personCode': '13731293508', 'openType': '61', 'orgCode': '001', 'paperNumber': '130634198612035821', 'errorDetail': '', 'from': 'evo-accesscontrol', 'id': 1098304215199125504, 'beginTime': 1681897834000, 'acsChannelName': '办公楼考勤面板_门禁通道_1', 'cardStatus': '0', 'faceImageUrl': [
'http://10.99.5.24:8927/6ad010cf-ce45-11ec-9715-e4246c7d1635/20230419/1/dsf_93e4c0ab-de83-11ed-af40-e4246c7d1635_45533104_45552146.jpg'], 'orgName': '河北省', 'openFailedCode': 0, 'sex': 1, 'deptId': '21', 'cardType': 0, 'deviceCode': '1002222', 'personName': '李贝', 'personImg': '6ae577ee-ce45-11ec-bb54-e4246c7d1635/20220907/1/9ab9a1eb-2e74-11ed-b073-e4246c7d1635.jpg', 'openResult': 1, 'personId': 331, 'recordImage1': '6ad010cf-ce45-11ec-9715-e4246c7d1635/20230419/1/dsf_93e4c0ab-de83-11ed-af40-e4246c7d1635_45533104_45552146.jpg', 'category': '0', 'cardNumber': '6705501569', 'rfidType': 0, 'age': 0}, 'alarmType': 61, 'channelSeq': 0, 'orgCode': '001', 'channelName': '办公楼考勤面板_门禁通道_1', 'alarmStat': 1, 'isEvent': True}, 'subsystem': 'evo-accesscontrol', 'userIds': None, 'sid': None, 'domainId': None, 'infoArray': None, 'protocol': None}
HrmService.swipe(data=data) HrmService.swipe(data=data)
return Response() return Response()
@ -572,6 +576,6 @@ class TestViewSet(CustomGenericViewSet):
@action(methods=['post'], detail=False, serializer_class=Serializer, permission_classes=[]) @action(methods=['post'], detail=False, serializer_class=Serializer, permission_classes=[])
def test_cal(self, request, pk=None): def test_cal(self, request, pk=None):
from apps.wpm.tasks import cal_shut_hour from apps.wpm.tasks import cal_exp_duration_hour
cal_shut_hour('3397169058570170368') cal_exp_duration_hour('3397169058570170368')
return Response() return Response()

View File

@ -14,7 +14,8 @@ def translate_eval_formula(exp_str: str, year: int, month: int, day: int, hour:
pattern = r"\${(.*?)}" pattern = r"\${(.*?)}"
matches = re.findall(pattern, exp_str) matches = re.findall(pattern, exp_str)
for match in matches: for match in matches:
mpst = MpointStat.objects.filter(Q(mpoint__id=match)|Q(mpoint__name=match)|Q(mpoint__code=match), type='hour', year=year, month=month, day=day, hour=hour).first() mpst = MpointStat.objects.filter(Q(mpoint__id=match) | Q(mpoint__name=match) | Q(
mpoint__code=match), type='hour', year=year, month=month, day=day, hour=hour).first()
if mpst: if mpst:
exp_str = exp_str.replace(f"${{{match}}}", str(mpst.val)) exp_str = exp_str.replace(f"${{{match}}}", str(mpst.val))
rval = eval(exp_str) rval = eval(exp_str)
@ -55,7 +56,7 @@ def get_day_s(year: int, month: int, day: int, hour: int, hour_split: int = 21):
# month_s = data['month_s'] # month_s = data['month_s']
# for item in data['qua_data']: # for item in data['qua_data']:
# qua_rate[f'{item["material_name"]}_{item["testitem_name"]}'] = item["rate_pass"] # qua_rate[f'{item["material_name"]}_{item["testitem_name"]}'] = item["rate_pass"]
# goal_dict = get_mgroup_goals(data['mgroup'], data['year_s'], False) # goal_dict = get_mgroup_goals(data['mgroup'], data['year_s'], False)
# goal_data = {} # goal_data = {}
# try: # try:
@ -76,27 +77,43 @@ def get_day_s(year: int, month: int, day: int, hour: int, hour_split: int = 21):
# print(traceback.format_exc()) # print(traceback.format_exc())
# return goal_data, score # return goal_data, score
def shutdown_or_startup(mplog: MpLog): def shutdown_or_startup(mplog: MpLog):
from apps.wpm.models import StLog from apps.wpm.models import StLog
from apps.wpm.tasks import cal_shut_hour from apps.wpm.tasks import cal_exp_duration_hour
from apps.wpm.services import get_sflog
mpoint = mplog.mpoint mpoint = mplog.mpoint
mgroup = mpoint.mgroup mgroup = mpoint.mgroup
last_stlog = StLog.objects.filter(mgroup=mgroup).order_by('start_time').last() last_stlog = StLog.objects.filter(
mgroup=mgroup, is_shutdown=True).order_by('start_time').last() # 找到最后一次停机记录
if last_stlog: if last_stlog:
if mplog.tag_update >= last_stlog.start_time: # 认为是有效信号 if mplog.tag_update >= last_stlog.start_time: # 认为是有效信号
if last_stlog.end_time is None and mplog.tag_val==1: if last_stlog.end_time is None and mplog.tag_val == 1: # 从停到开
last_stlog.end_time = mplog.tag_update last_stlog.end_time = mplog.tag_update
last_stlog.duration = (last_stlog.end_time - last_stlog.start_time).total_seconds()/3600 last_stlog.duration = (
last_stlog.end_time - last_stlog.start_time).total_seconds()/3600
last_stlog.save() last_stlog.save()
mgroup.is_runing = True mgroup.is_runing = True
mgroup.save() mgroup.save()
cal_shut_hour(last_stlog.id) # 触发停机时间分配 cal_exp_duration_hour(last_stlog.id) # 触发时间分配
elif last_stlog.end_time and mplog.tag_val==0 and mplog.tag_update > last_stlog.end_time: elif last_stlog.end_time and mplog.tag_val == 0 and mplog.tag_update > last_stlog.end_time: # 从开到停
StLog.objects.create(mgroup=mgroup, end_time=None, start_time=mplog.tag_update) StLog.objects.create(
title='停机',
is_shutdown=True,
mgroup=mgroup,
end_time=None,
start_time=mplog.tag_update,
sflog=get_sflog(mgroup, mplog.tag_update)
)
mgroup.is_runing = False mgroup.is_runing = False
mgroup.save() mgroup.save()
else: else:
StLog.objects.create(mgroup=mgroup, end_time=None, start_time=mplog.tag_update) StLog.objects.create(
title='停机',
is_shutdown=True,
mgroup=mgroup,
end_time=None,
start_time=mplog.tag_update,
sflog=get_sflog(mgroup, mplog.tag_update))
mgroup.is_runing = False mgroup.is_runing = False
mgroup.save() mgroup.save()

View File

@ -12,7 +12,7 @@ import datetime
from django.db.models import Sum, Avg from django.db.models import Sum, Avg
from dateutil import tz from dateutil import tz
from django.conf import settings from django.conf import settings
from apps.wpm.services import make_sflogs from apps.wpm.services import get_sflog
from apps.mtm.models import Mgroup, Material from apps.mtm.models import Mgroup, Material
from apps.fim.services import get_cost_unit, get_price_unit from apps.fim.services import get_cost_unit, get_price_unit
from apps.fim.models import Fee from apps.fim.models import Fee
@ -156,13 +156,7 @@ def cal_mpointstat_hour(mpointId: str, year: int, month: int, day: int, hour: in
mgroup = Mgroup.objects.get(id=allocate['mgroup']) mgroup = Mgroup.objects.get(id=allocate['mgroup'])
ratio = allocate['ratio'] ratio = allocate['ratio']
# 查找并绑定值班记录 # 查找并绑定值班记录
sflog = SfLog.objects.filter( sflog = get_sflog(mgroup, dt)
start_time__lt=dt, end_time__gte=dt, mgroup=mgroup).first()
if sflog is None: # 需要创建值班记录
make_sflogs(mgroup=mgroup, start_date=(
dt-datetime.timedelta(days=1)).date(), end_date=dt.date())
sflog = SfLog.objects.filter(
start_time__lt=dt, end_time__gte=dt, mgroup=mgroup).first()
year_s, month_s, day_s = sflog.get_ymd year_s, month_s, day_s = sflog.get_ymd
params_hour_s = {'type': 'hour_s', 'mpoint': mpoint, 'sflog': sflog, 'mgroup': mgroup, 'year': year, params_hour_s = {'type': 'hour_s', 'mpoint': mpoint, 'sflog': sflog, 'mgroup': mgroup, 'year': year,

View File

@ -1,5 +1,5 @@
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from apps.wpm.models import SfLog, SfLogExp, WMaterial, Mlog, Handover from apps.wpm.models import SfLog, StLog, WMaterial, Mlog, Handover
['mgroup', 'shift', 'team', 'leader', 'team__belong_dept'] ['mgroup', 'shift', 'team', 'leader', 'team__belong_dept']
@ -17,27 +17,18 @@ class SfLogFilter(filters.FilterSet):
} }
class SfLogExpFilter(filters.FilterSet): class StLogFilter(filters.FilterSet):
is_st = filters.BooleanFilter(method='filter_is_st', label='是否停机')
class Meta: class Meta:
model = SfLogExp model = StLog
fields = { fields = {
"mgroup": ["exact"],
"mgroup__name": ["exact"],
"start_time": ["day", "month", "year"],
"end_time": ["isnull"],
"sflog": ["exact"], "sflog": ["exact"],
"sflog__mgroup": ["exact"], "sflogs": ["exact"]
"sflog__mgroup__name": ["exact"],
"happen_time": ["day", "month", "year"],
"sflog__end_time": ["day", "month", "year"],
"stlog": ["exact"],
"stlog__start_time": ["day", "month", "year"],
"stlog__end_time": ["day", "month", "year"]
} }
def filter_is_st(self, queryset, name, value):
if value:
return queryset.exclude(stlog=None)
return queryset.filter(stlog=None)
class WMaterialFilter(filters.FilterSet): class WMaterialFilter(filters.FilterSet):
class Meta: class Meta:

View File

@ -0,0 +1,117 @@
# Generated by Django 3.2.12 on 2023-11-22 07:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0033_sflog_total_hour'),
]
operations = [
migrations.RemoveField(
model_name='sflogexp',
name='cate',
),
migrations.RemoveField(
model_name='sflogexp',
name='handler',
),
migrations.RemoveField(
model_name='sflogexp',
name='happen_time',
),
migrations.RemoveField(
model_name='sflogexp',
name='is_current_down',
),
migrations.RemoveField(
model_name='sflogexp',
name='measure',
),
migrations.RemoveField(
model_name='sflogexp',
name='reason',
),
migrations.RemoveField(
model_name='sflogexp',
name='title',
),
migrations.AddField(
model_name='sflogexp',
name='note',
field=models.TextField(blank=True, default='', verbose_name='处理备注'),
),
migrations.AddField(
model_name='stlog',
name='cate',
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='原因类别'),
),
migrations.AddField(
model_name='stlog',
name='handler',
field=models.CharField(default='', max_length=100, verbose_name='处理人'),
),
migrations.AddField(
model_name='stlog',
name='is_shutdown',
field=models.BooleanField(default=False, verbose_name='是否是停机'),
),
migrations.AddField(
model_name='stlog',
name='measure',
field=models.TextField(default='', max_length=100, verbose_name='处置措施'),
),
migrations.AddField(
model_name='stlog',
name='reason',
field=models.TextField(default='', max_length=100, verbose_name='事件原因'),
),
migrations.AddField(
model_name='stlog',
name='sflog',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.sflog', verbose_name='发生时所在值班'),
),
migrations.AddField(
model_name='stlog',
name='sflogs',
field=models.ManyToManyField(related_name='stlog_sflogs', through='wpm.SfLogExp', to='wpm.SfLog', verbose_name='关联所有当班'),
),
migrations.AddField(
model_name='stlog',
name='title',
field=models.CharField(default='', max_length=20, verbose_name='异常名称'),
),
migrations.AlterField(
model_name='sflog',
name='stlogs',
field=models.ManyToManyField(related_name='sflog_stlogs', through='wpm.SfLogExp', to='wpm.StLog', verbose_name='关联异常记录'),
),
migrations.AlterField(
model_name='sflogexp',
name='duration',
field=models.FloatField(blank=True, null=True, verbose_name='持续时长(h)'),
),
migrations.AlterField(
model_name='sflogexp',
name='stlog',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.stlog', verbose_name='关联异常记录'),
),
migrations.AlterField(
model_name='stlog',
name='duration',
field=models.FloatField(blank=True, null=True, verbose_name='持续时间(h)'),
),
migrations.AlterField(
model_name='stlog',
name='end_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='结束时间'),
),
migrations.AlterField(
model_name='stlog',
name='start_time',
field=models.DateTimeField(verbose_name='发生时间'),
),
]

View File

@ -10,17 +10,6 @@ from apps.system.models import Dept
# Create your models here. # Create your models here.
class StLog(CommonADModel):
"""
停机记录
"""
mgroup = models.ForeignKey(
Mgroup, verbose_name='关联工段', on_delete=models.CASCADE)
start_time = models.DateTimeField('停机开始')
end_time = models.DateTimeField('停机结束', null=True, blank=True)
duration = models.FloatField('停机时长(h)', null=True, blank=True)
class SfLog(CommonADModel): class SfLog(CommonADModel):
"""值班记录 """值班记录
""" """
@ -37,7 +26,7 @@ class SfLog(CommonADModel):
end_time = models.DateTimeField('值班结束') end_time = models.DateTimeField('值班结束')
note = models.TextField('其他备注', null=True, blank=True) note = models.TextField('其他备注', null=True, blank=True)
stlogs = models.ManyToManyField( stlogs = models.ManyToManyField(
'wpm.stlog', verbose_name='关联停机记录', through='wpm.sflogexp') 'wpm.stlog', verbose_name='关联异常记录', through='wpm.sflogexp', related_name='sflog_stlogs')
last_test_time = models.DateTimeField('最后质检时间', null=True, blank=True) last_test_time = models.DateTimeField('最后质检时间', null=True, blank=True)
total_hour_now = models.FloatField('总时长动', default=0) total_hour_now = models.FloatField('总时长动', default=0)
total_hour = models.FloatField('总时长', default=12) total_hour = models.FloatField('总时长', default=12)
@ -53,22 +42,37 @@ class SfLog(CommonADModel):
return end_time_local.year, end_time_local.month, end_time_local.day return end_time_local.year, end_time_local.month, end_time_local.day
class SfLogExp(CommonADModel): class StLog(CommonADModel):
""" """
生产异常情况记录 异常记录
""" """
title = models.CharField('异常名称', max_length=20, default='')
is_shutdown = models.BooleanField('是否是停机', default=False)
mgroup = models.ForeignKey(
Mgroup, verbose_name='关联工段', on_delete=models.CASCADE)
sflog = models.ForeignKey( sflog = models.ForeignKey(
SfLog, on_delete=models.CASCADE, verbose_name='关联值班记录') SfLog, on_delete=models.CASCADE, verbose_name='发生时所在值班', null=True, blank=True)
stlog = models.ForeignKey( sflogs = models.ManyToManyField(
StLog, verbose_name='关联停机记录', on_delete=models.CASCADE, null=True, blank=True) 'wpm.sflog', verbose_name='关联所有当班', through='wpm.sflogexp', related_name='stlog_sflogs')
title = models.CharField('异常名称', max_length=20) start_time = models.DateTimeField('发生时间')
happen_time = models.DateTimeField('发生时间', null=True, blank=True) end_time = models.DateTimeField('结束时间', null=True, blank=True)
duration = models.FloatField('持续时间(h)', null=True, blank=True)
cate = models.CharField('原因类别', max_length=10, null=True, blank=True) cate = models.CharField('原因类别', max_length=10, null=True, blank=True)
reason = models.TextField('事件原因', default='', max_length=100) reason = models.TextField('事件原因', default='', max_length=100)
measure = models.TextField('处置措施', default='', max_length=100) measure = models.TextField('处置措施', default='', max_length=100)
handler = models.CharField('处理人', default='', max_length=100) handler = models.CharField('处理人', default='', max_length=100)
is_current_down = models.BooleanField('是否本班停机', default=False)
duration = models.FloatField('停机时长(h)', null=True, blank=True)
class SfLogExp(CommonADModel):
"""
异常处理
"""
sflog = models.ForeignKey(
SfLog, on_delete=models.CASCADE, verbose_name='关联值班记录')
stlog = models.ForeignKey(
StLog, verbose_name='关联异常记录', on_delete=models.CASCADE)
duration = models.FloatField('持续时长(h)', null=True, blank=True)
note = models.TextField('处理备注', default='', blank=True)
class WMaterial(CommonBDModel): class WMaterial(CommonBDModel):

View File

@ -1,13 +1,15 @@
from apps.utils.constants import EXCLUDE_FIELDS from apps.utils.constants import EXCLUDE_FIELDS
from apps.utils.serializers import CustomModelSerializer from apps.utils.serializers import CustomModelSerializer
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError, ParseError
from datetime import datetime
from .models import SfLog, StLog, SfLogExp, WMaterial, Mlog, Handover, Mlogb, AttLog from .models import SfLog, StLog, SfLogExp, WMaterial, Mlog, Handover, Mlogb, AttLog
from apps.system.models import Dept, User from apps.system.models import Dept, User
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.pm.models import Mtask from apps.pm.models import Mtask
from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change, cal_exp_duration_hour
from apps.wpm.services import get_sflog
from apps.mtm.models import Mgroup, TeamMember, Shift from apps.mtm.models import Mgroup, TeamMember, Shift
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
from django.db import transaction from django.db import transaction
@ -16,10 +18,62 @@ from django.utils import timezone
class StLogSerializer(CustomModelSerializer): class StLogSerializer(CustomModelSerializer):
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True) mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
current_sflog = serializers.CharField(label='当前处理值班', write_only=True)
current_note = serializers.CharField(
label='值班处理备注', write_only=True, allow_blank=True)
class Meta: class Meta:
model = StLog model = StLog
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + \
['is_shutdown', 'sflog', 'duration']
def create(self, validated_data):
current_sflog = validated_data.pop('current_sflog')
current_note = validated_data.pop('note', '')
with transaction.atomic():
validated_data['sflog'] = get_sflog(
validated_data['mgroup'], validated_data['start_time'])
if current_sflog != validated_data['sflog']:
raise ParseError('值班时间与发生时间不一致')
instance = super().create(validated_data)
SfLogExp.objects.create(
sflog=current_sflog, stlog=instance, create_by=self.context['request'].user, note=current_note)
return instance
def update(self, instance, validated_data):
if instance.is_shutdown: # 停机不可编辑end_time
validated_data.pop('end_time', None)
if instance.end_time:
raise ParseError('该异常已结束无需编辑')
with transaction.atomic():
validated_data.pop('mgroup', None)
validated_data.pop('start_time', None)
end_time = validated_data.pop('end_time', None)
if end_time: # 需要把涉及到的sflog都拉入
cal_exp_duration_hour(instance.id)
current_sflog = validated_data.pop('current_sflog')
current_note = validated_data.pop('current_note', '')
instance = super().update(instance, validated_data)
try:
sflogexp = SfLogExp.objects.get(
stlog=instance.stlog, sflog=current_sflog)
sflogexp.note = current_note
sflogexp.save()
except SfLogExp.DoesNotExist:
raise ParseError('该异常无需本班填写')
return instance
def validate(self, attrs):
start_time: datetime = attrs['start_time']
end_time: datetime = attrs.get('end_time', None)
if end_time:
if end_time > start_time:
attrs['duration'] = (
end_time - start_time).total_seconds / 3600
else:
raise ParseError('结束时间应大于开始时间')
return super().validate(attrs)
class SfLogSerializer(CustomModelSerializer): class SfLogSerializer(CustomModelSerializer):
@ -68,17 +122,17 @@ class SfLogSerializer(CustomModelSerializer):
class SflogExpSerializer(CustomModelSerializer): class SflogExpSerializer(CustomModelSerializer):
mgroup = serializers.CharField(source='sflog.mgroup.id', read_only=True) # mgroup = serializers.CharField(source='sflog.mgroup.id', read_only=True)
mgroup_name = serializers.CharField( # mgroup_name = serializers.CharField(
source='sflog.mgroup.name', read_only=True) # source='sflog.mgroup.name', read_only=True)
stlog_ = StLogSerializer(source='stlog', read_only=True) # stlog_ = StLogSerializer(source='stlog', read_only=True)
happen_time = serializers.DateTimeField(required=True, label='发生时间') # happen_time = serializers.DateTimeField(required=True, label='发生时间')
cate = serializers.CharField(required=True, label='原因类别') # cate = serializers.CharField(required=True, label='原因类别')
class Meta: class Meta:
model = SfLogExp model = SfLogExp
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['stlog', 'is_current_down'] read_only_fields = EXCLUDE_FIELDS + ['sflog', 'stlog', 'duration']
class WMaterialSerializer(CustomModelSerializer): class WMaterialSerializer(CustomModelSerializer):

View File

@ -16,6 +16,17 @@ from apps.mtm.models import Mgroup, Shift, Material, Route
from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover
def get_sflog(mgroup: Mgroup, happen_time: datetime):
sflog = SfLog.objects.filter(
start_time__lt=happen_time, end_time__gte=happen_time, mgroup=mgroup).first()
if sflog is None: # 需要创建值班记录
make_sflogs(mgroup=mgroup, start_date=(
happen_time-datetime.timedelta(days=1)).date(), end_date=happen_time.date())
sflog = SfLog.objects.filter(
start_time__lt=happen_time, end_time__gte=happen_time, mgroup=mgroup).first()
return sflog
def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.date, create_by=None): def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.date, create_by=None):
shift_rule = mgroup.shift_rule shift_rule = mgroup.shift_rule
shifts = Shift.objects.filter(rule=shift_rule) # 根据排班规则制定排班记录 shifts = Shift.objects.filter(rule=shift_rule) # 根据排班规则制定排班记录

View File

@ -58,33 +58,34 @@ def get_total_hour_now(sflogId: str):
@shared_task(base=CustomTask) @shared_task(base=CustomTask)
def cal_shut_hour(stlogId: str): def cal_exp_duration_hour(stlogId: str):
""" """
计算停机记录对应的班停时长 计算异常记录对应的每班持续时间
""" """
from apps.enm.tasks import cal_enstat from apps.enm.tasks import cal_enstat # 如果是停机需要进行统计停机时长
if stlogId: if stlogId:
stlogs = StLog.objects.filter(id=stlogId) stlogs = StLog.objects.filter(id=stlogId)
else: else: # 不传就默认更新所有的
stlogs = StLog.objects.filter(end_time=None) stlogs = StLog.objects.filter(end_time=None)
now = timezone.now() now = timezone.now()
for stlog in stlogs: for stlog in stlogs:
need_cal_enstat = True
if stlog.is_shutdown is False:
need_cal_enstat = False
st_start = stlog.start_time st_start = stlog.start_time
if st_start >= now: if st_start >= now:
break break
if stlog.end_time is None: # 说明停机还未结束,此时也需要计算duration if stlog.end_time is None: # 说明异常还未结束,此时也需要计算duration
st_end = now st_end = now
else: else:
st_end = stlog.end_time st_end = stlog.end_time
sf_qs = SfLog.objects.filter(mgroup=stlog.mgroup) sf_qs = SfLog.objects.filter(mgroup=stlog.mgroup)
sf_qs = (sf_qs.filter(start_time__gte=st_start, start_time__lt=st_end) | sf_qs.filter(end_time__gt=st_start, sf_qs = (sf_qs.filter(start_time__gte=st_start, start_time__lt=st_end) | sf_qs.filter(end_time__gt=st_start,
end_time__lte=st_end) | sf_qs.filter(start_time__lte=st_start, end_time__gte=st_end)).order_by('start_time').distinct() end_time__lte=st_end) | sf_qs.filter(start_time__lte=st_start, end_time__gte=st_end)).order_by('start_time').distinct()
SfLogExp.objects.filter(stlog=stlog, sflog__in=sf_qs).delete()
for ind, sflog in enumerate(sf_qs): for ind, sflog in enumerate(sf_qs):
is_current_down = False
if ind == 0:
is_current_down = True
sflogexp, _ = SfLogExp.objects.get_or_create(stlog=stlog, sflog=sflog, defaults={ sflogexp, _ = SfLogExp.objects.get_or_create(stlog=stlog, sflog=sflog, defaults={
'stlog': stlog, 'sflog': sflog, 'is_current_down': is_current_down, 'title': '停机'}) 'stlog': stlog, 'sflog': sflog})
# 计算duration # 计算duration
sf_end, sf_start = sflog.end_time, sflog.start_time sf_end, sf_start = sflog.end_time, sflog.start_time
duration_item_delta = min(sf_end, st_end) - max(sf_start, st_start) duration_item_delta = min(sf_end, st_end) - max(sf_start, st_start)
@ -95,18 +96,19 @@ def cal_shut_hour(stlogId: str):
duration_item = total_seconds/3600 duration_item = total_seconds/3600
sflogexp.duration = duration_item sflogexp.duration = duration_item
sflogexp.save() sflogexp.save()
# 计算每班的总停机时间 if need_cal_enstat:
ret = SfLogExp.objects.filter(sflog=sflog).exclude( # 计算每班的总停机时间
stlog=None).aggregate(sum=Sum('duration')) ret = SfLogExp.objects.filter(
if ret.get('sum', 0): sflog=sflog, stlog__is_shutdown=True).aggregate(sum=Sum('duration'))
sflog.shut_hour = ret['sum'] if ret.get('sum', 0):
sflog.save() sflog.shut_hour = ret['sum']
# 更新sflog总时长 sflog.save()
if sflog.end_time > now: # 更新sflog总时长
get_total_hour_now(sflog.id) if sflog.end_time > now:
if stlogId: get_total_hour_now(sflog.id)
cal_enstat('sflog', sflog.id, sflog.mgroup.id, None, None, None, if stlogId:
None, None, None, None, cascade=True, cal_attrs=['run_hour']) cal_enstat('sflog', sflog.id, sflog.mgroup.id, None, None, None,
None, None, None, None, cascade=True, cal_attrs=['run_hour'])
@shared_task(base=CustomTask) @shared_task(base=CustomTask)

View File

@ -13,7 +13,7 @@ from apps.pm.models import Mtask
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin from apps.utils.mixins import BulkCreateModelMixin
from .filters import SfLogExpFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer,
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
@ -22,19 +22,24 @@ from .services import mlog_submit, update_mtask, handover_submit
# Create your views here. # Create your views here.
class StLogViewSet(ListModelMixin, CustomGenericViewSet): class StLogViewSet(CustomModelViewSet):
""" """
list:停机记录 list:异常记录
停机记录 异常记录
""" """
perms_map = {'get': '*'}
queryset = StLog.objects.all() queryset = StLog.objects.all()
serializer_class = StLogSerializer serializer_class = StLogSerializer
select_related_fields = ['mgroup'] select_related_fields = ['mgroup']
filterset_fields = ['mgroup'] filterset_class = StLogFilter
ordering = ['-start_time'] ordering = ['-start_time']
def destroy(self, request, *args, **kwargs):
obj: StLog = self.get_object()
if obj.is_shutdown:
raise ParseError('停机记录不可删除')
return super().destroy(request, *args, **kwargs)
class SfLogViewSet(UpdateModelMixin, ListModelMixin, DestroyModelMixin, CustomGenericViewSet): class SfLogViewSet(UpdateModelMixin, ListModelMixin, DestroyModelMixin, CustomGenericViewSet):
""" """
@ -76,16 +81,16 @@ class SfLogViewSet(UpdateModelMixin, ListModelMixin, DestroyModelMixin, CustomGe
return Response(sr.data) return Response(sr.data)
class SfLogExpViewSet(CustomModelViewSet): class SfLogExpViewSet(ListModelMixin, UpdateModelMixin, CustomGenericViewSet):
""" """
list:生产异常动态 list:异常值班处理
生产异常动态 异常值班处理
""" """
queryset = SfLogExp.objects.all() queryset = SfLogExp.objects.all()
serializer_class = SflogExpSerializer serializer_class = SflogExpSerializer
select_related_fields = ['sflog', 'sflog__mgroup', 'stlog'] select_related_fields = ['sflog', 'sflog__mgroup', 'stlog']
filterset_class = SfLogExpFilter filterset_fields = ['sflog', 'stlog']
class WMaterialViewSet(ListModelMixin, CustomGenericViewSet): class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):