From a216e179041530caae89f4900d0fcf3adb4bec55 Mon Sep 17 00:00:00 2001 From: zty Date: Thu, 28 Mar 2024 14:15:15 +0800 Subject: [PATCH] =?UTF-8?q?feature:=20feat:=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加质量表彰功能 --- server/apps/information/apps.py | 2 +- .../migrations/0003_auto_20240327_1315.py | 58 +++++++ .../migrations/0004_auto_20240327_1741.py | 37 +++++ .../0005_qualitycommendation_department.py | 20 +++ server/apps/information/models.py | 27 +--- server/apps/information/serializers.py | 38 +++-- server/apps/information/url.py | 19 --- server/apps/information/urls.py | 13 ++ server/apps/information/views.py | 147 ++++++++++++++++-- server/server/settings.py | 2 +- server/server/urls.py | 1 + 11 files changed, 295 insertions(+), 69 deletions(-) create mode 100644 server/apps/information/migrations/0003_auto_20240327_1315.py create mode 100644 server/apps/information/migrations/0004_auto_20240327_1741.py create mode 100644 server/apps/information/migrations/0005_qualitycommendation_department.py delete mode 100644 server/apps/information/url.py create mode 100644 server/apps/information/urls.py diff --git a/server/apps/information/apps.py b/server/apps/information/apps.py index 339005f..246dfa3 100644 --- a/server/apps/information/apps.py +++ b/server/apps/information/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class InformationConfig(AppConfig): - name = 'information' + name = 'apps.information' diff --git a/server/apps/information/migrations/0003_auto_20240327_1315.py b/server/apps/information/migrations/0003_auto_20240327_1315.py new file mode 100644 index 0000000..e9fac84 --- /dev/null +++ b/server/apps/information/migrations/0003_auto_20240327_1315.py @@ -0,0 +1,58 @@ +# Generated by Django 3.2.12 on 2024-03-27 05:15 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0023_alter_user_first_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('information', '0002_auto_20240326_0932'), + ] + + operations = [ + migrations.AddField( + model_name='qualitycommendation', + name='awardee_people', + field=models.CharField(max_length=20, null=True, verbose_name='获奖人'), + ), + migrations.AddField( + model_name='qualitycommendation', + name='belong_dept', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualitycommendation_belong_dept', to='system.organization', verbose_name='所属部门'), + ), + migrations.AddField( + model_name='qualitycommendation', + name='create_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualitycommendation_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'), + ), + migrations.AddField( + model_name='qualitycommendation', + name='create_time', + field=models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间'), + ), + migrations.AddField( + model_name='qualitycommendation', + name='is_deleted', + field=models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记'), + ), + migrations.AddField( + model_name='qualitycommendation', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='qualitycommendation_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + migrations.AddField( + model_name='qualitycommendation', + name='update_time', + field=models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间'), + ), + migrations.AlterField( + model_name='qualitycommendation', + name='awardee_company', + field=models.CharField(max_length=20, null=True, verbose_name='获奖单位'), + ), + ] diff --git a/server/apps/information/migrations/0004_auto_20240327_1741.py b/server/apps/information/migrations/0004_auto_20240327_1741.py new file mode 100644 index 0000000..8501f8b --- /dev/null +++ b/server/apps/information/migrations/0004_auto_20240327_1741.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.12 on 2024-03-27 09:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('information', '0003_auto_20240327_1315'), + ] + + operations = [ + migrations.RemoveField( + model_name='qualitycommendation', + name='belong_dept', + ), + migrations.RemoveField( + model_name='qualitycommendation', + name='create_by', + ), + migrations.RemoveField( + model_name='qualitycommendation', + name='create_time', + ), + migrations.RemoveField( + model_name='qualitycommendation', + name='is_deleted', + ), + migrations.RemoveField( + model_name='qualitycommendation', + name='update_by', + ), + migrations.RemoveField( + model_name='qualitycommendation', + name='update_time', + ), + ] diff --git a/server/apps/information/migrations/0005_qualitycommendation_department.py b/server/apps/information/migrations/0005_qualitycommendation_department.py new file mode 100644 index 0000000..b806c54 --- /dev/null +++ b/server/apps/information/migrations/0005_qualitycommendation_department.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2024-03-27 11:25 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0023_alter_user_first_name'), + ('information', '0004_auto_20240327_1741'), + ] + + operations = [ + migrations.AddField( + model_name='qualitycommendation', + name='department', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='system.organization', verbose_name='所属单位'), + ), + ] diff --git a/server/apps/information/models.py b/server/apps/information/models.py index 0fdd528..1bab567 100644 --- a/server/apps/information/models.py +++ b/server/apps/information/models.py @@ -1,7 +1,5 @@ from django.db import models - -# Create your models here. - +from apps.system.models import Organization class AbilityReview(models.Model): type_method = ( (0, '文审'), @@ -25,26 +23,21 @@ class AbilityReview(models.Model): verbose_name = '评审情况' db_table = 'ability_review' - def __str__(self): - return self.name - class QualityCommendation(models.Model): name = models.CharField(max_length=20, unique=True, verbose_name='项目名称') commendation_name = models.CharField(max_length=20, verbose_name='表彰名称') Awards_level = models.CharField(max_length=20, verbose_name='获奖等级') - awardee_company = models.CharField(max_length=20, verbose_name='获奖单位') + awardee_company = models.CharField(max_length=20, verbose_name='获奖单位', null=True) + awardee_people = models.CharField(max_length=20, verbose_name='获奖人', null=True) awarded_by = models.CharField(max_length=20, verbose_name='颁奖单位') awarded_date = models.DateField(verbose_name='获奖日期', null=True) - + department = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True, verbose_name='所属单位') + class Meta: verbose_name = '质量表彰' db_table = 'quality_commendation' - def __str__(self): - return self.name - - class QualityActivities(models.Model): role = (('组织方','组织方'),('活动方','活动方')) name = models.CharField(max_length=20, unique=True, verbose_name='活动名称') @@ -61,9 +54,6 @@ class QualityActivities(models.Model): verbose_name = '质量活动' db_table = 'quality_activities' - def __str__(self): - return self.name - class Contact(models.Model): name = models.CharField(max_length=20, unique=True, verbose_name='姓名') @@ -82,10 +72,6 @@ class Contact(models.Model): verbose_name = '实验室联系方式' db_table = 'contact' - def __str__(self): - return self.name - - class ExternalAuditors(models.Model): review_type = ( ('CNAS', 'CNAS'), ('CMA', 'CMA'), @@ -101,6 +87,3 @@ class ExternalAuditors(models.Model): class Meta: verbose_name = '外审员情况' db_table = 'externalauditors' - - def __str__(self): - return self.name \ No newline at end of file diff --git a/server/apps/information/serializers.py b/server/apps/information/serializers.py index 44bec81..6ceffec 100644 --- a/server/apps/information/serializers.py +++ b/server/apps/information/serializers.py @@ -1,28 +1,38 @@ from rest_framework import serializers -from apps.information.models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors +from apps.system.serializers import OrganizationSimpleSerializer +from .models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors + class AbilityReviewSerializer(serializers.ModelSerializer): - model = AbilityReview - fields = '__all__' + class Meta: + model = AbilityReview + fields = '__all__' class QualityCommendationSerializer(serializers.ModelSerializer): - model = QualityCommendation - fields = '__all__' - - + class Meta: + model = QualityCommendation + fields = '__all__' + read_only_fields = ['id'] + class QualityActivitiesSerializer(serializers.ModelSerializer): - model = QualityActivities - fields = '__all __' + + class Meta: + model = QualityActivities + fields = '__all __' class ContactSerializer(serializers.ModelSerializer): - model = Contact - fields = '__all__' + + class Meta: + model = Contact + fields = '__all__' class ExternalAuditorsSerializer(serializers.ModelSerializer): - model = ExternalAuditors - fields = '__all__' - + class Meta: + model = ExternalAuditors + fields = '__all__' +class ImpSerializer(serializers.Serializer): + path = serializers.FileField(label='文件地址') diff --git a/server/apps/information/url.py b/server/apps/information/url.py deleted file mode 100644 index a6f5a1a..0000000 --- a/server/apps/information/url.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.urls import path, include -from rest_framework.routers import DefaultRouter -from .views import AbilityReviewViewSet, \ - QualityCommendationBViewSet, \ - QualityActivitiesViewSet,\ - ContactViewSet, \ - ExternalAuditorsViewSet - -API_BASE_URL = 'api/information/' - -router = DefaultRouter() -router.register(r'abilityreviews', AbilityReviewViewSet, basename='评审情况') -router.register(r'qualitycommendation', QualityCommendationBViewSet, basename='质量表彰') -router.register(r'qualityactivities', QualityActivitiesViewSet, basename='质量活动') -router.register(r'contact', ContactViewSet, basename='实验室联系方式') -router.register(r'externalauditors', ExternalAuditorsViewSet, basename='外审员情况') - - -urlpatterns = [path(API_BASE_URL, include(router.urls)),] \ No newline at end of file diff --git a/server/apps/information/urls.py b/server/apps/information/urls.py new file mode 100644 index 0000000..fcc304d --- /dev/null +++ b/server/apps/information/urls.py @@ -0,0 +1,13 @@ +from django.urls import path, include +from rest_framework import routers +from .views import AbilityReviewViewSet, QualityCommendationViewSet, QualityActivitiesViewSet, ContactViewSet, ExternalAuditorsViewSet + +router = routers.DefaultRouter() +router.register('abilityreviews', AbilityReviewViewSet, basename='abilityreviews') +router.register('qualitycommendation', QualityCommendationViewSet, basename='qualitycommendation') +router.register('qualityactivities', QualityActivitiesViewSet, basename='qualityactivities') +router.register('contact', ContactViewSet, basename='contact') +router.register('externalauditors', ExternalAuditorsViewSet, basename='externalauditors') +urlpatterns = [ + path('', include(router.urls)) +] \ No newline at end of file diff --git a/server/apps/information/views.py b/server/apps/information/views.py index be88fe2..7581969 100644 --- a/server/apps/information/views.py +++ b/server/apps/information/views.py @@ -1,11 +1,26 @@ -from rest_framework import viewsets, mixins -from .models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors -from .serializers import AbilityReviewSerializer, \ - QualityCommendationSerializer, \ - QualityActivitiesSerializer,\ - ContactSerializer, \ - ExternalAuditorsSerializer +from rest_framework import viewsets, mixins +from rest_framework.viewsets import ViewSet +from rest_framework import status + +from django.conf import settings +from rest_framework.decorators import action +from rest_framework.views import APIView +from rest_framework.viewsets import ModelViewSet +# from .models import AbilityReview, QualityCommendation, QualityActivities, Contact, ExternalAuditors +# from .serializers import AbilityReviewSerializer, QualityCommendationSerializer, QualityActivitiesSerializer,ContactSerializer, ExternalAuditorsSerializer +from utils.queryset import get_child_queryset2 +from rest_framework.response import Response +from apps.system.permission import has_permission +from openpyxl import load_workbook +from django.db import transaction +from rest_framework.exceptions import ParseError +from apps.system.models import Organization +from .models import * +from .serializers import * + +from datetime import datetime +import os class AbilityReviewViewSet( mixins.CreateModelMixin, mixins.ListModelMixin, @@ -19,15 +34,123 @@ class AbilityReviewViewSet( mixins.CreateModelMixin, def get_queryset(self): pass +class ImpMixin: + def get_queryset(self): + mydept = self.request.user.dept + qs = super().get_queryset() + if has_permission('task2', self.request.user): + return qs + return qs.filter(belong_dept=mydept) -class QualityCommendationBViewSet(mixins.CreateModelMixin, - mixins.ListModelMixin, - mixins.DestroyModelMixin, - mixins.UpdateModelMixin, - viewsets.GenericViewSet): + def format_date(self, ind, val): + new_val = val + if isinstance(val, datetime.datetime): + new_val = val.date() + elif isinstance(val, datetime.date): + new_val = val + elif isinstance(val, str): + try: + new_val = datetime.datetime.strptime(val, '%Y-%m-%d').date() + except ValueError: + raise ParseError(f'第{ind}行, 日期时间格式错误') + elif val is None: + pass + else: + raise ParseError(f'第{ind}行, 日期时间格式错误') + return new_val + + def get_enum(self, val, atuple, ind): + for i in atuple: + if i[1] == val: + return i[0] + raise ParseError('第{}: 请选择固定选项值'.format(ind)) + + def F(self, data, sheet, i, etype): + raise NotImplementedError() + + def gen_imp_view(self, request, start: int, mySerializer, types = None): + if 'file' not in request.data: + raise ParseError('请提供文件') + path = request.data['file'] + + if not str(path).endswith('.xlsx'): + raise ParseError('请提供xlsx格式文件') + fullpath = os.path.join(settings.BASE_DIR, str(path)) + wb = load_workbook(fullpath,data_only=True) + sheet = wb.active + # 遍历Excel文件中的数据 + if types.lower() == "qt": + data_list = self.build_qt_data(sheet) + else: + pass + serializer = mySerializer(data=data_list, many=True, context={'request': request}) + if serializer.is_valid(): + serializer.save() + else: + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + return Response({'uploaded': 'File uploaded successfully'}, status=status.HTTP_201_CREATED) + + +class QualityCommendationViewSet(ImpMixin, ModelViewSet): queryset = QualityCommendation.objects.all() serializer_class = QualityCommendationSerializer + perms_map = {"get": "*", "post": "*", "put": "*", "delete": "*"} + def create(self): + if Organization.objects.filter(name=self.request.data['awardee_company']).exists(): + department_id = Organization.objects.filter(name=self.request.data['awardee_company']).first().id + self.request.data['department'] = department_id + serializer = self.get_serializer(data=self.request.data) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response(serializer.data, status = status.HTTP_201_CREATED) + # 人名存在重复 + else: + raise ParseError("部门不存在") + + # 导入表格 + @action(methods=['post'], detail=False) + @transaction.atomic + def imp(self, request, *args, **kwargs): + return self.gen_imp_view(request, 5, QualityCommendationSerializer, 'qt') + + #构造导入的数据格式 + def build_qt_data(self, sheet): + data_list = [] + for row in sheet.iter_rows(min_row=2, values_only=True): # 假设第一行是表头,从第二行开始读取数据 + if row[0] is not None: + awarded_date = row[6].strftime("%Y-%m-%d") + # 判断获奖的是人还是公司 + department_id = Organization.objects.filter(name=row[4]).first().id + if department_id: + awardee_people = None + awardee_company = row[4] + else: + awardee_company = None + awardee_people = row[4] + serializer_data = { + 'name': row[1], # 第一列是名字 + 'commendation_name':row[2], + 'Awards_level':row[3], + 'awardee_company':awardee_company, + 'awardee_people':awardee_people, + 'awarded_by':row[5], + 'awarded_date':awarded_date, + 'department':department_id, + } + data_list.append(serializer_data) + return data_list + + + # 查询子以及已经本公司的质量表彰 + @action(detail=False, methods=['get']) + def commentdation_info(self, *args, **kwargs): + father_dept = self.request.user.dept + child_dept = get_child_queryset2(father_dept) + query = QualityCommendation.objects.filter(department__in=child_dept) + serializer = QualityCommendationSerializer(query, many=True) + return Response(serializer.data) class QualityActivitiesViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, diff --git a/server/server/settings.py b/server/server/settings.py index 75a22ea..b6998d2 100644 --- a/server/server/settings.py +++ b/server/server/settings.py @@ -49,13 +49,13 @@ INSTALLED_APPS = [ 'apps.crm', 'apps.ability', 'apps.supervision', + 'apps.information', 'apps.quality', 'apps.vod', 'apps.consulting', 'apps.exam', 'apps.ops', 'apps.edu', - 'apps.information' ] MIDDLEWARE = [ diff --git a/server/server/urls.py b/server/server/urls.py index f5ad6fd..bc508a3 100644 --- a/server/server/urls.py +++ b/server/server/urls.py @@ -59,6 +59,7 @@ urlpatterns = [ path('api/supervision/', include('apps.supervision.urls')), path('api/quality/', include('apps.quality.urls')), path('api/vod/', include('apps.vod.urls')), + path('api/info/', include('apps.information.urls')), path('api/consulting/', include('apps.consulting.urls')), path('', include('apps.ops.urls')), path('', include('apps.exam.urls')),