-
-
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)
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
#!/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