feat: 增加wpm,qm,mtm三个app

This commit is contained in:
caoqianming 2023-06-20 17:05:50 +08:00
parent 0312de5cf5
commit 1d16949d8a
41 changed files with 930 additions and 437 deletions

View File

@ -1,4 +1,4 @@
# Generated by Django 3.2.12 on 2023-06-15 08:27 # Generated by Django 3.2.12 on 2023-06-20 09:05
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -11,27 +11,26 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('em', '0001_initial'), ('em', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0002_myschedule'), ('system', '0002_myschedule'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Mgroup', name='MpLog',
fields=[ fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')), ('tag_id', models.BigIntegerField(db_index=True, verbose_name='记录ID')),
('cate', models.CharField(default='section', max_length=50, verbose_name='分类')), ('tag_code', models.CharField(max_length=50, verbose_name='测点编号')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mgroup_belong_dept', to='system.dept', verbose_name='所属部门')), ('tag_update', models.DateTimeField(verbose_name='更新时间')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mgroup_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), ('tag_val', models.FloatField(verbose_name='当前值')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mgroup_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
], ],
options={ options={
'verbose_name': '测点集', 'abstract': False,
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
@ -49,135 +48,11 @@ class Migration(migrations.Migration):
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('ep_belong', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_belong', to='em.equipment', verbose_name='属于哪个设备')), ('ep_belong', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_belong', to='em.equipment', verbose_name='属于哪个设备')),
('ep_monitored', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_monitored', to='em.equipment', verbose_name='监测哪个设备')), ('ep_monitored', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mp_ep_monitored', to='em.equipment', verbose_name='监测哪个设备')),
('mgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='enm.mgroup', verbose_name='所在集合')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
], ],
options={ options={
'abstract': False, 'abstract': False,
}, },
), ),
migrations.CreateModel(
name='SfLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('start_time', models.DateTimeField(verbose_name='值班开始')),
('end_time', models.DateTimeField(verbose_name='值班结束')),
('note', models.TextField(blank=True, null=True, verbose_name='生产情况记录')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sflog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='班长')),
('mgroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.mgroup', verbose_name='关联工段')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='StLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('start_time', models.DateTimeField(verbose_name='停机开始')),
('end_time', models.DateTimeField(blank=True, null=True, verbose_name='停机结束')),
('duration', models.FloatField(blank=True, null=True, verbose_name='停机时长(h)')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stlog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('mgroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.mgroup', verbose_name='关联工段')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stlog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Team',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_belong_dept', to='system.dept', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='班长')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='StSfLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('is_current_down', models.BooleanField(default=False, verbose_name='是否本班停机')),
('reason', models.TextField(blank=True, null=True, verbose_name='停机原因')),
('sflog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.sflog', verbose_name='关联值班记录')),
('stlog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.stlog', verbose_name='关联停机记录')),
],
options={
'ordering': ['sflog__start_time'],
},
),
migrations.CreateModel(
name='Shift',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('start_time_o', models.TimeField(verbose_name='开始时间')),
('end_time_o', models.TimeField(verbose_name='结束时间')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shift_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shift_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '班次',
},
),
migrations.AddField(
model_name='sflog',
name='shift',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.shift', verbose_name='当班班次'),
),
migrations.AddField(
model_name='sflog',
name='stlogs',
field=models.ManyToManyField(through='enm.StSfLog', to='enm.StLog', verbose_name='关联停机记录'),
),
migrations.AddField(
model_name='sflog',
name='team',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enm.team', verbose_name='班组'),
),
migrations.AddField(
model_name='sflog',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sflog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('code', models.CharField(blank=True, max_length=50, null=True, verbose_name='编号')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '产品',
},
),
migrations.CreateModel( migrations.CreateModel(
name='MpointStat', name='MpointStat',
fields=[ fields=[
@ -197,53 +72,4 @@ class Migration(migrations.Migration):
'abstract': False, 'abstract': False,
}, },
), ),
migrations.CreateModel(
name='MpLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('tag_id', models.BigIntegerField(db_index=True, verbose_name='记录ID')),
('tag_code', models.CharField(max_length=50, verbose_name='测点编号')),
('tag_update', models.DateTimeField(verbose_name='更新时间')),
('tag_val', models.FloatField(verbose_name='当前值')),
('mpoint', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='enm.mpoint', verbose_name='关联测点')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Goal',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('year', models.PositiveSmallIntegerField(verbose_name='')),
('goal_name', models.CharField(help_text='总产量/台时产量/单位产品分布电耗/', max_length=50, verbose_name='目标名称')),
('goal_val', models.FloatField(verbose_name='全年目标值')),
('goal_val_1', models.FloatField(verbose_name='1月份目标值')),
('goal_val_2', models.FloatField(verbose_name='2月份目标值')),
('goal_val_3', models.FloatField(verbose_name='3月份目标值')),
('goal_val_4', models.FloatField(verbose_name='4月份目标值')),
('goal_val_5', models.FloatField(verbose_name='5月份目标值')),
('goal_val_6', models.FloatField(verbose_name='6月份目标值')),
('goal_val_7', models.FloatField(verbose_name='7月份目标值')),
('goal_val_8', models.FloatField(verbose_name='8月份目标值')),
('goal_val_9', models.FloatField(verbose_name='9月份目标值')),
('goal_val_10', models.FloatField(verbose_name='10月份目标值')),
('goal_val_11', models.FloatField(verbose_name='11月份目标值')),
('goal_val_12', models.FloatField(verbose_name='12月份目标值')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='goal_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('mgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='enm.mgroup', verbose_name='关联工段')),
('product', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='enm.product', verbose_name='关联产品')),
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='enm.team', verbose_name='关联班组')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='goal_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
] ]

View File

@ -0,0 +1,34 @@
# Generated by Django 3.2.12 on 2023-06-20 09:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('enm', '0001_initial'),
('mtm', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='mpoint',
name='mgroup',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.mgroup', verbose_name='所在集合'),
),
migrations.AddField(
model_name='mpoint',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mpoint_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
migrations.AddField(
model_name='mplog',
name='mpoint',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='enm.mpoint', verbose_name='关联测点'),
),
]

View File

@ -1,44 +1,8 @@
from django.db import models from django.db import models
from apps.utils.models import CommonBDModel, BaseModel, CommonADModel, CommonAModel, CommonBModel from apps.utils.models import BaseModel, CommonBModel
class Product(CommonAModel):
"""
产品
"""
name = models.CharField('名称', max_length=50)
code = models.CharField('编号', max_length=50, null=True, blank=True)
class Meta:
verbose_name = '产品'
class Shift(CommonAModel):
"""班次
"""
name = models.CharField('名称', max_length=50)
start_time_o = models.TimeField('开始时间')
end_time_o = models.TimeField('结束时间')
class Meta:
verbose_name = '班次'
class Mgroup(CommonBModel):
"""测点集
"""
name = models.CharField('名称', max_length=50)
cate = models.CharField('分类', max_length=50, default='section') # section是工段
class Meta:
verbose_name = '测点集'
class Team(CommonBModel): class Mpoint(CommonBModel):
"""班组
"""
name = models.CharField('名称', max_length=50)
leader = models.ForeignKey('system.user', verbose_name='班长', on_delete=models.CASCADE)
class Mpoint(CommonBDModel):
"""测点 """测点
""" """
name = models.CharField('测点名称', max_length=50) name = models.CharField('测点名称', max_length=50)
@ -47,7 +11,7 @@ class Mpoint(CommonBDModel):
cate = models.CharField('分类', max_length=50, default='elec') cate = models.CharField('分类', max_length=50, default='elec')
ep_monitored = models.ForeignKey('em.equipment', verbose_name='监测哪个设备', related_name='mp_ep_monitored', on_delete=models.SET_NULL, null=True, blank=True) ep_monitored = models.ForeignKey('em.equipment', verbose_name='监测哪个设备', related_name='mp_ep_monitored', on_delete=models.SET_NULL, null=True, blank=True)
ep_belong = models.ForeignKey('em.equipment', verbose_name='属于哪个设备', related_name='mp_ep_belong', on_delete=models.SET_NULL, null=True, blank=True) ep_belong = models.ForeignKey('em.equipment', verbose_name='属于哪个设备', related_name='mp_ep_belong', on_delete=models.SET_NULL, null=True, blank=True)
mgroup = models.ForeignKey('enm.mgroup', verbose_name='所在集合', on_delete=models.SET_NULL, null=True, blank=True) mgroup = models.ForeignKey('mtm.mgroup', verbose_name='所在集合', on_delete=models.SET_NULL, null=True, blank=True)
class MpLog(BaseModel): class MpLog(BaseModel):
@ -58,42 +22,7 @@ class MpLog(BaseModel):
tag_code = models.CharField('测点编号', max_length=50) tag_code = models.CharField('测点编号', max_length=50)
tag_update = models.DateTimeField('更新时间') tag_update = models.DateTimeField('更新时间')
tag_val = models.FloatField('当前值') tag_val = models.FloatField('当前值')
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):
"""值班记录
"""
mgroup = models.ForeignKey(Mgroup, verbose_name='关联工段', on_delete=models.CASCADE)
team = models.ForeignKey(Team, verbose_name='班组', on_delete=models.CASCADE)
shift = models.ForeignKey(Shift, verbose_name='当班班次', on_delete=models.CASCADE)
leader = models.ForeignKey('system.user', verbose_name='班长', on_delete=models.CASCADE)
start_time = models.DateTimeField('值班开始')
end_time = models.DateTimeField('值班结束')
note = models.TextField('生产情况记录', null=True, blank=True)
stlogs = models.ManyToManyField('enm.stlog', verbose_name='关联停机记录', through='enm.stsflog')
class StSfLog(BaseModel):
"""
停机-值班记录关联表
"""
stlog = models.ForeignKey(StLog, verbose_name='关联停机记录', on_delete=models.CASCADE)
sflog = models.ForeignKey(SfLog, verbose_name='关联值班记录', on_delete=models.CASCADE)
is_current_down = models.BooleanField('是否本班停机', default=False)
reason = models.TextField('停机原因', null=True, blank=True)
class Meta:
ordering = ['sflog__start_time']
class MpointStat(BaseModel): class MpointStat(BaseModel):
"""测点统计表 """测点统计表
@ -104,27 +33,4 @@ class MpointStat(BaseModel):
day = models.PositiveSmallIntegerField('', null=True, blank=True) day = models.PositiveSmallIntegerField('', null=True, blank=True)
hour = models.PositiveSmallIntegerField('', null=True, blank=True) hour = models.PositiveSmallIntegerField('', null=True, blank=True)
mpoint = models.ForeignKey(Mpoint, verbose_name='关联测点', on_delete=models.CASCADE) mpoint = models.ForeignKey(Mpoint, verbose_name='关联测点', on_delete=models.CASCADE)
val = models.FloatField('统计值', default=0) val = models.FloatField('统计值', default=0)
class Goal(CommonADModel):
"""目标
"""
mgroup = models.ForeignKey(Mgroup, verbose_name='关联工段', on_delete=models.CASCADE, null=True, blank=True)
product = models.ForeignKey(Product, verbose_name='关联产品', on_delete=models.CASCADE, null=True, blank=True)
team = models.ForeignKey(Team, verbose_name='关联班组', on_delete=models.CASCADE, null=True, blank=True)
year = models.PositiveSmallIntegerField('')
goal_name = models.CharField('目标名称', max_length=50, help_text='总产量/台时产量/单位产品分布电耗/')
goal_val = models.FloatField('全年目标值')
goal_val_1 = models.FloatField('1月份目标值')
goal_val_2 = models.FloatField('2月份目标值')
goal_val_3 = models.FloatField('3月份目标值')
goal_val_4 = models.FloatField('4月份目标值')
goal_val_5 = models.FloatField('5月份目标值')
goal_val_6 = models.FloatField('6月份目标值')
goal_val_7 = models.FloatField('7月份目标值')
goal_val_8 = models.FloatField('8月份目标值')
goal_val_9 = models.FloatField('9月份目标值')
goal_val_10 = models.FloatField('10月份目标值')
goal_val_11 = models.FloatField('11月份目标值')
goal_val_12 = models.FloatField('12月份目标值')

View File

@ -1,25 +1,8 @@
from apps.utils.serializers import CustomModelSerializer from apps.utils.serializers import CustomModelSerializer
from apps.enm.models import Mpoint, Mgroup, MpLog, Team, SfLog, StLog, StSfLog from apps.enm.models import Mpoint, MpLog
from apps.system.models import Dept from apps.utils.constants import EXCLUDE_FIELDS
from apps.system.serializers import DeptSimpleSerializer
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
from rest_framework import serializers from rest_framework import serializers
class MgroupSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
class Meta:
model = Mgroup
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class TeamSerializer(CustomModelSerializer):
leader_name = serializers.CharField(source='leader.name', read_only=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
class Meta:
model = Team
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class MpointSerializer(CustomModelSerializer): class MpointSerializer(CustomModelSerializer):
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True) mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
@ -37,39 +20,4 @@ class MpLogSerializer(CustomModelSerializer):
class Meta: class Meta:
model = MpLog model = MpLog
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['mpoint_name'] read_only_fields = EXCLUDE_FIELDS + ['mpoint_name']
class StLogSerializer(CustomModelSerializer):
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
class Meta:
model = StLog
fields = '__all__'
class SfLogSerializer(CustomModelSerializer):
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
team_name = serializers.CharField(source='team.name', read_only=True)
shift_name = serializers.CharField(source='shift.name', read_only=True)
leader_name = serializers.CharField(source='leader.name', read_only=True)
class Meta:
model = SfLog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
update_fields = ['start_time', 'end_time', 'note']
def to_internal_value(self, data):
if hasattr(self.Meta, 'update_fields') and self.context['request'].method in ['PUT', 'PATCH']:
u_fields = self.Meta.update_fields
new_data = {key: data[key] for key in u_fields if key in data}
return super().to_internal_value(new_data)
return super().to_internal_value(data)
class StSfLogSerializer(CustomModelSerializer):
stlog_ = StLogSerializer(source='stlog', read_only=True)
sflog_ = SfLogSerializer(source='sflog', read_only=True)
class Meta:
model = StSfLog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['stlog', 'sflog', 'is_current_down']

View File

@ -103,4 +103,6 @@ def cal_mpointstats(is_now=1):
cal_mpointstat_hour.delay(mpoint.id, now.year, now.moth, now.day, now.hour) cal_mpointstat_hour.delay(mpoint.id, now.year, now.moth, now.day, now.hour)
else: else:
for mpoint in Mpoint.objects.all(): for mpoint in Mpoint.objects.all():
cal_mpointstat_hour.delay(mpoint.id, pre.year, pre.month, pre.day, pre.hour) cal_mpointstat_hour.delay(mpoint.id, pre.year, pre.month, pre.day, pre.hour)

View File

@ -1,18 +1,13 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.enm.views import MpointViewSet, MgroupViewSet, TeamViewSet, MpLogViewSet, SfLogViewSet, StLogViewSet, StSfLogViewSet from apps.enm.views import (MpointViewSet, MpLogViewSet)
API_BASE_URL = 'api/enm/' API_BASE_URL = 'api/enm/'
HTML_BASE_URL = 'enm/' HTML_BASE_URL = 'enm/'
router = DefaultRouter() router = DefaultRouter()
router.register('mpoint', MpointViewSet, basename='mpoint') router.register('mpoint', MpointViewSet, basename='mpoint')
router.register('mgroup', MgroupViewSet, basename='mgroup')
router.register('team', TeamViewSet, basename='team')
router.register('mplog', MpLogViewSet, basename='mplog') router.register('mplog', MpLogViewSet, basename='mplog')
router.register('sflog', SfLogViewSet, basename='sflog')
router.register('stlog', StLogViewSet, basename='stlog')
router.register('stsflog', StSfLogViewSet, basename='stsflog')
urlpatterns = [ urlpatterns = [
path(API_BASE_URL, include(router.urls)), path(API_BASE_URL, include(router.urls)),
] ]

View File

@ -1,36 +1,8 @@
from django.shortcuts import render from django.shortcuts import render
from apps.enm.models import Mpoint, Mgroup, Team, MpLog, SfLog, StLog, StSfLog from apps.enm.models import Mpoint, MpLog
from apps.utils.viewsets import CustomModelViewSet, GenericViewSet from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
from rest_framework.mixins import ListModelMixin, UpdateModelMixin from rest_framework.mixins import ListModelMixin
from apps.enm.serializers import MpointSerializer, TeamSerializer, MgroupSerializer, MpLogSerializer, SfLogSerializer, StLogSerializer, StSfLogSerializer from apps.enm.serializers import (MpointSerializer, MpLogSerializer)
from rest_framework.exceptions import ParseError
from django.db import transaction
from django.db.models import Q
class MgroupViewSet(CustomModelViewSet):
"""
list:测点集
测点集
"""
queryset = Mgroup.objects.all()
serializer_class = MgroupSerializer
select_related_fields = ['create_by', 'belong_dept']
filterset_fields = ['belong_dept']
search_fields = ['number']
class TeamViewSet(CustomModelViewSet):
"""
list:班组
班组
"""
queryset = Team.objects.all()
serializer_class = TeamSerializer
select_related_fields = ['belong_dept', 'leader']
filterset_fields = ['belong_dept']
search_fields = ['name']
class MpointViewSet(CustomModelViewSet): class MpointViewSet(CustomModelViewSet):
@ -46,7 +18,7 @@ class MpointViewSet(CustomModelViewSet):
search_fields = ['number', 'code'] search_fields = ['number', 'code']
class MpLogViewSet(ListModelMixin, GenericViewSet): class MpLogViewSet(ListModelMixin, CustomGenericViewSet):
""" """
list:测点原始记录 list:测点原始记录
@ -55,64 +27,4 @@ class MpLogViewSet(ListModelMixin, GenericViewSet):
queryset = MpLog.objects.all() queryset = MpLog.objects.all()
serializer_class = MpLogSerializer serializer_class = MpLogSerializer
select_related_fields = ['mpoint'] select_related_fields = ['mpoint']
filterset_fields = ['mpoint'] filterset_fields = ['mpoint']
class StLogViewSet(ListModelMixin, GenericViewSet):
"""
list:停机记录
停机记录
"""
queryset = StLog.objects.all()
serializer_class = StLogSerializer
select_related_fields = ['mgroup']
filterset_fields = ['mgroup']
ordering = ['-start_time']
class SfLogViewSet(CustomModelViewSet):
"""
list:值班记录
值班记录
"""
queryset = SfLog.objects.all()
serializer_class = SfLogSerializer
select_related_fields = ['mgroup', 'shift', 'team', 'leader']
filterset_fields = ['mgroup', 'shift', 'team', 'leader', 'team__belong_dept']
search_fields = ['note']
@transaction.atomic
def perform_create(self, serializer):
ins = serializer.save()
# 查看并比对停机记录
stls = StLog.objects.filter(mroup=ins.mgroup)
stls_ = (stls.filter(start_time__gte=ins.start_time, start_time__lt=ins.end_time)|
stls.exclude(end_time=None).filter(start_time__lte=ins.start_time, end_time__gt=ins.end_time)|
stls.exclude(end_time=None).filter(end_time__gte=ins.start_time, end_time__lt=ins.end_time))
for i in stls_:
StSfLog.objects.get_or_create(stlog=i, sflog=ins, defaults={'stlog': i, 'sflog': ins})
stsflog = StSfLog.objects.filter(stlog=i).order_by('-sflog__start_time').first()
if stsflog:
stsflog.is_current_down = True
stsflog.save()
class StSfLogViewSet(ListModelMixin, UpdateModelMixin, GenericViewSet):
"""
list:值班停机关系
值班停机关系
"""
queryset = StSfLog.objects.all()
serializer_class = StSfLogSerializer
select_related_fields = ['stlog', 'sflog']
filterset_fields = ['stlog', 'sflog']
search_fields = ['reason']
def filter_queryset(self, queryset):
params = self.request.query_params
if 'stlog' not in params or 'sflog' not in params:
raise ParseError('请指定所属停机或值班记录')
return super().filter_queryset(queryset)

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

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

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

6
apps/mtm/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class MtmConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.mtm'

View File

