0

I have the following org-checkbox.

Project 1
    [x] module 1
    [x] module 2
    [] module 3
    [x] module 4

How do i extract the checked values of Project 1 and pass the result to the code below?

#+BEGIN_SRC elisp
    (json-pretty-print module-list)
#+END_SRC
3
  • emacs.stackexchange.com/tags/elisp/info
    – Drew
    Commented Sep 25, 2022 at 17:06
  • What "code below"? json_print is not known to my emacs (there is a json-pretty-print from json.el but no json_print) and the rest is eitherr pseudocode or a syntax error. Why don't you describe what output you expect and leave it at that? Also specify where the output is to go: in the Org mode buffer, some other buffer, to a file? While you are at it, you should also fix the Org mode file to make it legal, Are you expecting the source block to be in the same file always, or are you interested in a more general function? If the latter, how is the input list specified?
    – NickD
    Commented Sep 26, 2022 at 1:21
  • You need to specify how the json output looks like; if you can't do that then you need to think more about your use case. (Where are you going to copy paste the json code; how does it look like; do they look like import module etc) Think about it. The example I have added show that the org mode checklist are pretty compex (that is, they could be nested), so your assumption that it is a simple checklist is a bit of an approximation. You need to think more about how the output looks like. My guess is you don't need json; are you importing in to haskell or js or python.
    – user31220
    Commented Sep 26, 2022 at 8:03

4 Answers 4

1

This response has to be read in conjuction with https://emacs.stackexchange.com/a/73824/31220

  1. Copy the below snippet to todo.json
  2. Put your cursor on #+begin_src emacs-lisp ... and do C-c C-c.
  3. Ensure that the resulting json src_block reflect the state of the checklist
** TODO Organize party [2/4]
:PROPERTIES:
:ID:       checklist
:END:
  - [-] call people [1/3]
    - [ ] Peter
    - [X] Sarah
    - [ ] Sam
  - [X] order food
  - [ ] think about what music to play
  - [X] talk to the neighbors


#+name: checklist-json
#+begin_src emacs-lisp  :result value verbatim :wrap src json
  (org-tree-to-json (org-element-map (org-element-parse-buffer) 'headline
              (lambda (headline)
            (when (string= (org-element-property :ID
                                 headline)
                       "checklist")
              headline))
              nil
              t))

#+end_src

#+RESULTS: checklist-json
#+begin_src json
[
  "org-data",
  null,
  [
    "headline",
    {
      "raw-value": "Organize party [2/4]",
      "level": 1,
      "todo-keyword": "TODO",
      "todo-type": "todo",
      "title": [
        "Organize party ",
        [
          "statistics-cookie",
          {
            "value": "[2/4]"
          }
        ]
      ]
    },
    [
      "section",
      null,
      [
        "plain-list",
        {
          "type": "unordered"
        },
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "trans"
          },
          [
            "paragraph",
            null,
            "call people ",
            [
              "statistics-cookie",
              {
                "value": "[1/3]"
              }
            ],
            "\n"
          ],
          [
            "plain-list",
            {
              "type": "unordered"
            },
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "off"
              },
              [
                "paragraph",
                null,
                "Peter\n"
              ]
            ],
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "on"
              },
              [
                "paragraph",
                null,
                "Sarah\n"
              ]
            ],
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "off"
              },
              [
                "paragraph",
                null,
                "Sam\n"
              ]
            ]
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "on"
          },
          [
            "paragraph",
            null,
            "order food\n"
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "off"
          },
          [
            "paragraph",
            null,
            "think about what music to play\n"
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "on"
          },
          [
            "paragraph",
            null,
            "talk to the neighbors\n"
          ]
        ]
      ]
    ]
  ]
]
#+end_src

