deadline of features

* features can have a deadline
* features with non-null deadline < now are deleted upon request for data
This commit is contained in:
zegkljan 2022-02-13 02:40:28 +01:00
parent 3615deb985
commit 7b4eb8d5d2
3 changed files with 54 additions and 13 deletions

View File

@ -2,6 +2,7 @@ package models
import (
"io"
"time"
geojson "github.com/paulmach/go.geojson"
)
@ -23,6 +24,7 @@ type Feature struct {
ID FeatureID `json:"id"`
OwnerID UserID `json:"owner_id"`
Name string `json:"name"`
Deadline *time.Time `json:"deadline,omitempty"`
Properties map[string]interface{} `json:"properties"`
Geometry geojson.Geometry `json:"geometry"`
// PhotoIDs contains a list IDs of photos associated with the feature.

View File

@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS features (
id integer PRIMARY KEY AUTOINCREMENT,
owner_id integer NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name text NOT NULL,
deadline text,
properties text NOT NULL,
geom text NOT NULL
);

View File

@ -194,6 +194,10 @@ func (s *Server) getData() (models.Data, error) {
return func() (data models.Data, err error) {
defer sqlitex.Save(conn)(&err)
err = s.deleteExpiredFeatures(conn)
if err != nil {
return models.Data{}, fmt.Errorf("failed to delete expired featured: %w", err)
}
people, err := s.getPeople(conn)
if err != nil {
return models.Data{}, fmt.Errorf("failed to retreive people: %w", err)
@ -263,6 +267,21 @@ func (s *Server) update(data models.Update, photos map[string]models.Photo) erro
}()
}
func (s *Server) deleteExpiredFeatures(conn *sqlite.Conn) error {
stmt, err := conn.Prepare("delete from features where deadline < ?")
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Finalize()
now := time.Now().Unix()
err = sqlitex.Exec(conn, "delete from features where deadline < ?", func(stmt *sqlite.Stmt) error { return nil }, now)
if err != nil {
return fmt.Errorf("failed to delete expired features: %w", err)
}
return nil
}
func (s *Server) getPeople(conn *sqlite.Conn) ([]models.User, error) {
if conn == nil {
conn = s.getDbConn()
@ -290,7 +309,7 @@ func (s *Server) getFeatures(conn *sqlite.Conn) ([]models.Feature, error) {
}
features := make([]models.Feature, 0, 100)
err := sqlitex.Exec(conn, `select f.id, f.owner_id, f.name, f.properties, f.geom, '[' || coalesce(group_concat(p.id, ', '), '') || ']'
err := sqlitex.Exec(conn, `select f.id, f.owner_id, f.name, f.deadline, f.properties, f.geom, '[' || coalesce(group_concat(p.id, ', '), '') || ']'
from features f
left join feature_photos p on f.id = p.feature_id
group by f.id, f.owner_id, f.name, f.properties, f.geom`, func(stmt *sqlite.Stmt) error {
@ -301,21 +320,27 @@ func (s *Server) getFeatures(conn *sqlite.Conn) ([]models.Feature, error) {
name := stmt.ColumnText(2)
propertiesRaw := stmt.ColumnText(3)
var deadline *time.Time
if stmt.ColumnType(3) != sqlite.SQLITE_NULL {
dl := time.Unix(stmt.ColumnInt64(3), 0)
deadline = &dl
}
propertiesRaw := stmt.ColumnText(4)
var properties map[string]interface{}
err := json.Unmarshal([]byte(propertiesRaw), &properties)
if err != nil {
return fmt.Errorf("failed to parse properties for feature id=%d: %w", id, err)
}
geomRaw := stmt.ColumnText(4)
geomRaw := stmt.ColumnText(5)
var geom geojson.Geometry
err = json.Unmarshal([]byte(geomRaw), &geom)
if err != nil {
return fmt.Errorf("failed to parse geometry for feature id=%d: %w", id, err)
}
photosRaw := stmt.ColumnText(5)
photosRaw := stmt.ColumnText(6)
var photos []models.FeaturePhotoID
err = json.Unmarshal([]byte(photosRaw), &photos)
if err != nil {
@ -326,6 +351,7 @@ func (s *Server) getFeatures(conn *sqlite.Conn) ([]models.Feature, error) {
ID: models.FeatureID(id),
OwnerID: models.UserID(ownerID),
Name: name,
Deadline: deadline,
Properties: properties,
Geometry: geom,
PhotoIDs: photos,
@ -341,7 +367,7 @@ func (s *Server) getFeatures(conn *sqlite.Conn) ([]models.Feature, error) {
}
func (s *Server) addFeatures(conn *sqlite.Conn, features []models.Feature) (map[models.FeatureID]models.FeatureID, error) {
stmt, err := conn.Prepare("insert into features(owner_id, name, properties, geom) values(?, ?, ?, ?)")
stmt, err := conn.Prepare("insert into features(owner_id, name, deadline, properties, geom) values(?, ?, ?, ?, ?)")
if err != nil {
return nil, fmt.Errorf("failed to prepare statement: %w", err)
}
@ -365,21 +391,27 @@ func (s *Server) addFeatures(conn *sqlite.Conn, features []models.Feature) (map[
stmt.BindText(2, feature.Name)
if feature.Deadline == nil {
stmt.BindNull(3)
} else {
stmt.BindInt64(3, feature.Deadline.Unix())
}
if feature.Properties == nil {
stmt.BindText(3, "{}")
stmt.BindText(4, "{}")
} else {
propertiesBytes, err := json.Marshal(feature.Properties)
if err != nil {
return nil, fmt.Errorf("failed to marshal properties of feature id=%d: %w", feature.ID, err)
}
stmt.BindText(3, string(propertiesBytes))
stmt.BindText(4, string(propertiesBytes))
}
geomBytes, err := json.Marshal(feature.Geometry)
if err != nil {
return nil, fmt.Errorf("failed to marshal geometry of feature id=%d: %w", feature.ID, err)
}
stmt.BindText(4, string(geomBytes))
stmt.BindText(5, string(geomBytes))
_, err = stmt.Step()
if err != nil {
@ -464,7 +496,7 @@ func (s *Server) addPhotos(conn *sqlite.Conn, createdFeatureMapping, addedFeatur
}
func (s *Server) updateFeatures(conn *sqlite.Conn, features []models.Feature) error {
stmt, err := conn.Prepare("update features set owner_id = ?, name = ?, properties = ?, geom = ? where id = ?")
stmt, err := conn.Prepare("update features set owner_id = ?, name = ?, deadline = ?, properties = ?, geom = ? where id = ?")
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
@ -484,23 +516,29 @@ func (s *Server) updateFeatures(conn *sqlite.Conn, features []models.Feature) er
stmt.BindText(2, feature.Name)
if feature.Deadline == nil {
stmt.BindNull(3)
} else {
stmt.BindInt64(3, feature.Deadline.Unix())
}
if feature.Properties == nil {
stmt.BindText(3, "{}")
stmt.BindText(4, "{}")
} else {
propertiesBytes, err := json.Marshal(feature.Properties)
if err != nil {
return fmt.Errorf("failed to marshal properties of feature id=%d: %w", feature.ID, err)
}
stmt.BindText(3, string(propertiesBytes))
stmt.BindText(4, string(propertiesBytes))
}
geomBytes, err := json.Marshal(feature.Geometry)
if err != nil {
return fmt.Errorf("failed to marshal geometry of feature ID %d: %w", feature.ID, err)
}
stmt.BindText(4, string(geomBytes))
stmt.BindText(5, string(geomBytes))
stmt.BindInt64(5, int64(feature.ID))
stmt.BindInt64(6, int64(feature.ID))
_, err = stmt.Step()
if err != nil {