flashcard.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // SiYuan - Build Your Eternal Digital Garden
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package model
  17. import (
  18. "os"
  19. "path/filepath"
  20. "strings"
  21. "sync"
  22. "github.com/88250/lute/ast"
  23. "github.com/siyuan-note/logging"
  24. "github.com/siyuan-note/riff"
  25. "github.com/siyuan-note/siyuan/kernel/util"
  26. )
  27. var Decks = map[string]*riff.Deck{}
  28. var deckLock = sync.Mutex{}
  29. func ReviewFlashcard(deckID string, blockID string, rating riff.Rating) (err error) {
  30. deckLock.Lock()
  31. deck := Decks[deckID]
  32. deckLock.Unlock()
  33. deck.Review(blockID, rating)
  34. err = deck.Save()
  35. if nil != err {
  36. logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
  37. return
  38. }
  39. return
  40. }
  41. func GetDueFlashcards(deckID string) (ret []string, err error) {
  42. if "" == deckID {
  43. return getAllDueFlashcards()
  44. }
  45. deckLock.Lock()
  46. deck := Decks[deckID]
  47. deckLock.Unlock()
  48. cards := deck.Dues()
  49. for _, card := range cards {
  50. blockID := card.BlockID()
  51. _, getErr := GetBlock(blockID)
  52. if nil != getErr {
  53. continue
  54. }
  55. ret = append(ret, blockID)
  56. }
  57. return
  58. }
  59. func getAllDueFlashcards() (ret []string, err error) {
  60. blockIDs := map[string]bool{}
  61. for _, deck := range Decks {
  62. cards := deck.Dues()
  63. for _, card := range cards {
  64. blockID := card.BlockID()
  65. _, getErr := GetBlock(blockID)
  66. if nil != getErr {
  67. continue
  68. }
  69. if blockIDs[blockID] {
  70. continue
  71. }
  72. ret = append(ret, blockID)
  73. blockIDs[blockID] = true
  74. }
  75. }
  76. return
  77. }
  78. func RemoveFlashcards(deckID string, blockIDs []string) (err error) {
  79. deckLock.Lock()
  80. deck := Decks[deckID]
  81. deckLock.Unlock()
  82. for _, blockID := range blockIDs {
  83. deck.RemoveCard(blockID)
  84. }
  85. err = deck.Save()
  86. if nil != err {
  87. logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
  88. return
  89. }
  90. return
  91. }
  92. func AddFlashcards(deckID string, blockIDs []string) (err error) {
  93. deckLock.Lock()
  94. deck := Decks[deckID]
  95. deckLock.Unlock()
  96. for _, blockID := range blockIDs {
  97. cardID := ast.NewNodeID()
  98. deck.AddCard(cardID, blockID)
  99. }
  100. err = deck.Save()
  101. if nil != err {
  102. logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
  103. return
  104. }
  105. return
  106. }
  107. func InitFlashcards() {
  108. riffSavePath := getRiffDir()
  109. if err := os.MkdirAll(riffSavePath, 0755); nil != err {
  110. logging.LogErrorf("create riff dir [%s] failed: %s", riffSavePath, err)
  111. return
  112. }
  113. entries, err := os.ReadDir(riffSavePath)
  114. if nil != err {
  115. logging.LogErrorf("read riff dir failed: %s", err)
  116. return
  117. }
  118. for _, entry := range entries {
  119. name := entry.Name()
  120. if strings.HasSuffix(name, ".deck") {
  121. deckID := strings.TrimSuffix(name, ".deck")
  122. deck, loadErr := riff.LoadDeck(riffSavePath, deckID)
  123. if nil != loadErr {
  124. logging.LogErrorf("load deck [%s] failed: %s", name, loadErr)
  125. continue
  126. }
  127. Decks[deckID] = deck
  128. }
  129. }
  130. if 1 > len(Decks) {
  131. deck, createErr := CreateDeck("Default Deck")
  132. if nil == createErr {
  133. Decks[deck.ID] = deck
  134. }
  135. }
  136. }
  137. func RenameDeck(deckID string, name string) (err error) {
  138. deckLock.Lock()
  139. deck := Decks[deckID]
  140. deckLock.Unlock()
  141. deck.Name = name
  142. err = deck.Save()
  143. if nil != err {
  144. logging.LogErrorf("save deck [%s] failed: %s", deckID, err)
  145. return
  146. }
  147. return
  148. }
  149. func CreateDeck(name string) (deck *riff.Deck, err error) {
  150. riffSavePath := getRiffDir()
  151. deckID := ast.NewNodeID()
  152. deck, err = riff.LoadDeck(riffSavePath, deckID)
  153. if nil != err {
  154. logging.LogErrorf("load deck [%s] failed: %s", deckID, err)
  155. return
  156. }
  157. deck.Name = name
  158. deckLock.Lock()
  159. Decks[deckID] = deck
  160. deckLock.Unlock()
  161. return
  162. }
  163. func GetDecks() (decks []*riff.Deck) {
  164. deckLock.Lock()
  165. defer deckLock.Unlock()
  166. for _, deck := range Decks {
  167. decks = append(decks, deck)
  168. }
  169. if 1 > len(decks) {
  170. decks = []*riff.Deck{}
  171. }
  172. return
  173. }
  174. func getRiffDir() string {
  175. return filepath.Join(util.DataDir, "storage", "riff")
  176. }