6097 comments
2357 subscribers
6256 on Twitter
Subscribe! Feed reader E-mail

On this page:

Buying time: Experimenting with scheduling

One of my business validation experiments has suddenly kicked into high gear. People love the sketchnotes I’ve been taking. Since they’re interested in illustration and event coverage, I’m happy to take advantage of that opportunity to learn more about business. I want to see where we can take this. In addition, I want to connect with way more people and find out how I can help them.

I’ve been investing more time into delegation as a way to buy time and share opportunities. One of the small processes I’d like to delegate is scheduling, which can be quite stressful for me. I often review my mail on the subway, and it’s difficult for me to look up locations or refer to notes. I worry about time zones and missed connections, so I want to make sure that there are calendar entries at the right time, with the right people, and with backup contact information. As I dig deeper into validating business ideas and connecting with people, I’ll be trying to set up appointments with so many people that I’d worry about dropping the ball, not getting back to people, or not following up in case people haven’t gotten back to me.

Software tools such as ScheduleOnce aren’t quite there yet in terms of completely handling the scheduling process. I want to be able to delegate appointment-setting to someone who can arrange times, suggest venues, make sure important information is included in the event description, and follow up as needed.

One of my friends was open to the idea of working as my virtual assistant, so I set her up on oDesk and gave her access to my accounts. It’s good to know people you can trust with your Google account. While you can delegate without giving full mail and calendar access, some things are just easier when people can find what they need. I did set up a separate email account for her so that she wouldn’t have to clutter her personal mail with all the requests.

Even though we’re just starting out, it’s such a relief to be able to forward her mail and know that she’s going to keep track of things. We’re not quite at a smoothly running process yet, but maybe we’ll get there in the next few weeks. I’d love to get to the point where I have a few keyboard shortcuts for templates that explain what’s going to happen (including details she’ll need in order to plan), and she’ll follow up and make it happen. I’d also like to be able to keep track of the people that we’re trying to reach and where we are in the process. She’s been busy catching up with other work this week, but I hope that as her coursework settles down and we work out the kinks in the process, things will run even more smoothly.

I’m also experimenting with automated ways to make it easier to arrange times. ScheduleOnce seems to be the most promising of the bunch. Doodle often gets timezones quietly wrong, and Tungle is just about to close its doors. I’m not completely sold on ScheduleOnce, but people seem to have the fewest problems scheduling with it. There’s so much more to setting appointments, though.

If we’ve got a lunch, coffee, or call coming up in the next month or so, I appreciate your patience as we experiment with the scheduling process!

Short URL: http://sachachua.com/blog/p/24002

Finding out if I’m overscheduled

If you’re anything like me, your task list is long and growing, but
the time you have is just as fixed. How do you manage that? I handle
that problem by looking at my calendar and my task list together when
I’m planning my day. I tend to be too optimistic about my tasks,
trying to schedule more into my day than I should. Fortunately, Emacs
can help me make sure I don’t overcommit. Here’s how it works.

When I create and schedule tasks, I try my best to add time estimates.
The numbers in the TODO headline represent minutes.

* TODO 15 Announce tea party
  SCHEDULED: <2007-12-08 Sat>
* DONE 30 Drop letters off at post office
  SCHEDULED: <2007-12-08 Sat>
* TODO 60 Write blog post about tasks
  SCHEDULED: <2007-12-08 Sat>
* TODO 60 Make book notes
  SCHEDULED: <2007-12-09 Sun>
* TODO 30 Start on my letter for 2007
  SCHEDULED: <2007-12-08 Sat>
* TODO 60 Follow up with DemoCamp contacts
  SCHEDULED: <2007-12-08 Sat>
  DEADLINE: <2007-12-09 Sun>
* TODO 60 Respond to mail
  SCHEDULED: <2007-12-08 Sat>

Then my custom agenda view looks like this:

Day-agenda:
Saturday   8 December 2007
  6:00......  --------------------
  8:00......  --------------------
 10:00......  --------------------
 12:00......  --------------------
 14:00......  --------------------
 14:00-17:15  Scheduled:  TODO Take another driving lesson - emergency stuff
 16:00......  --------------------
 18:00......  --------------------
 20:00......  --------------------
 22:00......  --------------------
              In   1 d.:  TODO 60 Follow up with DemoCamp contacts
              Scheduled:  TODO 15 Announce tea party
              Scheduled:  TODO 60 Write blog post about tasks
              Scheduled:  TODO 30 Start on my letter for 2007
              Scheduled:  TODO 60 Follow up with DemoCamp contacts
              Scheduled:  TODO 60 Respond to mail
              In 937 d.:  101 things in 1001 days
62.7% load: 225 minutes to be scheduled, 359 minutes free, 134 minutes gap

I’ll need to figure out over the next few weeks what kind of a load
threshold is good (you really don’t want to try for 100%), but at
least it’s visible!

(setq org-agenda-custom-commands
      '(("i" "My agenda"
         ((org-agenda-list nil nil 1)
          (sacha/org-load)))
        ;; ... other stuff goes here
      ))

(defun sacha/org-show-load ()
  "Show my unscheduled time and free time for the day."
  (interactive)
  (let ((time (sacha/org-calculate-free-time
               ;; today
               (calendar-gregorian-from-absolute (time-to-days (current-time)))
               ;; now
               (let* ((now (decode-time))
                      (cur-hour (nth 2 now))
                      (cur-min (nth 1 now)))
                 (+ (* cur-hour 60) cur-min))
               ;; until the last time in my time grid
               (let ((last (car (last (elt org-agenda-time-grid 2)))))
                 (+ (* (/ last 100) 60) (% last 100))))))
    (message "%.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-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, else start of day
               (if (= org-starting-day
                      (time-to-days (current-time)))
                   (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))))
                 ;; 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]+ \\([0-9]+\\)" (get-text-property 1 'txt entry)))
          (setq scheduled-entries
                (let ((start (and (string-match "\\([^-]+\\)\\.+" time)
                                 (appt-convert-time (match-string 1 time)))))
                  (cons (cons start
                              (and (string-match "^[A-Z]+ \\([0-9]+\\)" (get-text-property 1 'txt entry))
                                   (+ start (string-to-number (match-string 1 (get-text-property 1 'txt entry))))))
                        scheduled-entries))))
         ((string-match "^[A-Z]+ \\([0-9]+\\)" (get-text-property 1 'txt entry))
          (setq total-unscheduled (+ (string-to-number
                                      (match-string 1 (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: select-safe-coding-system-function – Variable: Function to call to select safe coding system for encoding a text.

Short URL: http://sachachua.com/blog/p/4494

Get the highlights as a PDF!

Stories from my Twenties: Highlights from a Decade of Blogging

Free sample!