Last active
December 11, 2015 02:29
-
-
Save trestletech/4531146 to your computer and use it in GitHub Desktop.
Import My PivotalTracker Stories into Astrid
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
setup.sh | |
token |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Required Environment Variables: | |
# PIVOTAL_TOKEN - the API key. Obtained as described here: https://www.pivotaltracker.com/help/api?version=v3#retrieve_token | |
# PIVOTAL_PROJECT - the numerical ID of the project on which we're working. | |
# PIVOTAL_OWNER - Include only tasks owned by this user in parsing. | |
# ASTRID_SECRET - The secret Key of your Astrid Application on their API | |
# ASTRID_APP - The application ID you registered with Astrid | |
# LIST_NAME - The name of the list to apply to all tasks inserted into Astrid. | |
import os | |
import urllib2 | |
import urllib | |
from xml.dom import minidom | |
import time | |
import md5 | |
import simplejson | |
import getpass | |
def fetchAllPivotal () : | |
request = urllib2.Request("http://www.pivotaltracker.com/services/v3/projects/"+os.environ['PIVOTAL_PROJECT']+"/iterations/current?", headers={"X-TrackerToken" : os.environ['PIVOTAL_TOKEN']}) | |
contents = urllib2.urlopen(request).read() | |
xml = minidom.parseString(contents) | |
return xml | |
def astridRequest (method, params) : | |
sigurl = method | |
params['app_id'] = os.environ['ASTRID_APP'] | |
params['time'] = (str(int(time.time()))) | |
for par in sorted(params.iterkeys()): | |
if not isinstance(params[par], (basestring,int)) and len(params[par]) > 1: | |
params[par] = sorted(params[par]) | |
for p in params[par] : | |
sigurl += str(par) + str(p) | |
else : | |
sigurl += str(par) + str(params[par]) | |
sig = md5.new(sigurl + os.environ['ASTRID_SECRET']).hexdigest() | |
params['sig'] = sig | |
data = urllib.urlencode(params, True) | |
request = urllib2.Request("http://astrid.com/api/7/" + method, data) | |
contents = urllib2.urlopen(request).read() | |
return [contents] | |
def doAstridLogin() : | |
#try fetching the existing token and test it | |
try: | |
f = open('token', 'r') | |
token = f.readline() | |
f.close() | |
return token | |
except IOError: | |
#no token, get new one. | |
return getNewToken() | |
def getNewToken () : | |
username = raw_input("Enter your Astrid Email: ") | |
# password = raw_input("Enter your Astrid password:") | |
password = getpass.getpass() | |
params = {'email': username, 'provider': 'password', 'secret': password} | |
req = astridRequest("user_signin", params) | |
decoded = simplejson.loads(req[0]) | |
token = decoded['token'] | |
f = open('token', 'w') | |
f.write(token) | |
f.close() | |
return str(token) | |
def saveTask (token, pivotalID, pivotalState, pivotalURL, pivotalDesc, pivotalName, dueDate, astridID=None): | |
url = "task_save" | |
params = {} | |
#If this is a new task, set the date to the end of the Sprint. If it's an existing task, just let Astrid manage the due date. | |
if astridID == None : | |
params['due'] = str(dueDate) | |
params['has_due_time'] = 1 | |
if astridID != None : | |
params['id'] = astridID | |
params['notes'] = (pivotalURL) + " - " + (pivotalDesc) | |
params['title'] = (pivotalName) | |
params['token'] = token | |
params['tags[]'] = ['Work', os.environ['LIST_NAME']] | |
if (pivotalState == 1) : | |
params['completed_at'] = (str(int(time.time()))) | |
if (pivotalState == 0) : | |
params['completed_at'] = 0 | |
results = astridRequest(url, params) | |
#TODO: check for "Deleted_at" parameter. If it exists, unbind the Pivotal story and create a new Astrid task. | |
decoded = simplejson.loads(results[0]) | |
return decoded['id'] | |
def annotateStory (storyID, astridID, integrationID=18645) : | |
opener = urllib2.build_opener(urllib2.HTTPHandler) | |
payload = "<story><other_id>" + str(astridID) + "</other_id><integration_id>" + str(integrationID) + "</integration_id></story>" | |
request = urllib2.Request("http://www.pivotaltracker.com/services/v3/projects/"+os.environ['PIVOTAL_PROJECT']+"/stories/"+str(storyID), data=payload) | |
request.add_header('X-TrackerToken', os.environ['PIVOTAL_TOKEN']) | |
request.add_header('Content-Type', 'application/xml') | |
request.get_method = lambda: 'PUT' | |
url = opener.open(request) | |
contents = urllib2.urlopen(request).read() | |
#print contents | |
xml = fetchAllPivotal() | |
iterations = xml.getElementsByTagName('iteration') | |
print "Parsing " + str(len(iterations)) + " iterations..." | |
#First login a user so we can start interacting with their tasks. | |
#token = doAstridLogin(os.environ['ASTRID_USERNAME'], os.environ['ASTRID_PASSWORD']) | |
token = doAstridLogin() | |
for iter in iterations : | |
stories = iter.getElementsByTagName('story') | |
iterID = iter.getElementsByTagName('id')[0].childNodes[0].nodeValue | |
iterFinish = iter.getElementsByTagName('finish')[0].childNodes[0].nodeValue | |
iterFinish = int(time.mktime(time.strptime(iterFinish, '%Y/%m/%d %H:%M:%S UTC'))) | |
print "Parsing " + str(len(stories)) + " stories in iteration #" + str(iterID) | |
for story in stories : | |
storyID = story.getElementsByTagName('id')[0].childNodes[0].nodeValue | |
storyState = story.getElementsByTagName('current_state')[0].childNodes[0].nodeValue | |
storyComplete = 0 | |
if storyState == "finished" or storyState == "delivered" or storyState == "accepted" : | |
storyComplete = 1 | |
storyURL = story.getElementsByTagName('url')[0].childNodes[0].nodeValue | |
storyDesc = "" #Check if description is given | |
if len(story.getElementsByTagName('description')[0].childNodes) > 0 : | |
storyDesc = story.getElementsByTagName('description')[0].childNodes[0].nodeValue | |
storyOwner = "" #Check if owner exists | |
if len(story.getElementsByTagName('owned_by')) > 0 : | |
storyOwner = story.getElementsByTagName('owned_by')[0].childNodes[0].nodeValue | |
storyName = story.getElementsByTagName('name')[0].childNodes[0].nodeValue | |
storyAstridID = None #Check if already bound to Astrid | |
if len(story.getElementsByTagName('other_id')) > 0 : | |
storyAstridID = story.getElementsByTagName('other_id')[0].childNodes[0].nodeValue | |
storyName = story.getElementsByTagName('name')[0].childNodes[0].nodeValue | |
if storyOwner == os.environ['PIVOTAL_OWNER'] : | |
astID = saveTask(token, storyID, storyComplete, storyURL, storyDesc, storyName, iterFinish, storyAstridID) | |
if (storyAstridID == None) : | |
print " Created Astrid task #" + str(astID) | |
#Now bind the Pivotal story to the Astrid task | |
annotateStory(storyID, astID) | |
if str(astID) == str(storyAstridID) : | |
print " Refreshed Astrid task #" + str(astID) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment