class Base(AbstractConcreteBase): _submittedAt = db.Column('submittedAt', db.DateTime(pytz.utc), nullable=False, default=datetime.utcnow) _shippedAt = db.Column('shippedAt', db.DateTime(pytz.utc)) # Dates are always returned in UTC time and ISO8601 format to make them # as transportable as possible. @hybrid_property def submittedAt(self): return pytz.utc.localize(self._submittedAt).isoformat() @submittedAt.setter def submittedAt(self, submittedAt): self._submittedAt = submittedAt # Dates are always returned in UTC time and ISO8601 format to make them # as transportable as possible. @hybrid_property def shippedAt(self): if self._shippedAt: return pytz.utc.localize(self._shippedAt).isoformat() else: # Not (yet) shipped releaes return None @shippedAt.setter def shippedAt(self, shippedAt): self._shippedAt = shippedAt
class Release(object): """A base class with all of the common columns for any release.""" name = db.Column(db.String(100), primary_key=True) submitter = db.Column(db.String(250), nullable=False) _submittedAt = db.Column('submittedAt', db.DateTime(pytz.utc), nullable=False, default=datetime.utcnow) _shippedAt = db.Column('shippedAt', db.DateTime(pytz.utc)) version = db.Column(db.String(10), nullable=False) buildNumber = db.Column(db.Integer(), nullable=False) branch = db.Column(db.String(50), nullable=False) mozillaRevision = db.Column(db.String(100), nullable=False) l10nChangesets = db.Column(db.Text(), nullable=False) ready = db.Column(db.Boolean(), nullable=False, default=False) complete = db.Column(db.Boolean(), nullable=False, default=False) status = db.Column(db.String(250), default="") mozillaRelbranch = db.Column(db.String(50), default=None, nullable=True) comment = db.Column(db.Text, default=None, nullable=True) description = db.Column(db.Text, default=None, nullable=True) isSecurityDriven = db.Column(db.Boolean(), nullable=False, default=False) starter = db.Column(db.String(250), nullable=True) mh_changeset = db.Column(db.String(100), nullable=True) # Dates are always returned in UTC time and ISO8601 format to make them # as transportable as possible. @hybrid_property def submittedAt(self): return pytz.utc.localize(self._submittedAt).isoformat() @submittedAt.setter def submittedAt(self, submittedAt): self._submittedAt = submittedAt # Dates are always returned in UTC time and ISO8601 format to make them # as transportable as possible. @hybrid_property def shippedAt(self): if self._shippedAt: return pytz.utc.localize(self._shippedAt).isoformat() else: # Not (yet) shipped releaes return None @shippedAt.setter def shippedAt(self, shippedAt): self._shippedAt = shippedAt def __init__(self, submitter, version, buildNumber, branch, mozillaRevision, l10nChangesets, mozillaRelbranch, submittedAt=None, shippedAt=None, comment=None, description=None, isSecurityDriven=False, mh_changeset=None): self.name = getReleaseName(self.product, version, buildNumber) self.submitter = submitter self.version = version.strip() self.buildNumber = buildNumber self.branch = branch.strip() self.mozillaRevision = mozillaRevision.strip() self.l10nChangesets = l10nChangesets self.mozillaRelbranch = mozillaRelbranch if submittedAt: self.submittedAt = submittedAt if shippedAt: self.shippedAt = shippedAt if comment: self.comment = comment if description: self.description = description self.isSecurityDriven = isSecurityDriven self.mh_changeset = mh_changeset @property def isShippedWithL10n(self): return self._shippedAt and self.l10nChangesets != config.LEGACY_KEYWORD def toDict(self): me = {'product': self.product} for c in self.__table__.columns: me[c.name] = getattr(self, c.name) me['submittedAt'] = me['submittedAt'] me['shippedAt'] = me['shippedAt'] return me @classmethod def createFromForm(cls, *args, **kwargs): raise NotImplementedError def updateFromForm(self, form): self.version = form.version.data self.buildNumber = form.buildNumber.data self.branch = form.branch.data self.mozillaRevision = form.mozillaRevision.data self.l10nChangesets = form.l10nChangesets.data self.mozillaRelbranch = form.mozillaRelbranch.data self.name = getReleaseName(self.product, self.version, self.buildNumber) self.comment = form.comment.data self.description = form.description.data self.isSecurityDriven = form.isSecurityDriven.data self.mh_changeset = form.mh_changeset.data @classmethod def getRecent(cls, age=timedelta(weeks=7)): """Returns all releases of 'age' or newer.""" since = datetime.now() - age return cls.query.filter(cls._submittedAt > since).all() @classmethod def getRecentShipped(cls, age=timedelta(weeks=40)): """Returns all shipped releases of 'age' or newer.""" since = datetime.now() - age return [ r for r in cls.query.filter(cls._shippedAt > since).filter( cls._shippedAt != None).order_by(cls._shippedAt).all() ] @classmethod def getMaxBuildNumber(cls, version): """Returns the highest build number known for the version provided.""" return cls.query \ .with_entities(func.max(cls.buildNumber)) \ .filter_by(version=version) \ .one()[0] def __repr__(self): return '<Release %r>' % self.name
class ReleaseEvents(db.Model): """A base class to store release events primarily from buildbot.""" __tablename__ = 'release_events' name = db.Column(db.String(100), nullable=False, primary_key=True) _sent = db.Column('sent', db.DateTime(pytz.utc), nullable=False) event_name = db.Column(db.String(150), nullable=False, primary_key=True) platform = db.Column(db.String(500), nullable=True) results = db.Column(db.Integer(), nullable=False) chunkNum = db.Column(db.Integer(), default=0, nullable=False) chunkTotal = db.Column(db.Integer(), default=0, nullable=False) group = db.Column(db.String(100), default=None, nullable=True) # Dates are always returned in UTC time and ISO8601 format to make them # as transportable as possible. @hybrid_property def sent(self): return pytz.utc.localize(self._sent).isoformat() @sent.setter def sent(self, sent): self._sent = sent def __init__(self, name, sent, event_name, platform, results, chunkNum=0, chunkTotal=0, group=None): self.name = name if sent: self.sent = sent self.event_name = event_name self.platform = platform self.results = results self.chunkNum = chunkNum self.chunkTotal = chunkTotal self.group = group def toDict(self): me = {} for c in self.__table__.columns: me[c.name] = str(getattr(self, c.name)) return me @classmethod def createFromForm(cls, releaseName, form): return cls(releaseName, form.sent.data, form.event_name.data, form.platform.data, form.results.data, form.chunkNum.data, form.chunkTotal.data, form.group.data) def __repr__(self): return '<ReleaseEvents %r>' % self.name @classmethod def getEvents(cls, group=None): filters = {} if group is not None: filters['group'] = group if filters: return cls.query.filter_by(**filters) else: return cls.query.all() @classmethod def getStatus(cls, name): if not cls.query.filter_by(name=name).first(): return None status = { 'tag': cls.tagStatus, 'build': cls.buildStatus, 'repack': cls.repackStatus, 'update': cls.updateStatus, 'releasetest': cls.releasetestStatus, 'readyforrelease': cls.readyForReleaseStatus, 'postrelease': cls.postreleaseStatus } for step in status: status[step] = status[step](name) status['name'] = name return status @classmethod def getCurrentStatus(cls, name): status = cls.getStatus(name) status_order = [ 'tag', 'build', 'repack', 'update', 'releasetest', 'readyforrelease', 'postrelease' ] currentStatus = None if status: for s in reversed(status_order): if status[s]['progress'] != 0: return s return currentStatus @classmethod def tagStatus(cls, name): if cls.query.filter_by(name=name, group='tag').count() > 0: return {'progress': 1.00} return {'progress': 0.00} @classmethod def buildStatus(cls, name): build_events = cls.query.filter_by(name=name, group='build') builds = {'platforms': {}, 'progress': 0.00} for platform in cls.getEnUSPlatforms(name): builds['platforms'][platform] = 0.00 for build in build_events: builds['platforms'][build.platform] = 1.00 builds['progress'] += (1.00 / len(builds['platforms'])) return builds @classmethod def repackStatus(cls, name): repack_events = cls.query.filter_by(name=name, group='repack') repacks = {'platforms': {}, 'progress': 0.00} for platform in cls.getEnUSPlatforms(name): repacks['platforms'][platform] = 0.00 for repack in repack_events: if repacks['platforms'][repack.platform] != 1: if 'complete' not in repack.event_name: repacks['platforms'][repack.platform] += ( 1.00 / repack.chunkTotal) else: repacks['platforms'][repack.platform] = 1.00 repacks['progress'] = (sum(repacks['platforms'].values()) / len(repacks['platforms'])) for platform, progress in repacks['platforms'].items(): repacks['platforms'][platform] = round(progress, 2) return repacks @classmethod def updateStatus(cls, name): if cls.query.filter_by(name=name, group='update').count() > 0: return {'progress': 1.00} return {'progress': 0.00} @classmethod def releasetestStatus(cls, name): if cls.query.filter_by(name=name, group='releasetest').count() > 0: return {'progress': 1.00} return {'progress': 0.00} @classmethod def readyForReleaseStatus(cls, name): update_verify_events = cls.query.filter_by(name=name, group='update_verify') release_events = cls.query.filter_by(name=name, group='release') update_verifys = {} for platform in cls.getEnUSPlatforms(name): update_verifys[platform] = 0.00 for update_verify in update_verify_events: if update_verifys[update_verify.platform] != 1: if 'complete' not in update_verify.event_name: update_verifys[update_verify.platform] += ( 1.00 / update_verify.chunkTotal) else: update_verifys[update_verify.platform] = 1.00 data = {'platforms': update_verifys, 'progress': 0.00} data['progress'] = (sum(data['platforms'].values()) / len(data['platforms'])) for platform, progress in data['platforms'].items(): data['platforms'][platform] = round(progress, 2) if release_events.first(): data['progress'] = 1.00 return data @classmethod def postreleaseStatus(cls, name): if cls.query.filter_by(name=name, group='postrelease').count() > 0: return {'progress': 1.00} return {'progress': 0.00} @classmethod def getEnUSPlatforms(cls, name): releaseTable = getReleaseTable(name.split('-')[0].title()) release = releaseTable.query.filter_by(name=name).first() return json.loads(release.enUSPlatforms)
class Release(object): """A base class with all of the common columns for any release.""" name = db.Column(db.String(100), primary_key=True) submitter = db.Column(db.String(250), nullable=False) _submittedAt = db.Column('submittedAt', db.DateTime(pytz.utc), nullable=False, default=datetime.utcnow) version = db.Column(db.String(10), nullable=False) buildNumber = db.Column(db.Integer(), nullable=False) branch = db.Column(db.String(50), nullable=False) mozillaRevision = db.Column(db.String(100), nullable=False) l10nChangesets = db.Column(db.Text(), nullable=False) dashboardCheck = db.Column(db.Boolean(), nullable=False, default=False) ready = db.Column(db.Boolean(), nullable=False, default=False) complete = db.Column(db.Boolean(), nullable=False, default=False) status = db.Column(db.String(250), default="") mozillaRelbranch = db.Column(db.String(50), default=None, nullable=True) enUSPlatforms = db.Column(db.String(500), default=None, nullable=True) comment = db.Column(db.Text, default=None, nullable=True) starter = db.Column(db.String(250), nullable=True) # Dates are always returned in UTC time and ISO8601 format to make them # as transportable as possible. @hybrid_property def submittedAt(self): return pytz.utc.localize(self._submittedAt).isoformat() @submittedAt.setter def submittedAt(self, submittedAt): self._submittedAt = submittedAt def __init__(self, submitter, version, buildNumber, branch, mozillaRevision, l10nChangesets, dashboardCheck, mozillaRelbranch, enUSPlatforms=None, submittedAt=None, comment=None): self.name = getReleaseName(self.product, version, buildNumber) self.submitter = submitter self.version = version.strip() self.buildNumber = buildNumber self.branch = branch.strip() self.mozillaRevision = mozillaRevision.strip() self.l10nChangesets = l10nChangesets self.dashboardCheck = dashboardCheck self.mozillaRelbranch = mozillaRelbranch self.enUSPlatforms = enUSPlatforms if submittedAt: self.submittedAt = submittedAt if comment: self.comment = comment def toDict(self): me = {'product': self.product} for c in self.__table__.columns: me[c.name] = getattr(self, c.name) me['submittedAt'] = me['submittedAt'] return me @classmethod def createFromForm(cls, form): raise NotImplementedError def updateFromForm(self, form): self.version = form.version.data self.buildNumber = form.buildNumber.data self.branch = form.branch.data self.mozillaRevision = form.mozillaRevision.data self.l10nChangesets = form.l10nChangesets.data self.dashboardCheck = form.dashboardCheck.data self.mozillaRelbranch = form.mozillaRelbranch.data self.name = getReleaseName(self.product, self.version, self.buildNumber) self.comment = form.comment.data @classmethod def getRecent(cls, age=timedelta(weeks=7)): """Returns all releases of 'age' or newer.""" since = datetime.now() - age return cls.query.filter(cls._submittedAt > since).all() @classmethod def getRecentShipped(cls, age=timedelta(weeks=15)): """Returns all shipped releases of 'age' or newer.""" since = datetime.now() - age return [ r for r in cls.query.filter(cls._submittedAt > since).all() if r.status == "postrelease" or ReleaseEvents.getCurrentStatus(r.name) == "postrelease" ] @classmethod def getMaxBuildNumber(cls, version): """Returns the highest build number known for the version provided.""" return cls.query \ .with_entities(func.max(cls.buildNumber)) \ .filter_by(version=version) \ .one()[0] def __repr__(self): return '<Release %r>' % self.name