Skip to content

Instantly share code, notes, and snippets.

@ebot
Created September 30, 2008 22:08
Show Gist options
  • Save ebot/13972 to your computer and use it in GitHub Desktop.
Save ebot/13972 to your computer and use it in GitHub Desktop.
A script that generates a gtd worlist from your backpack pages.
#!/usr/bin/env ruby
# Creates a weekly gtd report from your backpack account
# Based on the sample wrapper script from
# David Heinemeier Hansson, 37signals and
# Gmail sample script from shunsuk
# Requires that XmlSimple, RedCloth, tlsmail, and time
# are installed.
# Assumptions:
# ** All personal/home pages start with a space ' '
# ** All closed projects start with zzc
# ** All waiting projects start with zw
# Author: Ed Botzum
require 'net/https'
require 'rexml/document'
require 'yaml'
require 'rubygems'
require 'RedCloth'
require 'XmlSimple'
require 'tlsmail'
require 'time'
include REXML
class Gmail
attr_accessor :username, :password
def initialize(username, password)
@username, @password = username, password
end
def send_mail(recepient, subject, content)
msg = <<EOF
From: #{@username}
To: #{recepient}
MIME-Version: 1.0
Content-type: text/html
Subject: #{subject}
Date: #{Time.now.rfc2822}
#{content}
EOF
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
Net::SMTP.start('smtp.gmail.com', 587, 'gmail.com', @username, @password, :login) do |smtp|
smtp.send_message(msg, @username, recepient)
end
end
end
class Backpack
attr_accessor :username, :token, :ignore_pages
def initialize(username, token)
@username, @token = username, token
connect
end
def generate_gtd_report
report = <<EOF
<style type='text/css' media='screen'>
li {font-size: .8em;}
h1 {border-bottom: solid thick black;padding-left: .3em;}
h2 {border-bottom: solid thin black}
h3 {border-bottom: solid thin black}
#active_projects h4 {border-bottom: solid thin black}
</style>
EOF
report << "h1. My Worklist\n" <<
"\nThis is your weekly GTD wrklist report.\n"
# List all reminders for the upcoming week
report << "\nh2. Reminders\n\n"
list_reminders.each do |reminder|
report << "* #{reminder['remind_at']}: #{reminder['content']}\n"
end
# Get the list of pages
projects = { :home => [], :work => [], :waiting => [], :closed => [] }
list_pages.each do |page|
page_info = {:title => page['title'], :id => page['id']}
# Split into groups of home, work, closed, and waiting
if page['title'][0,1] == ' ' then
projects[:home] << page_info
elsif page['title'][0,3] == 'zzc'
projects[:closed] << page_info
elsif page['title'][0,2] == 'zw'
projects[:waiting] << page_info
else
projects[:work] << page_info
end
end
report << "\nh2. Projects\n"
# List all work projects
report << "\n<div id=\"active_projects\">\n"
report << list_projects('work', 'Work Projects', projects[:work])
report << list_projects('home', 'Home Projects', projects[:home])
report << "\n</div>\n"
report << list_projects('waiting', 'Projects on Hold', projects[:waiting])
report << list_projects('closed', 'Completed Projects', projects[:closed])
# Generate the report
return RedCloth.new(report).to_html
end
private
# Backpack API Methods
def connect(use_ssl = false)
@connection = Net::HTTP.new("#{@username}.backpackit.com", use_ssl ? 443 : 80)
@connection.use_ssl = use_ssl
@connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl
end
def request(path, parameters = {}, second_try = false)
parameters = { "token" => @token }.merge(parameters)
response = @connection.post("/ws/#{path}", parameters.to_yaml, "X-POST_DATA_FORMAT" => "yaml")
if response.code == "200"
result = XmlSimple.xml_in(response.body)
result.delete "success"
result.empty? ? true : result
elsif response.code == "302" && !second_try
connect(true)
request(path, parameters, true)
else
raise "Error occured (#{response.code}): #{response.body}"
end
end
def list_page_lists(page_id)
request("page/#{page_id}/lists/list")['lists'][0]['list']
end
def last_item_completed(page_id, list_id)
xs = request "page/#{page_id}/lists/#{list_id}/items/list"
items = Document.new XmlSimple.xml_out(xs)
completed_items = XPath.match( items, "//item[@completed='true']" )
return completed_items[completed_items.size-1]
end
def list_open_items(page_id, list_id)
xs = request "page/#{page_id}/lists/#{list_id}/items/list"
items = Document.new XmlSimple.xml_out(xs)
completed_items = XPath.match( items, "//item[@completed='false']" )
end
def list_pages
return request("pages/all")['pages'][0]['page']
end
def list_reminders
return request("reminders")['reminder']
end
# Reporting Methods
def list_items(page)
list = ''
# Get the lists on each page
begin
list_page_lists(page[:id]).each do |page_list|
list << "\nh5. #{page_list['name']}\n\n"
last_one = last_item_completed(page[:id], page_list['id'])
list <<"* [-#{last_one.text}-]\n" unless last_one == nil
count = 0
list_open_items(page[:id], page_list['id']).each do |item|
if count == 0 then
list << "* *%{color:green}#{item.text}%*\n"
elsif count < 3
list << "* #{item.text}\n"
end
count += 1
end
end
rescue
# There are no valid
list = "\n* No valid to-do exist for this project.\n"
end
return list
end
def list_projects(category, title, pages)
list = "\nh3. #{title}\n"
pages.each do |page|
unless @ignore_pages.include?(page[:title])
list << "\nh4. #{page[:title]}\n"
list << list_items(page) if category == 'work' or category == 'home'
end
end
return list
end
end
bp = Backpack.new 'user_name', 'token'
bp.ignore_pages = ['Ignore Works', ' Ignore Home', 'zw - Ignore Waiting']
report = bp.generate_gtd_report
gmail = Gmail.new 'your_email@gmail.com', 'your_password'
gmail.send_mail 'your_email@gmail.com', 'Test Report', report
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.edbotz.gtd.plist</string>
<key>ProgramArguments</key>
<array>
<string>/Users/[your_user_name_here]/Library/Scripts/bp_worklist.rb</string>
<string>daily</string>
</array>
<key>LowPriorityIO</key>
<true/>
<key>Nice</key>
<integer>1</integer>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>15</integer>
</dict>
</dict>
</plist>
# bash commands
# Create a directory named Scripts in your ~/Library directory.
# Put the ruby files in the Scripts directory
# Create a directory named LaunchAgents in your ~/Library directory.
# Open a bash prompt and navigate to ~/
# Load the service into launchd with the following command:
launchctl load Library/LaunchAgents/com.edbotz.gtd.plist
# Verify that it was loaded with this command:
launchctl list | grep gtd
# You can unload it at any time with:
launchctl unload Library/LaunchAgents/com.edbotz.gtd.plist
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment