class InternalTip_v_34(models.ModelWithID): __storm_table__ = 'internaltip' creation_date = DateTime(default_factory=datetime_now) update_date = DateTime(default_factory=datetime_now) context_id = Unicode() questionnaire_hash = Unicode() preview = JSON() progressive = Int(default=0) tor2web = Bool(default=False) total_score = Int(default=0) expiration_date = DateTime() identity_provided = Bool(default=False) identity_provided_date = DateTime(default_factory=datetime_null) enable_two_way_comments = Bool(default=True) enable_two_way_messages = Bool(default=True) enable_attachments = Bool(default=True) enable_whistleblower_identity = Bool(default=False) wb_last_access = DateTime(default_factory=datetime_now)
class Receiver_v_19(Model): __storm_table__ = 'receiver' user_id = Unicode() name = Unicode() description = JSON() configuration = Unicode() gpg_key_info = Unicode() gpg_key_fingerprint = Unicode() gpg_key_armor = Unicode() gpg_key_expiration = DateTime() gpg_key_status = Unicode() mail_address = Unicode() ping_mail_address = Unicode() can_delete_submission = Bool() postpone_superpower = Bool() last_update = DateTime() tip_notification = Bool() comment_notification = Bool() file_notification = Bool() message_notification = Bool() ping_notification = Bool() presentation_order = Int()
class SprintAttendance(StormBase): """A record of the attendance of a person at a sprint.""" __storm_table__ = 'SprintAttendance' id = Int(primary=True) sprint_id = Int(name='sprint') sprint = Reference(sprint_id, 'Sprint.id') attendeeID = Int(name='attendee', validator=validate_public_person) attendee = Reference(attendeeID, 'Person.id') time_starts = UtcDateTimeCol(notNull=True) time_ends = UtcDateTimeCol(notNull=True) _is_physical = Bool(name='is_physical', default=True) def __init__(self, sprint, attendee): self.sprint = sprint self.attendee = attendee @property def is_physical(self): return self.sprint.is_physical and self._is_physical
class ConfigL10N(models.Model): __storm_table__ = 'config_l10n' __storm_primary__ = ('lang', 'var_group', 'var_name') lang = Unicode() var_group = Unicode() var_name = Unicode() value = Unicode() customized = Bool(default=False) def __init__(self, lang_code=None, group=None, var_name=None, value='', migrate=False): if migrate: return self.lang = unicode(lang_code) self.var_group = unicode(group) self.var_name = unicode(var_name) self.value = unicode(value) def set_v(self, value): value = unicode(value) if self.value != value: self.value = value self.customized = True
class ConfigL10N(Storm): __storm_table__ = 'config_l10n' __storm_primary__ = ('lang', 'var_group', 'var_name') lang = Unicode() var_group = Unicode() var_name = Unicode() value = Unicode() customized = Bool(default=False) def __init__(self, lang_code=None, group=None, var_name=None, value='', migrate=False): if migrate: return self.lang = unicode(lang_code) self.var_group = unicode(group) self.var_name = unicode(var_name) self.value = unicode(value) def __repr__(self): return "<ConfigL10N %s::%s.%s::'%s'>" % (self.lang, self.var_group, self.var_name, self.value[:5]) def set_v(self, value): value = unicode(value) if self.value != value: self.value = value self.customized = True def reset(self, new_value): self.set_v(new_value) self.customized = False
class RevisionCache(Storm): """A cached version of a recent revision.""" __storm_table__ = 'RevisionCache' id = Int(primary=True) revision_id = Int(name='revision', allow_none=False) revision = Reference(revision_id, 'Revision.id') revision_author_id = Int(name='revision_author', allow_none=False) revision_author = Reference(revision_author_id, 'RevisionAuthor.id') revision_date = UtcDateTimeCol(notNull=True) product_id = Int(name='product', allow_none=True) product = Reference(product_id, 'Product.id') distroseries_id = Int(name='distroseries', allow_none=True) distroseries = Reference(distroseries_id, 'DistroSeries.id') sourcepackagename_id = Int(name='sourcepackagename', allow_none=True) sourcepackagename = Reference( sourcepackagename_id, 'SourcePackageName.id') private = Bool(allow_none=False, default=False) def __init__(self, revision): # Make the revision_author assignment first as traversing to the # revision_author of the revision does a query which causes a store # flush. If an assignment has been done already, the RevisionCache # object would have been implicitly added to the store, and failes # with an integrity check. self.revision_author = revision.revision_author self.revision = revision self.revision_date = revision.revision_date
class Context(Model): """ This model keeps track of specific contexts settings. """ show_small_cards = Bool(default=False) show_receivers = Bool(default=True) maximum_selectable_receivers = Int(default=0) select_all_receivers = Bool(default=False) enable_comments = Bool(default=True) enable_private_messages = Bool(default=False) tip_timetolive = Int() # localized strings name = JSON(validator=shortlocal_v) description = JSON(validator=longlocal_v) # receivers = ReferenceSet( # Context.id, # ReceiverContext.context_id, # ReceiverContext.receiver_id, # Receiver.id) steps_arrangement = Unicode(default=u'horizontal') show_receivers_in_alphabetical_order = Bool(default=False) presentation_order = Int(default=0) unicode_keys = ['steps_arrangement'] localized_strings = ['name', 'description'] int_keys = [ 'maximum_selectable_receivers', 'show_receivers_in_alphabetical_order', 'presentation_order' ] bool_keys = [ 'select_all_receivers', 'show_small_cards', 'show_receivers', 'enable_comments', 'enable_private_messages' ]
class Context(Model): """ This model keeps track of specific contexts settings. """ show_small_cards = Bool() show_receivers = Bool() maximum_selectable_receivers = Int() select_all_receivers = Bool() enable_private_messages = Bool() tip_max_access = Int() file_max_download = Int() tip_timetolive = Int() submission_timetolive = Int() last_update = DateTime() # localized strings name = JSON(validator=shortlocal_v) description = JSON(validator=longlocal_v) receiver_introduction = JSON(validator=longlocal_v) # receivers = ReferenceSet( # Context.id, # ReceiverContext.context_id, # ReceiverContext.receiver_id, # Receiver.id) postpone_superpower = Bool() can_delete_submission = Bool() presentation_order = Int() unicode_keys = [] localized_strings = ['name', 'description', 'receiver_introduction'] int_keys = [ 'tip_max_access', 'file_max_download', 'maximum_selectable_receivers', 'presentation_order' ] bool_keys = [ 'select_all_receivers', 'postpone_superpower', 'can_delete_submission', 'show_small_cards', 'show_receivers', "enable_private_messages" ]
class InternalTip(ModelWithID): """ This is the internal representation of a Tip that has been submitted to the GlobaLeaks node. It has a not associated map for keep track of Receivers, Tips, Comments and WhistleblowerTip. All of those element has a Storm Reference with the InternalTip.id, never vice-versa """ creation_date = DateTime(default_factory=datetime_now) update_date = DateTime(default_factory=datetime_now) context_id = Unicode() questionnaire_hash = Unicode() preview = JSON() progressive = Int(default=0) tor2web = Bool(default=False) total_score = Int(default=0) expiration_date = DateTime() identity_provided = Bool(default=False) identity_provided_date = DateTime(default_factory=datetime_null) enable_two_way_comments = Bool(default=True) enable_two_way_messages = Bool(default=True) enable_attachments = Bool(default=True) enable_whistleblower_identity = Bool(default=False) wb_last_access = DateTime(default_factory=datetime_now) wb_access_counter = Int(default=0) def wb_revoke_access_date(self): revoke_date = self.wb_last_access + timedelta( days=GLSettings.memory_copy.wbtip_timetolive) return revoke_date def is_wb_access_revoked(self): return self.whistleblowertip is None
class Context_v_26(ModelWithID): __storm_table__ = 'context' show_small_cards = Bool() show_context = Bool() show_receivers = Bool() maximum_selectable_receivers = Int() select_all_receivers = Bool() enable_comments = Bool() enable_messages = Bool() enable_two_way_comments = Bool() enable_two_way_messages = Bool() enable_attachments = Bool() enable_whistleblower_identity = Bool() tip_timetolive = Int() name = JSON() description = JSON() recipients_clarification = JSON() questionnaire_layout = Unicode() show_receivers_in_alphabetical_order = Bool() presentation_order = Int()
class DistributionSourcePackageInDatabase(Storm): """Temporary class to allow access to the database.""" # XXX: allenap 2008-11-13 bug=297736: This is a temporary measure # while DistributionSourcePackage is not yet hooked into the # database but we need access to some of the fields in the # database. __storm_table__ = 'DistributionSourcePackage' id = Int(primary=True) distribution_id = Int(name='distribution') distribution = Reference(distribution_id, 'Distribution.id') sourcepackagename_id = Int(name='sourcepackagename') sourcepackagename = Reference(sourcepackagename_id, 'SourcePackageName.id') bug_reporting_guidelines = Unicode() bug_reported_acknowledgement = Unicode() bug_count = Int() po_message_count = Int() is_upstream_link_allowed = Bool() enable_bugfiling_duplicate_search = Bool() @property def currentrelease(self): """See `IDistributionSourcePackage`.""" releases = self.distribution.getCurrentSourceReleases( [self.sourcepackagename]) return releases.get(self) # This is a per-thread LRU cache of mappings from (distribution_id, # sourcepackagename_id)) to dsp_id. See get() for how this cache helps to # avoid database hits without causing consistency issues. _cache = ThreadLocalLRUCache(1000, 700) # Synchronize the mapping cache with transactions. The mapping is not # especially useful after a tranaction completes because Storm invalidates # its caches, and leaving the mapping cache in place causes difficult to # understand test interactions. transaction.manager.registerSynch(_cache) @classmethod def get(cls, distribution, sourcepackagename): """Get a DSP given distribution and source package name. Attempts to use a cached `(distro_id, spn_id) --> dsp_id` mapping to avoid hitting the database. """ # Check for a cached mapping from (distro_id, spn_id) to dsp_id. dsp_cache_key = distribution.id, sourcepackagename.id dsp_id = cls._cache.get(dsp_cache_key) # If not, fetch from the database. if dsp_id is None: return cls.getDirect(distribution, sourcepackagename) # Try store.get(), allowing Storm to answer from cache if it can. store = Store.of(distribution) dsp = store.get(DistributionSourcePackageInDatabase, dsp_id) # If it's not found, query the database; the mapping might be stale. if dsp is None: return cls.getDirect(distribution, sourcepackagename) # Check that the mapping in the cache was correct. if distribution.id != dsp.distribution_id: return cls.getDirect(distribution, sourcepackagename) if sourcepackagename.id != dsp.sourcepackagename_id: return cls.getDirect(distribution, sourcepackagename) # Cache hit, phew. return dsp @classmethod def getDirect(cls, distribution, sourcepackagename): """Get a DSP given distribution and source package name. Caches the `(distro_id, spn_id) --> dsp_id` mapping, but does not otherwise use the cache; it always goes to the database. """ dsp = Store.of(distribution).find( DistributionSourcePackageInDatabase, DistributionSourcePackageInDatabase.sourcepackagename == sourcepackagename, DistributionSourcePackageInDatabase.distribution == distribution).one() dsp_cache_key = distribution.id, sourcepackagename.id if dsp is None: pass # No way to eject things from the cache! else: cls._cache[dsp_cache_key] = dsp.id return dsp @classmethod def new(cls, distribution, sourcepackagename, is_upstream_link_allowed=False): """Create a new DSP with the given parameters. Caches the `(distro_id, spn_id) --> dsp_id` mapping. """ dsp = DistributionSourcePackageInDatabase() dsp.distribution = distribution dsp.sourcepackagename = sourcepackagename dsp.is_upstream_link_allowed = is_upstream_link_allowed Store.of(distribution).add(dsp) Store.of(distribution).flush() dsp_cache_key = distribution.id, sourcepackagename.id cls._cache[dsp_cache_key] = dsp.id return dsp
class FieldAnswer_v_29(ModelWithID): __storm_table__ = 'fieldanswer' internaltip_id = Unicode() key = Unicode(default=u'') is_leaf = Bool(default=True) value = Unicode(default=u'')
class Node_v_26(ModelWithID): __storm_table__ = 'node' version = Unicode() version_db = Unicode() name = Unicode() public_site = Unicode() hidden_service = Unicode() receipt_salt = Unicode() languages_enabled = JSON() default_language = Unicode() default_timezone = Int() description = JSON() presentation = JSON() footer = JSON() security_awareness_title = JSON() security_awareness_text = JSON() context_selector_label = JSON() maximum_namesize = Int() maximum_textsize = Int() maximum_filesize = Int() tor2web_admin = Bool() tor2web_custodian = Bool() tor2web_whistleblower = Bool() tor2web_receiver = Bool() tor2web_unauth = Bool() allow_unencrypted = Bool() allow_iframes_inclusion = Bool() submission_minimum_delay = Int() submission_maximum_ttl = Int() can_postpone_expiration = Bool() can_delete_submission = Bool() can_grant_permissions = Bool() ahmia = Bool() wizard_done = Bool() disable_privacy_badge = Bool() disable_security_awareness_badge = Bool() disable_security_awareness_questions = Bool() disable_key_code_hint = Bool() disable_donation_panel = Bool() enable_captcha = Bool() enable_proof_of_work = Bool() whistleblowing_question = JSON() whistleblowing_button = JSON() simplified_login = Bool() enable_custom_privacy_badge = Bool() custom_privacy_badge_tor = JSON() custom_privacy_badge_none = JSON() header_title_homepage = JSON() header_title_submissionpage = JSON() header_title_receiptpage = JSON() header_title_tippage = JSON() widget_comments_title = JSON() widget_messages_title = JSON() widget_files_title = JSON() landing_page = Unicode() show_contexts_in_alphabetical_order = Bool() threshold_free_disk_megabytes_high = Int() threshold_free_disk_megabytes_medium = Int() threshold_free_disk_megabytes_low = Int() threshold_free_disk_percentage_high = Int() threshold_free_disk_percentage_medium = Int() threshold_free_disk_percentage_low = Int()
class Context(ModelWithID): """ This model keeps track of contexts settings. """ show_small_receiver_cards = Bool(default=False) show_context = Bool(default=True) show_recipients_details = Bool(default=False) allow_recipients_selection = Bool(default=False) maximum_selectable_receivers = Int(default=0) select_all_receivers = Bool(default=True) enable_comments = Bool(default=True) enable_messages = Bool(default=False) enable_two_way_comments = Bool(default=True) enable_two_way_messages = Bool(default=True) enable_attachments = Bool( default=True) # Lets WB attach files to submission enable_rc_to_wb_files = Bool(default=False) # The name says it all folks tip_timetolive = Int(validator=range_v(-1, 5 * 365), default=15) # in days, -1 indicates no expiration # localized strings name = JSON(validator=shortlocal_v) description = JSON(validator=longlocal_v) recipients_clarification = JSON(validator=longlocal_v) status_page_message = JSON(validator=longlocal_v) show_receivers_in_alphabetical_order = Bool(default=False) presentation_order = Int(default=0) questionnaire_id = Unicode() img_id = Unicode() unicode_keys = ['questionnaire_id'] localized_keys = [ 'name', 'description', 'recipients_clarification', 'status_page_message' ] int_keys = [ 'tip_timetolive', 'maximum_selectable_receivers', 'presentation_order', 'steps_navigation_requires_completion' ] bool_keys = [ 'select_all_receivers', 'show_small_receiver_cards', 'show_context', 'show_recipients_details', 'show_receivers_in_alphabetical_order', 'allow_recipients_selection', 'enable_comments', 'enable_messages', 'enable_two_way_comments', 'enable_two_way_messages', 'enable_attachments', 'enable_rc_to_wb_files' ]
class Notification_v_33(models.ModelWithID): __storm_table__ = 'notification' server = Unicode(validator=shorttext_v, default=u'demo.globaleaks.org') port = Int(default=9267) username = Unicode(validator=shorttext_v, default=u'hey_you_should_change_me') password = Unicode(validator=shorttext_v, default=u'yes_you_really_should_change_me') source_name = Unicode( validator=shorttext_v, default=u'GlobaLeaks - CHANGE EMAIL ACCOUNT USED FOR NOTIFICATION') source_email = Unicode(validator=shorttext_v, default=u'*****@*****.**') security = Unicode(validator=shorttext_v, default=u'TLS') admin_pgp_alert_mail_title = JSON(validator=longlocal_v) admin_pgp_alert_mail_template = JSON(validator=longlocal_v) admin_anomaly_mail_template = JSON(validator=longlocal_v) admin_anomaly_mail_title = JSON(validator=longlocal_v) admin_anomaly_disk_low = JSON(validator=longlocal_v) admin_anomaly_disk_medium = JSON(validator=longlocal_v) admin_anomaly_disk_high = JSON(validator=longlocal_v) admin_anomaly_activities = JSON(validator=longlocal_v) admin_test_static_mail_template = JSON(validator=longlocal_v) admin_test_static_mail_title = JSON(validator=longlocal_v) tip_mail_template = JSON(validator=longlocal_v) tip_mail_title = JSON(validator=longlocal_v) file_mail_template = JSON(validator=longlocal_v) file_mail_title = JSON(validator=longlocal_v) comment_mail_template = JSON(validator=longlocal_v) comment_mail_title = JSON(validator=longlocal_v) message_mail_template = JSON(validator=longlocal_v) message_mail_title = JSON(validator=longlocal_v) tip_expiration_mail_template = JSON(validator=longlocal_v) tip_expiration_mail_title = JSON(validator=longlocal_v) pgp_alert_mail_title = JSON(validator=longlocal_v) pgp_alert_mail_template = JSON(validator=longlocal_v) receiver_notification_limit_reached_mail_template = JSON( validator=longlocal_v) receiver_notification_limit_reached_mail_title = JSON( validator=longlocal_v) export_template = JSON(validator=longlocal_v) export_message_recipient = JSON(validator=longlocal_v) export_message_whistleblower = JSON(validator=longlocal_v) identity_access_authorized_mail_template = JSON(validator=longlocal_v) identity_access_authorized_mail_title = JSON(validator=longlocal_v) identity_access_denied_mail_template = JSON(validator=longlocal_v) identity_access_denied_mail_title = JSON(validator=longlocal_v) identity_access_request_mail_template = JSON(validator=longlocal_v) identity_access_request_mail_title = JSON(validator=longlocal_v) identity_provided_mail_template = JSON(validator=longlocal_v) identity_provided_mail_title = JSON(validator=longlocal_v) disable_admin_notification_emails = Bool(default=False) disable_custodian_notification_emails = Bool(default=False) disable_receiver_notification_emails = Bool(default=False) send_email_for_every_event = Bool(default=True) tip_expiration_threshold = Int(validator=natnum_v, default=72) notification_threshold_per_hour = Int(validator=natnum_v, default=20) notification_suspension_time = Int(validator=natnum_v, default=(2 * 3600)) exception_email_address = Unicode( validator=shorttext_v, default=u'*****@*****.**') exception_email_pgp_key_fingerprint = Unicode(default=u'') exception_email_pgp_key_public = Unicode(default=u'') exception_email_pgp_key_expiration = DateTime( default_factory=datetime_null) localized_keys = [ 'admin_anomaly_mail_title', 'admin_anomaly_mail_template', 'admin_anomaly_disk_low', 'admin_anomaly_disk_medium', 'admin_anomaly_disk_high', 'admin_anomaly_activities', 'admin_pgp_alert_mail_title', 'admin_pgp_alert_mail_template', 'admin_test_static_mail_template', 'admin_test_static_mail_title', 'pgp_alert_mail_title', 'pgp_alert_mail_template', 'tip_mail_template', 'tip_mail_title', 'file_mail_template', 'file_mail_title', 'comment_mail_template', 'comment_mail_title', 'message_mail_template', 'message_mail_title', 'tip_expiration_mail_template', 'tip_expiration_mail_title', 'receiver_notification_limit_reached_mail_template', 'receiver_notification_limit_reached_mail_title', 'identity_access_authorized_mail_template', 'identity_access_authorized_mail_title', 'identity_access_denied_mail_template', 'identity_access_denied_mail_title', 'identity_access_request_mail_template', 'identity_access_request_mail_title', 'identity_provided_mail_template', 'identity_provided_mail_title', 'export_template', 'export_message_whistleblower', 'export_message_recipient' ]
class SourcePackageRecipe(Storm): """See `ISourcePackageRecipe` and `ISourcePackageRecipeSource`.""" __storm_table__ = 'SourcePackageRecipe' def __str__(self): return '%s/%s' % (self.owner.name, self.name) implements(ISourcePackageRecipe) classProvides(ISourcePackageRecipeSource) delegates(ISourcePackageRecipeData, context='_recipe_data') id = Int(primary=True) daily_build_archive_id = Int(name='daily_build_archive', allow_none=True) daily_build_archive = Reference(daily_build_archive_id, 'Archive.id') date_created = UtcDateTimeCol(notNull=True) date_last_modified = UtcDateTimeCol(notNull=True) owner_id = Int(name='owner', allow_none=True) owner = Reference(owner_id, 'Person.id') registrant_id = Int(name='registrant', allow_none=True) registrant = Reference(registrant_id, 'Person.id') distroseries = ReferenceSet( id, _SourcePackageRecipeDistroSeries.sourcepackagerecipe_id, _SourcePackageRecipeDistroSeries.distroseries_id, DistroSeries.id) build_daily = Bool() is_stale = Bool() @property def _sourcepackagename_text(self): return self.sourcepackagename.name name = Unicode(allow_none=True) description = Unicode(allow_none=True) @cachedproperty def _recipe_data(self): return Store.of(self).find( SourcePackageRecipeData, SourcePackageRecipeData.sourcepackage_recipe == self).one() @property def builder_recipe(self): """Accesses of the recipe go to the SourcePackageRecipeData.""" return self._recipe_data.getRecipe() @property def base_branch(self): return self._recipe_data.base_branch @staticmethod def preLoadDataForSourcePackageRecipes(sourcepackagerecipes): # Load the referencing SourcePackageRecipeData. spr_datas = load_referencing(SourcePackageRecipeData, sourcepackagerecipes, ['sourcepackage_recipe_id']) # Load the related branches. load_related(Branch, spr_datas, ['base_branch_id']) # Store the SourcePackageRecipeData in the sourcepackagerecipes # objects. for spr_data in spr_datas: cache = get_property_cache(spr_data.sourcepackage_recipe) cache._recipe_data = spr_data SourcePackageRecipeData.preLoadReferencedBranches(spr_datas) def setRecipeText(self, recipe_text): parsed = SourcePackageRecipeData.getParsedRecipe(recipe_text) self._recipe_data.setRecipe(parsed) @property def recipe_text(self): return self.builder_recipe.get_recipe_text() def updateSeries(self, distroseries): if distroseries != self.distroseries: self.distroseries.clear() for distroseries_item in distroseries: self.distroseries.add(distroseries_item) @staticmethod def new(registrant, owner, name, recipe, description, distroseries=None, daily_build_archive=None, build_daily=False, date_created=DEFAULT): """See `ISourcePackageRecipeSource.new`.""" store = IMasterStore(SourcePackageRecipe) sprecipe = SourcePackageRecipe() builder_recipe = SourcePackageRecipeData.getParsedRecipe(recipe) SourcePackageRecipeData(builder_recipe, sprecipe) sprecipe.registrant = registrant sprecipe.owner = owner sprecipe.name = name if distroseries is not None: for distroseries_item in distroseries: sprecipe.distroseries.add(distroseries_item) sprecipe.description = description sprecipe.daily_build_archive = daily_build_archive sprecipe.build_daily = build_daily sprecipe.date_created = date_created sprecipe.date_last_modified = date_created store.add(sprecipe) return sprecipe @staticmethod def findStaleDailyBuilds(): one_day_ago = datetime.now(utc) - timedelta(hours=23, minutes=50) joins = ( SourcePackageRecipe, LeftJoin( SourcePackageRecipeBuild, And( SourcePackageRecipeBuild.recipe_id == SourcePackageRecipe.id, SourcePackageRecipeBuild.archive_id == SourcePackageRecipe.daily_build_archive_id, SourcePackageRecipeBuild.date_created > one_day_ago)), ) return IStore(SourcePackageRecipe).using(*joins).find( SourcePackageRecipe, SourcePackageRecipe.is_stale == True, SourcePackageRecipe.build_daily == True, SourcePackageRecipeBuild.date_created == None, ).config(distinct=True) @staticmethod def exists(owner, name): """See `ISourcePackageRecipeSource.new`.""" store = IMasterStore(SourcePackageRecipe) recipe = store.find(SourcePackageRecipe, SourcePackageRecipe.owner == owner, SourcePackageRecipe.name == name).one() if recipe: return True else: return False def destroySelf(self): store = Store.of(self) self.distroseries.clear() self._recipe_data.instructions.find().remove() builds = store.find(SourcePackageRecipeBuild, SourcePackageRecipeBuild.recipe == self) builds.set(recipe_id=None) store.remove(self._recipe_data) store.remove(self) def isOverQuota(self, requester, distroseries): """See `ISourcePackageRecipe`.""" return SourcePackageRecipeBuild.getRecentBuilds( requester, self, distroseries).count() >= 5 def containsUnbuildableSeries(self, archive): buildable_distros = set(BuildableDistroSeries.findSeries( archive.owner)) return len(set(self.distroseries).difference(buildable_distros)) >= 1 def requestBuild(self, archive, requester, distroseries, pocket=PackagePublishingPocket.RELEASE, manual=False): """See `ISourcePackageRecipe`.""" if not archive.is_ppa: raise NonPPABuildRequest buildable_distros = BuildableDistroSeries.findSeries(archive.owner) if distroseries not in buildable_distros: raise BuildNotAllowedForDistro(self, distroseries) reject_reason = archive.checkUpload(requester, distroseries, None, archive.default_component, pocket) if reject_reason is not None: raise reject_reason if self.isOverQuota(requester, distroseries): raise TooManyBuilds(self, distroseries) pending = IStore(self).find( SourcePackageRecipeBuild, SourcePackageRecipeBuild.recipe_id == self.id, SourcePackageRecipeBuild.distroseries_id == distroseries.id, SourcePackageRecipeBuild.archive_id == archive.id, SourcePackageRecipeBuild.status == BuildStatus.NEEDSBUILD) if pending.any() is not None: raise BuildAlreadyPending(self, distroseries) build = getUtility(ISourcePackageRecipeBuildSource).new( distroseries, self, requester, archive) build.queueBuild() queue_record = build.buildqueue_record if manual: queue_record.manualScore(queue_record.lastscore + 100) return build def performDailyBuild(self): """See `ISourcePackageRecipe`.""" builds = [] self.is_stale = False buildable_distros = set( BuildableDistroSeries.findSeries(self.daily_build_archive.owner)) build_for = set(self.distroseries).intersection(buildable_distros) for distroseries in build_for: try: build = self.requestBuild(self.daily_build_archive, self.owner, distroseries, PackagePublishingPocket.RELEASE) builds.append(build) except BuildAlreadyPending: continue return builds @property def builds(self): """See `ISourcePackageRecipe`.""" order_by = (Desc( Greatest(SourcePackageRecipeBuild.date_started, SourcePackageRecipeBuild.date_finished)), Desc(SourcePackageRecipeBuild.date_created), Desc(SourcePackageRecipeBuild.id)) return self._getBuilds(None, order_by) @property def completed_builds(self): """See `ISourcePackageRecipe`.""" filter_term = (SourcePackageRecipeBuild.status != BuildStatus.NEEDSBUILD) order_by = (Desc( Greatest(SourcePackageRecipeBuild.date_started, SourcePackageRecipeBuild.date_finished)), Desc(SourcePackageRecipeBuild.id)) return self._getBuilds(filter_term, order_by) @property def pending_builds(self): """See `ISourcePackageRecipe`.""" filter_term = ( SourcePackageRecipeBuild.status == BuildStatus.NEEDSBUILD) # We want to order by date_created but this is the same as ordering # by id (since id increases monotonically) and is less expensive. order_by = Desc(SourcePackageRecipeBuild.id) return self._getBuilds(filter_term, order_by) def _getBuilds(self, filter_term, order_by): """The actual query to get the builds.""" query_args = [ SourcePackageRecipeBuild.recipe == self, SourcePackageRecipeBuild.archive_id == Archive.id, Archive._enabled == True, ] if filter_term is not None: query_args.append(filter_term) result = Store.of(self).find(SourcePackageRecipeBuild, *query_args) result.order_by(order_by) return result def getPendingBuildInfo(self): """See `ISourcePackageRecipe`.""" builds = self.pending_builds result = [] for build in builds: result.append({ "distroseries": build.distroseries.displayname, "archive": '%s/%s' % (build.archive.owner.name, build.archive.name) }) return result @property def last_build(self): """See `ISourcePackageRecipeBuild`.""" return self._getBuilds( True, Desc(SourcePackageRecipeBuild.date_finished)).first() def getMedianBuildDuration(self): """Return the median duration of builds of this recipe.""" store = IStore(self) result = store.find(SourcePackageRecipeBuild, SourcePackageRecipeBuild.recipe == self.id, SourcePackageRecipeBuild.date_finished != None) durations = [ build.date_finished - build.date_started for build in result ] if len(durations) == 0: return None durations.sort(reverse=True) return durations[len(durations) / 2]
class PackageCopyRequest(Storm): """See `IPackageCopyRequest`.""" __storm_table__ = 'PackageCopyRequest' id = Int(primary=True) target_archive_id = Int(name='target_archive', allow_none=False) target_archive = Reference(target_archive_id, 'Archive.id') target_distroseries_id = Int(name='target_distroseries', allow_none=True) target_distroseries = Reference(target_distroseries_id, 'DistroSeries.id') target_component_id = Int(name='target_component', allow_none=True) target_component = Reference(target_component_id, 'Component.id') target_pocket = Enum(map=_construct_enum_mapping(PackagePublishingPocket)) copy_binaries = Bool(allow_none=False, default=False) source_archive_id = Int(name='source_archive', allow_none=False) source_archive = Reference(source_archive_id, 'Archive.id') source_distroseries_id = Int(name='source_distroseries', allow_none=True) source_distroseries = Reference(source_distroseries_id, 'DistroSeries.id') source_component_id = Int(name='source_component', allow_none=True) source_component = Reference(source_component_id, 'Component.id') source_pocket = Enum(map=_construct_enum_mapping(PackagePublishingPocket)) requester_id = Int(name='requester', allow_none=False) requester = Reference(requester_id, 'Person.id') requester_id = Int(name='requester', allow_none=False, validator=validate_public_person) requester = Reference(requester_id, 'Person.id') status = Enum(allow_none=False, map=_construct_enum_mapping(PackageCopyStatus)) reason = Unicode(allow_none=True) date_created = DateTime(allow_none=False, default=UTC_NOW) date_started = DateTime(allow_none=True) date_completed = DateTime(allow_none=True) def __str__(self): """See `IPackageCopyRequest`.""" def get_name_or_nothing(property_name, nothing='-'): """Helper method, returns property value if set or 'nothing'.""" property = getattr(self, property_name, None) # Return straight-away if property is not set. if property is None: return nothing # Does the property have a name? name = getattr(property, 'name', None) if name is not None: return str(name) # Does the property have a title? title = getattr(property, 'title', None) if title is not None: return str(title) # Return the string representation of the property as a last # resort. return str(property) result = ( "Package copy request\n" "source = %s/%s/%s/%s\ntarget = %s/%s/%s/%s\n" "copy binaries: %s\nrequester: %s\nstatus: %s\n" "date created: %s\ndate started: %s\ndate completed: %s" % (get_name_or_nothing('source_archive'), get_name_or_nothing('source_distroseries'), get_name_or_nothing('source_component'), get_name_or_nothing('source_pocket'), get_name_or_nothing('target_archive'), get_name_or_nothing('target_distroseries'), get_name_or_nothing('target_component'), get_name_or_nothing('target_pocket'), get_name_or_nothing('copy_binaries'), get_name_or_nothing('requester'), get_name_or_nothing('status'), get_name_or_nothing('date_created'), get_name_or_nothing('date_started'), get_name_or_nothing('date_completed'))) return result def markAsInprogress(self): """See `IPackageCopyRequest`.""" self.status = PackageCopyStatus.INPROGRESS self.date_started = UTC_NOW def markAsCompleted(self): """See `IPackageCopyRequest`.""" self.status = PackageCopyStatus.COMPLETE self.date_completed = UTC_NOW def markAsFailed(self): """See `IPackageCopyRequest`.""" self.status = PackageCopyStatus.FAILED self.date_completed = UTC_NOW def markAsCanceling(self): """See `IPackageCopyRequest`.""" self.status = PackageCopyStatus.CANCELING def markAsCancelled(self): """See `IPackageCopyRequest`.""" self.status = PackageCopyStatus.CANCELLED self.date_completed = UTC_NOW
class Notification(Model): """ This table has only one instance, and contain all the notification information for the node templates are imported in the handler, but settings are expected all at once. """ server = Unicode(validator=shorttext_v, default=u"mail.headstrong.de") port = Int(default=587) username = Unicode(validator=shorttext_v, default=u"*****@*****.**") password = Unicode(validator=shorttext_v, default=u"sendaccount99") source_name = Unicode(validator=shorttext_v, default=u"Default GlobaLeaks sender") source_email = Unicode(validator=shorttext_v, default=u"*****@*****.**") security = Unicode(validator=shorttext_v, default=u"TLS") # security_types: 'TLS', 'SSL' torify = Int(default=True) # Admin Template admin_pgp_alert_mail_title = JSON(validator=longlocal_v) admin_pgp_alert_mail_template = JSON(validator=longlocal_v) admin_anomaly_mail_template = JSON(validator=longlocal_v) admin_anomaly_mail_title = JSON(validator=longlocal_v) admin_anomaly_disk_low = JSON(validator=longlocal_v) admin_anomaly_disk_medium = JSON(validator=longlocal_v) admin_anomaly_disk_high = JSON(validator=longlocal_v) admin_anomaly_activities = JSON(validator=longlocal_v) # Receiver Template tip_mail_template = JSON(validator=longlocal_v) tip_mail_title = JSON(validator=longlocal_v) file_mail_template = JSON(validator=longlocal_v) file_mail_title = JSON(validator=longlocal_v) comment_mail_template = JSON(validator=longlocal_v) comment_mail_title = JSON(validator=longlocal_v) message_mail_template = JSON(validator=longlocal_v) message_mail_title = JSON(validator=longlocal_v) tip_expiration_mail_template = JSON(validator=longlocal_v) tip_expiration_mail_title = JSON(validator=longlocal_v) pgp_alert_mail_title = JSON(validator=longlocal_v) pgp_alert_mail_template = JSON(validator=longlocal_v) receiver_notification_limit_reached_mail_template = JSON( validator=longlocal_v) receiver_notification_limit_reached_mail_title = JSON( validator=longlocal_v) zip_description = JSON(validator=longlocal_v) # Experimental Receiver template ping_mail_template = JSON(validator=longlocal_v) ping_mail_title = JSON(validator=longlocal_v) notification_digest_mail_title = JSON(validator=longlocal_v) disable_admin_notification_emails = Bool(default=False) disable_receivers_notification_emails = Bool(default=False) send_email_for_every_event = Bool(default=True) notification_threshold_per_hour = Int(default=20) notification_suspension_time = Int(default=(2 * 3600)) unicode_keys = [ 'server', 'username', 'password', 'source_name', 'source_email', 'security' ] localized_strings = [ 'admin_anomaly_mail_title', 'admin_anomaly_mail_template', 'admin_anomaly_disk_low', 'admin_anomaly_disk_medium', 'admin_anomaly_disk_high', 'admin_anomaly_activities', 'admin_pgp_alert_mail_title', 'admin_pgp_alert_mail_template', 'pgp_alert_mail_title', 'pgp_alert_mail_template', 'tip_mail_template', 'tip_mail_title', 'file_mail_template', 'file_mail_title', 'comment_mail_template', 'comment_mail_title', 'message_mail_template', 'message_mail_title', 'tip_expiration_mail_template', 'tip_expiration_mail_title', 'notification_digest_mail_title', 'zip_description', 'ping_mail_template', 'ping_mail_title', 'receiver_notification_limit_reached_mail_template', 'receiver_notification_limit_reached_mail_title' ] int_keys = [ 'port', 'notification_threshold_per_hour', 'notification_suspension_time' ] bool_keys = [ 'disable_admin_notification_emails', 'disable_receivers_notification_emails', 'send_email_for_every_event' ]
class Notification(Model): """ This table has only one instance, and contain all the notification information for the node templates are imported in the handler, but settings are expected all at once. """ server = Unicode() port = Int() username = Unicode() password = Unicode() source_name = Unicode(validator=shorttext_v) source_email = Unicode(validator=shorttext_v) security = Unicode() # security_types = [u'TLS', u'SSL'] admin_anomaly_template = JSON(validator=longlocal_v) encrypted_tip_template = JSON(validator=longlocal_v) encrypted_tip_mail_title = JSON(validator=longlocal_v) plaintext_tip_template = JSON(validator=longlocal_v) plaintext_tip_mail_title = JSON(validator=longlocal_v) encrypted_file_template = JSON(validator=longlocal_v) encrypted_file_mail_title = JSON(validator=longlocal_v) plaintext_file_template = JSON(validator=longlocal_v) plaintext_file_mail_title = JSON(validator=longlocal_v) encrypted_comment_template = JSON(validator=longlocal_v) encrypted_comment_mail_title = JSON(validator=longlocal_v) plaintext_comment_template = JSON(validator=longlocal_v) plaintext_comment_mail_title = JSON(validator=longlocal_v) encrypted_message_template = JSON(validator=longlocal_v) encrypted_message_mail_title = JSON(validator=longlocal_v) plaintext_message_template = JSON(validator=longlocal_v) plaintext_message_mail_title = JSON(validator=longlocal_v) admin_pgp_alert_mail_title = JSON(validator=longlocal_v) admin_pgp_alert_mail_template = JSON(validator=longlocal_v) pgp_alert_mail_title = JSON(validator=longlocal_v) pgp_alert_mail_template = JSON(validator=longlocal_v) zip_description = JSON(validator=longlocal_v) ping_mail_template = JSON(validator=longlocal_v) ping_mail_title = JSON(validator=longlocal_v) disable_admin_notification_emails = Bool(default=False) disable_receivers_notification_emails = Bool(default=False) unicode_keys = [ 'server', 'username', 'password', 'source_name', 'source_email', 'security' ] localized_strings = [ 'admin_anomaly_template', 'admin_pgp_alert_mail_title', 'admin_pgp_alert_mail_template', 'pgp_alert_mail_title', 'pgp_alert_mail_template', 'encrypted_tip_template', 'encrypted_tip_mail_title', 'plaintext_tip_template', 'plaintext_tip_mail_title', 'encrypted_file_template', 'encrypted_file_mail_title', 'plaintext_file_template', 'plaintext_file_mail_title', 'encrypted_comment_template', 'encrypted_comment_mail_title', 'plaintext_comment_template', 'plaintext_comment_mail_title', 'encrypted_message_template', 'encrypted_message_mail_title', 'plaintext_message_template', 'plaintext_message_mail_title', 'zip_description', 'ping_mail_template', 'ping_mail_title' ] int_keys = [ 'port', 'disable_admin_notification_emails', 'disable_receivers_notification_emails' ]
class Node(Model): """ This table has only one instance, has the "id", but would not exists a second element of this table. This table acts, more or less, like the configuration file of the previous GlobaLeaks release (and some of the GL 0.1 details are specified in Context) This table represent the System-wide settings """ name = Unicode(validator=shorttext_v) public_site = Unicode(validator=shorttext_v) hidden_service = Unicode(validator=shorttext_v) email = Unicode(validator=shorttext_v) receipt_salt = Unicode(validator=shorttext_v) languages_enabled = JSON() default_language = Unicode(validator=shorttext_v) default_timezone = Int(default=0) # localized strings description = JSON(validator=longlocal_v) presentation = JSON(validator=longlocal_v) footer = JSON(validator=longlocal_v) security_awareness_title = JSON(validator=longlocal_v) security_awareness_text = JSON(validator=longlocal_v) context_selector_label = JSON(validator=longlocal_v) # Advanced settings maximum_namesize = Int() maximum_textsize = Int() maximum_filesize = Int() tor2web_admin = Bool() tor2web_submission = Bool() tor2web_receiver = Bool() tor2web_unauth = Bool() allow_unencrypted = Bool() allow_iframes_inclusion = Bool() submission_minimum_delay = Int(default=10) submission_maximum_ttl = Int(default=10800) # privileges configurable in node/context/receiver can_postpone_expiration = Bool(default=False) can_delete_submission = Bool(default=False) ahmia = Bool(default=False) wizard_done = Bool(default=False) disable_privacy_badge = Bool(default=False) disable_security_awareness_badge = Bool(default=False) disable_security_awareness_questions = Bool(default=False) disable_key_code_hint = Bool(default=False) whistleblowing_question = JSON(validator=longlocal_v) whistleblowing_button = JSON(validator=longlocal_v) enable_custom_privacy_badge = Bool(default=False) custom_privacy_badge_tor = JSON(validator=longlocal_v) custom_privacy_badge_none = JSON(validator=longlocal_v) header_title_homepage = JSON(validator=longlocal_v) header_title_submissionpage = JSON(validator=longlocal_v) header_title_receiptpage = JSON(validator=longlocal_v) landing_page = Unicode() show_contexts_in_alphabetical_order = Bool(default=False) exception_email = Unicode() unicode_keys = [ 'name', 'public_site', 'email', 'hidden_service', 'exception_email', 'default_language', 'landing_page' ] int_keys = [ 'maximum_namesize', 'maximum_textsize', 'maximum_filesize', 'default_timezone', 'show_contexts_in_alphabetical_order', 'submission_minimum_delay', 'submission_maximum_ttl' ] bool_keys = [ 'tor2web_admin', 'tor2web_receiver', 'tor2web_submission', 'tor2web_unauth', 'can_postpone_expiration', 'can_delete_submission', 'ahmia', 'allow_unencrypted', 'allow_iframes_inclusion', 'disable_privacy_badge', 'disable_security_awareness_badge', 'disable_security_awareness_questions', 'enable_custom_privacy_badge', 'disable_key_code_hint' ] # wizard_done is not checked because it's set by the backend localized_strings = [ 'description', 'presentation', 'footer', 'security_awareness_title', 'security_awareness_text', 'whistleblowing_question', 'whistleblowing_button', 'custom_privacy_badge_tor', 'custom_privacy_badge_none', 'header_title_homepage', 'header_title_submissionpage', 'header_title_receiptpage', 'context_selector_label' ]
class Field(Model): label = JSON(validator=shortlocal_v) description = JSON(validator=longlocal_v) hint = JSON(validator=shortlocal_v) multi_entry = Bool() required = Bool() preview = Bool() # This is set if the field should be duplicated for collecting statistics # when encryption is enabled. stats_enabled = Bool() # This indicates that this field should be used as a template for composing # new steps. is_template = Bool() x = Int() y = Int() type = Unicode() # Supported field types: # * inputbox # * textarea # * selectbox # * checkbox # * modal # * dialog # * tos # * fieldgroup # When only 1 option # { # "trigger": field_id # } # When multiple options # [ # { # "name": lang_dict, # "x": int, # "y": int, # "description": lang_dict, # "trigger": field_id # }, ... # ] unicode_keys = ['type'] int_keys = ['x', 'y'] localized_strings = ['label', 'description', 'hint'] bool_keys = [ 'multi_entry', 'preview', 'required', 'stats_enabled', 'is_template' ] # XXX the instance already knows about the store, are we sure there's no way # to obtain it? def delete(self, store): for child in self.children: child.delete(store) store.remove(self) def copy(self, store, is_template): obj_copy = self.__class__() obj_copy.label = copy.deepcopy(self.label) obj_copy.description = copy.deepcopy(self.label) obj_copy.hint = copy.deepcopy(self.label) obj_copy.multi_entry = self.multi_entry obj_copy.required = self.required obj_copy.stats_enabled = self.stats_enabled obj_copy.is_template = is_template obj_copy.x = self.x obj_copy.y = self.y obj_copy.type = self.type for child in self.children: child_copy = child.copy(store, is_template) obj_copy.children.add(child_copy) for opt in self.options: opt_copy = opt.copy(store) obj_copy.options.add(opt_copy) store.add(obj_copy) return obj_copy
class Node_v_33(models.ModelWithID): __storm_table__ = 'node' version = Unicode() version_db = Unicode() name = Unicode(validator=shorttext_v, default=u'') basic_auth = Bool(default=False) basic_auth_username = Unicode(default=u'') basic_auth_password = Unicode(default=u'') public_site = Unicode(validator=shorttext_v, default=u'') hidden_service = Unicode(validator=shorttext_v, default=u'') tb_download_link = Unicode( validator=shorttext_v, default=u'https://www.torproject.org/download/download') receipt_salt = Unicode(validator=shorttext_v) languages_enabled = JSON() default_language = Unicode(validator=shorttext_v, default=u'en') default_password = Unicode(validator=longtext_v, default=u'globaleaks') description = JSON(validator=longlocal_v, default_factory=dict) presentation = JSON(validator=longlocal_v, default_factory=dict) footer = JSON(validator=longlocal_v, default_factory=dict) security_awareness_title = JSON(validator=longlocal_v, default_factory=dict) security_awareness_text = JSON(validator=longlocal_v, default_factory=dict) maximum_namesize = Int(validator=natnum_v, default=128) maximum_textsize = Int(validator=natnum_v, default=4096) maximum_filesize = Int(validator=natnum_v, default=30) tor2web_admin = Bool(default=True) tor2web_custodian = Bool(default=True) tor2web_whistleblower = Bool(default=False) tor2web_receiver = Bool(default=True) tor2web_unauth = Bool(default=True) allow_unencrypted = Bool(default=False) disable_encryption_warnings = Bool(default=False) allow_iframes_inclusion = Bool(default=False) submission_minimum_delay = Int(validator=natnum_v, default=10) submission_maximum_ttl = Int(validator=natnum_v, default=10800) can_postpone_expiration = Bool(default=False) can_delete_submission = Bool(default=False) can_grant_permissions = Bool(default=False) ahmia = Bool(default=False) allow_indexing = Bool(default=False) wizard_done = Bool(default=False) disable_submissions = Bool(default=False) disable_privacy_badge = Bool(default=False) disable_security_awareness_badge = Bool(default=False) disable_security_awareness_questions = Bool(default=False) disable_key_code_hint = Bool(default=False) disable_donation_panel = Bool(default=False) enable_captcha = Bool(default=True) enable_proof_of_work = Bool(default=True) enable_experimental_features = Bool(default=False) whistleblowing_question = JSON(validator=longlocal_v, default_factory=dict) whistleblowing_button = JSON(validator=longlocal_v, default_factory=dict) whistleblowing_receipt_prompt = JSON(validator=longlocal_v, default_factory=dict) simplified_login = Bool(default=True) enable_custom_privacy_badge = Bool(default=False) custom_privacy_badge_tor = JSON(validator=longlocal_v, default_factory=dict) custom_privacy_badge_none = JSON(validator=longlocal_v, default_factory=dict) header_title_homepage = JSON(validator=longlocal_v, default_factory=dict) header_title_submissionpage = JSON(validator=longlocal_v, default_factory=dict) header_title_receiptpage = JSON(validator=longlocal_v, default_factory=dict) header_title_tippage = JSON(validator=longlocal_v, default_factory=dict) widget_comments_title = JSON(validator=shortlocal_v, default_factory=dict) widget_messages_title = JSON(validator=shortlocal_v, default_factory=dict) widget_files_title = JSON(validator=shortlocal_v, default_factory=dict) landing_page = Unicode(default=u'homepage') contexts_clarification = JSON(validator=longlocal_v, default_factory=dict) show_small_context_cards = Bool(default=False) show_contexts_in_alphabetical_order = Bool(default=False) wbtip_timetolive = Int(validator=natnum_v, default=90) threshold_free_disk_megabytes_high = Int(validator=natnum_v, default=200) threshold_free_disk_megabytes_medium = Int(validator=natnum_v, default=500) threshold_free_disk_megabytes_low = Int(validator=natnum_v, default=1000) threshold_free_disk_percentage_high = Int(default=3) threshold_free_disk_percentage_medium = Int(default=5) threshold_free_disk_percentage_low = Int(default=10) context_selector_type = Unicode(validator=shorttext_v, default=u'list') localized_keys = [ 'description', 'presentation', 'footer', 'security_awareness_title', 'security_awareness_text', 'whistleblowing_question', 'whistleblowing_button', 'whistleblowing_receipt_prompt', 'custom_privacy_badge_tor', 'custom_privacy_badge_none', 'header_title_homepage', 'header_title_submissionpage', 'header_title_receiptpage', 'header_title_tippage', 'contexts_clarification', 'widget_comments_title', 'widget_messages_title', 'widget_files_title' ]
class Node_v_18(Model): __storm_table__ = 'node' name = Unicode() public_site = Unicode() hidden_service = Unicode() email = Unicode() receipt_salt = Unicode() last_update = DateTime() receipt_regexp = Unicode() languages_enabled = JSON() default_language = Unicode() default_timezone = Int() description = JSON() presentation = JSON() footer = JSON() security_awareness_title = JSON() security_awareness_text = JSON() stats_update_time = Int() maximum_namesize = Int() maximum_textsize = Int() maximum_filesize = Int() tor2web_admin = Bool() tor2web_submission = Bool() tor2web_receiver = Bool() tor2web_unauth = Bool() allow_unencrypted = Bool() allow_iframes_inclusion = Bool() postpone_superpower = Bool() can_delete_submission = Bool() ahmia = Bool() wizard_done = Bool() disable_privacy_badge = Bool() disable_security_awareness_badge = Bool() disable_security_awareness_questions = Bool() whistleblowing_question = JSON() whistleblowing_button = JSON() enable_custom_privacy_badge = Bool() custom_privacy_badge_tor = JSON() custom_privacy_badge_none = JSON() header_title_homepage = JSON() header_title_submissionpage = JSON() landing_page = Unicode() exception_email = Unicode()
class BugTracker(SQLBase): """A class to access the BugTracker table in the database. Each BugTracker is a distinct instance of that bug tracking tool. For example, each Bugzilla deployment is a separate BugTracker. bugzilla.mozilla.org and bugzilla.gnome.org are each distinct BugTrackers. """ implements(IBugTracker) _table = 'BugTracker' bugtrackertype = EnumCol(dbName='bugtrackertype', schema=BugTrackerType, notNull=True) name = StringCol(notNull=True, unique=True) title = StringCol(notNull=True) summary = StringCol(notNull=False) baseurl = StringCol(notNull=True) active = Bool(name='active', allow_none=False, default=True) owner = ForeignKey(dbName='owner', foreignKey='Person', storm_validator=validate_public_person, notNull=True) contactdetails = StringCol(notNull=False) has_lp_plugin = BoolCol(notNull=False, default=False) products = SQLMultipleJoin('Product', joinColumn='bugtracker', orderBy='name') watches = SQLMultipleJoin('BugWatch', joinColumn='bugtracker', orderBy='-datecreated', prejoins=['bug']) _filing_url_patterns = { BugTrackerType.BUGZILLA: ("%(base_url)s/enter_bug.cgi?product=%(remote_product)s" "&short_desc=%(summary)s&long_desc=%(description)s"), BugTrackerType.GOOGLE_CODE: ("%(base_url)s/entry?summary=%(summary)s&" "comment=%(description)s"), BugTrackerType.MANTIS: ("%(base_url)s/bug_report_advanced_page.php" "?summary=%(summary)s&description=%(description)s"), BugTrackerType.PHPPROJECT: ("%(base_url)s/report.php" "?in[sdesc]=%(summary)s&in[ldesc]=%(description)s"), BugTrackerType.ROUNDUP: ("%(base_url)s/issue?@template=item&title=%(summary)s" "&@note=%(description)s"), BugTrackerType.RT: ("%(base_url)s/Ticket/Create.html?Queue=%(remote_product)s" "&Subject=%(summary)s&Content=%(description)s"), BugTrackerType.SAVANE: ("%(base_url)s/bugs/?func=additem&group=%(remote_product)s"), BugTrackerType.SOURCEFORGE: ("%(base_url)s/%(tracker)s/?func=add&" "group_id=%(group_id)s&atid=%(at_id)s"), BugTrackerType.TRAC: ("%(base_url)s/newticket?summary=%(summary)s&" "description=%(description)s"), } _search_url_patterns = { BugTrackerType.BUGZILLA: ("%(base_url)s/query.cgi?product=%(remote_product)s" "&short_desc=%(summary)s"), BugTrackerType.GOOGLE_CODE: "%(base_url)s/list?q=%(summary)s", BugTrackerType.DEBBUGS: ("%(base_url)s/cgi-bin/search.cgi?phrase=%(summary)s" "&attribute_field=package&attribute_operator=STROREQ" "&attribute_value=%(remote_product)s"), BugTrackerType.MANTIS: "%(base_url)s/view_all_bug_page.php", BugTrackerType.PHPPROJECT: ("%(base_url)s/search.php?search_for=%(summary)s"), BugTrackerType.ROUNDUP: ("%(base_url)s/issue?@template=search&@search_text=%(summary)s"), BugTrackerType.RT: ("%(base_url)s/Search/Build.html?Query=Queue = " "'%(remote_product)s' AND Subject LIKE '%(summary)s'"), BugTrackerType.SAVANE: ("%(base_url)s/bugs/?func=search&group=%(remote_product)s"), BugTrackerType.SOURCEFORGE: ("%(base_url)s/search/?group_id=%(group_id)s" "&some_word=%(summary)s&type_of_search=artifact"), BugTrackerType.TRAC: "%(base_url)s/search?ticket=on&q=%(summary)s", } @property def _custom_filing_url_patterns(self): """Return a dict of bugtracker-specific bugfiling URL patterns.""" gnome_bugzilla = getUtility(ILaunchpadCelebrities).gnome_bugzilla return { gnome_bugzilla: ("%(base_url)s/enter_bug.cgi?product=%(remote_product)s" "&short_desc=%(summary)s&comment=%(description)s"), } @property def latestwatches(self): """See `IBugTracker`.""" return self.watches[:10] @property def multi_product(self): """Return True if this BugTracker tracks multiple projects.""" if self.bugtrackertype not in SINGLE_PRODUCT_BUGTRACKERTYPES: return True else: return False def getBugFilingAndSearchLinks(self, remote_product, summary=None, description=None, remote_component=None): """See `IBugTracker`.""" bugtracker_urls = {'bug_filing_url': None, 'bug_search_url': None} if remote_product is None and self.multi_product: # Don't try to return anything if remote_product is required # for this BugTrackerType and one hasn't been passed. return bugtracker_urls if remote_product is None: # Turn the remote product into an empty string so that # quote() doesn't blow up later on. remote_product = '' if remote_component is None: # Ditto for remote component. remote_component = '' if self in self._custom_filing_url_patterns: # Some bugtrackers are customised to accept different # querystring parameters from the default. We special-case # these. bug_filing_pattern = self._custom_filing_url_patterns[self] else: bug_filing_pattern = self._filing_url_patterns.get( self.bugtrackertype, None) bug_search_pattern = self._search_url_patterns.get( self.bugtrackertype, None) # Make sure that we don't put > 1 '/' in returned URLs. base_url = self.baseurl.rstrip('/') # If summary or description are None, convert them to empty # strings to that we don't try to pass anything to the upstream # bug tracker. if summary is None: summary = '' if description is None: description = '' # UTF-8 encode the description and summary so that quote() # doesn't break if they contain unicode characters it doesn't # understand. summary = summary.encode('utf-8') description = description.encode('utf-8') if self.bugtrackertype == BugTrackerType.SOURCEFORGE: try: # SourceForge bug trackers use a group ID and an ATID to # file a bug, rather than a product name. remote_product # should be an ampersand-separated string in the form # 'group_id&atid' group_id, at_id = remote_product.split('&') except ValueError: # If remote_product contains something that's not valid # in a SourceForge context we just return early. return None # If this bug tracker is the SourceForge celebrity the link # is to the new bug tracker rather than the old one. sf_celeb = getUtility(ILaunchpadCelebrities).sourceforge_tracker if self == sf_celeb: tracker = 'tracker2' else: tracker = 'tracker' url_components = { 'base_url': base_url, 'tracker': quote(tracker), 'group_id': quote(group_id), 'at_id': quote(at_id), 'summary': quote(summary), 'description': quote(description), } else: url_components = { 'base_url': base_url, 'remote_product': quote(remote_product), 'remote_component': quote(remote_component), 'summary': quote(summary), 'description': quote(description), } if bug_filing_pattern is not None: bugtracker_urls['bug_filing_url'] = (bug_filing_pattern % url_components) if bug_search_pattern is not None: bugtracker_urls['bug_search_url'] = (bug_search_pattern % url_components) return bugtracker_urls def getBugsWatching(self, remotebug): """See `IBugTracker`.""" # We special-case email address bug trackers. Since we don't # record a remote bug id for them we can never know which bugs # are already watching a remote bug. if self.bugtrackertype == BugTrackerType.EMAILADDRESS: return [] return shortlist( Store.of(self).find(Bug, BugWatch.bugID == Bug.id, BugWatch.bugtrackerID == self.id, BugWatch.remotebug == remotebug).config( distinct=True).order_by(Bug.datecreated)) @property def watches_ready_to_check(self): return Store.of(self).find( BugWatch, BugWatch.bugtracker == self, Not(BugWatch.next_check == None), BugWatch.next_check <= datetime.now(timezone('UTC'))) @property def watches_with_unpushed_comments(self): return Store.of(self).find( BugWatch, BugWatch.bugtracker == self, BugMessage.bugwatch == BugWatch.id, BugMessage.remote_comment_id == None).config(distinct=True) @property def watches_needing_update(self): """All watches needing some sort of update. :return: The union of `watches_ready_to_check` and `watches_with_unpushed_comments`. """ return self.watches_ready_to_check.union( self.watches_with_unpushed_comments) # Join to return a list of BugTrackerAliases relating to this # BugTracker. _bugtracker_aliases = SQLMultipleJoin('BugTrackerAlias', joinColumn='bugtracker') def _get_aliases(self): """See `IBugTracker.aliases`.""" alias_urls = set(alias.base_url for alias in self._bugtracker_aliases) # Although it does no harm if the current baseurl is also an # alias, we hide it and all its permutations to avoid # confusion. alias_urls.difference_update(base_url_permutations(self.baseurl)) return tuple(sorted(alias_urls)) def _set_aliases(self, alias_urls): """See `IBugTracker.aliases`.""" if alias_urls is None: alias_urls = set() else: alias_urls = set(alias_urls) current_aliases_by_url = dict( (alias.base_url, alias) for alias in self._bugtracker_aliases) # Make a set of the keys, i.e. a set of current URLs. current_alias_urls = set(current_aliases_by_url) # URLs we need to add as aliases. to_add = alias_urls - current_alias_urls # URL aliases we need to delete. to_del = current_alias_urls - alias_urls for url in to_add: BugTrackerAlias(bugtracker=self, base_url=url) for url in to_del: alias = current_aliases_by_url[url] alias.destroySelf() aliases = property( _get_aliases, _set_aliases, None, """A list of the alias URLs. See `IBugTracker`. The aliases are found by querying BugTrackerAlias. Assign an iterable of URLs or None to set or remove aliases. """) @property def imported_bug_messages(self): """See `IBugTracker`.""" return Store.of(self).find( BugMessage, BugMessage.bugwatchID == BugWatch.id, BugWatch.bugtrackerID == self.id).order_by(BugMessage.id) def getLinkedPersonByName(self, name): """Return the Person with a given name on this bugtracker.""" return BugTrackerPerson.selectOneBy(name=name, bugtracker=self) def linkPersonToSelf(self, name, person): """See `IBugTrackerSet`.""" # Check that this name isn't already in use for this bugtracker. if self.getLinkedPersonByName(name) is not None: raise BugTrackerPersonAlreadyExists( "Name '%s' is already in use for bugtracker '%s'." % (name, self.name)) bugtracker_person = BugTrackerPerson(name=name, bugtracker=self, person=person) return bugtracker_person def ensurePersonForSelf(self, display_name, email, rationale, creation_comment): """Return a Person that is linked to this bug tracker.""" # If we have an email address to work with we can use # ensurePerson() to get the Person we need. if email is not None: return getUtility(IPersonSet).ensurePerson(email, display_name, rationale, creation_comment) # First, see if there's already a BugTrackerPerson for this # display_name on this bugtracker. If there is, return it. bugtracker_person = self.getLinkedPersonByName(display_name) if bugtracker_person is not None: return bugtracker_person.person # Generate a valid Launchpad name for the Person. base_canonical_name = ("%s-%s" % (sanitize_name(display_name), self.name)) canonical_name = base_canonical_name person_set = getUtility(IPersonSet) index = 0 while person_set.getByName(canonical_name) is not None: index += 1 canonical_name = "%s-%s" % (base_canonical_name, index) person = person_set.createPersonWithoutEmail(canonical_name, rationale, creation_comment, displayname=display_name) # Link the Person to the bugtracker for future reference. bugtracker_person = self.linkPersonToSelf(display_name, person) return person def resetWatches(self, new_next_check=None): """See `IBugTracker`.""" if new_next_check is None: new_next_check = SQL( "now() at time zone 'UTC' + (random() * interval '1 day')") store = Store.of(self) store.find(BugWatch, BugWatch.bugtracker == self).set(next_check=new_next_check, lastchecked=None, last_error_type=None) def addRemoteComponentGroup(self, component_group_name): """See `IBugTracker`.""" if component_group_name is None: component_group_name = "default" component_group = BugTrackerComponentGroup() component_group.name = component_group_name component_group.bug_tracker = self store = IStore(BugTrackerComponentGroup) store.add(component_group) store.commit() return component_group def getAllRemoteComponentGroups(self): """See `IBugTracker`.""" component_groups = [] component_groups = Store.of(self).find( BugTrackerComponentGroup, BugTrackerComponentGroup.bug_tracker == self.id) component_groups = component_groups.order_by( BugTrackerComponentGroup.name) return component_groups def getRemoteComponentGroup(self, component_group_name): """See `IBugTracker`.""" component_group = None store = IStore(BugTrackerComponentGroup) if component_group_name is None: return None elif component_group_name.isdigit(): component_group_id = int(component_group_name) component_group = store.find( BugTrackerComponentGroup, BugTrackerComponentGroup.id == component_group_id).one() else: component_group = store.find( BugTrackerComponentGroup, BugTrackerComponentGroup.name == component_group_name).one() return component_group def getRemoteComponentForDistroSourcePackageName(self, distribution, sourcepackagename): """See `IBugTracker`.""" if distribution is None: return None dsp = distribution.getSourcePackage(sourcepackagename) if dsp is None: return None return Store.of(self).find( BugTrackerComponent, BugTrackerComponent.distribution == distribution.id, BugTrackerComponent.source_package_name == dsp.sourcepackagename.id).one() def getRelatedPillars(self, user=None): """See `IBugTracker`.""" products = IStore(Product).find( Product, Product.bugtrackerID == self.id, Product.active == True, ProductSet.getProductPrivacyFilter(user)).order_by(Product.name) groups = IStore(ProjectGroup).find( ProjectGroup, ProjectGroup.bugtrackerID == self.id, ProjectGroup.active == True).order_by(ProjectGroup.name) return groups, products
class Node_v_23(Model): __storm_table__ = 'node' name = Unicode() public_site = Unicode() hidden_service = Unicode() email = Unicode() receipt_salt = Unicode() languages_enabled = JSON() default_language = Unicode() default_timezone = Int() description = JSON() presentation = JSON() footer = JSON() security_awareness_title = JSON() security_awareness_text = JSON() context_selector_label = JSON() maximum_namesize = Int() maximum_textsize = Int() maximum_filesize = Int() tor2web_admin = Bool() tor2web_submission = Bool() tor2web_receiver = Bool() tor2web_unauth = Bool() allow_unencrypted = Bool() allow_iframes_inclusion = Bool() submission_minimum_delay = Int() submission_maximum_ttl = Int() can_postpone_expiration = Bool() can_delete_submission = Bool() ahmia = Bool() wizard_done = Bool() disable_privacy_badge = Bool() disable_security_awareness_badge = Bool() disable_security_awareness_questions = Bool() disable_key_code_hint = Bool() whistleblowing_question = JSON() whistleblowing_button = JSON() enable_custom_privacy_badge = Bool() custom_privacy_badge_tor = JSON() custom_privacy_badge_none = JSON() header_title_homepage = JSON() header_title_submissionpage = JSON() header_title_receiptpage = JSON() landing_page = Unicode() show_contexts_in_alphabetical_order = Bool() exception_email = Unicode()
class Node_v_14(Model): __storm_table__ = 'node' name = Unicode() public_site = Unicode() hidden_service = Unicode() email = Unicode() receipt_salt = Unicode() last_update = DateTime() receipt_regexp = Unicode() languages_enabled = Pickle() default_language = Unicode() description = Pickle() presentation = Pickle() footer = Pickle() subtitle = Pickle() terms_and_conditions = Pickle() security_awareness_title = Pickle() security_awareness_text = Pickle() stats_update_time = Int() maximum_namesize = Int() maximum_textsize = Int() maximum_filesize = Int() tor2web_admin = Bool() tor2web_submission = Bool() tor2web_receiver = Bool() tor2web_unauth = Bool() allow_unencrypted = Bool() x_frame_options_mode = Unicode() x_frame_options_allow_from = Unicode() postpone_superpower = Bool() can_delete_submission = Bool() ahmia = Bool() wizard_done = Bool() anomaly_checks = Bool() exception_email = Unicode() disable_privacy_badge = Bool() disable_security_awareness_badge = Bool() disable_security_awareness_questions = Bool()
class TranslationTemplatesBuild(BuildFarmJobMixin, Storm): """A `BuildFarmJob` extension for translation templates builds.""" implements(ITranslationTemplatesBuild) classProvides(ITranslationTemplatesBuildSource) __storm_table__ = 'TranslationTemplatesBuild' job_type = BuildFarmJobType.TRANSLATIONTEMPLATESBUILD id = Int(name='id', primary=True) build_farm_job_id = Int(name='build_farm_job', allow_none=False) build_farm_job = Reference(build_farm_job_id, 'BuildFarmJob.id') branch_id = Int(name='branch', allow_none=False) branch = Reference(branch_id, 'Branch.id') processor_id = Int(name='processor') processor = Reference(processor_id, 'Processor.id') virtualized = Bool(name='virtualized') date_created = DateTime(name='date_created', tzinfo=pytz.UTC, allow_none=False) date_started = DateTime(name='date_started', tzinfo=pytz.UTC) date_finished = DateTime(name='date_finished', tzinfo=pytz.UTC) date_first_dispatched = DateTime(name='date_first_dispatched', tzinfo=pytz.UTC) builder_id = Int(name='builder') builder = Reference(builder_id, 'Builder.id') status = DBEnum(name='status', enum=BuildStatus, allow_none=False) log_id = Int(name='log') log = Reference(log_id, 'LibraryFileAlias.id') failure_count = Int(name='failure_count', allow_none=False) @property def title(self): return u'Translation template build for %s' % (self.branch.displayname) def __init__(self, build_farm_job, branch, processor): super(TranslationTemplatesBuild, self).__init__() self.build_farm_job = build_farm_job self.branch = branch self.status = BuildStatus.NEEDSBUILD self.processor = processor def makeJob(self): """See `IBuildFarmJobOld`.""" store = IStore(BranchJob) # Pass public HTTP URL for the branch. metadata = { 'branch_url': self.branch.composePublicURL(), 'build_id': self.id, } branch_job = BranchJob(self.branch, BranchJobType.TRANSLATION_TEMPLATES_BUILD, metadata) store.add(branch_job) return TranslationTemplatesBuildJob(branch_job) @classmethod def _getStore(cls, store=None): """Return `store` if given, or the default.""" if store is None: return IStore(cls) else: return store @classmethod def _getBuildArch(cls): """Returns an `IProcessor` to queue a translation build for.""" # XXX Danilo Segan bug=580429: we hard-code processor to the Ubuntu # default processor architecture. This stops the buildfarm from # accidentally dispatching the jobs to private builders. ubuntu = getUtility(ILaunchpadCelebrities).ubuntu return ubuntu.currentseries.nominatedarchindep.processor @classmethod def create(cls, branch): """See `ITranslationTemplatesBuildSource`.""" processor = cls._getBuildArch() build_farm_job = getUtility(IBuildFarmJobSource).new( BuildFarmJobType.TRANSLATIONTEMPLATESBUILD) build = TranslationTemplatesBuild(build_farm_job, branch, processor) store = cls._getStore() store.add(build) store.flush() return build @classmethod def getByID(cls, build_id, store=None): """See `ITranslationTemplatesBuildSource`.""" store = cls._getStore(store) match = store.find(TranslationTemplatesBuild, TranslationTemplatesBuild.id == build_id) return match.one() @classmethod def getByBuildFarmJob(cls, buildfarmjob, store=None): """See `ITranslationTemplatesBuildSource`.""" store = cls._getStore(store) match = store.find(TranslationTemplatesBuild, build_farm_job_id=buildfarmjob.id) return match.one() @classmethod def getByBuildFarmJobs(cls, buildfarmjobs, store=None): """See `ITranslationTemplatesBuildSource`.""" store = cls._getStore(store) rows = store.find( TranslationTemplatesBuild, TranslationTemplatesBuild.build_farm_job_id.is_in( bfj.id for bfj in buildfarmjobs)) return DecoratedResultSet(rows, pre_iter_hook=cls.preloadBuildsData) @classmethod def preloadBuildsData(cls, builds): # Circular imports. from lp.services.librarian.model import LibraryFileAlias # Load the related branches, products. branches = load_related(Branch, builds, ['branch_id']) load_related(Product, branches, ['productID']) # Preload branches cached associated product series and # suite source packages for all the related branches. GenericBranchCollection.preloadDataForBranches(branches) load_related(LibraryFileAlias, builds, ['log_id']) @classmethod def findByBranch(cls, branch, store=None): """See `ITranslationTemplatesBuildSource`.""" store = cls._getStore(store) return store.find(TranslationTemplatesBuild, TranslationTemplatesBuild.branch == branch) @property def log_url(self): """See `IBuildFarmJob`.""" if self.log is None: return None return self.log.http_url
class Config(Storm): __storm_table__ = 'config' __storm_primary__ = ('var_group', 'var_name') cfg_desc = GLConfig var_group = Unicode() var_name = Unicode() value = JSON() customized = Bool(default=False) def __init__(self, group=None, name=None, value=None, cfg_desc=None, migrate=False): """ :param value: This input is passed directly into set_v :param migrate: Added to comply with models.Model constructor which is used to copy every field returned by storm from the db from an old_obj to a new one. :param cfg_desc: Used to specify where to look for the Config objs descripitor. This is used in mig 34. """ if cfg_desc is not None: self.cfg_desc = cfg_desc if migrate: return self.var_group = unicode(group) self.var_name = unicode(name) self.set_v(value) @staticmethod def find_descriptor(config_desc_root, var_group, var_name): d = config_desc_root.get(var_group, {}).get(var_name, None) if d is None: raise ValueError('%s.%s descriptor cannot be None' % (var_group, var_name)) return d def set_v(self, val): desc = self.find_descriptor(self.cfg_desc, self.var_group, self.var_name) if val is None: val = desc._type() if isinstance(desc, config_desc.Unicode) and isinstance(val, str): val = unicode(val) if not isinstance(val, desc._type): raise ValueError("Cannot assign %s with %s" % (self, type(val))) if desc.validator is not None: desc.validator(self, self.var_name, val) if self.value is None: self.value = {'v': val} elif self.value['v'] != val: self.customized = True self.value = {'v': val} def get_v(self): return self.value['v'] def __repr__(self): return "<Config: %s.%s>" % (self.var_group, self.var_name)
class Context_v_34(models.ModelWithID): __storm_table__ = 'context' show_small_receiver_cards = Bool(default=False) show_context = Bool(default=True) show_recipients_details = Bool(default=False) allow_recipients_selection = Bool(default=False) maximum_selectable_receivers = Int(default=0) select_all_receivers = Bool(default=True) enable_comments = Bool(default=True) enable_messages = Bool(default=False) enable_two_way_comments = Bool(default=True) enable_two_way_messages = Bool(default=True) enable_attachments = Bool(default=True) tip_timetolive = Int(default=15) name = JSON(validator=shortlocal_v) description = JSON(validator=longlocal_v) recipients_clarification = JSON() status_page_message = JSON() show_receivers_in_alphabetical_order = Bool(default=False) presentation_order = Int(default=0) questionnaire_id = Unicode() img_id = Unicode()
class LiveFS(Storm): """See `ILiveFS`.""" __storm_table__ = 'LiveFS' id = Int(primary=True) date_created = DateTime(name='date_created', tzinfo=pytz.UTC, allow_none=False) date_last_modified = DateTime(name='date_last_modified', tzinfo=pytz.UTC, allow_none=False) registrant_id = Int(name='registrant', allow_none=False) registrant = Reference(registrant_id, 'Person.id') owner_id = Int(name='owner', allow_none=False) owner = Reference(owner_id, 'Person.id') distro_series_id = Int(name='distro_series', allow_none=False) distro_series = Reference(distro_series_id, 'DistroSeries.id') name = Unicode(name='name', allow_none=False) metadata = JSON('json_data') require_virtualized = Bool(name='require_virtualized') relative_build_score = Int(name='relative_build_score', allow_none=False) def __init__(self, registrant, owner, distro_series, name, metadata, require_virtualized, date_created): """Construct a `LiveFS`.""" if not getFeatureFlag(LIVEFS_FEATURE_FLAG): raise LiveFSFeatureDisabled super(LiveFS, self).__init__() self.registrant = registrant self.owner = owner self.distro_series = distro_series self.name = name self.metadata = metadata self.require_virtualized = require_virtualized self.relative_build_score = 0 self.date_created = date_created self.date_last_modified = date_created def requestBuild(self, requester, archive, distro_arch_series, pocket, unique_key=None, metadata_override=None, version=None): """See `ILiveFS`.""" if not requester.inTeam(self.owner): raise LiveFSNotOwner( "%s cannot create live filesystem builds owned by %s." % (requester.displayname, self.owner.displayname)) if not archive.enabled: raise ArchiveDisabled(archive.displayname) if archive.private and self.owner != archive.owner: # See rationale in `LiveFSBuildArchiveOwnerMismatch` docstring. raise LiveFSBuildArchiveOwnerMismatch() pending = IStore(self).find( LiveFSBuild, LiveFSBuild.livefs_id == self.id, LiveFSBuild.archive_id == archive.id, LiveFSBuild.distro_arch_series_id == distro_arch_series.id, LiveFSBuild.pocket == pocket, LiveFSBuild.unique_key == unique_key, LiveFSBuild.status == BuildStatus.NEEDSBUILD) if pending.any() is not None: raise LiveFSBuildAlreadyPending build = getUtility(ILiveFSBuildSet).new( requester, self, archive, distro_arch_series, pocket, unique_key=unique_key, metadata_override=metadata_override, version=version) build.queueBuild() return build def _getBuilds(self, filter_term, order_by): """The actual query to get the builds.""" query_args = [ LiveFSBuild.livefs == self, LiveFSBuild.archive_id == Archive.id, Archive._enabled == True, get_enabled_archive_filter(getUtility(ILaunchBag).user, include_public=True, include_subscribed=True) ] if filter_term is not None: query_args.append(filter_term) result = Store.of(self).find(LiveFSBuild, *query_args) result.order_by(order_by) return result @property def builds(self): """See `ILiveFS`.""" order_by = (NullsLast( Desc(Greatest(LiveFSBuild.date_started, LiveFSBuild.date_finished))), Desc(LiveFSBuild.date_created), Desc(LiveFSBuild.id)) return self._getBuilds(None, order_by) @property def _pending_states(self): """All the build states we consider pending (non-final).""" return [ BuildStatus.NEEDSBUILD, BuildStatus.BUILDING, BuildStatus.UPLOADING, BuildStatus.CANCELLING, ] @property def completed_builds(self): """See `ILiveFS`.""" filter_term = (Not(LiveFSBuild.status.is_in(self._pending_states))) order_by = (NullsLast( Desc(Greatest(LiveFSBuild.date_started, LiveFSBuild.date_finished))), Desc(LiveFSBuild.id)) return self._getBuilds(filter_term, order_by) @property def pending_builds(self): """See `ILiveFS`.""" filter_term = (LiveFSBuild.status.is_in(self._pending_states)) # We want to order by date_created but this is the same as ordering # by id (since id increases monotonically) and is less expensive. order_by = Desc(LiveFSBuild.id) return self._getBuilds(filter_term, order_by) def destroySelf(self): """See `ILiveFS`.""" if not self.builds.is_empty(): raise CannotDeleteLiveFS( "Cannot delete a live filesystem with builds.") IStore(LiveFS).remove(self)