gnu-application.org: Remove email addresses
[worg.git] / org-tutorials / org-import-rtm.org
blobf259ebfb42b0fd273598eca8b5447d4b352609af
1 #+OPTIONS:    H:3 num:nil toc:t \n:nil ::t |:t ^:t -:t f:t *:t tex:t d:(HIDE) tags:not-in-toc
2 #+STARTUP:    align fold nodlcheck hidestars oddeven lognotestate
3 #+SEQ_TODO:   TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
4 #+TAGS:       Write(w) Update(u) Fix(f) Check(c)
5 #+TITLE:      Import items from remember the milk
6 #+AUTHOR:     Arun Persaud
7 #+EMAIL:      arun@nubati.net
8 #+LANGUAGE:   en
9 #+PRIORITIES: A C B
10 #+CATEGORY:   worg
12 * Overview
13 This page will show you how to import items from [[http://www.rememberthemilk.com][remember the milk]](RTM) into org mode (one way sync).
14 The script will parse the atom feed from RTM adding more information such as scheduled date, location, tags and priority.
16 * Setup
18 The script uses the built-in feed parser from org-mode. Since the feed parser is very unspecific, we add a function that parses each RTM entry
19 and scans it for some org-mode relevant information such as the scheduled date, location, tags, etc. You will need to add these scripts to your .emacs file
20 and modify them to fit your needs (you need to add your RTM-atom feed url at least).
22 #+begin_src emacs-lisp
23 (defun org-feed-parse-RTM-entry (entry)
24   "Parse the `:item-full-text' as a sexp and create new properties."
25   (let ((xml (car (read-from-string (plist-get entry :item-full-text)))))
26     ;; Get first <link href='foo'/>.
27     (setq entry (plist-put entry :link
28                            (xml-get-attribute
29                             (car (xml-get-children xml 'link))
30                             'href)))
31     ;; Add <title/> as :title.
32     (setq entry (plist-put entry :title
33                            (xml-substitute-special
34                             (car (xml-node-children
35                                   (car (xml-get-children xml 'title)))))))
36     ;; look for some other information that's in the content of the entry
37     ;; the structure looks something like:
38     ;; <content><div>   <div item> <span itemname></span><span itemvalue></span></div>...
39     (let* ((content (car (xml-get-children xml 'content)))
40            (main  (car (xml-get-children content 'div)))
41            (items (xml-get-children main 'div)))
42       (when items
43         ; iterate over all items and check for certain classes
44         (while items
45           (setq item (car items))
46           ; get the second span entry
47           (setq valuesub (car (cdr (xml-node-children item))))
48              (cond
49               ((string= (xml-get-attribute item 'class) "rtm_due")
50                (setq entry (plist-put entry :due (car (xml-node-children valuesub))))
51                (setq mydate (car (xml-node-children valuesub)))
52                ;; Any time will be stripped
53                ;; Entries will be only schedued to a date
54                (if (string= mydate "never")
55                    nil
56                    ;; entries could be scheduled to a date "Tue 4 Aug 15" 
57                    ;; or to a date/time "Tue 4 Aug 15 at 10:00AM"
58                    (if (string-match "^\\([a-zA-Z]*\\) \\([0-9]*\\) \\([a-zA-Z]*\\) \\([0-9]*\\) at \\([0-9:]*\\)" mydate)
59                        (setq mydate (concat "20" (match-string 4 mydate) " " (match-string 3 mydate) " " (match-string 2 mydate) " " (match-string 5 mydate) ":01"))
60                      (progn
61                        (string-match "^\\([a-zA-Z]*\\) \\([0-9]*\\) \\([a-zA-Z]*\\) \\([0-9]*\\)$" mydate)
62                        (setq mydate (concat "20" (match-string 4 mydate) " " (match-string 3 mydate) " " (match-string 2 mydate) " 00:00:01"))))
63                  (progn
64                   (setq mydate (parse-time-string mydate))
65                   (setq mydate (apply #'encode-time mydate))
66                   (setq mydate (format-time-string (car org-time-stamp-formats) mydate))
67                   (setq entry (plist-put entry :dueorgformat mydate)))))
68               ((string= (xml-get-attribute item 'class) "rtm_tags")
69                (setq entry (plist-put entry :tags (car (xml-node-children valuesub)))))
70               ((string= (xml-get-attribute item 'class) "rtm_time_estimate")
71                (setq entry (plist-put entry :timeestimate (car (xml-node-children valuesub)))))
72               ((string= (xml-get-attribute item 'class) "rtm_priority")
73                (setq entry (plist-put entry :priority (car (xml-node-children valuesub)))))
74               ((string= (xml-get-attribute item 'class) "rtm_location")
75                (setq entry (plist-put entry :location (car (xml-node-children valuesub))))))
76           (setq items (cdr items))
77           )))
78     entry))
79 #+end_src
81 It's also easy to set up special filters for the RTM entries. Here, we for example set up a filter that skips all entries that don't have a due-date set in RTM.
83 #+begin_src emacs-lisp
84 (defun org-feed-RTM-filter-non-scheduled (entry)
85   "filter out all entries that don't have a due date set"
86   (if (string= (plist-get entry :due) "never")
87       nil
88     entry))
89 #+end_src
91 Next we need to tell org-mode where to find the feed, for this you need to copy the url location for your RTM atom-feed. The code below will
92 write the RTM information into a file called "~/org/RTM.org". If you want to change the file name, just edit the 4th line (you will also need to edit the file name in the scriptlet below). Furthermore, we define
93 here the functions we defined above to filter and parse the RTM entries. We also define a template that will be used in the org-file. Here, we use
94 the newly defined items for the due date and location, etc.
96 #+begin_src emacs-lisp
97 (setq org-feed-alist
98       '(("Remember The Milk"
99          "https://www.rememberthemilk.com/atom/<add url for RTM atom feed here>"
100          "~/org/RTM.org"
101          "Remember The Milk"
102          :parse-feed org-feed-parse-atom-feed
103          :parse-entry org-feed-parse-RTM-entry
104          :template "* TODO %title\n SCHEDULED:%dueorgformat\n Due: %due\n Location: %location\n Priority:%priority\n Tags:%tags\n %a\n "
105          :filter org-feed-RTM-filter-non-scheduled
106          )))
107 #+end_src
109 Finally, we need to tell emacs to run this automatically, for example, every hour. For this, we delete the old file and buffer and then recreate it. If you wanted to change the file name
110 for the RTM file you need to edit this too.
112 #+begin_src emacs-lisp
113 (defun myupdate-RTM ()
114   "deletes old RTM.org and generates a new one"
115   (save-current-buffer
116     (if (not (eq nil (get-buffer "RTM.org")))
117         (progn
118          (kill-buffer "RTM.org")
119          (delete-file "~/org/RTM.org")))
120     (set-buffer (get-buffer-create "RTM.org"))
121     (write-file "~/org/RTM.org")
122     (org-feed-update-all)
123     (save-buffer)))
125 ;;* rtm feed timer
126 (run-at-time 3600 3600 'myupdate-RTM)
128 #+end_src
130 [[file:index.org][{Back to Worg's index}]]