增加dpm 双控app

This commit is contained in:
caoqianming 2023-03-03 10:01:37 +08:00
parent 34de8fdfec
commit 2aad51811c
10 changed files with 410 additions and 0 deletions

0
apps/dpm/__init__.py Normal file
View File

3
apps/dpm/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

7
apps/dpm/apps.py Normal file
View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
class DpmConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'dpm'
verbose_name = '双重预防'

View File

155
apps/dpm/models.py Normal file
View File

@ -0,0 +1,155 @@
from django.db import models
from apps.utils.models import CommonBModel, CommonAModel
from django_celery_beat.models import PeriodicTask
# Create your models here.
class RiskPoint(CommonBModel):
"""
风险点表
"""
R_LEVEL = (
(10, '低风险'),
(20, '一般风险'),
(30, '较大风险'),
(40, '重大风险')
)
R_TYPE = (
(10, '作业活动类'),
(20, '设备设施类')
)
R_M_LEVEL = (
(10, '岗位级'),
(20, '班组级'),
(30, '部门级'),
(40, '公司级')
)
name = models.CharField('名称', max_length=40)
area = models.ForeignKey('am.area', on_delete=models.CASCADE, verbose_name='所在区域', related_name='riskpoint_area', null=True, blank=True)
place = models.CharField('具体地点', max_length=100, null=True, blank=True)
level = models.PositiveSmallIntegerField('风险点等级', default=10, choices=R_LEVEL)
type = models.PositiveSmallIntegerField('风险点类型', default=10, choices=R_TYPE)
manage_level = models.PositiveSmallIntegerField('管控层级', default=10, choices=R_M_LEVEL)
dept_res = models.ForeignKey('system.dept', on_delete=models.SET_NULL, verbose_name='责任部门', related_name='riskpoint_dept_res', null=True, blank=True)
user_res = models.ForeignKey('system.user', on_delete=models.CASCADE, verbose_name='责任人', related_name='hazard_user_discover', null=True, blank=True)
class Risk(CommonBModel):
"""
风险表
"""
name = models.TextField('项目/步骤')
level = models.PositiveSmallIntegerField('风险等级', default=10, choices=RiskPoint.R_LEVEL)
standard = models.TextField('检查标准', null=True, blank=True)
hazard = models.TextField('危害因素', null=True, blank=True)
results = models.ManyToManyField('system.dictionary', verbose_name='可能后果', related_name='risk_results', null=True)
sort = models.PositiveSmallIntegerField('排序', default=1)
measure_tech = models.TextField('工程技术措施', null=True, blank=True)
measure_manage = models.TextField('管理措施', null=True, blank=True)
measure_edu = models.TextField('培训教育措施', null=True, blank=True)
measure_protect = models.TextField('个体防护措施', null=True, blank=True)
measure_emer = models.TextField('应急处置措施', null=True, blank=True)
riskl = models.DecimalField(max_digits=6, decimal_places=1, null=True, blank=True)
riske = models.DecimalField(max_digits=6, decimal_places=1, null=True, blank=True)
riskc = models.DecimalField(max_digits=6, decimal_places=1, null=True, blank=True)
riskd = models.DecimalField(max_digits=6, decimal_places=1, null=True, blank=True)
riskpoint = models.ForeignKey(RiskPoint, on_delete=models.CASCADE, verbose_name='所属风险点')
class CheckTaskSet(CommonBModel):
"""
检查任务派发设置
"""
riskpoint = models.ForeignKey(RiskPoint, verbose_name='关联风险点', related_name='ctask_riskpoint', on_delete=models.SET_NULL, null=True, blank=True)
note = models.TextField('派发备注', null=True, blank=True)
expire = models.PositiveSmallIntegerField('执行有效期(h)', default=0, help_text='0代表不设限制直到下个任务派发')
user_duty = models.ForeignKey('system.user', verbose_name='指派人', on_delete=models.SET_NULL, null=True, blank=True)
myschedule = models.ForeignKey('system.myschedule', verbose_name='周期', on_delete=models.SET_NULL, null=True, blank=True)
periodictask = models.ForeignKey(PeriodicTask, verbose_name='关联定时任务', on_delete=models.CASCADE, related_name='checktask_periodictask')
class CheckWork(CommonBModel):
"""
检查工作
"""
CW_TYPE = (
(10, '手动'),
(20, '派发')
)
name = models.CharField('名称', max_length=40)
description = models.TextField('工作描述', null=True, blank=True)
type = models.PositiveSmallIntegerField('任务类型', default=10, choices=CW_TYPE)
category = models.ForeignKey('system.dictionary', on_delete=models.CASCADE, verbose_name='工作分类', null=True, blank=True)
time_start = models.DateTimeField('开启时间')
time_end = models.DateTimeField('关闭时间')
enabled = models.BooleanField('是否可用', default=True)
user_duty = models.ForeignKey('system.user', verbose_name='指派人', on_delete=models.SET_NULL, null=True, blank=True)
riskpoint = models.ForeignKey(RiskPoint, verbose_name='关联风险点', related_name='cwork_riskpoint', on_delete=models.SET_NULL, null=True, blank=True)
note = models.TextField('检查备注', null=True, blank=True)
time_check = models.BooleanField('最后检查时间', default=True)
checktaskset = models.ForeignKey(CheckTaskSet, verbose_name='关联派发任务', on_delete=models.CASCADE, null=True, blank=True)
class Hazard(CommonBModel):
"""
事故隐患表
"""
H_HARM = (
(10, '无伤害'),
(20, '轻微伤'),
(30, '轻伤'),
(40, '重伤'),
(50, '死亡')
)
H_LEVEL = (
(10, '无隐患'),
(20, '一般隐患'),
(30, '重大隐患')
)
H_STATE = (
(10, '待提交'),
(20, '流程中'),
(30, '已结束')
)
number = models.CharField('编号', max_length=20, null=True, blank=True)
checkwork = models.ForeignKey(CheckWork, verbose_name='关联检查工作', null=True, blank=True)
state = models.PositiveSmallIntegerField('隐患流程状态', default=10, choices=H_STATE)
time_discover = models.DateTimeField('发现时间')
dept_res = models.ForeignKey('system.dept', on_delete=models.SET_NULL, verbose_name='责任部门', related_name='hazard_dept_res', null=True, blank=True)
user_discover = models.ForeignKey('system.user', on_delete=models.CASCADE, verbose_name='发现人', related_name='hazard_user_discover')
area = models.ForeignKey('am.area', on_delete=models.CASCADE, verbose_name='所在区域', null=True, blank=True)
place = models.CharField('具体地点', max_length=100, null=True, blank=True)
level = models.PositiveSmallIntegerField('隐患等级', default=10, choices=H_LEVEL)
harm_access = models.PositiveSmallIntegerField('伤害评估', default=10, choices=H_HARM)
category = models.ForeignKey('system.dictionary', on_delete=models.CASCADE, verbose_name='隐患类别', null=True, blank=True)
description = models.TextField('隐患描述', null=True, blank=True)
reason = models.TextField('原因分析', null=True, blank=True)
correct_measure = models.TextField('整改措施', null=True, blank=True)
correct_solution = models.ForeignKey('system.file', verbose_name='整改方案', null=True, blank=True)
correct_deadline = models.DateTimeField('整改期限', null=True, blank=True)
photos = models.ManyToManyField('system.file', verbose_name='现场照片', related_name='hazard_photos', null=True)
correct_description = models.TextField('整改描述', null=True, blank=True)
photos_done = models.ManyToManyField('system.file', verbose_name='整改后照片', related_name='hazard_photos_done', null=True)
dept_correct = models.ForeignKey('system.dept', on_delete=models.CASCADE, verbose_name='整改部门', related_name='hazard_dept_correct', null=True, blank=True)
user_correct = models.ForeignKey('system.user', on_delete=models.SET_NULL, verbose_name='整改责任人', related_name='hazard_user_correct', null=True, blank=True)
correct_time = models.DateTimeField('整改完成时间', null=True, blank=True)
user_access = models.ForeignKey('system.user', on_delete=models.CASCADE, verbose_name='评估人', related_name='hazard_user_access')
opinion_access = models.TextField('评估意见', null=True, blank=True)
user_check = models.ForeignKey('system.user', on_delete=models.CASCADE, verbose_name='复查人', related_name='hazard_user_check')
opinion_check = models.TextField('复查意见', null=True, blank=True)
user_audit = models.ForeignKey('system.user', on_delete=models.CASCADE, verbose_name='审核人', related_name='hazard_user_audit')
opinion_audit = models.TextField('审核意见', null=True, blank=True)
ticket = models.ForeignKey('wf.ticket', on_delete=models.SET_NULL, verbose_name='关联工单', related_name='hazard_ticket', null=True, blank=True)
class CheckItem(CommonAModel):
"""
检查工作-隐患关联表
"""
CITEM_RESULT = (
(10, '未检查'),
(20, '合格'),
(30, '不合格')
)
checkwork = models.ForeignKey(CheckWork, verbose_name='关联检查工作', related_name='citem_checkwork', on_delete=models.CASCADE)
risk = models.ForeignKey(Risk, verbose_name='关联风险', related_name='citem_risk', on_delete=models.SET_NULL, null=True, blank=True)
hazard = models.ForeignKey(Hazard, verbose_name='关联隐患', related_name='citem_hazard', on_delete=models.SET_NULL, null=True, blank=True)
result = models.PositiveSmallIntegerField('检查结果', default=10)

