Make lead creation and invalidation for public reports work
The only thing wrong at this point that I can tell is that address aren't being correctly populated when I reverse geocode.
This commit is contained in:
parent
3e1b56a266
commit
e2af49a323
27 changed files with 821 additions and 365 deletions
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/file"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/geom"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
"github.com/aarondl/opt/omit"
|
||||
"github.com/aarondl/opt/omitnull"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
@ -214,7 +215,10 @@ func insertFlyover(ctx context.Context, txn bob.Tx, file *models.FileuploadFile,
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", lng, lat)
|
||||
}
|
||||
geom_query := geom.PostgisPointQuery(lng, lat)
|
||||
geom_query := geom.PostgisPointQuery(types.Location{
|
||||
Latitude: lat,
|
||||
Longitude: lng,
|
||||
})
|
||||
_, err = psql.Update(
|
||||
um.TableAs("fileupload.pool", "pool"),
|
||||
um.SetCol("h3cell").ToArg(cell),
|
||||
|
|
|
|||
|
|
@ -139,11 +139,11 @@ func geocodePool(ctx context.Context, txn bob.Tx, client *stadia.StadiaMaps, job
|
|||
PostalCode: pool.AddressPostalCode,
|
||||
Street: pool.AddressStreet,
|
||||
}
|
||||
address, err := geocode.Geocode(ctx, job.org, a)
|
||||
address, err := geocode.GeocodeStructured(ctx, job.org, a)
|
||||
if err != nil {
|
||||
addError(ctx, txn, job.csv, job.rownumber, 0, err.Error())
|
||||
}
|
||||
geom_query := geom.PostgisPointQuery(address.Longitude, address.Latitude)
|
||||
geom_query := geom.PostgisPointQuery(address.Location)
|
||||
_, err = psql.Update(
|
||||
um.Table("fileupload.pool"),
|
||||
um.SetCol("h3cell").ToArg(address.Cell),
|
||||
|
|
|
|||
|
|
@ -20,10 +20,9 @@ import (
|
|||
)
|
||||
|
||||
type GeocodeResult struct {
|
||||
Address types.Address
|
||||
Cell h3.Cell
|
||||
Longitude float64
|
||||
Latitude float64
|
||||
Address types.Address
|
||||
Cell h3.Cell
|
||||
Location types.Location
|
||||
}
|
||||
|
||||
var client *stadia.StadiaMaps
|
||||
|
|
@ -105,7 +104,7 @@ func EnsureAddressWithGeocode(ctx context.Context, txn bob.Tx, org *models.Organ
|
|||
return address, nil
|
||||
}
|
||||
// Geocode
|
||||
geo, err := Geocode(ctx, org, a)
|
||||
geo, err := GeocodeStructured(ctx, org, a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("geocode: %w", err)
|
||||
}
|
||||
|
|
@ -122,7 +121,7 @@ func EnsureAddressWithGeocode(ctx context.Context, txn bob.Tx, org *models.Organ
|
|||
psql.Arg(geo.Cell),
|
||||
psql.Raw("DEFAULT"),
|
||||
psql.Arg(geo.Address.Locality),
|
||||
psql.F("ST_Point", geo.Longitude, geo.Latitude, 4326),
|
||||
psql.F("ST_Point", geo.Location.Longitude, geo.Location.Latitude, 4326),
|
||||
psql.Arg(geo.Address.Number),
|
||||
psql.Arg(geo.Address.PostalCode),
|
||||
psql.Arg(geo.Address.Region),
|
||||
|
|
@ -149,51 +148,66 @@ func EnsureAddressWithGeocode(ctx context.Context, txn bob.Tx, org *models.Organ
|
|||
Number: geo.Address.Number,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Geocode(ctx context.Context, org *models.Organization, a types.Address) (GeocodeResult, error) {
|
||||
func GeocodeRaw(ctx context.Context, org *models.Organization, address string) (*GeocodeResult, error) {
|
||||
req := stadia.RequestGeocodeRaw{
|
||||
Text: address,
|
||||
}
|
||||
maybeAddServiceArea(&req, org)
|
||||
resp, err := client.GeocodeRaw(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client raw geocode failure on %s: %w", address, err)
|
||||
}
|
||||
return toGeocodeResult(*resp, address)
|
||||
}
|
||||
func GeocodeStructured(ctx context.Context, org *models.Organization, a types.Address) (*GeocodeResult, error) {
|
||||
street := fmt.Sprintf("%s %s", a.Number, a.Street)
|
||||
country_s := a.Country
|
||||
/*
|
||||
sublog := log.With().
|
||||
Str("street", street).
|
||||
Str("country", country).
|
||||
Str("locality", a.Locality).
|
||||
Str("postal", a.PostalCode).
|
||||
Str("region", a.Region).
|
||||
Logger()
|
||||
*/
|
||||
req := stadia.StructuredGeocodeRequest{
|
||||
Address: &street,
|
||||
Country: &country_s,
|
||||
req := stadia.RequestGeocodeStructured{
|
||||
Address: &street,
|
||||
//Country: &a.Country,
|
||||
Locality: &a.Locality,
|
||||
PostalCode: &a.PostalCode,
|
||||
Region: &a.Region,
|
||||
}
|
||||
maybeAddServiceArea(&req, org)
|
||||
resp, err := client.StructuredGeocode(ctx, req)
|
||||
resp, err := client.GeocodeStructured(ctx, req)
|
||||
if err != nil {
|
||||
return GeocodeResult{}, fmt.Errorf("client structured geocode failure on %s: %w", a.String(), err)
|
||||
return nil, fmt.Errorf("client structured geocode failure on %s: %w", a.String(), err)
|
||||
}
|
||||
return toGeocodeResult(*resp, a.String())
|
||||
}
|
||||
func ReverseGeocode(ctx context.Context, location types.Location) (*GeocodeResult, error) {
|
||||
req := stadia.RequestReverseGeocode{
|
||||
Latitude: location.Latitude,
|
||||
Longitude: location.Longitude,
|
||||
}
|
||||
resp, err := client.ReverseGeocode(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client reverse geocode failure on %s: %w", location.String(), err)
|
||||
}
|
||||
return toGeocodeResult(*resp, location.String())
|
||||
|
||||
}
|
||||
func toGeocodeResult(resp stadia.GeocodeResponse, address string) (*GeocodeResult, error) {
|
||||
if len(resp.Features) < 1 {
|
||||
return GeocodeResult{}, fmt.Errorf("%s matched no locations", a.String())
|
||||
return nil, fmt.Errorf("%s matched no locations", address)
|
||||
}
|
||||
feature := resp.Features[0]
|
||||
if len(resp.Features) > 1 {
|
||||
if !allFeaturesIdenticalEnough(resp.Features) {
|
||||
return GeocodeResult{}, fmt.Errorf("%s matched more than one location, and they differ a lot", a.String())
|
||||
return nil, fmt.Errorf("%s matched more than one location, and they differ a lot", address)
|
||||
}
|
||||
}
|
||||
if feature.Geometry.Type != "Point" {
|
||||
return GeocodeResult{}, fmt.Errorf("wrong type %s from %s", feature.Geometry.Type, a.String())
|
||||
return nil, fmt.Errorf("wrong type %s from %s", feature.Geometry.Type, address)
|
||||
}
|
||||
longitude := feature.Geometry.Coordinates[0]
|
||||
latitude := feature.Geometry.Coordinates[1]
|
||||
cell, err := h3utils.GetCell(longitude, latitude, 15)
|
||||
if err != nil {
|
||||
return GeocodeResult{}, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", longitude, latitude)
|
||||
return nil, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", longitude, latitude)
|
||||
}
|
||||
country_s = strings.ToLower(feature.Properties.CountryA)
|
||||
return GeocodeResult{
|
||||
country_s := strings.ToLower(feature.Properties.CountryA)
|
||||
return &GeocodeResult{
|
||||
Address: types.Address{
|
||||
Country: country_s,
|
||||
Locality: feature.Properties.Locality,
|
||||
|
|
@ -203,9 +217,11 @@ func Geocode(ctx context.Context, org *models.Organization, a types.Address) (Ge
|
|||
Street: feature.Properties.Street,
|
||||
Unit: "",
|
||||
},
|
||||
Cell: cell,
|
||||
Longitude: feature.Geometry.Coordinates[0],
|
||||
Latitude: feature.Geometry.Coordinates[1],
|
||||
Cell: cell,
|
||||
Location: types.Location{
|
||||
Longitude: feature.Geometry.Coordinates[0],
|
||||
Latitude: feature.Geometry.Coordinates[1],
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +255,7 @@ func allFeaturesIdenticalEnough(features []stadia.GeocodeFeature) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
func maybeAddServiceArea(req *stadia.StructuredGeocodeRequest, org *models.Organization) {
|
||||
func maybeAddServiceArea(req stadia.RequestGeocode, org *models.Organization) {
|
||||
if org.ServiceAreaXmax.IsNull() ||
|
||||
org.ServiceAreaYmax.IsNull() ||
|
||||
org.ServiceAreaXmin.IsNull() ||
|
||||
|
|
@ -250,10 +266,7 @@ func maybeAddServiceArea(req *stadia.StructuredGeocodeRequest, org *models.Organ
|
|||
ymax := org.ServiceAreaYmax.MustGet()
|
||||
xmin := org.ServiceAreaXmin.MustGet()
|
||||
ymin := org.ServiceAreaYmin.MustGet()
|
||||
req.BoundaryRectMaxLon = &xmax
|
||||
req.BoundaryRectMaxLat = &ymax
|
||||
req.BoundaryRectMinLon = &xmin
|
||||
req.BoundaryRectMinLat = &ymin
|
||||
req.SetBoundaryRect(xmin, ymin, xmax, ymax)
|
||||
|
||||
if org.ServiceAreaCentroidX.IsNull() || org.ServiceAreaCentroidY.IsNull() {
|
||||
return
|
||||
|
|
@ -261,6 +274,5 @@ func maybeAddServiceArea(req *stadia.StructuredGeocodeRequest, org *models.Organ
|
|||
centroid_x := org.ServiceAreaCentroidX.MustGet()
|
||||
centroid_y := org.ServiceAreaCentroidY.MustGet()
|
||||
|
||||
req.FocusPointLat = ¢roid_y
|
||||
req.FocusPointLng = ¢roid_x
|
||||
req.SetFocusPoint(centroid_x, centroid_y)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package geom
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
)
|
||||
|
||||
func PostgisPointQuery(longitude, latitude float64) string {
|
||||
return fmt.Sprintf("ST_SetSRID(ST_MakePoint(%f, %f), 4326)", longitude, latitude)
|
||||
func PostgisPointQuery(location types.Location) string {
|
||||
return fmt.Sprintf("ST_SetSRID(ST_MakePoint(%f, %f), 4326)", location.Longitude, location.Latitude)
|
||||
}
|
||||
|
|
|
|||
184
platform/lead.go
Normal file
184
platform/lead.go
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Gleipnir-Technology/bob"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql/um"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/geocode"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/geom"
|
||||
"github.com/aarondl/opt/omit"
|
||||
"github.com/aarondl/opt/omitnull"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Create a lead from the given signal and site
|
||||
func LeadCreate(ctx context.Context, user User, signal_id int32, site_id int32, pool_location *Location) (*int32, error) {
|
||||
txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil)
|
||||
defer txn.Rollback(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("start transaction: %w", err)
|
||||
}
|
||||
|
||||
lead, err := models.Leads.Insert(&models.LeadSetter{
|
||||
Created: omit.From(time.Now()),
|
||||
Creator: omit.From(int32(user.ID)),
|
||||
// ID
|
||||
OrganizationID: omit.From(int32(user.Organization.ID())),
|
||||
SiteID: omitnull.From(site_id),
|
||||
Type: omit.From(enums.LeadtypeGreenPool),
|
||||
}).One(ctx, txn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create lead: %w", err)
|
||||
}
|
||||
_, err = psql.Update(
|
||||
um.Table("signal"),
|
||||
um.SetCol("addressed").ToArg(time.Now()),
|
||||
um.SetCol("addressor").ToArg(user.ID),
|
||||
um.Where(psql.Quote("id").EQ(psql.Arg(signal_id))),
|
||||
).Exec(ctx, txn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update signal %d: %w", signal_id, err)
|
||||
}
|
||||
if pool_location != nil {
|
||||
log.Info().Float64("lat", pool_location.Latitude).Float64("lng", pool_location.Longitude).Msg("got pool location")
|
||||
geom_query := geom.PostgisPointQuery(*pool_location)
|
||||
_, err = psql.Update(
|
||||
um.Table("pool"),
|
||||
um.SetCol("geometry").To(geom_query),
|
||||
um.From("signal_pool"),
|
||||
um.Where(psql.Quote("signal_pool", "pool_id").EQ(psql.Quote("pool", "id"))),
|
||||
um.Where(psql.Quote("signal_pool", "signal_id").EQ(psql.Arg(signal_id))),
|
||||
).Exec(ctx, txn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update pool through signal %d: %w", signal_id, err)
|
||||
}
|
||||
}
|
||||
txn.Commit(ctx)
|
||||
return &lead.ID, nil
|
||||
}
|
||||
|
||||
// Create a lead from the given signal and site
|
||||
func LeadCreateFromPublicreport(ctx context.Context, user User, report_id string) (*int32, error) {
|
||||
txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil)
|
||||
defer txn.Rollback(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("start transaction: %w", err)
|
||||
}
|
||||
|
||||
location, err := models.PublicreportReportLocations.Query(
|
||||
models.SelectWhere.PublicreportReportLocations.PublicID.EQ(report_id),
|
||||
models.SelectWhere.PublicreportReportLocations.OrganizationID.EQ(user.Organization.ID()),
|
||||
).One(ctx, txn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query report existence: %w", err)
|
||||
}
|
||||
|
||||
// At this point we have a report. We need to decide where to put it based on either the address or
|
||||
// the location.
|
||||
var site_id int32
|
||||
if location.AddressID.IsValue() {
|
||||
site, err := siteFromAddress(ctx, txn, user, location.AddressID.MustGet())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("site from address: %w", err)
|
||||
}
|
||||
site_id = site.ID
|
||||
} else if location.LocationLatitude.IsValue() && location.LocationLongitude.IsValue() {
|
||||
site, err := siteFromLocation(ctx, txn, user, Location{
|
||||
Latitude: location.LocationLatitude.MustGet(),
|
||||
Longitude: location.LocationLongitude.MustGet(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("site from address: %w", err)
|
||||
}
|
||||
site_id = site.ID
|
||||
|
||||
} else if location.AddressRaw.GetOr("") != "" {
|
||||
// At this point we don't have an address, and we don't have GPS
|
||||
// We'll try geocoding and creating an address from that.
|
||||
site, err := siteFromAddressRaw(ctx, txn, user, location.AddressRaw.MustGet())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("site from address: %w", err)
|
||||
}
|
||||
site_id = site.ID
|
||||
} else {
|
||||
// We have no structured address, no GPS, no unstructued address.
|
||||
// There's really nothing we can make this lead from and have it be meaningful
|
||||
return nil, errors.New("Refusing to create a lead with no location data.")
|
||||
}
|
||||
|
||||
lead_type := enums.LeadtypeUnknown
|
||||
tablename := location.TableName.MustGet()
|
||||
switch tablename {
|
||||
case "nuisance":
|
||||
lead_type = enums.LeadtypePublicreportNuisance
|
||||
case "water":
|
||||
lead_type = enums.LeadtypePublicreportWater
|
||||
}
|
||||
lead, err := models.Leads.Insert(&models.LeadSetter{
|
||||
Created: omit.From(time.Now()),
|
||||
Creator: omit.From(int32(user.ID)),
|
||||
// ID
|
||||
OrganizationID: omit.From(int32(user.Organization.ID())),
|
||||
SiteID: omitnull.From(site_id),
|
||||
Type: omit.From(lead_type),
|
||||
}).One(ctx, txn)
|
||||
_, err = psql.Update(
|
||||
um.Table("publicreport."+tablename),
|
||||
um.SetCol("reviewed").ToArg(time.Now()),
|
||||
um.SetCol("reviewer_id").ToArg(user.ID),
|
||||
um.SetCol("status").ToArg(enums.PublicreportReportstatustypeReviewed),
|
||||
um.Where(psql.Quote("public_id").EQ(psql.Arg(report_id))),
|
||||
).Exec(ctx, txn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update report %d: %w", report_id, err)
|
||||
}
|
||||
txn.Commit(ctx)
|
||||
|
||||
return &lead.ID, nil
|
||||
}
|
||||
func siteFromAddress(ctx context.Context, txn bob.Tx, user User, address_id int32) (*models.Site, error) {
|
||||
site, err := models.Sites.Query(
|
||||
models.SelectWhere.Sites.AddressID.EQ(address_id),
|
||||
models.SelectWhere.Sites.OrganizationID.EQ(user.Organization.ID()),
|
||||
).One(ctx, txn)
|
||||
if err == nil {
|
||||
return site, nil
|
||||
}
|
||||
if err.Error() != "sql: no rows in result set" {
|
||||
return nil, fmt.Errorf("query site: %w", err)
|
||||
}
|
||||
return SiteCreate(ctx, txn, user, address_id)
|
||||
}
|
||||
func siteFromAddressRaw(ctx context.Context, txn bob.Tx, user User, address string) (*models.Site, error) {
|
||||
// Geocode
|
||||
geo, err := geocode.GeocodeRaw(ctx, user.Organization.model, address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("geocode: %w", err)
|
||||
}
|
||||
a, err := geocode.EnsureAddress(ctx, txn, geo.Address, geo.Location)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ensure address: %w", err)
|
||||
}
|
||||
return siteFromAddress(ctx, txn, user, a.ID)
|
||||
}
|
||||
func siteFromLocation(ctx context.Context, txn bob.Tx, user User, location Location) (*models.Site, error) {
|
||||
// Reverse geocode at the location
|
||||
resp, err := geocode.ReverseGeocode(ctx, location)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reverse geocode: %w", err)
|
||||
}
|
||||
// Ensure we have an address at that newly created location
|
||||
a, err := geocode.EnsureAddress(ctx, txn, resp.Address, resp.Location)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ensure address: %w", err)
|
||||
}
|
||||
return siteFromAddress(ctx, txn, user, a.ID)
|
||||
}
|
||||
40
platform/publicreport.go
Normal file
40
platform/publicreport.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
//"github.com/Gleipnir-Technology/bob"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql"
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql/um"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func PublicreportInvalid(ctx context.Context, user User, report_id string) error {
|
||||
location, err := models.PublicreportReportLocations.Query(
|
||||
models.SelectWhere.PublicreportReportLocations.PublicID.EQ(report_id),
|
||||
models.SelectWhere.PublicreportReportLocations.OrganizationID.EQ(user.Organization.ID()),
|
||||
).One(ctx, db.PGInstance.BobDB)
|
||||
if err != nil {
|
||||
return fmt.Errorf("query report existence: %w", err)
|
||||
}
|
||||
|
||||
tablename := location.TableName.MustGet()
|
||||
_, err = psql.Update(
|
||||
um.Table("publicreport."+tablename),
|
||||
um.SetCol("reviewed").ToArg(time.Now()),
|
||||
um.SetCol("reviewer_id").ToArg(user.ID),
|
||||
um.SetCol("status").ToArg(enums.PublicreportReportstatustypeInvalidated),
|
||||
um.Where(psql.Quote("public_id").EQ(psql.Arg(report_id))),
|
||||
).Exec(ctx, db.PGInstance.BobDB)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update report %s.%s: %w", tablename, report_id, err)
|
||||
}
|
||||
|
||||
log.Info().Str("report-id", report_id).Str("tablename", tablename).Msg("Marked as invalid")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -79,6 +79,7 @@ func NuisanceReportForOrganization(ctx context.Context, org_id int32) ([]Nuisanc
|
|||
),
|
||||
sm.From("publicreport.nuisance"),
|
||||
sm.Where(psql.Quote("publicreport", "nuisance", "organization_id").EQ(psql.Arg(org_id))),
|
||||
sm.Where(psql.Quote("publicreport", "nuisance", "reviewed").IsNull()),
|
||||
), scan.StructMapper[Nuisance]())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get reports: %w", err)
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ func WaterReportForOrganization(ctx context.Context, org_id int32) ([]Water, err
|
|||
),
|
||||
sm.From("publicreport.water"),
|
||||
sm.Where(psql.Quote("publicreport", "water", "organization_id").EQ(psql.Arg(org_id))),
|
||||
sm.Where(psql.Quote("publicreport", "water", "reviewed").IsNull()),
|
||||
), scan.StructMapper[Water]())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get reports: %w", err)
|
||||
|
|
|
|||
64
platform/site.go
Normal file
64
platform/site.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"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/bob/types/pgtypes"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
|
||||
"github.com/aarondl/opt/omit"
|
||||
"github.com/aarondl/opt/omitnull"
|
||||
"github.com/stephenafamo/scan"
|
||||
)
|
||||
|
||||
func SiteFromSignal(ctx context.Context, user User, signal_id int32) (*int32, error) {
|
||||
type _Row struct {
|
||||
ID int32 `db:"site_id"`
|
||||
}
|
||||
site, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select(
|
||||
sm.Columns(
|
||||
"pool.site_id AS site_id",
|
||||
),
|
||||
sm.From("signal_pool"),
|
||||
sm.InnerJoin("pool").OnEQ(
|
||||
psql.Quote("signal_pool", "pool_id"),
|
||||
psql.Quote("pool", "id"),
|
||||
),
|
||||
sm.InnerJoin("site").On(
|
||||
psql.Quote("pool", "site_id").EQ(psql.Quote("site", "id")),
|
||||
),
|
||||
sm.Where(psql.Quote("signal_pool", "signal_id").EQ(psql.Arg(signal_id))),
|
||||
sm.Where(psql.Quote("site", "organization_id").EQ(psql.Arg(user.Organization.ID()))),
|
||||
), scan.StructMapper[_Row]())
|
||||
if err != nil {
|
||||
if err.Error() == "sql: no rows in result set" {
|
||||
return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "Can't make a lead from signal %d: %w", signal_id, err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed getting site: %w", err)
|
||||
}
|
||||
return &site.ID, nil
|
||||
}
|
||||
func SiteCreate(ctx context.Context, txn bob.Tx, user User, address_id int32) (*models.Site, error) {
|
||||
return models.Sites.Insert(&models.SiteSetter{
|
||||
AddressID: omit.From(address_id),
|
||||
Created: omit.From(time.Now()),
|
||||
CreatorID: omit.From(int32(user.ID)),
|
||||
FileID: omitnull.FromPtr[int32](nil),
|
||||
//ID:
|
||||
Notes: omit.From(""),
|
||||
OrganizationID: omit.From(user.Organization.ID()),
|
||||
OwnerName: omit.From(""),
|
||||
OwnerPhoneE164: omitnull.FromPtr[string](nil),
|
||||
ParcelID: omitnull.FromPtr[int32](nil),
|
||||
ResidentOwned: omitnull.FromPtr[bool](nil),
|
||||
Tags: omit.From(pgtypes.HStore{}),
|
||||
Version: omit.From(int32(1)),
|
||||
}).One(ctx, txn)
|
||||
}
|
||||
|
|
@ -4,8 +4,11 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
||||
)
|
||||
|
||||
type Location = types.Location
|
||||
|
||||
type ClientSync struct {
|
||||
Fieldseeker FieldseekerRecordsSync
|
||||
Since time.Time
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
Latitude float64 `db:"latitude" json:"latitude"`
|
||||
Longitude float64 `db:"longitude" json:"longitude"`
|
||||
}
|
||||
|
||||
func (l Location) String() string {
|
||||
return fmt.Sprintf("%f %f", l.Longitude, l.Latitude)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue