emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [FYI] Programming with org-dp (by example)
@ 2018-03-04 23:44 Thorsten Jolitz
  2018-03-05  0:12 ` Thorsten Jolitz
  0 siblings, 1 reply; 2+ messages in thread
From: Thorsten Jolitz @ 2018-03-04 23:44 UTC (permalink / raw)
  To: emacs-orgmode


Hello List,
due to some interest in org-dp recently on this list, I actually took
the challenge of implementing a feature request by John Kitchin (without
actually sticking close to the specification, this is just a showcase
for org-dp).

Task: implement a radio-list with org checkboxes 
Extra feature: a read only radio-list that changes its values
conditional on the values of read/write radio-lists (when mapping the
buffer)

Example org buffer 

,----
| * org-mode radio-list test
| 
| #+NAME: temp
| #+ATTR_ORG: :radio
| - [ ] freezing
| - [ ] cold
| - [ ] normal
| - [X] warm
| - [ ] hot
| - [ ] tropical
| 
| #+NAME: wind
| #+ATTR_ORG: :radio
| - [X] breeze
| - [ ] storm
| - [ ] hurricane
| 
| #+LABEL: read-only
| #+NAME: take-a-walk
| #+ATTR_ORG: :radio
| - [ ] red
| - [ ] yellow
| - [X] green
`----

This is the main program.
The nice thing about this kind of declarative programming:
- parsing and interpretation is left to the org-element framework
- its very clear what we want/need to do: implement tj/radio-cont 

