π

Sharing Selected Org-Mode Data With Logseq Users

Show Sidebar

I do have a special use-case for sharing parts of my Org-mode content with my wife.

From my article Logseq from an Org-mode Point of View you might remember that my wife recently started with logseq because she doesn't want to use Org-mode instead. It's a shame but I respect that.

In my knowledge management, I do have many Org-mode heading sub-hierarchies that affect us both. Things related to household, recurring tasks around the house, financial stuff related to community offices, and so forth. It would be cool if I could privide (read-only) access to all those things within my wife's logseq.

The logseq graph of my wife is also synced (via Syncthing) to my machine for backup purposes.

Since Org-mode is the flexible beast here, I played around with Elisp a bit and was able to come up with a decent workflow I wanted to share with you.

Basic workflow

Your situation differs from mine. Therefore, you should take a look at the script and follow the minimal setup process:

From time to time, I manually update the exported files in logseq. Notice that this is overwriting any potential modification of the exported files that happened in logseq in the meanwhile.

Example List

The list of sub-hierarchies to export is a simple Org-mode syntax list with :ID: properties like I'm using to link headings. It also has a name so that the babel source code is able to find it. Here is an example list:

#+NAME: list-of-my-orgheadings-to-copy-to-logseq
- [[id:2020-10-27-contents-insurance][Insurance at COMPANY]]
- [[id:2015-10-27-waste][Waste management]]
- [[id:2016-03-25-canal-dues][Canal dues]]
- [[id:2024-06-02-companyX][Power company]]
- [[id:2021-03-10-windows][Yearly - greasing of metal fittings of windows]]
- [[id:2018-10-02-dishwasher][Check salt in dishwasher]]
- [[id:2014-02-05-washing-machine][Yearly - empty filter of the washing machine]]
- [[id:2018-11-30-fuse-check][Yearly - check fuses and test circuit breaker]]	  

The babel code below generates files like:

/home/user/share/logseq/pages/house___Insurance at COMPANY (autogenerated).md
/home/user/share/logseq/pages/house___Waste management (autogenerated).md
[...]	  

Design Decisions and Source Code

This is the code I came up with.

As you can see, I'm using a logseq hierarchy "house" as a name-space for the export files. Furthermore, I'm adding "(auto-generated)" to the file names. This should indicate my wife that any changes are overwritten with the next update of mine.

The name of the list above is provided as a parameter to the babel block. You don't see that line in the blog output, so I provide it here:

 #+BEGIN_SRC emacs-lisp :var mylistcontent=list-of-my-orgheadings-to-copy-to-logseq	  

I'm not sure if the list needs to be in the same file as the babel block in order to be able to locate it. In my case, they are both within the same heading.

You will notice that despite the fact that logseq supports Org-mode to some degree, I decided to generate Markdown files instead. This is the result of some export tests in the orgdown format which did not convince me. There are further things mentioned in my article that indicate that logseq will abandon orgdown in the future. Therefore, it's Markdown for this export. So far, it works in combination with the orgdown logseq files my wife writes.

Some things are not working. For example, orgdown ID links to headings not provided in the exported files are broken. Furthermore, links to files are broken. But that's fine with me, the important part to me was the direct information. If my wife needs to see a linked file, she can ask me or access it via our file share.

(defun my-export-to-logseq-using-id-and-filename (myid destinationfilename)
  "exports an existing unique ID sub-hierarchy (heading property)
   to a single logseq file."
  (org-id-goto myid);; jump to the heading
  (let ((mydestinationpath "/home/user/share/logseq/pages/") ;; path to the logseq pages including ending path separator (slash)
        (mydefaultfilenameprefix "house___") ;; this could be "foo___" when all files should be exported to the "foo" logseq hierarchy
        ;; (outputfilename (org-org-export-to-org nil t nil t));; export to Orgdown format instead
        (outputfilename (org-md-export-to-markdown nil t nil)) ;; export to Markdown format
        )
    (rename-file outputfilename (concat mydestinationpath mydefaultfilenameprefix
                                        destinationfilename " (auto-generated).md") t)
    ); let
  ); my-export-to-logseq

(defun my-export-org-list-with-descriptions-to-logseq-files (listcontent)
  "takes a list of org links, splits '[[id:foo][bar]]' into
   'foo' and 'bar' and invokes
   my-export-to-logseq-using-id-and-filename() with it"

    (dolist (element listcontent);; iterate over the list items of form "[[id:mylink][my description]]"
       (let* (
              (linkdescription (substring (car element) 5 -2));; get the string that contains link and description with separator
              (linkelements (split-string linkdescription "]\\["));; splits along the separator
              (mylink (car linkelements));; gets the link without "id:"
              (mydescription (nth 1 linkelements));; gets the description
              )
       (message (concat "Processing ID ⌜" mylink "⌟ with description ⌜" mydescription "⌟"))
       (my-export-to-logseq-using-id-and-filename mylink mydescription)
    );let
   );dolist
   (format-time-string "executed successfully on %Y-%m-%d %H:%M") ;; to leave a hint when executed the last time
)

(save-excursion ;; somehow, this doesn't work: after execution, the point is at the last heading to be exported
   (my-export-org-list-with-descriptions-to-logseq-files mylistcontent)
); save-excursion	  

If you do have any improvements, drop me a line in the comments below.

If you know why save-excursion (also within my-export-to-logseq-using-id-and-filename) isn't preserving my current point, let me know. I'm certain I'm doing it wrong somehow.


Related articles that link to this one:

Comment via email (persistent) or via Disqus (ephemeral) comments below: