package main import ( "os" "sort" "time" "github.com/google/uuid" "gopkg.in/yaml.v2" ) type ( yamlDB struct { addr string Knowledge knowledge Users map[IDU]user } knowledge struct { Questions map[IDQ]Question Answers map[IDA]Answer } user struct { Tags tags Cadence duration Resolution int } tags struct { Assignments []IDT } ) func newYamlDB(p string) (yamlDB, error) { db := yamlDB{ addr: p, Knowledge: knowledge{ Questions: map[IDQ]Question{}, Answers: map[IDA]Answer{}, }, Users: map[IDU]user{}, } if b, err := os.ReadFile(p); err != nil { return yamlDB{}, err } else if err := yaml.Unmarshal(b, &db); err != nil { return yamlDB{}, err } return db, nil } func (db yamlDB) Close() { if os.Getenv("RO") != "" { return } b, err := yaml.Marshal(db) if err != nil { panic(err) } if err := os.Rename(db.addr, db.addr+".bak"); err != nil { panic(err) } if err := os.WriteFile(db.addr, b, os.ModePerm); err != nil { panic(err) } } func (db yamlDB) HistoryOf(user IDU) map[IDQ][]Answer { result := map[IDQ][]Answer{} for _, v := range db.Knowledge.Answers { if v.Author == user { result[v.Q] = append(result[v.Q], v) } } for _, tag := range db.Users[user].Tags.Assignments { for qid, q := range db.Knowledge.Questions { if _, ok := result[qid]; !ok && q.Tagged(tag) { result[qid] = []Answer{} } } } for _, v := range result { sort.Slice(v, func(i, j int) bool { return v[i].TS < v[j].TS }) } return result } func (db yamlDB) Next(user IDU, q IDQ) time.Time { allHistory := db.HistoryOf(user) history := allHistory[q] last := db.lastTS(user, q) log := make([]int, 0, len(history)) for i := range history { v := 0 if history[i].Pass { v = 5 } log = append(log, v) } return SM2Next(last, log, db.cadence(user)) } func (db yamlDB) UserResolution(user IDU) int { u := db.Users[user] if u.Resolution == 0 { return 40 } return u.Resolution } func (db yamlDB) cadence(user IDU) time.Duration { u := db.Users[user] if u.Cadence == 0 { return time.Hour * 24 } return time.Duration(u.Cadence) } func (db yamlDB) Question(q IDQ) Question { return db.Knowledge.Questions[q] } func (db yamlDB) LastAnswer(user IDU, q IDQ) (IDA, Answer) { var maxk IDA var maxv Answer for k, v := range db.Knowledge.Answers { if v.Q == q && v.Author == user { if maxv.TS < v.TS { maxk = k maxv = v } } } return maxk, maxv } func (db yamlDB) Answer(a IDA) Answer { return db.Knowledge.Answers[a] } func (db yamlDB) PushAnswer(user IDU, q IDQ, a Renderable, pass bool) error { uuid := IDA(uuid.New().String()) db.Knowledge.Answers[uuid] = Answer{ Q: q, A: a, TS: time.Now().UnixNano(), Author: user, Pass: pass, } return nil } func (db yamlDB) lastTS(user IDU, q IDQ) time.Time { allHistory := db.HistoryOf(user) history := allHistory[q] if len(history) == 0 { return time.Unix(0, 0) } return time.Unix(0, history[len(history)-1].TS) }