Save address IDs when doing pool geocoding
This commit is contained in:
parent
6a8ae6d81a
commit
ac27c60e0c
8 changed files with 447 additions and 52 deletions
|
|
@ -222,6 +222,15 @@ var FileuploadPools = Table[
|
|||
Generated: false,
|
||||
AutoIncr: false,
|
||||
},
|
||||
AddressID: column{
|
||||
Name: "address_id",
|
||||
DBType: "integer",
|
||||
Default: "NULL",
|
||||
Comment: "",
|
||||
Nullable: true,
|
||||
Generated: false,
|
||||
AutoIncr: false,
|
||||
},
|
||||
},
|
||||
Indexes: fileuploadPoolIndexes{
|
||||
PoolPkey: index{
|
||||
|
|
@ -248,6 +257,15 @@ var FileuploadPools = Table[
|
|||
Comment: "",
|
||||
},
|
||||
ForeignKeys: fileuploadPoolForeignKeys{
|
||||
FileuploadPoolPoolAddressIDFkey: foreignKey{
|
||||
constraint: constraint{
|
||||
Name: "fileupload.pool.pool_address_id_fkey",
|
||||
Columns: []string{"address_id"},
|
||||
Comment: "",
|
||||
},
|
||||
ForeignTable: "address",
|
||||
ForeignColumns: []string{"id"},
|
||||
},
|
||||
FileuploadPoolPoolCreatorIDFkey: foreignKey{
|
||||
constraint: constraint{
|
||||
Name: "fileupload.pool.pool_creator_id_fkey",
|
||||
|
|
@ -313,11 +331,12 @@ type fileuploadPoolColumns struct {
|
|||
AddressLocality column
|
||||
AddressRegion column
|
||||
Condition column
|
||||
AddressID column
|
||||
}
|
||||
|
||||
func (c fileuploadPoolColumns) AsSlice() []column {
|
||||
return []column{
|
||||
c.AddressPostalCode, c.AddressStreet, c.Committed, c.Created, c.CreatorID, c.CSVFile, c.Deleted, c.Geom, c.H3cell, c.ID, c.IsInDistrict, c.IsNew, c.Notes, c.PropertyOwnerName, c.PropertyOwnerPhoneE164, c.ResidentOwned, c.ResidentPhoneE164, c.LineNumber, c.Tags, c.AddressNumber, c.AddressLocality, c.AddressRegion, c.Condition,
|
||||
c.AddressPostalCode, c.AddressStreet, c.Committed, c.Created, c.CreatorID, c.CSVFile, c.Deleted, c.Geom, c.H3cell, c.ID, c.IsInDistrict, c.IsNew, c.Notes, c.PropertyOwnerName, c.PropertyOwnerPhoneE164, c.ResidentOwned, c.ResidentPhoneE164, c.LineNumber, c.Tags, c.AddressNumber, c.AddressLocality, c.AddressRegion, c.Condition, c.AddressID,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -332,6 +351,7 @@ func (i fileuploadPoolIndexes) AsSlice() []index {
|
|||
}
|
||||
|
||||
type fileuploadPoolForeignKeys struct {
|
||||
FileuploadPoolPoolAddressIDFkey foreignKey
|
||||
FileuploadPoolPoolCreatorIDFkey foreignKey
|
||||
FileuploadPoolPoolCSVFileFkey foreignKey
|
||||
FileuploadPoolPoolPropertyOwnerPhoneE164Fkey foreignKey
|
||||
|
|
@ -340,7 +360,7 @@ type fileuploadPoolForeignKeys struct {
|
|||
|
||||
func (f fileuploadPoolForeignKeys) AsSlice() []foreignKey {
|
||||
return []foreignKey{
|
||||
f.FileuploadPoolPoolCreatorIDFkey, f.FileuploadPoolPoolCSVFileFkey, f.FileuploadPoolPoolPropertyOwnerPhoneE164Fkey, f.FileuploadPoolPoolResidentPhoneE164Fkey,
|
||||
f.FileuploadPoolPoolAddressIDFkey, f.FileuploadPoolPoolCreatorIDFkey, f.FileuploadPoolPoolCSVFileFkey, f.FileuploadPoolPoolPropertyOwnerPhoneE164Fkey, f.FileuploadPoolPoolResidentPhoneE164Fkey,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
4
db/migrations/00136_fileupload_pool_address.sql
Normal file
4
db/migrations/00136_fileupload_pool_address.sql
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
-- +goose Up
|
||||
ALTER TABLE fileupload.pool ADD COLUMN address_id INTEGER REFERENCES address(id);
|
||||
-- +goose Down
|
||||
ALTER TABLE fileupload.pool DROP COLUMN address_id;
|
||||
|
|
@ -56,6 +56,7 @@ type AddressesQuery = *psql.ViewQuery[*Address, AddressSlice]
|
|||
// addressR is where relationships are stored.
|
||||
type addressR struct {
|
||||
Mailers CommsMailerSlice // comms.mailer.mailer_address_id_fkey
|
||||
Pools FileuploadPoolSlice // fileupload.pool.pool_address_id_fkey
|
||||
NuisanceOlds PublicreportNuisanceOldSlice // publicreport.nuisance_old.nuisance_address_id_fkey
|
||||
Reports PublicreportReportSlice // publicreport.report.report_address_id_fkey
|
||||
WaterOlds PublicreportWaterOldSlice // publicreport.water_old.pool_address_id_fkey
|
||||
|
|
@ -635,6 +636,30 @@ func (os AddressSlice) Mailers(mods ...bob.Mod[*dialect.SelectQuery]) CommsMaile
|
|||
)...)
|
||||
}
|
||||
|
||||
// Pools starts a query for related objects on fileupload.pool
|
||||
func (o *Address) Pools(mods ...bob.Mod[*dialect.SelectQuery]) FileuploadPoolsQuery {
|
||||
return FileuploadPools.Query(append(mods,
|
||||
sm.Where(FileuploadPools.Columns.AddressID.EQ(psql.Arg(o.ID))),
|
||||
)...)
|
||||
}
|
||||
|
||||
func (os AddressSlice) Pools(mods ...bob.Mod[*dialect.SelectQuery]) FileuploadPoolsQuery {
|
||||
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 FileuploadPools.Query(append(mods,
|
||||
sm.Where(psql.Group(FileuploadPools.Columns.AddressID).OP("IN", PKArgExpr)),
|
||||
)...)
|
||||
}
|
||||
|
||||
// NuisanceOlds starts a query for related objects on publicreport.nuisance_old
|
||||
func (o *Address) NuisanceOlds(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportNuisanceOldsQuery {
|
||||
return PublicreportNuisanceOlds.Query(append(mods,
|
||||
|
|
@ -823,6 +848,74 @@ func (address0 *Address) AttachMailers(ctx context.Context, exec bob.Executor, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func insertAddressPools0(ctx context.Context, exec bob.Executor, fileuploadPools1 []*FileuploadPoolSetter, address0 *Address) (FileuploadPoolSlice, error) {
|
||||
for i := range fileuploadPools1 {
|
||||
fileuploadPools1[i].AddressID = omitnull.From(address0.ID)
|
||||
}
|
||||
|
||||
ret, err := FileuploadPools.Insert(bob.ToMods(fileuploadPools1...)).All(ctx, exec)
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf("insertAddressPools0: %w", err)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func attachAddressPools0(ctx context.Context, exec bob.Executor, count int, fileuploadPools1 FileuploadPoolSlice, address0 *Address) (FileuploadPoolSlice, error) {
|
||||
setter := &FileuploadPoolSetter{
|
||||
AddressID: omitnull.From(address0.ID),
|
||||
}
|
||||
|
||||
err := fileuploadPools1.UpdateAll(ctx, exec, *setter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("attachAddressPools0: %w", err)
|
||||
}
|
||||
|
||||
return fileuploadPools1, nil
|
||||
}
|
||||
|
||||
func (address0 *Address) InsertPools(ctx context.Context, exec bob.Executor, related ...*FileuploadPoolSetter) error {
|
||||
if len(related) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
fileuploadPools1, err := insertAddressPools0(ctx, exec, related, address0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
address0.R.Pools = append(address0.R.Pools, fileuploadPools1...)
|
||||
|
||||
for _, rel := range fileuploadPools1 {
|
||||
rel.R.Address = address0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (address0 *Address) AttachPools(ctx context.Context, exec bob.Executor, related ...*FileuploadPool) error {
|
||||
if len(related) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
fileuploadPools1 := FileuploadPoolSlice(related)
|
||||
|
||||
_, err = attachAddressPools0(ctx, exec, len(related), fileuploadPools1, address0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
address0.R.Pools = append(address0.R.Pools, fileuploadPools1...)
|
||||
|
||||
for _, rel := range related {
|
||||
rel.R.Address = address0
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertAddressNuisanceOlds0(ctx context.Context, exec bob.Executor, publicreportNuisanceOlds1 []*PublicreportNuisanceOldSetter, address0 *Address) (PublicreportNuisanceOldSlice, error) {
|
||||
for i := range publicreportNuisanceOlds1 {
|
||||
publicreportNuisanceOlds1[i].AddressID = omitnull.From(address0.ID)
|
||||
|
|
@ -1203,6 +1296,20 @@ func (o *Address) Preload(name string, retrieved any) error {
|
|||
|
||||
o.R.Mailers = rels
|
||||
|
||||
for _, rel := range rels {
|
||||
if rel != nil {
|
||||
rel.R.Address = o
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case "Pools":
|
||||
rels, ok := retrieved.(FileuploadPoolSlice)
|
||||
if !ok {
|
||||
return fmt.Errorf("address cannot load %T as %q", retrieved, name)
|
||||
}
|
||||
|
||||
o.R.Pools = rels
|
||||
|
||||
for _, rel := range rels {
|
||||
if rel != nil {
|
||||
rel.R.Address = o
|
||||
|
|
@ -1306,6 +1413,7 @@ func buildAddressPreloader() addressPreloader {
|
|||
|
||||
type addressThenLoader[Q orm.Loadable] struct {
|
||||
Mailers func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
Pools func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
NuisanceOlds func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
Reports func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
WaterOlds func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
|
|
@ -1317,6 +1425,9 @@ func buildAddressThenLoader[Q orm.Loadable]() addressThenLoader[Q] {
|
|||
type MailersLoadInterface interface {
|
||||
LoadMailers(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
|
||||
}
|
||||
type PoolsLoadInterface interface {
|
||||
LoadPools(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
|
||||
}
|
||||
type NuisanceOldsLoadInterface interface {
|
||||
LoadNuisanceOlds(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
|
||||
}
|
||||
|
|
@ -1340,6 +1451,12 @@ func buildAddressThenLoader[Q orm.Loadable]() addressThenLoader[Q] {
|
|||
return retrieved.LoadMailers(ctx, exec, mods...)
|
||||
},
|
||||
),
|
||||
Pools: thenLoadBuilder[Q](
|
||||
"Pools",
|
||||
func(ctx context.Context, exec bob.Executor, retrieved PoolsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
return retrieved.LoadPools(ctx, exec, mods...)
|
||||
},
|
||||
),
|
||||
NuisanceOlds: thenLoadBuilder[Q](
|
||||
"NuisanceOlds",
|
||||
func(ctx context.Context, exec bob.Executor, retrieved NuisanceOldsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
|
|
@ -1434,6 +1551,70 @@ func (os AddressSlice) LoadMailers(ctx context.Context, exec bob.Executor, mods
|
|||
return nil
|
||||
}
|
||||
|
||||
// LoadPools loads the address's Pools into the .R struct
|
||||
func (o *Address) LoadPools(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset the relationship
|
||||
o.R.Pools = nil
|
||||
|
||||
related, err := o.Pools(mods...).All(ctx, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rel := range related {
|
||||
rel.R.Address = o
|
||||
}
|
||||
|
||||
o.R.Pools = related
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadPools loads the address's Pools into the .R struct
|
||||
func (os AddressSlice) LoadPools(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
if len(os) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fileuploadPools, err := os.Pools(mods...).All(ctx, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, o := range os {
|
||||
if o == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
o.R.Pools = nil
|
||||
}
|
||||
|
||||
for _, o := range os {
|
||||
if o == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rel := range fileuploadPools {
|
||||
|
||||
if !rel.AddressID.IsValue() {
|
||||
continue
|
||||
}
|
||||
if !(rel.AddressID.IsValue() && o.ID == rel.AddressID.MustGet()) {
|
||||
continue
|
||||
}
|
||||
|
||||
rel.R.Address = o
|
||||
|
||||
o.R.Pools = append(o.R.Pools, rel)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadNuisanceOlds loads the address's NuisanceOlds into the .R struct
|
||||
func (o *Address) LoadNuisanceOlds(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
if o == nil {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ type FileuploadPool struct {
|
|||
AddressLocality string `db:"address_locality" `
|
||||
AddressRegion string `db:"address_region" `
|
||||
Condition enums.Poolconditiontype `db:"condition" `
|
||||
AddressID null.Val[int32] `db:"address_id" `
|
||||
|
||||
R fileuploadPoolR `db:"-" `
|
||||
}
|
||||
|
|
@ -65,6 +66,7 @@ type FileuploadPoolsQuery = *psql.ViewQuery[*FileuploadPool, FileuploadPoolSlice
|
|||
|
||||
// fileuploadPoolR is where relationships are stored.
|
||||
type fileuploadPoolR struct {
|
||||
Address *Address // fileupload.pool.pool_address_id_fkey
|
||||
CreatorUser *User // fileupload.pool.pool_creator_id_fkey
|
||||
CSVFileCSV *FileuploadCSV // fileupload.pool.pool_csv_file_fkey
|
||||
PropertyOwnerPhoneE164Phone *CommsPhone // fileupload.pool.pool_property_owner_phone_e164_fkey
|
||||
|
|
@ -74,7 +76,7 @@ type fileuploadPoolR struct {
|
|||
func buildFileuploadPoolColumns(alias string) fileuploadPoolColumns {
|
||||
return fileuploadPoolColumns{
|
||||
ColumnsExpr: expr.NewColumnsExpr(
|
||||
"address_postal_code", "address_street", "committed", "created", "creator_id", "csv_file", "deleted", "geom", "h3cell", "id", "is_in_district", "is_new", "notes", "property_owner_name", "property_owner_phone_e164", "resident_owned", "resident_phone_e164", "line_number", "tags", "address_number", "address_locality", "address_region", "condition",
|
||||
"address_postal_code", "address_street", "committed", "created", "creator_id", "csv_file", "deleted", "geom", "h3cell", "id", "is_in_district", "is_new", "notes", "property_owner_name", "property_owner_phone_e164", "resident_owned", "resident_phone_e164", "line_number", "tags", "address_number", "address_locality", "address_region", "condition", "address_id",
|
||||
).WithParent("fileupload.pool"),
|
||||
tableAlias: alias,
|
||||
AddressPostalCode: psql.Quote(alias, "address_postal_code"),
|
||||
|
|
@ -100,6 +102,7 @@ func buildFileuploadPoolColumns(alias string) fileuploadPoolColumns {
|
|||
AddressLocality: psql.Quote(alias, "address_locality"),
|
||||
AddressRegion: psql.Quote(alias, "address_region"),
|
||||
Condition: psql.Quote(alias, "condition"),
|
||||
AddressID: psql.Quote(alias, "address_id"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +132,7 @@ type fileuploadPoolColumns struct {
|
|||
AddressLocality psql.Expression
|
||||
AddressRegion psql.Expression
|
||||
Condition psql.Expression
|
||||
AddressID psql.Expression
|
||||
}
|
||||
|
||||
func (c fileuploadPoolColumns) Alias() string {
|
||||
|
|
@ -166,10 +170,11 @@ type FileuploadPoolSetter struct {
|
|||
AddressLocality omit.Val[string] `db:"address_locality" `
|
||||
AddressRegion omit.Val[string] `db:"address_region" `
|
||||
Condition omit.Val[enums.Poolconditiontype] `db:"condition" `
|
||||
AddressID omitnull.Val[int32] `db:"address_id" `
|
||||
}
|
||||
|
||||
func (s FileuploadPoolSetter) SetColumns() []string {
|
||||
vals := make([]string, 0, 23)
|
||||
vals := make([]string, 0, 24)
|
||||
if s.AddressPostalCode.IsValue() {
|
||||
vals = append(vals, "address_postal_code")
|
||||
}
|
||||
|
|
@ -239,6 +244,9 @@ func (s FileuploadPoolSetter) SetColumns() []string {
|
|||
if s.Condition.IsValue() {
|
||||
vals = append(vals, "condition")
|
||||
}
|
||||
if !s.AddressID.IsUnset() {
|
||||
vals = append(vals, "address_id")
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
|
|
@ -312,6 +320,9 @@ func (s FileuploadPoolSetter) Overwrite(t *FileuploadPool) {
|
|||
if s.Condition.IsValue() {
|
||||
t.Condition = s.Condition.MustGet()
|
||||
}
|
||||
if !s.AddressID.IsUnset() {
|
||||
t.AddressID = s.AddressID.MustGetNull()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileuploadPoolSetter) Apply(q *dialect.InsertQuery) {
|
||||
|
|
@ -320,7 +331,7 @@ func (s *FileuploadPoolSetter) Apply(q *dialect.InsertQuery) {
|
|||
})
|
||||
|
||||
q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
|
||||
vals := make([]bob.Expression, 23)
|
||||
vals := make([]bob.Expression, 24)
|
||||
if s.AddressPostalCode.IsValue() {
|
||||
vals[0] = psql.Arg(s.AddressPostalCode.MustGet())
|
||||
} else {
|
||||
|
|
@ -459,6 +470,12 @@ func (s *FileuploadPoolSetter) Apply(q *dialect.InsertQuery) {
|
|||
vals[22] = psql.Raw("DEFAULT")
|
||||
}
|
||||
|
||||
if !s.AddressID.IsUnset() {
|
||||
vals[23] = psql.Arg(s.AddressID.MustGetNull())
|
||||
} else {
|
||||
vals[23] = psql.Raw("DEFAULT")
|
||||
}
|
||||
|
||||
return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "")
|
||||
}))
|
||||
}
|
||||
|
|
@ -468,7 +485,7 @@ func (s FileuploadPoolSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] {
|
|||
}
|
||||
|
||||
func (s FileuploadPoolSetter) Expressions(prefix ...string) []bob.Expression {
|
||||
exprs := make([]bob.Expression, 0, 23)
|
||||
exprs := make([]bob.Expression, 0, 24)
|
||||
|
||||
if s.AddressPostalCode.IsValue() {
|
||||
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
|
||||
|
|
@ -631,6 +648,13 @@ func (s FileuploadPoolSetter) Expressions(prefix ...string) []bob.Expression {
|
|||
}})
|
||||
}
|
||||
|
||||
if !s.AddressID.IsUnset() {
|
||||
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
|
||||
psql.Quote(append(prefix, "address_id")...),
|
||||
psql.Arg(s.AddressID),
|
||||
}})
|
||||
}
|
||||
|
||||
return exprs
|
||||
}
|
||||
|
||||
|
|
@ -857,6 +881,30 @@ func (o FileuploadPoolSlice) ReloadAll(ctx context.Context, exec bob.Executor) e
|
|||
return nil
|
||||
}
|
||||
|
||||
// Address starts a query for related objects on address
|
||||
func (o *FileuploadPool) Address(mods ...bob.Mod[*dialect.SelectQuery]) AddressesQuery {
|
||||
return Addresses.Query(append(mods,
|
||||
sm.Where(Addresses.Columns.ID.EQ(psql.Arg(o.AddressID))),
|
||||
)...)
|
||||
}
|
||||
|
||||
func (os FileuploadPoolSlice) Address(mods ...bob.Mod[*dialect.SelectQuery]) AddressesQuery {
|
||||
pkAddressID := make(pgtypes.Array[null.Val[int32]], 0, len(os))
|
||||
for _, o := range os {
|
||||
if o == nil {
|
||||
continue
|
||||
}
|
||||
pkAddressID = append(pkAddressID, o.AddressID)
|
||||
}
|
||||
PKArgExpr := psql.Select(sm.Columns(
|
||||
psql.F("unnest", psql.Cast(psql.Arg(pkAddressID), "integer[]")),
|
||||
))
|
||||
|
||||
return Addresses.Query(append(mods,
|
||||
sm.Where(psql.Group(Addresses.Columns.ID).OP("IN", PKArgExpr)),
|
||||
)...)
|
||||
}
|
||||
|
||||
// CreatorUser starts a query for related objects on user_
|
||||
func (o *FileuploadPool) CreatorUser(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
|
||||
return Users.Query(append(mods,
|
||||
|
|
@ -953,6 +1001,54 @@ func (os FileuploadPoolSlice) ResidentPhoneE164Phone(mods ...bob.Mod[*dialect.Se
|
|||
)...)
|
||||
}
|
||||
|
||||
func attachFileuploadPoolAddress0(ctx context.Context, exec bob.Executor, count int, fileuploadPool0 *FileuploadPool, address1 *Address) (*FileuploadPool, error) {
|
||||
setter := &FileuploadPoolSetter{
|
||||
AddressID: omitnull.From(address1.ID),
|
||||
}
|
||||
|
||||
err := fileuploadPool0.Update(ctx, exec, setter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("attachFileuploadPoolAddress0: %w", err)
|
||||
}
|
||||
|
||||
return fileuploadPool0, nil
|
||||
}
|
||||
|
||||
func (fileuploadPool0 *FileuploadPool) InsertAddress(ctx context.Context, exec bob.Executor, related *AddressSetter) error {
|
||||
var err error
|
||||
|
||||
address1, err := Addresses.Insert(related).One(ctx, exec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting related objects: %w", err)
|
||||
}
|
||||
|
||||
_, err = attachFileuploadPoolAddress0(ctx, exec, 1, fileuploadPool0, address1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileuploadPool0.R.Address = address1
|
||||
|
||||
address1.R.Pools = append(address1.R.Pools, fileuploadPool0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fileuploadPool0 *FileuploadPool) AttachAddress(ctx context.Context, exec bob.Executor, address1 *Address) error {
|
||||
var err error
|
||||
|
||||
_, err = attachFileuploadPoolAddress0(ctx, exec, 1, fileuploadPool0, address1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileuploadPool0.R.Address = address1
|
||||
|
||||
address1.R.Pools = append(address1.R.Pools, fileuploadPool0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func attachFileuploadPoolCreatorUser0(ctx context.Context, exec bob.Executor, count int, fileuploadPool0 *FileuploadPool, user1 *User) (*FileuploadPool, error) {
|
||||
setter := &FileuploadPoolSetter{
|
||||
CreatorID: omit.From(user1.ID),
|
||||
|
|
@ -1169,6 +1265,7 @@ type fileuploadPoolWhere[Q psql.Filterable] struct {
|
|||
AddressLocality psql.WhereMod[Q, string]
|
||||
AddressRegion psql.WhereMod[Q, string]
|
||||
Condition psql.WhereMod[Q, enums.Poolconditiontype]
|
||||
AddressID psql.WhereNullMod[Q, int32]
|
||||
}
|
||||
|
||||
func (fileuploadPoolWhere[Q]) AliasedAs(alias string) fileuploadPoolWhere[Q] {
|
||||
|
|
@ -1200,6 +1297,7 @@ func buildFileuploadPoolWhere[Q psql.Filterable](cols fileuploadPoolColumns) fil
|
|||
AddressLocality: psql.Where[Q, string](cols.AddressLocality),
|
||||
AddressRegion: psql.Where[Q, string](cols.AddressRegion),
|
||||
Condition: psql.Where[Q, enums.Poolconditiontype](cols.Condition),
|
||||
AddressID: psql.WhereNull[Q, int32](cols.AddressID),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1209,6 +1307,18 @@ func (o *FileuploadPool) Preload(name string, retrieved any) error {
|
|||
}
|
||||
|
||||
switch name {
|
||||
case "Address":
|
||||
rel, ok := retrieved.(*Address)
|
||||
if !ok {
|
||||
return fmt.Errorf("fileuploadPool cannot load %T as %q", retrieved, name)
|
||||
}
|
||||
|
||||
o.R.Address = rel
|
||||
|
||||
if rel != nil {
|
||||
rel.R.Pools = FileuploadPoolSlice{o}
|
||||
}
|
||||
return nil
|
||||
case "CreatorUser":
|
||||
rel, ok := retrieved.(*User)
|
||||
if !ok {
|
||||
|
|
@ -1263,6 +1373,7 @@ func (o *FileuploadPool) Preload(name string, retrieved any) error {
|
|||
}
|
||||
|
||||
type fileuploadPoolPreloader struct {
|
||||
Address func(...psql.PreloadOption) psql.Preloader
|
||||
CreatorUser func(...psql.PreloadOption) psql.Preloader
|
||||
CSVFileCSV func(...psql.PreloadOption) psql.Preloader
|
||||
PropertyOwnerPhoneE164Phone func(...psql.PreloadOption) psql.Preloader
|
||||
|
|
@ -1271,6 +1382,19 @@ type fileuploadPoolPreloader struct {
|
|||
|
||||
func buildFileuploadPoolPreloader() fileuploadPoolPreloader {
|
||||
return fileuploadPoolPreloader{
|
||||
Address: func(opts ...psql.PreloadOption) psql.Preloader {
|
||||
return psql.Preload[*Address, AddressSlice](psql.PreloadRel{
|
||||
Name: "Address",
|
||||
Sides: []psql.PreloadSide{
|
||||
{
|
||||
From: FileuploadPools,
|
||||
To: Addresses,
|
||||
FromColumns: []string{"address_id"},
|
||||
ToColumns: []string{"id"},
|
||||
},
|
||||
},
|
||||
}, Addresses.Columns.Names(), opts...)
|
||||
},
|
||||
CreatorUser: func(opts ...psql.PreloadOption) psql.Preloader {
|
||||
return psql.Preload[*User, UserSlice](psql.PreloadRel{
|
||||
Name: "CreatorUser",
|
||||
|
|
@ -1327,6 +1451,7 @@ func buildFileuploadPoolPreloader() fileuploadPoolPreloader {
|
|||
}
|
||||
|
||||
type fileuploadPoolThenLoader[Q orm.Loadable] struct {
|
||||
Address func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
CreatorUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
CSVFileCSV func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
PropertyOwnerPhoneE164Phone func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
|
||||
|
|
@ -1334,6 +1459,9 @@ type fileuploadPoolThenLoader[Q orm.Loadable] struct {
|
|||
}
|
||||
|
||||
func buildFileuploadPoolThenLoader[Q orm.Loadable]() fileuploadPoolThenLoader[Q] {
|
||||
type AddressLoadInterface interface {
|
||||
LoadAddress(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
|
||||
}
|
||||
type CreatorUserLoadInterface interface {
|
||||
LoadCreatorUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
|
||||
}
|
||||
|
|
@ -1348,6 +1476,12 @@ func buildFileuploadPoolThenLoader[Q orm.Loadable]() fileuploadPoolThenLoader[Q]
|
|||
}
|
||||
|
||||
return fileuploadPoolThenLoader[Q]{
|
||||
Address: thenLoadBuilder[Q](
|
||||
"Address",
|
||||
func(ctx context.Context, exec bob.Executor, retrieved AddressLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
return retrieved.LoadAddress(ctx, exec, mods...)
|
||||
},
|
||||
),
|
||||
CreatorUser: thenLoadBuilder[Q](
|
||||
"CreatorUser",
|
||||
func(ctx context.Context, exec bob.Executor, retrieved CreatorUserLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
|
|
@ -1375,6 +1509,61 @@ func buildFileuploadPoolThenLoader[Q orm.Loadable]() fileuploadPoolThenLoader[Q]
|
|||
}
|
||||
}
|
||||
|
||||
// LoadAddress loads the fileuploadPool's Address into the .R struct
|
||||
func (o *FileuploadPool) LoadAddress(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset the relationship
|
||||
o.R.Address = nil
|
||||
|
||||
related, err := o.Address(mods...).One(ctx, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
related.R.Pools = FileuploadPoolSlice{o}
|
||||
|
||||
o.R.Address = related
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAddress loads the fileuploadPool's Address into the .R struct
|
||||
func (os FileuploadPoolSlice) LoadAddress(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
if len(os) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
addresses, err := os.Address(mods...).All(ctx, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, o := range os {
|
||||
if o == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rel := range addresses {
|
||||
if !o.AddressID.IsValue() {
|
||||
continue
|
||||
}
|
||||
|
||||
if !(o.AddressID.IsValue() && o.AddressID.MustGet() == rel.ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
rel.R.Pools = append(rel.R.Pools, o)
|
||||
|
||||
o.R.Address = rel
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadCreatorUser loads the fileuploadPool's CreatorUser into the .R struct
|
||||
func (o *FileuploadPool) LoadCreatorUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
|
||||
if o == nil {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ func geocodePool(ctx context.Context, txn bob.Tx, client *stadia.StadiaMaps, job
|
|||
um.Table("fileupload.pool"),
|
||||
um.SetCol("h3cell").ToArg(geo.Cell),
|
||||
um.SetCol("geom").To(geom_query),
|
||||
um.SetCol("address_id").To(*geo.Address.ID),
|
||||
um.Where(psql.Quote("id").EQ(psql.Arg(pool.ID))),
|
||||
).Exec(ctx, txn)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/Gleipnir-Technology/bob/dialect/psql"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql/dialect"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql/im"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
|
||||
//bobtypes "github.com/Gleipnir-Technology/bob/types"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
|
||||
|
|
@ -124,9 +125,11 @@ func insertAddress(ctx context.Context, txn bob.Executor, address types.Address)
|
|||
}
|
||||
return &row.ID, nil
|
||||
}
|
||||
func insertAddresses(ctx context.Context, txn bob.Executor, features []stadia.GeocodeFeature) ([]int32, error) {
|
||||
func insertAddresses(ctx context.Context, txn bob.Executor, features []stadia.GeocodeFeature) ([]types.Address, error) {
|
||||
query := addressQuery()
|
||||
for _, feature := range features {
|
||||
gids := make([]string, len(features))
|
||||
for i, feature := range features {
|
||||
gids[i] = feature.Properties.GID
|
||||
lng := feature.Geometry.Coordinates[0]
|
||||
lat := feature.Geometry.Coordinates[1]
|
||||
cell, err := h3utils.GetCell(lng, lat, 15)
|
||||
|
|
@ -148,15 +151,24 @@ func insertAddresses(ctx context.Context, txn bob.Executor, features []stadia.Ge
|
|||
psql.Arg(feature.Street()),
|
||||
psql.Raw("''"),
|
||||
),
|
||||
im.OnConflict("gid").DoNothing(),
|
||||
)
|
||||
}
|
||||
rows, err := bob.All(ctx, txn, query, scan.StructMapper[_rowWithID]())
|
||||
_, err := bob.All(ctx, txn, query, scan.StructMapper[_rowWithID]())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert: %w", err)
|
||||
}
|
||||
results := make([]int32, len(rows))
|
||||
for _, row := range rows {
|
||||
results = append(results, row.ID)
|
||||
addresses, err := models.Addresses.Query(
|
||||
sm.Where(
|
||||
models.Addresses.Columns.Gid.EQ(psql.Any(gids)),
|
||||
),
|
||||
).All(ctx, txn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query by gid: %w", err)
|
||||
}
|
||||
results := make([]types.Address, len(addresses))
|
||||
for i, address := range addresses {
|
||||
results[i] = types.AddressFromModel(address)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Gleipnir-Technology/bob"
|
||||
|
|
@ -58,8 +57,11 @@ func GeocodeRaw(ctx context.Context, org *models.Organization, address string) (
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("client raw geocode failure on %s: %w", address, err)
|
||||
}
|
||||
insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
|
||||
return toGeocodeResult(*resp, address)
|
||||
addresses, err := insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert addresses: %w", err)
|
||||
}
|
||||
return toGeocodeResult(*resp, address, addresses)
|
||||
}
|
||||
func GeocodeStructured(ctx context.Context, org *models.Organization, a types.Address) (*GeocodeResult, error) {
|
||||
street := fmt.Sprintf("%s %s", a.Number, a.Street)
|
||||
|
|
@ -75,8 +77,11 @@ func GeocodeStructured(ctx context.Context, org *models.Organization, a types.Ad
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("client structured geocode failure on %s: %w", a.String(), err)
|
||||
}
|
||||
insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
|
||||
return toGeocodeResult(*resp, a.String())
|
||||
addresses, err := insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert addresses: %w", err)
|
||||
}
|
||||
return toGeocodeResult(*resp, a.String(), addresses)
|
||||
}
|
||||
func ReverseGeocode(ctx context.Context, location types.Location) (*GeocodeResult, error) {
|
||||
req := stadia.RequestReverseGeocode{
|
||||
|
|
@ -87,54 +92,33 @@ func ReverseGeocode(ctx context.Context, location types.Location) (*GeocodeResul
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("client reverse geocode failure on %s: %w", location.String(), err)
|
||||
}
|
||||
insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
|
||||
return toGeocodeResult(*resp, location.String())
|
||||
addresses, err := insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert addresses: %w", err)
|
||||
}
|
||||
return toGeocodeResult(*resp, location.String(), addresses)
|
||||
|
||||
}
|
||||
func toGeocodeResult(resp stadia.GeocodeResponse, address_msg string) (*GeocodeResult, error) {
|
||||
func toGeocodeResult(resp stadia.GeocodeResponse, address_msg string, addresses []types.Address) (*GeocodeResult, error) {
|
||||
if len(resp.Features) < 1 {
|
||||
return nil, fmt.Errorf("%s matched no locations", address_msg)
|
||||
}
|
||||
feature := resp.Features[0]
|
||||
if len(addresses) < 1 {
|
||||
return nil, fmt.Errorf("no addresses")
|
||||
}
|
||||
if len(resp.Features) > 1 {
|
||||
if !allFeaturesIdenticalEnough(resp.Features) {
|
||||
return nil, fmt.Errorf("%s matched more than one location, and they differ a lot", address_msg)
|
||||
}
|
||||
}
|
||||
feature := resp.Features[0]
|
||||
address := addresses[0]
|
||||
if feature.Geometry.Type != "Point" {
|
||||
return nil, fmt.Errorf("wrong type %s from %s", feature.Geometry.Type, address_msg)
|
||||
}
|
||||
longitude := feature.Geometry.Coordinates[0]
|
||||
latitude := feature.Geometry.Coordinates[1]
|
||||
cell, err := h3utils.GetCell(longitude, latitude, 15)
|
||||
cell, err := h3utils.GetCell(address.Location.Longitude, address.Location.Latitude, 15)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", longitude, latitude)
|
||||
}
|
||||
country_s := strings.ToLower(feature.Properties.CountryA)
|
||||
// Depending on what kind of request we made we'll get wildly different result structures
|
||||
// This first structure generally works for forword geocoding
|
||||
address := types.Address{
|
||||
Country: country_s,
|
||||
GID: feature.Properties.GID,
|
||||
Locality: feature.Properties.Locality,
|
||||
Location: &types.Location{
|
||||
Longitude: feature.Geometry.Coordinates[0],
|
||||
Latitude: feature.Geometry.Coordinates[1],
|
||||
},
|
||||
Number: feature.Properties.HouseNumber,
|
||||
PostalCode: feature.Properties.PostalCode,
|
||||
Region: feature.Properties.Region,
|
||||
Raw: feature.Properties.FormattedAddressLine,
|
||||
Street: feature.Properties.Street,
|
||||
Unit: "",
|
||||
}
|
||||
// If we don't have a locality, try populating for reverse geocoding
|
||||
if address.Country == "" {
|
||||
address.Country = strings.ToLower(feature.Properties.Context.ISO3166A3)
|
||||
address.Locality = feature.Properties.Context.WhosOnFirst.Locality.Name
|
||||
address.Number = feature.Properties.AddressComponents.Number
|
||||
address.PostalCode = feature.Properties.AddressComponents.PostalCode
|
||||
address.Street = feature.Properties.AddressComponents.Street
|
||||
return nil, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", address.Location.Longitude, address.Location.Latitude)
|
||||
}
|
||||
return &GeocodeResult{
|
||||
Address: address,
|
||||
|
|
|
|||
|
|
@ -268,7 +268,11 @@ tr.has-error {
|
|||
{{ titleCase(pool.condition) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ pool.tags?.size || 0 }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li v-for="(v, k) in pool.tags">{{ k }}={{ v }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue