forked from aleksmaricic/print-pivotal-cards
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PivotalScraper.py
180 lines (151 loc) · 5.82 KB
/
PivotalScraper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import argparse
import requests
from bs4 import BeautifulSoup
import simplejson
import pdb
from JSONSoup import JSONSoup
from StoryRenderer import StoryRenderer
TIME_ZONE_OFFSET = 1
class PivotalScraper():
def __init__(self):
self.session = None
self.project_id = None
def reloadStories(self, project_id):
self.stories = None
self.loadStories(project_id)
def loadStories(self, project_id):
if not hasattr(self, 'stories') or self.stories == None:
self.loadProjectSoup(project_id)
stories = self.project_soup.matchKey('project').matchKey('stories')[0].get_raw()
project_name = self.project_soup.matchKey('project').matchKey('name').get_raw()[0]
self.loadAllEpics()
self.loadAllLabels()
self.loadAllMembers()
addProject = lambda x: self.addStatic('project_name', project_name, x)
stories = map(addProject, stories)
stories = map(self.epicsAndLabels_ID2String, stories)
stories = map(self.requestedBy_ID2String, stories)
stories = map(self.ownedBy_ID2String, stories)
stories = map(self.tasks_format, stories)
self.stories = {}
self.stories.update([(story['id'], story) for story in stories])
def login(self, username, password):
authenticity_token = self.getAuthenticityToken()
payload = {
'authenticity_token': authenticity_token,
'credentials[username]': username,
'credentials[password]': password,
'time_zone_offset': TIME_ZONE_OFFSET
}
r = requests.post("https://www.pivotaltracker.com/signin", verify=True, data=payload)
self.session = {'t_session': r.cookies['t_session']}
def getAuthenticityToken(self):
r = requests.get("https://www.pivotaltracker.com/signin", verify=True)
soup = BeautifulSoup(r.text)
return soup.form.find(attrs={'name': 'authenticity_token'})['value']
def get(self, url):
if self.session <> None:
r = requests.get(url, verify=True, cookies=self.session)
return r
else:
return None
def requestProjectDetails(self, project_id):
return self.get('https://www.pivotaltracker.com/services/v5/projects/%s' % project_id)
def reloadProjectSoup(self, project_id):
self.project_soup = None
self.loadProjectSoup(project_id)
def loadProjectSoup(self, project_id):
if self.project_id <> project_id or not hasattr(self, 'project_soup') or self.project_soup == None:
self.project_id = project_id
r = self.requestProjectDetails(project_id)
self.project_soup = JSONSoup()
self.project_soup.loads(r.text)
def reloadAllEpics(self):
self.all_epics = None
self.loadAllEpics()
def loadAllEpics(self):
if not hasattr(self, 'all_epics') or self.all_epics == None:
_epics = self.project_soup.matchKey('project').matchKey('epics')[0].get_raw()
self.all_epics = {}
for epic in _epics:
self.all_epics.update({epic['label_id']: epic['name']})
def reloadAllLabels(self):
self.all_labels = None
self.loadAllLables()
def loadAllLabels(self):
if not hasattr(self, 'all_labels') or self.all_labels == None:
_labels = self.project_soup.matchKey('project').matchKey('labels')[0].get_raw()
self.all_labels = {}
for label in _labels:
self.all_labels.update({label['id']: label['name']})
def reloadAllMembers(self):
self.all_members = None
self.loadAllMembers()
def loadAllMembers(self):
if not hasattr(self, 'all_members') or self.all_members == None:
_members = self.project_soup.matchKey('project').matchKey('members')[0].get_raw()
self.all_members = {}
for member in _members:
self.all_members.update({member['id']: member})
def addStatic(self, key, value, story):
story[key] = value
return story
def epicsAndLabels_ID2String(self, story):
story_labels = []
story_epics = []
for label_id in story['label_ids']:
if label_id in self.all_epics.keys():
story_epics.append(self.all_epics[label_id])
else:
story_labels.append(self.all_labels[label_id])
story['epics'] = story_epics
story['labels'] = story_labels
return story
def requestedBy_ID2String(self, story):
if story['requested_by_id'] in self.all_members.keys():
story['requested_by'] = self.all_members[story['requested_by_id']]['name']
else:
story['requested_by'] = "<Unknown: %s>" % story['requested_by_id']
return story
def ownedBy_ID2String(self, story):
if story['owned_by_id'] == -1:
story['owned_by'] = ""
elif story['owned_by_id'] in self.all_members.keys():
story['owned_by'] = self.all_members[story['owned_by_id']]['name']
else:
story['owned_by'] = "<Unknown: %s>" % story['owned_by_id']
return story
def tasks_format(self, story):
class Task:
def __init__(self):
self.complete = False
self.description = ""
story_tasks = []
for _task in story['tasks']:
task = Task()
task.description = _task['description']
task.complete = _task['complete']
story_tasks.append(task)
story['tasks_list'] = story_tasks
return story
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('username', help="Your Pivotal Tracker username")
parser.add_argument('password', help="Your Pivotal Tracker password")
parser.add_argument(
"pid",
help="Your Project ID (e.g. www.pivotaltracker.com/s/projects/<pid>)",
type=int
)
parser.add_argument(
"-o", "--output",
help="The HTML file you want to output to",
default=None
)
args = parser.parse_args()
scraper = PivotalScraper()
scraper.login(args.username, args.password)
scraper.reloadStories(project_id=args.pid)
stories = scraper.stories.values()
story_renderer = StoryRenderer()
story_renderer.render(stories, args.output)