diff --git a/db/query/publicreport/report.go b/db/query/publicreport/report.go index 9a323da3..2a9260d9 100644 --- a/db/query/publicreport/report.go +++ b/db/query/publicreport/report.go @@ -39,6 +39,18 @@ func ReportsFromIDs(ctx context.Context, report_ids []int64) ([]model.Report, er WHERE(table.Report.ID.IN(sql_ids...)) 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) { statement := table.Report.SELECT( table.Report.AllColumns, @@ -68,3 +80,11 @@ func ReportFromPublicIDForOrg(ctx context.Context, txn db.Ex, public_id string, } 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) +} diff --git a/platform/publicreport.go b/platform/publicreport.go index e79a6e78..634f4616 100644 --- a/platform/publicreport.go +++ b/platform/publicreport.go @@ -213,8 +213,8 @@ func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_ func PublicReportReporterUpdated(ctx context.Context, org_id int32, report_id string) { event.Updated(event.TypeRMOPublicReport, org_id, report_id) } -func PublicReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]*types.PublicReport, error) { - return publicreport.ReportsForOrganization(ctx, org_id, is_public) +func PublicReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]types.PublicReport, error) { + return publicreport.UnreviewedForOrganization(ctx, db.PGInstance.PGXPool, int64(org_id), is_public) } func PublicReportsFromIDs(ctx context.Context, report_ids []int64) ([]modelpublicreport.Report, error) { return querypublicreport.ReportsFromIDs(ctx, report_ids) diff --git a/platform/publicreport/report.go b/platform/publicreport/report.go index b3ba1e70..a2b87242 100644 --- a/platform/publicreport/report.go +++ b/platform/publicreport/report.go @@ -6,9 +6,12 @@ import ( "github.com/Gleipnir-Technology/bob" "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/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/google/uuid" "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) } -func ReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]*types.PublicReport, error) { - query := reportQuery() - query.Apply( - sm.Where(psql.Quote("r", "organization_id").EQ(psql.Arg(org_id))), - sm.Where(psql.Quote("r", "reviewed").IsNull()), - ) - return reportQueryToRows(ctx, query, is_public) +func UnreviewedForOrganization(ctx context.Context, txn db.Ex, org_id int64, is_public bool) ([]types.PublicReport, error) { + reports, err := querypublicreport.ReportsUnreviewedForOrganization(ctx, txn, org_id) + if err != nil { + return nil, fmt.Errorf("reports unreviewed: %w", err) + } + return reportQueryToRows(ctx, reports, is_public) } func byID(ctx context.Context, public_id string, is_public bool) (*types.PublicReport, error) { - query := reportQuery() - query.Apply( - sm.Where(psql.Quote("r", "public_id").EQ(psql.Arg(public_id))), - ) - reports, err := reportQueryToRows(ctx, query, is_public) + report, err := querypublicreport.ReportFromPublicID(ctx, db.PGInstance.PGXPool, public_id) + if err != nil { + return nil, fmt.Errorf("query report from public ID: %w", err) + } + if report == nil { + return nil, nil + } + reports, err := reportQueryToRows(ctx, []modelpublicreport.Report{*report}, is_public) if err != nil { 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 { 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) { - rows, err := bob.All(ctx, db.PGInstance.BobDB, query, scan.StructMapper[types.PublicReport]()) - - if err != nil { - return nil, fmt.Errorf("get reports: %w", err) - } - report_ids := make([]int32, len(rows)) - for i, row := range rows { - report_ids[i] = row.ID +func reportQueryToRows(ctx context.Context, reports []modelpublicreport.Report, is_public bool) ([]types.PublicReport, error) { + address_ids := make([]int64, 0) + report_ids := make([]int32, len(reports)) + for i, report := range reports { + report_ids[i] = report.ID + if report.AddressID != nil { + address_ids = append(address_ids, int64(*report.AddressID)) + } } images_by_id, err := loadImagesForReport(ctx, report_ids) if err != nil { @@ -86,31 +90,74 @@ func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQ if err != nil { 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)) - for i, row := range rows { + results := make([]types.PublicReport, len(reports)) + for i, row := range reports { + var images []types.Image images, ok := images_by_id[row.ID] - if ok { - row.Images = images - } else { - row.Images = []types.Image{} + if !ok { + images = []types.Image{} } - row.Log = logs_by_report_id[row.ID] - if row.Location.Latitude == 0.0 || row.Location.Longitude == 0.0 { - row.Location = nil + logs, ok := logs_by_report_id[row.ID] + if !ok { + 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 } -func Reports(ctx context.Context, org_id int32, ids []int32, is_public bool) ([]*types.PublicReport, error) { - query := reportQuery() - query.Apply( - sm.Where(psql.Quote("r", "organization_id").EQ(psql.Arg(org_id))), - sm.Where(psql.Quote("r", "id").EQ(psql.Any(ids))), - ) - return reportQueryToRows(ctx, query, is_public) +func Reports(ctx context.Context, org_id int64, ids []int64, is_public bool) ([]types.PublicReport, error) { + reports, err := querypublicreport.ReportsFromIDsForOrg(ctx, db.PGInstance.PGXPool, ids, org_id) + if err != nil { + return []types.PublicReport{}, fmt.Errorf("reports from ID for org: %w", err) + } + return reportQueryToRows(ctx, reports, is_public) } func ReportsForOrganizationCount(ctx context.Context, org_id int32) (uint, error) { type _Row struct { @@ -143,38 +190,3 @@ func copyReportContent(src types.PublicReport, dst *types.PublicReport) { dst.Type = src.Type 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"), - ), - ) -} diff --git a/platform/signal.go b/platform/signal.go index 0a201c89..470ac770 100644 --- a/platform/signal.go +++ b/platform/signal.go @@ -212,11 +212,11 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) { if err != nil { 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) for _, row := range rows { 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 { 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 { 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 { 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 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 { report_map[report.ID] = report } @@ -254,11 +254,8 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) { if !ok { 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.Report = report + row.Report = &report } else { log.Debug().Int32("id", row.ID).Msg("has no publicrreport nor pool") row.Pool = nil