December 26, 2007

Bugfix: Time estimation

December 26, 2007 - Categories: emacs, org

This is a slightly better version of sacha/org-calculate-free-time that can understand prioritized tasks. Also, if you're working after midnight, it takes into account the need to sleep. That is, it doesn't treat _now_ as the starting point for the free-time calculation unless it's after the start time, so if you're up at 12:30 writing Emacs Lisp code, Emacs doesn't assume you've got 450 extra minutes of work time.

This is what the result looks like:

Day-agenda:
Wednesday 26 December 2007
   8:00......  --------------------
  10:00......  --------------------
  12:00......  --------------------
 Scheduled:  12:00-21:00  TODO Christmas festivities with W-'s family
  14:00......  --------------------
  16:00......  --------------------
  18:00......  --------------------
  20:00......  --------------------
  22:00......  --------------------
 Scheduled:               TODO [#A] 120 Write Planner and Org comparison for tasks
 Scheduled:               TODO 30 Start on my letter for 2007
 Scheduled:               TODO 60 Respond to e-mail
 Scheduled:               TODO 30 Donate when my charity budget hits $1000
 Scheduled:               WAITING 30 Send money to Ateneo CS dept
 In 919 d.:               101 things in 1001 days
90.0% load: 270 minutes to be scheduled, 300 minutes free, 30 minutes ga

And here's the code:

(defun sacha/org-agenda-load (match)
  "Can be included in `org-agenda-custom-commands'."
  (let ((inhibit-read-only t)
        (time (sacha/org-calculate-free-time
               ;; today
               (calendar-gregorian-from-absolute org-starting-day)
               ;; now if today AND after starting time, else start of day
               (if (= org-starting-day
                      (time-to-days (current-time)))
                   (max
                    (let* ((now (decode-time))
                           (cur-hour (nth 2 now))
                           (cur-min (nth 1 now)))
                      (+ (* cur-hour 60) cur-min))
                    (let ((start (car (elt org-agenda-time-grid 2))))
                      (+ (* (/ start 100) 60) (% start 100))))
                 (let ((start (car (elt org-agenda-time-grid 2))))
                   (+ (* (/ start 100) 60) (% start 100))))
                 ;; until the last time in my time grid
               (let ((last (car (last (elt org-agenda-time-grid 2)))))
                 (+ (* (/ last 100) 60) (% last 100))))))
    (goto-char (point-max))
    (insert (format
             "%.1f%% load: %d minutes to be scheduled, %d minutes free, %d minutes gap"
             (/ (car time) (* .01 (cdr time)))
             (car time)
             (cdr time)
             (- (cdr time) (car time))))))

(defun sacha/org-calculate-free-time (date start-time end-of-day)
  "Return a cons cell of the form (TASK-TIME . FREE-TIME) for DATE, given START-TIME and END-OF-DAY.
DATE is a list of the form (MONTH DAY YEAR).
START-TIME and END-OF-DAY are the number of minutes past midnight."
  (save-window-excursion
  (let ((files org-agenda-files)
        (total-unscheduled 0)
        (total-gap 0)
        file
        rtn
        rtnall
        entry
        (last-timestamp start-time)
        scheduled-entries)
    (while (setq file (car files))
      (catch 'nextfile
        (org-check-agenda-file file)
        (setq rtn (org-agenda-get-day-entries file date :scheduled :timestamp))
        (setq rtnall (append rtnall rtn)))
      (setq files (cdr files)))
    ;; For each item on the list
    (while (setq entry (car rtnall))
      (let ((time (get-text-property 1 'time entry)))
        (cond
         ((and time (string-match "\\([^-]+\\)-\\([^-]+\\)" time))
          (setq scheduled-entries (cons (cons
                                         (save-match-data (appt-convert-time (match-string 1 time)))
                                         (save-match-data (appt-convert-time (match-string 2 time))))
                                        scheduled-entries)))
         ((and time
               (string-match "\\([^-]+\\)\\.+" time)
               (string-match "^[A-Z]+ \\(\\[#[A-Z]\\] \\)?\\([0-9]+\\)" (get-text-property 1 'txt entry)))
          (setq scheduled-entries
                (let ((start (and (string-match "\\([^-]+\\)\\.+" time)
                                 (appt-convert-time (match-string 2 time)))))
                  (cons (cons start
                              (and (string-match "^[A-Z]+ \\(\\[#[A-Z]\\] \\)?\\([0-9]+\\)" (get-text-property 1 'txt entry))
                                   (+ start (string-to-number (match-string 2 (get-text-property 1 'txt entry))))))
                        scheduled-entries))))
         ((string-match "^[A-Z]+ \\(\\[#[A-Z]\\] \\)?\\([0-9]+\\)" (get-text-property 1 'txt entry))
          (setq total-unscheduled (+ (string-to-number
                                      (match-string 2 (get-text-property 1 'txt entry)))
                                     total-unscheduled)))))
      (setq rtnall (cdr rtnall)))
    ;; Sort the scheduled entries by time
    (setq scheduled-entries (sort scheduled-entries (lambda (a b) (< (car a) (car b)))))

    (while scheduled-entries
      (let ((start (car (car scheduled-entries)))
            (end (cdr (car scheduled-entries))))
      (cond
       ;; are we in the middle of this timeslot?
       ((and (>= last-timestamp start)
             (<= last-timestamp end))
        ;; move timestamp later, no change to time
        (setq last-timestamp end))
       ;; are we completely before this timeslot?
       ((< last-timestamp start)
        ;; add gap to total, skip to the end
        (setq total-gap (+ (- start last-timestamp) total-gap))
        (setq last-timestamp end)))
      (setq scheduled-entries (cdr scheduled-entries))))
    (if (< last-timestamp end-of-day)
        (setq total-gap (+ (- end-of-day last-timestamp) total-gap)))
    (cons total-unscheduled total-gap))))

On Technorati: ,

Random Emacs symbol: gnus-summary-display-while-building - Variable: If non-nil, show and update the summary buffer as it's being built.

Emacs: Choosing between Org and Planner

December 26, 2007 - Categories: emacs, org, planner, wickedcoolemacs

jaaronfarr asked me why I switched from Planner to Org. Both of them are popular personal information managers for Emacs, and both of them have practically all the features I need. They both do a good job at helping people manage tasks, schedule, and notes. If you have a few months to explore this, I suggest that you try both for at least a month each. On the other hand, if you want quick results, some time thinking about how you plan can save you more time later.

I tried out Org because I was working on a chapter about schedule management and it wasn't fair to just rely on the manual or the mailing list. In the beginning, I felt frustrated by the lack of things I'd gotten used to in Planner: the freedom to edit anything on my day page, little conveniences like +2tue to mean two Tuesdays from now (which Carsten has just added), publishing my blog...

After two months of using Org almost every day, I'm starting to understand it. I've come to appreciate the ease of working with an outline. I love the way it clocks time. I find the daily and weekly views really helpful. I've hacked stuff for it: time/load estimation, time reporting, next action summaries, agenda publishing... I'm fairly deeply

As a geek, I have to confess—I like Planner more. Planner is more fun to code. Maybe it's because I've spent years with it, and I know my way around the source.

Maybe it's because we split Planner up into lots of little pieces that can be reused and advised. Maybe the modularity of Planner is because chunks of the code were written on a computer with a teensy screen, which forced me to write functions that fit 80x48 characters. (See, that limitation was there for a reason!) There are plenty of entry points. From time to time, I still find myself copying an entire function in order to change something in the middle, but usually I can just get away with wrapping something around something else. With Org, I find myself doing a lot of copy-and-paste programming. I'd fix this by breaking the functions down into smaller bits, but I don't have the brainspace right now. Maybe after the book.

Org is better for my brain, though. It gives me a better overview of both the ground-level tasks (what am I going to do right now, today, this week) as well as the 50,000-foot view (what are my big projects)? Planner's good at the ground-level tasks, but the overview's always been a little awkward because it has to visit a number of files to get a big picture. Org handles that easily.

And the one-place-for-data thing of Org is pretty cool, too. Org dynamically generates reports, which could take a bit longer if you have a large Org file. Planner copies data wherever it makes sense, so you'd have a copy of the task on your day page and a copy of the task on your plan page. Plan pages can get out of sync unless you're either religious about using planner-edit-task-description and other functions to edit your tasks, or you use planner-id and you're lucky. Timeclock entries get out of sync, too. You can trust Org more than Planner in terms of consistency.

So now I'm kinda in the middle of these two modules. I use Org for all my work tasks, and I'm moving towards using it for all of my personal tasks as well. But I still keep my blog entries in Planner, even though they get mirrored into a WordPress blog on my web server.

*What would I recommend?*

It depends on the way you think. If you're the kind of person who was never happy with day planners because you needed more space to doodle, write notes, move things around, add other things, try out Planner. If you like outlines and organizing your tasks into projects, try out Org.

Note that just because you work that way now doesn't mean you'll work that way in the future. Don't worry. Emacs will adapt. You can switch between Planner and Org fairly easily. Just give yourself a week or so to adjust (or a month if you've customized your old tool extensively and miss lots of things about it). Use the tool every day, and you'll be fine.

Have you tried out both? Or have you tried out one of them and are curious about the other? I'd love to learn from your experience or answer your questions.

On Technorati: , , ,

Random Emacs symbol: malayalam-composition-function - Function: Compose Malayalam characters in REGION, or STRING if specified.