Merge lp:~james-w/launchpad-work-items-tracker/blueprints-api into lp:launchpad-work-items-tracker

Proposed by James Westby on 2010-12-09
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~james-w/launchpad-work-items-tracker/blueprints-api
Merge into: lp:launchpad-work-items-tracker
Diff against target: 475 lines (+127/-187)
1 file modified
collect (+127/-187)
To merge this branch: bzr merge lp:~james-w/launchpad-work-items-tracker/blueprints-api
Reviewer Review Type Date Requested Status
Martin Pitt (community) Approve on 2011-02-09
Jamie Bennett 2010-12-09 Pending
Clint Byrum 2010-12-09 Pending
Review via email: mp+43240@code.launchpad.net

This proposal supersedes a proposal from 2010-12-08.

Description of the Change

Hi,

This makes use of the newly exposed blueprints API on Launchpad, reducing
the amount of screen scraping, round trips, and data transferred enormously.

The only regression that I think this will cause is that when someone writes
"bug 12345" in the whiteboard, it will no longer be a link to that bug
when viewed on the workitems page, but I think we can live with that for
now. If you disagree let me know and I will code it.

Apologies that the diff is a little large and not particularly clear, but
I couldn't think of a good way to migrate a bit at a time.

The change is now rolled out on the Launchpad production instance, so we
can make use of this.

Note that you may still get AttributeError: ... 'bugs' due to caching of the WADL.
If you delete ~/.launchpadlib/api.launchpad.net/cache/api.launchpad.net,devel,-application,vnd.sun.*
and retry it should work.

Thanks,

James

To post a comment you must log in.
Clint Byrum (clint-fewbar) wrote : Posted in a previous version of this proposal
Download full text (4.8 KiB)

Upon running the code as-is, I received a failure rather quickly:

clint@clint-MacBookPro:~/src/wi/bzr/trunk$ ./collect -d ../natty.db -c ../natty.cfg --debug
lp_import_milestones(): milestone table already filled
lp_import_teams(): teams table already filled
lp_import(): downloading cloud-server-n-automated-testing from https://api.launchpad.net/devel/ubuntu/+spec/cloud-server-n-automated-testing
lp_import_blueprint(cloud-server-n-automated-testing): finished parsing; data: {'status': 'Session for discussion about automated testing of Ubuntu Server; automated ISO testing was implemented for Maverick using libvirt/kvm + Hudson (see http://launchpad.net/ubuntu-server-iso-testing). This approach could be applied in other variants and for other aspects of server testing', 'definition': 'Approved', 'implementation': 'Started', 'milestone': 'natty-alpha-3', 'approver': 'robbie.w', 'details_url': None, 'priority': 'Essential', 'assignee': 'james-page', 'roadmap_notes': None, 'drafter': 'james-page'}
lp_import_blueprint_workitems(): processing cloud-server-n-automated-testing (spec milestone: natty-alpha-3, spec assignee: james-page, spec implementation: Started)
lp_import_blueprint_workitems(): starting work items block at Work items for natty-alpha-2:
  ... setting milestone to natty-alpha-2
 workitem (raw): '[hggdh2] Way forward on production deployment of ISO testing: TODO'
 workitem (clean): '[hggdh2] Way forward on production deployment of ISO testing: TODO'
 workitem (raw): '[james-page] Move Server ISO tests to normal PXE + TFTP instead for broader fit with potential test architectures: DONE'
 workitem (clean): '[james-page] Move Server ISO tests to normal PXE + TFTP instead for broader fit with potential test architectures: DONE'
 workitem (raw): '[james-page] Server ISO test - review what the iso overlay looks like and refactor as required: DONE'
 workitem (clean): '[james-page] Server ISO test - review what the iso overlay looks like and refactor as required: DONE'
 workitem (raw): '[james-page] Package ubuntu-server-iso-testing and locate in PPA: INPROGRESS'
 workitem (clean): '[james-page] Package ubuntu-server-iso-testing and locate in PPA: INPROGRESS'
 workitem (raw): ''
lp_import_blueprint_workitems(): closing work items block with line:
lp_import_blueprint_workitems(): starting work items block at Work items for natty-alpha-3:
  ... setting milestone to natty-alpha-3
 workitem (raw): '[james-page] Automate EC2 testing and increase depth of image testing using unittest/subunit: TODO'
 workitem (clean): '[james-page] Automate EC2 testing and increase depth of image testing using unittest/subunit: TODO'
 workitem (raw): '[james-page] Server ISO test - fix concurrency in ISO download: TODO'
 workitem (clean): '[james-page] Server ISO test - fix concurrency in ISO download: TODO'
 workitem (raw): '[cr2] Output plugin for checkbox to write to couchdb: TODO'
 workitem (clean): '[cr2] Output plugin for checkbox to write to couchdb: TODO'
https://launchpad.net/ubuntu/+spec/cloud-server-n-automated-testing
  [WARNING] assignee "cr2" is not a valid Launchpad account
 workitem (raw): '[cr2] Checkbox plugin to download tests from couchdb to ex...

Read more...

review: Resubmit
Jamie Bennett (jamiebennett) wrote : Posted in a previous version of this proposal

On testing the code I was prompted to log in using open id (from the command-line). As this was tested via ssh to a development box 'Links' was launched as the web browser to gather my open id information. From there you cannot log in (Launchpad/Links issue). This did not happen on previous versions.

review: Needs Information
James Westby (james-w) wrote :

The change is now rolled out on the Launchpad production instance, so we
can make use of this.

Note that you may still get AttributeError: ... 'bugs' due to caching of the WADL.
If you delete ~/.launchpadlib/api.launchpad.net/cache/api.launchpad.net,devel,-application,vnd.sun.*
and retry it should work.

James Westby (james-w) wrote :

Jamie, I don't see how that could have been caused by this change, as
the API was still used for some things in the old code. I suspect it is
just coincidence that you are seeing this for the first time now.

Thanks,

James

James Westby (james-w) wrote :

Jamie, I've worked out why you saw that behaviour now, it's because
I changed from "edge" to "production" as edge is going away, so this
will indeed affect all users.

The other problem is that people.canonical.com doesn't have a new enough
version of launchpadlib to know about the "version" argument to
Launchpad.login_with, which is required here as blueprints are only exposed
on "devel".

Thanks,

James

253. By Martin Pitt on 2010-12-10

revert extra-projects merge for now; this is creating a conceptual conflict on milestones, and should rather be handled in separate configurations for linaro

254. By Martin Pitt on 2010-12-10

consider fixed bugs in bug list based reports as well

255. By Martin Pitt on 2010-12-17

add "Unknown" bug state

256. By Martin Pitt on 2011-01-03

drop Unknown bug state, does not exist any more

257. By Martin Pitt on 2011-01-03

better fix for "Unknown" task status

258. By Martin Pitt on 2011-01-03

finally fix unknown bug states

Martin Pitt (pitti) wrote :

lillypilly (aka people.u.c.) got upgraded to lucid over the holidays, so we have a recent enough launchpadlib available now.

This looks a lot better than the screenscraping, thanks for porting this! Surprising that they added API for blueprints, I thought they were doomed to go away entirely.

review: Approve
Martin Pitt (pitti) wrote :

This breaks the --pattern option, which is quite handy for debugging. I'll fix this when merging.

Martin Pitt (pitti) wrote :

Unfortunately this exposes another bug in launchpadlib for building the cache file names:

IOError: [Errno 36] File name too long: '/home/martin/.launchpadlib/api.launchpad.net/cache/api.launchpad.net,devel,ubuntu,+spec,packageselection-desktop-n-bringing-desktop-and-netbook-image-closer,bugs-applic,89e26dc98142f3393a4550f041f7b150'

In order to not lose my modifications, I pushed the merge (and fix) to lp:~work-items-tracker-hackers/launchpad-work-items-tracker/blueprints-api

259. By Martin Pitt on 2011-01-11

html-report: Add textual stats over time

260. By Martin Pitt on 2011-01-11

html-report: sort dates

261. By Martin Pitt on 2011-01-11

fix time-stats todo column

262. By Martin Pitt on 2011-01-11

time-stats: not all states exist always

263. By Martin Pitt on 2011-01-29

collect: add "Expired" bug state

264. By James Westby on 2011-02-07

Merge blueprints API branch.

Martin Pitt (pitti) wrote :

This is all merged into lp:~work-items-tracker-hackers/launchpad-work-items-tracker/blueprints-api now. That branch collects up all of your recent work, many thanks! It works fine locally, and it seems the "too long file name" problem was just transient. Please see https://code.launchpad.net/~work-items-tracker-hackers/launchpad-work-items-tracker/blueprints-api/+merge/49047 for the final merge request into trunk, which is currently blocked by an RT.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'collect'
2--- collect 2011-01-29 09:08:54 +0000
3+++ collect 2011-02-07 20:14:16 +0000
4@@ -3,11 +3,11 @@
5 # Pull work items from various sources (blueprint whiteboards, linked blueprint
6 # bugs, wiki pages) and put them into a database.
7
8-import urllib, re, sys, optparse, smtplib, pwd, os
9+import urllib, re, sys, optparse, smtplib, pwd, os, urlparse
10 from email.mime.text import MIMEText
11 import sqlite3 as dbapi2
12
13-from launchpadlib.launchpad import Launchpad, EDGE_SERVICE_ROOT
14+from launchpadlib.launchpad import Launchpad
15
16 import report_tools
17
18@@ -59,51 +59,20 @@
19 dbg('Queueing data error: ' + s)
20
21
22+def web_link(item):
23+ """Get a link to the Launchpad API object on the website."""
24+ api_link = item.self_link
25+ parts = urlparse.urlparse(api_link)
26+ return parts.scheme + "://" + parts.netloc.replace("api.", "") + "/" + parts.path.split("/", 2)[2]
27+
28+
29 ########################################################################
30 #
31 # Functions for parsing Launchpad data
32 #
33 ########################################################################
34
35-def lp_blueprints_from_list(url, name_pattern = None):
36- '''Return blueprints from a LP list view.
37-
38- This can optionally specify a name pattern (which is mostly useful for
39- testing and debugging, to only parse a small subset of blueprints)
40-
41- Return a dictionary name -> (url, milestone).
42- '''
43- blueprint_name_filter = re.compile('href="(/[a-zA-Z0-9-]+/\+spec/%s[^"]+)"(?!.*class.*sprite)' %
44- (name_pattern or '.'))
45- milestone_re = re.compile('href=".*/[a-zA-Z0-9-]+/\+milestone/([^"]+)"')
46-
47- result = {}
48- scan_tr_end = False
49- milestone = None
50- bp = None
51- dbg("lp_blueprints_from_list(): Loading '%s'" % url)
52- for l in urllib.urlopen(url):
53- if scan_tr_end:
54- m = milestone_re.search(l)
55- if m:
56- milestone = m.group(1)
57- dbg('lp_blueprints_from_list(): current spec milestone: ' + milestone)
58- if '</tr>' in l:
59- scan_tr_end = False
60- if bp:
61- result[bp.split('/')[-1]] = (bp, milestone)
62- bp = None
63- milestone = None
64- else:
65- m = blueprint_name_filter.search(l)
66- if m:
67- bp = report_tools.blueprints_base_url + m.group(1)
68- dbg('lp_blueprints_from_list(): found BP: ' + bp)
69- scan_tr_end = True
70-
71- return result
72-
73-def lp_import_blueprint(db, name, url, contents):
74+def lp_import_blueprint(db, bp):
75 '''Import details of a blueprint into the specs table.
76
77 If a blueprint does not have a status, use the description.
78@@ -117,56 +86,50 @@
79 cur.execute('SELECT name FROM milestones')
80 valid_milestones = [m[0] for m in cur] + [None]
81
82- queries = {
83- 'priority': '<dt>Priority:</dt>\s*<dd>\s*([^\n]*)',
84- 'definition': '<dt>Definition:</dt>\s*<dd>\s*([^\n]*)',
85- 'implementation': '<dt>Implementation:</dt>\s*<dd>\s*([^\n]*)',
86- 'approver': '<dt>Approver:</dt>\s*<dd>\s*<a href="https://.*?launchpad.net/~([a-zA-Z0-9_-]+)" ',
87- 'drafter': '<dt>Drafter:</dt>\s*<dd>\s*<a href="https://.*?launchpad.net/~([a-zA-Z0-9_-]+)" ',
88- 'assignee': '<dt>Assignee:</dt>\s*<dd>\s*<a href="https://.*?launchpad.net/~([a-zA-Z0-9_-]+)" ',
89- 'milestone': '<dt>Milestone target:</dt>\s*<dd>\s*<a href="https://.*?launchpad.net/\w+/\+milestone/([a-zA-Z0-9_\.-]+)"',
90- 'description': '<div class="top-portlet">\s*<p>(.*?)</p>',
91- 'status': '(?:<p>|^)[Ss]tatus:\s*<br />(.*?)</p>',
92- 'details_url': 'href="([^"]*)">Read the full specification</a>',
93- 'roadmap_notes': '(?:<p>|^)[Rr]oadmap\s+[Nn]otes:\s*<br />(.*?)</p>',
94- }
95+ def get_whiteboard_section(heading):
96+ if bp.whiteboard is None:
97+ return None
98+ regex = '^' + heading + ':\s*\n(.*?)\n\n'
99+ m = re.search(regex, bp.whiteboard, re.S | re.I | re.M)
100+ if m is not None:
101+ section = m.group(1).strip()
102+ else:
103+ section = None
104+ return section
105
106 data = {}
107- for key, r in queries.iteritems():
108- m = re.search(r, contents, re.S)
109- if not m:
110- dbg('lp_import_blueprint(%s): did not find a match for key %s' % (name, key))
111- if key == 'description':
112- data[key] = '(no description)'
113- else:
114- data[key] = None
115- else:
116- data[key] = m.group(1).strip()
117+ data['priority'] = bp.priority
118+ data['definition'] = bp.definition_status
119+ data['implementation'] = bp.implementation_status
120+ data['approver'] = bp.approver and bp.approver.name or None
121+ data['drafter'] = bp.drafter and bp.drafter.name or None
122+ data['assignee'] = bp.assignee and bp.assignee.name or None
123+ data['milestone'] = bp.milestone and bp.milestone.name or None
124+ data['description'] = bp.summary or "(no description)"
125+ data['status'] = get_whiteboard_section('Status')
126+ data['details_url'] = bp.specification_url
127+ data['roadmap_notes'] = get_whiteboard_section('Roadmap\s+Notes')
128
129 # ignore "later" milestone"
130 if data['milestone'] == 'later':
131- dbg('lp_import_blueprint(%s): spec has "later" milestone, ignoring it' % name)
132+ dbg('lp_import_blueprint(%s): spec has "later" milestone, ignoring it' % bp.name)
133 return False
134
135 if data['milestone'] not in valid_milestones:
136- data_error(url, 'milestone "%s" is unknown/invalid' % data['milestone'], True)
137+ data_error(web_link(bp), 'milestone "%s" is unknown/invalid' % data['milestone'], True)
138
139 # if we have an explicit status: in the whiteboard, use that; otherwise use
140 # description as status text
141 if data['status']:
142- data['status'] = data['status'].replace('<br />',
143- '\n').replace('</div>', '').replace('&nbsp;', ' ').replace(
144- '<wbr></wbr>', '').strip()
145+ data['status'] = data['status'].strip()
146 else:
147- data['status'] = data['description'].replace('<br />',
148- '\n').replace('</div>', '').replace('&nbsp;', ' ').replace(
149- '<wbr></wbr>', '').strip()
150+ data['status'] = data['description'].strip()
151 del data['description']
152
153- dbg('lp_import_blueprint(%s): finished parsing; data: %s' % (name, str(data)))
154+ dbg('lp_import_blueprint(%s): finished parsing; data: %s' % (bp.name, str(data)))
155
156 db.cursor().execute('INSERT INTO specs VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?,?)',
157- (name, url, data['priority'], data['implementation'],
158+ (bp.name, web_link(bp), data['priority'], data['implementation'],
159 data['assignee'], data['status'], data['milestone'],
160 data['definition'], data['drafter'], data['approver'],
161 data['details_url'], data['roadmap_notes']))
162@@ -178,10 +141,7 @@
163
164 '''
165
166- ### cargo culted from parse_blueprint_workitem
167- line = line.replace('<br />', '').replace('</div>', '').replace('</p>',
168- '').replace('<wbr></wbr>', '').replace('&nbsp;', ' ').replace(
169- '<a href="/', '<a href="https://launchpad.net/').strip()
170+ line = line.strip()
171 if not line:
172 return
173
174@@ -201,14 +161,9 @@
175 db.commit()
176
177 def parse_complexity_item(lp, db, line, bp_name, bp_url, def_milestone, def_assignee):
178-
179- line = line.replace('<br />', '').replace('</div>', '').replace('</p>',
180- '').replace('<wbr></wbr>', '').replace('&nbsp;', ' ').replace(
181- '<a href="/', '<a href="https://launchpad.net/').strip()
182-
183- # remove special characters people tend to type
184+ line = line.strip()
185+ # remove special characters people tend to type
186 line = re.sub('[^\w -.]', '', line)
187-
188 if not line:
189 return
190
191@@ -219,17 +174,15 @@
192 assignee = None
193
194 try:
195- list = line.split()
196- list.reverse()
197+ complexity_list = line.split()
198+ complexity_list.reverse()
199
200 # we may not have any values in the list, so append our
201 # default values in the right order so they can be mapped
202 defs = [None, def_milestone, def_assignee]
203- for i in range(len(list), len(defs)):
204- list.append(defs[i])
205-
206- (num, milestone, assignee) = list
207-
208+ for i in range(len(complexity_list), len(defs)):
209+ complexity_list.append(defs[i])
210+ (num, milestone, assignee) = complexity_list
211 if not num:
212 data_error(bp_url, 'No complexity points defined for %s' % line)
213
214@@ -238,17 +191,17 @@
215 cur = db.cursor()
216 cur.execute('INSERT INTO complexity VALUES(?,?,?,?,date(CURRENT_TIMESTAMP))',
217 (assignee, num, milestone, bp_name))
218-
219 except ValueError:
220 data_error(bp_url, "\tComplexity line '%s' could not be parsed %s" % (line, ValueError))
221
222+
223 def parse_blueprint_workitem(line, default_assignee, milestone,
224 blueprint_url, launchpad, result_list):
225 '''Parse a work item line of a blueprint whiteboard.'''
226
227- line = line.replace('<br />', '').replace('</div>', '').replace('</p>',
228- '').replace('<wbr></wbr>', '').replace('&nbsp;', ' ').replace(
229- '<a href="/', '<a href="https://launchpad.net/').strip()
230+ # FIXME: we lose bug linking etc. that is done by the tales
231+ # formatters in LP here.
232+ line = line.strip()
233 if not line:
234 return
235 dbg("\tworkitem (clean): '%s'" % (line))
236@@ -291,13 +244,10 @@
237
238 result_list.append((desc, state, assignee, milestone))
239
240-def follow_blueprint_buglink(bugnum, default_assignee, blueprint_name,
241+def follow_blueprint_buglink(bug, default_assignee, blueprint_name,
242 launchpad, release, default_milestone, result_list):
243 '''Query launchpad for the information on a linked bug'''
244
245- bugnum = int(bugnum)
246- dbg('follow_blueprint_buglink(): processing bug %i (default milestone: %s)' % (bugnum, default_milestone))
247- bug = launchpad.bugs[bugnum]
248 for task in bug.bug_tasks:
249 if task.status == 'Unknown':
250 # this can happen for upstream tasks
251@@ -338,7 +288,7 @@
252 if better_task:
253 continue
254
255- desc = '<a href="https://launchpad.net/bugs/%d">LP: #%d</a>: ' % (bugnum, bugnum) + bug.title
256+ desc = '<a href="https://launchpad.net/bugs/%d">LP: #%d</a>: ' % (bug.id, bug.id) + bug.title
257 if rtype != 'distribution':
258 desc += ' (%s)' % target.name
259
260@@ -363,32 +313,27 @@
261 return word
262 return None
263
264-def lp_import_blueprint_workitems(lp, db, bp_name, bp_url, contents, release):
265+def lp_import_blueprint_workitems(lp, db, bp, release):
266 '''Collect work items from a Launchpad blueprint.
267
268 This includes work items from the whiteboard as well as linked bugs.
269 '''
270- linked_bugs_re = re.compile('<div id="bug_links".*>', re.I)
271- bugnum_re = re.compile('<a href="https://bugs\..*launchpad\.net/bugs/([0-9]+)" class="sprite bug">')
272- work_items_re = re.compile('(<p>|^)work items(.*)\s*:\s*<br />', re.I)
273- meta_re = re.compile( '(<p>|^)Meta.*?:<br />', re.I )
274- complexity_re = re.compile( '(<p>|^)Complexity.*?:<br />', re.I )
275+ work_items_re = re.compile('^work items(.*)\s*:\s*$', re.I)
276+ meta_re = re.compile('^Meta.*?:$', re.I)
277+ complexity_re = re.compile('^Complexity.*?:$', re.I)
278
279 in_workitems_block = False
280- in_linked_bugs_block = False
281 in_meta_block = False
282 in_complexity_block = False
283- found_linked_bugs = False
284 work_items = []
285- found_wb_workitems = False
286 milestone = None
287
288 cur = db.cursor()
289- cur.execute('SELECT assignee, milestone, implementation FROM specs WHERE name = ?', (bp_name,))
290+ cur.execute('SELECT assignee, milestone, implementation FROM specs WHERE name = ?', (bp.name,))
291 (spec_assignee, spec_milestone, spec_implementation) = cur.fetchone()
292
293 dbg('lp_import_blueprint_workitems(): processing %s (spec milestone: %s, spec assignee: %s, spec implementation: %s)' % (
294- bp_name, spec_milestone, spec_assignee, spec_implementation))
295+ bp.name, spec_milestone, spec_assignee, spec_implementation))
296
297 cur.execute('SELECT team FROM teams WHERE name = ?', (spec_assignee,))
298 assignee_teams = [t[0] for t in cur]
299@@ -396,69 +341,51 @@
300 cur.execute('SELECT name FROM milestones')
301 valid_milestones = [m[0] for m in cur]
302
303- for l in contents.splitlines():
304-
305- if not in_workitems_block and not in_linked_bugs_block \
306- and not in_meta_block and not in_complexity_block:
307- m = work_items_re.search(l)
308- if m:
309- in_workitems_block = True
310- found_wb_workitems = True
311- dbg('lp_import_blueprint_workitems(): starting work items block at ' + l)
312- if not milestone:
313- milestone = milestone_extract(m.group(2), valid_milestones)
314- dbg(' ... setting milestone to ' + str(milestone))
315- if linked_bugs_re.search(l):
316- in_linked_bugs_block = True
317- found_linked_bugs = True
318- if meta_re.search(l):
319- in_meta_block = True
320- if complexity_re.search(l):
321- in_complexity_block = True
322- continue
323-
324- if in_workitems_block:
325- dbg("\tworkitem (raw): '%s'" % (l.strip()))
326- parse_blueprint_workitem(l, spec_assignee, milestone or
327- spec_milestone, bp_url, lp, work_items)
328-
329- if '</p>' in l:
330- dbg('lp_import_blueprint_workitems(): closing work items block with line: ' + l)
331- in_workitems_block = False
332- milestone = None
333-
334- if in_linked_bugs_block:
335- dbg("\tbug block line (raw): '%s'" % (l.strip()))
336- m = bugnum_re.search(l)
337- if m and lp:
338- follow_blueprint_buglink(m.group(1), spec_assignee,
339- bp_name, lp, release,
340- milestone or spec_milestone, work_items)
341-
342- if '</div>' in l:
343- in_linked_bugs_block = False
344- continue
345-
346- if in_meta_block:
347- dbg("\tmeta line (raw): '%s'" % (l.strip()))
348- parse_meta_item( lp, db, l, bp_name )
349-
350- # done with the meta information?
351- if '</p>' in l:
352- in_meta_block = False
353- continue
354-
355- if in_complexity_block:
356- dbg("\tcomplexity block line (raw): '%s'" % (l.strip()))
357- parse_complexity_item(lp, db, l, bp_name, bp_url, spec_milestone, spec_assignee)
358-
359- # done with the meta information?
360- if '</p>' in l:
361- in_complexity_block = False
362- continue
363-
364- if found_wb_workitems and '</div>' in l:
365- break
366+ if bp.whiteboard:
367+ for l in bp.whiteboard.splitlines():
368+
369+ if (not in_workitems_block
370+ and not in_meta_block and not in_complexity_block):
371+ m = work_items_re.search(l)
372+ if m:
373+ in_workitems_block = True
374+ dbg('lp_import_blueprint_workitems(): starting work items block at ' + l)
375+ if not milestone:
376+ milestone = milestone_extract(m.group(1), valid_milestones)
377+ dbg(' ... setting milestone to ' + str(milestone))
378+ if meta_re.search(l):
379+ in_meta_block = True
380+ if complexity_re.search(l):
381+ in_complexity_block = True
382+ continue
383+
384+ if in_workitems_block:
385+ dbg("\tworkitem (raw): '%s'" % (l.strip()))
386+ if not l.strip():
387+ dbg('lp_import_blueprint_workitems(): closing work items block with line: ' + l)
388+ in_workitems_block = False
389+ milestone = None
390+ parse_blueprint_workitem(l, spec_assignee, milestone or
391+ spec_milestone, web_link(bp), lp, work_items)
392+
393+ if in_meta_block:
394+ dbg("\tmeta line (raw): '%s'" % (l.strip()))
395+ if not l.strip():
396+ in_meta_block = False
397+ continue
398+ parse_meta_item(lp, db, l, bp.name)
399+
400+ if in_complexity_block:
401+ dbg("\tcomplexity block line (raw): '%s'" % (l.strip()))
402+ if not l.strip():
403+ in_complexity_block = False
404+ continue
405+ parse_complexity_item(lp, db, l, bp.name, web_link(bp), spec_milestone, spec_assignee)
406+
407+ if lp:
408+ for bug in bp.bugs:
409+ follow_blueprint_buglink(bug, spec_assignee, bp.name, lp, release,
410+ spec_milestone, work_items)
411
412 if not work_items:
413 #data_error(bp_url, 'no work items defined', True)
414@@ -474,7 +401,8 @@
415
416 for (desc, status, assignee, milestone) in work_items:
417 db.cursor().execute('INSERT INTO work_items VALUES (?, ?, ?, ?, ?, date(CURRENT_TIMESTAMP))',
418- (desc, bp_name, status, assignee, milestone))
419+ (desc, bp.name, status, assignee, milestone))
420+
421
422 def lp_import_milestones(lp_project, db):
423 '''Import milestones from Launchpad into DB.
424@@ -550,29 +478,41 @@
425 db.cursor().execute('INSERT INTO work_items VALUES (?, ?, ?, ?, ?, date(CURRENT_TIMESTAMP))',
426 (title, name, state, assignee, milestone))
427
428-def lp_import(db, cfg, name_pattern = None):
429+def lp_import(db, cfg, name_pattern=None):
430 '''Collect blueprint work items and status from Launchpad into DB.'''
431
432- lp = Launchpad.login_with('ubuntu-work-items', service_root=EDGE_SERVICE_ROOT)
433+ lp = Launchpad.login_with('ubuntu-work-items', service_root="production", version="devel")
434+ projects = []
435
436 if 'release' in cfg:
437 lp_project = lp.distributions['ubuntu'].getSeries(name_or_version=cfg['release'])
438- url = '%s/ubuntu/%s/+specs?batch=300' % (
439- report_tools.blueprints_base_url, cfg['release'])
440+ projects.append(lp_project)
441 else:
442 assert 'project' in cfg, 'Configuration needs to specify project or release'
443 lp_project = lp.projects[cfg['project']]
444- url = '%s/%s/+specs?batch=300' % (
445- report_tools.blueprints_base_url, cfg['project'])
446+ projects.append(lp_project)
447
448 lp_import_milestones(lp_project, db)
449 lp_import_teams(lp, db, cfg)
450
451- for (bp, (url, milestone)) in lp_blueprints_from_list(url, name_pattern).iteritems():
452- dbg('lp_import(): downloading %s from %s' % (bp, url))
453- contents = urllib.urlopen(url).read().decode('UTF-8')
454- if lp_import_blueprint(db, bp, url, contents):
455- lp_import_blueprint_workitems(lp, db, bp, url, contents, cfg.get('release'))
456+ extra_projects = cfg.get('extra_projects', [])
457+ for extra_project_name in extra_projects:
458+ extra_project = lp.projects[extra_project_name]
459+ lp_import_milestones(extra_project, db)
460+ projects.append(extra_project)
461+
462+ if name_pattern:
463+ name_filter = re.compile(name_pattern)
464+
465+ for project in projects:
466+ # XXX: should this be valid_ or all_specifications?
467+ for bp in project.valid_specifications:
468+ if name_pattern:
469+ if not name_filter.search(bp.name):
470+ continue
471+ dbg('lp_import(): downloading %s from %s' % (bp.name, bp.self_link))
472+ if lp_import_blueprint(db, bp):
473+ lp_import_blueprint_workitems(lp, db, bp, cfg.get('release'))
474
475 lp_import_bug_workitems(lp_project, db, cfg)
476

Subscribers

People subscribed via source and target branches

to all changes: