diff --git a/db/sql/publicreport_publicid_table.bob.go b/db/sql/publicreport_publicid_table.bob.go
new file mode 100644
index 00000000..9883cb40
--- /dev/null
+++ b/db/sql/publicreport_publicid_table.bob.go
@@ -0,0 +1,112 @@
+// Code generated by BobGen psql v0.0.4-0.20260105020634-53e08d840e47+dirty. DO NOT EDIT.
+// This file is meant to be re-generated in place and/or deleted at any time.
+
+package sql
+
+import (
+ "context"
+ _ "embed"
+ "io"
+ "iter"
+
+ "github.com/lib/pq"
+ "github.com/stephenafamo/bob"
+ "github.com/stephenafamo/bob/dialect/psql"
+ "github.com/stephenafamo/bob/dialect/psql/dialect"
+ "github.com/stephenafamo/bob/orm"
+ "github.com/stephenafamo/scan"
+)
+
+//go:embed publicreport_publicid_table.bob.sql
+var formattedQueries_publicreport_publicid_table string
+
+var publicreportIDTableSQL = formattedQueries_publicreport_publicid_table[192:659]
+
+type PublicreportIDTableQuery = orm.ModQuery[*dialect.SelectQuery, publicreportIDTable, PublicreportIDTableRow, []PublicreportIDTableRow, publicreportIDTableTransformer]
+
+func PublicreportIDTable(PublicID string) *PublicreportIDTableQuery {
+ var expressionTypArgs publicreportIDTable
+
+ expressionTypArgs.PublicID = psql.Arg(PublicID)
+
+ return &PublicreportIDTableQuery{
+ Query: orm.Query[publicreportIDTable, PublicreportIDTableRow, []PublicreportIDTableRow, publicreportIDTableTransformer]{
+ ExecQuery: orm.ExecQuery[publicreportIDTable]{
+ BaseQuery: bob.BaseQuery[publicreportIDTable]{
+ Expression: expressionTypArgs,
+ Dialect: dialect.Dialect,
+ QueryType: bob.QueryTypeSelect,
+ },
+ },
+ Scanner: func(context.Context, []string) (func(*scan.Row) (any, error), func(any) (PublicreportIDTableRow, error)) {
+ return func(row *scan.Row) (any, error) {
+ var t PublicreportIDTableRow
+ row.ScheduleScanByIndex(0, &t.ExistsSomewhere)
+ row.ScheduleScanByIndex(1, &t.FoundInTables)
+ return &t, nil
+ }, func(v any) (PublicreportIDTableRow, error) {
+ return *(v.(*PublicreportIDTableRow)), nil
+ }
+ },
+ },
+ Mod: bob.ModFunc[*dialect.SelectQuery](func(q *dialect.SelectQuery) {
+ q.AppendCTE(expressionTypArgs.subExpr(5, 335))
+ q.AppendSelect(expressionTypArgs.subExpr(348, 449))
+ q.SetTable(expressionTypArgs.subExpr(455, 467))
+ }),
+ }
+}
+
+type PublicreportIDTableRow = struct {
+ ExistsSomewhere bool `db:"exists_somewhere"`
+ FoundInTables pq.StringArray `db:"found_in_tables"`
+}
+
+type publicreportIDTableTransformer = bob.SliceTransformer[PublicreportIDTableRow, []PublicreportIDTableRow]
+
+type publicreportIDTable struct {
+ PublicID bob.Expression
+}
+
+func (o publicreportIDTable) args() iter.Seq[orm.ArgWithPosition] {
+ return func(yield func(arg orm.ArgWithPosition) bool) {
+ if !yield(orm.ArgWithPosition{
+ Name: "publicID",
+ Start: 112,
+ Stop: 114,
+ Expression: o.PublicID,
+ }) {
+ return
+ }
+
+ if !yield(orm.ArgWithPosition{
+ Name: "publicID",
+ Start: 221,
+ Stop: 223,
+ Expression: o.PublicID,
+ }) {
+ return
+ }
+
+ if !yield(orm.ArgWithPosition{
+ Name: "publicID",
+ Start: 331,
+ Stop: 333,
+ Expression: o.PublicID,
+ }) {
+ return
+ }
+ }
+}
+
+func (o publicreportIDTable) raw(from, to int) string {
+ return publicreportIDTableSQL[from:to]
+}
+
+func (o publicreportIDTable) subExpr(from, to int) bob.Expression {
+ return orm.ArgsToExpression(publicreportIDTableSQL, from, to, o.args())
+}
+
+func (o publicreportIDTable) WriteSQL(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
+ return o.subExpr(0, len(publicreportIDTableSQL)).WriteSQL(ctx, w, d, start)
+}
diff --git a/db/sql/publicreport_publicid_table.bob.sql b/db/sql/publicreport_publicid_table.bob.sql
new file mode 100644
index 00000000..8a6381d9
--- /dev/null
+++ b/db/sql/publicreport_publicid_table.bob.sql
@@ -0,0 +1,25 @@
+-- Code generated by BobGen psql v0.0.4-0.20260105020634-53e08d840e47+dirty. DO NOT EDIT.
+-- This file is meant to be re-generated in place and/or deleted at any time.
+
+-- PublicreportIDTable
+WITH found_tables AS (
+ SELECT 'nuisance' as table_name
+ FROM publicreport.nuisance
+ WHERE public_id = $1
+
+ UNION ALL
+
+ SELECT 'pool' as table_name
+ FROM publicreport.pool
+ WHERE public_id = $2
+
+ UNION ALL
+
+ SELECT 'quick' as table_name
+ FROM publicreport.quick
+ WHERE public_id = $3
+)
+SELECT
+ EXISTS (SELECT 1 FROM found_tables) as exists_somewhere,
+ array_agg(table_name) as found_in_tables
+FROM found_tables;
diff --git a/db/sql/publicreport_publicid_table.sql b/db/sql/publicreport_publicid_table.sql
new file mode 100644
index 00000000..6bf74029
--- /dev/null
+++ b/db/sql/publicreport_publicid_table.sql
@@ -0,0 +1,22 @@
+-- PublicreportIDTable
+WITH found_tables AS (
+ SELECT 'nuisance' as table_name
+ FROM publicreport.nuisance
+ WHERE public_id = $1
+
+ UNION ALL
+
+ SELECT 'pool' as table_name
+ FROM publicreport.pool
+ WHERE public_id = $1
+
+ UNION ALL
+
+ SELECT 'quick' as table_name
+ FROM publicreport.quick
+ WHERE public_id = $1
+)
+SELECT
+ EXISTS (SELECT 1 FROM found_tables) as exists_somewhere,
+ array_agg(table_name) as found_in_tables
+FROM found_tables;
diff --git a/public-report/status.go b/public-report/status.go
index 7b14a371..d00a8fc6 100644
--- a/public-report/status.go
+++ b/public-report/status.go
@@ -1,30 +1,36 @@
package publicreport
import (
+ "fmt"
"net/http"
+ "strings"
+ "github.com/Gleipnir-Technology/nidus-sync/db"
+ "github.com/Gleipnir-Technology/nidus-sync/db/sql"
"github.com/Gleipnir-Technology/nidus-sync/htmlpage"
"github.com/go-chi/chi/v5"
+ "github.com/rs/zerolog/log"
/*
- "fmt"
- "strconv"
- "time"
+ "strconv"
+ "time"
- "github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
- "github.com/Gleipnir-Technology/nidus-sync/h3utils"
- "github.com/aarondl/opt/omit"
- "github.com/aarondl/opt/omitnull"
- "github.com/rs/zerolog/log"
- "github.com/stephenafamo/bob/dialect/psql"
- "github.com/stephenafamo/bob/dialect/psql/um"
+ "github.com/Gleipnir-Technology/nidus-sync/db"
+ "github.com/Gleipnir-Technology/nidus-sync/h3utils"
+ "github.com/aarondl/opt/omit"
+ "github.com/aarondl/opt/omitnull"
+ "github.com/stephenafamo/bob/dialect/psql"
+ "github.com/stephenafamo/bob/dialect/psql/um"
*/)
type Report struct {
ID string
}
-type ContextStatus struct{}
+type ContextStatus struct {
+ Error string
+ ReportID string
+}
type ContextStatusByID struct {
Report Report
}
@@ -34,11 +40,69 @@ var (
StatusByID = buildTemplate("status-by-id", "base")
)
+func formatReportID(s string) string {
+ // truncate down if too long
+ if len(s) > 12 {
+ s = s[:12]
+ }
+
+ // If less than 4 characters, return as is
+ if len(s) < 4 {
+ return s
+ }
+
+ // If at least 8 characters, add hyphens at positions 4 and 8
+ if len(s) >= 8 {
+ return s[0:4] + "-" + s[4:8] + "-" + s[8:]
+ }
+
+ // If at least 4 characters but less than 8, add hyphen only at position 4
+ return s[0:4] + "-" + s[4:]
+}
+
func getStatus(w http.ResponseWriter, r *http.Request) {
+ report_id_str := r.URL.Query().Get("report")
+ if report_id_str == "" {
+ htmlpage.RenderOrError(
+ w,
+ Status,
+ ContextStatus{
+ Error: "",
+ ReportID: "",
+ },
+ )
+ return
+ }
+ report_id := sanitizeReportID(report_id_str)
+ report_id_str = formatReportID(report_id)
+ results, err := sql.PublicreportIDTable(report_id).All(r.Context(), db.PGInstance.BobDB)
+ if err != nil {
+ respondError(w, "Failed to query for report", err, http.StatusInternalServerError)
+ return
+ }
+ if len(results) != 1 {
+ log.Error().Int("count", len(results)).Str("report_id", report_id_str).Msg("Got too many results for report id. This is a programmer error.")
+ htmlpage.RenderOrError(
+ w,
+ Status,
+ ContextStatus{
+ Error: "Sorry, server's confused",
+ ReportID: report_id_str,
+ },
+ )
+ }
+ result := results[0]
+ if result.ExistsSomewhere {
+ http.Redirect(w, r, fmt.Sprintf("/status/%s", report_id), http.StatusFound)
+ return
+ }
htmlpage.RenderOrError(
w,
Status,
- ContextStatus{},
+ ContextStatus{
+ Error: "Sorry, we can't find that report",
+ ReportID: report_id_str,
+ },
)
}
func getStatusByID(w http.ResponseWriter, r *http.Request) {
@@ -55,91 +119,103 @@ func getStatusByID(w http.ResponseWriter, r *http.Request) {
}
/*
-func getQuick(w http.ResponseWriter, r *http.Request) {
- htmlpage.RenderOrError(
- w,
- Quick,
- ContextQuick{},
- )
-}
-func getQuickSubmitComplete(w http.ResponseWriter, r *http.Request) {
- report := r.URL.Query().Get("report")
- htmlpage.RenderOrError(
- w,
- QuickSubmitComplete,
- ContextQuickSubmitComplete{
- ReportID: report,
- },
- )
-}
-func postQuick(w http.ResponseWriter, r *http.Request) {
- err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
- if err != nil {
- respondError(w, "Failed to parse form", err, http.StatusBadRequest)
- return
+ func getQuick(w http.ResponseWriter, r *http.Request) {
+ htmlpage.RenderOrError(
+ w,
+ Quick,
+ ContextQuick{},
+ )
}
- lat := r.FormValue("latitude")
- lng := r.FormValue("longitude")
- comments := r.FormValue("comments")
- //photos := r.FormValue("photos")
- latitude, err := strconv.ParseFloat(lat, 64)
- if err != nil {
- respondError(w, "Failed to create parse latitude", err, http.StatusBadRequest)
- return
+ func getQuickSubmitComplete(w http.ResponseWriter, r *http.Request) {
+ report := r.URL.Query().Get("report")
+ htmlpage.RenderOrError(
+ w,
+ QuickSubmitComplete,
+ ContextQuickSubmitComplete{
+ ReportID: report,
+ },
+ )
}
- longitude, err := strconv.ParseFloat(lng, 64)
- if err != nil {
- respondError(w, "Failed to create parse longitude", err, http.StatusBadRequest)
- return
+
+ func postQuick(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseMultipartForm(32 << 10) // 32 MB buffer
+ if err != nil {
+ respondError(w, "Failed to parse form", err, http.StatusBadRequest)
+ return
+ }
+ lat := r.FormValue("latitude")
+ lng := r.FormValue("longitude")
+ comments := r.FormValue("comments")
+ //photos := r.FormValue("photos")
+
+ latitude, err := strconv.ParseFloat(lat, 64)
+ if err != nil {
+ respondError(w, "Failed to create parse latitude", err, http.StatusBadRequest)
+ return
+ }
+ longitude, err := strconv.ParseFloat(lng, 64)
+ if err != nil {
+ respondError(w, "Failed to create parse longitude", err, http.StatusBadRequest)
+ return
+ }
+ u, err := GenerateReportID()
+ if err != nil {
+ respondError(w, "Failed to create quick report public ID", err, http.StatusInternalServerError)
+ return
+ }
+ c, err := h3utils.GetCell(longitude, latitude, 15)
+ setter := models.PublicreportQuickSetter{
+ Created: omit.From(time.Now()),
+ Comments: omit.From(comments),
+ //Location: omitnull.From(fmt.Sprintf("ST_GeometryFromText(Point(%s %s))", longitude, latitude)),
+ H3cell: omitnull.From(c.String()),
+ PublicID: omit.From(u),
+ ReporterEmail: omit.From(""),
+ ReporterPhone: omit.From(""),
+ }
+ quick, err := models.PublicreportQuicks.Insert(&setter).One(r.Context(), db.PGInstance.BobDB)
+ if err != nil {
+ respondError(w, "Failed to create database record", err, http.StatusInternalServerError)
+ return
+ }
+ _, err = psql.Update(
+ um.Table("publicreport.quick"),
+ um.SetCol("location").To(fmt.Sprintf("ST_GeometryFromText('Point(%f %f)')", longitude, latitude)),
+ um.Where(psql.Quote("id").EQ(psql.Arg(quick.ID))),
+ ).Exec(r.Context(), db.PGInstance.BobDB)
+ if err != nil {
+ respondError(w, "Failed to insert publicreport", err, http.StatusInternalServerError)
+ return
+ }
+ log.Info().Float64("latitude", latitude).Float64("longitude", longitude).Msg("Got upload")
+ photoSetters := make([]*models.PublicreportQuickPhotoSetter, 0)
+ uploads, err := extractPhotoUploads(r)
+ if err != nil {
+ respondError(w, "Failed to extract photo uploads", err, http.StatusInternalServerError)
+ return
+ }
+ for _, u := range uploads {
+ photoSetters = append(photoSetters, &models.PublicreportQuickPhotoSetter{
+ Filename: omit.From(u.Filename),
+ Size: omit.From(u.Size),
+ UUID: omit.From(u.UUID),
+ })
+ }
+ err = quick.InsertQuickPhotos(r.Context(), db.PGInstance.BobDB, photoSetters...)
+ if err != nil {
+ respondError(w, "Failed to create photo records", err, http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, fmt.Sprintf("/quick-submit-complete?report=%s", u), http.StatusFound)
}
- u, err := GenerateReportID()
- if err != nil {
- respondError(w, "Failed to create quick report public ID", err, http.StatusInternalServerError)
- return
+*/
+func sanitizeReportID(r string) string {
+ result := ""
+ for _, char := range r {
+ if char != '-' {
+ result += string(char)
+ }
}
- c, err := h3utils.GetCell(longitude, latitude, 15)
- setter := models.PublicreportQuickSetter{
- Created: omit.From(time.Now()),
- Comments: omit.From(comments),
- //Location: omitnull.From(fmt.Sprintf("ST_GeometryFromText(Point(%s %s))", longitude, latitude)),
- H3cell: omitnull.From(c.String()),
- PublicID: omit.From(u),
- ReporterEmail: omit.From(""),
- ReporterPhone: omit.From(""),
- }
- quick, err := models.PublicreportQuicks.Insert(&setter).One(r.Context(), db.PGInstance.BobDB)
- if err != nil {
- respondError(w, "Failed to create database record", err, http.StatusInternalServerError)
- return
- }
- _, err = psql.Update(
- um.Table("publicreport.quick"),
- um.SetCol("location").To(fmt.Sprintf("ST_GeometryFromText('Point(%f %f)')", longitude, latitude)),
- um.Where(psql.Quote("id").EQ(psql.Arg(quick.ID))),
- ).Exec(r.Context(), db.PGInstance.BobDB)
- if err != nil {
- respondError(w, "Failed to insert publicreport", err, http.StatusInternalServerError)
- return
- }
- log.Info().Float64("latitude", latitude).Float64("longitude", longitude).Msg("Got upload")
- photoSetters := make([]*models.PublicreportQuickPhotoSetter, 0)
- uploads, err := extractPhotoUploads(r)
- if err != nil {
- respondError(w, "Failed to extract photo uploads", err, http.StatusInternalServerError)
- return
- }
- for _, u := range uploads {
- photoSetters = append(photoSetters, &models.PublicreportQuickPhotoSetter{
- Filename: omit.From(u.Filename),
- Size: omit.From(u.Size),
- UUID: omit.From(u.UUID),
- })
- }
- err = quick.InsertQuickPhotos(r.Context(), db.PGInstance.BobDB, photoSetters...)
- if err != nil {
- respondError(w, "Failed to create photo records", err, http.StatusInternalServerError)
- return
- }
- http.Redirect(w, r, fmt.Sprintf("/quick-submit-complete?report=%s", u), http.StatusFound)
-}*/
+ return strings.ToUpper(result)
+}
diff --git a/public-report/template/status.html b/public-report/template/status.html
index 8e8a0b52..b1dfcf18 100644
--- a/public-report/template/status.html
+++ b/public-report/template/status.html
@@ -35,6 +35,43 @@
}
}
+
{{end}}
{{define "content"}}