1 import datetime
2 import markdown
3
4 from sqlalchemy.ext.associationproxy import association_proxy
5 from libravatar import libravatar_url
6
7 from coprs import constants
8 from coprs import db
9 from coprs import helpers
13 """Usage:
14 SQLAlchObject.to_dict() => returns a flat dict of the object
15 SQLAlchObject.to_dict({'foo': {}}) => returns a dict of the object and will include a
16 flat dict of object foo inside of that
17 SQLAlchObject.to_dict({'foo': {'bar': {}}, 'spam': {}}) => returns a dict of the object,
18 which will include dict of foo (which will include dict of bar) and dict of spam
19
20 Options can also contain two special values: __columns_only__ and __columns_except__
21 If present, the first makes only specified fiels appear, the second removes specified fields.
22 Both of these fields must be either strings (only works for one field) or lists (for one and more fields).
23 SQLAlchObject.to_dict({'foo': {'__columns_except__': ['id']}, '__columns_only__': 'name'}) =>
24 The SQLAlchObject will only put its 'name' into the resulting dict, while 'foo' all of its fields except 'id'.
25
26 Options can also specify whether to include foo_id when displaying related foo object
27 (__included_ids__, defaults to True). This doesn't apply when __columns_only__ is specified.
28 """
29 result = {}
30 columns = self.serializable_attributes
31
32 if options.has_key('__columns_only__'):
33 columns = options['__columns_only__']
34 else:
35 columns = set(columns)
36 if options.has_key('__columns_except__'):
37 columns_except = options['__columns_except__'] if isinstance(options['__columns_except__'], list) else [options['__columns_except__']]
38 columns -= set(columns_except)
39 if options.has_key('__included_ids__') and options['__included_ids__'] == False:
40 related_objs_ids = [r + '_id' for r, o in options.items() if not r.startswith('__')]
41 columns -= set(related_objs_ids)
42
43 columns = list(columns)
44
45 for column in columns:
46 result[column] = getattr(self, column)
47
48 for related, values in options.items():
49 if hasattr(self, related):
50 result[related] = getattr(self, related).to_dict(values)
51 return result
52
53 @property
56
57 -class User(db.Model, Serializer):
58 """Represents user of the copr frontend"""
59 id = db.Column(db.Integer, primary_key = True)
60
61 openid_name = db.Column(db.String(100), nullable = False)
62
63 mail = db.Column(db.String(150), nullable = False)
64
65 timezone = db.Column(db.String(50), nullable = True)
66
67 proven = db.Column(db.Boolean, default = False)
68
69 admin = db.Column(db.Boolean, default = False)
70
71 api_login = db.Column(db.String(40), nullable = False, default = 'abc')
72 api_token = db.Column(db.String(40), nullable = False, default = 'abc')
73 api_token_expiration = db.Column(db.Date, nullable = False, default = datetime.date(2000, 1, 1))
74
75 @property
77 """Returns the short username of the user, e.g. bkabrda"""
78 return self.openid_name.replace('.id.fedoraproject.org/', '').replace('http://', '')
79
81 """Get permissions of this user for the given copr.
82 Caches the permission during one request, so use this if you access them multiple times
83 """
84 if not hasattr(self, '_permissions_for_copr'):
85 self._permissions_for_copr = {}
86 if not copr.name in self._permissions_for_copr:
87 self._permissions_for_copr[copr.name] = CoprPermission.query.filter_by(user = self).filter_by(copr = copr).first()
88 return self._permissions_for_copr[copr.name]
89
99
109
110 @classmethod
112 """Creates proper openid_name from short name.
113
114 >>> user.openid_name == User.openidize_name(user.name)
115 True
116 """
117 return 'http://{0}.id.fedoraproject.org/'.format(name)
118
119 @property
121
122 return ['id', 'name']
123
124 @property
126 """Get number of coprs for this user."""
127 return Copr.query.filter_by(owner=self).\
128 filter_by(deleted=False).\
129 count()
130
131 @property
133 """Return url to libravatar image."""
134 try:
135 return libravatar_url(email = self.mail)
136 except IOError:
137 return ""
138
139 -class Copr(db.Model, Serializer):
140 """Represents a single copr (private repo with builds, mock chroots, etc.)."""
141 id = db.Column(db.Integer, primary_key = True)
142
143 name = db.Column(db.String(100), nullable = False)
144
145
146 repos = db.Column(db.Text)
147
148 created_on = db.Column(db.Integer)
149
150 description = db.Column(db.Text)
151 instructions = db.Column(db.Text)
152 deleted = db.Column(db.Boolean, default=False)
153
154
155 owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
156 owner = db.relationship('User', backref = db.backref('coprs'))
157 mock_chroots = association_proxy('copr_chroots', 'mock_chroot')
158
159 __mapper_args__ = {
160 'order_by' : created_on.desc()
161 }
162
163 @property
165 """Returns repos of this copr as a list of strings"""
166 return self.repos.split()
167
168 @property
170 md = markdown.Markdown(safe_mode='replace',
171 html_replacement_text='--RAW HTML NOT ALLOWED--')
172 return md.convert(self.description) or 'Description not filled in by author. Very likely personal repository for testing purpose, which you should not use.'
173
174 @property
176 md = markdown.Markdown(safe_mode='replace',
177 html_replacement_text='--RAW HTML NOT ALLOWED--')
178 return md.convert(self.instructions) or 'Instructions not filled in by author. Author knows what to do. Everybody else should avoid this repo.'
179
180 @property
182 """Returns list of active mock_chroots of this copr"""
183 return filter(lambda x: x.is_active, self.mock_chroots)
184
185 @property
187 """ Return number of builds in this copr """
188
189 return len(self.builds)
190
192 """Return object of chroot, if is related to our copr or None"""
193 result = None
194
195 for copr_chroot in self.copr_chroots:
196 if copr_chroot.mock_chroot_id == chroot.id:
197 result = copr_chroot
198 break
199 return result
200
209
211 """Association class for Copr<->Permission relation"""
212
213
214 copr_builder = db.Column(db.SmallInteger, default = 0)
215
216 copr_admin = db.Column(db.SmallInteger, default = 0)
217
218
219 user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key = True)
220 user = db.relationship('User', backref = db.backref('copr_permissions'))
221 copr_id = db.Column(db.Integer, db.ForeignKey('copr.id'), primary_key = True)
222 copr = db.relationship('Copr', backref = db.backref('copr_permissions'))
223
224 -class Build(db.Model, Serializer):
225 """Representation of one build in one copr"""
226 id = db.Column(db.Integer, primary_key = True)
227
228 pkgs = db.Column(db.Text)
229
230 canceled = db.Column(db.Boolean, default = False)
231
232 repos = db.Column(db.Text)
233
234
235 submitted_on = db.Column(db.Integer, nullable = False)
236 started_on = db.Column(db.Integer)
237 ended_on = db.Column(db.Integer)
238
239 results = db.Column(db.Text)
240
241 memory_reqs = db.Column(db.Integer, default = constants.DEFAULT_BUILD_MEMORY)
242
243 timeout = db.Column(db.Integer, default = constants.DEFAULT_BUILD_TIMEOUT)
244
245
246 user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
247 user = db.relationship('User', backref = db.backref('builds'))
248 copr_id = db.Column(db.Integer, db.ForeignKey('copr.id'))
249 copr = db.relationship('Copr', backref = db.backref('builds'))
250
251 chroots = association_proxy('build_chroots', 'mock_chroot')
252
253 @property
255 return map(lambda chroot: chroot.status, self.build_chroots)
256
257 @property
260
261 @property
270
271 @property
273 """ Return text representation of status of this build """
274 if self.status is not None:
275 return helpers.StatusEnum(self.status)
276
277 return 'unknown'
278
279 @property
281 """
282 Find out if this build is cancelable.
283
284 ATM, build is cancelable only if it wasn't grabbed by backend.
285 """
286
287 return self.status == helpers.StatusEnum('pending')
288
291 """Representation of mock chroot"""
292 id = db.Column(db.Integer, primary_key = True)
293
294 os_release = db.Column(db.String(50), nullable = False)
295
296 os_version = db.Column(db.String(50), nullable = False)
297
298 arch = db.Column(db.String(50), nullable = False)
299 is_active = db.Column(db.Boolean, default = True)
300
301 @property
303 """Textual representation of name of this chroot"""
304 if self.os_version:
305 format_string = '{rel}-{ver}-{arch}'
306 else:
307 format_string = '{rel}-{arch}'
308 return format_string.format(rel=self.os_release,
309 ver=self.os_version,
310 arch=self.arch)
311
313 """Representation of Copr<->MockChroot relation"""
314 buildroot_pkgs = db.Column(db.Text)
315 mock_chroot_id = db.Column(db.Integer, db.ForeignKey('mock_chroot.id'), primary_key = True)
316 mock_chroot = db.relationship('MockChroot', backref = db.backref('copr_chroots'))
317 copr_id = db.Column(db.Integer, db.ForeignKey('copr.id'), primary_key = True)
318 copr = db.relationship('Copr', backref = db.backref('copr_chroots',
319 single_parent=True,
320 cascade='all,delete,delete-orphan'))
321
324 """Representation of Build<->MockChroot relation"""
325 mock_chroot_id = db.Column(db.Integer, db.ForeignKey('mock_chroot.id'),
326 primary_key=True)
327 mock_chroot = db.relationship('MockChroot', backref=db.backref('builds'))
328 build_id = db.Column(db.Integer, db.ForeignKey('build.id'),
329 primary_key=True)
330 build = db.relationship('Build', backref=db.backref('build_chroots'))
331 status = db.Column(db.Integer, default=helpers.StatusEnum('pending'))
332
333 @property
335 """ Textual representation of name of this chroot """
336 return self.mock_chroot.name
337
338 @property
340 """ Return text representation of status of this build chroot """
341 if self.status is not None:
342 return helpers.StatusEnum(self.status)
343
344 return 'unknown'
345
346
347
348 -class LegalFlag(db.Model, Serializer):
349 id = db.Column(db.Integer, primary_key=True)
350
351 raise_message = db.Column(db.Text)
352
353 raised_on = db.Column(db.Integer)
354
355 resolved_on = db.Column(db.Integer)
356
357
358 copr_id = db.Column(db.Integer, db.ForeignKey('copr.id'), nullable=True)
359
360 copr = db.relationship('Copr', backref=db.backref('legal_flags', cascade='all'))
361
362 reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
363 reporter = db.relationship('User',
364 backref=db.backref('legal_flags_raised'),
365 foreign_keys=[reporter_id],
366 primaryjoin='LegalFlag.reporter_id==User.id')
367
368 resolver_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
369 resolver = db.relationship('User',
370 backref=db.backref('legal_flags_resolved'),
371 foreign_keys=[resolver_id],
372 primaryjoin='LegalFlag.resolver_id==User.id')
373
374
375 -class Action(db.Model, Serializer):
415