Skip to content

Instantly share code, notes, and snippets.

@darkhelmet
Last active December 11, 2015 14:58
Show Gist options
  • Save darkhelmet/4618013 to your computer and use it in GitHub Desktop.
Save darkhelmet/4618013 to your computer and use it in GitHub Desktop.
A migrator for 37Signals' BCX.
require 'httparty'
require 'active_support/all'
module PartyPooper
def get(url, options = {})
self.class.get("/#{account_id}/api/v1/#{url}", options.merge(extras))
rescue => boom
puts boom.message
retry
end
def post(url, data, options = {})
self.class.post("/#{account_id}/api/v1/#{url}", options.merge(extras(post_headers)).merge(body: data.to_json))
rescue => boom
puts boom.message
retry
end
def put(url, data, options = {})
self.class.put("/#{account_id}/api/v1/#{url}", options.merge(extras(post_headers)).merge(body: data.to_json))
rescue => boom
puts boom.message
retry
end
def extras(h = headers)
{ basic_auth: basic_auth, headers: h }
end
def basic_auth
{ username: username, password: password }
end
def headers
{ 'User-Agent' => user_agent }
end
def post_headers
headers.merge('Content-Type' => 'application/json')
end
end
Client = Struct.new(:username, :password, :user_agent, :account_id, :project_id) do
include HTTParty
include PartyPooper
base_uri 'https://basecamp.com'
def todo_lists
get("/projects/#{project_id}/todolists.json")
end
def completed_todo_lists
get("/projects/#{project_id}/todolists/completed.json")
end
def todo_list(id)
get("/projects/#{project_id}/todolists/#{id}.json")
end
def create_todo_list(data)
post("/projects/#{project_id}/todolists.json", data)
end
def todos(list)
get("/projects/#{project_id}/todolists/#{list}.json")
end
def todo(id)
get("/projects/#{project_id}/todos/#{id}.json")
end
def create_todo(list, data)
post("/projects/#{project_id}/todolists/#{list}/todos.json", data)
end
def complete_todo(id)
put("/projects/#{project_id}/todos/#{id}.json", completed: true)
end
def create_todo_comment(id, data)
post("/projects/#{project_id}/todos/#{id}/comments.json", data)
end
end
Migrator = Struct.new(:from, :to, :log) do
def migrate
lists.each { |list| migrate_list(list) }
end
def migrate_list(list)
log.info("Migrating #{list['name']}")
destination = to.create_todo_list(list.slice('name'))
todos_from_list(list).each { |todo| migrate_todo(todo, destination) }
end
def migrate_todo(todo, destination)
log.info("\tMigrating todo #{todo['content']}")
todo = from.todo(todo['id'])
ctodo = to.create_todo(destination['id'], todo.slice('content'))
comments = comments_from_todo(todo)
if comments.present?
log.info("\tMigrating #{comments.count} comments")
comments.each { |comment| migrate_comment(comment, ctodo) }
end
complete_todo(ctodo) if todo['completed']
end
def complete_todo(todo)
log.info("\tCompleting #{todo['content']}")
to.complete_todo(todo['id'])
end
def comments_from_todo(todo)
Array(todo['comments'])
end
def migrate_comment(comment, todo)
to.create_todo_comment(todo['id'], comment.slice('content'))
end
def lists
from.completed_todo_lists.reverse + from.todo_lists.reverse
end
def todos_from_list(list)
todos = from.todo_list(list['id'])['todos']
Array(todos['remaining']) + Array(todos['completed'])
end
end
require 'logger'
require_relative 'client'
username = 'username'
password = 'password'
user_agent = 'BCX Mover (email@example.com)'
from_account_id = 1234
from_project_id = 2345
to_account_id = 5678
to_project_id = 6789
from = Client.new(username, password, user_agent, from_account_id, from_project_id)
to = Client.new(username, password, user_agent, to_account_id, to_project_id)
log = Logger.new(STDOUT)
log.formatter = Logger::Formatter.new
Migrator.new(from, to, log).migrate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment