Move review actions into the platform, emit events on change

Still not seeing updates in the sidebar, however.
This commit is contained in:
Eli Ribble 2026-03-19 16:55:49 +00:00
parent 954a4330ee
commit ee61b6d24b
No known key found for this signature in database
6 changed files with 181 additions and 133 deletions

14
platform/error.go Normal file
View file

@ -0,0 +1,14 @@
package platform
import (
"fmt"
)
type ErrorNotFound struct {
message string
}
func (e ErrorNotFound) Error() string { return fmt.Sprintf("not found: %s", e.message) }
func newNotFound(format string, m ...any) error {
return &ErrorNotFound{message: fmt.Sprintf(format, m...)}
}

View file

@ -1,6 +1,7 @@
package event
import (
"encoding/json"
"time"
"github.com/Gleipnir-Technology/nidus-sync/config"
@ -14,6 +15,16 @@ type Event struct {
Type EventType `json:"type"`
URI string `json:"uri"`
}
func (e Event) MarshalJSON() ([]byte, error) {
to_marshal := make(map[string]any, 0)
to_marshal["resource"] = e.Resource
to_marshal["time"] = e.Time
to_marshal["type"] = e.Type.String()
to_marshal["uri"] = e.URI
return json.Marshal(to_marshal)
}
type Envelope struct {
OrganizationID int32
Event Event
@ -73,6 +84,7 @@ type ResourceType int
const (
TypeUnknown = iota
TypeFileCSV
TypeReviewTask
TypeRMONuisance
TypeRMOReport
TypeRMOWater
@ -107,6 +119,8 @@ func resourceString(t ResourceType) string {
switch t {
case TypeFileCSV:
return "sync:filecsv"
case TypeReviewTask:
return "sync:review_task"
case TypeRMONuisance:
return "rmo:nuisance"
case TypeRMOReport:
@ -121,6 +135,8 @@ func makeURI(t ResourceType, id string) string {
switch t {
case TypeFileCSV:
return config.MakeURLNidus("/upload/%s", id)
case TypeReviewTask:
return config.MakeURLNidus("/review/%s", id)
case TypeRMONuisance:
return config.MakeURLReport("/report/%s", id)
case TypeRMOWater:

137
platform/review.go Normal file
View file

@ -0,0 +1,137 @@
package platform
import (
"context"
"fmt"
"net/http"
"strconv"
"time"
"github.com/Gleipnir-Technology/bob"
"github.com/Gleipnir-Technology/bob/dialect/psql"
"github.com/Gleipnir-Technology/bob/dialect/psql/um"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
"github.com/Gleipnir-Technology/nidus-sync/platform/event"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/rs/zerolog/log"
)
type PoolUpdate struct {
Condition *string `json:"condition"`
Latitude *float32 `json:"latitude"`
Longitude *float32 `json:"longitude"`
}
func ReviewPoolCreate(ctx context.Context, user User, task_id int32, status string, update *PoolUpdate) (int32, error) {
txn, err := db.PGInstance.BobDB.BeginTx(ctx, nil)
if err != nil {
return 0, nhttp.NewError("start txn: %w", err)
}
defer txn.Rollback(ctx)
review_task, err := models.ReviewTasks.Query(
models.SelectWhere.ReviewTasks.ID.EQ(task_id),
models.SelectWhere.ReviewTasks.OrganizationID.EQ(user.Organization.ID()),
).One(ctx, txn)
if err != nil {
if err.Error() == "sql: no rows in result set" {
return 0, newNotFound("review task %d", task_id)
}
return 0, fmt.Errorf("find review task: %d", task_id)
}
var resolution enums.Reviewtaskresolutiontype
err = resolution.Scan(status)
if err != nil {
return 0, fmt.Errorf("status '%s' is not recognized: %w", status, err)
}
review_task.Update(ctx, txn, &models.ReviewTaskSetter{
Resolution: omitnull.From(resolution),
Reviewed: omitnull.From(time.Now()),
ReviewerID: omitnull.From(int32(user.ID)),
})
review_task_pool, err := models.ReviewTaskPools.Query(
models.SelectWhere.ReviewTaskPools.ReviewTaskID.EQ(review_task.ID),
).One(ctx, txn)
switch status {
case "discarded":
// Nothing to do, we already discarded it above
case "committed":
err = commitReviewPool(ctx, txn, user, review_task_pool, update)
default:
return 0, nhttp.NewErrorStatus(http.StatusBadRequest, "unrecognized status %s", status)
}
if err != nil {
return 0, err
}
event.Updated(event.TypeReviewTask, user.Organization.ID(), strconv.Itoa(int(review_task.ID)))
txn.Commit(ctx)
log.Info().Int32("id", review_task.ID).Str("status", status).Msg("review completed")
return review_task.ID, err
}
func commitReviewPool(ctx context.Context, txn bob.Tx, user User, review_task_pool *models.ReviewTaskPool, update *PoolUpdate) error {
if update == nil {
return nil
}
feature_pool, err := models.FindFeaturePool(ctx, txn, review_task_pool.FeaturePoolID)
if err != nil {
return nhttp.NewError("find feature pool: %w", err)
}
if update.Condition != nil {
var condition enums.Poolconditiontype
err := condition.Scan(*update.Condition)
if err != nil {
return nhttp.NewErrorStatus(http.StatusBadRequest, "unrecognized condition %s", update.Condition)
}
err = review_task_pool.Update(ctx, txn, &models.ReviewTaskPoolSetter{
Condition: omitnull.From(condition),
})
if err != nil {
return nhttp.NewError("update rewiew task: %w", err)
}
err = feature_pool.Update(ctx, txn, &models.FeaturePoolSetter{
Condition: omit.From(condition),
})
if err != nil {
return nhttp.NewError("update feature_pool: %w", err)
}
}
if update.Latitude != nil || update.Longitude != nil {
if update.Latitude == nil || update.Longitude == nil {
return nhttp.NewErrorStatus(http.StatusBadRequest, "you have to specify lat and lng together")
}
_, err = psql.Update(
um.Table("review_task_pool"),
um.SetCol("location").To(
psql.F("ST_SetSRID",
psql.F("ST_MakePoint",
psql.Arg(update.Longitude),
psql.Arg(update.Latitude),
), psql.Arg(4326),
),
),
um.Where(psql.Quote("review_task_pool", "review_task_id").EQ(psql.Arg(review_task_pool.ReviewTaskID))),
).Exec(ctx, txn)
if err != nil {
return nhttp.NewError("save task: %w", err)
}
_, err = psql.Update(
um.Table("feature"),
um.SetCol("location").To(
psql.F("ST_SetSRID",
psql.F("ST_MakePoint",
psql.Arg(*update.Longitude),
psql.Arg(*update.Latitude),
), psql.Arg(4326),
),
),
um.Where(psql.Quote("feature", "id").EQ(psql.Arg(review_task_pool.FeaturePoolID))),
).Exec(ctx, txn)
if err != nil {
return nhttp.NewError("save feature: %w", err)
}
}
return nil
}