diff --git a/server/apps/ability/apps.py b/server/apps/ability/apps.py index f4be2ff..7bcdb3b 100644 --- a/server/apps/ability/apps.py +++ b/server/apps/ability/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class AbilityConfig(AppConfig): - name = 'ability' + name = 'apps.ability' diff --git a/server/apps/consulting/apps.py b/server/apps/consulting/apps.py index b3c0c5a..8306fe1 100644 --- a/server/apps/consulting/apps.py +++ b/server/apps/consulting/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class QualityConfig(AppConfig): - name = 'consulting' + name = 'apps.consulting' diff --git a/server/apps/crm/apps.py b/server/apps/crm/apps.py index d6f91d2..ede510e 100644 --- a/server/apps/crm/apps.py +++ b/server/apps/crm/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class CrmConfig(AppConfig): - name = 'crm' + name = 'apps.crm' diff --git a/server/apps/exam/apps.py b/server/apps/exam/apps.py index 9649087..c01c2f6 100644 --- a/server/apps/exam/apps.py +++ b/server/apps/exam/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class ExamConfig(AppConfig): - name = 'exam' + name = 'apps.exam' diff --git a/server/apps/ops/apps.py b/server/apps/ops/apps.py index 23b85df..84687ff 100644 --- a/server/apps/ops/apps.py +++ b/server/apps/ops/apps.py @@ -2,5 +2,5 @@ from django.apps import AppConfig class OpsConfig(AppConfig): - name = 'ops' + name = 'apps.ops' verbose_name = '系统监控' diff --git a/server/apps/quality/apps.py b/server/apps/quality/apps.py index 2455d38..c4ffb5c 100644 --- a/server/apps/quality/apps.py +++ b/server/apps/quality/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class QualityConfig(AppConfig): - name = 'quality' + name = 'apps.quality' diff --git a/server/apps/supervision/apps.py b/server/apps/supervision/apps.py index 7da9691..589f5aa 100644 --- a/server/apps/supervision/apps.py +++ b/server/apps/supervision/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class SupervisionConfig(AppConfig): - name = 'supervision' + name = 'apps.supervision' diff --git a/server/apps/supervision/migrations/0040_auto_20230522_1515.py b/server/apps/supervision/migrations/0040_auto_20230522_1515.py new file mode 100644 index 0000000..286be76 --- /dev/null +++ b/server/apps/supervision/migrations/0040_auto_20230522_1515.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2023-05-22 07:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0039_auto_20230517_1419'), + ] + + operations = [ + migrations.AlterField( + model_name='task2', + name='state', + field=models.PositiveSmallIntegerField(choices=[(10, '创建中'), (20, '进行中'), (30, '已关闭')], default=10, verbose_name='任务状态'), + ), + ] diff --git a/server/apps/supervision/migrations/0041_auto_20230522_1518.py b/server/apps/supervision/migrations/0041_auto_20230522_1518.py new file mode 100644 index 0000000..2b7ee81 --- /dev/null +++ b/server/apps/supervision/migrations/0041_auto_20230522_1518.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.5 on 2023-05-22 07:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0040_auto_20230522_1515'), + ] + + operations = [ + migrations.AlterField( + model_name='task2', + name='cycle', + field=models.PositiveSmallIntegerField(blank=True, default=0, help_text='1-6个周期', verbose_name='所属周期'), + ), + ] diff --git a/server/apps/supervision/migrations/0042_alter_pgoaldept_goal_value_b.py b/server/apps/supervision/migrations/0042_alter_pgoaldept_goal_value_b.py new file mode 100644 index 0000000..90df6a6 --- /dev/null +++ b/server/apps/supervision/migrations/0042_alter_pgoaldept_goal_value_b.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-05-23 06:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0041_auto_20230522_1518'), + ] + + operations = [ + migrations.AlterField( + model_name='pgoaldept', + name='goal_value_b', + field=models.FloatField(blank=True, null=True, verbose_name='设定值'), + ), + ] diff --git a/server/apps/supervision/migrations/0043_alter_pgoaldept_goal_value_a.py b/server/apps/supervision/migrations/0043_alter_pgoaldept_goal_value_a.py new file mode 100644 index 0000000..6548a55 --- /dev/null +++ b/server/apps/supervision/migrations/0043_alter_pgoaldept_goal_value_a.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-05-26 08:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0042_alter_pgoaldept_goal_value_b'), + ] + + operations = [ + migrations.AlterField( + model_name='pgoaldept', + name='goal_value_a', + field=models.FloatField(blank=True, null=True, verbose_name='基础值'), + ), + ] diff --git a/server/apps/supervision/migrations/0044_rename_num_ocheck_task2do_num_oinspect.py b/server/apps/supervision/migrations/0044_rename_num_ocheck_task2do_num_oinspect.py new file mode 100644 index 0000000..089137c --- /dev/null +++ b/server/apps/supervision/migrations/0044_rename_num_ocheck_task2do_num_oinspect.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-05-29 00:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0043_alter_pgoaldept_goal_value_a'), + ] + + operations = [ + migrations.RenameField( + model_name='task2do', + old_name='num_ocheck', + new_name='num_oinspect', + ), + ] diff --git a/server/apps/supervision/migrations/0045_rc_remark.py b/server/apps/supervision/migrations/0045_rc_remark.py new file mode 100644 index 0000000..829d9ca --- /dev/null +++ b/server/apps/supervision/migrations/0045_rc_remark.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-05-29 01:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0044_rename_num_ocheck_task2do_num_oinspect'), + ] + + operations = [ + migrations.AddField( + model_name='rc', + name='remark', + field=models.TextField(blank=True, null=True, verbose_name='备注'), + ), + ] diff --git a/server/apps/supervision/migrations/0046_auto_20230529_1206.py b/server/apps/supervision/migrations/0046_auto_20230529_1206.py new file mode 100644 index 0000000..b4480af --- /dev/null +++ b/server/apps/supervision/migrations/0046_auto_20230529_1206.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.12 on 2023-05-29 04:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0045_rc_remark'), + ] + + operations = [ + migrations.AddField( + model_name='pt', + name='handle_result', + field=models.PositiveSmallIntegerField(choices=[(0, '/'), (10, '已整改'), (20, '被暂停'), (30, '已被撤销认可资格')], default=0, help_text="((0, '/'), (10, '已整改'), (20, '被暂停'), (30, '已被撤销认可资格'))", verbose_name='处理结果'), + ), + migrations.AlterField( + model_name='oinspect', + name='cate', + field=models.PositiveSmallIntegerField(choices=[(10, '市场监管部门检查'), (20, '行业主管部门检查'), (30, '其他检查')], default=30, help_text="((10, '市场监管部门检查'), (20, '行业主管部门检查'), (30, '其他检查'))", verbose_name='检查分类'), + ), + migrations.AlterField( + model_name='pt', + name='result', + field=models.PositiveSmallIntegerField(choices=[(10, '满意'), (20, '有问题'), (30, '不满意')], default=10, help_text="((10, '满意'), (20, '有问题'), (30, '不满意'))", verbose_name='结果'), + ), + migrations.AlterField( + model_name='risk', + name='level', + field=models.PositiveSmallIntegerField(choices=[(10, '主要风险'), (20, '严重风险'), (30, '重大风险')], default=10, help_text="((10, '主要风险'), (20, '严重风险'), (30, '重大风险'))", verbose_name='风险等级'), + ), + migrations.AlterField( + model_name='risk', + name='state', + field=models.PositiveSmallIntegerField(choices=[(10, '未整改'), (20, '整改中'), (30, '整改完成')], default=10, help_text="((10, '未整改'), (20, '整改中'), (30, '整改完成'))", verbose_name='整改状态'), + ), + ] diff --git a/server/apps/supervision/migrations/0047_auto_20230531_1629.py b/server/apps/supervision/migrations/0047_auto_20230531_1629.py new file mode 100644 index 0000000..c6cbef7 --- /dev/null +++ b/server/apps/supervision/migrations/0047_auto_20230531_1629.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.12 on 2023-05-31 08:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('supervision', '0046_auto_20230529_1206'), + ] + + operations = [ + migrations.AddField( + model_name='task2do', + name='num_acc', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='重大质量事故'), + ), + migrations.AddField( + model_name='task2do', + name='num_complaint_10', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='满意客户投诉数'), + ), + migrations.AddField( + model_name='task2do', + name='num_pt_10', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='满意能力验证数'), + ), + ] diff --git a/server/apps/supervision/models.py b/server/apps/supervision/models.py index 6b3dd7f..6ce7664 100644 --- a/server/apps/supervision/models.py +++ b/server/apps/supervision/models.py @@ -1,6 +1,6 @@ from django.db import models from utils.model import BaseModel -from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File +from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File, CommonADModel, CommonBDModel from django.contrib.postgres.fields import JSONField # Create your models here. @@ -114,7 +114,7 @@ class TAction(CommonAModel): value_old = JSONField('原值', null=True, blank=True, default=dict) value_new = JSONField('新值', null=True, blank=True, default=dict) -class Pgoal(CommonAModel): +class Pgoal(CommonADModel): """ 基础目标 """ @@ -137,7 +137,7 @@ class Pgoal(CommonAModel): pgoal_4_file = models.ForeignKey(File, related_name='pgoal_4_file', on_delete=models.SET_NULL, null=True, blank=True) pgoal_5_file = models.ForeignKey(File, related_name='pgoal_5_file', on_delete=models.SET_NULL, null=True, blank=True) -class Task2(CommonAModel): +class Task2(CommonADModel): """ 报送任务2 """ @@ -157,46 +157,49 @@ class Task2(CommonAModel): ) year = models.PositiveSmallIntegerField('年份', default=2023) - cycle = models.PositiveSmallIntegerField('所属周期', default=0, help_text='1-6个周期') + cycle = models.PositiveSmallIntegerField('所属周期', default=0, help_text='1-6个周期', blank=True) type = models.PositiveSmallIntegerField('任务类型', choices=TASK2_TYPES, default=20) - state = models.PositiveSmallIntegerField('任务状态', choices=TASK2_STATES, default=20) + state = models.PositiveSmallIntegerField('任务状态', choices=TASK2_STATES, default=10) depts = models.ManyToManyField(Organization, verbose_name='执行公司', through='supervision.task2do', blank=True) -class Task2Do(CommonBModel): +class Task2Do(CommonBDModel): """任务执行情况 """ task2 = models.ForeignKey(Task2, verbose_name='关联任务', on_delete=models.CASCADE, related_name='task2dept_task2') count_up = models.PositiveSmallIntegerField('完成上报内容数', default=0) - count_all = models.PositiveSmallIntegerField('需上报内容数', default=8) + count_all = models.PositiveSmallIntegerField('需上报内容数', default=9) num_issue = models.PositiveSmallIntegerField('已发放报告数量', null=True, blank=True) num_expect = models.PositiveSmallIntegerField('应发报告数', null=True, blank=True) + num_acc = models.PositiveSmallIntegerField('重大质量事故', null=True, blank=True) num_error = models.PositiveSmallIntegerField('不准确报告数', null=True, blank=True) num_overdue = models.PositiveSmallIntegerField('超期报告数', null=True, blank=True) num_complaint = models.PositiveSmallIntegerField('客户投诉数', null=True, blank=True) + num_complaint_10 = models.PositiveSmallIntegerField('满意客户投诉数', null=True, blank=True) num_pt = models.PositiveSmallIntegerField('能力验证数', null=True, blank=True) + num_pt_10 = models.PositiveSmallIntegerField('满意能力验证数', null=True, blank=True) num_risk = models.PositiveSmallIntegerField('风险识别数', null=True, blank=True) - num_ocheck = models.PositiveSmallIntegerField('外部检查数', null=True, blank=True) + num_oinspect = models.PositiveSmallIntegerField('外部检查数', null=True, blank=True) -class PgoalDept(CommonBModel): +class PgoalDept(CommonBDModel): """企业年度目标值 """ year = models.PositiveSmallIntegerField('年份') goal_name = models.CharField('目标名称', max_length=100) goal_key = models.CharField('目标key值', max_length=100, default='other', blank=True) - goal_value_a = models.FloatField('基础值') + goal_value_a = models.FloatField('基础值', null=True, blank=True) goal_o_file = models.ForeignKey(File, related_name='goal_o_file', verbose_name='原文件ID', on_delete=models.SET_NULL, null=True, blank=True) - goal_value_b = models.FloatField('设定值') + goal_value_b = models.FloatField('设定值', null=True, blank=True) goal_value_c = models.FloatField('实际值', null=True, blank=True) goal_file = models.ForeignKey(File, related_name='goal_file', verbose_name='当前文件ID', on_delete=models.SET_NULL, null=True, blank=True) goal_files = models.ManyToManyField(File, related_name='goal_files', verbose_name='历史文件', blank=True) task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True) -class Rc(CommonBModel): +class Rc(CommonBDModel): """报告或证书 """ etype_choices = ( @@ -211,9 +214,10 @@ class Rc(CommonBModel): date_expect = models.DateField('预计发放日期', null=True, blank=True) date_issue = models.DateField('实际发放日期', null=True, blank=True) task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True) + remark = models.TextField('备注', null=True, blank=True) -class Complaint(CommonBModel): +class Complaint(CommonBDModel): """客户投诉 """ complainant = models.CharField('投诉人', max_length=100) @@ -224,7 +228,7 @@ class Complaint(CommonBModel): task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True) -class Pt(CommonBModel): +class Pt(CommonBDModel): """能力验证 """ pt_result_choices = ( @@ -232,15 +236,22 @@ class Pt(CommonBModel): (20, '有问题'), (30, '不满意') ) + pt_hresult_choices = ( + (0, '/'), + (10, '已整改'), + (20, '被暂停'), + (30, '已被撤销认可资格') + ) name = models.CharField('名称', max_length=100) number = models.CharField('编号', max_length=100) organizer = models.CharField('组织方', max_length=100) params = models.TextField('参加参数名称') - result = models.PositiveSmallIntegerField('结果', choices=pt_result_choices, default=10) + result = models.PositiveSmallIntegerField('结果', choices=pt_result_choices, default=10, help_text=str(pt_result_choices)) + handle_result = models.PositiveSmallIntegerField('处理结果', choices = pt_hresult_choices, default=0, help_text=str(pt_hresult_choices)) task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True) -class Risk(CommonBModel): +class Risk(CommonBDModel): """风险识别 """ risk_level_choices = ( @@ -254,13 +265,13 @@ class Risk(CommonBModel): (30, '整改完成') ) content = models.TextField('内容') - level = models.PositiveSmallIntegerField('风险等级', choices=risk_level_choices, default=10) + level = models.PositiveSmallIntegerField('风险等级', choices=risk_level_choices, default=10, help_text=str(risk_level_choices)) code = models.CharField('风险代码', max_length=100) - state = models.PositiveSmallIntegerField('整改状态', choices=risk_state_choices, default=10) + state = models.PositiveSmallIntegerField('整改状态', choices=risk_state_choices, default=10, help_text=str(risk_state_choices)) task2do = models.ForeignKey('supervision.task2do', verbose_name= '关联任务执行', on_delete=models.SET_NULL, null=True, blank=True) -class Oinspect(CommonBModel): +class Oinspect(CommonBDModel): """外部检查 """ oinspect_cate_choices = ( @@ -268,7 +279,7 @@ class Oinspect(CommonBModel): (20, '行业主管部门检查'), (30, '其他检查') ) - cate = models.PositiveSmallIntegerField('检查分类', choices=oinspect_cate_choices, default=30) + cate = models.PositiveSmallIntegerField('检查分类', choices=oinspect_cate_choices, default=30, help_text=str(oinspect_cate_choices)) checker = models.CharField('检查方', max_length=100) date_inspect = models.DateField('检查日期') result = models.TextField('检查结果') diff --git a/server/apps/supervision/serializers.py b/server/apps/supervision/serializers.py index 78db033..28a35dc 100644 --- a/server/apps/supervision/serializers.py +++ b/server/apps/supervision/serializers.py @@ -112,7 +112,7 @@ class Task2Serializer(serializers.ModelSerializer): read_only_fields = ['create_time', 'update_time', 'create_by', 'update_by', 'belong_dept', 'state', 'is_deleted', 'depts'] def update(self, instance, validated_data): - validated_data.pop('type') # type不可编辑 + validated_data.pop('type', None) # type不可编辑 if instance.state != Task2.TASK2_CLOSE: raise ParseError('非创建状态不可编辑') return super().update(instance, validated_data) @@ -131,24 +131,36 @@ class Task2DoSerializer(serializers.ModelSerializer): class ValidateMixin: def validate(self, attrs): task2do = attrs.get('task2do', None) + attrs['create_by'] = self.context['request'].user + attrs['belong_dept'] = self.context['request'].user.dept if task2do: if task2do.task2.state == Task2.TASK2_CLOSE: raise ParseError('任务已关闭不可处理') - attrs['year'] = task2do.task2.year - attrs['belong_dept'] = task2do.task2.belong_dept + attrs['belong_dept'] = task2do.belong_dept return attrs class BaseMeta: fields = '__all__' - read_only_fields = ['create_time', 'update_time', 'create_by', 'update_by', 'is_deleted'] + read_only_fields = ['create_time', 'update_time', 'create_by', 'update_by', 'is_deleted', 'belong_dept'] -class PgoalDeptSerializer(ValidateMixin, serializers.ModelSerializer): +class PgoalDeptSerializer(serializers.ModelSerializer): goal_file_ = FileSimpleSerializer(source='goal_file', read_only=True) + goal_o_file_ = FileSimpleSerializer(source='goal_o_file', read_only=True) class Meta(BaseMeta): model = PgoalDept - read_only_fields = BaseMeta.read_only_fields + ['goal_value_a', 'goal_value_c', 'goal_files', 'goal_key'] - + read_only_fields = BaseMeta.read_only_fields + ['goal_value_a', 'goal_value_c', 'goal_files', 'goal_key', 'goal_file_', 'goal_o_file', 'goal_o_file_'] + def validate(self, attrs): + task2do = attrs.get('task2do', None) + attrs['create_by'] = self.context['request'].user + attrs['belong_dept'] = self.context['request'].user.dept + if task2do: + if task2do.task2.state == Task2.TASK2_CLOSE: + raise ParseError('任务已关闭不可处理') + attrs['year'] = task2do.task2.year + attrs['belong_dept'] = task2do.belong_dept + return attrs + class RcSerializer(ValidateMixin, serializers.ModelSerializer): class Meta(BaseMeta): @@ -176,4 +188,18 @@ class OinspectSerializer(ValidateMixin, serializers.ModelSerializer): class NoRecordSerializer(serializers.Serializer): - key = serializers.ChoiceField(label='字段key', choices=['num_error', 'num_overdue', 'num_complaint', 'num_pt', 'num_risk', 'num_oinspect']) \ No newline at end of file + key = serializers.ChoiceField(label='字段key', choices=['num_error', 'num_overdue', 'num_complaint', 'num_pt', 'num_risk', 'num_oinspect']) + + +class UpdateFieldSerializer(serializers.ModelSerializer): + + class Meta: + model = Task2Do + fields = ['num_issue', 'num_expect', 'num_acc'] + extra_kwargs = {'num_issue': {'required': True}, 'num_expect': {'required': True}, 'num_acc': {'required': True}} + + + +class ImpSerializer(serializers.Serializer): + path = serializers.CharField(label='文件地址') + task2do = serializers.PrimaryKeyRelatedField(label='关联task2do的ID值', queryset = Task2Do.objects.all()) \ No newline at end of file diff --git a/server/apps/supervision/views.py b/server/apps/supervision/views.py index 2bd25c0..bbff424 100644 --- a/server/apps/supervision/views.py +++ b/server/apps/supervision/views.py @@ -11,6 +11,7 @@ from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelM from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet, ModelViewSet from utils.pagination import PageOrNot +from openpyxl import load_workbook from apps.system.mixins import CreateUpdateCustomMixin from apps.system.models import Organization @@ -409,6 +410,7 @@ class PogalViewSet(CreateUpdateCustomMixin, ModelViewSet): if self.action in ['create', 'update']: return PgoalCreateUpdateSerializer return super().get_serializer_class() + class Task2ViewSet(CreateUpdateCustomMixin, ModelViewSet): perms_map = {'get': '*', 'post': 'task2', @@ -434,7 +436,7 @@ class Task2ViewSet(CreateUpdateCustomMixin, ModelViewSet): return Response() @transaction.atomic - @action(methods=['put'], detail=True, perms_map = {'put':'task2'}) + @action(methods=['put'], detail=True, perms_map = {'put':'task2'}, serializer_class=serializers.Serializer) def start(self, request, *args, **kwargs): """ 开始执行 @@ -450,13 +452,13 @@ class Task2ViewSet(CreateUpdateCustomMixin, ModelViewSet): except: raise ParseError('未配置年度基础目标') for i in tds: - for k, v in enumerate(Pgoal.basedict): - pds = PgoalDept.objects.filter(year=i.year, task2do=i, goal_key=k) + for key in Pgoal.basedict: + pds = PgoalDept.objects.filter(task2do=i, goal_key=key) if pds.exists(): - pds.update(**{'goal_name': v, 'goal_value_a': getattr(pg, k), 'goal_o_file': getattr(pg, k+'_file')}) + pds.update(**{'goal_name': Pgoal.basedict[key], 'goal_value_a': getattr(pg, key), 'goal_o_file': getattr(pg, key+'_file')}) else: - PgoalDept.objects.create(**{'year': i.year, 'task2do': i, 'belong_dept': i.belong_dept, - 'goal_name': v, 'goal_key': k, 'goal_value_a': getattr(pg, k), 'goal_o_file': getattr(pg, k+'_file')}) + PgoalDept.objects.create(**{'year': i.task2.year, 'task2do': i, 'belong_dept': i.belong_dept, + 'goal_name': Pgoal.basedict[key], 'goal_key': key, 'goal_value_a': getattr(pg, key), 'goal_o_file': getattr(pg, key+'_file')}) obj.state = Task2.TASK2_DOING obj.save() return Response() @@ -471,11 +473,11 @@ class Task2ViewSet(CreateUpdateCustomMixin, ModelViewSet): sr = Task2DeptsSerializer(data=request.data) sr.is_valid(raise_exception=True) vdata = sr.validated_data - count_all = 8 + count_all = 9 if task2.type == Task2.TASK2_F: count_all = 5 for i in vdata['ids']: - Task2Do.objects.get_or_create(task2=task2, belong_dept=i, defaults={'task2': task2, 'belong_dept': i, 'count_all': count_all}) + Task2Do.objects.get_or_create(task2=task2, belong_dept=i, defaults={'task2': task2, 'belong_dept': i, 'count_all': count_all, 'create_by': self.request.user}) return Response() @@ -484,7 +486,7 @@ class Task2DoViewSet(ListModelMixin, GenericViewSet): queryset = Task2Do.objects.select_related('belong_dept', 'task2').all() serializer_class = Task2DoSerializer ordering = ['belong_dept__sort'] - + filterset_fields = ['task2', 'belong_dept', 'task2__year', 'task2__cycle'] cfields = ['num_issue', 'num_expect', 'num_error', 'num_overdue', 'num_complaint', 'num_pt', 'num_risk', 'num_oinspect'] @classmethod @@ -498,19 +500,38 @@ class Task2DoViewSet(ListModelMixin, GenericViewSet): instance.count_up = count_up instance.save() - @action(methods=['put'], detail=True, perms_map = {'post':'task2do'}, serializer_class=NoRecordSerializer) + @action(methods=['put'], detail=True, perms_map = {'put':'task2do'}, serializer_class=UpdateFieldSerializer) + def save_num(self, request, *args, **kwargs): + """保存数值 + + 保存数值 + """ + obj = self.get_object() + obj.num_issue = request.data.get('num_issue', 0) + obj.num_expect = request.data.get('num_expect', 0) + obj.num_acc = request.data.get('num_acc', 0) + obj.update_by = request.user + obj.save() + self.countup_task2do(obj) + return Response() + + @action(methods=['put'], detail=True, perms_map = {'put':'task2do'}, serializer_class=NoRecordSerializer) def no_record(self, request, *args, **kwargs): """没有记录 没有记录 """ obj = self.get_object() - setattr(obj, request.data.get('key'), 0) + theattr = getattr(obj, request.data.get('key'), None) + if theattr is None: + theattr = 0 + else: + raise ParseError('已有记录') obj.save() self.countup_task2do(obj) return Response() - @action(methods=['get'], detail=False, perms_map = {'post':'task2do'}) + @action(methods=['get'], detail=False, perms_map = {'get':'task2do'}) def my(self, request, *args, **kwargs): """我的任务 @@ -523,7 +544,7 @@ class Task2DoViewSet(ListModelMixin, GenericViewSet): else: mydept = request.user.dept belong_depts = get_child_queryset2(mydept) - queryset = queryset.filter(belong_dept = belong_depts) + queryset = queryset.filter(belong_dept__in = belong_depts) serializer = Task2DoSerializer(queryset, many=True) return Response(serializer.data) @@ -532,80 +553,233 @@ class Domixin: perms_map = {'get': '*', 'put': 'task2do', 'post': 'task2do', 'delete': 'task2do'} ordering = ['create_time'] ordering_fields = ['create_time', 'belong_dept__sort'] + filterset_fields = ['task2do', 'task2do__task2', 'task2do__belong_dept'] def countnum_task2do_field(self, task2do): - for k, v in self.count_dict: - setattr(task2do, k, self.queryset.model.objects.filter({**{'task2do': task2do}, **v}).count()) + for k, v in self.count_dict.items(): + setattr(task2do, k, self.queryset.model.objects.filter(**{**{'task2do': task2do}, **v}).count()) task2do.save() # 更新count_up字段 Task2DoViewSet.countup_task2do(task2do) def countup_task2do(self, task2do): if task2do.task2.type == Task2.TASK2_F: - task2do.count_up = PgoalDept.objects.filter(task2do=task2do).count() + task2do.count_up = PgoalDept.objects.filter(task2do=task2do).exclude(goal_value_b=None).count() task2do.save() elif task2do.task2.type == Task2.TASK2_S: self.countnum_task2do_field(task2do) @transaction.atomic def perform_create(self, serializer): - instance = super().perform_create(serializer) + instance = serializer.save() self.countup_task2do(instance.task2do) @transaction.atomic def perform_destroy(self, instance): task2do = instance.task2do - instance.delete(soft=False) + instance.delete() self.countup_task2do(task2do) class PgoalDeptViewSet(Domixin, CreateUpdateCustomMixin, ModelViewSet): queryset = PgoalDept.objects.all() serializer_class = PgoalDeptSerializer + filterset_fields = ['goal_key', 'belong_dept', 'year', 'task2do'] def perform_update(self, serializer): - instance = super().perform_update(serializer) + instance = serializer.save() goal_file = instance.goal_file if goal_file: instance.goal_files.add(goal_file) +class ImpMixin: + def get_enum(self, val, atuple, ind): + for i in atuple: + if i[1] == val: + return i[0] + raise ParseError('第{}: 请选择固定选项值'.format(ind)) + + def make_data(self, data, sheet, i, etype): + raise NotImplementedError() -class RcViewSet(Domixin, CreateUpdateCustomMixin, ModelViewSet): + def gen_imp_view(self, request, start: int, mySerializer, etype=0): + sr = ImpSerializer(data=request.data) + sr.is_valid(raise_exception=True) + vdata = sr.validated_data + path = vdata['path'] + if not path.endswith('.xlsx'): + raise ParseError('请提供xlsx格式文件') + task2do = vdata['task2do'] + fullpath = settings.BASE_DIR + path + wb = load_workbook(fullpath,data_only=True) + sheet = wb.worksheets[0] + datalist = [] + i = start + while sheet['b'+str(i)].value: + data = {} + data['task2do'] = task2do.id + data = self.make_data(data, sheet, i, etype) + datalist.append(data) + i = i + 1 + sr = mySerializer(data=datalist, many=True) + sr.is_valid() + err_msg = '' + for ind, val in enumerate(sr.errors): + if val: + for i in val: + err_msg = '第{}行: {}'.format(5+ind, i + str(val[i][0])) + if err_msg != '': + raise ParseError(err_msg) + else: + sr.save(create_by=request.user) + return Response() + +class RcViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet): queryset = Rc.objects.all() serializer_class = RcSerializer count_dict = { "num_error": {'etype': 10}, "num_overdue": {'etype': 20} } + filterset_fields = ['etype', 'task2do', 'task2do__belong_dept', 'task2do__task2__year'] + + + def make_data(self, data, sheet, i, etype): + data['etype'] = etype + if etype == 10: + data['number'] = sheet['b'+str(i)].value + data['number_correct'] = sheet['c'+str(i)].value + data['reason_error'] = sheet['d'+str(i)].value + data['date_issue'] = sheet['e'+str(i)].value + else: + data['number'] = sheet['b'+str(i)].value + data['date_accept'] = sheet['c'+str(i)].value + data['date_expect'] = sheet['d'+str(i)].value + data['date_issue'] = sheet['e'+str(i)].value + data['remark'] = sheet['f'+str(i)].value + return data + + @action(methods=['post'], detail=False, perms_map = {'post':'task2do'}, serializer_class=ImpSerializer) + @transaction.atomic + def imp_error(self, request, *args, **kwargs): + """导入表格 + + 导入表格 + """ + return self.gen_imp_view(request, 5, RcSerializer, 10) + + + @action(methods=['post'], detail=False, perms_map = {'post':'task2do'}, serializer_class=ImpSerializer) + @transaction.atomic + def imp_overdue(self, request, *args, **kwargs): + """导入表格 + + 导入表格 + """ + return self.gen_imp_view(request, 5, RcSerializer, 20) + -class ComplaintViewSet(Domixin, CreateUpdateCustomMixin, ModelViewSet): +class ComplaintViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet): queryset = Complaint.objects.all() serializer_class = ComplaintSerializer count_dict = { - "num_complaint": {} + "num_complaint": {}, + "num_complaint_10": {'is_satisfied': True} } + filterset_fields = ['task2do', 'task2do__belong_dept', 'task2do__task2__year'] + + def make_data(self, data, sheet, i, etype): + data['complainant'] = sheet['b'+str(i)].value + data['date_occurrence'] = sheet['c'+str(i)].value + data['content'] = sheet['d'+str(i)].value + data['date_feedback'] = sheet['e'+str(i)].value + data['is_satisfied'] = True if sheet['f'+str(i)].value == '是' else False + return data + + @action(methods=['post'], detail=False, perms_map = {'post':'task2do'}, serializer_class=ImpSerializer) + @transaction.atomic + def imp(self, request, *args, **kwargs): + """导入表格 + + 导入表格 + """ + return self.gen_imp_view(request, 5, ComplaintSerializer) -class PtViewSet(Domixin, CreateUpdateCustomMixin, ModelViewSet): +class PtViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet): queryset = Pt.objects.all() serializer_class = PtSerializer count_dict = { - "num_pt": {} + "num_pt": {}, + "num_pt_10": {'result': 10} } + filterset_fields = ['task2do', 'task2do__belong_dept', 'task2do__task2__year'] + + def make_data(self, data, sheet, i, etype): + data['name'] = sheet['b'+str(i)].value + data['number'] = sheet['c'+str(i)].value + data['organizer'] = sheet['d'+str(i)].value + data['params'] = sheet['e'+str(i)].value + data['result'] = self.get_enum(sheet['f'+str(i)].value, Pt.pt_result_choices, i) + data['handle_result'] = self.get_enum(sheet['g'+str(i)].value, Pt.pt_hresult_choices, i) + return data + + @action(methods=['post'], detail=False, perms_map = {'post':'task2do'}, serializer_class=ImpSerializer) + @transaction.atomic + def imp(self, request, *args, **kwargs): + """导入表格 + + 导入表格 + """ + return self.gen_imp_view(request, 5, PtSerializer) -class RiskViewSet(Domixin, CreateUpdateCustomMixin, ModelViewSet): +class RiskViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet): queryset = Risk.objects.all() serializer_class = RiskSerializer count_dict = { "num_risk": {} } + filterset_fields = ['task2do', 'task2do__belong_dept', 'task2do__task2__year'] + + def make_data(self, data, sheet, i, etype): + data['content'] = sheet['b'+str(i)].value + data['level'] = self.get_enum(sheet['c'+str(i)].value, Risk.risk_level_choices, i) + data['code'] = sheet['d'+str(i)].value + data['state'] = self.get_enum(sheet['e'+str(i)].value, Risk.risk_state_choices, i) + return data + + @action(methods=['post'], detail=False, perms_map = {'post':'task2do'}, serializer_class=ImpSerializer) + @transaction.atomic + def imp(self, request, *args, **kwargs): + """导入表格 + + 导入表格 + """ + return self.gen_imp_view(request, 5, RiskSerializer) -class OinspectViewSet(Domixin, CreateUpdateCustomMixin, ModelViewSet): +class OinspectViewSet(ImpMixin, Domixin, CreateUpdateCustomMixin, ModelViewSet): queryset = Oinspect.objects.all() serializer_class = OinspectSerializer count_dict = { "num_oinspect": {} } + filterset_fields = ['task2do', 'task2do__belong_dept', 'task2do__task2__year'] + + def make_data(self, data, sheet, i, etype): + data['cate'] = self.get_enum(sheet['b'+str(i)].value, Oinspect.oinspect_cate_choices, i) + data['checker'] = sheet['c'+str(i)].value + data['date_inspect'] = sheet['d'+str(i)].value + data['result'] = sheet['e'+str(i)].value + return data + + @action(methods=['post'], detail=False, perms_map = {'post':'task2do'}, serializer_class=ImpSerializer) + @transaction.atomic + def imp(self, request, *args, **kwargs): + """导入表格 + + 导入表格 + """ + return self.gen_imp_view(request, 5, OinspectSerializer) \ No newline at end of file diff --git a/server/apps/system/apps.py b/server/apps/system/apps.py index 0e3baeb..7d38593 100644 --- a/server/apps/system/apps.py +++ b/server/apps/system/apps.py @@ -2,5 +2,5 @@ from django.apps import AppConfig class RbacConfig(AppConfig): - name = 'system' + name = 'apps.system' verbose_name = '系统管理' diff --git a/server/apps/system/migrations/0023_alter_user_first_name.py b/server/apps/system/migrations/0023_alter_user_first_name.py new file mode 100644 index 0000000..e3498fc --- /dev/null +++ b/server/apps/system/migrations/0023_alter_user_first_name.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-05-23 06:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0022_delete_historicaldict'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='first_name', + field=models.CharField(blank=True, max_length=150, verbose_name='first name'), + ), + ] diff --git a/server/apps/system/models.py b/server/apps/system/models.py index 8b6781f..fd84af5 100644 --- a/server/apps/system/models.py +++ b/server/apps/system/models.py @@ -167,6 +167,17 @@ class Dict(SoftModel): def __str__(self): return self.name +class CommonADModel(BaseModel): + """ + 业务用基本表A,包含create_by, update_by字段,物理删除 + """ + create_by = models.ForeignKey( + User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='%(class)s_create_by') + update_by = models.ForeignKey( + User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='%(class)s_update_by') + + class Meta: + abstract = True class CommonAModel(SoftModel): """ @@ -194,6 +205,19 @@ class CommonBModel(SoftModel): class Meta: abstract = True +class CommonBDModel(BaseModel): + """ + 业务用基本表B,包含create_by, update_by, belong_dept字段,物理删除 + """ + create_by = models.ForeignKey( + User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='创建人', related_name='%(class)s_create_by') + update_by = models.ForeignKey( + User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='最后编辑人', related_name='%(class)s_update_by') + belong_dept = models.ForeignKey( + Organization, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='所属部门', related_name='%(class)s_belong_dept') + + class Meta: + abstract = True class File(CommonAModel): """ diff --git a/server/apps/vod/apps.py b/server/apps/vod/apps.py index d5b0761..ed6dc55 100644 --- a/server/apps/vod/apps.py +++ b/server/apps/vod/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class VodConfig(AppConfig): - name = 'vod' + name = 'apps.vod' diff --git a/server/requirements.txt b/server/requirements.txt index 77fa7d5..1b1b0b1 100644 Binary files a/server/requirements.txt and b/server/requirements.txt differ diff --git a/server/server/settings.py b/server/server/settings.py index b7396ec..1934997 100644 --- a/server/server/settings.py +++ b/server/server/settings.py @@ -126,6 +126,9 @@ USE_TZ = True CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True +# 默认主键 +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/