From a8fc54717065a6a2f6a4043bfed06b95e13c074f Mon Sep 17 00:00:00 2001 From: Mark Chadwick Date: Tue, 20 Jun 2023 17:49:22 +1200 Subject: [PATCH] feat(notes): add a environment notes table --- environment/notes.csv | 1 + meta/generate/main.go | 2 + meta/notes.go | 96 +++++++++++++++++++++++++++++++++++++++ meta/notes_test.go | 25 ++++++++++ meta/set.go | 3 ++ meta/set_auto.go | 21 +++++++++ meta/testdata/notes.csv | 4 ++ tests/consistency_test.go | 1 + tests/notes_test.go | 43 ++++++++++++++++++ 9 files changed, 196 insertions(+) create mode 100644 environment/notes.csv create mode 100644 meta/notes.go create mode 100644 meta/notes_test.go create mode 100644 meta/testdata/notes.csv create mode 100644 tests/notes_test.go diff --git a/environment/notes.csv b/environment/notes.csv new file mode 100644 index 000000000..d20d0cc86 --- /dev/null +++ b/environment/notes.csv @@ -0,0 +1 @@ +Code,Network,Entry diff --git a/meta/generate/main.go b/meta/generate/main.go index 659ddf445..1fb83baca 100644 --- a/meta/generate/main.go +++ b/meta/generate/main.go @@ -34,6 +34,7 @@ func main() { "monuments": {"Monument"}, "mounts": {"Mount"}, "networks": {"Network"}, + "notes": {"Note"}, "placenames": {"Placename"}, "polarities": {"Polarity"}, "preamps": {"Preamp"}, @@ -59,6 +60,7 @@ func main() { "monuments": {"Monument", []string{"mark"}}, "mounts": {"Mount", []string{"code"}}, "networks": {"Network", []string{"code"}}, + "notes": {"Note", []string{"code", "network"}}, "placenames": {"Placename", []string{"name"}}, "samples": {"Sample", []string{"code"}}, "sites": {"Site", []string{"station", "location"}}, diff --git a/meta/notes.go b/meta/notes.go new file mode 100644 index 000000000..2c761c60c --- /dev/null +++ b/meta/notes.go @@ -0,0 +1,96 @@ +package meta + +import ( + "sort" + "strings" +) + +const ( + noteCode = iota + noteNetwork + noteEntry + noteLast +) + +var noteHeaders Header = map[string]int{ + "Code": noteCode, + "Network": noteNetwork, + "Entry": noteEntry, +} + +type Note struct { + Code string + Network string + Entry string +} + +type NoteList []Note + +func (n NoteList) Len() int { return len(n) } +func (n NoteList) Swap(i, j int) { n[i], n[j] = n[j], n[i] } +func (n NoteList) Less(i, j int) bool { + switch { + case n[i].Code < n[j].Code: + return true + case n[i].Code > n[j].Code: + return false + case n[i].Network < n[j].Network: + return true + case n[i].Network > n[j].Network: + return false + case n[i].Entry < n[j].Entry: + return true + default: + return false + } +} + +func (n NoteList) encode() [][]string { + var data [][]string + + data = append(data, noteHeaders.Columns()) + + for _, l := range n { + data = append(data, []string{ + strings.TrimSpace(l.Code), + strings.TrimSpace(l.Network), + strings.TrimSpace(l.Entry), + }) + } + return data +} + +func (n *NoteList) decode(data [][]string) error { + if !(len(data) > 1) { + return nil + } + + var notes []Note + + fields := noteHeaders.Fields(data[0]) + for _, v := range data[1:] { + d := fields.Remap(v) + + notes = append(notes, Note{ + Code: strings.TrimSpace(d[noteCode]), + Network: strings.TrimSpace(d[noteNetwork]), + Entry: strings.TrimSpace(d[noteEntry]), + }) + } + + *n = NoteList(notes) + + return nil +} + +func LoadNotes(path string) ([]Note, error) { + var v []Note + + if err := LoadList(path, (*NoteList)(&v)); err != nil { + return nil, err + } + + sort.Sort(NoteList(v)) + + return v, nil +} diff --git a/meta/notes_test.go b/meta/notes_test.go new file mode 100644 index 000000000..c0fce7582 --- /dev/null +++ b/meta/notes_test.go @@ -0,0 +1,25 @@ +package meta + +import ( + "testing" +) + +func TestNoteList(t *testing.T) { + t.Run("check notes", testListFunc("testdata/notes.csv", &NoteList{ + Note{ + Code: "ARTA", + Network: "CG", + Entry: "Concrete pillar with 3 stainless rods drilled to 0.5m into concrete butress.Note that between 2006-10-12 and 2006-11-08 the existing pillar adjacent to this one was occupied using the code ATIA as a test.", + }, + Note{ + Code: "ATIA", + Network: "XX", + Entry: "Test deployment for site ARTA", + }, + Note{ + Code: "AUCK", + Network: "LI", + Entry: "Site upgraded to Trimble NETRS/Trimble Zephyr combination on 3rd November 2005", + }, + })) +} diff --git a/meta/set.go b/meta/set.go index 7b932bebe..6a3ad672f 100644 --- a/meta/set.go +++ b/meta/set.go @@ -45,6 +45,7 @@ const ( ConstituentsFile = "environment/constituents.csv" FeaturesFile = "environment/features.csv" GaugesFile = "environment/gauges.csv" + NotesFile = "environment/notes.csv" PlacenamesFile = "environment/placenames.csv" VisibilityFile = "environment/visibility.csv" @@ -95,6 +96,7 @@ type Set struct { constituents ConstituentList features FeatureList gauges GaugeList + notes NoteList placenames PlacenameList visibilities VisibilityList @@ -139,6 +141,7 @@ func (s *Set) files() map[string]List { ConstituentsFile: &s.constituents, FeaturesFile: &s.features, GaugesFile: &s.gauges, + NotesFile: &s.notes, PlacenamesFile: &s.placenames, VisibilityFile: &s.visibilities, diff --git a/meta/set_auto.go b/meta/set_auto.go index 1385c43ac..0710f60d8 100644 --- a/meta/set_auto.go +++ b/meta/set_auto.go @@ -185,6 +185,13 @@ func (s Set) Networks() []Network { return networks } +// Notes is a helper function to return a slice copy of Note values. +func (s Set) Notes() []Note { + notes := make([]Note, len(s.notes)) + copy(notes, s.notes) + return notes +} + // Placenames is a helper function to return a slice copy of Placename values. func (s Set) Placenames() []Placename { placenames := make([]Placename, len(s.placenames)) @@ -345,6 +352,20 @@ func (s Set) Network(code string) (Network, bool) { return Network{}, false } +// Note is a helper function to return a Note value and true if one exists. +func (s Set) Note(code, network string) (Note, bool) { + for _, v := range s.notes { + if code != v.Code { + continue + } + if network != v.Network { + continue + } + return v, true + } + return Note{}, false +} + // Placename is a helper function to return a Placename value and true if one exists. func (s Set) Placename(name string) (Placename, bool) { for _, v := range s.placenames { diff --git a/meta/testdata/notes.csv b/meta/testdata/notes.csv new file mode 100644 index 000000000..824ed018b --- /dev/null +++ b/meta/testdata/notes.csv @@ -0,0 +1,4 @@ +Code,Network,Entry +ARTA,CG,Concrete pillar with 3 stainless rods drilled to 0.5m into concrete butress.Note that between 2006-10-12 and 2006-11-08 the existing pillar adjacent to this one was occupied using the code ATIA as a test. +ATIA,XX,Test deployment for site ARTA +AUCK,LI,Site upgraded to Trimble NETRS/Trimble Zephyr combination on 3rd November 2005 diff --git a/tests/consistency_test.go b/tests/consistency_test.go index 6648a9809..b4c668b33 100644 --- a/tests/consistency_test.go +++ b/tests/consistency_test.go @@ -62,6 +62,7 @@ func TestConsistency(t *testing.T) { "gauges": {f: "../environment/gauges.csv", l: &meta.GaugeList{}}, "constituents": {f: "../environment/constituents.csv", l: &meta.ConstituentList{}}, "features": {f: "../environment/features.csv", l: &meta.FeatureList{}}, + "notes": {f: "../environment/notes.csv", l: &meta.NoteList{}}, "visibility": {f: "../environment/visibility.csv", l: &meta.VisibilityList{}}, "citations": {f: "../references/citations.csv", l: &meta.CitationList{}}, } diff --git a/tests/notes_test.go b/tests/notes_test.go new file mode 100644 index 000000000..64015573d --- /dev/null +++ b/tests/notes_test.go @@ -0,0 +1,43 @@ +package delta_test + +import ( + "testing" + + "github.com/GeoNet/delta" + "github.com/GeoNet/delta/meta" +) + +var noteChecks = map[string]func(*meta.Set) func(t *testing.T){ + + "check for duplicated notes": func(set *meta.Set) func(t *testing.T) { + return func(t *testing.T) { + notes := set.Notes() + for i := 0; i < len(notes); i++ { + for j := i + 1; j < len(notes); j++ { + if notes[i].Code != notes[j].Code { + continue + } + if notes[i].Network != notes[j].Network { + continue + } + if notes[i].Entry != notes[j].Entry { + continue + } + t.Errorf("note duplication: %s/%s", notes[i].Code, notes[i].Network) + } + } + } + }, +} + +func TestNotes(t *testing.T) { + + set, err := delta.New() + if err != nil { + t.Fatal(err) + } + + for k, v := range noteChecks { + t.Run(k, v(set)) + } +}