diff --git a/cmd/email.go b/cmd/email.go index d5f5d88..a3773c1 100644 --- a/cmd/email.go +++ b/cmd/email.go @@ -15,16 +15,17 @@ var ( Use: "email [subcommand]", Short: "send email", Long: `Send emails. -primuss-data [all|] --- send emails to teachers about primuss data and nta -constraints --- ask for constraints -prepared --- announce exams to plan and constraints -draft --- announce draft plan -published-exams --- announce published exams -published-rooms --- announce published rooms -invigilations --- send email requesting invigilations constraints -published-invigilations --- announce published invigilations -nta-with-room-alone --- send emails to students with room alone before planning -nta-planned --- send emails about rooms to all students with nta after planning +primuss-data [all|] --- send emails to teachers about primuss data and nta +constraints --- ask for constraints +prepared --- announce exams to plan and constraints +draft --- announce draft plan +published-exams --- announce published exams +published-rooms --- announce published rooms +invigilations --- send email requesting invigilations constraints +published-invigilations --- announce published invigilations +nta-with-room-alone --- send emails to students with room alone before planning +nta-planned --- send emails about rooms to all students with nta after planning +cover-pages [all|] --- send emails with externally generated cover pages `, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { @@ -32,7 +33,7 @@ nta-planned --- send emails about rooms to all students with nta after plann switch args[0] { case "primuss-data": if len(args) < 2 { - log.Fatal("need program and primuss-ancode") + log.Fatal("need ancode or all") } if args[1] == "all" { err := plexams.SendGeneratedExamMails(context.Background(), run) @@ -95,7 +96,26 @@ nta-planned --- send emails about rooms to all students with nta after plann if err != nil { log.Fatalf("got error: %v\n", err) } - + case "cover-pages": + if len(args) < 2 { + log.Fatal("need teacher id or all") + } + if args[1] == "all" { + err := plexams.SendCoverPagesMails(context.Background(), run) + if err != nil { + log.Fatalf("got error: %v\n", err) + } + } else { + teacherID, err := strconv.Atoi(args[1]) + if err != nil { + fmt.Printf("cannot use %s as teacher id", args[1]) + os.Exit(1) + } + err = plexams.SendCoverPageMail(context.Background(), teacherID, run) + if err != nil { + log.Fatalf("got error: %v\n", err) + } + } default: fmt.Println("email called with unknown sub command") } diff --git a/cmd/export.go b/cmd/export.go index 7e65d05..1972d5a 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -12,14 +12,14 @@ var ( Use: "export", Short: "export [subcommand]", Long: `Generate various CSVs. - plannedRooms - export rooms of planned exams.`, + planned-rooms - export rooms of planned exams.`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { plexams := initPlexamsConfig() switch args[0] { - case "plannedRooms": + case "planned-rooms": if len(jsonfile) == 0 { - jsonfile = "PlannedRooms.json" + jsonfile = "planned-rooms.json" } fmt.Printf("generating %s\n", jsonfile) err := plexams.ExportPlannedRooms(jsonfile) diff --git a/plexams/email.go b/plexams/email.go index 31e9ec4..15a0689 100644 --- a/plexams/email.go +++ b/plexams/email.go @@ -18,6 +18,8 @@ import ( //go:embed tmpl/constraintsEmail.tmpl //go:embed tmpl/constraintsEmailHTML.tmpl +//go:embed tmpl/coverPageEmail.tmpl +//go:embed tmpl/coverPageEmailHTML.tmpl //go:embed tmpl/draftEmailFS.tmpl //go:embed tmpl/draftEmailFSHTML.tmpl //go:embed tmpl/draftEmailZPA.tmpl diff --git a/plexams/email_cover_pages.go b/plexams/email_cover_pages.go new file mode 100644 index 0000000..18ab8f2 --- /dev/null +++ b/plexams/email_cover_pages.go @@ -0,0 +1,152 @@ +package plexams + +import ( + "bytes" + "context" + "fmt" + "html/template" + "os" + "strings" + "time" + + set "github.com/deckarep/golang-set/v2" + "github.com/jordan-wright/email" + "github.com/logrusorgru/aurora" + "github.com/obcode/plexams.go/graph/model" + "github.com/rs/zerolog/log" + "github.com/spf13/viper" + "github.com/theckman/yacspin" +) + +type CoverMailData struct { + Teacher *model.Teacher + PlanerName string + GeneratorName string +} + +func (p *Plexams) SendCoverPagesMails(ctx context.Context, run bool) error { + plannedExams, err := p.PlannedExams(ctx) + if err != nil { + return err + } + + examerIDs := set.NewSet[int]() + for _, exam := range plannedExams { + if exam.Constraints != nil && exam.Constraints.NotPlannedByMe { + continue + } + examerIDs.Add(exam.ZpaExam.MainExamerID) + } + + for examerID := range examerIDs.Iter() { + p.SendCoverPageMail(ctx, examerID, run) + } + + return nil +} + +func (p *Plexams) SendCoverPageMail(ctx context.Context, examerID int, run bool) error { + cfg := yacspin.Config{ + Frequency: 100 * time.Millisecond, + CharSet: yacspin.CharSets[69], + Suffix: aurora.Sprintf(aurora.Cyan(" sending email with cover pages for %4d"), examerID), + SuffixAutoColon: true, + StopCharacter: "✓", + StopColors: []string{"fgGreen"}, + StopFailMessage: "error happend", + StopFailCharacter: "✗", + StopFailColors: []string{"fgRed"}, + } + spinner, err := yacspin.New(cfg) + if err != nil { + log.Debug().Err(err).Msg("cannot create spinner") + } + err = spinner.Start() + if err != nil { + log.Debug().Err(err).Msg("cannot start spinner") + } + + teacher, err := p.GetTeacher(ctx, examerID) + if err != nil { + log.Debug().Err(err).Msg("cannot get teacher by ID") + return err + } + + dir := viper.GetString("coverPages.dir") + prefix := viper.GetString("coverPages.prefix") + filename := fmt.Sprintf("%s/%s%d.pdf", dir, prefix, examerID) + + pdfData, err := os.ReadFile(filename) + if err != nil { + spinner.StopFailMessage(aurora.Sprintf(aurora.Red(" %s: file not found: %s"), + aurora.Magenta(teacher.Fullname), aurora.Magenta(filename))) + spinner.StopFail() //nolint:errcheck + return err + } + + coverMailData := &CoverMailData{ + PlanerName: p.planer.Name, + Teacher: teacher, + GeneratorName: "Edda Eich-Söllner", + } + + tmpl, err := template.ParseFS(emailTemplates, "tmpl/coverPageEmail.tmpl") + if err != nil { + return err + } + bufText := new(bytes.Buffer) + err = tmpl.Execute(bufText, coverMailData) + if err != nil { + return err + } + + tmpl, err = template.ParseFS(emailTemplates, "tmpl/coverPageEmailHTML.tmpl") + if err != nil { + return err + } + bufHTML := new(bytes.Buffer) + err = tmpl.Execute(bufHTML, coverMailData) + if err != nil { + return err + } + + subject := fmt.Sprintf("[Prüfungsplanung %s] Deckblätter für Ihre Prüfungen", + p.semester) + + var to []string + if run { + to = []string{teacher.Email} + } else { + to = []string{"galority@gmail.com"} + } + + err = p.sendMail(to, + nil, + subject, + bufText.Bytes(), + bufHTML.Bytes(), + []*email.Attachment{{ + Filename: strings.ReplaceAll(fmt.Sprintf("%s_Deckblaetter_Pruefungen_%s.pdf", p.semester, teacher.Fullname), " ", "_"), + ContentType: "application/pdf", + Header: map[string][]string{}, + Content: pdfData, + HTMLRelated: false, + }}, + true, + ) + + if err != nil { + spinner.StopFailMessage(aurora.Sprintf(aurora.Red(" error while sending email to %s"), teacher.Fullname)) + return err + } + + spinner.StopMessage(aurora.Sprintf(aurora.Cyan(" successfully send to %s"), teacher.Fullname)) + + err = spinner.Stop() + + if err != nil { + log.Debug().Err(err).Msg("cannot stop spinner") + } + + return nil +} diff --git a/plexams/tmpl/coverPageEmail.tmpl b/plexams/tmpl/coverPageEmail.tmpl new file mode 100644 index 0000000..3876187 --- /dev/null +++ b/plexams/tmpl/coverPageEmail.tmpl @@ -0,0 +1,10 @@ +Hallo {{ .Teacher.Fullname }}, + +im Anhang finden Sie die von {{ .GeneratorName }} generierten Deckblätter für Ihre Prüfungen. + +Mit freundlichen Grüßen +{{ .PlanerName }} +Prüfungsplaner der FK07 + +-- +Diese E-Mail wurde generiert und gesendet von https://github.com/obcode/plexams.go diff --git a/plexams/tmpl/coverPageEmailHTML.tmpl b/plexams/tmpl/coverPageEmailHTML.tmpl new file mode 100644 index 0000000..fe5d24e --- /dev/null +++ b/plexams/tmpl/coverPageEmailHTML.tmpl @@ -0,0 +1,13 @@ +

Hallo {{ .Teacher.Fullname }},

+ +

im Anhang finden Sie die von {{ .GeneratorName }} generierten Deckblätter für Ihre Prüfungen.

+ +

Mit freundlichen Grüßen

+

{{ .PlanerName }}
+Prüfungsplaner der FK07 +

+ +
+-- 
+Diese E-Mail wurde generiert und gesendet von https://github.com/obcode/plexams.go
+
\ No newline at end of file diff --git a/plexams/tmpl/publishedEmailRooms.tmpl b/plexams/tmpl/publishedEmailRooms.tmpl index f308390..0614f83 100644 --- a/plexams/tmpl/publishedEmailRooms.tmpl +++ b/plexams/tmpl/publishedEmailRooms.tmpl @@ -1,5 +1,7 @@ [Antworten bitte nicht via E-Mail, sondern via JIRA (https://jira.cc.hm.edu/servicedesk/customer/portal/13)] +Liebe Prüfende, + ich habe gerade die Räume ins ZPA gepusht. Bitte schauen Sie auf https://zpa.cs.hm.edu/teacher/exam_plan/ ob das so für Ihre Prüfungen passt. Ich habe versucht die Raumnutzung sehr zu optimieren, daher gibt es z.B. mehrere kleine (unter 10 Anmeldungen) Prüfungen in einem Raum oder ein Raum für eine Prüfung ist gleichzeitig Reserveraum für eine weitere Prüfung. diff --git a/plexams/tmpl/publishedEmailRoomsHTML.tmpl b/plexams/tmpl/publishedEmailRoomsHTML.tmpl index 91a896a..fd7890a 100644 --- a/plexams/tmpl/publishedEmailRoomsHTML.tmpl +++ b/plexams/tmpl/publishedEmailRoomsHTML.tmpl @@ -1,6 +1,8 @@

[Antworten bitte nicht via E-Mail, sondern via JIRA]

+

Liebe Prüfende,

+

ich habe gerade die Räume ins ZPA gepusht. Bitte schauen Sie auf https://zpa.cs.hm.edu/teacher/exam_plan/ ob das so für Ihre Prüfungen passt.

Ich habe versucht die Raumnutzung sehr zu optimieren, daher gibt es z.B. mehrere kleine (unter 10 Anmeldungen) Prüfungen in einem Raum oder