Skip to content

Instantly share code, notes, and snippets.

@Rexeh
Forked from ryanmark/import_tix.py
Last active March 15, 2016 19:14
Show Gist options
  • Save Rexeh/8401336 to your computer and use it in GitHub Desktop.
Save Rexeh/8401336 to your computer and use it in GitHub Desktop.
Forked from work by ryanmark - Fixed some parse issues with certain characters - Accepts multiple text files - Milestones requested during the import process - Automatic list of projects based on those available under account (allows fast import to many projects) - User/Pass requested in application (bit more secure)
#!/usr/bin/env python
# Modified by Robert Cox
#
# Please enter UNFUDDLE URL below as unfuddle_domain
#
# 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.
#
# usage: UFimport.py coretickets.txt othertickets.txt
#
# File format(Project dates requested in runtime for milestone dates:
#
# # Project Name
#
# ## Iteration 1
# * Write a script to import all these tasks
# * build a website
# - This is a description of the website to build
# - This is some more description on new line
#
# ## Iteration 2
# * 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
import fileinput
import urllib2
import simplejson
from dateutil.parser import parse
from pprint import pprint
ufuser = raw_input("Enter username: ")
ufpass = raw_input ("Enter password: ")
project_name = ""
auth=(
ufuser,
ufpass
)
unfuddle_httpb = 'https://'
unfuddle_domain = 'yourdomain.unfuddle.com'
unfuddle_apipath = '/api/v1/'
def get_data(api_end_point):
# url = 'https://subdomain.unfuddle.com/api/v1/projects'
url = ('https://%s/api/v1/%s' % (unfuddle_domain, api_end_point))
auth_handler = urllib2.HTTPBasicAuthHandler()
auth_handler.add_password(realm='Unfuddle API',
uri=url,
user=ufuser,
passwd=ufpass)
opener = urllib2.build_opener(auth_handler)
opener.addheaders = [('Content-Type', 'application/xml'), ('Accept', 'application/json')]
try:
response = opener.open(url).read().strip()
# print 'response:', response
return simplejson.loads(response)
except IOError, e:
print IOError, e
def get_projects():
return get_data('projects')
def dateinputter():
inputresult = ""
while inputresult == "":
try:
inputresult = raw_input("Please input a date for '%s' in format DD/MM/YY: " % milestone_title)
if inputresult == "":
print("Don't enter empty values...")
else:
return inputresult
except:
print("Don't enter empty values...")
def select_project():
projects = get_projects()
if len(projects) == 1:
print 'There is only one project I will use this "%s"' % (projects[0]['title'])
return projects[0]['title']
for index, project in enumerate(projects):
print '%s. %s' % (index+1, project['title'])
project_index = int(raw_input('Enter the project number: ')) - 1
return projects[project_index]['title']
project_name = select_project()
print("%s" % project_name)
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]
project = project = get_project(project_name)
milestone = None
#print('Config is "%s"...' % datafile)
for filename in fileinput.input():
f = fileinput.filename()
#print('Config is "%s"...' % f)
fil = open(f, "U")
# 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)
lines = fil.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 = line.strip()[2:].strip()
print('Adding tickets to milestone "%s"...' % milestone_title)
while True:
try:
ddmilestone = dateinputter()
ddmilestone = parse(ddmilestone)
break
except ValueError:
print "The date isn't valid! Please try again"
milestone = get_or_create_milestone(project['id'], milestone_title, ddmilestone)
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()
ticket_title = ticket_title.replace("&", "and")
# 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
ticket_description = [w.replace('&', 'and') for w in ticket_description]
print 'Adding ticket "%s"...' % ticket_title
ticket = get_or_create_ticket(
project['id'], milestone['id'], ticket_title, "\n".join(ticket_description))
fileinput.nextfile()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment