// Package `tracking` defines a generic track (sort of record) type // and a container type that allows storing of tracks in a SQL database. package tracking import ( sqllib "database/sql" "encoding/json" "time" lib "git.sr.ht/~cco/go-scopes" "git.sr.ht/~cco/go-scopes/logging/log" "git.sr.ht/~cco/go-scopes/storage" sql "git.sr.ht/~cco/go-scopes/storage" ) type ItemFactory func(*Container, ...string) *Track // basic track implementation type Track struct { TrackId lib.Ident Head lib.StrMap TimeStamp *time.Time Data lib.Map Container *Container } func MakeTrack(cont *Container, h ...string) *Track { tr := Track{ Head: lib.StrMap{}, Data: lib.Map{}, Container: cont, } tr.SetHead(h...) return &tr } func (tr *Track) SetHead(h ...string) { for i, k := range tr.Container.HeadFields { if i >= len(h) { break } if h[i] != "" { tr.Head[k] = h[i] } } } // basic container implementation type ContDef struct { ItemFactory ItemFactory TableName string HeadFields []string Indexes [][]string } type Container struct { *ContDef Storage *sql.Storage } func Tracks(db *sql.Storage) *Container { return &Container{container_definition, db} } func (cont *Container) Get(id lib.Ident) *Track { db := cont.Storage qu := lib.Map{ "schema": db.Schema, "tablename": cont.TableName, "scols": append(cont.HeadFields, "timestamp", "data"), "qucols": lib.StrSlice{"taskid"}, } sql := storage.BuildSql(SqlSelect, qu) print(sql) var h []string tr := cont.ItemFactory(cont, h...) return tr } func (cont *Container) Query(headValues lib.StrSlice) *Track { db := cont.Storage data := lib.Map{ "schema": db.Schema, "tablename": cont.TableName, "scols": lib.StrSlice{"taskid", "data"}, "qucols": lib.StrSlice{"taskid", "username"}, "ordcols": lib.StrSlice{"timestamp"}, } sql := storage.BuildSql(SqlSelect, data) //print(sql) _ = sql tr := cont.ItemFactory(cont) return tr } func (cont *Container) NewTrack(h []string, data lib.Map) *Track { tr := cont.ItemFactory(cont, h...) //tr.SetData(data) cont.Insert(tr) return tr } func (cont *Container) Save(t *Track) lib.Ident { return 0 } func (cont *Container) Insert(tr *Track) lib.Ident { var columns []string var values []any for _, k := range cont.HeadFields { columns = append(columns, k) values = append(values, tr.Head[k]) } columns = append(columns, "Data") b, _ := json.Marshal(tr.Data) values = append(values, b) db := cont.Storage data := lib.Map{ "schema": db.Schema, "tablename": cont.TableName, "columns": columns, } sql := storage.BuildSql(SqlInsert, data) var trid lib.Ident var ts *time.Time var tsstr string proc := func(r *sqllib.Rows) error { err := r.Scan(&trid, &ts) if err != nil { err = r.Scan(&trid, &tsstr) ts = ParseDateTime(tsstr) } return err } if err := db.Query(proc, sql, values...); err == nil { //tr.Update(&TrackTemplate{TrackId: trid, TimeStamp: ts}) tr.TrackId = trid tr.TimeStamp = ts return trid } return 0 } func ParseDateTime(inp string) *time.Time { ts, err := time.Parse("2006-01-02 15:04:05", inp) if err == nil { //fmt.Printf("%+v\n", ts) return &ts } log.Error(err).Msg("storage.tracking.ParseDateTime") return nil } func (cont *Container) CreateTable() { db := cont.Storage data := lib.Map{ "schema": db.Schema, "tablename": cont.TableName, "headFields": cont.HeadFields, "indexes": cont.Indexes, "params": db.Params, } sql := storage.BuildSql(SqlCreate, data) if _, err := db.Exec(sql); err != nil { panic(err) } } // container definition var container_definition *ContDef func init() { container_definition = &ContDef{ ItemFactory: MakeTrack, TableName: "tracks", HeadFields: []string{"taskId", "userName"}, Indexes: [][]string{ []string{"taskId", "userName"}, []string{"userName"}, }, } }