changeset 1646:a0982c38eac0

Import queue: Implemented email notifications.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 20 Dec 2018 15:33:29 +0100
parents 14bb1289b97b
children 0828fcb80647
files pkg/controllers/bnimports.go pkg/controllers/gmimports.go pkg/controllers/srimports.go pkg/imports/bn.go pkg/imports/gm.go pkg/imports/queue.go pkg/imports/sr.go pkg/models/bn.go pkg/models/gauge.go
diffstat 9 files changed, 110 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/bnimports.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/controllers/bnimports.go	Thu Dec 20 15:33:29 2018 +0100
@@ -38,19 +38,18 @@
 	}
 
 	var serialized string
-	serialized, err = common.ToJSONString(bn)
-	if err != nil {
+	if serialized, err = common.ToJSONString(bn); err != nil {
 		return
 	}
 
 	session, _ := auth.GetSession(req)
 
-	jobID, err := imports.AddJob(
+	var jobID int64
+	if jobID, err = imports.AddJob(
 		imports.BNJobKind, session.User,
-		false, false,
-		serialized)
-
-	if err != nil {
+		bi.SendEmail, false,
+		serialized,
+	); err != nil {
 		return
 	}
 
--- a/pkg/controllers/gmimports.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/controllers/gmimports.go	Thu Dec 20 15:33:29 2018 +0100
@@ -30,28 +30,27 @@
 	conn *sql.Conn,
 ) (jr JSONResult, err error) {
 
-	bi := input.(*models.GaugeMeasurementImport)
+	gi := input.(*models.GaugeMeasurementImport)
 
-	bn := &imports.GaugeMeasurement{
-		URL:      bi.URL,
-		Insecure: bi.Insecure,
+	gm := &imports.GaugeMeasurement{
+		URL:      gi.URL,
+		Insecure: gi.Insecure,
 	}
 
 	var serialized string
-	serialized, err = common.ToJSONString(bn)
-	if err != nil {
+	if serialized, err = common.ToJSONString(gm); err != nil {
 		return
 	}
 
 	session, _ := auth.GetSession(req)
 
-	jobID, err := imports.AddJob(
+	var jobID int64
+	if jobID, err = imports.AddJob(
 		imports.GMJobKind,
 		session.User,
-		false, true,
-		serialized)
-
-	if err != nil {
+		gi.SendEmail, true,
+		serialized,
+	); err != nil {
 		return
 	}
 
--- a/pkg/controllers/srimports.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/controllers/srimports.go	Thu Dec 20 15:33:29 2018 +0100
@@ -161,10 +161,12 @@
 
 	session, _ := auth.GetSession(req)
 
+	sendEmail := req.FormValue("bottleneck") != ""
+
 	jobID, err := imports.AddJob(
 		imports.SRJobKind,
 		session.User,
-		false, false,
+		sendEmail, false,
 		serialized)
 
 	if err != nil {
--- a/pkg/imports/bn.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/imports/bn.go	Thu Dec 20 15:33:29 2018 +0100
@@ -79,6 +79,10 @@
 	RegisterJobCreator(BNJobKind, bnJobCreator{})
 }
 
+func (bnJobCreator) Description() string {
+	return "bottlenecks"
+}
+
 func (bnJobCreator) Create(_ JobKind, data string) (Job, error) {
 	bn := new(Bottleneck)
 	if err := common.FromJSONString(data, bn); err != nil {
--- a/pkg/imports/gm.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/imports/gm.go	Thu Dec 20 15:33:29 2018 +0100
@@ -81,6 +81,10 @@
 	RegisterJobCreator(GMJobKind, gmJobCreator{})
 }
 
+func (gmJobCreator) Description() string {
+	return "gauge measurements"
+}
+
 func (gmJobCreator) Create(_ JobKind, data string) (Job, error) {
 	gm := new(GaugeMeasurement)
 	if err := common.FromJSONString(data, gm); err != nil {
--- a/pkg/imports/queue.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/imports/queue.go	Thu Dec 20 15:33:29 2018 +0100
@@ -18,6 +18,7 @@
 	"database/sql"
 	"encoding/json"
 	"fmt"
+	"html/template"
 	"log"
 	"runtime/debug"
 	"strings"
@@ -28,6 +29,7 @@
 
 	"gemma.intevation.de/gemma/pkg/auth"
 	"gemma.intevation.de/gemma/pkg/config"
+	"gemma.intevation.de/gemma/pkg/misc"
 )
 
 type (
@@ -66,6 +68,8 @@
 	// JobCreator is used to bring a job to life as it is stored
 	// in pure meta-data form to the database.
 	JobCreator interface {
+		// Description is the long name of the import.
+		Description() string
 		// Create build the actual job.
 		// kind is the name of the import type.
 		// data is a free form string to pass arguments to the creation
@@ -519,6 +523,75 @@
 			}
 			// TODO: Send email if sendEmail is set.
 			log.Printf("import #%d finished: %s\n", idj.id, state)
+			if idj.sendEmail {
+				go sendNotificationMail(idj.user, jc.Description(), state, idj.id)
+			}
 		}(jc, idj)
 	}
 }
+
+const (
+	selectEmailSQL = `SELECT email_address FROM users.list_users WHERE username = $1`
+
+	importNotificationMailSubject = `import notification mail`
+)
+
+var (
+	importNotificationMailTmpl = template.Must(
+		template.New("notification").Parse(`
+Dear {{ .User }},
+
+a {{ .Description }} import on server {{ .Server }} triggered
+this email notification.
+
+{{ if eq .State "accepted" }}The imported data were successfully integrated into the database.{{ end -}}
+{{ if eq .State "failed" }}The import failed for some reasons.{{ end -}}
+{{ if eq .State "pending" }}The imported data could be integrated into the database
+but your final decision is needed.{{ end }}
+
+Please follow this link to have a closer look at the details:
+
+{{ .Server }}/#?review={{ .ID }}
+
+Best regards
+    Your service team`))
+)
+
+func sendNotificationMail(user, description, state string, id int64) {
+	config.WaitReady()
+
+	ctx := context.Background()
+	var email string
+	if err := auth.RunAs(ctx, user,
+		func(conn *sql.Conn) error {
+			return conn.QueryRowContext(ctx, selectEmailSQL, user).Scan(&email)
+		},
+	); err != nil {
+		log.Printf("error: %v\n", err)
+		return
+	}
+
+	data := struct {
+		User        string
+		Description string
+		Server      string
+		State       string
+		ID          int64
+	}{
+		User:        user,
+		Description: description,
+		Server:      config.ExternalURL(),
+		State:       state,
+		ID:          id,
+	}
+
+	var body strings.Builder
+	if err := importNotificationMailTmpl.Execute(&body, &data); err != nil {
+		log.Printf("error: %v\n", err)
+		return
+	}
+
+	if err := misc.SendMail(email, importNotificationMailSubject, body.String()); err != nil {
+		log.Printf("error: %v\n", err)
+	}
+}
--- a/pkg/imports/sr.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/imports/sr.go	Thu Dec 20 15:33:29 2018 +0100
@@ -73,6 +73,10 @@
 	RegisterJobCreator(SRJobKind, srJobCreator{})
 }
 
+func (srJobCreator) Description() string {
+	return "sounding results"
+}
+
 func (srJobCreator) Create(_ JobKind, data string) (Job, error) {
 	sr := new(SoundingResult)
 	if err := common.FromJSONString(data, sr); err != nil {
--- a/pkg/models/bn.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/models/bn.go	Thu Dec 20 15:33:29 2018 +0100
@@ -14,6 +14,7 @@
 package models
 
 type BottleneckImport struct {
-	URL      string `json:"url"`
-	Insecure bool   `json:"insecure"`
+	URL       string `json:"url"`
+	Insecure  bool   `json:"insecure"`
+	SendEmail bool   `json:"send-email"`
 }
--- a/pkg/models/gauge.go	Thu Dec 20 15:20:49 2018 +0100
+++ b/pkg/models/gauge.go	Thu Dec 20 15:33:29 2018 +0100
@@ -22,8 +22,9 @@
 
 // GaugeMeasurementImport contains data used to define the endpoint
 type GaugeMeasurementImport struct {
-	URL      string `json:"url"`
-	Insecure bool   `json:"insecure"`
+	URL       string `json:"url"`
+	Insecure  bool   `json:"insecure"`
+	SendEmail bool   `json:"send-email"`
 }
 
 // GaugeMeasurement holds information about a gauge and the latest measurement