Merge lp:~ursinha/shadow-database/add-milestone-distro-distroseries into lp:shadow-database

Proposed by Ursula Junque
Status: Merged
Merge reported by: Chris J Arges
Merged at revision: not available
Proposed branch: lp:~ursinha/shadow-database/add-milestone-distro-distroseries
Merge into: lp:shadow-database
Prerequisite: lp:~ursinha/shadow-database/pep8-compliant-stuff
Diff against target: 444 lines (+225/-33)
4 files modified
ShadowDatabase/lp.py (+11/-1)
ShadowDatabase/models.py (+34/-7)
ShadowDatabase/sql.py (+171/-19)
database/tables.sql (+9/-6)
To merge this branch: bzr merge lp:~ursinha/shadow-database/add-milestone-distro-distroseries
Reviewer Review Type Date Requested Status
Chris J Arges Pending
Review via email: mp+143596@code.launchpad.net

Description of the change

Implements add_milestone, add_distro, add_distroseries and also handles when an user is gone or merged in launchpad, that was causing the script to fail and abort.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ShadowDatabase/lp.py'
2--- ShadowDatabase/lp.py 2013-01-16 21:43:19 +0000
3+++ ShadowDatabase/lp.py 2013-01-16 21:43:19 +0000
4@@ -77,11 +77,21 @@
5 try:
6 search_json = self.get_json_from_link(search_link)
7 break
8- except (NotFound, HTTPError, ClientError, ServerError):
9+ except ClientError, e:
10+ response_status = e.response["status"]
11+ if response_status == '410':
12+ self.logger.error("Object %s is gone." % search_link)
13+ raise
14+ if response_status == '404':
15+ self.logger.error("Object %s not found." % search_link)
16+ raise
17+ continue
18+ except (NotFound, HTTPError, ServerError), e:
19 continue
20 pass
21 except Exception, e:
22 self.logger.error("Unexpected exception: %s" % str(e))
23+ self.logger.exception(e)
24 pass
25
26 return search_json
27
28=== modified file 'ShadowDatabase/models.py'
29--- ShadowDatabase/models.py 2013-01-16 21:43:19 +0000
30+++ ShadowDatabase/models.py 2013-01-16 21:43:19 +0000
31@@ -8,6 +8,7 @@
32 from storm.locals import Int
33 from storm.expr import SQL
34 from datetime import datetime
35+from pytz import UTC
36 import dateutil.parser
37
38
39@@ -29,6 +30,7 @@
40 series_target = IntCol()
41 target = IntCol()
42 release = IntCol()
43+ self_link = StringCol()
44
45
46 class Person(object):
47@@ -46,6 +48,29 @@
48 private = BoolCol(notNull=True, default=False,)
49 irc_nicknames = StringCol()
50 is_team = BoolCol(notNull=True, default=False,)
51+ is_gone = BoolCol(notNull=True, default=False,)
52+
53+ def __init__(self):
54+ pass
55+
56+
57+class MissingPerson(Person):
58+
59+ def __init__(self, person_link):
60+ super(MissingPerson, self).__init__()
61+ # The only thing we have is the name, because of the link.
62+ self.name = self.display_name = "".join(person_link.split("~")[1:])
63+ self.date_created = datetime.utcnow().replace(tzinfo=UTC)
64+ self.hide_email_addresses = True
65+ self.private = False
66+ self.is_team = False
67+ self.is_valid = False
68+ if not self.is_merged:
69+ self.is_gone = True
70+
71+ @property
72+ def is_merged(self):
73+ return "merged" in self.name
74
75
76 class Bug(object):
77@@ -125,6 +150,7 @@
78 web_link = StringCol(notNull=True,)
79 name = StringCol(notNull=True,)
80 display_name = StringCol(notNull=True,)
81+ target_type = StringCol(notNull=True,)
82
83
84 class Bugtracker(object):
85@@ -235,13 +261,13 @@
86 self.date_left_closed = parseDate(json['date_left_closed'])
87 self.date_left_new = parseDate(json['date_left_new'])
88 self.date_triaged = parseDate(json['date_triaged'])
89- # self.milestone = 0 # FIXME
90+ self.milestone = sql.add_milestone(json['milestone_link'])
91 self.owner = sql.add_person(json['owner_link'])
92 self.status = sql.lp.StatusMap[json['status']]
93 self.importance = (sql.lp.ImportanceMap[json['importance']])
94 self.target = sql.add_bug_target(json['target_link']) # FIXME
95 self.bug_target_name = json['bug_target_name']
96- # self.bug_watch = 0 # FIXME
97+ # self.bug_watch = sql.add_bug_watch(json['bug_watch_link'])
98
99 def update(self, db):
100 # FIXME: there has to be a better way.
101@@ -284,10 +310,10 @@
102 display_name = StringCol(notNull=True,)
103 summary = StringCol(notNull=True,)
104 bug_supervisor = IntCol()
105- security_contact = IntCol()
106 driver = IntCol()
107 current_series = IntCol()
108 date_created = UtcDateTimeCol(notNull=True,)
109+ self_link = StringCol(notNull=True,)
110
111
112 class Sourcepackage(object):
113@@ -316,15 +342,15 @@
114 sourcepackage = IntCol()
115
116
117-class Distroseries(object):
118+class DistroSeries(object):
119 __storm_table__ = "distroseries"
120 id = Int(primary=True,)
121 owner = IntCol()
122 registrant = IntCol()
123 status = StringCol()
124 name = StringCol()
125- display_name = StringCol()
126- full_series_name = StringCol()
127+ displayname = StringCol()
128+ fullseriesname = StringCol()
129 summary = StringCol()
130 supported = BoolCol(notNull=True, default=False,)
131 title = StringCol()
132@@ -333,7 +359,8 @@
133 component_names = StringCol()
134 distribution = IntCol()
135 date_created = UtcDateTimeCol(notNull=True,)
136- date_released = UtcDateTimeCol()
137+ datereleased = UtcDateTimeCol()
138+ self_link = StringCol(notNull=True,)
139
140
141 class Distrosourcepackage(object):
142
143=== modified file 'ShadowDatabase/sql.py'
144--- ShadowDatabase/sql.py 2013-01-16 21:43:19 +0000
145+++ ShadowDatabase/sql.py 2013-01-16 21:43:19 +0000
146@@ -15,6 +15,8 @@
147 import json
148
149 from datetime import datetime
150+from lazr.restfulclient.errors import ClientError
151+from pytz import UTC
152 from ShadowDatabase.lp import LP
153 from ShadowDatabase.models import *
154 from storm.expr import *
155@@ -154,6 +156,8 @@
156 bug_target.web_link = bug_target_json['web_link']
157 bug_target.name = bug_target_json['name']
158 bug_target.display_name = bug_target_json['display_name']
159+ target_type = bug_target_json['resource_type_link'].split("#")[1]
160+ bug_target.target_type = target_type
161
162 # Add the object.
163 self.store.add(bug_target)
164@@ -171,12 +175,142 @@
165 ret_str = ",".join(list(set(ret)))
166 return ",".join(ret)
167
168+ def add_bug_watch(self, bug_watch_link):
169+ # TODO: implement this
170+ pass
171+
172+ def add_milestone(self, milestone_link):
173+ if milestone_link is None:
174+ return None
175+
176+ # Get milestone id and check if it exists:
177+ result = self.store.find(Milestone,
178+ Milestone.self_link == milestone_link).one()
179+ if result:
180+ return result.id
181+
182+ milestone_json = self.lp.check_and_fetch_json(milestone_link)
183+ if milestone_json is None:
184+ return 0
185+
186+ milestone = Milestone()
187+ milestone.name = milestone_json["name"]
188+ milestone.code_name = milestone_json["code_name"]
189+ milestone.title = milestone_json["title"]
190+ # milestone.release = self.add_release(milestone_json["release_link"])
191+ milestone.is_active = milestone_json["is_active"]
192+ milestone.summary = milestone_json["summary"]
193+ milestone.target = self.add_distro(milestone_json["target_link"])
194+ milestone.series_target = self.add_distroseries(
195+ milestone_json["series_target_link"])
196+ milestone.self_link = milestone_json["self_link"]
197+ milestone.official_bug_tags = ",".join(
198+ milestone_json["official_bug_tags"])
199+ try:
200+ date_targeted = milestone.date_targeted = self.parseDate(
201+ milestone_json["date_targeted"])
202+ except ValueError:
203+ date_targeted = self.parseDate(
204+ milestone_json["date_targeted"]).replace(tzinfo=UTC)
205+ milestone.date_targeted = date_targeted
206+
207+ # Add the object.
208+ self.store.add(milestone)
209+ self.store.flush()
210+ self.logger.debug("MILESTONE: %s ... added." % str(milestone.name))
211+ return milestone.id
212+
213+ def add_distro(self, distro_link):
214+ if distro_link is None:
215+ return None
216+
217+ result = self.store.find(Distribution,
218+ Distribution.self_link == distro_link).one()
219+
220+ if result:
221+ return result.id
222+
223+ distro_json = self.lp.check_and_fetch_json(distro_link)
224+ if distro_json is None:
225+ return 0
226+
227+ distro = Distribution()
228+ distro.name = distro_json["name"]
229+ distro.title = distro_json["title"]
230+ distro.description = distro_json["description"]
231+ distro.owner = self.add_person(distro_json["owner_link"])
232+ distro.display_name = distro_json["display_name"]
233+ distro.summary = distro_json["summary"]
234+ distro.bug_supervisor = self.add_person(
235+ distro_json["bug_supervisor_link"])
236+ distro.driver = self.add_person(distro_json["driver_link"])
237+ distro.date_created = self.parseDate(distro_json["date_created"])
238+ distro.self_link = distro_json["self_link"]
239+
240+ # Add the object.
241+ self.store.add(distro)
242+ self.store.flush()
243+ # Have to add the distro after adding the distroseries to avoid
244+ # circular dependency.
245+ distro.current_series = self.add_distroseries(
246+ distro_json["current_series_link"])
247+ self.store.commit()
248+
249+ self.logger.debug("DISTRO: %s ... added." % str(distro.name))
250+ return distro.id
251+
252+ def add_distroseries(self, distroseries_link):
253+ if distroseries_link is None:
254+ return None
255+
256+ result = self.store.find(
257+ DistroSeries, DistroSeries.self_link == distroseries_link).one()
258+
259+ if result:
260+ return result.id
261+
262+ distroseries_json = self.lp.check_and_fetch_json(distroseries_link)
263+ if distroseries_json is None:
264+ return 0
265+
266+ distroseries = DistroSeries()
267+ distroseries.name = distroseries_json["name"]
268+ distroseries.owner = self.add_person(distroseries_json["owner_link"])
269+ distroseries.registrant = self.add_person(
270+ distroseries_json["registrant_link"])
271+ distroseries.status = distroseries_json["status"]
272+ distroseries.displayname = distroseries_json["displayname"]
273+ distroseries.fullseriesname = distroseries_json["fullseriesname"]
274+ distroseries.summary = distroseries_json["summary"]
275+ distroseries.supported = distroseries_json["supported"]
276+ distroseries.title = distroseries_json["title"]
277+ distroseries.version = distroseries_json["version"]
278+ distroseries.suite_names = ",".join(distroseries_json["suite_names"])
279+ distroseries.component_names = ",".join(
280+ distroseries_json["component_names"])
281+ distroseries.distribution = self.add_distro(
282+ distroseries_json["distribution_link"])
283+ distroseries.date_created = self.parseDate(
284+ distroseries_json["date_created"])
285+ distroseries.date_released = self.parseDate(
286+ distroseries_json["datereleased"])
287+ distroseries.self_link = distroseries_json["self_link"]
288+
289+ # Add the object.
290+ self.store.add(distroseries)
291+ self.store.flush()
292+ self.logger.debug("DISTROSERIES: %s ... added." % str(
293+ distroseries.name))
294+ return distroseries.id
295+
296 def add_person(self, person_link):
297 if person_link is None:
298 return None
299
300 # Quickly get the username by taking the end of the URL.
301 person_name = person_link.split('~')[1]
302+ person_is_gone = False
303+ person_is_merged = False
304 result = self.store.find(Person, Person.name == person_name).one()
305 if result:
306 # self.logger.debug("PERSON: %s ... skipping (exists)." %
307@@ -186,26 +320,44 @@
308 return result.id
309
310 # if the person wasn't found lets add it
311- person_json = self.lp.check_and_fetch_json(person_link)
312- if person_json is None:
313+ person_json = None
314+ try:
315+ person_json = self.lp.check_and_fetch_json(person_link)
316+ except ClientError, e:
317+ # If user is no longer available, was deactivated or deleted
318+ # because it is spam, we have to add it anyway, marking on the
319+ # table it's gone.
320+ response_status = e.response["status"]
321+ if response_status == '410':
322+ # Oops, user is no more
323+ person_is_gone = True
324+ if response_status == '404':
325+ # User not found (was merged or hidden -- spammer)
326+ if "merged" in person_name:
327+ person_is_merged = True
328+
329+ if person_is_gone or person_is_merged:
330+ person = MissingPerson(person_link)
331+ self.logger.debug("PERSON: %s ... is gone." % str(person.name))
332+ elif person_json is None:
333 return 0
334-
335- person = Person()
336- person.display_name = person_json['display_name']
337- if 'description' in person_json.keys():
338- person.description = person_json['description']
339- person.name = person_json['name']
340- person.date_created = self.parseDate(person_json['date_created'])
341- person.hide_email_addresses = person_json['hide_email_addresses']
342- person.preferred_mail_address_id = 0 # FIXME
343- person.is_valid = person_json['is_valid']
344- person.private = person_json['private']
345- person.irc_nicknames = self.parse_irc_nicknames_collection(
346- person_json['irc_nicknames_collection_link'])
347- person.is_team = person_json['is_team']
348- person.karma = person_json['karma']
349- if person.is_team:
350- person.owner = self.add_person(person_json['team_owner_link'])
351+ else:
352+ person = Person()
353+ person.display_name = person_json['display_name']
354+ if 'description' in person_json.keys():
355+ person.description = person_json['description']
356+ person.name = person_json['name']
357+ person.date_created = self.parseDate(person_json['date_created'])
358+ person.hide_email_addresses = person_json['hide_email_addresses']
359+ person.preferred_mail_address_id = 0 # FIXME
360+ person.is_valid = person_json['is_valid']
361+ person.private = person_json['private']
362+ person.irc_nicknames = self.parse_irc_nicknames_collection(
363+ person_json['irc_nicknames_collection_link'])
364+ person.is_team = person_json['is_team']
365+ person.karma = person_json['karma']
366+ if person.is_team:
367+ person.owner = self.add_person(person_json['team_owner_link'])
368
369 # Add the object.
370 self.store.add(person)
371
372=== modified file 'database/tables.sql'
373--- database/tables.sql 2012-10-17 18:52:52 +0000
374+++ database/tables.sql 2013-01-16 21:43:19 +0000
375@@ -9,6 +9,7 @@
376 series_target integer,
377 target integer,
378 release integer,
379+ self_link text NOT NULL,
380 PRIMARY KEY (id)
381 );
382
383@@ -24,6 +25,7 @@
384 hide_email_addresses boolean DEFAULT false NOT NULL,
385 preferred_mail_address integer,
386 is_valid boolean,
387+ is_gone boolean DEFAULT false NOT NULL,
388 private boolean DEFAULT false NOT NULL,
389 irc_nicknames text,
390 is_team boolean DEFAULT false NOT NULL,
391@@ -64,6 +66,7 @@
392 web_link text NOT NULL,
393 name text NOT NULL,
394 display_name text NOT NULL,
395+ target_type text NOT NULL,
396 PRIMARY KEY (id)
397 );
398
399@@ -124,7 +127,7 @@
400 web_link text,
401 PRIMARY KEY (id),
402 FOREIGN KEY (bug) REFERENCES bug(id),
403- FOREIGN KEY (message) REFERENCES bugmessage(id),
404+ FOREIGN KEY (message) REFERENCES bugmessage(id)
405 );
406
407 CREATE TABLE bugstatus (
408@@ -238,14 +241,13 @@
409 display_name text NOT NULL,
410 summary text NOT NULL,
411 bug_supervisor integer,
412- security_contact integer,
413 driver integer,
414 current_series integer,
415 date_created timestamp without time zone NOT NULL,
416+ self_link text NOT NULL,
417 PRIMARY KEY (id),
418 FOREIGN KEY (owner) REFERENCES person(id),
419 FOREIGN KEY (bug_supervisor) REFERENCES person(id),
420- FOREIGN KEY (security_contact) REFERENCES person(id),
421 FOREIGN KEY (driver) REFERENCES person(id)
422 );
423
424@@ -285,8 +287,8 @@
425 registrant integer,
426 status text,
427 name text,
428- display_name text,
429- full_series_name text,
430+ displayname text,
431+ fullseriesname text,
432 summary text,
433 supported boolean DEFAULT false NOT NULL,
434 title text,
435@@ -295,7 +297,8 @@
436 component_names text,
437 distribution integer,
438 date_created timestamp without time zone NOT NULL,
439- date_released timestamp without time zone,
440+ datereleased timestamp without time zone,
441+ self_link text NOT NULL,
442 PRIMARY KEY (id),
443 FOREIGN KEY (distribution) REFERENCES distribution(id),
444 FOREIGN KEY (owner) REFERENCES person(id),

Subscribers

People subscribed via source and target branches