From 785ca83fa08b923f6ff129ee9586c943e6c67dff Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 4 Jan 2024 21:41:30 +0800 Subject: [PATCH 1/4] :art: Database block supports export as CSV https://github.com/siyuan-note/siyuan/issues/10072 --- kernel/model/export.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/model/export.go b/kernel/model/export.go index 118c37f87..4b06ac064 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -169,7 +169,7 @@ func ExportAv2CSV(avID string) (zipPath string, err error) { if nil != removeErr { logging.LogErrorf("remove export folder [%s] failed: %s", exportFolder, removeErr) } - zipPath = "/export/" + url.PathEscape(filepath.Base(zipPath)) + zipPath = "/export/csv/" + url.PathEscape(filepath.Base(zipPath)) return } From 9b6cb211ab52010c5aa8dc43fbacc313beecd98f Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 4 Jan 2024 21:57:47 +0800 Subject: [PATCH 2/4] :art: Database block supports export as CSV https://github.com/siyuan-note/siyuan/issues/10072 --- kernel/av/value.go | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/kernel/av/value.go b/kernel/av/value.go index ebf1317aa..d1708c292 100644 --- a/kernel/av/value.go +++ b/kernel/av/value.go @@ -303,19 +303,35 @@ const ( func NewFormattedValueDate(content, content2 int64, format DateFormat, isNotTime bool) (ret *ValueDate) { var formatted string - if isNotTime { - formatted = time.UnixMilli(content).Format("2006-01-02") - } else { - formatted = time.UnixMilli(content).Format("2006-01-02 15:04") + contentTime := time.UnixMilli(content) + if 0 == content || contentTime.IsZero() { + ret = &ValueDate{ + Content: content, + Content2: content2, + HasEndDate: false, + IsNotTime: true, + FormattedContent: formatted, + } + return } + + if isNotTime { + formatted = contentTime.Format("2006-01-02") + } else { + formatted = contentTime.Format("2006-01-02 15:04") + } + if 0 < content2 { var formattedContent2 string + content2Time := time.UnixMilli(content2) if isNotTime { - formattedContent2 = time.UnixMilli(content2).Format("2006-01-02") + formattedContent2 = content2Time.Format("2006-01-02") } else { - formattedContent2 = time.UnixMilli(content2).Format("2006-01-02 15:04") + formattedContent2 = content2Time.Format("2006-01-02 15:04") + } + if !content2Time.IsZero() { + formatted += " → " + formattedContent2 } - formatted += " → " + formattedContent2 } switch format { case DateFormatNone: From 20cd2ac69629ce582eb82438a610dc79d66fd0ac Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 4 Jan 2024 22:04:29 +0800 Subject: [PATCH 3/4] :art: The database template column supports `queryBlocks` function https://github.com/siyuan-note/siyuan/issues/10077 --- kernel/model/template.go | 28 +--------------------------- kernel/treenode/node.go | 2 +- kernel/util/template.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/kernel/model/template.go b/kernel/model/template.go index 43fa5190f..f68ec071b 100644 --- a/kernel/model/template.go +++ b/kernel/model/template.go @@ -26,13 +26,11 @@ import ( "sort" "strings" "text/template" - "time" "github.com/88250/gulu" "github.com/88250/lute/ast" "github.com/88250/lute/parse" "github.com/88250/lute/render" - "github.com/araddon/dateparse" "github.com/siyuan-note/filelock" "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/av" @@ -217,32 +215,8 @@ func renderTemplate(p, id string, preview bool) (string, error) { dataModel["alias"] = block.Alias } - funcMap := util.BuiltInTemplateFuncs() - funcMap["queryBlocks"] = func(stmt string, args ...string) (ret []*sql.Block) { - for _, arg := range args { - stmt = strings.Replace(stmt, "?", arg, 1) - } - ret = sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit) - return - } - funcMap["querySpans"] = func(stmt string, args ...string) (ret []*sql.Span) { - for _, arg := range args { - stmt = strings.Replace(stmt, "?", arg, 1) - } - ret = sql.SelectSpansRawStmt(stmt, Conf.Search.Limit) - return - } - funcMap["parseTime"] = func(dateStr string) time.Time { - now := time.Now() - ret, err := dateparse.ParseIn(dateStr, now.Location()) - if nil != err { - logging.LogWarnf("parse date [%s] failed [%s], return current time instead", dateStr, err) - return now - } - return ret - } - goTpl := template.New("").Delims(".action{", "}") + funcMap := util.BuiltInTemplateFuncs() tpl, err := goTpl.Funcs(funcMap).Parse(gulu.Str.FromBytes(md)) if nil != err { return "", errors.New(fmt.Sprintf(Conf.Language(44), err.Error())) diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index 0853c513b..c752dbb12 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -962,8 +962,8 @@ func renderTemplateCol(ial map[string]string, tplContent string, rowValues []*av ial["updated"] = time.UnixMilli(block.Block.Updated).Format("20060102150405") } - funcMap := util.BuiltInTemplateFuncs() goTpl := template.New("").Delims(".action{", "}") + funcMap := util.BuiltInTemplateFuncs() tpl, tplErr := goTpl.Funcs(funcMap).Parse(tplContent) if nil != tplErr { logging.LogWarnf("parse template [%s] failed: %s", tplContent, tplErr) diff --git a/kernel/util/template.go b/kernel/util/template.go index 053790ade..b5fc612b2 100644 --- a/kernel/util/template.go +++ b/kernel/util/template.go @@ -18,9 +18,14 @@ package util import ( "math" + "strings" "text/template" + "time" "github.com/Masterminds/sprig/v3" + "github.com/araddon/dateparse" + "github.com/siyuan-note/logging" + "github.com/siyuan-note/siyuan/kernel/sql" "github.com/spf13/cast" ) @@ -34,6 +39,30 @@ func BuiltInTemplateFuncs() (ret template.FuncMap) { ret["powf"] = powf ret["log"] = log ret["logf"] = logf + + ret["queryBlocks"] = func(stmt string, args ...string) (retBlocks []*sql.Block) { + for _, arg := range args { + stmt = strings.Replace(stmt, "?", arg, 1) + } + retBlocks = sql.SelectBlocksRawStmt(stmt, 1, 512) + return + } + ret["querySpans"] = func(stmt string, args ...string) (retSpans []*sql.Span) { + for _, arg := range args { + stmt = strings.Replace(stmt, "?", arg, 1) + } + retSpans = sql.SelectSpansRawStmt(stmt, 512) + return + } + ret["parseTime"] = func(dateStr string) time.Time { + now := time.Now() + retTime, err := dateparse.ParseIn(dateStr, now.Location()) + if nil != err { + logging.LogWarnf("parse date [%s] failed [%s], return current time instead", dateStr, err) + return now + } + return retTime + } return } From 108bdec5f699a08cdf05afb52780ccb67358bf66 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 4 Jan 2024 22:31:42 +0800 Subject: [PATCH 4/4] :art: The database template column supports `queryBlocks` function https://github.com/siyuan-note/siyuan/issues/10077 --- kernel/model/attribute_view.go | 4 +++- kernel/model/template.go | 38 +++++++++++++++++++++++++++++++--- kernel/treenode/node.go | 6 ++++-- kernel/util/template.go | 34 ++---------------------------- 4 files changed, 44 insertions(+), 38 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 8de0e42d3..14be072c3 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -604,7 +604,9 @@ func renderTemplateCol(ial map[string]string, tplContent string, rowValues []*av } goTpl := template.New("").Delims(".action{", "}") - goTpl = goTpl.Funcs(util.BuiltInTemplateFuncs()) + tplFuncMap := util.BuiltInTemplateFuncs() + SQLTemplateFuncs(&tplFuncMap) + goTpl = goTpl.Funcs(tplFuncMap) tpl, tplErr := goTpl.Parse(tplContent) if nil != tplErr { logging.LogWarnf("parse template [%s] failed: %s", tplContent, tplErr) diff --git a/kernel/model/template.go b/kernel/model/template.go index f68ec071b..f6d8d6519 100644 --- a/kernel/model/template.go +++ b/kernel/model/template.go @@ -20,12 +20,14 @@ import ( "bytes" "errors" "fmt" + "github.com/araddon/dateparse" "io/fs" "os" "path/filepath" "sort" "strings" "text/template" + "time" "github.com/88250/gulu" "github.com/88250/lute/ast" @@ -42,7 +44,9 @@ import ( func RenderGoTemplate(templateContent string) (ret string, err error) { tmpl := template.New("") - tmpl = tmpl.Funcs(util.BuiltInTemplateFuncs()) + tplFuncMap := util.BuiltInTemplateFuncs() + SQLTemplateFuncs(&tplFuncMap) + tmpl = tmpl.Funcs(tplFuncMap) tpl, err := tmpl.Parse(templateContent) if nil != err { return "", errors.New(fmt.Sprintf(Conf.Language(44), err.Error())) @@ -216,8 +220,10 @@ func renderTemplate(p, id string, preview bool) (string, error) { } goTpl := template.New("").Delims(".action{", "}") - funcMap := util.BuiltInTemplateFuncs() - tpl, err := goTpl.Funcs(funcMap).Parse(gulu.Str.FromBytes(md)) + tplFuncMap := util.BuiltInTemplateFuncs() + SQLTemplateFuncs(&tplFuncMap) + goTpl = goTpl.Funcs(tplFuncMap) + tpl, err := goTpl.Funcs(tplFuncMap).Parse(gulu.Str.FromBytes(md)) if nil != err { return "", errors.New(fmt.Sprintf(Conf.Language(44), err.Error())) } @@ -388,3 +394,29 @@ func addBlockIALNodes(tree *parse.Tree, removeUpdated bool) { block.InsertAfter(&ast.Node{Type: ast.NodeKramdownBlockIAL, Tokens: parse.IAL2Tokens(block.KramdownIAL)}) } } + +func SQLTemplateFuncs(templateFuncMap *template.FuncMap) { + (*templateFuncMap)["queryBlocks"] = func(stmt string, args ...string) (retBlocks []*sql.Block) { + for _, arg := range args { + stmt = strings.Replace(stmt, "?", arg, 1) + } + retBlocks = sql.SelectBlocksRawStmt(stmt, 1, 512) + return + } + (*templateFuncMap)["querySpans"] = func(stmt string, args ...string) (retSpans []*sql.Span) { + for _, arg := range args { + stmt = strings.Replace(stmt, "?", arg, 1) + } + retSpans = sql.SelectSpansRawStmt(stmt, 512) + return + } + (*templateFuncMap)["parseTime"] = func(dateStr string) time.Time { + now := time.Now() + retTime, err := dateparse.ParseIn(dateStr, now.Location()) + if nil != err { + logging.LogWarnf("parse date [%s] failed [%s], return current time instead", dateStr, err) + return now + } + return retTime + } +} diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index c752dbb12..e5f836cb4 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -963,8 +963,10 @@ func renderTemplateCol(ial map[string]string, tplContent string, rowValues []*av } goTpl := template.New("").Delims(".action{", "}") - funcMap := util.BuiltInTemplateFuncs() - tpl, tplErr := goTpl.Funcs(funcMap).Parse(tplContent) + tplFuncMap := util.BuiltInTemplateFuncs() + // 这里存在依赖问题所以不支持 SQLTemplateFuncs(&tplFuncMap) + goTpl = goTpl.Funcs(tplFuncMap) + tpl, tplErr := goTpl.Funcs(tplFuncMap).Parse(tplContent) if nil != tplErr { logging.LogWarnf("parse template [%s] failed: %s", tplContent, tplErr) return "" diff --git a/kernel/util/template.go b/kernel/util/template.go index b5fc612b2..a388c43f9 100644 --- a/kernel/util/template.go +++ b/kernel/util/template.go @@ -17,16 +17,10 @@ package util import ( - "math" - "strings" - "text/template" - "time" - "github.com/Masterminds/sprig/v3" - "github.com/araddon/dateparse" - "github.com/siyuan-note/logging" - "github.com/siyuan-note/siyuan/kernel/sql" "github.com/spf13/cast" + "math" + "text/template" ) func BuiltInTemplateFuncs() (ret template.FuncMap) { @@ -39,30 +33,6 @@ func BuiltInTemplateFuncs() (ret template.FuncMap) { ret["powf"] = powf ret["log"] = log ret["logf"] = logf - - ret["queryBlocks"] = func(stmt string, args ...string) (retBlocks []*sql.Block) { - for _, arg := range args { - stmt = strings.Replace(stmt, "?", arg, 1) - } - retBlocks = sql.SelectBlocksRawStmt(stmt, 1, 512) - return - } - ret["querySpans"] = func(stmt string, args ...string) (retSpans []*sql.Span) { - for _, arg := range args { - stmt = strings.Replace(stmt, "?", arg, 1) - } - retSpans = sql.SelectSpansRawStmt(stmt, 512) - return - } - ret["parseTime"] = func(dateStr string) time.Time { - now := time.Now() - retTime, err := dateparse.ParseIn(dateStr, now.Location()) - if nil != err { - logging.LogWarnf("parse date [%s] failed [%s], return current time instead", dateStr, err) - return now - } - return retTime - } return }