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.services import WfService
from django.utils.timezone import localtime
@ -150,10 +150,11 @@ class CorrectViewSet(CustomGenericViewSet):
from apps.enm.models import Mpoint
for mpoint in Mpoint.objects.exclude(mgroup=None):
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()
return Response()
@action(methods=['post'], detail=False, serializer_class=Serializer)
def mpointstat(self, request, pk=None):
"""矫正测点统计数据
@ -194,7 +195,7 @@ class CorrectViewSet(CustomGenericViewSet):
item.shift = item.sflog.shift
item.save()
return Response()
# @action(methods=['post'], detail=False, serializer_class=Serializer)
# def global_img_compressed(self, request, pk=None):
# """矫正全景压缩照片
@ -209,6 +210,7 @@ class CorrectViewSet(CustomGenericViewSet):
# event.save()
# return Response()
class TestViewSet(CustomGenericViewSet):
authentication_classes = ()
permission_classes = ()
@ -261,7 +263,8 @@ class TestViewSet(CustomGenericViewSet):
m = importlib.import_module(module)
f = getattr(m, func)
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})
else:
f(*vdata.get('args', []), **vdata.get('kwargs', {}))
@ -299,7 +302,8 @@ class TestViewSet(CustomGenericViewSet):
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': 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)
return Response()
@ -572,6 +576,6 @@ class TestViewSet(CustomGenericViewSet):
@action(methods=['post'], detail=False, serializer_class=Serializer, permission_classes=[])
def test_cal(self, request, pk=None):
from apps.wpm.tasks import cal_shut_hour
cal_shut_hour('3397169058570170368')
return Response()
from apps.wpm.tasks import cal_exp_duration_hour
cal_exp_duration_hour('3397169058570170368')
return Response()

View File

@ -14,7 +14,8 @@ def translate_eval_formula(exp_str: str, year: int, month: int, day: int, hour:
pattern = r"\${(.*?)}"
matches = re.findall(pattern, exp_str)
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:
exp_str = exp_str.replace(f"${{{match}}}", str(mpst.val))
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']
# for item in data['qua_data']:
# qua_rate[f'{item["material_name"]}_{item["testitem_name"]}'] = item["rate_pass"]
# goal_dict = get_mgroup_goals(data['mgroup'], data['year_s'], False)
# goal_data = {}
# 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())
# return goal_data, score
def shutdown_or_startup(mplog: MpLog):
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
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 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.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()
mgroup.is_runing = True
mgroup.save()
cal_shut_hour(last_stlog.id) # 触发停机时间分配
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)
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: # 从开到停
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.save()
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.save()

View File

@ -12,7 +12,7 @@ import datetime
from django.db.models import Sum, Avg
from dateutil import tz
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.fim.services import get_cost_unit, get_price_unit
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'])
ratio = allocate['ratio']
# 查找并绑定值班记录
sflog = SfLog.objects.filter(
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()
sflog = get_sflog(mgroup, dt)
year_s, month_s, day_s = sflog.get_ymd
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 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']
@ -17,27 +17,18 @@ class SfLogFilter(filters.FilterSet):
}
class SfLogExpFilter(filters.FilterSet):
is_st = filters.BooleanFilter(method='filter_is_st', label='是否停机')
class StLogFilter(filters.FilterSet):
class Meta:
model = SfLogExp
model = StLog
fields = {
"mgroup": ["exact"],
"mgroup__name": ["exact"],
"start_time": ["day", "month", "year"],
"end_time": ["isnull"],
"sflog": ["exact"],
"sflog__mgroup": ["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"]
"sflogs": ["exact"]
}
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 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.
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):
"""值班记录
"""
@ -37,7 +26,7 @@ class SfLog(CommonADModel):
end_time = models.DateTimeField('值班结束')
note = models.TextField('其他备注', null=True, blank=True)
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)
total_hour_now = models.FloatField('总时长动', default=0)
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
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, on_delete=models.CASCADE, verbose_name='关联值班记录')
stlog = models.ForeignKey(
StLog, verbose_name='关联停机记录', on_delete=models.CASCADE, null=True, blank=True)
title = models.CharField('异常名称', max_length=20)
happen_time = models.DateTimeField('发生时间', null=True, blank=True)
SfLog, on_delete=models.CASCADE, verbose_name='发生时所在值班', null=True, blank=True)
sflogs = models.ManyToManyField(
'wpm.sflog', verbose_name='关联所有当班', through='wpm.sflogexp', related_name='stlog_sflogs')
start_time = models.DateTimeField('发生时间')
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)
reason = models.TextField('事件原因', default='', max_length=100)
measure = models.TextField('处置措施', 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):

View File