@ -0,0 +1,121 @@
# Generated by Django 3.2.12 on 2023-06-20 09:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0002_myschedule'),
]
operations = [
migrations.CreateModel(
name='Material',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('code', models.CharField(blank=True, max_length=50, null=True, verbose_name='标识')),
('type', models.PositiveSmallIntegerField(choices=[(10, '成品'), (20, '半成品'), (30, '主要原料'), (40, '辅助材料'), (50, '加工工具'), (60, '辅助工装')], default=1, help_text="((10, '成品'), (20, '半成品'), (30, '主要原料'), (40, '辅助材料'), (50, '加工工具'), (60, '辅助工装'))", verbose_name='物料类型')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='material_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('qitems', models.ManyToManyField(blank=True, to='system.Dictionary', verbose_name='质检项目')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='material_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '物料表',
},
),
migrations.CreateModel(
name='Team',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_belong_dept', to='system.dept', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='班长')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Shift',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('start_time_o', models.TimeField(verbose_name='开始时间')),
('end_time_o', models.TimeField(verbose_name='结束时间')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shift_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shift_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '班次',
},
),
migrations.CreateModel(
name='Mgroup',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=50, verbose_name='名称')),
('cate', models.CharField(default='section', help_text='section/other', max_length=50, verbose_name='分类')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mgroup_belong_dept', to='system.dept', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mgroup_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('product', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.material', verbose_name='主要产品')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mgroup_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'verbose_name': '测点集',
},
),
migrations.CreateModel(
name='Goal',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('year', models.PositiveSmallIntegerField(verbose_name='')),
('goal_val', models.FloatField(verbose_name='全年目标值')),
('goal_val_1', models.FloatField(verbose_name='1月份目标值')),
('goal_val_2', models.FloatField(verbose_name='2月份目标值')),
('goal_val_3', models.FloatField(verbose_name='3月份目标值')),
('goal_val_4', models.FloatField(verbose_name='4月份目标值')),
('goal_val_5', models.FloatField(verbose_name='5月份目标值')),
('goal_val_6', models.FloatField(verbose_name='6月份目标值')),
('goal_val_7', models.FloatField(verbose_name='7月份目标值')),
('goal_val_8', models.FloatField(verbose_name='8月份目标值')),
('goal_val_9', models.FloatField(verbose_name='9月份目标值')),
('goal_val_10', models.FloatField(verbose_name='10月份目标值')),
('goal_val_11', models.FloatField(verbose_name='11月份目标值')),
('goal_val_12', models.FloatField(verbose_name='12月份目标值')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='goal_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('goal_cate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.dictionary', verbose_name='目标种类')),
('mgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.mgroup', verbose_name='关联工段')),
('product', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联产品')),
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.team', verbose_name='关联班组')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='goal_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

78
apps/mtm/models.py Normal file
View File

@ -0,0 +1,78 @@
from django.db import models
from apps.system.models import CommonAModel, Dictionary, CommonBModel, CommonADModel
# Create your models here.
class Material(CommonAModel):
MA_TYPE_GOOD = 10
MA_TYPE_HALFGOOD = 20
MA_TYPE_MAINSO = 30
MA_TYPE_HELPSO = 40
MA_TYPE_TOOL = 50
MA_TYPE_HELPTOOL = 60
type_choices=(
(MA_TYPE_GOOD, '成品'),
(MA_TYPE_HALFGOOD, '半成品'),
(MA_TYPE_MAINSO, '主要原料'),
(MA_TYPE_HELPSO, '辅助材料'),
(MA_TYPE_TOOL, '加工工具'),
(MA_TYPE_HELPTOOL, '辅助工装')
)
name = models.CharField('名称', max_length=50)
code = models.CharField('标识', max_length=50, null=True, blank=True)
type = models.PositiveSmallIntegerField('物料类型', choices= type_choices, default=1, help_text=str(type_choices))
qitems = models.ManyToManyField(Dictionary, verbose_name='质检项目', blank=True)
class Meta:
verbose_name = '物料表'
class Shift(CommonAModel):
"""班次
"""
name = models.CharField('名称', max_length=50)
start_time_o = models.TimeField('开始时间')
end_time_o = models.TimeField('结束时间')
class Meta:
verbose_name = '班次'
class Team(CommonBModel):
"""班组
"""
name = models.CharField('名称', max_length=50)
leader = models.ForeignKey('system.user', verbose_name='班长', on_delete=models.CASCADE)
class Mgroup(CommonBModel):
"""测点集
"""
name = models.CharField('名称', max_length=50)
cate = models.CharField('分类', max_length=50, default='section', help_text='section/other') # section是工段
product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
verbose_name = '测点集'
class Goal(CommonADModel):
"""目标
"""
mgroup = models.ForeignKey(Mgroup, verbose_name='关联工段', on_delete=models.CASCADE, null=True, blank=True)
product = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE, null=True, blank=True)
team = models.ForeignKey(Team, verbose_name='关联班组', on_delete=models.CASCADE, null=True, blank=True)
year = models.PositiveSmallIntegerField('')
goal_cate = models.ForeignKey(Dictionary, verbose_name='目标种类', on_delete=models.CASCADE)
goal_val = models.FloatField('全年目标值')
goal_val_1 = models.FloatField('1月份目标值')
goal_val_2 = models.FloatField('2月份目标值')
goal_val_3 = models.FloatField('3月份目标值')
goal_val_4 = models.FloatField('4月份目标值')
goal_val_5 = models.FloatField('5月份目标值')
goal_val_6 = models.FloatField('6月份目标值')
goal_val_7 = models.FloatField('7月份目标值')
goal_val_8 = models.FloatField('8月份目标值')
goal_val_9 = models.FloatField('9月份目标值')
goal_val_10 = models.FloatField('10月份目标值')
goal_val_11 = models.FloatField('11月份目标值')
goal_val_12 = models.FloatField('12月份目标值')

34
apps/mtm/serializers.py Normal file
View File

@ -0,0 +1,34 @@
from apps.utils.serializers import CustomModelSerializer
from apps.mtm.models import Shift, Material, Mgroup, Team
from apps.utils.constants import EXCLUDE_FIELDS
from rest_framework import serializers
from apps.system.models import Dept
class ShiftSerializer(CustomModelSerializer):
class Meta:
model = Shift
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class MaterialSerializer(CustomModelSerializer):
class Meta:
model = Material
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class MgroupSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
class Meta:
model = Mgroup
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class TeamSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True)
leader_name = serializers.CharField(source='leader.name', read_only=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
class Meta:
model = Team
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS

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

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

17
apps/mtm/urls.py Normal file
View File

@ -0,0 +1,17 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.mtm.views import MgroupViewSet, ShiftViewSet, TeamViewSet, MaterialViewSet
API_BASE_URL = 'api/mtm/'
HTML_BASE_URL = 'mtm/'
router = DefaultRouter()
router.register('mgroup', MgroupViewSet, basename='mgroup')
router.register('team', TeamViewSet, basename='team')
router.register('material', MaterialViewSet, basename='material')
router.register('shift', ShiftViewSet, basename='shift')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),
]

57
apps/mtm/views.py Normal file
View File

@ -0,0 +1,57 @@
from django.shortcuts import render
from rest_framework.mixins import ListModelMixin
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.mtm.models import Material, Mgroup, Shift, Team
from apps.mtm.serializers import MaterialSerializer, MgroupSerializer, ShiftSerializer, TeamSerializer
# Create your views here.
class MaterialViewSet(ListModelMixin, CustomGenericViewSet):
"""
list:产品
产品
"""
queryset = Material.objects.all()
serializer_class = MaterialSerializer
filterset_fields = ['code']
search_fields = ['name', 'code']
ordering = ['id']
class ShiftViewSet(ListModelMixin, CustomGenericViewSet):
"""
list:班次
班次
"""
queryset = Shift.objects.all()
serializer_class = ShiftSerializer
search_fields = ['name']
ordering = ['id']
class MgroupViewSet(CustomModelViewSet):
"""
list:测点集
测点集
"""
queryset = Mgroup.objects.all()
serializer_class = MgroupSerializer
select_related_fields = ['create_by', 'belong_dept']
filterset_fields = ['belong_dept']
search_fields = ['number']
class TeamViewSet(CustomModelViewSet):
"""
list:班组
班组
"""
queryset = Team.objects.all()
serializer_class = TeamSerializer
select_related_fields = ['belong_dept', 'leader']
filterset_fields = ['belong_dept']
search_fields = ['name']

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

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

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

