From be1c5f75c414dbfbe5fc879f0c1d27eda380252b Mon Sep 17 00:00:00 2001 From: Oliver Braun Date: Thu, 9 Jan 2025 20:16:20 +0100 Subject: [PATCH] feat: email for all NTAs --- cmd/email.go | 9 +- plexams/email.go | 134 ++++++++++++++++++++++++++++- tmpl/handicapEmailPlanned.tmpl | 18 ++++ tmpl/handicapEmailPlannedHTML.tmpl | 22 +++++ 4 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 tmpl/handicapEmailPlanned.tmpl create mode 100644 tmpl/handicapEmailPlannedHTML.tmpl diff --git a/cmd/email.go b/cmd/email.go index bc14477..4b29276 100644 --- a/cmd/email.go +++ b/cmd/email.go @@ -15,8 +15,8 @@ var ( Use: "email [subcommand]", Short: "send email", Long: `Send emails. -nta --- send emails to teachers about nta, -nta-with-room-alone --- send emails to students with room alone, +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.`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { @@ -27,6 +27,11 @@ primuss-data [all|] --- send emails to teachers about primuss data and n if err != nil { log.Fatalf("got error: %v\n", err) } + case "nta-planned": + err := plexams.SendHandicapsMailsNTAPlanned(context.Background(), run) + if err != nil { + log.Fatalf("got error: %v\n", err) + } case "primuss-data": if len(args) < 2 { log.Fatal("need program and primuss-ancode") diff --git a/plexams/email.go b/plexams/email.go index 8bea2cf..300e365 100644 --- a/plexams/email.go +++ b/plexams/email.go @@ -11,6 +11,8 @@ import ( "time" // TODO: Ersetzen durch github.com/wneessen/go-mail + + set "github.com/deckarep/golang-set/v2" "github.com/jordan-wright/email" "github.com/logrusorgru/aurora" "github.com/obcode/plexams.go/graph/model" @@ -349,7 +351,7 @@ func (p *Plexams) SendHandicapsMailsNTARoomAlone(ctx context.Context, run bool) } } - err = p.SendHandicapsMailToStudent(ctx, to, cc, &NTAEmail{ + err = p.SendHandicapsMailToStudentRoomAlone(ctx, to, cc, &NTAEmail{ NTA: nta, Exams: exams, PlanerName: p.planer.Name, @@ -362,7 +364,7 @@ func (p *Plexams) SendHandicapsMailsNTARoomAlone(ctx context.Context, run bool) return nil } -func (p *Plexams) SendHandicapsMailToStudent(ctx context.Context, to []string, cc []string, handicapsEmail *NTAEmail) error { +func (p *Plexams) SendHandicapsMailToStudentRoomAlone(ctx context.Context, to []string, cc []string, handicapsEmail *NTAEmail) error { log.Debug().Interface("to", to).Msg("sending email") tmpl, err := template.ParseFiles("tmpl/handicapEmailRoomAlone.tmpl") @@ -393,6 +395,134 @@ func (p *Plexams) SendHandicapsMailToStudent(ctx context.Context, to []string, c ) } +type NTAEmailExamAndRoom struct { + Exam *model.PlannedExam + Room *model.PlannedRoom + Invigilator *model.Teacher +} + +type NTAEmailWithRooms struct { + NTA *model.Student + ExamsWithRoom []NTAEmailExamAndRoom + PlanerName string +} + +func (p *Plexams) SendHandicapsMailsNTAPlanned(ctx context.Context, run bool) error { + ntas, err := p.NtasWithRegs(ctx) + if err != nil { + log.Error().Err(err).Msg("cannot get ntas") + return err + } + + atLeastOneEmailMissing := false + for _, nta := range ntas { + if nta.Nta.Email == nil || *nta.Nta.Email == "" { + log.Error().Str("mtknr", nta.Mtknr).Str("name", nta.Name).Msg("no email set") + atLeastOneEmailMissing = true + } + } + if atLeastOneEmailMissing { + return fmt.Errorf("at least one email missing") + } + + for _, nta := range ntas { + exams := make([]*model.PlannedExam, 0, len(nta.Regs)) + for _, ancode := range nta.Regs { + exam, err := p.PlannedExam(ctx, ancode) + if err != nil { + log.Error().Err(err).Int("ancode", ancode).Msg("cannot get exam") + return err + } + if exam.Constraints == nil || !exam.Constraints.NotPlannedByMe { + exams = append(exams, exam) + } + } + + var to, cc []string + + examsWithRoom := make([]NTAEmailExamAndRoom, 0, len(exams)) + to = []string{*nta.Nta.Email} + ccSet := set.NewSet[string]() + for _, exam := range exams { + teacher, err := p.GetTeacher(ctx, exam.ZpaExam.MainExamerID) + if err != nil { + log.Error().Err(err).Int("ancode", exam.Ancode).Msg("cannot get teacher") + return err + } + ccSet.Add(teacher.Email) + // invigilators cc + room, err := p.PlannedRoomForStudent(ctx, exam.Ancode, nta.Mtknr) + if room == nil || err != nil { + log.Error().Int("ancode", exam.Ancode).Str("mtknr", nta.Mtknr).Msg("no room") + continue + } + invigilator, err := p.GetInvigilatorInSlot(ctx, room.RoomName, exam.PlanEntry.DayNumber, exam.PlanEntry.SlotNumber) + if err != nil || invigilator == nil { + log.Error().Err(err).Int("ancode", exam.Ancode).Str("room", room.RoomName). + Int("slot", exam.PlanEntry.SlotNumber).Int("day", exam.PlanEntry.DayNumber). + Msg("cannot get invigilator") + continue + } + log.Debug().Str("mtknr", nta.Mtknr).Str("name", nta.Name).Str("room", room.RoomName).Str("invigilator", invigilator.Fullname). + Msg("found info") + ccSet.Add(invigilator.Email) + examsWithRoom = append(examsWithRoom, NTAEmailExamAndRoom{ + Exam: exam, + Room: room, + Invigilator: invigilator, + }) + } + cc = ccSet.ToSlice() + + if !run { + to = []string{p.planer.Email} + log.Debug().Interface("cc", cc).Msg("not sending cc") + cc = []string{} + } + + err = p.SendHandicapsMailToStudentPlanned(ctx, to, cc, &NTAEmailWithRooms{ + NTA: nta, + ExamsWithRoom: examsWithRoom, + PlanerName: p.planer.Name, + }) + if err != nil { + return err + } + } + return nil +} + +func (p *Plexams) SendHandicapsMailToStudentPlanned(ctx context.Context, to []string, cc []string, handicapsEmail *NTAEmailWithRooms) error { + log.Debug().Interface("to", to).Msg("sending email") + + tmpl, err := template.ParseFiles("tmpl/handicapEmailPlanned.tmpl") + if err != nil { + return err + } + bufText := new(bytes.Buffer) + err = tmpl.Execute(bufText, handicapsEmail) + if err != nil { + return err + } + + tmpl, err = template.ParseFiles("tmpl/handicapEmailPlannedHTML.tmpl") + if err != nil { + return err + } + bufHTML := new(bytes.Buffer) + err = tmpl.Execute(bufHTML, handicapsEmail) + if err != nil { + return err + } + + return p.sendMail(to, + cc, + fmt.Sprintf("[Prüfungsplanung %s] Räume für Ihre Prüfung(en)", p.semester), + bufText.Bytes(), + bufHTML.Bytes(), + ) +} + func (p *Plexams) sendMail(to []string, cc []string, subject string, text []byte, html []byte) error { e := &email.Email{ To: to, diff --git a/tmpl/handicapEmailPlanned.tmpl b/tmpl/handicapEmailPlanned.tmpl new file mode 100644 index 0000000..2fcfbf1 --- /dev/null +++ b/tmpl/handicapEmailPlanned.tmpl @@ -0,0 +1,18 @@ +Hallo {{ .NTA.Name }}, + +Sie haben gemäß den mir vorliegenden Informationen Anspruch auf Nachteilsausgleich +bei Ihren Prüfungen. + +Für folgende Prüfungen, für die Sie sich angemeldet haben, habe ich Sie im jeweils angegebenen Raum eingeplant: + +{{ range .ExamsWithRoom -}} +- {{ .Room.RoomName }} --- {{ .Exam.ZpaExam.MainExamer }}: {{ .Exam.ZpaExam.Module }} +{{ end }} + +Mit freundlichen Grüßen +{{ .PlanerName }} + +
+-- 
+Diese E-Mail wurde generiert und gesendet von https://github.com/obcode/plexams.go
+
diff --git a/tmpl/handicapEmailPlannedHTML.tmpl b/tmpl/handicapEmailPlannedHTML.tmpl new file mode 100644 index 0000000..e387655 --- /dev/null +++ b/tmpl/handicapEmailPlannedHTML.tmpl @@ -0,0 +1,22 @@ +

Hallo {{ .NTA.Name }},

+ +

Sie haben gemäß den mir vorliegenden Informationen Anspruch auf Nachteilsausgleich +bei Ihren Prüfungen.

+ +

Für folgende Prüfungen habe ich Sie im jeweils angegebenen Raum eingeplant:

+ +
    +{{ range .ExamsWithRoom -}} +
  • +{{ .Room.RoomName }} --- {{ .Exam.ZpaExam.MainExamer }}: {{ .Exam.ZpaExam.Module }} +
  • +{{ end }} +
+ +

Mit freundlichen Grüßen
+{{ .PlanerName }}

+ +
+-- 
+Diese E-Mail wurde generiert und gesendet von https://github.com/obcode/plexams.go
+