128
apps/dpm/serializers.py Normal file
View File

@ -0,0 +1,128 @@
import json
from apps.dpm.models import CheckTaskSet, CheckWork, RiskPoint, Risk
from rest_framework import serializers
from apps.utils.serializers import CustomModelSerializer
from apps.utils.constants import EXCLUDE_FIELDS
from apps.system.models import Dictionary
from django_celery_beat.models import PeriodicTask
import time
class RiskPointCreateUpdateSerializer(CustomModelSerializer):
class Meta:
model = RiskPoint
exclude = EXCLUDE_FIELDS
class RiskPointSerializer(CustomModelSerializer):
class Meta:
model = RiskPoint
fields = '__all__'
class RiskCreateUpdateSerializer(CustomModelSerializer):
class Meta:
model = Risk
exclude = EXCLUDE_FIELDS + ['riskd']
def save(self, **kwargs):
obj = super().save(**kwargs)
if obj.riskl and obj.riske and obj.riskc:
obj.riskd = obj.riskl * obj.riske * obj.riskc
if obj.riskd >= 320:
obj.level = 40
elif 320 > obj.riskd >= 160:
obj.level = 30
elif 160 > obj.riskd >= 70:
obj.level = 20
else:
obj.level = 10
obj.save()
self.cal_riskpoint_level(obj)
return obj
@staticmethod
def cal_riskpoint_level(risk):
"""
计算风险点风险等级
"""
riskpoint = risk.riskpoint
levels = Risk.objects.filter(riskpoint=riskpoint).values_list('level', flat=True)
riskpoint.level = max(levels)
riskpoint.save()
RiskCreateUpdateSerializer.cal_area_level(riskpoint)
@staticmethod
def cal_area_level(riskpoint):
"""
计算区域风险等级
"""
area = riskpoint.area
if area:
levels = RiskPoint.objects.filter(area=area).values_list('level', flat=True)
area.level = max(levels)
area.save()
class RiskSerializer(CustomModelSerializer):
class Meta:
model = Risk
fields = '__all__'
class CheckTaskSetCreateUpdateSerializer(CustomModelSerializer):
class Meta:
model = CheckTaskSet
exclude = EXCLUDE_FIELDS + ['periodictask']
def save(self, **kwargs):
ins = super().save(**kwargs)
if ins.periodictask:
pobj = ins.periodictask
pobj.name = 'checktask_' + time.strftime('%Y%m%d%H%M%S')
pobj.task = 'apps.dpm.dispath_checkwork_task'
pobj.kwargs = json.dumps({'checktaskset': ins.id})
else:
pobj = PeriodicTask()
sch = ins.myschedule
if sch.crontab:
pobj.crontab = sch.crontab
elif sch.interval:
pobj.interval = sch.interval
pobj.save()
ins.periodictask = pobj
ins.save()
return ins
class CheckTaskSetSerializer(CustomModelSerializer):
enabled = serializers.BooleanField(source='periodictask.enabled', read_only=True)
last_run_at = serializers.DateTimeField(source='periodictask.last_run_at', read_only=True)
total_run_count = serializers.IntegerField(source='periodictask.total_run_count', read_only=True)
class Meta:
model = CheckTaskSet
fields = '__all__'
class CheckWork_10_CreateUpdateSerializer(CustomModelSerializer):
category = serializers.PrimaryKeyRelatedField(
label='检查分类', queryset=Dictionary.objects.filter(type__code='checkwork_category'))
class Meta:
model = CheckWork
fields = ['name', 'description', 'category', 'time_start', 'time_end', 'enabled']
def save(self, **kwargs):
kwargs['type'] = 10 # 创建/编辑只有手动类型
return super().save(**kwargs)
class CheckWorkSerializer(CustomModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True)
user_duty_name = serializers.CharField(source='user_duty.name', read_only=True)
riskpoint_name = serializers.CharField(source='riskpoint.name', read_only=True)
class Meta:
model = CheckWork
fields = '__all__'

30
apps/dpm/tasks.py Normal file
View File

@ -0,0 +1,30 @@
from apps.utils.tasks import CustomTask
from celery import shared_task
from apps.dpm.models import CheckTaskSet, CheckWork
from django.utils import timezone
from datetime import timedelta
@shared_task(base=CustomTask)
def dispath_checkwork_task(checktaskset: str):
cts = CheckTaskSet.objects.get(id=checktaskset)
cw = CheckWork()
cw.type = 20
cw.checktaskset = cts
cw.name = '风险点检查(自动派发)'
now = timezone.now()
cw.time_start = now
if cts.expire:
cw.time_end = now + timedelta(hours=cts.expire)
cw.user_duty = cts.user_duty
cw.riskpoint = cts.riskpoint
cw.note = cts.note
cw.save()
# 发送通知
pass
@shared_task(base=CustomTask)
def expire_checkwork():
now = timezone.now()
CheckWork.objects.filter(expire__gte=now).update(enabled=False)

3
apps/dpm/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
apps/dpm/urls.py Normal file
View File

@ -0,0 +1,13 @@
from apps.dpm.views import RiskPointViewSet, RiskViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
API_BASE_URL = 'api/dpm/'
HTML_BASE_URL = 'dpm/'
# cache_areas_info()
router = DefaultRouter()
router.register('riskpoint', RiskPointViewSet, basename='riskpoint')
router.register('risk', RiskViewSet, basename='risk')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),
]