6
apps/qm/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class QmConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.qm'

View File

@ -0,0 +1,60 @@
# Generated by Django 3.2.12 on 2023-06-20 09:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('mtm', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0002_myschedule'),
]
operations = [
migrations.CreateModel(
name='TestItem',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=100, verbose_name='名称')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='testitem_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='testitem_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='QuaStat',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('type', models.CharField(default='day', help_text='year/month/day', max_length=50, verbose_name='统计维度')),
('year', models.PositiveSmallIntegerField(verbose_name='')),
('month', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='')),
('day', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='')),
('val_avg', models.FloatField(verbose_name='平均值')),
('num_test', models.PositiveSmallIntegerField(verbose_name='检测次数')),
('num_ok', models.PositiveSmallIntegerField(verbose_name='合格次数')),
('rate_pass', models.FloatField(verbose_name='合格率')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quastat_belong_dept', to='system.dept', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quastat_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联产物')),
('testitem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.testitem', verbose_name='质检项目')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quastat_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

26
apps/qm/models.py Normal file
View File

@ -0,0 +1,26 @@
from django.db import models
from apps.system.models import CommonAModel
from apps.utils.models import CommonBDModel
from apps.mtm.models import Material
class TestItem(CommonAModel):
"""
检验项目
"""
name = models.CharField('名称', max_length=100)
# Create your models here.
class QuaStat(CommonBDModel):
"""
质量数据统计表 需要有belong_dept
"""
type = models.CharField('统计维度', max_length=50, default='day', help_text='year/month/day')
year = models.PositiveSmallIntegerField('')
month = models.PositiveSmallIntegerField('', null=True, blank=True)
day = models.PositiveSmallIntegerField('', null=True, blank=True)
product = models.ForeignKey(Material, verbose_name='关联产物', on_delete=models.CASCADE)
testitem = models.ForeignKey(TestItem, verbose_name='质检项目', on_delete=models.CASCADE)
val_avg = models.FloatField('平均值')
num_test = models.PositiveSmallIntegerField('检测次数')
num_ok = models.PositiveSmallIntegerField('合格次数')
rate_pass = models.FloatField('合格率')

24
apps/qm/serializers.py Normal file
View File

@ -0,0 +1,24 @@
from apps.qm.models import QuaStat, TestItem
from apps.utils.constants import EXCLUDE_FIELDS
from apps.utils.serializers import CustomModelSerializer
from rest_framework import serializers
from apps.system.models import Dept, Dictionary
class TestItemSerializer(CustomModelSerializer):
class Meta:
model = TestItem
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class QuaStatSerializer(CustomModelSerializer):
year = serializers.IntegerField(label='', required=True)
month = serializers.IntegerField(label='', required=True)
day = serializers.IntegerField(label='', required=True)
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
product_name = serializers.CharField(source='product.name', read_only=True)
qitem_name = serializers.CharField(source='qitem.name', read_only=True)
class Meta:
model = QuaStat
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['type', 'rate_pass']

73
apps/qm/tasks.py Normal file
View File

@ -0,0 +1,73 @@
from __future__ import absolute_import, unicode_literals
from celery import shared_task
from apps.qm.models import QuaStat
from apps.utils.tasks import CustomTask
from django.db.models import Sum, F, ExpressionWrapper, FloatField
@shared_task(base=CustomTask)
def cal_quastat(quastatId: str):
# 可以直接使用sql语言
# query = """
# SELECT field1, SUM(field2 * field3) AS field2_field3_sum, SUM(field1) AS field1_sum,
# SUM(field2 * field3) / SUM(field1) AS result
# FROM your_table_name
# GROUP BY field1
# """
ins = QuaStat.objects.get(id=quastatId)
# 月统计
params = {
'testitem': ins.testitem,
'product': ins.product,
'belong_dept': ins.belong_dept,
'year': ins.year,
'month': ins.month
}
results_month = QuaStat.objects.filter(**params).values('belong_dept', 'product',
'testitem', 'year', 'month').annotate(
avg_val_total=Sum(F('avg_val')*F('num_test')),
num_test_1=Sum('num_test'),
num_ok_1=Sum('num_ok')).annotate(
avg_val_1=ExpressionWrapper(F('avg_val_total')/F('num_test_1'), output_field=FloatField()),
rate_pass_1=ExpressionWrapper(F('num_ok_1')/F('num_test_1'), output_field=FloatField())
)
for r1 in results_month:
stat_params = {
'avg_val': r1['avg_val_1'],
'num_test': r1['num_test_1'],
'num_ok': r1['num_ok_1'],
'rate_pass': r1['rate_pass_1']
}
qua, is_created = QuaStat.objects.get_or_create(
**params, **{'type': 'month'}, defaults={**params, **{'type': 'month'}, **stat_params}
)
if not is_created:
for k in stat_params:
setattr(qua, k, stat_params[k])
qua.save()
# 年统计
params.pop('month')
results_year = QuaStat.objects.filter(**params).values('belong_dept', 'product',
'testitem', 'year').annotate(
avg_val_total=Sum(F('avg_val')*F('num_test')),
num_test_1=Sum('num_test'),
num_ok_1=Sum('num_ok')).annotate(
avg_val_1=ExpressionWrapper(F('avg_val_total')/F('num_test_1'), output_field=FloatField()),
rate_pass_1=ExpressionWrapper(F('num_ok_1')/F('num_test_1'), output_field=FloatField())
)
for r1 in results_year:
stat_params = {
'avg_val': r1['avg_val_1'],
'num_test': r1['num_test_1'],
'num_ok': r1['num_ok_1'],
'rate_pass': r1['rate_pass_1']
}
qua, is_created = QuaStat.objects.get_or_create(
**params, **{'type': 'year'}, defaults={**params, **{'type': 'year'}, **stat_params}
)
if not is_created:
for k in stat_params:
setattr(qua, k, stat_params[k])
qua.save()

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

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