#+RESULTS:
#+begin_src json
[
  "org-data",
  null,
  [
    "headline",
    {
      "raw-value": "Organize party [2/4]",
      "level": 1,
      "todo-keyword": "TODO",
      "todo-type": "todo",
      "title": [
        "Organize party ",
        [
          "statistics-cookie",
          {
            "value": "[2/4]"
          }
        ]
      ]
    },
    [
      "section",
      null,
      [
        "plain-list",
        {
          "type": "unordered"
        },
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "trans"
          },
          [
            "paragraph",
            null,
            "call people ",
            [
              "statistics-cookie",
              {
                "value": "[1/3]"
              }
            ],
            "\n"
          ],
          [
            "plain-list",
            {
              "type": "unordered"
            },
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "off"
              },
              [
                "paragraph",
                null,
                "Peter\n"
              ]
            ],
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "on"
              },
              [
                "paragraph",
                null,
                "Sarah\n"
              ]
            ],
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "off"
              },
              [
                "paragraph",
                null,
                "Sam\n"
              ]
            ]
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "on"
          },
          [
            "paragraph",
            null,
            "order food\n"
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "off"
          },
          [
            "paragraph",
            null,
            "think about what music to play\n"
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "on"
          },
          [
            "paragraph",
            null,
            "talk to the neighbors\n"
          ]
        ]
      ]
    ]
  ]
]
#+end_src



1

Are you new to org-mode?

Project 1
    [x] module 1
    [x] module 2
    [] module 3
    [x] module 4

This is not at all a checklist ...

You need to take sometime to read the manual, and the habit of reading the instructions manual--be it emacs or an an ikea item--is going to serve you for your lifetime.

That said I don't want you to feel discouraged, so read on ...

This is what you are trying to do, and having difficulty in articulating it. Put your cursor in the babel block, and do C-c C-c. You will get a bunch of import statements which you insert via noweb reference at the top of the library you are trying to build.

#+name: module-list
- [X] module 1
- [X] module 2
- [] module 3
- [X] module 4

#+name: build-imports
#+begin_src emacs-lisp  :results value :wrap src js
  (let* ((data (org-element-parse-buffer))
         (plain-list
          (org-element-map
              data
              'plain-list
            (lambda (plain-list)
              (when (string= (org-element-property :name
                                                   plain-list)
                             "module-list")
                plain-list))
            nil
            t)))
    (apply #'concat
           (org-element-map
               plain-list
               'item
             (lambda (item)
               (when (memq (org-element-property :checkbox item) '(on))
                 (format "import \"%s\";\n"
                         (org-element-map item 'paragraph
                           (lambda (paragraph)
                             (let ((s (car (org-element-contents paragraph))))
                               (when s
                                 (replace-regexp-in-string "\n+" " " (substring-no-properties s) t t))))
                           nil
                           t))))
             nil nil t)))

#+end_src

#+RESULTS: build-imports
#+begin_src js
import "module 1 ";
import "module 2 ";
import "module 4 ";
#+end_src

1

I am assuming that you aren't comfortable with elisp, but can work with javascript.

The snippet below gets you from elisp to json.

How you manipulate that resulting structure to a form that fits your needs, is left as an exercise to you.


  1. Copy the following snippet to todo.org
* TODO Organize party [2/4]
  - [-] call people [1/3]
    - [ ] Peter
    - [X] Sarah
    - [ ] Sam
  - [X] order food
  - [ ] think about what music to play
  - [X] talk to the neighbors
  1. Copy the following snippet to *scratch* and do M-x eval-buffer
(defun simplify-org-data (tree)
  (cond
   ((stringp tree)
    (substring-no-properties tree))
   ((and (consp tree)
         (not (symbolp (car tree))))
    (mapcar #'simplify-org-data tree))
   ((and (consp tree)
         (symbolp (car tree)))
    (append
     (list (nth 0 tree)
           (cl-loop for (a b . rest) on (nth 1 tree) by #'cddr
                    if (or
                        (memq a '(:contents-begin
                                  :contents-end
                                  :parent
                                  :begin
                                  :end
                                  :pre-blank
                                  :post-blank
                                  :post-affiliated
                                  :structure
                                  ))
                        (null b))
                    do (ignore)
                    else
                    append (list a (simplify-org-data b))))
     (simplify-org-data (org-element-contents tree))))
   (t tree)))

(defun org-tree-to-json (&optional in-data)
  (interactive)
  (let* ((tree (or in-data (org-element-parse-buffer)))
         (json-file (format "%s.json" (file-name-sans-extension (buffer-file-name))))
         (simplified-data (simplify-org-data tree))
         (json-string (with-temp-buffer
                        (save-excursion
                          (insert (json-encode (simplify-org-data tree))))
                        (json-pretty-print-buffer)
                        (buffer-string))))
    (unless in-data
      (write-region json-string nil json-file nil nil nil t))
    json-string))  

  1. C-x C-f todo.org, and do M-x org-tree-to-json.
  2. Above command would have written todo.json.
  3. C-x C-f todo.json, and this what you will see
[
  "org-data",
  null,
  [
    "headline",
    {
      "raw-value": "Organize party [2/4]",
      "level": 1,
      "todo-keyword": "TODO",
      "todo-type": "todo",
      "title": [
        "Organize party ",
        [
          "statistics-cookie",
          {
            "value": "[2/4]"
          }
        ]
      ]
    },
    [
      "section",
      null,
      [
        "plain-list",
        {
          "type": "unordered"
        },
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "trans"
          },
          [
            "paragraph",
            null,
            "call people ",
            [
              "statistics-cookie",
              {
                "value": "[1/3]"
              }
            ],
            "\n"
          ],
          [
            "plain-list",
            {
              "type": "unordered"
            },
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "off"
              },
              [
                "paragraph",
                null,
                "Peter\n"
              ]
            ],
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "on"
              },
              [
                "paragraph",
                null,
                "Sarah\n"
              ]
            ],
            [
              "item",
              {
                "bullet": "- ",
                "checkbox": "off"
              },
              [
                "paragraph",
                null,
                "Sam\n"
              ]
            ]
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "on"
          },
          [
            "paragraph",
            null,
            "order food\n"
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "off"
          },
          [
            "paragraph",
            null,
            "think about what music to play\n"
          ]
        ],
        [
          "item",
          {
            "bullet": "- ",
            "checkbox": "on"
          },
          [
            "paragraph",
            null,
            "talk to the neighbors\n"
          ]
        ]
      ]
    ]
  ]
]

FWIW,

  1. org-element-parse-buffer on todo.org looks like this
#2=(org-data
    nil
    #1=(headline
        (
         :raw-value "Organize party [2/4]"
         :begin 1
         :end 194
         :pre-blank 0
         :contents-begin 29
         :contents-end 189
         :level 1
         :priority nil
         :tags nil
         :todo-keyword
         #("TODO" 0 4
           (face
            (org-todo org-level-1)
            fontified t))

         :todo-type todo
         :post-blank 3
         :footnote-section-p nil
         :archivedp nil
         :commentedp nil
         :post-affiliated 1
         :title
         (#("Organize party " 0 15
            (
             :parent #1#))
          (statistics-cookie
           (
            :begin 23
            :end 28
            :value "[2/4]"
            :post-blank 0
            :parent #1#)))

         :parent #2#)
        #3=(section
            (
             :begin 29
             :end 194
             :contents-begin 29
             :contents-end 189
             :post-blank 3
             :post-affiliated 29
             :parent #1#)
            #5=(plain-list
                (
                 :type unordered
                 :begin 29
                 :end 189
                 :contents-begin 29
                 :contents-end 189
                 :structure #4=((29 2 "- " nil "[-]" nil 101)
                                (55 4 "- " nil "[ ]" nil 71)
                                (71 4 "- " nil "[X]" nil 87)
                                (87 4 "- " nil "[ ]" nil 101)
                                (101 2 "- " nil "[X]" nil 120)
                                (120 2 "- " nil "[ ]" nil 159)
                                (159 2 "- " nil "[X]" nil 189))

                 :post-blank 0
                 :post-affiliated 29
                 :parent #3#)
                #6=(item
                    (
                     :bullet "- "
                     :begin 29
                     :end 101
                     :contents-begin 37
                     :contents-end 101
                     :checkbox trans
                     :counter nil
                     :structure #4#
                     :pre-blank 0
                     :post-blank 0
                     :post-affiliated 29
                     :tag nil
                     :parent #5#)
                    #7=(paragraph
                        (
                         :begin 37
                         :end 55
                         :contents-begin 37
                         :contents-end 55
                         :post-blank 0
                         :post-affiliated 37
                         :parent #6#)
                        #("call people " 0 12
                          (
                           :parent #7#))
                        (statistics-cookie
                         (
                          :begin 49
                          :end 54
                          :value "[1/3]"
                          :post-blank 0
                          :parent #7#))
                        #("\n" 0 1
                          (
                           :parent #7#)))
                    #8=(plain-list
                        (
                         :type unordered
                         :begin 55
                         :end 101
                         :contents-begin 55
                         :contents-end 101
                         :structure #4#
                         :post-blank 0
                         :post-affiliated 55
                         :parent #6#)
                        #9=(item
                            (
                             :bullet "- "
                             :begin 55
                             :end 71
                             :contents-begin 65
                             :contents-end 71
                             :checkbox off
                             :counter nil
                             :structure #4#
                             :pre-blank 0
                             :post-blank 0
                             :post-affiliated 55
                             :tag nil
                             :parent #8#)
                            #10=(paragraph
                                 (
                                  :begin 65
                                  :end 71
                                  :contents-begin 65
                                  :contents-end 71
                                  :post-blank 0
                                  :post-affiliated 65
                                  :parent #9#)
                                 #("Peter\n" 0 6
                                   (
                                    :parent #10#))))
                        #11=(item
                             (
                              :bullet "- "
                              :begin 71
                              :end 87
                              :contents-begin 81
                              :contents-end 87
                              :checkbox on
                              :counter nil
                              :structure #4#
                              :pre-blank 0
                              :post-blank 0
                              :post-affiliated 71
                              :tag nil
                              :parent #8#)
                             #12=(paragraph
                                  (
                                   :begin 81
                                   :end 87
                                   :contents-begin 81
                                   :contents-end 87
                                   :post-blank 0
                                   :post-affiliated 81
                                   :parent #11#)
                                  #("Sarah\n" 0 6
                                    (
                                     :parent #12#))))
                        #13=(item
                             (
                              :bullet "- "
                              :begin 87
                              :end 101
                              :contents-begin 97
                              :contents-end 101
                              :checkbox off
                              :counter nil
                              :structure #4#
                              :pre-blank 0
                              :post-blank 0
                              :post-affiliated 87
                              :tag nil
                              :parent #8#)
                             #14=(paragraph
                                  (
                                   :begin 97
                                   :end 101
                                   :contents-begin 97
                                   :contents-end 101
                                   :post-blank 0
                                   :post-affiliated 97
                                   :parent #13#)
                                  #("Sam\n" 0 4
                                    (
                                     :parent #14#))))))
                #15=(item
                     (
                      :bullet "- "
                      :begin 101
                      :end 120
                      :contents-begin 109
                      :contents-end 120
                      :checkbox on
                      :counter nil
                      :structure #4#
                      :pre-blank 0
                      :post-blank 0
                      :post-affiliated 101
                      :tag nil
                      :parent #5#)
                     #16=(paragraph
                          (
                           :begin 109
                           :end 120
                           :contents-begin 109
                           :contents-end 120
                           :post-blank 0
                           :post-affiliated 109
                           :parent #15#)
                          #("order food\n" 0 11
                            (
                             :parent #16#))))
                #17=(item
                     (
                      :bullet "- "
                      :begin 120
                      :end 159
                      :contents-begin 128
                      :contents-end 159
                      :checkbox off
                      :counter nil
                      :structure #4#
                      :pre-blank 0
                      :post-blank 0
                      :post-affiliated 120
                      :tag nil
                      :parent #5#)
                     #18=(paragraph
                          (
                           :begin 128
                           :end 159
                           :contents-begin 128
                           :contents-end 159
                           :post-blank 0
                           :post-affiliated 128
                           :parent #17#)
                          #("think about what music to play\n" 0 31
                            (
                             :parent #18#))))
                #19=(item
                     (
                      :bullet "- "
                      :begin 159
                      :end 189
                      :contents-begin 167
                      :contents-end 189
                      :checkbox on
                      :counter nil
                      :structure #4#
                      :pre-blank 0
                      :post-blank 0
                      :post-affiliated 159
                      :tag nil
                      :parent #5#)
                     #20=(paragraph
                          (
                           :begin 167
                           :end 189
                           :contents-begin 167
                           :contents-end 189
                           :post-blank 0
                           :post-affiliated 167
                           :parent #19#)
                          #("talk to the neighbors\n" 0 22
                            (
                             :parent #20#))))))))
  1. (simplify-org-data (org-element-parse-buffer)) looks as below. IOW, it essentially removes all the "crap" from org-element-parse-buffer.
(org-data
 nil
 (headline
  (
   :raw-value "Organize party [2/4]"
   :level 1
   :todo-keyword "TODO"
   :todo-type todo
   :title
   ("Organize party "
    (statistics-cookie
     (
      :value "[2/4]"))))
  (section nil
           (plain-list
            (
             :type unordered)
            (item
             (
              :bullet "- "
              :checkbox trans)
             (paragraph nil "call people "
                        (statistics-cookie
                         (
                          :value "[1/3]"))
                        "\n")
             (plain-list
              (
               :type unordered)
              (item
               (
                :bullet "- "
                :checkbox off)
               (paragraph nil "Peter\n"))
              (item
               (
                :bullet "- "
                :checkbox on)
               (paragraph nil "Sarah\n"))
              (item
               (
                :bullet "- "
                :checkbox off)
               (paragraph nil "Sam\n"))))
            (item
             (
              :bullet "- "
              :checkbox on)
             (paragraph nil "order food\n"))
            (item
             (
              :bullet "- "
              :checkbox off)
             (paragraph nil "think about what music to play\n"))
            (item
             (
              :bullet "- "
              :checkbox on)
             (paragraph nil "talk to the neighbors\n"))))))

5
  • The first tree should be in-data in org-tree-to-json. The second tree should be simplified-data - maybe? Where does -transform come from? I guessed from dash.el but it doesn't seem to be defined there, at least in the version I have.
    – NickD
    Commented Sep 26, 2022 at 11:58
  • Thanks. I was trying to use -tree-map or -tree-map-nodes and found it unsatisfactory. That is where the back history of -transform . -transform synonymous with simplify-org-data. The data (or the parse tree) gets simplified in two senses (a) The buffer positions of parsing tokens are stripped; the properties that are null are stripped (b) The :parent property is removed. (With the :parent property lurking around, the lisp structure will be cyclic, and you will run in to issues with json-encode. Try again. I will be happy to correct any further problems.
    – user31220
    Commented Sep 26, 2022 at 12:34
  • I have fixed the snippet now. Try again. If it works, says so. The snippet I have shared goes miles beyond the scope of the original question (obviously), and the snippet could be useful for people who want to work with js-data (as opposed to lisp data).
    – user31220
    Commented Sep 26, 2022 at 12:35
  • To add to my earlier comment, one can flatten the :parent references to an "id" by using org-export-get-reference. In other words, there is no need to strip :parent; it can be replaced with org ids. This way one get richer json data with :parent (but without the buffer positions). (The prefix org-export in org-export-get-reference is a hint that it is meant for use within an export context; IME, setting info to nil in org-export-* APIs usually "works")
    – user31220
    Commented Sep 26, 2022 at 12:59
  • I can run the snippet without errors now: thanks! One more nit: simplified-data is not used any longer, so you can get rid of one of the calls to simplify-org-data in org-tree-to-json.
    – NickD
    Commented Sep 26, 2022 at 13:45
1

Here's a more general solution for getting checked box values from NAME.

(defun get-checkbox (name)
  (let (items)
    (org-element-map
        (org-element-parse-buffer) 'plain-list
      (lambda (plain-list)
        (when (string= name (org-element-property :name plain-list))
          (dolist (i (org-element-property :structure plain-list))
            (let ((position (car i))
                  (value (nth 4 i)))
              (save-excursion
                (when (string= value "[X]")
                  (goto-char position)
                  (push (substring (string-trim (thing-at-point 'line t)) 6) items))))))))
    (nreverse items)))
#+name: module-list
- Project 1
  - [X] module 1
  - [X] module 2
  - [ ] module 3
  - [X] module 4

#+begin_src bash :results output :var modules=(get-checkbox "module-list")
echo Modules include:
echo "${modules[*]}"
#+end_src

#+results:
: Modules include:
: module 1 module 2 module 4

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.