Remove string-based queries for public report data

Use the new jet hotness
This commit is contained in:
Eli Ribble 2026-05-07 23:22:50 +00:00
parent 12213fb31b
commit 61ad3fbe45
No known key found for this signature in database
4 changed files with 115 additions and 86 deletions

View file

@ -39,6 +39,18 @@ func ReportsFromIDs(ctx context.Context, report_ids []int64) ([]model.Report, er
WHERE(table.Report.ID.IN(sql_ids...)) WHERE(table.Report.ID.IN(sql_ids...))
return db.ExecuteMany[model.Report](ctx, statement) return db.ExecuteMany[model.Report](ctx, statement)
} }
func ReportsFromIDsForOrg(ctx context.Context, txn db.Ex, report_ids []int64, org_id int64) ([]model.Report, error) {
sql_ids := make([]postgres.Expression, len(report_ids))
for i, report_id := range report_ids {
sql_ids[i] = postgres.Int(report_id)
}
statement := table.Report.SELECT(
table.Report.AllColumns,
).FROM(table.Report).
WHERE(table.Report.ID.IN(sql_ids...).AND(
table.Report.OrganizationID.EQ(postgres.Int(org_id))))
return db.ExecuteManyTx[model.Report](ctx, txn, statement)
}
func ReportFromPublicID(ctx context.Context, txn db.Ex, public_id string) (*model.Report, error) { func ReportFromPublicID(ctx context.Context, txn db.Ex, public_id string) (*model.Report, error) {
statement := table.Report.SELECT( statement := table.Report.SELECT(
table.Report.AllColumns, table.Report.AllColumns,
@ -68,3 +80,11 @@ func ReportFromPublicIDForOrg(ctx context.Context, txn db.Ex, public_id string,
} }
return &result, nil return &result, nil
} }
func ReportsUnreviewedForOrganization(ctx context.Context, txn db.Ex, org_id int64) ([]model.Report, error) {
statement := table.Report.SELECT(
table.Report.AllColumns,
).FROM(table.Report).
WHERE(table.Report.Reviewed.IS_NULL().AND(
table.Report.OrganizationID.EQ(postgres.Int(org_id))))
return db.ExecuteManyTx[model.Report](ctx, txn, statement)
}

View file

@ -213,8 +213,8 @@ func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_
func PublicReportReporterUpdated(ctx context.Context, org_id int32, report_id string) { func PublicReportReporterUpdated(ctx context.Context, org_id int32, report_id string) {
event.Updated(event.TypeRMOPublicReport, org_id, report_id) event.Updated(event.TypeRMOPublicReport, org_id, report_id)
} }
func PublicReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]*types.PublicReport, error) { func PublicReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]types.PublicReport, error) {
return publicreport.ReportsForOrganization(ctx, org_id, is_public) return publicreport.UnreviewedForOrganization(ctx, db.PGInstance.PGXPool, int64(org_id), is_public)
} }
func PublicReportsFromIDs(ctx context.Context, report_ids []int64) ([]modelpublicreport.Report, error) { func PublicReportsFromIDs(ctx context.Context, report_ids []int64) ([]modelpublicreport.Report, error) {
return querypublicreport.ReportsFromIDs(ctx, report_ids) return querypublicreport.ReportsFromIDs(ctx, report_ids)

View file

@ -6,9 +6,12 @@ import (
"github.com/Gleipnir-Technology/bob" "github.com/Gleipnir-Technology/bob"
"github.com/Gleipnir-Technology/bob/dialect/psql" "github.com/Gleipnir-Technology/bob/dialect/psql"
"github.com/Gleipnir-Technology/bob/dialect/psql/dialect"
"github.com/Gleipnir-Technology/bob/dialect/psql/sm" "github.com/Gleipnir-Technology/bob/dialect/psql/sm"
"github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db"
querypublic "github.com/Gleipnir-Technology/nidus-sync/db/query/public"
querypublicreport "github.com/Gleipnir-Technology/nidus-sync/db/query/publicreport"
modelpublic "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model"
modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model"
"github.com/Gleipnir-Technology/nidus-sync/platform/types" "github.com/Gleipnir-Technology/nidus-sync/platform/types"
//"github.com/google/uuid" //"github.com/google/uuid"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -45,20 +48,22 @@ func ByIDWater(ctx context.Context, public_id string, is_public bool) (*types.Pu
} }
return water(ctx, public_id, *report) return water(ctx, public_id, *report)
} }
func ReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]*types.PublicReport, error) { func UnreviewedForOrganization(ctx context.Context, txn db.Ex, org_id int64, is_public bool) ([]types.PublicReport, error) {
query := reportQuery() reports, err := querypublicreport.ReportsUnreviewedForOrganization(ctx, txn, org_id)
query.Apply( if err != nil {
sm.Where(psql.Quote("r", "organization_id").EQ(psql.Arg(org_id))), return nil, fmt.Errorf("reports unreviewed: %w", err)
sm.Where(psql.Quote("r", "reviewed").IsNull()), }
) return reportQueryToRows(ctx, reports, is_public)
return reportQueryToRows(ctx, query, is_public)
} }
func byID(ctx context.Context, public_id string, is_public bool) (*types.PublicReport, error) { func byID(ctx context.Context, public_id string, is_public bool) (*types.PublicReport, error) {
query := reportQuery() report, err := querypublicreport.ReportFromPublicID(ctx, db.PGInstance.PGXPool, public_id)
query.Apply( if err != nil {
sm.Where(psql.Quote("r", "public_id").EQ(psql.Arg(public_id))), return nil, fmt.Errorf("query report from public ID: %w", err)
) }
reports, err := reportQueryToRows(ctx, query, is_public) if report == nil {
return nil, nil
}
reports, err := reportQueryToRows(ctx, []modelpublicreport.Report{*report}, is_public)
if err != nil { if err != nil {
return nil, fmt.Errorf("query to rows: %w", err) return nil, fmt.Errorf("query to rows: %w", err)
} }
@ -66,17 +71,16 @@ func byID(ctx context.Context, public_id string, is_public bool) (*types.PublicR
if len(reports) != 1 { if len(reports) != 1 {
return nil, nil return nil, nil
} }
return reports[0], nil return &reports[0], nil
} }
func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQuery], is_public bool) ([]*types.PublicReport, error) { func reportQueryToRows(ctx context.Context, reports []modelpublicreport.Report, is_public bool) ([]types.PublicReport, error) {
rows, err := bob.All(ctx, db.PGInstance.BobDB, query, scan.StructMapper[types.PublicReport]()) address_ids := make([]int64, 0)
report_ids := make([]int32, len(reports))
if err != nil { for i, report := range reports {
return nil, fmt.Errorf("get reports: %w", err) report_ids[i] = report.ID
if report.AddressID != nil {
address_ids = append(address_ids, int64(*report.AddressID))
} }
report_ids := make([]int32, len(rows))
for i, row := range rows {
report_ids[i] = row.ID
} }
images_by_id, err := loadImagesForReport(ctx, report_ids) images_by_id, err := loadImagesForReport(ctx, report_ids)
if err != nil { if err != nil {
@ -86,31 +90,74 @@ func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQ
if err != nil { if err != nil {
return nil, fmt.Errorf("log entries for reports: %w", err) return nil, fmt.Errorf("log entries for reports: %w", err)
} }
addresses, err := querypublic.AddressesFromIDs(ctx, db.PGInstance.PGXPool, address_ids)
if err != nil {
return nil, fmt.Errorf("addresses for reports: %w", err)
}
addresses_by_id := make(map[int64]modelpublic.Address, 0)
for _, address := range addresses {
addresses_by_id[int64(address.ID)] = address
}
results := make([]*types.PublicReport, len(rows)) results := make([]types.PublicReport, len(reports))
for i, row := range rows { for i, row := range reports {
var images []types.Image
images, ok := images_by_id[row.ID] images, ok := images_by_id[row.ID]
if ok { if !ok {
row.Images = images images = []types.Image{}
} else {
row.Images = []types.Image{}
} }
row.Log = logs_by_report_id[row.ID] logs, ok := logs_by_report_id[row.ID]
if row.Location.Latitude == 0.0 || row.Location.Longitude == 0.0 { if !ok {
row.Location = nil return nil, fmt.Errorf("impossible, missing logs for %d", row.ID)
}
var location *types.Location
if row.Location == nil {
location = nil
}
var address *types.Address
if row.AddressID != nil {
addr, ok := addresses_by_id[int64(*row.AddressID)]
if !ok {
return nil, fmt.Errorf("impossible, missing address %d", row.AddressID)
}
a := types.AddressFromModel(addr)
address = &a
}
if address == nil {
return nil, fmt.Errorf("nil address: %w", err)
}
results[i] = types.PublicReport{
Address: *address,
Concerns: nil,
Created: row.Created,
ID: row.ID,
Images: images,
Location: location,
Log: logs,
DistrictID: &row.OrganizationID,
District: nil,
PublicID: row.PublicID,
Reporter: types.Contact{
CanSMS: &row.ReporterPhoneCanSms,
Email: &row.ReporterEmail,
HasEmail: row.ReporterEmail != "",
HasPhone: row.ReporterPhone != "",
Name: &row.ReporterName,
Phone: &row.ReporterPhone,
},
Status: row.Status.String(),
Type: row.ReportType.String(),
URI: "",
} }
row.Address.Raw = types.AddressToRaw(row.Address)
results[i] = &row
} }
return results, nil return results, nil
} }
func Reports(ctx context.Context, org_id int32, ids []int32, is_public bool) ([]*types.PublicReport, error) { func Reports(ctx context.Context, org_id int64, ids []int64, is_public bool) ([]types.PublicReport, error) {
query := reportQuery() reports, err := querypublicreport.ReportsFromIDsForOrg(ctx, db.PGInstance.PGXPool, ids, org_id)
query.Apply( if err != nil {
sm.Where(psql.Quote("r", "organization_id").EQ(psql.Arg(org_id))), return []types.PublicReport{}, fmt.Errorf("reports from ID for org: %w", err)
sm.Where(psql.Quote("r", "id").EQ(psql.Any(ids))), }
) return reportQueryToRows(ctx, reports, is_public)
return reportQueryToRows(ctx, query, is_public)
} }
func ReportsForOrganizationCount(ctx context.Context, org_id int32) (uint, error) { func ReportsForOrganizationCount(ctx context.Context, org_id int32) (uint, error) {
type _Row struct { type _Row struct {
@ -143,38 +190,3 @@ func copyReportContent(src types.PublicReport, dst *types.PublicReport) {
dst.Type = src.Type dst.Type = src.Type
dst.URI = src.URI dst.URI = src.URI
} }
func reportQuery() bob.BaseQuery[*dialect.SelectQuery] {
return psql.Select(
sm.Columns(
"COALESCE(a.country, '') AS \"address.country\"",
"a.id AS \"address.id\"",
"COALESCE(a.gid, '') AS \"address.gid\"",
"COALESCE(a.location_latitude, 0) AS \"address.location.latitude\"",
"COALESCE(a.location_longitude, 0) AS \"address.location.longitude\"",
"COALESCE(a.locality, '') AS \"address.locality\"",
"COALESCE(a.number_, '') AS \"address.number_\"",
"COALESCE(a.postal_code, '') AS \"address.postal_code\"",
"COALESCE(a.region, '') AS \"address.region\"",
"COALESCE(a.street, '') AS \"address.street\"",
"r.address_raw AS \"address.raw\"",
"r.created",
"r.id",
"r.latlng_accuracy_value AS \"location.accuracy\"",
"COALESCE(ST_Y(r.location::geometry::geometry(point, 4326)), 0.0) AS \"location.latitude\"",
"COALESCE(ST_X(r.location::geometry::geometry(point, 4326)), 0.0) AS \"location.longitude\"",
"r.organization_id",
"r.public_id",
"r.report_type",
"r.reporter_email AS \"reporter.email\"",
"r.reporter_name AS \"reporter.name\"",
"r.reporter_phone AS \"reporter.phone\"",
"r.reporter_phone_can_sms AS \"reporter.can_sms\"",
"r.status",
),
sm.From("publicreport.report").As("r"),
sm.LeftJoin("address").As("a").OnEQ(
psql.Quote("r", "address_id"),
psql.Quote("a", "id"),
),
)
}

View file

@ -212,11 +212,11 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get signals: %w", err) return nil, fmt.Errorf("failed to get signals: %w", err)
} }
report_ids := make([]int32, 0) report_ids := make([]int64, 0)
pool_ids := make([]int32, 0) pool_ids := make([]int32, 0)
for _, row := range rows { for _, row := range rows {
if row.Report.ID != 0 { if row.Report.ID != 0 {
report_ids = append(report_ids, row.Report.ID) report_ids = append(report_ids, int64(row.Report.ID))
} else if row.Pool.ID != 0 { } else if row.Pool.ID != 0 {
pool_ids = append(pool_ids, row.Pool.ID) pool_ids = append(pool_ids, row.Pool.ID)
} }
@ -225,7 +225,7 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("getting pools by ID: %w", err) return nil, fmt.Errorf("getting pools by ID: %w", err)
} }
reports, err := publicreport.Reports(ctx, org_id, report_ids, false) reports, err := publicreport.Reports(ctx, int64(org_id), report_ids, false)
if err != nil { if err != nil {
return nil, fmt.Errorf("getting reports by ID: %w", err) return nil, fmt.Errorf("getting reports by ID: %w", err)
} }
@ -234,7 +234,7 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) {
pool_map[pool.ID] = pool pool_map[pool.ID] = pool
log.Debug().Int32("pool", pool.ID).Msg("Added to map") log.Debug().Int32("pool", pool.ID).Msg("Added to map")
} }
report_map := make(map[int32]*types.PublicReport, len(report_ids)) report_map := make(map[int32]types.PublicReport, len(report_ids))
for _, report := range reports { for _, report := range reports {
report_map[report.ID] = report report_map[report.ID] = report
} }
@ -254,11 +254,8 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) {
if !ok { if !ok {
return nil, fmt.Errorf("failed to get report %d for %d", row.Report.ID, row.ID) return nil, fmt.Errorf("failed to get report %d for %d", row.Report.ID, row.ID)
} }
if report == nil {
return nil, fmt.Errorf("got nil for report %d for %d", row.Report.ID, row.ID)
}
row.Pool = nil row.Pool = nil
row.Report = report row.Report = &report
} else { } else {
log.Debug().Int32("id", row.ID).Msg("has no publicrreport nor pool") log.Debug().Int32("id", row.ID).Msg("has no publicrreport nor pool")
row.Pool = nil row.Pool = nil