diff --git a/sync/dash.go b/sync/dash.go
index 04b464ac..11858361 100644
--- a/sync/dash.go
+++ b/sync/dash.go
@@ -27,18 +27,34 @@ var (
districtT = buildTemplate("district", "base")
settingsT = buildTemplate("settings", "authenticated")
sourceT = buildTemplate("source", "authenticated")
+ trapT = buildTemplate("trap", "authenticated")
)
type Config struct {
URLTegola string
}
+type ContentSource struct {
+ Inspections []Inspection
+ MapData ComponentMap
+ Source *BreedingSourceDetail
+ Traps []TrapNearby
+ Treatments []Treatment
+ //TreatmentCadence TreatmentCadence
+ TreatmentModels []TreatmentModel
+ User User
+}
+type ContentTrap struct {
+ MapData ComponentMap
+ Trap Trap
+ User User
+}
type ContextCell struct {
BreedingSources []BreedingSourceSummary
CellBoundary h3.CellBoundary
Inspections []Inspection
MapData ComponentMap
- Traps []Trap
+ Traps []TrapSummary
Treatments []Treatment
User User
}
@@ -133,6 +149,20 @@ func getSource(w http.ResponseWriter, r *http.Request, u *models.User) {
source(w, r, u, globalid)
}
+func getTrap(w http.ResponseWriter, r *http.Request, u *models.User) {
+ globalid_s := chi.URLParam(r, "globalid")
+ if globalid_s == "" {
+ respondError(w, "No globalid provided", nil, http.StatusBadRequest)
+ return
+ }
+ globalid, err := uuid.Parse(globalid_s)
+ if err != nil {
+ respondError(w, "globalid is not a UUID", nil, http.StatusBadRequest)
+ return
+ }
+ trap(w, r, u, globalid)
+}
+
func cell(ctx context.Context, w http.ResponseWriter, user *models.User, c int64) {
org, err := user.Organization().One(ctx, db.PGInstance.BobDB)
if err != nil {
@@ -342,3 +372,43 @@ func source(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.U
htmlpage.RenderOrError(w, sourceT, data)
}
+
+func trap(w http.ResponseWriter, r *http.Request, user *models.User, id uuid.UUID) {
+ org, err := user.Organization().One(r.Context(), db.PGInstance.BobDB)
+ if err != nil {
+ respondError(w, "Failed to get org", err, http.StatusInternalServerError)
+ return
+ }
+ userContent, err := contentForUser(r.Context(), user)
+ if err != nil {
+ respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
+ return
+ }
+ t, err := trapByGlobalId(r.Context(), org, id)
+ if err != nil {
+ respondError(w, "Failed to get trap", err, http.StatusInternalServerError)
+ return
+ }
+ latlng, err := t.H3Cell.LatLng()
+ if err != nil {
+ respondError(w, "Failed to get latlng", err, http.StatusInternalServerError)
+ return
+ }
+ data := ContentTrap{
+ MapData: ComponentMap{
+ Center: latlng,
+ //GeoJSON:
+ MapboxToken: config.MapboxToken,
+ Markers: []MapMarker{
+ MapMarker{
+ LatLng: latlng,
+ },
+ },
+ Zoom: 13,
+ },
+ Trap: t,
+ User: userContent,
+ }
+
+ htmlpage.RenderOrError(w, trapT, data)
+}
diff --git a/sync/model_conversion.go b/sync/model_conversion.go
index 31156274..9b4fa373 100644
--- a/sync/model_conversion.go
+++ b/sync/model_conversion.go
@@ -76,10 +76,13 @@ type BreedingSourceDetail struct {
Comments string `json:"comments"`
}
-type TrapNearby struct {
- Counts []*TrapCount
- Distance string
- ID uuid.UUID
+type Trap struct {
+ Active bool
+ Comments string
+ Collections []TrapData
+ Description string
+ GlobalID uuid.UUID
+ H3Cell h3.Cell
}
type TrapCount struct {
@@ -152,9 +155,18 @@ type TrapData struct {
LastEditedDate *time.Time `json:"lastEditedDate"`
LastEditedUser string `json:"lastEditedUser"`
Comments string `json:"comments"`
+
+ // Stuff I actually use
+ Count TrapCount
}
-type Trap struct {
+type TrapNearby struct {
+ Counts []*TrapCount
+ Distance string
+ ID uuid.UUID
+}
+
+type TrapSummary struct {
Active bool
Comments string
Description string
@@ -169,9 +181,57 @@ type Treatment struct {
Product string
}
-func toTemplateTrap(traps models.FieldseekerTraplocationSlice) (results []Trap, err error) {
+func toTemplateTrap(trap *models.FieldseekerTraplocation, trap_data []sql.TrapDataByLocationIDRecentRow, count_slice []sql.TrapCountByLocationIDRow) (result Trap, err error) {
+ log.Debug().Str("globalid", trap.Globalid.String()).Msg("Working on trap")
+ cell, err := h3utils.ToCell(trap.H3cell.MustGet())
+ if err != nil {
+ return result, fmt.Errorf("Failed to convert h3 cell: %w", err)
+ }
+
+ count_by_trapdata_id := make(map[uuid.UUID]TrapCount, 0)
+ for _, count := range count_slice {
+ count_by_trapdata_id[count.TrapdataGlobalid] = TrapCount{
+ Ended: count.TrapdataEnddate.MustGet(),
+ Females: int(count.TotalFemales),
+ Males: int(count.TotalMales),
+ Total: int(count.Total),
+ }
+ }
+
+ data_by_id := make(map[uuid.UUID]TrapData, 0)
+ for _, dt := range trap_data {
+ if dt.LocID != trap.Globalid {
+ return result, fmt.Errorf("Bad query")
+ }
+ log.Debug().Str("trapdata", dt.Globalid.String()).Msg("Aggregating trapdata")
+ count, ok := count_by_trapdata_id[dt.Globalid]
+ if !ok {
+ count = TrapCount{}
+ }
+ data_by_id[dt.Globalid] = TrapData{
+ Count: count,
+ EndDateTime: &dt.Enddatetime,
+ GlobalID: dt.Globalid,
+ }
+ }
+ data := make([]TrapData, 0)
+ for _, v := range data_by_id {
+ data = append(data, v)
+ }
+
+ return Trap{
+ Active: toBool16Or(trap.Active, false),
+ Comments: trap.Comments.GetOr(""),
+ Collections: data,
+ Description: trap.Description.GetOr(""),
+ GlobalID: trap.Globalid,
+ H3Cell: cell,
+ }, nil
+}
+
+func toTemplateTrapSummary(traps models.FieldseekerTraplocationSlice) (results []TrapSummary, err error) {
for _, t := range traps {
- results = append(results, Trap{
+ results = append(results, TrapSummary{
Active: toBool16Or(t.Active, false),
Comments: t.Comments.GetOr(""),
Description: t.Description.GetOr(""),
diff --git a/sync/routes.go b/sync/routes.go
index 89cf6444..6fa8ae8d 100644
--- a/sync/routes.go
+++ b/sync/routes.go
@@ -66,6 +66,7 @@ func Router() chi.Router {
r.Method("GET", "/cell/{cell}", auth.NewEnsureAuth(getCellDetails))
r.Method("GET", "/settings", auth.NewEnsureAuth(getSettings))
r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource))
+ r.Method("GET", "/trap/{globalid}", auth.NewEnsureAuth(getTrap))
htmlpage.AddStaticRoute(r, "/static")
return r
diff --git a/sync/template/trap.html b/sync/template/trap.html
new file mode 100644
index 00000000..3f22a94e
--- /dev/null
+++ b/sync/template/trap.html
@@ -0,0 +1,131 @@
+{{template "authenticated.html" .}}
+
+{{define "title"}}Dash{{end}}
+{{define "extraheader"}}
+{{template "map" .MapData}}
+
+{{end}}
+{{define "content"}}
+
+
+
+
+
+
+
+
+
+
Trap ID: {{ .Trap.GlobalID }}
+
+
+ | Active: |
+ {{ .Trap.Active }} |
+
+
+ | Comments: |
+ {{ .Trap.Comments }} |
+
+
+ | Description: |
+ {{ .Trap.Description }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Collection Date |
+ Collection ID |
+ Females |
+ Male |
+ Total |
+
+
+
+
+ {{ range .Trap.Collections }}
+
+ | {{ .EndDateTime|timeSince }} |
+ {{ .GlobalID }} |
+ {{ .Count.Females }} |
+ {{ .Count.Males }} |
+ {{ .Count.Total }} |
+
+ {{ end }}
+
+
+
+
+
+
+{{end}}
diff --git a/sync/types.go b/sync/types.go
index a31b3712..8306d280 100644
--- a/sync/types.go
+++ b/sync/types.go
@@ -65,16 +65,6 @@ type ContentSignin struct {
InvalidCredentials bool
}
type ContentSignup struct{}
-type ContentSource struct {
- Inspections []Inspection
- MapData ComponentMap
- Source *BreedingSourceDetail
- Traps []TrapNearby
- Treatments []Treatment
- //TreatmentCadence TreatmentCadence
- TreatmentModels []TreatmentModel
- User User
-}
type Inspection struct {
Action string
Date *time.Time
diff --git a/sync/utils.go b/sync/utils.go
index 7cba5560..94f2dcd7 100644
--- a/sync/utils.go
+++ b/sync/utils.go
@@ -203,7 +203,28 @@ func treatmentsBySource(ctx context.Context, org *models.Organization, sourceID
return toTemplateTreatment(rows)
}
-func trapsByCell(ctx context.Context, org *models.Organization, c h3.Cell) (results []Trap, err error) {
+func trapByGlobalId(ctx context.Context, org *models.Organization, id uuid.UUID) (result Trap, err error) {
+ row, err := org.Traplocations(
+ sm.Where(models.FieldseekerTraplocations.Columns.Globalid.EQ(psql.Arg(id))),
+ ).One(ctx, db.PGInstance.BobDB)
+ if err != nil {
+ return result, fmt.Errorf("Failed to get trap location: %w", err)
+ }
+
+ trap_data, err := sql.TrapDataByLocationIDRecent(org.ID, []uuid.UUID{id}).All(ctx, db.PGInstance.BobDB)
+ if err != nil {
+ return result, fmt.Errorf("Failed to query trap data: %w", err)
+ }
+
+ counts, err := sql.TrapCountByLocationID(org.ID, []uuid.UUID{id}).All(ctx, db.PGInstance.BobDB)
+ if err != nil {
+ return result, fmt.Errorf("Failed to query trap counts: %w", err)
+ }
+
+ return toTemplateTrap(row, trap_data, counts)
+}
+
+func trapsByCell(ctx context.Context, org *models.Organization, c h3.Cell) (results []TrapSummary, err error) {
boundary, err := c.Boundary()
if err != nil {
return results, fmt.Errorf("Failed to get cell boundary: %w", err)
@@ -218,7 +239,7 @@ func trapsByCell(ctx context.Context, org *models.Organization, c h3.Cell) (resu
if err != nil {
return results, fmt.Errorf("Failed to query rows: %w", err)
}
- return toTemplateTrap(rows)
+ return toTemplateTrapSummary(rows)
}
func treatmentsByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]Treatment, error) {