,----
| ;; rewire function
| (defun tj/radio-switch ()
|   "docstring"
|   (interactive)
|   (let ((stmp (time-stamp-string)))
|     (forward-line)
|     (org-dp-rewire 'plain-list 
| 		   'tj/radio-cont ;cont
| 		   t ;ins 
| 		   `(:attr_last_changed (,stmp)) ;aff 
| 		   nil ;elem 
| 		   )))
| 
| ;; mapping function
| (defun tj/radio-map ()
|   "docstring"
|   (interactive)
|   (let (temp wind)
|   (org-dp-map '(tj/radio-switch) tj/radio-rgxp)))
`----

tj/radio-switch () is the 'rewire' function to modify the plain
(checkbox) list at point.
It does two things:

1. call function 'tj/radio-cont to modifiy the content (= items) of the
plain list

2. add a new affiliated keyword with a current timestamp
`(:attr_last_changed (,stmp))

tj/radio-map () is the mapping function 

Here is the result of switching to more extreme weather conditions by
calling tj/radio-map and answering the user prompts two times:

,----
| #+NAME: temp
| #+ATTR_ORG: :radio
| #+ATTR_LAST_CHANGED: 2018-03-05 00:12:02 tj
| - [ ] freezing
| - [ ] cold
| - [ ] normal
| - [ ] warm
| - [ ] hot
| - [X] tropical
| 
| #+NAME: wind
| #+ATTR_ORG: :radio
| #+ATTR_LAST_CHANGED: 2018-03-05 00:12:05 tj
| - [ ] breeze
| - [ ] storm
| - [X] hurricane
| 
| #+NAME: take-a-walk
| #+ATTR_ORG: :radio
| #+ATTR_LAST_CHANGED: 2018-03-05 00:12:10 tj
| - [X] red
| - [ ] yellow
| - [ ] green
`----

Emacs Lisp is very good for working with text, but with regards to org
elements, whats needed for parsing and interpreting has already be
written by Nicolas, why not reuse it and work on a higher level.

So org-dp might be a light-weight alternative when acting locally on org
elements (no need for a full parse tree). It produces pretty clean code,
since working on text is replaced by working with lists.

PS 1

There was a bug in org-dp.el, I fixed it locally but cannot push to
origin due to github authentication problems (sight..), so if you want to
try the code here, you need to change this too:

,----
| (cont (let ((orig-elem-cont (org-dp-contents elem)))
|  (cond
|   ;; ((and (consp contents) (functionp contents))
|   ((and contents (functionp contents)) ...
`----

magit diff of same change:

,----
| 1 file changed, 2 insertions(+), 1 deletion(-)
| org-dp.el | 3 ++-
| 
| modified   org-dp.el
| @@ -709,7 +709,8 @@ (cl-defun org-dp-rewire (elem-type &optional contents replace affiliated element
|  	       (make-marker) (org-element-property :end elem)))
|  	 (cont (let ((orig-elem-cont (org-dp-contents elem)))
|  		 (cond
| -		  ((and (consp contents) (functionp contents))
| +		  ;; ((and (consp contents) (functionp contents))
| +		  ((and contents (functionp contents))
|  		   (apply contents (list orig-elem-cont elem)))
|  		  ((and contents (booleanp contents))
|  		   orig-elem-cont)
`----

PS 2 

here is the complete code:

#+BEGIN_SRC emacs-lisp  
(defconst tj/radio-rgxp "^#\\+attr_org:[[:space:]]+:radio")
(defconst tj/radio-temp "temp")
(defconst tj/radio-wind "wind")

(defvar tj/radio-rw '("temp" "wind")) ;read/write
(defvar tj/radio-r '("take-a-walk")) ;read only

(defvar tj/radio-temp-red '("freezing" "tropical"))
(defvar tj/radio-temp-yellow '("cold" "hot"))
(defvar tj/radio-temp-green '("normal" "warm"))

(defvar tj/radio-wind-red '("breeze"))
(defvar tj/radio-wind-yellow '("storm"))
(defvar tj/radio-wind-green '("hurricane"))

;; rewire function
(defun tj/radio-switch ()
  "docstring"
  (interactive)
  (let ((stmp (time-stamp-string)))
    (forward-line)
    (org-dp-rewire 'plain-list
		   'tj/radio-cont ;cont
		   t ;ins 
		   `(:attr_last_changed (,stmp)) ;aff 
		   nil ;elem 
		   )))

;; mapping function
(defun tj/radio-map ()
  "docstring"
  (interactive)
  (let (temp wind)
  (org-dp-map '(tj/radio-switch) tj/radio-rgxp)))



;; HELPER FUNCTIONS
;; helper function to actually modify the content
(defun tj/radio-cont (cont elem)
  "docstring"
  (let ((name (org-element-property :name elem))
	(prompt-options (tj/radio-get-itm-labels cont))
	(new-cont)
	(users-choice))
    (cond
     ((member name tj/radio-rw) ;prompt user for value
      (progn
	(setq users-choice (ido-completing-read name prompt-options))
	(set (intern name) users-choice)
        (setq new-cont (mapcar 'tj/radio-itm-rw cont))))
     ((member name tj/radio-r) ;set value
      (setq new-cont
	    (mapcar 'tj/radio-itm-r cont)))
     (t)) ;do nothing
    (or new-cont cont)))


(defun tj/radio-get-itm-labels (cont)
  "docstring"
  (mapcar #'(lambda (itm)
	      (string-remove-suffix
	       "\n"
	       (org-dp-contents itm t t)))
	   cont))

(defun tj/radio-itm-rw (itm)
  "docstring"
  (let ((label (string-remove-suffix
		"\n" (org-dp-contents itm t t))))
    (if (string-equal label users-choice)
	(org-element-put-property itm :checkbox 'on)
	(org-element-put-property itm :checkbox 'off))
    itm))

(defun tj/radio-itm-r (itm)
  "docstring"
  (let ((label (string-remove-suffix
		"\n" (org-dp-contents itm t t))))

    (org-element-put-property itm :checkbox 'off)

    (cond
     ((and (string-equal label "red")
	   (or (member temp tj/radio-temp-red)
	       (member temp tj/radio-wind-red)))
      (org-element-put-property itm :checkbox 'on))

     ((and (string-equal label "yellow")
	   (or (member temp tj/radio-temp-yellow)
	       (member temp tj/radio-wind-yellow)))
      (org-element-put-property itm :checkbox 'on))

     ((and (string-equal label "green")
	   (or (member temp tj/radio-temp-green)
	       (member temp tj/radio-wind-green)))
      (org-element-put-property itm :checkbox 'on)))
    itm))
#+END_SRC

-- 
cheers,
Thorsten

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [FYI] Programming with org-dp (by example)
  2018-03-04 23:44 [FYI] Programming with org-dp (by example) Thorsten Jolitz
@ 2018-03-05  0:12 ` Thorsten Jolitz
  0 siblings, 0 replies; 2+ messages in thread
From: Thorsten Jolitz @ 2018-03-05  0:12 UTC (permalink / raw)
  To: emacs-orgmode

Thorsten Jolitz <tjolitz@gmail.com> writes:

PS
Ups ... a few little bugs in the code, here is version 2

#+BEGIN_SRC emacs-lisp  
(defconst tj/radio-rgxp "^#\\+attr_org:[[:space:]]+:radio")
(defconst tj/radio-temp "temp")
(defconst tj/radio-wind "wind")

(defvar tj/radio-rw '("temp" "wind")) ;read/write
(defvar tj/radio-r '("take-a-walk")) ;read only

(defvar tj/radio-temp-red '("freezing" "tropical"))
(defvar tj/radio-temp-yellow '("cold" "hot"))
(defvar tj/radio-temp-green '("normal" "warm"))

(defvar tj/radio-wind-red '("hurricane"))
(defvar tj/radio-wind-yellow '("storm"))
(defvar tj/radio-wind-green '("breeze"))

;; rewire function
(defun tj/radio-switch ()
  "docstring"
  (interactive)
  (let ((stmp (time-stamp-string)))
    (forward-line)
    (org-dp-rewire 'plain-list
		   'tj/radio-cont ;cont
		   t ;ins 
		   `(:attr_last_changed (,stmp)) ;aff 
		   nil ;elem 
		   )))

;; mapping function
(defun tj/radio-map ()
  "docstring"
  (interactive)
  (let (temp wind)
  (org-dp-map '(tj/radio-switch) tj/radio-rgxp)))



;; HELPER FUNCTIONS
;; helper function to actually modify the content
(defun tj/radio-cont (cont elem)
  "docstring"
  (let ((name (org-element-property :name elem))
	(prompt-options (tj/radio-get-itm-labels cont))
	(new-cont)
	(users-choice)
	(box-checked-p))
    (cond
     ((member name tj/radio-rw) ;prompt user for value
      (progn
	(setq users-choice (ido-completing-read name prompt-options))
	(set (intern name) users-choice)
        (setq new-cont (mapcar 'tj/radio-itm-rw cont))))
     ((member name tj/radio-r) ;set value
      (setq new-cont
	    (mapcar 'tj/radio-itm-r cont)))
     (t)) ;do nothing
    (or new-cont cont)))


(defun tj/radio-get-itm-labels (cont)
  "docstring"
  (mapcar #'(lambda (itm)
	      (string-remove-suffix
	       "\n"
	       (org-dp-contents itm t t)))
	   cont))

(defun tj/radio-itm-rw (itm)
  "docstring"
  (let ((label (string-remove-suffix
		"\n" (org-dp-contents itm t t))))
    (if (string-equal label users-choice)
	(org-element-put-property itm :checkbox 'on)
	(org-element-put-property itm :checkbox 'off))
    itm))

(defun tj/radio-itm-r (itm)
  "docstring"
  (let ((label (string-remove-suffix
		"\n" (org-dp-contents itm t t))))

    (org-element-put-property itm :checkbox 'off)

    (cond
     ((and (string-equal label "red")
	   (not box-checked-p)
	   (or (member temp tj/radio-temp-red)
	       (member wind tj/radio-wind-red)))
      (org-element-put-property itm :checkbox 'on)
      (setq box-checked-p t))

     ((and (string-equal label "yellow")
	   (not box-checked-p)
	   (or (member temp tj/radio-temp-yellow)
	       (member wind tj/radio-wind-yellow)))
      (org-element-put-property itm :checkbox 'on)
      (setq box-checked-p t))

     ((and (string-equal label "green")
	   (not box-checked-p)
	   (or (member temp tj/radio-temp-green)
	       (member wind tj/radio-wind-green)))
      (org-element-put-property itm :checkbox 'on)
      (setq box-checked-p t)))
    itm))
#+END_SRC


> Hello List,
> due to some interest in org-dp recently on this list, I actually took
> the challenge of implementing a feature request by John Kitchin (without
> actually sticking close to the specification, this is just a showcase
> for org-dp).
>
> Task: implement a radio-list with org checkboxes 
> Extra feature: a read only radio-list that changes its values
> conditional on the values of read/write radio-lists (when mapping the
> buffer)

-- 
cheers,
Thorsten

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2018-03-05  0:13 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-04 23:44 [FYI] Programming with org-dp (by example) Thorsten Jolitz
2018-03-05  0:12 ` Thorsten Jolitz

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).