Add example update script

Split out persist-graph as it is useful outside of just creating graphs.
Also updated docstring with more known limitations
This commit is contained in:
Gabriel Horner
2023-08-21 14:54:29 -04:00
parent 6212714812
commit fc9b0d7b2c
5 changed files with 117 additions and 33 deletions

View File

@@ -2,41 +2,19 @@
"This ns provides fns to create a DB graph using EDN. See `init-conn` for
initializing a DB graph with a datascript connection that syncs to a sqlite DB
at the given directory. See `create-blocks-tx` for the EDN format to create a
graph. Note that block creation is limited to top level blocks for now. This
ns can likely be used to also update graphs"
graph and current limitations"
(:require [logseq.db.sqlite.db :as sqlite-db]
[logseq.db.sqlite.util :as sqlite-util]
[cljs-bean.core :as bean]
[logseq.tasks.db-graph.persist-graph :as persist-graph]
[logseq.db :as ldb]
[clojure.string :as string]
[datascript.core :as d]
["fs" :as fs]
["path" :as node-path]
[nbb.classpath :as cp]
;; TODO: Move these namespaces to more stable deps/ namespaces
[frontend.modules.datascript-report.core :as ds-report]
[frontend.modules.outliner.pipeline-util :as pipeline-util]
;; TODO: Move this namespace to more stable deps/ namespaces
[frontend.handler.common.repo :as repo-common-handler]))
(defn- invoke-hooks
"Modified copy frontend.modules.outliner.pipeline/invoke-hooks that doesn't
handle :block/path-refs recalculation"
[{:keys [db-after] :as tx-report}]
(let [{:keys [blocks]} (ds-report/get-blocks-and-pages tx-report)
deleted-block-uuids (set (pipeline-util/filter-deleted-blocks (:tx-data tx-report)))
upsert-blocks (pipeline-util/build-upsert-blocks blocks deleted-block-uuids db-after)]
{:blocks upsert-blocks
:deleted-block-uuids deleted-block-uuids}))
(defn- update-sqlite-db
"Modified copy of :db-transact-data defmethod in electron.handler"
[db-name {:keys [blocks deleted-block-uuids]}]
(when (seq deleted-block-uuids)
(sqlite-db/delete-blocks! db-name deleted-block-uuids))
(when (seq blocks)
(let [blocks' (mapv sqlite-util/ds->sqlite-block blocks)]
(sqlite-db/upsert-blocks! db-name (bean/->js blocks')))))
(defn- find-on-classpath [rel-path]
(some (fn [dir]
(let [f (node-path/join dir rel-path)]
@@ -61,8 +39,7 @@
(sqlite-db/open-db! dir db-name)
;; Same order as frontend.db.conn/start!
(let [conn (ldb/start-conn :create-default-pages? false)]
(d/listen! conn :persist-to-sqlite (fn persist-to-sqlite [tx-report]
(update-sqlite-db db-name (invoke-hooks tx-report))))
(persist-graph/add-listener conn db-name)
(ldb/create-default-pages! conn)
(setup-init-data conn)
conn))
@@ -145,8 +122,15 @@
(defn create-blocks-tx
"Given an EDN map for defining pages, blocks and properties, this creates a
vector of transactable data for use with d/transact!. The EDN map is basic and
only supports defining blocks at the top level. The EDN map has the following keys:
vector of transactable data for use with d/transact!. The blocks that can be created
have the following limitations:
* Only top level blocks can be easily defined. Other level blocks can be
defined but they require explicit setting of attributes like :block/left and :block/parent
* Block content containing page refs or tags is not supported yet
* Property types :object and :date aren't supported yet
The EDN map has the following keys:
* :pages-and-blocks - This is a vector of maps containing a :page key and optionally a :blocks
key when defining a page's blocks. More about each key:

View File

@@ -5,6 +5,7 @@
[clojure.string :as string]
[datascript.core :as d]
["path" :as node-path]
["os" :as os]
[nbb.core :as nbb]))
(defn- date-journal-title [date]
@@ -67,7 +68,10 @@
(when (not= 1 (count args))
(println "Usage: $0 GRAPH-DIR")
(js/process.exit 1))
(let [[dir db-name] ((juxt node-path/dirname node-path/basename) (first args))
(let [graph-dir (first args)
[dir db-name] (if (string/includes? graph-dir "/")
((juxt node-path/dirname node-path/basename) graph-dir)
[(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
conn (create-graph/init-conn dir db-name)
blocks-tx (create-graph/create-blocks-tx (create-init-data))]
(println "Generating" (count (filter :block/name blocks-tx)) "pages and"

View File

@@ -0,0 +1,38 @@
(ns logseq.tasks.db-graph.persist-graph
"This ns allows DB graphs to persist datascript changes to their respective
sqlite db. Since changes are persisted, this can be used to create or update graphs.
Known limitations:
* Changes to block references don't update :block/path-refs"
(:require [datascript.core :as d]
[logseq.db.sqlite.db :as sqlite-db]
[logseq.db.sqlite.util :as sqlite-util]
[cljs-bean.core :as bean]
;; TODO: Move these namespaces to more stable deps/ namespaces
[frontend.modules.datascript-report.core :as ds-report]
[frontend.modules.outliner.pipeline-util :as pipeline-util]))
(defn- invoke-hooks
"Modified copy frontend.modules.outliner.pipeline/invoke-hooks that doesn't
handle :block/path-refs recalculation"
[{:keys [db-after] :as tx-report}]
(let [{:keys [blocks]} (ds-report/get-blocks-and-pages tx-report)
deleted-block-uuids (set (pipeline-util/filter-deleted-blocks (:tx-data tx-report)))
upsert-blocks (pipeline-util/build-upsert-blocks blocks deleted-block-uuids db-after)]
{:blocks upsert-blocks
:deleted-block-uuids deleted-block-uuids}))
(defn- update-sqlite-db
"Modified copy of :db-transact-data defmethod in electron.handler"
[db-name {:keys [blocks deleted-block-uuids]}]
(when (seq deleted-block-uuids)
(sqlite-db/delete-blocks! db-name deleted-block-uuids))
(when (seq blocks)
(let [blocks' (mapv sqlite-util/ds->sqlite-block blocks)]
(sqlite-db/upsert-blocks! db-name (bean/->js blocks')))))
(defn add-listener
"Adds a listener to the datascript connection to persist changes to the given
sqlite db name"
[conn db-name]
(d/listen! conn :persist-to-sqlite (fn persist-to-sqlite [tx-report]
(update-sqlite-db db-name (invoke-hooks tx-report)))))

View File

@@ -0,0 +1,42 @@
(ns logseq.tasks.db-graph.update-graph-to-add-todos
"This script updates blocks that match the given query and turns them into TODOs"
(:require [logseq.tasks.db-graph.persist-graph :as persist-graph]
[logseq.db.sqlite.cli :as sqlite-cli]
[logseq.db.sqlite.db :as sqlite-db]
[logseq.db.rules :as rules]
[datascript.core :as d]
[clojure.edn :as edn]
[clojure.string :as string]
[nbb.core :as nbb]
["path" :as node-path]
["os" :as os]))
(defn -main [args]
(when (not= 2 (count args))
(println "Usage: $0 GRAPH-DIR QUERY")
(js/process.exit 1))
(let [[graph-dir query*] args
[dir db-name] (if (string/includes? graph-dir "/")
((juxt node-path/dirname node-path/basename) graph-dir)
[(node-path/join (os/homedir) "logseq" "graphs") graph-dir])
_ (sqlite-db/open-db! dir db-name)
conn (sqlite-cli/read-graph db-name)
;; find blocks to update
query (into (edn/read-string query*) [:in '$ '%]) ;; assumes no :in are in queries
blocks-to-update (mapv first (d/q query @conn (rules/extract-rules rules/db-query-dsl-rules)))
;; update
todo-id (or (:db/id (d/entity @conn [:block/name "todo"]))
(throw (ex-info "No :db/id for TODO" {})))
update-tx (vec (keep #(when-not (:block/marker %)
(hash-map :db/id (:db/id %)
:block/content (str "TODO " (:block/content %))
:block/marker "TODO"
:block/refs (into [{:db/id todo-id}] (:block/refs %))
:block/path-refs (into [{:db/id todo-id}] (:block/path-refs %))))
blocks-to-update))]
(persist-graph/add-listener conn db-name)
(d/transact! conn update-tx)
(println "Updated" (count update-tx) "block(s) with a 'TODO' for graph" (str db-name "!"))))
(when (= nbb/*file* (:file (meta #'-main)))
(-main *command-line-args*))