14
apps/qm/urls.py Normal file
View File

@ -0,0 +1,14 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.qm.views import QuaStatViewSet, TestItemViewSet
API_BASE_URL = 'api/qm/'
HTML_BASE_URL = 'qm/'
router = DefaultRouter()
router.register('quastat', QuaStatViewSet, basename='quastat')
router.register('testitem', TestItemViewSet, basename='testitem')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),
]

37
apps/qm/views.py Normal file
View File

@ -0,0 +1,37 @@
from django.shortcuts import render
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from apps.qm.models import QuaStat, TestItem
from apps.qm.serializers import QuaStatSerializer, TestItemSerializer
from apps.qm.tasks import cal_quastat
from apps.utils.viewsets import CustomGenericViewSet
# Create your views here.
class TestItemViewSet(ListModelMixin, CustomGenericViewSet):
"""
list:质检项目
质检项目
"""
queryset = TestItem.objects.all()
serializer_class = TestItemSerializer
filterset_fields = []
ordering = ['id']
class QuaStatViewSet(ListModelMixin, CreateModelMixin, CustomGenericViewSet):
"""
list:质量分析报告
质量分析报告
"""
queryset = QuaStat.objects.all()
serializer_class = QuaStatSerializer
filterset_fields = ['type', 'year', 'month', 'day', 'product', 'testitem', 'belong_dept']
select_related_fields = ['belong_dept', 'product', 'testitem']
def perform_create(self, serializer):
ins = serializer.save()
ins.rate_pass = ins.num_ok/ ins.num_test
ins.save()
# 计算月和年的统计
cal_quastat.delay(ins.id)

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

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

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

6
apps/wpm/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class WpmConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.wpm'

View File

@ -0,0 +1,88 @@
# Generated by Django 3.2.12 on 2023-06-20 09:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('mtm', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='SfLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('start_time', models.DateTimeField(verbose_name='值班开始')),
('end_time', models.DateTimeField(verbose_name='值班结束')),
('note', models.TextField(blank=True, null=True, verbose_name='其他备注')),
('qua_data', models.JSONField(blank=True, null=True, verbose_name='质检数据')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sflog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='班长')),
('mgroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.mgroup', verbose_name='关联工段')),
('shift', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.shift', verbose_name='当班班次')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='StLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('start_time', models.DateTimeField(verbose_name='停机开始')),
('end_time', models.DateTimeField(blank=True, null=True, verbose_name='停机结束')),
('duration', models.FloatField(blank=True, null=True, verbose_name='停机时长(h)')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stlog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('mgroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.mgroup', verbose_name='关联工段')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stlog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='StSfLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('is_current_down', models.BooleanField(default=False, verbose_name='是否本班停机')),
('reason', models.TextField(blank=True, null=True, verbose_name='停机原因')),
('sflog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.sflog', verbose_name='关联值班记录')),
('stlog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.stlog', verbose_name='关联停机记录')),
],
options={
'ordering': ['sflog__start_time'],
},
),
migrations.AddField(
model_name='sflog',
name='stlogs',
field=models.ManyToManyField(through='wpm.StSfLog', to='wpm.StLog', verbose_name='关联停机记录'),
),
migrations.AddField(
model_name='sflog',
name='team',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.team', verbose_name='班组'),
),
migrations.AddField(
model_name='sflog',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sflog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
]

View File

40
apps/wpm/models.py Normal file
View File

@ -0,0 +1,40 @@
from django.db import models
from apps.system.models import CommonADModel, BaseModel
from apps.mtm.models import Mgroup, Team, Shift
# 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):
"""值班记录
"""
mgroup = models.ForeignKey(Mgroup, verbose_name='关联工段', on_delete=models.CASCADE)
team = models.ForeignKey(Team, verbose_name='班组', on_delete=models.CASCADE)
shift = models.ForeignKey(Shift, verbose_name='当班班次', on_delete=models.CASCADE)
leader = models.ForeignKey('system.user', verbose_name='班长', on_delete=models.CASCADE)
start_time = models.DateTimeField('值班开始')
end_time = models.DateTimeField('值班结束')
note = models.TextField('其他备注', null=True, blank=True)
stlogs = models.ManyToManyField('wpm.stlog', verbose_name='关联停机记录', through='wpm.stsflog')
qua_data = models.JSONField('质检数据', null=True, blank=True)
class StSfLog(BaseModel):
"""
停机-值班记录关联表
"""
stlog = models.ForeignKey(StLog, verbose_name='关联停机记录', on_delete=models.CASCADE)
sflog = models.ForeignKey(SfLog, verbose_name='关联值班记录', on_delete=models.CASCADE)
is_current_down = models.BooleanField('是否本班停机', default=False)
reason = models.TextField('停机原因', null=True, blank=True)
class Meta:
ordering = ['sflog__start_time']

52
apps/wpm/serializers.py Normal file
View File