@ -1,13 +1,15 @@
from apps.utils.constants import EXCLUDE_FIELDS
from apps.utils.serializers import CustomModelSerializer
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 apps.system.models import Dept, User
from apps.system.serializers import UserSimpleSerializer
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.serializers import MaterialSimpleSerializer
from django.db import transaction
@ -16,10 +18,62 @@ from django.utils import timezone
class StLogSerializer(CustomModelSerializer):
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:
model = StLog
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):
@ -68,17 +122,17 @@ class SfLogSerializer(CustomModelSerializer):
class SflogExpSerializer(CustomModelSerializer):
mgroup = serializers.CharField(source='sflog.mgroup.id', read_only=True)
mgroup_name = serializers.CharField(
source='sflog.mgroup.name', read_only=True)
stlog_ = StLogSerializer(source='stlog', read_only=True)
happen_time = serializers.DateTimeField(required=True, label='发生时间')
cate = serializers.CharField(required=True, label='原因类别')
# mgroup = serializers.CharField(source='sflog.mgroup.id', read_only=True)
# mgroup_name = serializers.CharField(
# source='sflog.mgroup.name', read_only=True)
# stlog_ = StLogSerializer(source='stlog', read_only=True)
# happen_time = serializers.DateTimeField(required=True, label='发生时间')
# cate = serializers.CharField(required=True, label='原因类别')
class Meta:
model = SfLogExp
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['stlog', 'is_current_down']
read_only_fields = EXCLUDE_FIELDS + ['sflog', 'stlog', 'duration']
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
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):
shift_rule = mgroup.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)
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:
stlogs = StLog.objects.filter(id=stlogId)
else:
else: # 不传就默认更新所有的
stlogs = StLog.objects.filter(end_time=None)
now = timezone.now()
for stlog in stlogs:
need_cal_enstat = True
if stlog.is_shutdown is False:
need_cal_enstat = False
st_start = stlog.start_time
if st_start >= now:
break
if stlog.end_time is None: # 说明停机还未结束,此时也需要计算duration
if stlog.end_time is None: # 说明异常还未结束,此时也需要计算duration
st_end = now
else:
st_end = stlog.end_time
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,
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):
is_current_down = False
if ind == 0:
is_current_down = True
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
sf_end, sf_start = sflog.end_time, sflog.start_time
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
sflogexp.duration = duration_item
sflogexp.save()
# 计算每班的总停机时间
ret = SfLogExp.objects.filter(sflog=sflog).exclude(
stlog=None).aggregate(sum=Sum('duration'))
if ret.get('sum', 0):
sflog.shut_hour = ret['sum']
sflog.save()
# 更新sflog总时长
if sflog.end_time > now:
get_total_hour_now(sflog.id)
if stlogId:
cal_enstat('sflog', sflog.id, sflog.mgroup.id, None, None, None,
None, None, None, None, cascade=True, cal_attrs=['run_hour'])
if need_cal_enstat:
# 计算每班的总停机时间
ret = SfLogExp.objects.filter(
sflog=sflog, stlog__is_shutdown=True).aggregate(sum=Sum('duration'))
if ret.get('sum', 0):
sflog.shut_hour = ret['sum']
sflog.save()
# 更新sflog总时长
if sflog.end_time > now:
get_total_hour_now(sflog.id)
if stlogId:
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)

View File

@ -13,7 +13,7 @@ from apps.pm.models import Mtask
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
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 .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer,
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
@ -22,19 +22,24 @@ from .services import mlog_submit, update_mtask, handover_submit
# Create your views here.
class StLogViewSet(ListModelMixin, CustomGenericViewSet):
class StLogViewSet(CustomModelViewSet):
"""
list:停机记录
list:异常记录
停机记录
异常记录
"""
perms_map = {'get': '*'}
queryset = StLog.objects.all()
serializer_class = StLogSerializer
select_related_fields = ['mgroup']
filterset_fields = ['mgroup']
filterset_class = StLogFilter
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):
"""
@ -76,16 +81,16 @@ class SfLogViewSet(UpdateModelMixin, ListModelMixin, DestroyModelMixin, CustomGe
return Response(sr.data)
class SfLogExpViewSet(CustomModelViewSet):
class SfLogExpViewSet(ListModelMixin, UpdateModelMixin, CustomGenericViewSet):
"""
list:生产异常动态
list:异常值班处理
生产异常动态
异常值班处理
"""
queryset = SfLogExp.objects.all()
serializer_class = SflogExpSerializer
select_related_fields = ['sflog', 'sflog__mgroup', 'stlog']
filterset_class = SfLogExpFilter
filterset_fields = ['sflog', 'stlog']
class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):