71
apps/dpm/views.py Normal file
View File

@ -0,0 +1,71 @@
from apps.dpm.serializers import RiskPointCreateUpdateSerializer, RiskPointSerializer, RiskCreateUpdateSerializer, RiskSerializer, CheckTaskSetCreateUpdateSerializer, CheckTaskSetSerializer, CheckWork_10_CreateUpdateSerializer, CheckWorkSerializer
from apps.utils.viewsets import CustomModelViewSet
from apps.dpm.models import CheckTaskSet, CheckWork, RiskPoint, Risk
from rest_framework.decorators import action
from rest_framework.response import Response
from django.db import transaction
# Create your views here.
class RiskPointViewSet(CustomModelViewSet):
queryset = RiskPoint.objects.all()
create_serializer_class = RiskPointCreateUpdateSerializer
update_serializer_class = RiskPointCreateUpdateSerializer
serializer_class = RiskPointSerializer
select_related_fields = ['area', 'dept_res', 'user_res']
search_fields = ['name']
filterset_fields = ['area', 'type', 'level', 'manage_level']
class RiskViewSet(CustomModelViewSet):
queryset = Risk.objects.all()
create_serializer_class = RiskCreateUpdateSerializer
update_serializer_class = RiskCreateUpdateSerializer
serializer_class = RiskSerializer
select_related_fields = ['riskpoint']
prefetch_related_fields = ['results']
search_fields = ['name']
filterset_fields = ['results']
class CheckTaskSetViewSet(CustomModelViewSet):
queryset = CheckTaskSet.objects.all()
create_serializer_class = CheckTaskSetCreateUpdateSerializer
update_serializer_class = CheckTaskSetCreateUpdateSerializer
serializer_class = CheckTaskSetSerializer
select_related_fields = ['periodictask']
search_fields = ['note']
filterset_fields = ['periodictask__enabled']
@action(methods=['put'], detail=True, perms_map={'put': 'checktaskset.update'})
def toggle(self, request, pk=None):
"""修改启用禁用状态
修改启用禁用状态
"""
obj = self.get_object()
periodictask = obj.periodictask
periodictask.enabled = False if periodictask.enabled else True
periodictask.save()
return Response()
def perform_destroy(self, instance):
with transaction.atomic():
periodictask = instance.periodictask
instance.delete()
periodictask.enabled = False
periodictask.save()
class CheckWorkViewSet(CustomModelViewSet):
queryset = CheckWork.objects.all()
create_serializer_class = CheckWork_10_CreateUpdateSerializer
update_serializer_class = CheckWork_10_CreateUpdateSerializer
serializer_class = CheckWorkSerializer
search_fields = ['name', 'description']
select_related_fields = ['category', 'user_duty', 'riskpoint']
filterset_fields = ['type', 'category', 'riskpoint']
class HazardViewSet(CustomModelViewSet):
pass