2026-01-29 23:55:41 +00:00
|
|
|
package rmo
|
2026-01-09 19:45:33 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
2026-02-05 16:56:36 +00:00
|
|
|
"slices"
|
2026-01-09 19:45:33 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
2026-01-30 18:21:27 +00:00
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/html"
|
2026-03-13 17:33:39 +00:00
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
2026-01-31 20:08:08 +00:00
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/platform/report"
|
2026-01-09 19:45:33 +00:00
|
|
|
"github.com/aarondl/opt/omit"
|
2026-01-13 19:52:25 +00:00
|
|
|
"github.com/aarondl/opt/omitnull"
|
2026-01-09 19:45:33 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-30 20:41:02 +00:00
|
|
|
type ContentNuisance struct {
|
|
|
|
|
District *ContentDistrict
|
|
|
|
|
MapboxToken string
|
|
|
|
|
URL ContentURL
|
|
|
|
|
}
|
|
|
|
|
type ContentNuisanceSubmitComplete struct {
|
2026-01-31 16:44:41 +00:00
|
|
|
District *ContentDistrict
|
2026-01-09 19:45:33 +00:00
|
|
|
ReportID string
|
2026-01-31 16:44:41 +00:00
|
|
|
URL ContentURL
|
2026-01-09 19:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getNuisance(w http.ResponseWriter, r *http.Request) {
|
2026-01-30 18:21:27 +00:00
|
|
|
html.RenderOrError(
|
2026-01-09 19:45:33 +00:00
|
|
|
w,
|
2026-02-07 05:51:21 +00:00
|
|
|
"rmo/nuisance.html",
|
2026-01-30 20:41:02 +00:00
|
|
|
ContentNuisance{
|
2026-03-09 18:02:22 +00:00
|
|
|
District: nil,
|
|
|
|
|
URL: makeContentURL(nil),
|
2026-01-30 20:41:02 +00:00
|
|
|
},
|
2026-01-09 19:45:33 +00:00
|
|
|
)
|
|
|
|
|
}
|
2026-02-01 03:14:36 +00:00
|
|
|
func getNuisanceDistrict(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
district, err := districtBySlug(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
respondError(w, "Failed to lookup organization", err, http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
html.RenderOrError(
|
|
|
|
|
w,
|
2026-02-07 05:51:21 +00:00
|
|
|
"rmo/nuisance.html",
|
2026-02-01 03:14:36 +00:00
|
|
|
ContentNuisance{
|
2026-03-09 18:02:22 +00:00
|
|
|
District: newContentDistrict(district),
|
|
|
|
|
URL: makeContentURL(nil),
|
2026-02-01 03:14:36 +00:00
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-01-31 16:44:41 +00:00
|
|
|
func getSubmitComplete(w http.ResponseWriter, r *http.Request) {
|
2026-02-01 02:37:35 +00:00
|
|
|
report_id := r.URL.Query().Get("report")
|
|
|
|
|
district, err := report.DistrictForReport(r.Context(), report_id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
respondError(w, fmt.Sprintf("Failed to get district for report '%s'", report_id, err), err, http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-01-30 18:21:27 +00:00
|
|
|
html.RenderOrError(
|
2026-01-09 19:45:33 +00:00
|
|
|
w,
|
2026-02-07 05:51:21 +00:00
|
|
|
"rmo/submit-complete.html",
|
2026-01-30 20:41:02 +00:00
|
|
|
ContentNuisanceSubmitComplete{
|
2026-02-01 02:37:35 +00:00
|
|
|
District: newContentDistrict(district),
|
|
|
|
|
ReportID: report_id,
|
2026-02-01 03:07:46 +00:00
|
|
|
URL: makeContentURL(nil),
|
2026-01-09 19:45:33 +00:00
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
func postNuisance(w http.ResponseWriter, r *http.Request) {
|
2026-01-31 22:14:46 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
|
err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
|
2026-01-09 19:45:33 +00:00
|
|
|
if err != nil {
|
|
|
|
|
respondError(w, "Failed to parse form", err, http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-02-05 16:56:36 +00:00
|
|
|
additional_info := r.PostFormValue("additional-info")
|
2026-03-09 19:09:42 +00:00
|
|
|
address_raw := r.PostFormValue("address")
|
2026-02-05 16:56:36 +00:00
|
|
|
address_country := r.PostFormValue("address-country")
|
2026-03-09 18:02:22 +00:00
|
|
|
address_locality := r.PostFormValue("address-locality")
|
2026-03-08 03:14:38 +00:00
|
|
|
address_number := r.PostFormValue("address-number")
|
2026-03-09 18:02:22 +00:00
|
|
|
address_postal_code := r.PostFormValue("address-postalcode")
|
2026-02-05 16:56:36 +00:00
|
|
|
address_region := r.PostFormValue("address-region")
|
|
|
|
|
address_street := r.PostFormValue("address-street")
|
|
|
|
|
duration_str := postFormValueOrNone(r, "duration")
|
2026-01-09 19:45:33 +00:00
|
|
|
source_stagnant := boolFromForm(r, "source-stagnant")
|
|
|
|
|
source_container := boolFromForm(r, "source-container")
|
2026-02-05 16:56:36 +00:00
|
|
|
source_description := r.PostFormValue("source-description")
|
2026-01-31 15:39:14 +00:00
|
|
|
source_gutters := boolFromForm(r, "source-gutters")
|
2026-02-05 16:56:36 +00:00
|
|
|
source_locations := r.Form["source-location"]
|
|
|
|
|
tod_early := boolFromForm(r, "tod-early")
|
|
|
|
|
tod_day := boolFromForm(r, "tod-day")
|
|
|
|
|
tod_evening := boolFromForm(r, "tod-evening")
|
|
|
|
|
tod_night := boolFromForm(r, "tod-night")
|
|
|
|
|
|
|
|
|
|
duration := enums.PublicreportNuisancedurationtypeNone
|
|
|
|
|
is_location_frontyard := false
|
|
|
|
|
is_location_backyard := false
|
|
|
|
|
is_location_garden := false
|
|
|
|
|
is_location_pool := false
|
|
|
|
|
is_location_other := false
|
2026-02-05 21:43:29 +00:00
|
|
|
|
|
|
|
|
latlng, err := parseLatLng(r)
|
2026-01-09 19:45:33 +00:00
|
|
|
|
|
|
|
|
err = duration.Scan(duration_str)
|
|
|
|
|
if err != nil {
|
2026-02-05 16:56:36 +00:00
|
|
|
log.Warn().Err(err).Str("duration_str", duration_str).Msg("Failed to interpret 'duration'")
|
2026-01-09 19:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 22:33:44 +00:00
|
|
|
//log.Debug().Strs("source_locations", source_locations).Msg("parsing")
|
2026-02-05 16:56:36 +00:00
|
|
|
if slices.Contains(source_locations, "backyard") {
|
|
|
|
|
is_location_backyard = true
|
|
|
|
|
}
|
|
|
|
|
if slices.Contains(source_locations, "frontyard") {
|
|
|
|
|
is_location_frontyard = true
|
|
|
|
|
}
|
|
|
|
|
if slices.Contains(source_locations, "garden") {
|
|
|
|
|
is_location_garden = true
|
|
|
|
|
}
|
|
|
|
|
if slices.Contains(source_locations, "other") {
|
|
|
|
|
is_location_other = true
|
|
|
|
|
}
|
|
|
|
|
if slices.Contains(source_locations, "pool-area") {
|
|
|
|
|
is_location_pool = true
|
|
|
|
|
}
|
2026-03-09 19:09:42 +00:00
|
|
|
|
2026-01-31 22:14:46 +00:00
|
|
|
uploads, err := extractImageUploads(r)
|
|
|
|
|
log.Info().Int("len", len(uploads)).Msg("extracted uploads")
|
|
|
|
|
if err != nil {
|
|
|
|
|
respondError(w, "Failed to extract image uploads", err, http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-03-13 17:33:39 +00:00
|
|
|
address := platform.Address{
|
|
|
|
|
Country: address_country,
|
|
|
|
|
Locality: address_locality,
|
|
|
|
|
Number: address_number,
|
|
|
|
|
PostalCode: address_postal_code,
|
|
|
|
|
Raw: address_raw,
|
|
|
|
|
Region: address_region,
|
|
|
|
|
Street: address_street,
|
|
|
|
|
Unit: "",
|
2026-02-10 15:09:57 +00:00
|
|
|
}
|
2026-03-18 15:36:20 +00:00
|
|
|
setter_report := models.PublicreportReportSetter{
|
2026-03-13 17:33:39 +00:00
|
|
|
//AddressID: omitnull.From(latlng.Cell.String()),
|
|
|
|
|
AddressRaw: omit.From(address.Raw),
|
|
|
|
|
AddressCountry: omit.From(address.Country),
|
|
|
|
|
AddressNumber: omit.From(address.Number),
|
|
|
|
|
AddressLocality: omit.From(address.Locality),
|
|
|
|
|
AddressPostalCode: omit.From(address.PostalCode),
|
|
|
|
|
AddressRegion: omit.From(address.Region),
|
|
|
|
|
AddressStreet: omit.From(address.Street),
|
2026-03-09 18:02:22 +00:00
|
|
|
Created: omit.From(time.Now()),
|
2026-03-13 17:33:39 +00:00
|
|
|
//H3cell: omitnull.From(latlng.Cell.String()),
|
2026-02-05 21:43:29 +00:00
|
|
|
LatlngAccuracyType: omit.From(latlng.AccuracyType),
|
2026-03-13 17:33:39 +00:00
|
|
|
LatlngAccuracyValue: omit.From(float32(latlng.AccuracyValue)),
|
2026-01-31 22:14:46 +00:00
|
|
|
//Location: omitnull.From(fmt.Sprintf("ST_GeometryFromText(Point(%s %s))", longitude, latitude)),
|
2026-03-13 17:33:39 +00:00
|
|
|
Location: omitnull.FromPtr[string](nil),
|
|
|
|
|
MapZoom: omit.From(latlng.MapZoom),
|
|
|
|
|
//OrganizationID: omitnull.FromPtr(organization_id),
|
|
|
|
|
//PublicID: omit.From(public_id),
|
2026-03-18 15:36:20 +00:00
|
|
|
ReporterEmail: omit.From(""),
|
|
|
|
|
ReporterName: omit.From(""),
|
|
|
|
|
ReporterPhone: omit.From(""),
|
2026-03-18 18:45:18 +00:00
|
|
|
ReportType: omit.From(enums.PublicreportReporttypeNuisance),
|
2026-03-18 15:36:20 +00:00
|
|
|
Status: omit.From(enums.PublicreportReportstatustypeReported),
|
|
|
|
|
}
|
|
|
|
|
setter_nuisance := models.PublicreportNuisanceSetter{
|
|
|
|
|
AdditionalInfo: omit.From(additional_info),
|
|
|
|
|
Duration: omit.From(duration),
|
|
|
|
|
IsLocationBackyard: omit.From(is_location_backyard),
|
|
|
|
|
IsLocationFrontyard: omit.From(is_location_frontyard),
|
|
|
|
|
IsLocationGarden: omit.From(is_location_garden),
|
|
|
|
|
IsLocationOther: omit.From(is_location_other),
|
|
|
|
|
IsLocationPool: omit.From(is_location_pool),
|
|
|
|
|
//ReportID omit.Val[int32]
|
2026-01-31 15:39:14 +00:00
|
|
|
SourceContainer: omit.From(source_container),
|
|
|
|
|
SourceDescription: omit.From(source_description),
|
|
|
|
|
SourceGutter: omit.From(source_gutters),
|
|
|
|
|
SourceStagnant: omit.From(source_stagnant),
|
2026-02-05 16:56:36 +00:00
|
|
|
TodDay: omit.From(tod_day),
|
2026-03-18 15:36:20 +00:00
|
|
|
TodEarly: omit.From(tod_early),
|
2026-02-05 16:56:36 +00:00
|
|
|
TodEvening: omit.From(tod_evening),
|
|
|
|
|
TodNight: omit.From(tod_night),
|
2026-01-09 19:45:33 +00:00
|
|
|
}
|
2026-03-18 18:45:18 +00:00
|
|
|
report, err := platform.ReportNuisanceCreate(ctx, setter_report, setter_nuisance, latlng, address, uploads)
|
|
|
|
|
http.Redirect(w, r, fmt.Sprintf("/submit-complete?report=%s", report.PublicID), http.StatusFound)
|
2026-01-09 19:45:33 +00:00
|
|
|
}
|