From 9803292be3a819cee3efda2f493fdcbab7b3b3ba Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Mon, 2 Oct 2023 13:04:27 +0300 Subject: [PATCH] modify work with template --- config.yaml | 4 +- .../service/api/handlers/create_template.go | 73 +++++++++---------- internal/service/api/handlers/get_users.go | 2 +- .../service/api/requests/create_template.go | 4 +- internal/service/core/pdf/container.go | 18 ++++- internal/service/core/pdf/default_data.go | 25 ++++--- internal/service/core/pdf/pdf.go | 61 ++++++++++++---- internal/service/core/pdf/setters.go | 6 +- internal/service/core/pdf/types.go | 5 +- resources/model_resource_type.go | 6 +- 10 files changed, 123 insertions(+), 81 deletions(-) diff --git a/config.yaml b/config.yaml index a64d88a1..04b7061b 100644 --- a/config.yaml +++ b/config.yaml @@ -18,7 +18,7 @@ networks: ipfs_pr_key: "" sbt: - external_url: "https://dlt-academy.com/certificates" + external_url: "https://distributed.education/certificates" google: secret_path: "./client.json" @@ -35,7 +35,7 @@ tables: qr_code: qr_path: "./qr/" - template: "message:\n%s\n\naddress:\n%s\n\nsignature:\n%s\n\ncertificate page:\nhttps://dlt-academy.com/certificates" + template: "message:\n%s\n\naddress:\n%s\n\nsignature:\n%s\n\ncertificate page:\nhttps://distributed.education/certificates" templates: list: diff --git a/internal/service/api/handlers/create_template.go b/internal/service/api/handlers/create_template.go index d3a62b8f..b00c23c7 100644 --- a/internal/service/api/handlers/create_template.go +++ b/internal/service/api/handlers/create_template.go @@ -7,19 +7,18 @@ import ( "gitlab.com/distributed_lab/ape/problems" "gitlab.com/tokend/course-certificates/ccp/internal/data" "gitlab.com/tokend/course-certificates/ccp/internal/service/api/requests" - "gitlab.com/tokend/course-certificates/ccp/internal/service/core/pdf" "gitlab.com/tokend/course-certificates/ccp/resources" "net/http" + "strings" ) func CreateTemplate(w http.ResponseWriter, r *http.Request) { - template, backgroundImg, req, err := requests.NewGenerateTemplate(r) + template, _, req, err := requests.NewGenerateTemplate(r) if err != nil { Log(r).WithError(err).Error("failed to generate template") ape.RenderErr(w, problems.BadRequest(err)...) return } - defaultData := pdf.DefaultData client, err := MasterQ(r).ClientQ().FilterByName(req.Data.Relationships.User).Get() if err != nil { Log(r).WithError(err).Error("failed to get client") @@ -31,36 +30,36 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { ape.RenderErr(w, problems.NotFound()) return } - - if template.Width == 0 || template.High == 0 { - tp := pdf.DefaultTemplateTall - _, _, imgBytes, err := tp.Prepare(defaultData, pdf.NewPDFConfig(Config(r)), MasterQ(r), backgroundImg, client.ID, StaticConfiger(r).Location) - if err != nil { - Log(r).WithError(err).Error("failed to prepare pdf") - ape.RenderErr(w, problems.InternalError()) - return - } - ape.Render(w, newTemplateImageResp(imgBytes)) - return - } - - file := pdf.NewPDF(template.High, template.Width) - - file.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font) - file.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font) - file.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font) - file.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font) - file.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font) - file.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font) - file.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font) - file.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font) - file.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.High, template.Width) - _, _, imgBytes, err := template.Prepare(defaultData, pdf.NewPDFConfig(Config(r)), MasterQ(r), backgroundImg, client.ID, StaticConfiger(r).Location) - if err != nil { - Log(r).WithError(err).Error("failed to prepare pdf") - ape.RenderErr(w, problems.InternalError()) - return - } + // + //if template.Width == 0 || template.Height == 0 { + // tp := pdf.DefaultTemplateTall + // _, _, imgBytes, err := tp.Prepare(defaultData, pdf.NewPDFConfig(Config(r)), MasterQ(r), backgroundImg, client.ID, StaticConfiger(r).Location) + // if err != nil { + // Log(r).WithError(err).Error("failed to prepare pdf") + // ape.RenderErr(w, problems.InternalError()) + // return + // } + // ape.Render(w, newTemplateImageResp(imgBytes)) + // return + //} + // + //file := pdf.NewPDF(template.Height, template.Width) + // + //file.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font) + //file.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font) + //file.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font) + //file.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font) + //file.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font) + //file.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font) + //file.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font) + //file.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font) + //file.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.Height, template.Width) + //_, _, imgBytes, err := template.Prepare(defaultData, pdf.NewPDFConfig(Config(r)), MasterQ(r), backgroundImg, client.ID, StaticConfiger(r).Location) + //if err != nil { + // Log(r).WithError(err).Error("failed to prepare pdf") + // ape.RenderErr(w, problems.InternalError()) + // return + //} if req.Data.Attributes.IsCompleted { templateBytes, err := json.Marshal(template) if err != nil { @@ -71,9 +70,9 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { err = MasterQ(r).TemplateQ().Insert(&data.Template{ Template: templateBytes, - //ImgBytes: backgroundImg, - Name: req.Data.Attributes.TemplateName, - UserID: client.ID, + ImgBytes: []byte(strings.Replace(req.Data.Attributes.BackgroundImg, "data:image/png;base64,", "", 1)), + Name: req.Data.Attributes.TemplateName, + UserID: client.ID, }) if err != nil { Log(r).WithError(err).Error("failed to insert template") @@ -81,7 +80,7 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { return } } - ape.Render(w, newTemplateImageResp(imgBytes)) + w.WriteHeader(http.StatusNoContent) return } diff --git a/internal/service/api/handlers/get_users.go b/internal/service/api/handlers/get_users.go index 66b45a36..768f1f8d 100644 --- a/internal/service/api/handlers/get_users.go +++ b/internal/service/api/handlers/get_users.go @@ -61,7 +61,7 @@ func GetUsers(w http.ResponseWriter, r *http.Request) { } Log(r).Error("failed to parse table: Errors:", errs) - ape.RenderErr(w, problems.BadRequest(err)...) + ape.RenderErr(w, problems.Unauthorized()) return } diff --git a/internal/service/api/requests/create_template.go b/internal/service/api/requests/create_template.go index 864635e9..5a6a281a 100644 --- a/internal/service/api/requests/create_template.go +++ b/internal/service/api/requests/create_template.go @@ -9,7 +9,7 @@ import ( "gitlab.com/tokend/course-certificates/ccp/internal/service/core/pdf" "gitlab.com/tokend/course-certificates/ccp/resources" "image" - "image/jpeg" + "image/png" "net/http" "strings" ) @@ -52,7 +52,7 @@ func base64toJpg(data string) ([]byte, error) { } buf := new(bytes.Buffer) - if err = jpeg.Encode(buf, m, &jpeg.Options{Quality: 75}); err != nil { + if err = png.Encode(buf, m); err != nil { return nil, err } diff --git a/internal/service/core/pdf/container.go b/internal/service/core/pdf/container.go index 39553f82..10f5238d 100644 --- a/internal/service/core/pdf/container.go +++ b/internal/service/core/pdf/container.go @@ -53,9 +53,14 @@ func (c *Container) Generate() error { files = append(files, google.FilesBytes{File: file, Name: name, ID: user.ID, Type: "image/svg+xml"}) pdf := PDF{} - certificate := pdf.SetTemplateData(DefaultTemplateTall) + certificateTemplate, err := pdf.InitTemplate(c.masterQ, user.CourseTitle, c.owner.ID) + if err != nil { + return errors.Wrap(err, "failed to get template") + } + + certificate := pdf.SetTemplateData(*certificateTemplate) - pdfData := NewData(user.Participant, user.CourseTitle, "45 hours / 1.5 ECTS Credit", user.Points, user.SerialNumber, user.Date, img, user.Note, "", "") + pdfData := NewData(user.Participant, user.CourseTitle, certificateTemplate.Credits.Text, user.Points, user.SerialNumber, user.Date, img, user.Note, "", "") fileBytes, name, certificateImg, err := certificate.Prepare(pdfData, NewPDFConfig(c.config), c.masterQ, nil, c.owner.ID, c.config.StaticConfig().Location) if err != nil { return errors.Wrap(err, "failed to create pdf") @@ -105,9 +110,14 @@ func (c *Container) Update() error { files = append(files, google.FilesBytes{File: file, Name: name, ID: user.ID, Type: "image/svg+xml"}) pdf := PDF{} - certificate := pdf.SetTemplateData(DefaultTemplateTall) + certificateTemplate, err := pdf.InitTemplate(c.masterQ, user.CourseTitle, c.owner.ID) + if err != nil { + return errors.Wrap(err, "failed to get template") + } + + certificate := pdf.SetTemplateData(*certificateTemplate) - pdfData := NewData(user.Participant, user.CourseTitle, "45 hours / 1.5 ECTS Credit", user.Points, user.SerialNumber, user.Date, img, user.Note, "", "") + pdfData := NewData(user.Participant, user.CourseTitle, certificateTemplate.Credits.Text, user.Points, user.SerialNumber, user.Date, img, user.Note, "", "") fileBytes, name, certificateImg, err := certificate.Prepare(pdfData, NewPDFConfig(c.config), c.masterQ, nil, c.owner.ID, c.config.StaticConfig().Location) if err != nil { return errors.Wrap(err, "failed to create pdf") diff --git a/internal/service/core/pdf/default_data.go b/internal/service/core/pdf/default_data.go index 6891f23e..fc53c22f 100644 --- a/internal/service/core/pdf/default_data.go +++ b/internal/service/core/pdf/default_data.go @@ -1,8 +1,8 @@ package pdf var DefaultTemplateNormal = PDF{ - High: 595, - Width: 842, + Height: 595, + Width: 842, Name: Field{ X: 200, Y: 217, @@ -40,10 +40,10 @@ var DefaultTemplateNormal = PDF{ Font: "regular", }, QR: Field{ - X: 658, - Y: 106, - High: 114, - Width: 114, + X: 658, + Y: 106, + Height: 114, + Width: 114, }, Exam: Field{ X: 300, @@ -60,8 +60,8 @@ var DefaultTemplateNormal = PDF{ } var DefaultTemplateTall = PDF{ - High: 1190, - Width: 1684, + Height: 1190, + Width: 1684, Name: Field{ Y: 434, FontSize: 56, @@ -77,6 +77,7 @@ var DefaultTemplateTall = PDF{ Y: 112, FontSize: 24, Font: "regular", + Text: "45 hours / 1.5 ECTS Credit", }, Points: Field{ X: 140, @@ -99,10 +100,10 @@ var DefaultTemplateTall = PDF{ Font: "regular", }, QR: Field{ - X: 1316, - Y: 212, - High: 228, - Width: 228, + X: 1316, + Y: 212, + Height: 228, + Width: 228, }, Exam: Field{ Y: 600, diff --git a/internal/service/core/pdf/pdf.go b/internal/service/core/pdf/pdf.go index c145b258..b0ecda21 100644 --- a/internal/service/core/pdf/pdf.go +++ b/internal/service/core/pdf/pdf.go @@ -2,6 +2,7 @@ package pdf import ( "bytes" + "encoding/json" "fmt" "github.com/signintech/gopdf" "gitlab.com/distributed_lab/logan/v3/errors" @@ -14,7 +15,7 @@ import ( func (p *PDF) Prepare(data PDFData, config *PDFConfig, masterQ data.MasterQ, backgroundImg []byte, userID int64, abs string) ([]byte, string, []byte, error) { pdf := new(gopdf.GoPdf) - pdf.Start(gopdf.Config{PageSize: gopdf.Rect{W: p.Width, H: p.High}}) + pdf.Start(gopdf.Config{PageSize: gopdf.Rect{W: p.Width, H: p.Height}}) pdf.AddPage() pdf.SetTextColor(255, 255, 255) @@ -24,6 +25,9 @@ func (p *PDF) Prepare(data PDFData, config *PDFConfig, masterQ data.MasterQ, bac templateImg := config.templates[data.Course] + if templateImg == "" { + templateImg = data.Course + } if backgroundImg == nil { if err := p.initBackground(pdf, masterQ.TemplateQ(), templateImg, abs, userID); err != nil { return nil, "", nil, errors.Wrap(err, "failed to init background") @@ -94,7 +98,7 @@ func (p *PDF) setBackground(pdf *gopdf.GoPdf, image []byte) error { return errors.Wrap(err, "failed to prepare background") } - err = pdf.ImageByHolder(backgroundImgHolder, 0, 0, &gopdf.Rect{W: p.Width, H: p.High}) + err = pdf.ImageByHolder(backgroundImgHolder, 0, 0, &gopdf.Rect{W: p.Width, H: p.Height}) if err != nil { return errors.Wrap(err, "failed to set background") } @@ -121,7 +125,7 @@ func (p *PDF) setLevel(pdf *gopdf.GoPdf, level string) error { } pdf.SetX(0) pdf.SetY(p.Level.Y) - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.High}, level, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, level, gopdf.CellOption{Align: gopdf.Center}); err != nil { return errors.Wrap(err, "failed to cell Level") } @@ -135,7 +139,7 @@ func (p *PDF) setExam(pdf *gopdf.GoPdf, exam string) error { pdf.SetX(0) pdf.SetY(p.Exam.Y) - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.High}, exam, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, exam, gopdf.CellOption{Align: gopdf.Center}); err != nil { return errors.Wrap(err, "failed to cell Exam") } @@ -148,7 +152,7 @@ func (p *PDF) setQR(pdf *gopdf.GoPdf, qr []byte) error { return errors.Wrap(err, "failed to convert bytes to image QR") } - err = pdf.ImageFrom(img, p.QR.X, p.QR.Y, &gopdf.Rect{W: p.QR.High, H: p.QR.High}) + err = pdf.ImageFrom(img, p.QR.X, p.QR.Y, &gopdf.Rect{W: p.QR.Width, H: p.QR.Height}) if err != nil { return errors.Wrap(err, "failed to set image QR") } @@ -156,14 +160,17 @@ func (p *PDF) setQR(pdf *gopdf.GoPdf, qr []byte) error { return nil } -func (p *PDF) setCourse(pdf *gopdf.GoPdf, courseTitle string) error { +func (p *PDF) setCourse(pdf *gopdf.GoPdf, courseTitle string, templateImg string) error { if err := pdf.SetFont("italic", "", p.Course.FontSize); err != nil { return errors.Wrap(err, "failed to set font Course") } pdf.SetX(0) pdf.SetY(p.Course.Y) + if courseTitle == "" { + courseTitle = templateImg + } - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.High}, courseTitle, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, courseTitle, gopdf.CellOption{Align: gopdf.Center}); err != nil { return errors.Wrap(err, "failed to cell Course") } @@ -192,7 +199,7 @@ func (p *PDF) setPoints(pdf *gopdf.GoPdf, points string) error { } pdf.SetX(p.Points.X) pdf.SetY(p.Points.Y) - if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.High}, fmt.Sprintf("Count of points: %s", points)); err != nil { + if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.Height}, fmt.Sprintf("Count of points: %s", points)); err != nil { return errors.Wrap(err, "failed to cell points") } @@ -219,7 +226,7 @@ func (p *PDF) setCredits(pdf *gopdf.GoPdf, credits string) error { pdf.SetX(p.Credits.X) pdf.SetY(p.Credits.Y) - if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.High}, fmt.Sprintf(credits)); err != nil { + if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.Height}, fmt.Sprintf(credits)); err != nil { return errors.Wrap(err, "failed to cell credits") } @@ -232,7 +239,7 @@ func (p *PDF) setName(pdf *gopdf.GoPdf, name string) error { } pdf.SetY(p.Name.Y) pdf.SetX(0) - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.High}, name, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, name, gopdf.CellOption{Align: gopdf.Center}); err != nil { return errors.Wrap(err, "failed to cell name") } @@ -268,7 +275,7 @@ func (p *PDF) prepareName(name, course string) string { } func (p *PDF) SetTemplateData(template PDF) *PDF { - certificate := NewPDF(template.High, template.Width) + certificate := NewPDF(template.Height, template.Width) certificate.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font) certificate.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font) certificate.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font) @@ -277,11 +284,39 @@ func (p *PDF) SetTemplateData(template PDF) *PDF { certificate.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font) certificate.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font) certificate.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font) - certificate.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.High, template.Width) + certificate.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.Height, template.QR.Width) return certificate } +func (p *PDF) InitTemplate(masterQ data.MasterQ, templateName string, userID int64) (*PDF, error) { + template, err := masterQ.TemplateQ().FilterByName(templateName).FilterByUser(userID).Get() + if err != nil { + return nil, errors.Wrap(err, "failed to get template data") + } + if template.Template == nil { + return &DefaultTemplateTall, nil + } + + pdf, err := p.templateDecoder(template.Template) + if err != nil { + return nil, errors.Wrap(err, "failed to decode template") + } + + return pdf, nil + +} + +func (p *PDF) templateDecoder(templateBytes []byte) (*PDF, error) { + pdf := new(PDF) + err := json.Unmarshal(templateBytes, pdf) + if err != nil { + return nil, errors.Wrap(err, "failed to decode template") + } + + return pdf, nil +} + func (p *PDF) CellAllPdfFields(pdf *gopdf.GoPdf, data PDFData, config *PDFConfig, templateImg string) error { if err := p.setName(pdf, data.Name); err != nil { return errors.Wrap(err, "failed to set name") @@ -304,7 +339,7 @@ func (p *PDF) CellAllPdfFields(pdf *gopdf.GoPdf, data PDFData, config *PDFConfig } isLevel, title, level := p.checkLevel(config.titles[templateImg]) - if err := p.setCourse(pdf, title); err != nil { + if err := p.setCourse(pdf, title, templateImg); err != nil { return errors.Wrap(err, "failed to set course") } diff --git a/internal/service/core/pdf/setters.go b/internal/service/core/pdf/setters.go index 2273c4a2..e1ba9e14 100644 --- a/internal/service/core/pdf/setters.go +++ b/internal/service/core/pdf/setters.go @@ -2,8 +2,8 @@ package pdf func NewPDF(high, width float64) *PDF { return &PDF{ - High: high, - Width: width, + Height: high, + Width: width, } } @@ -102,7 +102,7 @@ func (p *PDF) SetQR(x, y float64, size int, high, width float64) { X: x, Y: y, FontSize: size, - High: high, + Height: high, Width: width, } diff --git a/internal/service/core/pdf/types.go b/internal/service/core/pdf/types.go index 1d354333..b391a045 100644 --- a/internal/service/core/pdf/types.go +++ b/internal/service/core/pdf/types.go @@ -6,7 +6,7 @@ const ( ) type PDF struct { - High float64 `json:"high"` + Height float64 `json:"height"` Width float64 `json:"width"` Name Field `json:"name"` Course Field `json:"course"` @@ -28,8 +28,9 @@ type Field struct { FontSize int `json:"font_size"` Color string `json:"color"` Font string `json:"font"` - High float64 `json:"high"` + Height float64 `json:"height"` Width float64 `json:"width"` + Text string `json:"text"` } type PDFData struct { diff --git a/resources/model_resource_type.go b/resources/model_resource_type.go index bda12f2d..13948a59 100644 --- a/resources/model_resource_type.go +++ b/resources/model_resource_type.go @@ -9,6 +9,7 @@ type ResourceType string // List of ResourceType const ( CONTAINER ResourceType = "container" + EXPIRED_TOKEN ResourceType = "expired_token" IPFS_FILE ResourceType = "ipfs_file" IPFS_FILE_UPLOAD ResourceType = "ipfs_file_upload" LINK ResourceType = "link" @@ -17,9 +18,4 @@ const ( SETTINGS ResourceType = "settings" TEMPLATE ResourceType = "template" USER ResourceType = "user" - EXPIRED_TOKEN ResourceType = "expired_token" - IPFS ResourceType = "ipfs" - LINK ResourceType = "link" - TEMPLATE ResourceType = "template" - USER ResourceType = "user" )