package main import ( "os" "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 History map[IDQ][]History } 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("W") == "" { 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][]History { result := map[IDQ][]History{} for k, v := range db.Users[user].History { result[k] = append([]History{}, 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] = []History{} } } } return result } func (db yamlDB) Next(user IDU, q IDQ) time.Time { history := db.Users[user].History[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 Next(last, log) } 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, } db.Users[user].History[q] = append(db.Users[user].History[q], History{ A: uuid, Pass: pass, TS: db.Knowledge.Answers[uuid].TS, }) return nil } func (db yamlDB) lastTS(user IDU, q IDQ) time.Time { max := int64(0) for _, v := range db.Users[user].History[q] { if v.TS > max { max = v.TS } } return time.Unix(0, max) }