feat: 新版证书发放初步完成
This commit is contained in:
parent
a4c5058a26
commit
301130d4bf
|
@ -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)
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,13 +1,18 @@
|
||||||
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 +21,7 @@ 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.CharField(max_length=100, null=True, blank=True)
|
证书地址 = models.CharField(max_length=100, null=True, blank=True)
|
||||||
字体方案 = models.PositiveSmallIntegerField(default=1)
|
|
||||||
|
|
|
@ -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):
|
||||||
|
@ -11,4 +11,11 @@ class CertificateSerializer(serializers.ModelSerializer):
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
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']
|
|
@ -1,6 +1,7 @@
|
||||||
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
|
||||||
|
from datetime import date
|
||||||
font_dict = {
|
font_dict = {
|
||||||
'font_1': "/media/cert/template/weibei.ttf",
|
'font_1': "/media/cert/template/weibei.ttf",
|
||||||
'font_2': "/media/cert/template/SIMLI.TTF"
|
'font_2': "/media/cert/template/SIMLI.TTF"
|
||||||
|
@ -8,17 +9,78 @@ font_dict = {
|
||||||
|
|
||||||
|
|
||||||
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.发证日期, 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, 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, 155)
|
||||||
|
font_courses = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/simkai.ttf", 100)
|
||||||
|
font_number = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/timesbd.ttf", 58)
|
||||||
|
font_day = ImageFont.truetype(settings.BASE_DIR + "/media/cert/template/Dengl.ttf", 68)
|
||||||
|
position_number = (1730, 606)
|
||||||
|
position_name = (720, 840)
|
||||||
|
position_courses = (490, 1070)
|
||||||
|
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 = 2800 # 根据实际证书的内容区域来设置最大宽度
|
||||||
|
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_a_y, str(att_date.year), 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_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):
|
||||||
# 打开底版图片
|
# 打开底版图片
|
||||||
|
|
|
@ -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':
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
line-length = 200
|
||||||
|
fix = true
|
Loading…
Reference in New Issue