🎨 Spaced repetition interface supports review by document selection https://github.com/siyuan-note/siyuan/issues/7954

This commit is contained in:
Liang Ding 2023-04-13 19:07:09 +08:00
parent 0ebdd49f8a
commit 66bd5e91a5
No known key found for this signature in database
GPG key ID: 136F30F901A2231D
7 changed files with 112 additions and 38 deletions

View file

@ -95,7 +95,7 @@ func heading2Doc(c *gin.Context) {
name := path.Base(targetPath) name := path.Base(targetPath)
box := model.Conf.Box(targetNotebook) box := model.Conf.Box(targetNotebook)
files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort, false)
evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast) evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
@ -140,7 +140,7 @@ func li2Doc(c *gin.Context) {
name := path.Base(targetPath) name := path.Base(targetPath)
box := model.Conf.Box(targetNotebook) box := model.Conf.Box(targetNotebook)
files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort, false)
evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast) evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
@ -448,7 +448,7 @@ func createDailyNote(c *gin.Context) {
evt.AppId = app evt.AppId = app
name := path.Base(p) name := path.Base(p)
files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort, false)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
"path": p, "path": p,
@ -590,8 +590,13 @@ func searchDocs(c *gin.Context) {
return return
} }
flashcard := false
if arg["flashcard"] != nil {
flashcard = arg["flashcard"].(bool)
}
k := arg["k"].(string) k := arg["k"].(string)
ret.Data = model.SearchDocsByKeyword(k) ret.Data = model.SearchDocsByKeyword(k, flashcard)
} }
func listDocsByPath(c *gin.Context) { func listDocsByPath(c *gin.Context) {
@ -610,7 +615,12 @@ func listDocsByPath(c *gin.Context) {
if nil != sortParam { if nil != sortParam {
sortMode = int(sortParam.(float64)) sortMode = int(sortParam.(float64))
} }
files, totals, err := model.ListDocTree(notebook, p, sortMode) flashcard := false
if arg["flashcard"] != nil {
flashcard = arg["flashcard"].(bool)
}
files, totals, err := model.ListDocTree(notebook, p, sortMode, flashcard)
if nil != err { if nil != err {
ret.Code = -1 ret.Code = -1
ret.Msg = err.Error() ret.Msg = err.Error()
@ -708,7 +718,7 @@ func getDoc(c *gin.Context) {
func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) { func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) {
evt := util.NewCmdResult("create", 0, util.PushModeBroadcast) evt := util.NewCmdResult("create", 0, util.PushModeBroadcast)
name := path.Base(p) name := path.Base(p)
files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort) files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort, false)
evt.Data = map[string]interface{}{ evt.Data = map[string]interface{}{
"box": box, "box": box,
"path": p, "path": p,

View file

@ -308,11 +308,27 @@ func lsNotebooks(c *gin.Context) {
ret := gulu.Ret.NewResult() ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret) defer c.JSON(http.StatusOK, ret)
notebooks, err := model.ListNotebooks() arg, ok := util.JsonArg(c, ret)
if nil != err { if !ok {
return return
} }
flashcard := false
if arg["flashcard"] != nil {
flashcard = arg["flashcard"].(bool)
}
var notebooks []*model.Box
if flashcard {
notebooks = model.GetFlashcardNotebooks()
} else {
var err error
notebooks, err = model.ListNotebooks()
if nil != err {
return
}
}
ret.Data = map[string]interface{}{ ret.Data = map[string]interface{}{
"notebooks": notebooks, "notebooks": notebooks,
} }

View file

@ -27,16 +27,6 @@ import (
"github.com/siyuan-note/siyuan/kernel/util" "github.com/siyuan-note/siyuan/kernel/util"
) )
func getRiffCardNotebooks(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
notebooks := model.GetFlashcardNotebooks()
ret.Data = map[string]interface{}{
"notebooks": notebooks,
}
}
func getNotebookRiffCards(c *gin.Context) { func getNotebookRiffCards(c *gin.Context) {
ret := gulu.Ret.NewResult() ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret) defer c.JSON(http.StatusOK, ret)

View file

@ -110,7 +110,7 @@ func loadTreeNodes(box string, p string, level int) (ret []*ast.Node, err error)
} }
func buildBlockChildren(block *Block) (err error) { func buildBlockChildren(block *Block) (err error) {
files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort) files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort, false)
if nil != err { if nil != err {
return return
} }

View file

@ -141,9 +141,18 @@ func (box *Box) moveCorruptedData(filePath string) {
logging.LogWarnf("moved corrupted data file [%s] to [%s]", filePath, to) logging.LogWarnf("moved corrupted data file [%s] to [%s]", filePath, to)
} }
func SearchDocsByKeyword(keyword string) (ret []map[string]string) { func SearchDocsByKeyword(keyword string, flashcard bool) (ret []map[string]string) {
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() openedBoxes := Conf.GetOpenedBoxes()
boxes := map[string]*Box{} boxes := map[string]*Box{}
for _, box := range openedBoxes { for _, box := range openedBoxes {
@ -154,7 +163,13 @@ func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
if "" != keyword { if "" != keyword {
for _, box := range boxes { for _, box := range boxes {
if strings.Contains(box.Name, keyword) { if strings.Contains(box.Name, keyword) {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) if flashcard {
if isBoxContainFlashcard(box.ID, deckBlockIDs) {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
}
} else {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
}
} }
} }
@ -168,17 +183,29 @@ func SearchDocsByKeyword(keyword string) (ret []map[string]string) {
rootBlocks = sql.QueryRootBlockByCondition(condition) rootBlocks = sql.QueryRootBlockByCondition(condition)
} else { } else {
for _, box := range boxes { for _, box := range boxes {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon}) if flashcard {
if isBoxContainFlashcard(box.ID, deckBlockIDs) {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
}
} else {
ret = append(ret, map[string]string{"path": "/", "hPath": box.Name + "/", "box": box.ID, "boxIcon": box.Icon})
}
} }
} }
for _, block := range rootBlocks { for _, rootBlock := range rootBlocks {
b := boxes[block.Box] b := boxes[rootBlock.Box]
if nil == b { if nil == b {
continue continue
} }
hPath := b.Name + block.HPath hPath := b.Name + rootBlock.HPath
ret = append(ret, map[string]string{"path": block.Path, "hPath": hPath, "box": block.Box, "boxIcon": b.Icon}) if flashcard {
if isTreeContainFlashcard(rootBlock.ID, deckBlockIDs) {
ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon})
}
} else {
ret = append(ret, map[string]string{"path": rootBlock.Path, "hPath": hPath, "box": rootBlock.Box, "boxIcon": b.Icon})
}
} }
sort.Slice(ret, func(i, j int) bool { sort.Slice(ret, func(i, j int) bool {
@ -194,7 +221,7 @@ type FileInfo struct {
isdir bool isdir bool
} }
func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err error) { func ListDocTree(boxID, path string, sortMode int, flashcard bool) (ret []*File, totals int, err error) {
//os.MkdirAll("pprof", 0755) //os.MkdirAll("pprof", 0755)
//cpuProfile, _ := os.Create("pprof/cpu_profile_list_doc_tree") //cpuProfile, _ := os.Create("pprof/cpu_profile_list_doc_tree")
//pprof.StartCPUProfile(cpuProfile) //pprof.StartCPUProfile(cpuProfile)
@ -202,6 +229,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
ret = []*File{} ret = []*File{}
var deckBlockIDs []string
if flashcard {
deck := Decks[builtinDeckID]
if nil != deck {
return
}
deckBlockIDs = deck.GetBlockIDs()
}
box := Conf.Box(boxID) box := Conf.Box(boxID)
if nil == box { if nil == box {
return nil, 0, errors.New(Conf.Language(0)) return nil, 0, errors.New(Conf.Language(0))
@ -247,7 +283,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
} }
} }
} }
docs = append(docs, doc)
if flashcard {
rootID := strings.TrimSuffix(filepath.Base(parentDocPath), ".sy")
if isTreeContainFlashcard(rootID, deckBlockIDs) {
docs = append(docs, doc)
}
} else {
docs = append(docs, doc)
}
} }
continue continue
} }
@ -259,8 +303,15 @@ func ListDocTree(boxID, path string, sortMode int) (ret []*File, totals int, err
if ial := box.docIAL(file.path); nil != ial { if ial := box.docIAL(file.path); nil != ial {
doc := box.docFromFileInfo(file, ial) doc := box.docFromFileInfo(file, ial)
docs = append(docs, doc)
continue if flashcard {
rootID := strings.TrimSuffix(filepath.Base(file.path), ".sy")
if isTreeContainFlashcard(rootID, deckBlockIDs) {
docs = append(docs, doc)
}
} else {
docs = append(docs, doc)
}
} }
} }
elapsed = time.Now().Sub(start).Milliseconds() elapsed = time.Now().Sub(start).Milliseconds()

