184 lines
5.0 KiB
Clojure
184 lines
5.0 KiB
Clojure
(ns net.deertopia.doerg.roam
|
||
(:require [babashka.fs :as fs]
|
||
[net.deertopia.doerg.config :as cfg]
|
||
[net.deertopia.doerg.elisp :as elisp]
|
||
[net.deertopia.doerg.slug :as slug]
|
||
[next.jdbc :as sql])
|
||
(:import (java.util UUID)))
|
||
|
||
|
||
;;; Global database
|
||
|
||
(defonce ^:dynamic *use-db-cache?* true)
|
||
|
||
(defn ds []
|
||
(sql/get-datasource
|
||
{:dbtype "sqlite"
|
||
:dbname (-> cfg/*cfg* ::cfg/org-roam-db-path str)}))
|
||
|
||
|
||
;;; Elisp sexp (de)serialisation
|
||
|
||
(defn id [node]
|
||
(-> node :id))
|
||
|
||
(defn slug [node]
|
||
(-> node :id slug/from-uuid))
|
||
|
||
(defn- print-id [node]
|
||
(-> node id elisp/print))
|
||
|
||
|
||
;;; Node
|
||
|
||
(defrecord Node [id cache])
|
||
|
||
(defn uuid-exists? [uuid]
|
||
(sql/execute-one! (ds)
|
||
["select 1 from nodes where id = ? limit 1"
|
||
(-> uuid str elisp/print)]))
|
||
|
||
(defn make-node
|
||
([uuid] (make-node uuid {}))
|
||
([uuid props]
|
||
(and (uuid-exists? uuid)
|
||
(->Node uuid (atom props)))))
|
||
|
||
(defn- fetch-with-cache [node field fetch]
|
||
(if *use-db-cache?*
|
||
(-> (:cache node)
|
||
(swap! (fn [cache]
|
||
(update cache field #(or % (fetch node)))))
|
||
(get field))
|
||
(fetch node)))
|
||
|
||
(defn org-file [node]
|
||
(fetch-with-cache
|
||
node :org-file
|
||
(fn [node]
|
||
(when-some [r (sql/execute-one!
|
||
(ds)
|
||
["select file from nodes where id = ?"
|
||
(-> node :id str elisp/print)])]
|
||
(-> r :nodes/file elisp/read-string)))))
|
||
|
||
(defn title [node]
|
||
(fetch-with-cache
|
||
node :title
|
||
#(when-some [r (sql/execute-one!
|
||
(ds)
|
||
["select title from nodes where id = ?"
|
||
(print-id %)])]
|
||
(-> r :nodes/title elisp/read-string))))
|
||
|
||
(defprotocol GetNode
|
||
(get-node [this]
|
||
"Return the node associated with `this` or nil."))
|
||
|
||
(extend-protocol GetNode
|
||
String
|
||
(get-node [this]
|
||
(or (some-> this slug/from-string get-node)
|
||
(some-> this parse-uuid get-node)
|
||
(throw (IllegalArgumentException.
|
||
"Give `get-node` a UUID or slug string plz. }:)"))))
|
||
java.util.UUID
|
||
(get-node [this]
|
||
(make-node this))
|
||
net.deertopia.doerg.slug.Slug
|
||
(get-node [this]
|
||
(-> this slug/to-uuid make-node))
|
||
Node
|
||
(get-node [this]
|
||
this))
|
||
|
||
(comment
|
||
(def node (get-node "68XqhHerTWCbE--RYLEdHw"))
|
||
(fetch-with-cache
|
||
node :title
|
||
#(do (println "fetch")
|
||
(sql/execute-one! (ds) ["select title from nodes where id = ?"
|
||
(elisp/print (:id %))]))))
|
||
|
||
|
||
;;; Node operations
|
||
|
||
(defn level [node]
|
||
(fetch-with-cache
|
||
node :level
|
||
#(-> (sql/execute-one!
|
||
(ds) ["select level from nodes where id = ?"
|
||
(print-id %)])
|
||
:nodes/level)))
|
||
|
||
(defn top-level? [node]
|
||
(zero? (level node)))
|
||
|
||
(defn file [node]
|
||
(fetch-with-cache
|
||
node :file
|
||
#(-> (sql/execute-one!
|
||
(ds) ["select file from nodes where id = ?"
|
||
(print-id %)])
|
||
:nodes/file
|
||
elisp/read-string)))
|
||
|
||
(defn properties [node]
|
||
(fetch-with-cache
|
||
node :properties
|
||
#(-> (sql/execute-one!
|
||
(ds) ["select properties from nodes where id = ?"
|
||
(print-id %)])
|
||
:nodes/properties
|
||
elisp/read-alist)))
|
||
|
||
(defn public? [node]
|
||
(-> node properties (get "DEERTOPIAVISIBILITY") (= "public")))
|
||
|
||
(defn graph-visible? [node]
|
||
(#{"public" "graphonly"}
|
||
(-> node properties (get "DEERTOPIAVISIBILITY"))))
|
||
|
||
(defn backlinks
|
||
"Returns a collection of nodes linking to `node`."
|
||
[node]
|
||
(for [{id :nodes/id title :nodes/title}
|
||
(sql/execute! (ds) ["select distinct nodes.id, nodes.title from links
|
||
inner join nodes
|
||
on nodes.id = links.source
|
||
where links.dest = ?"
|
||
(elisp/print (str (:id node)))])
|
||
:let [id' (elisp/read-string id)]
|
||
:when (-> id' parse-uuid get-node public?)]
|
||
(make-node id' {:title (elisp/read-string title)})))
|
||
|
||
|
||
;;; Graph support
|
||
|
||
(defn- read-string-field [n field]
|
||
(-> n (get field) elisp/read-string))
|
||
|
||
(defn- uuid-graph-visible? [uuid]
|
||
(-> uuid parse-uuid get-node graph-visible?))
|
||
|
||
(defn get-graph []
|
||
(let [nodes (sql/execute! (ds) ["select id, title from nodes"])
|
||
links (sql/execute!
|
||
(ds)
|
||
["select n1.id as source, nodes.id as target from
|
||
((nodes as n1) join links on n1.id = links.source)
|
||
join (nodes as n2) on links.dest = nodes.id
|
||
where links.type = '\"id\"'"])]
|
||
{:nodes (for [n nodes
|
||
:let [id (read-string-field n :nodes/id)]
|
||
:when (uuid-graph-visible? id)]
|
||
{:id id
|
||
:title (read-string-field n :nodes/title)})
|
||
:links (for [l links
|
||
:let [source (read-string-field l :nodes/source)
|
||
target (read-string-field l :nodes/target)]
|
||
:when (and (uuid-graph-visible? source)
|
||
(uuid-graph-visible? target))]
|
||
{:source source
|
||
:target target})}))
|