diff --git a/db/bobgen.yaml b/db/bobgen.yaml index 184c0432..8cb9b619 100644 --- a/db/bobgen.yaml +++ b/db/bobgen.yaml @@ -18,7 +18,6 @@ aliases: no_tests: true psql: schemas: - - "comms" - "fieldseeker" - "fileupload" - "lob" diff --git a/db/gen/nidus-sync/comms/model/contact.go b/db/gen/nidus-sync/comms/model/contact.go new file mode 100644 index 00000000..8b21a73f --- /dev/null +++ b/db/gen/nidus-sync/comms/model/contact.go @@ -0,0 +1,19 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +import ( + "time" +) + +type Contact struct { + Created time.Time + ID int32 `sql:"primary_key"` + Name string + OrganizationID int32 +} diff --git a/db/gen/nidus-sync/comms/model/contact_email.go b/db/gen/nidus-sync/comms/model/contact_email.go new file mode 100644 index 00000000..0e83dba0 --- /dev/null +++ b/db/gen/nidus-sync/comms/model/contact_email.go @@ -0,0 +1,16 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type ContactEmail struct { + Address string `sql:"primary_key"` + Confirmed bool + ContactID int32 + ID int32 + IsSubscribed bool +} diff --git a/db/gen/nidus-sync/comms/model/contact_phone.go b/db/gen/nidus-sync/comms/model/contact_phone.go new file mode 100644 index 00000000..0477da3c --- /dev/null +++ b/db/gen/nidus-sync/comms/model/contact_phone.go @@ -0,0 +1,17 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type ContactPhone struct { + CanSms bool + ConfirmedMessageID *int32 + ContactID int32 + E164 string `sql:"primary_key"` + IsSubscribed bool + StopMessageID *int32 +} diff --git a/db/gen/nidus-sync/comms/table/contact.go b/db/gen/nidus-sync/comms/table/contact.go new file mode 100644 index 00000000..5dd2a7b5 --- /dev/null +++ b/db/gen/nidus-sync/comms/table/contact.go @@ -0,0 +1,87 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/Gleipnir-Technology/jet/postgres" +) + +var Contact = newContactTable("comms", "contact", "") + +type contactTable struct { + postgres.Table + + // Columns + Created postgres.ColumnTimestamp + ID postgres.ColumnInteger + Name postgres.ColumnString + OrganizationID postgres.ColumnInteger + + AllColumns postgres.ColumnList + MutableColumns postgres.ColumnList + DefaultColumns postgres.ColumnList +} + +type ContactTable struct { + contactTable + + EXCLUDED contactTable +} + +// AS creates new ContactTable with assigned alias +func (a ContactTable) AS(alias string) *ContactTable { + return newContactTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ContactTable with assigned schema name +func (a ContactTable) FromSchema(schemaName string) *ContactTable { + return newContactTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new ContactTable with assigned table prefix +func (a ContactTable) WithPrefix(prefix string) *ContactTable { + return newContactTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new ContactTable with assigned table suffix +func (a ContactTable) WithSuffix(suffix string) *ContactTable { + return newContactTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newContactTable(schemaName, tableName, alias string) *ContactTable { + return &ContactTable{ + contactTable: newContactTableImpl(schemaName, tableName, alias), + EXCLUDED: newContactTableImpl("", "excluded", ""), + } +} + +func newContactTableImpl(schemaName, tableName, alias string) contactTable { + var ( + CreatedColumn = postgres.TimestampColumn("created") + IDColumn = postgres.IntegerColumn("id") + NameColumn = postgres.StringColumn("name") + OrganizationIDColumn = postgres.IntegerColumn("organization_id") + allColumns = postgres.ColumnList{CreatedColumn, IDColumn, NameColumn, OrganizationIDColumn} + mutableColumns = postgres.ColumnList{CreatedColumn, NameColumn, OrganizationIDColumn} + defaultColumns = postgres.ColumnList{IDColumn} + ) + + return contactTable{ + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + Created: CreatedColumn, + ID: IDColumn, + Name: NameColumn, + OrganizationID: OrganizationIDColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + DefaultColumns: defaultColumns, + } +} diff --git a/db/gen/nidus-sync/comms/table/contact_email.go b/db/gen/nidus-sync/comms/table/contact_email.go new file mode 100644 index 00000000..33f47522 --- /dev/null +++ b/db/gen/nidus-sync/comms/table/contact_email.go @@ -0,0 +1,90 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/Gleipnir-Technology/jet/postgres" +) + +var ContactEmail = newContactEmailTable("comms", "contact_email", "") + +type contactEmailTable struct { + postgres.Table + + // Columns + Address postgres.ColumnString + Confirmed postgres.ColumnBool + ContactID postgres.ColumnInteger + ID postgres.ColumnInteger + IsSubscribed postgres.ColumnBool + + AllColumns postgres.ColumnList + MutableColumns postgres.ColumnList + DefaultColumns postgres.ColumnList +} + +type ContactEmailTable struct { + contactEmailTable + + EXCLUDED contactEmailTable +} + +// AS creates new ContactEmailTable with assigned alias +func (a ContactEmailTable) AS(alias string) *ContactEmailTable { + return newContactEmailTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ContactEmailTable with assigned schema name +func (a ContactEmailTable) FromSchema(schemaName string) *ContactEmailTable { + return newContactEmailTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new ContactEmailTable with assigned table prefix +func (a ContactEmailTable) WithPrefix(prefix string) *ContactEmailTable { + return newContactEmailTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new ContactEmailTable with assigned table suffix +func (a ContactEmailTable) WithSuffix(suffix string) *ContactEmailTable { + return newContactEmailTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newContactEmailTable(schemaName, tableName, alias string) *ContactEmailTable { + return &ContactEmailTable{ + contactEmailTable: newContactEmailTableImpl(schemaName, tableName, alias), + EXCLUDED: newContactEmailTableImpl("", "excluded", ""), + } +} + +func newContactEmailTableImpl(schemaName, tableName, alias string) contactEmailTable { + var ( + AddressColumn = postgres.StringColumn("address") + ConfirmedColumn = postgres.BoolColumn("confirmed") + ContactIDColumn = postgres.IntegerColumn("contact_id") + IDColumn = postgres.IntegerColumn("id") + IsSubscribedColumn = postgres.BoolColumn("is_subscribed") + allColumns = postgres.ColumnList{AddressColumn, ConfirmedColumn, ContactIDColumn, IDColumn, IsSubscribedColumn} + mutableColumns = postgres.ColumnList{ConfirmedColumn, ContactIDColumn, IDColumn, IsSubscribedColumn} + defaultColumns = postgres.ColumnList{IDColumn} + ) + + return contactEmailTable{ + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + Address: AddressColumn, + Confirmed: ConfirmedColumn, + ContactID: ContactIDColumn, + ID: IDColumn, + IsSubscribed: IsSubscribedColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + DefaultColumns: defaultColumns, + } +} diff --git a/db/gen/nidus-sync/comms/table/contact_phone.go b/db/gen/nidus-sync/comms/table/contact_phone.go new file mode 100644 index 00000000..1d59e635 --- /dev/null +++ b/db/gen/nidus-sync/comms/table/contact_phone.go @@ -0,0 +1,93 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/Gleipnir-Technology/jet/postgres" +) + +var ContactPhone = newContactPhoneTable("comms", "contact_phone", "") + +type contactPhoneTable struct { + postgres.Table + + // Columns + CanSms postgres.ColumnBool + ConfirmedMessageID postgres.ColumnInteger + ContactID postgres.ColumnInteger + E164 postgres.ColumnString + IsSubscribed postgres.ColumnBool + StopMessageID postgres.ColumnInteger + + AllColumns postgres.ColumnList + MutableColumns postgres.ColumnList + DefaultColumns postgres.ColumnList +} + +type ContactPhoneTable struct { + contactPhoneTable + + EXCLUDED contactPhoneTable +} + +// AS creates new ContactPhoneTable with assigned alias +func (a ContactPhoneTable) AS(alias string) *ContactPhoneTable { + return newContactPhoneTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ContactPhoneTable with assigned schema name +func (a ContactPhoneTable) FromSchema(schemaName string) *ContactPhoneTable { + return newContactPhoneTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new ContactPhoneTable with assigned table prefix +func (a ContactPhoneTable) WithPrefix(prefix string) *ContactPhoneTable { + return newContactPhoneTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new ContactPhoneTable with assigned table suffix +func (a ContactPhoneTable) WithSuffix(suffix string) *ContactPhoneTable { + return newContactPhoneTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newContactPhoneTable(schemaName, tableName, alias string) *ContactPhoneTable { + return &ContactPhoneTable{ + contactPhoneTable: newContactPhoneTableImpl(schemaName, tableName, alias), + EXCLUDED: newContactPhoneTableImpl("", "excluded", ""), + } +} + +func newContactPhoneTableImpl(schemaName, tableName, alias string) contactPhoneTable { + var ( + CanSmsColumn = postgres.BoolColumn("can_sms") + ConfirmedMessageIDColumn = postgres.IntegerColumn("confirmed_message_id") + ContactIDColumn = postgres.IntegerColumn("contact_id") + E164Column = postgres.StringColumn("e164") + IsSubscribedColumn = postgres.BoolColumn("is_subscribed") + StopMessageIDColumn = postgres.IntegerColumn("stop_message_id") + allColumns = postgres.ColumnList{CanSmsColumn, ConfirmedMessageIDColumn, ContactIDColumn, E164Column, IsSubscribedColumn, StopMessageIDColumn} + mutableColumns = postgres.ColumnList{CanSmsColumn, ConfirmedMessageIDColumn, ContactIDColumn, IsSubscribedColumn, StopMessageIDColumn} + defaultColumns = postgres.ColumnList{} + ) + + return contactPhoneTable{ + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + CanSms: CanSmsColumn, + ConfirmedMessageID: ConfirmedMessageIDColumn, + ContactID: ContactIDColumn, + E164: E164Column, + IsSubscribed: IsSubscribedColumn, + StopMessageID: StopMessageIDColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + DefaultColumns: defaultColumns, + } +} diff --git a/db/gen/nidus-sync/comms/table/table_use_schema.go b/db/gen/nidus-sync/comms/table/table_use_schema.go index 7264d7c9..83ad79b1 100644 --- a/db/gen/nidus-sync/comms/table/table_use_schema.go +++ b/db/gen/nidus-sync/comms/table/table_use_schema.go @@ -10,6 +10,9 @@ package table // UseSchema sets a new schema name for all generated table SQL builder types. It is recommended to invoke // this method only once at the beginning of the program. func UseSchema(schema string) { + Contact = Contact.FromSchema(schema) + ContactEmail = ContactEmail.FromSchema(schema) + ContactPhone = ContactPhone.FromSchema(schema) EmailContact = EmailContact.FromSchema(schema) EmailLog = EmailLog.FromSchema(schema) EmailTemplate = EmailTemplate.FromSchema(schema) diff --git a/db/gen/nidus-sync/publicreport/model/report.go b/db/gen/nidus-sync/publicreport/model/report.go index 1265ae09..e1d330e6 100644 --- a/db/gen/nidus-sync/publicreport/model/report.go +++ b/db/gen/nidus-sync/publicreport/model/report.go @@ -36,4 +36,5 @@ type Report struct { AddressGid string ClientUUID *uuid.UUID ReporterPhoneCanSms bool + ReporterContactID *int32 } diff --git a/db/gen/nidus-sync/publicreport/table/report.go b/db/gen/nidus-sync/publicreport/table/report.go index bd04cf1f..22bdefa7 100644 --- a/db/gen/nidus-sync/publicreport/table/report.go +++ b/db/gen/nidus-sync/publicreport/table/report.go @@ -39,6 +39,7 @@ type reportTable struct { AddressGid postgres.ColumnString ClientUUID postgres.ColumnString ReporterPhoneCanSms postgres.ColumnBool + ReporterContactID postgres.ColumnInteger AllColumns postgres.ColumnList MutableColumns postgres.ColumnList @@ -102,8 +103,9 @@ func newReportTableImpl(schemaName, tableName, alias string) reportTable { AddressGidColumn = postgres.StringColumn("address_gid") ClientUUIDColumn = postgres.StringColumn("client_uuid") ReporterPhoneCanSmsColumn = postgres.BoolColumn("reporter_phone_can_sms") - allColumns = postgres.ColumnList{AddressRawColumn, AddressIDColumn, CreatedColumn, LocationColumn, H3cellColumn, IDColumn, LatlngAccuracyTypeColumn, LatlngAccuracyValueColumn, MapZoomColumn, OrganizationIDColumn, PublicIDColumn, ReporterNameColumn, ReporterEmailColumn, ReporterPhoneColumn, ReporterContactConsentColumn, ReportTypeColumn, ReviewedColumn, ReviewerIDColumn, StatusColumn, AddressGidColumn, ClientUUIDColumn, ReporterPhoneCanSmsColumn} - mutableColumns = postgres.ColumnList{AddressRawColumn, AddressIDColumn, CreatedColumn, LocationColumn, H3cellColumn, LatlngAccuracyTypeColumn, LatlngAccuracyValueColumn, MapZoomColumn, OrganizationIDColumn, PublicIDColumn, ReporterNameColumn, ReporterEmailColumn, ReporterPhoneColumn, ReporterContactConsentColumn, ReportTypeColumn, ReviewedColumn, ReviewerIDColumn, StatusColumn, AddressGidColumn, ClientUUIDColumn, ReporterPhoneCanSmsColumn} + ReporterContactIDColumn = postgres.IntegerColumn("reporter_contact_id") + allColumns = postgres.ColumnList{AddressRawColumn, AddressIDColumn, CreatedColumn, LocationColumn, H3cellColumn, IDColumn, LatlngAccuracyTypeColumn, LatlngAccuracyValueColumn, MapZoomColumn, OrganizationIDColumn, PublicIDColumn, ReporterNameColumn, ReporterEmailColumn, ReporterPhoneColumn, ReporterContactConsentColumn, ReportTypeColumn, ReviewedColumn, ReviewerIDColumn, StatusColumn, AddressGidColumn, ClientUUIDColumn, ReporterPhoneCanSmsColumn, ReporterContactIDColumn} + mutableColumns = postgres.ColumnList{AddressRawColumn, AddressIDColumn, CreatedColumn, LocationColumn, H3cellColumn, LatlngAccuracyTypeColumn, LatlngAccuracyValueColumn, MapZoomColumn, OrganizationIDColumn, PublicIDColumn, ReporterNameColumn, ReporterEmailColumn, ReporterPhoneColumn, ReporterContactConsentColumn, ReportTypeColumn, ReviewedColumn, ReviewerIDColumn, StatusColumn, AddressGidColumn, ClientUUIDColumn, ReporterPhoneCanSmsColumn, ReporterContactIDColumn} defaultColumns = postgres.ColumnList{IDColumn} ) @@ -133,6 +135,7 @@ func newReportTableImpl(schemaName, tableName, alias string) reportTable { AddressGid: AddressGidColumn, ClientUUID: ClientUUIDColumn, ReporterPhoneCanSms: ReporterPhoneCanSmsColumn, + ReporterContactID: ReporterContactIDColumn, AllColumns: allColumns, MutableColumns: mutableColumns, diff --git a/db/migrations/00151_comms_contact.sql b/db/migrations/00151_comms_contact.sql new file mode 100644 index 00000000..55cd3251 --- /dev/null +++ b/db/migrations/00151_comms_contact.sql @@ -0,0 +1,119 @@ +-- +goose Up +CREATE TABLE comms.contact ( + created TIMESTAMP WITHOUT TIME ZONE NOT NULL, + id SERIAL NOT NULL, + name TEXT NOT NULL, + organization_id INTEGER NOT NULL REFERENCES organization(id), + PRIMARY KEY(id) +); +CREATE TABLE comms.contact_email ( + address TEXT NOT NULL, + confirmed BOOLEAN NOT NULL, + contact_id INTEGER NOT NULL REFERENCES comms.contact(id), + id SERIAL NOT NULL, + is_subscribed BOOLEAN NOT NULL, + PRIMARY KEY(address) +); +CREATE TABLE comms.contact_phone ( + can_sms BOOLEAN NOT NULL, + confirmed_message_id INTEGER REFERENCES comms.text_log(id), + contact_id INTEGER NOT NULL REFERENCES comms.contact(id), + e164 TEXT NOT NULL, + is_subscribed BOOLEAN NOT NULL, + stop_message_id INTEGER REFERENCES comms.text_log(id), + PRIMARY KEY(e164) +); + +ALTER TABLE publicreport.report + ADD COLUMN reporter_contact_id INTEGER REFERENCES comms.contact(id); + +-- insert a placeholder contact for the system +INSERT INTO comms.contact ( + created, + name, + organization_id +) SELECT + CURRENT_TIMESTAMP, + 'Nidus', + id +FROM organization WHERE is_catchall = TRUE; + +-- insert contacts where we have names, dropping duplicates +INSERT INTO comms.contact ( + created, + name, + organization_id +) SELECT DISTINCT ON (reporter_name, organization_id) + created, + reporter_name, + organization_id +FROM publicreport.report +WHERE reporter_name != '' +ORDER BY reporter_name, organization_id, created ASC; + +UPDATE publicreport.report +SET reporter_contact_id = comms.contact.id +FROM comms.contact +WHERE comms.contact.name = report.reporter_name +AND reporter_name != ''; + +-- insert contacts where we don't have names +INSERT INTO comms.contact ( + created, + name, + organization_id +) SELECT + created, + reporter_name, + organization_id +FROM publicreport.report +WHERE report.reporter_name = ''; + +UPDATE publicreport.report +SET reporter_contact_id = comms.contact.id +FROM comms.contact +WHERE comms.contact.created = report.created; + +-- At this point every publicreport.report should have an associated contact ID. +-- We'll make sure of this via constraint before moving the email and phone data into +-- the contacts +ALTER TABLE publicreport.report + ALTER COLUMN reporter_contact_id SET NOT NULL; + +-- Now copy over all of the contact information we have into the new contact rows +INSERT INTO comms.contact_email ( + address, + confirmed, + contact_id, + is_subscribed +) SELECT + reporter_email, + COALESCE(reporter_contact_consent, false), + reporter_contact_id, + FALSE +FROM publicreport.report WHERE report.reporter_email != '' +ON CONFLICT DO NOTHING; + +INSERT INTO comms.contact_phone ( + can_sms, + confirmed_message_id, + contact_id, + e164, + is_subscribed, + stop_message_id +) SELECT + reporter_phone_can_sms, + NULL, + reporter_contact_id, + reporter_phone, + FALSE, + NULL +FROM publicreport.report WHERE publicreport.report.reporter_phone != '' +ON CONFLICT DO NOTHING; + +-- +goose Down +ALTER TABLE publicreport.report + DROP COLUMN reporter_contact_id; +DROP TABLE comms.contact_phone; +DROP TABLE comms.contact_email; +DROP TABLE comms.contact; diff --git a/db/models/publicreport.report.bob.go b/db/models/publicreport.report.bob.go index a444d640..8accf66e 100644 --- a/db/models/publicreport.report.bob.go +++ b/db/models/publicreport.report.bob.go @@ -153,28 +153,23 @@ func (publicreportReportColumns) AliasedAs(alias string) publicreportReportColum // All values are optional, and do not have to be set // Generated columns are not included type PublicreportReportSetter struct { - AddressRaw omit.Val[string] `db:"address_raw" ` - AddressID omitnull.Val[int32] `db:"address_id" ` - Created omit.Val[time.Time] `db:"created" ` - Location omitnull.Val[string] `db:"location" ` - H3cell omitnull.Val[string] `db:"h3cell" ` - ID omit.Val[int32] `db:"id,pk" ` - LatlngAccuracyType omit.Val[enums.PublicreportAccuracytype] `db:"latlng_accuracy_type" ` - LatlngAccuracyValue omit.Val[float32] `db:"latlng_accuracy_value" ` - MapZoom omit.Val[float32] `db:"map_zoom" ` - OrganizationID omit.Val[int32] `db:"organization_id" ` - PublicID omit.Val[string] `db:"public_id" ` - ReporterName omit.Val[string] `db:"reporter_name" ` - ReporterEmail omit.Val[string] `db:"reporter_email" ` - ReporterPhone omit.Val[string] `db:"reporter_phone" ` - ReporterContactConsent omitnull.Val[bool] `db:"reporter_contact_consent" ` - ReportType omit.Val[enums.PublicreportReporttype] `db:"report_type" ` - Reviewed omitnull.Val[time.Time] `db:"reviewed" ` - ReviewerID omitnull.Val[int32] `db:"reviewer_id" ` - Status omit.Val[enums.PublicreportReportstatustype] `db:"status" ` - AddressGid omit.Val[string] `db:"address_gid" ` - ClientUUID omitnull.Val[uuid.UUID] `db:"client_uuid" ` - ReporterPhoneCanSMS omit.Val[bool] `db:"reporter_phone_can_sms" ` + AddressRaw omit.Val[string] `db:"address_raw" ` + AddressID omitnull.Val[int32] `db:"address_id" ` + Created omit.Val[time.Time] `db:"created" ` + Location omitnull.Val[string] `db:"location" ` + H3cell omitnull.Val[string] `db:"h3cell" ` + ID omit.Val[int32] `db:"id,pk" ` + LatlngAccuracyType omit.Val[enums.PublicreportAccuracytype] `db:"latlng_accuracy_type" ` + LatlngAccuracyValue omit.Val[float32] `db:"latlng_accuracy_value" ` + MapZoom omit.Val[float32] `db:"map_zoom" ` + OrganizationID omit.Val[int32] `db:"organization_id" ` + PublicID omit.Val[string] `db:"public_id" ` + ReportType omit.Val[enums.PublicreportReporttype] `db:"report_type" ` + Reviewed omitnull.Val[time.Time] `db:"reviewed" ` + ReviewerID omitnull.Val[int32] `db:"reviewer_id" ` + Status omit.Val[enums.PublicreportReportstatustype] `db:"status" ` + AddressGid omit.Val[string] `db:"address_gid" ` + ClientUUID omitnull.Val[uuid.UUID] `db:"client_uuid" ` } func (s PublicreportReportSetter) SetColumns() []string { @@ -212,18 +207,6 @@ func (s PublicreportReportSetter) SetColumns() []string { if s.PublicID.IsValue() { vals = append(vals, "public_id") } - if s.ReporterName.IsValue() { - vals = append(vals, "reporter_name") - } - if s.ReporterEmail.IsValue() { - vals = append(vals, "reporter_email") - } - if s.ReporterPhone.IsValue() { - vals = append(vals, "reporter_phone") - } - if !s.ReporterContactConsent.IsUnset() { - vals = append(vals, "reporter_contact_consent") - } if s.ReportType.IsValue() { vals = append(vals, "report_type") } @@ -242,9 +225,6 @@ func (s PublicreportReportSetter) SetColumns() []string { if !s.ClientUUID.IsUnset() { vals = append(vals, "client_uuid") } - if s.ReporterPhoneCanSMS.IsValue() { - vals = append(vals, "reporter_phone_can_sms") - } return vals } @@ -282,18 +262,6 @@ func (s PublicreportReportSetter) Overwrite(t *PublicreportReport) { if s.PublicID.IsValue() { t.PublicID = s.PublicID.MustGet() } - if s.ReporterName.IsValue() { - t.ReporterName = s.ReporterName.MustGet() - } - if s.ReporterEmail.IsValue() { - t.ReporterEmail = s.ReporterEmail.MustGet() - } - if s.ReporterPhone.IsValue() { - t.ReporterPhone = s.ReporterPhone.MustGet() - } - if !s.ReporterContactConsent.IsUnset() { - t.ReporterContactConsent = s.ReporterContactConsent.MustGetNull() - } if s.ReportType.IsValue() { t.ReportType = s.ReportType.MustGet() } @@ -312,9 +280,6 @@ func (s PublicreportReportSetter) Overwrite(t *PublicreportReport) { if !s.ClientUUID.IsUnset() { t.ClientUUID = s.ClientUUID.MustGetNull() } - if s.ReporterPhoneCanSMS.IsValue() { - t.ReporterPhoneCanSMS = s.ReporterPhoneCanSMS.MustGet() - } } func (s *PublicreportReportSetter) Apply(q *dialect.InsertQuery) { @@ -390,29 +355,13 @@ func (s *PublicreportReportSetter) Apply(q *dialect.InsertQuery) { vals[10] = psql.Raw("DEFAULT") } - if s.ReporterName.IsValue() { - vals[11] = psql.Arg(s.ReporterName.MustGet()) - } else { - vals[11] = psql.Raw("DEFAULT") - } + vals[11] = psql.Raw("DEFAULT") - if s.ReporterEmail.IsValue() { - vals[12] = psql.Arg(s.ReporterEmail.MustGet()) - } else { - vals[12] = psql.Raw("DEFAULT") - } + vals[12] = psql.Raw("DEFAULT") - if s.ReporterPhone.IsValue() { - vals[13] = psql.Arg(s.ReporterPhone.MustGet()) - } else { - vals[13] = psql.Raw("DEFAULT") - } + vals[13] = psql.Raw("DEFAULT") - if !s.ReporterContactConsent.IsUnset() { - vals[14] = psql.Arg(s.ReporterContactConsent.MustGetNull()) - } else { - vals[14] = psql.Raw("DEFAULT") - } + vals[14] = psql.Raw("DEFAULT") if s.ReportType.IsValue() { vals[15] = psql.Arg(s.ReportType.MustGet()) @@ -450,11 +399,7 @@ func (s *PublicreportReportSetter) Apply(q *dialect.InsertQuery) { vals[20] = psql.Raw("DEFAULT") } - if s.ReporterPhoneCanSMS.IsValue() { - vals[21] = psql.Arg(s.ReporterPhoneCanSMS.MustGet()) - } else { - vals[21] = psql.Raw("DEFAULT") - } + vals[21] = psql.Raw("DEFAULT") return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) @@ -544,34 +489,6 @@ func (s PublicreportReportSetter) Expressions(prefix ...string) []bob.Expression }}) } - if s.ReporterName.IsValue() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - psql.Quote(append(prefix, "reporter_name")...), - psql.Arg(s.ReporterName), - }}) - } - - if s.ReporterEmail.IsValue() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - psql.Quote(append(prefix, "reporter_email")...), - psql.Arg(s.ReporterEmail), - }}) - } - - if s.ReporterPhone.IsValue() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - psql.Quote(append(prefix, "reporter_phone")...), - psql.Arg(s.ReporterPhone), - }}) - } - - if !s.ReporterContactConsent.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - psql.Quote(append(prefix, "reporter_contact_consent")...), - psql.Arg(s.ReporterContactConsent), - }}) - } - if s.ReportType.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ psql.Quote(append(prefix, "report_type")...), @@ -614,13 +531,6 @@ func (s PublicreportReportSetter) Expressions(prefix ...string) []bob.Expression }}) } - if s.ReporterPhoneCanSMS.IsValue() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - psql.Quote(append(prefix, "reporter_phone_can_sms")...), - psql.Arg(s.ReporterPhoneCanSMS), - }}) - } - return exprs } diff --git a/db/query/comms/contact.go b/db/query/comms/contact.go new file mode 100644 index 00000000..8b6bf80f --- /dev/null +++ b/db/query/comms/contact.go @@ -0,0 +1,47 @@ +package comms + +import ( + "context" + + //"github.com/Gleipnir-Technology/bob" + "github.com/Gleipnir-Technology/nidus-sync/db" + //"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/enum" + "github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/table" +) + +func ContactInsert(ctx context.Context, txn db.Ex, m model.Contact) (model.Contact, error) { + statement := table.Contact.INSERT(table.Contact.MutableColumns). + MODEL(m). + RETURNING(table.Contact.AllColumns) + return db.ExecuteOneTx[model.Contact](ctx, txn, statement) +} + +func ContactFromID(ctx context.Context, txn db.Ex, id int64) (model.Contact, error) { + statement := table.Contact.SELECT( + table.Contact.AllColumns, + ).FROM(table.Contact). + WHERE(table.Contact.ID.EQ(postgres.Int(id))) + return db.ExecuteOne[model.Contact](ctx, statement) +} + +func ContactUpdateName(ctx context.Context, txn db.Ex, id int64, name string) error { + statement := table.Contact.UPDATE(). + SET( + table.Contact.Name.SET(postgres.String(name)), + ). + WHERE(table.Contact.OrganizationID.EQ(postgres.Int(id))) + return db.ExecuteNoneTx(ctx, txn, statement) +} + +/* +func ContactsFromAddress(ctx context.Context, address string) ([]model.Contact, error) { + statement := table.Contact.SELECT( + table.Contact.AllColumns, + ).FROM(table.Contact). + WHERE(table.Contact.Source.EQ(postgres.String(address)).OR( + table.Contact.Destination.EQ(postgres.String(address)))) + return db.ExecuteMany[model.Contact](ctx, statement) +} +*/ diff --git a/db/query/comms/contact_phone.go b/db/query/comms/contact_phone.go new file mode 100644 index 00000000..001f2ba5 --- /dev/null +++ b/db/query/comms/contact_phone.go @@ -0,0 +1,59 @@ +package comms + +import ( + "context" + + //"github.com/Gleipnir-Technology/bob" + "github.com/Gleipnir-Technology/nidus-sync/db" + //"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/enum" + "github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/table" +) + +func ContactPhoneInsert(ctx context.Context, txn db.Ex, m model.ContactPhone) (model.ContactPhone, error) { + statement := table.ContactPhone.INSERT(table.ContactPhone.MutableColumns). + MODEL(m). + RETURNING(table.ContactPhone.AllColumns) + return db.ExecuteOneTx[model.ContactPhone](ctx, txn, statement) +} + +func ContactPhoneFromE164(ctx context.Context, txn db.Ex, e164 string) (model.ContactPhone, error) { + statement := table.ContactPhone.SELECT( + table.ContactPhone.AllColumns, + ).FROM(table.ContactPhone). + WHERE(table.ContactPhone.E164.EQ(postgres.String(e164))) + return db.ExecuteOneTx[model.ContactPhone](ctx, txn, statement) +} + +func ContactPhoneUpdateConfirmedMessageID(ctx context.Context, txn db.Ex, e164 string, message_id *int32) error { + statement := table.ContactPhone.UPDATE(). + SET(table.ContactPhone.ConfirmedMessageID.SET(postgres.IntExp(postgres.NULL))). + WHERE(table.ContactPhone.E164.EQ(postgres.String(e164))) + return db.ExecuteNoneTx(ctx, txn, statement) +} +func ContactPhoneUpdateStopMessageID(ctx context.Context, txn db.Ex, e164 string, message_id *int32) error { + /* + m := model.ContactPhone{} + m.StopMessageID = message_id + statement := table.ContactPhone.UPDATE( + table.ContactPhone.StopMessageID, + ).MODEL(m). + WHERE(table.ContactPhone.E164.EQ(postgres.String(e164))) + */ + statement := table.ContactPhone.UPDATE(). + SET(table.ContactPhone.StopMessageID.SET(postgres.IntExp(postgres.NULL))). + WHERE(table.ContactPhone.E164.EQ(postgres.String(e164))) + return db.ExecuteNoneTx(ctx, txn, statement) +} + +/* +func ContactPhonesFromAddress(ctx context.Context, address string) ([]model.ContactPhone, error) { + statement := table.ContactPhone.SELECT( + table.ContactPhone.AllColumns, + ).FROM(table.ContactPhone). + WHERE(table.ContactPhone.Source.EQ(postgres.String(address)).OR( + table.ContactPhone.Destination.EQ(postgres.String(address)))) + return db.ExecuteMany[model.ContactPhone](ctx, statement) +} +*/ diff --git a/db/query/comms/text_job.go b/db/query/comms/text_job.go new file mode 100644 index 00000000..6e545a20 --- /dev/null +++ b/db/query/comms/text_job.go @@ -0,0 +1,42 @@ +package comms + +import ( + "context" + + "github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/table" +) + +func TextJobComplete(ctx context.Context, txn db.Ex, id int64) error { + statement := table.TextJob.UPDATE( + table.TextJob.Completed, + ).SET( + table.TextJob.Completed.SET(postgres.LOCALTIMESTAMP()), + ).WHERE( + table.TextJob.ID.EQ(postgres.Int(id)), + ) + return db.ExecuteNoneTx(ctx, txn, statement) +} +func TextJobFromID(ctx context.Context, txn db.Ex, id int64) (model.TextJob, error) { + statement := table.TextJob.SELECT( + table.TextJob.AllColumns, + ).FROM(table.TextJob). + WHERE(table.TextJob.ID.EQ(postgres.Int(id))) + return db.ExecuteOneTx[model.TextJob](ctx, txn, statement) +} +func TextJobInsert(ctx context.Context, txn db.Ex, m model.TextJob) (model.TextJob, error) { + statement := table.TextJob.INSERT( + table.TextJob.MutableColumns, + ).MODEL(m) + return db.ExecuteOneTx[model.TextJob](ctx, txn, statement) +} +func TextJobsWaitingFromDestination(ctx context.Context, txn db.Ex, destination string) ([]model.TextJob, error) { + statement := table.TextJob.SELECT( + table.TextJob.AllColumns, + ).FROM(table.TextJob). + WHERE(table.TextJob.Destination.EQ(postgres.String(destination)).AND( + table.TextJob.Completed.IS_NULL())) + return db.ExecuteManyTx[model.TextJob](ctx, txn, statement) +} diff --git a/db/query/comms/text_log.go b/db/query/comms/text_log.go index 1bfbf18a..86705481 100644 --- a/db/query/comms/text_log.go +++ b/db/query/comms/text_log.go @@ -9,18 +9,42 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/table" ) -func TextLogFromID(ctx context.Context, id int64) (model.TextLog, error) { +func TextLogInsert(ctx context.Context, txn db.Ex, m model.TextLog) (model.TextLog, error) { + statement := table.TextLog.INSERT( + table.TextLog.MutableColumns, + ).MODEL(m) + return db.ExecuteOneTx[model.TextLog](ctx, txn, statement) +} +func TextLogFromID(ctx context.Context, txn db.Ex, id int64) (model.TextLog, error) { statement := table.TextLog.SELECT( table.TextLog.AllColumns, ).FROM(table.TextLog). WHERE(table.TextLog.ID.EQ(postgres.Int(id))) - return db.ExecuteOne[model.TextLog](ctx, statement) + return db.ExecuteOneTx[model.TextLog](ctx, txn, statement) } -func TextLogsFromPhoneNumber(ctx context.Context, number string) ([]model.TextLog, error) { +func TextLogsFromPhoneNumber(ctx context.Context, txn db.Ex, number string) ([]model.TextLog, error) { statement := table.TextLog.SELECT( table.TextLog.AllColumns, ).FROM(table.TextLog). WHERE(table.TextLog.Source.EQ(postgres.String(number)).OR( table.TextLog.Destination.EQ(postgres.String(number)))) - return db.ExecuteMany[model.TextLog](ctx, statement) + return db.ExecuteManyTx[model.TextLog](ctx, txn, statement) +} +func TextLogWelcomeFromDestination(ctx context.Context, txn db.Ex, destination string) ([]model.TextLog, error) { + statement := table.TextLog.SELECT( + table.TextLog.AllColumns, + ).FROM(table.TextLog). + WHERE(table.TextLog.Destination.EQ(postgres.String(destination)).AND( + table.TextLog.IsWelcome.EQ(postgres.Bool(true)))) + return db.ExecuteManyTx[model.TextLog](ctx, txn, statement) +} +func TextLogUpdate(ctx context.Context, txn db.Ex, id int64, twilio_sid string, twilio_status string) error { + statement := table.TextLog.UPDATE( + table.TextLog.TwilioSid, + table.TextLog.TwilioStatus, + ).SET( + table.TextLog.TwilioSid.SET(postgres.String(twilio_sid)), + table.TextLog.TwilioStatus.SET(postgres.String(twilio_status)), + ).WHERE(table.TextLog.ID.EQ(postgres.Int(id))) + return db.ExecuteNoneTx(ctx, txn, statement) } diff --git a/db/query/public/communication.go b/db/query/public/communication.go index e251feb1..6da6c2a8 100644 --- a/db/query/public/communication.go +++ b/db/query/public/communication.go @@ -3,7 +3,6 @@ package public import ( "context" - //"github.com/Gleipnir-Technology/bob" "github.com/Gleipnir-Technology/nidus-sync/db" //"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/enum" "github.com/Gleipnir-Technology/jet/postgres" @@ -11,28 +10,28 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/table" ) -func CommunicationInsert(ctx context.Context, txn db.Tx, m model.Communication) (model.Communication, error) { +func CommunicationInsert(ctx context.Context, txn db.Ex, m model.Communication) (model.Communication, error) { statement := table.Communication.INSERT(table.Communication.MutableColumns). MODEL(m). RETURNING(table.Communication.AllColumns) return db.ExecuteOneTx[model.Communication](ctx, txn, statement) } -func CommunicationFromID(ctx context.Context, comm_id int64) (model.Communication, error) { +func CommunicationFromID(ctx context.Context, txn db.Ex, comm_id int64) (model.Communication, error) { statement := table.Communication.SELECT( table.Communication.AllColumns, ).FROM(table.Communication). WHERE(table.Communication.ID.EQ(postgres.Int(comm_id))) - return db.ExecuteOne[model.Communication](ctx, statement) + return db.ExecuteOneTx[model.Communication](ctx, txn, statement) } -func CommunicationsFromOrganization(ctx context.Context, org_id int64) ([]model.Communication, error) { +func CommunicationsFromOrganization(ctx context.Context, txn db.Ex, org_id int64) ([]model.Communication, error) { statement := table.Communication.SELECT( table.Communication.AllColumns, ).FROM(table.Communication). WHERE(table.Communication.OrganizationID.EQ(postgres.Int(org_id))). ORDER_BY(table.Communication.Created.DESC()) - return db.ExecuteMany[model.Communication](ctx, statement) + return db.ExecuteManyTx[model.Communication](ctx, txn, statement) } -func CommunicationSetStatus(ctx context.Context, txn db.Tx, org_id int64, comm_id int64, status model.Communicationstatus) error { +func CommunicationSetStatus(ctx context.Context, txn db.Ex, org_id int64, comm_id int64, status model.Communicationstatus) error { statement := table.Communication.UPDATE(). SET( table.Communication.Status.SET(postgres.NewEnumValue(status.String())), diff --git a/db/query/public/organization.go b/db/query/public/organization.go new file mode 100644 index 00000000..c406c462 --- /dev/null +++ b/db/query/public/organization.go @@ -0,0 +1,36 @@ +package public + +import ( + "context" + + "github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/table" +) + +func OrganizationInsert(ctx context.Context, txn db.Ex, m model.Organization) (model.Organization, error) { + statement := table.Organization.INSERT(table.Organization.MutableColumns). + MODEL(m). + RETURNING(table.Organization.AllColumns) + return db.ExecuteOneTx[model.Organization](ctx, txn, statement) +} +func OrganizationFromID(ctx context.Context, txn db.Ex, org_id int64) (model.Organization, error) { + statement := table.Organization.SELECT( + table.Organization.AllColumns, + ).FROM(table.Organization). + WHERE(table.Organization.ID.EQ(postgres.Int(org_id))) + return db.ExecuteOne[model.Organization](ctx, statement) +} + +/* +func OrganizationSetStatus(ctx context.Context, txn db.Tx, org_id int64, org_id int64, status model.Organizationstatus) error { + statement := table.Organization.UPDATE(). + SET( + table.Organization.Status.SET(postgres.NewEnumValue(status.String())), + ). + WHERE(table.Organization.OrganizationID.EQ(postgres.Int(org_id)).AND( + table.Organization.ID.EQ(postgres.Int(org_id)))) + return db.ExecuteNoneTx(ctx, txn, statement) +} +*/ diff --git a/db/query/public/report_text.go b/db/query/public/report_text.go new file mode 100644 index 00000000..031870fd --- /dev/null +++ b/db/query/public/report_text.go @@ -0,0 +1,18 @@ +package public + +import ( + "context" + + "github.com/Gleipnir-Technology/nidus-sync/db" + //"github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/enum" + //"github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/table" +) + +func ReportTextInsert(ctx context.Context, txn db.Tx, m model.ReportText) (model.ReportText, error) { + statement := table.ReportText.INSERT(table.ReportText.MutableColumns). + MODEL(m). + RETURNING(table.ReportText.AllColumns) + return db.ExecuteOneTx[model.ReportText](ctx, txn, statement) +} diff --git a/db/query/publicreport/notify_email.go b/db/query/publicreport/notify_email.go new file mode 100644 index 00000000..35c9de61 --- /dev/null +++ b/db/query/publicreport/notify_email.go @@ -0,0 +1,26 @@ +package publicreport + +import ( + "context" + "time" + + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/table" + //"github.com/Gleipnir-Technology/jet/postgres" +) + +func NotifyEmailInsert(ctx context.Context, txn db.Ex, m model.NotifyEmail) (model.NotifyEmail, error) { + statement := table.NotifyEmail.INSERT(table.NotifyEmail.MutableColumns). + MODEL(m). + RETURNING(table.NotifyEmail.AllColumns) + return db.ExecuteOneTx[model.NotifyEmail](ctx, txn, statement) +} +func NotifyEmailCreate(ctx context.Context, txn db.Ex, report_id int32, destination string) (model.NotifyEmail, error) { + return NotifyEmailInsert(ctx, txn, model.NotifyEmail{ + Created: time.Now(), + Deleted: nil, + EmailAddress: destination, + ReportID: report_id, + }) +} diff --git a/db/query/publicreport/notify_phone.go b/db/query/publicreport/notify_phone.go new file mode 100644 index 00000000..acc2c1cf --- /dev/null +++ b/db/query/publicreport/notify_phone.go @@ -0,0 +1,26 @@ +package publicreport + +import ( + "context" + "time" + + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/table" + //"github.com/Gleipnir-Technology/jet/postgres" +) + +func NotifyPhoneInsert(ctx context.Context, txn db.Ex, m model.NotifyPhone) (model.NotifyPhone, error) { + statement := table.NotifyPhone.INSERT(table.NotifyPhone.MutableColumns). + MODEL(m). + RETURNING(table.NotifyPhone.AllColumns) + return db.ExecuteOneTx[model.NotifyPhone](ctx, txn, statement) +} +func NotifyPhoneCreate(ctx context.Context, txn db.Ex, report_id int32, destination string) (model.NotifyPhone, error) { + return NotifyPhoneInsert(ctx, txn, model.NotifyPhone{ + Created: time.Now(), + Deleted: nil, + PhoneE164: destination, + ReportID: report_id, + }) +} diff --git a/db/query/publicreport/report.go b/db/query/publicreport/report.go index 349d1b31..8a9896fe 100644 --- a/db/query/publicreport/report.go +++ b/db/query/publicreport/report.go @@ -9,6 +9,7 @@ import ( //"github.com/Gleipnir-Technology/bob" "github.com/Gleipnir-Technology/jet/postgres" "github.com/Gleipnir-Technology/nidus-sync/db" + tablecomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/table" "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/table" ) @@ -36,12 +37,12 @@ func ReportsFromAddressID(ctx context.Context, txn db.Ex, org_id int64, address_ table.Report.OrganizationID.EQ(postgres.Int(org_id)))) return db.ExecuteManyTx[model.Report](ctx, txn, statement) } -func ReportFromID(ctx context.Context, report_id int64) (model.Report, error) { +func ReportFromID(ctx context.Context, txn db.Ex, report_id int64) (model.Report, error) { statement := table.Report.SELECT( table.Report.AllColumns, ).FROM(table.Report). WHERE(table.Report.ID.EQ(postgres.Int(report_id))) - return db.ExecuteOne[model.Report](ctx, statement) + return db.ExecuteOneTx[model.Report](ctx, txn, statement) } func ReportsFromIDs(ctx context.Context, report_ids []int64) ([]model.Report, error) { sql_ids := make([]postgres.Expression, len(report_ids)) @@ -111,3 +112,17 @@ func ReportsUnreviewedForOrganization(ctx context.Context, txn db.Ex, org_id int table.Report.OrganizationID.EQ(postgres.Int(org_id)))) return db.ExecuteManyTx[model.Report](ctx, txn, statement) } +func ReportsFromReporterPhone(ctx context.Context, txn db.Ex, destination string) ([]model.Report, error) { + statement := table.Report.SELECT( + table.Report.AllColumns, + ).FROM(table.Report). + FROM(table.Report.INNER_JOIN( + tablecomms.TextJob, + tablecomms.TextJob.ReportID.EQ(table.Report.ID), + )).WHERE( + tablecomms.TextJob.ReportID.IS_NOT_NULL().AND( + tablecomms.TextJob.Destination.EQ(postgres.String(destination))).AND( + table.Report.Status.EQ(postgres.NewEnumValue(model.Reportstatustype_Reported.String()))), + ) + return db.ExecuteManyTx[model.Report](ctx, txn, statement) +} diff --git a/db/query/publicreport/subscribe_email.go b/db/query/publicreport/subscribe_email.go new file mode 100644 index 00000000..c73a21d6 --- /dev/null +++ b/db/query/publicreport/subscribe_email.go @@ -0,0 +1,19 @@ +package publicreport + +import ( + "context" + //"time" + + //"github.com/Gleipnir-Technology/bob" + //"github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/table" +) + +func SubscribeEmailInsert(ctx context.Context, txn db.Ex, m model.SubscribeEmail) (model.SubscribeEmail, error) { + statement := table.SubscribeEmail.INSERT(table.SubscribeEmail.AllColumns). + MODEL(m). + RETURNING(table.SubscribeEmail.AllColumns) + return db.ExecuteOneTx[model.SubscribeEmail](ctx, txn, statement) +} diff --git a/db/query/publicreport/subscribe_phone.go b/db/query/publicreport/subscribe_phone.go new file mode 100644 index 00000000..bb6457b7 --- /dev/null +++ b/db/query/publicreport/subscribe_phone.go @@ -0,0 +1,18 @@ +package publicreport + +import ( + "context" + //"time" + + //"github.com/Gleipnir-Technology/jet/postgres" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" + "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/table" +) + +func SubscribePhoneInsert(ctx context.Context, txn db.Ex, m model.SubscribePhone) (model.SubscribePhone, error) { + statement := table.SubscribePhone.INSERT(table.SubscribePhone.AllColumns). + MODEL(m). + RETURNING(table.SubscribePhone.AllColumns) + return db.ExecuteOneTx[model.SubscribePhone](ctx, txn, statement) +} diff --git a/platform/background/background.go b/platform/background/background.go index d43cd3a6..0ae832ff 100644 --- a/platform/background/background.go +++ b/platform/background/background.go @@ -36,8 +36,8 @@ func NewLabelStudioAudioCreate(ctx context.Context, txn bob.Executor, note_audio func NewTextRespond(ctx context.Context, txn bob.Executor, text_id int32) error { return newJob(ctx, txn, enums.JobtypeTextRespond, text_id) } -func NewTextSend(ctx context.Context, txn bob.Executor, job_id int32) error { - return newJob(ctx, txn, enums.JobtypeTextSend, job_id) +func NewTextSend(ctx context.Context, txn db.Ex, job_id int32) error { + return newJob2(ctx, txn, model.Jobtype_TextSend, job_id) } func newJob(ctx context.Context, txn bob.Executor, t enums.Jobtype, id int32) error { _, err := models.Jobs.Insert(&models.JobSetter{ diff --git a/platform/communication.go b/platform/communication.go index 629891fa..788d58f2 100644 --- a/platform/communication.go +++ b/platform/communication.go @@ -53,6 +53,7 @@ func CommunicationRelatedRecords(ctx context.Context, user User, comm *modelpubl // * phone number // * email // * name + txn := db.PGInstance.PGXPool result := make([]RelatedRecord, 0) if comm.SourceEmailLogID != nil { email_log, err := querycomms.EmailLogFromID(ctx, int64(*comm.SourceEmailLogID)) @@ -71,7 +72,7 @@ func CommunicationRelatedRecords(ctx context.Context, user User, comm *modelpubl }) } } else if comm.SourceTextLogID != nil { - text_log, err := querycomms.TextLogFromID(ctx, int64(*comm.SourceTextLogID)) + text_log, err := querycomms.TextLogFromID(ctx, txn, int64(*comm.SourceTextLogID)) if err != nil { return result, fmt.Errorf("text log from ID: %w", err) } @@ -87,7 +88,7 @@ func CommunicationRelatedRecords(ctx context.Context, user User, comm *modelpubl }) } } else if comm.SourceReportID != nil { - report, err := querypublicreport.ReportFromID(ctx, int64(*comm.SourceReportID)) + report, err := querypublicreport.ReportFromID(ctx, txn, int64(*comm.SourceReportID)) if err != nil { return result, fmt.Errorf("report from ID: %w", err) } @@ -123,10 +124,12 @@ func CommunicationRelatedRecords(ctx context.Context, user User, comm *modelpubl return result, nil } func CommunicationsForOrganization(ctx context.Context, org_id int64) ([]modelpublic.Communication, error) { - return querypublic.CommunicationsFromOrganization(ctx, org_id) + txn := db.PGInstance.PGXPool + return querypublic.CommunicationsFromOrganization(ctx, txn, org_id) } func CommunicationFromID(ctx context.Context, user User, comm_id int64) (*modelpublic.Communication, error) { - comm, err := querypublic.CommunicationFromID(ctx, comm_id) + txn := db.PGInstance.PGXPool + comm, err := querypublic.CommunicationFromID(ctx, txn, comm_id) if err != nil { return nil, err } diff --git a/platform/csv/pool.go b/platform/csv/pool.go index dafc3f5d..e95d6f56 100644 --- a/platform/csv/pool.go +++ b/platform/csv/pool.go @@ -13,10 +13,10 @@ import ( "github.com/Gleipnir-Technology/bob/dialect/psql" "github.com/Gleipnir-Technology/bob/dialect/psql/um" "github.com/Gleipnir-Technology/nidus-sync/config" - "github.com/Gleipnir-Technology/nidus-sync/lint" "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/lint" "github.com/Gleipnir-Technology/nidus-sync/platform/file" "github.com/Gleipnir-Technology/nidus-sync/platform/geocode" "github.com/Gleipnir-Technology/nidus-sync/platform/geom" @@ -260,8 +260,8 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile, parts := strings.SplitN(col, " ", 2) if len(parts) != 2 { lint.LogOnErrCtx(func(ctx context.Context) error { - return addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a house number and street. It needs to be in the form '123 main'", col)) - }, ctx, "add address parse error") + return addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a house number and street. It needs to be in the form '123 main'", col)) + }, ctx, "add address parse error") continue } setter.AddressNumber = omit.From(parts[0]) @@ -277,8 +277,8 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile, err := condition.Scan(col_translated) if err != nil { lint.LogOnErrCtx(func(ctx context.Context) error { - return addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a pool condition that we recognize. It should be one of %s", col, poolConditionValidValues())) - }, ctx, "add pool condition error") + return addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a pool condition that we recognize. It should be one of %s", col, poolConditionValidValues())) + }, ctx, "add pool condition error") setter.Condition = omit.From(enums.PoolconditiontypeUnknown) continue } @@ -291,13 +291,8 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile, phone, err := text.ParsePhoneNumber(col) if err != nil { lint.LogOnErrCtx(func(ctx context.Context) error { - return addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a phone number that we recognize. Ideally it should be of the form '+12223334444'", col)) - }, ctx, "add phone number error") - continue - } - err = text.EnsureInDB(ctx, txn, *phone) - if err != nil { - log.Error().Err(err).Str("phone", col).Msg("ensure in DB failure") + return addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a phone number that we recognize. Ideally it should be of the form '+12223334444'", col)) + }, ctx, "add phone number error") continue } setter.PropertyOwnerPhoneE164 = omitnull.From(phone.PhoneString()) @@ -318,11 +313,6 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile, }, ctx, "add phone number error") continue } - err = text.EnsureInDB(ctx, txn, *phone) - if err != nil { - log.Error().Err(err).Str("phone", col).Msg("ensure in DB failure") - continue - } setter.ResidentPhoneE164 = omitnull.From(phone.PhoneString()) case headerPoolTag: tags[header_names[i]] = col diff --git a/platform/email/email.go b/platform/email/email.go index 4f9b5e1d..d016c348 100644 --- a/platform/email/email.go +++ b/platform/email/email.go @@ -13,6 +13,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/enums" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/platform/background" "github.com/aarondl/opt/omit" @@ -20,7 +21,7 @@ import ( "github.com/rs/zerolog/log" ) -func EnsureInDB(ctx context.Context, destination string) (err error) { +func EnsureInDB(ctx context.Context, txn db.Ex, contact modelcomms.Contact, destination string) (err error) { _, err = models.FindCommsEmailContact(ctx, db.PGInstance.BobDB, destination) if err != nil { // doesn't exist diff --git a/platform/email/initial.go b/platform/email/initial.go index 9cba8eba..c546998b 100644 --- a/platform/email/initial.go +++ b/platform/email/initial.go @@ -6,12 +6,13 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" "github.com/Gleipnir-Technology/nidus-sync/db/models" //"github.com/rs/zerolog/log" ) -func maybeSendInitialEmail(ctx context.Context, destination string) error { - err := EnsureInDB(ctx, destination) +func maybeSendInitialEmail(ctx context.Context, txn db.Ex, contact modelcomms.Contact, destination string) error { + err := EnsureInDB(ctx, txn, contact, destination) if err != nil { return fmt.Errorf("Failed to add email recipient to database: %w", err) } diff --git a/platform/email/report_notification_confirmation.go b/platform/email/report_notification_confirmation.go index 7578e148..081d2310 100644 --- a/platform/email/report_notification_confirmation.go +++ b/platform/email/report_notification_confirmation.go @@ -5,11 +5,13 @@ import ( "fmt" "github.com/Gleipnir-Technology/nidus-sync/config" + "github.com/Gleipnir-Technology/nidus-sync/db" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" //"github.com/rs/zerolog/log" ) -func SendReportConfirmation(ctx context.Context, destination, report_id string) error { - err := maybeSendInitialEmail(ctx, destination) +func SendReportConfirmation(ctx context.Context, txn db.Ex, contact modelcomms.Contact, destination, report_id string) error { + err := maybeSendInitialEmail(ctx, txn, contact, destination) if err != nil { return fmt.Errorf("Failed to handle initial email: %w", err) } @@ -25,5 +27,3 @@ func SendReportConfirmation(ctx context.Context, destination, report_id string) subject := fmt.Sprintf("Mosquito Report Submission - %s", report_id_str) return sendEmailBegin(ctx, config.ForwardEmailRMOAddress, destination, templateReportNotificationConfirmationID, subject, data) } - - diff --git a/platform/publicreport.go b/platform/publicreport.go index 8321c1b2..9e90cbab 100644 --- a/platform/publicreport.go +++ b/platform/publicreport.go @@ -11,13 +11,15 @@ import ( "github.com/Gleipnir-Technology/jet/postgres" "github.com/Gleipnir-Technology/nidus-sync/db" - "github.com/Gleipnir-Technology/nidus-sync/lint" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model" tablepublic "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/table" modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" tablepublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/table" + querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms" querypublic "github.com/Gleipnir-Technology/nidus-sync/db/query/public" querypublicreport "github.com/Gleipnir-Technology/nidus-sync/db/query/publicreport" + "github.com/Gleipnir-Technology/nidus-sync/lint" "github.com/Gleipnir-Technology/nidus-sync/platform/email" "github.com/Gleipnir-Technology/nidus-sync/platform/event" "github.com/Gleipnir-Technology/nidus-sync/platform/geocode" @@ -106,7 +108,7 @@ func PublicReportInvalid(ctx context.Context, user User, public_id string) error } func PublicReportMessageCreate(ctx context.Context, user User, public_id, message string) (message_id *int32, err error) { - txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) + txn, err := db.BeginTxn(ctx) if err != nil { return nil, fmt.Errorf("create txn: %w", err) } @@ -148,8 +150,7 @@ func PublicReportMessageCreate(ctx context.Context, user User, public_id, messag return nil, errors.New("no contact methods available") } } -func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_updates querypublicreport.ReportUpdater, compliance_updates querypublicreport.ComplianceUpdater, address *types.Address, location *types.Location) error { - //txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) +func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_updates querypublicreport.ReportUpdater, compliance_updates querypublicreport.ComplianceUpdater, address *types.Address, location *types.Location, reporter *types.Contact) error { txn, err := db.BeginTxn(ctx) if err != nil { return fmt.Errorf("create txn: %w", err) @@ -159,7 +160,6 @@ func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_ if err != nil { return fmt.Errorf("query report existence: %w", err) } - //compliance, err := models.FindPublicreportCompliance(ctx, txn, report.ID) compliance, err := querypublicreport.ComplianceFromID(ctx, txn, int64(report.ID)) if err != nil { return fmt.Errorf("find compliance %d: %w", report.ID, err) @@ -224,6 +224,26 @@ func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_ return fmt.Errorf("update location: %w", err) } } + if reporter != nil { + if report.ReporterContactID == nil { + contact := modelcomms.Contact{ + Created: time.Now(), + Name: reporter.Name, + OrganizationID: report.OrganizationID, + } + _, err = querycomms.ContactInsert(ctx, txn, contact) + if err != nil { + return fmt.Errorf("insert contact: %w", err) + } + } else if reporter.Name != "" { + err = querycomms.ContactUpdateName(ctx, txn, int64(*report.ReporterContactID), reporter.Name) + if err != nil { + return fmt.Errorf("update contact: %w", err) + } + } else { + log.Warn().Int32("report.id", report.ID).Msg("not sure what to do with empty reporter name") + } + } if err := txn.Commit(ctx); err != nil { return fmt.Errorf("commit: %w", err) } diff --git a/platform/publicreport/notification.go b/platform/publicreport/notification.go new file mode 100644 index 00000000..3dd73b5c --- /dev/null +++ b/platform/publicreport/notification.go @@ -0,0 +1,105 @@ +package publicreport + +import ( + "context" + "fmt" + "time" + + "github.com/Gleipnir-Technology/nidus-sync/db" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" + modelpublic "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model" + modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" + querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms" + querypublic "github.com/Gleipnir-Technology/nidus-sync/db/query/public" + querypublicreport "github.com/Gleipnir-Technology/nidus-sync/db/query/publicreport" + "github.com/Gleipnir-Technology/nidus-sync/platform/email" + "github.com/Gleipnir-Technology/nidus-sync/platform/text" + "github.com/Gleipnir-Technology/nidus-sync/platform/types" + //"github.com/rs/zerolog/log" +) + +func DistrictForReport(ctx context.Context, report_id string) (modelpublic.Organization, error) { + report, err := querypublicreport.ReportFromPublicID(ctx, db.PGInstance.PGXPool, report_id) + if err != nil { + return modelpublic.Organization{}, fmt.Errorf("Failed to find report %s: %w", report_id, err) + } + result, e := querypublic.OrganizationFromID(ctx, db.PGInstance.PGXPool, int64(report.OrganizationID)) + if e != nil { + return modelpublic.Organization{}, fmt.Errorf("Failed to load organization %d: %w", report.OrganizationID, e) + } + return result, nil +} + +func RegisterNotificationEmail(ctx context.Context, txn db.Ex, report modelpublicreport.Report, contact modelcomms.Contact, destination string) error { + err := email.EnsureInDB(ctx, txn, contact, destination) + if err != nil { + return fmt.Errorf("Failed to ensure phone is in DB: %w", err) + } + _, err = querypublicreport.NotifyEmailCreate(ctx, txn, report.ID, destination) + if err != nil { + return err + } + return email.SendReportConfirmation(ctx, txn, contact, destination, report.PublicID) +} + +func RegisterNotificationPhone(ctx context.Context, txn db.Ex, report modelpublicreport.Report, contact modelcomms.Contact, phone types.E164) error { + err := text.EnsureInDB(ctx, txn, contact, phone) + if err != nil { + return fmt.Errorf("Failed to ensure phone is in DB: %w", err) + } + _, err = querypublicreport.NotifyPhoneCreate(ctx, txn, report.ID, phone.PhoneString()) + if err != nil { + return err + } + return text.ReportSubscriptionConfirmationText(ctx, db.PGInstance.PGXPool, phone, report.PublicID) +} + +func RegisterSubscriptionEmail(ctx context.Context, txn db.Ex, contact modelcomms.Contact, destination string) error { + _, err := querypublicreport.SubscribeEmailInsert(ctx, txn, modelpublicreport.SubscribeEmail{ + Created: time.Now(), + Deleted: nil, + EmailAddress: destination, + }) + if err != nil { + return fmt.Errorf("Failed to save new subscription email row: %w", err) + } + + return nil +} +func RegisterSubscriptionPhone(ctx context.Context, txn db.Ex, contact modelcomms.Contact, phone types.E164) error { + _, err := querypublicreport.SubscribePhoneInsert(ctx, txn, modelpublicreport.SubscribePhone{ + Created: time.Now(), + Deleted: nil, + PhoneE164: phone.PhoneString(), + }) + if err != nil { + return fmt.Errorf("Failed to save new subscription phone row: %w", err) + } + return nil +} + +func SaveReporter(ctx context.Context, txn db.Ex, report modelpublicreport.Report, name string) (contact modelcomms.Contact, err error) { + if report.ReporterContactID == nil { + contact = modelcomms.Contact{ + Created: time.Now(), + Name: name, + OrganizationID: report.OrganizationID, + } + contact, err = querycomms.ContactInsert(ctx, txn, contact) + if err != nil { + return contact, fmt.Errorf("contact insert: %w", err) + } + } else { + contact, err = querycomms.ContactFromID(ctx, txn, int64(*report.ReporterContactID)) + if err != nil { + return contact, fmt.Errorf("contact query: %w", err) + } + if name != "" && contact.Name != name { + err = querycomms.ContactUpdateName(ctx, txn, int64(contact.ID), name) + if err != nil { + return contact, fmt.Errorf("contact update name: %w", err) + } + } + } + return contact, nil +} diff --git a/platform/publicreport/report.go b/platform/publicreport/report.go index 7af42b39..704ff68a 100644 --- a/platform/publicreport/report.go +++ b/platform/publicreport/report.go @@ -148,7 +148,7 @@ func reportQueryToRows(ctx context.Context, reports []modelpublicreport.Report, Email: &row.ReporterEmail, HasEmail: row.ReporterEmail != "", HasPhone: row.ReporterPhone != "", - Name: &row.ReporterName, + Name: row.ReporterName, Phone: &row.ReporterPhone, }, Status: row.Status.String(), diff --git a/platform/publicreport_notification.go b/platform/publicreport_notification.go index f60dfb38..b1509a92 100644 --- a/platform/publicreport_notification.go +++ b/platform/publicreport_notification.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/Gleipnir-Technology/nidus-sync/db" + querypublicreport "github.com/Gleipnir-Technology/nidus-sync/db/query/publicreport" "github.com/Gleipnir-Technology/nidus-sync/lint" - "github.com/Gleipnir-Technology/nidus-sync/db/models" - "github.com/Gleipnir-Technology/nidus-sync/platform/report" + "github.com/Gleipnir-Technology/nidus-sync/platform/publicreport" "github.com/Gleipnir-Technology/nidus-sync/platform/types" //"github.com/rs/zerolog/log" ) @@ -23,31 +23,33 @@ type PublicreportNotification struct { } func PublicreportNotificationCreate(ctx context.Context, pn PublicreportNotification) error { - txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) + txn, err := db.BeginTxn(ctx) if err != nil { return fmt.Errorf("begin txn: %w", err) } defer lint.LogOnErrRollback(txn.Rollback, ctx, "rollback") - rep, err := models.PublicreportReports.Query( - models.SelectWhere.PublicreportReports.PublicID.EQ(pn.ReportID), - ).One(ctx, db.PGInstance.BobDB) + + report, err := querypublicreport.ReportFromPublicID(ctx, txn, pn.ReportID) if err != nil { return fmt.Errorf("find report '%s': %w", pn.ReportID, err) } + if report == nil { + return fmt.Errorf("no such report '%s'", pn.ReportID) + } - err = report.SaveReporter(ctx, txn, pn.ReportID, pn.Name, pn.Email, pn.Phone, pn.Consent) + contact, err := publicreport.SaveReporter(ctx, txn, *report, pn.Name) if err != nil { return fmt.Errorf("save reporter: %w", err) } if pn.Email != "" { if pn.Subscription { - err = report.RegisterSubscriptionEmail(ctx, txn, pn.Email) + err = publicreport.RegisterSubscriptionEmail(ctx, txn, contact, pn.Email) if err != nil { return fmt.Errorf("register subscription email: %w", err) } } if pn.Notification { - err = report.RegisterNotificationEmail(ctx, txn, pn.ReportID, pn.Email) + err = publicreport.RegisterNotificationEmail(ctx, txn, *report, contact, pn.Email) if err != nil { return fmt.Errorf("register notification email: %w", err) } @@ -55,13 +57,13 @@ func PublicreportNotificationCreate(ctx context.Context, pn PublicreportNotifica } if pn.Phone != nil { if pn.Subscription { - err = report.RegisterSubscriptionPhone(ctx, txn, *pn.Phone) + err = publicreport.RegisterSubscriptionPhone(ctx, txn, contact, *pn.Phone) if err != nil { return fmt.Errorf("register subscription phone: %w", err) } } if pn.Notification { - err = report.RegisterNotificationPhone(ctx, txn, pn.ReportID, *pn.Phone) + err = publicreport.RegisterNotificationPhone(ctx, txn, *report, contact, *pn.Phone) if err != nil { return fmt.Errorf("register notification phone: %w", err) } @@ -70,6 +72,6 @@ func PublicreportNotificationCreate(ctx context.Context, pn PublicreportNotifica if err := txn.Commit(ctx); err != nil { return fmt.Errorf("commit: %w", err) } - PublicReportReporterUpdated(ctx, rep.OrganizationID, pn.ReportID) + PublicReportReporterUpdated(ctx, report.OrganizationID, pn.ReportID) return nil } diff --git a/platform/report/notification.go b/platform/report/notification.go deleted file mode 100644 index 4da5c053..00000000 --- a/platform/report/notification.go +++ /dev/null @@ -1,197 +0,0 @@ -package report - -import ( - "context" - "fmt" - "time" - - "github.com/Gleipnir-Technology/bob" - "github.com/Gleipnir-Technology/nidus-sync/db" - "github.com/Gleipnir-Technology/nidus-sync/lint" - "github.com/Gleipnir-Technology/nidus-sync/db/models" - "github.com/Gleipnir-Technology/nidus-sync/platform/email" - "github.com/Gleipnir-Technology/nidus-sync/platform/text" - "github.com/Gleipnir-Technology/nidus-sync/platform/types" - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" - //"github.com/rs/zerolog/log" -) - -func DistrictForReport(ctx context.Context, report_id string) (*models.Organization, error) { - report, err := reportByPublicID(ctx, db.PGInstance.BobDB, report_id) - if err != nil { - return nil, fmt.Errorf("Failed to find report %s: %w", report_id, err) - } - result, e := models.FindOrganization(ctx, db.PGInstance.BobDB, report.OrganizationID) - if e != nil { - return nil, fmt.Errorf("Failed to load organization %d: %w", report.OrganizationID, e) - } - return result, nil -} - -func RegisterNotificationEmail(ctx context.Context, txn bob.Executor, report_id string, destination string) error { - report, e := reportByPublicID(ctx, db.PGInstance.BobDB, report_id) - if e != nil { - return fmt.Errorf("Failed to find report: %w", e) - } - e = email.EnsureInDB(ctx, destination) - if e != nil { - return fmt.Errorf("Failed to ensure phone is in DB: %w", e) - } - err := addNotificationEmail(ctx, txn, report, destination) - if err != nil { - return err - } - lint.LogOnErrCtx(func(ctx context.Context) error { - return email.SendReportConfirmation(ctx, destination, report_id) - }, ctx, "send report confirmation") - return nil -} - -func RegisterNotificationPhone(ctx context.Context, txn bob.Executor, report_id string, phone types.E164) error { - report, e := reportByPublicID(ctx, db.PGInstance.BobDB, report_id) - if e != nil { - return fmt.Errorf("Failed to find report: %w", e) - } - e = text.EnsureInDB(ctx, db.PGInstance.BobDB, phone) - if e != nil { - return fmt.Errorf("Failed to ensure phone is in DB: %w", e) - } - err := addNotificationPhone(ctx, txn, report, phone) - if err != nil { - return err - } - lint.LogOnErrCtx(func(ctx context.Context) error { - return text.ReportSubscriptionConfirmationText(ctx, db.PGInstance.BobDB, phone, report.PublicID) - }, ctx, "report subscription confirmation text") - return nil -} - -func RegisterSubscriptionEmail(ctx context.Context, txn bob.Executor, destination string) error { - e := email.EnsureInDB(ctx, destination) - if e != nil { - return fmt.Errorf("Failed to ensure email is in DB: %w", e) - } - setter := models.PublicreportSubscribeEmailSetter{ - Created: omit.From(time.Now()), - Deleted: omitnull.FromPtr[time.Time](nil), - //DistrictID: omit.FromPtr[int32](nil), - EmailAddress: omit.From(destination), - } - _, err := models.PublicreportSubscribeEmails.Insert(&setter).Exec(ctx, txn) - if err != nil { - return fmt.Errorf("Failed to save new subscription email row: %w", err) - } - - return nil -} -func RegisterSubscriptionPhone(ctx context.Context, txn bob.Executor, phone types.E164) error { - e := text.EnsureInDB(ctx, db.PGInstance.BobDB, phone) - if e != nil { - return fmt.Errorf("Failed to ensure phone is in DB: %w", e) - } - setter := models.PublicreportSubscribePhoneSetter{ - Created: omit.From(time.Now()), - Deleted: omitnull.FromPtr[time.Time](nil), - //DistrictID: omitnull.FromPtr[int32](nil), - PhoneE164: omit.From(phone.PhoneString()), - } - _, err := models.PublicreportSubscribePhones.Insert(&setter).Exec(ctx, txn) - if err != nil { - return fmt.Errorf("Failed to save new subscription phone row: %w", err) - } - return nil -} - -func SaveReporter(ctx context.Context, txn bob.Executor, report_id string, name string, email string, phone *types.E164, has_consent bool) error { - report, e := reportByPublicID(ctx, db.PGInstance.BobDB, report_id) - if e != nil { - return fmt.Errorf("Failed to find report: %w", e) - } - if name != "" { - err := updateReporterName(ctx, txn, report, name) - if err != nil { - return err - } - } - if phone != nil { - err := updateReporterPhone(ctx, txn, report, *phone) - if err != nil { - return err - } - } - if email != "" { - err := updateReporterEmail(ctx, txn, report, email) - if err != nil { - return err - } - } - err := updateReporterConsent(ctx, txn, report, has_consent) - if err != nil { - return err - } - return nil -} -func reportByPublicID(ctx context.Context, txn bob.Executor, public_id string) (*models.PublicreportReport, error) { - return models.PublicreportReports.Query( - models.SelectWhere.PublicreportReports.PublicID.EQ(public_id), - ).One(ctx, txn) -} -func addNotificationEmail(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, email string) error { - setter := models.PublicreportNotifyEmailSetter{ - Created: omit.From(time.Now()), - Deleted: omitnull.FromPtr[time.Time](nil), - EmailAddress: omit.From(email), - ReportID: omit.From(report.ID), - } - _, err := models.PublicreportNotifyEmails.Insert(&setter).Exec(ctx, txn) - if err != nil { - return fmt.Errorf("Failed to save new notification email row: %w", err) - } - return nil -} -func addNotificationPhone(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, phone types.E164) error { - var err error - setter := models.PublicreportNotifyPhoneSetter{ - Created: omit.From(time.Now()), - Deleted: omitnull.FromPtr[time.Time](nil), - PhoneE164: omit.From(phone.PhoneString()), - ReportID: omit.From(report.ID), - } - _, err = models.PublicreportNotifyPhones.Insert(&setter).Exec(ctx, txn) - if err != nil { - return fmt.Errorf("Failed to save new notification phone row: %w", err) - } - return nil -} -func updateReporterConsent(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, has_consent bool) error { - return updateReportCol(ctx, txn, report, &models.PublicreportReportSetter{ - ReporterContactConsent: omitnull.From(has_consent), - }) -} -func updateReporterEmail(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, email string) error { - return updateReportCol(ctx, txn, report, &models.PublicreportReportSetter{ - ReporterEmail: omit.From(email), - }) -} -func updateReporterName(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, name string) error { - return updateReportCol(ctx, txn, report, &models.PublicreportReportSetter{ - ReporterName: omit.From(name), - }) -} -func updateReportCol(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, setter *models.PublicreportReportSetter) error { - err := report.Update(ctx, txn, setter) - if err != nil { - return fmt.Errorf("Failed to update nuisance report in the database: %w", err) - } - return nil -} -func updateReporterPhone(ctx context.Context, txn bob.Executor, report *models.PublicreportReport, phone types.E164) error { - err := report.Update(ctx, txn, &models.PublicreportReportSetter{ - ReporterPhone: omit.From(phone.PhoneString()), - }) - if err != nil { - return fmt.Errorf("Failed to update report: %w", err) - } - return nil -} diff --git a/platform/report/some_report.go b/platform/report/some_report.go deleted file mode 100644 index 2cbd2d77..00000000 --- a/platform/report/some_report.go +++ /dev/null @@ -1,37 +0,0 @@ -package report - -import ( - "context" - //"crypto/rand" - //"fmt" - //"math/big" - //"strconv" - //"strings" - //"time" - - //"github.com/aarondl/opt/omit" - //"github.com/aarondl/opt/omitnull" - "github.com/Gleipnir-Technology/bob" - //"github.com/Gleipnir-Technology/bob/dialect/psql" - //"github.com/Gleipnir-Technology/bob/dialect/psql/sm" - //"github.com/Gleipnir-Technology/bob/dialect/psql/um" - //"github.com/Gleipnir-Technology/nidus-sync/background" - //"github.com/Gleipnir-Technology/nidus-sync/db" - //"github.com/Gleipnir-Technology/nidus-sync/db/models" - //"github.com/Gleipnir-Technology/nidus-sync/db/sql" - "github.com/Gleipnir-Technology/nidus-sync/platform/types" - //"github.com/rs/zerolog/log" - //"github.com/stephenafamo/scan" -) - -type SomeReport interface { - addNotificationEmail(context.Context, bob.Executor, string) error - addNotificationPhone(context.Context, bob.Executor, types.E164) error - districtID(context.Context) *int32 - updateReporterConsent(context.Context, bob.Executor, bool) error - updateReporterEmail(context.Context, bob.Executor, string) error - updateReporterName(context.Context, bob.Executor, string) error - updateReporterPhone(context.Context, bob.Executor, types.E164) error - PublicReportID() string - reportID() int32 -} diff --git a/platform/text/job.go b/platform/text/job.go index 476932c5..f07fb600 100644 --- a/platform/text/job.go +++ b/platform/text/job.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/Gleipnir-Technology/nidus-sync/db" - "github.com/Gleipnir-Technology/nidus-sync/db/models" + querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms" "github.com/Gleipnir-Technology/nidus-sync/platform/types" //"github.com/rs/zerolog/log" ) @@ -14,8 +14,8 @@ func JobRespond(ctx context.Context, log_id int32) error { return respondText(ctx, log_id) } func JobSend(ctx context.Context, job_id int32) error { - bxn := db.PGInstance.BobDB - job, err := models.FindCommsTextJob(ctx, bxn, job_id) + bxn := db.PGInstance.PGXPool + job, err := querycomms.TextJobFromID(ctx, bxn, int64(job_id)) if err != nil { return fmt.Errorf("find text: %w", err) } @@ -23,11 +23,8 @@ func JobSend(ctx context.Context, job_id int32) error { return sendTextComplete(ctx, job) } func handleWaitingTextJobs(ctx context.Context, dst types.E164) error { - bxn := db.PGInstance.BobDB - jobs, err := models.CommsTextJobs.Query( - models.SelectWhere.CommsTextJobs.Destination.EQ(dst.PhoneString()), - models.SelectWhere.CommsTextJobs.Completed.IsNull(), - ).All(ctx, bxn) + bxn := db.PGInstance.PGXPool + jobs, err := querycomms.TextJobsWaitingFromDestination(ctx, bxn, dst.PhoneString()) if err != nil { return fmt.Errorf("query jobs: %w", err) } diff --git a/platform/text/llm.go b/platform/text/llm.go index 6acf58ec..313d95bb 100644 --- a/platform/text/llm.go +++ b/platform/text/llm.go @@ -6,12 +6,10 @@ import ( "fmt" "github.com/rs/zerolog/log" - "github.com/Gleipnir-Technology/bob" "github.com/Gleipnir-Technology/bob/dialect/psql" "github.com/Gleipnir-Technology/bob/dialect/psql/um" "github.com/Gleipnir-Technology/nidus-sync/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/db/sql" "github.com/Gleipnir-Technology/nidus-sync/llm" @@ -34,7 +32,7 @@ func generateNextMessage(ctx context.Context, history []llm.Message, customer_ph } return llm.GenerateNextMessage(ctx, history, _handle_report_status, _handle_contact_district, _handle_contact_supervisor) } -func handleResetConversation(ctx context.Context, txn bob.Executor, src types.E164) error { +func handleResetConversation(ctx context.Context, txn db.Ex, src types.E164) error { err := wipeLLMMemory(ctx, src) sublog := log.With().Str("src", src.PhoneString()).Logger() if err != nil { diff --git a/platform/text/phone_number.go b/platform/text/phone_number.go index 9e261f53..970843b7 100644 --- a/platform/text/phone_number.go +++ b/platform/text/phone_number.go @@ -4,31 +4,28 @@ import ( "context" "fmt" - "github.com/Gleipnir-Technology/bob" - "github.com/Gleipnir-Technology/bob/dialect/psql" - "github.com/Gleipnir-Technology/bob/dialect/psql/im" "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/enums" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" "github.com/Gleipnir-Technology/nidus-sync/db/models" + querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms" "github.com/Gleipnir-Technology/nidus-sync/platform/types" - "github.com/aarondl/opt/omit" - "github.com/rs/zerolog/log" + //"github.com/rs/zerolog/log" ) -func EnsureInDB(ctx context.Context, txn bob.Executor, dst types.E164) (err error) { - return ensureInDB(ctx, txn, dst.PhoneString()) +func EnsureInDB(ctx context.Context, txn db.Ex, contact modelcomms.Contact, dst types.E164) (err error) { + return ensureInDB(ctx, txn, contact, dst.PhoneString()) } -func ensureInDB(ctx context.Context, txn bob.Executor, destination string) (err error) { - _, err = psql.Insert( - im.Into("comms.phone", "can_sms", "e164", "is_subscribed", "status"), - im.Values( - psql.Arg(true), - psql.Arg(destination), - psql.Arg(false), - psql.Arg("unconfirmed"), - ), - im.OnConflict("e164").DoNothing(), - ).Exec(ctx, txn) +func ensureInDB(ctx context.Context, txn db.Ex, contact modelcomms.Contact, destination string) (err error) { + contact_phone := modelcomms.ContactPhone{ + CanSms: true, + ConfirmedMessageID: nil, + ContactID: contact.ID, + E164: destination, + IsSubscribed: false, + StopMessageID: nil, + } + _, err = querycomms.ContactPhoneInsert(ctx, txn, contact_phone) return err } func phoneStatus(ctx context.Context, src types.E164) (enums.CommsPhonestatustype, error) { @@ -38,17 +35,3 @@ func phoneStatus(ctx context.Context, src types.E164) (enums.CommsPhonestatustyp } return phone.Status, nil } -func setPhoneStatus(ctx context.Context, txn bob.Executor, src types.E164, status enums.CommsPhonestatustype) error { - phone, err := models.FindCommsPhone(ctx, txn, src.PhoneString()) - if err != nil { - return fmt.Errorf("Failed to determine if '%s' is subscribed: %w", src, err) - } - err = phone.Update(ctx, txn, &models.CommsPhoneSetter{ - Status: omit.From(status), - }) - if err != nil { - return fmt.Errorf("update phone status: %w", err) - } - log.Info().Str("src", src.PhoneString()).Str("status", string(status)).Msg("Set number subscribed") - return nil -} diff --git a/platform/text/report.go b/platform/text/report.go index 2a836d6d..d20bd899 100644 --- a/platform/text/report.go +++ b/platform/text/report.go @@ -4,20 +4,15 @@ import ( "context" "fmt" - "github.com/Gleipnir-Technology/bob" - "github.com/Gleipnir-Technology/bob/dialect/psql" - "github.com/Gleipnir-Technology/bob/dialect/psql/sm" "github.com/Gleipnir-Technology/nidus-sync/db" - "github.com/Gleipnir-Technology/nidus-sync/db/enums" - //"github.com/Gleipnir-Technology/nidus-sync/db/models" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" "github.com/Gleipnir-Technology/nidus-sync/platform/types" //"github.com/rs/zerolog/log" - "github.com/stephenafamo/scan" ) // Send a message from a district to a public reporter within the context of the public report -func ReportMessage(ctx context.Context, txn bob.Executor, user_id int32, report_id int32, destination types.E164, content string) (*int32, error) { - job_id, err := sendTextBegin(ctx, txn, &user_id, &report_id, destination, content, enums.CommsTextjobtypeReportMessage) +func ReportMessage(ctx context.Context, txn db.Ex, user_id int32, report_id int32, destination types.E164, content string) (*int32, error) { + job_id, err := sendTextBegin(ctx, txn, &user_id, &report_id, destination, content, modelcomms.Textjobtype_ReportMessage) if err != nil { return nil, fmt.Errorf("Failed to send initial confirmation: %w", err) } @@ -25,43 +20,11 @@ func ReportMessage(ctx context.Context, txn bob.Executor, user_id int32, report_ } // Send a message from the system to a public reporter indicating they are subscribed to updates on the report -func ReportSubscriptionConfirmationText(ctx context.Context, txn bob.Executor, destination types.E164, report_id string) error { +func ReportSubscriptionConfirmationText(ctx context.Context, txn db.Ex, destination types.E164, report_id string) error { content := fmt.Sprintf("Thanks for submitting mosquito report %s. Text for any questions. We'll send you updates as we get them.", report_id) - _, err := sendTextBegin(ctx, txn, nil, nil, destination, content, enums.CommsTextjobtypeReportConfirmation) + _, err := sendTextBegin(ctx, txn, nil, nil, destination, content, modelcomms.Textjobtype_ReportConfirmation) if err != nil { return fmt.Errorf("Failed to send initial confirmation: %w", err) } return err } - -type reportIDs struct { - ID int32 `db:"id"` - PublicID string `db:"public_id"` - OrganizationID int32 `db:"organization_id"` -} - -// Get the list of reports that are still open for a particular text message recipient -// 'still open' is not well-defined throughout the system, but for now we'll go with -// 'not reviewed in any way'. -func reportsForTextRecipient(ctx context.Context, txn bob.Executor, destination types.E164) ([]reportIDs, error) { - rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select( - sm.Columns( - "r.id", - "r.public_id", - "r.organization_id", - ), - sm.From("comms.text_job").As("t"), - sm.InnerJoin("publicreport.report").As("r").OnEQ( - psql.Quote("t", "report_id"), - psql.Quote("r", "id"), - ), - sm.Where(psql.Quote("t", "report_id").IsNotNull()), - sm.Where(psql.Quote("t", "destination").EQ(psql.Arg(destination.PhoneString()))), - sm.Where(psql.Quote("r", "status").EQ(psql.Arg(enums.PublicreportReportstatustypeReported))), - ), scan.StructMapper[reportIDs]()) - if err != nil { - return []reportIDs{}, fmt.Errorf("query reports: %w", err) - } - - return rows, nil -} diff --git a/platform/text/send.go b/platform/text/send.go index 3cbbc193..b669c6a6 100644 --- a/platform/text/send.go +++ b/platform/text/send.go @@ -5,50 +5,47 @@ import ( "fmt" "time" - "github.com/Gleipnir-Technology/bob" "github.com/Gleipnir-Technology/nidus-sync/comms/text" "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/enums" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" + modelpublic "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model" + modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" + querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms" + querypublic "github.com/Gleipnir-Technology/nidus-sync/db/query/public" + querypublicreport "github.com/Gleipnir-Technology/nidus-sync/db/query/publicreport" "github.com/Gleipnir-Technology/nidus-sync/lint" - "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/platform/background" "github.com/Gleipnir-Technology/nidus-sync/platform/event" "github.com/Gleipnir-Technology/nidus-sync/platform/types" - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" "github.com/rs/zerolog/log" ) -func ensureInitialText(ctx context.Context, txn bob.Executor, dst types.E164) error { - rows, err := models.CommsTextLogs.Query( - models.SelectWhere.CommsTextLogs.Destination.EQ(dst.PhoneString()), - models.SelectWhere.CommsTextLogs.IsWelcome.EQ(true), - ).All(ctx, txn) +func ensureInitialText(ctx context.Context, txn db.Ex, dst types.E164) error { + logs, err := querycomms.TextLogWelcomeFromDestination(ctx, txn, dst.PhoneString()) if err != nil { return fmt.Errorf("Failed to query text logs: %w", err) } - if len(rows) > 0 { + if len(logs) > 0 { return nil } return sendInitialText(ctx, txn, dst) } -func resendInitialText(ctx context.Context, txn bob.Executor, dst types.E164) error { - phone, err := models.FindCommsPhone(ctx, txn, dst.PhoneString()) +func resendInitialText(ctx context.Context, txn db.Ex, dst types.E164) error { + phone, err := querycomms.ContactPhoneFromE164(ctx, txn, dst.PhoneString()) if err != nil { return fmt.Errorf("Failed to find phone %s: %w", dst, err) } - err = phone.Update(ctx, txn, &models.CommsPhoneSetter{ - Status: omit.From(enums.CommsPhonestatustypeUnconfirmed), - }) + err = querycomms.ContactPhoneUpdateStopMessageID(ctx, txn, phone.E164, nil) if err != nil { return fmt.Errorf("Failed to clear subscription on phone %s: %w", dst, err) } return nil } -func sendInitialText(ctx context.Context, txn bob.Executor, dst types.E164) error { +func sendInitialText(ctx context.Context, txn db.Ex, dst types.E164) error { content := "Welcome to Report Mosquitoes Online. We received your request and want to confirm text updates. Reply YES to continue. Reply STOP at any time to unsubscribe" - _, err := sendTextDirect(ctx, txn, enums.CommsTextoriginWebsiteAction, dst.PhoneString(), content, false, true) + _, err := sendTextDirect(ctx, txn, modelcomms.Textorigin_WebsiteAction, dst.PhoneString(), content, false, true) if err != nil { return fmt.Errorf("send text: %w", err) } @@ -57,21 +54,17 @@ func sendInitialText(ctx context.Context, txn bob.Executor, dst types.E164) erro // Begin the process of sending the text message, but only get as far as adding it to // the database, then let the backend finish sending. -func sendTextBegin(ctx context.Context, txn bob.Executor, user_id *int32, report_id *int32, destination types.E164, content string, type_ enums.CommsTextjobtype) (*int32, error) { - err := EnsureInDB(ctx, txn, destination) - if err != nil { - return nil, fmt.Errorf("Failed to ensure text message destination is in the DB: %w", err) - } - job, err := models.CommsTextJobs.Insert(&models.CommsTextJobSetter{ - Content: omit.From(content), - CreatorID: omitnull.FromPtr(user_id), - Created: omit.From(time.Now()), - Destination: omit.From(destination.PhoneString()), +func sendTextBegin(ctx context.Context, txn db.Ex, user_id *int32, report_id *int32, destination types.E164, content string, type_ modelcomms.Textjobtype) (*int32, error) { + job, err := querycomms.TextJobInsert(ctx, txn, modelcomms.TextJob{ + Content: content, + CreatorID: user_id, + Created: time.Now(), + Destination: destination.PhoneString(), //ID: - ReportID: omitnull.FromPtr(report_id), - Source: omit.From(enums.CommsTextjobsourceRmo), - Type: omit.From(type_), - }).One(ctx, txn) + ReportID: report_id, + Source: modelcomms.Textjobsource_Rmo, + Type: type_, + }) if err != nil { return nil, fmt.Errorf("Failed to add delayed text job: %w", err) } @@ -81,12 +74,12 @@ func sendTextBegin(ctx context.Context, txn bob.Executor, user_id *int32, report } return &job.ID, nil } -func sendTextCommandResponse(ctx context.Context, txn bob.Executor, dst types.E164, content string) error { - _, err := sendTextDirect(ctx, txn, enums.CommsTextoriginCommandResponse, dst.PhoneString(), content, false, false) +func sendTextCommandResponse(ctx context.Context, txn db.Ex, dst types.E164, content string) error { + _, err := sendTextDirect(ctx, txn, modelcomms.Textorigin_CommandResponse, dst.PhoneString(), content, false, false) return err } -func sendTextComplete(ctx context.Context, job *models.CommsTextJob) error { - txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) +func sendTextComplete(ctx context.Context, job modelcomms.TextJob) error { + txn, err := db.BeginTxn(ctx) if err != nil { return fmt.Errorf("begin tx: %w", err) } @@ -95,12 +88,12 @@ func sendTextComplete(ctx context.Context, job *models.CommsTextJob) error { if err != nil { return fmt.Errorf("parse phone: %w", err) } - var origin enums.CommsTextorigin + var origin modelcomms.Textorigin switch job.Type { - case enums.CommsTextjobtypeReportConfirmation: - origin = enums.CommsTextoriginWebsiteAction - case enums.CommsTextjobtypeReportMessage: - origin = enums.CommsTextoriginDistrict + case modelcomms.Textjobtype_ReportConfirmation: + origin = modelcomms.Textorigin_WebsiteAction + case modelcomms.Textjobtype_ReportMessage: + origin = modelcomms.Textorigin_District default: return fmt.Errorf("incomplete switch: %s", string(job.Type)) } @@ -128,37 +121,35 @@ func sendTextComplete(ctx context.Context, job *models.CommsTextJob) error { if err != nil { return fmt.Errorf("send text direct: %w", err) } - err = job.Update(ctx, txn, &models.CommsTextJobSetter{ - Completed: omitnull.From(time.Now()), - }) + err = querycomms.TextJobComplete(ctx, txn, int64(job.ID)) if err != nil { return fmt.Errorf("update job: %w", err) } - if job.ReportID.IsValue() { - creator_id := job.CreatorID.MustGet() - report_id := job.ReportID.MustGet() + if job.ReportID != nil { + creator_id := *job.CreatorID + report_id := *job.ReportID log.Debug().Int32("creator", creator_id).Int32("report_id", report_id).Msg("Creating report entries for text message") - _, err := models.ReportTexts.Insert(&models.ReportTextSetter{ - CreatorID: omit.From(creator_id), - ReportID: omit.From(report_id), - TextLogID: omit.From(text_log.ID), - }).One(ctx, txn) + querypublic.ReportTextInsert(ctx, txn, modelpublic.ReportText{ + CreatorID: creator_id, + ReportID: report_id, + TextLogID: text_log.ID, + }) if err != nil { return fmt.Errorf("insert report_text: %w", err) } - _, err = models.PublicreportReportLogs.Insert(&models.PublicreportReportLogSetter{ - Created: omit.From(time.Now()), - EmailLogID: omitnull.FromPtr[int32](nil), + _, err = querypublicreport.ReportLogInsert(ctx, txn, modelpublicreport.ReportLog{ + Created: time.Now(), + EmailLogID: nil, // ID - ReportID: omit.From(report_id), - TextLogID: omitnull.From(text_log.ID), - Type: omit.From(enums.PublicreportReportlogtypeMessageText), - UserID: omitnull.From(creator_id), - }).One(ctx, txn) + ReportID: report_id, + TextLogID: &text_log.ID, + Type: modelpublicreport.Reportlogtype_MessageText, + UserID: &creator_id, + }) if err != nil { return fmt.Errorf("insert report log: %w", err) } - report, err := models.FindPublicreportReport(ctx, txn, report_id) + report, err := querypublicreport.ReportFromID(ctx, txn, int64(report_id)) if err != nil { return fmt.Errorf("find public report: %w", err) } @@ -174,33 +165,28 @@ func sendTextComplete(ctx context.Context, job *models.CommsTextJob) error { // Send a text message and save the appropriate database records. // Send immediately using the current goroutine -func sendTextDirect(ctx context.Context, txn bob.Executor, origin enums.CommsTextorigin, destination, content string, is_visible_to_llm, is_welcome bool) (*models.CommsTextLog, error) { - text_log, err := models.CommsTextLogs.Insert(&models.CommsTextLogSetter{ - //ID: - Content: omit.From(content), - Created: omit.From(time.Now()), - Destination: omit.From(destination), - IsVisibleToLLM: omit.From(is_visible_to_llm), - IsWelcome: omit.From(is_welcome), - Origin: omit.From(origin), - Source: omit.From(config.PhoneNumberReportStr), - TwilioSid: omitnull.FromPtr[string](nil), - TwilioStatus: omit.From(""), - }).One(ctx, txn) +func sendTextDirect(ctx context.Context, txn db.Ex, origin modelcomms.Textorigin, destination, content string, is_visible_to_llm, is_welcome bool) (modelcomms.TextLog, error) { + text_log, err := querycomms.TextLogInsert(ctx, txn, modelcomms.TextLog{ + Content: content, + Created: time.Now(), + Destination: destination, + IsVisibleToLlm: is_visible_to_llm, + IsWelcome: is_welcome, + Origin: origin, + Source: config.PhoneNumberReportStr, + TwilioSid: nil, + TwilioStatus: "", + }) if err != nil { - return nil, fmt.Errorf("insert text log: %w", err) + return modelcomms.TextLog{}, fmt.Errorf("insert text log: %w", err) } pid, err := text.SendText(ctx, config.VoipMSNumber, destination, content) if err != nil { - return nil, fmt.Errorf("send text: %w", err) + return modelcomms.TextLog{}, fmt.Errorf("send text: %w", err) } - err = text_log.Update(ctx, txn, &models.CommsTextLogSetter{ - TwilioSid: omitnull.From(pid), - TwilioStatus: omit.From("created"), - }) + err = querycomms.TextLogUpdate(ctx, txn, int64(text_log.ID), pid, "created") if err != nil { - return nil, fmt.Errorf("update %w", err) + return modelcomms.TextLog{}, fmt.Errorf("update %w", err) } - return text_log, nil } diff --git a/platform/text/text.go b/platform/text/text.go index 1031600f..f800003c 100644 --- a/platform/text/text.go +++ b/platform/text/text.go @@ -9,8 +9,12 @@ import ( "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/lint" + modelcomms "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/comms/model" + modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" "github.com/Gleipnir-Technology/nidus-sync/db/models" + querycomms "github.com/Gleipnir-Technology/nidus-sync/db/query/comms" + querypublicreport "github.com/Gleipnir-Technology/nidus-sync/db/query/publicreport" + "github.com/Gleipnir-Technology/nidus-sync/lint" "github.com/Gleipnir-Technology/nidus-sync/platform/background" "github.com/Gleipnir-Technology/nidus-sync/platform/event" "github.com/Gleipnir-Technology/nidus-sync/platform/types" @@ -68,12 +72,12 @@ func HandleTextMessage(ctx context.Context, source string, destination string, c } func respondText(ctx context.Context, log_id int32) error { - txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) + txn, err := db.BeginTxn(ctx) if err != nil { return fmt.Errorf("begin tx: %w", err) } defer lint.LogOnErrRollback(txn.Rollback, ctx, "rollback") - l, err := models.FindCommsTextLog(ctx, txn, log_id) + l, err := querycomms.TextLogFromID(ctx, txn, int64(log_id)) if err != nil { return fmt.Errorf("find comms: %w", err) } @@ -82,19 +86,19 @@ func respondText(ctx context.Context, log_id int32) error { return fmt.Errorf("parse source: %w", err) } - status, err := phoneStatus(ctx, *src) + contact_phone, err := querycomms.ContactPhoneFromE164(ctx, txn, src.PhoneString()) if err != nil { - return fmt.Errorf("Failed to get phone status") + return fmt.Errorf("Failed to get contact phone") } body_l := strings.TrimSpace(strings.ToLower(l.Content)) // If the user isn't confirmed for sending regular texts ensure they get a reprompt - if status == enums.CommsPhonestatustypeUnconfirmed { + if contact_phone.ConfirmedMessageID == nil { switch body_l { case "yes": - err = setPhoneStatus(ctx, txn, *src, enums.CommsPhonestatustypeOkToSend) + err = querycomms.ContactPhoneUpdateConfirmedMessageID(ctx, txn, src.PhoneString(), &l.ID) if err != nil { - return fmt.Errorf("set phone status: %w", err) + return fmt.Errorf("set phone confirmed message ID: %w", err) } content := "Thanks, we've confirmed your phone number. You can text STOP at any time if you change your mind" err = sendTextCommandResponse(ctx, txn, *src, content) @@ -118,14 +122,15 @@ func respondText(ctx context.Context, log_id int32) error { } switch body_l { case "stop": + err = querycomms.ContactPhoneUpdateStopMessageID(ctx, txn, src.PhoneString(), &l.ID) + if err != nil { + return fmt.Errorf("set phone stop message ID: %w", err) + } content := "You have successfully been unsubscribed. You will not receive any more messages from this number. Reply START to resubscribe." err = sendTextCommandResponse(ctx, txn, *src, content) if err != nil { log.Error().Err(err).Msg("Failed to send unsubscribe acknowledgement.") } - lint.LogOnErrCtx(func(ctx context.Context) error { - return setPhoneStatus(ctx, txn, *src, enums.CommsPhonestatustypeStopped) - }, ctx, "set phone status") return nil case "reset conversation": err = handleResetConversation(ctx, txn, *src) @@ -140,20 +145,23 @@ func respondText(ctx context.Context, log_id int32) error { return nil } // If we've got an open public report from this phone number then we'll let the district respond - reports, err := reportsForTextRecipient(ctx, txn, *src) + // Get the list of reports that are still open for a particular text message recipient + // 'still open' is not well-defined throughout the system, but for now we'll go with + // 'not reviewed in any way'. + reports, err := querypublicreport.ReportsFromReporterPhone(ctx, txn, src.PhoneString()) if err != nil { return fmt.Errorf("has open report: %w", err) } for _, report := range reports { - _, err = models.PublicreportReportLogs.Insert(&models.PublicreportReportLogSetter{ - Created: omit.From(time.Now()), - EmailLogID: omitnull.FromPtr[int32](nil), + _, err = querypublicreport.ReportLogInsert(ctx, txn, modelpublicreport.ReportLog{ + Created: time.Now(), + EmailLogID: nil, // ID - ReportID: omit.From(report.ID), - TextLogID: omitnull.From(log_id), - Type: omit.From(enums.PublicreportReportlogtypeMessageText), - UserID: omitnull.FromPtr[int32](nil), - }).One(ctx, txn) + ReportID: report.ID, + TextLogID: &log_id, + Type: modelpublicreport.Reportlogtype_MessageText, + UserID: nil, + }) if err != nil { return fmt.Errorf("insert report log: %w", err) } @@ -164,7 +172,8 @@ func respondText(ctx context.Context, log_id int32) error { return nil } // Otherwise let the LLM handle the response - return respondTextLLM(ctx, *src) + //return respondTextLLM(ctx, *src) + return nil } func respondTextLLM(ctx context.Context, src types.E164) error { @@ -177,12 +186,12 @@ func respondTextLLM(ctx context.Context, src types.E164) error { if err != nil { return fmt.Errorf("Failed to generate next message: %w", err) } - txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil) + txn, err := db.BeginTxn(ctx) if err != nil { return fmt.Errorf("start txn: %w", err) } defer lint.LogOnErrRollback(txn.Rollback, ctx, "rollback") - _, err = sendTextDirect(ctx, txn, enums.CommsTextoriginLLM, src.PhoneString(), next_message.Content, true, false) + _, err = sendTextDirect(ctx, txn, modelcomms.Textorigin_Llm, src.PhoneString(), next_message.Content, true, false) if err != nil { return fmt.Errorf("Failed to send response text: %w", err) } @@ -201,22 +210,28 @@ func ParsePhoneNumber(input string) (*types.E164, error) { } func StoreSources() error { + var err error ctx := context.TODO() + txn := db.PGInstance.PGXPool + // Magical id 1 is set in migration 00151 + contact, err := querycomms.ContactFromID(ctx, txn, 1) + if err != nil { + return fmt.Errorf("contact from ID 1: %w", err) + } for _, n := range []string{config.PhoneNumberReportStr, config.PhoneNumberSupportStr, config.VoipMSNumber} { - var err error // Deal with Voip.ms not expecting API calls with the prefixed +1 if !strings.HasPrefix(n, "+1") { dest, err := ParsePhoneNumber("+1" + n) if err != nil { return fmt.Errorf("Failed to parse +1'%s' as phone number: %w", n, err) } - err = EnsureInDB(ctx, db.PGInstance.BobDB, *dest) + err = EnsureInDB(ctx, txn, contact, *dest) } else { dest, err := ParsePhoneNumber(n) if err != nil { return fmt.Errorf("Failed to parse '%s' as phone number: %w", n, err) } - err = EnsureInDB(ctx, db.PGInstance.BobDB, *dest) + err = EnsureInDB(ctx, txn, contact, *dest) } if err != nil { return fmt.Errorf("Failed to add number '%s' to DB: %w", n, err) diff --git a/platform/types/contact.go b/platform/types/contact.go index c7b07bef..be045d82 100644 --- a/platform/types/contact.go +++ b/platform/types/contact.go @@ -10,7 +10,7 @@ type Contact struct { Email *string `db:"email" json:"email"` HasEmail bool `json:"has_email"` HasPhone bool `json:"has_phone"` - Name *string `db:"name" json:"name"` + Name string `db:"name" json:"name"` Phone *string `db:"phone" json:"phone"` } diff --git a/platform/types/site.go b/platform/types/site.go index 8ec819ce..ae66811c 100644 --- a/platform/types/site.go +++ b/platform/types/site.go @@ -40,7 +40,7 @@ func SiteFromModel(s *models.Site) Site { Notes: s.Notes, OrganizationID: s.OrganizationID, Owner: Contact{ - Name: &s.OwnerName, + Name: s.OwnerName, Phone: &owner_phone, }, ResidentOwned: resident_owned, diff --git a/resource/communication.go b/resource/communication.go index f47a8f70..58b6edb1 100644 --- a/resource/communication.go +++ b/resource/communication.go @@ -11,7 +11,7 @@ import ( modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model" nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/platform" - //"github.com/rs/zerolog/log" + //"github.com/rs/zerolog/log":q ) type communicationR struct { diff --git a/resource/publicreport_compliance.go b/resource/publicreport_compliance.go index 2a9b9fd8..3d8960ea 100644 --- a/resource/publicreport_compliance.go +++ b/resource/publicreport_compliance.go @@ -78,12 +78,8 @@ func (res *complianceR) Create(ctx context.Context, r *http.Request, n publicRep MapZoom: float32(0.0), //OrganizationID: , //PublicID: - ReporterEmail: "", - ReporterName: "", - ReporterPhone: "", - ReporterPhoneCanSms: true, - ReportType: modelpublicreport.Reporttype_Compliance, - Status: modelpublicreport.Reportstatustype_Reported, + ReportType: modelpublicreport.Reporttype_Compliance, + Status: modelpublicreport.Reportstatustype_Reported, } setter_compliance := modelpublicreport.Compliance{ AccessInstructions: "", @@ -166,28 +162,10 @@ func (res *complianceR) Update(ctx context.Context, r *http.Request, prf publicR report_updater.Set(tablepublicreport.Report.LatlngAccuracyValue) } } + var reporter *types.Contact if prf.Reporter.IsValue() { - reporter := prf.Reporter.MustGet() - if reporter.Email != nil { - //report_setter.ReporterEmail = omit.From(*reporter.Email) - report_updater.Model.ReporterEmail = *reporter.Email - report_updater.Set(tablepublicreport.Report.ReporterEmail) - } - if reporter.Name != nil { - //report_setter.ReporterName = omit.From(*reporter.Name) - report_updater.Model.ReporterName = *reporter.Name - report_updater.Set(tablepublicreport.Report.ReporterName) - } - if reporter.Phone != nil { - //report_setter.ReporterPhone = omit.From(*reporter.Phone) - report_updater.Model.ReporterPhone = *reporter.Phone - report_updater.Set(tablepublicreport.Report.ReporterPhone) - } - if reporter.CanSMS != nil { - //report_setter.ReporterPhoneCanSMS = omit.FromPtr(reporter.CanSMS) - report_updater.Model.ReporterPhoneCanSms = *reporter.CanSMS - report_updater.Set(tablepublicreport.Report.ReporterPhoneCanSms) - } + l := prf.Reporter.MustGet() + reporter = &l } var address *types.Address if prf.Address.IsValue() { @@ -244,7 +222,7 @@ func (res *complianceR) Update(ctx context.Context, r *http.Request, prf publicR compliance_updater.Model.Submitted = &now compliance_updater.Set(tablepublicreport.Compliance.Submitted) } - err = platform.PublicReportUpdateCompliance(ctx, public_id, report_updater, compliance_updater, address, location) + err = platform.PublicReportUpdateCompliance(ctx, public_id, report_updater, compliance_updater, address, location, reporter) if err != nil { return nil, nhttp.NewError("platform update report compliance: %w", err) }