mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 22:25:01 +00:00
enhance: make closed values scriptable
Added closed value examples of all 5 property types to properties graph. Part of LOG-2903
This commit is contained in:
@@ -124,6 +124,7 @@
|
||||
logseq.common.config common-config
|
||||
logseq.db.frontend.property db-property
|
||||
logseq.db.frontend.property.type db-property-type
|
||||
logseq.db.frontend.property.util db-property-util
|
||||
logseq.db.frontend.rules rules
|
||||
logseq.db.frontend.schema db-schema
|
||||
logseq.db.sqlite.db sqlite-db
|
||||
|
||||
1
deps/db/.carve/config.edn
vendored
1
deps/db/.carve/config.edn
vendored
@@ -5,6 +5,7 @@
|
||||
logseq.db.sqlite.util
|
||||
logseq.db.sqlite.cli
|
||||
logseq.db.frontend.property
|
||||
logseq.db.frontend.property.util
|
||||
logseq.db.frontend.malli-schema
|
||||
;; Some fns are used by frontend but not worth moving over yet
|
||||
logseq.db.frontend.schema]
|
||||
|
||||
47
deps/db/src/logseq/db/frontend/property/util.cljs
vendored
Normal file
47
deps/db/src/logseq/db/frontend/property/util.cljs
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
(ns logseq.db.frontend.property.util
|
||||
"Util fns for building core property concepts"
|
||||
(:require [logseq.db.sqlite.util :as sqlite-util]
|
||||
[datascript.core :as d]))
|
||||
|
||||
(defonce hidden-page-name-prefix "$$$")
|
||||
|
||||
(defn- closed-value-new-block
|
||||
[page-id block-id value property]
|
||||
{:block/type #{"closed value"}
|
||||
:block/format :markdown
|
||||
:block/uuid block-id
|
||||
:block/page page-id
|
||||
:block/metadata {:created-from-property (:block/uuid property)}
|
||||
:block/schema {:value value}
|
||||
:block/parent page-id})
|
||||
|
||||
(defn build-closed-value-block
|
||||
"Builds a closed value block to be transacted"
|
||||
[block-uuid block-value page-id property {:keys [icon-id icon description]}]
|
||||
(cond->
|
||||
(closed-value-new-block page-id (or block-uuid (d/squuid)) block-value property)
|
||||
icon
|
||||
(assoc :block/properties {icon-id icon})
|
||||
|
||||
description
|
||||
(update :block/schema assoc :description description)
|
||||
|
||||
true
|
||||
sqlite-util/block-with-timestamps))
|
||||
|
||||
(defn- build-new-page
|
||||
"Builds a basic page to be transacted. A minimal version of gp-block/page-name->map"
|
||||
[page-name]
|
||||
(sqlite-util/block-with-timestamps
|
||||
{:block/name (sqlite-util/sanitize-page-name page-name)
|
||||
:block/original-name page-name
|
||||
:block/journal? false
|
||||
:block/uuid (d/squuid)}))
|
||||
|
||||
(defn build-property-hidden-page
|
||||
"Builds a hidden property page for closed values to be transacted"
|
||||
[property]
|
||||
(let [page-name (str hidden-page-name-prefix (:block/uuid property))]
|
||||
(-> (build-new-page page-name)
|
||||
(assoc :block/type #{"hidden"}
|
||||
:block/format :markdown))))
|
||||
@@ -5,6 +5,7 @@
|
||||
graph and current limitations"
|
||||
(:require [logseq.db.sqlite.db :as sqlite-db]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.db.frontend.property.util :as db-property-util]
|
||||
[logseq.outliner.cli.persist-graph :as persist-graph]
|
||||
[logseq.db :as ldb]
|
||||
[clojure.string :as string]
|
||||
@@ -120,6 +121,35 @@
|
||||
{:block/properties (->block-properties-tx (:properties m) uuid-maps)
|
||||
:block/refs (build-property-refs (:properties m) property-db-ids)})))
|
||||
|
||||
(defn- ->property-tx
|
||||
[prop-name prop-schema prop-uuid property-db-ids]
|
||||
{:db/id (or (property-db-ids (name prop-name))
|
||||
(throw (ex-info "No :db/id for property" {:property prop-name})))
|
||||
:block/uuid prop-uuid
|
||||
:block/schema (merge {:type :default} prop-schema)
|
||||
:block/original-name (name prop-name)
|
||||
:block/name (sqlite-util/sanitize-page-name (name prop-name))})
|
||||
|
||||
(defn- build-closed-values-tx [prop-name {:block/keys [uuid] :as property} property-db-ids uuid-maps icon-id]
|
||||
(let [page-tx (db-property-util/build-property-hidden-page property)
|
||||
page-id [:block/uuid (:block/uuid page-tx)]
|
||||
closed-value-page-uuids? (contains? #{:page :date} (get-in property [:block/schema :type]))
|
||||
closed-value-blocks-tx
|
||||
(if closed-value-page-uuids?
|
||||
(map #(hash-map :block/uuid (translate-property-value (:value %) uuid-maps))
|
||||
(:closed-values property))
|
||||
(map (fn [{:keys [value icon description uuid]}]
|
||||
(db-property-util/build-closed-value-block
|
||||
uuid value page-id property {:icon-id icon-id
|
||||
:icon icon
|
||||
:description description}))
|
||||
(:closed-values property)))
|
||||
property-schema (assoc (:block/schema property)
|
||||
:values (mapv :block/uuid closed-value-blocks-tx))
|
||||
property-tx (sqlite-util/build-new-property (->property-tx prop-name property-schema uuid property-db-ids))]
|
||||
(into [property-tx page-tx]
|
||||
(when-not closed-value-page-uuids? closed-value-blocks-tx))))
|
||||
|
||||
(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 blocks that can be created
|
||||
@@ -138,15 +168,20 @@
|
||||
* :blocks - This is a vec of datascript attribute maps e.g. `{:block/content \"bar\"}`.
|
||||
:block/content is required and :properties can be passed to define block properties
|
||||
* :properties - This is a map to configure properties where the keys are property names
|
||||
and the values are maps of datascript attributes e.g. `{:block/schema {:type :checkbox}}`
|
||||
and the values are maps of datascript attributes e.g. `{:block/schema {:type :checkbox}}`.
|
||||
Within `:block/schema`, closed values can be defined with :closed-values. The key takes
|
||||
a vec of maps containing keys :uuid, :value and :icon.
|
||||
|
||||
The :properties for :pages-and-blocks is a map of property names to property
|
||||
values. Multiple property values for a many cardinality property are defined
|
||||
as a set. The following property types are supported: :default, :url,
|
||||
:checkbox, :number and :page. :checkbox and :number values are written
|
||||
:checkbox, :number, :page and :date. :checkbox and :number values are written
|
||||
as booleans and integers. :page and :block are references that are written as
|
||||
vectors e.g. `[:page \"PAGE NAME\"]` and `[:block \"block content\"]`"
|
||||
[{:keys [pages-and-blocks properties]}]
|
||||
vectors e.g. `[:page \"PAGE NAME\"]` and `[:block \"block content\"]`
|
||||
|
||||
This fn also takes an optional map arg which supports these keys:
|
||||
* :property-uuids - A map of property keyword names to uuids to provide ids for built-in properties"
|
||||
[{:keys [pages-and-blocks properties]} & {:as options}]
|
||||
(let [;; add uuids before tx for refs in :properties
|
||||
pages-and-blocks' (mapv (fn [{:keys [page blocks]}]
|
||||
(cond-> {:page (merge {:block/uuid (random-uuid)} page)}
|
||||
@@ -157,19 +192,22 @@
|
||||
property-db-ids (->> property-uuids
|
||||
(map #(vector (name (first %)) (new-db-id)))
|
||||
(into {}))
|
||||
new-properties-tx (mapv (fn [[prop-name uuid]]
|
||||
(sqlite-util/build-new-property
|
||||
(merge {:db/id (or (property-db-ids (name prop-name))
|
||||
(throw (ex-info "No :db/id for property" {:property prop-name})))
|
||||
:block/uuid uuid
|
||||
:block/schema (merge {:type :default}
|
||||
(get-in properties [prop-name :block/schema]))
|
||||
:block/original-name (name prop-name)
|
||||
:block/name (sqlite-util/sanitize-page-name (name prop-name))}
|
||||
(when-let [props (not-empty (get-in properties [prop-name :properties]))]
|
||||
{:block/properties (->block-properties-tx props uuid-maps)
|
||||
:block/refs (build-property-refs props property-db-ids)}))))
|
||||
property-uuids)
|
||||
new-properties-tx (vec
|
||||
(mapcat
|
||||
(fn [[prop-name uuid]]
|
||||
(if (get-in properties [prop-name :closed-values])
|
||||
(build-closed-values-tx prop-name
|
||||
(assoc (get properties prop-name)
|
||||
:block/uuid uuid)
|
||||
property-db-ids
|
||||
uuid-maps
|
||||
(get-in options [:property-uuids :icon]))
|
||||
[(sqlite-util/build-new-property
|
||||
(merge (->property-tx prop-name (get-in properties [prop-name :block/schema]) uuid property-db-ids)
|
||||
(when-let [props (not-empty (get-in properties [prop-name :properties]))]
|
||||
{:block/properties (->block-properties-tx props uuid-maps)
|
||||
:block/refs (build-property-refs props property-db-ids)})))]))
|
||||
property-uuids))
|
||||
pages-and-blocks-tx
|
||||
(vec
|
||||
(mapcat
|
||||
|
||||
@@ -26,10 +26,36 @@
|
||||
[date days]
|
||||
(new js/Date (- (.getTime date) (* days 24 60 60 1000))))
|
||||
|
||||
(defn- build-closed-values-config
|
||||
[{:keys [dates]}]
|
||||
{:default-closed
|
||||
(mapv #(hash-map :value %
|
||||
:uuid (random-uuid)
|
||||
:icon {:id % :name % :type :emoji})
|
||||
["joy" "sob" "upside_down_face"])
|
||||
:url-closed
|
||||
(mapv #(hash-map :value %
|
||||
:uuid (random-uuid))
|
||||
["https://logseq.com" "https://docs.logseq.com" "https://github.com/logseq/logseq"])
|
||||
:number-closed
|
||||
(mapv #(hash-map :value %
|
||||
:uuid (random-uuid))
|
||||
[10 42 (rand 100)])
|
||||
:page-closed
|
||||
(mapv #(hash-map :value [:page %])
|
||||
["page 1" "page 2" "page 3"])
|
||||
:date-closed
|
||||
(mapv #(hash-map :value [:page (date-journal-title %)])
|
||||
dates)})
|
||||
|
||||
(defn- create-init-data
|
||||
[]
|
||||
(let [today (new js/Date)
|
||||
yesterday (subtract-days today 1)]
|
||||
yesterday (subtract-days today 1)
|
||||
two-days-ago (subtract-days today 2)
|
||||
closed-values-config (build-closed-values-config {:dates [today yesterday two-days-ago]})
|
||||
random-closed-value #(-> closed-values-config % rand-nth :uuid)
|
||||
random-page-closed-value #(-> closed-values-config % rand-nth :value)]
|
||||
{:pages-and-blocks
|
||||
[{:page
|
||||
{:block/name (date-journal-title today) :block/journal? true :block/journal-day (date-journal-day today)}
|
||||
@@ -38,19 +64,26 @@
|
||||
{:block/content "[[Queries]]"}]}
|
||||
{:page
|
||||
{:block/name (date-journal-title yesterday) :block/journal? true :block/journal-day (date-journal-day yesterday)}}
|
||||
{:page
|
||||
{:block/name (date-journal-title two-days-ago) :block/journal? true :block/journal-day (date-journal-day two-days-ago)}}
|
||||
{:page {:block/name "properties"}
|
||||
:blocks
|
||||
[{:block/content "default property block" :properties {:default "haha"}}
|
||||
{:block/content "default-closed property block" :properties {:default-closed (random-closed-value :default-closed)}}
|
||||
{:block/content "url property block" :properties {:url "https://logseq.com"}}
|
||||
{:block/content "url-many property block" :properties {:url-many #{"https://logseq.com" "https://docs.logseq.com"}}}
|
||||
{:block/content "url-closed property block" :properties {:url-closed (random-closed-value :url-closed)}}
|
||||
{:block/content "checkbox property block" :properties {:checkbox true}}
|
||||
{:block/content "number property block" :properties {:number 5}}
|
||||
{:block/content "number-many property block" :properties {:number-many #{5 10}}}
|
||||
{:block/content "number-closed property block" :properties {:number-closed (random-closed-value :number-closed)}}
|
||||
{:block/content "page property block" :properties {:page [:page "page 1"]}}
|
||||
{:block/content "page-many property block" :properties {:page-many #{[:page "page 1"] [:page "page 2"]}}}
|
||||
{:block/content "page-closed property block" :properties {:page-closed (random-page-closed-value :page-closed)}}
|
||||
{:block/content "date property block" :properties {:date [:page (date-journal-title today)]}}
|
||||
{:block/content "date-many property block" :properties {:date-many #{[:page (date-journal-title today)]
|
||||
[:page (date-journal-title yesterday)]}}}]}
|
||||
[:page (date-journal-title yesterday)]}}}
|
||||
{:block/content "date-closed property block" :properties {:date-closed (random-page-closed-value :date-closed)}}]}
|
||||
{:page {:block/name "queries"}
|
||||
:blocks
|
||||
[{:block/content "{{query (property :default \"haha\")}}"}
|
||||
@@ -67,12 +100,17 @@
|
||||
:blocks
|
||||
[{:block/content "yee"}
|
||||
{:block/content "haw"}]}
|
||||
{:page {:block/name "page 2"}}]
|
||||
{:page {:block/name "page 2"}}
|
||||
{:page {:block/name "page 3"}}]
|
||||
:properties
|
||||
(->> [:default :url :checkbox :number :page :date]
|
||||
(mapcat #(cond-> [[% {:block/schema {:type %}}]]
|
||||
(db-property-type/property-type-allows-schema-attribute? % :cardinality)
|
||||
(conj [(keyword (str (name %) "-many")) {:block/schema {:type % :cardinality :many}}])))
|
||||
(into (mapv #(vector (keyword (str (name %) "-closed"))
|
||||
{:closed-values (closed-values-config (keyword (str (name %) "-closed")))
|
||||
:block/schema {:type %}})
|
||||
[:default :url :number :page :date]))
|
||||
(into {}))}))
|
||||
|
||||
(defn -main [args]
|
||||
@@ -84,7 +122,9 @@
|
||||
((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))]
|
||||
blocks-tx (create-graph/create-blocks-tx
|
||||
(create-init-data)
|
||||
{:property-uuids {:icon (:block/uuid (d/entity @conn [:block/name "icon"]))}})]
|
||||
(println "Generating" (count (filter :block/name blocks-tx)) "pages and"
|
||||
(count (filter :block/content blocks-tx)) "blocks ...")
|
||||
(d/transact! conn blocks-tx)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[logseq.db.frontend.property.util :as db-property-util]
|
||||
[malli.util :as mu]
|
||||
[malli.error :as me]
|
||||
[logseq.graph-parser.util.page-ref :as page-ref]
|
||||
@@ -636,24 +637,11 @@
|
||||
{:page page-tx
|
||||
:blocks [new-block]}))
|
||||
|
||||
(defn- closed-value-new-block
|
||||
[page-id block-id value property]
|
||||
{:block/type #{"closed value"}
|
||||
:block/format :markdown
|
||||
:block/uuid block-id
|
||||
:block/page page-id
|
||||
:block/metadata {:created-from-property (:block/uuid property)}
|
||||
:block/schema {:value value}
|
||||
:block/parent page-id})
|
||||
|
||||
(defn- get-property-hidden-page
|
||||
[property]
|
||||
(let [page-name (str "$$$" (:block/uuid property))
|
||||
page-entity (db/entity [:block/name page-name])]
|
||||
(or page-entity
|
||||
(-> (block/page-name->map page-name true)
|
||||
(assoc :block/type #{"hidden"}
|
||||
:block/format :markdown)))))
|
||||
(let [page-name (str db-property-util/hidden-page-name-prefix (:block/uuid property))]
|
||||
(or (db/entity [:block/name page-name])
|
||||
(db-property-util/build-property-hidden-page property))))
|
||||
|
||||
(defn upsert-closed-value
|
||||
"id should be a block UUID or nil"
|
||||
@@ -720,16 +708,10 @@
|
||||
(let [page (get-property-hidden-page property)
|
||||
page-tx (when-not (e/entity? page) page)
|
||||
page-id [:block/uuid (:block/uuid page)]
|
||||
new-block (cond->
|
||||
(closed-value-new-block page-id block-id resolved-value property)
|
||||
icon
|
||||
(assoc :block/properties {icon-id icon})
|
||||
|
||||
description
|
||||
(update :block/schema assoc :description description)
|
||||
|
||||
true
|
||||
sqlite-util/block-with-timestamps)
|
||||
new-block (db-property-util/build-closed-value-block
|
||||
block-id resolved-value page-id property {:icon-id icon-id
|
||||
:icon icon
|
||||
:description description})
|
||||
new-values (vec (conj closed-values block-id))]
|
||||
(->> (cons page-tx [new-block
|
||||
{:db/id (:db/id property)
|
||||
@@ -749,11 +731,12 @@
|
||||
page-tx (when-not (e/entity? page) page)
|
||||
page-id (:block/uuid page)
|
||||
closed-value-blocks (map (fn [value]
|
||||
(sqlite-util/block-with-timestamps
|
||||
(closed-value-new-block [:block/uuid page-id]
|
||||
(db/new-block-id)
|
||||
value
|
||||
property)))
|
||||
(db-property-util/build-closed-value-block
|
||||
(db/new-block-id)
|
||||
value
|
||||
[:block/uuid page-id]
|
||||
property
|
||||
{}))
|
||||
(remove string/blank? values))
|
||||
value->block-id (zipmap
|
||||
(map #(get-in % [:block/schema :value]) closed-value-blocks)
|
||||
|
||||
Reference in New Issue
Block a user