Add logic for searching for a report and getting the status.
This commit is contained in:
parent
f6127af058
commit
4303534396
5 changed files with 376 additions and 99 deletions
112
db/sql/publicreport_publicid_table.bob.go
Normal file
112
db/sql/publicreport_publicid_table.bob.go
Normal file
|
|
@ -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)
|
||||
}
|
||||
25
db/sql/publicreport_publicid_table.bob.sql
Normal file
25
db/sql/publicreport_publicid_table.bob.sql
Normal file
|
|
@ -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;
|
||||
22
db/sql/publicreport_publicid_table.sql
Normal file
22
db/sql/publicreport_publicid_table.sql
Normal file
|
|
@ -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;
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,43 @@
|
|||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function formatReportID(inputElement) {
|
||||
// Save current cursor position
|
||||
const cursorPos = inputElement.selectionStart;
|
||||
|
||||
// Get current value and remove existing hyphens
|
||||
let value = inputElement.value.replace(/-/g, '');
|
||||
|
||||
// Calculate how many hyphens were before the cursor
|
||||
const beforeCursor = inputElement.value.substring(0, cursorPos);
|
||||
const hyphensBefore = (beforeCursor.match(/-/g) || []).length;
|
||||
|
||||
// Format the value with hyphens at positions 4 and 8
|
||||
if (value.length > 8) {
|
||||
value = value.substring(0, 4) + '-' + value.substring(4, 8) + '-' + value.substring(8);
|
||||
} else if (value.length > 4) {
|
||||
value = value.substring(0, 4) + '-' + value.substring(4);
|
||||
}
|
||||
|
||||
// Update input field value
|
||||
inputElement.value = value;
|
||||
|
||||
// Calculate new cursor position
|
||||
const newHyphensBefore = (value.substring(0, cursorPos - hyphensBefore +
|
||||
Math.min(hyphensBefore, 2)).match(/-/g) || []).length;
|
||||
const newPosition = cursorPos - hyphensBefore + newHyphensBefore;
|
||||
|
||||
// Restore cursor position
|
||||
inputElement.setSelectionRange(newPosition, newPosition);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('report').addEventListener('input', function() {
|
||||
formatReportID(this);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
{{define "content"}}
|
||||
<main>
|
||||
|
|
@ -72,14 +109,19 @@
|
|||
If you have a report ID from a previous request, enter it below to view the details and current status.
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<form action="/status" method="GET">
|
||||
<div class="mb-3">
|
||||
<label for="reportId" class="form-label">Report ID</label>
|
||||
<input type="text" class="form-control" id="reportId" name="reportId" placeholder="Enter your report ID" required>
|
||||
<label for="report" class="form-label">Report ID</label>
|
||||
<input type="text" class="form-control" id="report" name="report" placeholder="Enter your report ID" value="{{.ReportID}}" required>
|
||||
<div class="form-text">Example: MMD-2023-12345</div>
|
||||
{{ if ne .Error "" }}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{{ .Error }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/service-request/abc-123" type="submit" class="btn btn-primary">View Report Details</a>
|
||||
<button type="submit" class="btn btn-success w-100 submit-btn">View Report Details</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue