changeset 5560:f2204f91d286

Join the log lines of imports to the log exports to recover data from them. Used in SR export to extract information that where in the meta json but now are only found in the log.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 09 Feb 2022 18:34:40 +0100
parents ce9a9a1bf92f
children b91716d2acc6
files pkg/controllers/importqueue.go pkg/imports/bn.go pkg/imports/dma.go pkg/imports/dmv.go pkg/imports/dsr.go pkg/imports/fa.go pkg/imports/fd.go pkg/imports/gm.go pkg/imports/report.go pkg/imports/sec.go pkg/imports/sr.go
diffstat 11 files changed, 167 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/importqueue.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/controllers/importqueue.go	Wed Feb 09 18:34:40 2022 +0100
@@ -65,15 +65,17 @@
   state::varchar,
   enqueued,
   changed,
-  kind,
+  imports.kind,
   username,
   (SELECT country FROM users.list_users lu
     WHERE lu.username = import.imports.username) AS country,
   signer,
   EXISTS(SELECT 1 FROM import.import_logs
     WHERE kind = 'warn'::log_type and import_id = imports.id) AS has_warnings,
-  data
-FROM import.imports
+  data,
+  il.msg
+FROM import.imports RIGHT JOIN import.import_logs il
+  ON import.imports.id = il.import_id
 WHERE
 `
 	selectEnqueuedSQL = `
@@ -122,11 +124,11 @@
 	}
 
 	// Always filter review jobs. They are only for internal use.
