|
@@ -0,0 +1,223 @@
|
|
|
+// SiYuan - Refactor your thinking
|
|
|
+// Copyright (c) 2020-present, b3log.org
|
|
|
+//
|
|
|
+// This program is free software: you can redistribute it and/or modify
|
|
|
+// it under the terms of the GNU Affero General Public License as published by
|
|
|
+// the Free Software Foundation, either version 3 of the License, or
|
|
|
+// (at your option) any later version.
|
|
|
+//
|
|
|
+// This program is distributed in the hope that it will be useful,
|
|
|
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+// GNU Affero General Public License for more details.
|
|
|
+//
|
|
|
+// You should have received a copy of the GNU Affero General Public License
|
|
|
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
+
|
|
|
+package filesys
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "github.com/88250/lute"
|
|
|
+ "github.com/88250/lute/ast"
|
|
|
+ "github.com/88250/lute/parse"
|
|
|
+ "github.com/siyuan-note/siyuan/kernel/av"
|
|
|
+ "github.com/siyuan-note/siyuan/kernel/treenode"
|
|
|
+ "github.com/siyuan-note/siyuan/kernel/util"
|
|
|
+)
|
|
|
+
|
|
|
+func ContentStat(content string) (ret *util.BlockStatResult) {
|
|
|
+ luteEngine := util.NewLute()
|
|
|
+ return contentStat(content, luteEngine)
|
|
|
+}
|
|
|
+
|
|
|
+func contentStat(content string, luteEngine *lute.Lute) (ret *util.BlockStatResult) {
|
|
|
+ tree := luteEngine.BlockDOM2Tree(content)
|
|
|
+ runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat()
|
|
|
+ return &util.BlockStatResult{
|
|
|
+ RuneCount: runeCnt,
|
|
|
+ WordCount: wordCnt,
|
|
|
+ LinkCount: linkCnt,
|
|
|
+ ImageCount: imgCnt,
|
|
|
+ RefCount: refCnt,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func StatBlock(id string) (ret *util.BlockStatResult) {
|
|
|
+ trees := LoadTrees([]string{id})
|
|
|
+ if 1 > len(trees) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ tree := trees[id]
|
|
|
+ if nil == tree {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ node := treenode.GetNodeInTree(tree, id)
|
|
|
+ if nil == node {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if ast.NodeDocument == node.Type {
|
|
|
+ return statTree(tree)
|
|
|
+ }
|
|
|
+
|
|
|
+ runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat()
|
|
|
+ ret = &util.BlockStatResult{
|
|
|
+ runeCnt,
|
|
|
+ wordCnt,
|
|
|
+ linkCnt,
|
|
|
+ imgCnt,
|
|
|
+ refCnt,
|
|
|
+ 1,
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func StatTree(id string) (ret *util.BlockStatResult) {
|
|
|
+ trees := LoadTrees([]string{id})
|
|
|
+ if 1 > len(trees) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ tree := trees[id]
|
|
|
+ if nil == tree {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ return statTree(tree)
|
|
|
+}
|
|
|
+
|
|
|
+func statTree(tree *parse.Tree) (ret *util.BlockStatResult) {
|
|
|
+ blockCount := 0
|
|
|
+ var databaseBlockNodes []*ast.Node
|
|
|
+ ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
|
|
+ if !entering {
|
|
|
+ return ast.WalkContinue
|
|
|
+ }
|
|
|
+
|
|
|
+ if n.IsBlock() {
|
|
|
+ blockCount++
|
|
|
+ }
|
|
|
+
|
|
|
+ if ast.NodeAttributeView != n.Type {
|
|
|
+ return ast.WalkContinue
|
|
|
+ }
|
|
|
+
|
|
|
+ databaseBlockNodes = append(databaseBlockNodes, n)
|
|
|
+ return ast.WalkContinue
|
|
|
+ })
|
|
|
+
|
|
|
+ luteEngine := util.NewLute()
|
|
|
+ var dbRuneCnt, dbWordCnt, dbLinkCnt, dbImgCnt, dbRefCnt int
|
|
|
+ for _, n := range databaseBlockNodes {
|
|
|
+ if "" == n.AttributeViewID {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ attrView, _ := av.ParseAttributeView(n.AttributeViewID)
|
|
|
+ if nil == attrView {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ content := bytes.Buffer{}
|
|
|
+ for _, kValues := range attrView.KeyValues {
|
|
|
+ for _, v := range kValues.Values {
|
|
|
+ switch kValues.Key.Type {
|
|
|
+ case av.KeyTypeURL:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ dbLinkCnt++
|
|
|
+ content.WriteString(v.URL.Content)
|
|
|
+ case av.KeyTypeMAsset:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, asset := range v.MAsset {
|
|
|
+ if av.AssetTypeImage == asset.Type {
|
|
|
+ dbImgCnt++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case av.KeyTypeBlock:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if !v.IsDetached {
|
|
|
+ dbRefCnt++
|
|
|
+ }
|
|
|
+ content.WriteString(v.Block.Content)
|
|
|
+ case av.KeyTypeText:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ content.WriteString(v.Text.Content)
|
|
|
+ case av.KeyTypeNumber:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ v.Number.FormatNumber()
|
|
|
+ content.WriteString(v.Number.FormattedContent)
|
|
|
+ case av.KeyTypeEmail:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ content.WriteString(v.Email.Content)
|
|
|
+ case av.KeyTypePhone:
|
|
|
+ if v.IsEmpty() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ content.WriteString(v.Phone.Content)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dbStat := contentStat(content.String(), luteEngine)
|
|
|
+ dbRuneCnt += dbStat.RuneCount
|
|
|
+ dbWordCnt += dbStat.WordCount
|
|
|
+ }
|
|
|
+
|
|
|
+ runeCnt, wordCnt, linkCnt, imgCnt, refCnt := tree.Root.Stat()
|
|
|
+ runeCnt += dbRuneCnt
|
|
|
+ wordCnt += dbWordCnt
|
|
|
+ linkCnt += dbLinkCnt
|
|
|
+ imgCnt += dbImgCnt
|
|
|
+ refCnt += dbRefCnt
|
|
|
+ return &util.BlockStatResult{
|
|
|
+ RuneCount: runeCnt,
|
|
|
+ WordCount: wordCnt,
|
|
|
+ LinkCount: linkCnt,
|
|
|
+ ImageCount: imgCnt,
|
|
|
+ RefCount: refCnt,
|
|
|
+ BlockCount: blockCount,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func BlocksWordCount(ids []string) (ret *util.BlockStatResult) {
|
|
|
+ ret = &util.BlockStatResult{}
|
|
|
+ trees := LoadTrees(ids)
|
|
|
+ for _, id := range ids {
|
|
|
+ tree := trees[id]
|
|
|
+ if nil == tree {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ node := treenode.GetNodeInTree(tree, id)
|
|
|
+ if nil == node {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ runeCnt, wordCnt, linkCnt, imgCnt, refCnt := node.Stat()
|
|
|
+ ret.RuneCount += runeCnt
|
|
|
+ ret.WordCount += wordCnt
|
|
|
+ ret.LinkCount += linkCnt
|
|
|
+ ret.ImageCount += imgCnt
|
|
|
+ ret.RefCount += refCnt
|
|
|
+ }
|
|
|
+ ret.BlockCount = len(ids)
|
|
|
+ return
|
|
|
+}
|