Make signals include the object they are attached to (pool, report)

This means pushing the types into the common types module, which
required a refactor of a bunch of other libraries.
This commit is contained in:
Eli Ribble 2026-03-21 01:19:36 +00:00
parent ddc63bfa91
commit 9b6cacda0e
No known key found for this signature in database
18 changed files with 1135 additions and 262 deletions

View file

@ -10,7 +10,7 @@ import (
) )
type contentListSignal struct { type contentListSignal struct {
Signals []platform.Signal `json:"signals"` Signals []*platform.Signal `json:"signals"`
} }
func listSignal(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*contentListSignal, *nhttp.ErrorWithStatus) { func listSignal(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*contentListSignal, *nhttp.ErrorWithStatus) {

View file

@ -78,15 +78,6 @@ var Signals = Table[
Generated: false, Generated: false,
AutoIncr: false, AutoIncr: false,
}, },
Title: column{
Name: "title",
DBType: "text",
Default: "",
Comment: "",
Nullable: false,
Generated: false,
AutoIncr: false,
},
Type: column{ Type: column{
Name: "type_", Name: "type_",
DBType: "public.signaltype", DBType: "public.signaltype",
@ -123,6 +114,24 @@ var Signals = Table[
Generated: true, Generated: true,
AutoIncr: false, AutoIncr: false,
}, },
FeaturePoolFeatureID: column{
Name: "feature_pool_feature_id",
DBType: "integer",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
ReportID: column{
Name: "report_id",
DBType: "integer",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
}, },
Indexes: signalIndexes{ Indexes: signalIndexes{
SignalPkey: index{ SignalPkey: index{
@ -201,6 +210,15 @@ var Signals = Table[
ForeignTable: "user_", ForeignTable: "user_",
ForeignColumns: []string{"id"}, ForeignColumns: []string{"id"},
}, },
SignalSignalFeaturePoolFeatureIDFkey: foreignKey{
constraint: constraint{
Name: "signal.signal_feature_pool_feature_id_fkey",
Columns: []string{"feature_pool_feature_id"},
Comment: "",
},
ForeignTable: "feature_pool",
ForeignColumns: []string{"feature_id"},
},
SignalSignalOrganizationIDFkey: foreignKey{ SignalSignalOrganizationIDFkey: foreignKey{
constraint: constraint{ constraint: constraint{
Name: "signal.signal_organization_id_fkey", Name: "signal.signal_organization_id_fkey",
@ -210,6 +228,15 @@ var Signals = Table[
ForeignTable: "organization", ForeignTable: "organization",
ForeignColumns: []string{"id"}, ForeignColumns: []string{"id"},
}, },
SignalSignalReportIDFkey: foreignKey{
constraint: constraint{
Name: "signal.signal_report_id_fkey",
Columns: []string{"report_id"},
Comment: "",
},
ForeignTable: "publicreport.report",
ForeignColumns: []string{"id"},
},
SignalSignalSiteIDFkey: foreignKey{ SignalSignalSiteIDFkey: foreignKey{
constraint: constraint{ constraint: constraint{
Name: "signal.signal_site_id_fkey", Name: "signal.signal_site_id_fkey",
@ -222,6 +249,14 @@ var Signals = Table[
}, },
Checks: signalChecks{ Checks: signalChecks{
CheckExclusiveReference: check{
constraint: constraint{
Name: "check_exclusive_reference",
Columns: []string{"feature_pool_feature_id", "report_id"},
Comment: "",
},
Expression: "((feature_pool_feature_id IS NULL) OR (report_id IS NULL))",
},
ValidLocationTypes: check{ ValidLocationTypes: check{
constraint: constraint{ constraint: constraint{
Name: "valid_location_types", Name: "valid_location_types",
@ -235,23 +270,24 @@ var Signals = Table[
} }
type signalColumns struct { type signalColumns struct {
Addressed column Addressed column
Addressor column Addressor column
Created column Created column
Creator column Creator column
ID column ID column
OrganizationID column OrganizationID column
Species column Species column
Title column Type column
Type column SiteID column
SiteID column Location column
Location column LocationType column
LocationType column FeaturePoolFeatureID column
ReportID column
} }
func (c signalColumns) AsSlice() []column { func (c signalColumns) AsSlice() []column {
return []column{ return []column{
c.Addressed, c.Addressor, c.Created, c.Creator, c.ID, c.OrganizationID, c.Species, c.Title, c.Type, c.SiteID, c.Location, c.LocationType, c.Addressed, c.Addressor, c.Created, c.Creator, c.ID, c.OrganizationID, c.Species, c.Type, c.SiteID, c.Location, c.LocationType, c.FeaturePoolFeatureID, c.ReportID,
} }
} }
@ -268,15 +304,17 @@ func (i signalIndexes) AsSlice() []index {
} }
type signalForeignKeys struct { type signalForeignKeys struct {
SignalSignalAddressorFkey foreignKey SignalSignalAddressorFkey foreignKey
SignalSignalCreatorFkey foreignKey SignalSignalCreatorFkey foreignKey
SignalSignalOrganizationIDFkey foreignKey SignalSignalFeaturePoolFeatureIDFkey foreignKey
SignalSignalSiteIDFkey foreignKey SignalSignalOrganizationIDFkey foreignKey
SignalSignalReportIDFkey foreignKey
SignalSignalSiteIDFkey foreignKey
} }
func (f signalForeignKeys) AsSlice() []foreignKey { func (f signalForeignKeys) AsSlice() []foreignKey {
return []foreignKey{ return []foreignKey{
f.SignalSignalAddressorFkey, f.SignalSignalCreatorFkey, f.SignalSignalOrganizationIDFkey, f.SignalSignalSiteIDFkey, f.SignalSignalAddressorFkey, f.SignalSignalCreatorFkey, f.SignalSignalFeaturePoolFeatureIDFkey, f.SignalSignalOrganizationIDFkey, f.SignalSignalReportIDFkey, f.SignalSignalSiteIDFkey,
} }
} }
@ -287,11 +325,12 @@ func (u signalUniques) AsSlice() []constraint {
} }
type signalChecks struct { type signalChecks struct {
ValidLocationTypes check CheckExclusiveReference check
ValidLocationTypes check
} }
func (c signalChecks) AsSlice() []check { func (c signalChecks) AsSlice() []check {
return []check{ return []check{
c.ValidLocationTypes, c.CheckExclusiveReference, c.ValidLocationTypes,
} }
} }

View file

@ -0,0 +1,4 @@
-- +goose up
ALTER TABLE signal DROP COLUMN title;
-- +goose down
ALTER TABLE signal ADD COLUMN title TEXT NOT NULL;

View file

@ -0,0 +1,12 @@
-- +goose Up
ALTER TABLE signal ADD COLUMN feature_pool_feature_id INTEGER REFERENCES feature_pool(feature_id);
ALTER TABLE signal ADD COLUMN report_id INTEGER REFERENCES publicreport.report(id);
ALTER TABLE signal
ADD CONSTRAINT check_exclusive_reference
CHECK (
(feature_pool_feature_id IS NULL OR report_id IS NULL)
);
-- +goose Down
ALTER TABLE signal DROP CONSTRAINT check_exclusive_reference;
ALTER TABLE signal DROP COLUMN report_id;
ALTER TABLE signal DROP COLUMN feature_pool_feature_id;

View file

@ -45,8 +45,9 @@ type FeaturePoolsQuery = *psql.ViewQuery[*FeaturePool, FeaturePoolSlice]
// featurePoolR is where relationships are stored. // featurePoolR is where relationships are stored.
type featurePoolR struct { type featurePoolR struct {
Feature *Feature // feature_pool.feature_pool_feature_id_fkey Feature *Feature // feature_pool.feature_pool_feature_id_fkey
ReviewTaskPools ReviewTaskPoolSlice // review_task_pool.review_task_pool_feature_pool_fkey ReviewTaskPools ReviewTaskPoolSlice // review_task_pool.review_task_pool_feature_pool_fkey
FeaturePoolFeatureSignals SignalSlice // signal.signal_feature_pool_feature_id_fkey
} }
func buildFeaturePoolColumns(alias string) featurePoolColumns { func buildFeaturePoolColumns(alias string) featurePoolColumns {
@ -465,6 +466,30 @@ func (os FeaturePoolSlice) ReviewTaskPools(mods ...bob.Mod[*dialect.SelectQuery]
)...) )...)
} }
// FeaturePoolFeatureSignals starts a query for related objects on signal
func (o *FeaturePool) FeaturePoolFeatureSignals(mods ...bob.Mod[*dialect.SelectQuery]) SignalsQuery {
return Signals.Query(append(mods,
sm.Where(Signals.Columns.FeaturePoolFeatureID.EQ(psql.Arg(o.FeatureID))),
)...)
}
func (os FeaturePoolSlice) FeaturePoolFeatureSignals(mods ...bob.Mod[*dialect.SelectQuery]) SignalsQuery {
pkFeatureID := make(pgtypes.Array[int32], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkFeatureID = append(pkFeatureID, o.FeatureID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkFeatureID), "integer[]")),
))
return Signals.Query(append(mods,
sm.Where(psql.Group(Signals.Columns.FeaturePoolFeatureID).OP("IN", PKArgExpr)),
)...)
}
func attachFeaturePoolFeature0(ctx context.Context, exec bob.Executor, count int, featurePool0 *FeaturePool, feature1 *Feature) (*FeaturePool, error) { func attachFeaturePoolFeature0(ctx context.Context, exec bob.Executor, count int, featurePool0 *FeaturePool, feature1 *Feature) (*FeaturePool, error) {
setter := &FeaturePoolSetter{ setter := &FeaturePoolSetter{
FeatureID: omit.From(feature1.ID), FeatureID: omit.From(feature1.ID),
@ -581,6 +606,74 @@ func (featurePool0 *FeaturePool) AttachReviewTaskPools(ctx context.Context, exec
return nil return nil
} }
func insertFeaturePoolFeaturePoolFeatureSignals0(ctx context.Context, exec bob.Executor, signals1 []*SignalSetter, featurePool0 *FeaturePool) (SignalSlice, error) {
for i := range signals1 {
signals1[i].FeaturePoolFeatureID = omitnull.From(featurePool0.FeatureID)
}
ret, err := Signals.Insert(bob.ToMods(signals1...)).All(ctx, exec)
if err != nil {
return ret, fmt.Errorf("insertFeaturePoolFeaturePoolFeatureSignals0: %w", err)
}
return ret, nil
}
func attachFeaturePoolFeaturePoolFeatureSignals0(ctx context.Context, exec bob.Executor, count int, signals1 SignalSlice, featurePool0 *FeaturePool) (SignalSlice, error) {
setter := &SignalSetter{
FeaturePoolFeatureID: omitnull.From(featurePool0.FeatureID),
}
err := signals1.UpdateAll(ctx, exec, *setter)
if err != nil {
return nil, fmt.Errorf("attachFeaturePoolFeaturePoolFeatureSignals0: %w", err)
}
return signals1, nil
}
func (featurePool0 *FeaturePool) InsertFeaturePoolFeatureSignals(ctx context.Context, exec bob.Executor, related ...*SignalSetter) error {
if len(related) == 0 {
return nil
}
var err error
signals1, err := insertFeaturePoolFeaturePoolFeatureSignals0(ctx, exec, related, featurePool0)
if err != nil {
return err
}
featurePool0.R.FeaturePoolFeatureSignals = append(featurePool0.R.FeaturePoolFeatureSignals, signals1...)
for _, rel := range signals1 {
rel.R.FeaturePoolFeatureFeaturePool = featurePool0
}
return nil
}
func (featurePool0 *FeaturePool) AttachFeaturePoolFeatureSignals(ctx context.Context, exec bob.Executor, related ...*Signal) error {
if len(related) == 0 {
return nil
}
var err error
signals1 := SignalSlice(related)
_, err = attachFeaturePoolFeaturePoolFeatureSignals0(ctx, exec, len(related), signals1, featurePool0)
if err != nil {
return err
}
featurePool0.R.FeaturePoolFeatureSignals = append(featurePool0.R.FeaturePoolFeatureSignals, signals1...)
for _, rel := range related {
rel.R.FeaturePoolFeatureFeaturePool = featurePool0
}
return nil
}
type featurePoolWhere[Q psql.Filterable] struct { type featurePoolWhere[Q psql.Filterable] struct {
FeatureID psql.WhereMod[Q, int32] FeatureID psql.WhereMod[Q, int32]
Condition psql.WhereMod[Q, enums.Poolconditiontype] Condition psql.WhereMod[Q, enums.Poolconditiontype]
@ -633,6 +726,20 @@ func (o *FeaturePool) Preload(name string, retrieved any) error {
} }
} }
return nil return nil
case "FeaturePoolFeatureSignals":
rels, ok := retrieved.(SignalSlice)
if !ok {
return fmt.Errorf("featurePool cannot load %T as %q", retrieved, name)
}
o.R.FeaturePoolFeatureSignals = rels
for _, rel := range rels {
if rel != nil {
rel.R.FeaturePoolFeatureFeaturePool = o
}
}
return nil
default: default:
return fmt.Errorf("featurePool has no relationship %q", name) return fmt.Errorf("featurePool has no relationship %q", name)
} }
@ -661,8 +768,9 @@ func buildFeaturePoolPreloader() featurePoolPreloader {
} }
type featurePoolThenLoader[Q orm.Loadable] struct { type featurePoolThenLoader[Q orm.Loadable] struct {
Feature func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] Feature func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
ReviewTaskPools func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] ReviewTaskPools func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
FeaturePoolFeatureSignals func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
} }
func buildFeaturePoolThenLoader[Q orm.Loadable]() featurePoolThenLoader[Q] { func buildFeaturePoolThenLoader[Q orm.Loadable]() featurePoolThenLoader[Q] {
@ -672,6 +780,9 @@ func buildFeaturePoolThenLoader[Q orm.Loadable]() featurePoolThenLoader[Q] {
type ReviewTaskPoolsLoadInterface interface { type ReviewTaskPoolsLoadInterface interface {
LoadReviewTaskPools(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error LoadReviewTaskPools(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
} }
type FeaturePoolFeatureSignalsLoadInterface interface {
LoadFeaturePoolFeatureSignals(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
return featurePoolThenLoader[Q]{ return featurePoolThenLoader[Q]{
Feature: thenLoadBuilder[Q]( Feature: thenLoadBuilder[Q](
@ -686,6 +797,12 @@ func buildFeaturePoolThenLoader[Q orm.Loadable]() featurePoolThenLoader[Q] {
return retrieved.LoadReviewTaskPools(ctx, exec, mods...) return retrieved.LoadReviewTaskPools(ctx, exec, mods...)
}, },
), ),
FeaturePoolFeatureSignals: thenLoadBuilder[Q](
"FeaturePoolFeatureSignals",
func(ctx context.Context, exec bob.Executor, retrieved FeaturePoolFeatureSignalsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadFeaturePoolFeatureSignals(ctx, exec, mods...)
},
),
} }
} }
@ -801,3 +918,67 @@ func (os FeaturePoolSlice) LoadReviewTaskPools(ctx context.Context, exec bob.Exe
return nil return nil
} }
// LoadFeaturePoolFeatureSignals loads the featurePool's FeaturePoolFeatureSignals into the .R struct
func (o *FeaturePool) LoadFeaturePoolFeatureSignals(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.FeaturePoolFeatureSignals = nil
related, err := o.FeaturePoolFeatureSignals(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, rel := range related {
rel.R.FeaturePoolFeatureFeaturePool = o
}
o.R.FeaturePoolFeatureSignals = related
return nil
}
// LoadFeaturePoolFeatureSignals loads the featurePool's FeaturePoolFeatureSignals into the .R struct
func (os FeaturePoolSlice) LoadFeaturePoolFeatureSignals(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
signals, err := os.FeaturePoolFeatureSignals(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
o.R.FeaturePoolFeatureSignals = nil
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range signals {
if !rel.FeaturePoolFeatureID.IsValue() {
continue
}
if !(rel.FeaturePoolFeatureID.IsValue() && o.FeatureID == rel.FeaturePoolFeatureID.MustGet()) {
continue
}
rel.R.FeaturePoolFeatureFeaturePool = o
o.R.FeaturePoolFeatureSignals = append(o.R.FeaturePoolFeatureSignals, rel)
}
}
return nil
}

View file

@ -81,6 +81,7 @@ type publicreportReportR struct {
ReportLogs PublicreportReportLogSlice // publicreport.report_log.report_log_report_id_fkey ReportLogs PublicreportReportLogSlice // publicreport.report_log.report_log_report_id_fkey
Water *PublicreportWater // publicreport.water.water_report_id_fkey Water *PublicreportWater // publicreport.water.water_report_id_fkey
ReportTexts ReportTextSlice // report_text.report_text_report_id_fkey ReportTexts ReportTextSlice // report_text.report_text_report_id_fkey
Signals SignalSlice // signal.signal_report_id_fkey
} }
func buildPublicreportReportColumns(alias string) publicreportReportColumns { func buildPublicreportReportColumns(alias string) publicreportReportColumns {
@ -1186,6 +1187,30 @@ func (os PublicreportReportSlice) ReportTexts(mods ...bob.Mod[*dialect.SelectQue
)...) )...)
} }
// Signals starts a query for related objects on signal
func (o *PublicreportReport) Signals(mods ...bob.Mod[*dialect.SelectQuery]) SignalsQuery {
return Signals.Query(append(mods,
sm.Where(Signals.Columns.ReportID.EQ(psql.Arg(o.ID))),
)...)
}
func (os PublicreportReportSlice) Signals(mods ...bob.Mod[*dialect.SelectQuery]) SignalsQuery {
pkID := make(pgtypes.Array[int32], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkID = append(pkID, o.ID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkID), "integer[]")),
))
return Signals.Query(append(mods,
sm.Where(psql.Group(Signals.Columns.ReportID).OP("IN", PKArgExpr)),
)...)
}
func insertPublicreportReportTextJobs0(ctx context.Context, exec bob.Executor, commsTextJobs1 []*CommsTextJobSetter, publicreportReport0 *PublicreportReport) (CommsTextJobSlice, error) { func insertPublicreportReportTextJobs0(ctx context.Context, exec bob.Executor, commsTextJobs1 []*CommsTextJobSetter, publicreportReport0 *PublicreportReport) (CommsTextJobSlice, error) {
for i := range commsTextJobs1 { for i := range commsTextJobs1 {
commsTextJobs1[i].ReportID = omitnull.From(publicreportReport0.ID) commsTextJobs1[i].ReportID = omitnull.From(publicreportReport0.ID)
@ -1843,6 +1868,74 @@ func (publicreportReport0 *PublicreportReport) AttachReportTexts(ctx context.Con
return nil return nil
} }
func insertPublicreportReportSignals0(ctx context.Context, exec bob.Executor, signals1 []*SignalSetter, publicreportReport0 *PublicreportReport) (SignalSlice, error) {
for i := range signals1 {
signals1[i].ReportID = omitnull.From(publicreportReport0.ID)
}
ret, err := Signals.Insert(bob.ToMods(signals1...)).All(ctx, exec)
if err != nil {
return ret, fmt.Errorf("insertPublicreportReportSignals0: %w", err)
}
return ret, nil
}
func attachPublicreportReportSignals0(ctx context.Context, exec bob.Executor, count int, signals1 SignalSlice, publicreportReport0 *PublicreportReport) (SignalSlice, error) {
setter := &SignalSetter{
ReportID: omitnull.From(publicreportReport0.ID),
}
err := signals1.UpdateAll(ctx, exec, *setter)
if err != nil {
return nil, fmt.Errorf("attachPublicreportReportSignals0: %w", err)
}
return signals1, nil
}
func (publicreportReport0 *PublicreportReport) InsertSignals(ctx context.Context, exec bob.Executor, related ...*SignalSetter) error {
if len(related) == 0 {
return nil
}
var err error
signals1, err := insertPublicreportReportSignals0(ctx, exec, related, publicreportReport0)
if err != nil {
return err
}
publicreportReport0.R.Signals = append(publicreportReport0.R.Signals, signals1...)
for _, rel := range signals1 {
rel.R.Report = publicreportReport0
}
return nil
}
func (publicreportReport0 *PublicreportReport) AttachSignals(ctx context.Context, exec bob.Executor, related ...*Signal) error {
if len(related) == 0 {
return nil
}
var err error
signals1 := SignalSlice(related)
_, err = attachPublicreportReportSignals0(ctx, exec, len(related), signals1, publicreportReport0)
if err != nil {
return err
}
publicreportReport0.R.Signals = append(publicreportReport0.R.Signals, signals1...)
for _, rel := range related {
rel.R.Report = publicreportReport0
}
return nil
}
type publicreportReportWhere[Q psql.Filterable] struct { type publicreportReportWhere[Q psql.Filterable] struct {
AddressRaw psql.WhereMod[Q, string] AddressRaw psql.WhereMod[Q, string]
AddressNumber psql.WhereMod[Q, string] AddressNumber psql.WhereMod[Q, string]
@ -2053,6 +2146,20 @@ func (o *PublicreportReport) Preload(name string, retrieved any) error {
o.R.ReportTexts = rels o.R.ReportTexts = rels
for _, rel := range rels {
if rel != nil {
rel.R.Report = o
}
}
return nil
case "Signals":
rels, ok := retrieved.(SignalSlice)
if !ok {
return fmt.Errorf("publicreportReport cannot load %T as %q", retrieved, name)
}
o.R.Signals = rels
for _, rel := range rels { for _, rel := range rels {
if rel != nil { if rel != nil {
rel.R.Report = o rel.R.Report = o
@ -2154,6 +2261,7 @@ type publicreportReportThenLoader[Q orm.Loadable] struct {
ReportLogs func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] ReportLogs func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Water func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] Water func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
ReportTexts func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] ReportTexts func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Signals func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
} }
func buildPublicreportReportThenLoader[Q orm.Loadable]() publicreportReportThenLoader[Q] { func buildPublicreportReportThenLoader[Q orm.Loadable]() publicreportReportThenLoader[Q] {
@ -2190,6 +2298,9 @@ func buildPublicreportReportThenLoader[Q orm.Loadable]() publicreportReportThenL
type ReportTextsLoadInterface interface { type ReportTextsLoadInterface interface {
LoadReportTexts(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error LoadReportTexts(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
} }
type SignalsLoadInterface interface {
LoadSignals(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
return publicreportReportThenLoader[Q]{ return publicreportReportThenLoader[Q]{
TextJobs: thenLoadBuilder[Q]( TextJobs: thenLoadBuilder[Q](
@ -2258,6 +2369,12 @@ func buildPublicreportReportThenLoader[Q orm.Loadable]() publicreportReportThenL
return retrieved.LoadReportTexts(ctx, exec, mods...) return retrieved.LoadReportTexts(ctx, exec, mods...)
}, },
), ),
Signals: thenLoadBuilder[Q](
"Signals",
func(ctx context.Context, exec bob.Executor, retrieved SignalsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadSignals(ctx, exec, mods...)
},
),
} }
} }
@ -2915,3 +3032,67 @@ func (os PublicreportReportSlice) LoadReportTexts(ctx context.Context, exec bob.
return nil return nil
} }
// LoadSignals loads the publicreportReport's Signals into the .R struct
func (o *PublicreportReport) LoadSignals(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.Signals = nil
related, err := o.Signals(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, rel := range related {
rel.R.Report = o
}
o.R.Signals = related
return nil
}
// LoadSignals loads the publicreportReport's Signals into the .R struct
func (os PublicreportReportSlice) LoadSignals(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
signals, err := os.Signals(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
o.R.Signals = nil
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range signals {
if !rel.ReportID.IsValue() {
continue
}
if !(rel.ReportID.IsValue() && o.ID == rel.ReportID.MustGet()) {
continue
}
rel.R.Report = o
o.R.Signals = append(o.R.Signals, rel)
}
}
return nil
}

View file

@ -26,18 +26,19 @@ import (
// Signal is an object representing the database table. // Signal is an object representing the database table.
type Signal struct { type Signal struct {
Addressed null.Val[time.Time] `db:"addressed" ` Addressed null.Val[time.Time] `db:"addressed" `
Addressor null.Val[int32] `db:"addressor" ` Addressor null.Val[int32] `db:"addressor" `
Created time.Time `db:"created" ` Created time.Time `db:"created" `
Creator int32 `db:"creator" ` Creator int32 `db:"creator" `
ID int32 `db:"id,pk" ` ID int32 `db:"id,pk" `
OrganizationID int32 `db:"organization_id" ` OrganizationID int32 `db:"organization_id" `
Species null.Val[enums.Mosquitospecies] `db:"species" ` Species null.Val[enums.Mosquitospecies] `db:"species" `
Title string `db:"title" ` Type enums.Signaltype `db:"type_" `
Type enums.Signaltype `db:"type_" ` SiteID null.Val[int32] `db:"site_id" `
SiteID null.Val[int32] `db:"site_id" ` Location string `db:"location" `
Location string `db:"location" ` LocationType null.Val[string] `db:"location_type,generated" `
LocationType null.Val[string] `db:"location_type,generated" ` FeaturePoolFeatureID null.Val[int32] `db:"feature_pool_feature_id" `
ReportID null.Val[int32] `db:"report_id" `
R signalR `db:"-" ` R signalR `db:"-" `
} }
@ -54,48 +55,52 @@ type SignalsQuery = *psql.ViewQuery[*Signal, SignalSlice]
// signalR is where relationships are stored. // signalR is where relationships are stored.
type signalR struct { type signalR struct {
AddressorUser *User // signal.signal_addressor_fkey AddressorUser *User // signal.signal_addressor_fkey
CreatorUser *User // signal.signal_creator_fkey CreatorUser *User // signal.signal_creator_fkey
Organization *Organization // signal.signal_organization_id_fkey FeaturePoolFeatureFeaturePool *FeaturePool // signal.signal_feature_pool_feature_id_fkey
Site *Site // signal.signal_site_id_fkey Organization *Organization // signal.signal_organization_id_fkey
Report *PublicreportReport // signal.signal_report_id_fkey
Site *Site // signal.signal_site_id_fkey
} }
func buildSignalColumns(alias string) signalColumns { func buildSignalColumns(alias string) signalColumns {
return signalColumns{ return signalColumns{
ColumnsExpr: expr.NewColumnsExpr( ColumnsExpr: expr.NewColumnsExpr(
"addressed", "addressor", "created", "creator", "id", "organization_id", "species", "title", "type_", "site_id", "location", "location_type", "addressed", "addressor", "created", "creator", "id", "organization_id", "species", "type_", "site_id", "location", "location_type", "feature_pool_feature_id", "report_id",
).WithParent("signal"), ).WithParent("signal"),
tableAlias: alias, tableAlias: alias,
Addressed: psql.Quote(alias, "addressed"), Addressed: psql.Quote(alias, "addressed"),
Addressor: psql.Quote(alias, "addressor"), Addressor: psql.Quote(alias, "addressor"),
Created: psql.Quote(alias, "created"), Created: psql.Quote(alias, "created"),
Creator: psql.Quote(alias, "creator"), Creator: psql.Quote(alias, "creator"),
ID: psql.Quote(alias, "id"), ID: psql.Quote(alias, "id"),
OrganizationID: psql.Quote(alias, "organization_id"), OrganizationID: psql.Quote(alias, "organization_id"),
Species: psql.Quote(alias, "species"), Species: psql.Quote(alias, "species"),
Title: psql.Quote(alias, "title"), Type: psql.Quote(alias, "type_"),
Type: psql.Quote(alias, "type_"), SiteID: psql.Quote(alias, "site_id"),
SiteID: psql.Quote(alias, "site_id"), Location: psql.Quote(alias, "location"),
Location: psql.Quote(alias, "location"), LocationType: psql.Quote(alias, "location_type"),
LocationType: psql.Quote(alias, "location_type"), FeaturePoolFeatureID: psql.Quote(alias, "feature_pool_feature_id"),
ReportID: psql.Quote(alias, "report_id"),
} }
} }
type signalColumns struct { type signalColumns struct {
expr.ColumnsExpr expr.ColumnsExpr
tableAlias string tableAlias string
Addressed psql.Expression Addressed psql.Expression
Addressor psql.Expression Addressor psql.Expression
Created psql.Expression Created psql.Expression
Creator psql.Expression Creator psql.Expression
ID psql.Expression ID psql.Expression
OrganizationID psql.Expression OrganizationID psql.Expression
Species psql.Expression Species psql.Expression
Title psql.Expression Type psql.Expression
Type psql.Expression SiteID psql.Expression
SiteID psql.Expression Location psql.Expression
Location psql.Expression LocationType psql.Expression
LocationType psql.Expression FeaturePoolFeatureID psql.Expression
ReportID psql.Expression
} }
func (c signalColumns) Alias() string { func (c signalColumns) Alias() string {
@ -110,21 +115,22 @@ func (signalColumns) AliasedAs(alias string) signalColumns {
// All values are optional, and do not have to be set // All values are optional, and do not have to be set
// Generated columns are not included // Generated columns are not included
type SignalSetter struct { type SignalSetter struct {
Addressed omitnull.Val[time.Time] `db:"addressed" ` Addressed omitnull.Val[time.Time] `db:"addressed" `
Addressor omitnull.Val[int32] `db:"addressor" ` Addressor omitnull.Val[int32] `db:"addressor" `
Created omit.Val[time.Time] `db:"created" ` Created omit.Val[time.Time] `db:"created" `
Creator omit.Val[int32] `db:"creator" ` Creator omit.Val[int32] `db:"creator" `
ID omit.Val[int32] `db:"id,pk" ` ID omit.Val[int32] `db:"id,pk" `
OrganizationID omit.Val[int32] `db:"organization_id" ` OrganizationID omit.Val[int32] `db:"organization_id" `
Species omitnull.Val[enums.Mosquitospecies] `db:"species" ` Species omitnull.Val[enums.Mosquitospecies] `db:"species" `
Title omit.Val[string] `db:"title" ` Type omit.Val[enums.Signaltype] `db:"type_" `
Type omit.Val[enums.Signaltype] `db:"type_" ` SiteID omitnull.Val[int32] `db:"site_id" `
SiteID omitnull.Val[int32] `db:"site_id" ` Location omit.Val[string] `db:"location" `
Location omit.Val[string] `db:"location" ` FeaturePoolFeatureID omitnull.Val[int32] `db:"feature_pool_feature_id" `
ReportID omitnull.Val[int32] `db:"report_id" `
} }
func (s SignalSetter) SetColumns() []string { func (s SignalSetter) SetColumns() []string {
vals := make([]string, 0, 11) vals := make([]string, 0, 12)
if !s.Addressed.IsUnset() { if !s.Addressed.IsUnset() {
vals = append(vals, "addressed") vals = append(vals, "addressed")
} }
@ -146,9 +152,6 @@ func (s SignalSetter) SetColumns() []string {
if !s.Species.IsUnset() { if !s.Species.IsUnset() {
vals = append(vals, "species") vals = append(vals, "species")
} }
if s.Title.IsValue() {
vals = append(vals, "title")
}
if s.Type.IsValue() { if s.Type.IsValue() {
vals = append(vals, "type_") vals = append(vals, "type_")
} }
@ -158,6 +161,12 @@ func (s SignalSetter) SetColumns() []string {
if s.Location.IsValue() { if s.Location.IsValue() {
vals = append(vals, "location") vals = append(vals, "location")
} }
if !s.FeaturePoolFeatureID.IsUnset() {
vals = append(vals, "feature_pool_feature_id")
}
if !s.ReportID.IsUnset() {
vals = append(vals, "report_id")
}
return vals return vals
} }
@ -183,9 +192,6 @@ func (s SignalSetter) Overwrite(t *Signal) {
if !s.Species.IsUnset() { if !s.Species.IsUnset() {
t.Species = s.Species.MustGetNull() t.Species = s.Species.MustGetNull()
} }
if s.Title.IsValue() {
t.Title = s.Title.MustGet()
}
if s.Type.IsValue() { if s.Type.IsValue() {
t.Type = s.Type.MustGet() t.Type = s.Type.MustGet()
} }
@ -195,6 +201,12 @@ func (s SignalSetter) Overwrite(t *Signal) {
if s.Location.IsValue() { if s.Location.IsValue() {
t.Location = s.Location.MustGet() t.Location = s.Location.MustGet()
} }
if !s.FeaturePoolFeatureID.IsUnset() {
t.FeaturePoolFeatureID = s.FeaturePoolFeatureID.MustGetNull()
}
if !s.ReportID.IsUnset() {
t.ReportID = s.ReportID.MustGetNull()
}
} }
func (s *SignalSetter) Apply(q *dialect.InsertQuery) { func (s *SignalSetter) Apply(q *dialect.InsertQuery) {
@ -203,7 +215,7 @@ func (s *SignalSetter) Apply(q *dialect.InsertQuery) {
}) })
q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) { q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
vals := make([]bob.Expression, 11) vals := make([]bob.Expression, 12)
if !s.Addressed.IsUnset() { if !s.Addressed.IsUnset() {
vals[0] = psql.Arg(s.Addressed.MustGetNull()) vals[0] = psql.Arg(s.Addressed.MustGetNull())
} else { } else {
@ -246,30 +258,36 @@ func (s *SignalSetter) Apply(q *dialect.InsertQuery) {
vals[6] = psql.Raw("DEFAULT") vals[6] = psql.Raw("DEFAULT")
} }
if s.Title.IsValue() { if s.Type.IsValue() {
vals[7] = psql.Arg(s.Title.MustGet()) vals[7] = psql.Arg(s.Type.MustGet())
} else { } else {
vals[7] = psql.Raw("DEFAULT") vals[7] = psql.Raw("DEFAULT")
} }
if s.Type.IsValue() { if !s.SiteID.IsUnset() {
vals[8] = psql.Arg(s.Type.MustGet()) vals[8] = psql.Arg(s.SiteID.MustGetNull())
} else { } else {
vals[8] = psql.Raw("DEFAULT") vals[8] = psql.Raw("DEFAULT")
} }
if !s.SiteID.IsUnset() { if s.Location.IsValue() {
vals[9] = psql.Arg(s.SiteID.MustGetNull()) vals[9] = psql.Arg(s.Location.MustGet())
} else { } else {
vals[9] = psql.Raw("DEFAULT") vals[9] = psql.Raw("DEFAULT")
} }
if s.Location.IsValue() { if !s.FeaturePoolFeatureID.IsUnset() {
vals[10] = psql.Arg(s.Location.MustGet()) vals[10] = psql.Arg(s.FeaturePoolFeatureID.MustGetNull())
} else { } else {
vals[10] = psql.Raw("DEFAULT") vals[10] = psql.Raw("DEFAULT")
} }
if !s.ReportID.IsUnset() {
vals[11] = psql.Arg(s.ReportID.MustGetNull())
} else {
vals[11] = psql.Raw("DEFAULT")
}
return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "")
})) }))
} }
@ -279,7 +297,7 @@ func (s SignalSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] {
} }
func (s SignalSetter) Expressions(prefix ...string) []bob.Expression { func (s SignalSetter) Expressions(prefix ...string) []bob.Expression {
exprs := make([]bob.Expression, 0, 11) exprs := make([]bob.Expression, 0, 12)
if !s.Addressed.IsUnset() { if !s.Addressed.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
@ -330,13 +348,6 @@ func (s SignalSetter) Expressions(prefix ...string) []bob.Expression {
}}) }})
} }
if s.Title.IsValue() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "title")...),
psql.Arg(s.Title),
}})
}
if s.Type.IsValue() { if s.Type.IsValue() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "type_")...), psql.Quote(append(prefix, "type_")...),
@ -358,6 +369,20 @@ func (s SignalSetter) Expressions(prefix ...string) []bob.Expression {
}}) }})
} }
if !s.FeaturePoolFeatureID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "feature_pool_feature_id")...),
psql.Arg(s.FeaturePoolFeatureID),
}})
}
if !s.ReportID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "report_id")...),
psql.Arg(s.ReportID),
}})
}
return exprs return exprs
} }
@ -632,6 +657,30 @@ func (os SignalSlice) CreatorUser(mods ...bob.Mod[*dialect.SelectQuery]) UsersQu
)...) )...)
} }
// FeaturePoolFeatureFeaturePool starts a query for related objects on feature_pool
func (o *Signal) FeaturePoolFeatureFeaturePool(mods ...bob.Mod[*dialect.SelectQuery]) FeaturePoolsQuery {
return FeaturePools.Query(append(mods,
sm.Where(FeaturePools.Columns.FeatureID.EQ(psql.Arg(o.FeaturePoolFeatureID))),
)...)
}
func (os SignalSlice) FeaturePoolFeatureFeaturePool(mods ...bob.Mod[*dialect.SelectQuery]) FeaturePoolsQuery {
pkFeaturePoolFeatureID := make(pgtypes.Array[null.Val[int32]], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkFeaturePoolFeatureID = append(pkFeaturePoolFeatureID, o.FeaturePoolFeatureID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkFeaturePoolFeatureID), "integer[]")),
))
return FeaturePools.Query(append(mods,
sm.Where(psql.Group(FeaturePools.Columns.FeatureID).OP("IN", PKArgExpr)),
)...)
}
// Organization starts a query for related objects on organization // Organization starts a query for related objects on organization
func (o *Signal) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { func (o *Signal) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery {
return Organizations.Query(append(mods, return Organizations.Query(append(mods,
@ -656,6 +705,30 @@ func (os SignalSlice) Organization(mods ...bob.Mod[*dialect.SelectQuery]) Organi
)...) )...)
} }
// Report starts a query for related objects on publicreport.report
func (o *Signal) Report(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportReportsQuery {
return PublicreportReports.Query(append(mods,
sm.Where(PublicreportReports.Columns.ID.EQ(psql.Arg(o.ReportID))),
)...)
}
func (os SignalSlice) Report(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportReportsQuery {
pkReportID := make(pgtypes.Array[null.Val[int32]], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkReportID = append(pkReportID, o.ReportID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkReportID), "integer[]")),
))
return PublicreportReports.Query(append(mods,
sm.Where(psql.Group(PublicreportReports.Columns.ID).OP("IN", PKArgExpr)),
)...)
}
// Site starts a query for related objects on site // Site starts a query for related objects on site
func (o *Signal) Site(mods ...bob.Mod[*dialect.SelectQuery]) SitesQuery { func (o *Signal) Site(mods ...bob.Mod[*dialect.SelectQuery]) SitesQuery {
return Sites.Query(append(mods, return Sites.Query(append(mods,
@ -776,6 +849,54 @@ func (signal0 *Signal) AttachCreatorUser(ctx context.Context, exec bob.Executor,
return nil return nil
} }
func attachSignalFeaturePoolFeatureFeaturePool0(ctx context.Context, exec bob.Executor, count int, signal0 *Signal, featurePool1 *FeaturePool) (*Signal, error) {
setter := &SignalSetter{
FeaturePoolFeatureID: omitnull.From(featurePool1.FeatureID),
}
err := signal0.Update(ctx, exec, setter)
if err != nil {
return nil, fmt.Errorf("attachSignalFeaturePoolFeatureFeaturePool0: %w", err)
}
return signal0, nil
}
func (signal0 *Signal) InsertFeaturePoolFeatureFeaturePool(ctx context.Context, exec bob.Executor, related *FeaturePoolSetter) error {
var err error
featurePool1, err := FeaturePools.Insert(related).One(ctx, exec)
if err != nil {
return fmt.Errorf("inserting related objects: %w", err)
}
_, err = attachSignalFeaturePoolFeatureFeaturePool0(ctx, exec, 1, signal0, featurePool1)
if err != nil {
return err
}
signal0.R.FeaturePoolFeatureFeaturePool = featurePool1
featurePool1.R.FeaturePoolFeatureSignals = append(featurePool1.R.FeaturePoolFeatureSignals, signal0)
return nil
}
func (signal0 *Signal) AttachFeaturePoolFeatureFeaturePool(ctx context.Context, exec bob.Executor, featurePool1 *FeaturePool) error {
var err error
_, err = attachSignalFeaturePoolFeatureFeaturePool0(ctx, exec, 1, signal0, featurePool1)
if err != nil {
return err
}
signal0.R.FeaturePoolFeatureFeaturePool = featurePool1
featurePool1.R.FeaturePoolFeatureSignals = append(featurePool1.R.FeaturePoolFeatureSignals, signal0)
return nil
}
func attachSignalOrganization0(ctx context.Context, exec bob.Executor, count int, signal0 *Signal, organization1 *Organization) (*Signal, error) { func attachSignalOrganization0(ctx context.Context, exec bob.Executor, count int, signal0 *Signal, organization1 *Organization) (*Signal, error) {
setter := &SignalSetter{ setter := &SignalSetter{
OrganizationID: omit.From(organization1.ID), OrganizationID: omit.From(organization1.ID),
@ -824,6 +945,54 @@ func (signal0 *Signal) AttachOrganization(ctx context.Context, exec bob.Executor
return nil return nil
} }
func attachSignalReport0(ctx context.Context, exec bob.Executor, count int, signal0 *Signal, publicreportReport1 *PublicreportReport) (*Signal, error) {
setter := &SignalSetter{
ReportID: omitnull.From(publicreportReport1.ID),
}
err := signal0.Update(ctx, exec, setter)
if err != nil {
return nil, fmt.Errorf("attachSignalReport0: %w", err)
}
return signal0, nil
}
func (signal0 *Signal) InsertReport(ctx context.Context, exec bob.Executor, related *PublicreportReportSetter) error {
var err error
publicreportReport1, err := PublicreportReports.Insert(related).One(ctx, exec)
if err != nil {
return fmt.Errorf("inserting related objects: %w", err)
}
_, err = attachSignalReport0(ctx, exec, 1, signal0, publicreportReport1)
if err != nil {
return err
}
signal0.R.Report = publicreportReport1
publicreportReport1.R.Signals = append(publicreportReport1.R.Signals, signal0)
return nil
}
func (signal0 *Signal) AttachReport(ctx context.Context, exec bob.Executor, publicreportReport1 *PublicreportReport) error {
var err error
_, err = attachSignalReport0(ctx, exec, 1, signal0, publicreportReport1)
if err != nil {
return err
}
signal0.R.Report = publicreportReport1
publicreportReport1.R.Signals = append(publicreportReport1.R.Signals, signal0)
return nil
}
func attachSignalSite0(ctx context.Context, exec bob.Executor, count int, signal0 *Signal, site1 *Site) (*Signal, error) { func attachSignalSite0(ctx context.Context, exec bob.Executor, count int, signal0 *Signal, site1 *Site) (*Signal, error) {
setter := &SignalSetter{ setter := &SignalSetter{
SiteID: omitnull.From(site1.ID), SiteID: omitnull.From(site1.ID),
@ -873,18 +1042,19 @@ func (signal0 *Signal) AttachSite(ctx context.Context, exec bob.Executor, site1
} }
type signalWhere[Q psql.Filterable] struct { type signalWhere[Q psql.Filterable] struct {
Addressed psql.WhereNullMod[Q, time.Time] Addressed psql.WhereNullMod[Q, time.Time]
Addressor psql.WhereNullMod[Q, int32] Addressor psql.WhereNullMod[Q, int32]
Created psql.WhereMod[Q, time.Time] Created psql.WhereMod[Q, time.Time]
Creator psql.WhereMod[Q, int32] Creator psql.WhereMod[Q, int32]
ID psql.WhereMod[Q, int32] ID psql.WhereMod[Q, int32]
OrganizationID psql.WhereMod[Q, int32] OrganizationID psql.WhereMod[Q, int32]
Species psql.WhereNullMod[Q, enums.Mosquitospecies] Species psql.WhereNullMod[Q, enums.Mosquitospecies]
Title psql.WhereMod[Q, string] Type psql.WhereMod[Q, enums.Signaltype]
Type psql.WhereMod[Q, enums.Signaltype] SiteID psql.WhereNullMod[Q, int32]
SiteID psql.WhereNullMod[Q, int32] Location psql.WhereMod[Q, string]
Location psql.WhereMod[Q, string] LocationType psql.WhereNullMod[Q, string]
LocationType psql.WhereNullMod[Q, string] FeaturePoolFeatureID psql.WhereNullMod[Q, int32]
ReportID psql.WhereNullMod[Q, int32]
} }
func (signalWhere[Q]) AliasedAs(alias string) signalWhere[Q] { func (signalWhere[Q]) AliasedAs(alias string) signalWhere[Q] {
@ -893,18 +1063,19 @@ func (signalWhere[Q]) AliasedAs(alias string) signalWhere[Q] {
func buildSignalWhere[Q psql.Filterable](cols signalColumns) signalWhere[Q] { func buildSignalWhere[Q psql.Filterable](cols signalColumns) signalWhere[Q] {
return signalWhere[Q]{ return signalWhere[Q]{
Addressed: psql.WhereNull[Q, time.Time](cols.Addressed), Addressed: psql.WhereNull[Q, time.Time](cols.Addressed),
Addressor: psql.WhereNull[Q, int32](cols.Addressor), Addressor: psql.WhereNull[Q, int32](cols.Addressor),
Created: psql.Where[Q, time.Time](cols.Created), Created: psql.Where[Q, time.Time](cols.Created),
Creator: psql.Where[Q, int32](cols.Creator), Creator: psql.Where[Q, int32](cols.Creator),
ID: psql.Where[Q, int32](cols.ID), ID: psql.Where[Q, int32](cols.ID),
OrganizationID: psql.Where[Q, int32](cols.OrganizationID), OrganizationID: psql.Where[Q, int32](cols.OrganizationID),
Species: psql.WhereNull[Q, enums.Mosquitospecies](cols.Species), Species: psql.WhereNull[Q, enums.Mosquitospecies](cols.Species),
Title: psql.Where[Q, string](cols.Title), Type: psql.Where[Q, enums.Signaltype](cols.Type),
Type: psql.Where[Q, enums.Signaltype](cols.Type), SiteID: psql.WhereNull[Q, int32](cols.SiteID),
SiteID: psql.WhereNull[Q, int32](cols.SiteID), Location: psql.Where[Q, string](cols.Location),
Location: psql.Where[Q, string](cols.Location), LocationType: psql.WhereNull[Q, string](cols.LocationType),
LocationType: psql.WhereNull[Q, string](cols.LocationType), FeaturePoolFeatureID: psql.WhereNull[Q, int32](cols.FeaturePoolFeatureID),
ReportID: psql.WhereNull[Q, int32](cols.ReportID),
} }
} }
@ -938,6 +1109,18 @@ func (o *Signal) Preload(name string, retrieved any) error {
rel.R.CreatorSignals = SignalSlice{o} rel.R.CreatorSignals = SignalSlice{o}
} }
return nil return nil
case "FeaturePoolFeatureFeaturePool":
rel, ok := retrieved.(*FeaturePool)
if !ok {
return fmt.Errorf("signal cannot load %T as %q", retrieved, name)
}
o.R.FeaturePoolFeatureFeaturePool = rel
if rel != nil {
rel.R.FeaturePoolFeatureSignals = SignalSlice{o}
}
return nil
case "Organization": case "Organization":
rel, ok := retrieved.(*Organization) rel, ok := retrieved.(*Organization)
if !ok { if !ok {
@ -946,6 +1129,18 @@ func (o *Signal) Preload(name string, retrieved any) error {
o.R.Organization = rel o.R.Organization = rel
if rel != nil {
rel.R.Signals = SignalSlice{o}
}
return nil
case "Report":
rel, ok := retrieved.(*PublicreportReport)
if !ok {
return fmt.Errorf("signal cannot load %T as %q", retrieved, name)
}
o.R.Report = rel
if rel != nil { if rel != nil {
rel.R.Signals = SignalSlice{o} rel.R.Signals = SignalSlice{o}
} }
@ -968,10 +1163,12 @@ func (o *Signal) Preload(name string, retrieved any) error {
} }
type signalPreloader struct { type signalPreloader struct {
AddressorUser func(...psql.PreloadOption) psql.Preloader AddressorUser func(...psql.PreloadOption) psql.Preloader
CreatorUser func(...psql.PreloadOption) psql.Preloader CreatorUser func(...psql.PreloadOption) psql.Preloader
Organization func(...psql.PreloadOption) psql.Preloader FeaturePoolFeatureFeaturePool func(...psql.PreloadOption) psql.Preloader
Site func(...psql.PreloadOption) psql.Preloader Organization func(...psql.PreloadOption) psql.Preloader
Report func(...psql.PreloadOption) psql.Preloader
Site func(...psql.PreloadOption) psql.Preloader
} }
func buildSignalPreloader() signalPreloader { func buildSignalPreloader() signalPreloader {
@ -1002,6 +1199,19 @@ func buildSignalPreloader() signalPreloader {
}, },
}, Users.Columns.Names(), opts...) }, Users.Columns.Names(), opts...)
}, },
FeaturePoolFeatureFeaturePool: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*FeaturePool, FeaturePoolSlice](psql.PreloadRel{
Name: "FeaturePoolFeatureFeaturePool",
Sides: []psql.PreloadSide{
{
From: Signals,
To: FeaturePools,
FromColumns: []string{"feature_pool_feature_id"},
ToColumns: []string{"feature_id"},
},
},
}, FeaturePools.Columns.Names(), opts...)
},
Organization: func(opts ...psql.PreloadOption) psql.Preloader { Organization: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*Organization, OrganizationSlice](psql.PreloadRel{ return psql.Preload[*Organization, OrganizationSlice](psql.PreloadRel{
Name: "Organization", Name: "Organization",
@ -1015,6 +1225,19 @@ func buildSignalPreloader() signalPreloader {
}, },
}, Organizations.Columns.Names(), opts...) }, Organizations.Columns.Names(), opts...)
}, },
Report: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*PublicreportReport, PublicreportReportSlice](psql.PreloadRel{
Name: "Report",
Sides: []psql.PreloadSide{
{
From: Signals,
To: PublicreportReports,
FromColumns: []string{"report_id"},
ToColumns: []string{"id"},
},
},
}, PublicreportReports.Columns.Names(), opts...)
},
Site: func(opts ...psql.PreloadOption) psql.Preloader { Site: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*Site, SiteSlice](psql.PreloadRel{ return psql.Preload[*Site, SiteSlice](psql.PreloadRel{
Name: "Site", Name: "Site",
@ -1032,10 +1255,12 @@ func buildSignalPreloader() signalPreloader {
} }
type signalThenLoader[Q orm.Loadable] struct { type signalThenLoader[Q orm.Loadable] struct {
AddressorUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] AddressorUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
CreatorUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] CreatorUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Organization func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] FeaturePoolFeatureFeaturePool func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Site func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] Organization func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Report func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Site func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
} }
func buildSignalThenLoader[Q orm.Loadable]() signalThenLoader[Q] { func buildSignalThenLoader[Q orm.Loadable]() signalThenLoader[Q] {
@ -1045,9 +1270,15 @@ func buildSignalThenLoader[Q orm.Loadable]() signalThenLoader[Q] {
type CreatorUserLoadInterface interface { type CreatorUserLoadInterface interface {
LoadCreatorUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error LoadCreatorUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
} }
type FeaturePoolFeatureFeaturePoolLoadInterface interface {
LoadFeaturePoolFeatureFeaturePool(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type OrganizationLoadInterface interface { type OrganizationLoadInterface interface {
LoadOrganization(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error LoadOrganization(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
} }
type ReportLoadInterface interface {
LoadReport(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type SiteLoadInterface interface { type SiteLoadInterface interface {
LoadSite(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error LoadSite(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
} }
@ -1065,12 +1296,24 @@ func buildSignalThenLoader[Q orm.Loadable]() signalThenLoader[Q] {
return retrieved.LoadCreatorUser(ctx, exec, mods...) return retrieved.LoadCreatorUser(ctx, exec, mods...)
}, },
), ),
FeaturePoolFeatureFeaturePool: thenLoadBuilder[Q](
"FeaturePoolFeatureFeaturePool",
func(ctx context.Context, exec bob.Executor, retrieved FeaturePoolFeatureFeaturePoolLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadFeaturePoolFeatureFeaturePool(ctx, exec, mods...)
},
),
Organization: thenLoadBuilder[Q]( Organization: thenLoadBuilder[Q](
"Organization", "Organization",
func(ctx context.Context, exec bob.Executor, retrieved OrganizationLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { func(ctx context.Context, exec bob.Executor, retrieved OrganizationLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadOrganization(ctx, exec, mods...) return retrieved.LoadOrganization(ctx, exec, mods...)
}, },
), ),
Report: thenLoadBuilder[Q](
"Report",
func(ctx context.Context, exec bob.Executor, retrieved ReportLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadReport(ctx, exec, mods...)
},
),
Site: thenLoadBuilder[Q]( Site: thenLoadBuilder[Q](
"Site", "Site",
func(ctx context.Context, exec bob.Executor, retrieved SiteLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { func(ctx context.Context, exec bob.Executor, retrieved SiteLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
@ -1187,6 +1430,61 @@ func (os SignalSlice) LoadCreatorUser(ctx context.Context, exec bob.Executor, mo
return nil return nil
} }
// LoadFeaturePoolFeatureFeaturePool loads the signal's FeaturePoolFeatureFeaturePool into the .R struct
func (o *Signal) LoadFeaturePoolFeatureFeaturePool(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.FeaturePoolFeatureFeaturePool = nil
related, err := o.FeaturePoolFeatureFeaturePool(mods...).One(ctx, exec)
if err != nil {
return err
}
related.R.FeaturePoolFeatureSignals = SignalSlice{o}
o.R.FeaturePoolFeatureFeaturePool = related
return nil
}
// LoadFeaturePoolFeatureFeaturePool loads the signal's FeaturePoolFeatureFeaturePool into the .R struct
func (os SignalSlice) LoadFeaturePoolFeatureFeaturePool(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
featurePools, err := os.FeaturePoolFeatureFeaturePool(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range featurePools {
if !o.FeaturePoolFeatureID.IsValue() {
continue
}
if !(o.FeaturePoolFeatureID.IsValue() && o.FeaturePoolFeatureID.MustGet() == rel.FeatureID) {
continue
}
rel.R.FeaturePoolFeatureSignals = append(rel.R.FeaturePoolFeatureSignals, o)
o.R.FeaturePoolFeatureFeaturePool = rel
break
}
}
return nil
}
// LoadOrganization loads the signal's Organization into the .R struct // LoadOrganization loads the signal's Organization into the .R struct
func (o *Signal) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { func (o *Signal) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil { if o == nil {
@ -1239,6 +1537,61 @@ func (os SignalSlice) LoadOrganization(ctx context.Context, exec bob.Executor, m
return nil return nil
} }
// LoadReport loads the signal's Report into the .R struct
func (o *Signal) LoadReport(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.Report = nil
related, err := o.Report(mods...).One(ctx, exec)
if err != nil {
return err
}
related.R.Signals = SignalSlice{o}
o.R.Report = related
return nil
}
// LoadReport loads the signal's Report into the .R struct
func (os SignalSlice) LoadReport(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
publicreportReports, err := os.Report(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range publicreportReports {
if !o.ReportID.IsValue() {
continue
}
if !(o.ReportID.IsValue() && o.ReportID.MustGet() == rel.ID) {
continue
}
rel.R.Signals = append(rel.R.Signals, o)
o.R.Report = rel
break
}
}
return nil
}
// LoadSite loads the signal's Site into the .R struct // LoadSite loads the signal's Site into the .R struct
func (o *Signal) LoadSite(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { func (o *Signal) LoadSite(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil { if o == nil {

View file

@ -6,12 +6,21 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/Gleipnir-Technology/bob"
"github.com/Gleipnir-Technology/bob/dialect/psql"
"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
"github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/enums" "github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/platform/types" "github.com/Gleipnir-Technology/nidus-sync/platform/types"
//"github.com/rs/zerolog/log"
"github.com/stephenafamo/scan"
) )
type Pool struct {
Condition string `db:"condition" json:"condition"`
ID int32 `db:"id" json:"-"`
}
type UploadPoolDetail struct { type UploadPoolDetail struct {
CountExisting int CountExisting int
CountNew int CountNew int
@ -149,3 +158,19 @@ func errorsByLine(ctx context.Context, file *models.FileuploadFile) ([]UploadPoo
} }
return file_errors, errors_by_line, nil return file_errors, errors_by_line, nil
} }
func poolList(ctx context.Context, org_id int32, pool_ids []int32) ([]*Pool, error) {
pools, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
sm.Columns(
"condition",
"feature_id AS id",
),
sm.From(psql.Quote("feature_pool")),
sm.Where(
models.FeaturePools.Columns.FeatureID.EQ(psql.Any(pool_ids)),
),
), scan.StructMapper[*Pool]())
if err != nil {
return nil, fmt.Errorf("query feature_pool: %w", err)
}
return pools, nil
}

View file

@ -3,29 +3,20 @@ package publicreport
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"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/sm" "github.com/Gleipnir-Technology/bob/dialect/psql/sm"
"github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/stephenafamo/scan" "github.com/stephenafamo/scan"
) )
type LogEntry struct { func logEntriesByReportID(ctx context.Context, report_ids []int32) (map[int32][]types.LogEntry, error) {
Created time.Time `db:"created" json:"created"` results := make(map[int32][]types.LogEntry, len(report_ids))
ID int32 `db:"id" json:"-"`
Message string `db:"message" json:"message"`
ReportID int32 `db:"report_id" json:"-"`
Type string `db:"type_" json:"type"`
UserID *int32 `db:"user_id" json:"user_id"`
}
func logEntriesByReportID(ctx context.Context, report_ids []int32) (map[int32][]LogEntry, error) {
results := make(map[int32][]LogEntry, len(report_ids))
for _, report_id := range report_ids { for _, report_id := range report_ids {
results[report_id] = make([]LogEntry, 0) results[report_id] = make([]types.LogEntry, 0)
} }
rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select( rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
@ -48,7 +39,7 @@ func logEntriesByReportID(ctx context.Context, report_ids []int32) (map[int32][]
), ),
sm.Where(psql.Quote("l", "report_id").EQ(psql.Any(report_ids))), sm.Where(psql.Quote("l", "report_id").EQ(psql.Any(report_ids))),
sm.OrderBy(psql.Quote("l", "created")), sm.OrderBy(psql.Quote("l", "created")),
), scan.StructMapper[LogEntry]()) ), scan.StructMapper[types.LogEntry]())
if err != nil { if err != nil {
return results, fmt.Errorf("query created: %w", err) return results, fmt.Errorf("query created: %w", err)
} }

View file

@ -9,31 +9,13 @@ import (
"github.com/Gleipnir-Technology/bob/dialect/psql/sm" "github.com/Gleipnir-Technology/bob/dialect/psql/sm"
//"github.com/Gleipnir-Technology/nidus-sync/config" //"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db"
"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"
"github.com/stephenafamo/scan" "github.com/stephenafamo/scan"
) )
type Nuisance struct { func nuisancesByReportID(ctx context.Context, report_ids []int32) (map[int32]*types.Nuisance, error) {
AdditionalInfo string `db:"additional_info" json:"additional_info"`
Duration string `db:"duration" json:"duration"`
IsLocationBackyard bool `db:"is_location_backyard" json:"is_location_backyard"`
IsLocationFrontyard bool `db:"is_location_frontyard" json:"is_location_frontyard"`
IsLocationGarden bool `db:"is_location_garden" json:"is_location_garden"`
IsLocationOther bool `db:"is_location_other" json:"is_location_other"`
IsLocationPool bool `db:"is_location_pool" json:"is_location_pool"`
ReportID int32 `db:"report_id" json:"-"`
SourceContainer bool `db:"source_container" json:"source_container"`
SourceDescription string `db:"source_description" json:"source_description"`
SourceGutter bool `db:"source_gutter" json:"source_gutter"`
SourceStagnant bool `db:"source_stagnant" json:"source_stagnant"`
TODDay bool `db:"tod_day" json:"time_of_day_day"`
TODEarly bool `db:"tod_early" json:"time_of_day_early"`
TODEvening bool `db:"tod_evening" json:"time_of_day_evening"`
TODNight bool `db:"tod_night" json:"time_of_day_night"`
}
func nuisancesByReportID(ctx context.Context, report_ids []int32) (map[int32]*Nuisance, error) {
rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select( rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
sm.Columns( sm.Columns(
"additional_info", "additional_info",
@ -57,13 +39,13 @@ func nuisancesByReportID(ctx context.Context, report_ids []int32) (map[int32]*Nu
sm.Where(psql.Quote("report_id").EQ( sm.Where(psql.Quote("report_id").EQ(
psql.Any(report_ids), psql.Any(report_ids),
)), )),
), scan.StructMapper[Nuisance]()) ), scan.StructMapper[types.Nuisance]())
if err != nil { if err != nil {
return nil, fmt.Errorf("query nuisance: %w", err) return nil, fmt.Errorf("query nuisance: %w", err)
} }
results := make(map[int32]*Nuisance, len(rows)) results := make(map[int32]*types.Nuisance, len(rows))
for _, row := range rows { for _, row := range rows {
results[row.ReportID] = &Nuisance{ results[row.ReportID] = &types.Nuisance{
AdditionalInfo: row.AdditionalInfo, AdditionalInfo: row.AdditionalInfo,
Duration: row.Duration, Duration: row.Duration,
IsLocationBackyard: row.IsLocationBackyard, IsLocationBackyard: row.IsLocationBackyard,

View file

@ -3,10 +3,10 @@ package publicreport
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"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/config" //"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db"
@ -17,47 +17,15 @@ import (
"github.com/stephenafamo/scan" "github.com/stephenafamo/scan"
) )
type Report struct { func ReportsForOrganization(ctx context.Context, org_id int32) ([]*types.Report, error) {
Log []LogEntry `db:"-" json:"log"` query := reportQuery(org_id)
Address types.Address `db:"address" json:"address"` query.Apply(
AddressRaw string `db:"address_raw" json:"address_raw"`
Created time.Time `db:"created" json:"created"`
ID int32 `db:"id" json:"-"`
Images []types.Image `db:"images" json:"images"`
Location *types.Location `db:"location" json:"location"`
Nuisance *Nuisance `db:"nuisance" json:"nuisance"`
PublicID string `db:"public_id" json:"public_id"`
Reporter types.Contact `db:"reporter" json:"reporter"`
Status string `db:"status" json:"status"`
Type string `db:"report_type" json:"type"`
Water *Water `db:"water" json:"water"`
}
func ReportsForOrganization(ctx context.Context, org_id int32) ([]*Report, error) {
rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
sm.Columns(
"address_country AS \"address.country\"",
"address_locality AS \"address.locality\"",
"address_number AS \"address.number\"",
"address_postal_code AS \"address.postal_code\"",
"address_raw AS address_raw",
"address_region AS \"address.region\"",
"address_street AS \"address.street\"",
"created",
"id",
"COALESCE(ST_Y(location::geometry::geometry(point, 4326)), 0.0) AS \"location.latitude\"",
"COALESCE(ST_X(location::geometry::geometry(point, 4326)), 0.0) AS \"location.longitude\"",
"public_id",
"report_type",
"reporter_email AS \"reporter.email\"",
"reporter_name AS \"reporter.name\"",
"reporter_phone AS \"reporter.phone\"",
"status",
),
sm.From("publicreport.report"),
sm.Where(psql.Quote("publicreport", "report", "organization_id").EQ(psql.Arg(org_id))),
sm.Where(psql.Quote("publicreport", "report", "reviewed").IsNull()), sm.Where(psql.Quote("publicreport", "report", "reviewed").IsNull()),
), scan.StructMapper[Report]()) )
return reportQueryToRows(ctx, org_id, query)
}
func reportQueryToRows(ctx context.Context, org_id int32, query bob.BaseQuery[*dialect.SelectQuery]) ([]*types.Report, error) {
rows, err := bob.All(ctx, db.PGInstance.BobDB, query, scan.StructMapper[types.Report]())
if err != nil { if err != nil {
return nil, fmt.Errorf("get reports: %w", err) return nil, fmt.Errorf("get reports: %w", err)
@ -83,7 +51,7 @@ func ReportsForOrganization(ctx context.Context, org_id int32) ([]*Report, error
return nil, fmt.Errorf("waters: %w", err) return nil, fmt.Errorf("waters: %w", err)
} }
results := make([]*Report, len(rows)) results := make([]*types.Report, len(rows))
for i, row := range rows { for i, row := range rows {
images, ok := images_by_id[row.ID] images, ok := images_by_id[row.ID]
if ok { if ok {
@ -101,6 +69,13 @@ func ReportsForOrganization(ctx context.Context, org_id int32) ([]*Report, error
} }
return results, nil return results, nil
} }
func Reports(ctx context.Context, org_id int32, ids []int32) ([]*types.Report, error) {
query := reportQuery(org_id)
query.Apply(
sm.Where(psql.Quote("publicreport", "report", "reviewed").IsNull()),
)
return reportQueryToRows(ctx, org_id, query)
}
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 {
Count uint `db:"count"` Count uint `db:"count"`
@ -117,3 +92,28 @@ func ReportsForOrganizationCount(ctx context.Context, org_id int32) (uint, error
} }
return row.Count, nil return row.Count, nil
} }
func reportQuery(org_id int32) bob.BaseQuery[*dialect.SelectQuery] {
return psql.Select(
sm.Columns(
"address_country AS \"address.country\"",
"address_locality AS \"address.locality\"",
"address_number AS \"address.number\"",
"address_postal_code AS \"address.postal_code\"",
"address_raw AS address_raw",
"address_region AS \"address.region\"",
"address_street AS \"address.street\"",
"created",
"id",
"COALESCE(ST_Y(location::geometry::geometry(point, 4326)), 0.0) AS \"location.latitude\"",
"COALESCE(ST_X(location::geometry::geometry(point, 4326)), 0.0) AS \"location.longitude\"",
"public_id",
"report_type",
"reporter_email AS \"reporter.email\"",
"reporter_name AS \"reporter.name\"",
"reporter_phone AS \"reporter.phone\"",
"status",
),
sm.From("publicreport.report"),
sm.Where(psql.Quote("publicreport", "report", "organization_id").EQ(psql.Arg(org_id))),
)
}

View file

@ -16,25 +16,7 @@ import (
"github.com/stephenafamo/scan" "github.com/stephenafamo/scan"
) )
type Water struct { func watersByReportID(ctx context.Context, report_ids []int32) (map[int32]*types.Water, error) {
AccessComments string `db:"access_comments" json:"access_comments"`
AccessGate bool `db:"access_gate" json:"access_gate"`
AccessFence bool `db:"access_fence" json:"access_fence"`
AccessLocked bool `db:"access_locked" json:"access_locked"`
AccessDog bool `db:"access_dog" json:"access_dog"`
AccessOther bool `db:"access_other" json:"access_other"`
Comments string `db:"comments" json:"comments"`
HasAdult bool `db:"has_adult" json:"has_adult"`
HasBackyardPermission bool `db:"has_backyard_permission" json:"has_backyard_permission"`
HasLarvae bool `db:"has_larvae" json:"has_larvae"`
HasPupae bool `db:"has_pupae" json:"has_pupae"`
IsReporterConfidential bool `db:"is_reporter_confidential" json:"is_reporter_confidential"`
IsReporterOwner bool `db:"is_reporter_owner" json:"is_reporter_owner"`
Owner types.Contact `db:"owner" json:"owner"`
ReportID int32 `db:"report_id" json:"-"`
}
func watersByReportID(ctx context.Context, report_ids []int32) (map[int32]*Water, error) {
rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select( rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
sm.Columns( sm.Columns(
"access_comments", "access_comments",
@ -59,13 +41,13 @@ func watersByReportID(ctx context.Context, report_ids []int32) (map[int32]*Water
sm.Where(psql.Quote("report_id").EQ( sm.Where(psql.Quote("report_id").EQ(
psql.Any(report_ids), psql.Any(report_ids),
)), )),
), scan.StructMapper[Water]()) ), scan.StructMapper[types.Water]())
if err != nil { if err != nil {
return nil, fmt.Errorf("query water: %w", err) return nil, fmt.Errorf("query water: %w", err)
} }
results := make(map[int32]*Water, len(rows)) results := make(map[int32]*types.Water, len(rows))
for _, row := range rows { for _, row := range rows {
results[row.ReportID] = &Water{ results[row.ReportID] = &types.Water{
AccessComments: row.AccessComments, AccessComments: row.AccessComments,
AccessGate: row.AccessGate, AccessGate: row.AccessGate,
AccessFence: row.AccessFence, AccessFence: row.AccessFence,

View file

@ -141,14 +141,15 @@ func commitReviewPool(ctx context.Context, txn bob.Tx, user User, review_task_po
return nhttp.NewError("find feature %d: %w", feature_pool.FeatureID, err) return nhttp.NewError("find feature %d: %w", feature_pool.FeatureID, err)
} }
signal, err := models.Signals.Insert(&models.SignalSetter{ signal, err := models.Signals.Insert(&models.SignalSetter{
Addressed: omitnull.FromPtr[time.Time](nil), Addressed: omitnull.FromPtr[time.Time](nil),
Addressor: omitnull.FromPtr[int32](nil), Addressor: omitnull.FromPtr[int32](nil),
Created: omit.From(time.Now()), Created: omit.From(time.Now()),
Creator: omit.From[int32](int32(user.ID)), Creator: omit.From[int32](int32(user.ID)),
FeaturePoolFeatureID: omitnull.From(feature_pool.FeatureID),
//ID: omit.Val[int32], //ID: omit.Val[int32],
OrganizationID: omit.From(user.Organization.ID()), OrganizationID: omit.From(user.Organization.ID()),
ReportID: omitnull.FromPtr[int32](nil),
Species: omitnull.FromPtr[enums.Mosquitospecies](nil), Species: omitnull.FromPtr[enums.Mosquitospecies](nil),
Title: omit.From[string](""),
Type: omit.From(enums.SignaltypeFlyoverPool), Type: omit.From(enums.SignaltypeFlyoverPool),
SiteID: omitnull.From(feature.SiteID), SiteID: omitnull.From(feature.SiteID),
Location: omit.From[string](feature.Location.GetOr("")), Location: omit.From[string](feature.Location.GetOr("")),

View file

@ -15,6 +15,7 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/db/enums" "github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/platform/event" "github.com/Gleipnir-Technology/nidus-sync/platform/event"
"github.com/Gleipnir-Technology/nidus-sync/platform/publicreport"
"github.com/Gleipnir-Technology/nidus-sync/platform/types" "github.com/Gleipnir-Technology/nidus-sync/platform/types"
//"github.com/Gleipnir-Technology/nidus-sync/platform/geocode" //"github.com/Gleipnir-Technology/nidus-sync/platform/geocode"
//"github.com/Gleipnir-Technology/nidus-sync/platform/geom" //"github.com/Gleipnir-Technology/nidus-sync/platform/geom"
@ -32,8 +33,9 @@ type Signal struct {
Creator int32 `db:"creator" json:"creator"` Creator int32 `db:"creator" json:"creator"`
ID int32 `db:"id" json:"id"` ID int32 `db:"id" json:"id"`
Location types.Location `db:"location" json:"location"` Location types.Location `db:"location" json:"location"`
Pool *Pool `db:"pool" json:"pool"`
Report *types.Report `db:"report" json:"report"`
Species *string `db:"species" json:"species"` Species *string `db:"species" json:"species"`
Title string `db:"title" json:"title"`
Type string `db:"type" json:"type"` Type string `db:"type" json:"type"`
} }
@ -115,16 +117,17 @@ func SignalCreateFromPublicreport(ctx context.Context, user User, report_id stri
} }
log.Debug().Str("location", location).Msg("inserting signal") log.Debug().Str("location", location).Msg("inserting signal")
signal, err := models.Signals.Insert(&models.SignalSetter{ signal, err := models.Signals.Insert(&models.SignalSetter{
Addressed: omitnull.FromPtr[time.Time](nil), Addressed: omitnull.FromPtr[time.Time](nil),
Addressor: omitnull.FromPtr[int32](nil), Addressor: omitnull.FromPtr[int32](nil),
Created: omit.From(time.Now()), Created: omit.From(time.Now()),
Creator: omit.From(int32(user.ID)), Creator: omit.From(int32(user.ID)),
FeaturePoolFeatureID: omitnull.FromPtr[int32](nil),
// ID // ID
OrganizationID: omit.From(int32(user.Organization.ID())), OrganizationID: omit.From(int32(user.Organization.ID())),
Location: omit.From(location), Location: omit.From(location),
ReportID: omitnull.From(report.ID),
Species: omitnull.FromPtr[enums.Mosquitospecies](nil), Species: omitnull.FromPtr[enums.Mosquitospecies](nil),
SiteID: omitnull.From(site_id), SiteID: omitnull.From(site_id),
Title: omit.From[string](""),
Type: omit.From[enums.Signaltype](signal_type), Type: omit.From[enums.Signaltype](signal_type),
}).One(ctx, txn) }).One(ctx, txn)
if err != nil { if err != nil {
@ -146,7 +149,8 @@ func SignalCreateFromPublicreport(ctx context.Context, user User, report_id stri
return &signal.ID, nil return &signal.ID, nil
} }
func SignalList(ctx context.Context, user User, limit int) ([]Signal, error) { func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) {
org_id := user.Organization.ID()
rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select( rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
sm.Columns( sm.Columns(
"signal.addressed AS addressed", "signal.addressed AS addressed",
@ -154,8 +158,9 @@ func SignalList(ctx context.Context, user User, limit int) ([]Signal, error) {
"signal.created AS created", "signal.created AS created",
"signal.creator AS creator", "signal.creator AS creator",
"signal.id AS id", "signal.id AS id",
"COALESCE(signal.feature_pool_feature_id, 0) AS \"pool.id\"",
"COALESCE(signal.report_id, 0) AS \"report.id\"",
"signal.species AS species", "signal.species AS species",
"signal.title AS title",
"signal.type_ AS type", "signal.type_ AS type",
"address.country AS \"address.country\"", "address.country AS \"address.country\"",
"address.locality AS \"address.locality\"", "address.locality AS \"address.locality\"",
@ -164,8 +169,9 @@ func SignalList(ctx context.Context, user User, limit int) ([]Signal, error) {
"address.region AS \"address.region\"", "address.region AS \"address.region\"",
"address.street AS \"address.street\"", "address.street AS \"address.street\"",
"address.unit AS \"address.unit\"", "address.unit AS \"address.unit\"",
"ST_Y(address.location) AS \"location.latitude\"", // This will work great, up until we add polygons to signal
"ST_X(address.location) AS \"location.longitude\"", "ST_Y(signal.location) AS \"location.latitude\"",
"ST_X(signal.location) AS \"location.longitude\"",
), ),
sm.From("signal"), sm.From("signal"),
sm.LeftJoin("site").OnEQ( sm.LeftJoin("site").OnEQ(
@ -176,13 +182,55 @@ func SignalList(ctx context.Context, user User, limit int) ([]Signal, error) {
psql.Quote("site", "address_id"), psql.Quote("site", "address_id"),
psql.Quote("address", "id"), psql.Quote("address", "id"),
), ),
sm.Where(psql.Quote("signal", "organization_id").EQ(psql.Arg(user.Organization.ID()))), sm.Where(psql.Quote("signal", "organization_id").EQ(psql.Arg(org_id))),
sm.Where(psql.Quote("signal", "addressed").IsNull()), sm.Where(psql.Quote("signal", "addressed").IsNull()),
sm.Limit(limit), sm.Limit(limit),
), scan.StructMapper[Signal]()) ), scan.StructMapper[*Signal]())
log.Debug().Int("len", len(rows)).Msg("got signals") log.Debug().Int("len", len(rows)).Msg("got signals")
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)
pool_ids := make([]int32, 0)
for _, row := range rows {
if row.Report.ID != 0 {
report_ids = append(report_ids, row.Report.ID)
} else if row.Pool.ID != 0 {
pool_ids = append(pool_ids, row.Pool.ID)
}
}
pools, err := poolList(ctx, org_id, pool_ids)
if err != nil {
return nil, fmt.Errorf("getting pools by ID: %w", err)
}
reports, err := publicreport.Reports(ctx, org_id, report_ids)
if err != nil {
return nil, fmt.Errorf("getting reports by ID: %w", err)
}
pool_map := make(map[int32]*Pool, len(pools))
for _, pool := range pools {
pool_map[pool.ID] = pool
log.Debug().Int32("pool", pool.ID).Msg("Added to map")
}
report_map := make(map[int32]*types.Report, len(report_ids))
for _, report := range reports {
report_map[report.ID] = report
}
for _, row := range rows {
if row.Pool.ID != 0 {
p, ok := pool_map[row.Pool.ID]
if !ok {
return nil, fmt.Errorf("failed to get pool %d for %d", row.Pool.ID, row.ID)
}
if p == nil {
return nil, fmt.Errorf("got nil pool from %d for %d", row.Pool.ID, row.ID)
}
row.Pool = p
row.Report = nil
} else if row.Report.ID != 0 {
row.Pool = nil
row.Report = report_map[row.Report.ID]
}
}
return rows, nil return rows, nil
} }

View file

@ -0,0 +1,14 @@
package types
import (
"time"
)
type LogEntry struct {
Created time.Time `db:"created" json:"created"`
ID int32 `db:"id" json:"-"`
Message string `db:"message" json:"message"`
ReportID int32 `db:"report_id" json:"-"`
Type string `db:"type_" json:"type"`
UserID *int32 `db:"user_id" json:"user_id"`
}

View file

@ -0,0 +1,20 @@
package types
type Nuisance struct {
AdditionalInfo string `db:"additional_info" json:"additional_info"`
Duration string `db:"duration" json:"duration"`
IsLocationBackyard bool `db:"is_location_backyard" json:"is_location_backyard"`
IsLocationFrontyard bool `db:"is_location_frontyard" json:"is_location_frontyard"`
IsLocationGarden bool `db:"is_location_garden" json:"is_location_garden"`
IsLocationOther bool `db:"is_location_other" json:"is_location_other"`
IsLocationPool bool `db:"is_location_pool" json:"is_location_pool"`
ReportID int32 `db:"report_id" json:"-"`
SourceContainer bool `db:"source_container" json:"source_container"`
SourceDescription string `db:"source_description" json:"source_description"`
SourceGutter bool `db:"source_gutter" json:"source_gutter"`
SourceStagnant bool `db:"source_stagnant" json:"source_stagnant"`
TODDay bool `db:"tod_day" json:"time_of_day_day"`
TODEarly bool `db:"tod_early" json:"time_of_day_early"`
TODEvening bool `db:"tod_evening" json:"time_of_day_evening"`
TODNight bool `db:"tod_night" json:"time_of_day_night"`
}

21
platform/types/report.go Normal file
View file

@ -0,0 +1,21 @@
package types
import (
"time"
)
type Report struct {
Log []LogEntry `db:"-" json:"log"`
Address Address `db:"address" json:"address"`
AddressRaw string `db:"address_raw" json:"address_raw"`
Created time.Time `db:"created" json:"created"`
ID int32 `db:"id" json:"-"`
Images []Image `db:"images" json:"images"`
Location *Location `db:"location" json:"location"`
Nuisance *Nuisance `db:"nuisance" json:"nuisance"`
PublicID string `db:"public_id" json:"public_id"`
Reporter Contact `db:"reporter" json:"reporter"`
Status string `db:"status" json:"status"`
Type string `db:"report_type" json:"type"`
Water *Water `db:"water" json:"water"`
}

19
platform/types/water.go Normal file
View file

@ -0,0 +1,19 @@
package types
type Water struct {
AccessComments string `db:"access_comments" json:"access_comments"`
AccessGate bool `db:"access_gate" json:"access_gate"`
AccessFence bool `db:"access_fence" json:"access_fence"`
AccessLocked bool `db:"access_locked" json:"access_locked"`
AccessDog bool `db:"access_dog" json:"access_dog"`
AccessOther bool `db:"access_other" json:"access_other"`
Comments string `db:"comments" json:"comments"`
HasAdult bool `db:"has_adult" json:"has_adult"`
HasBackyardPermission bool `db:"has_backyard_permission" json:"has_backyard_permission"`
HasLarvae bool `db:"has_larvae" json:"has_larvae"`
HasPupae bool `db:"has_pupae" json:"has_pupae"`
IsReporterConfidential bool `db:"is_reporter_confidential" json:"is_reporter_confidential"`
IsReporterOwner bool `db:"is_reporter_owner" json:"is_reporter_owner"`
Owner Contact `db:"owner" json:"owner"`
ReportID int32 `db:"report_id" json:"-"`
}