@ -0,0 +1,52 @@
from apps.utils.constants import EXCLUDE_FIELDS
from apps.utils.serializers import CustomModelSerializer
from rest_framework import serializers
from apps.wpm.models import SfLog, StLog, StSfLog
from apps.system.models import Dictionary
class StLogSerializer(CustomModelSerializer):
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
class Meta:
model = StLog
fields = '__all__'
class SfLogSerializer(CustomModelSerializer):
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
team_name = serializers.CharField(source='team.name', read_only=True)
shift_name = serializers.CharField(source='shift.name', read_only=True)
leader_name = serializers.CharField(source='leader.name', read_only=True)
class Meta:
model = SfLog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['leader', 'qua_data']
update_fields = ['start_time', 'end_time', 'note']
def validate(self, attrs):
attrs['leader'] = attrs['mgroup'].leader
return super().validate(attrs)
def to_internal_value(self, data):
if hasattr(self.Meta, 'update_fields') and self.context['request'].method in ['PUT', 'PATCH']:
u_fields = self.Meta.update_fields
new_data = {key: data[key] for key in u_fields if key in data}
return super().to_internal_value(new_data)
return super().to_internal_value(data)
class StSfLogSerializer(CustomModelSerializer):
stlog_ = StLogSerializer(source='stlog', read_only=True)
sflog_ = SfLogSerializer(source='sflog', read_only=True)
class Meta:
model = StSfLog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + ['stlog', 'sflog', 'is_current_down']
class QItemDataSerializer(serializers.Serializer):
qitem = serializers.PrimaryKeyRelatedField(label='检测项目', queryset=Dictionary.objects.filter(type__code='qitem'))
val = serializers.FloatField(label='')
class SfLogQuaSerializer(serializers.Serializer):
qua_data = QItemDataSerializer(many=True)

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

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

17
apps/wpm/urls.py Normal file
View File

@ -0,0 +1,17 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.wpm.views import SfLogViewSet, StLogViewSet, StSfLogViewSet
API_BASE_URL = 'api/wpm/'
HTML_BASE_URL = 'wpm/'
router = DefaultRouter()
router.register('sflog', SfLogViewSet, basename='sflog')
router.register('stlog', StLogViewSet, basename='stlog')
router.register('stsflog', StSfLogViewSet, basename='stsflog')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),
]

88
apps/wpm/views.py Normal file
View File

@ -0,0 +1,88 @@
from django.shortcuts import render
from rest_framework.mixins import ListModelMixin, UpdateModelMixin
from rest_framework.decorators import action
from django.db import transaction
from rest_framework.response import Response
from rest_framework.exceptions import ParseError
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.wpm.models import SfLog, StLog, StSfLog
from apps.wpm.serializers import SfLogQuaSerializer, SfLogSerializer, StLogSerializer, StSfLogSerializer
# Create your views here.
class StLogViewSet(ListModelMixin, CustomGenericViewSet):
"""
list:停机记录
停机记录
"""
queryset = StLog.objects.all()
serializer_class = StLogSerializer
select_related_fields = ['mgroup']
filterset_fields = ['mgroup']
ordering = ['-start_time']
class SfLogViewSet(CustomModelViewSet):
"""
list:值班记录
值班记录
"""
queryset = SfLog.objects.all()
serializer_class = SfLogSerializer
select_related_fields = ['mgroup', 'shift', 'team', 'leader']
filterset_fields = ['mgroup', 'shift', 'team', 'leader', 'team__belong_dept']
search_fields = ['note']
@transaction.atomic
def perform_create(self, serializer):
ins = serializer.save()
# 查看并比对停机记录
stls = StLog.objects.filter(mroup=ins.mgroup)
stls_ = (stls.filter(start_time__gte=ins.start_time, start_time__lt=ins.end_time)|
stls.exclude(end_time=None).filter(start_time__lte=ins.start_time, end_time__gt=ins.end_time)|
stls.exclude(end_time=None).filter(end_time__gte=ins.start_time, end_time__lt=ins.end_time))
for i in stls_:
StSfLog.objects.get_or_create(stlog=i, sflog=ins, defaults={'stlog': i, 'sflog': ins})
stsflog = StSfLog.objects.filter(stlog=i).order_by('-sflog__start_time').first()
if stsflog:
stsflog.is_current_down = True
stsflog.save()
@action(methods=['put'], detail=True, perms_map={'put': 'sflogqua.update'}, serializer_class=SfLogQuaSerializer)
def qua_data(self, request, pk=None):
"""
质量数据录入
质量数据录入
"""
sflog = self.get_object()
sr = SfLogQuaSerializer(data=request.data)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
qua_data = vdata['qua_data']
qua_data_new = []
for i in qua_data:
qua_data_new.append({'qitem': i['qitem'].id, 'qitem_name': i['qitem'].name, 'val': i['val']})
sflog.qua_data = qua_data_new
sflog.save()
return Response()
class StSfLogViewSet(ListModelMixin, UpdateModelMixin, CustomGenericViewSet):
"""
list:值班停机关系
值班停机关系
"""
queryset = StSfLog.objects.all()
serializer_class = StSfLogSerializer
select_related_fields = ['stlog', 'sflog']
filterset_fields = ['stlog', 'sflog']
search_fields = ['reason']
def filter_queryset(self, queryset):
params = self.request.query_params
if 'stlog' not in params or 'sflog' not in params:
raise ParseError('请指定所属停机或值班记录')
return super().filter_queryset(queryset)

2
ruff.toml Normal file
View File

@ -0,0 +1,2 @@
[tool.ruff]
line-length = 120

View File

@ -98,6 +98,9 @@ INSTALLED_APPS = [
'apps.opm', 'apps.opm',
'apps.bi', 'apps.bi',
'apps.em', 'apps.em',
'apps.mtm',
'apps.wpm',
'apps.qm',
'apps.enm' 'apps.enm'
] ]

View File

@ -56,6 +56,9 @@ urlpatterns = [
path('', include('apps.vm.urls')), path('', include('apps.vm.urls')),
path('', include('apps.bi.urls')), path('', include('apps.bi.urls')),
path('', include('apps.em.urls')), path('', include('apps.em.urls')),
path('', include('apps.mtm.urls')),
path('', include('apps.wpm.urls')),
path('', include('apps.qm.urls')),
path('', include('apps.enm.urls')), path('', include('apps.enm.urls')),