Add treatment and inspection history to a source

This commit is contained in:
Eli Ribble 2025-11-20 15:24:54 +00:00
parent f599d831c8
commit 9e3d12ae2a
No known key found for this signature in database
3 changed files with 92 additions and 98 deletions

75
html.go
View file

@ -18,6 +18,7 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/models"
"github.com/aarondl/opt/null"
//"github.com/riverqueue/river/rivershared/util/slogutil"
//"github.com/rs/zerolog/log"
"github.com/stephenafamo/bob/dialect/psql"
"github.com/stephenafamo/bob/dialect/psql/sm"
"github.com/uber/h3-go/v4"
@ -120,9 +121,11 @@ type ContentSignin struct {
}
type ContentSignup struct{}
type ContentSource struct {
MapData ComponentMap
Source *BreedingSourceDetail
User User
Inspections []Inspection
MapData ComponentMap
Source *BreedingSourceDetail
Treatments []Treatment
User User
}
type LatLng struct {
Lat float64
@ -493,7 +496,18 @@ func htmlSource(w http.ResponseWriter, r *http.Request, user *models.User, id st
respondError(w, "Failed to get source", err, http.StatusInternalServerError)
return
}
inspections, err := inspectionsBySource(r.Context(), org, id)
if err != nil {
respondError(w, "Failed to get inspections", err, http.StatusInternalServerError)
return
}
treatments, err := treatmentsBySource(r.Context(), org, id)
if err != nil {
respondError(w, "Failed to get treatments", err, http.StatusInternalServerError)
return
}
data := ContentSource{
Inspections: inspections,
MapData: ComponentMap{
Center: LatLng{
Lat: s.GeometryY,
@ -511,8 +525,9 @@ func htmlSource(w http.ResponseWriter, r *http.Request, user *models.User, id st
},
Zoom: 13,
},
Source: s,
User: userContent,
Source: s,
Treatments: treatments,
User: userContent,
}
renderOrError(w, source, data)
@ -666,6 +681,21 @@ func renderOrError(w http.ResponseWriter, template BuiltTemplate, context interf
buf.WriteTo(w)
}
func treatmentsBySource(ctx context.Context, org *models.Organization, sourceID string) ([]Treatment, error) {
var results []Treatment
rows, err := org.FSTreatments(
sm.Where(
models.FSTreatments.Columns.Pointlocid.EQ(psql.Arg(sourceID)),
),
sm.OrderBy("enddatetime"),
).All(ctx, PGInstance.BobDB)
if err != nil {
return results, fmt.Errorf("Failed to query rows: %w", err)
}
//log.Info().Int("row count", len(rows)).Msg("Getting treatments")
return toTemplateTreatment(rows)
}
func treatmentsByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]Treatment, error) {
var results []Treatment
boundary, err := c.Boundary()
@ -683,15 +713,7 @@ func treatmentsByCell(ctx context.Context, org *models.Organization, c h3.Cell)
if err != nil {
return results, fmt.Errorf("Failed to query rows: %w", err)
}
for _, r := range rows {
results = append(results, Treatment{
Date: *fsTimestampToTime(r.Enddatetime),
LocationID: r.Pointlocid.GetOr("none"),
Notes: r.Comments.GetOr("none"),
Product: r.Product.GetOr("none"),
})
}
return results, nil
return toTemplateTreatment(rows)
}
func inspectionsByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]Inspection, error) {
var results []Inspection
@ -711,16 +733,21 @@ func inspectionsByCell(ctx context.Context, org *models.Organization, c h3.Cell)
if err != nil {
return results, fmt.Errorf("Failed to query rows: %w", err)
}
for _, r := range rows {
results = append(results, Inspection{
Action: r.Actiontaken.GetOr("none"),
Date: *fsTimestampToTime(r.Enddatetime),
Notes: r.Comments.GetOr("none"),
Location: r.Locationname.GetOr("none"),
LocationID: r.Pointlocid.GetOr(""),
})
return toTemplateInspection(rows)
}
func inspectionsBySource(ctx context.Context, org *models.Organization, sourceID string) ([]Inspection, error) {
var results []Inspection
rows, err := org.FSMosquitoinspections(
sm.Where(
models.FSMosquitoinspections.Columns.Pointlocid.EQ(psql.Arg(sourceID)),
),
sm.OrderBy("enddatetime").Desc(),
).All(ctx, PGInstance.BobDB)
if err != nil {
return results, fmt.Errorf("Failed to query rows: %w", err)
}
return results, nil
return toTemplateInspection(rows)
}
func breedingSourcesByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]BreedingSourceSummary, error) {
var results []BreedingSourceSummary
@ -765,5 +792,5 @@ func sourceByGlobalId(ctx context.Context, org *models.Organization, id string)
if err != nil {
return nil, fmt.Errorf("Failed to get point location: %w", err)
}
return ConvertToDisplayModel(row), nil
return toTemplateBreedingSource(row), nil
}

View file

@ -72,8 +72,35 @@ type BreedingSourceDetail struct {
Comments string `json:"comments"`
}
// ConvertToDisplayModel transforms the DB model into the display model
func ConvertToDisplayModel(source *models.FSPointlocation) *BreedingSourceDetail {
func toTemplateTreatment(rows models.FSTreatmentSlice) ([]Treatment, error) {
var results []Treatment
for _, r := range rows {
results = append(results, Treatment{
Date: *fsTimestampToTime(r.Enddatetime),
LocationID: r.Pointlocid.GetOr("none"),
Notes: r.Comments.GetOr("none"),
Product: r.Product.GetOr("none"),
})
}
return results, nil
}
func toTemplateInspection(rows models.FSMosquitoinspectionSlice) ([]Inspection, error) {
var results []Inspection
for _, r := range rows {
results = append(results, Inspection{
Action: r.Actiontaken.GetOr("none"),
Date: *fsTimestampToTime(r.Enddatetime),
Notes: r.Comments.GetOr("none"),
Location: r.Locationname.GetOr("none"),
LocationID: r.Pointlocid.GetOr(""),
})
}
return results, nil
}
// toTemplateBreedingSource transforms the DB model into the display model
func toTemplateBreedingSource(source *models.FSPointlocation) *BreedingSourceDetail {
// Helper function to convert unix timestamp to time.Time
toTime := func(val null.Val[int64]) time.Time {
v, ok := val.Get()

View file

@ -244,53 +244,17 @@
</tr>
</thead>
<tbody>
{{ range .Treatments }}
<tr>
<td>04/16/2023</td>
<td>Bacillus thuringiensis israelensis (Bti)</td>
<td>{{.Date|timeSince}}</td>
<td>{{.Product}}</td>
<td class="time-delta-neutral">On time</td>
<td>Applied larvicide to standing water.</td>
</tr>
<tr>
<td>04/02/2023</td>
<td>Methoprene</td>
<td class="time-delta-neutral">On time</td>
<td>Applied to ditch with extended release formula.</td>
</tr>
<tr>
<td>03/18/2023</td>
<td>Bacillus sphaericus</td>
<td class="time-delta-negative">-1 day</td>
<td>Preemptive treatment before rain event.</td>
</tr>
<tr>
<td>03/01/2023</td>
<td>Bacillus thuringiensis israelensis (Bti)</td>
<td class="time-delta-positive">+3 days</td>
<td>Delayed due to weather conditions.</td>
</tr>
<tr>
<td>02/15/2023</td>
<td>Methoprene</td>
<td class="time-delta-neutral">On time</td>
<td>Regular treatment cycle.</td>
<td>{{.Notes}}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
<nav aria-label="Treatments pagination">
<ul class="pagination justify-content-center">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
</div>
</div>
@ -303,42 +267,18 @@
<thead>
<tr>
<th>Inspection Date</th>
<th>Inspector</th>
<th>Larvae Present</th>
<th>Comments</th>
<th>Action</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{{ range .Inspections }}
<tr>
<td>04/15/2023</td>
<td>J. Smith</td>
<td><span class="badge bg-danger">Yes</span></td>
<td>Moderate larvae presence, treatment recommended.</td>
</tr>
<tr>
<td>04/01/2023</td>
<td>M. Johnson</td>
<td><span class="badge bg-danger">Yes</span></td>
<td>High larvae count, immediate treatment needed.</td>
</tr>
<tr>
<td>03/17/2023</td>
<td>J. Smith</td>
<td><span class="badge bg-success">No</span></td>
<td>No larvae observed, previous treatment effective.</td>
</tr>
<tr>
<td>03/03/2023</td>
<td>R. Williams</td>
<td><span class="badge bg-danger">Yes</span></td>
<td>Low larvae count, monitoring recommended.</td>
</tr>
<tr>
<td>02/14/2023</td>
<td>M. Johnson</td>
<td><span class="badge bg-danger">Yes</span></td>
<td>Moderate larvae count, treatment scheduled.</td>
<td>{{.Date|timeSince}}</td>
<td>{{.Action}}</td>
<td>{{.Notes}}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>