diff --git a/kernel/go.mod b/kernel/go.mod index 43eef3d65..23cfaa28f 100644 --- a/kernel/go.mod +++ b/kernel/go.mod @@ -38,7 +38,7 @@ require ( github.com/mitchellh/go-ps v1.0.0 github.com/mssola/user_agent v0.6.0 github.com/olahol/melody v1.1.3 - github.com/open-spaced-repetition/go-fsrs v0.1.0 + github.com/open-spaced-repetition/go-fsrs v0.1.1 github.com/panjf2000/ants/v2 v2.7.3 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/radovskyb/watcher v1.0.7 @@ -50,7 +50,7 @@ require ( github.com/siyuan-note/filelock v0.0.0-20230501032014-b981a05568ef github.com/siyuan-note/httpclient v0.0.0-20230501032226-9e9018416f53 github.com/siyuan-note/logging v0.0.0-20230327073243-ebe83aec1493 - github.com/siyuan-note/riff v0.0.0-20230224144841-cfbe0748ddb7 + github.com/siyuan-note/riff v0.0.0-20230508133423-21cd63a7ef20 github.com/steambap/captcha v1.4.1 github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 github.com/vmihailenco/msgpack/v5 v5.3.5 @@ -131,7 +131,7 @@ require ( golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/tools v0.8.0 // indirect google.golang.org/protobuf v1.29.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/kernel/go.sum b/kernel/go.sum index 609271903..b5caf217a 100644 --- a/kernel/go.sum +++ b/kernel/go.sum @@ -226,8 +226,8 @@ github.com/olahol/melody v1.1.3/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7Cv github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/open-spaced-repetition/go-fsrs v0.1.0 h1:6H1nCuxuR9p/GmKji0zET1uT5KDwOmW++k7jgr8L0Gk= -github.com/open-spaced-repetition/go-fsrs v0.1.0/go.mod h1:H07GOB0A1OBeu3401x8qWKGaa43QjfrDoWy9nba7QCc= +github.com/open-spaced-repetition/go-fsrs v0.1.1 h1:lsUeslUmA2omWqUzRDVLh4pijilUgD8TrylxaLDcXFs= +github.com/open-spaced-repetition/go-fsrs v0.1.1/go.mod h1:H07GOB0A1OBeu3401x8qWKGaa43QjfrDoWy9nba7QCc= github.com/panjf2000/ants/v2 v2.7.3 h1:rHQ0hH0DQvuNUqqlWIMJtkMcDuL1uQAfpX2mIhQ5/s0= github.com/panjf2000/ants/v2 v2.7.3/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -289,8 +289,12 @@ github.com/siyuan-note/httpclient v0.0.0-20230501032226-9e9018416f53 h1:CXYTR4Qh github.com/siyuan-note/httpclient v0.0.0-20230501032226-9e9018416f53/go.mod h1:S/pXlPZYCJTOZjmdmQyVga//24x3XEM+MG8vIYO26gw= github.com/siyuan-note/logging v0.0.0-20230327073243-ebe83aec1493 h1:oaN5b0WDFkjdBgGxmmBnMrtZxaJ76LZLwhQSZnznJMI= github.com/siyuan-note/logging v0.0.0-20230327073243-ebe83aec1493/go.mod h1:6mRFtAAvYPn3cDzqvyv+t8BVPGqpONDMMb5ywOhY1D4= -github.com/siyuan-note/riff v0.0.0-20230224144841-cfbe0748ddb7 h1:Kr8hhMhr6v+U24TMDCP5WdP4dWrXm5maar+TycTZs9I= -github.com/siyuan-note/riff v0.0.0-20230224144841-cfbe0748ddb7/go.mod h1:XJtLlKCr8cZE+lzykM4edHHih92M9M50UNw/nDLYRN8= +github.com/siyuan-note/riff v0.0.0-20230508130104-373b932395bc h1:wTLamZP/NgBvfyAXrePWlwt3WOUgWSBtGCQTiCqcbYQ= +github.com/siyuan-note/riff v0.0.0-20230508130104-373b932395bc/go.mod h1:XJtLlKCr8cZE+lzykM4edHHih92M9M50UNw/nDLYRN8= +github.com/siyuan-note/riff v0.0.0-20230508132857-8b47ce84ef09 h1:WcnK8pGTRXngW5UG2gY344mkJwmoVQYRyL44AmQopCM= +github.com/siyuan-note/riff v0.0.0-20230508132857-8b47ce84ef09/go.mod h1:XJtLlKCr8cZE+lzykM4edHHih92M9M50UNw/nDLYRN8= +github.com/siyuan-note/riff v0.0.0-20230508133423-21cd63a7ef20 h1:p3jeBOabPCMwbmivIC/JKbtW2iwqUJOlYKqQf5pvx8w= +github.com/siyuan-note/riff v0.0.0-20230508133423-21cd63a7ef20/go.mod h1:XJtLlKCr8cZE+lzykM4edHHih92M9M50UNw/nDLYRN8= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= @@ -410,8 +414,8 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/kernel/model/box.go b/kernel/model/box.go index e2f79e131..fcfd0e38b 100644 --- a/kernel/model/box.go +++ b/kernel/model/box.go @@ -53,6 +53,9 @@ type Box struct { SortMode int `json:"sortMode"` Closed bool `json:"closed"` + NewFlashcardCount int `json:"newFlashcardCount"` + DueFlashcardCount int `json:"dueFlashcardCount"` + historyGenerated int64 // 最近一次历史生成时间 } diff --git a/kernel/model/file.go b/kernel/model/file.go index 0bd5a1aec..35701f4d2 100644 --- a/kernel/model/file.go +++ b/kernel/model/file.go @@ -48,23 +48,25 @@ import ( ) type File struct { - Path string `json:"path"` - Name string `json:"name"` // 标题,即 ial["title"] - Icon string `json:"icon"` - Name1 string `json:"name1"` // 命名,即 ial["name"] - Alias string `json:"alias"` - Memo string `json:"memo"` - Bookmark string `json:"bookmark"` - ID string `json:"id"` - Count int `json:"count"` - Size uint64 `json:"size"` - HSize string `json:"hSize"` - Mtime int64 `json:"mtime"` - CTime int64 `json:"ctime"` - HMtime string `json:"hMtime"` - HCtime string `json:"hCtime"` - Sort int `json:"sort"` - SubFileCount int `json:"subFileCount"` + Path string `json:"path"` + Name string `json:"name"` // 标题,即 ial["title"] + Icon string `json:"icon"` + Name1 string `json:"name1"` // 命名,即 ial["name"] + Alias string `json:"alias"` + Memo string `json:"memo"` + Bookmark string `json:"bookmark"` + ID string `json:"id"` + Count int `json:"count"` + Size uint64 `json:"size"` + HSize string `json:"hSize"` + Mtime int64 `json:"mtime"` + CTime int64 `json:"ctime"` + HMtime string `json:"hMtime"` + HCtime string `json:"hCtime"` + Sort int `json:"sort"` + SubFileCount int `json:"subFileCount"` + NewFlashcardCount int `json:"newFlashcardCount"` + DueFlashcardCount int `json:"dueFlashcardCount"` } func (box *Box) docFromFileInfo(fileInfo *FileInfo, ial map[string]string) (ret *File) { @@ -144,13 +146,11 @@ func (box *Box) moveCorruptedData(filePath string) { func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]string) { ret = []map[string]string{} - var deckBlockIDs []string if flashcard { deck := Decks[builtinDeckID] if nil == deck { return } - deckBlockIDs = deck.GetBlockIDs() } openedBoxes := Conf.GetOpenedBoxes() @@ -164,8 +164,9 @@ func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]strin for _, box := range boxes { if strings.Contains(box.Name, keyword) { if flashcard { - if isBoxContainFlashcard(box.ID, deckBlockIDs) { - ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) + newFlashcardCount, dueFlashcardCount, containFlashcard := countBoxFlashcard(box.ID) + if containFlashcard { + ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon, "newFlashcardCount": strconv.Itoa(newFlashcardCount), "dueFlashcardCount": strconv.Itoa(dueFlashcardCount)}) } } else { ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) @@ -184,8 +185,9 @@ func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]strin } else { for _, box := range boxes { if flashcard { - if isBoxContainFlashcard(box.ID, deckBlockIDs) { - ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) + newFlashcardCount, dueFlashcardCount, containFlashcard := countBoxFlashcard(box.ID) + if containFlashcard { + ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon, "newFlashcardCount": strconv.Itoa(newFlashcardCount), "dueFlashcardCount": strconv.Itoa(dueFlashcardCount)}) } } else { ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) @@ -200,8 +202,9 @@ func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]strin } hPath := b.Name + rootBlock.HPath if flashcard { - if isTreeContainFlashcard(rootBlock.ID, deckBlockIDs) { - ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon}) + newFlashcardCount, dueFlashcardCount, containFlashcard := countTreeFlashcard(rootBlock.ID) + if containFlashcard { + ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon, "newFlashcardCount": strconv.Itoa(newFlashcardCount), "dueFlashcardCount": strconv.Itoa(dueFlashcardCount)}) } } else { ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon}) @@ -229,13 +232,11 @@ func ListDocTree(boxID, path string, sortMode int, flashcard bool, maxListCount ret = []*File{} - var deckBlockIDs []string if flashcard { deck := Decks[builtinDeckID] if nil == deck { return } - deckBlockIDs = deck.GetBlockIDs() } box := Conf.Box(boxID) @@ -290,7 +291,10 @@ func ListDocTree(boxID, path string, sortMode int, flashcard bool, maxListCount if flashcard { rootID := strings.TrimSuffix(filepath.Base(parentDocPath), ".sy") - if isTreeContainFlashcard(rootID, deckBlockIDs) { + newFlashcardCount, dueFlashcardCount, containFlashcard := countTreeFlashcard(rootID) + if containFlashcard { + doc.NewFlashcardCount = newFlashcardCount + doc.DueFlashcardCount = dueFlashcardCount docs = append(docs, doc) } } else { @@ -310,7 +314,10 @@ func ListDocTree(boxID, path string, sortMode int, flashcard bool, maxListCount if flashcard { rootID := strings.TrimSuffix(filepath.Base(file.path), ".sy") - if isTreeContainFlashcard(rootID, deckBlockIDs) { + newFlashcardCount, dueFlashcardCount, containFlashcard := countTreeFlashcard(rootID) + if containFlashcard { + doc.NewFlashcardCount = newFlashcardCount + doc.DueFlashcardCount = dueFlashcardCount docs = append(docs, doc) } } else { diff --git a/kernel/model/flashcard.go b/kernel/model/flashcard.go index b98d32f5e..4f0d2f38b 100644 --- a/kernel/model/flashcard.go +++ b/kernel/model/flashcard.go @@ -42,34 +42,58 @@ func GetFlashcardNotebooks() (ret []*Box) { if nil == deck { return } - deckBlockIDs := deck.GetBlockIDs() boxes := Conf.GetOpenedBoxes() for _, box := range boxes { - if isBoxContainFlashcard(box.ID, deckBlockIDs) { + newFlashcardCount, dueFlashcardCount, containFlashcard := countBoxFlashcard(box.ID) + if containFlashcard { + box.NewFlashcardCount = newFlashcardCount + box.DueFlashcardCount = dueFlashcardCount ret = append(ret, box) } } return } -func isTreeContainFlashcard(rootID string, deckBlockIDs []string) (ret bool) { +func countTreeFlashcard(rootID string) (newFlashcardCount, dueFlashcardCount int, containFlashcard bool) { + deck := Decks[builtinDeckID] + if nil == deck { + return + } + + deckBlockIDs := deck.GetBlockIDs() blockIDs := getTreeSubTreeChildBlocks(rootID) + containFlashcard = false for _, blockID := range deckBlockIDs { if gulu.Str.Contains(blockID, blockIDs) { - return true + containFlashcard = true + break } } + if !containFlashcard { + return + } + + newFlashCards := deck.GetNewCardsByBlockIDs(blockIDs) + newFlashcardCount = len(newFlashCards) + newDueFlashcards := deck.GetDueCardsByBlockIDs(blockIDs) + dueFlashcardCount = len(newDueFlashcards) return } -func isBoxContainFlashcard(boxID string, deckBlockIDs []string) (ret bool) { +func countBoxFlashcard(boxID string) (newFlashcardCount, dueFlashcardCount int, containFlashcard bool) { + deck := Decks[builtinDeckID] + if nil == deck { + return + } + entries, err := os.ReadDir(filepath.Join(util.DataDir, boxID)) if nil != err { logging.LogErrorf("read dir failed: %s", err) return } + containFlashcard = false for _, entry := range entries { if entry.IsDir() { continue @@ -80,8 +104,11 @@ func isBoxContainFlashcard(boxID string, deckBlockIDs []string) (ret bool) { } rootID := strings.TrimSuffix(entry.Name(), ".sy") - if isTreeContainFlashcard(rootID, deckBlockIDs) { - return true + treeNewFlashcardCount, treeDueFlashcardCount, treeContainFlashcard := countTreeFlashcard(rootID) + if treeContainFlashcard { + containFlashcard = true + newFlashcardCount += treeNewFlashcardCount + dueFlashcardCount += treeDueFlashcardCount } } return