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:
- Initial setup process:
- Adapt
mydestinationpath
in the script - Adapt
mydefaultfilenameprefix
in the script - Optionally: adapt the export function (+ file name extension) for using a different format
- Adapt
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.
- Periodically execute workflow to update data:
- Check list of headings: add new, remove outdated, …
- Link target are
:ID:
properties of sub-headings within my agenda files - Link descriptions are the target file names
- Be aware to avoid characters that Microsoft doesn't want to see!
- Link target are
- Execute Elisp babel block from below
- Runs approximately 35 seconds for 19 sub-hierarchies
- If error:
- Maybe disable a few
src_python{return()}
likeXsrc_python{return()}
: somehow, some of them produce errors - (Collect more errors)
- Maybe disable a few
- Manually change back to this file (
save-excursion
somehow doesn't work as expected) - Check result in file:/home/user/share/logseq/pages/ and logseq
- Notice that items that are removed or renamed still persists in the logseq path as long as you don't delete them.
- Check list of headings: add new, remove outdated, …
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.