Skip to content

Instantly share code, notes, and snippets.

@ryanmark
Forked from brianboyer/import_tix.py
Created May 11, 2012 19:13
Show Gist options
  • Save ryanmark/2661823 to your computer and use it in GitHub Desktop.
Save ryanmark/2661823 to your computer and use it in GitHub Desktop.
Unfuddle ticket generator
#!/usr/bin/env python
# Takes a markdown formatted sked of milestones and tickets and
# pushes it all into unfuddle. Tries to prevent duplicates, but
# doesn't do a great job because Unfuddle's API acts strangely.
#
# TODO: Figure out why new tickets don't show up in the get tickets API request
#
# usage: import_tix.py sked.txt
#
# File format:
#
# # Project Name
#
# ## Iteration 1 (Nov. 18)
# * Write a script to import all these tasks
# * build a website
# - this is a description of the website to build
#
# ## Iteration 2 (Nov. 25)
# * Launch the website
# - Don't forget to tell people
# * Profit!!!
import os.path
import os
import sys
import requests
import json
import re
import time
from dateutil.parser import parse
from pprint import pprint
auth=(
"UNFUDDLE_USERNAME",
"UNFUDDLE_PASSWORD"
)
unfuddle_domain = 'example.unfuddle.com'
def get_project(name):
# return the dictionary of a project with a name like the one given
r=requests.get(
'https://%s/api/v1/projects' % unfuddle_domain,
auth=auth,
headers={'Accept':'application/json'}
)
if r.ok:
projects = json.loads(r.content)
for p in projects:
if name.strip().lower() == p['title'].lower() or name.strip().lower() == p['short_name']:
return p
return None
else:
raise Exception('API call failed')
def get_or_create_milestone(project_id, title, due_date):
# return the dictionary of the milestone with the given title.
# Create it if a matching one can't be found
milestone = get_milestone(project_id, title)
if not milestone:
# didn't find a match, create it
r=requests.post(
'https://%s/api/v1/projects/%i/milestones' % (unfuddle_domain, project_id),
auth=auth,
headers={'Accept':'application/json', 'Content-Type':'application/xml'},
data="<milestone><due-on>%s</due-on><title>%s</title></milestone>" %
(due_date.strftime('%Y/%m/%d'), title)
)
milestone = get_milestone(project_id, title)
if not milestone:
raise Exception('API call failed')
return milestone
def get_milestone(project_id, title):
r=requests.get(
'https://%s/api/v1/projects/%i/milestones' % (unfuddle_domain, project_id),
auth=auth,
headers={'Accept':'application/json'}
)
if r.ok:
milestones = json.loads(r.content)
for m in milestones:
if title.strip().lower() == m['title'].lower():
return m
return None
else:
raise Exception('API call failed')
def get_or_create_ticket(project_id, milestone_id, summary, description, priority=3):
# return the dictionary of the milestone with the given title.
# Create it if a matching one can't be found
ticket = get_ticket(project_id, milestone_id, summary)
if not ticket:
# didn't find a match, create it
r=requests.post(
'https://%s/api/v1/projects/%i/tickets' % (unfuddle_domain, project_id),
auth=auth,
headers={'Accept':'application/json', 'Content-Type':'application/xml'},
data="""<ticket>
<milestone-id type="integer">%i</milestone-id>
<priority>%i</priority>
<description>%s</description>
<summary>%s</summary>
</ticket>""" %
(milestone_id, priority, description, summary)
)
if not r.ok:
raise Exception('API call failed')
ticket = get_ticket(project_id, milestone_id, summary)
if not ticket:
print("WARNING: Ticket was created, but I couldn't retrieve it.")
return ticket
def get_ticket(project_id, milestone_id, summary):
r=requests.get(
'https://%s/api/v1/projects/%i/tickets' % (unfuddle_domain, project_id),
auth=auth,
headers={'Accept':'application/json'}
)
if r.ok:
tickets = json.loads(r.content)
for t in tickets:
if summary.strip().lower() == t['summary'].lower():
return t
return None
else:
raise Exception('API call failed')
if __name__ == "__main__":
# A line that begins with # is the name of the project
# A line that begins with ## is the name of the milestone
# Milestones need due dates: ## Milestone 1 (due date)
# A line that begins with an * is a ticket
# A line that begins with an - and follows a line that starts
# with a * is a ticket description
# Any other line is ignored
datafile = sys.argv[1]
f = open(datafile, "U")
project = None
milestone = None
lines = f.readlines()
for idx, line in enumerate(lines):
if line.startswith('##'):
# milestone and due date
if not project:
raise Exception("Don't know which project to put this stuff in!")
milestone_title, due_date = re.match(
r'##\s+([\w\s]+)\s+\(([^\)]+)\)',
line.strip() ).group(1,2)
print('Adding tickets to milestone "%s"...' % milestone_title)
milestone = get_or_create_milestone(project['id'], milestone_title, parse(due_date))
elif line.startswith('#'):
# project name
project_name = line.strip()[1:].strip()
print('Switching to project "%s"...' % project_name)
project = get_project(project_name)
if not project:
raise Exception("Couldn't find project '%s'" % project_name)
elif line.startswith('*'):
# ticket
if not project or not milestone:
raise Exception("Don't know which project or milestone to put this stuff in!")
# title
ticket_title = line.strip()[1:].strip()
# description
position = idx + 1
ticket_description = []
while lines[position].strip().startswith('-'):
ticket_description.append(lines[position].strip())
if position + 1 < len(lines):
position += 1
else:
break
print 'Adding ticket "%s"...' % ticket_title
ticket = get_or_create_ticket(
project['id'], milestone['id'], ticket_title, "\n".join(ticket_description))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment