// 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" "fmt" "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() Track type Track interface { TrackId() lib.Ident } type Container interface { New(lib.StrSlice, lib.Map) CreateTable() } type BaseTrack = track type BaseContainer = container // basic track implementation type track struct { trackId lib.Ident head lib.StrMap timeStamp *time.Time data lib.Map container *container } func (tr *track) TrackId() lib.Ident { return tr.trackId } // basic container implementation type container struct { tableName string headFields []string indexes [][]string storage *sql.Storage } func Tracks(db *sql.Storage) *container { return &container{ tableName: "tracks", headFields: []string{"taskId", "userName"}, indexes: [][]string{[]string{"taskId", "userName"}, []string{"userName"}}, storage: db, } } func (cont *container) New(headValues lib.StrSlice, data lib.Map) Track { head := lib.StrMap{} for i, k := range cont.headFields { head[k] = headValues[i] } tr := &track{head: head, data: data} cont.insert(tr) return tr } 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) type rt struct { trid lib.Ident ts string //ts *time.Time } var res []rt proc := func(r *sqllib.Rows) error { var rec rt err := r.Scan(&rec.trid, &rec.ts) res = append(res, rec) return err } if err := db.Query(proc, sql, values...); err == nil { row := res[0] trid := row.trid tr.trackId = trid if ts := ParseDateTime(row.ts); ts != nil { fmt.Printf("%+v\n", ts) tr.timeStamp = ts } tr.container = cont return trid } return 0 } func ParseDateTime(inp string) *time.Time { if ts, err := time.Parse(time.RFC3339, inp); err == nil { return &ts } ts, err := time.Parse("2006-01-02 15:04:05", inp) if err == nil { 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) } }