Make organization.name not-nullable, consolidate org in dash context

This commit is contained in:
Eli Ribble 2026-01-14 21:49:12 +00:00
parent 4f0b73c769
commit a4c0e367a8
No known key found for this signature in database
13 changed files with 118 additions and 50 deletions

View file

@ -131,7 +131,7 @@ func SignupUser(ctx context.Context, username string, name string, password stri
return nil, fmt.Errorf("Cannot signup user, failed to create hashed password: %w", err) return nil, fmt.Errorf("Cannot signup user, failed to create hashed password: %w", err)
} }
o_setter := models.OrganizationSetter{ o_setter := models.OrganizationSetter{
Name: omitnull.From(fmt.Sprintf("%s's organization", username)), Name: omit.From(fmt.Sprintf("%s's organization", username)),
ArcgisID: omitnull.From(""), ArcgisID: omitnull.From(""),
ArcgisName: omitnull.From(""), ArcgisName: omitnull.From(""),
FieldseekerURL: omitnull.From(""), FieldseekerURL: omitnull.From(""),

View file

@ -213,6 +213,15 @@ var Districts = Table[
Generated: false, Generated: false,
AutoIncr: false, AutoIncr: false,
}, },
Geom4326: column{
Name: "geom_4326",
DBType: "geometry",
Default: "GENERATED",
Comment: "",
Nullable: true,
Generated: true,
AutoIncr: false,
},
}, },
Indexes: districtIndexes{ Indexes: districtIndexes{
DistrictPkey: index{ DistrictPkey: index{
@ -282,11 +291,12 @@ type districtColumns struct {
ShapeLe1 column ShapeLe1 column
ShapeArea column ShapeArea column
Geom column Geom column
Geom4326 column
} }
func (c districtColumns) AsSlice() []column { func (c districtColumns) AsSlice() []column {
return []column{ return []column{
c.Gid, c.ID, c.Website, c.Contact, c.Address, c.Regionid, c.PostalCod, c.Phone1, c.Fax1, c.Agency, c.Code1, c.City1, c.ShapeLeng, c.Address2, c.GeneralMG, c.City2, c.PostalC1, c.Fax2, c.Phone2, c.ShapeLe1, c.ShapeArea, c.Geom, c.Gid, c.ID, c.Website, c.Contact, c.Address, c.Regionid, c.PostalCod, c.Phone1, c.Fax1, c.Agency, c.Code1, c.City1, c.ShapeLeng, c.Address2, c.GeneralMG, c.City2, c.PostalC1, c.Fax2, c.Phone2, c.ShapeLe1, c.ShapeArea, c.Geom, c.Geom4326,
} }
} }

View file

@ -27,9 +27,9 @@ var Organizations = Table[
Name: column{ Name: column{
Name: "name", Name: "name",
DBType: "text", DBType: "text",
Default: "NULL", Default: "",
Comment: "", Comment: "",
Nullable: true, Nullable: false,
Generated: false, Generated: false,
AutoIncr: false, AutoIncr: false,
}, },

View file

@ -118,6 +118,7 @@ func (f *Factory) FromExistingDistrict(m *models.District) *DistrictTemplate {
o.ShapeLe1 = func() null.Val[decimal.Decimal] { return m.ShapeLe1 } o.ShapeLe1 = func() null.Val[decimal.Decimal] { return m.ShapeLe1 }
o.ShapeArea = func() null.Val[decimal.Decimal] { return m.ShapeArea } o.ShapeArea = func() null.Val[decimal.Decimal] { return m.ShapeArea }
o.Geom = func() null.Val[string] { return m.Geom } o.Geom = func() null.Val[string] { return m.Geom }
o.Geom4326 = func() null.Val[string] { return m.Geom4326 }
return o return o
} }
@ -2317,7 +2318,7 @@ func (f *Factory) FromExistingOrganization(m *models.Organization) *Organization
o := &OrganizationTemplate{f: f, alreadyPersisted: true} o := &OrganizationTemplate{f: f, alreadyPersisted: true}
o.ID = func() int32 { return m.ID } o.ID = func() int32 { return m.ID }
o.Name = func() null.Val[string] { return m.Name } o.Name = func() string { return m.Name }
o.ArcgisID = func() null.Val[string] { return m.ArcgisID } o.ArcgisID = func() null.Val[string] { return m.ArcgisID }
o.ArcgisName = func() null.Val[string] { return m.ArcgisName } o.ArcgisName = func() null.Val[string] { return m.ArcgisName }
o.FieldseekerURL = func() null.Val[string] { return m.FieldseekerURL } o.FieldseekerURL = func() null.Val[string] { return m.FieldseekerURL }

View file

@ -59,6 +59,7 @@ type DistrictTemplate struct {
ShapeLe1 func() null.Val[decimal.Decimal] ShapeLe1 func() null.Val[decimal.Decimal]
ShapeArea func() null.Val[decimal.Decimal] ShapeArea func() null.Val[decimal.Decimal]
Geom func() null.Val[string] Geom func() null.Val[string]
Geom4326 func() null.Val[string]
f *Factory f *Factory
@ -257,6 +258,9 @@ func (o DistrictTemplate) Build() *models.District {
if o.Geom != nil { if o.Geom != nil {
m.Geom = o.Geom() m.Geom = o.Geom()
} }
if o.Geom4326 != nil {
m.Geom4326 = o.Geom4326()
}
o.setModelRels(m) o.setModelRels(m)
@ -399,6 +403,7 @@ func (m districtMods) RandomizeAllColumns(f *faker.Faker) DistrictMod {
DistrictMods.RandomShapeLe1(f), DistrictMods.RandomShapeLe1(f),
DistrictMods.RandomShapeArea(f), DistrictMods.RandomShapeArea(f),
DistrictMods.RandomGeom(f), DistrictMods.RandomGeom(f),
DistrictMods.RandomGeom4326(f),
} }
} }
@ -1546,6 +1551,59 @@ func (m districtMods) RandomGeomNotNull(f *faker.Faker) DistrictMod {
}) })
} }
// Set the model columns to this value
func (m districtMods) Geom4326(val null.Val[string]) DistrictMod {
return DistrictModFunc(func(_ context.Context, o *DistrictTemplate) {
o.Geom4326 = func() null.Val[string] { return val }
})
}
// Set the Column from the function
func (m districtMods) Geom4326Func(f func() null.Val[string]) DistrictMod {
return DistrictModFunc(func(_ context.Context, o *DistrictTemplate) {
o.Geom4326 = f
})
}
// Clear any values for the column
func (m districtMods) UnsetGeom4326() DistrictMod {
return DistrictModFunc(func(_ context.Context, o *DistrictTemplate) {
o.Geom4326 = 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 districtMods) RandomGeom4326(f *faker.Faker) DistrictMod {
return DistrictModFunc(func(_ context.Context, o *DistrictTemplate) {
o.Geom4326 = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(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 districtMods) RandomGeom4326NotNull(f *faker.Faker) DistrictMod {
return DistrictModFunc(func(_ context.Context, o *DistrictTemplate) {
o.Geom4326 = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
func (m districtMods) WithParentsCascading() DistrictMod { func (m districtMods) WithParentsCascading() DistrictMod {
return DistrictModFunc(func(ctx context.Context, o *DistrictTemplate) { return DistrictModFunc(func(ctx context.Context, o *DistrictTemplate) {
if isDone, _ := districtWithParentsCascadingCtx.Value(ctx); isDone { if isDone, _ := districtWithParentsCascadingCtx.Value(ctx); isDone {

View file

@ -37,7 +37,7 @@ func (mods OrganizationModSlice) Apply(ctx context.Context, n *OrganizationTempl
// all columns are optional and should be set by mods // all columns are optional and should be set by mods
type OrganizationTemplate struct { type OrganizationTemplate struct {
ID func() int32 ID func() int32
Name func() null.Val[string] Name func() string
ArcgisID func() null.Val[string] ArcgisID func() null.Val[string]
ArcgisName func() null.Val[string] ArcgisName func() null.Val[string]
FieldseekerURL func() null.Val[string] FieldseekerURL func() null.Val[string]
@ -650,7 +650,7 @@ func (o OrganizationTemplate) BuildSetter() *models.OrganizationSetter {
} }
if o.Name != nil { if o.Name != nil {
val := o.Name() val := o.Name()
m.Name = omitnull.FromNull(val) m.Name = omit.From(val)
} }
if o.ArcgisID != nil { if o.ArcgisID != nil {
val := o.ArcgisID() val := o.ArcgisID()
@ -721,6 +721,10 @@ func (o OrganizationTemplate) BuildMany(number int) models.OrganizationSlice {
} }
func ensureCreatableOrganization(m *models.OrganizationSetter) { func ensureCreatableOrganization(m *models.OrganizationSetter) {
if !(m.Name.IsValue()) {
val := random_string(nil)
m.Name = omit.From(val)
}
} }
// insertOptRels creates and inserts any optional the relationships on *models.Organization // insertOptRels creates and inserts any optional the relationships on *models.Organization
@ -1501,14 +1505,14 @@ func (m organizationMods) RandomID(f *faker.Faker) OrganizationMod {
} }
// Set the model columns to this value // Set the model columns to this value
func (m organizationMods) Name(val null.Val[string]) OrganizationMod { func (m organizationMods) Name(val string) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.Name = func() null.Val[string] { return val } o.Name = func() string { return val }
}) })
} }
// Set the Column from the function // Set the Column from the function
func (m organizationMods) NameFunc(f func() null.Val[string]) OrganizationMod { func (m organizationMods) NameFunc(f func() string) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.Name = f o.Name = f
}) })
@ -1523,32 +1527,10 @@ func (m organizationMods) UnsetName() OrganizationMod {
// Generates a random value for the column using the given faker // Generates a random value for the column using the given faker
// if faker is nil, a default faker is used // if faker is nil, a default faker is used
// The generated value is sometimes null
func (m organizationMods) RandomName(f *faker.Faker) OrganizationMod { func (m organizationMods) RandomName(f *faker.Faker) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) { return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.Name = func() null.Val[string] { o.Name = func() string {
if f == nil { return random_string(f)
f = &defaultFaker
}
val := random_string(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) RandomNameNotNull(f *faker.Faker) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.Name = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
} }
}) })
} }

View file

@ -0,0 +1,5 @@
-- +goose Up
ALTER TABLE organization ALTER COLUMN name SET NOT NULL;
-- +goose Down
ALTER TABLE organization ALTER COLUMN name DROP NOT NULL;

View file

@ -44,6 +44,7 @@ type District struct {
ShapeLe1 null.Val[decimal.Decimal] `db:"shape_le_1" ` ShapeLe1 null.Val[decimal.Decimal] `db:"shape_le_1" `
ShapeArea null.Val[decimal.Decimal] `db:"shape_area" ` ShapeArea null.Val[decimal.Decimal] `db:"shape_area" `
Geom null.Val[string] `db:"geom" ` Geom null.Val[string] `db:"geom" `
Geom4326 null.Val[string] `db:"geom_4326,generated" `
} }
// DistrictSlice is an alias for a slice of pointers to District. // DistrictSlice is an alias for a slice of pointers to District.
@ -59,7 +60,7 @@ type DistrictsQuery = *psql.ViewQuery[*District, DistrictSlice]
func buildDistrictColumns(alias string) districtColumns { func buildDistrictColumns(alias string) districtColumns {
return districtColumns{ return districtColumns{
ColumnsExpr: expr.NewColumnsExpr( ColumnsExpr: expr.NewColumnsExpr(
"gid", "id", "website", "contact", "address", "regionid", "postal_cod", "phone1", "fax1", "agency", "code1", "city1", "shape_leng", "address2", "general_mg", "city2", "postal_c_1", "fax2", "phone2", "shape_le_1", "shape_area", "geom", "gid", "id", "website", "contact", "address", "regionid", "postal_cod", "phone1", "fax1", "agency", "code1", "city1", "shape_leng", "address2", "general_mg", "city2", "postal_c_1", "fax2", "phone2", "shape_le_1", "shape_area", "geom", "geom_4326",
).WithParent("district"), ).WithParent("district"),
tableAlias: alias, tableAlias: alias,
Gid: psql.Quote(alias, "gid"), Gid: psql.Quote(alias, "gid"),
@ -84,6 +85,7 @@ func buildDistrictColumns(alias string) districtColumns {
ShapeLe1: psql.Quote(alias, "shape_le_1"), ShapeLe1: psql.Quote(alias, "shape_le_1"),
ShapeArea: psql.Quote(alias, "shape_area"), ShapeArea: psql.Quote(alias, "shape_area"),
Geom: psql.Quote(alias, "geom"), Geom: psql.Quote(alias, "geom"),
Geom4326: psql.Quote(alias, "geom_4326"),
} }
} }
@ -112,6 +114,7 @@ type districtColumns struct {
ShapeLe1 psql.Expression ShapeLe1 psql.Expression
ShapeArea psql.Expression ShapeArea psql.Expression
Geom psql.Expression Geom psql.Expression
Geom4326 psql.Expression
} }
func (c districtColumns) Alias() string { func (c districtColumns) Alias() string {
@ -842,6 +845,7 @@ type districtWhere[Q psql.Filterable] struct {
ShapeLe1 psql.WhereNullMod[Q, decimal.Decimal] ShapeLe1 psql.WhereNullMod[Q, decimal.Decimal]
ShapeArea psql.WhereNullMod[Q, decimal.Decimal] ShapeArea psql.WhereNullMod[Q, decimal.Decimal]
Geom psql.WhereNullMod[Q, string] Geom psql.WhereNullMod[Q, string]
Geom4326 psql.WhereNullMod[Q, string]
} }
func (districtWhere[Q]) AliasedAs(alias string) districtWhere[Q] { func (districtWhere[Q]) AliasedAs(alias string) districtWhere[Q] {
@ -872,5 +876,6 @@ func buildDistrictWhere[Q psql.Filterable](cols districtColumns) districtWhere[Q
ShapeLe1: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLe1), ShapeLe1: psql.WhereNull[Q, decimal.Decimal](cols.ShapeLe1),
ShapeArea: psql.WhereNull[Q, decimal.Decimal](cols.ShapeArea), ShapeArea: psql.WhereNull[Q, decimal.Decimal](cols.ShapeArea),
Geom: psql.WhereNull[Q, string](cols.Geom), Geom: psql.WhereNull[Q, string](cols.Geom),
Geom4326: psql.WhereNull[Q, string](cols.Geom4326),
} }
} }

View file

@ -26,7 +26,7 @@ import (
// Organization is an object representing the database table. // Organization is an object representing the database table.
type Organization struct { type Organization struct {
ID int32 `db:"id,pk" ` ID int32 `db:"id,pk" `
Name null.Val[string] `db:"name" ` Name string `db:"name" `
ArcgisID null.Val[string] `db:"arcgis_id" ` ArcgisID null.Val[string] `db:"arcgis_id" `
ArcgisName null.Val[string] `db:"arcgis_name" ` ArcgisName null.Val[string] `db:"arcgis_name" `
FieldseekerURL null.Val[string] `db:"fieldseeker_url" ` FieldseekerURL null.Val[string] `db:"fieldseeker_url" `
@ -117,7 +117,7 @@ func (organizationColumns) AliasedAs(alias string) organizationColumns {
// Generated columns are not included // Generated columns are not included
type OrganizationSetter struct { type OrganizationSetter struct {
ID omit.Val[int32] `db:"id,pk" ` ID omit.Val[int32] `db:"id,pk" `
Name omitnull.Val[string] `db:"name" ` Name omit.Val[string] `db:"name" `
ArcgisID omitnull.Val[string] `db:"arcgis_id" ` ArcgisID omitnull.Val[string] `db:"arcgis_id" `
ArcgisName omitnull.Val[string] `db:"arcgis_name" ` ArcgisName omitnull.Val[string] `db:"arcgis_name" `
FieldseekerURL omitnull.Val[string] `db:"fieldseeker_url" ` FieldseekerURL omitnull.Val[string] `db:"fieldseeker_url" `
@ -128,7 +128,7 @@ func (s OrganizationSetter) SetColumns() []string {
if s.ID.IsValue() { if s.ID.IsValue() {
vals = append(vals, "id") vals = append(vals, "id")
} }
if !s.Name.IsUnset() { if s.Name.IsValue() {
vals = append(vals, "name") vals = append(vals, "name")
} }
if !s.ArcgisID.IsUnset() { if !s.ArcgisID.IsUnset() {
@ -147,8 +147,8 @@ func (s OrganizationSetter) Overwrite(t *Organization) {
if s.ID.IsValue() { if s.ID.IsValue() {
t.ID = s.ID.MustGet() t.ID = s.ID.MustGet()
} }
if !s.Name.IsUnset() { if s.Name.IsValue() {
t.Name = s.Name.MustGetNull() t.Name = s.Name.MustGet()
} }
if !s.ArcgisID.IsUnset() { if !s.ArcgisID.IsUnset() {
t.ArcgisID = s.ArcgisID.MustGetNull() t.ArcgisID = s.ArcgisID.MustGetNull()
@ -174,8 +174,8 @@ func (s *OrganizationSetter) Apply(q *dialect.InsertQuery) {
vals[0] = psql.Raw("DEFAULT") vals[0] = psql.Raw("DEFAULT")
} }
if !s.Name.IsUnset() { if s.Name.IsValue() {
vals[1] = psql.Arg(s.Name.MustGetNull()) vals[1] = psql.Arg(s.Name.MustGet())
} else { } else {
vals[1] = psql.Raw("DEFAULT") vals[1] = psql.Raw("DEFAULT")
} }
@ -216,7 +216,7 @@ func (s OrganizationSetter) Expressions(prefix ...string) []bob.Expression {
}}) }})
} }
if !s.Name.IsUnset() { if s.Name.IsValue() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "name")...), psql.Quote(append(prefix, "name")...),
psql.Arg(s.Name), psql.Arg(s.Name),
@ -3416,7 +3416,7 @@ func (organization0 *Organization) AttachUser(ctx context.Context, exec bob.Exec
type organizationWhere[Q psql.Filterable] struct { type organizationWhere[Q psql.Filterable] struct {
ID psql.WhereMod[Q, int32] ID psql.WhereMod[Q, int32]
Name psql.WhereNullMod[Q, string] Name psql.WhereMod[Q, string]
ArcgisID psql.WhereNullMod[Q, string] ArcgisID psql.WhereNullMod[Q, string]
ArcgisName psql.WhereNullMod[Q, string] ArcgisName psql.WhereNullMod[Q, string]
FieldseekerURL psql.WhereNullMod[Q, string] FieldseekerURL psql.WhereNullMod[Q, string]
@ -3429,7 +3429,7 @@ func (organizationWhere[Q]) AliasedAs(alias string) organizationWhere[Q] {
func buildOrganizationWhere[Q psql.Filterable](cols organizationColumns) organizationWhere[Q] { func buildOrganizationWhere[Q psql.Filterable](cols organizationColumns) organizationWhere[Q] {
return organizationWhere[Q]{ return organizationWhere[Q]{
ID: psql.Where[Q, int32](cols.ID), ID: psql.Where[Q, int32](cols.ID),
Name: psql.WhereNull[Q, string](cols.Name), Name: psql.Where[Q, string](cols.Name),
ArcgisID: psql.WhereNull[Q, string](cols.ArcgisID), ArcgisID: psql.WhereNull[Q, string](cols.ArcgisID),
ArcgisName: psql.WhereNull[Q, string](cols.ArcgisName), ArcgisName: psql.WhereNull[Q, string](cols.ArcgisName),
FieldseekerURL: psql.WhereNull[Q, string](cols.FieldseekerURL), FieldseekerURL: psql.WhereNull[Q, string](cols.FieldseekerURL),

View file

@ -42,7 +42,6 @@ type ContextDashboard struct {
IsSyncOngoing bool IsSyncOngoing bool
LastSync *time.Time LastSync *time.Time
MapData ComponentMap MapData ComponentMap
Org string
RecentRequests []ServiceRequestSummary RecentRequests []ServiceRequestSummary
User User User User
} }
@ -249,7 +248,6 @@ func dashboard(ctx context.Context, w http.ResponseWriter, user *models.User) {
MapData: ComponentMap{ MapData: ComponentMap{
MapboxToken: config.MapboxToken, MapboxToken: config.MapboxToken,
}, },
Org: org.Name.MustGet(),
RecentRequests: requests, RecentRequests: requests,
User: userContent, User: userContent,
} }

View file

@ -28,7 +28,7 @@ function onLoad() {
map.addSource('tegola-nidus', { map.addSource('tegola-nidus', {
'type': 'vector', 'type': 'vector',
'tiles': [ 'tiles': [
'https://{{.Config.URLTegola}}/maps/nidus/{z}/{x}/{y}?organization_id={{.User.OrganizationID}}' 'https://{{.Config.URLTegola}}/maps/nidus/{z}/{x}/{y}?organization_id={{.User.Organization.ID}}'
] ]
//'minzoom': 6, //'minzoom': 6,
//'maxzoom': 14 //'maxzoom': 14
@ -172,7 +172,7 @@ body {
<!-- Dashboard Header --> <!-- Dashboard Header -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-md-6"> <div class="col-md-6">
<h1>{{ .Org }} Dashboard</h1> <h1>{{ .User.Organization.Name }} Dashboard</h1>
<p class="text-muted">Overview of mosquito control activities in your district</p> <p class="text-muted">Overview of mosquito control activities in your district</p>
</div> </div>
<div class="col-md-6 text-md-end d-flex align-items-center justify-content-md-end"> <div class="col-md-6 text-md-end d-flex align-items-center justify-content-md-end">

View file

@ -94,6 +94,10 @@ type Link struct {
Href string Href string
Title string Title string
} }
type Organization struct {
ID int
Name string
}
type ServiceRequestSummary struct { type ServiceRequestSummary struct {
Date time.Time Date time.Time
Location string Location string
@ -103,6 +107,6 @@ type User struct {
DisplayName string DisplayName string
Initials string Initials string
Notifications []notification.Notification Notifications []notification.Notification
OrganizationID int Organization Organization
Username string Username string
} }

View file

@ -95,10 +95,15 @@ func contentForUser(ctx context.Context, user *models.User) (User, error) {
if err != nil { if err != nil {
return User{}, err return User{}, err
} }
org := user.R.Organization
return User{ return User{
DisplayName: user.DisplayName, DisplayName: user.DisplayName,
Initials: extractInitials(user.DisplayName), Initials: extractInitials(user.DisplayName),
Notifications: notifications, Notifications: notifications,
Organization: Organization {
ID: int(org.ID),
Name: org.Name,
},
Username: user.Username, Username: user.Username,
}, nil }, nil