-	cond(` NOT kind LIKE '%%` + imports.ReviewJobSuffix + `'`)
+	cond(` NOT imports.kind LIKE '%%` + imports.ReviewJobSuffix + `'`)
 
 	if query := req.FormValue("query"); query != "" {
 		query = "%" + query + "%"
-		cond(` (kind ILIKE $%d OR username ILIKE $%d OR signer ILIKE $%d OR `+
+		cond(` (imports.kind ILIKE $%d OR username ILIKE $%d OR signer ILIKE $%d OR `+
 			`id IN (SELECT import_id FROM import.import_logs WHERE msg ILIKE $%d)) `,
 			query, query, query, query)
 	}
@@ -146,7 +148,7 @@
 
 	if ks := req.FormValue("kinds"); ks != "" {
 		kinds := toTextArray(ks, imports.ImportKindNames())
-		cond(" kind = ANY($%d) ", kinds)
+		cond(" imports.kind = ANY($%d) ", kinds)
 	}
 
 	if idss := req.FormValue("ids"); idss != "" {
@@ -305,65 +307,109 @@
 
 	// Extract some meta infos from the import.
 	type Description interface {
-		Description() (string, error)
+		Description([]string) (string, error)
+	}
+
+	type dataset struct {
+		id       int64
+		state    string
+		enqueued time.Time
+		changed  time.Time
+		kind     string
+		user     string
+		country  string
+		signer   sql.NullString
+		warnings bool
+		data     string
+		msgs     []string
 	}
 
+	// Log unsupported description interfaces per kind only once.
+	unsupported := make(map[string]bool)
+
+	store := func(ds *dataset) error {
+		if ds == nil {
+			return nil
+		}
+
+		var description string
+
+		// Do some introspection on the job to be more verbose.
+		if jc := imports.FindJobCreator(imports.JobKind(ds.kind)); jc != nil {
+			job := jc.Create()
+			if err := common.FromJSONString(ds.data, job); err != nil {
+				log.Errorf("%v\n", err)
+			} else if desc, ok := job.(Description); ok {
+				description, err = desc.Description(ds.msgs)
+				if err != nil {
+					log.Errorf("%v\n", err)
+				}
+				description = strings.Replace(description, ",", "|", -1)
+			} else {
+				if !unsupported[ds.kind] {
+					unsupported[ds.kind] = true
+					log.Debugf("%s: description not supported\n", ds.kind)
+				}
+			}
+		}
+
+		record[0] = strconv.FormatInt(ds.id, 10)
+		record[1] = ds.kind
+		record[2] = ds.enqueued.UTC().Format(common.TimeFormat)
+		record[3] = ds.changed.UTC().Format(common.TimeFormat)
+		record[4] = ds.user
+		record[5] = ds.country
+		record[6] = stringString(ds.signer)
+		record[7] = ds.state
+		record[8] = strconv.FormatBool(ds.warnings)
+		record[9] = description
+
+		return out.Write(record)
+	}
+
+	var last *dataset
+
 	for rows.Next() {
 		var (
-			id          int64
-			state       string
-			enqueued    time.Time
-			changed     time.Time
-			kind        string
-			user        string
-			country     string
-			signer      sql.NullString
-			warnings    bool
-			data        string
-			description string
+			curr dataset
+			msg  sql.NullString
 		)
 		if err = rows.Scan(
-			&id,
-			&state,
-			&enqueued,
-			&changed,
-			&kind,
-			&user,
-			&country,
-			&signer,
-			&warnings,
-			&data,
+			&curr.id,
+			&curr.state,
+			&curr.enqueued,
+			&curr.changed,
+			&curr.kind,
+			&curr.user,
+			&curr.country,
+			&curr.signer,
+			&curr.warnings,
+			&curr.data,
+			&msg,
 		); err != nil {
 			return
 		}
 
-		// Do some introspection on the job to be more verbose.
-		if jc := imports.FindJobCreator(imports.JobKind(kind)); jc != nil {
-			job := jc.Create()
-			if err := common.FromJSONString(data, job); err != nil {
-				log.Errorf("%v\n", err)
-			} else if desc, ok := job.(Description); ok {
-				if description, err = desc.Description(); err != nil {
-					log.Errorf("%v\n", err)
-				}
+		if last != nil && last.id == curr.id {
+			if msg.Valid {
+				last.msgs = append(last.msgs, msg.String)
 			}
+			continue
+		}
+		if msg.Valid {
+			curr.msgs = append(curr.msgs, msg.String)
 		}
 
-		record[0] = strconv.FormatInt(id, 10)
-		record[1] = kind
-		record[2] = enqueued.UTC().Format(common.TimeFormat)
-		record[3] = changed.UTC().Format(common.TimeFormat)
-		record[4] = user
-		record[5] = country
-		record[6] = stringString(signer)
-		record[7] = state
-		record[8] = strconv.FormatBool(warnings)
-		record[9] = strings.Replace(description, ",", "|", -1)
-
-		if err := out.Write(record); err != nil {
+		if err := store(last); err != nil {
 			log.Errorf("%v\n", err)
 			return
 		}
+		last = &curr
+	}
+
+	if err := store(last); err != nil {
+		log.Errorf("%v\n", err)
+		return
 	}
 
 	out.Flush()
--- a/pkg/imports/bn.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/bn.go	Wed Feb 09 18:34:40 2022 +0100
@@ -160,7 +160,7 @@
 )
 
 // Description gives a short info about relevant facts of this import.
-func (bn *Bottleneck) Description() (string, error) {
+func (bn *Bottleneck) Description([]string) (string, error) {
 	return bn.URL, nil
 }
 
--- a/pkg/imports/dma.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/dma.go	Wed Feb 09 18:34:40 2022 +0100
@@ -42,7 +42,7 @@
 }
 
 // Description gives a short info about relevant facts of this import.
-func (dma *DistanceMarksAshore) Description() (string, error) {
+func (dma *DistanceMarksAshore) Description([]string) (string, error) {
 	return dma.URL + "|" + dma.FeatureType, nil
 }
 
--- a/pkg/imports/dmv.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/dmv.go	Wed Feb 09 18:34:40 2022 +0100
@@ -37,7 +37,7 @@
 }
 
 // Description gives a short info about relevant facts of this import.
-func (dmv *DistanceMarksVirtual) Description() (string, error) {
+func (dmv *DistanceMarksVirtual) Description([]string) (string, error) {
 	return dmv.URL, nil
 }
 
--- a/pkg/imports/dsr.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/dsr.go	Wed Feb 09 18:34:40 2022 +0100
@@ -30,7 +30,7 @@
 }
 
 // Description gives a short info about relevant facts of this import.
-func (dsr *DeleteSoundingResult) Description() (string, error) {
+func (dsr *DeleteSoundingResult) Description([]string) (string, error) {
 	return dsr.BottleneckID + "|" + dsr.Date.Format(common.DateFormat), nil
 }
 
--- a/pkg/imports/fa.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/fa.go	Wed Feb 09 18:34:40 2022 +0100
@@ -160,7 +160,7 @@
 )
 
 // Description gives a short info about relevant facts of this import.
-func (fa *FairwayAvailability) Description() (string, error) {
+func (fa *FairwayAvailability) Description([]string) (string, error) {
 	return fa.URL, nil
 }
 
--- a/pkg/imports/fd.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/fd.go	Wed Feb 09 18:34:40 2022 +0100
@@ -48,7 +48,7 @@
 }
 
 // Description gives a short info about relevant facts of this import.
-func (fd *FairwayDimension) Description() (string, error) {
+func (fd *FairwayDimension) Description([]string) (string, error) {
 	return strings.Join([]string{
 		fd.URL,
 		fd.FeatureType,
@@ -486,7 +486,7 @@
 					oldID,
 				).Scan(&fdid, &lat, &lon)
 			}); err != nil {
-				feedback.Error(pgxutils.ReadableError{Err: err}.Error() +
+				feedback.Error(pgxutils.ReadableError{Err: err}.Error()+
 					"- while tracking invalidation of: %d", oldID)
 				continue
 			}
--- a/pkg/imports/gm.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/gm.go	Wed Feb 09 18:34:40 2022 +0100
@@ -43,7 +43,7 @@
 }
 
 // Description gives a short info about relevant facts of this import.
-func (gm *GaugeMeasurement) Description() (string, error) {
+func (gm *GaugeMeasurement) Description([]string) (string, error) {
 	return gm.URL, nil
 }
 
--- a/pkg/imports/report.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/report.go	Wed Feb 09 18:34:40 2022 +0100
@@ -87,7 +87,7 @@
 // RequiresRoles enforces to be a sys_admin to run this .
 func (*Report) RequiresRoles() auth.Roles { return auth.Roles{"sys_admin"} }
 
-func (r *Report) Description() (string, error) { return string(r.Name), nil }
+func (r *Report) Description([]string) (string, error) { return string(r.Name), nil }
 
 func (*Report) CleanUp() error { return nil }
 
--- a/pkg/imports/sec.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/sec.go	Wed Feb 09 18:34:40 2022 +0100
@@ -35,7 +35,7 @@
 }
 
 // Description gives a short info about relevant facts of this import.
-func (sec *Section) Description() (string, error) {
+func (sec *Section) Description([]string) (string, error) {
 	return strings.Join([]string{
 		sec.Name,
 		sec.ObjNam,
--- a/pkg/imports/sr.go	Thu Dec 02 12:37:33 2021 +0100
+++ b/pkg/imports/sr.go	Wed Feb 09 18:34:40 2022 +0100
@@ -28,13 +28,16 @@
 	"os"
 	"path"
 	"path/filepath"
+	"regexp"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	shp "github.com/jonas-p/go-shp"
 
 	"gemma.intevation.de/gemma/pkg/common"
+	"gemma.intevation.de/gemma/pkg/log"
 	"gemma.intevation.de/gemma/pkg/mesh"
 	"gemma.intevation.de/gemma/pkg/models"
 	"gemma.intevation.de/gemma/pkg/wkb"
@@ -239,23 +242,81 @@
 `
 )
 
+var (
+	bottleneckRe,
+	surveyTypeRe,
+	dateRe,
+	negateZRe *regexp.Regexp
+	recoverRegsOnce sync.Once
+)
+
+func compileRecoverRegs() {
+	bottleneckRe = regexp.MustCompile(`Bottleneck:\s*(.+)\s*$`)
+	surveyTypeRe = regexp.MustCompile(`Processing as\s+([^\s]+)\s+beam scan.`)
+	dateRe = regexp.MustCompile(`Survey date:\s*(\d{4}-\d{2}-\d{2}).`)
+	negateZRe = regexp.MustCompile(`Z values will be negated.`)
+}
+
 // Description gives a short info about relevant facts of this import.
-func (sr *SoundingResult) Description() (string, error) {
+func (sr *SoundingResult) Description(msgs []string) (string, error) {
+
+	recoverRegsOnce.Do(compileRecoverRegs)
+
+	//log.Debugln(strings.Join(msgs, "\n") + "\n\n")
+
+	var (
+		bottleneck, st, date string
+		negZ                 *bool
+	)
+
+	for _, msg := range msgs {
+		if m := bottleneckRe.FindStringSubmatch(msg); m != nil {
+			bottleneck = m[1]
+			continue
+		}
+		if m := surveyTypeRe.FindStringSubmatch(msg); m != nil {
+			st = m[1]
+			continue
+		}
+		if m := dateRe.FindStringSubmatch(msg); m != nil {
+			date = m[1]
+			continue
+		}
+		if negateZRe.MatchString(msg) {
+			t := true
+			negZ = &t
+		}
+	}
 
 	var descs []string
 
 	if sr.Bottleneck != nil {
 		descs = append(descs, *sr.Bottleneck)
+	} else if bottleneck != "" {
+		log.Debugf("bottleneck recovered: %s\n", bottleneck)
+		descs = append(descs, bottleneck)
 	}
+
 	if sr.Date != nil {
 		descs = append(descs, (*sr).Date.Format(common.DateFormat))
+	} else if date != "" {
+		log.Debugf("date recovered: %s\n", date)
+		descs = append(descs, date)
 	}
+
 	if sr.NegateZ != nil && *sr.NegateZ {
+	} else if negZ != nil && *negZ {
+		log.Debugln("negateZ recovered")
 		descs = append(descs, "negateZ")
 	}
+
 	if sr.SurveyType != nil {
 		descs = append(descs, string(*sr.SurveyType))
+	} else if st != "" {
+		log.Debugf("survey type recovered: %s\n", st)
+		descs = append(descs, st)
 	}
+
 	return strings.Join(descs, "|"), nil
 }