View file

@ -47,14 +47,24 @@ func GetFlashcardNotebooks() (ret []*Box) {
boxes := Conf.GetOpenedBoxes() boxes := Conf.GetOpenedBoxes()
for _, box := range boxes { for _, box := range boxes {
if isNotebookContainFlashcard(box.ID, deckBlockIDs) { if isBoxContainFlashcard(box.ID, deckBlockIDs) {
ret = append(ret, box) ret = append(ret, box)
} }
} }
return return
} }
func isNotebookContainFlashcard(boxID string, deckBlockIDs []string) (ret bool) { func isTreeContainFlashcard(rootID string, deckBlockIDs []string) (ret bool) {
blockIDs := getTreeSubTreeChildBlocks(rootID)
for _, blockID := range deckBlockIDs {
if gulu.Str.Contains(blockID, blockIDs) {
return true
}
}
return
}
func isBoxContainFlashcard(boxID string, deckBlockIDs []string) (ret bool) {
entries, err := os.ReadDir(filepath.Join(util.DataDir, boxID)) entries, err := os.ReadDir(filepath.Join(util.DataDir, boxID))
if nil != err { if nil != err {
logging.LogErrorf("read dir failed: %s", err) logging.LogErrorf("read dir failed: %s", err)
@ -71,11 +81,8 @@ func isNotebookContainFlashcard(boxID string, deckBlockIDs []string) (ret bool)
} }
rootID := strings.TrimSuffix(entry.Name(), ".sy") rootID := strings.TrimSuffix(entry.Name(), ".sy")
blockIDs := getTreeSubTreeChildBlocks(rootID) if isTreeContainFlashcard(rootID, deckBlockIDs) {
for _, blockID := range deckBlockIDs { return true
if gulu.Str.Contains(blockID, blockIDs) {
return true
}
} }
} }
return return

View file

@ -194,7 +194,7 @@ func Mount(boxID string) (alreadyMount bool, err error) {
box.Index() box.Index()
// 缓存根一级的文档树展开 // 缓存根一级的文档树展开
ListDocTree(box.ID, "/", Conf.FileTree.Sort) ListDocTree(box.ID, "/", Conf.FileTree.Sort, false)
treenode.SaveBlockTree(false) treenode.SaveBlockTree(false)
util.ClearPushProgress(100) util.ClearPushProgress(100)