This commit is contained in:
shijing 2024-01-02 14:41:15 +08:00
commit 4628111076
9 changed files with 231 additions and 37 deletions

View File

@ -1,3 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Course
# Register your models here. # Register your models here.
admin.site.register(Course)

View File

@ -0,0 +1,62 @@
# Generated by Django 3.2.12 on 2023-12-26 07:17
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 = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('edu', '0003_certificate_字体方案'),
]
operations = [
migrations.AddField(
model_name='certificate',
name='发证日期',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='certificate',
name='培训日期',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='certificate',
name='证书方案',
field=models.CharField(default='202309', max_length=20),
),
migrations.AlterField(
model_name='certificate',
name='单位名称',
field=models.CharField(blank=True, max_length=30, null=True),
),
migrations.AlterField(
model_name='certificate',
name='所属单位',
field=models.CharField(blank=True, max_length=20, null=True),
),
migrations.CreateModel(
name='Course',
fields=[
('id', models.BigAutoField(auto_created=True, 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=20, verbose_name='课程名')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='course_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='course_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='certificate',
name='课程列表',
field=models.ManyToManyField(blank=True, to='edu.Course'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2023-12-28 09:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('edu', '0004_auto_20231226_1517'),
]
operations = [
migrations.AddField(
model_name='certificate',
name='培训结束日期',
field=models.DateField(blank=True, null=True),
),
]

View File

@ -1,13 +1,20 @@
from django.db import models from django.db import models
from apps.system.models import CommonADModel from apps.system.models import CommonADModel, CommonAModel
# Create your models here. # Create your models here.
class Course(CommonAModel):
name = models.CharField("课程名", max_length=20)
class Certificate(CommonADModel): class Certificate(CommonADModel):
证书方案 = models.CharField(max_length=20, default="202309")
字体方案 = models.PositiveSmallIntegerField(default=1)
姓名 = models.CharField(max_length=20) 姓名 = models.CharField(max_length=20)
性别 = models.CharField(max_length=10, default='', null=True, blank=True) 性别 = models.CharField(max_length=10, default="", null=True, blank=True)
证书编号 = models.CharField(max_length=20) 证书编号 = models.CharField(max_length=20)
所属单位 = models.CharField(max_length=20) 所属单位 = models.CharField(max_length=20, null=True, blank=True)
单位名称 = models.CharField(max_length=30) 单位名称 = models.CharField(max_length=30, null=True, blank=True)
职务 = models.CharField(max_length=20, null=True, blank=True) 职务 = models.CharField(max_length=20, null=True, blank=True)
手机号 = models.CharField(max_length=11, null=True, blank=True) 手机号 = models.CharField(max_length=11, null=True, blank=True)
是否内审员 = models.BooleanField(default=False) 是否内审员 = models.BooleanField(default=False)
@ -16,5 +23,8 @@ class Certificate(CommonADModel):
是否最高管理者 = models.BooleanField(default=False) 是否最高管理者 = models.BooleanField(default=False)
是否需要集团证书 = models.BooleanField(default=False) 是否需要集团证书 = models.BooleanField(default=False)
是否需要北京标研培训合格 = models.BooleanField(default=False) 是否需要北京标研培训合格 = models.BooleanField(default=False)
课程列表 = models.ManyToManyField(Course, blank=True)
培训日期 = models.DateField(null=True, blank=True)
培训结束日期 = models.DateField(null=True, blank=True)
发证日期 = models.DateField(null=True, blank=True)
证书地址 = models.CharField(max_length=100, null=True, blank=True) 证书地址 = models.CharField(max_length=100, null=True, blank=True)
字体方案 = models.PositiveSmallIntegerField(default=1)

View File

@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from apps.edu.models import Certificate from apps.edu.models import Certificate, Course
from apps.edu.services import make_img_x from apps.edu.services import make_img_x
class CertificateSerializer(serializers.ModelSerializer): class CertificateSerializer(serializers.ModelSerializer):
@ -12,3 +12,10 @@ class CertificateSerializer(serializers.ModelSerializer):
instance = super().save(**kwargs) instance = super().save(**kwargs)
make_img_x(instance) make_img_x(instance)
return instance return instance
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = '__all__'
read_only_fields = ['create_time', 'update_time', 'create_by', 'update_by', 'is_deleted']

View File

@ -1,33 +1,113 @@
from PIL import ImageFont, ImageDraw, Image from PIL import ImageFont, ImageDraw, Image
from django.conf import settings from django.conf import settings
from apps.edu.models import Certificate from apps.edu.models import Certificate
font_dict = { from datetime import date
'font_1': "/media/cert/template/weibei.ttf",
'font_2': "/media/cert/template/SIMLI.TTF" font_dict = {"font_1": "/media/cert/template/weibei.ttf", "font_2": "/media/cert/template/SIMLI.TTF"}
}
def make_img_x(instance: Certificate): def make_img_x(instance: Certificate):
titles = [] if instance.证书方案 == "202309":
if instance.是否内审员: titles = []
titles.append('内审员') if instance.是否内审员:
if instance.是否授权签字人: titles.append("内审员")
titles.append('授权签字人') if instance.是否授权签字人:
if instance.是否质量负责人: titles.append("授权签字人")
titles.append('质量负责人') if instance.是否质量负责人:
if instance.是否最高管理者: titles.append("质量负责人")
titles.append('最高管理者') if instance.是否最高管理者:
instance.证书地址 = make_img(instance.证书编号, instance.姓名, ''.join(titles), font_dict[f'font_{instance.字体方案}']) titles.append("最高管理者")
instance.save() instance.证书地址 = make_img(instance.证书编号, instance.姓名, "".join(titles), font_dict[f"font_{instance.字体方案}"])
instance.save()
elif instance.证书方案 == "202312":
courses_list = [i.name for i in instance.课程列表.all()]
courses = "".join(courses_list) + ""
instance.证书地址 = make_img_2(instance.证书编号, instance.姓名, instance.培训日期, instance.培训结束日期, instance.发证日期, courses, font_dict[f"font_{instance.字体方案}"])
instance.save()
def text_wrap(draw, text, font, max_width):
lines = []
# 将文本分割成单词
words = list(text)
# 尝试将单词拼成一行,使其宽度不要超过最大宽度
while words:
line = ""
# 当加上一个新单词不会让行宽超出最大宽度时就加上它
while words and draw.textlength(line + words[0], font=font) <= max_width:
line += words.pop(0)
lines.append(line.strip()) # 移除多余的空格
return lines
def make_img_2(number, name, att_date: date, att_date2: date, issue_date: date, courses, font_name_path):
# 打开底版图片
imageFile = settings.BASE_DIR + "/media/cert/template/background2.jpg"
img = Image.open(imageFile)
# 选择字体与大小
font = ImageFont.truetype(settings.BASE_DIR + font_name_path, 140)
font_courses = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/simkai.ttf", 90)
font_number = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/timesbd.ttf", 58)
font_day = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/siyuan.otf", 68)
font_day2 = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/siyuan.otf", 58)
position_number = (1730, 606)
if len(name) <= 3:
position_name = (740, 840)
else:
position_name = (580, 840)
position_courses = (500, 1070)
position_note = (1990, 1600)
att_date_str = att_date.strftime("%Y年%m月%d")
issue_date_str = issue_date.strftime("%Y年%m月%d")
if att_date == att_date2:
date_str = f"同志于{att_date_str}参加了以下培训课程:"
else:
att_date2_str = att_date2.strftime("%Y年%m月%d")
date_str = f"同志于{att_date_str}-{att_date2_str}参加了以下培训课程:"
position_date = (1170, 900)
position_issue_date = (1990, 1910)
# position_a_y = (1530, 900)
# position_a_m = (1870, 900)
# position_a_d = (2170, 900)
# position_i_y = (2010, 1910)
# position_i_m = (2254, 1910)
# position_i_d = (2446, 1910)
color_name = (82, 68, 19)
color = (0, 0, 0)
draw = ImageDraw.Draw(img)
draw.text(position_name, name, color_name, font=font)
max_width = 2600 # 根据实际证书的内容区域来设置最大宽度
wrapped_courses = text_wrap(draw, courses, font_courses, max_width)
y_offset = 0
for line in wrapped_courses:
draw.text((position_courses[0], position_courses[1] + y_offset), line, (0, 0, 0), font=font_courses)
y_offset = y_offset + 180
draw.text(position_number, number, color_name, font=font_number)
draw.text(position_date, date_str, color, font=font_day)
draw.text(position_note, "经考试成绩合格,特发此证。", color, font=font_day)
# draw.text(position_a_m, str(att_date.month), color, font=font_day)
# draw.text(position_a_d, str(att_date.day), color, font=font_day)
draw.text(position_issue_date, issue_date_str, color, font=font_day2)
# draw.text(position_i_y, str(issue_date.year), color, font=font_day)
# draw.text(position_i_m, str(issue_date.month), color, font=font_day)
# draw.text(position_i_d, str(issue_date.day), color, font=font_day)
# 保存图片
path = f"/media/cert/{number}.jpg"
img.save(settings.BASE_DIR + path)
return path
def make_img(number, name, title, font_name_path): def make_img(number, name, title, font_name_path):
# 打开底版图片 # 打开底版图片
imageFile = settings.BASE_DIR + '/media/cert/template/background.jpg' imageFile = settings.BASE_DIR + "/media/cert/template/background.jpg"
img = Image.open(imageFile) img = Image.open(imageFile)
# 选择字体与大小 # 选择字体与大小
font = ImageFont.truetype(settings.BASE_DIR+ font_name_path, 155) font = ImageFont.truetype(settings.BASE_DIR + font_name_path, 155)
font_title = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/siyuan.otf", 76) font_title = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/siyuan.otf", 76)
font_number = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/timesbd.ttf", 58) font_number = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/timesbd.ttf", 58)
# print(font) # print(font)
# 在图片上添加文字 # 在图片上添加文字
# word = """ # word = """
@ -41,9 +121,9 @@ def make_img(number, name, title, font_name_path):
# ldc 2020-07-25 # ldc 2020-07-25
# """ # """
word_number = number #"CTCZL20230001" word_number = number # "CTCZL20230001"
word_name = name #"曹前明" word_name = name # "曹前明"
word_title = title #"内审员、授权签字人、质量负责人、最高管理者" word_title = title # "内审员、授权签字人、质量负责人、最高管理者"
# width = img.width # width = img.width
# height = img.height # height = img.height
# # 查看图片宽高 # # 查看图片宽高
@ -52,12 +132,12 @@ def make_img(number, name, title, font_name_path):
position_name = (628, 860) position_name = (628, 860)
position_title = (900, 1230) position_title = (900, 1230)
color_name = (82, 68, 19) color_name = (82, 68, 19)
color = (0,0,0) color = (0, 0, 0)
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
draw.text(position_name, word_name, color_name, font=font) draw.text(position_name, word_name, color_name, font=font)
draw.text(position_title, word_title, color, font=font_title) draw.text(position_title, word_title, color, font=font_title)
draw.text(position_number, word_number, color_name, font=font_number) draw.text(position_number, word_number, color_name, font=font_number)
# 保存图片 # 保存图片
path = f"/media/cert/{number}.jpg" path = f"/media/cert/{number}.jpg"
img.save(settings.BASE_DIR + path) img.save(settings.BASE_DIR + path)
return path return path

View File

@ -1,7 +1,7 @@
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from apps.system.mixins import CreateUpdateCustomMixin from apps.system.mixins import CreateUpdateCustomMixin
from apps.edu.serializers import CertificateSerializer from apps.edu.serializers import CertificateSerializer, CourseSerializer
from apps.edu.models import Certificate from apps.edu.models import Certificate, Course
from rest_framework.decorators import action from rest_framework.decorators import action
from django.conf import settings from django.conf import settings
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
@ -11,6 +11,14 @@ from rest_framework.serializers import Serializer
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from apps.edu.services import make_img_x from apps.edu.services import make_img_x
# Create your views here. # Create your views here.
class CourseViewSet(CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post': 'course',
'put': 'course', 'delete': 'course'}
queryset = Course.objects.all()
serializer_class = CourseSerializer
search_fields = ['name']
ordering = ['create_time']
class CertificateViewSet(CreateUpdateCustomMixin, ModelViewSet): class CertificateViewSet(CreateUpdateCustomMixin, ModelViewSet):
perms_map = {'get': '*', 'post': 'certificate', perms_map = {'get': '*', 'post': 'certificate',
@ -18,8 +26,8 @@ class CertificateViewSet(CreateUpdateCustomMixin, ModelViewSet):
queryset = Certificate.objects.all() queryset = Certificate.objects.all()
serializer_class = CertificateSerializer serializer_class = CertificateSerializer
search_fields = ['姓名', '证书编号', '所属单位'] search_fields = ['姓名', '证书编号', '所属单位']
filterset_fields = ['是否内审员','是否授权签字人', '是否质量负责人', '是否最高管理者', '姓名', '证书编号', '所属单位', '单位名称'] filterset_fields = ['是否内审员','是否授权签字人', '是否质量负责人', '是否最高管理者', '姓名', '证书编号', '所属单位', '单位名称', '课程列表']
ordering = ['-create_time'] ordering = ['-create_time', '证书编号']
def get_authenticators(self): def get_authenticators(self):
if self.request.method == 'GET': if self.request.method == 'GET':

View File

@ -28,6 +28,9 @@ class AnalyseViewSet(GenericViewSet):
质量目标集团统计 质量目标集团统计
""" """
vdata = self.is_valid(request) vdata = self.is_valid(request)
cycle_str = ''
if vdata['year'] == 2023:
cycle_str = 'AND task2.cycle in (3,4,5)'
sql_str = f"""select task2.year as 年份, sql_str = f"""select task2.year as 年份,
dept.name as 单位, dept.name as 单位,
dict.name as 单位类型, dict.name as 单位类型,
@ -67,6 +70,7 @@ left join supervision_pgoaldept pdept5 on pdept5.goal_key = 'pgoal_5' and pdept5
left join supervision_pgoal pgoal on pgoal.year = 2023 left join supervision_pgoal pgoal on pgoal.year = 2023
left join system_dict dict on dict.id = dept.type_id left join system_dict dict on dict.id = dept.type_id
where task2.year = {vdata['year']} where task2.year = {vdata['year']}
{cycle_str}
and (dept.name = '{vdata['dept_name']}' or '{vdata['dept_name']}'='') and (dept.name = '{vdata['dept_name']}' or '{vdata['dept_name']}'='')
and (dict.name = '{vdata['dept_type_name']}' or '{vdata['dept_type_name']}'='') and (dict.name = '{vdata['dept_type_name']}' or '{vdata['dept_type_name']}'='')
GROUP BY task2.year, dept.id, dict.name, GROUP BY task2.year, dept.id, dict.name,

3
server/ruff.toml Normal file
View File

@ -0,0 +1,3 @@
line-length = 200
fix = true