From 5dd0b8c1623c9641d9e5c5c91cb51c619a35b378 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Fri, 2 Jan 2026 08:58:57 -0700 Subject: [PATCH] Checkpoin on initial idea for passing through models --- api/api.go | 37 ++-- api/endpoint.go | 2 +- api/types.go | 550 +++--------------------------------------------- 3 files changed, 57 insertions(+), 532 deletions(-) diff --git a/api/api.go b/api/api.go index f01b807e..4fcbec9e 100644 --- a/api/api.go +++ b/api/api.go @@ -13,6 +13,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/platform" "github.com/Gleipnir-Technology/nidus-sync/queue" "github.com/Gleipnir-Technology/nidus-sync/userfile" "github.com/go-chi/chi/v5" @@ -64,24 +65,36 @@ func apiAudioContentPost(w http.ResponseWriter, r *http.Request, u *models.User) w.WriteHeader(http.StatusOK) } -func apiClientIos(w http.ResponseWriter, r *http.Request, u *models.User) { - sources, err := db.MosquitoSourceQuery() +func handleClientIos(w http.ResponseWriter, r *http.Request, u *models.User) { + var sinceStr string + err := r.ParseForm() if err != nil { - render.Render(w, r, errRender(err)) + render.Render(w, r, errRender(fmt.Errorf("Failed to parse GET form: %w", err))) return + } else { + sinceStr = r.FormValue("since") } - requests, err := db.ServiceRequestQuery() - if err != nil { - render.Render(w, r, errRender(err)) - return + + var since *time.Time + if sinceStr == "" { + since = nil + } else { + since, err = parseTime(sinceStr) + if err != nil { + render.Render(w, r, errRender(fmt.Errorf("Failed to parse 'since' value: %w", err))) + return + } } - traps, err := db.TrapDataQuery() + + csync, err := platform.ContentClientIos(r.Context(), u, since) if err != nil { render.Render(w, r, errRender(err)) return } - response := NewResponseClientIos(sources, requests, traps) + response := ResponseClientIos{ + Fieldseeker: toResponseFieldseeker(csync), + } if err := render.Render(w, r, response); err != nil { render.Render(w, r, errRender(err)) return @@ -324,12 +337,12 @@ func webhookFieldseeker(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } -func parseTime(x string) time.Time { +func parseTime(x string) (*time.Time, error) { created_epoch, err := strconv.ParseInt(x, 10, 64) if err != nil { - log.Error().Err(err).Msg("Unable to convert inspection timestamp") + return &time.Time{}, fmt.Errorf("Failed to parse time '%s': %w", x, err) } created := time.UnixMilli(created_epoch) - return created + return &created, nil } diff --git a/api/endpoint.go b/api/endpoint.go index ab413554..adc4b10b 100644 --- a/api/endpoint.go +++ b/api/endpoint.go @@ -13,7 +13,7 @@ func AddRoutes(r chi.Router) { r.Method("GET", "/mosquito-source", auth.NewEnsureAuth(apiMosquitoSource)) r.Method("GET", "/service-request", auth.NewEnsureAuth(apiServiceRequest)) r.Method("GET", "/trap-data", auth.NewEnsureAuth(apiTrapData)) - r.Method("GET", "/client/ios", auth.NewEnsureAuth(apiClientIos)) + r.Method("GET", "/client/ios", auth.NewEnsureAuth(handleClientIos)) r.Method("PUT", "/client/ios/note/{uuid}", auth.NewEnsureAuth(apiClientIosNotePut)) r.Method("POST", "/audio/{uuid}", auth.NewEnsureAuth(apiAudioPost)) r.Method("POST", "/audio/{uuid}/content", auth.NewEnsureAuth(apiAudioContentPost)) diff --git a/api/types.go b/api/types.go index 1ad1c4ca..7de9bf4b 100644 --- a/api/types.go +++ b/api/types.go @@ -2,12 +2,11 @@ package api import ( "net/http" - "sort" "time" - "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/platform" "github.com/go-chi/render" - "github.com/google/uuid" ) type H3Cell uint64 @@ -15,104 +14,6 @@ type H3Cell uint64 type hasCreated interface { getCreated() string } - -type FS_Geometry struct { - X float64 `db:"X"` - Y float64 `db:"Y"` -} - -func (geo FS_Geometry) Latitude() float64 { - return geo.Y -} -func (geo FS_Geometry) Longitude() float64 { - return geo.X -} - -type FS_InspectionSample struct { - Geometry FS_Geometry `db:"geometry"` - CreationDate string `db:"creationdate"` - Creator string `db:"creator"` - EditDate string `db:"editdate"` - Editor string `db:"editor"` - IDByTech string `db:"idbytech"` - InspectionID string `db:"insp_id"` - Processed int `db:"processed"` - SampleID string `db:"sampleid"` -} - -type FS_MosquitoInspection struct { - ActionTaken *string `db:"actiontaken"` - Comments *string `db:"comments"` - Condition *string `db:"sitecond"` - EndDateTime string `db:"enddatetime"` - FieldTech *string `db:"fieldtech"` - GlobalID string `db:"globalid"` - LocationName *string `db:"locationname"` - PointLocationID string `db:"pointlocid"` - SiteCond *string `db:"sitecond"` - Zone *string `db:"zone"` -} - -type FS_PointLocation struct { - Access *string `db:"accessdesc"` - Active *int `db:"active"` - Comments *string `db:"comments"` - CreationDate *int64 `db:"creationdate"` - Description *string `db:"description"` - Geometry FS_Geometry `db:"geometry"` - GlobalID string `db:"globalid"` - Habitat *string `db:"habitat"` - Inspections MosquitoInspectionSlice - LastInspectDate *int64 `db:"lastinspectdate"` - Name *string `db:"name"` - NextActionDateScheduled *int64 `db:"nextactiondatescheduled"` - Treatments []MosquitoTreatment - UseType *string `db:"usetype"` - WaterOrigin *string `db:"waterorigin"` - Zone *string `db:"zone"` -} - -type FS_ServiceRequest struct { - AssignedTech *string `db:"assignedtech"` - CreationDate *int64 `db:"creationdate"` - City *string `db:"reqcity"` - Dog *int `db:"dog"` - Geometry FS_Geometry `db:"geometry"` - GlobalID string `db:"globalid"` - Priority *string `db:"priority"` - RecDateTime *int64 `db:"recdatetime"` - ReqAddr1 *string `db:"reqaddr1"` - ReqTarget *string `db:"reqtarget"` - ReqZip *string `db:"reqzip"` - Source *string `db:"source"` - Spanish *int `db:"spanish"` - Status *string `db:"status"` -} -type FS_TrapLocation struct { - Access *string `db:"accessdesc"` - CreationDate *int64 `db:"creationdate"` - Description *string `db:"description"` - Geometry FS_Geometry `db:"geometry"` - GlobalID string `db:"globalid"` - ObjectID int `db:"objectid"` - Name *string `db:"name"` -} - -type FS_Treatment struct { - Comments *string `db:"comments"` - EndDateTime *int64 `db:"enddatetime"` - FieldTech *string `db:"fieldtech"` - GlobalID string `db:"globalid"` - Habitat *string `db:"habitat"` - PointLocationID string `db:"pointlocid"` - Product *string `db:"product"` - Quantity float64 `db:"qty"` - QuantityUnit *string `db:"qtyunit"` - SiteCondition *string `db:"sitecond"` - TreatAcres *float64 `db:"treatacres"` - TreatHectares *float64 `db:"treathectares"` -} - /* type User struct { DisplayName string `db:"display_name"` @@ -140,416 +41,26 @@ func NewBounds() Bounds { } type MosquitoInspection struct { - data *FS_MosquitoInspection } - -func (mi MosquitoInspection) ActionTaken() string { - if mi.data.ActionTaken == nil { - return "" - } - return *mi.data.ActionTaken -} - -func (mi MosquitoInspection) Comments() string { - if mi.data.Comments == nil { - return "" - } - return *mi.data.Comments -} - -func (mi MosquitoInspection) Condition() string { - if mi.data.Condition == nil { - return "" - } - return *mi.data.Condition -} - -func (mi MosquitoInspection) Created() time.Time { - return parseTime(mi.data.EndDateTime) -} - -func (mi MosquitoInspection) FieldTechnician() string { - if mi.data.FieldTech == nil { - return "" - } - return *mi.data.FieldTech -} - -func (mi MosquitoInspection) ID() string { - return mi.data.GlobalID -} - -func (mi MosquitoInspection) LocationName() string { - if mi.data.LocationName == nil { - return "" - } - return *mi.data.LocationName -} - -func (mi MosquitoInspection) SiteCondition() string { - if mi.data.SiteCond == nil { - return "" - } - return *mi.data.SiteCond -} - -func NewMosquitoInspections(inspections []*FS_MosquitoInspection) []MosquitoInspection { - results := make([]MosquitoInspection, 0) - for _, t := range inspections { - results = append(results, MosquitoInspection{data: t}) - } - MosquitoInspectionSlice(results).Sort() - - return results -} - -type MosquitoInspectionSlice []MosquitoInspection -type ByCreatedMI []MosquitoInspection - -func (a ByCreatedMI) Len() int { return len(a) } -func (a ByCreatedMI) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByCreatedMI) Less(i, j int) bool { return a[i].Created().After(a[j].Created()) } - -func (inspections MosquitoInspectionSlice) Sort() { - sort.Sort(ByCreatedMI(inspections)) +type MosquitoTreatment struct { } type MosquitoSource struct { - location *FS_PointLocation + //location *FS_PointLocation Inspections []MosquitoInspection Treatments []MosquitoTreatment } -func (s MosquitoSource) Access() string { - if s.location.Access == nil { - return "" - } - return *s.location.Access -} - -func (s MosquitoSource) Active() *bool { - var result bool - if s.location.Active == nil { - return nil - } else if *s.location.Active == 0 { - result = false - } else { - result = true - } - return &result -} - -func (s MosquitoSource) Comments() string { - if s.location.Comments == nil { - return "" - } - return *s.location.Comments -} - -func (s MosquitoSource) Created() time.Time { - if s.location.CreationDate == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*s.location.CreationDate) -} - -func (s MosquitoSource) Description() string { - if s.location.Description == nil { - return "" - } - return *s.location.Description -} - -func (s MosquitoSource) ID() uuid.UUID { - return uuid.MustParse(s.location.GlobalID) -} -func (s MosquitoSource) Habitat() string { - if s.location.Habitat == nil { - return "" - } - return *s.location.Habitat -} - -func (s MosquitoSource) LastInspectionDate() time.Time { - if s.location.LastInspectDate == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*s.location.LastInspectDate) -} - -func (s MosquitoSource) Location() LatLong { - return s.location.Geometry -} - -func (s MosquitoSource) Name() string { - if s.location.Name == nil { - return "" - } - return *s.location.Name -} - -func (s MosquitoSource) NextActionDateScheduled() time.Time { - if s.location.NextActionDateScheduled == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*s.location.NextActionDateScheduled) -} - -func (s MosquitoSource) UseType() string { - if s.location.UseType == nil { - return "" - } - return *s.location.UseType -} -func (s MosquitoSource) WaterOrigin() string { - if s.location.WaterOrigin == nil { - return "" - } - return *s.location.WaterOrigin -} -func (s MosquitoSource) Zone() string { - if s.location.Zone == nil { - return "" - } - return *s.location.Zone -} -func NewMosquitoSource(location *FS_PointLocation, inspections []*FS_MosquitoInspection, treatments []*FS_Treatment) MosquitoSource { - return MosquitoSource{ - location: location, - Inspections: NewMosquitoInspections(inspections), - Treatments: NewMosquitoTreatments(treatments), - } -} - -type MosquitoTreatment struct { - data *FS_Treatment -} - -func (t MosquitoTreatment) Comments() string { - if t.data.Comments == nil { - return "" - } - return *t.data.Comments -} -func (t MosquitoTreatment) Created() time.Time { - if t.data.EndDateTime == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*t.data.EndDateTime) -} -func (t MosquitoTreatment) FieldTechnician() string { - if t.data.FieldTech == nil { - return "" - } - return *t.data.FieldTech -} -func (mi MosquitoTreatment) ID() string { - return mi.data.GlobalID -} -func (t MosquitoTreatment) Habitat() string { - if t.data.Habitat == nil { - return "" - } - return *t.data.Habitat -} -func (t MosquitoTreatment) Product() string { - if t.data.Product == nil { - return "" - } - return *t.data.Product -} -func (t MosquitoTreatment) Quantity() float64 { - return t.data.Quantity -} -func (t MosquitoTreatment) QuantityUnit() string { - if t.data.QuantityUnit == nil { - return "" - } - return *t.data.QuantityUnit -} -func (t MosquitoTreatment) SiteCondition() string { - if t.data.SiteCondition == nil { - return "" - } - return *t.data.SiteCondition -} -func (t MosquitoTreatment) TreatAcres() float64 { - if t.data.TreatAcres == nil { - return 0 - } - return *t.data.TreatAcres -} -func (t MosquitoTreatment) TreatHectares() float64 { - if t.data.TreatHectares == nil { - return 0 - } - return *t.data.TreatHectares -} -func NewMosquitoTreatments(treatments []*FS_Treatment) []MosquitoTreatment { - results := make([]MosquitoTreatment, 0) - for _, t := range treatments { - results = append(results, MosquitoTreatment{data: t}) - } - MosquitoTreatmentSlice(results).Sort() - return results -} - -type MosquitoTreatmentSlice []MosquitoTreatment -type ByCreatedMT []MosquitoTreatment - -func (a ByCreatedMT) Len() int { return len(a) } -func (a ByCreatedMT) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByCreatedMT) Less(i, j int) bool { return a[i].Created().After(a[j].Created()) } - -func (inspections MosquitoTreatmentSlice) Sort() { - sort.Sort(ByCreatedMT(inspections)) -} - type LatLong interface { Latitude() float64 Longitude() float64 } type ServiceRequest struct { - data *FS_ServiceRequest -} - -func (sr ServiceRequest) Address() string { - if sr.data.ReqAddr1 == nil { - return "" - } - return *sr.data.ReqAddr1 -} -func (sr ServiceRequest) AssignedTechnician() string { - if sr.data.AssignedTech == nil { - return "" - } - return *sr.data.AssignedTech -} -func (sr ServiceRequest) City() string { - if sr.data.City == nil { - return "" - } - return *sr.data.City -} -func (sr ServiceRequest) Created() time.Time { - if sr.data.CreationDate == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*sr.data.CreationDate) -} -func (sr ServiceRequest) HasDog() *bool { - var result bool - if sr.data.Dog == nil { - return nil - } else if *sr.data.Dog == 0 { - result = false - } else { - result = true - } - return &result -} -func (sr ServiceRequest) HasSpanishSpeaker() *bool { - var result bool - if sr.data.Spanish == nil { - return nil - } else if *sr.data.Spanish == 0 { - result = false - } else { - result = true - } - return &result -} -func (sr ServiceRequest) ID() uuid.UUID { - return uuid.MustParse(sr.data.GlobalID) -} -func (sr ServiceRequest) Location() LatLong { - return sr.data.Geometry -} -func (sr ServiceRequest) Priority() string { - if sr.data.Priority == nil { - return "" - } - return *sr.data.Priority -} -func (sr ServiceRequest) RecDateTime() time.Time { - if sr.data.RecDateTime == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*sr.data.RecDateTime) -} -func (sr ServiceRequest) Status() string { - if sr.data.Status == nil { - return "" - } - return *sr.data.Status -} -func (sr ServiceRequest) Source() string { - if sr.data.Source == nil { - return "" - } - return *sr.data.Source -} -func (sr ServiceRequest) Target() string { - if sr.data.ReqTarget == nil { - return "" - } - return *sr.data.ReqTarget -} -func (sr ServiceRequest) UseType() string { - return "" -} -func (sr ServiceRequest) WaterOrigin() string { - return "" -} -func (sr ServiceRequest) Zip() string { - if sr.data.ReqZip == nil { - return "" - } - return *sr.data.ReqZip -} -func NewServiceRequest(data *FS_ServiceRequest) ServiceRequest { - return ServiceRequest{data: data} } type TrapData struct { - data *FS_TrapLocation } - -func (tl TrapData) Access() string { - if tl.data.Access == nil { - return "" - } - return *tl.data.Access -} -func (tl TrapData) Created() time.Time { - if tl.data.CreationDate == nil { - return time.UnixMilli(0) - } - return time.UnixMilli(*tl.data.CreationDate) -} -func (tl TrapData) Description() string { - if tl.data.Description == nil { - return "" - } - return *tl.data.Description -} -func (tl TrapData) ID() uuid.UUID { - return uuid.MustParse(tl.data.GlobalID) -} -func (tl TrapData) Location() LatLong { - return tl.data.Geometry -} -func (tl TrapData) Name() string { - if tl.data.Name == nil { - return "" - } - return *tl.data.Name -} -func NewTrapData(data *FS_TrapLocation) TrapData { - return TrapData{data: data} -} - type Location struct { Latitude float64 Longitude float64 @@ -620,23 +131,19 @@ type NidusNotePayload struct { } -// ResponseErr renderer type for handling all sorts of errors. -type ResponseClientIos struct { +type ResponseFieldseeker struct { MosquitoSources []ResponseMosquitoSource `json:"sources"` ServiceRequests []ResponseServiceRequest `json:"requests"` TrapData []ResponseTrapData `json:"traps"` } +// ResponseErr renderer type for handling all sorts of errors. +type ResponseClientIos struct { + Fieldseeker ResponseFieldseeker `json:"fieldseeker"` +} func (i ResponseClientIos) Render(w http.ResponseWriter, r *http.Request) error { return nil } -func NewResponseClientIos(sources []db.MosquitoSource, requests []db.ServiceRequest, traps []db.TrapData) ResponseClientIos { - return ResponseClientIos{ - MosquitoSources: NewResponseMosquitoSources(sources), - ServiceRequests: NewResponseServiceRequests(requests), - TrapData: NewResponseTrapData(traps), - } -} // In the best case scenario, the excellent github.com/pkg/errors package // helps reveal information on the error, setting it on Err, and in the Render() @@ -686,18 +193,18 @@ type ResponseMosquitoInspection struct { func (rtd ResponseMosquitoInspection) Render(w http.ResponseWriter, r *http.Request) error { return nil } -func NewResponseMosquitoInspection(i MosquitoInspection) ResponseMosquitoInspection { +func NewResponseMosquitoInspection(i models.FieldseekerMosquitoinspection) ResponseMosquitoInspection { return ResponseMosquitoInspection{ - ActionTaken: i.ActionTaken(), - Comments: i.Comments(), - Condition: i.Condition(), - Created: i.Created().Format("2006-01-02T15:04:05.000Z"), - ID: i.ID(), - LocationName: i.LocationName(), - SiteCondition: i.SiteCondition(), + ActionTaken: i.Actiontaken.GetOr(""), + Comments: i.Comments.GetOr(""), + Condition: i.Sitecond.GetOr(""), + Created: i.Creationdate.MustGet().Format("2006-01-02T15:04:05.000Z"), + ID: i.Globalid.MustGet().String(), + LocationName: i.Locationname.GetOr(""), + SiteCondition: i.Sitecond.GetOr(""), } } -func NewResponseMosquitoInspections(inspections []MosquitoInspection) []ResponseMosquitoInspection { +func NewResponseMosquitoInspections(inspections []models.FieldseekerMosquitoinspection) []ResponseMosquitoInspection { results := make([]ResponseMosquitoInspection, 0) for _, i := range inspections { results = append(results, NewResponseMosquitoInspection(i)) @@ -709,7 +216,7 @@ func (rtd ResponseMosquitoSource) Render(w http.ResponseWriter, r *http.Request) return nil } -func NewResponseMosquitoSource(ms db.MosquitoSource) ResponseMosquitoSource { +func NewResponseMosquitoSource(ms platform.MosquitoSource) ResponseMosquitoSource { return ResponseMosquitoSource{ /* @@ -732,7 +239,7 @@ func NewResponseMosquitoSource(ms db.MosquitoSource) ResponseMosquitoSource { */ } } -func NewResponseMosquitoSources(sources []db.MosquitoSource) []ResponseMosquitoSource { +func NewResponseMosquitoSources(sources []platform.MosquitoSource) []ResponseMosquitoSource { results := make([]ResponseMosquitoSource, 0) for _, i := range sources { results = append(results, NewResponseMosquitoSource(i)) @@ -758,7 +265,7 @@ type ResponseMosquitoTreatment struct { func (rtd ResponseMosquitoTreatment) Render(w http.ResponseWriter, r *http.Request) error { return nil } -func NewResponseMosquitoTreatment(i db.MosquitoTreatment) ResponseMosquitoTreatment { +func NewResponseMosquitoTreatment(i platform.MosquitoTreatment) ResponseMosquitoTreatment { return ResponseMosquitoTreatment{ /* Comments: i.Comments(), @@ -775,7 +282,7 @@ func NewResponseMosquitoTreatment(i db.MosquitoTreatment) ResponseMosquitoTreatm */ } } -func NewResponseMosquitoTreatments(treatments []db.MosquitoTreatment) []ResponseMosquitoTreatment { +func NewResponseMosquitoTreatments(treatments []platform.MosquitoTreatment) []ResponseMosquitoTreatment { results := make([]ResponseMosquitoTreatment, 0) for _, i := range treatments { results = append(results, NewResponseMosquitoTreatment(i)) @@ -817,7 +324,7 @@ func (srr ResponseServiceRequest) Render(w http.ResponseWriter, r *http.Request) return nil } -func NewResponseServiceRequest(sr db.ServiceRequest) ResponseServiceRequest { +func NewResponseServiceRequest(sr platform.ServiceRequest) ResponseServiceRequest { return ResponseServiceRequest{ /* Address: sr.Address(), @@ -836,7 +343,7 @@ func NewResponseServiceRequest(sr db.ServiceRequest) ResponseServiceRequest { */ } } -func NewResponseServiceRequests(requests []db.ServiceRequest) []ResponseServiceRequest { +func NewResponseServiceRequests(requests []platform.ServiceRequest) []ResponseServiceRequest { results := make([]ResponseServiceRequest, 0) for _, i := range requests { results = append(results, NewResponseServiceRequest(i)) @@ -855,7 +362,7 @@ type ResponseTrapData struct { func (rtd ResponseTrapData) Render(w http.ResponseWriter, r *http.Request) error { return nil } -func NewResponseTrapDatum(td db.TrapData) ResponseTrapData { +func NewResponseTrapDatum(td platform.TrapData) ResponseTrapData { return ResponseTrapData{ /* Created: td.Created.Format("2006-01-02T15:04:05.000Z"), @@ -866,10 +373,15 @@ func NewResponseTrapDatum(td db.TrapData) ResponseTrapData { */ } } -func NewResponseTrapData(data []db.TrapData) []ResponseTrapData { +func NewResponseTrapData(data []platform.TrapData) []ResponseTrapData { results := make([]ResponseTrapData, 0) for _, i := range data { results = append(results, NewResponseTrapDatum(i)) } return results } + +func toResponseFieldseeker(csync platform.ClientSync) ResponseFieldseeker { + return ResponseFieldseeker{ + } +}