From 1e51821f31c5dfbdf7ff8cef183d7fd3af44ce89 Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Mon, 8 May 2023 14:49:36 +0300 Subject: [PATCH 1/9] return error if token is expired --- .../components/schemas/ExpiredTokenError.yaml | 14 +++++++ .../components/schemas/ExpiredTokenKey.yaml | 9 ++++ docs/web_deploy/openapi.json | 42 +++++++++++++++++++ docs/web_deploy/openapi.yaml | 25 +++++++++++ internal/service/google/google_speets.go | 19 ++++++--- internal/service/handlers/get_empty_users.go | 18 +++++++- internal/service/handlers/get_users.go | 10 ++++- internal/service/handlers/set_settings.go | 7 ++++ internal/service/router.go | 3 +- resources/model_expired_token_error.go | 31 ++++++++++++++ .../model_expired_token_error_attributes.go | 9 ++++ resources/model_pdfs_create_request.go | 8 ++-- resources/model_resource_type.go | 9 ++-- 13 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 docs/spec/components/schemas/ExpiredTokenError.yaml create mode 100644 docs/spec/components/schemas/ExpiredTokenKey.yaml create mode 100644 resources/model_expired_token_error.go create mode 100644 resources/model_expired_token_error_attributes.go diff --git a/docs/spec/components/schemas/ExpiredTokenError.yaml b/docs/spec/components/schemas/ExpiredTokenError.yaml new file mode 100644 index 00000000..f5300d92 --- /dev/null +++ b/docs/spec/components/schemas/ExpiredTokenError.yaml @@ -0,0 +1,14 @@ +allOf: + - $ref: '#/components/schemas/ExpiredTokenKey' + - type: object + required: + - attributes + properties: + attributes: + type: object + required: + - error + properties: + error: + type: boolean + example: false \ No newline at end of file diff --git a/docs/spec/components/schemas/ExpiredTokenKey.yaml b/docs/spec/components/schemas/ExpiredTokenKey.yaml new file mode 100644 index 00000000..4ecf31be --- /dev/null +++ b/docs/spec/components/schemas/ExpiredTokenKey.yaml @@ -0,0 +1,9 @@ +type: object +required: + - type + - id +properties: + type: + type: string + enum: + - expired_token \ No newline at end of file diff --git a/docs/web_deploy/openapi.json b/docs/web_deploy/openapi.json index 94e706b3..c0871b06 100644 --- a/docs/web_deploy/openapi.json +++ b/docs/web_deploy/openapi.json @@ -429,6 +429,48 @@ } } }, + "ExpiredTokenError": { + "allOf": [ + { + "$ref": "#/components/schemas/ExpiredTokenKey" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "boolean", + "example": false + } + } + } + } + } + ] + }, + "ExpiredTokenKey": { + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "expired_token" + ] + } + } + }, "IpfsFile": { "allOf": [ { diff --git a/docs/web_deploy/openapi.yaml b/docs/web_deploy/openapi.yaml index c829cb3f..9be52a6e 100644 --- a/docs/web_deploy/openapi.yaml +++ b/docs/web_deploy/openapi.yaml @@ -284,6 +284,31 @@ components: enum: - session_token_not_found - session_token_invalid + ExpiredTokenError: + allOf: + - $ref: '#/components/schemas/ExpiredTokenKey' + - type: object + required: + - attributes + properties: + attributes: + type: object + required: + - error + properties: + error: + type: boolean + example: false + ExpiredTokenKey: + type: object + required: + - type + - id + properties: + type: + type: string + enum: + - expired_token IpfsFile: allOf: - $ref: '#/components/schemas/IpfsFile' diff --git a/internal/service/google/google_speets.go b/internal/service/google/google_speets.go index ae5870ea..940402d9 100644 --- a/internal/service/google/google_speets.go +++ b/internal/service/google/google_speets.go @@ -8,6 +8,7 @@ import ( "google.golang.org/api/sheets/v4" "log" "reflect" + "strings" "time" ) @@ -26,24 +27,25 @@ func (g *Google) GetTable(readRange, spreadsheetId string) error { return nil } -func (g *Google) ParseFromWeb(spreadsheetId, readRange string, log *logan.Entry) ([]*helpers.User, []error) { +func (g *Google) ParseFromWeb(spreadsheetId, readRange string, log *logan.Entry) ([]*helpers.User, []error, bool) { errs := make([]error, 0) resp, err := g.sheetSrv.Spreadsheets.Values.Get(spreadsheetId, readRange).Do() if err != nil { log.Info("Unable to retrieve data from sheet: %v", err) errs = append(errs, err) - return nil, errs + return nil, errs, strings.Contains(err.Error(), "invalid_grant") + } if len(resp.Values) == 0 { - return nil, nil + return nil, nil, false } else { log.Info(resp.Values) } users := make([]*helpers.User, 0) if err != nil { - return nil, append(errs, errors.Wrap(err, "failed to open file")) + return nil, append(errs, errors.Wrap(err, "failed to open file")), false } for id, row := range resp.Values { @@ -60,9 +62,9 @@ func (g *Google) ParseFromWeb(spreadsheetId, readRange string, log *logan.Entry) users = append(users, userInfo) } if len(errs) != 0 { - return nil, append(errs, errors.Wrap(err, "failed to parse xlsx")) + return nil, append(errs, errors.Wrap(err, "failed to parse xlsx")), false } - return users, nil + return users, nil, false } func (g *Google) SetRes(users []*helpers.User, sheetID string) []error { @@ -104,3 +106,8 @@ func stringToInterface(strs []string) []interface{} { } return res } + +type TokenError struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description"` +} diff --git a/internal/service/handlers/get_empty_users.go b/internal/service/handlers/get_empty_users.go index 9af613b4..2c0deeec 100644 --- a/internal/service/handlers/get_empty_users.go +++ b/internal/service/handlers/get_empty_users.go @@ -36,12 +36,18 @@ func GetUsersEmpty(w http.ResponseWriter, r *http.Request) { return } - users, errs := client.ParseFromWeb(req.Data.Url, "A1:K", helpers.Config(r).Log()) + users, errs, isTokenExpired := client.ParseFromWeb(req.Data.Url, "A1:K", helpers.Config(r).Log()) if errs != nil { helpers.Log(r).Error("failed to parse table: Errors:", errs) ape.Render(w, problems.BadRequest(err)) return } + if isTokenExpired { + helpers.Log(r).Error("failed to connect: token expired") + w.WriteHeader(http.StatusForbidden) + ape.Render(w, newTokenExpired()) + return + } emptyUsers := make([]*helpers.User, 0) for id, user := range users { user.ID = id @@ -85,5 +91,13 @@ func newUserResponse(users []*helpers.User) resources.UserListResponse { return resources.UserListResponse{ Data: usersData, } - +} +func newTokenExpired() resources.ExpiredTokenErrorResponse { + return resources.ExpiredTokenErrorResponse{ + Data: resources.ExpiredTokenError{ + Attributes: resources.ExpiredTokenErrorAttributes{ + Error: true, + }, + }, + } } diff --git a/internal/service/handlers/get_users.go b/internal/service/handlers/get_users.go index 5cf68b09..b4939f21 100644 --- a/internal/service/handlers/get_users.go +++ b/internal/service/handlers/get_users.go @@ -35,12 +35,20 @@ func GetUsers(w http.ResponseWriter, r *http.Request) { return } - users, errs := client.ParseFromWeb(req.Data.Url, "A1:K", helpers.Config(r).Log()) + users, errs, isTokenExpired := client.ParseFromWeb(req.Data.Url, "A1:K", helpers.Config(r).Log()) //todo revoke token if errs != nil { helpers.Log(r).Error("failed to parse table: Errors:", errs) ape.Render(w, problems.BadRequest(err)) return } + + if isTokenExpired { + helpers.Log(r).Error("failed to connect: token expired") + w.WriteHeader(http.StatusForbidden) + ape.Render(w, newTokenExpired()) + return + } + readyUsers := make([]*helpers.User, 0) for id, user := range users { user.ID = id diff --git a/internal/service/handlers/set_settings.go b/internal/service/handlers/set_settings.go index 65d2f1bb..6aaa70e2 100644 --- a/internal/service/handlers/set_settings.go +++ b/internal/service/handlers/set_settings.go @@ -46,6 +46,13 @@ func SetSettings(w http.ResponseWriter, r *http.Request) { return } if req.Data.Code != "" { + settings.Token = nil + err = helpers.ClientQ(r).Update(settings) + if err != nil { + helpers.Log(r).WithError(err).Error("failed to update settings") + ape.Render(w, problems.InternalError()) + return + } client := google.NewGoogleClient(helpers.Config(r)) _, err = client.Connect(helpers.Config(r).Google().SecretPath, helpers.ClientQ(r), req.Data.Name) if err != nil { diff --git a/internal/service/router.go b/internal/service/router.go index 75ce4f09..4221a7c2 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -30,11 +30,12 @@ func (s *service) router(cfg config.Config) chi.Router { r.Post("/", handlers.GetUsers) r.Post("/empty", handlers.GetUsersEmpty) r.Post("/settings", handlers.SetSettings) + }) r.Route("/certificate", func(r chi.Router) { r.Post("/", handlers.PrepareCertificate) r.Post("/template", handlers.CreateTemplate) - r.Post("/bitcoin", handlers.UpdateCertificate) + r.Put("/", handlers.UpdateCertificate) r.Post("/ipfs", handlers.UploadFileToIpfs) }) }) diff --git a/resources/model_expired_token_error.go b/resources/model_expired_token_error.go new file mode 100644 index 00000000..c7cf7a95 --- /dev/null +++ b/resources/model_expired_token_error.go @@ -0,0 +1,31 @@ +/* + * GENERATED. Do not modify. Your changes might be overwritten! + */ + +package resources + +type ExpiredTokenError struct { + Key + Attributes ExpiredTokenErrorAttributes `json:"attributes"` +} +type ExpiredTokenErrorResponse struct { + Data ExpiredTokenError `json:"data"` + Included Included `json:"included"` +} + +type ExpiredTokenErrorListResponse struct { + Data []ExpiredTokenError `json:"data"` + Included Included `json:"included"` + Links *Links `json:"links"` +} + +// MustExpiredTokenError - returns ExpiredTokenError from include collection. +// if entry with specified key does not exist - returns nil +// if entry with specified key exists but type or ID mismatches - panics +func (c *Included) MustExpiredTokenError(key Key) *ExpiredTokenError { + var expiredTokenError ExpiredTokenError + if c.tryFindEntry(key, &expiredTokenError) { + return &expiredTokenError + } + return nil +} diff --git a/resources/model_expired_token_error_attributes.go b/resources/model_expired_token_error_attributes.go new file mode 100644 index 00000000..23233b3c --- /dev/null +++ b/resources/model_expired_token_error_attributes.go @@ -0,0 +1,9 @@ +/* + * GENERATED. Do not modify. Your changes might be overwritten! + */ + +package resources + +type ExpiredTokenErrorAttributes struct { + Error bool `json:"error"` +} diff --git a/resources/model_pdfs_create_request.go b/resources/model_pdfs_create_request.go index d7aabf26..72456d24 100644 --- a/resources/model_pdfs_create_request.go +++ b/resources/model_pdfs_create_request.go @@ -5,8 +5,8 @@ package resources type PdfsCreateRequest struct { - Address string `json:"address"` - Data []User `json:"data"` - Name string `json:"name"` - Url string `json:"url"` + Address string `json:"address"` + Data []AnyOfUser `json:"data"` + Name string `json:"name"` + Url string `json:"url"` } diff --git a/resources/model_resource_type.go b/resources/model_resource_type.go index 4c200ce6..779d287f 100644 --- a/resources/model_resource_type.go +++ b/resources/model_resource_type.go @@ -8,8 +8,9 @@ type ResourceType string // List of ResourceType const ( - IPFS ResourceType = "ipfs" - LINK ResourceType = "link" - TEMPLATE ResourceType = "template" - USER ResourceType = "user" + EXPIRED_TOKEN ResourceType = "expired_token" + IPFS ResourceType = "ipfs" + LINK ResourceType = "link" + TEMPLATE ResourceType = "template" + USER ResourceType = "user" ) From 13f142e13fc6fa61ec01960f7d9aee697714badd Mon Sep 17 00:00:00 2001 From: MarkCherepovskyi <88227619+MarkCherepovskyi@users.noreply.github.com> Date: Tue, 9 May 2023 13:30:46 +0300 Subject: [PATCH 2/9] Create README.md --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..4825c655 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# qid-kyc-service + +## Install + + ``` + git clone https://github.com/Digital-Certificates-DL/certificates-svc + cd certificates-svc + go build main.go + export KV_VIPER_FILE=./config.yaml + ./main migrate up + ./main run service + ``` + +## Documentation + +We do use openapi:json standard for API. We use swagger for documenting our API. + +To open online documentation, go to [swagger editor](http://localhost:8080/swagger-editor/) here is how you can start it +``` + cd docs + npm install + npm start +``` +To build documentation use `npm run build` command, +that will create open-api documentation in `web_deploy` folder. + +To generate resources for Go models run `./generate.sh` script in root folder. +use `./generate.sh --help` to see all available options. + + + +## Running from Source + +* Set up environment value with config file path `KV_VIPER_FILE=./config.yaml` +* Provide valid config file +* Launch the service with `migrate up` command to create database schema +* Launch the service with `run service` command + + +### Database +For services, we do use ***PostgresSQL*** database. +You can [install it locally](https://www.postgresql.org/download/) or use [docker image](https://hub.docker.com/_/postgres/). + + From f79cb560a4a12c7c518f0ea2d224634ccc9ca627 Mon Sep 17 00:00:00 2001 From: MarkCherepovskyi <88227619+MarkCherepovskyi@users.noreply.github.com> Date: Tue, 9 May 2023 13:31:27 +0300 Subject: [PATCH 3/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4825c655..1e46c457 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# qid-kyc-service +# certificates-svc ## Install From 9803292be3a819cee3efda2f493fdcbab7b3b3ba Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Mon, 2 Oct 2023 13:04:27 +0300 Subject: [PATCH 4/9] 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" ) From 01951c9b4db6f557b7f492d9ae45f4f3ce21e50d Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Tue, 3 Oct 2023 11:16:39 +0300 Subject: [PATCH 5/9] update db --- docs/spec/components/schemas/Template.yaml | 3 + internal/assets/migrations/001_initial.sql | 3 +- internal/data/pg/template.go | 7 ++ internal/data/template.go | 12 +- .../service/api/handlers/create_template.go | 9 +- internal/service/api/requests/ipfs_upload.go | 3 - internal/service/core/pdf/pdf.go | 104 ++++++++++++++++-- internal/service/core/pdf/setters.go | 23 ++-- resources/model_template_attributes.go | 9 +- 9 files changed, 139 insertions(+), 34 deletions(-) diff --git a/docs/spec/components/schemas/Template.yaml b/docs/spec/components/schemas/Template.yaml index c141b934..8733d525 100644 --- a/docs/spec/components/schemas/Template.yaml +++ b/docs/spec/components/schemas/Template.yaml @@ -19,6 +19,7 @@ allOf: - background_img - is_completed - template_name + - template_short_name properties: template: type: object @@ -29,4 +30,6 @@ allOf: type: boolean template_name: type: string + template_short_name: + type: string diff --git a/internal/assets/migrations/001_initial.sql b/internal/assets/migrations/001_initial.sql index 9b4bb962..0025feb8 100644 --- a/internal/assets/migrations/001_initial.sql +++ b/internal/assets/migrations/001_initial.sql @@ -11,7 +11,8 @@ CREATE TABLE template ( user_id INTEGER REFERENCES users(id), template TEXT, img_bytes text, - name TEXT + name TEXT, + short_name TEXT ); -- +migrate Down diff --git a/internal/data/pg/template.go b/internal/data/pg/template.go index 0bd3c4a2..efe12c8b 100644 --- a/internal/data/pg/template.go +++ b/internal/data/pg/template.go @@ -104,3 +104,10 @@ func (q *TemplateQ) FilterByName(name string) data.TemplateQ { return q } + +func (q *TemplateQ) FilterByShortName(name string) data.TemplateQ { + q.sql = q.sql.Where(sq.Eq{nameField: name}) + q.upd = q.upd.Where(sq.Eq{nameField: name}) + + return q +} diff --git a/internal/data/template.go b/internal/data/template.go index 2b12a8d1..131f3469 100644 --- a/internal/data/template.go +++ b/internal/data/template.go @@ -8,13 +8,15 @@ type TemplateQ interface { Select() ([]Template, error) FilterByUser(ids int64) TemplateQ FilterByName(name string) TemplateQ + FilterByShortName(name string) TemplateQ FilterByID(ids int64) TemplateQ } type Template struct { - ID int64 `db:"id" structs:"-"` - UserID int64 `db:"user_id" structs:"user_id"` - Name string `db:"name" structs:"name"` - Template []byte `db:"template" structs:"template"` - ImgBytes []byte `db:"img_bytes" structs:"img_bytes"` //todo make better + ID int64 `db:"id" structs:"-"` + UserID int64 `db:"user_id" structs:"user_id"` + Name string `db:"name" structs:"name"` + ShortName string `db:"short_name" structs:"short_name"` + Template []byte `db:"template" structs:"template"` + ImgBytes []byte `db:"img_bytes" structs:"img_bytes"` //todo make better } diff --git a/internal/service/api/handlers/create_template.go b/internal/service/api/handlers/create_template.go index b00c23c7..a85c4ae7 100644 --- a/internal/service/api/handlers/create_template.go +++ b/internal/service/api/handlers/create_template.go @@ -69,10 +69,11 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { } err = MasterQ(r).TemplateQ().Insert(&data.Template{ - Template: templateBytes, - ImgBytes: []byte(strings.Replace(req.Data.Attributes.BackgroundImg, "data:image/png;base64,", "", 1)), - Name: req.Data.Attributes.TemplateName, - UserID: client.ID, + Template: templateBytes, + ImgBytes: []byte(strings.Replace(req.Data.Attributes.BackgroundImg, "data:image/png;base64,", "", 1)), + Name: req.Data.Attributes.TemplateName, + ShortName: req.Data.Attributes.TemplateShortName, + UserID: client.ID, }) if err != nil { Log(r).WithError(err).Error("failed to insert template") diff --git a/internal/service/api/requests/ipfs_upload.go b/internal/service/api/requests/ipfs_upload.go index dbaf6796..da0467af 100644 --- a/internal/service/api/requests/ipfs_upload.go +++ b/internal/service/api/requests/ipfs_upload.go @@ -6,7 +6,6 @@ import ( "github.com/pkg/errors" "gitlab.com/tokend/course-certificates/ccp/resources" "net/http" - "regexp" ) type IpfsFileUpload struct { @@ -32,7 +31,5 @@ func validateIpfsData(request resources.IpfsFileUpload) error { validation.Required), "/attributes/img": validation.Validate(request.Attributes.Img, validation.Required), - "/attributes/name": validation.Validate(request.Attributes.Name, - validation.Required, validation.Match(regexp.MustCompile("^([A-Za-z])[A-Za-z\\s]+$"))), }).Filter() } diff --git a/internal/service/core/pdf/pdf.go b/internal/service/core/pdf/pdf.go index b0ecda21..c6a12882 100644 --- a/internal/service/core/pdf/pdf.go +++ b/internal/service/core/pdf/pdf.go @@ -10,6 +10,7 @@ import ( "image" "io" "os" + "strconv" "strings" ) @@ -125,6 +126,14 @@ func (p *PDF) setLevel(pdf *gopdf.GoPdf, level string) error { } pdf.SetX(0) pdf.SetY(p.Level.Y) + + if p.Level.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Level.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + 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") } @@ -139,6 +148,13 @@ func (p *PDF) setExam(pdf *gopdf.GoPdf, exam string) error { pdf.SetX(0) pdf.SetY(p.Exam.Y) + if p.Exam.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Exam.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + 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") } @@ -166,6 +182,14 @@ func (p *PDF) setCourse(pdf *gopdf.GoPdf, courseTitle string, templateImg string } pdf.SetX(0) pdf.SetY(p.Course.Y) + + if p.Course.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Course.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + if courseTitle == "" { courseTitle = templateImg } @@ -184,6 +208,14 @@ func (p *PDF) setSerialNumber(pdf *gopdf.GoPdf, serialNumber string) error { pdf.SetX(p.SerialNumber.X) pdf.SetY(p.SerialNumber.Y) + + if p.SerialNumber.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.SerialNumber.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + if err := pdf.CellWithOption(&gopdf.Rect{W: 300, H: 300}, serialNumber, gopdf.CellOption{Align: gopdf.Right}); err != nil { return errors.Wrap(err, "failed to cell SerialNumber ") } @@ -199,6 +231,14 @@ func (p *PDF) setPoints(pdf *gopdf.GoPdf, points string) error { } pdf.SetX(p.Points.X) pdf.SetY(p.Points.Y) + + if p.Points.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Points.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + 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") } @@ -213,6 +253,14 @@ func (p *PDF) setDate(pdf *gopdf.GoPdf, date string) error { pdf.SetX(p.Date.X) pdf.SetY(p.Date.Y) + + if p.Date.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Date.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + if err := pdf.CellWithOption(&gopdf.Rect{W: 300, H: 300}, fmt.Sprintf("Issued on: %s", date), gopdf.CellOption{Align: gopdf.Right}); err != nil { return errors.Wrap(err, "failed to cell Date") } @@ -226,6 +274,13 @@ func (p *PDF) setCredits(pdf *gopdf.GoPdf, credits string) error { pdf.SetX(p.Credits.X) pdf.SetY(p.Credits.Y) + if p.Credits.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Credits.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + 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") } @@ -239,6 +294,14 @@ func (p *PDF) setName(pdf *gopdf.GoPdf, name string) error { } pdf.SetY(p.Name.Y) pdf.SetX(0) + + if p.Name.Color != "" { + rgb, err := p.hex2RGB(strings.Replace(p.Name.Color, "#", "", 1)) + if err == nil { + pdf.SetTextColor(rgb.Red, rgb.Green, rgb.Blue) + } + } + 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") } @@ -276,14 +339,14 @@ func (p *PDF) prepareName(name, course string) string { func (p *PDF) SetTemplateData(template PDF) *PDF { 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) - certificate.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font) - certificate.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font) - 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.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font, template.Name.Color) + certificate.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font, template.Date.Color) + certificate.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font, template.Course.Color) + certificate.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font, template.Credits.Color) + certificate.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font, template.Exam.Color) + certificate.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font, template.Level.Color) + certificate.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font, template.SerialNumber.Color) + certificate.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font, template.Points.Color) certificate.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.Height, template.QR.Width) return certificate @@ -294,7 +357,7 @@ func (p *PDF) InitTemplate(masterQ data.MasterQ, templateName string, userID int if err != nil { return nil, errors.Wrap(err, "failed to get template data") } - if template.Template == nil { + if template == nil || template.Template == nil { return &DefaultTemplateTall, nil } @@ -361,3 +424,26 @@ func (p *PDF) CellAllPdfFields(pdf *gopdf.GoPdf, data PDFData, config *PDFConfig return nil } + +type RGB struct { + Red uint8 + Green uint8 + Blue uint8 +} + +func (p *PDF) hex2RGB(hex string) (RGB, error) { + var rgb RGB + values, err := strconv.ParseUint(hex, 16, 32) + + if err != nil { + return RGB{}, err + } + + rgb = RGB{ + Red: uint8(values >> 16), + Green: uint8((values >> 8) & 0xFF), + Blue: uint8(values & 0xFF), + } + + return rgb, nil +} diff --git a/internal/service/core/pdf/setters.go b/internal/service/core/pdf/setters.go index e1ba9e14..5d07b22f 100644 --- a/internal/service/core/pdf/setters.go +++ b/internal/service/core/pdf/setters.go @@ -22,61 +22,66 @@ func NewData(name, course, credits, points, serialNumber, date string, qr []byte } } -func (p *PDF) SetName(x, y float64, size int, font string) { +func (p *PDF) SetName(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Name = fl } -func (p *PDF) SetCourse(x, y float64, size int, font string) { +func (p *PDF) SetCourse(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Course = fl } -func (p *PDF) SetCredits(x, y float64, size int, font string) { +func (p *PDF) SetCredits(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Credits = fl } -func (p *PDF) SetLevel(x, y float64, size int, font string) { +func (p *PDF) SetLevel(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Level = fl } -func (p *PDF) SetPoints(x, y float64, size int, font string) { +func (p *PDF) SetPoints(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Points = fl } -func (p *PDF) SetSerialNumber(x, y float64, size int, font string) { +func (p *PDF) SetSerialNumber(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, @@ -87,12 +92,13 @@ func (p *PDF) SetSerialNumber(x, y float64, size int, font string) { p.SerialNumber = fl } -func (p *PDF) SetDate(x, y float64, size int, font string) { +func (p *PDF) SetDate(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Date = fl @@ -109,12 +115,13 @@ func (p *PDF) SetQR(x, y float64, size int, high, width float64) { p.QR = fl } -func (p *PDF) SetExam(x, y float64, size int, font string) { +func (p *PDF) SetExam(x, y float64, size int, font string, color string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, } p.Exam = fl diff --git a/resources/model_template_attributes.go b/resources/model_template_attributes.go index e175c7f3..e1fdbe33 100644 --- a/resources/model_template_attributes.go +++ b/resources/model_template_attributes.go @@ -7,8 +7,9 @@ package resources import "encoding/json" type TemplateAttributes struct { - BackgroundImg string `json:"background_img"` - IsCompleted bool `json:"is_completed"` - Template json.RawMessage `json:"template"` - TemplateName string `json:"template_name"` + BackgroundImg string `json:"background_img"` + IsCompleted bool `json:"is_completed"` + Template json.RawMessage `json:"template"` + TemplateName string `json:"template_name"` + TemplateShortName string `json:"template_short_name"` } From e697e61fe82e5d51852e3fc692d0882dabfc92ea Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Wed, 4 Oct 2023 12:34:57 +0300 Subject: [PATCH 6/9] update color on template and response code --- .../api/handlers/check_container_state.go | 2 +- internal/service/core/pdf/pdf.go | 18 ++++++++++++++++-- internal/service/core/pdf/setters.go | 6 ++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/internal/service/api/handlers/check_container_state.go b/internal/service/api/handlers/check_container_state.go index 403821af..2a7c7051 100644 --- a/internal/service/api/handlers/check_container_state.go +++ b/internal/service/api/handlers/check_container_state.go @@ -21,7 +21,7 @@ func CheckContainerState(w http.ResponseWriter, r *http.Request) { container := PdfCreator(r).CheckContainerState(containerID) if container == nil { Log(r).WithError(err).Error("user not found") - w.WriteHeader(http.StatusProcessing) + w.WriteHeader(http.StatusNoContent) return } Log(r).Debug("container on handler: ", container) diff --git a/internal/service/core/pdf/pdf.go b/internal/service/core/pdf/pdf.go index c6a12882..ff8cc9b4 100644 --- a/internal/service/core/pdf/pdf.go +++ b/internal/service/core/pdf/pdf.go @@ -126,6 +126,7 @@ func (p *PDF) setLevel(pdf *gopdf.GoPdf, level string) error { } pdf.SetX(0) pdf.SetY(p.Level.Y) + pdf.SetTextColor(255, 255, 255) if p.Level.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Level.Color, "#", "", 1)) @@ -147,6 +148,7 @@ func (p *PDF) setExam(pdf *gopdf.GoPdf, exam string) error { } pdf.SetX(0) pdf.SetY(p.Exam.Y) + pdf.SetTextColor(255, 255, 255) if p.Exam.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Exam.Color, "#", "", 1)) @@ -182,6 +184,7 @@ func (p *PDF) setCourse(pdf *gopdf.GoPdf, courseTitle string, templateImg string } pdf.SetX(0) pdf.SetY(p.Course.Y) + pdf.SetTextColor(255, 255, 255) if p.Course.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Course.Color, "#", "", 1)) @@ -208,6 +211,7 @@ func (p *PDF) setSerialNumber(pdf *gopdf.GoPdf, serialNumber string) error { pdf.SetX(p.SerialNumber.X) pdf.SetY(p.SerialNumber.Y) + pdf.SetTextColor(255, 255, 255) if p.SerialNumber.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.SerialNumber.Color, "#", "", 1)) @@ -231,6 +235,7 @@ func (p *PDF) setPoints(pdf *gopdf.GoPdf, points string) error { } pdf.SetX(p.Points.X) pdf.SetY(p.Points.Y) + pdf.SetTextColor(255, 255, 255) if p.Points.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Points.Color, "#", "", 1)) @@ -253,6 +258,7 @@ func (p *PDF) setDate(pdf *gopdf.GoPdf, date string) error { pdf.SetX(p.Date.X) pdf.SetY(p.Date.Y) + pdf.SetTextColor(255, 255, 255) if p.Date.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Date.Color, "#", "", 1)) @@ -273,6 +279,7 @@ func (p *PDF) setCredits(pdf *gopdf.GoPdf, credits string) error { } pdf.SetX(p.Credits.X) pdf.SetY(p.Credits.Y) + pdf.SetTextColor(255, 255, 255) if p.Credits.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Credits.Color, "#", "", 1)) @@ -294,6 +301,7 @@ func (p *PDF) setName(pdf *gopdf.GoPdf, name string) error { } pdf.SetY(p.Name.Y) pdf.SetX(0) + pdf.SetTextColor(255, 255, 255) if p.Name.Color != "" { rgb, err := p.hex2RGB(strings.Replace(p.Name.Color, "#", "", 1)) @@ -341,10 +349,10 @@ func (p *PDF) SetTemplateData(template PDF) *PDF { certificate := NewPDF(template.Height, template.Width) certificate.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font, template.Name.Color) certificate.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font, template.Date.Color) - certificate.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font, template.Course.Color) + certificate.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font, template.Course.Color, template.Course.Text) certificate.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font, template.Credits.Color) certificate.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font, template.Exam.Color) - certificate.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font, template.Level.Color) + certificate.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font, template.Level.Color, template.Level.Text) certificate.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font, template.SerialNumber.Color) certificate.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font, template.Points.Color) certificate.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.Height, template.QR.Width) @@ -402,6 +410,12 @@ func (p *PDF) CellAllPdfFields(pdf *gopdf.GoPdf, data PDFData, config *PDFConfig } isLevel, title, level := p.checkLevel(config.titles[templateImg]) + if title == "" { + level = p.Level.Text + title = p.Course.Text + isLevel = len(level) > 0 + } + 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 5d07b22f..789464fb 100644 --- a/internal/service/core/pdf/setters.go +++ b/internal/service/core/pdf/setters.go @@ -34,13 +34,14 @@ func (p *PDF) SetName(x, y float64, size int, font string, color string) { p.Name = fl } -func (p *PDF) SetCourse(x, y float64, size int, font string, color string) { +func (p *PDF) SetCourse(x, y float64, size int, font string, color string, text string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + Text: text, } p.Course = fl @@ -57,13 +58,14 @@ func (p *PDF) SetCredits(x, y float64, size int, font string, color string) { p.Credits = fl } -func (p *PDF) SetLevel(x, y float64, size int, font string, color string) { +func (p *PDF) SetLevel(x, y float64, size int, font string, color string, text string) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + Text: text, } p.Level = fl From 00430e0232dacd5ea195bec3eefb530985c99c9b Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Mon, 9 Oct 2023 17:35:29 +0300 Subject: [PATCH 7/9] update save template request --- docs/spec/components/schemas/Template.yaml | 3 +- .../service/api/handlers/create_template.go | 45 +------------------ resources/model_template_relationships.go | 2 +- 3 files changed, 5 insertions(+), 45 deletions(-) diff --git a/docs/spec/components/schemas/Template.yaml b/docs/spec/components/schemas/Template.yaml index 8733d525..d270938c 100644 --- a/docs/spec/components/schemas/Template.yaml +++ b/docs/spec/components/schemas/Template.yaml @@ -11,7 +11,8 @@ allOf: - user properties: user: - type: string + type: object + $ref: '#/components/schemas/UserKey' attributes: type: object required: diff --git a/internal/service/api/handlers/create_template.go b/internal/service/api/handlers/create_template.go index a85c4ae7..bd60ca6b 100644 --- a/internal/service/api/handlers/create_template.go +++ b/internal/service/api/handlers/create_template.go @@ -1,13 +1,11 @@ package handlers import ( - "encoding/base64" "encoding/json" "gitlab.com/distributed_lab/ape" "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/resources" "net/http" "strings" ) @@ -19,7 +17,7 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { ape.RenderErr(w, problems.BadRequest(err)...) return } - client, err := MasterQ(r).ClientQ().FilterByName(req.Data.Relationships.User).Get() + client, err := MasterQ(r).ClientQ().FilterByName(req.Data.Relationships.User.Data.ID).Get() if err != nil { Log(r).WithError(err).Error("failed to get client") ape.RenderErr(w, problems.InternalError()) @@ -30,36 +28,7 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { ape.RenderErr(w, problems.NotFound()) 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 { @@ -84,13 +53,3 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) return } - -func newTemplateImageResp(img []byte) resources.TemplateResponse { - return resources.TemplateResponse{ - Data: resources.Template{ - Attributes: resources.TemplateAttributes{ - BackgroundImg: base64.StdEncoding.EncodeToString(img), - }, - }, - } -} diff --git a/resources/model_template_relationships.go b/resources/model_template_relationships.go index 228cba84..131aa9bf 100644 --- a/resources/model_template_relationships.go +++ b/resources/model_template_relationships.go @@ -5,5 +5,5 @@ package resources type TemplateRelationships struct { - User string `json:"user"` + User Relation `json:"user"` } From 4df8129f5e09de9ab72307eacb6b1e5e50f5a553 Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Fri, 3 Nov 2023 12:11:49 +0200 Subject: [PATCH 8/9] fix: dont centralize field --- .../service/api/handlers/get_templates.go | 6 +- internal/service/api/router.go | 1 - internal/service/core/pdf/pdf.go | 89 ++++++++++++++++--- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/internal/service/api/handlers/get_templates.go b/internal/service/api/handlers/get_templates.go index b79c6301..e8b5aafd 100644 --- a/internal/service/api/handlers/get_templates.go +++ b/internal/service/api/handlers/get_templates.go @@ -41,9 +41,9 @@ func GetTemplates(w http.ResponseWriter, r *http.Request) { } func newTemlateListResp(tmps []data.Template) resources.TemplateListResponse { - var reponseTmpList []resources.Template + responseTmpList := make([]resources.Template, 0) for _, tmp := range tmps { - reponseTmpList = append(reponseTmpList, resources.Template{ + responseTmpList = append(responseTmpList, resources.Template{ Attributes: resources.TemplateAttributes{ BackgroundImg: string(tmp.ImgBytes), TemplateName: tmp.Name, @@ -51,6 +51,6 @@ func newTemlateListResp(tmps []data.Template) resources.TemplateListResponse { }) } return resources.TemplateListResponse{ - Data: reponseTmpList, + Data: responseTmpList, } } diff --git a/internal/service/api/router.go b/internal/service/api/router.go index d2b64ca0..82680102 100644 --- a/internal/service/api/router.go +++ b/internal/service/api/router.go @@ -36,7 +36,6 @@ func (s *service) router(cfg config.Config) chi.Router { r.Route("/certificate", func(r chi.Router) { r.Post("/", handlers.PrepareCertificate) r.Post("/bitcoin", handlers.UpdateCertificate) - r.Post("/template", handlers.CreateTemplate) r.Put("/", handlers.UpdateCertificate) r.Post("/ipfs", handlers.UploadFileToIpfs) r.Get("/{container}", handlers.CheckContainerState) diff --git a/internal/service/core/pdf/pdf.go b/internal/service/core/pdf/pdf.go index ff8cc9b4..6dfccf9a 100644 --- a/internal/service/core/pdf/pdf.go +++ b/internal/service/core/pdf/pdf.go @@ -124,7 +124,6 @@ func (p *PDF) setLevel(pdf *gopdf.GoPdf, level string) error { if err := pdf.SetFont("italic", "", p.Level.FontSize); err != nil { return errors.Wrap(err, "failed to set font Level") } - pdf.SetX(0) pdf.SetY(p.Level.Y) pdf.SetTextColor(255, 255, 255) @@ -135,7 +134,18 @@ func (p *PDF) setLevel(pdf *gopdf.GoPdf, level string) error { } } - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, level, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if p.Level.XCenter { + pdf.SetX(0) + + 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") + } + + return nil + } + + pdf.SetX(p.Level.X) + if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.Height}, level); err != nil { return errors.Wrap(err, "failed to cell Level") } @@ -146,7 +156,6 @@ func (p *PDF) setExam(pdf *gopdf.GoPdf, exam string) error { if err := pdf.SetFont("italic", "", p.Exam.FontSize); err != nil { return errors.Wrap(err, "failed to set font Exam") } - pdf.SetX(0) pdf.SetY(p.Exam.Y) pdf.SetTextColor(255, 255, 255) @@ -157,7 +166,17 @@ func (p *PDF) setExam(pdf *gopdf.GoPdf, exam string) error { } } - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, exam, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if p.Exam.XCenter { + pdf.SetX(0) + 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") + } + + return nil + } + + pdf.SetX(p.Exam.X) + if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.Height}, exam); err != nil { return errors.Wrap(err, "failed to cell Exam") } @@ -182,7 +201,7 @@ func (p *PDF) setCourse(pdf *gopdf.GoPdf, courseTitle string, templateImg string 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) pdf.SetTextColor(255, 255, 255) @@ -197,7 +216,16 @@ func (p *PDF) setCourse(pdf *gopdf.GoPdf, courseTitle string, templateImg string courseTitle = templateImg } - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, courseTitle, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if p.Course.XCenter { + pdf.SetX(0) + 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") + } + return nil + } + + pdf.SetX(p.Course.X) + if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.Height}, courseTitle); err != nil { return errors.Wrap(err, "failed to cell Course") } @@ -209,7 +237,6 @@ func (p *PDF) setSerialNumber(pdf *gopdf.GoPdf, serialNumber string) error { return errors.Wrap(err, "failed to set font SerialNumber") } - pdf.SetX(p.SerialNumber.X) pdf.SetY(p.SerialNumber.Y) pdf.SetTextColor(255, 255, 255) @@ -220,6 +247,17 @@ func (p *PDF) setSerialNumber(pdf *gopdf.GoPdf, serialNumber string) error { } } + if p.SerialNumber.XCenter { + pdf.SetX(0) + + if err := pdf.CellWithOption(&gopdf.Rect{W: 300, H: 300}, serialNumber, gopdf.CellOption{Align: gopdf.Center}); err != nil { + return errors.Wrap(err, "failed to cell SerialNumber ") + } + return nil + } + + pdf.SetX(p.SerialNumber.X) + if err := pdf.CellWithOption(&gopdf.Rect{W: 300, H: 300}, serialNumber, gopdf.CellOption{Align: gopdf.Right}); err != nil { return errors.Wrap(err, "failed to cell SerialNumber ") } @@ -233,7 +271,6 @@ func (p *PDF) setPoints(pdf *gopdf.GoPdf, points string) error { return errors.Wrap(err, "failed to set font points") } - pdf.SetX(p.Points.X) pdf.SetY(p.Points.Y) pdf.SetTextColor(255, 255, 255) @@ -244,6 +281,17 @@ func (p *PDF) setPoints(pdf *gopdf.GoPdf, points string) error { } } + if p.Points.XCenter { + pdf.SetX(0) + + if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, fmt.Sprintf("Count of points: %s", points), gopdf.CellOption{Align: gopdf.Center}); err != nil { + return errors.Wrap(err, "failed to cell points") + } + + return nil + } + + pdf.SetX(p.Points.X) 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") } @@ -256,7 +304,6 @@ func (p *PDF) setDate(pdf *gopdf.GoPdf, date string) error { return errors.Wrap(err, "failed to set font Date") } - pdf.SetX(p.Date.X) pdf.SetY(p.Date.Y) pdf.SetTextColor(255, 255, 255) @@ -267,6 +314,16 @@ func (p *PDF) setDate(pdf *gopdf.GoPdf, date string) error { } } + if p.Date.XCenter { + pdf.SetX(0) + + if err := pdf.CellWithOption(&gopdf.Rect{W: 300, H: 300}, fmt.Sprintf("Issued on: %s", date), gopdf.CellOption{Align: gopdf.Center}); err != nil { + return errors.Wrap(err, "failed to cell Date") + } + return nil + } + pdf.SetX(p.Date.X) + if err := pdf.CellWithOption(&gopdf.Rect{W: 300, H: 300}, fmt.Sprintf("Issued on: %s", date), gopdf.CellOption{Align: gopdf.Right}); err != nil { return errors.Wrap(err, "failed to cell Date") } @@ -300,7 +357,6 @@ func (p *PDF) setName(pdf *gopdf.GoPdf, name string) error { return errors.Wrap(err, "failed to set font name") } pdf.SetY(p.Name.Y) - pdf.SetX(0) pdf.SetTextColor(255, 255, 255) if p.Name.Color != "" { @@ -310,11 +366,22 @@ func (p *PDF) setName(pdf *gopdf.GoPdf, name string) error { } } - if err := pdf.CellWithOption(&gopdf.Rect{W: p.Width, H: p.Height}, name, gopdf.CellOption{Align: gopdf.Center}); err != nil { + if p.Name.XCenter { + pdf.SetX(0) + + 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") + } + return nil + } + + pdf.SetX(p.Name.X) + if err := pdf.Cell(&gopdf.Rect{W: p.Width, H: p.Height}, name); err != nil { return errors.Wrap(err, "failed to cell name") } return nil + } func (p *PDF) initBackground(pdf *gopdf.GoPdf, templateQ data.TemplateQ, templateImg, abs string, userID int64) error { From cd158513149de3cf8bb7373a68f49990b549b857 Mon Sep 17 00:00:00 2001 From: "mark.cherepovskyi" Date: Fri, 3 Nov 2023 13:06:25 +0200 Subject: [PATCH 9/9] hot fix: setup more info to template --- internal/service/core/google/google_drive.go | 8 +++---- internal/service/core/pdf/pdf.go | 16 ++++++------- internal/service/core/pdf/setters.go | 25 +++++++++++++------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/internal/service/core/google/google_drive.go b/internal/service/core/google/google_drive.go index e14e1fe9..75295d15 100644 --- a/internal/service/core/google/google_drive.go +++ b/internal/service/core/google/google_drive.go @@ -47,11 +47,11 @@ func (g *Google) GetFiles() ([]*drive.File, error) { if len(r.Files) == 0 { return nil, errors.New("No files found.") - } else { - for _, i := range r.Files { - g.cfg.Log().Info("%s (%s)\n", i.Name, i.Id) - } } + for _, i := range r.Files { + g.cfg.Log().Info("%s (%s)\n", i.Name, i.Id) + } + return r.Files, nil } diff --git a/internal/service/core/pdf/pdf.go b/internal/service/core/pdf/pdf.go index 6dfccf9a..c3718f3f 100644 --- a/internal/service/core/pdf/pdf.go +++ b/internal/service/core/pdf/pdf.go @@ -414,14 +414,14 @@ func (p *PDF) prepareName(name, course string) string { func (p *PDF) SetTemplateData(template PDF) *PDF { certificate := NewPDF(template.Height, template.Width) - certificate.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font, template.Name.Color) - certificate.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font, template.Date.Color) - certificate.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font, template.Course.Color, template.Course.Text) - certificate.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font, template.Credits.Color) - certificate.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font, template.Exam.Color) - certificate.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font, template.Level.Color, template.Level.Text) - certificate.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font, template.SerialNumber.Color) - certificate.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font, template.Points.Color) + certificate.SetName(template.Name.X, template.Name.Y, template.Name.FontSize, template.Name.Font, template.Name.Color, template.Name.XCenter) + certificate.SetDate(template.Date.X, template.Date.Y, template.Date.FontSize, template.Date.Font, template.Date.Color, template.Date.XCenter) + certificate.SetCourse(template.Course.X, template.Course.Y, template.Course.FontSize, template.Course.Font, template.Course.Color, template.Course.Text, template.Course.XCenter) + certificate.SetCredits(template.Credits.X, template.Credits.Y, template.Credits.FontSize, template.Credits.Font, template.Credits.Color, template.Credits.XCenter) + certificate.SetExam(template.Exam.X, template.Exam.Y, template.Exam.FontSize, template.Exam.Font, template.Exam.Color, template.Exam.XCenter) + certificate.SetLevel(template.Level.X, template.Level.Y, template.Level.FontSize, template.Level.Font, template.Level.Color, template.Level.Text, template.Level.XCenter) + certificate.SetSerialNumber(template.SerialNumber.X, template.SerialNumber.Y, template.SerialNumber.FontSize, template.SerialNumber.Font, template.SerialNumber.Color, template.SerialNumber.XCenter) + certificate.SetPoints(template.Points.X, template.Points.Y, template.Points.FontSize, template.Points.Font, template.Points.Color, template.Points.XCenter) certificate.SetQR(template.QR.X, template.QR.Y, template.QR.FontSize, template.QR.Height, template.QR.Width) return certificate diff --git a/internal/service/core/pdf/setters.go b/internal/service/core/pdf/setters.go index 789464fb..5c64c978 100644 --- a/internal/service/core/pdf/setters.go +++ b/internal/service/core/pdf/setters.go @@ -22,19 +22,20 @@ func NewData(name, course, credits, points, serialNumber, date string, qr []byte } } -func (p *PDF) SetName(x, y float64, size int, font string, color string) { +func (p *PDF) SetName(x, y float64, size int, font string, color string, isXCenter bool) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + XCenter: isXCenter, } p.Name = fl } -func (p *PDF) SetCourse(x, y float64, size int, font string, color string, text string) { +func (p *PDF) SetCourse(x, y float64, size int, font string, color string, text string, isXCenter bool) { fl := Field{ X: x, Y: y, @@ -42,23 +43,25 @@ func (p *PDF) SetCourse(x, y float64, size int, font string, color string, text Font: font, Color: color, Text: text, + XCenter: isXCenter, } p.Course = fl } -func (p *PDF) SetCredits(x, y float64, size int, font string, color string) { +func (p *PDF) SetCredits(x, y float64, size int, font string, color string, isXCenter bool) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + XCenter: isXCenter, } p.Credits = fl } -func (p *PDF) SetLevel(x, y float64, size int, font string, color string, text string) { +func (p *PDF) SetLevel(x, y float64, size int, font string, color string, text string, isXCenter bool) { fl := Field{ X: x, Y: y, @@ -66,41 +69,46 @@ func (p *PDF) SetLevel(x, y float64, size int, font string, color string, text s Font: font, Color: color, Text: text, + XCenter: isXCenter, } p.Level = fl } -func (p *PDF) SetPoints(x, y float64, size int, font string, color string) { +func (p *PDF) SetPoints(x, y float64, size int, font string, color string, isXCenter bool) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + XCenter: isXCenter, } p.Points = fl } -func (p *PDF) SetSerialNumber(x, y float64, size int, font string, color string) { +func (p *PDF) SetSerialNumber(x, y float64, size int, font string, color string, isXCenter bool) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, + Color: color, + XCenter: isXCenter, } p.SerialNumber = fl } -func (p *PDF) SetDate(x, y float64, size int, font string, color string) { +func (p *PDF) SetDate(x, y float64, size int, font string, color string, isXCenter bool) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + XCenter: isXCenter, } p.Date = fl @@ -117,13 +125,14 @@ func (p *PDF) SetQR(x, y float64, size int, high, width float64) { p.QR = fl } -func (p *PDF) SetExam(x, y float64, size int, font string, color string) { +func (p *PDF) SetExam(x, y float64, size int, font string, color string, isXCenter bool) { fl := Field{ X: x, Y: y, FontSize: size, Font: font, Color: color, + XCenter: isXCenter, } p.Exam = fl