Skip to content

Commit f42da95

Browse files
committed
fix: preserve imported task statuses
1 parent ee6b38b commit f42da95

2 files changed

Lines changed: 129 additions & 17 deletions

File tree

deps/graph-parser/src/logseq/graph_parser/exporter.cljs

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -409,24 +409,59 @@
409409
block)]
410410
block'))
411411

412+
(def ^:private built-in-status-markers
413+
{"TODO" :logseq.property/status.todo
414+
"LATER" :logseq.property/status.todo
415+
"NOW" :logseq.property/status.doing
416+
"DOING" :logseq.property/status.doing
417+
"DONE" :logseq.property/status.done
418+
"CANCELED" :logseq.property/status.canceled
419+
"CANCELLED" :logseq.property/status.canceled})
420+
421+
(def ^:private custom-status-marker?
422+
#{"WAIT" "WAITING" "IN-PROGRESS"})
423+
424+
(defn- find-status-choice-by-content
425+
[db marker]
426+
(some #(when (= marker (db-property/closed-value-content %)) %)
427+
(db-property/get-closed-property-values db :logseq.property/status)))
428+
429+
(defn- build-status-choice-tx
430+
[marker block-uuid]
431+
(assoc (db-property-build/build-closed-value-block
432+
block-uuid
433+
:default
434+
marker
435+
{:db/ident :logseq.property/status}
436+
{})
437+
:block/order (db-order/gen-key)))
438+
439+
(defn- custom-marker-status-ref
440+
[db marker {:keys [import-state custom-status-tx]}]
441+
(or (get @(:custom-status-markers import-state) marker)
442+
(let [status-ref (if-let [status (find-status-choice-by-content db marker)]
443+
(:db/id status)
444+
(let [block-uuid (common-uuid/gen-uuid)]
445+
(swap! custom-status-tx conj (build-status-choice-tx marker block-uuid))
446+
[:block/uuid block-uuid]))]
447+
(swap! (:custom-status-markers import-state) assoc marker status-ref)
448+
status-ref)))
449+
412450
(defn- update-block-marker
413451
"If a block has a marker, convert it to a task object"
414-
[block {:keys [log-fn]}]
452+
[block db {:keys [log-fn] :as options}]
415453
(if-let [marker (:block/marker block)]
416-
(let [old-to-new {"TODO" :logseq.property/status.todo
417-
"LATER" :logseq.property/status.todo
418-
"IN-PROGRESS" :logseq.property/status.doing
419-
"NOW" :logseq.property/status.doing
420-
"DOING" :logseq.property/status.doing
421-
"DONE" :logseq.property/status.done
422-
"WAIT" :logseq.property/status.backlog
423-
"WAITING" :logseq.property/status.backlog
424-
"CANCELED" :logseq.property/status.canceled
425-
"CANCELLED" :logseq.property/status.canceled}
426-
status-ident (or (old-to-new marker)
427-
(do
428-
(log-fn :invalid-todo (str (pr-str marker) " is not a valid marker so setting it to TODO"))
429-
:logseq.property/status.todo))]
454+
(let [status-ident (cond
455+
(contains? built-in-status-markers marker)
456+
(built-in-status-markers marker)
457+
458+
(custom-status-marker? marker)
459+
(custom-marker-status-ref db marker options)
460+
461+
:else
462+
(do
463+
(log-fn :invalid-todo (str (pr-str marker) " is not a valid marker so setting it to TODO"))
464+
:logseq.property/status.todo))]
430465
(-> block
431466
(assoc :logseq.property/status status-ident)
432467
(update :block/title string/replace-first (re-pattern (str marker "\\s*")) "")
@@ -1850,7 +1885,7 @@
18501885
(handle-embeds page-names-to-uuids walked-ast-blocks (select-keys options [:log-fn]))
18511886
(handle-quotes (select-keys options [:log-fn]))
18521887
(handle-math)
1853-
(update-block-marker options)
1888+
(update-block-marker db options)
18541889
(update-block-priority options)
18551890
add-missing-timestamps
18561891
(dissoc :block/format :block.temp/ast-blocks)
@@ -2105,6 +2140,8 @@
21052140
:all-idents (atom {})
21062141
;; Set of children pages turned into classes by :property-parent-classes option
21072142
:classes-from-property-parents (atom #{})
2143+
;; Map of imported legacy task markers to their Status closed value lookup refs.
2144+
:custom-status-markers (atom {})
21082145
;; Map of block uuids to their :block/properties-text-values value.
21092146
;; Used if a property value changes to :default
21102147
:block-properties-text-values (atom {})
@@ -2120,6 +2157,8 @@
21202157
:upstream-properties (atom {})
21212158
;; Track per file class tx so that their tx isn't embedded in individual :block/tags and can be post processed
21222159
:classes-tx (atom [])
2160+
;; Track per file Status closed values required by imported legacy task markers.
2161+
:custom-status-tx (atom [])
21232162
:user-options
21242163
(merge user-options
21252164
{:tag-classes (set (map string/lower-case (:tag-classes user-options)))
@@ -2354,6 +2393,7 @@
23542393
classes-tx @(:classes-tx tx-options)
23552394
{:keys [retract-page-tags-tx] pages-tx'' :pages-tx} (clean-extra-invalid-tags @conn pages-tx' classes-tx existing-pages)
23562395
classes-tx' (concat classes-tx retract-page-tags-tx)
2396+
custom-status-tx @(:custom-status-tx tx-options)
23572397
;; Build indices
23582398
pages-index (->> (map #(select-keys % [:block/uuid]) pages-tx'')
23592399
(concat (map #(select-keys % [:block/uuid]) classes-tx))
@@ -2368,7 +2408,7 @@
23682408
blocks-index (set/union (set block-ids) (set block-refs-ids))
23692409
;; Order matters. pages-index and blocks-index needs to come before their corresponding tx for
23702410
;; uuids to be valid. Also upstream-properties-tx comes after blocks-tx to possibly override blocks
2371-
tx (concat pages-index page-properties-tx property-page-properties-tx pages-tx'' classes-tx' blocks-index blocks-tx)
2411+
tx (concat pages-index page-properties-tx property-page-properties-tx pages-tx'' classes-tx' custom-status-tx blocks-index blocks-tx)
23722412
tx' (common-util/fast-remove-nils tx)
23732413
;; _ (prn :tx-counts (map #(vector %1 (count %2))
23742414
;; [:pages-index :page-properties-tx :property-page-properties-tx :pages-tx' :classes-tx :blocks-index :blocks-tx]

deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
[logseq.db.frontend.asset :as db-asset]
1616
[logseq.db.frontend.content :as db-content]
1717
[logseq.db.frontend.malli-schema :as db-malli-schema]
18+
[logseq.db.frontend.property :as db-property]
1819
[logseq.db.frontend.rules :as rules]
1920
[logseq.db.frontend.validate :as db-validate]
2021
[logseq.db.test.helper :as db-test]
@@ -79,6 +80,24 @@
7980
ordered-children
8081
(mapv block-tree-with-properties)))
8182

83+
(defn- block-status
84+
[db content]
85+
(:logseq.property/status (db-test/find-block-by-content db content)))
86+
87+
(defn- status-content
88+
[db content]
89+
(db-property/closed-value-content (block-status db content)))
90+
91+
(defn- status-closed-value-contents
92+
[db]
93+
(set (map db-property/closed-value-content
94+
(db-property/get-closed-property-values db :logseq.property/status))))
95+
96+
(defn- status-closed-value-content-frequencies
97+
[db]
98+
(frequencies (map db-property/closed-value-content
99+
(db-property/get-closed-property-values db :logseq.property/status))))
100+
82101

83102
(defn- build-graph-files
84103
"Given a file graph directory, return all files including assets and adds relative paths
@@ -267,6 +286,59 @@
267286
(is (empty? (map :entity (:errors (db-validate/validate-local-db! @conn))))
268287
"Imported graph validates"))))
269288

289+
(deftest-async import-preserves-legacy-task-markers-as-status-choices
290+
(p/let [file (write-temp-graph-file
291+
"pages/tasks.md"
292+
"- TODO todo item\n- LATER later item\n- NOW now item\n- DOING doing item\n- WAIT waiting item\n- WAITING waiting full item\n- IN-PROGRESS in-progress item\n- DONE done item\n")
293+
conn (db-test/create-conn)
294+
_ (db-pipeline/add-listener conn)
295+
_ (import-files-to-db [file] conn {})]
296+
(is (= :logseq.property/status.todo
297+
(:db/ident (block-status @conn "todo item")))
298+
"TODO still imports to the built-in Todo status")
299+
(is (= :logseq.property/status.doing
300+
(:db/ident (block-status @conn "doing item")))
301+
"DOING still imports to the built-in Doing status")
302+
(is (= :logseq.property/status.done
303+
(:db/ident (block-status @conn "done item")))
304+
"DONE still imports to the built-in Done status")
305+
(is (= :logseq.property/status.todo
306+
(:db/ident (block-status @conn "later item")))
307+
"LATER imports to the built-in Todo status")
308+
(is (= :logseq.property/status.doing
309+
(:db/ident (block-status @conn "now item")))
310+
"NOW imports to the built-in Doing status")
311+
(is (= "WAIT" (status-content @conn "waiting item"))
312+
"WAIT imports as its own status choice")
313+
(is (= "WAITING" (status-content @conn "waiting full item"))
314+
"WAITING imports as its own status choice")
315+
(is (= "IN-PROGRESS" (status-content @conn "in-progress item"))
316+
"IN-PROGRESS imports as its own status choice")
317+
(is (set/subset? #{"WAIT" "WAITING" "IN-PROGRESS"}
318+
(status-closed-value-contents @conn))
319+
"Custom imported markers are added to Status closed values")
320+
(is (empty? (map :entity (:errors (db-validate/validate-local-db! @conn))))
321+
"Imported graph validates")))
322+
323+
(deftest-async import-custom-task-marker-across-multiple-files
324+
(p/let [first-file (write-temp-graph-file
325+
"pages/custom-status-a.md"
326+
"- WAITING first custom status item\n")
327+
second-file (write-temp-graph-file
328+
"pages/custom-status-b.md"
329+
"- WAITING second custom status item\n")
330+
conn (db-test/create-conn)
331+
_ (db-pipeline/add-listener conn)
332+
_ (import-files-to-db [first-file second-file] conn {})]
333+
(is (= "WAITING" (status-content @conn "first custom status item"))
334+
"Custom status marker imports from the first file")
335+
(is (= "WAITING" (status-content @conn "second custom status item"))
336+
"Custom status marker imports from the second file")
337+
(is (= 1 (get (status-closed-value-content-frequencies @conn) "WAITING"))
338+
"Custom status closed value is shared across imported files")
339+
(is (empty? (map :entity (:errors (db-validate/validate-local-db! @conn))))
340+
"Imported graph validates")))
341+
270342
(deftest update-asset-links-in-block-title
271343
(are [x y]
272344
(= y (@#'gp-exporter/update-asset-links-in-block-title (first x) {(second x) "UUID"} (atom {})))

0 commit comments

Comments
 (0)