Table of Contents
To help improving the maintenance of org-trello, we will describe it using literate programming. For this, we will use org-mode and babel.
We'll begin by:
- a simple how to
- a little description
- the license
- a manual
- a full description step-by-step of the code (may need to find another organisation that the code order)
1 Howto
- Open this file
- Tangle (extract) the code
M-x org-babel-tangle or C-c C-v t
Or
``` sh make tangle ```
2 Description
org-trello is an emacs's org minor mode to permit the 2-way synchonization between org buffer and a trello board. The headers needs to start with ;;; as a prerequisite for packaging. This is an important step where we describe the different information about the project:
- principal author
- maintainer
- current version
- dependencies
;;; org-trello.el --- Org minor mode to synchronize with trello ;; Copyright (C) 2013 Antoine R. Dumont <eniotna.t AT gmail.com> ;; Author: Antoine R. Dumont <eniotna.t AT gmail.com> ;; Maintainer: Antoine R. Dumont <eniotna.t AT gmail.com> ;; Version: 0.1.1 ;; Package-Requires: ((org "8.0.7") (dash "1.5.0") (request "0.2.0") (cl-lib "0.3.0") (json "1.2")) ;; Keywords: org-mode trello sync org-trello ;; URL: https://github.com/org-trello/org-trello
3 License
First, org-trello is a free software under the GPL v3.
;; This file is NOT part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA.
4 Manual
A fast setup that we, end-users, can see from the package installation procedure.
;;; Commentary: ;; Minor mode for org-mode to sync org-mode and trello ;; ;; 1) Add the following to your emacs init file ;; (require 'org-trello) ;; ;; Automatically ;; 2) Once - Install the consumer-key and the read-write token for org-trello to be able to work in your name with your trello boards (C-c o i) ;; M-x org-trello/install-key-and-token ;; ;; 3) Once per org-mode file/board you want to connect to (C-c o I) ;; M-x org-trello/install-board-and-lists-ids ;; ;; *Beware* you must setup your trello board with the name you use as keywords (TODO, DONE e.g) on your org-mode file. ;; ;; 4) You can also create a board directly from a org-mode buffer (C-c o b) ;; M-x org-trello/create-board ;;
5 Code
5.1 Setup
First, we will begin simply by showing the dependencies. Then the personal setup that the user can override.
- Library dependencies
We depend on:
lib description org As org-trello is a org minor mode json The transit of data to trello's api is in json dash To provide some specific lisp constructs request Awesome http client cl-lib To provide some common-lisp functions parse-time Some date time manipulation ;;; Code: (require 'org) (require 'json) (require 'dash) (require 'request) (eval-when-compile (require 'cl-lib)) (require 'parse-time)
- Personal setup
At the moment, we have only one possible setup. This is relative to the checklist behaviour. By default, the status of the checklist overrides the item's status.
;; #################### overriding setup (defvar *ORGTRELLO-CHECKLIST-UPDATE-ITEMS* t "A variable to permit the checklist's status to be pass along to its items. t, if checklist's status is DONE, the items are updated to DONE (org-mode buffer and trello board), nil only the items's status is used. To deactivate such behavior, update in your init.el: (require 'org-trello) (setq *ORGTRELLO-CHECKLIST-UPDATE-ITEMS* nil)")
If the user does not want this, he/she can modify his/her setup in his/her emacs startup file:
(setq *ORGTRELLO-CHECKLIST-UPDATE-ITEMS* nil)
5.2 Namespace
As each emacs developer knows, there is no real namespace in emacs-lisp. But, we like to separate function depending on perimeters, so we tried to keep the code as much separated as we can.
At the moment, we sliced the code into 6 namespaces:
name | description |
---|---|
org-trello | Main namespace which describe the minor mode, in charge of controls before executing anything |
orgtrello | Primitive routines that executes the code without checks |
orgtrello-query | Interface to the http request |
orgtrello-api | Interface to the trello api |
orgtrello-data | Interface to the org data |
orgtrello-hash | Utility interface to simplify the construction of data |
5.3 org-trello
This is the main entry namespace of org-trello. Starting with the minor mode definition, we will show each higher interactive command after this.
- Minor mode
As we said in the description step, org-trello is a minor mode.
- Description
Simply put, we offer a simple interface to the user through a map. We declare the minor mode to have 'ot' as the emacs modeline name. And we propose default bindings for the user to access the org-trello routine.
;;;###autoload (define-minor-mode org-trello-mode "Sync your org-mode and your trello together." :lighter " ot" ;; the name on the modeline :keymap (let ((map (make-sparse-keymap))) ;; binding will change (define-key map (kbd "C-c o i") 'org-trello/install-key-and-token) (define-key map (kbd "C-c o I") 'org-trello/install-board-and-lists-ids) (define-key map (kbd "C-c o b") 'org-trello/create-board) (define-key map (kbd "C-c o c") 'org-trello/create-simple-entity) (define-key map (kbd "C-c o C") 'org-trello/create-complex-entity) (define-key map (kbd "C-c o s") 'org-trello/sync-to-trello) (define-key map (kbd "C-c o S") 'org-trello/sync-from-trello) (define-key map (kbd "C-c o k") 'org-trello/kill-entity) (define-key map (kbd "C-c o h") 'org-trello/help-describing-bindings) (define-key map (kbd "C-c o d") 'org-trello/check-setup) ;; define other bindings... map) :after-hook (message "ot is on! To begin with, hit C-c o h or M-x 'org-trello/help-describing-bindings")) (add-hook 'org-mode-hook 'org-trello-mode) (message "org-trello loaded!")
- Override
If the user is not satisfied with the default bindings, he/she can always override in his/her own setup files. I kept the default one but the idea is to replace those bindings by the one you want.
(define-key org-trello-mode-map (kbd "C-c o i") 'org-trello/install-key-and-token) (define-key org-trello-mode-map (kbd "C-c o I") 'org-trello/install-board-and-lists-ids) (define-key org-trello-mode-map (kbd "C-c o b") 'org-trello/create-board) (define-key org-trello-mode-map (kbd "C-c o c") 'org-trello/create-simple-entity) (define-key org-trello-mode-map (kbd "C-c o C") 'org-trello/create-complex-entity) (define-key org-trello-mode-map (kbd "C-c o s") 'org-trello/sync-to-trello) (define-key org-trello-mode-map (kbd "C-c o S") 'org-trello/sync-from-trello) (define-key org-trello-mode-map (kbd "C-c o k") 'org-trello/kill-entity) (define-key org-trello-mode-map (kbd "C-c o h") 'org-trello/help-describing-bindings) (define-key org-trello-mode-map (kbd "C-c o d") 'org-trello/check-setup)
- Description
- Help
Let's begin simple, we offer an interactive command to display the current possible bindings. Hitting C-c o h, this will display a simple message on the minibuffer.
(defun org-trello/help-describing-bindings () "A simple message to describe the standard bindings used." (interactive) (message "C-c o i - M-x org-trello/install-key-and-token - Install the keys and the access-token. C-c o I - M-x org-trello/install-board-and-lists-ids - Select the board and attach the todo, doing and done list. C-c o b - M-x org-trello/create-board - Create interactively a board and attach the org-mode file to this trello board. C-c o c - M-x org-trello/create-simple-entity - Create/Update an entity (card/checklist/item) depending on its level and status. Do not deal with level superior to 4. C-c o C - M-x org-trello/create-complex-entity - Create/Update a complete entity card/checklist/item and its subtree (depending on its level). C-c o s - M-x org-trello/sync-to-trello - Synchronize the org-mode file to the trello board (org-mode -> trello). C-c o S - M-x org-trello/sync-from-trello - Synchronize the org-mode file from the trello board (trello -> org-mode). C-c o k - M-x org-trello/kill-entity - Kill the entity (and its arborescence tree). C-c o d - M-x org-trello/check-setup - Simple routine to check that the setup is ok. If everything is ok, will simply display 'Setup ok!' C-c o h - M-x org-trello/help-describing-bindings - This help message."))
- Install the consumer-key and the read/write access token
One of the first interaction we must have with org-trello is the setup. For this, we declare an interactive command. We delegate the code to the function
org-trello/--msg-deco-control-and-do
. This will:- log the actions "Setup key and token" in the mini-buffer.
- execute no control as none is needed (thus the nil as second parameter)
- as there are no control, directly execute the 'org-trello/do-install-key-and-token.
- as there is writing involve, we ask to save the buffer at the end (t)
(defun org-trello/install-key-and-token () "No control, trigger the setup installation of the key and the read/write token." (interactive) (org-trello/--msg-deco-control-and-do "Setup key and token" nil 'orgtrello/do-install-key-and-token t))
- Decorator/Controller
As we've seen before, we have a higher-order function
org-trello/--msg-deco-control-and-do
which is in charge of:- displaying the message
msg
in the mini-buffer - ask for the
org-trello/--control-and-do
function to execute - displaying the result string from the call of the previous function if there is some
- optionally, we can ask for saving the buffer through the flag
save-buffer-p
;; #################### org-trello (defun org-trello/--msg-deco-control-and-do (msg control-fns fn-to-control-and-execute &optional save-buffer-p) "A simple decorator function to display message in mini-buffer before and after the execution of the control" (message (concat msg "...")) (let ((org-trello/--result-action (org-trello/--control-and-do control-fns fn-to-control-and-execute))) ;; do we have to save the buffer (if save-buffer-p (progn (save-buffer) (org-mode-restart))) (if (string-or-null-p org-trello/--result-action) (message org-trello/--result-action) (message (concat msg " - done!")))))
This is the main function which is in charge:
- executing a list of controls
control-fns
- if there is no control or the controls are ok, execute the function
fn-to-control-and-execute
- returns the resulting string from the execution of the function
(defun org-trello/--control-and-do (control-fns fn-to-control-and-execute) "Execute the function fn if control-fns is nil or if the result of apply every function to fn is ok." (if control-fns (let* ((org-trello/--error-messages (--filter (not (equal :ok (funcall it))) control-fns))) (if org-trello/--error-messages ;; there are some trouble, we display all the error messages to help the user understand the problem (message "List of errors:\n %s" (--mapcat (concat "- " it "\n") org-trello/--error-messages)) ;; ok execute the function as the controls are ok (funcall fn-to-control-and-execute))) ;; no control, we simply execute the function (funcall fn-to-control-and-execute)))
- displaying the message
- Setup trello
To use trello, we need to either install a trello board or create one. In either case, this will do some action and update the org-mode buffer with some needed metadata:
- board-id
- board-name (for the user to see which board he/she uses)
- every keyword (org) / list id (trello)
- Install the board
To install a trello board, we need:
- to display a message on the minibuffer "Install boards and lists"
- execute the loading of the setup via
setup-properties
function (no control but a setup) - execute the control of the key and access-token are ok
- if everything is ok, do install the board and lists on the trello buffer
- as some update of the org-mode buffer is done, we ask for the buffer to be saved
(defun org-trello/install-board-and-lists-ids () "Control first, then if ok, trigger the setup installation of the trello board to sync with." (interactive) (org-trello/--msg-deco-control-and-do "Install boards and lists" '(orgtrello/--setup-properties orgtrello/--control-keys) 'orgtrello/do-install-board-and-lists t))
- Create a board
To create a trello board from scratch, we need:
- to display a message on the minibuffer "Create boards and lists"
- execute the loading of the setup via
setup-properties
function (no control but a setup) - execute the control of the key and access-token are ok
- if everything is ok, do create the board and lists on the trello buffer
- as some update of the org-mode buffer is done, we ask for the buffer to be saved
(defun org-trello/create-board () "Control first, then if ok, trigger the board creation." (interactive) (org-trello/--msg-deco-control-and-do "Create board and lists" '(orgtrello/--setup-properties orgtrello/--control-keys) 'orgtrello/do-create-board-and-lists t))
- Check the installation
Now that we setuped the org buffer to work with a trello board, we can ensure that the setup is ok. For this, we simply call the
org-trello/--control-and-do
routine with:orgtrello/--setup-properties
which will load the metadata from the fileorgtrello/--control-keys
to ensure the key and access token are loadedorgtrello/--control-properties
to ensure the properties are properly setuped
If any of those are badly setuped, a message will explicit the problem. Otherwise, a simple message "Setup ok!" will be displayed on the minibuffer.
(defun org-trello/check-setup () "Check the current setup." (interactive) (org-trello/--control-and-do '(orgtrello/--setup-properties orgtrello/--control-keys orgtrello/--control-properties orgtrello/--control-encoding) (lambda () (message "Setup ok!"))))
- Sync a simple entity
To create/synchronize a simple entity (without its arborescence), we need to ensure some standard controls are ok. Then we can call the primitive routine
orgtrello/do-create-simple-entity
to do the actual creation. At last, saving the buffer as the creation involves some buffer updates (with the trello id).(defun org-trello/create-simple-entity () "Control first, then if ok, create a simple entity." (interactive) (org-trello/--msg-deco-control-and-do "Synchronizing entity" '(orgtrello/--setup-properties orgtrello/--control-keys orgtrello/--control-properties orgtrello/--control-encoding) (lambda () (orgtrello/do-create-simple-entity t)) t))
- Sync a complex entity
To create/synchronize a complex entity (with its arborescence), we need to ensure some standard controls are ok. Then we can call the primitive routine
orgtrello/do-create-complex-entity
to do the actual creation. At last, saving the buffer as the creation involves some buffer updates.(defun org-trello/create-complex-entity () "Control first, then if ok, create an entity and all its arborescence if need be." (interactive) (org-trello/--msg-deco-control-and-do "Synchronizing complex entity" '(orgtrello/--setup-properties orgtrello/--control-keys orgtrello/--control-properties orgtrello/--control-encoding) 'orgtrello/do-create-complex-entity t))
- Synchronize the org-mode buffer to trello
Now that we have the basic brick (synchronize simple/complex entity), we can use this to synchronize the all buffer to trello. But first, we need to ensure some standard controls are ok. Then calling the primitive routine
orgtrello/do-sync-full-file
which does the actual syncing. At last, we save the buffer as the buffer has been updated.(defun org-trello/sync-to-trello () "Control first, then if ok, sync the org-mode file completely to trello." (interactive) (org-trello/--msg-deco-control-and-do "Synchronizing org-mode file to trello" '(orgtrello/--setup-properties orgtrello/--control-keys orgtrello/--control-properties orgtrello/--control-encoding) 'orgtrello/do-sync-full-file t))
- Synchronize the org-mode buffer from trello
The other way around is also possible. You know the drill by now, we must ensure we have the standard control that pass. Then calling the routine
orgtrello/do-sync-full-from-trello
which really does the action. Then saving the buffer as the action involved some buffer updates.(defun org-trello/sync-from-trello () "Control first, then if ok, sync the org-mode file from the trello board." (interactive) (org-trello/--msg-deco-control-and-do "Synchronizing trello board to org-mode file" '(orgtrello/--setup-properties orgtrello/--control-keys orgtrello/--control-properties orgtrello/--control-encoding) 'orgtrello/do-sync-full-from-trello t))
- Kill/Remove/Delete an entity
As we can create entity on trello, we can also remove them (from trello and the current org buffer). As usual, we ensure standard controls are ok. Then calling the subroutine
orgtrello/do-delete-simple
which does the action. Then saving the buffer as the action involved some buffer updates.(defun org-trello/kill-entity () "Control first, then if ok, delete the entity and all its arborescence." (interactive) (org-trello/--msg-deco-control-and-do "Delete entity" '(orgtrello/--setup-properties orgtrello/--control-keys orgtrello/--control-properties orgtrello/--control-encoding) (lambda () (orgtrello/do-delete-simple t)) t))
- Providing
Now that we defined all of our code, we need to provide the library we created. Simplify using emacs's primitive provide.
(provide 'org-trello) ;;; org-trello.el ends here
5.4 orgtrello
This is the namespace in charge of the primitive functions that actually trigger the action. Those functions reflect the same action as we define earlier but without any controls first. They are not to be called directly from the user.
- Namespace setup
First, we'll begin by some setup variables that are actually used throughout the namespace:
- TODO representation of org's "TODO" keyword
- DONE representation of org's "DONE" keyword
- BOARD-ID will be the trello board's identifier for org-trello to know which board to use
- BOARD-NAME will be the trello board's name for the user to know to which board he/she works with
- LIST-NAMES is the keyword the user use with org in reverse order
- HMAP-ID-NAME is a map of those same keyword with a sequence identifier
- CONFIG-DIR is the org-trello's home folder
- CONFIG-FILE is the org-trello's setup file (for consumer-key and access-token)
- consumer-key is the user's consumer-key for trello
- access-token is the user's read/write access-token for trello
- ORGTRELLO-MARKER is a marker used by org-trello to know which entity it has synced. This is dependent on the consumer-key
The user does not have to touch anything on this.
;; #################### orgtrello ;; Specific state - FIXME check if they do not already exist on org-mode to avoid potential collisions (defvar *TODO* "TODO" "org-mode todo state") (defvar *DONE* "DONE" "org-mode done state") ;; Properties key for the orgtrello headers #+PROPERTY board-id, etc... (defvar *BOARD-ID* "board-id" "orgtrello property board-id entry") (defvar *BOARD-NAME* "board-name" "orgtrello property board-name entry") (defvar *LIST-NAMES* nil "orgtrello property names of the different lists. This use the standard 'org-todo-keywords property from org-mode.") (defvar *HMAP-ID-NAME* nil "orgtrello hash map containing for each id, the associated name (or org keyword).") (defvar *CONFIG-DIR* (concat (getenv "HOME") "/" ".trello")) (defvar *CONFIG-FILE* (concat *CONFIG-DIR* "/config.el")) (defvar *consumer-key* nil "Id representing the user") (defvar *access-token* nil "Read/write Access token to use trello in the user's name ") (defvar *ORGTRELLO-MARKER* nil "Marker used for syncing the data in trello")
- Control/setup routines
Some control/setup important routines because they will permit or not to launch the main actions.
- orgtrello/–setup-properties
This is an important routine in charge of loading the keywords the user want to use. Note that this routine reverse the list of org keywords. This is a trick to help create the list in trello in the right order (assuming the user defines in the right order his/her keywords, e.g TODO DOING | DONE FAIL)
(defun orgtrello/--setup-properties () "Setup the properties according to the org-mode setup. Return :ok." (let* ((orgtrello/--list-keywords (nreverse (orgtrello/filtered-kwds))) (orgtrello/--hmap-id-name (cl-reduce (lambda (hmap name) (progn (puthash (assoc-default name org-file-properties) name hmap) hmap)) orgtrello/--list-keywords :initial-value (make-hash-table :test 'equal)))) (setq *LIST-NAMES* orgtrello/--list-keywords) (setq *HMAP-ID-NAME* orgtrello/--hmap-id-name) :ok))
- orgtrello/filtered-kwds
This is a function in charge of retrieving the specific keywords the user wants to use with trello. This will map later as the list in trello and as keyword in emacs's org-mode buffer.
(defun orgtrello/filtered-kwds () "org keywords used (based on org-todo-keywords-1)." org-todo-keywords-1)
- orgtrello/–control-encoding
A simple message to make the user aware he needs to use utf-8 encoding.
(defun orgtrello/--control-encoding () "Use utf-8, otherwise, there will be trouble." (progn (message "Ensure you use utf-8 encoding for your org buffer.") :ok))
- orgtrello/–control-properties
An important higher routine to ensure that the setup regarding the trello board is rightly setuped on the org buffer. We simply ensure that we have the right amount of properties regarding the org keywords. If all is ok, we return the :ok value, otherwise, we return an error message indicating the user what he must do.
(defun orgtrello/--control-properties () "org-trello needs the properties board-id and all list id from the trello board to be setuped on header property file. Returns :ok if everything is ok, or the error message if problems." (let ((orgtrello/--hmap-count (hash-table-count *HMAP-ID-NAME*))) (if (and (assoc-default *BOARD-ID* org-file-properties) (= (length *LIST-NAMES*) orgtrello/--hmap-count)) :ok "Setup problem.\nEither you did not connect your org-mode buffer with a trello board, to correct this:\n * attach to a board through C-c o I or M-x org-trello/install-board-and-lists-ids\n * or create a board from scratch with C-c o b or M-x org-trello/create-board).\nEither your org-mode's todo keyword list and your trello board lists are not named the same way (which they must).\nFor this, connect to trello and rename your board's list according to your org-mode's todo list.\nAlso, you can specify on your org-mode buffer the todo list you want to work with, for example: #+TODO: TODO DOING | DONE FAIL (hit C-c C-c to refresh the setup)")))
- orgtrello/–control-keys
Another important check routine to ensure that the user has rightfully setuped his/her consumer-key and read/write access-token. If everything is ok, we return :ok. Otherwise, we return an error message indicating what the user must do.
(defun orgtrello/--control-keys () "org-trello needs the *consumer-key* and the *access-token* to access the trello resources. Returns :ok if everything is ok, or the error message if problems." (if (or (and *consumer-key* *access-token*) ;; the data are not set, (and (file-exists-p *CONFIG-FILE*) ;; trying to load them (load *CONFIG-FILE*) ;; still not loaded, something is not right! (and *consumer-key* *access-token*) ;; setting the marker once (setq *ORGTRELLO-MARKER* (format "orgtrello-marker-%s" *consumer-key*)))) :ok "Setup problem - You need to install the consumer-key and the read/write access-token - C-c o i or M-x org-trello/install-board-and-lists-ids"))
- orgtrello/–setup-properties
- Abstraction access routines
Those are simple routines to abstract away the representation of the org data we manipulate.
- orgtrello/–keyword
Extract the status/keyword from the current entity (e.g TODO, DONE, etc…).
(defun orgtrello/--keyword (entity-meta &optional default-value) "Retrieve the keyword from the entity. If default-value is specified, this is the default value if no keyword is present" (gethash :keyword entity-meta default-value))
- orgtrello/–label
To extract the label from the entity. This is what's map to the name of the entity on trello.
(defun orgtrello/--label (entity-meta) "Retrieve the label from the entity." (gethash :title entity-meta))
- orgtrello/–id
The identifier of the entity once it has been synchronized on trello.
(defun orgtrello/--id (entity-meta) "Retrieve the id from the entity." (gethash :id entity-meta))
- orgtrello/–level
The current level (mapped to the number of stars).
(defun orgtrello/--level (entity-meta) "Retrieve the level from the entity." (gethash :level entity-meta))
- orgtrello/–due
The deadline (org notion) mapped to due date (on trello).
(defun orgtrello/--due (entity-meta) "Retrieve the due date from the entity." (gethash :due entity-meta))
- orgtrello/–keyword
- Create simple entity
- orgtrello/do-create-simple-entity
The orchestration to trigger the simple synchronization of an entity (without any arborescence):
- compute the metadata (current, parent, grandparent) from the current org entry (this may be need if too deep level)
- if there is some metadata
- if this is an error message, transit the message
- otherwise
- set the marker on org buffer for the current entry
- compute and execute the http query
- return a success message
(defun orgtrello/do-create-simple-entity (&optional sync) "Do the actual simple creation of a card, checklist or task. Optionally, we can render the creation synchronous." (let ((entry-metadata (orgtrello-data/entry-get-full-metadata))) (if entry-metadata (let ((query-http-or-error-msg (orgtrello/--dispatch-create (gethash :current entry-metadata) (gethash :parent entry-metadata) (gethash :grandparent entry-metadata)))) (if (hash-table-p query-http-or-error-msg) ;; if it's a hash-table we can do the sync (progn ;; set the consumer-key to make a pointer to get back to when the request is finished (orgtrello/--set-marker) ;; request (orgtrello-query/http query-http-or-error-msg sync 'orgtrello-query/--post-put-success-callback-update-id) "Synchronizing simple entity done!") ;; else it's a string to display query-http-or-error-msg)))))
- creation/update routine
Those functions does not actually do any request, they compute the map representing the request.
- card
- orgtrello/–card
This is the main entry to create/update a card. There is a series of check done by the
orgtrello/--checks-before-sync-card
function. If ok, then we can continue with the main intent of the function. We extract from the metadata the:- keyword status of the card (TODO, DONE, etc…)
- trello list identifier to which the card belongs to (depending on the keyword status)
- card's identifier
- card's name
- card's due
Then we create or update the card depending on the presence or not of the card identifier. if card id present update else create.
(defun orgtrello/--card (card-meta &optional parent-meta grandparent-meta) "Deal with create/update card query build. If the checks are ko, the error message is returned." (let ((checks-ok-or-error-message (orgtrello/--checks-before-sync-card card-meta))) ;; title is mandatory (if (equal :ok checks-ok-or-error-message) ;; parent and grandparent are useless here (let* ((orgtrello/--card-kwd (orgtrello/--retrieve-state-of-card card-meta)) (orgtrello/--list-id (assoc-default orgtrello/--card-kwd org-file-properties)) (orgtrello/--card-id (orgtrello/--id card-meta)) (orgtrello/--card-name (orgtrello/--label card-meta)) (orgtrello/--card-due (orgtrello/--due card-meta))) (if orgtrello/--card-id ;; update (orgtrello-api/move-card orgtrello/--card-id orgtrello/--list-id orgtrello/--card-name orgtrello/--card-due) ;; create (orgtrello-api/add-card orgtrello/--card-name orgtrello/--list-id orgtrello/--card-due))) checks-ok-or-error-message)))
- orgtrello/–retrieve-state-of-card
This function helps computes the status of the card. If no status is present, TODO is assumed.
(defun orgtrello/--retrieve-state-of-card (card-meta) "Given a card, retrieve its state depending on its :keyword metadata. If empty or no keyword then, its equivalence is *TODO*, otherwise, return its current state." (let* ((orgtrello/--card-kwd (orgtrello/--keyword card-meta *TODO*))) (if orgtrello/--card-kwd orgtrello/--card-kwd *TODO*)))
- orgtrello/–checks-before-sync-card
Does some basic checks. Typically, here only the name/title/label is mandatory.
(defun orgtrello/--checks-before-sync-card (card-meta) "Checks done before synchronizing the cards." (let ((orgtrello/--card-name (orgtrello/--label card-meta))) (if orgtrello/--card-name :ok "Cannot synchronize the card - missing mandatory label. Skip it...")))
- orgtrello/–card
- checklist
- orgtrello/–checklist
The idea is similar than the card function. First, checks using orgtrello–checks-before-sync-checklist/ function. If ok, continue otherwise return the error message. Then extract the needed metadata:
- checklist identifier (optional)
- card identifier (mandatory, a checklist belongs to a card)
- checklist name (mandatory)
Again, if checklist identifier present, we update otherwise we create.
(defun orgtrello/--checklist (checklist-meta &optional card-meta grandparent-meta) "Deal with create/update checklist query build. If the checks are ko, the error message is returned." (let ((checks-ok-or-error-message (orgtrello/--checks-before-sync-checklist checklist-meta card-meta))) ;; title is mandatory (if (equal :ok checks-ok-or-error-message) ;; grandparent is useless here (let* ((orgtrello/--checklist-id (orgtrello/--id checklist-meta)) (orgtrello/--card-id (orgtrello/--id card-meta)) (orgtrello/--checklist-name (orgtrello/--label checklist-meta))) (if orgtrello/--checklist-id ;; update (orgtrello-api/update-checklist orgtrello/--checklist-id orgtrello/--checklist-name) ;; create (orgtrello-api/add-checklist orgtrello/--card-id orgtrello/--checklist-name))) checks-ok-or-error-message)))
- orgtrello/–checks-before-sync-checklist
A little more complex check are done. We need to ensure:
- the card's id is present
- the checklist's name is present too.
If some are missing, send the error message corresponding.
(defun orgtrello/--checks-before-sync-checklist (checklist-meta card-meta) "Checks done before synchronizing the checklist." (let ((orgtrello/--checklist-name (orgtrello/--label checklist-meta)) (orgtrello/--card-id (orgtrello/--id card-meta))) (if orgtrello/--checklist-name (if orgtrello/--card-id :ok "Cannot synchronize the checklist - the card must be synchronized first. Skip it...") "Cannot synchronize the checklist - missing mandatory label. Skip it...")))
- orgtrello/–checklist
- task/item
- orgtrello/–task
Update the task/item. First checks. If ok, continue otherwise return the error message. Metadata extracted:
- task/item's id (optional)
- checklist's id (mandatory)
- card's id (mandatory)
- task/item's name (mandatory)
- checklist's state (trello api distinguish between the state at update time from the check status at creation time)
- checklist's check status
Depending on the presence of the task/item's identifier, we update or create.
(defun orgtrello/--task (task-meta &optional checklist-meta card-meta) "Deal with create/update task query build. If the checks are ko, the error message is returned." (let ((checks-ok-or-error-message (orgtrello/--checks-before-sync-item task-meta checklist-meta card-meta))) ;; title is mandatory (if (equal :ok checks-ok-or-error-message) ;; card-meta is only usefull for the update part (let* ((orgtrello/--task-id (orgtrello/--id task-meta)) (orgtrello/--checklist-id (orgtrello/--id checklist-meta)) (orgtrello/--card-id (orgtrello/--id card-meta)) (orgtrello/--task-name (orgtrello/--label task-meta)) (orgtrello/--task-state (orgtrello/--keyword task-meta)) (orgtrello/--checklist-state (orgtrello/--keyword checklist-meta))) (orgtrello/--update-item-according-to-checklist-status *ORGTRELLO-CHECKLIST-UPDATE-ITEMS* checklist-meta) ;; update/create items (if orgtrello/--task-id ;; update - rename, check or uncheck the task (orgtrello-api/update-task orgtrello/--card-id orgtrello/--checklist-id orgtrello/--task-id orgtrello/--task-name (orgtrello/--task-compute-state *ORGTRELLO-CHECKLIST-UPDATE-ITEMS* orgtrello/--task-state orgtrello/--checklist-state)) ;; create (orgtrello-api/add-tasks orgtrello/--checklist-id orgtrello/--task-name (orgtrello/--task-compute-check *ORGTRELLO-CHECKLIST-UPDATE-ITEMS* orgtrello/--task-state orgtrello/--checklist-state)))) checks-ok-or-error-message)))
Also, there is a sublety. ORGTRELLO-CHECKLIST-UPDATE-ITEMS is a global setup that permits the override of the task/item's status (checked or not). if ORGTRELLO-CHECKLIST-UPDATE-ITEMS is set to 't, this will override such setup. Otherwise, the item's status is computed depending on org's keyword. This means that if ORGTRELLO-CHECKLIST-UPDATE-ITEMS is 't, we need to update the buffer accordingly to such status since this will be synchronized on trello. That's the responsibility of the function orgtrello–update-item-according-to-checklist-status/.
- orgtrello/–checks-before-sync-item
The control function which ensures:
- task/item's id
- checklist's id
- card's id
are presents. If some are missing, the corresponding error message is sent, :ok otherwise.
(defun orgtrello/--checks-before-sync-item (task-meta checklist-meta card-meta) "Checks done before synchronizing the checklist." (let ((orgtrello/--task-name (orgtrello/--label task-meta)) (orgtrello/--checklist-id (orgtrello/--id checklist-meta)) (orgtrello/--card-id (orgtrello/--id card-meta))) (if orgtrello/--task-name (if orgtrello/--checklist-id (if orgtrello/--card-id :ok "Cannot synchronize the item - the card must be synchronized first. Skip it...") "Cannot synchronize the item - the checklist must be synchronized first. Skip it...") "Cannot synchronize the item - missing mandatory label. Skip it...")))
- orgtrello/–task-compute-state-or-check
An HOF (higher order function) that captures the computation of the state (update) or the check (create) of an item/task.
(defun orgtrello/--task-compute-state-or-check (checklist-update-items-p task-state checklist-state possible-states) "Compute the task's state/check (for creation/update). The 2 possible states are in the list possible states, first position is the 'checked' one, and second the unchecked one." (let* ((orgtrello/--task-checked (first possible-states)) (orgtrello/--task-unchecked (second possible-states))) (cond ((and checklist-update-items-p (string= *DONE* checklist-state)) orgtrello/--task-checked) ((and checklist-update-items-p (or checklist-state (string= *TODO* checklist-state))) orgtrello/--task-unchecked) ((string= *DONE* task-state) orgtrello/--task-checked) (t orgtrello/--task-unchecked))))
- orgtrello/–task-compute-state
A function to compute the state (update) of the task/item:
- complete
- incomplete
There is a subtlety here. checklist-update-items-p represents a global setup to make the checklist's status more important than the current item/task's status.
Here is the truth table:
checklist-update-items-p checklist's status task/item's status Result t c-st X (= c-st DONE "complete" "incomplete") nil X t-st (= t-st DONE "complete" "incomplete") n
(defun orgtrello/--task-compute-state (checklist-update-items-p task-state checklist-state) "Compute the task's state (for creation)." (orgtrello/--task-compute-state-or-check checklist-update-items-p task-state checklist-state '("complete" "incomplete")))
- orgtrello/–task-compute-check
A function to compute the check status of the task/item:
- t
- nil
There is a subtlety here. checklist-update-items-p represents a global setup to make the checklist's status more important than the current item/task's status.
Here is the truth table:
checklist-update-items-p checklist's status task/item's check status Result t c-st X (= c-st DONE t nil) nil X t-st (= t-st DONE t nil) (defun orgtrello/--task-compute-check (checklist-update-items-p task-state checklist-state) "Compute the task's check status (for update)." (orgtrello/--task-compute-state-or-check checklist-update-items-p task-state checklist-state '(t nil)))
- orgtrello/–update-item-according-to-checklist-status
This is a function, depending on checklist-update-items-p, which is in charge of aligning the status of the keyword in the org buffer according to the checklist's keyword. if checklist-update-items-p is 't, then update else does nothing.
(defun orgtrello/--update-item-according-to-checklist-status (checklist-update-items-p checklist-meta) "Update the item of the checklist according to the status of the checklist." (if checklist-update-items-p (let ((orgtrello/--checklist-status (orgtrello/--compute-state-from-keyword (orgtrello/--keyword checklist-meta)))) (org-todo orgtrello/--checklist-status))))
- orgtrello/–compute-state-from-keyword
Given a state, compute its org equivalent:
state org nil "" "" "" DONE DONE TODO TODO Otherwise TODO (defun orgtrello/--compute-state-from-keyword (state) "Given a state, compute the org equivalent (to use with org-todo function)" (cond ((or (not state) (string= "" state)) *TODO*) ((string= *DONE* state) 'done) ((string= *TODO* state) *TODO*) (t *TODO*)))
- orgtrello/–task
- card
- orgtrello/–too-deep-level
A function which simply displays that the arborescence depth is too deep. We only deal with 3 levels (1 -> card, 2 -> checklist, 3 -> item/task).
(defun orgtrello/--too-deep-level (meta &optional parent-meta grandparent-meta) "Deal with too deep level." "Your arborescence depth is too deep. We only support up to depth 3.\nLevel 1 - card\nLevel 2 - checklist\nLevel 3 - items/tasks")
- orgtrello/–dispatch-map-creation
An initialization of the dispatch creation/update function depending on the level (1, 2, 3). We use this function to initialize once the MAP-DISPATCH-CREATE-UPDATE which will be use to dispatch on the level of the entity to sync.
(defun orgtrello/--dispatch-map-creation () "Dispatch map for the creation of card/checklist/item." (let* ((dispatch-map (make-hash-table :test 'equal))) (puthash 1 'orgtrello/--card dispatch-map) (puthash 2 'orgtrello/--checklist dispatch-map) (puthash 3 'orgtrello/--task dispatch-map) dispatch-map)) (defvar *MAP-DISPATCH-CREATE-UPDATE* (orgtrello/--dispatch-map-creation) "Dispatch map for the creation/update of card/checklist/task")
- orgtrello/–set-marker
A simple routine to install an orgtrello-marker before launching any synchronization.
(defun orgtrello/--set-marker () "Set the consumer-key to make a pointer to get back to when the request is finished" (org-set-property *ORGTRELLO-MARKER* *ORGTRELLO-MARKER*))
- orgtrello/–dispatch-create
The function which will extract the current level of the entity and call the function to generate the create/update request for the current entity.
(defun orgtrello/--dispatch-create (meta &optional parent-meta grandparent-meta) (let* ((level (orgtrello/--level meta)) (dispatch-fn (gethash level *MAP-DISPATCH-CREATE-UPDATE* 'orgtrello/--too-deep-level))) ;; then execute the call (funcall dispatch-fn meta parent-meta grandparent-meta)))
- orgtrello/do-create-simple-entity
- Create complex entity
- orgtrello/–board-name
Extract the board name from the org buffer's metadata.
(defun orgtrello/--board-name () "Compute the board's name" (assoc-default *BOARD-NAME* org-file-properties))
- orgtrello/do-create-complex-entity
The main function in charge of synchronizing the entity (with its arborescence):
- Get back to the upper level of the current entry
- Map over the full arborescence (in order) and sync the entity (using the primitive orgtrello/do-create-simple-entity)
- Return a success message
(defun orgtrello/do-create-complex-entity () "Do the actual full card creation - from card to task. Beware full side effects..." (let ((orgtrello/--board-name-to-sync (orgtrello/--board-name))) (message "Synchronizing full entity with its structure on board '%s'..." orgtrello/--board-name-to-sync) (save-excursion ;; iterate over the map of (org-map-tree (lambda () (orgtrello/do-create-simple-entity t)))) (format "Synchronizing full entity with its structure on board '%s' - done" orgtrello/--board-name-to-sync)))
- orgtrello/–board-name
- Synchronize full org buffer to trello
Synchronize the full org file to the trello board. The idea is to send all the cards presents in the buffer to the trello board (no merge, org buffer has all the rights here). At the end, return a success message.
(defun orgtrello/do-sync-full-file () "Full org-mode file synchronisation. Beware, this will block emacs as the request is synchronous." (let ((orgtrello/--board-name-to-sync (orgtrello/--board-name))) (message "Synchronizing org-mode file to the board '%s'. This may take some time, some coffee may be a good idea..." (orgtrello/--board-name)) (org-map-entries (lambda () (orgtrello/do-create-simple-entity t)) t 'file) (format "Synchronizing org-mode file to the board '%s' - done!" orgtrello/--board-name-to-sync)))
- Synchronize full org buffer from trello
Synchronize the full org file from the trello board. The idea is to compute all the trello cards from trello. Map over the current org buffer, synchronize from trello (no merge, trello has all power here) and overwrite the trello data for each entry. Each data remaining not already present on the buffer are then dumped in the current buffer.
- orgtrello/do-sync-full-from-trello
Main function:
- retrieve the board id from the org metadata
- compute the cards from the trello board
- synchronize all the entities present on the buffer with the data from trello (remove them as soon as they are synced)
- synchronize all the remaining entities into the current buffer
- return a successfull message
(defun orgtrello/do-sync-full-from-trello () "Full org-mode file synchronisation. Beware, this will block emacs as the request is synchronous." (let ((orgtrello/--board-name-to-sync (orgtrello/--board-name))) (message "Synchronizing the trello board '%s' to the org-mode file. This may take a moment, some coffee may be a good idea..." orgtrello/--board-name-to-sync) (let* ((orgtrello/--board-id (assoc-default *BOARD-ID* org-file-properties)) (orgtrello/--cards (orgtrello-query/http (orgtrello-api/get-cards orgtrello/--board-id) t)) (orgtrello/--entities-hash-map (orgtrello/--compute-full-entities-from-trello orgtrello/--cards)) (orgtrello/--remaining-entities (orgtrello/--sync-buffer-with-trello-data orgtrello/--entities-hash-map))) (orgtrello/--update-buffer-with-remaining-trello-data orgtrello/--remaining-entities)) (format "Synchronizing the trello board '%s' to the org-mode file - done!" orgtrello/--board-name-to-sync)))
- orgtrello/–sync-buffer-with-trello-data
Given a map of entities to sync, update each entry with such data. After each entry update, the entity is removed. Return the map of entities with the sync entry removed.
(defun orgtrello/--sync-buffer-with-trello-data (entities) "Given all the entities, update the current buffer with those." (with-current-buffer (current-buffer) (org-map-entries (lambda () (let ((entry-metadata (orgtrello-data/entry-get-full-metadata))) (if entry-metadata ;; if level > 4, entry-metadata is not considered as this is not represented in trello board ;; will search 'entities' hash table for updates (do not compute diffs, take them as is) (let* ((orgtrello/--entity (gethash :current entry-metadata)) (orgtrello/--entity-id (orgtrello/--id orgtrello/--entity)) (orgtrello/--entity-updated (gethash orgtrello/--entity-id entities))) (if orgtrello/--entity-updated ;; found something, we update by squashing the current contents (let* ((orgtrello/--entry-new-id (orgtrello-query/--id orgtrello/--entity-updated)) (orgtrello/--entity-due-date (orgtrello-query/--due orgtrello/--entity-updated)) (orgtrello/--entry-new-name (orgtrello-query/--name orgtrello/--entity-updated))) ;; update the buffer with the new updates (there may be none but naively we will overwrite at the moment) (message "Synchronizing entity '%s' with id '%s'..." orgtrello/--entry-new-name orgtrello/--entry-new-id) (org-show-entry) (kill-whole-line) (if orgtrello/--entity-due-date (kill-whole-line)) (insert (orgtrello/--compute-entity-to-org-entry orgtrello/--entity-updated)) ;; remove the entry from the hash-table (remhash orgtrello/--entity-id entities))))))) t 'file)) ;; return the entities which has been dryed entities)
- orgtrello/–update-buffer-with-remaining-trello-data
Given a map of entities:
- goes at the end of the file
- add the entities present on such map in the org format
- return at the beginning of the file
- sort all the buffer on the org todo keywords order (only the first level -> card).
(defun orgtrello/--update-buffer-with-remaining-trello-data (entities) "Given a map of entities, dump those entities in the current buffer." (if entities ;; could be empty (with-current-buffer (current-buffer) ;; go at the end of the file (goto-char (point-max)) ;; dump the remaining entities (maphash (lambda (orgtrello/--entry-new-id orgtrello/--entity) (let ((orgtrello/--entry-new-name (orgtrello-query/--name orgtrello/--entity))) (message "Synchronizing new entity '%s' with id '%s'..." orgtrello/--entry-new-name orgtrello/--entry-new-id) (insert (orgtrello/--compute-entity-to-org-entry orgtrello/--entity)) (org-set-property *ORGTRELLO-ID* orgtrello/--entry-new-id))) entities) (goto-char (point-min)) (org-sort-entries t ?o))))
- orgtrello/–compute-card-status
Given a card list id (which represent an org keyword), compute the keywords (this works with the metadata of the file).
(defun orgtrello/--compute-card-status (card-id-list) "Given a card's id, compute its status." (gethash card-id-list *HMAP-ID-NAME*))
- orgtrello/–compute-card-to-org-entry
Given an entry which represents a card, compute its equivalent org format.
(defun orgtrello/--compute-card-to-org-entry (card) "Given a card, compute its org-mode entry equivalence." (let* ((orgtrello/--card-name (orgtrello-query/--name card)) (orgtrello/--card-status (orgtrello/--compute-card-status (orgtrello-query/--list-id card))) (orgtrello/--card-due-date (orgtrello-query/--due card))) (format "* %s %s\n%s" orgtrello/--card-status orgtrello/--card-name (if orgtrello/--card-due-date (format "DEADLINE: <%s>\n" orgtrello/--card-due-date) ""))))
- orgtrello/–compute-checklist-to-org-entry
Given an entry which represents a checklist, compute its equivalent org format.
(defun orgtrello/--compute-checklist-to-org-entry (checklist) "Given a checklist, compute its org-mode entry equivalence." (let ((orgtrello/--checklist-name (orgtrello-query/--name checklist))) (format "** %s\n" orgtrello/--checklist-name)))
- orgtrello/–compute-item-to-org-entry
Given an entry which represents an item/task, compute its equivalent org format.
(defun orgtrello/--compute-item-to-org-entry (item) "Given a checklist item, compute its org-mode entry equivalence." (let* ((orgtrello/--item-name (orgtrello-query/--name item)) (orgtrello/--item-state (orgtrello-query/--state item))) (format "*** %s %s\n" (if (string= "complete" orgtrello/--item-state) *DONE* *TODO*) orgtrello/--item-name)))
- orgtrello/–compute-entity-to-org-entry
Given an entry, determine according to its structure the nature of such entity:
- list-id, it's a card
- card-id, it's a checklist
- state, it's an item/task
(defun orgtrello/--compute-entity-to-org-entry (entity) "Given an entity, compute its org representation." (cond ((orgtrello-query/--list-id entity) (orgtrello/--compute-card-to-org-entry entity)) ;; card (level 1) ((orgtrello-query/--card-id entity) (orgtrello/--compute-checklist-to-org-entry entity)) ;; checklist (level 2) ((orgtrello-query/--state entity) (orgtrello/--compute-item-to-org-entry entity)))) ;; items (level 3)
- orgtrello/–do-retrieve-checklists-from-card
Given a card, retrieve its full checklist and return a list composed of the card cons'ed to such entities.
(defun orgtrello/--do-retrieve-checklists-from-card (card) "Given a card, return the list containing the card, the checklists from this card, and the items from the checklists. The order is guaranted." (cl-reduce (lambda (acc-list checklist-id) (let ((orgtrello/--checklist (orgtrello-query/http (orgtrello-api/get-checklist checklist-id) t))) (append (cons orgtrello/--checklist (orgtrello/--do-retrieve-checklists-and-items orgtrello/--checklist)) acc-list))) (orgtrello-query/--checklist-ids card) :initial-value nil))
- orgtrello/–do-retrieve-checklists-and-items
Given a checklist, retrieve its full items/tasks and return a list composed of the checklist cons'ed to such entities.
(defun orgtrello/--do-retrieve-checklists-and-items (checklist) "Given a checklist id, retrieve all the items from the checklist and return a list containing first the checklist, then the items." (--map it (orgtrello-query/--check-items checklist)))
- orgtrello/–compute-full-entities-from-trello
Given a list of cards, retrieve the full arborescence of such cards (computing their checklists and items/tasks) and return a map of entities.
(defun orgtrello/--compute-full-entities-from-trello (cards) "Given a list of cards, compute the full cards data from the trello boards. The order from the trello board is now kept." ;; will compute the hash-table of entities (id, entity) (cl-reduce (lambda (orgtrello/--acc-hash orgtrello/--entity-card) (message "Computing card '%s' data..." (orgtrello-query/--name orgtrello/--entity-card)) ;; adding the entity card (puthash (orgtrello-query/--id orgtrello/--entity-card) orgtrello/--entity-card orgtrello/--acc-hash) ;; fill in the other remaining entities (checklist/items) (mapc (lambda (it) (puthash (orgtrello-query/--id it) it orgtrello/--acc-hash)) (orgtrello/--do-retrieve-checklists-from-card orgtrello/--entity-card)) orgtrello/--acc-hash) cards :initial-value (make-hash-table :test 'equal)))
- orgtrello/do-sync-full-from-trello
- Delete entity
- orgtrello/do-delete-simple
The main function to delete the current entity. This checks if the id is present. If not present, return an error message explaining the entity must be synced first. Otherwise, execute the trello deletion. Return a message of success.
(defun orgtrello/do-delete-simple (&optional sync) "Do the simple deletion of a card, checklist or task." (let* ((entry-metadata (orgtrello-data/entry-get-full-metadata)) (current-metadata (gethash :current entry-metadata)) (id (orgtrello/--id current-metadata))) (if (and current-metadata id) (let ((query-http-or-error-msg (orgtrello/--dispatch-delete (gethash :current entry-metadata) (gethash :parent entry-metadata)))) (if (hash-table-p query-http-or-error-msg) (progn (orgtrello-query/http query-http-or-error-msg sync 'orgtrello-query/--delete-success-callback) "Delete entity done!") query-http-or-error-msg)) "Entity not synchronized on trello yet!")))
- orgtrello/–dispatch-map-delete
The computation of the MAP-DISPATCH-DELETE which will be used to dispatch the deletion of an entity.
(defun orgtrello/--dispatch-map-delete () "Dispatch map for the deletion of card/checklist/item." (let* ((dispatch-map (make-hash-table :test 'equal))) (puthash 1 'orgtrello/--card-delete dispatch-map) (puthash 2 'orgtrello/--checklist-delete dispatch-map) (puthash 3 'orgtrello/--task-delete dispatch-map) dispatch-map)) (defvar *MAP-DISPATCH-DELETE* (orgtrello/--dispatch-map-delete) "Dispatch map for the deletion query of card/checklist/task.")
- orgtrello/–dispatch-delete
The actual entity deletion using the MAP-DISPATCH-DELETE as a dispatch function.
(defun orgtrello/--dispatch-delete (meta &optional parent-meta) (let* ((level (orgtrello/--level meta)) (dispatch-fn (gethash level *MAP-DISPATCH-DELETE* 'orgtrello/--too-deep-level))) (funcall dispatch-fn meta parent-meta)))
- orgtrello/–card-delete
Specific card deletion request computation.
(defun orgtrello/--card-delete (card-meta &optional parent-meta) "Deal with the deletion query of a card" ;; parent is useless here (orgtrello-api/delete-card (orgtrello/--id card-meta)))
- orgtrello/–checklist-delete
Specific checklist deletion request computation.
(defun orgtrello/--checklist-delete (checklist-meta &optional parent-meta) "Deal with the deletion query of a checklist" ;; parent is useless here (orgtrello-api/delete-checklist (orgtrello/--id checklist-meta)))
- orgtrello/–task-delete
Specific task/item deletion request computation.
(defun orgtrello/--task-delete (task-meta &optional checklist-meta) "Deal with create/update task query build" (let* ((orgtrello/--task-id (orgtrello/--id task-meta)) (orgtrello/--checklist-id (orgtrello/--id checklist-meta))) (orgtrello-api/delete-task orgtrello/--checklist-id orgtrello/--task-id)))
- orgtrello/do-delete-simple
- Install key and token configuration
- orgtrello/do-install-key-and-token
First routine to ask input for the user:
- open the consumer key page for the user to retrieve his/her consumer key.
- then open the access token page to ask for the user to permit org-trello to act on her/his behalf.
- at last, generate the config.el file inside his/her home.
- return a successfull message
(defun orgtrello/do-install-key-and-token () "Procedure to install the *consumer-key* and the token for the user in the config-file." (interactive) (browse-url "https://trello.com/1/appKey/generate") (let ((orgtrello/--*consumer-key* (read-string "*consumer-key*: "))) (browse-url (format "https://trello.com/1/authorize?response_type=token&name=org-trello&scope=read,write&expiration=never&key=%s" orgtrello/--*consumer-key*)) (let ((orgtrello/--access-token (read-string "Access-token: "))) (orgtrello/--do-install-config-file orgtrello/--*consumer-key* orgtrello/--access-token) "Install key and read/write access token done!")))
- orgtrello/–do-install-config-file
Given a consumer-key and an access token, generate a config.el file.
(defun orgtrello/--do-install-config-file (*consumer-key* *access-token*) "Persist the file config-file with the input of the user." (make-directory *CONFIG-DIR* t) (with-temp-file *CONFIG-FILE* (erase-buffer) (goto-char (point-min)) (insert (format "(setq *consumer-key* \"%s\")\n" *consumer-key*)) (insert (format "(setq *access-token* \"%s\")" *access-token*)) (write-file *CONFIG-FILE* 't)))
- orgtrello/do-install-key-and-token
- Install board and lists configuration
The install board and lists configuration. There is an alternative to such setup with the create board routine.
- orgtrello/do-install-board-and-lists
Main routine to ask for the user's input to setup his/her board to attach to his/her current org buffer. This:
- generates a list of his/her current board from his/her trello's account.
- ask for the user to input which board he/she wants to use
- then generate the metadata regarding his/her choice to the org buffer's beginning
- return a message of success
(defun orgtrello/do-install-board-and-lists () "Interactive command to install the list boards" (interactive) (cl-destructuring-bind (orgtrello/--chosen-board-id orgtrello/--chosen-board-name) (-> (orgtrello/--list-boards) orgtrello/--id-name orgtrello/--choose-board) (let ((orgtrello/--board-lists-hname-id (-> orgtrello/--chosen-board-id orgtrello/--list-board-lists orgtrello/--name-id))) ;; remove any eventual present entry (orgtrello/--remove-properties-file orgtrello/--board-lists-hname-id t) ;; update with new ones (orgtrello/update-orgmode-file-with-properties orgtrello/--chosen-board-name orgtrello/--chosen-board-id orgtrello/--board-lists-hname-id t))) "Install board and list ids done!")
- orgtrello/–choose-board
The routine that asks the user:
- to select the board he/she wants to work with
- return the list of board id, board name
(defun orgtrello/--choose-board (boards) "Given a map of boards, display the possible boards for the user to choose which one he wants to work with." ;; ugliest ever (defvar orgtrello/--board-chosen nil) (setq orgtrello/--board-chosen nil) (let* ((str-key-val "") (i 0) (i-id (make-hash-table :test 'equal))) (maphash (lambda (id name) (setq str-key-val (format "%s%d: %s\n" str-key-val i name)) (puthash (format "%d" i) id i-id) (setq i (+ 1 i))) boards) (while (not (gethash orgtrello/--board-chosen i-id)) (setq orgtrello/--board-chosen (read-string (format "%s\nInput the number of the board desired: " str-key-val)))) (let* ((orgtrello/--chosen-board-id (gethash orgtrello/--board-chosen i-id)) (orgtrello/--chosen-board-name (gethash orgtrello/--chosen-board-id boards))) `(,orgtrello/--chosen-board-id ,orgtrello/--chosen-board-name))))
- orgtrello/–remove-properties-file
Remove some file properties before applying new ones.
(defun orgtrello/--delete-buffer-property (property-name) "A simple routine to delete a #+property: entry from the org-mode buffer." (let ((current-point (search-forward property-name nil t))) (if current-point (progn (goto-char current-point) (beginning-of-line) (kill-line) (kill-line))))) (defun orgtrello/--remove-properties-file (board-lists-hash-name-id &optional update-todo-keywords) "Remove the current org-trello properties" (with-current-buffer (current-buffer) (goto-char (point-min)) (orgtrello/--delete-buffer-property (format "#+property: %s" *BOARD-ID*)) (orgtrello/--delete-buffer-property (format "#+property: %s" *BOARD-NAME*)) (maphash (lambda (name id) (orgtrello/--delete-buffer-property (format "#+property: %s" (orgtrello/convention-property-name name)))) board-lists-hash-name-id) (if update-todo-keywords (orgtrello/--delete-buffer-property "#+TODO: "))))
- orgtrello/update-orgmode-file-with-properties
Given a board name, board id, list of list ids, map of list names, insert such informations to the beginning of the org buffer. Then save the buffer and restart org-mode.
(defun orgtrello/update-orgmode-file-with-properties (board-name board-id board-lists-hash-name-id &optional update-todo-keywords) "Update the orgmode file with the needed headers for org-trello to work." (with-current-buffer (current-buffer) (goto-char (point-min)) ;; force utf-8 (set-buffer-file-coding-system 'utf-8-auto) ;; install board-name and board-id (insert (format "#+property: %s %s\n" *BOARD-NAME* board-name)) (insert (format "#+property: %s %s\n" *BOARD-ID* board-id)) ;; install the other properties regarding the org keywords (maphash (lambda (name id) (insert (format "#+property: %s %s\n" (orgtrello/convention-property-name name) id))) board-lists-hash-name-id) (if update-todo-keywords (progn ;; install the todo list (insert "#+TODO: ") (maphash (lambda (name _) (insert (concat (orgtrello/convention-property-name name) " "))) board-lists-hash-name-id) (insert "\n"))) ;; save the buffer (save-buffer) ;; restart org to make org-trello aware of the new setup (org-mode-restart)))
- orgtrello/–id-name
Given a list of entities, return a map of (id . name)
(defun orgtrello/--id-name (entities) "Given a list of entities, return a map of (id, name)." (let* ((id-name (make-hash-table :test 'equal))) (mapc (lambda (it) (puthash (orgtrello-query/--id it) (orgtrello-query/--name it) id-name)) entities) id-name))
- orgtrello/–name-id
Given a list of entities, return a map of (name . id)
(defun orgtrello/--name-id (entities) "Given a list of entities, return a map of (id, name)." (let* ((name-id (make-hash-table :test 'equal))) (mapc (lambda (it) (puthash (orgtrello-query/--name it) (orgtrello-query/--id it) name-id)) entities) name-id))
- orgtrello/–list-boards
Execute the query that return a list of boards from the user's current trello account.
(defun orgtrello/--list-boards () "Return the map of the existing boards associated to the current account. (Synchronous request)" (cl-remove-if-not (lambda (board) (equal :json-false (orgtrello-query/--close-property board))) (orgtrello-query/http (orgtrello-api/get-boards) t)))
- orgtrello/–list-board-lists
Execute the query that return a list of the board lists from the user's current trello account.
(defun orgtrello/--list-board-lists (board-id) "Return the map of the existing list of the board with id board-id. (Synchronous request)" (orgtrello-query/http (orgtrello-api/get-lists board-id) t))
- orgtrello/convention-property-name
A function to help eventually decorate the name of the keywords. Replace the space by dash at the moment.
(defun orgtrello/convention-property-name (name) "Use the right convention for the property used in the headers of the org-mode file." (replace-regexp-in-string " " "-" name))
- orgtrello/do-install-board-and-lists
- Create board
The main function regarding the creation of the board and the org attachment to such board.
- orgtrello/do-create-board-and-lists
The main entry. We will ask the user to:
- input the name of the board he/she wants.
- input an optional board description
Then launch the creation of the board. This will:
- create the board
- close the default lists created by trello
- create as much list as the user's keywords with the same name as the corresponding keywords.
- at last, update the beginning of the org buffer with the corresponding metadata:
- board-name
- board-id
- list-id for all lists created
(defun orgtrello/do-create-board-and-lists () "Interactive command to create a board and the lists" (interactive) (defvar orgtrello/--board-name nil) (setq orgtrello/--board-name nil) (defvar orgtrello/--board-description nil) (setq orgtrello/--board-description nil) (while (not orgtrello/--board-name) (setq orgtrello/--board-name (read-string "Please, input the desired board name: "))) (setq orgtrello/--board-description (read-string "Please, input the board description (empty for none): ")) (cl-destructuring-bind (orgtrello/--board-id orgtrello/--board-name) (orgtrello/--create-board orgtrello/--board-name orgtrello/--board-description) (let* ((orgtrello/--board-list-ids (--map (orgtrello-query/--id it) (orgtrello/--list-board-lists orgtrello/--board-id))) ;; first retrieve the existing lists (created by default on trello) (orgtrello/--lists-to-close (orgtrello/--close-lists orgtrello/--board-list-ids)) ;; close those lists (they may surely not match the name we want) (orgtrello/--board-lists-hname-id (orgtrello/--create-lists-according-to-keywords orgtrello/--board-id *LIST-NAMES*))) ;; create the list, this returns the ids list ;; remove eventual already present entry (orgtrello/--remove-properties-file orgtrello/--board-lists-hname-id) ;; update org buffer with new ones (orgtrello/update-orgmode-file-with-properties orgtrello/--board-name orgtrello/--board-id orgtrello/--board-lists-hname-id))) "Create board and lists done!") (message "org-trello - orgtrello loaded!")
- orgtrello/–create-board
The actual board creation. This return a list of board id, board name.
(defun orgtrello/--create-board (board-name &optional board-description) "Create a board with name and eventually a description." (progn (message "Creating board '%s'" board-name) (let* ((board-data (orgtrello-query/http (orgtrello-api/add-board board-name board-description) t))) (list (orgtrello-query/--id board-data) (orgtrello-query/--name board-data)))))
- orgtrello/–close-lists
The close list routine. Given a list of list ids, close each of those list.
(defun orgtrello/--close-lists (list-ids) "Given a list of ids, close those lists." (mapc (lambda (list-id) (progn (message "Closing default list with id %s" list-id) (orgtrello-query/http (orgtrello-api/close-list list-id) nil nil 'simple-error-callback))) list-ids))
- orgtrello/–create-lists-according-to-keywords
Create as much list as the keywords to the board board-id.
(defun orgtrello/--create-lists-according-to-keywords (board-id list-keywords) "Given a list of names, build those lists on the trello boards. Return the hashmap (name, id) of the new lists created." (cl-reduce (lambda (acc-hash-name-id list-name) (progn (message "Board id %s - Creating list '%s'" board-id list-name) (puthash list-name (orgtrello-query/--id (orgtrello-query/http (orgtrello-api/add-list list-name board-id) t)) acc-hash-name-id) acc-hash-name-id)) list-keywords :initial-value (make-hash-table :test 'equal)))
- orgtrello/do-create-board-and-lists
- Utility function
An utility decorator function to help in debugging without breakpoint. This is mostly for user when we need them to help us remotely debug.
(defun trace (e &optional label) "Decorator for some inaccessible code to easily 'message'." (progn (if label (message "TRACE: %s: %S" label e) (message "TRACE: %S" e)) e))
5.5 orgtrello-query
Namespace in charge of the http request to the trello api.
- Namespace Setup
Static setup the user does not need to tinker with. This resumes to only one variable which is the prefix url for the trello api access.
;; #################### orgtrello-query/ (defvar *TRELLO-URL* "https://api.trello.com/1" "The needed prefix url for trello")
- orgtrello-query/–make-dispatch-http-query
As we do not have multi-methods (as in clojure), we tried to reproduce a similar behaviour. This describes a function which will setup a map to dispatch on the method (:get, :post, :put, :delete) on the other function describes in the namespace).
This function is used only once to initialize the variable MAP-DISPATCH-HTTP-QUERY, which will be used later to dispatch on the http request built.
(defun orgtrello-query/--make-dispatch-http-query () "Make a map that will dispatch the function to call depending on the http verb :get, :put, :post, etc..." (let* ((map-dispatch (make-hash-table :test 'equal))) (puthash :get 'orgtrello-query/--get map-dispatch) (puthash :put 'orgtrello-query/--post-or-put map-dispatch) (puthash :post 'orgtrello-query/--post-or-put map-dispatch) (puthash :delete 'orgtrello-query/--delete map-dispatch) map-dispatch)) (defvar *MAP-DISPATCH-HTTP-QUERY* (orgtrello-query/--make-dispatch-http-query))
- orgtrello-query/http
This is the main function from the namespace in charge of executing the http request. We pass the query-map which is been built from the orgtrello-api namespace. Optionally, we pass a:
- success-callback in charge of doing some action when the request finishes successfully.
- error-callback in charge of doing some action when the request finishes erroneously.
- sync flag to render the request synchronous (default is asynchronous)
(defun orgtrello-query/http (query-map &optional sync success-callback error-callback) "Query the trello api asynchronously." (let* ((method (gethash :method query-map)) (fn-dispatch (gethash method *MAP-DISPATCH-HTTP-QUERY*))) (if sync (progn ;; synchronous request (puthash :sync t query-map) (let ((request-response (funcall fn-dispatch query-map success-callback error-callback))) (request-response-data request-response))) (funcall fn-dispatch query-map success-callback error-callback))))
- orgtrello-query/–map-dispatch-http-verb
As described earlier, no multi-method, we use the same techniques to setup a map to dispatch on the method (:get, :post, :put, :delete) to map to the request http client DSL. This function is again called once.
(defun orgtrello-query/--map-dispatch-http-verb () (let* ((map-dispatch (make-hash-table :test 'equal))) (puthash :get "GET" map-dispatch) (puthash :put "PUT" map-dispatch) (puthash :post "POST" map-dispatch) (puthash :delete "DELETE" map-dispatch) map-dispatch)) (defvar *MAP-DISPATCH-HTTP-VERB* (orgtrello-query/--map-dispatch-http-verb))
- orgtrello-query/–compute-method
Exploiting the dispatch map, we compute the http method (GET, POST, PUT, DELETE). We could also have implemented this using simply cond.
(defun orgtrello-query/--compute-method (method) "Given the keywords :get, :post, :put, :delete, map them into standard uppercase string." (gethash method *MAP-DISPATCH-HTTP-VERB*))
- orgtrello-query/–compute-url
A function to compute the full trello url given an uri.
(defun orgtrello-query/--compute-url (uri) "Compute the trello url from the given uri." (format "%s%s" *TRELLO-URL* uri))
- Input request abstraction extract functions
Some functions around the data stored in the query-map, hiding the implementation details of such query-map.
- orgtrello-query/–method
Extract the http method.
(defun orgtrello-query/--method (query-map) "Retrieve the http method" (gethash :method query-map))
- orgtrello-query/–uri
Extract the trello api uri.
(defun orgtrello-query/--uri (query-map) "Retrieve the http uri" (gethash :uri query-map))
- orgtrello-query/–sync
Retrieve the flag sync or not of the query-map.
(defun orgtrello-query/--sync (query-map) "Retrieve the http sync flag" (gethash :sync query-map))
- orgtrello-query/–params
Retrieve the params of the query.
(defun orgtrello-query/--params (query-map) "Retrieve the http params" (gethash :params query-map))
- orgtrello-query/–method
- Output request abstraction extract function
Some functions around the data stored in the http query response, hiding the implementation details.
- orgtrello-query/–id
Extract the id of the entity from the entity-data.
(defun orgtrello-query/--id (entity-data) "Extract the id of the entity from the entity" (assoc-default 'id entity-data))
- orgtrello-query/–name
Extract the name of the entity from the entity-data.
(defun orgtrello-query/--name (entity-data) "Extract the name of the entity from the entity" (assoc-default 'name entity-data))
- orgtrello-query/–list-id
Extract the list identifier from the entity-data.
(defun orgtrello-query/--list-id (entity-data) "Extract the list identitier of the entity from the entity" (assoc-default 'idList entity-data))
- orgtrello-query/–checklist-ids
Extract the list of checklist ids from the entity-data.
(defun orgtrello-query/--checklist-ids (entity-data) "Extract the checklist identifier of the entity from the entity" (assoc-default 'idChecklists entity-data))
- orgtrello-query/–check-items
Extract the list of items/tasks from the entity-data.
(defun orgtrello-query/--check-items (entity-data) "Extract the checklist identifier of the entity from the entity" (assoc-default 'checkItems entity-data))
- orgtrello-query/–card-id
Extract the card id from the entity-data.
(defun orgtrello-query/--card-id (entity-data) "Extract the card identifier of the entity from the entity" (assoc-default 'idCard entity-data))
- orgtrello-query/–due
Extract the card id from the entity-data.
(defun orgtrello-query/--due (entity-data) "Extract the due date of the entity from the query response" (assoc-default 'due entity-data))
- orgtrello-query/–state
Extract the state from the entity-data.
(defun orgtrello-query/--state (entity-data) "Extract the state of the entity" (assoc-default 'state entity-data))
- orgtrello-query/–close-property
Extract the close flag from the entity-data.
(defun orgtrello-query/--close-property (entity-data) "Extract the closed property of the entity" (assoc-default 'closed entity-data))
- orgtrello-query/–id
- HTTP request functions
Those represent the actual http actions. They are closures on the trello identifier (consumer-key and access-token).
- orgtrello-query/–get
The function around the GET http request. This will extract the method, uri and sync flag and build the http request. This is a closure around the consumer-key and access-token. The request's response is some json which will be parsed by the function 'json-read. In case of optional success or error callback are passed, they will be used. Otherwise, the 'standard-success-callback or the 'standard-error-callback will be used.
(defun orgtrello-query/--get (query-map &optional success-callback error-callback) "GET" (let* ((method (orgtrello-query/--method query-map)) (uri (orgtrello-query/--uri query-map)) (sync (orgtrello-query/--sync query-map))) (request (orgtrello-query/--compute-url uri) :sync sync :type (orgtrello-query/--compute-method method) :params `((key . ,*consumer-key*) (token . ,*access-token*)) :parser 'json-read :success (if success-callback success-callback 'standard-success-callback) :error (if error-callback error-callback 'standard-error-callback))))
- orgtrello-query/–post-or-put
The function around the POST/PUT verbs. This is similar to the get function but does some extract actions:
- It extracts the params (which represents the needed information for the trello entity we will sync).
- It encodes the payload in json with the json-encode function
(defun orgtrello-query/--post-or-put (query-map &optional success-callback error-callback) "POST or PUT" (let* ((method (orgtrello-query/--method query-map)) (uri (orgtrello-query/--uri query-map)) (payload (orgtrello-query/--params query-map)) (sync (orgtrello-query/--sync query-map))) (request (orgtrello-query/--compute-url uri) :sync sync :type (orgtrello-query/--compute-method method) :params `((key . ,*consumer-key*) (token . ,*access-token*)) :headers '(("Content-type" . "application/json")) :data (json-encode payload) :parser 'json-read :success (if success-callback success-callback 'standard-success-callback) :error (if error-callback error-callback 'standard-error-callback))))
- orgtrello-query/–delete
The last verb we deal with, the DELETE action. Again similar as GET but does a DELETE.
(defun orgtrello-query/--delete (query-map &optional success-callback error-callback) "DELETE" (let* ((method (orgtrello-query/--method query-map)) (uri (orgtrello-query/--uri query-map)) (sync (orgtrello-query/--sync query-map))) (request (orgtrello-query/--compute-url uri) :sync sync :type (orgtrello-query/--compute-method method) :params `((key . ,*consumer-key*) (token . ,*access-token*)) :success (if success-callback success-callback 'standard-success-callback) :error (if error-callback error-callback 'standard-error-callback)))) (message "org-trello - orgtrello-query/ loaded!")
- orgtrello-query/–get
- HTTP callbacks
Those are actions done at the end of the http request.
- standard-error-callback
The standard error-callback in charge of removing the orgtrello-marker written before synchronization. This display an error message on the minibuffer too.
(cl-defun standard-error-callback (&key error-thrown &allow-other-keys) "Standard error callback" (save-excursion ;; find the current entry through the pointer (org-goto-local-search-headings *ORGTRELLO-MARKER* nil t) ;; remove the marker now that we're done (org-delete-property *ORGTRELLO-MARKER*)) (message "There was some problem during the request to trello: %s" error-thrown))
- standard-success-callback
A standard success callback that displays a simple message "Success." in the minibuffer.
(cl-defun standard-success-callback () "Standard success callback" (message "Success."))
- simple-error-callback
A simpler error message callback which simply displays an error message on the minibuffer, indicating the error thrown by the execution of the request.
(cl-defun simple-error-callback (&key error-thrown &allow-other-keys) "Standard error callback" (message "There was some problem during the request to trello: %s" error-thrown))
- orgtrello-query/–delete-success-callback
This one is the default delete success callback. It needs to synchronize back the buffer to remove the entry we just destroyed on trello.
- Return to the heading we want to delete
- Delete the property which represents the trello identifier
- Remove the full entity (for this we hide the subtree)
- Go at the beginning of the line
- Kill the next 2 lines
- At last display a simple message to notify the end of the removal.
(cl-defun orgtrello-query/--delete-success-callback (&key data response &allow-other-keys) "Callback function called at the end of a successful delete request." (progn (org-back-to-heading t) (org-delete-property *ORGTRELLO-ID*) (hide-subtree) (beginning-of-line) (kill-line) (kill-line) (message "Entity deleted!")))
- orgtrello-query/–post-put-success-callback-update-id
This one is the default post/put success callback. It's in charge of updating the org buffer with the trello id for the entity that has been synchronized. What it does:
- Get back to the current max header
- Then place itself to the org-trello marker put at the beginning of the synchronization.
- Remove such marker
- Get the current header information
- If an identifier is already present, simply display a message. Otherwise, add the property orgtrello-id with the identifier returned by the query.
(cl-defun orgtrello-query/--post-put-success-callback-update-id (&key data &allow-other-keys) "Called back function at the end of the post/put request to update the trello id in the org-mode file." (let* ((orgtrello-query/--entry-new-id (orgtrello-query/--id data)) (orgtrello-query/--entry-name (orgtrello-query/--name data))) ;; will update via tag the trello id of the new persisted data (if needed) (save-excursion ;;(while (org-up-heading-safe)) ;; find the current entry through the pointer (org-goto-local-search-headings *ORGTRELLO-MARKER* nil t) ;; remove the marker now that we're done (org-delete-property *ORGTRELLO-MARKER*) ;; now we extract the data (let* ((orgtrello-query/--entry-metadata (orgtrello-data/metadata)) (orgtrello-query/--entry-id (orgtrello/--id orgtrello-query/--entry-metadata))) (if orgtrello-query/--entry-id ;; id already present in the org-mode file ;; no need to add another (message "Entity '%s' synced with id '%s'" orgtrello-query/--entry-name orgtrello-query/--entry-id) (progn ;; not present, this was just created, we add a simple property (org-set-property *ORGTRELLO-ID* orgtrello-query/--entry-new-id) (message "Newly entity '%s' synced with id '%s'" orgtrello-query/--entry-name orgtrello-query/--entry-new-id)))))))
- standard-error-callback
5.6 orgtrello-api
This is the namespace responsible for the interface with trello's public api. We have decoupled the http request from the http request data structure. This way, we can test the api's request.
- orgtrello-api/add-board
A simple function to create a board. We need a name and an optional description.
;; #################### orgtrello-api (defun orgtrello-api/add-board (name &optional description) "Create a board" (let* ((payload (if description `(("name" . ,name) ("desc" . ,description)) `(("name" . ,name))))) (orgtrello-hash/make-hash :post "/boards" payload)))
- orgtrello-api/get-boards
A function to retrieve the list of boards of the user.
(defun orgtrello-api/get-boards () "Retrieve the boards of the current user." (orgtrello-hash/make-hash :get "/members/me/boards"))
- orgtrello-api/get-board
A function to retrieve all the information about the board with id.
(defun orgtrello-api/get-board (id) "Retrieve the boards of the current user." (orgtrello-hash/make-hash :get (format "/boards/%s" id)))
- orgtrello-api/get-cards
Retrieve all the cards from the board with id board-id.
(defun orgtrello-api/get-cards (board-id) "cards of a board" (orgtrello-hash/make-hash :get (format "/boards/%s/cards" board-id)))
- orgtrello-api/get-card
Retrieve the information of the specific card with id card-id.
(defun orgtrello-api/get-card (card-id) "Detail of a card with id card-id." (orgtrello-hash/make-hash :get (format "/cards/%s" card-id)))
- orgtrello-api/delete-card
Delete the card with id card-id.
(defun orgtrello-api/delete-card (card-id) "Delete a card with id card-id." (orgtrello-hash/make-hash :delete (format "/cards/%s" card-id)))
- orgtrello-api/get-lists
Retrieve the lists of the board with id board-id.
(defun orgtrello-api/get-lists (board-id) "Display the lists of the board" (orgtrello-hash/make-hash :get (format "/boards/%s/lists" board-id)))
- orgtrello-api/close-list
We need a routine to be able to close a list (typically when we create a board, we do not want the default list created by trello). So a function to close a specific list with id list-id.
(defun orgtrello-api/close-list (list-id) "'Close' the list with id list-id." (orgtrello-hash/make-hash :put (format "/lists/%s/closed" list-id) '((value . t))))
- orgtrello-api/get-list
Retrieve the information about the list with id list-id.
(defun orgtrello-api/get-list (list-id) "Get a list by id" (orgtrello-hash/make-hash :get (format "/lists/%s" list-id)))
- orgtrello-api/add-list
Adding a list named name to a board with id idBoard.
(defun orgtrello-api/add-list (name idBoard) "Add a list - the name and the board id are mandatory (so i say!)." (orgtrello-hash/make-hash :post "/lists/" `(("name" . ,name) ("idBoard" . ,idBoard))))
- orgtrello-api/add-card
Adding a card with name name to the list idList. Optionally, we can use a due date.
(defun orgtrello-api/add-card (name idList &optional due) "Add a card to a board" (let* ((orgtrello-api/add-card--default-params `(("name" . ,name) ("idList" . ,idList))) (orgtrello-api/add-card--params (if due (cons `("due" . ,due) orgtrello-api/add-card--default-params) orgtrello-api/add-card--default-params))) (orgtrello-hash/make-hash :post "/cards/" orgtrello-api/add-card--params)))
- orgtrello-api/get-cards-from-list
A function to retrieve the full list of cards a list with id list-id owns.
(defun orgtrello-api/get-cards-from-list (list-id) "List all the cards" (orgtrello-hash/make-hash :get (format "/lists/%s/cards" list-id)))
- orgtrello-api/move-card
An update function for the card card-id which belongs to the idList. This can simply move the card from its current list to another. Optionally, we can rename the card to name and update its due date.
(defun orgtrello-api/move-card (card-id idList &optional name due) "Move a card to another list" (let* ((orgtrello-api/move-card--default-params `(("idList" . ,idList))) (orgtrello-api/move-card--params-name (if name (cons `("name" . ,name) orgtrello-api/move-card--default-params) orgtrello-api/move-card--default-params)) (orgtrello-api/move-card--params-due (if due (cons `("due" . ,due) orgtrello-api/move-card--params-name) orgtrello-api/move-card--params-name))) (orgtrello-hash/make-hash :put (format "/cards/%s" card-id) orgtrello-api/move-card--params-due)))
- orgtrello-api/add-checklist
We can add a checklist entitled name to the card with id card-id.
(defun orgtrello-api/add-checklist (card-id name) "Add a checklist to a card" (orgtrello-hash/make-hash :post (format "/cards/%s/checklists" card-id) `(("name" . ,name))))
- orgtrello-api/update-checklist
Also, we can update the checklist with id checklist-id to a new name name.
(defun orgtrello-api/update-checklist (checklist-id name) "Update the checklist's name" (orgtrello-hash/make-hash :put (format "/checklists/%s" checklist-id) `(("name" . ,name))))
- orgtrello-api/get-checklists
Retrieve the list of checklist from the card with id card-id.
(defun orgtrello-api/get-checklists (card-id) "List the checklists of a card" (orgtrello-hash/make-hash :get (format "/cards/%s/checklists" card-id)))
- orgtrello-api/get-checklist
Or simply retrieve the detail about the checklist with id checklist-id.
(defun orgtrello-api/get-checklist (checklist-id) "Retrieve all the information from a checklist" (orgtrello-hash/make-hash :get (format "/checklists/%s" checklist-id)))
- orgtrello-api/delete-checklist
We need to be able to remove checklist checklist-id.
(defun orgtrello-api/delete-checklist (checklist-id) "Delete a checklist with checklist-id" (orgtrello-hash/make-hash :delete (format "/checklists/%s" checklist-id)))
- orgtrello-api/add-tasks
Adding a task/item entitled name to the checklist checklist-id with an optional checked state.
(defun orgtrello-api/add-tasks (checklist-id name &optional checked) "Add todo tasks (trello items) to a checklist with id 'id'" (let* ((payload (if checked `(("name" . ,name) ("checked" . ,checked)) `(("name" . ,name))))) (orgtrello-hash/make-hash :post (format "/checklists/%s/checkItems" checklist-id) payload)))
- orgtrello-api/update-task
A function to deal with the update of the task/item with id task-id and name name from the checklist checklist-id (trello api forces the card-id too). Optionally, we can change its checked state.
(defun orgtrello-api/update-task (card-id checklist-id task-id name &optional state) "Update a task" (let* ((payload (if state `(("name" . ,name) ("state" . ,state)) `(("name" . ,name))))) (orgtrello-hash/make-hash :put (format "/cards/%s/checklist/%s/checkItem/%s" card-id checklist-id task-id) payload)))
- orgtrello-api/get-tasks
Retrieve the list of tasks from the checklist checklist-id.
(defun orgtrello-api/get-tasks (checklist-id) "List the checklist items." (orgtrello-hash/make-hash :get (format "/checklists/%s/checkItems/" checklist-id)))
- orgtrello-api/delete-task
To keep the symmetry with the other entities, we need to be able to destroy the task/item task-id from the checklist checklist-id.
(defun orgtrello-api/delete-task (checklist-id task-id) "Delete a task with id task-id" (orgtrello-hash/make-hash :delete (format "/checklists/%s/checkItems/%s" checklist-id task-id))) (message "org-trello - orgtrello-api loaded!")
5.7 orgtrello-data
Namespace in charge of manipulating/extracting the org buffer's data.
- orgtrello-data/–convert-orgmode-date-to-trello-date
A simple function to help in transforming the org format used in deadline into trello format. This is really basic. We must improve this.
;; #################### orgtrello-data (defvar *ORGTRELLO-ID* "orgtrello-id" "Marker used for the trello identifier") (defun orgtrello-data/--convert-orgmode-date-to-trello-date (orgmode-date) "Convert the org-mode deadline into a time adapted for trello." (if (and orgmode-date (not (string-match-p "T*Z" orgmode-date))) (cl-destructuring-bind (sec min hour day mon year dow dst tz) (--map (if it (if (< it 10) (concat "0" (int-to-string it)) (int-to-string it))) (parse-time-string orgmode-date)) (let* ((year-mon-day (concat year "-" mon "-" day "T")) (hour-min-sec (if hour (concat hour ":" min ":" sec) "00:00:00"))) (concat year-mon-day hour-min-sec ".000Z"))) orgmode-date))
- orgtrello-data/metadata
This is an important routine which will extract all the informations from the org buffer for a given entity (typically the entity under the caret's current position):
- level
- label
- id
- due
This uses org's api and add some extra information specific to org-trello.
(defun orgtrello-data/metadata () "Compute the metadata from the org-heading-components entry, add the identifier and extract the metadata needed." (let* ((orgtrello-data/metadata--id (org-entry-get (point) *ORGTRELLO-ID*)) (orgtrello-data/metadata--due (orgtrello-data/--convert-orgmode-date-to-trello-date (org-entry-get (point) "DEADLINE"))) (orgtrello-data/metadata--metadata (org-heading-components))) (->> orgtrello-data/metadata--metadata (cons orgtrello-data/metadata--due) (cons orgtrello-data/metadata--id) orgtrello-data/--get-metadata)))
- orgtrello-data/–parent-metadata
This function permits to retrieve the metadata of the current entry. Note that if we are to the upper level already, this will return the exact same data as the current entry.
(defun orgtrello-data/--parent-metadata () "Extract the metadata from the current heading's parent." (save-excursion (org-up-heading-safe) (orgtrello-data/metadata)))
- orgtrello-data/–grandparent-metadata
Again, from a current entry, return the metadata of the grand-parent entry. Note again that if we are to the upper level, the same data as the current entry will be returned.
(defun orgtrello-data/--grandparent-metadata () "Extract the metadata from the current heading's grandparent." (save-excursion (org-up-heading-safe) (org-up-heading-safe) (orgtrello-data/metadata)))
- orgtrello-data/entry-get-full-metadata
This is an orchestration from the previous functions. This will return a map with as key:
- :current the current entity under the caret's position
- :parent the current entity's parent
- :grandparent the current entity's grandparent
If the level of the current entity exceeds level 4, the entry is dismissed (only 3 levels can be compatible in trello) so the function will return nil.
(defun orgtrello-data/entry-get-full-metadata () "Compute the metadata needed for one entry into a map with keys :current, :parent, :grandparent. Returns nil if the level is superior to 4." (let* ((heading (orgtrello-data/metadata)) (level (gethash :level heading))) (if (< level 4) (let* ((parent-heading (orgtrello-data/--parent-metadata)) (grandparent-heading (orgtrello-data/--grandparent-metadata)) (mapdata (make-hash-table :test 'equal))) ;; build the metadata with every important pieces (puthash :current heading mapdata) (puthash :parent parent-heading mapdata) (puthash :grandparent grandparent-heading mapdata) mapdata))))
- orgtrello-data/–get-metadata
An adapter function to transform any org data into org-trello data.
(defun orgtrello-data/--get-metadata (heading-metadata) "Given the heading-metadata returned by the function 'org-heading-components, make it a hashmap with key :level, :keyword, :title. and their respective value" (cl-destructuring-bind (id due level _ keyword _ title &rest) heading-metadata (orgtrello-hash/make-hash-org level keyword title id due))) (message "org-trello - orgtrello-data loaded!")
5.8 orgtrello-hash
Utility namespace in charge of simplifying the construction of org-trello data.
- orgtrello-hash/make-hash-org
A simple factory function to create a map from the needed org entity data.
;; #################### orgtrello-hash (defun orgtrello-hash/make-hash-org (level keyword title id due) "Utility function to ease the creation of the orgtrello-metadata" (let ((h (make-hash-table :test 'equal))) (puthash :level level h) (puthash :keyword keyword h) (puthash :title title h) (puthash :id id h) (puthash :due due h) h))
- orgtrello-hash/make-hash
A simple utility function to help in creating the http request map to feed to the orgtrello-query namespace.
(defun orgtrello-hash/make-hash (method uri &optional params) "Utility function to ease the creation of the map - wait, where are my clojure data again!?" (let ((h (make-hash-table :test 'equal))) (puthash :method method h) (puthash :uri uri h) (if params (puthash :params params h)) h)) (message "org-trello - orgtrello-hash loaded!")
6 Resulting
Now let's generate the full code.
7 Source
Project: http://ardumont.github.io/org-trello/ source: org-trello.org