Anki/yamldb.go

160 lines
3.0 KiB
Go

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)
}