Add traps to cell details page
This commit is contained in:
parent
0bd1a10753
commit
885b58a0ab
6 changed files with 109 additions and 22 deletions
|
|
@ -132,14 +132,13 @@ class MapAggregate extends HTMLElement {
|
||||||
'fill-color': '#0dcaf0'
|
'fill-color': '#0dcaf0'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var self = this;
|
|
||||||
map.addInteraction("tegola-click-interaction", {
|
map.addInteraction("tegola-click-interaction", {
|
||||||
type: "click",
|
type: "click",
|
||||||
target: { layerId: "mosquito_source" },
|
target: { layerId: "mosquito_source" },
|
||||||
handler: (e) => {
|
handler: (e) => {
|
||||||
const coordinates = e.feature.geometry.coordinates.slice();
|
const coordinates = e.feature.geometry.coordinates.slice();
|
||||||
const properties = e.feature.properties;
|
const properties = e.feature.properties;
|
||||||
self.dispatchEvent(new CustomEvent("cell-click", {
|
this.dispatchEvent(new CustomEvent("cell-click", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true, // Allows event to cross shadow DOM boundary
|
composed: true, // Allows event to cross shadow DOM boundary
|
||||||
detail: {
|
detail: {
|
||||||
|
|
|
||||||
18
sync/dash.go
18
sync/dash.go
|
|
@ -33,6 +33,15 @@ type Config struct {
|
||||||
URLTegola string
|
URLTegola string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContextCell struct {
|
||||||
|
BreedingSources []BreedingSourceSummary
|
||||||
|
CellBoundary h3.CellBoundary
|
||||||
|
Inspections []Inspection
|
||||||
|
MapData ComponentMap
|
||||||
|
Traps []Trap
|
||||||
|
Treatments []Treatment
|
||||||
|
User User
|
||||||
|
}
|
||||||
type ContextDashboard struct {
|
type ContextDashboard struct {
|
||||||
Config Config
|
Config Config
|
||||||
CountTraps int
|
CountTraps int
|
||||||
|
|
@ -161,12 +170,18 @@ func cell(ctx context.Context, w http.ResponseWriter, user *models.User, c int64
|
||||||
respondError(w, "Failed to get sources", err, http.StatusInternalServerError)
|
respondError(w, "Failed to get sources", err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
traps, err := trapsByCell(ctx, org, h3.Cell(c))
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, "Failed to get traps", err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
treatments, err := treatmentsByCell(ctx, org, h3.Cell(c))
|
treatments, err := treatmentsByCell(ctx, org, h3.Cell(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, "Failed to get treatments", err, http.StatusInternalServerError)
|
respondError(w, "Failed to get treatments", err, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := ContentCell{
|
data := ContextCell{
|
||||||
BreedingSources: sources,
|
BreedingSources: sources,
|
||||||
CellBoundary: boundary,
|
CellBoundary: boundary,
|
||||||
Inspections: inspections,
|
Inspections: inspections,
|
||||||
|
|
@ -179,6 +194,7 @@ func cell(ctx context.Context, w http.ResponseWriter, user *models.User, c int64
|
||||||
MapboxToken: config.MapboxToken,
|
MapboxToken: config.MapboxToken,
|
||||||
Zoom: resolution + 5,
|
Zoom: resolution + 5,
|
||||||
},
|
},
|
||||||
|
Traps: traps,
|
||||||
Treatments: treatments,
|
Treatments: treatments,
|
||||||
User: userContent,
|
User: userContent,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,13 @@ type TrapData struct {
|
||||||
Comments string `json:"comments"`
|
Comments string `json:"comments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Trap struct {
|
||||||
|
Active bool
|
||||||
|
Comments string
|
||||||
|
Description string
|
||||||
|
GlobalID uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
type Treatment struct {
|
type Treatment struct {
|
||||||
CadenceDelta time.Duration
|
CadenceDelta time.Duration
|
||||||
Date *time.Time
|
Date *time.Time
|
||||||
|
|
@ -162,7 +169,18 @@ type Treatment struct {
|
||||||
Product string
|
Product string
|
||||||
}
|
}
|
||||||
|
|
||||||
func toTemplateTraps(locations []sql.TrapLocationBySourceIDRow, trap_data []sql.TrapDataByLocationIDRecentRow, counts []sql.TrapCountByLocationIDRow) ([]TrapNearby, error) {
|
func toTemplateTrap(traps models.FieldseekerTraplocationSlice) (results []Trap, err error) {
|
||||||
|
for _, t := range traps {
|
||||||
|
results = append(results, Trap{
|
||||||
|
Active: toBool16Or(t.Active, false),
|
||||||
|
Comments: t.Comments.GetOr(""),
|
||||||
|
Description: t.Description.GetOr(""),
|
||||||
|
GlobalID: t.Globalid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
func toTemplateTrapsNearby(locations []sql.TrapLocationBySourceIDRow, trap_data []sql.TrapDataByLocationIDRecentRow, counts []sql.TrapCountByLocationIDRow) ([]TrapNearby, error) {
|
||||||
results := make([]TrapNearby, 0)
|
results := make([]TrapNearby, 0)
|
||||||
count_by_trap_data_id := make(map[uuid.UUID]*sql.TrapCountByLocationIDRow)
|
count_by_trap_data_id := make(map[uuid.UUID]*sql.TrapCountByLocationIDRow)
|
||||||
for _, c := range counts {
|
for _, c := range counts {
|
||||||
|
|
@ -409,3 +427,17 @@ func getTimeOrNull(v null.Val[time.Time]) *time.Time {
|
||||||
val := v.MustGet()
|
val := v.MustGet()
|
||||||
return &val
|
return &val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toBool16Or(t null.Val[int16], def bool) bool {
|
||||||
|
if t.IsNull() {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
val := t.MustGet()
|
||||||
|
var b bool
|
||||||
|
if val == 0 {
|
||||||
|
b = false
|
||||||
|
} else {
|
||||||
|
b = true
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,36 @@
|
||||||
|
|
||||||
<!-- Right Column -->
|
<!-- Right Column -->
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
<h2 class="section-header">Traps</h2>
|
||||||
|
{{ if gt (len .Traps) 0}}
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Active</th>
|
||||||
|
<th>Comments</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .Traps }}
|
||||||
|
<tr>
|
||||||
|
<td><a href="/trap/{{.GlobalID}}">{{.GlobalID|uuidShort}}</a></td>
|
||||||
|
<td>{{.Active}}</td>
|
||||||
|
<td>{{.Comments}}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<p>No traps</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
<!-- Treatments Section -->
|
<!-- Treatments Section -->
|
||||||
<h2 class="section-header">Treatment History</h2>
|
<h2 class="section-header">Treatment History</h2>
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,6 @@ type ComponentMap struct {
|
||||||
type ContentAuthenticatedPlaceholder struct {
|
type ContentAuthenticatedPlaceholder struct {
|
||||||
User User
|
User User
|
||||||
}
|
}
|
||||||
type ContentCell struct {
|
|
||||||
BreedingSources []BreedingSourceSummary
|
|
||||||
CellBoundary h3.CellBoundary
|
|
||||||
Inspections []Inspection
|
|
||||||
MapData ComponentMap
|
|
||||||
Treatments []Treatment
|
|
||||||
User User
|
|
||||||
}
|
|
||||||
type ContentMockURLs struct {
|
type ContentMockURLs struct {
|
||||||
Dispatch string
|
Dispatch string
|
||||||
DispatchResults string
|
DispatchResults string
|
||||||
|
|
@ -95,7 +87,7 @@ type Link struct {
|
||||||
Title string
|
Title string
|
||||||
}
|
}
|
||||||
type Organization struct {
|
type Organization struct {
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
type ServiceRequestSummary struct {
|
type ServiceRequestSummary struct {
|
||||||
|
|
@ -104,9 +96,9 @@ type ServiceRequestSummary struct {
|
||||||
Status string
|
Status string
|
||||||
}
|
}
|
||||||
type User struct {
|
type User struct {
|
||||||
DisplayName string
|
DisplayName string
|
||||||
Initials string
|
Initials string
|
||||||
Notifications []notification.Notification
|
Notifications []notification.Notification
|
||||||
Organization Organization
|
Organization Organization
|
||||||
Username string
|
Username string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,11 @@ func contentForUser(ctx context.Context, user *models.User) (User, error) {
|
||||||
DisplayName: user.DisplayName,
|
DisplayName: user.DisplayName,
|
||||||
Initials: extractInitials(user.DisplayName),
|
Initials: extractInitials(user.DisplayName),
|
||||||
Notifications: notifications,
|
Notifications: notifications,
|
||||||
Organization: Organization {
|
Organization: Organization{
|
||||||
ID: int(org.ID),
|
ID: int(org.ID),
|
||||||
Name: org.Name,
|
Name: org.Name,
|
||||||
},
|
},
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +181,7 @@ func trapsBySource(ctx context.Context, org *models.Organization, sourceID uuid.
|
||||||
return nil, fmt.Errorf("Failed to query trap counts: %w", err)
|
return nil, fmt.Errorf("Failed to query trap counts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
traps, err := toTemplateTraps(locations, trap_data, counts)
|
traps, err := toTemplateTrapsNearby(locations, trap_data, counts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to convert trap data: %w", err)
|
return nil, fmt.Errorf("Failed to convert trap data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -203,6 +203,24 @@ func treatmentsBySource(ctx context.Context, org *models.Organization, sourceID
|
||||||
return toTemplateTreatment(rows)
|
return toTemplateTreatment(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trapsByCell(ctx context.Context, org *models.Organization, c h3.Cell) (results []Trap, err error) {
|
||||||
|
boundary, err := c.Boundary()
|
||||||
|
if err != nil {
|
||||||
|
return results, fmt.Errorf("Failed to get cell boundary: %w", err)
|
||||||
|
}
|
||||||
|
geom_query := gisStatement(boundary)
|
||||||
|
rows, err := org.Traplocations(
|
||||||
|
sm.Where(
|
||||||
|
psql.F("ST_Within", "geospatial", geom_query),
|
||||||
|
),
|
||||||
|
sm.OrderBy("objectid"),
|
||||||
|
).All(ctx, db.PGInstance.BobDB)
|
||||||
|
if err != nil {
|
||||||
|
return results, fmt.Errorf("Failed to query rows: %w", err)
|
||||||
|
}
|
||||||
|
return toTemplateTrap(rows)
|
||||||
|
}
|
||||||
|
|
||||||
func treatmentsByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]Treatment, error) {
|
func treatmentsByCell(ctx context.Context, org *models.Organization, c h3.Cell) ([]Treatment, error) {
|
||||||
var results []Treatment
|
var results []Treatment
|
||||||
boundary, err := c.Boundary()
|
boundary, err := c.Boundary()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue