sacha chua :: living an awesome life

2156 blog subscribers
2750 on Twitter
Subscribe!
E-mail Feed reader

Bugfix: Time estimation

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.

So-soHmmGoodGreatAwesome! (No Ratings Yet)
Loading ... Loading ...
Save to - del.icio.us - Digg it - reddit - StumbleUpon -

2 Responses to “Bugfix: Time estimation”

  1. Hi Sacha,

    I really like your blog and found so many good tricks for the use of emacs.

    Unfortunately I couldn’t get the bugfix for time estimation running on Aquamacs 1.5, Mac OS 10.4. I got the other code from “Finding out if I’m overscheduled” working though. I get the following error: “wrong type argument: stringp, nil”. I tried to substitude the old code with the new one the .emacs file. What do I have to do to get it running?

    Thanx,
    Stefan
    Bayreuth, Germany

  2. I also got the same error of “wrong type argument: stringp, nil”using this bug-fixed version of sacha/org-calculate-free-time. It happens with appt-convert-time, when a nil string was passed to it. I guess that it’s caused by some regular expression for time expression not complete enough.

    I’ll do some debug to try to fix it, or at least add some protection there, and post here.

    If there is already some fix, please post here to save my time.

    Really appreciate Sacha’s dedication to emacs, which helped me to resume using emacs after departing for 5 years. Now I find that emacs, especially, org-mode and remember is still the best ever for productivity management.

    Thanks,

    Yu

Discussion Area - Leave a Comment

Please comment as you, not your organization.





On This Day...

  • 2009: How you know your training sessions are working; Remote Presentations That Rock — We facilitated “Remote Presentations That Rock” for the second women’s leadership group a few weeks ago. After watching the video, [...]
  • 2007: Emacs: Choosing between Org and Planner — jaaronfarr asked me why I switched from Planner to Org. Both of them are popular personal information managers for Emacs, [...]
  • 2006: Back from wakeboarding — We spent the afternoon at the Camsur Watersports Complex. My sisters and I tried wakeboarding. Now I can barely raise my [...]
  • 2006: My favorite blog posts — Here are my other favorite blog posts: 2006.01.07#2: Things every geek should know 2006.02.05#2: Why I like the Emacs editor 2006.03.18#3: Upon reflection 2006.03.26#1: [...]
  • 2006: Thoughts on life — I’ve thought a fair bit about life this year, and I’m getting a better idea of what I want to do. [...]
  • 2006: A year in review: Emacs — Over the last five months, I’ve added all sorts of code to my Emacs to make it a contact relationship management [...]
  • 2006: I keep forgetting that I’m on vacation — Me: “So, what are we doing today?” Mom: “Nothing.” <brain implodes> Hmm. Maybe I should meditate a little bit before going to the [...]
  • 2006: Ay, my sister — So my sister’s drafting a letter to this guy she really likes, and she ends up explaining her feelings in terms [...]
  • 2006: From the Freeciv AI documentation — The code base used to be in a bad shape but it has gotten a lot better. The reason for [...]
  • 2006: Slowly but surely making progress — Goal: inbox zero by New Year! This will be somewhat easier if I don’t check mail between now and then, of [...]
  • 2006: Another day, another sunrise — This time from a watersports resort said to be the best in the world for wakeboarding. I’ll give it a shot, [...]
  • 2005: Fireworks festival — Went to the first day of the international fireworks festival at Manila Bay. Papa wrangled a press pass. =) Took pictures, [...]