From 61d8d14fc295655bf62973c18b867227a3eccd0f Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Thu, 22 Jan 2026 03:27:32 +0000 Subject: [PATCH] Bunch of work around assigning reports to districts I added some DB schema to track logos and to relate reports to organizations. I reworked how GPS data comes from EXIF data on images because it wasn't working for JPEGs. I might have broken PNGs in the process. Also made the config options for domain names more standardized. --- api/api.go | 40 - api/district.go | 74 ++ api/{endpoint.go => routes.go} | 1 + arcgis-go | 2 +- background/arcgis.go | 4 +- comms/email.go | 8 +- config/config.go | 86 +- db/bobgen.yaml | 3 + db/dbinfo/organization.bob.go | 12 +- db/dbinfo/publicreport.nuisance.bob.go | 32 +- db/dbinfo/publicreport.pool.bob.go | 32 +- db/dbinfo/publicreport.quick.bob.go | 52 +- db/factory/bobfactory_context.bob.go | 6 + db/factory/bobfactory_main.bob.go | 24 + db/factory/organization.bob.go | 325 ++++++- db/factory/publicreport.nuisance.bob.go | 134 ++- db/factory/publicreport.pool.bob.go | 133 ++- db/factory/publicreport.quick.bob.go | 153 +++- db/migrations/00037_publicreport_district.sql | 8 + db/migrations/00038_organization_logo.sql | 2 + db/models/bob_joins.bob.go | 2 + db/models/bob_loaders.bob.go | 4 + db/models/organization.bob.go | 833 +++++++++++++++++- db/models/publicreport.nuisance.bob.go | 269 +++++- db/models/publicreport.pool.bob.go | 226 ++++- db/models/publicreport.quick.bob.go | 350 ++++++-- go.mod | 11 +- go.sum | 224 +++++ main.go | 6 +- platform/district.go | 34 +- public-report/endpoint.go | 3 - public-report/image-upload.go | 85 +- public-report/quick.go | 118 ++- public-report/search.go | 2 +- public-report/status.go | 4 +- .../template/quick-submit-complete.html | 5 + public-report/template/status-by-id.html | 4 +- sync/dash.go | 2 +- sync/mock.go | 2 +- sync/oauth.go | 31 +- userfile/userfile.go | 20 +- 41 files changed, 3029 insertions(+), 337 deletions(-) create mode 100644 api/district.go rename api/{endpoint.go => routes.go} (95%) create mode 100644 db/migrations/00037_publicreport_district.sql create mode 100644 db/migrations/00038_organization_logo.sql diff --git a/api/api.go b/api/api.go index 60226a1a..3b255c50 100644 --- a/api/api.go +++ b/api/api.go @@ -78,46 +78,6 @@ func apiAudioContentPost(w http.ResponseWriter, r *http.Request, u *models.User) w.WriteHeader(http.StatusOK) } -func apiGetDistrict(w http.ResponseWriter, r *http.Request) { - var latStr, lngStr string - err := r.ParseForm() - if err != nil { - render.Render(w, r, errRender(fmt.Errorf("Failed to parse GET form: %w", err))) - return - } else { - latStr = r.FormValue("lat") - lngStr = r.FormValue("lng") - } - lat, err := strconv.ParseFloat(latStr, 64) - if err != nil { - render.Render(w, r, errRender(fmt.Errorf("Failed to parse lat as float: %w", err))) - return - } - lng, err := strconv.ParseFloat(lngStr, 64) - if err != nil { - render.Render(w, r, errRender(fmt.Errorf("Failed to parse lng as float: %w", err))) - return - } - district, err := platform.DistrictForLocation(r.Context(), lng, lat) - if err != nil { - render.Render(w, r, errRender(fmt.Errorf("Failed to get district: %w", err))) - return - } - if district == nil { - http.NotFound(w, r) - return - } - d := ResponseDistrict{ - Agency: district.Agency.GetOr(""), - Manager: district.GeneralMG.GetOr(""), - Phone: district.Phone1.GetOr(""), - Website: district.Website.GetOr(""), - } - if err := render.Render(w, r, d); err != nil { - render.Render(w, r, errRender(err)) - } -} - func handleClientIos(w http.ResponseWriter, r *http.Request, u *models.User) { var sinceStr string err := r.ParseForm() diff --git a/api/district.go b/api/district.go new file mode 100644 index 00000000..360e473b --- /dev/null +++ b/api/district.go @@ -0,0 +1,74 @@ +package api + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/platform" + "github.com/Gleipnir-Technology/nidus-sync/userfile" + "github.com/go-chi/chi/v5" + "github.com/go-chi/render" +) + +func apiGetDistrict(w http.ResponseWriter, r *http.Request) { + var latStr, lngStr string + err := r.ParseForm() + if err != nil { + render.Render(w, r, errRender(fmt.Errorf("Failed to parse GET form: %w", err))) + return + } else { + latStr = r.FormValue("lat") + lngStr = r.FormValue("lng") + } + lat, err := strconv.ParseFloat(latStr, 64) + if err != nil { + render.Render(w, r, errRender(fmt.Errorf("Failed to parse lat as float: %w", err))) + return + } + lng, err := strconv.ParseFloat(lngStr, 64) + if err != nil { + render.Render(w, r, errRender(fmt.Errorf("Failed to parse lng as float: %w", err))) + return + } + district, _, err := platform.DistrictForLocation(r.Context(), lng, lat) + if err != nil { + render.Render(w, r, errRender(fmt.Errorf("Failed to get district: %w", err))) + return + } + if district == nil { + http.NotFound(w, r) + return + } + d := ResponseDistrict{ + Agency: district.Agency.GetOr(""), + Manager: district.GeneralMG.GetOr(""), + Phone: district.Phone1.GetOr(""), + Website: district.Website.GetOr(""), + } + if err := render.Render(w, r, d); err != nil { + render.Render(w, r, errRender(err)) + } +} + +func apiGetDistrictLogo(w http.ResponseWriter, r *http.Request) { + id_str := chi.URLParam(r, "id") + org_id, err := strconv.ParseInt(id_str, 10, 32) + if err != nil { + render.Render(w, r, errRender(fmt.Errorf("%s is not a recognized organization ID: %w", id_str, err))) + return + } + ctx := r.Context() + org, err := models.FindOrganization(ctx, db.PGInstance.BobDB, int32(org_id)) + if err != nil { + http.Error(w, "Organization not found", http.StatusNotFound) + return + } + if org.LogoUUID.IsNull() { + http.Error(w, "Logo not found", http.StatusNotFound) + return + } + userfile.ImageFileContentWriteLogo(w, org.LogoUUID.MustGet()) +} diff --git a/api/endpoint.go b/api/routes.go similarity index 95% rename from api/endpoint.go rename to api/routes.go index 14d59cb1..81458890 100644 --- a/api/endpoint.go +++ b/api/routes.go @@ -21,6 +21,7 @@ func AddRoutes(r chi.Router) { // Unauthenticated endpoints r.Get("/district", apiGetDistrict) + r.Get("/district/{id}/logo", apiGetDistrictLogo) r.Post("/twilio/message", twilioMessagePost) r.Post("/twilio/status", twilioStatusPost) r.Post("/twilio/text", twilioTextPost) diff --git a/arcgis-go b/arcgis-go index af786fab..09fda12a 160000 --- a/arcgis-go +++ b/arcgis-go @@ -1 +1 @@ -Subproject commit af786fabcc08ed506a23718a71aa0dd52ce047ac +Subproject commit 09fda12abb8af08b4d95ea519fdda73b5399bf63 diff --git a/background/arcgis.go b/background/arcgis.go index 94aae8ca..0e7e166a 100644 --- a/background/arcgis.go +++ b/background/arcgis.go @@ -72,7 +72,7 @@ func HandleOauthAccessCode(ctx context.Context, user *models.User, code string) "grant_type": []string{"authorization_code"}, "code": []string{code}, "client_id": []string{config.ClientID}, - "redirect_uri": []string{config.RedirectURL()}, + "redirect_uri": []string{config.ArcGISOauthRedirectURL()}, } req, err := http.NewRequest("POST", baseURL, strings.NewReader(form.Encode())) @@ -652,7 +652,7 @@ func refreshRefreshToken(ctx context.Context, oauth *models.OauthToken) error { form := url.Values{ "grant_type": []string{"exchange_refresh_token"}, "client_id": []string{config.ClientID}, - "redirect_uri": []string{config.RedirectURL()}, + "redirect_uri": []string{config.ArcGISOauthRedirectURL()}, "refresh_token": []string{oauth.RefreshToken}, } diff --git a/comms/email.go b/comms/email.go index 64d7f970..a2e2a992 100644 --- a/comms/email.go +++ b/comms/email.go @@ -106,10 +106,10 @@ type emailResponse struct { func contentEmailSubscriptionConfirmation(report_id string) contentEmailReportConfirmation { return contentEmailReportConfirmation{ - URLLogo: fmt.Sprintf("https://%s/static/img/nidus-logo-no-lettering-64.png", config.URLReport), - URLReportStatus: fmt.Sprintf("https://%s/status/%s", config.URLReport, report_id), - URLReportUnsubscribe: fmt.Sprintf("https://%s/report/%s/unsubscribe", config.URLReport, report_id), - URLViewInBrowser: fmt.Sprintf("https://%s/email/report/%s/subscription-confirmation", config.URLReport, report_id), + URLLogo: config.MakeURLReport("/static/img/nidus-logo-no-lettering-64.png"), + URLReportStatus: config.MakeURLReport("/status/%s", report_id), + URLReportUnsubscribe: config.MakeURLReport("/report/%s/unsubscribe", report_id), + URLViewInBrowser: config.MakeURLReport("/email/report/%s/subscription-confirmation", report_id), } } diff --git a/config/config.go b/config/config.go index e82e04ea..51a72f85 100644 --- a/config/config.go +++ b/config/config.go @@ -2,9 +2,7 @@ package config import ( "fmt" - "net/url" "os" - "strconv" "github.com/nyaruka/phonenumbers" ) @@ -13,7 +11,11 @@ var ( Bind string ClientID string ClientSecret string + DomainRMO string + DomainNidus string + DomainTegola string Environment string + FilesDirectoryLogo string FilesDirectoryPublic string FilesDirectoryUser string FieldseekerSchemaDirectory string @@ -23,47 +25,29 @@ var ( ForwardEmailReportUsername string MapboxToken string PGDSN string - RMODomain string RMOPhoneNumber phonenumbers.PhoneNumber - URLReport string - URLSync string - URLTegola string TwilioAuthToken string TwilioAccountSID string TwilioMessagingServiceSID string ) -// Build the ArcGIS authorization URL with PKCE -func BuildArcGISAuthURL(clientID string) string { - baseURL := "https://www.arcgis.com/sharing/rest/oauth2/authorize/" - - params := url.Values{} - params.Add("client_id", clientID) - params.Add("redirect_uri", RedirectURL()) - params.Add("response_type", "code") - //params.Add("code_challenge", generateCodeChallenge(codeVerifier)) - //params.Add("code_challenge_method", "S256") - - // See https://developers.arcgis.com/rest/users-groups-and-items/token/ - // expiration is defined in minutes - var expiration int - if IsProductionEnvironment() { - // 2 weeks is the maximum allowed - expiration = 20160 - } else { - expiration = 20 - } - params.Add("expiration", strconv.Itoa(expiration)) - - return baseURL + "?" + params.Encode() -} - func IsProductionEnvironment() bool { return Environment == "PRODUCTION" } -func MakeURLSync(path string) string { - return fmt.Sprintf("https://%s%s", URLSync, path) +func makeURL(domain, path string, args ...interface{}) string { + pattern := "https://" + domain + path + return fmt.Sprintf(pattern, args...) +} + +func MakeURLNidus(path string, args ...interface{}) string { + return makeURL(DomainNidus, path, args...) +} +func MakeURLReport(path string, args ...interface{}) string { + return makeURL(DomainRMO, path, args...) +} +func MakeURLTegola(path string, args ...interface{}) string { + return makeURL(DomainTegola, path, args...) } func Parse() (err error) { @@ -79,6 +63,18 @@ func Parse() (err error) { if ClientSecret == "" { return fmt.Errorf("You must specify a non-empty ARCGIS_CLIENT_SECRET") } + DomainNidus = os.Getenv("DOMAIN_NIDUS") + if DomainNidus == "" { + return fmt.Errorf("You must specify a non-empty DOMAIN_NIDUS") + } + DomainRMO = os.Getenv("DOMAIN_RMO") + if DomainRMO == "" { + return fmt.Errorf("You must specify a non-empty DOMAIN_RMO") + } + DomainTegola = os.Getenv("DOMAIN_TEGOLA") + if DomainTegola == "" { + return fmt.Errorf("You must specify a non-empty DOMAIN_TEGOLA") + } Environment = os.Getenv("ENVIRONMENT") if Environment == "" { return fmt.Errorf("You must specify a non-empty ENVIRONMENT") @@ -90,6 +86,10 @@ func Parse() (err error) { if FieldseekerSchemaDirectory == "" { return fmt.Errorf("You must specify a non-empty FIELDSEEKER_SCHEMA_DIRECTORY") } + FilesDirectoryLogo = os.Getenv("FILES_DIRECTORY_LOGO") + if FilesDirectoryLogo == "" { + return fmt.Errorf("You must specify a non-empty FILES_DIRECTORY_LOGO") + } FilesDirectoryPublic = os.Getenv("FILES_DIRECTORY_PUBLIC") if FilesDirectoryPublic == "" { return fmt.Errorf("You must specify a non-empty FILES_DIRECTORY_PUBLIC") @@ -122,10 +122,6 @@ func Parse() (err error) { if PGDSN == "" { return fmt.Errorf("You must specify a non-empty POSTGRES_DSN") } - RMODomain = os.Getenv("RMO_DOMAIN") - if RMODomain == "" { - return fmt.Errorf("You must specify a non-empty RMO_DOMAIN") - } rmo_phone_number := os.Getenv("RMO_PHONE_NUMBER") if rmo_phone_number == "" { return fmt.Errorf("You must specify a non-empty RMO_PHONE_NUMBER") @@ -136,18 +132,6 @@ func Parse() (err error) { } RMOPhoneNumber = *p - URLReport = os.Getenv("URL_REPORT") - if URLReport == "" { - return fmt.Errorf("You must specify a non-empty URL_REPORT") - } - URLSync = os.Getenv("URL_SYNC") - if URLSync == "" { - return fmt.Errorf("You must specify a non-empty URL_SYNC") - } - URLTegola = os.Getenv("URL_TEGOLA") - if URLTegola == "" { - return fmt.Errorf("You must specify a non-empty URL_TEGOLA") - } TwilioAccountSID = os.Getenv("TWILIO_ACCOUNT_SID") if TwilioAccountSID == "" { return fmt.Errorf("You must specify a non-empty TWILIO_ACCOUNT_SID") @@ -163,6 +147,6 @@ func Parse() (err error) { return nil } -func RedirectURL() string { - return MakeURLSync("/arcgis/oauth/callback") +func ArcGISOauthRedirectURL() string { + return MakeURLNidus("/arcgis/oauth/callback") } diff --git a/db/bobgen.yaml b/db/bobgen.yaml index 1480da9e..92053a07 100644 --- a/db/bobgen.yaml +++ b/db/bobgen.yaml @@ -4,6 +4,9 @@ aliases: up_singular: "ArcgisUser" down_plural: "arcgisusers" down_singular: "arcgisuser" + organization: + relationships: + publicreport.pool.pool_organization_id_fkey: "PublicreportPool" user_: up_plural: "Users" up_singular: "User" diff --git a/db/dbinfo/organization.bob.go b/db/dbinfo/organization.bob.go index cd42ae32..5013dc15 100644 --- a/db/dbinfo/organization.bob.go +++ b/db/dbinfo/organization.bob.go @@ -78,6 +78,15 @@ var Organizations = Table[ Generated: false, AutoIncr: false, }, + LogoUUID: column{ + Name: "logo_uuid", + DBType: "uuid", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, }, Indexes: organizationIndexes{ OrganizationPkey: index{ @@ -172,11 +181,12 @@ type organizationColumns struct { FieldseekerURL column ImportDistrictGid column Website column + LogoUUID column } func (c organizationColumns) AsSlice() []column { return []column{ - c.ID, c.Name, c.ArcgisID, c.ArcgisName, c.FieldseekerURL, c.ImportDistrictGid, c.Website, + c.ID, c.Name, c.ArcgisID, c.ArcgisName, c.FieldseekerURL, c.ImportDistrictGid, c.Website, c.LogoUUID, } } diff --git a/db/dbinfo/publicreport.nuisance.bob.go b/db/dbinfo/publicreport.nuisance.bob.go index 625162eb..20debe36 100644 --- a/db/dbinfo/publicreport.nuisance.bob.go +++ b/db/dbinfo/publicreport.nuisance.bob.go @@ -258,6 +258,15 @@ var PublicreportNuisances = Table[ Generated: false, AutoIncr: false, }, + OrganizationID: column{ + Name: "organization_id", + DBType: "integer", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, }, Indexes: publicreportNuisanceIndexes{ NuisancePkey: index{ @@ -300,7 +309,17 @@ var PublicreportNuisances = Table[ Columns: []string{"id"}, Comment: "", }, - + ForeignKeys: publicreportNuisanceForeignKeys{ + PublicreportNuisanceNuisanceOrganizationIDFkey: foreignKey{ + constraint: constraint{ + Name: "publicreport.nuisance.nuisance_organization_id_fkey", + Columns: []string{"organization_id"}, + Comment: "", + }, + ForeignTable: "organization", + ForeignColumns: []string{"id"}, + }, + }, Uniques: publicreportNuisanceUniques{ NuisancePublicIDKey: constraint{ Name: "nuisance_public_id_key", @@ -340,11 +359,12 @@ type publicreportNuisanceColumns struct { Address column Location column Status column + OrganizationID column } func (c publicreportNuisanceColumns) AsSlice() []column { return []column{ - c.ID, c.AdditionalInfo, c.Created, c.Duration, c.Email, c.InspectionType, c.SourceLocation, c.PreferredDateRange, c.PreferredTime, c.RequestCall, c.Severity, c.SourceContainer, c.SourceDescription, c.SourceRoof, c.SourceStagnant, c.TimeOfDayDay, c.TimeOfDayEarly, c.TimeOfDayEvening, c.TimeOfDayNight, c.PublicID, c.ReporterAddress, c.ReporterEmail, c.ReporterName, c.ReporterPhone, c.Address, c.Location, c.Status, + c.ID, c.AdditionalInfo, c.Created, c.Duration, c.Email, c.InspectionType, c.SourceLocation, c.PreferredDateRange, c.PreferredTime, c.RequestCall, c.Severity, c.SourceContainer, c.SourceDescription, c.SourceRoof, c.SourceStagnant, c.TimeOfDayDay, c.TimeOfDayEarly, c.TimeOfDayEvening, c.TimeOfDayNight, c.PublicID, c.ReporterAddress, c.ReporterEmail, c.ReporterName, c.ReporterPhone, c.Address, c.Location, c.Status, c.OrganizationID, } } @@ -359,10 +379,14 @@ func (i publicreportNuisanceIndexes) AsSlice() []index { } } -type publicreportNuisanceForeignKeys struct{} +type publicreportNuisanceForeignKeys struct { + PublicreportNuisanceNuisanceOrganizationIDFkey foreignKey +} func (f publicreportNuisanceForeignKeys) AsSlice() []foreignKey { - return []foreignKey{} + return []foreignKey{ + f.PublicreportNuisanceNuisanceOrganizationIDFkey, + } } type publicreportNuisanceUniques struct { diff --git a/db/dbinfo/publicreport.pool.bob.go b/db/dbinfo/publicreport.pool.bob.go index 045b24d7..a749fdba 100644 --- a/db/dbinfo/publicreport.pool.bob.go +++ b/db/dbinfo/publicreport.pool.bob.go @@ -285,6 +285,15 @@ var PublicreportPools = Table[ Generated: false, AutoIncr: false, }, + OrganizationID: column{ + Name: "organization_id", + DBType: "integer", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, }, Indexes: publicreportPoolIndexes{ PoolPkey: index{ @@ -327,7 +336,17 @@ var PublicreportPools = Table[ Columns: []string{"id"}, Comment: "", }, - + ForeignKeys: publicreportPoolForeignKeys{ + PublicreportPoolPoolOrganizationIDFkey: foreignKey{ + constraint: constraint{ + Name: "publicreport.pool.pool_organization_id_fkey", + Columns: []string{"organization_id"}, + Comment: "", + }, + ForeignTable: "organization", + ForeignColumns: []string{"id"}, + }, + }, Uniques: publicreportPoolUniques{ PoolPublicIDKey: constraint{ Name: "pool_public_id_key", @@ -370,11 +389,12 @@ type publicreportPoolColumns struct { ReporterPhone column Subscribe column Status column + OrganizationID column } func (c publicreportPoolColumns) AsSlice() []column { return []column{ - c.ID, c.AccessComments, c.AccessGate, c.AccessFence, c.AccessLocked, c.AccessDog, c.AccessOther, c.Address, c.AddressCountry, c.AddressPostCode, c.AddressPlace, c.AddressStreet, c.AddressRegion, c.Comments, c.Created, c.H3cell, c.HasAdult, c.HasLarvae, c.HasPupae, c.Location, c.MapZoom, c.OwnerEmail, c.OwnerName, c.OwnerPhone, c.PublicID, c.ReporterEmail, c.ReporterName, c.ReporterPhone, c.Subscribe, c.Status, + c.ID, c.AccessComments, c.AccessGate, c.AccessFence, c.AccessLocked, c.AccessDog, c.AccessOther, c.Address, c.AddressCountry, c.AddressPostCode, c.AddressPlace, c.AddressStreet, c.AddressRegion, c.Comments, c.Created, c.H3cell, c.HasAdult, c.HasLarvae, c.HasPupae, c.Location, c.MapZoom, c.OwnerEmail, c.OwnerName, c.OwnerPhone, c.PublicID, c.ReporterEmail, c.ReporterName, c.ReporterPhone, c.Subscribe, c.Status, c.OrganizationID, } } @@ -389,10 +409,14 @@ func (i publicreportPoolIndexes) AsSlice() []index { } } -type publicreportPoolForeignKeys struct{} +type publicreportPoolForeignKeys struct { + PublicreportPoolPoolOrganizationIDFkey foreignKey +} func (f publicreportPoolForeignKeys) AsSlice() []foreignKey { - return []foreignKey{} + return []foreignKey{ + f.PublicreportPoolPoolOrganizationIDFkey, + } } type publicreportPoolUniques struct { diff --git a/db/dbinfo/publicreport.quick.bob.go b/db/dbinfo/publicreport.quick.bob.go index 77afb678..a986b5b8 100644 --- a/db/dbinfo/publicreport.quick.bob.go +++ b/db/dbinfo/publicreport.quick.bob.go @@ -105,6 +105,15 @@ var PublicreportQuicks = Table[ Generated: false, AutoIncr: false, }, + OrganizationID: column{ + Name: "organization_id", + DBType: "integer", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, }, Indexes: publicreportQuickIndexes{ QuickPkey: index{ @@ -147,7 +156,17 @@ var PublicreportQuicks = Table[ Columns: []string{"id"}, Comment: "", }, - + ForeignKeys: publicreportQuickForeignKeys{ + PublicreportQuickQuickOrganizationIDFkey: foreignKey{ + constraint: constraint{ + Name: "publicreport.quick.quick_organization_id_fkey", + Columns: []string{"organization_id"}, + Comment: "", + }, + ForeignTable: "organization", + ForeignColumns: []string{"id"}, + }, + }, Uniques: publicreportQuickUniques{ QuickPublicIDKey: constraint{ Name: "quick_public_id_key", @@ -160,21 +179,22 @@ var PublicreportQuicks = Table[ } type publicreportQuickColumns struct { - ID column - Created column - Comments column - Location column - H3cell column - PublicID column - ReporterEmail column - ReporterPhone column - Address column - Status column + ID column + Created column + Comments column + Location column + H3cell column + PublicID column + ReporterEmail column + ReporterPhone column + Address column + Status column + OrganizationID column } func (c publicreportQuickColumns) AsSlice() []column { return []column{ - c.ID, c.Created, c.Comments, c.Location, c.H3cell, c.PublicID, c.ReporterEmail, c.ReporterPhone, c.Address, c.Status, + c.ID, c.Created, c.Comments, c.Location, c.H3cell, c.PublicID, c.ReporterEmail, c.ReporterPhone, c.Address, c.Status, c.OrganizationID, } } @@ -189,10 +209,14 @@ func (i publicreportQuickIndexes) AsSlice() []index { } } -type publicreportQuickForeignKeys struct{} +type publicreportQuickForeignKeys struct { + PublicreportQuickQuickOrganizationIDFkey foreignKey +} func (f publicreportQuickForeignKeys) AsSlice() []foreignKey { - return []foreignKey{} + return []foreignKey{ + f.PublicreportQuickQuickOrganizationIDFkey, + } } type publicreportQuickUniques struct { diff --git a/db/factory/bobfactory_context.bob.go b/db/factory/bobfactory_context.bob.go index ac0e7e05..6c9c023b 100644 --- a/db/factory/bobfactory_context.bob.go +++ b/db/factory/bobfactory_context.bob.go @@ -240,6 +240,9 @@ var ( organizationRelNoteAudiosCtx = newContextual[bool]("note_audio.organization.note_audio.note_audio_organization_id_fkey") organizationRelNoteImagesCtx = newContextual[bool]("note_image.organization.note_image.note_image_organization_id_fkey") organizationRelImportDistrictGidDistrictCtx = newContextual[bool]("import.district.organization.organization.organization_import_district_gid_fkey") + organizationRelNuisancesCtx = newContextual[bool]("organization.publicreport.nuisance.publicreport.nuisance.nuisance_organization_id_fkey") + organizationRelPublicreportPoolCtx = newContextual[bool]("organization.publicreport.pool.publicreport.pool.pool_organization_id_fkey") + organizationRelQuicksCtx = newContextual[bool]("organization.publicreport.quick.publicreport.quick.quick_organization_id_fkey") organizationRelUserCtx = newContextual[bool]("organization.user_.user_.user__organization_id_fkey") // Relationship Contexts for publicreport.image @@ -254,9 +257,11 @@ var ( // Relationship Contexts for publicreport.nuisance publicreportNuisanceWithParentsCascadingCtx = newContextual[bool]("publicreportNuisanceWithParentsCascading") + publicreportNuisanceRelOrganizationCtx = newContextual[bool]("organization.publicreport.nuisance.publicreport.nuisance.nuisance_organization_id_fkey") // Relationship Contexts for publicreport.pool publicreportPoolWithParentsCascadingCtx = newContextual[bool]("publicreportPoolWithParentsCascading") + publicreportPoolRelOrganizationCtx = newContextual[bool]("organization.publicreport.pool.publicreport.pool.pool_organization_id_fkey") publicreportPoolRelImagesCtx = newContextual[bool]("publicreport.image.publicreport.pool.publicreport.pool_image.pool_image_image_id_fkeypublicreport.pool_image.pool_image_pool_id_fkey") // Relationship Contexts for publicreport.pool_image @@ -266,6 +271,7 @@ var ( // Relationship Contexts for publicreport.quick publicreportQuickWithParentsCascadingCtx = newContextual[bool]("publicreportQuickWithParentsCascading") + publicreportQuickRelOrganizationCtx = newContextual[bool]("organization.publicreport.quick.publicreport.quick.quick_organization_id_fkey") publicreportQuickRelImagesCtx = newContextual[bool]("publicreport.image.publicreport.quick.publicreport.quick_image.quick_image_image_id_fkeypublicreport.quick_image.quick_image_quick_id_fkey") // Relationship Contexts for publicreport.quick_image diff --git a/db/factory/bobfactory_main.bob.go b/db/factory/bobfactory_main.bob.go index e49d9b54..d1f5124a 100644 --- a/db/factory/bobfactory_main.bob.go +++ b/db/factory/bobfactory_main.bob.go @@ -2550,6 +2550,7 @@ func (f *Factory) FromExistingOrganization(m *models.Organization) *Organization o.FieldseekerURL = func() null.Val[string] { return m.FieldseekerURL } o.ImportDistrictGid = func() null.Val[int32] { return m.ImportDistrictGid } o.Website = func() null.Val[string] { return m.Website } + o.LogoUUID = func() null.Val[uuid.UUID] { return m.LogoUUID } ctx := context.Background() if len(m.R.Containerrelates) > 0 { @@ -2648,6 +2649,15 @@ func (f *Factory) FromExistingOrganization(m *models.Organization) *Organization if m.R.ImportDistrictGidDistrict != nil { OrganizationMods.WithExistingImportDistrictGidDistrict(m.R.ImportDistrictGidDistrict).Apply(ctx, o) } + if len(m.R.Nuisances) > 0 { + OrganizationMods.AddExistingNuisances(m.R.Nuisances...).Apply(ctx, o) + } + if len(m.R.PublicreportPool) > 0 { + OrganizationMods.AddExistingPublicreportPool(m.R.PublicreportPool...).Apply(ctx, o) + } + if len(m.R.Quicks) > 0 { + OrganizationMods.AddExistingQuicks(m.R.Quicks...).Apply(ctx, o) + } if len(m.R.User) > 0 { OrganizationMods.AddExistingUser(m.R.User...).Apply(ctx, o) } @@ -2775,6 +2785,12 @@ func (f *Factory) FromExistingPublicreportNuisance(m *models.PublicreportNuisanc o.Address = func() string { return m.Address } o.Location = func() null.Val[string] { return m.Location } o.Status = func() enums.PublicreportReportstatustype { return m.Status } + o.OrganizationID = func() null.Val[int32] { return m.OrganizationID } + + ctx := context.Background() + if m.R.Organization != nil { + PublicreportNuisanceMods.WithExistingOrganization(m.R.Organization).Apply(ctx, o) + } return o } @@ -2828,8 +2844,12 @@ func (f *Factory) FromExistingPublicreportPool(m *models.PublicreportPool) *Publ o.ReporterPhone = func() string { return m.ReporterPhone } o.Subscribe = func() bool { return m.Subscribe } o.Status = func() enums.PublicreportReportstatustype { return m.Status } + o.OrganizationID = func() null.Val[int32] { return m.OrganizationID } ctx := context.Background() + if m.R.Organization != nil { + PublicreportPoolMods.WithExistingOrganization(m.R.Organization).Apply(ctx, o) + } if len(m.R.Images) > 0 { PublicreportPoolMods.AddExistingImages(m.R.Images...).Apply(ctx, o) } @@ -2899,8 +2919,12 @@ func (f *Factory) FromExistingPublicreportQuick(m *models.PublicreportQuick) *Pu o.ReporterPhone = func() string { return m.ReporterPhone } o.Address = func() string { return m.Address } o.Status = func() enums.PublicreportReportstatustype { return m.Status } + o.OrganizationID = func() null.Val[int32] { return m.OrganizationID } ctx := context.Background() + if m.R.Organization != nil { + PublicreportQuickMods.WithExistingOrganization(m.R.Organization).Apply(ctx, o) + } if len(m.R.Images) > 0 { PublicreportQuickMods.AddExistingImages(m.R.Images...).Apply(ctx, o) } diff --git a/db/factory/organization.bob.go b/db/factory/organization.bob.go index 957d1c96..a30832a3 100644 --- a/db/factory/organization.bob.go +++ b/db/factory/organization.bob.go @@ -11,6 +11,7 @@ import ( "github.com/aarondl/opt/null" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" + "github.com/google/uuid" "github.com/jaswdr/faker/v2" "github.com/stephenafamo/bob" ) @@ -43,6 +44,7 @@ type OrganizationTemplate struct { FieldseekerURL func() null.Val[string] ImportDistrictGid func() null.Val[int32] Website func() null.Val[string] + LogoUUID func() null.Val[uuid.UUID] r organizationR f *Factory @@ -83,6 +85,9 @@ type organizationR struct { NoteAudios []*organizationRNoteAudiosR NoteImages []*organizationRNoteImagesR ImportDistrictGidDistrict *organizationRImportDistrictGidDistrictR + Nuisances []*organizationRNuisancesR + PublicreportPool []*organizationRPublicreportPoolR + Quicks []*organizationRQuicksR User []*organizationRUserR } @@ -213,6 +218,18 @@ type organizationRNoteImagesR struct { type organizationRImportDistrictGidDistrictR struct { o *ImportDistrictTemplate } +type organizationRNuisancesR struct { + number int + o *PublicreportNuisanceTemplate +} +type organizationRPublicreportPoolR struct { + number int + o *PublicreportPoolTemplate +} +type organizationRQuicksR struct { + number int + o *PublicreportQuickTemplate +} type organizationRUserR struct { number int o *UserTemplate @@ -638,6 +655,45 @@ func (t OrganizationTemplate) setModelRels(o *models.Organization) { o.R.ImportDistrictGidDistrict = rel } + if t.r.Nuisances != nil { + rel := models.PublicreportNuisanceSlice{} + for _, r := range t.r.Nuisances { + related := r.o.BuildMany(r.number) + for _, rel := range related { + rel.OrganizationID = null.From(o.ID) // h2 + rel.R.Organization = o + } + rel = append(rel, related...) + } + o.R.Nuisances = rel + } + + if t.r.PublicreportPool != nil { + rel := models.PublicreportPoolSlice{} + for _, r := range t.r.PublicreportPool { + related := r.o.BuildMany(r.number) + for _, rel := range related { + rel.OrganizationID = null.From(o.ID) // h2 + rel.R.Organization = o + } + rel = append(rel, related...) + } + o.R.PublicreportPool = rel + } + + if t.r.Quicks != nil { + rel := models.PublicreportQuickSlice{} + for _, r := range t.r.Quicks { + related := r.o.BuildMany(r.number) + for _, rel := range related { + rel.OrganizationID = null.From(o.ID) // h2 + rel.R.Organization = o + } + rel = append(rel, related...) + } + o.R.Quicks = rel + } + if t.r.User != nil { rel := models.UserSlice{} for _, r := range t.r.User { @@ -685,6 +741,10 @@ func (o OrganizationTemplate) BuildSetter() *models.OrganizationSetter { val := o.Website() m.Website = omitnull.FromNull(val) } + if o.LogoUUID != nil { + val := o.LogoUUID() + m.LogoUUID = omitnull.FromNull(val) + } return m } @@ -728,6 +788,9 @@ func (o OrganizationTemplate) Build() *models.Organization { if o.Website != nil { m.Website = o.Website() } + if o.LogoUUID != nil { + m.LogoUUID = o.LogoUUID() + } o.setModelRels(m) @@ -1399,6 +1462,66 @@ func (o *OrganizationTemplate) insertOptRels(ctx context.Context, exec bob.Execu } + isNuisancesDone, _ := organizationRelNuisancesCtx.Value(ctx) + if !isNuisancesDone && o.r.Nuisances != nil { + ctx = organizationRelNuisancesCtx.WithValue(ctx, true) + for _, r := range o.r.Nuisances { + if r.o.alreadyPersisted { + m.R.Nuisances = append(m.R.Nuisances, r.o.Build()) + } else { + rel32, err := r.o.CreateMany(ctx, exec, r.number) + if err != nil { + return err + } + + err = m.AttachNuisances(ctx, exec, rel32...) + if err != nil { + return err + } + } + } + } + + isPublicreportPoolDone, _ := organizationRelPublicreportPoolCtx.Value(ctx) + if !isPublicreportPoolDone && o.r.PublicreportPool != nil { + ctx = organizationRelPublicreportPoolCtx.WithValue(ctx, true) + for _, r := range o.r.PublicreportPool { + if r.o.alreadyPersisted { + m.R.PublicreportPool = append(m.R.PublicreportPool, r.o.Build()) + } else { + rel33, err := r.o.CreateMany(ctx, exec, r.number) + if err != nil { + return err + } + + err = m.AttachPublicreportPool(ctx, exec, rel33...) + if err != nil { + return err + } + } + } + } + + isQuicksDone, _ := organizationRelQuicksCtx.Value(ctx) + if !isQuicksDone && o.r.Quicks != nil { + ctx = organizationRelQuicksCtx.WithValue(ctx, true) + for _, r := range o.r.Quicks { + if r.o.alreadyPersisted { + m.R.Quicks = append(m.R.Quicks, r.o.Build()) + } else { + rel34, err := r.o.CreateMany(ctx, exec, r.number) + if err != nil { + return err + } + + err = m.AttachQuicks(ctx, exec, rel34...) + if err != nil { + return err + } + } + } + } + isUserDone, _ := organizationRelUserCtx.Value(ctx) if !isUserDone && o.r.User != nil { ctx = organizationRelUserCtx.WithValue(ctx, true) @@ -1406,12 +1529,12 @@ func (o *OrganizationTemplate) insertOptRels(ctx context.Context, exec bob.Execu if r.o.alreadyPersisted { m.R.User = append(m.R.User, r.o.Build()) } else { - rel32, err := r.o.CreateMany(ctx, exec, r.number) + rel35, err := r.o.CreateMany(ctx, exec, r.number) if err != nil { return err } - err = m.AttachUser(ctx, exec, rel32...) + err = m.AttachUser(ctx, exec, rel35...) if err != nil { return err } @@ -1518,6 +1641,7 @@ func (m organizationMods) RandomizeAllColumns(f *faker.Faker) OrganizationMod { OrganizationMods.RandomFieldseekerURL(f), OrganizationMods.RandomImportDistrictGid(f), OrganizationMods.RandomWebsite(f), + OrganizationMods.RandomLogoUUID(f), } } @@ -1848,6 +1972,59 @@ func (m organizationMods) RandomWebsiteNotNull(f *faker.Faker) OrganizationMod { }) } +// Set the model columns to this value +func (m organizationMods) LogoUUID(val null.Val[uuid.UUID]) OrganizationMod { + return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { + o.LogoUUID = func() null.Val[uuid.UUID] { return val } + }) +} + +// Set the Column from the function +func (m organizationMods) LogoUUIDFunc(f func() null.Val[uuid.UUID]) OrganizationMod { + return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { + o.LogoUUID = f + }) +} + +// Clear any values for the column +func (m organizationMods) UnsetLogoUUID() OrganizationMod { + return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { + o.LogoUUID = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is sometimes null +func (m organizationMods) RandomLogoUUID(f *faker.Faker) OrganizationMod { + return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { + o.LogoUUID = func() null.Val[uuid.UUID] { + if f == nil { + f = &defaultFaker + } + + val := random_uuid_UUID(f) + return null.From(val) + } + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is never null +func (m organizationMods) RandomLogoUUIDNotNull(f *faker.Faker) OrganizationMod { + return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { + o.LogoUUID = func() null.Val[uuid.UUID] { + if f == nil { + f = &defaultFaker + } + + val := random_uuid_UUID(f) + return null.From(val) + } + }) +} + func (m organizationMods) WithParentsCascading() OrganizationMod { return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { if isDone, _ := organizationWithParentsCascadingCtx.Value(ctx); isDone { @@ -3380,6 +3557,150 @@ func (m organizationMods) WithoutNoteImages() OrganizationMod { }) } +func (m organizationMods) WithNuisances(number int, related *PublicreportNuisanceTemplate) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.Nuisances = []*organizationRNuisancesR{{ + number: number, + o: related, + }} + }) +} + +func (m organizationMods) WithNewNuisances(number int, mods ...PublicreportNuisanceMod) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + related := o.f.NewPublicreportNuisanceWithContext(ctx, mods...) + m.WithNuisances(number, related).Apply(ctx, o) + }) +} + +func (m organizationMods) AddNuisances(number int, related *PublicreportNuisanceTemplate) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.Nuisances = append(o.r.Nuisances, &organizationRNuisancesR{ + number: number, + o: related, + }) + }) +} + +func (m organizationMods) AddNewNuisances(number int, mods ...PublicreportNuisanceMod) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + related := o.f.NewPublicreportNuisanceWithContext(ctx, mods...) + m.AddNuisances(number, related).Apply(ctx, o) + }) +} + +func (m organizationMods) AddExistingNuisances(existingModels ...*models.PublicreportNuisance) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + for _, em := range existingModels { + o.r.Nuisances = append(o.r.Nuisances, &organizationRNuisancesR{ + o: o.f.FromExistingPublicreportNuisance(em), + }) + } + }) +} + +func (m organizationMods) WithoutNuisances() OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.Nuisances = nil + }) +} + +func (m organizationMods) WithPublicreportPool(number int, related *PublicreportPoolTemplate) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.PublicreportPool = []*organizationRPublicreportPoolR{{ + number: number, + o: related, + }} + }) +} + +func (m organizationMods) WithNewPublicreportPool(number int, mods ...PublicreportPoolMod) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + related := o.f.NewPublicreportPoolWithContext(ctx, mods...) + m.WithPublicreportPool(number, related).Apply(ctx, o) + }) +} + +func (m organizationMods) AddPublicreportPool(number int, related *PublicreportPoolTemplate) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.PublicreportPool = append(o.r.PublicreportPool, &organizationRPublicreportPoolR{ + number: number, + o: related, + }) + }) +} + +func (m organizationMods) AddNewPublicreportPool(number int, mods ...PublicreportPoolMod) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + related := o.f.NewPublicreportPoolWithContext(ctx, mods...) + m.AddPublicreportPool(number, related).Apply(ctx, o) + }) +} + +func (m organizationMods) AddExistingPublicreportPool(existingModels ...*models.PublicreportPool) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + for _, em := range existingModels { + o.r.PublicreportPool = append(o.r.PublicreportPool, &organizationRPublicreportPoolR{ + o: o.f.FromExistingPublicreportPool(em), + }) + } + }) +} + +func (m organizationMods) WithoutPublicreportPool() OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.PublicreportPool = nil + }) +} + +func (m organizationMods) WithQuicks(number int, related *PublicreportQuickTemplate) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.Quicks = []*organizationRQuicksR{{ + number: number, + o: related, + }} + }) +} + +func (m organizationMods) WithNewQuicks(number int, mods ...PublicreportQuickMod) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + related := o.f.NewPublicreportQuickWithContext(ctx, mods...) + m.WithQuicks(number, related).Apply(ctx, o) + }) +} + +func (m organizationMods) AddQuicks(number int, related *PublicreportQuickTemplate) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.Quicks = append(o.r.Quicks, &organizationRQuicksR{ + number: number, + o: related, + }) + }) +} + +func (m organizationMods) AddNewQuicks(number int, mods ...PublicreportQuickMod) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + related := o.f.NewPublicreportQuickWithContext(ctx, mods...) + m.AddQuicks(number, related).Apply(ctx, o) + }) +} + +func (m organizationMods) AddExistingQuicks(existingModels ...*models.PublicreportQuick) OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + for _, em := range existingModels { + o.r.Quicks = append(o.r.Quicks, &organizationRQuicksR{ + o: o.f.FromExistingPublicreportQuick(em), + }) + } + }) +} + +func (m organizationMods) WithoutQuicks() OrganizationMod { + return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { + o.r.Quicks = nil + }) +} + func (m organizationMods) WithUser(number int, related *UserTemplate) OrganizationMod { return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) { o.r.User = []*organizationRUserR{{ diff --git a/db/factory/publicreport.nuisance.bob.go b/db/factory/publicreport.nuisance.bob.go index 3590e4e1..faccd615 100644 --- a/db/factory/publicreport.nuisance.bob.go +++ b/db/factory/publicreport.nuisance.bob.go @@ -65,12 +65,22 @@ type PublicreportNuisanceTemplate struct { Address func() string Location func() null.Val[string] Status func() enums.PublicreportReportstatustype + OrganizationID func() null.Val[int32] + r publicreportNuisanceR f *Factory alreadyPersisted bool } +type publicreportNuisanceR struct { + Organization *publicreportNuisanceROrganizationR +} + +type publicreportNuisanceROrganizationR struct { + o *OrganizationTemplate +} + // Apply mods to the PublicreportNuisanceTemplate func (o *PublicreportNuisanceTemplate) Apply(ctx context.Context, mods ...PublicreportNuisanceMod) { for _, mod := range mods { @@ -80,7 +90,14 @@ func (o *PublicreportNuisanceTemplate) Apply(ctx context.Context, mods ...Public // setModelRels creates and sets the relationships on *models.PublicreportNuisance // according to the relationships in the template. Nothing is inserted into the db -func (t PublicreportNuisanceTemplate) setModelRels(o *models.PublicreportNuisance) {} +func (t PublicreportNuisanceTemplate) setModelRels(o *models.PublicreportNuisance) { + if t.r.Organization != nil { + rel := t.r.Organization.o.Build() + rel.R.Nuisances = append(rel.R.Nuisances, o) + o.OrganizationID = null.From(rel.ID) // h2 + o.R.Organization = rel + } +} // BuildSetter returns an *models.PublicreportNuisanceSetter // this does nothing with the relationship templates @@ -195,6 +212,10 @@ func (o PublicreportNuisanceTemplate) BuildSetter() *models.PublicreportNuisance val := o.Status() m.Status = omit.From(val) } + if o.OrganizationID != nil { + val := o.OrganizationID() + m.OrganizationID = omitnull.FromNull(val) + } return m } @@ -298,6 +319,9 @@ func (o PublicreportNuisanceTemplate) Build() *models.PublicreportNuisance { if o.Status != nil { m.Status = o.Status() } + if o.OrganizationID != nil { + m.OrganizationID = o.OrganizationID() + } o.setModelRels(m) @@ -426,6 +450,25 @@ func ensureCreatablePublicreportNuisance(m *models.PublicreportNuisanceSetter) { func (o *PublicreportNuisanceTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.PublicreportNuisance) error { var err error + isOrganizationDone, _ := publicreportNuisanceRelOrganizationCtx.Value(ctx) + if !isOrganizationDone && o.r.Organization != nil { + ctx = publicreportNuisanceRelOrganizationCtx.WithValue(ctx, true) + if o.r.Organization.o.alreadyPersisted { + m.R.Organization = o.r.Organization.o.Build() + } else { + var rel0 *models.Organization + rel0, err = o.r.Organization.o.Create(ctx, exec) + if err != nil { + return err + } + err = m.AttachOrganization(ctx, exec, rel0) + if err != nil { + return err + } + } + + } + return err } @@ -545,6 +588,7 @@ func (m publicreportNuisanceMods) RandomizeAllColumns(f *faker.Faker) Publicrepo PublicreportNuisanceMods.RandomAddress(f), PublicreportNuisanceMods.RandomLocation(f), PublicreportNuisanceMods.RandomStatus(f), + PublicreportNuisanceMods.RandomOrganizationID(f), } } @@ -1407,11 +1451,99 @@ func (m publicreportNuisanceMods) RandomStatus(f *faker.Faker) PublicreportNuisa }) } +// Set the model columns to this value +func (m publicreportNuisanceMods) OrganizationID(val null.Val[int32]) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(_ context.Context, o *PublicreportNuisanceTemplate) { + o.OrganizationID = func() null.Val[int32] { return val } + }) +} + +// Set the Column from the function +func (m publicreportNuisanceMods) OrganizationIDFunc(f func() null.Val[int32]) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(_ context.Context, o *PublicreportNuisanceTemplate) { + o.OrganizationID = f + }) +} + +// Clear any values for the column +func (m publicreportNuisanceMods) UnsetOrganizationID() PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(_ context.Context, o *PublicreportNuisanceTemplate) { + o.OrganizationID = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is sometimes null +func (m publicreportNuisanceMods) RandomOrganizationID(f *faker.Faker) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(_ context.Context, o *PublicreportNuisanceTemplate) { + o.OrganizationID = func() null.Val[int32] { + if f == nil { + f = &defaultFaker + } + + val := random_int32(f) + return null.From(val) + } + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is never null +func (m publicreportNuisanceMods) RandomOrganizationIDNotNull(f *faker.Faker) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(_ context.Context, o *PublicreportNuisanceTemplate) { + o.OrganizationID = func() null.Val[int32] { + if f == nil { + f = &defaultFaker + } + + val := random_int32(f) + return null.From(val) + } + }) +} + func (m publicreportNuisanceMods) WithParentsCascading() PublicreportNuisanceMod { return PublicreportNuisanceModFunc(func(ctx context.Context, o *PublicreportNuisanceTemplate) { if isDone, _ := publicreportNuisanceWithParentsCascadingCtx.Value(ctx); isDone { return } ctx = publicreportNuisanceWithParentsCascadingCtx.WithValue(ctx, true) + { + + related := o.f.NewOrganizationWithContext(ctx, OrganizationMods.WithParentsCascading()) + m.WithOrganization(related).Apply(ctx, o) + } + }) +} + +func (m publicreportNuisanceMods) WithOrganization(rel *OrganizationTemplate) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(ctx context.Context, o *PublicreportNuisanceTemplate) { + o.r.Organization = &publicreportNuisanceROrganizationR{ + o: rel, + } + }) +} + +func (m publicreportNuisanceMods) WithNewOrganization(mods ...OrganizationMod) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(ctx context.Context, o *PublicreportNuisanceTemplate) { + related := o.f.NewOrganizationWithContext(ctx, mods...) + + m.WithOrganization(related).Apply(ctx, o) + }) +} + +func (m publicreportNuisanceMods) WithExistingOrganization(em *models.Organization) PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(ctx context.Context, o *PublicreportNuisanceTemplate) { + o.r.Organization = &publicreportNuisanceROrganizationR{ + o: o.f.FromExistingOrganization(em), + } + }) +} + +func (m publicreportNuisanceMods) WithoutOrganization() PublicreportNuisanceMod { + return PublicreportNuisanceModFunc(func(ctx context.Context, o *PublicreportNuisanceTemplate) { + o.r.Organization = nil }) } diff --git a/db/factory/publicreport.pool.bob.go b/db/factory/publicreport.pool.bob.go index 72b2f50e..103060a5 100644 --- a/db/factory/publicreport.pool.bob.go +++ b/db/factory/publicreport.pool.bob.go @@ -68,6 +68,7 @@ type PublicreportPoolTemplate struct { ReporterPhone func() string Subscribe func() bool Status func() enums.PublicreportReportstatustype + OrganizationID func() null.Val[int32] r publicreportPoolR f *Factory @@ -76,9 +77,13 @@ type PublicreportPoolTemplate struct { } type publicreportPoolR struct { - Images []*publicreportPoolRImagesR + Organization *publicreportPoolROrganizationR + Images []*publicreportPoolRImagesR } +type publicreportPoolROrganizationR struct { + o *OrganizationTemplate +} type publicreportPoolRImagesR struct { number int o *PublicreportImageTemplate @@ -94,6 +99,13 @@ func (o *PublicreportPoolTemplate) Apply(ctx context.Context, mods ...Publicrepo // setModelRels creates and sets the relationships on *models.PublicreportPool // according to the relationships in the template. Nothing is inserted into the db func (t PublicreportPoolTemplate) setModelRels(o *models.PublicreportPool) { + if t.r.Organization != nil { + rel := t.r.Organization.o.Build() + rel.R.PublicreportPool = append(rel.R.PublicreportPool, o) + o.OrganizationID = null.From(rel.ID) // h2 + o.R.Organization = rel + } + if t.r.Images != nil { rel := models.PublicreportImageSlice{} for _, r := range t.r.Images { @@ -232,6 +244,10 @@ func (o PublicreportPoolTemplate) BuildSetter() *models.PublicreportPoolSetter { val := o.Status() m.Status = omit.From(val) } + if o.OrganizationID != nil { + val := o.OrganizationID() + m.OrganizationID = omitnull.FromNull(val) + } return m } @@ -344,6 +360,9 @@ func (o PublicreportPoolTemplate) Build() *models.PublicreportPool { if o.Status != nil { m.Status = o.Status() } + if o.OrganizationID != nil { + m.OrganizationID = o.OrganizationID() + } o.setModelRels(m) @@ -480,6 +499,25 @@ func ensureCreatablePublicreportPool(m *models.PublicreportPoolSetter) { func (o *PublicreportPoolTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.PublicreportPool) error { var err error + isOrganizationDone, _ := publicreportPoolRelOrganizationCtx.Value(ctx) + if !isOrganizationDone && o.r.Organization != nil { + ctx = publicreportPoolRelOrganizationCtx.WithValue(ctx, true) + if o.r.Organization.o.alreadyPersisted { + m.R.Organization = o.r.Organization.o.Build() + } else { + var rel0 *models.Organization + rel0, err = o.r.Organization.o.Create(ctx, exec) + if err != nil { + return err + } + err = m.AttachOrganization(ctx, exec, rel0) + if err != nil { + return err + } + } + + } + isImagesDone, _ := publicreportPoolRelImagesCtx.Value(ctx) if !isImagesDone && o.r.Images != nil { ctx = publicreportPoolRelImagesCtx.WithValue(ctx, true) @@ -487,12 +525,12 @@ func (o *PublicreportPoolTemplate) insertOptRels(ctx context.Context, exec bob.E if r.o.alreadyPersisted { m.R.Images = append(m.R.Images, r.o.Build()) } else { - rel0, err := r.o.CreateMany(ctx, exec, r.number) + rel1, err := r.o.CreateMany(ctx, exec, r.number) if err != nil { return err } - err = m.AttachImages(ctx, exec, rel0...) + err = m.AttachImages(ctx, exec, rel1...) if err != nil { return err } @@ -622,6 +660,7 @@ func (m publicreportPoolMods) RandomizeAllColumns(f *faker.Faker) PublicreportPo PublicreportPoolMods.RandomReporterPhone(f), PublicreportPoolMods.RandomSubscribe(f), PublicreportPoolMods.RandomStatus(f), + PublicreportPoolMods.RandomOrganizationID(f), } } @@ -1599,12 +1638,100 @@ func (m publicreportPoolMods) RandomStatus(f *faker.Faker) PublicreportPoolMod { }) } +// Set the model columns to this value +func (m publicreportPoolMods) OrganizationID(val null.Val[int32]) PublicreportPoolMod { + return PublicreportPoolModFunc(func(_ context.Context, o *PublicreportPoolTemplate) { + o.OrganizationID = func() null.Val[int32] { return val } + }) +} + +// Set the Column from the function +func (m publicreportPoolMods) OrganizationIDFunc(f func() null.Val[int32]) PublicreportPoolMod { + return PublicreportPoolModFunc(func(_ context.Context, o *PublicreportPoolTemplate) { + o.OrganizationID = f + }) +} + +// Clear any values for the column +func (m publicreportPoolMods) UnsetOrganizationID() PublicreportPoolMod { + return PublicreportPoolModFunc(func(_ context.Context, o *PublicreportPoolTemplate) { + o.OrganizationID = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is sometimes null +func (m publicreportPoolMods) RandomOrganizationID(f *faker.Faker) PublicreportPoolMod { + return PublicreportPoolModFunc(func(_ context.Context, o *PublicreportPoolTemplate) { + o.OrganizationID = func() null.Val[int32] { + if f == nil { + f = &defaultFaker + } + + val := random_int32(f) + return null.From(val) + } + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is never null +func (m publicreportPoolMods) RandomOrganizationIDNotNull(f *faker.Faker) PublicreportPoolMod { + return PublicreportPoolModFunc(func(_ context.Context, o *PublicreportPoolTemplate) { + o.OrganizationID = func() null.Val[int32] { + if f == nil { + f = &defaultFaker + } + + val := random_int32(f) + return null.From(val) + } + }) +} + func (m publicreportPoolMods) WithParentsCascading() PublicreportPoolMod { return PublicreportPoolModFunc(func(ctx context.Context, o *PublicreportPoolTemplate) { if isDone, _ := publicreportPoolWithParentsCascadingCtx.Value(ctx); isDone { return } ctx = publicreportPoolWithParentsCascadingCtx.WithValue(ctx, true) + { + + related := o.f.NewOrganizationWithContext(ctx, OrganizationMods.WithParentsCascading()) + m.WithOrganization(related).Apply(ctx, o) + } + }) +} + +func (m publicreportPoolMods) WithOrganization(rel *OrganizationTemplate) PublicreportPoolMod { + return PublicreportPoolModFunc(func(ctx context.Context, o *PublicreportPoolTemplate) { + o.r.Organization = &publicreportPoolROrganizationR{ + o: rel, + } + }) +} + +func (m publicreportPoolMods) WithNewOrganization(mods ...OrganizationMod) PublicreportPoolMod { + return PublicreportPoolModFunc(func(ctx context.Context, o *PublicreportPoolTemplate) { + related := o.f.NewOrganizationWithContext(ctx, mods...) + + m.WithOrganization(related).Apply(ctx, o) + }) +} + +func (m publicreportPoolMods) WithExistingOrganization(em *models.Organization) PublicreportPoolMod { + return PublicreportPoolModFunc(func(ctx context.Context, o *PublicreportPoolTemplate) { + o.r.Organization = &publicreportPoolROrganizationR{ + o: o.f.FromExistingOrganization(em), + } + }) +} + +func (m publicreportPoolMods) WithoutOrganization() PublicreportPoolMod { + return PublicreportPoolModFunc(func(ctx context.Context, o *PublicreportPoolTemplate) { + o.r.Organization = nil }) } diff --git a/db/factory/publicreport.quick.bob.go b/db/factory/publicreport.quick.bob.go index 9f372444..30ff2b16 100644 --- a/db/factory/publicreport.quick.bob.go +++ b/db/factory/publicreport.quick.bob.go @@ -38,16 +38,17 @@ func (mods PublicreportQuickModSlice) Apply(ctx context.Context, n *Publicreport // PublicreportQuickTemplate is an object representing the database table. // all columns are optional and should be set by mods type PublicreportQuickTemplate struct { - ID func() int32 - Created func() time.Time - Comments func() string - Location func() null.Val[string] - H3cell func() null.Val[string] - PublicID func() string - ReporterEmail func() string - ReporterPhone func() string - Address func() string - Status func() enums.PublicreportReportstatustype + ID func() int32 + Created func() time.Time + Comments func() string + Location func() null.Val[string] + H3cell func() null.Val[string] + PublicID func() string + ReporterEmail func() string + ReporterPhone func() string + Address func() string + Status func() enums.PublicreportReportstatustype + OrganizationID func() null.Val[int32] r publicreportQuickR f *Factory @@ -56,9 +57,13 @@ type PublicreportQuickTemplate struct { } type publicreportQuickR struct { - Images []*publicreportQuickRImagesR + Organization *publicreportQuickROrganizationR + Images []*publicreportQuickRImagesR } +type publicreportQuickROrganizationR struct { + o *OrganizationTemplate +} type publicreportQuickRImagesR struct { number int o *PublicreportImageTemplate @@ -74,6 +79,13 @@ func (o *PublicreportQuickTemplate) Apply(ctx context.Context, mods ...Publicrep // setModelRels creates and sets the relationships on *models.PublicreportQuick // according to the relationships in the template. Nothing is inserted into the db func (t PublicreportQuickTemplate) setModelRels(o *models.PublicreportQuick) { + if t.r.Organization != nil { + rel := t.r.Organization.o.Build() + rel.R.Quicks = append(rel.R.Quicks, o) + o.OrganizationID = null.From(rel.ID) // h2 + o.R.Organization = rel + } + if t.r.Images != nil { rel := models.PublicreportImageSlice{} for _, r := range t.r.Images { @@ -132,6 +144,10 @@ func (o PublicreportQuickTemplate) BuildSetter() *models.PublicreportQuickSetter val := o.Status() m.Status = omit.From(val) } + if o.OrganizationID != nil { + val := o.OrganizationID() + m.OrganizationID = omitnull.FromNull(val) + } return m } @@ -184,6 +200,9 @@ func (o PublicreportQuickTemplate) Build() *models.PublicreportQuick { if o.Status != nil { m.Status = o.Status() } + if o.OrganizationID != nil { + m.OrganizationID = o.OrganizationID() + } o.setModelRels(m) @@ -240,6 +259,25 @@ func ensureCreatablePublicreportQuick(m *models.PublicreportQuickSetter) { func (o *PublicreportQuickTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.PublicreportQuick) error { var err error + isOrganizationDone, _ := publicreportQuickRelOrganizationCtx.Value(ctx) + if !isOrganizationDone && o.r.Organization != nil { + ctx = publicreportQuickRelOrganizationCtx.WithValue(ctx, true) + if o.r.Organization.o.alreadyPersisted { + m.R.Organization = o.r.Organization.o.Build() + } else { + var rel0 *models.Organization + rel0, err = o.r.Organization.o.Create(ctx, exec) + if err != nil { + return err + } + err = m.AttachOrganization(ctx, exec, rel0) + if err != nil { + return err + } + } + + } + isImagesDone, _ := publicreportQuickRelImagesCtx.Value(ctx) if !isImagesDone && o.r.Images != nil { ctx = publicreportQuickRelImagesCtx.WithValue(ctx, true) @@ -247,12 +285,12 @@ func (o *PublicreportQuickTemplate) insertOptRels(ctx context.Context, exec bob. if r.o.alreadyPersisted { m.R.Images = append(m.R.Images, r.o.Build()) } else { - rel0, err := r.o.CreateMany(ctx, exec, r.number) + rel1, err := r.o.CreateMany(ctx, exec, r.number) if err != nil { return err } - err = m.AttachImages(ctx, exec, rel0...) + err = m.AttachImages(ctx, exec, rel1...) if err != nil { return err } @@ -362,6 +400,7 @@ func (m publicreportQuickMods) RandomizeAllColumns(f *faker.Faker) PublicreportQ PublicreportQuickMods.RandomReporterPhone(f), PublicreportQuickMods.RandomAddress(f), PublicreportQuickMods.RandomStatus(f), + PublicreportQuickMods.RandomOrganizationID(f), } } @@ -719,12 +758,100 @@ func (m publicreportQuickMods) RandomStatus(f *faker.Faker) PublicreportQuickMod }) } +// Set the model columns to this value +func (m publicreportQuickMods) OrganizationID(val null.Val[int32]) PublicreportQuickMod { + return PublicreportQuickModFunc(func(_ context.Context, o *PublicreportQuickTemplate) { + o.OrganizationID = func() null.Val[int32] { return val } + }) +} + +// Set the Column from the function +func (m publicreportQuickMods) OrganizationIDFunc(f func() null.Val[int32]) PublicreportQuickMod { + return PublicreportQuickModFunc(func(_ context.Context, o *PublicreportQuickTemplate) { + o.OrganizationID = f + }) +} + +// Clear any values for the column +func (m publicreportQuickMods) UnsetOrganizationID() PublicreportQuickMod { + return PublicreportQuickModFunc(func(_ context.Context, o *PublicreportQuickTemplate) { + o.OrganizationID = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is sometimes null +func (m publicreportQuickMods) RandomOrganizationID(f *faker.Faker) PublicreportQuickMod { + return PublicreportQuickModFunc(func(_ context.Context, o *PublicreportQuickTemplate) { + o.OrganizationID = func() null.Val[int32] { + if f == nil { + f = &defaultFaker + } + + val := random_int32(f) + return null.From(val) + } + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is never null +func (m publicreportQuickMods) RandomOrganizationIDNotNull(f *faker.Faker) PublicreportQuickMod { + return PublicreportQuickModFunc(func(_ context.Context, o *PublicreportQuickTemplate) { + o.OrganizationID = func() null.Val[int32] { + if f == nil { + f = &defaultFaker + } + + val := random_int32(f) + return null.From(val) + } + }) +} + func (m publicreportQuickMods) WithParentsCascading() PublicreportQuickMod { return PublicreportQuickModFunc(func(ctx context.Context, o *PublicreportQuickTemplate) { if isDone, _ := publicreportQuickWithParentsCascadingCtx.Value(ctx); isDone { return } ctx = publicreportQuickWithParentsCascadingCtx.WithValue(ctx, true) + { + + related := o.f.NewOrganizationWithContext(ctx, OrganizationMods.WithParentsCascading()) + m.WithOrganization(related).Apply(ctx, o) + } + }) +} + +func (m publicreportQuickMods) WithOrganization(rel *OrganizationTemplate) PublicreportQuickMod { + return PublicreportQuickModFunc(func(ctx context.Context, o *PublicreportQuickTemplate) { + o.r.Organization = &publicreportQuickROrganizationR{ + o: rel, + } + }) +} + +func (m publicreportQuickMods) WithNewOrganization(mods ...OrganizationMod) PublicreportQuickMod { + return PublicreportQuickModFunc(func(ctx context.Context, o *PublicreportQuickTemplate) { + related := o.f.NewOrganizationWithContext(ctx, mods...) + + m.WithOrganization(related).Apply(ctx, o) + }) +} + +func (m publicreportQuickMods) WithExistingOrganization(em *models.Organization) PublicreportQuickMod { + return PublicreportQuickModFunc(func(ctx context.Context, o *PublicreportQuickTemplate) { + o.r.Organization = &publicreportQuickROrganizationR{ + o: o.f.FromExistingOrganization(em), + } + }) +} + +func (m publicreportQuickMods) WithoutOrganization() PublicreportQuickMod { + return PublicreportQuickModFunc(func(ctx context.Context, o *PublicreportQuickTemplate) { + o.r.Organization = nil }) } diff --git a/db/migrations/00037_publicreport_district.sql b/db/migrations/00037_publicreport_district.sql new file mode 100644 index 00000000..c351abd4 --- /dev/null +++ b/db/migrations/00037_publicreport_district.sql @@ -0,0 +1,8 @@ +-- +goose Up +ALTER TABLE publicreport.quick ADD COLUMN organization_id INTEGER REFERENCES "public"."organization"(id); +ALTER TABLE publicreport.pool ADD COLUMN organization_id INTEGER REFERENCES "public"."organization"(id); +ALTER TABLE publicreport.nuisance ADD COLUMN organization_id INTEGER REFERENCES "public"."organization"(id); +-- +goose Down +ALTER TABLE publicreport.nuisance DROP COLUMN organization_id; +ALTER TABLE publicreport.pool DROP COLUMN organization_id; +ALTER TABLE publicreport.quick DROP COLUMN organization_id; diff --git a/db/migrations/00038_organization_logo.sql b/db/migrations/00038_organization_logo.sql new file mode 100644 index 00000000..8da51a4a --- /dev/null +++ b/db/migrations/00038_organization_logo.sql @@ -0,0 +1,2 @@ +-- +goose UP +ALTER TABLE public.organization ADD COLUMN logo_uuid UUID; diff --git a/db/models/bob_joins.bob.go b/db/models/bob_joins.bob.go index e3a3c0af..4455b426 100644 --- a/db/models/bob_joins.bob.go +++ b/db/models/bob_joins.bob.go @@ -79,6 +79,7 @@ type joins[Q dialect.Joinable] struct { Organizations joinSet[organizationJoins[Q]] PublicreportImages joinSet[publicreportImageJoins[Q]] PublicreportImageExifs joinSet[publicreportImageExifJoins[Q]] + PublicreportNuisances joinSet[publicreportNuisanceJoins[Q]] PublicreportPools joinSet[publicreportPoolJoins[Q]] PublicreportPoolImages joinSet[publicreportPoolImageJoins[Q]] PublicreportQuicks joinSet[publicreportQuickJoins[Q]] @@ -143,6 +144,7 @@ func getJoins[Q dialect.Joinable]() joins[Q] { Organizations: buildJoinSet[organizationJoins[Q]](Organizations.Columns, buildOrganizationJoins), PublicreportImages: buildJoinSet[publicreportImageJoins[Q]](PublicreportImages.Columns, buildPublicreportImageJoins), PublicreportImageExifs: buildJoinSet[publicreportImageExifJoins[Q]](PublicreportImageExifs.Columns, buildPublicreportImageExifJoins), + PublicreportNuisances: buildJoinSet[publicreportNuisanceJoins[Q]](PublicreportNuisances.Columns, buildPublicreportNuisanceJoins), PublicreportPools: buildJoinSet[publicreportPoolJoins[Q]](PublicreportPools.Columns, buildPublicreportPoolJoins), PublicreportPoolImages: buildJoinSet[publicreportPoolImageJoins[Q]](PublicreportPoolImages.Columns, buildPublicreportPoolImageJoins), PublicreportQuicks: buildJoinSet[publicreportQuickJoins[Q]](PublicreportQuicks.Columns, buildPublicreportQuickJoins), diff --git a/db/models/bob_loaders.bob.go b/db/models/bob_loaders.bob.go index 405ee6d6..1b3475de 100644 --- a/db/models/bob_loaders.bob.go +++ b/db/models/bob_loaders.bob.go @@ -64,6 +64,7 @@ type preloaders struct { Organization organizationPreloader PublicreportImage publicreportImagePreloader PublicreportImageExif publicreportImageExifPreloader + PublicreportNuisance publicreportNuisancePreloader PublicreportPool publicreportPoolPreloader PublicreportPoolImage publicreportPoolImagePreloader PublicreportQuick publicreportQuickPreloader @@ -120,6 +121,7 @@ func getPreloaders() preloaders { Organization: buildOrganizationPreloader(), PublicreportImage: buildPublicreportImagePreloader(), PublicreportImageExif: buildPublicreportImageExifPreloader(), + PublicreportNuisance: buildPublicreportNuisancePreloader(), PublicreportPool: buildPublicreportPoolPreloader(), PublicreportPoolImage: buildPublicreportPoolImagePreloader(), PublicreportQuick: buildPublicreportQuickPreloader(), @@ -182,6 +184,7 @@ type thenLoaders[Q orm.Loadable] struct { Organization organizationThenLoader[Q] PublicreportImage publicreportImageThenLoader[Q] PublicreportImageExif publicreportImageExifThenLoader[Q] + PublicreportNuisance publicreportNuisanceThenLoader[Q] PublicreportPool publicreportPoolThenLoader[Q] PublicreportPoolImage publicreportPoolImageThenLoader[Q] PublicreportQuick publicreportQuickThenLoader[Q] @@ -238,6 +241,7 @@ func getThenLoaders[Q orm.Loadable]() thenLoaders[Q] { Organization: buildOrganizationThenLoader[Q](), PublicreportImage: buildPublicreportImageThenLoader[Q](), PublicreportImageExif: buildPublicreportImageExifThenLoader[Q](), + PublicreportNuisance: buildPublicreportNuisanceThenLoader[Q](), PublicreportPool: buildPublicreportPoolThenLoader[Q](), PublicreportPoolImage: buildPublicreportPoolImageThenLoader[Q](), PublicreportQuick: buildPublicreportQuickThenLoader[Q](), diff --git a/db/models/organization.bob.go b/db/models/organization.bob.go index c735bef2..af84e294 100644 --- a/db/models/organization.bob.go +++ b/db/models/organization.bob.go @@ -11,6 +11,7 @@ import ( "github.com/aarondl/opt/null" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" + "github.com/google/uuid" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/psql" "github.com/stephenafamo/bob/dialect/psql/dialect" @@ -25,13 +26,14 @@ import ( // Organization is an object representing the database table. type Organization struct { - ID int32 `db:"id,pk" ` - Name string `db:"name" ` - ArcgisID null.Val[string] `db:"arcgis_id" ` - ArcgisName null.Val[string] `db:"arcgis_name" ` - FieldseekerURL null.Val[string] `db:"fieldseeker_url" ` - ImportDistrictGid null.Val[int32] `db:"import_district_gid" ` - Website null.Val[string] `db:"website" ` + ID int32 `db:"id,pk" ` + Name string `db:"name" ` + ArcgisID null.Val[string] `db:"arcgis_id" ` + ArcgisName null.Val[string] `db:"arcgis_name" ` + FieldseekerURL null.Val[string] `db:"fieldseeker_url" ` + ImportDistrictGid null.Val[int32] `db:"import_district_gid" ` + Website null.Val[string] `db:"website" ` + LogoUUID null.Val[uuid.UUID] `db:"logo_uuid" ` R organizationR `db:"-" ` @@ -82,13 +84,16 @@ type organizationR struct { NoteAudios NoteAudioSlice // note_audio.note_audio_organization_id_fkey NoteImages NoteImageSlice // note_image.note_image_organization_id_fkey ImportDistrictGidDistrict *ImportDistrict // organization.organization_import_district_gid_fkey + Nuisances PublicreportNuisanceSlice // publicreport.nuisance.nuisance_organization_id_fkey + PublicreportPool PublicreportPoolSlice // publicreport.pool.pool_organization_id_fkey + Quicks PublicreportQuickSlice // publicreport.quick.quick_organization_id_fkey User UserSlice // user_.user__organization_id_fkey } func buildOrganizationColumns(alias string) organizationColumns { return organizationColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "name", "arcgis_id", "arcgis_name", "fieldseeker_url", "import_district_gid", "website", + "id", "name", "arcgis_id", "arcgis_name", "fieldseeker_url", "import_district_gid", "website", "logo_uuid", ).WithParent("organization"), tableAlias: alias, ID: psql.Quote(alias, "id"), @@ -98,6 +103,7 @@ func buildOrganizationColumns(alias string) organizationColumns { FieldseekerURL: psql.Quote(alias, "fieldseeker_url"), ImportDistrictGid: psql.Quote(alias, "import_district_gid"), Website: psql.Quote(alias, "website"), + LogoUUID: psql.Quote(alias, "logo_uuid"), } } @@ -111,6 +117,7 @@ type organizationColumns struct { FieldseekerURL psql.Expression ImportDistrictGid psql.Expression Website psql.Expression + LogoUUID psql.Expression } func (c organizationColumns) Alias() string { @@ -125,17 +132,18 @@ func (organizationColumns) AliasedAs(alias string) organizationColumns { // All values are optional, and do not have to be set // Generated columns are not included type OrganizationSetter struct { - ID omit.Val[int32] `db:"id,pk" ` - Name omit.Val[string] `db:"name" ` - ArcgisID omitnull.Val[string] `db:"arcgis_id" ` - ArcgisName omitnull.Val[string] `db:"arcgis_name" ` - FieldseekerURL omitnull.Val[string] `db:"fieldseeker_url" ` - ImportDistrictGid omitnull.Val[int32] `db:"import_district_gid" ` - Website omitnull.Val[string] `db:"website" ` + ID omit.Val[int32] `db:"id,pk" ` + Name omit.Val[string] `db:"name" ` + ArcgisID omitnull.Val[string] `db:"arcgis_id" ` + ArcgisName omitnull.Val[string] `db:"arcgis_name" ` + FieldseekerURL omitnull.Val[string] `db:"fieldseeker_url" ` + ImportDistrictGid omitnull.Val[int32] `db:"import_district_gid" ` + Website omitnull.Val[string] `db:"website" ` + LogoUUID omitnull.Val[uuid.UUID] `db:"logo_uuid" ` } func (s OrganizationSetter) SetColumns() []string { - vals := make([]string, 0, 7) + vals := make([]string, 0, 8) if s.ID.IsValue() { vals = append(vals, "id") } @@ -157,6 +165,9 @@ func (s OrganizationSetter) SetColumns() []string { if !s.Website.IsUnset() { vals = append(vals, "website") } + if !s.LogoUUID.IsUnset() { + vals = append(vals, "logo_uuid") + } return vals } @@ -182,6 +193,9 @@ func (s OrganizationSetter) Overwrite(t *Organization) { if !s.Website.IsUnset() { t.Website = s.Website.MustGetNull() } + if !s.LogoUUID.IsUnset() { + t.LogoUUID = s.LogoUUID.MustGetNull() + } } func (s *OrganizationSetter) Apply(q *dialect.InsertQuery) { @@ -190,7 +204,7 @@ func (s *OrganizationSetter) 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, 7) + vals := make([]bob.Expression, 8) if s.ID.IsValue() { vals[0] = psql.Arg(s.ID.MustGet()) } else { @@ -233,6 +247,12 @@ func (s *OrganizationSetter) Apply(q *dialect.InsertQuery) { vals[6] = psql.Raw("DEFAULT") } + if !s.LogoUUID.IsUnset() { + vals[7] = psql.Arg(s.LogoUUID.MustGetNull()) + } else { + vals[7] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -242,7 +262,7 @@ func (s OrganizationSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s OrganizationSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 7) + exprs := make([]bob.Expression, 0, 8) if s.ID.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -293,6 +313,13 @@ func (s OrganizationSetter) Expressions(prefix ...string) []bob.Expression { }}) } + if !s.LogoUUID.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "logo_uuid")...), + psql.Arg(s.LogoUUID), + }}) + } + return exprs } @@ -1287,6 +1314,78 @@ func (os OrganizationSlice) ImportDistrictGidDistrict(mods ...bob.Mod[*dialect.S )...) } +// Nuisances starts a query for related objects on publicreport.nuisance +func (o *Organization) Nuisances(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportNuisancesQuery { + return PublicreportNuisances.Query(append(mods, + sm.Where(PublicreportNuisances.Columns.OrganizationID.EQ(psql.Arg(o.ID))), + )...) +} + +func (os OrganizationSlice) Nuisances(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportNuisancesQuery { + 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 PublicreportNuisances.Query(append(mods, + sm.Where(psql.Group(PublicreportNuisances.Columns.OrganizationID).OP("IN", PKArgExpr)), + )...) +} + +// PublicreportPool starts a query for related objects on publicreport.pool +func (o *Organization) PublicreportPool(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportPoolsQuery { + return PublicreportPools.Query(append(mods, + sm.Where(PublicreportPools.Columns.OrganizationID.EQ(psql.Arg(o.ID))), + )...) +} + +func (os OrganizationSlice) PublicreportPool(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportPoolsQuery { + 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 PublicreportPools.Query(append(mods, + sm.Where(psql.Group(PublicreportPools.Columns.OrganizationID).OP("IN", PKArgExpr)), + )...) +} + +// Quicks starts a query for related objects on publicreport.quick +func (o *Organization) Quicks(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportQuicksQuery { + return PublicreportQuicks.Query(append(mods, + sm.Where(PublicreportQuicks.Columns.OrganizationID.EQ(psql.Arg(o.ID))), + )...) +} + +func (os OrganizationSlice) Quicks(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportQuicksQuery { + 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 PublicreportQuicks.Query(append(mods, + sm.Where(psql.Group(PublicreportQuicks.Columns.OrganizationID).OP("IN", PKArgExpr)), + )...) +} + // User starts a query for related objects on user_ func (o *Organization) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { return Users.Query(append(mods, @@ -3467,6 +3566,210 @@ func (organization0 *Organization) AttachImportDistrictGidDistrict(ctx context.C return nil } +func insertOrganizationNuisances0(ctx context.Context, exec bob.Executor, publicreportNuisances1 []*PublicreportNuisanceSetter, organization0 *Organization) (PublicreportNuisanceSlice, error) { + for i := range publicreportNuisances1 { + publicreportNuisances1[i].OrganizationID = omitnull.From(organization0.ID) + } + + ret, err := PublicreportNuisances.Insert(bob.ToMods(publicreportNuisances1...)).All(ctx, exec) + if err != nil { + return ret, fmt.Errorf("insertOrganizationNuisances0: %w", err) + } + + return ret, nil +} + +func attachOrganizationNuisances0(ctx context.Context, exec bob.Executor, count int, publicreportNuisances1 PublicreportNuisanceSlice, organization0 *Organization) (PublicreportNuisanceSlice, error) { + setter := &PublicreportNuisanceSetter{ + OrganizationID: omitnull.From(organization0.ID), + } + + err := publicreportNuisances1.UpdateAll(ctx, exec, *setter) + if err != nil { + return nil, fmt.Errorf("attachOrganizationNuisances0: %w", err) + } + + return publicreportNuisances1, nil +} + +func (organization0 *Organization) InsertNuisances(ctx context.Context, exec bob.Executor, related ...*PublicreportNuisanceSetter) error { + if len(related) == 0 { + return nil + } + + var err error + + publicreportNuisances1, err := insertOrganizationNuisances0(ctx, exec, related, organization0) + if err != nil { + return err + } + + organization0.R.Nuisances = append(organization0.R.Nuisances, publicreportNuisances1...) + + for _, rel := range publicreportNuisances1 { + rel.R.Organization = organization0 + } + return nil +} + +func (organization0 *Organization) AttachNuisances(ctx context.Context, exec bob.Executor, related ...*PublicreportNuisance) error { + if len(related) == 0 { + return nil + } + + var err error + publicreportNuisances1 := PublicreportNuisanceSlice(related) + + _, err = attachOrganizationNuisances0(ctx, exec, len(related), publicreportNuisances1, organization0) + if err != nil { + return err + } + + organization0.R.Nuisances = append(organization0.R.Nuisances, publicreportNuisances1...) + + for _, rel := range related { + rel.R.Organization = organization0 + } + + return nil +} + +func insertOrganizationPublicreportPool0(ctx context.Context, exec bob.Executor, publicreportPools1 []*PublicreportPoolSetter, organization0 *Organization) (PublicreportPoolSlice, error) { + for i := range publicreportPools1 { + publicreportPools1[i].OrganizationID = omitnull.From(organization0.ID) + } + + ret, err := PublicreportPools.Insert(bob.ToMods(publicreportPools1...)).All(ctx, exec) + if err != nil { + return ret, fmt.Errorf("insertOrganizationPublicreportPool0: %w", err) + } + + return ret, nil +} + +func attachOrganizationPublicreportPool0(ctx context.Context, exec bob.Executor, count int, publicreportPools1 PublicreportPoolSlice, organization0 *Organization) (PublicreportPoolSlice, error) { + setter := &PublicreportPoolSetter{ + OrganizationID: omitnull.From(organization0.ID), + } + + err := publicreportPools1.UpdateAll(ctx, exec, *setter) + if err != nil { + return nil, fmt.Errorf("attachOrganizationPublicreportPool0: %w", err) + } + + return publicreportPools1, nil +} + +func (organization0 *Organization) InsertPublicreportPool(ctx context.Context, exec bob.Executor, related ...*PublicreportPoolSetter) error { + if len(related) == 0 { + return nil + } + + var err error + + publicreportPools1, err := insertOrganizationPublicreportPool0(ctx, exec, related, organization0) + if err != nil { + return err + } + + organization0.R.PublicreportPool = append(organization0.R.PublicreportPool, publicreportPools1...) + + for _, rel := range publicreportPools1 { + rel.R.Organization = organization0 + } + return nil +} + +func (organization0 *Organization) AttachPublicreportPool(ctx context.Context, exec bob.Executor, related ...*PublicreportPool) error { + if len(related) == 0 { + return nil + } + + var err error + publicreportPools1 := PublicreportPoolSlice(related) + + _, err = attachOrganizationPublicreportPool0(ctx, exec, len(related), publicreportPools1, organization0) + if err != nil { + return err + } + + organization0.R.PublicreportPool = append(organization0.R.PublicreportPool, publicreportPools1...) + + for _, rel := range related { + rel.R.Organization = organization0 + } + + return nil +} + +func insertOrganizationQuicks0(ctx context.Context, exec bob.Executor, publicreportQuicks1 []*PublicreportQuickSetter, organization0 *Organization) (PublicreportQuickSlice, error) { + for i := range publicreportQuicks1 { + publicreportQuicks1[i].OrganizationID = omitnull.From(organization0.ID) + } + + ret, err := PublicreportQuicks.Insert(bob.ToMods(publicreportQuicks1...)).All(ctx, exec) + if err != nil { + return ret, fmt.Errorf("insertOrganizationQuicks0: %w", err) + } + + return ret, nil +} + +func attachOrganizationQuicks0(ctx context.Context, exec bob.Executor, count int, publicreportQuicks1 PublicreportQuickSlice, organization0 *Organization) (PublicreportQuickSlice, error) { + setter := &PublicreportQuickSetter{ + OrganizationID: omitnull.From(organization0.ID), + } + + err := publicreportQuicks1.UpdateAll(ctx, exec, *setter) + if err != nil { + return nil, fmt.Errorf("attachOrganizationQuicks0: %w", err) + } + + return publicreportQuicks1, nil +} + +func (organization0 *Organization) InsertQuicks(ctx context.Context, exec bob.Executor, related ...*PublicreportQuickSetter) error { + if len(related) == 0 { + return nil + } + + var err error + + publicreportQuicks1, err := insertOrganizationQuicks0(ctx, exec, related, organization0) + if err != nil { + return err + } + + organization0.R.Quicks = append(organization0.R.Quicks, publicreportQuicks1...) + + for _, rel := range publicreportQuicks1 { + rel.R.Organization = organization0 + } + return nil +} + +func (organization0 *Organization) AttachQuicks(ctx context.Context, exec bob.Executor, related ...*PublicreportQuick) error { + if len(related) == 0 { + return nil + } + + var err error + publicreportQuicks1 := PublicreportQuickSlice(related) + + _, err = attachOrganizationQuicks0(ctx, exec, len(related), publicreportQuicks1, organization0) + if err != nil { + return err + } + + organization0.R.Quicks = append(organization0.R.Quicks, publicreportQuicks1...) + + for _, rel := range related { + rel.R.Organization = organization0 + } + + return nil +} + func insertOrganizationUser0(ctx context.Context, exec bob.Executor, users1 []*UserSetter, organization0 *Organization) (UserSlice, error) { for i := range users1 { users1[i].OrganizationID = omit.From(organization0.ID) @@ -3543,6 +3846,7 @@ type organizationWhere[Q psql.Filterable] struct { FieldseekerURL psql.WhereNullMod[Q, string] ImportDistrictGid psql.WhereNullMod[Q, int32] Website psql.WhereNullMod[Q, string] + LogoUUID psql.WhereNullMod[Q, uuid.UUID] } func (organizationWhere[Q]) AliasedAs(alias string) organizationWhere[Q] { @@ -3558,6 +3862,7 @@ func buildOrganizationWhere[Q psql.Filterable](cols organizationColumns) organiz FieldseekerURL: psql.WhereNull[Q, string](cols.FieldseekerURL), ImportDistrictGid: psql.WhereNull[Q, int32](cols.ImportDistrictGid), Website: psql.WhereNull[Q, string](cols.Website), + LogoUUID: psql.WhereNull[Q, uuid.UUID](cols.LogoUUID), } } @@ -4013,6 +4318,48 @@ func (o *Organization) Preload(name string, retrieved any) error { rel.R.ImportDistrictGidOrganization = o } return nil + case "Nuisances": + rels, ok := retrieved.(PublicreportNuisanceSlice) + if !ok { + return fmt.Errorf("organization cannot load %T as %q", retrieved, name) + } + + o.R.Nuisances = rels + + for _, rel := range rels { + if rel != nil { + rel.R.Organization = o + } + } + return nil + case "PublicreportPool": + rels, ok := retrieved.(PublicreportPoolSlice) + if !ok { + return fmt.Errorf("organization cannot load %T as %q", retrieved, name) + } + + o.R.PublicreportPool = rels + + for _, rel := range rels { + if rel != nil { + rel.R.Organization = o + } + } + return nil + case "Quicks": + rels, ok := retrieved.(PublicreportQuickSlice) + if !ok { + return fmt.Errorf("organization cannot load %T as %q", retrieved, name) + } + + o.R.Quicks = rels + + for _, rel := range rels { + if rel != nil { + rel.R.Organization = o + } + } + return nil case "User": rels, ok := retrieved.(UserSlice) if !ok { @@ -4087,6 +4434,9 @@ type organizationThenLoader[Q orm.Loadable] struct { NoteAudios func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] NoteImages func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] ImportDistrictGidDistrict func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Nuisances func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + PublicreportPool func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Quicks func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] User func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] } @@ -4187,6 +4537,15 @@ func buildOrganizationThenLoader[Q orm.Loadable]() organizationThenLoader[Q] { type ImportDistrictGidDistrictLoadInterface interface { LoadImportDistrictGidDistrict(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error } + type NuisancesLoadInterface interface { + LoadNuisances(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } + type PublicreportPoolLoadInterface interface { + LoadPublicreportPool(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } + type QuicksLoadInterface interface { + LoadQuicks(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } type UserLoadInterface interface { LoadUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error } @@ -4384,6 +4743,24 @@ func buildOrganizationThenLoader[Q orm.Loadable]() organizationThenLoader[Q] { return retrieved.LoadImportDistrictGidDistrict(ctx, exec, mods...) }, ), + Nuisances: thenLoadBuilder[Q]( + "Nuisances", + func(ctx context.Context, exec bob.Executor, retrieved NuisancesLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadNuisances(ctx, exec, mods...) + }, + ), + PublicreportPool: thenLoadBuilder[Q]( + "PublicreportPool", + func(ctx context.Context, exec bob.Executor, retrieved PublicreportPoolLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadPublicreportPool(ctx, exec, mods...) + }, + ), + Quicks: thenLoadBuilder[Q]( + "Quicks", + func(ctx context.Context, exec bob.Executor, retrieved QuicksLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadQuicks(ctx, exec, mods...) + }, + ), User: thenLoadBuilder[Q]( "User", func(ctx context.Context, exec bob.Executor, retrieved UserLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { @@ -6339,6 +6716,198 @@ func (os OrganizationSlice) LoadImportDistrictGidDistrict(ctx context.Context, e return nil } +// LoadNuisances loads the organization's Nuisances into the .R struct +func (o *Organization) LoadNuisances(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.Nuisances = nil + + related, err := o.Nuisances(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, rel := range related { + rel.R.Organization = o + } + + o.R.Nuisances = related + return nil +} + +// LoadNuisances loads the organization's Nuisances into the .R struct +func (os OrganizationSlice) LoadNuisances(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + publicreportNuisances, err := os.Nuisances(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + if o == nil { + continue + } + + o.R.Nuisances = nil + } + + for _, o := range os { + if o == nil { + continue + } + + for _, rel := range publicreportNuisances { + + if !rel.OrganizationID.IsValue() { + continue + } + if !(rel.OrganizationID.IsValue() && o.ID == rel.OrganizationID.MustGet()) { + continue + } + + rel.R.Organization = o + + o.R.Nuisances = append(o.R.Nuisances, rel) + } + } + + return nil +} + +// LoadPublicreportPool loads the organization's PublicreportPool into the .R struct +func (o *Organization) LoadPublicreportPool(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.PublicreportPool = nil + + related, err := o.PublicreportPool(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, rel := range related { + rel.R.Organization = o + } + + o.R.PublicreportPool = related + return nil +} + +// LoadPublicreportPool loads the organization's PublicreportPool into the .R struct +func (os OrganizationSlice) LoadPublicreportPool(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + publicreportPools, err := os.PublicreportPool(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + if o == nil { + continue + } + + o.R.PublicreportPool = nil + } + + for _, o := range os { + if o == nil { + continue + } + + for _, rel := range publicreportPools { + + if !rel.OrganizationID.IsValue() { + continue + } + if !(rel.OrganizationID.IsValue() && o.ID == rel.OrganizationID.MustGet()) { + continue + } + + rel.R.Organization = o + + o.R.PublicreportPool = append(o.R.PublicreportPool, rel) + } + } + + return nil +} + +// LoadQuicks loads the organization's Quicks into the .R struct +func (o *Organization) LoadQuicks(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.Quicks = nil + + related, err := o.Quicks(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, rel := range related { + rel.R.Organization = o + } + + o.R.Quicks = related + return nil +} + +// LoadQuicks loads the organization's Quicks into the .R struct +func (os OrganizationSlice) LoadQuicks(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + publicreportQuicks, err := os.Quicks(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + if o == nil { + continue + } + + o.R.Quicks = nil + } + + for _, o := range os { + if o == nil { + continue + } + + for _, rel := range publicreportQuicks { + + if !rel.OrganizationID.IsValue() { + continue + } + if !(rel.OrganizationID.IsValue() && o.ID == rel.OrganizationID.MustGet()) { + continue + } + + rel.R.Organization = o + + o.R.Quicks = append(o.R.Quicks, rel) + } + } + + return nil +} + // LoadUser loads the organization's User into the .R struct func (o *Organization) LoadUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { if o == nil { @@ -6433,6 +7002,9 @@ type organizationC struct { H3Aggregations *int64 NoteAudios *int64 NoteImages *int64 + Nuisances *int64 + PublicreportPool *int64 + Quicks *int64 User *int64 } @@ -6505,6 +7077,12 @@ func (o *Organization) PreloadCount(name string, count int64) error { o.C.NoteAudios = &count case "NoteImages": o.C.NoteImages = &count + case "Nuisances": + o.C.Nuisances = &count + case "PublicreportPool": + o.C.PublicreportPool = &count + case "Quicks": + o.C.Quicks = &count case "User": o.C.User = &count } @@ -6543,6 +7121,9 @@ type organizationCountPreloader struct { H3Aggregations func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader NoteAudios func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader NoteImages func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader + Nuisances func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader + PublicreportPool func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader + Quicks func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader User func(...bob.Mod[*dialect.SelectQuery]) psql.Preloader } @@ -7075,6 +7656,57 @@ func buildOrganizationCountPreloader() organizationCountPreloader { return psql.Group(psql.Select(subqueryMods...).Expression) }) }, + Nuisances: func(mods ...bob.Mod[*dialect.SelectQuery]) psql.Preloader { + return countPreloader[*Organization]("Nuisances", func(parent string) bob.Expression { + // Build a correlated subquery: (SELECT COUNT(*) FROM related WHERE fk = parent.pk) + if parent == "" { + parent = Organizations.Alias() + } + + subqueryMods := []bob.Mod[*dialect.SelectQuery]{ + sm.Columns(psql.Raw("count(*)")), + + sm.From(PublicreportNuisances.Name()), + sm.Where(psql.Quote(PublicreportNuisances.Alias(), "organization_id").EQ(psql.Quote(parent, "id"))), + } + subqueryMods = append(subqueryMods, mods...) + return psql.Group(psql.Select(subqueryMods...).Expression) + }) + }, + PublicreportPool: func(mods ...bob.Mod[*dialect.SelectQuery]) psql.Preloader { + return countPreloader[*Organization]("PublicreportPool", func(parent string) bob.Expression { + // Build a correlated subquery: (SELECT COUNT(*) FROM related WHERE fk = parent.pk) + if parent == "" { + parent = Organizations.Alias() + } + + subqueryMods := []bob.Mod[*dialect.SelectQuery]{ + sm.Columns(psql.Raw("count(*)")), + + sm.From(PublicreportPools.Name()), + sm.Where(psql.Quote(PublicreportPools.Alias(), "organization_id").EQ(psql.Quote(parent, "id"))), + } + subqueryMods = append(subqueryMods, mods...) + return psql.Group(psql.Select(subqueryMods...).Expression) + }) + }, + Quicks: func(mods ...bob.Mod[*dialect.SelectQuery]) psql.Preloader { + return countPreloader[*Organization]("Quicks", func(parent string) bob.Expression { + // Build a correlated subquery: (SELECT COUNT(*) FROM related WHERE fk = parent.pk) + if parent == "" { + parent = Organizations.Alias() + } + + subqueryMods := []bob.Mod[*dialect.SelectQuery]{ + sm.Columns(psql.Raw("count(*)")), + + sm.From(PublicreportQuicks.Name()), + sm.Where(psql.Quote(PublicreportQuicks.Alias(), "organization_id").EQ(psql.Quote(parent, "id"))), + } + subqueryMods = append(subqueryMods, mods...) + return psql.Group(psql.Select(subqueryMods...).Expression) + }) + }, User: func(mods ...bob.Mod[*dialect.SelectQuery]) psql.Preloader { return countPreloader[*Organization]("User", func(parent string) bob.Expression { // Build a correlated subquery: (SELECT COUNT(*) FROM related WHERE fk = parent.pk) @@ -7127,6 +7759,9 @@ type organizationCountThenLoader[Q orm.Loadable] struct { H3Aggregations func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] NoteAudios func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] NoteImages func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Nuisances func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + PublicreportPool func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Quicks func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] User func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] } @@ -7224,6 +7859,15 @@ func buildOrganizationCountThenLoader[Q orm.Loadable]() organizationCountThenLoa type NoteImagesCountInterface interface { LoadCountNoteImages(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error } + type NuisancesCountInterface interface { + LoadCountNuisances(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } + type PublicreportPoolCountInterface interface { + LoadCountPublicreportPool(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } + type QuicksCountInterface interface { + LoadCountQuicks(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } type UserCountInterface interface { LoadCountUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error } @@ -7415,6 +8059,24 @@ func buildOrganizationCountThenLoader[Q orm.Loadable]() organizationCountThenLoa return retrieved.LoadCountNoteImages(ctx, exec, mods...) }, ), + Nuisances: countThenLoadBuilder[Q]( + "Nuisances", + func(ctx context.Context, exec bob.Executor, retrieved NuisancesCountInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadCountNuisances(ctx, exec, mods...) + }, + ), + PublicreportPool: countThenLoadBuilder[Q]( + "PublicreportPool", + func(ctx context.Context, exec bob.Executor, retrieved PublicreportPoolCountInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadCountPublicreportPool(ctx, exec, mods...) + }, + ), + Quicks: countThenLoadBuilder[Q]( + "Quicks", + func(ctx context.Context, exec bob.Executor, retrieved QuicksCountInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadCountQuicks(ctx, exec, mods...) + }, + ), User: countThenLoadBuilder[Q]( "User", func(ctx context.Context, exec bob.Executor, retrieved UserCountInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { @@ -8354,6 +9016,96 @@ func (os OrganizationSlice) LoadCountNoteImages(ctx context.Context, exec bob.Ex return nil } +// LoadCountNuisances loads the count of Nuisances into the C struct +func (o *Organization) LoadCountNuisances(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + count, err := o.Nuisances(mods...).Count(ctx, exec) + if err != nil { + return err + } + + o.C.Nuisances = &count + return nil +} + +// LoadCountNuisances loads the count of Nuisances for a slice +func (os OrganizationSlice) LoadCountNuisances(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + for _, o := range os { + if err := o.LoadCountNuisances(ctx, exec, mods...); err != nil { + return err + } + } + + return nil +} + +// LoadCountPublicreportPool loads the count of PublicreportPool into the C struct +func (o *Organization) LoadCountPublicreportPool(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + count, err := o.PublicreportPool(mods...).Count(ctx, exec) + if err != nil { + return err + } + + o.C.PublicreportPool = &count + return nil +} + +// LoadCountPublicreportPool loads the count of PublicreportPool for a slice +func (os OrganizationSlice) LoadCountPublicreportPool(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + for _, o := range os { + if err := o.LoadCountPublicreportPool(ctx, exec, mods...); err != nil { + return err + } + } + + return nil +} + +// LoadCountQuicks loads the count of Quicks into the C struct +func (o *Organization) LoadCountQuicks(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + count, err := o.Quicks(mods...).Count(ctx, exec) + if err != nil { + return err + } + + o.C.Quicks = &count + return nil +} + +// LoadCountQuicks loads the count of Quicks for a slice +func (os OrganizationSlice) LoadCountQuicks(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + for _, o := range os { + if err := o.LoadCountQuicks(ctx, exec, mods...); err != nil { + return err + } + } + + return nil +} + // LoadCountUser loads the count of User into the C struct func (o *Organization) LoadCountUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { if o == nil { @@ -8418,6 +9170,9 @@ type organizationJoins[Q dialect.Joinable] struct { NoteAudios modAs[Q, noteAudioColumns] NoteImages modAs[Q, noteImageColumns] ImportDistrictGidDistrict modAs[Q, importDistrictColumns] + Nuisances modAs[Q, publicreportNuisanceColumns] + PublicreportPool modAs[Q, publicreportPoolColumns] + Quicks modAs[Q, publicreportQuickColumns] User modAs[Q, userColumns] } @@ -8876,6 +9631,48 @@ func buildOrganizationJoins[Q dialect.Joinable](cols organizationColumns, typ st return mods }, }, + Nuisances: modAs[Q, publicreportNuisanceColumns]{ + c: PublicreportNuisances.Columns, + f: func(to publicreportNuisanceColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, PublicreportNuisances.Name().As(to.Alias())).On( + to.OrganizationID.EQ(cols.ID), + )) + } + + return mods + }, + }, + PublicreportPool: modAs[Q, publicreportPoolColumns]{ + c: PublicreportPools.Columns, + f: func(to publicreportPoolColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, PublicreportPools.Name().As(to.Alias())).On( + to.OrganizationID.EQ(cols.ID), + )) + } + + return mods + }, + }, + Quicks: modAs[Q, publicreportQuickColumns]{ + c: PublicreportQuicks.Columns, + f: func(to publicreportQuickColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, PublicreportQuicks.Name().As(to.Alias())).On( + to.OrganizationID.EQ(cols.ID), + )) + } + + return mods + }, + }, User: modAs[Q, userColumns]{ c: Users.Columns, f: func(to userColumns) bob.Mod[Q] { diff --git a/db/models/publicreport.nuisance.bob.go b/db/models/publicreport.nuisance.bob.go index 031d4bf0..0797594d 100644 --- a/db/models/publicreport.nuisance.bob.go +++ b/db/models/publicreport.nuisance.bob.go @@ -5,6 +5,7 @@ package models import ( "context" + "fmt" "io" "time" @@ -19,6 +20,9 @@ import ( "github.com/stephenafamo/bob/dialect/psql/sm" "github.com/stephenafamo/bob/dialect/psql/um" "github.com/stephenafamo/bob/expr" + "github.com/stephenafamo/bob/mods" + "github.com/stephenafamo/bob/orm" + "github.com/stephenafamo/bob/types/pgtypes" ) // PublicreportNuisance is an object representing the database table. @@ -50,6 +54,9 @@ type PublicreportNuisance struct { Address string `db:"address" ` Location null.Val[string] `db:"location" ` Status enums.PublicreportReportstatustype `db:"status" ` + OrganizationID null.Val[int32] `db:"organization_id" ` + + R publicreportNuisanceR `db:"-" ` } // PublicreportNuisanceSlice is an alias for a slice of pointers to PublicreportNuisance. @@ -62,10 +69,15 @@ var PublicreportNuisances = psql.NewTablex[*PublicreportNuisance, PublicreportNu // PublicreportNuisancesQuery is a query on the nuisance table type PublicreportNuisancesQuery = *psql.ViewQuery[*PublicreportNuisance, PublicreportNuisanceSlice] +// publicreportNuisanceR is where relationships are stored. +type publicreportNuisanceR struct { + Organization *Organization // publicreport.nuisance.nuisance_organization_id_fkey +} + func buildPublicreportNuisanceColumns(alias string) publicreportNuisanceColumns { return publicreportNuisanceColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "additional_info", "created", "duration", "email", "inspection_type", "source_location", "preferred_date_range", "preferred_time", "request_call", "severity", "source_container", "source_description", "source_roof", "source_stagnant", "time_of_day_day", "time_of_day_early", "time_of_day_evening", "time_of_day_night", "public_id", "reporter_address", "reporter_email", "reporter_name", "reporter_phone", "address", "location", "status", + "id", "additional_info", "created", "duration", "email", "inspection_type", "source_location", "preferred_date_range", "preferred_time", "request_call", "severity", "source_container", "source_description", "source_roof", "source_stagnant", "time_of_day_day", "time_of_day_early", "time_of_day_evening", "time_of_day_night", "public_id", "reporter_address", "reporter_email", "reporter_name", "reporter_phone", "address", "location", "status", "organization_id", ).WithParent("publicreport.nuisance"), tableAlias: alias, ID: psql.Quote(alias, "id"), @@ -95,6 +107,7 @@ func buildPublicreportNuisanceColumns(alias string) publicreportNuisanceColumns Address: psql.Quote(alias, "address"), Location: psql.Quote(alias, "location"), Status: psql.Quote(alias, "status"), + OrganizationID: psql.Quote(alias, "organization_id"), } } @@ -128,6 +141,7 @@ type publicreportNuisanceColumns struct { Address psql.Expression Location psql.Expression Status psql.Expression + OrganizationID psql.Expression } func (c publicreportNuisanceColumns) Alias() string { @@ -169,10 +183,11 @@ type PublicreportNuisanceSetter struct { Address omit.Val[string] `db:"address" ` Location omitnull.Val[string] `db:"location" ` Status omit.Val[enums.PublicreportReportstatustype] `db:"status" ` + OrganizationID omitnull.Val[int32] `db:"organization_id" ` } func (s PublicreportNuisanceSetter) SetColumns() []string { - vals := make([]string, 0, 27) + vals := make([]string, 0, 28) if s.ID.IsValue() { vals = append(vals, "id") } @@ -254,6 +269,9 @@ func (s PublicreportNuisanceSetter) SetColumns() []string { if s.Status.IsValue() { vals = append(vals, "status") } + if !s.OrganizationID.IsUnset() { + vals = append(vals, "organization_id") + } return vals } @@ -339,6 +357,9 @@ func (s PublicreportNuisanceSetter) Overwrite(t *PublicreportNuisance) { if s.Status.IsValue() { t.Status = s.Status.MustGet() } + if !s.OrganizationID.IsUnset() { + t.OrganizationID = s.OrganizationID.MustGetNull() + } } func (s *PublicreportNuisanceSetter) Apply(q *dialect.InsertQuery) { @@ -347,7 +368,7 @@ func (s *PublicreportNuisanceSetter) 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, 27) + vals := make([]bob.Expression, 28) if s.ID.IsValue() { vals[0] = psql.Arg(s.ID.MustGet()) } else { @@ -510,6 +531,12 @@ func (s *PublicreportNuisanceSetter) Apply(q *dialect.InsertQuery) { vals[26] = psql.Raw("DEFAULT") } + if !s.OrganizationID.IsUnset() { + vals[27] = psql.Arg(s.OrganizationID.MustGetNull()) + } else { + vals[27] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -519,7 +546,7 @@ func (s PublicreportNuisanceSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s PublicreportNuisanceSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 27) + exprs := make([]bob.Expression, 0, 28) if s.ID.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -710,6 +737,13 @@ func (s PublicreportNuisanceSetter) Expressions(prefix ...string) []bob.Expressi }}) } + if !s.OrganizationID.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "organization_id")...), + psql.Arg(s.OrganizationID), + }}) + } + return exprs } @@ -771,6 +805,7 @@ func (o *PublicreportNuisance) Update(ctx context.Context, exec bob.Executor, s return err } + o.R = v.R *o = *v return nil @@ -790,7 +825,7 @@ func (o *PublicreportNuisance) Reload(ctx context.Context, exec bob.Executor) er if err != nil { return err } - + o2.R = o.R *o = *o2 return nil @@ -837,7 +872,7 @@ func (o PublicreportNuisanceSlice) copyMatchingRows(from ...*PublicreportNuisanc if new.ID != old.ID { continue } - + new.R = old.R o[i] = new break } @@ -935,6 +970,78 @@ func (o PublicreportNuisanceSlice) ReloadAll(ctx context.Context, exec bob.Execu return nil } +// Organization starts a query for related objects on organization +func (o *PublicreportNuisance) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { + return Organizations.Query(append(mods, + sm.Where(Organizations.Columns.ID.EQ(psql.Arg(o.OrganizationID))), + )...) +} + +func (os PublicreportNuisanceSlice) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { + pkOrganizationID := make(pgtypes.Array[null.Val[int32]], 0, len(os)) + for _, o := range os { + if o == nil { + continue + } + pkOrganizationID = append(pkOrganizationID, o.OrganizationID) + } + PKArgExpr := psql.Select(sm.Columns( + psql.F("unnest", psql.Cast(psql.Arg(pkOrganizationID), "integer[]")), + )) + + return Organizations.Query(append(mods, + sm.Where(psql.Group(Organizations.Columns.ID).OP("IN", PKArgExpr)), + )...) +} + +func attachPublicreportNuisanceOrganization0(ctx context.Context, exec bob.Executor, count int, publicreportNuisance0 *PublicreportNuisance, organization1 *Organization) (*PublicreportNuisance, error) { + setter := &PublicreportNuisanceSetter{ + OrganizationID: omitnull.From(organization1.ID), + } + + err := publicreportNuisance0.Update(ctx, exec, setter) + if err != nil { + return nil, fmt.Errorf("attachPublicreportNuisanceOrganization0: %w", err) + } + + return publicreportNuisance0, nil +} + +func (publicreportNuisance0 *PublicreportNuisance) InsertOrganization(ctx context.Context, exec bob.Executor, related *OrganizationSetter) error { + var err error + + organization1, err := Organizations.Insert(related).One(ctx, exec) + if err != nil { + return fmt.Errorf("inserting related objects: %w", err) + } + + _, err = attachPublicreportNuisanceOrganization0(ctx, exec, 1, publicreportNuisance0, organization1) + if err != nil { + return err + } + + publicreportNuisance0.R.Organization = organization1 + + organization1.R.Nuisances = append(organization1.R.Nuisances, publicreportNuisance0) + + return nil +} + +func (publicreportNuisance0 *PublicreportNuisance) AttachOrganization(ctx context.Context, exec bob.Executor, organization1 *Organization) error { + var err error + + _, err = attachPublicreportNuisanceOrganization0(ctx, exec, 1, publicreportNuisance0, organization1) + if err != nil { + return err + } + + publicreportNuisance0.R.Organization = organization1 + + organization1.R.Nuisances = append(organization1.R.Nuisances, publicreportNuisance0) + + return nil +} + type publicreportNuisanceWhere[Q psql.Filterable] struct { ID psql.WhereMod[Q, int32] AdditionalInfo psql.WhereMod[Q, string] @@ -963,6 +1070,7 @@ type publicreportNuisanceWhere[Q psql.Filterable] struct { Address psql.WhereMod[Q, string] Location psql.WhereNullMod[Q, string] Status psql.WhereMod[Q, enums.PublicreportReportstatustype] + OrganizationID psql.WhereNullMod[Q, int32] } func (publicreportNuisanceWhere[Q]) AliasedAs(alias string) publicreportNuisanceWhere[Q] { @@ -998,5 +1106,154 @@ func buildPublicreportNuisanceWhere[Q psql.Filterable](cols publicreportNuisance Address: psql.Where[Q, string](cols.Address), Location: psql.WhereNull[Q, string](cols.Location), Status: psql.Where[Q, enums.PublicreportReportstatustype](cols.Status), + OrganizationID: psql.WhereNull[Q, int32](cols.OrganizationID), + } +} + +func (o *PublicreportNuisance) Preload(name string, retrieved any) error { + if o == nil { + return nil + } + + switch name { + case "Organization": + rel, ok := retrieved.(*Organization) + if !ok { + return fmt.Errorf("publicreportNuisance cannot load %T as %q", retrieved, name) + } + + o.R.Organization = rel + + if rel != nil { + rel.R.Nuisances = PublicreportNuisanceSlice{o} + } + return nil + default: + return fmt.Errorf("publicreportNuisance has no relationship %q", name) + } +} + +type publicreportNuisancePreloader struct { + Organization func(...psql.PreloadOption) psql.Preloader +} + +func buildPublicreportNuisancePreloader() publicreportNuisancePreloader { + return publicreportNuisancePreloader{ + Organization: func(opts ...psql.PreloadOption) psql.Preloader { + return psql.Preload[*Organization, OrganizationSlice](psql.PreloadRel{ + Name: "Organization", + Sides: []psql.PreloadSide{ + { + From: PublicreportNuisances, + To: Organizations, + FromColumns: []string{"organization_id"}, + ToColumns: []string{"id"}, + }, + }, + }, Organizations.Columns.Names(), opts...) + }, + } +} + +type publicreportNuisanceThenLoader[Q orm.Loadable] struct { + Organization func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] +} + +func buildPublicreportNuisanceThenLoader[Q orm.Loadable]() publicreportNuisanceThenLoader[Q] { + type OrganizationLoadInterface interface { + LoadOrganization(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } + + return publicreportNuisanceThenLoader[Q]{ + Organization: thenLoadBuilder[Q]( + "Organization", + func(ctx context.Context, exec bob.Executor, retrieved OrganizationLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadOrganization(ctx, exec, mods...) + }, + ), + } +} + +// LoadOrganization loads the publicreportNuisance's Organization into the .R struct +func (o *PublicreportNuisance) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.Organization = nil + + related, err := o.Organization(mods...).One(ctx, exec) + if err != nil { + return err + } + + related.R.Nuisances = PublicreportNuisanceSlice{o} + + o.R.Organization = related + return nil +} + +// LoadOrganization loads the publicreportNuisance's Organization into the .R struct +func (os PublicreportNuisanceSlice) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + organizations, err := os.Organization(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + if o == nil { + continue + } + + for _, rel := range organizations { + if !o.OrganizationID.IsValue() { + continue + } + + if !(o.OrganizationID.IsValue() && o.OrganizationID.MustGet() == rel.ID) { + continue + } + + rel.R.Nuisances = append(rel.R.Nuisances, o) + + o.R.Organization = rel + break + } + } + + return nil +} + +type publicreportNuisanceJoins[Q dialect.Joinable] struct { + typ string + Organization modAs[Q, organizationColumns] +} + +func (j publicreportNuisanceJoins[Q]) aliasedAs(alias string) publicreportNuisanceJoins[Q] { + return buildPublicreportNuisanceJoins[Q](buildPublicreportNuisanceColumns(alias), j.typ) +} + +func buildPublicreportNuisanceJoins[Q dialect.Joinable](cols publicreportNuisanceColumns, typ string) publicreportNuisanceJoins[Q] { + return publicreportNuisanceJoins[Q]{ + typ: typ, + Organization: modAs[Q, organizationColumns]{ + c: Organizations.Columns, + f: func(to organizationColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, Organizations.Name().As(to.Alias())).On( + to.ID.EQ(cols.OrganizationID), + )) + } + + return mods + }, + }, } } diff --git a/db/models/publicreport.pool.bob.go b/db/models/publicreport.pool.bob.go index 0a5a9d8e..3b5f7c08 100644 --- a/db/models/publicreport.pool.bob.go +++ b/db/models/publicreport.pool.bob.go @@ -59,6 +59,7 @@ type PublicreportPool struct { ReporterPhone string `db:"reporter_phone" ` Subscribe bool `db:"subscribe" ` Status enums.PublicreportReportstatustype `db:"status" ` + OrganizationID null.Val[int32] `db:"organization_id" ` R publicreportPoolR `db:"-" ` @@ -77,13 +78,14 @@ type PublicreportPoolsQuery = *psql.ViewQuery[*PublicreportPool, PublicreportPoo // publicreportPoolR is where relationships are stored. type publicreportPoolR struct { - Images PublicreportImageSlice // publicreport.pool_image.pool_image_image_id_fkeypublicreport.pool_image.pool_image_pool_id_fkey + Organization *Organization // publicreport.pool.pool_organization_id_fkey + Images PublicreportImageSlice // publicreport.pool_image.pool_image_image_id_fkeypublicreport.pool_image.pool_image_pool_id_fkey } func buildPublicreportPoolColumns(alias string) publicreportPoolColumns { return publicreportPoolColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "access_comments", "access_gate", "access_fence", "access_locked", "access_dog", "access_other", "address", "address_country", "address_post_code", "address_place", "address_street", "address_region", "comments", "created", "h3cell", "has_adult", "has_larvae", "has_pupae", "location", "map_zoom", "owner_email", "owner_name", "owner_phone", "public_id", "reporter_email", "reporter_name", "reporter_phone", "subscribe", "status", + "id", "access_comments", "access_gate", "access_fence", "access_locked", "access_dog", "access_other", "address", "address_country", "address_post_code", "address_place", "address_street", "address_region", "comments", "created", "h3cell", "has_adult", "has_larvae", "has_pupae", "location", "map_zoom", "owner_email", "owner_name", "owner_phone", "public_id", "reporter_email", "reporter_name", "reporter_phone", "subscribe", "status", "organization_id", ).WithParent("publicreport.pool"), tableAlias: alias, ID: psql.Quote(alias, "id"), @@ -116,6 +118,7 @@ func buildPublicreportPoolColumns(alias string) publicreportPoolColumns { ReporterPhone: psql.Quote(alias, "reporter_phone"), Subscribe: psql.Quote(alias, "subscribe"), Status: psql.Quote(alias, "status"), + OrganizationID: psql.Quote(alias, "organization_id"), } } @@ -152,6 +155,7 @@ type publicreportPoolColumns struct { ReporterPhone psql.Expression Subscribe psql.Expression Status psql.Expression + OrganizationID psql.Expression } func (c publicreportPoolColumns) Alias() string { @@ -196,10 +200,11 @@ type PublicreportPoolSetter struct { ReporterPhone omit.Val[string] `db:"reporter_phone" ` Subscribe omit.Val[bool] `db:"subscribe" ` Status omit.Val[enums.PublicreportReportstatustype] `db:"status" ` + OrganizationID omitnull.Val[int32] `db:"organization_id" ` } func (s PublicreportPoolSetter) SetColumns() []string { - vals := make([]string, 0, 30) + vals := make([]string, 0, 31) if s.ID.IsValue() { vals = append(vals, "id") } @@ -290,6 +295,9 @@ func (s PublicreportPoolSetter) SetColumns() []string { if s.Status.IsValue() { vals = append(vals, "status") } + if !s.OrganizationID.IsUnset() { + vals = append(vals, "organization_id") + } return vals } @@ -384,6 +392,9 @@ func (s PublicreportPoolSetter) Overwrite(t *PublicreportPool) { if s.Status.IsValue() { t.Status = s.Status.MustGet() } + if !s.OrganizationID.IsUnset() { + t.OrganizationID = s.OrganizationID.MustGetNull() + } } func (s *PublicreportPoolSetter) Apply(q *dialect.InsertQuery) { @@ -392,7 +403,7 @@ func (s *PublicreportPoolSetter) 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, 30) + vals := make([]bob.Expression, 31) if s.ID.IsValue() { vals[0] = psql.Arg(s.ID.MustGet()) } else { @@ -573,6 +584,12 @@ func (s *PublicreportPoolSetter) Apply(q *dialect.InsertQuery) { vals[29] = psql.Raw("DEFAULT") } + if !s.OrganizationID.IsUnset() { + vals[30] = psql.Arg(s.OrganizationID.MustGetNull()) + } else { + vals[30] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -582,7 +599,7 @@ func (s PublicreportPoolSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s PublicreportPoolSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 30) + exprs := make([]bob.Expression, 0, 31) if s.ID.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -794,6 +811,13 @@ func (s PublicreportPoolSetter) Expressions(prefix ...string) []bob.Expression { }}) } + if !s.OrganizationID.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "organization_id")...), + psql.Arg(s.OrganizationID), + }}) + } + return exprs } @@ -1020,6 +1044,30 @@ func (o PublicreportPoolSlice) ReloadAll(ctx context.Context, exec bob.Executor) return nil } +// Organization starts a query for related objects on organization +func (o *PublicreportPool) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { + return Organizations.Query(append(mods, + sm.Where(Organizations.Columns.ID.EQ(psql.Arg(o.OrganizationID))), + )...) +} + +func (os PublicreportPoolSlice) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { + pkOrganizationID := make(pgtypes.Array[null.Val[int32]], 0, len(os)) + for _, o := range os { + if o == nil { + continue + } + pkOrganizationID = append(pkOrganizationID, o.OrganizationID) + } + PKArgExpr := psql.Select(sm.Columns( + psql.F("unnest", psql.Cast(psql.Arg(pkOrganizationID), "integer[]")), + )) + + return Organizations.Query(append(mods, + sm.Where(psql.Group(Organizations.Columns.ID).OP("IN", PKArgExpr)), + )...) +} + // Images starts a query for related objects on publicreport.image func (o *PublicreportPool) Images(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportImagesQuery { return PublicreportImages.Query(append(mods, @@ -1049,6 +1097,54 @@ func (os PublicreportPoolSlice) Images(mods ...bob.Mod[*dialect.SelectQuery]) Pu )...) } +func attachPublicreportPoolOrganization0(ctx context.Context, exec bob.Executor, count int, publicreportPool0 *PublicreportPool, organization1 *Organization) (*PublicreportPool, error) { + setter := &PublicreportPoolSetter{ + OrganizationID: omitnull.From(organization1.ID), + } + + err := publicreportPool0.Update(ctx, exec, setter) + if err != nil { + return nil, fmt.Errorf("attachPublicreportPoolOrganization0: %w", err) + } + + return publicreportPool0, nil +} + +func (publicreportPool0 *PublicreportPool) InsertOrganization(ctx context.Context, exec bob.Executor, related *OrganizationSetter) error { + var err error + + organization1, err := Organizations.Insert(related).One(ctx, exec) + if err != nil { + return fmt.Errorf("inserting related objects: %w", err) + } + + _, err = attachPublicreportPoolOrganization0(ctx, exec, 1, publicreportPool0, organization1) + if err != nil { + return err + } + + publicreportPool0.R.Organization = organization1 + + organization1.R.PublicreportPool = append(organization1.R.PublicreportPool, publicreportPool0) + + return nil +} + +func (publicreportPool0 *PublicreportPool) AttachOrganization(ctx context.Context, exec bob.Executor, organization1 *Organization) error { + var err error + + _, err = attachPublicreportPoolOrganization0(ctx, exec, 1, publicreportPool0, organization1) + if err != nil { + return err + } + + publicreportPool0.R.Organization = organization1 + + organization1.R.PublicreportPool = append(organization1.R.PublicreportPool, publicreportPool0) + + return nil +} + func attachPublicreportPoolImages0(ctx context.Context, exec bob.Executor, count int, publicreportPool0 *PublicreportPool, publicreportImages2 PublicreportImageSlice) (PublicreportPoolImageSlice, error) { setters := make([]*PublicreportPoolImageSetter, count) for i := range count { @@ -1145,6 +1241,7 @@ type publicreportPoolWhere[Q psql.Filterable] struct { ReporterPhone psql.WhereMod[Q, string] Subscribe psql.WhereMod[Q, bool] Status psql.WhereMod[Q, enums.PublicreportReportstatustype] + OrganizationID psql.WhereNullMod[Q, int32] } func (publicreportPoolWhere[Q]) AliasedAs(alias string) publicreportPoolWhere[Q] { @@ -1183,6 +1280,7 @@ func buildPublicreportPoolWhere[Q psql.Filterable](cols publicreportPoolColumns) ReporterPhone: psql.Where[Q, string](cols.ReporterPhone), Subscribe: psql.Where[Q, bool](cols.Subscribe), Status: psql.Where[Q, enums.PublicreportReportstatustype](cols.Status), + OrganizationID: psql.WhereNull[Q, int32](cols.OrganizationID), } } @@ -1192,6 +1290,18 @@ func (o *PublicreportPool) Preload(name string, retrieved any) error { } switch name { + case "Organization": + rel, ok := retrieved.(*Organization) + if !ok { + return fmt.Errorf("publicreportPool cannot load %T as %q", retrieved, name) + } + + o.R.Organization = rel + + if rel != nil { + rel.R.PublicreportPool = PublicreportPoolSlice{o} + } + return nil case "Images": rels, ok := retrieved.(PublicreportImageSlice) if !ok { @@ -1211,22 +1321,48 @@ func (o *PublicreportPool) Preload(name string, retrieved any) error { } } -type publicreportPoolPreloader struct{} +type publicreportPoolPreloader struct { + Organization func(...psql.PreloadOption) psql.Preloader +} func buildPublicreportPoolPreloader() publicreportPoolPreloader { - return publicreportPoolPreloader{} + return publicreportPoolPreloader{ + Organization: func(opts ...psql.PreloadOption) psql.Preloader { + return psql.Preload[*Organization, OrganizationSlice](psql.PreloadRel{ + Name: "Organization", + Sides: []psql.PreloadSide{ + { + From: PublicreportPools, + To: Organizations, + FromColumns: []string{"organization_id"}, + ToColumns: []string{"id"}, + }, + }, + }, Organizations.Columns.Names(), opts...) + }, + } } type publicreportPoolThenLoader[Q orm.Loadable] struct { - Images func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Organization func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Images func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] } func buildPublicreportPoolThenLoader[Q orm.Loadable]() publicreportPoolThenLoader[Q] { + type OrganizationLoadInterface interface { + LoadOrganization(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } type ImagesLoadInterface interface { LoadImages(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error } return publicreportPoolThenLoader[Q]{ + Organization: thenLoadBuilder[Q]( + "Organization", + func(ctx context.Context, exec bob.Executor, retrieved OrganizationLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadOrganization(ctx, exec, mods...) + }, + ), Images: thenLoadBuilder[Q]( "Images", func(ctx context.Context, exec bob.Executor, retrieved ImagesLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { @@ -1236,6 +1372,61 @@ func buildPublicreportPoolThenLoader[Q orm.Loadable]() publicreportPoolThenLoade } } +// LoadOrganization loads the publicreportPool's Organization into the .R struct +func (o *PublicreportPool) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.Organization = nil + + related, err := o.Organization(mods...).One(ctx, exec) + if err != nil { + return err + } + + related.R.PublicreportPool = PublicreportPoolSlice{o} + + o.R.Organization = related + return nil +} + +// LoadOrganization loads the publicreportPool's Organization into the .R struct +func (os PublicreportPoolSlice) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + organizations, err := os.Organization(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + if o == nil { + continue + } + + for _, rel := range organizations { + if !o.OrganizationID.IsValue() { + continue + } + + if !(o.OrganizationID.IsValue() && o.OrganizationID.MustGet() == rel.ID) { + continue + } + + rel.R.PublicreportPool = append(rel.R.PublicreportPool, o) + + o.R.Organization = rel + break + } + } + + return nil +} + // LoadImages loads the publicreportPool's Images into the .R struct func (o *PublicreportPool) LoadImages(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { if o == nil { @@ -1414,8 +1605,9 @@ func (os PublicreportPoolSlice) LoadCountImages(ctx context.Context, exec bob.Ex } type publicreportPoolJoins[Q dialect.Joinable] struct { - typ string - Images modAs[Q, publicreportImageColumns] + typ string + Organization modAs[Q, organizationColumns] + Images modAs[Q, publicreportImageColumns] } func (j publicreportPoolJoins[Q]) aliasedAs(alias string) publicreportPoolJoins[Q] { @@ -1425,6 +1617,20 @@ func (j publicreportPoolJoins[Q]) aliasedAs(alias string) publicreportPoolJoins[ func buildPublicreportPoolJoins[Q dialect.Joinable](cols publicreportPoolColumns, typ string) publicreportPoolJoins[Q] { return publicreportPoolJoins[Q]{ typ: typ, + Organization: modAs[Q, organizationColumns]{ + c: Organizations.Columns, + f: func(to organizationColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, Organizations.Name().As(to.Alias())).On( + to.ID.EQ(cols.OrganizationID), + )) + } + + return mods + }, + }, Images: modAs[Q, publicreportImageColumns]{ c: PublicreportImages.Columns, f: func(to publicreportImageColumns) bob.Mod[Q] { diff --git a/db/models/publicreport.quick.bob.go b/db/models/publicreport.quick.bob.go index c6514bb6..0486aebb 100644 --- a/db/models/publicreport.quick.bob.go +++ b/db/models/publicreport.quick.bob.go @@ -29,16 +29,17 @@ import ( // PublicreportQuick is an object representing the database table. type PublicreportQuick struct { - ID int32 `db:"id,pk" ` - Created time.Time `db:"created" ` - Comments string `db:"comments" ` - Location null.Val[string] `db:"location" ` - H3cell null.Val[string] `db:"h3cell" ` - PublicID string `db:"public_id" ` - ReporterEmail string `db:"reporter_email" ` - ReporterPhone string `db:"reporter_phone" ` - Address string `db:"address" ` - Status enums.PublicreportReportstatustype `db:"status" ` + ID int32 `db:"id,pk" ` + Created time.Time `db:"created" ` + Comments string `db:"comments" ` + Location null.Val[string] `db:"location" ` + H3cell null.Val[string] `db:"h3cell" ` + PublicID string `db:"public_id" ` + ReporterEmail string `db:"reporter_email" ` + ReporterPhone string `db:"reporter_phone" ` + Address string `db:"address" ` + Status enums.PublicreportReportstatustype `db:"status" ` + OrganizationID null.Val[int32] `db:"organization_id" ` R publicreportQuickR `db:"-" ` @@ -57,41 +58,44 @@ type PublicreportQuicksQuery = *psql.ViewQuery[*PublicreportQuick, PublicreportQ // publicreportQuickR is where relationships are stored. type publicreportQuickR struct { - Images PublicreportImageSlice // publicreport.quick_image.quick_image_image_id_fkeypublicreport.quick_image.quick_image_quick_id_fkey + Organization *Organization // publicreport.quick.quick_organization_id_fkey + Images PublicreportImageSlice // publicreport.quick_image.quick_image_image_id_fkeypublicreport.quick_image.quick_image_quick_id_fkey } func buildPublicreportQuickColumns(alias string) publicreportQuickColumns { return publicreportQuickColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "created", "comments", "location", "h3cell", "public_id", "reporter_email", "reporter_phone", "address", "status", + "id", "created", "comments", "location", "h3cell", "public_id", "reporter_email", "reporter_phone", "address", "status", "organization_id", ).WithParent("publicreport.quick"), - tableAlias: alias, - ID: psql.Quote(alias, "id"), - Created: psql.Quote(alias, "created"), - Comments: psql.Quote(alias, "comments"), - Location: psql.Quote(alias, "location"), - H3cell: psql.Quote(alias, "h3cell"), - PublicID: psql.Quote(alias, "public_id"), - ReporterEmail: psql.Quote(alias, "reporter_email"), - ReporterPhone: psql.Quote(alias, "reporter_phone"), - Address: psql.Quote(alias, "address"), - Status: psql.Quote(alias, "status"), + tableAlias: alias, + ID: psql.Quote(alias, "id"), + Created: psql.Quote(alias, "created"), + Comments: psql.Quote(alias, "comments"), + Location: psql.Quote(alias, "location"), + H3cell: psql.Quote(alias, "h3cell"), + PublicID: psql.Quote(alias, "public_id"), + ReporterEmail: psql.Quote(alias, "reporter_email"), + ReporterPhone: psql.Quote(alias, "reporter_phone"), + Address: psql.Quote(alias, "address"), + Status: psql.Quote(alias, "status"), + OrganizationID: psql.Quote(alias, "organization_id"), } } type publicreportQuickColumns struct { expr.ColumnsExpr - tableAlias string - ID psql.Expression - Created psql.Expression - Comments psql.Expression - Location psql.Expression - H3cell psql.Expression - PublicID psql.Expression - ReporterEmail psql.Expression - ReporterPhone psql.Expression - Address psql.Expression - Status psql.Expression + tableAlias string + ID psql.Expression + Created psql.Expression + Comments psql.Expression + Location psql.Expression + H3cell psql.Expression + PublicID psql.Expression + ReporterEmail psql.Expression + ReporterPhone psql.Expression + Address psql.Expression + Status psql.Expression + OrganizationID psql.Expression } func (c publicreportQuickColumns) Alias() string { @@ -106,20 +110,21 @@ func (publicreportQuickColumns) AliasedAs(alias string) publicreportQuickColumns // All values are optional, and do not have to be set // Generated columns are not included type PublicreportQuickSetter struct { - ID omit.Val[int32] `db:"id,pk" ` - Created omit.Val[time.Time] `db:"created" ` - Comments omit.Val[string] `db:"comments" ` - Location omitnull.Val[string] `db:"location" ` - H3cell omitnull.Val[string] `db:"h3cell" ` - PublicID omit.Val[string] `db:"public_id" ` - ReporterEmail omit.Val[string] `db:"reporter_email" ` - ReporterPhone omit.Val[string] `db:"reporter_phone" ` - Address omit.Val[string] `db:"address" ` - Status omit.Val[enums.PublicreportReportstatustype] `db:"status" ` + ID omit.Val[int32] `db:"id,pk" ` + Created omit.Val[time.Time] `db:"created" ` + Comments omit.Val[string] `db:"comments" ` + Location omitnull.Val[string] `db:"location" ` + H3cell omitnull.Val[string] `db:"h3cell" ` + PublicID omit.Val[string] `db:"public_id" ` + ReporterEmail omit.Val[string] `db:"reporter_email" ` + ReporterPhone omit.Val[string] `db:"reporter_phone" ` + Address omit.Val[string] `db:"address" ` + Status omit.Val[enums.PublicreportReportstatustype] `db:"status" ` + OrganizationID omitnull.Val[int32] `db:"organization_id" ` } func (s PublicreportQuickSetter) SetColumns() []string { - vals := make([]string, 0, 10) + vals := make([]string, 0, 11) if s.ID.IsValue() { vals = append(vals, "id") } @@ -150,6 +155,9 @@ func (s PublicreportQuickSetter) SetColumns() []string { if s.Status.IsValue() { vals = append(vals, "status") } + if !s.OrganizationID.IsUnset() { + vals = append(vals, "organization_id") + } return vals } @@ -184,6 +192,9 @@ func (s PublicreportQuickSetter) Overwrite(t *PublicreportQuick) { if s.Status.IsValue() { t.Status = s.Status.MustGet() } + if !s.OrganizationID.IsUnset() { + t.OrganizationID = s.OrganizationID.MustGetNull() + } } func (s *PublicreportQuickSetter) Apply(q *dialect.InsertQuery) { @@ -192,7 +203,7 @@ func (s *PublicreportQuickSetter) 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, 10) + vals := make([]bob.Expression, 11) if s.ID.IsValue() { vals[0] = psql.Arg(s.ID.MustGet()) } else { @@ -253,6 +264,12 @@ func (s *PublicreportQuickSetter) Apply(q *dialect.InsertQuery) { vals[9] = psql.Raw("DEFAULT") } + if !s.OrganizationID.IsUnset() { + vals[10] = psql.Arg(s.OrganizationID.MustGetNull()) + } else { + vals[10] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -262,7 +279,7 @@ func (s PublicreportQuickSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s PublicreportQuickSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 10) + exprs := make([]bob.Expression, 0, 11) if s.ID.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -334,6 +351,13 @@ func (s PublicreportQuickSetter) Expressions(prefix ...string) []bob.Expression }}) } + if !s.OrganizationID.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "organization_id")...), + psql.Arg(s.OrganizationID), + }}) + } + return exprs } @@ -560,6 +584,30 @@ func (o PublicreportQuickSlice) ReloadAll(ctx context.Context, exec bob.Executor return nil } +// Organization starts a query for related objects on organization +func (o *PublicreportQuick) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { + return Organizations.Query(append(mods, + sm.Where(Organizations.Columns.ID.EQ(psql.Arg(o.OrganizationID))), + )...) +} + +func (os PublicreportQuickSlice) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery { + pkOrganizationID := make(pgtypes.Array[null.Val[int32]], 0, len(os)) + for _, o := range os { + if o == nil { + continue + } + pkOrganizationID = append(pkOrganizationID, o.OrganizationID) + } + PKArgExpr := psql.Select(sm.Columns( + psql.F("unnest", psql.Cast(psql.Arg(pkOrganizationID), "integer[]")), + )) + + return Organizations.Query(append(mods, + sm.Where(psql.Group(Organizations.Columns.ID).OP("IN", PKArgExpr)), + )...) +} + // Images starts a query for related objects on publicreport.image func (o *PublicreportQuick) Images(mods ...bob.Mod[*dialect.SelectQuery]) PublicreportImagesQuery { return PublicreportImages.Query(append(mods, @@ -589,6 +637,54 @@ func (os PublicreportQuickSlice) Images(mods ...bob.Mod[*dialect.SelectQuery]) P )...) } +func attachPublicreportQuickOrganization0(ctx context.Context, exec bob.Executor, count int, publicreportQuick0 *PublicreportQuick, organization1 *Organization) (*PublicreportQuick, error) { + setter := &PublicreportQuickSetter{ + OrganizationID: omitnull.From(organization1.ID), + } + + err := publicreportQuick0.Update(ctx, exec, setter) + if err != nil { + return nil, fmt.Errorf("attachPublicreportQuickOrganization0: %w", err) + } + + return publicreportQuick0, nil +} + +func (publicreportQuick0 *PublicreportQuick) InsertOrganization(ctx context.Context, exec bob.Executor, related *OrganizationSetter) error { + var err error + + organization1, err := Organizations.Insert(related).One(ctx, exec) + if err != nil { + return fmt.Errorf("inserting related objects: %w", err) + } + + _, err = attachPublicreportQuickOrganization0(ctx, exec, 1, publicreportQuick0, organization1) + if err != nil { + return err + } + + publicreportQuick0.R.Organization = organization1 + + organization1.R.Quicks = append(organization1.R.Quicks, publicreportQuick0) + + return nil +} + +func (publicreportQuick0 *PublicreportQuick) AttachOrganization(ctx context.Context, exec bob.Executor, organization1 *Organization) error { + var err error + + _, err = attachPublicreportQuickOrganization0(ctx, exec, 1, publicreportQuick0, organization1) + if err != nil { + return err + } + + publicreportQuick0.R.Organization = organization1 + + organization1.R.Quicks = append(organization1.R.Quicks, publicreportQuick0) + + return nil +} + func attachPublicreportQuickImages0(ctx context.Context, exec bob.Executor, count int, publicreportQuick0 *PublicreportQuick, publicreportImages2 PublicreportImageSlice) (PublicreportQuickImageSlice, error) { setters := make([]*PublicreportQuickImageSetter, count) for i := range count { @@ -655,16 +751,17 @@ func (publicreportQuick0 *PublicreportQuick) AttachImages(ctx context.Context, e } type publicreportQuickWhere[Q psql.Filterable] struct { - ID psql.WhereMod[Q, int32] - Created psql.WhereMod[Q, time.Time] - Comments psql.WhereMod[Q, string] - Location psql.WhereNullMod[Q, string] - H3cell psql.WhereNullMod[Q, string] - PublicID psql.WhereMod[Q, string] - ReporterEmail psql.WhereMod[Q, string] - ReporterPhone psql.WhereMod[Q, string] - Address psql.WhereMod[Q, string] - Status psql.WhereMod[Q, enums.PublicreportReportstatustype] + ID psql.WhereMod[Q, int32] + Created psql.WhereMod[Q, time.Time] + Comments psql.WhereMod[Q, string] + Location psql.WhereNullMod[Q, string] + H3cell psql.WhereNullMod[Q, string] + PublicID psql.WhereMod[Q, string] + ReporterEmail psql.WhereMod[Q, string] + ReporterPhone psql.WhereMod[Q, string] + Address psql.WhereMod[Q, string] + Status psql.WhereMod[Q, enums.PublicreportReportstatustype] + OrganizationID psql.WhereNullMod[Q, int32] } func (publicreportQuickWhere[Q]) AliasedAs(alias string) publicreportQuickWhere[Q] { @@ -673,16 +770,17 @@ func (publicreportQuickWhere[Q]) AliasedAs(alias string) publicreportQuickWhere[ func buildPublicreportQuickWhere[Q psql.Filterable](cols publicreportQuickColumns) publicreportQuickWhere[Q] { return publicreportQuickWhere[Q]{ - ID: psql.Where[Q, int32](cols.ID), - Created: psql.Where[Q, time.Time](cols.Created), - Comments: psql.Where[Q, string](cols.Comments), - Location: psql.WhereNull[Q, string](cols.Location), - H3cell: psql.WhereNull[Q, string](cols.H3cell), - PublicID: psql.Where[Q, string](cols.PublicID), - ReporterEmail: psql.Where[Q, string](cols.ReporterEmail), - ReporterPhone: psql.Where[Q, string](cols.ReporterPhone), - Address: psql.Where[Q, string](cols.Address), - Status: psql.Where[Q, enums.PublicreportReportstatustype](cols.Status), + ID: psql.Where[Q, int32](cols.ID), + Created: psql.Where[Q, time.Time](cols.Created), + Comments: psql.Where[Q, string](cols.Comments), + Location: psql.WhereNull[Q, string](cols.Location), + H3cell: psql.WhereNull[Q, string](cols.H3cell), + PublicID: psql.Where[Q, string](cols.PublicID), + ReporterEmail: psql.Where[Q, string](cols.ReporterEmail), + ReporterPhone: psql.Where[Q, string](cols.ReporterPhone), + Address: psql.Where[Q, string](cols.Address), + Status: psql.Where[Q, enums.PublicreportReportstatustype](cols.Status), + OrganizationID: psql.WhereNull[Q, int32](cols.OrganizationID), } } @@ -692,6 +790,18 @@ func (o *PublicreportQuick) Preload(name string, retrieved any) error { } switch name { + case "Organization": + rel, ok := retrieved.(*Organization) + if !ok { + return fmt.Errorf("publicreportQuick cannot load %T as %q", retrieved, name) + } + + o.R.Organization = rel + + if rel != nil { + rel.R.Quicks = PublicreportQuickSlice{o} + } + return nil case "Images": rels, ok := retrieved.(PublicreportImageSlice) if !ok { @@ -711,22 +821,48 @@ func (o *PublicreportQuick) Preload(name string, retrieved any) error { } } -type publicreportQuickPreloader struct{} +type publicreportQuickPreloader struct { + Organization func(...psql.PreloadOption) psql.Preloader +} func buildPublicreportQuickPreloader() publicreportQuickPreloader { - return publicreportQuickPreloader{} + return publicreportQuickPreloader{ + Organization: func(opts ...psql.PreloadOption) psql.Preloader { + return psql.Preload[*Organization, OrganizationSlice](psql.PreloadRel{ + Name: "Organization", + Sides: []psql.PreloadSide{ + { + From: PublicreportQuicks, + To: Organizations, + FromColumns: []string{"organization_id"}, + ToColumns: []string{"id"}, + }, + }, + }, Organizations.Columns.Names(), opts...) + }, + } } type publicreportQuickThenLoader[Q orm.Loadable] struct { - Images func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Organization func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] + Images func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q] } func buildPublicreportQuickThenLoader[Q orm.Loadable]() publicreportQuickThenLoader[Q] { + type OrganizationLoadInterface interface { + LoadOrganization(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error + } type ImagesLoadInterface interface { LoadImages(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error } return publicreportQuickThenLoader[Q]{ + Organization: thenLoadBuilder[Q]( + "Organization", + func(ctx context.Context, exec bob.Executor, retrieved OrganizationLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { + return retrieved.LoadOrganization(ctx, exec, mods...) + }, + ), Images: thenLoadBuilder[Q]( "Images", func(ctx context.Context, exec bob.Executor, retrieved ImagesLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error { @@ -736,6 +872,61 @@ func buildPublicreportQuickThenLoader[Q orm.Loadable]() publicreportQuickThenLoa } } +// LoadOrganization loads the publicreportQuick's Organization into the .R struct +func (o *PublicreportQuick) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if o == nil { + return nil + } + + // Reset the relationship + o.R.Organization = nil + + related, err := o.Organization(mods...).One(ctx, exec) + if err != nil { + return err + } + + related.R.Quicks = PublicreportQuickSlice{o} + + o.R.Organization = related + return nil +} + +// LoadOrganization loads the publicreportQuick's Organization into the .R struct +func (os PublicreportQuickSlice) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { + if len(os) == 0 { + return nil + } + + organizations, err := os.Organization(mods...).All(ctx, exec) + if err != nil { + return err + } + + for _, o := range os { + if o == nil { + continue + } + + for _, rel := range organizations { + if !o.OrganizationID.IsValue() { + continue + } + + if !(o.OrganizationID.IsValue() && o.OrganizationID.MustGet() == rel.ID) { + continue + } + + rel.R.Quicks = append(rel.R.Quicks, o) + + o.R.Organization = rel + break + } + } + + return nil +} + // LoadImages loads the publicreportQuick's Images into the .R struct func (o *PublicreportQuick) LoadImages(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { if o == nil { @@ -914,8 +1105,9 @@ func (os PublicreportQuickSlice) LoadCountImages(ctx context.Context, exec bob.E } type publicreportQuickJoins[Q dialect.Joinable] struct { - typ string - Images modAs[Q, publicreportImageColumns] + typ string + Organization modAs[Q, organizationColumns] + Images modAs[Q, publicreportImageColumns] } func (j publicreportQuickJoins[Q]) aliasedAs(alias string) publicreportQuickJoins[Q] { @@ -925,6 +1117,20 @@ func (j publicreportQuickJoins[Q]) aliasedAs(alias string) publicreportQuickJoin func buildPublicreportQuickJoins[Q dialect.Joinable](cols publicreportQuickColumns, typ string) publicreportQuickJoins[Q] { return publicreportQuickJoins[Q]{ typ: typ, + Organization: modAs[Q, organizationColumns]{ + c: Organizations.Columns, + f: func(to organizationColumns) bob.Mod[Q] { + mods := make(mods.QueryMods[Q], 0, 1) + + { + mods = append(mods, dialect.Join[Q](typ, Organizations.Name().As(to.Alias())).On( + to.ID.EQ(cols.OrganizationID), + )) + } + + return mods + }, + }, Images: modAs[Q, publicreportImageColumns]{ c: PublicreportImages.Columns, f: func(to publicreportImageColumns) bob.Mod[Q] { diff --git a/go.mod b/go.mod index cde54c4f..bc3b05ca 100644 --- a/go.mod +++ b/go.mod @@ -34,13 +34,19 @@ require ( require ( github.com/ajg/form v1.5.1 // indirect github.com/beevik/etree v1.1.0 // indirect + github.com/dsoprea/go-exif-extra v0.0.0-20210513050430-a8c8bb657ad5 // indirect github.com/dsoprea/go-exif/v3 v3.0.1 // indirect + github.com/dsoprea/go-heic-exif-extractor/v2 v2.0.0-20210512044107-62067e44c235 // indirect github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb // indirect github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20221012074422-4f3f7e934102 // indirect github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect + github.com/dsoprea/go-png-image-structure/v2 v2.0.0-20210512044023-23bdd883ee8e // indirect + github.com/dsoprea/go-tiff-image-structure/v2 v2.0.0-20210512044046-dc78da6a809b // indirect github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect + github.com/dsoprea/go-webp-image-structure v0.0.0-20210512044215-f98af2b0401e // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/evanoberholster/imagemeta v0.3.1 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect @@ -64,20 +70,23 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect github.com/rs/xid v1.6.0 // indirect + github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/stretchr/testify v1.11.1 // indirect github.com/tetratelabs/wazero v1.9.0 // indirect github.com/tidwall/geoindex v1.4.4 // indirect github.com/tidwall/gjson v1.12.1 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/rtree v1.3.1 // indirect github.com/tidwall/sjson v1.2.4 // indirect github.com/tinylib/msgp v1.3.0 // indirect github.com/twilio/twilio-go v1.29.1 // indirect github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect go.uber.org/multierr v1.11.0 // indirect + go4.org v0.0.0-20200411211856-f5505b9728dd // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/image v0.0.0-20200618115811-c13761719519 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/go.sum b/go.sum index 41a400eb..85b48017 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,26 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Gleipnir-Technology/arcgis-go v0.0.5 h1:7UdgFZv7bnmLqkvGLivKurLKICmwZGWctPxESjDjeA8= github.com/Gleipnir-Technology/arcgis-go v0.0.5/go.mod h1:Stx2sn5Lvuyhy4SaTQpbLNCAfenboDINi/UU5gQvz4k= github.com/Gleipnir-Technology/go-geojson2h3/v2 v2.0.0 h1:6OMVxoiX9r7dEkIyYYKtSu7I2UDq64dww4JxJTo3p78= @@ -22,6 +41,11 @@ github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= @@ -45,16 +69,25 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dsoprea/go-exif-extra v0.0.0-20210513050430-a8c8bb657ad5 h1:F5HkHi38eOQECRJTJIV1Amn+J5q2atJf2feGPJ8/v+U= +github.com/dsoprea/go-exif-extra v0.0.0-20210513050430-a8c8bb657ad5/go.mod h1:9RNGsP4bsNJg7MDbFLmCw1lovrpGB2xCOjzyugh+4iY= github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= +github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0= github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= +github.com/dsoprea/go-exif/v3 v3.0.0-20200717071058-9393e7afd446/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= github.com/dsoprea/go-exif/v3 v3.0.0-20210428042052-dca55bf8ca15/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= +github.com/dsoprea/go-exif/v3 v3.0.0-20210512043655-120bcdb2a55e/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= +github.com/dsoprea/go-exif/v3 v3.0.0-20210512055020-8213cfabc61b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= github.com/dsoprea/go-exif/v3 v3.0.0-20221003160559-cf5cd88aa559/go.mod h1:rW6DMEv25U9zCtE5ukC7ttBRllXj7g7TAHl7tQrT5No= github.com/dsoprea/go-exif/v3 v3.0.0-20221003171958-de6cb6e380a8/go.mod h1:akyZEJZ/k5bmbC9gA612ZLQkcED8enS9vuTiuAkENr0= github.com/dsoprea/go-exif/v3 v3.0.1 h1:/IE4iW7gvY7BablV1XY0unqhMv26EYpOquVMwoBo/wc= github.com/dsoprea/go-exif/v3 v3.0.1/go.mod h1:10HkA1Wz3h398cDP66L+Is9kKDmlqlIJGPv8pk4EWvc= +github.com/dsoprea/go-heic-exif-extractor/v2 v2.0.0-20210512044107-62067e44c235 h1:a/XFkZdudAjXegNFRIf5vFjsF9LgFbiR5kjfHJZfIRA= +github.com/dsoprea/go-heic-exif-extractor/v2 v2.0.0-20210512044107-62067e44c235/go.mod h1:3mWA3lvkafMCuqoYKYXx/9YQgonsiAXf+KPSNlB/ZtI= github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4= github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM= +github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20210512043942-b434301c6836/go.mod h1:WaARaUjQuSuDCDFAiU/GwzfxMTJBulfEhqEA2Tx6B4Y= github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20221012074422-4f3f7e934102 h1:gmTXQdSuuuORRFPTS2uaYpAXU5oUNkXdeYSlZe5NvsE= github.com/dsoprea/go-jpeg-image-structure/v2 v2.0.0-20221012074422-4f3f7e934102/go.mod h1:WaARaUjQuSuDCDFAiU/GwzfxMTJBulfEhqEA2Tx6B4Y= github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA= @@ -63,6 +96,12 @@ github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6 github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c h1:7j5aWACOzROpr+dvMtu8GnI97g9ShLWD72XIELMgn+c= github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E= +github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d h1:8+qI8ant/vZkNSsbwSjIR6XJfWcDVTg/qx/3pRUUZNA= +github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d/go.mod h1:yTR3tKgyk20phAFg6IE9ulMA5NjEDD2wyx+okRFLVtw= +github.com/dsoprea/go-png-image-structure/v2 v2.0.0-20210512044023-23bdd883ee8e h1:7xT+Xgi019P9KVdcl+QUnSrgKCwIZWqkPNk5GygFTsw= +github.com/dsoprea/go-png-image-structure/v2 v2.0.0-20210512044023-23bdd883ee8e/go.mod h1:scnx0wQSM7UiCMK66dSdiPZvL2hl6iF5DvpZ7uT59MY= +github.com/dsoprea/go-tiff-image-structure/v2 v2.0.0-20210512044046-dc78da6a809b h1:r5TpplS/qPIevKTw6B9ft3p00FP6Flp3lXizranDiVQ= +github.com/dsoprea/go-tiff-image-structure/v2 v2.0.0-20210512044046-dc78da6a809b/go.mod h1:O3HgJ0u+ZTGEk2HZq4/7OvE8QPXWrDM2I9hrD8Qq0o4= github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf h1:/w4QxepU4AHh3AuO6/g8y/YIIHH5+aKP3Bj8sg5cqhU= github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8= github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU= @@ -70,10 +109,17 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU= github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw= github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8= +github.com/dsoprea/go-webp-image-structure v0.0.0-20210512044215-f98af2b0401e h1:Hj7L0xxjASwligRr2F2qy7i4UOk422xcZDvWQSU4m8I= +github.com/dsoprea/go-webp-image-structure v0.0.0-20210512044215-f98af2b0401e/go.mod h1:SRCiMUH7zHuGUQAEnxmURDSsXUIQZfCrQiIkywxxnSs= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807/go.mod h1:Xoiu5VdKMvbRgHuY7+z64lhu/7lvax/22nzASF6GrO8= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanoberholster/imagemeta v0.3.1 h1:E4GUjXcvlVMjP9joN25+bBNf3Al3MTTfMqCrDOCW+LE= +github.com/evanoberholster/imagemeta v0.3.1/go.mod h1:V0vtDJmjTqvwAYO8r+u33NRVIMXQb0qSqEfImoKEiXM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= @@ -88,6 +134,8 @@ github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -109,14 +157,42 @@ github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgR github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo= github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= @@ -130,8 +206,12 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jaswdr/faker/v2 v2.8.1 h1:2AcPgHDBXYQregFUH9LgVZKfFupc4SIquYhp29sf5wQ= github.com/jaswdr/faker/v2 v2.8.1/go.mod h1:jZq+qzNQr8/P+5fHd9t3txe2GNPnthrTfohtnJ7B+68= +github.com/jdeng/goheif v0.0.0-20200323230657-a0d6a8b3e68f/go.mod h1:G7IyA3/eR9IFmUIPdyP3c0l4ZaqEvXAk876WfaQ8plc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -205,10 +285,12 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmYrbRiUgv+g37W5suLLLxwwniTSc= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= @@ -216,6 +298,9 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= @@ -263,6 +348,8 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/rtree v1.3.1 h1:xu3vJPKJrmGce7YJcFUCoqLrp9DTUEJBnVgdPSXHgHs= github.com/tidwall/rtree v1.3.1/go.mod h1:S+JSsqPTI8LfWA4xHBo5eXzie8WJLVFeppAutSegl6M= github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= @@ -281,10 +368,15 @@ github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfP github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM= github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4= github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= +github.com/wayneashleyberry/terminal-dimensions v1.0.0/go.mod h1:PW2XrtV6KmKOPhuf7wbtcmw1/IFnC39mryRET2XbxeE= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= @@ -297,24 +389,67 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= +go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -324,14 +459,35 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -355,7 +511,10 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -363,8 +522,32 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= @@ -372,6 +555,39 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= @@ -392,6 +608,11 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= @@ -400,3 +621,6 @@ modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/main.go b/main.go index f3c432e1..4b73dfac 100644 --- a/main.go +++ b/main.go @@ -52,11 +52,11 @@ func main() { sr := nidussync.Router() hr.Map("", sr) // default hr.Map("*", sr) // default - hr.Map(config.URLReport, publicreport.Router()) // report.mosquitoes.online - hr.Map(config.URLSync, sr) + hr.Map(config.DomainRMO, publicreport.Router()) // report.mosquitoes.online + hr.Map(config.DomainNidus, sr) r.Mount("/", hr) - log.Info().Str("report url", config.URLReport).Str("sync url", config.URLSync).Msg("Serving at URLs") + log.Info().Str("report url", config.DomainRMO).Str("sync url", config.DomainNidus).Msg("Serving at URLs") // Start up background processes ctx, cancel := context.WithCancel(context.Background()) diff --git a/platform/district.go b/platform/district.go index d9c821ba..076848d5 100644 --- a/platform/district.go +++ b/platform/district.go @@ -2,30 +2,50 @@ package platform import ( "context" + "errors" "fmt" "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/rs/zerolog/log" "github.com/stephenafamo/bob/dialect/psql" "github.com/stephenafamo/bob/dialect/psql/sm" ) -func DistrictForLocation(ctx context.Context, lng float64, lat float64) (*models.ImportDistrict, error) { - rows, err := models.ImportDistricts.Query( +func DistrictForLocation(ctx context.Context, lng float64, lat float64) (*models.ImportDistrict, *models.Organization, error) { + districts, err := models.ImportDistricts.Query( sm.Where( psql.F("ST_Contains", psql.Raw("geom_4326"), psql.F("ST_SetSRID", psql.F("ST_MakePoint", psql.Arg(lng), psql.Arg(lat)), psql.Arg(4326))), ), ).All(ctx, db.PGInstance.BobDB) + + log.Debug().Int("len", len(districts)).Float64("lng", lng).Float64("lat", lat).Msg("Attempting district match") if err != nil { - return nil, fmt.Errorf("failed to query district: %w", err) + return nil, nil, fmt.Errorf("failed to query district: %w", err) } - switch len(rows) { + switch len(districts) { case 0: - return nil, nil + return nil, nil, nil case 1: - return rows[0], nil + district := districts[0] + organizations, err := models.Organizations.Query( + sm.Where( + models.Organizations.Columns.ImportDistrictGid.EQ(psql.Arg(district.Gid)), + ), + ).All(ctx, db.PGInstance.BobDB) + if err != nil { + return nil, nil, fmt.Errorf("failed to query organization: %w", err) + } + switch len(organizations) { + case 0: + return nil, nil, nil + case 1: + return district, organizations[0], nil + default: + return nil, nil, errors.New("too many organizations") + } default: - return nil, nil + return nil, nil, errors.New("too many districts") } } diff --git a/public-report/endpoint.go b/public-report/endpoint.go index 397d536d..ef923f1c 100644 --- a/public-report/endpoint.go +++ b/public-report/endpoint.go @@ -8,9 +8,6 @@ import ( "github.com/rs/zerolog/log" ) -type ContextRegisterNotificationsComplete struct { - ReportID string -} type ContextRoot struct{} var ( diff --git a/public-report/image-upload.go b/public-report/image-upload.go index ea40a0cd..b566f06e 100644 --- a/public-report/image-upload.go +++ b/public-report/image-upload.go @@ -13,21 +13,27 @@ import ( "net/http" "time" - "github.com/aarondl/opt/omit" - "github.com/dsoprea/go-exif/v3" - exifcommon "github.com/dsoprea/go-exif/v3/common" - //"github.com/dsoprea/go-jpeg-image-structure/v2" "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/userfile" + "github.com/aarondl/opt/omit" + "github.com/aarondl/opt/omitnull" "github.com/google/uuid" "github.com/rs/zerolog/log" + "github.com/rwcarlsen/goexif/exif" "github.com/stephenafamo/bob" "github.com/stephenafamo/bob/dialect/psql" "github.com/stephenafamo/bob/dialect/psql/um" + //exif "github.com/rwcarlsen/goexif/exif" + //"github.com/dsoprea/go-exif-extra/format" ) +type GPS struct { + Latitude float64 + Longitude float64 +} + type ExifCollection struct { - GPS *exif.GpsInfo + GPS *GPS Tags map[string]string } @@ -42,43 +48,30 @@ type ImageUpload struct { UUID uuid.UUID } -func extractExif(file_bytes []byte) (result ExifCollection, err error) { +func extractExif(content_type string, file_bytes []byte) (result ExifCollection, err error) { + /* + Using "github.com/evanoberholster/imagemeta" + meta, err := imagemeta.Decode(bytes.NewReader(file_bytes)) + if err != nil { + return result, fmt.Errorf("Failed to decode image meta: %w", err) + } + result.GPS = &GPS{ + Latitude: meta.GPS.Latitude(), + Longitude: meta.GPS.Longitude(), + } + return result, err + */ - raw_exif, err := exif.SearchAndExtractExifWithReader(bytes.NewReader(file_bytes)) + exif, err := exif.Decode(bytes.NewReader(file_bytes)) if err != nil { - return result, fmt.Errorf("Failed to find exif: %w", err) + return result, fmt.Errorf("Failed to decode image meta: %w", err) } - im, err := exifcommon.NewIfdMappingWithStandard() - if err != nil { - return result, fmt.Errorf("Failed to create new idf mapping: %w", err) + lat, lng, _ := exif.LatLong() + result.GPS = &GPS{ + Latitude: lat, + Longitude: lng, } - ti := exif.NewTagIndex() - _, index, err := exif.Collect(im, ti, raw_exif) - if err != nil { - return result, fmt.Errorf("Failed to collect exif: %w", err) - } - ifd, err := index.RootIfd.ChildWithIfdPath(exifcommon.IfdGpsInfoStandardIfdIdentity) - if err != nil { - return result, fmt.Errorf("Failed to find gps exif: %w", err) - } - gi, err := ifd.GpsInfo() - if err != nil { - log.Info().Err(err).Msg("Failed to get GPS info for uploaded image") - result.GPS = nil - } else { - result.GPS = gi - } - result.Tags = make(map[string]string, 0) - - tags, _, err := exif.GetFlatExifData(raw_exif, &exif.ScanOptions{}) - if err != nil { - return result, fmt.Errorf("Failed to gather flat exif: %w", err) - } - for _, t := range tags { - result.Tags[t.TagName] = t.Formatted - } - log.Info().Str("GPS", fmt.Sprintf("%s", gi)).Int("count", len(result.Tags)).Msg("Extracted exif tags") - return result, nil + return result, err } func extractImageUpload(headers *multipart.FileHeader) (upload ImageUpload, err error) { @@ -91,11 +84,12 @@ func extractImageUpload(headers *multipart.FileHeader) (upload ImageUpload, err file_bytes, err := io.ReadAll(file) content_type := http.DetectContentType(file_bytes) - exif, err := extractExif(file_bytes) + exif, err := extractExif(content_type, file_bytes) if err != nil { //return upload, fmt.Errorf("Failed to extract EXIF data: %w", err) log.Warn().Err(err).Msg("Failed to extract EXIF data") } + log.Debug().Float64("lat", exif.GPS.Latitude).Float64("lng", exif.GPS.Longitude).Msg("extracted GPS from exif") i, format, err := image.Decode(bytes.NewReader(file_bytes)) if err != nil { @@ -143,7 +137,8 @@ func saveImageUploads(ctx context.Context, tx bob.Tx, uploads []ImageUpload) (mo ContentType: omit.From(u.ContentType), Created: omit.From(time.Now()), - //Location: omitnull.From(nil), + //Location: psql.Raw("NULL"), + Location: omitnull.FromPtr[string](nil), ResolutionX: omit.From(int32(u.Bounds.Max.X)), ResolutionY: omit.From(int32(u.Bounds.Max.Y)), StorageUUID: omit.From(u.UUID), @@ -158,7 +153,7 @@ func saveImageUploads(ctx context.Context, tx bob.Tx, uploads []ImageUpload) (mo if u.Exif.GPS != nil { _, err = psql.Update( um.Table("publicreport.image"), - um.SetCol("location").To(fmt.Sprintf("ST_GeometryFromText('Point(%f %f)')", u.Exif.GPS.Longitude.Decimal(), u.Exif.GPS.Latitude.Decimal())), + um.SetCol("location").To(fmt.Sprintf("ST_GeometryFromText('Point(%f %f)')", u.Exif.GPS.Longitude, u.Exif.GPS.Latitude)), um.Where(psql.Quote("id").EQ(psql.Arg(image.ID))), ).Exec(ctx, tx) } @@ -171,9 +166,11 @@ func saveImageUploads(ctx context.Context, tx bob.Tx, uploads []ImageUpload) (mo Value: omit.From(v), }) } - _, err = models.PublicreportImageExifs.Insert(bob.ToMods(exif_setters...)).Exec(ctx, tx) - if err != nil { - return images, fmt.Errorf("Failed to create photo exif records: %w", err) + if len(exif_setters) > 0 { + _, err = models.PublicreportImageExifs.Insert(bob.ToMods(exif_setters...)).Exec(ctx, tx) + if err != nil { + return images, fmt.Errorf("Failed to create photo exif records: %w", err) + } } images = append(images, image) log.Info().Int32("id", image.ID).Int("tags", len(u.Exif.Tags)).Msg("Saved an uploaded file to the database") diff --git a/public-report/quick.go b/public-report/quick.go index 561ddaae..bf2106c8 100644 --- a/public-report/quick.go +++ b/public-report/quick.go @@ -1,6 +1,7 @@ package publicreport import ( + "context" "fmt" "net/http" "strconv" @@ -8,11 +9,13 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/background" "github.com/Gleipnir-Technology/nidus-sync/comms" + "github.com/Gleipnir-Technology/nidus-sync/config" "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/h3utils" "github.com/Gleipnir-Technology/nidus-sync/htmlpage" + "github.com/Gleipnir-Technology/nidus-sync/platform" "github.com/aarondl/opt/omit" "github.com/aarondl/opt/omitnull" "github.com/rs/zerolog/log" @@ -21,10 +24,18 @@ import ( "github.com/stephenafamo/bob/dialect/psql/um" ) -type ContextQuick struct{} -type ContextQuickSubmitComplete struct { +type ContentQuick struct{} +type ContentQuickSubmitComplete struct { + District *District ReportID string } +type ContentRegisterNotificationsComplete struct { + ReportID string +} +type District struct { + LogoURL string + Name string +} var ( quickT = buildTemplate("quick", "base") @@ -36,16 +47,45 @@ func getQuick(w http.ResponseWriter, r *http.Request) { htmlpage.RenderOrError( w, quickT, - ContextQuick{}, + ContentQuick{}, ) } func getQuickSubmitComplete(w http.ResponseWriter, r *http.Request) { - report := r.URL.Query().Get("report") + ctx := r.Context() + report_id := r.URL.Query().Get("report") + report, err := models.PublicreportQuicks.Query( + models.SelectWhere.PublicreportQuicks.PublicID.EQ(report_id), + ).One(ctx, db.PGInstance.BobDB) + if err != nil { + respondError(w, "Failed to get report", err, http.StatusInternalServerError) + return + } + var district *District + if !report.OrganizationID.IsNull() { + org_id := report.OrganizationID.MustGet() + org, err := models.Organizations.Query( + models.Preload.Organization.ImportDistrictGidDistrict(), + models.SelectWhere.Organizations.ID.EQ(org_id), + ).One(ctx, db.PGInstance.BobDB) + if err != nil { + respondError(w, "Failed to get org", err, http.StatusInternalServerError) + return + } + d := org.R.ImportDistrictGidDistrict + log.Debug().Int32("org_id", org.ID).Int32("d_gid", d.Gid).Msg("Getting district") + if d != nil { + district = &District{ + LogoURL: config.MakeURLNidus("/api/district/%d/logo", org_id), + Name: d.Agency.GetOr("Unknown"), + } + } + } htmlpage.RenderOrError( w, quickSubmitCompleteT, - ContextQuickSubmitComplete{ - ReportID: report, + ContentQuickSubmitComplete{ + District: district, + ReportID: report.PublicID, }, ) } @@ -54,11 +94,38 @@ func getRegisterNotificationsComplete(w http.ResponseWriter, r *http.Request) { htmlpage.RenderOrError( w, registerNotificationsCompleteT, - ContextRegisterNotificationsComplete{ + ContentRegisterNotificationsComplete{ ReportID: report, }, ) } +func matchDistrict(ctx context.Context, longitude, latitude float64, images []ImageUpload) (*int32, error) { + for _, image := range images { + if image.Exif.GPS == nil { + continue + } + _, org, err := platform.DistrictForLocation(ctx, image.Exif.GPS.Longitude, image.Exif.GPS.Latitude) + if err != nil { + log.Warn().Err(err).Msg("Failed to get district for location") + continue + } + if org != nil { + return &org.ID, nil + } + } + _, org, err := platform.DistrictForLocation(ctx, longitude, latitude) + if err != nil { + log.Warn().Err(err).Msg("Failed to get district for location") + return nil, fmt.Errorf("Failed to get district for location: %w", err) + } + if org == nil { + log.Debug().Err(err).Float64("lng", longitude).Float64("lat", latitude).Msg("No district match by report location") + return nil, nil + } + log.Debug().Err(err).Int32("org_id", org.ID).Float64("lng", longitude).Float64("lat", latitude).Msg("Found district match by report location") + return &org.ID, nil +} + func postQuick(w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(32 << 10) // 32 MB buffer if err != nil { @@ -92,11 +159,30 @@ func postQuick(w http.ResponseWriter, r *http.Request) { } defer tx.Rollback(ctx) + uploads, err := extractImageUploads(r) + log.Info().Int("len", len(uploads)).Msg("extracted uploads") + if err != nil { + respondError(w, "Failed to extract image uploads", err, http.StatusInternalServerError) + return + } + images, err := saveImageUploads(ctx, tx, uploads) + if err != nil { + respondError(w, "Failed to save image uploads", err, http.StatusInternalServerError) + return + } + + organization_id, err := matchDistrict(ctx, longitude, latitude, uploads) + if err != nil { + log.Warn().Err(err).Msg("Failed to match district") + } + + log.Info().Int("len", len(images)).Msg("saved uploads") c, err := h3utils.GetCell(longitude, latitude, 15) setter := models.PublicreportQuickSetter{ - Address: omit.From(""), - Created: omit.From(time.Now()), - Comments: omit.From(comments), + Address: omit.From(""), + Created: omit.From(time.Now()), + Comments: omit.From(comments), + OrganizationID: omitnull.FromPtr(organization_id), //Location: omitnull.From(fmt.Sprintf("ST_GeometryFromText(Point(%s %s))", longitude, latitude)), H3cell: omitnull.From(c.String()), PublicID: omit.From(u), @@ -119,18 +205,6 @@ func postQuick(w http.ResponseWriter, r *http.Request) { return } log.Info().Float64("latitude", latitude).Float64("longitude", longitude).Msg("Got upload") - uploads, err := extractImageUploads(r) - log.Info().Int("len", len(uploads)).Msg("extracted uploads") - if err != nil { - respondError(w, "Failed to extract image uploads", err, http.StatusInternalServerError) - return - } - images, err := saveImageUploads(ctx, tx, uploads) - if err != nil { - respondError(w, "Failed to save image uploads", err, http.StatusInternalServerError) - return - } - log.Info().Int("len", len(images)).Msg("saved uploads") if len(images) > 0 { setters := make([]*models.PublicreportQuickImageSetter, 0) for _, image := range images { diff --git a/public-report/search.go b/public-report/search.go index 1ed6a637..e66e1566 100644 --- a/public-report/search.go +++ b/public-report/search.go @@ -22,7 +22,7 @@ func getSearch(w http.ResponseWriter, r *http.Request) { Search, ContentSearch{ MapboxToken: config.MapboxToken, - URLTegola: config.URLTegola, + URLTegola: config.MakeURLTegola("/"), }, ) } diff --git a/public-report/status.go b/public-report/status.go index f9cd6641..04a10351 100644 --- a/public-report/status.go +++ b/public-report/status.go @@ -38,6 +38,7 @@ type Report struct { Address string Comments string Created time.Time + District string ID string Images []Image Location string // GeoJSON @@ -175,6 +176,7 @@ func contentFromQuick(ctx context.Context, report_id string) (result ContentStat result.Report.Address = quick.Address result.Report.Comments = quick.Comments result.Report.Created = quick.Created + result.Report.District = "Unknown" result.Report.Reporter.Email = quick.ReporterEmail result.Report.Reporter.Name = "-" result.Report.Reporter.Phone = quick.ReporterPhone @@ -183,7 +185,7 @@ func contentFromQuick(ctx context.Context, report_id string) (result ContentStat for _, image := range images { result.Report.Images = append(result.Report.Images, Image{ Location: image.LocationJSON, - URL: fmt.Sprintf("https://%s/image/%s", config.RMODomain, image.StorageUUID), + URL: config.MakeURLReport("/image/%s", image.StorageUUID), }) } type LocationGeoJSON struct { diff --git a/public-report/template/quick-submit-complete.html b/public-report/template/quick-submit-complete.html index da711c4b..333059d6 100644 --- a/public-report/template/quick-submit-complete.html +++ b/public-report/template/quick-submit-complete.html @@ -29,6 +29,11 @@ {{.ReportID|publicReportID}}

Please save this ID for your reference.

+ {{ if not (eq .District nil) }} +

Your report has been assigned to

+

{{ .District.Name }}

+ + {{ end }}
diff --git a/public-report/template/status-by-id.html b/public-report/template/status-by-id.html index 35bebe4f..3daf7b16 100644 --- a/public-report/template/status-by-id.html +++ b/public-report/template/status-by-id.html @@ -82,8 +82,8 @@ document.addEventListener("DOMContentLoaded", onLoad); {{.Report.Created|timeSince}}
- Next Step: - July 19, 2023 (Estimated) + District: + {{.Report.District}}
diff --git a/sync/dash.go b/sync/dash.go index 11858361..c0233739 100644 --- a/sync/dash.go +++ b/sync/dash.go @@ -284,7 +284,7 @@ func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) { } data := ContextDashboard{ Config: Config{ - URLTegola: config.URLTegola, + URLTegola: config.MakeURLTegola("/"), }, CountTraps: int(trapCount), CountMosquitoSources: int(sourceCount), diff --git a/sync/mock.go b/sync/mock.go index 0425e17d..923c3840 100644 --- a/sync/mock.go +++ b/sync/mock.go @@ -49,7 +49,7 @@ func getQRCodeReport(w http.ResponseWriter, r *http.Request) { if code == "" { respondError(w, "There should always be a code", nil, http.StatusBadRequest) } - content := config.MakeURLSync("/report/" + code) + content := config.MakeURLNidus("/report/%s", code) // Get optional size parameter (default to 256) size := 256 if sizeStr := r.URL.Query().Get("size"); sizeStr != "" { diff --git a/sync/oauth.go b/sync/oauth.go index cac03063..25129363 100644 --- a/sync/oauth.go +++ b/sync/oauth.go @@ -2,6 +2,8 @@ package sync import ( "net/http" + "net/url" + "strconv" "github.com/Gleipnir-Technology/nidus-sync/auth" "github.com/Gleipnir-Technology/nidus-sync/background" @@ -19,8 +21,33 @@ type ContextOauthPrompt struct { User User } +// Build the ArcGIS authorization URL with PKCE +func buildArcGISAuthURL(clientID string) string { + baseURL := "https://www.arcgis.com/sharing/rest/oauth2/authorize/" + + params := url.Values{} + params.Add("client_id", clientID) + params.Add("redirect_uri", config.ArcGISOauthRedirectURL()) + params.Add("response_type", "code") + //params.Add("code_challenge", generateCodeChallenge(codeVerifier)) + //params.Add("code_challenge_method", "S256") + + // See https://developers.arcgis.com/rest/users-groups-and-items/token/ + // expiration is defined in minutes + var expiration int + if config.IsProductionEnvironment() { + // 2 weeks is the maximum allowed + expiration = 20160 + } else { + expiration = 20 + } + params.Add("expiration", strconv.Itoa(expiration)) + + return baseURL + "?" + params.Encode() +} + func getArcgisOauthBegin(w http.ResponseWriter, r *http.Request) { - authURL := config.BuildArcGISAuthURL(config.ClientID) + authURL := buildArcGISAuthURL(config.ClientID) http.Redirect(w, r, authURL, http.StatusFound) } @@ -41,7 +68,7 @@ func getArcgisOauthCallback(w http.ResponseWriter, r *http.Request) { respondError(w, "Failed to handle access code", err, http.StatusInternalServerError) return } - http.Redirect(w, r, config.MakeURLSync("/"), http.StatusFound) + http.Redirect(w, r, config.MakeURLNidus("/"), http.StatusFound) } func getOAuthRefresh(w http.ResponseWriter, r *http.Request) { diff --git a/userfile/userfile.go b/userfile/userfile.go index aceea6ea..fc9f20d6 100644 --- a/userfile/userfile.go +++ b/userfile/userfile.go @@ -41,11 +41,17 @@ func AudioFileContentWrite(audioUUID uuid.UUID, body io.Reader) error { log.Info().Str("filepath", filepath).Msg("Save audio file content") return nil } -func ImageFileContentPathRaw(uid string) string { - return fmt.Sprintf("%s/%s.raw", config.FilesDirectoryUser, uid) +func ImageFileContentPathRawUser(uid string) string { + return imageFileContentPath(config.FilesDirectoryUser, uid, "raw") +} +func imageFileContentPathLogoPng(uid string) string { + return imageFileContentPath(config.FilesDirectoryLogo, uid, "png") +} +func imageFileContentPath(dir string, uid string, ext string) string { + return fmt.Sprintf("%s/%s.%s", dir, uid, ext) } func ImageFileContentWrite(uid uuid.UUID, body io.Reader) error { - filepath := ImageFileContentPathRaw(uid.String()) + filepath := ImageFileContentPathRawUser(uid.String()) // Create file in configured directory dst, err := os.Create(filepath) @@ -61,6 +67,11 @@ func ImageFileContentWrite(uid uuid.UUID, body io.Reader) error { } return nil } +func ImageFileContentWriteLogo(w http.ResponseWriter, uid uuid.UUID) { + image_path := imageFileContentPathLogoPng(uid.String()) + writeFileContent(w, image_path) +} + func PublicImageFileContentWrite(uid uuid.UUID, body io.Reader) error { // Create file in configured directory filepath := PublicImageFileContentPathRaw(uid.String()) @@ -86,7 +97,10 @@ func PublicImageFileContentPathRaw(uid string) string { func PublicImageFileToResponse(w http.ResponseWriter, uid string) { image_path := PublicImageFileContentPathRaw(uid) + writeFileContent(w, image_path) +} +func writeFileContent(w http.ResponseWriter, image_path string) { // Open the file file, err := os.Open(image_path) if err != nil {