From 6fdd3c2941573ccc8ad2bfb85b4b13642eea5356 Mon Sep 17 00:00:00 2001 From: Harper Reed Date: Sun, 2 Oct 2011 22:00:40 -0500 Subject: [PATCH 1/9] moved from v2 to v3 api. didn't test anything so it all may be broken. haha --- pivotaltracker/_client.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index 81ca934..b414b76 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -14,9 +14,13 @@ def __init__(self, token, secure=True, parse_xml=True): these options by setting 'secure' or 'parse_xml' to False""" self.__token = token protocol = "https" if secure else "http" - self.__base_url = "%(protocol)s://www.pivotaltracker.com/services/v2/" % dict(protocol=protocol) + self.__base_url = "%(protocol)s://www.pivotaltracker.com/services/v3/" % dict(protocol=protocol) self.__parse_xml = parse_xml - + + def get_activity(self): + """gets all projects for this user""" + return self.__remote_http_get("projects") + def get_project(self, project_id): """gets a project from the tracker""" return self.__remote_http_get("projects/%s" % project_id) @@ -187,7 +191,7 @@ def parse_by_type(node): return value elif obj_type == "commalist": - value = node.childNodes[0].wholeText.strip() + value = ''#node.childNodes[0].wholeText.strip() return value.split(",") elif obj_type == "dictionary": From 34dabef07fcdc674040b7826aeeb337282345e5e Mon Sep 17 00:00:00 2001 From: Harper Reed Date: Sun, 2 Oct 2011 22:01:05 -0500 Subject: [PATCH 2/9] added get_activity function --- pivotaltracker/_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index b414b76..d4d0ce6 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -19,7 +19,7 @@ def __init__(self, token, secure=True, parse_xml=True): def get_activity(self): """gets all projects for this user""" - return self.__remote_http_get("projects") + return self.__remote_http_get("activities") def get_project(self, project_id): """gets a project from the tracker""" From b1241935dd2a1fc452caac5d6e749d6a219d46bc Mon Sep 17 00:00:00 2001 From: Harper Reed Date: Sun, 2 Oct 2011 22:05:05 -0500 Subject: [PATCH 3/9] built out the get_activity function to handle limit and occurred_since_date --- pivotaltracker/_client.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index d4d0ce6..b0b7384 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -17,8 +17,20 @@ def __init__(self, token, secure=True, parse_xml=True): self.__base_url = "%(protocol)s://www.pivotaltracker.com/services/v3/" % dict(protocol=protocol) self.__parse_xml = parse_xml - def get_activity(self): - """gets all projects for this user""" + def get_activity(self,limit=None,occurred_since_date=None): + """gets activity for all projects""" + params = {} + if limit: + params["limit"] = limit + if occurred_since_date: + params["occurred_since_date"] = occurred_since_date + if params: + # we have parameters to send + encoded_params = urllib.urlencode(params) + return self.__remote_http_get("activities?%s" % (encoded_params)) + else: + # no arguments, get all stories + return self.__remote_http_get("activities" ) return self.__remote_http_get("activities") def get_project(self, project_id): From 35f5fcdfab028187f5cfddb3a5f17591c443eeb5 Mon Sep 17 00:00:00 2001 From: Egor Semiguzov Date: Mon, 30 Sep 2013 17:34:30 +0900 Subject: [PATCH 4/9] Api v4 not tested. Getting story activity method --- pivotaltracker/_client.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index b0b7384..6683e0f 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -14,7 +14,7 @@ def __init__(self, token, secure=True, parse_xml=True): these options by setting 'secure' or 'parse_xml' to False""" self.__token = token protocol = "https" if secure else "http" - self.__base_url = "%(protocol)s://www.pivotaltracker.com/services/v3/" % dict(protocol=protocol) + self.__base_url = "%(protocol)s://www.pivotaltracker.com/services/v4/" % dict(protocol=protocol) self.__parse_xml = parse_xml def get_activity(self,limit=None,occurred_since_date=None): @@ -44,6 +44,11 @@ def get_all_projects(self): def get_story(self, project_id, story_id): """gets an individual story""" return self.__remote_http_get("projects/%s/stories/%s" % (project_id, story_id)) + + def get_story_activity(self, project_id, story_id): + """get story activities""" + + return self.__remote_http_get("projects/%s/stories/%s/activities" % (project_id, story_id)) def get_stories(self, project_id, query=None, limit=None, offset=None): """gets stories from a project. These stories can be filtered via 'query', and From ed36d854163595200debe3bd6dc597cf814f124c Mon Sep 17 00:00:00 2001 From: Ian Gibbs Date: Wed, 27 Aug 2014 11:23:19 +0100 Subject: [PATCH 5/9] * Add method to retrieve the tasks for a story, and a warning that PT's name filter doesn't work --- pivotaltracker/_client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index 6683e0f..9c53558 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -47,12 +47,12 @@ def get_story(self, project_id, story_id): def get_story_activity(self, project_id, story_id): """get story activities""" - return self.__remote_http_get("projects/%s/stories/%s/activities" % (project_id, story_id)) def get_stories(self, project_id, query=None, limit=None, offset=None): """gets stories from a project. These stories can be filtered via 'query', and paginated via 'limit' and 'offset'""" + # WARNING The name filter just doesn't work due to some PT api bug. You'll either get back nothing or everthing, irrespective of search terms. params = {} if query: params["filter"] = query @@ -68,6 +68,10 @@ def get_stories(self, project_id, query=None, limit=None, offset=None): # no arguments, get all stories return self.__remote_http_get("projects/%s/stories" % project_id) + def get_tasks(self, project_id, story_id): + """gets the tasks associated with an individual story""" + return self.__remote_http_get("projects/%s/stories/%s/tasks" % (project_id, story_id)) + def get_iterations(self, project_id, limit=None, offset=None): """gets iterations from a project. These iterations can be paginated via 'limit' and 'offset'""" return self.__iterations_request_helper(sub_url="iterations", project_id=project_id, limit=limit, offset=offset) From 4cb6ff571532723d92934b3fc40e18b8570fada6 Mon Sep 17 00:00:00 2001 From: Ian Gibbs Date: Wed, 27 Aug 2014 11:24:51 +0100 Subject: [PATCH 6/9] Ignore compiled Python files --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc From 4158e9212276317efee285b5e21813e1d446b51c Mon Sep 17 00:00:00 2001 From: Ian Gibbs Date: Wed, 27 Aug 2014 13:29:05 +0100 Subject: [PATCH 7/9] Add task handling functions --- pivotaltracker/_client.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index 9c53558..9bc00f7 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -68,10 +68,30 @@ def get_stories(self, project_id, query=None, limit=None, offset=None): # no arguments, get all stories return self.__remote_http_get("projects/%s/stories" % project_id) + def get_task(self, project_id, story_id, task_id): + """gets a specific task associated with an individual story""" + return self.__remote_http_get("projects/%s/stories/%s/tasks/%s" % (project_id, story_id, task_id)) + def get_tasks(self, project_id, story_id): """gets the tasks associated with an individual story""" return self.__remote_http_get("projects/%s/stories/%s/tasks" % (project_id, story_id)) + def add_task(self, project_id, story_id, description, complete=None): + """adds a task to a story""" + xml = self.__get_task_xml(project_id, story_id, description, complete) + data = xml.toxml() + return self.__remote_http_post("projects/%s/stories/%s/tasks" % (project_id, story_id), data=data) + + def update_task(self, project_id, story_id, task_id, description, complete=None): + """updates a task on a story""" + xml = self.__get_task_xml(project_id, story_id, description, complete) + data = xml.toxml() + return self.__remote_http_post("projects/%s/stories/%s/tasks/%s" % (project_id, story_id, task_id), data=data) + + def delete_task(self, project_id, story_id, task_id): + """deletes a task in a story""" + return self.__remote_http_delete("projects/%s/stories/%s/tasks/%s" % (project_id, story_id, task_id)) + def get_iterations(self, project_id, limit=None, offset=None): """gets iterations from a project. These iterations can be paginated via 'limit' and 'offset'""" return self.__iterations_request_helper(sub_url="iterations", project_id=project_id, limit=limit, offset=offset) @@ -92,6 +112,7 @@ def get_backlog_iterations(self, project_id, limit=None, offset=None): def add_story(self, project_id, name, description, story_type, requested_by=None, estimate=None, current_state=None, labels=None): """adds a story to a project""" + #WARNING: Wait for 3 seconds after adding a story before trying to retrieve it to let the API catch up xml = self.__get_story_xml(project_id, name, description, requested_by, story_type, estimate, current_state, labels) data = xml.toxml() return self.__remote_http_post("projects/%s/stories" % project_id, data=data) @@ -156,6 +177,22 @@ def __get_story_xml(self, project_id, name, description, requested_by, story_typ xml = minidom.parseString(xml_string.strip()) return xml + def __get_task_xml(self, project_id, story_id, description, complete): + + # build XML elements + elements = [] + if description is not None: + elements.append("%s" % description) + if complete is not None: + elements.append("%s" % complete) + else: + elements.append("false") + + # build XML + xml_string = "%s" % "".join(elements) + xml = minidom.parseString(xml_string.strip()) + return xml + def __iterations_request_helper(self, sub_url, project_id, limit, offset): params = {} if limit is not None: From ea2ad4798decbe66bba14593ebf515224e11173f Mon Sep 17 00:00:00 2001 From: Ian Gibbs Date: Wed, 27 Aug 2014 13:50:28 +0100 Subject: [PATCH 8/9] Add move_story function --- pivotaltracker/_client.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index 9bc00f7..fbbe6b7 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -123,6 +123,12 @@ def update_story(self, project_id, story_id, name=None, description=None, reques data = xml.toxml() return self.__remote_http_put("projects/%s/stories/%s" % (project_id, story_id), data=data) + def move_story(self, project_id, story_id, target_story_id, precedence=None): + """moves a story before (default) or after another story""" + xml = self.__get_move_xml(target_story_id, precedence) + data = xml.toxml() + return self.__remote_http_post("projects/%s/stories/%s/moves" % (project_id, story_id), data=data) + def delete_story(self, project_id, story_id): """deletes a story in a project""" return self.__remote_http_delete("projects/%s/stories/%s" % (project_id, story_id)) @@ -193,6 +199,22 @@ def __get_task_xml(self, project_id, story_id, description, complete): xml = minidom.parseString(xml_string.strip()) return xml + def __get_move_xml(self, target_story_id, precedence): + + # build XML elements + elements = [] + if target_story_id is not None: + elements.append("%s" % target_story_id) + if precedence is not None: + elements.append("%s" % precedence) + else: + elements.append("before") + + # build XML + xml_string = "%s" % "".join(elements) + xml = minidom.parseString(xml_string.strip()) + return xml + def __iterations_request_helper(self, sub_url, project_id, limit, offset): params = {} if limit is not None: From ef82e47ffda2cdd76843b2c01a748d41fc9041f0 Mon Sep 17 00:00:00 2001 From: Ian Gibbs Date: Wed, 27 Aug 2014 13:53:49 +0100 Subject: [PATCH 9/9] Remove superfluous params to XML functions --- pivotaltracker/_client.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pivotaltracker/_client.py b/pivotaltracker/_client.py index fbbe6b7..af17333 100644 --- a/pivotaltracker/_client.py +++ b/pivotaltracker/_client.py @@ -78,13 +78,13 @@ def get_tasks(self, project_id, story_id): def add_task(self, project_id, story_id, description, complete=None): """adds a task to a story""" - xml = self.__get_task_xml(project_id, story_id, description, complete) + xml = self.__get_task_xml(description, complete) data = xml.toxml() return self.__remote_http_post("projects/%s/stories/%s/tasks" % (project_id, story_id), data=data) def update_task(self, project_id, story_id, task_id, description, complete=None): """updates a task on a story""" - xml = self.__get_task_xml(project_id, story_id, description, complete) + xml = self.__get_task_xml(description, complete) data = xml.toxml() return self.__remote_http_post("projects/%s/stories/%s/tasks/%s" % (project_id, story_id, task_id), data=data) @@ -113,13 +113,13 @@ def get_backlog_iterations(self, project_id, limit=None, offset=None): def add_story(self, project_id, name, description, story_type, requested_by=None, estimate=None, current_state=None, labels=None): """adds a story to a project""" #WARNING: Wait for 3 seconds after adding a story before trying to retrieve it to let the API catch up - xml = self.__get_story_xml(project_id, name, description, requested_by, story_type, estimate, current_state, labels) + xml = self.__get_story_xml(name, description, requested_by, story_type, estimate, current_state, labels) data = xml.toxml() return self.__remote_http_post("projects/%s/stories" % project_id, data=data) def update_story(self, project_id, story_id, name=None, description=None, requested_by=None, story_type=None, estimate=None, current_state=None, labels=None): """updates a story in a project""" - xml = self.__get_story_xml(project_id, name, description, requested_by, story_type, estimate, current_state, labels) + xml = self.__get_story_xml(name, description, requested_by, story_type, estimate, current_state, labels) data = xml.toxml() return self.__remote_http_put("projects/%s/stories/%s" % (project_id, story_id), data=data) @@ -154,7 +154,7 @@ def deliver_all_finished_stories(self, project_id): for live testing.""" return self.__remote_http_put("projects/%s/stories/deliver_all_finished" % project_id) - def __get_story_xml(self, project_id, name, description, requested_by, story_type, estimate, current_state, labels): + def __get_story_xml(self, name, description, requested_by, story_type, estimate, current_state, labels): # build XML elements elements = [] @@ -183,7 +183,7 @@ def __get_story_xml(self, project_id, name, description, requested_by, story_typ xml = minidom.parseString(xml_string.strip()) return xml - def __get_task_xml(self, project_id, story_id, description, complete): + def __get_task_xml(self, description, complete): # build XML elements elements = []