class Node(Storm): """A PubSub node.""" __storm_table__ = u'nodes' node = Unicode(primary=True, allow_none=False) config = ReferenceSet(node, 'NodeConfig.node') items = ReferenceSet(node, 'Item.node') subscriptions = ReferenceSet(node, 'Subscription.node') affiliations = ReferenceSet(node, 'Affiliation.node') def __init__(self, node): super(Node, self).__init__() self.node = unicode(node)
class Account(SQLBase): """An Account.""" implements(IAccount) date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW) displayname = StringCol(dbName='displayname', notNull=True) creation_rationale = EnumCol(dbName='creation_rationale', schema=AccountCreationRationale, notNull=True) status = AccountStatusEnumCol(enum=AccountStatus, default=AccountStatus.NOACCOUNT, notNull=True) date_status_set = UtcDateTimeCol(notNull=True, default=UTC_NOW) status_comment = StringCol(dbName='status_comment', default=None) openid_identifiers = ReferenceSet("Account.id", OpenIdIdentifier.account_id) def __repr__(self): displayname = self.displayname.encode('ASCII', 'backslashreplace') return "<%s '%s' (%s)>" % (self.__class__.__name__, displayname, self.status) def reactivate(self, comment): """See `IAccountSpecialRestricted`.""" self.status = AccountStatus.ACTIVE self.status_comment = comment
class Namespace(Storm): """A namespace is a container for L{Tag}s and other namespaces. @param creator: The L{User} that owns the namespace. @param path: The full C{unicode} path for the namespace. @param name: The C{unicode} name of the namespace (should be the same as the last segment of C{path}). @param parent: Optionally, the C{Namespace} instance that represents the parent of this namespace. """ __storm_table__ = 'namespaces' id = Int('id', primary=True, allow_none=False, default=AutoReload) objectID = UUID('object_id', allow_none=False) parentID = Int('parent_id') creatorID = Int('creator_id', allow_none=False) path = Unicode('path', allow_none=False) name = Unicode('name', allow_none=False) creationTime = DateTime('creation_time', default=AutoReload) creator = Reference(creatorID, 'User.id') parent = Reference(parentID, id) children = ReferenceSet(id, parentID) permission = Reference(id, 'NamespacePermission.namespaceID') def __init__(self, creator, path, name, parentID=None): self.objectID = uuid4() self.creator = creator self.path = path self.name = name self.parentID = parentID
class RoleEntity(Storm): __storm_table__ = 'ROLES' __storm_primary__ = 'id' id = Unicode(name='id') name = Unicode(name='name') permissions = ReferenceSet(id, 'Role2PermissionEntity.role_id', 'Role2PermissionEntity.permission_id', PermissionEntity.id)
class Category(Storm): """ A thread category """ __storm_table__ = "category" id = Int(primary=True) name = Unicode() threads = ReferenceSet(id, Thread.category_id) def __init__(self, name): self.name = unicode(name)
class UserEntity(Storm): __storm_table__ = 'USERS' __storm_primary__ = 'id' id = Unicode(name='id') fullname = Unicode(name='fullname') password = Unicode(name='password') is_active = Int(name='is_active') is_superuser = Int(name='is_superuser') creation_date = DateTime(name='creation_date') roles = ReferenceSet(id, 'User2RoleEntity.user_id', 'User2RoleEntity.role_id', RoleEntity.id)
class Account(SQLBase): """An Account.""" date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW) displayname = StringCol(dbName='displayname', notNull=True) creation_rationale = EnumCol(dbName='creation_rationale', schema=AccountCreationRationale, notNull=True) status = AccountStatusEnumCol(enum=AccountStatus, default=AccountStatus.NOACCOUNT, notNull=True) date_status_set = UtcDateTimeCol(notNull=True, default=UTC_NOW) status_history = StringCol(dbName='status_comment', default=None) openid_identifiers = ReferenceSet("Account.id", OpenIdIdentifier.account_id) def __repr__(self): displayname = self.displayname.encode('ASCII', 'backslashreplace') return "<%s '%s' (%s)>" % (self.__class__.__name__, displayname, self.status) def addStatusComment(self, user, comment): """See `IAccountModerateRestricted`.""" prefix = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') if user is not None: prefix += ' %s' % user.name old_lines = (self.status_history.splitlines() if self.status_history else []) self.status_history = '\n'.join(old_lines + ['%s: %s' % (prefix, comment), '']) def setStatus(self, status, user, comment): """See `IAccountModerateRestricted`.""" comment = comment or '' self.addStatusComment( user, '%s -> %s: %s' % (self.status.title, status.title, comment)) # date_status_set is maintained by a DB trigger. self.status = status def reactivate(self, comment): """See `IAccountSpecialRestricted`.""" self.setStatus(AccountStatus.ACTIVE, None, comment)
class Thread(Storm): """ I represent a discussion on a Board. @ivar created: Timestamp of my entry in the database @ivar name: The topic that will be discussed @ivar board_id: ID of the Board to which I belong @ivar user_id: ID of the User that created me @ReferenceSet messages: A storm ReferenceSet object with all of my Messages @Reference user: The User that created me. """ __storm_table__ = 'boards_threads' id = Int(primary=True) created = DateTime(default=datetime.datetime.now()) name = Unicode(validator=unicoder) board_id = Int() user_id = Int() board = Reference(board_id, "Board.id") messages = ReferenceSet(id, "Message.thread_id") user = Reference(user_id, "User.id") def getJSON(self, extra=False): """ Return a json object of my attributes and other useful stuff @param extra: Whether info from other classes will be included in the json """ return_json = { 'id': self.id, 'created': self.created, 'name': self.name, } if extra: user = store.find(User, User.id == self.user_id).one() messages = self.messages extra_data = { 'username': user.username, } return_json.update(extra_data) return return_json
class Email(Storm): """ An archived email, from a mailing-list. It is identified by both the list name and the message id. """ implements(IMessage) __storm_table__ = "email" __storm_primary__ = "list_name", "message_id" list_name = Unicode() message_id = Unicode() sender_name = Unicode() sender_email = Unicode() user_id = Unicode() subject = Unicode() content = Unicode() date = DateTime() timezone = Int() in_reply_to = Unicode() message_id_hash = Unicode() thread_id = Unicode() archived_date = DateTime(default_factory=datetime.datetime.now) thread_depth = Int(default=0) thread_order = Int(default=0) # path is required by IMessage, but it makes no sense here path = None # References attachments = ReferenceSet( (list_name, message_id), ("Attachment.list_name", "Attachment.message_id"), order_by="Attachment.counter") thread = Reference((list_name, thread_id), ("Thread.list_name", "Thread.thread_id")) full_email = Reference((list_name, message_id), ("EmailFull.list_name", "EmailFull.message_id")) full = Proxy(full_email, "EmailFull.full") mlist = Reference(list_name, "List.name") def __init__(self, list_name, message_id): self.list_name = unicode(list_name) self.message_id = unicode(message_id) self.message_id_hash = unicode(get_message_id_hash(self.message_id))
class User(Storm): """ I am a website user. I can browse the site, view boards, and potentially modify other User objects. @ivar username: A unique name to identify me @ivar email: The email address tied to me @ivar first_name: My first name @ivar last_name: My surname @ReferenceSet board_perms: A storm ReferenceSet of all my Board permissions """ __storm_table__ = 'users' id = Int(primary=True) username = Unicode(validator=unicoder) email = Unicode(validator=unicoder) first_name = Unicode(validator=unicoder) last_name = Unicode(validator=unicoder) board_perms = ReferenceSet(id, _LinkUserBoardPerms.user_id, _LinkUserBoardPerms.perm_id, BoardPerms.id)
class Location(Storm): __storm_table__ = "location" name = Unicode(primary=True) systems = ReferenceSet(name, "IcepapSystem.location_name") log = logging.getLogger('{}.Location'.format(__name__)) @loggingInfo def __init__(self, name): self.name = name self.initialize() @loggingInfo def initialize(self): self._inmemory_systems = {} @loggingInfo def __storm_loaded__(self): self.initialize() @loggingInfo def loadSystemsfromDB(self): for system in self.systems: self._inmemory_systems[system.name] = system @loggingInfo def addSystem(self, system): self.systems.add(system) self._inmemory_systems[system.name] = system @loggingInfo def deleteSystem(self, name): system = self.systems.find(IcepapSystem.name == name).one() for driver in system.getDrivers(): system.removeDriver(driver.addr) StormManager().deleteIcepapSystem(system) try: del self._inmemory_systems[name] self._inmemory_drivers = {} except Exception: pass
class IcepapDriverCfg(Storm): __storm_table__ = "icepapdrivercfg" id = Int(primary=True) icepapsystem_name = Unicode() driver_addr = Int() name = Unicode() description = Unicode() signature = Unicode() date = DateTime() """ references """ icepap_driver = Reference((icepapsystem_name, driver_addr), ("IcepapDriver.icepapsystem_name", "IcepapDriver.addr")) parameters = ReferenceSet(id, "CfgParameter.cfg_id") log = logging.getLogger('{}.IcepapDriverCfg'.format(__name__)) @loggingInfo def __init__(self, name, description=None): if description is None: description = str("") self.description = str(description) self.name = str(name) self.date = datetime.datetime.now() self.initialize() @loggingInfo def __storm_loaded__(self): self.initialize() for cfgpar in self.parameters: self._inmemory_parameters[cfgpar.name] = cfgpar @loggingInfo def initialize(self): self._inmemory_parameters = {} @loggingInfo def setDriver(self, driver): self.icepap_driver = driver @loggingInfo def resetDriver(self): self.icepap_driver = None self.icepapsystem_name = None self.driver_addr = None @loggingInfo def setSignature(self, signature): self.signature = str(signature) @loggingInfo def getSignature(self): return str(self.signature) @loggingInfo def setParameter(self, name, value): name = str(name) value = str(value) cfgpar = None try: cfgpar = self.parameters.find(CfgParameter.name == name).one() except Exception: pass if cfgpar is None: cfgpar = CfgParameter(self, name, value) self.parameters.add(cfgpar) else: cfgpar.value = value self._inmemory_parameters[str(name)] = cfgpar @loggingInfo def getParameter(self, name, in_memory=False): if in_memory: if str(name) in self._inmemory_parameters: return self._inmemory_parameters[str(name)].value else: return None else: cfgpar = self.parameters.find(CfgParameter.name == name).one() if cfgpar is not None: return cfgpar.value else: return None @loggingInfo def toList(self): l = [] for cfgpar in self._inmemory_parameters.values(): l.append((cfgpar.name, cfgpar.value)) return l @loggingInfo def __str__(self): text = "Configuration" for par in self.parameters: text = text + "\n" + par.name + ":\t" + par.value return text @loggingInfo def __ne__(self, other): return not self.__eq__(other) @loggingInfo def __eq__(self, other): self_list = self.toList() for name, value in self_list: other_value = other.getParameter(name, True) if other_value is not None: if value != other_value: return False else: return False return True
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 FieldAnswerGroupFieldAnswer(BaseModel): """ Class used to implement references between FieldAnswerGroup and FieldAnswer """ __storm_table__ = 'fieldanswergroup_fieldanswer' __storm_primary__ = 'fieldanswergroup_id', 'fieldanswer_id' fieldanswergroup_id = Unicode() fieldanswer_id = Unicode() Field.template = Reference(Field.template_id, Field.id) Field.options = ReferenceSet(Field.id, FieldOption.field_id) Field.children = ReferenceSet(Field.id, FieldField.parent_id, FieldField.child_id, Field.id) Field.attrs = ReferenceSet(Field.id, FieldAttr.field_id) Field.activated_by = ReferenceSet(Field.id, OptionActivateField.field_id, OptionActivateField.field_option_id, FieldOption.id) FieldOption.field = Reference(FieldOption.field_id, Field.id) FieldOption.activated_fields = ReferenceSet( FieldOption.id, OptionActivateField.field_option_id, OptionActivateField.field_id, Field.id)
class IcepapDriver(Storm): __storm_table__ = "icepapdriver" __storm_primary__ = ("icepapsystem_name", "addr") icepapsystem_name = Unicode() addr = Int() name = Unicode() mode = Unicode() """ references """ icepap_system = Reference(icepapsystem_name, "IcepapSystem.name") historic_cfgs = ReferenceSet( (icepapsystem_name, addr), ("IcepapDriverCfg.icepapsystem_name", "IcepapDriverCfg.driver_addr")) log = logging.getLogger('{}.IcepapDriver'.format(__name__)) @loggingInfo def __init__(self, icepap_name, addr): self.icepapsystem_name = str(icepap_name) self.addr = addr self.current_cfg = None self.initialize() @loggingInfo def __storm_loaded__(self): self.current_cfg = self.historic_cfgs.order_by("date").last() self.initialize() @loggingInfo def initialize(self): self.drivernr = self.addr % 10 self.cratenr = self.addr // 10 self._undo_list = [] self.startup_cfg = self.current_cfg self.conflict = Conflict.NO_CONFLICT @loggingInfo def addConfiguration(self, cfg, current=True): if current: if self.current_cfg is not None: self._undo_list.append(self.current_cfg) else: self.startup_cfg = cfg self.current_cfg = cfg cfg.setDriver(self) self.historic_cfgs.add(cfg) @loggingInfo def setConflict(self, conflict): self.conflict = conflict @loggingInfo def getName(self): return self.name @loggingInfo def setName(self, name): self.name = str(name) @loggingInfo def setMode(self, mode): self.mode = str(mode) @loggingInfo def signDriver(self): # AS ESRF SAYS, WHEN SIGNING THE DRIVER CONFIG, THE COMMIT SHOULD # BE DONE IN THE DATABASE FIRST, AND IF NO ERRORS, THEN COMMUNICATE # THE DRIVER THAT THE VALUES SHOULD BE SIGNED. try: user = ConfigManager().username host = socket.gethostname() signature = user + "@" + host + "_" + \ datetime.now().strftime('%Y/%m/%d_%H:%M:%S') IcepapsManager().signDriverConfiguration(self.icepapsystem_name, self.addr, signature) self.mode = str(Mode.OPER) db = StormManager() db.commitTransaction() self.current_cfg.name = str(time.ctime()) self.current_cfg.setSignature(signature) self.startup_cfg = self.current_cfg self.conflict = Conflict.NO_CONFLICT except Exception as e: self.log.error("some exception while trying to sign the driver %s", e) @loggingInfo def setStartupCfg(self): self.current_cfg = self.startup_cfg self.conflict = Conflict.NO_CONFLICT @loggingInfo def undo(self, config): self.addConfiguration(config) # THE CURRENT CONFIGURATION SHOULD NOT BE IN THE UNDO LIST return self._undo_list.pop() @loggingInfo def getUndoList(self): return self._undo_list.pop() @loggingInfo def hasUndoList(self): return len(self._undo_list) > 0 @loggingInfo def saveHistoricCfg(self, now, name, desc): self.current_cfg.name = str(name) self.current_cfg.description = str(desc) @loggingInfo def deleteHistoricCfg(self, cfg): self.historic_cfgs.remove(cfg) @loggingInfo def __ne__(self, other): return not self.__eq__(other) @loggingInfo def __eq__(self, other): if self.current_cfg == other.current_cfg: self.setConflict(Conflict.NO_CONFLICT) return True self.setConflict(Conflict.DRIVER_CHANGED) return False # TO SORT THE ICEPAP DRIVERS IN THE TREE @loggingInfo def __lt__(self, other): if isinstance(other, IcepapDriver): return self.addr < other.addr
def epilogue(self): self.fail_on_count_mismatch['Step'] = False self.fail_on_count_mismatch['Field'] = False self.fail_on_count_mismatch['FieldOption'] = False self.fail_on_count_mismatch['FieldAttr'] = False # Add the required references Context_v_29.steps = ReferenceSet(Context_v_29.id, Step_v_29.context_id) Step_v_29.children = ReferenceSet(Step_v_29.id, self.model_from['Field'].step_id) default_questionnaire_id = db_get_default_questionnaire_id( self.store_new) default_language = self.store_old.find( self.model_from['Node']).one().default_language old_contexts = self.store_old.find(self.model_from['Context']) for old_context in old_contexts: map_on_default = False new_questionnaire_id = None for old_step in old_context.steps: if old_step.children.count() != 4: break for field in old_step.children: if 'en' in field.label and field.label[ 'en'] == 'Short title': map_on_default = True break if map_on_default: break if not map_on_default: new_questionnaire = self.model_to['Questionnaire']() new_questionnaire.name = old_context.name[ default_language] if default_language in old_context.name else '' new_questionnaire.layout = old_context.questionnaire_layout new_questionnaire.show_steps_navigation_bar = old_context.show_steps_navigation_bar new_questionnaire.steps_navigation_requires_completion = old_context.steps_navigation_requires_completion new_questionnaire.enable_whistleblower_identity = old_context.enable_whistleblower_identity self.store_new.add(new_questionnaire) new_questionnaire_id = new_questionnaire.id for old_step in old_context.steps: new_step = self.model_to['Step']() for _, v in new_step._storm_columns.iteritems(): if v.name == 'questionnaire_id': new_step.questionnaire_id = new_questionnaire.id continue setattr(new_step, v.name, getattr(old_step, v.name)) self.store_new.add(new_step) new_context = self.model_to['Context']() for _, v in new_context._storm_columns.iteritems(): if v.name == 'status_page_message': new_context.status_page_message = '' continue if v.name == 'questionnaire_id': new_context.questionnaire_id = default_questionnaire_id if new_questionnaire_id is None else new_questionnaire_id continue setattr(new_context, v.name, getattr(old_context, v.name)) self.store_new.add(new_context)
class BugTrackerComponentGroup(StormBase): """A collection of components in a remote bug tracker. Some bug trackers organize sets of components into higher level groups, such as Bugzilla's 'product'. """ implements(IBugTrackerComponentGroup) __storm_table__ = 'BugTrackerComponentGroup' id = Int(primary=True) name = Unicode(allow_none=False) bug_tracker_id = Int('bug_tracker') bug_tracker = Reference(bug_tracker_id, 'BugTracker.id') components = ReferenceSet(id, BugTrackerComponent.component_group_id, order_by=BugTrackerComponent.name) def addComponent(self, component_name): """Adds a component that is synced from a remote bug tracker""" component = BugTrackerComponent() component.name = component_name component.component_group = self store = IStore(BugTrackerComponent) store.add(component) store.flush() return component def getComponent(self, component_name): """Retrieves a component by the given name or id number. None is returned if there is no component by that name in the group. """ if component_name is None: return None elif component_name.isdigit(): component_id = int(component_name) return Store.of(self).find( BugTrackerComponent, BugTrackerComponent.id == component_id, BugTrackerComponent.component_group == self.id).one() else: return Store.of(self).find( BugTrackerComponent, BugTrackerComponent.name == component_name, BugTrackerComponent.component_group == self.id).one() def addCustomComponent(self, component_name): """Adds a component locally that isn't synced from a remote tracker """ component = BugTrackerComponent() component.name = component_name component.component_group = self component.is_custom = True store = IStore(BugTrackerComponent) store.add(component) store.flush() return component
class IcepapSystem(Storm): __storm_table__ = "icepapsystem" name = Unicode(primary=True) host = Unicode() port = Int() description = Unicode() version = Unicode() location_name = Unicode() """ references """ drivers = ReferenceSet(name, "IcepapDriver.icepapsystem_name") location = ReferenceSet(location_name, "Location.name") log = logging.getLogger('{}.IcepapSystem'.format(__name__)) @loggingInfo def __init__(self, name, host, port, location_name, description=None): self.name = str(name) if description is None: description = str("") self.description = str(description) self.location_name = location_name self.host = str(host) self.port = int(port) self.initialize() @loggingInfo def __storm_loaded__(self): self.initialize() @loggingInfo def loadDriversfromDB(self): for driver in self.drivers: self._inmemory_drivers[driver.addr] = driver @loggingInfo def initialize(self): self._inmemory_drivers = {} self.conflict = Conflict.NO_CONFLICT self.child_conflicts = 0 @loggingInfo def getDriver(self, addr, in_memory=True): if in_memory: if addr in self._inmemory_drivers: return self._inmemory_drivers[addr] else: return None else: return self.drivers.find(IcepapDriver.addr == addr).one() @loggingInfo def getDrivers(self, in_memory=True): if in_memory: if len(self._inmemory_drivers) == 0: self.loadDriversfromDB() return list(self._inmemory_drivers.values()) else: return self.drivers @loggingInfo def addDriver(self, driver): self.drivers.add(driver) self._inmemory_drivers[driver.addr] = driver @loggingInfo def addDriverList(self, driver_list): for driver in list(driver_list.values()): self.addDriver(driver) @loggingInfo def removeDriver(self, addr): driver = self.drivers.find(IcepapDriver.addr == addr).one() StormManager().deleteDriver(driver) del self._inmemory_drivers[addr] @loggingInfo def setConflict(self, conflict): self.conflict = conflict @loggingInfo def signSystem(self): for driver in self.getDrivers(): driver.signDriver() @loggingInfo def signCrate(self, cratenr): min_addr = cratenr * 10 max_addr = (cratenr + 1) * 10 for driver in self.drivers.find(IcepapDriver.addr > min_addr, IcepapDriver.addr < max_addr): driver.signDriver() @loggingInfo def compareDriverList(self, driver_list): self.child_conflicts = 0 conflictsList = [] self.conflict = Conflict.NO_CONFLICT ''' comparing drivers ''' for driver in self.drivers: addr = driver.addr if addr not in driver_list: conflictsList.append([Conflict.DRIVER_NOT_PRESENT, self, addr]) self.child_conflicts += 1 else: driver_cmp = driver_list[addr] if driver != driver_cmp: # HOOK TO CHECK AUTO-SOLVE CONFLICTS dsp_cfg = driver_cmp.current_cfg db_cfg = driver.current_cfg conflict = self.checkAutoSolvedConflict(dsp_cfg, db_cfg) conflictsList.append([conflict, self, addr]) self.child_conflicts += 1 # checking for new drivers for addr, driver in list(driver_list.items()): if self.drivers.find(IcepapDriver.addr == addr).count() == 0: self.addDriver(driver) # determine if it is a new driver or if it has been moved # ALWAYS TREAT AS NEW DRIVER, 'MOVED' HAS NO SENSE conflictsList.append([Conflict.NEW_DRIVER, self, addr]) return conflictsList def _auto_solved_conflict(self, dsp_cfg, db_cfg, skip_params=[], check_db_values=None, check_dsp_values=None): dsp_values = dsp_cfg.toList() db_values = db_cfg.toList() diff_values = set(dsp_values).difference(db_values) # Force to update the DB when the following parameter change. The DSP # always is right: VER, IPAPNAME and ID if 'VER' not in skip_params: skip_params.append('VER') if 'IPAPNAME' not in skip_params: skip_params.append('IPAPNAME') # ID previous IcepapCMS version (2.0.2) the DB only saves the ?ID HW, # now the icepap library returns ('?ID HW', '?ID SN') if 'ID' not in skip_params: skip_params.append('ID') # Check skip params for p, _ in diff_values: if p not in skip_params: return Conflict.DRIVER_CHANGED # Check DB values if check_db_values: for p, v in db_values: if p in check_db_values: if v not in check_db_values[p]: return Conflict.DRIVER_CHANGED # Check DSP values if check_dsp_values: for p, v in dsp_values: if p in check_dsp_values: if v not in check_dsp_values[p]: return Conflict.DRIVER_CHANGED return Conflict.DRIVER_AUTOSOLVE_EXPERT @loggingInfo def checkAutoSolvedConflict(self, dsp_cfg, db_cfg): # 20130710 ESRF ASKED FOR A HOOK TO 'SKIP' SOME CONFLICTS # ISSUE 053 in WIKI MINUTES # http://wikiserv.esrf.fr/esl/index.php/IcePAP_minute_130708 # # TWO NEW CONFLICT TYPES ADDED: DRIVER_AUTOSOLVE, # DRIVER_AUTOSOLVE_EXPERT # Since there is the possibility to keep current behaviour, # the method can return also DRIVER_CHANGED # NOTE: u'VER' and u'IPAPNAME' are also available to resolve # conflicts... # NOTE: configs have the .getParameter(par) method # BUT dsp values are not stored in the database, so in_memory # has to be set to True # ## par = u'VER' # ## print 'dsp', par, dsp_cfg.getParameter(par, in_memory=True) # ## print 'db', par, db_cfg.getParameter(par) # NOTE: it is also possible to operate with lists: # ##dsp_values = dsp_cfg.toList() # ##db_values = db_cfg.toList() # ##for p,v in dsp_values: # ## if p == par: # ## print 'dsp', p, v # ##for p,v in db_values: # ## if p == par: # ## print 'db', p, v try: dsp_cfg_ver = float( dsp_cfg.getParameter(str("VER"), in_memory=True)) except Exception: self.log.error("%s: missing VERsion parameter in DSP config", self.name) return Conflict.DRIVER_CHANGED try: db_cfg_ver = float(db_cfg.getParameter(str("VER"))) except Exception: self.log.error("%s: missing VERsion parameter in database config", self.name) return Conflict.DRIVER_CHANGED check_db_values = None if dsp_cfg_ver == 2.0 and 1.22 <= db_cfg_ver < 2.0: skip_params = [ 'EXTDISABLE', 'PCLMODE', 'EXTBUSY', 'POSUPDATE', 'LNKNAME', 'EXTPOWER', 'OUTPSRC' ] elif 3.14 < dsp_cfg_ver <= 3.17 and 2.0 <= db_cfg_ver <= 3.14: skip_params = [ 'HOLDTIME', 'EXTHOLD', 'INFOASRC', 'INFOBSRC', 'INFOCSRC' ] elif dsp_cfg_ver == 3.35 and db_cfg_ver == 3.14: # ignore new parameters or parameters that normally change # ['VER', 'HOLDTIME', 'EXTHOLD']: # parameters which value is not backward compatible # ['INFOASRC', 'INFOBSRC', 'INFOCSRC']: skip_params = [ 'SSISTRTB', 'SSILDC', 'SSIMSKNB', 'SSIEEPOL', 'SSIEWPOL', 'SSIOVF', 'HOLDTIME', 'EXTHOLD', 'INFOASRC', 'INFOBSRC', 'INFOCSRC' ] elif (dsp_cfg_ver == 3.35) and (db_cfg_ver == 3.15): # ignore new parameters or parameters that normally change # [ 'SSISTRTB', 'SSILDC', 'SSIMSKNB', 'SSIEEPOL', 'SSIEWPOL', # 'SSIOVF', 'HOLDTIME', 'EXTHOLD'] # parameters which value is not backward compatible # ['CATENTRY', 'ABSMODE', 'INFOASRC', 'INFOBSRC', 'INFOCSRC'] skip_params = [ 'SSISTRTB', 'SSILDC', 'SSIMSKNB', 'SSIEEPOL', 'SSIEWPOL', 'SSIOVF', 'HOLDTIME', 'EXTHOLD', 'INFOASRC', 'INFOBSRC', 'INFOCSRC' ] check_db_values = {'ABSMODE': ['SSI']} elif dsp_cfg_ver == 3.35 and db_cfg_ver in [3.17, 3.182, 3.185, 3.187]: # ignore new parameters or parameters that normally change # ['VER', 'SSISTRTB', 'SSILDC', 'SSIMSKNB', 'SSIEEPOL', # 'SSIEWPOL', 'SSIOVF'] skip_params = [ 'SSISTRTB', 'SSILDC', 'SSIMSKNB', 'SSIEEPOL', 'SSIEWPOL', 'SSIOVF' ] return self._auto_solved_conflict(dsp_cfg, db_cfg, skip_params) elif dsp_cfg_ver == 3.35 and db_cfg_ver in [ 3.184, 3.192, 3.193, 3.195 ]: # ignore new parameters or parameters that normally change # ['VER','SSISTRTB','SSILDC','SSIMSKNB','SSIEEPOL', 'SSIEWPOL', # 'SSIOVF'] # parameters which value is not backward compatible # ['CATENTRY','ABSMODE','SSIALDC','SSIMSKMSB', # 'SSIEECHK','SSIEEPOS','SSIEWCHK', 'SSIEWPOS','SSISIGNED'] skip_params = [ 'SSISTRTB', 'SSILDC', 'SSIMSKNB', 'SSIEEPOL', 'SSIEWPOL', 'SSIOVF', 'SSIALDC', 'SSIMSKMSB', 'SSIEECHK', 'SSIEEPOS', 'SSIEWCHK', 'SSIEWPOS', 'SSISIGNED' ] check_db_values = {'ABSMODE': ['SSI']} elif dsp_cfg_ver == 3.35 and \ db_cfg_ver in [3.23, 3.25, 3.29, 3.31, 3.33]: # ignore new parameters or parameters that normally change # ['VER', 'SSIOVF'] skip_params = ['SSIOVF'] else: skip_params = [] return self._auto_solved_conflict(dsp_cfg, db_cfg, skip_params, check_db_values)
class Stats(Model): """ Stats collection! """ __storm_table__ = 'stats' content = Pickle() #_*_# References tracking below #_*_# Receiver.user = Reference(Receiver.user_id, User.id) Receiver.internaltips = ReferenceSet(Receiver.id, ReceiverInternalTip.receiver_id, ReceiverInternalTip.internaltip_id, InternalTip.id) InternalTip.receivers = ReferenceSet(InternalTip.id, ReceiverInternalTip.internaltip_id, ReceiverInternalTip.receiver_id, Receiver.id) InternalTip.context = Reference(InternalTip.context_id, Context.id) InternalTip.comments = ReferenceSet(InternalTip.id, Comment.internaltip_id) InternalTip.receivertips = ReferenceSet(InternalTip.id, ReceiverTip.internaltip_id) InternalTip.internalfiles = ReferenceSet(InternalTip.id,
class Thread(Storm): """ A thread of archived email, from a mailing-list. It is identified by both the list name and the thread id. """ __storm_table__ = "thread" __storm_primary__ = "list_name", "thread_id" list_name = Unicode() thread_id = Unicode() date_active = DateTime() category_id = Int() emails = ReferenceSet((list_name, thread_id), (Email.list_name, Email.thread_id), order_by=Email.date) emails_by_reply = ReferenceSet((list_name, thread_id), (Email.list_name, Email.thread_id), order_by=Email.thread_order) category_obj = Reference(category_id, "Category.id") _starting_email = None def __init__(self, list_name, thread_id, date_active=None): self.list_name = unicode(list_name) self.thread_id = unicode(thread_id) self.date_active = date_active @property def _starting_email_req(self): """ Returns the request to get the starting email. If there are no results with in_reply_to IS NULL, then it's probably a partial import and we don't have the real first email. In this case, use the date. """ return self.emails.order_by(Email.in_reply_to != None, Email.date) @property def starting_email(self): """Return (and cache) the email starting this thread""" if self._starting_email is None: self._starting_email = self._starting_email_req.first() return self._starting_email @property def last_email(self): return self.emails.order_by(Desc(Email.date)).first() @property def subject(self): """Return the subject of this thread""" if self._starting_email is not None: return self.starting_email.subject else: # Don't get the whole message if it's not cached yet (useful for # HyperKitty's thread view). return self._starting_email_req.values(Email.subject).next() @property def participants(self): """Set of email senders in this thread""" p = [] for sender in self.emails.find().config( distinct=True).order_by().values(Email.sender_name, Email.sender_email): p.append(sender) return p @property def email_ids(self): return list(self.emails.find().order_by().values(Email.message_id)) @property def email_id_hashes(self): return list(self.emails.find().order_by().values( Email.message_id_hash)) def __len__(self): return self.emails.count() def replies_after(self, date): return self.emails.find(Email.date > date) def _get_category(self): if not self.category_id: return None return self.category_obj.name def _set_category(self, name): if not name: self.category_id = None return store = Store.of(self) category = store.find(Category, Category.name == name).one() if category is None: category = Category(name) store.add(category) store.flush() self.category_id = category.id category = property(_get_category, _set_category) def __storm_pre_flush__(self): """Auto-set the active date from the last email in thread""" if self.date_active is not None: return email_dates = list( self.emails.order_by(Desc(Email.date)).config(limit=1).values( Email.date)) if email_dates: self.date_active = email_dates[0] else: self.date_active = datetime.datetime.now()
class SourcePackageRecipeData(Storm): """The database representation of a BaseRecipeBranch from bzr-builder. This is referenced from the SourcePackageRecipe table as the 'recipe_data' column and from the SourcePackageRecipeBuild table as the 'manifest' column. """ __storm_table__ = "SourcePackageRecipeData" id = Int(primary=True) base_branch_id = Int(name='base_branch', allow_none=True) base_branch = Reference(base_branch_id, 'Branch.id') base_git_repository_id = Int(name='base_git_repository', allow_none=True) base_git_repository = Reference(base_git_repository_id, 'GitRepository.id') @property def base(self): if self.base_branch is not None: return self.base_branch else: assert self.base_git_repository is not None return self.base_git_repository @base.setter def base(self, value): if IGitRepository.providedBy(value): self.base_git_repository = value self.base_branch = None elif IBranch.providedBy(value): self.base_branch = value self.base_git_repository = None else: raise AssertionError("Unsupported base: %r" % (value, )) recipe_format = Unicode(allow_none=False) deb_version_template = Unicode(allow_none=True) revspec = Unicode(allow_none=True) instructions = ReferenceSet( id, _SourcePackageRecipeDataInstruction.recipe_data_id, order_by=_SourcePackageRecipeDataInstruction.line_number) sourcepackage_recipe_id = Int(name='sourcepackage_recipe', allow_none=True) sourcepackage_recipe = Reference(sourcepackage_recipe_id, 'SourcePackageRecipe.id') sourcepackage_recipe_build_id = Int(name='sourcepackage_recipe_build', allow_none=True) sourcepackage_recipe_build = Reference(sourcepackage_recipe_build_id, 'SourcePackageRecipeBuild.id') @staticmethod def getParsedRecipe(recipe_text): """See `IRecipeBranchSource`.""" # We're using bzr-builder to parse the recipe text. While the # formats are mostly compatible, the header line must say # "bzr-builder" even though git-build-recipe also supports its own # name there. recipe_text, git_substitutions = re.subn(r"^(#\s*)git-build-recipe", r"\1bzr-builder", recipe_text) recipe_branch_type = (RecipeBranchType.GIT if git_substitutions else RecipeBranchType.BZR) parser = RecipeParser(recipe_text) recipe_branch = parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) return recipe_branch, recipe_branch_type @staticmethod def findRecipes(branch_or_repository, revspecs=None): """Find recipes for a given branch or repository. :param branch_or_repository: The branch or repository to search for. :param revspecs: If not None, return only recipes whose `revspec` is in this sequence. :return: a collection of `ISourcePackageRecipe`s. """ from lp.code.model.sourcepackagerecipe import SourcePackageRecipe store = Store.of(branch_or_repository) if IGitRepository.providedBy(branch_or_repository): data_clause = (SourcePackageRecipeData.base_git_repository == branch_or_repository) insn_clause = (_SourcePackageRecipeDataInstruction.git_repository == branch_or_repository) elif IBranch.providedBy(branch_or_repository): data_clause = ( SourcePackageRecipeData.base_branch == branch_or_repository) insn_clause = (_SourcePackageRecipeDataInstruction.branch == branch_or_repository) else: raise AssertionError("Unsupported source: %r" % (branch_or_repository, )) if revspecs is not None: concrete_revspecs = [ revspec for revspec in revspecs if revspec is not None ] data_revspec_clause = In(SourcePackageRecipeData.revspec, concrete_revspecs) insn_revspec_clause = In( _SourcePackageRecipeDataInstruction.revspec, concrete_revspecs) if None in revspecs: data_revspec_clause = Or( data_revspec_clause, SourcePackageRecipeData.revspec == None) insn_revspec_clause = Or( insn_revspec_clause, _SourcePackageRecipeDataInstruction.revspec == None) data_clause = And(data_clause, data_revspec_clause) insn_clause = And(insn_clause, insn_revspec_clause) return store.find( SourcePackageRecipe, SourcePackageRecipe.id.is_in( Union( Select(SourcePackageRecipeData.sourcepackage_recipe_id, data_clause), Select( SourcePackageRecipeData.sourcepackage_recipe_id, And( _SourcePackageRecipeDataInstruction.recipe_data_id == SourcePackageRecipeData.id, insn_clause))))) @classmethod def createManifestFromText(cls, text, sourcepackage_recipe_build): """See `ISourcePackageRecipeDataSource`.""" parsed, recipe_branch_type = cls.getParsedRecipe(text) return cls(parsed, recipe_branch_type, sourcepackage_recipe_build=sourcepackage_recipe_build) def getRecipe(self): """The BaseRecipeBranch version of the recipe.""" base_branch = BaseRecipeBranch(self.base.identity, self.deb_version_template, self.recipe_format, self.revspec) insn_stack = [] for insn in self.instructions: while insn_stack and \ insn_stack[-1]['insn'] != insn.parent_instruction: insn_stack.pop() if insn_stack: target_branch = insn_stack[-1]['recipe_branch'] else: target_branch = base_branch recipe_branch = insn.appendToRecipe(target_branch) insn_stack.append(dict(insn=insn, recipe_branch=recipe_branch)) return base_branch def _scanInstructions(self, base, recipe_branch): """Check the recipe_branch doesn't use 'run' and look up the branches. We do all the lookups before we start constructing database objects to avoid flushing half-constructed objects to the database. :return: A map ``{branch_url: db_branch}``. """ r = {} if IGitRepository.providedBy(base): lookup = getUtility(IGitLookup) missing_error = NoSuchGitRepository private_error = PrivateGitRepositoryRecipe else: lookup = getUtility(IBranchLookup) missing_error = NoSuchBranch private_error = PrivateBranchRecipe for instruction in recipe_branch.child_branches: db_branch = lookup.getByUrl(instruction.recipe_branch.url) if db_branch is None: raise missing_error(instruction.recipe_branch.url) if db_branch.private: raise private_error(db_branch) r[instruction.recipe_branch.url] = db_branch r.update(self._scanInstructions(base, instruction.recipe_branch)) return r def _recordInstructions(self, recipe_branch, parent_insn, branch_map, line_number=0): """Build _SourcePackageRecipeDataInstructions for the recipe_branch. """ for instruction in recipe_branch.child_branches: nest_path = instruction.nest_path source_directory = None if isinstance(instruction, MergeInstruction): type = InstructionType.MERGE elif isinstance(instruction, NestInstruction): type = InstructionType.NEST elif isinstance(instruction, NestPartInstruction): type = InstructionType.NEST_PART nest_path = instruction.target_subdir source_directory = instruction.subpath else: # Unsupported instructions should have been filtered out by # _scanInstructions; if we get surprised here, that's a bug. raise AssertionError("Unsupported instruction %r" % instruction) line_number += 1 comment = None db_branch_or_repository = branch_map[instruction.recipe_branch.url] insn = _SourcePackageRecipeDataInstruction( instruction.recipe_branch.name, type, comment, line_number, db_branch_or_repository, instruction.recipe_branch.revspec, nest_path, self, parent_insn, source_directory) line_number = self._recordInstructions(instruction.recipe_branch, insn, branch_map, line_number) return line_number def setRecipe(self, builder_recipe, recipe_branch_type): """Convert the BaseRecipeBranch `builder_recipe` to the db form.""" clear_property_cache(self) if builder_recipe.format > MAX_RECIPE_FORMAT: raise TooNewRecipeFormat(builder_recipe.format, MAX_RECIPE_FORMAT) if recipe_branch_type == RecipeBranchType.BZR: base = getUtility(IBranchLookup).getByUrl(builder_recipe.url) if base is None: raise NoSuchBranch(builder_recipe.url) elif base.private: raise PrivateBranchRecipe(base) elif recipe_branch_type == RecipeBranchType.GIT: base = getUtility(IGitLookup).getByUrl(builder_recipe.url) if base is None: raise NoSuchGitRepository(builder_recipe.url) elif base.private: raise PrivateGitRepositoryRecipe(base) else: raise AssertionError('Unknown recipe_branch_type: %r' % recipe_branch_type) branch_map = self._scanInstructions(base, builder_recipe) # If this object hasn't been added to a store yet, there can't be any # instructions linking to us yet. if Store.of(self) is not None: self.instructions.find().remove() if builder_recipe.revspec is not None: self.revspec = unicode(builder_recipe.revspec) else: self.revspec = None self._recordInstructions(builder_recipe, parent_insn=None, branch_map=branch_map) self.base = base if builder_recipe.deb_version is None: self.deb_version_template = None else: self.deb_version_template = unicode(builder_recipe.deb_version) self.recipe_format = unicode(builder_recipe.format) def __init__(self, recipe, recipe_branch_type, sourcepackage_recipe=None, sourcepackage_recipe_build=None): """Initialize from the bzr-builder recipe and link it to a db recipe. """ super(SourcePackageRecipeData, self).__init__() self.setRecipe(recipe, recipe_branch_type) self.sourcepackage_recipe = sourcepackage_recipe self.sourcepackage_recipe_build = sourcepackage_recipe_build @staticmethod def preLoadReferencedBranches(sourcepackagerecipedatas): # Circular imports. from lp.code.model.branchcollection import GenericBranchCollection from lp.code.model.gitcollection import GenericGitCollection # Load the related Branch, _SourcePackageRecipeDataInstruction. base_branches = load_related(Branch, sourcepackagerecipedatas, ['base_branch_id']) base_repositories = load_related(GitRepository, sourcepackagerecipedatas, ['base_git_repository_id']) sprd_instructions = load_referencing( _SourcePackageRecipeDataInstruction, sourcepackagerecipedatas, ['recipe_data_id']) sub_branches = load_related(Branch, sprd_instructions, ['branch_id']) sub_repositories = load_related(GitRepository, sprd_instructions, ['git_repository_id']) all_branches = base_branches + sub_branches all_repositories = base_repositories + sub_repositories # Pre-load branches'/repositories' data. if all_branches: GenericBranchCollection.preloadDataForBranches(all_branches) if all_repositories: GenericGitCollection.preloadDataForRepositories(all_repositories) # Store the pre-fetched objects on the sourcepackagerecipedatas # objects. branch_to_recipe_data = { instr.branch_id: instr.recipe_data_id for instr in sprd_instructions if instr.branch_id is not None } repository_to_recipe_data = { instr.git_repository_id: instr.recipe_data_id for instr in sprd_instructions if instr.git_repository_id is not None } caches = { sprd.id: [sprd, get_property_cache(sprd)] for sprd in sourcepackagerecipedatas } for _, [sprd, cache] in caches.items(): cache._referenced_branches = [sprd.base] for branch in sub_branches: cache = caches[branch_to_recipe_data[branch.id]][1] cache._referenced_branches.append(branch) for repository in sub_repositories: cache = caches[repository_to_recipe_data[repository.id]][1] cache._referenced_branches.append(repository) def getReferencedBranches(self): """Return an iterator of the Branch/GitRepository objects referenced by this recipe. """ return self._referenced_branches @cachedproperty def _referenced_branches(self): referenced_branches = [self.base] sub_branches = IStore(self).find( Branch, _SourcePackageRecipeDataInstruction.recipe_data == self, Branch.id == _SourcePackageRecipeDataInstruction.branch_id) referenced_branches.extend(sub_branches) sub_repositories = IStore(self).find( GitRepository, _SourcePackageRecipeDataInstruction.recipe_data == self, GitRepository.id == _SourcePackageRecipeDataInstruction.git_repository_id) referenced_branches.extend(sub_repositories) return referenced_branches
class Board(Storm): """ I represent a category of discussion on the forums. @ivar created: Timestamp of my entry in the database @ivar name: Word or phrase that will be used to represent me on the website @ivar description: A brief explanation of what subject matter my Threads will contain @ReferenceSet threads: A storm ReferenceSet object with all of my Threads @ReferenceSet perms: A storm ReferenceSet object with all permissions required for me to be viewed by a User """ __storm_table__ = 'boards' id = Int(primary=True) created = DateTime(default=datetime.datetime.now()) name = Unicode(validator=unicoder) description = Unicode(validator=unicoder) threads = ReferenceSet(id, "Thread.board_id") perms = ReferenceSet(id, "_LinkBoardPerms.board_id", "_LinkBoardPerms.perm_id", "BoardPerms.id") def getJSON(self, extra=False): """ Get a JSON representation of my attributes and other useful things @param extra: Determines if additional data from other classes will be added """ return_json = { 'id': self.id, 'created': self.created.strftime('%Y-%m-%d %H:%M:%S'), 'name': self.name, 'description': self.description, } if extra: #Other useful things threads = self.threads thread_count = threads.count() messages_count = 0 for thread in threads: messages = thread.messages.count() messages_count = messages_count + messages extra_data = {'topics': thread_count, 'messages': messages_count} return_json.update(extra_data) return return_json def addPermByName(self, perm_name): """ Tie me to a BoardPerm by it's name. @param perm_name: The 'name' value of the BoardPerm to which I'll be joined. """ store = getStore() perm = store.find(BoardPerm, BoardPerm.name == perm_name) self.perms.add(perm) def removePermByName(self, perm_name): """ Remove my ties to a BoardPerm by it's name. @param perm_name: The 'name' value of the BoardPerm to which I'll no longer be tied """ store = getStore() perm = store.find(BoardPerm, BoardPerm.name == perm_name) self.perms.remove(perm) def listPerms(self): """ Return a list of names of BoardPerms to which I am tied @type return: A list of unicodes """ perms = [] for perm in self.perms: perms.append(perm.name) return perms
class FieldAnswerGroup_v_29(ModelWithID): __storm_table__ = 'fieldanswergroup' number = Int(default=0) fieldanswer_id = Unicode() class FieldAnswerGroupFieldAnswer_v_29(Model): __storm_table__ = 'fieldanswergroup_fieldanswer' __storm_primary__ = 'fieldanswergroup_id', 'fieldanswer_id' fieldanswergroup_id = Unicode() fieldanswer_id = Unicode() FieldAnswerGroup_v_29.fieldanswers = ReferenceSet( FieldAnswerGroup_v_29.id, FieldAnswerGroupFieldAnswer_v_29.fieldanswergroup_id, FieldAnswerGroupFieldAnswer_v_29.fieldanswer_id, FieldAnswer_v_29.id) class MigrationScript(MigrationBase): def prologue(self): appdata = load_appdata() steps = appdata['default_questionnaire']['steps'] del appdata['default_questionnaire']['steps'] questionnaire = db_forge_obj(self.store_new, Questionnaire, appdata['default_questionnaire']) for step in steps: f_children = step['children']
context_id = Unicode() receiver_id = Unicode() class ReceiverInternalTip(BaseModel): """ Class used to implement references between Receivers and IntInternalTips """ __storm_table__ = 'receiver_internaltip' __storm_primary__ = 'receiver_id', 'internaltip_id' receiver_id = Unicode() internaltip_id = Unicode() Field.options = ReferenceSet(Field.id, FieldOption.field_id) FieldOption.field = Reference(FieldOption.field_id, Field.id) Context.steps = ReferenceSet(Context.id, Step.context_id) Step.context = Reference(Step.context_id, Context.id) # _*_# References tracking below #_*_# Receiver.user = Reference(Receiver.user_id, User.id) Receiver.internaltips = ReferenceSet(Receiver.id, ReceiverInternalTip.receiver_id, ReceiverInternalTip.internaltip_id, InternalTip.id)
maximum_selectable_receivers = Int() select_all_receivers = Bool() enable_private_messages = Bool() tip_timetolive = Int() last_update = DateTime() name = JSON() description = JSON() receiver_introduction = JSON() can_postpone_expiration = Bool() can_delete_submission = Bool() show_receivers_in_alphabetical_order = Bool() presentation_order = Int() Context_v_20.receivers = ReferenceSet(Context_v_20.id, ReceiverContext.context_id, ReceiverContext.receiver_id, Receiver_v_20.id) class Step_v_20(Model): __storm_table__ = 'step' context_id = Unicode() label = JSON() description = JSON() hint = JSON() number = Int() class Field_v_20(Model): __storm_table__ = 'field' label = JSON()
class SourcePackageRecipeData(Storm): """The database representation of a BaseRecipeBranch from bzr-builder. This is referenced from the SourcePackageRecipe table as the 'recipe_data' column and from the SourcePackageRecipeBuild table as the 'manifest' column. """ __storm_table__ = "SourcePackageRecipeData" id = Int(primary=True) base_branch_id = Int(name='base_branch', allow_none=False) base_branch = Reference(base_branch_id, 'Branch.id') recipe_format = Unicode(allow_none=False) deb_version_template = Unicode(allow_none=True) revspec = Unicode(allow_none=True) instructions = ReferenceSet( id, _SourcePackageRecipeDataInstruction.recipe_data_id, order_by=_SourcePackageRecipeDataInstruction.line_number) sourcepackage_recipe_id = Int( name='sourcepackage_recipe', allow_none=True) sourcepackage_recipe = Reference( sourcepackage_recipe_id, 'SourcePackageRecipe.id') sourcepackage_recipe_build_id = Int( name='sourcepackage_recipe_build', allow_none=True) sourcepackage_recipe_build = Reference( sourcepackage_recipe_build_id, 'SourcePackageRecipeBuild.id') @staticmethod def getParsedRecipe(recipe_text): parser = RecipeParser(recipe_text) return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) @staticmethod def findRecipes(branch): from lp.code.model.sourcepackagerecipe import SourcePackageRecipe store = Store.of(branch) return store.find( SourcePackageRecipe, SourcePackageRecipe.id.is_in(Union( Select( SourcePackageRecipeData.sourcepackage_recipe_id, SourcePackageRecipeData.base_branch == branch), Select( SourcePackageRecipeData.sourcepackage_recipe_id, And( _SourcePackageRecipeDataInstruction.recipe_data_id == SourcePackageRecipeData.id, _SourcePackageRecipeDataInstruction.branch == branch) ) )) ) @classmethod def createManifestFromText(cls, text, sourcepackage_recipe_build): """Create a manifest for the specified build. :param text: The text of the recipe to create a manifest for. :param sourcepackage_recipe_build: The build to associate the manifest with. :return: an instance of SourcePackageRecipeData. """ parsed = cls.getParsedRecipe(text) return cls( parsed, sourcepackage_recipe_build=sourcepackage_recipe_build) def getRecipe(self): """The BaseRecipeBranch version of the recipe.""" base_branch = BaseRecipeBranch( self.base_branch.bzr_identity, self.deb_version_template, self.recipe_format, self.revspec) insn_stack = [] for insn in self.instructions: while insn_stack and \ insn_stack[-1]['insn'] != insn.parent_instruction: insn_stack.pop() if insn_stack: target_branch = insn_stack[-1]['recipe_branch'] else: target_branch = base_branch recipe_branch = insn.appendToRecipe(target_branch) insn_stack.append( dict(insn=insn, recipe_branch=recipe_branch)) return base_branch def _scanInstructions(self, recipe_branch): """Check the recipe_branch doesn't use 'run' and look up the branches. We do all the lookups before we start constructing database objects to avoid flushing half-constructed objects to the database. :return: A map ``{branch_url: db_branch}``. """ r = {} for instruction in recipe_branch.child_branches: db_branch = getUtility(IBranchLookup).getByUrl( instruction.recipe_branch.url) if db_branch is None: raise NoSuchBranch(instruction.recipe_branch.url) if db_branch.private: raise PrivateBranchRecipe(db_branch) r[instruction.recipe_branch.url] = db_branch r.update(self._scanInstructions(instruction.recipe_branch)) return r def _recordInstructions(self, recipe_branch, parent_insn, branch_map, line_number=0): """Build _SourcePackageRecipeDataInstructions for the recipe_branch. """ for instruction in recipe_branch.child_branches: nest_path = instruction.nest_path source_directory = None if isinstance(instruction, MergeInstruction): type = InstructionType.MERGE elif isinstance(instruction, NestInstruction): type = InstructionType.NEST elif isinstance(instruction, NestPartInstruction): type = InstructionType.NEST_PART nest_path = instruction.target_subdir source_directory = instruction.subpath else: # Unsupported instructions should have been filtered out by # _scanInstructions; if we get surprised here, that's a bug. raise AssertionError( "Unsupported instruction %r" % instruction) line_number += 1 comment = None db_branch = branch_map[instruction.recipe_branch.url] insn = _SourcePackageRecipeDataInstruction( instruction.recipe_branch.name, type, comment, line_number, db_branch, instruction.recipe_branch.revspec, nest_path, self, parent_insn, source_directory) line_number = self._recordInstructions( instruction.recipe_branch, insn, branch_map, line_number) return line_number def setRecipe(self, builder_recipe): """Convert the BaseRecipeBranch `builder_recipe` to the db form.""" clear_property_cache(self) if builder_recipe.format > MAX_RECIPE_FORMAT: raise TooNewRecipeFormat(builder_recipe.format, MAX_RECIPE_FORMAT) branch_map = self._scanInstructions(builder_recipe) # If this object hasn't been added to a store yet, there can't be any # instructions linking to us yet. if Store.of(self) is not None: self.instructions.find().remove() branch_lookup = getUtility(IBranchLookup) base_branch = branch_lookup.getByUrl(builder_recipe.url) if base_branch is None: raise NoSuchBranch(builder_recipe.url) if base_branch.private: raise PrivateBranchRecipe(base_branch) if builder_recipe.revspec is not None: self.revspec = unicode(builder_recipe.revspec) else: self.revspec = None self._recordInstructions( builder_recipe, parent_insn=None, branch_map=branch_map) self.base_branch = base_branch if builder_recipe.deb_version is None: self.deb_version_template = None else: self.deb_version_template = unicode(builder_recipe.deb_version) self.recipe_format = unicode(builder_recipe.format) def __init__(self, recipe, sourcepackage_recipe=None, sourcepackage_recipe_build=None): """Initialize from the bzr-builder recipe and link it to a db recipe. """ super(SourcePackageRecipeData, self).__init__() self.setRecipe(recipe) self.sourcepackage_recipe = sourcepackage_recipe self.sourcepackage_recipe_build = sourcepackage_recipe_build @staticmethod def preLoadReferencedBranches(sourcepackagerecipedatas): # Load the related Branch, _SourcePackageRecipeDataInstruction. load_related( Branch, sourcepackagerecipedatas, ['base_branch_id']) sprd_instructions = load_referencing( _SourcePackageRecipeDataInstruction, sourcepackagerecipedatas, ['recipe_data_id']) sub_branches = load_related( Branch, sprd_instructions, ['branch_id']) # Store the pre-fetched objects on the sourcepackagerecipedatas # objects. branch_to_recipe_data = dict([ (instr.branch_id, instr.recipe_data_id) for instr in sprd_instructions]) caches = dict((sprd.id, [sprd, get_property_cache(sprd)]) for sprd in sourcepackagerecipedatas) for unused, [sprd, cache] in caches.items(): cache._referenced_branches = [sprd.base_branch] for recipe_data_id, branches in groupby( sub_branches, lambda branch: branch_to_recipe_data[branch.id]): cache = caches[recipe_data_id][1] cache._referenced_branches.extend(list(branches)) def getReferencedBranches(self): """Return an iterator of the Branch objects referenced by this recipe. """ return self._referenced_branches @cachedproperty def _referenced_branches(self): referenced_branches = [self.base_branch] sub_branches = IStore(self).find( Branch, _SourcePackageRecipeDataInstruction.recipe_data == self, Branch.id == _SourcePackageRecipeDataInstruction.branch_id) referenced_branches.extend(sub_branches) return referenced_branches
""" lang = Unicode(primary=True, validator=shorttext_v) texts = JSON() unicode_keys = ['lang'] json_keys = ['texts'] Context.picture = Reference(Context.img_id, File.id) User.picture = Reference(User.img_id, File.id) Field.fieldgroup = Reference(Field.fieldgroup_id, Field.id) Field.step = Reference(Field.step_id, Step.id) Field.template = Reference(Field.template_id, Field.id) Field.options = ReferenceSet(Field.id, FieldOption.field_id) Field.children = ReferenceSet(Field.id, Field.fieldgroup_id) Field.attrs = ReferenceSet(Field.id, FieldAttr.field_id) Field.triggered_by_options = ReferenceSet(Field.id, FieldOption.trigger_field) Step.triggered_by_options = ReferenceSet(Step.id, FieldOption.trigger_step) FieldAnswer.groups = ReferenceSet(FieldAnswer.id, FieldAnswerGroup.fieldanswer_id) FieldAnswerGroup.fieldanswers = ReferenceSet(FieldAnswerGroup.id, FieldAnswer.fieldanswergroup_id) Step.children = ReferenceSet(Step.id, Field.step_id)
preview = Bool() stats_enabled = Bool() is_template = Bool() x = Int() y = Int() type = Unicode() class FieldOption_v_22(Model): __storm_table__ = 'fieldoption' field_id = Unicode() presentation_order = Int() attrs = JSON() Field_v_22.options = ReferenceSet(Field_v_22.id, FieldOption_v_22.field_id) class InternalTip_v_22(Model): __storm_table__ = 'internaltip' creation_date = DateTime() context_id = Unicode() wb_steps = JSON() preview = JSON() progressive = Int() tor2web = Bool() expiration_date = DateTime() last_activity = DateTime() new = Int()