class AbstractCard_v6(SQLObject): """Table used to upgrade AbstractCard from v6""" class sqlmeta: """meta class used to set the correct table""" table = AbstractCard.sqlmeta.table cacheValues = False canonicalName = UnicodeCol(alternateID=True, length=MAX_ID_LENGTH) name = UnicodeCol() text = UnicodeCol() search_text = UnicodeCol(default="") group = IntCol(default=None, dbName='grp') capacity = IntCol(default=None) cost = IntCol(default=None) life = IntCol(default=None) costtype = EnumCol(enumValues=['pool', 'blood', 'conviction', None], default=None) level = EnumCol(enumValues=['advanced', None], default=None) # Most of these names are singular when they should be plural # since they refer to lists. We've decided to live with the # inconsistency for old columns but do the right thing for new # ones. discipline = RelatedJoin('DisciplinePair', intermediateTable='abs_discipline_pair_map', createRelatedTable=False) rarity = RelatedJoin('RarityPair', intermediateTable='abs_rarity_pair_map', createRelatedTable=False) clan = RelatedJoin('Clan', intermediateTable='abs_clan_map', createRelatedTable=False) cardtype = RelatedJoin('CardType', intermediateTable='abs_type_map', createRelatedTable=False) sect = RelatedJoin('Sect', intermediateTable='abs_sect_map', createRelatedTable=False) title = RelatedJoin('Title', intermediateTable='abs_title_map', createRelatedTable=False) creed = RelatedJoin('Creed', intermediateTable='abs_creed_map', createRelatedTable=False) virtue = RelatedJoin('Virtue', intermediateTable='abs_virtue_map', createRelatedTable=False) rulings = RelatedJoin('Ruling', intermediateTable='abs_ruling_map', createRelatedTable=False) artists = RelatedJoin('Artist', intermediateTable='abs_artist_map', createRelatedTable=False) keywords = RelatedJoin('Keyword', intermediateTable='abs_keyword_map', createRelatedTable=False) physicalCards = MultipleJoin('PhysicalCard')
class Task(SQLObject): title = UnicodeCol() creationDate = DateTimeCol(notNone=True) dueDate = DateTimeCol(default=None) doneDate = DateTimeCol(default=None) description = UnicodeCol(default="", notNone=True) urgency = IntCol(default=0, notNone=True) status = EnumCol(enumValues=['new', 'started', 'done']) project = ForeignKey("Project") keywords = RelatedJoin("Keyword", createRelatedTable=False, intermediateTable="task_keyword", joinColumn="task_id", otherColumn="keyword_id") recurrence = ForeignKey("Recurrence", default=None) def setKeywordDict(self, dct): """ Defines keywords of a task. Dict is of the form: keywordName => value """ for taskKeyword in TaskKeyword.selectBy(task=self): taskKeyword.destroySelf() for name, value in dct.items(): keyword = Keyword.selectBy(name=name)[0] TaskKeyword(task=self, keyword=keyword, value=value) def getKeywordDict(self): """ Returns all keywords of a task as a dict of the form: keywordName => value """ dct = {} for keyword in TaskKeyword.selectBy(task=self): dct[keyword.keyword.name] = keyword.value return dct def getKeywordsAsString(self): """ Returns all keywords as a string like "key1=value1, key2=value2..." """ return ", ".join( list(("%s=%s" % k for k in self.getKeywordDict().items()))) def getUserKeywordsNameAsString(self): """ Returns all keywords keys as a string like "key1, key2, key3...". Internal keywords (starting with _) are ignored. """ keywords = [ k for k in self.getKeywordDict().keys() if not k.startswith("_") ] keywords.sort() if keywords: return ", ".join(keywords) else: return ""
class DisciplinePair(SQLObject): tableversion = 1 discipline = ForeignKey('Discipline') level = EnumCol(enumValues=['inferior', 'superior']) disciplineLevelIndex = DatabaseIndex(discipline, level, unique=True) cards = RelatedJoin('SutekhAbstractCard', intermediateTable='abs_discipline_pair_map', otherColumn="abstract_card_id", createRelatedTable=False)
class SutekhAbstractCard(AbstractCard): """The abstract card specialised to the needs of VtES.""" _inheritable = False tableversion = 1 search_text = UnicodeCol(default="") group = IntCol(default=None, dbName='grp') capacity = IntCol(default=None) cost = IntCol(default=None) life = IntCol(default=None) costtype = EnumCol(enumValues=['pool', 'blood', 'conviction', None], default=None) level = EnumCol(enumValues=['advanced', None], default=None) # Most of these names are singular when they should be plural # since they refer to lists. We've decided to live with the # inconsistency for old columns but do the right thing for new # ones. discipline = CachedRelatedJoin('DisciplinePair', intermediateTable='abs_discipline_pair_map', joinColumn="abstract_card_id", createRelatedTable=False) clan = CachedRelatedJoin('Clan', joinColumn="abstract_card_id", intermediateTable='abs_clan_map', createRelatedTable=False) sect = CachedRelatedJoin('Sect', intermediateTable='abs_sect_map', joinColumn="abstract_card_id", createRelatedTable=False) title = CachedRelatedJoin('Title', intermediateTable='abs_title_map', joinColumn="abstract_card_id", createRelatedTable=False) creed = CachedRelatedJoin('Creed', intermediateTable='abs_creed_map', joinColumn="abstract_card_id", createRelatedTable=False) virtue = CachedRelatedJoin('Virtue', intermediateTable='abs_virtue_map', joinColumn="abstract_card_id", createRelatedTable=False)
class Role(ICTVObject): role_id = DatabaseIndex('user', 'channel', unique=True) user = ForeignKey('User', cascade=True) channel = ForeignKey('PluginChannel', cascade=True) permission_level = EnumCol( enumValues=['channel_contributor', 'channel_administrator']) def _get_permission_level(self): """ Magic method to hide enums from SQLObject into a more elegant abstraction. """ return UserPermissions[self._SO_get_permission_level()] def _set_permission_level(self, value): """ Magic method to hide enums from SQLObject into a more elegant abstraction. """ self._SO_set_permission_level( UserPermissions.get_permission_string(value))
class Dataset(SQLObject): class sqlmeta: table = 'wh_dataset' whitehall_id = IntCol(alternateID=True) stats_type = EnumCol(enumValues=[ 'Official Statistics', 'National Statistics', 'Statistical data set', None ], default=None) title = StringCol() url = StringCol(alternateID=True, length=255) orgs = RelatedJoin('Organisation') publication_date = DateTimeCol() government_name = StringCol() collections = RelatedJoin('Collection')
class Plugin(ICTVObject): name = StringCol(notNone=True, alternateID=True) description = StringCol(default=None) version = IntCol(notNone=True, default=0) activated = EnumCol(notNone=True, enumValues=['yes', 'no', 'notfound']) webapp = BoolCol(notNone=True, default=False) static = BoolCol(notNone=True, default=False) channels_params = JSONCol( notNone=True, default={} ) # The type and default param's values needed by every plugin instance channels = SQLMultipleJoin('PluginChannel') params_access_rights = SQLMultipleJoin('PluginParamAccessRights') cache_activated_default = BoolCol(default=True) cache_validity_default = IntCol(default=60) keep_noncomplying_capsules_default = BoolCol(default=False) def _get_channels_number(self): """ Return the number of channels instantiated with this plugin. """ return self.channels.count() def _get_screens_number(self): """ Return the number of screens that are subscribed to channels of this plugin. """ plugin_channels = PluginChannel.select().filter( PluginChannel.q.plugin == self) screens = set(plugin_channels.throughTo.subscriptions.throughTo.screen. distinct()) bundles = set(c for c in ChannelBundle.select() if any(bc.plugin == self for bc in c.flatten())) for b in bundles: screens |= set(Subscription.select().filter( Subscription.q.channel == b).throughTo.screen.distinct()) return len(screens) def _get_package_path(self): """ Returns the path to the package of this plugin. """ try: m = importlib.import_module('ictv.plugins.' + self.name) return m.__path__[0] except ImportError: return None @classmethod def update_plugins(cls, dirs): """ Takes the list of the plugins directories located in ictv/plugins and updates the database if they're not in db :param dirs: The directory listing of ictv/plugins :return: the list of plugins present in updated database """ s = set() plugins_list = [] for p in Plugin.select(): s.add(p.name) if p.name not in dirs: # Plugin exists in database but was not found in the plugins directory p.activated = 'notfound' else: path = os.path.join(p.package_path, 'config.yaml') if os.path.isfile(path): # Plugin is considered to be found if p.activated == 'notfound': p.activated = 'no' with open(path, 'r') as f: config = yaml.load(f, Loader=yamlordereddictloader.Loader) p.webapp = config['plugin']['webapp'] p.static = config['plugin']['static'] p.description = config['plugin'].get( 'description', None) if 'channels_params' in config: # The plugin has channel specific parameters that can be changed from channel to channel order = 0 for k, v in config['channels_params'].items(): p.channels_params[ k] = v # Sets the parameter to its default value if 'order' not in p.channels_params[k]: p.channels_params[k]['order'] = order order += 1 if PluginParamAccessRights.selectBy( plugin=p, name=k).getOne(None) is None: PluginParamAccessRights(plugin=p, name=k) for k in list(p.channels_params): if k not in config['channels_params'].keys(): p.channels_params.pop(k) PluginParamAccessRights.deleteBy(plugin=p, name=k) p.channels_params = p.channels_params # Force SQLObject update else: p.activated = 'notfound' plugins_list.append(p) for p in dirs: if p not in s: # Plugin was not in database, it should be added but not activated plugins_list.append(Plugin(name=p, activated='no')) return plugins_list
class Channel(InheritableSQLObject): name = StringCol(notNone=True, unique=True) description = StringCol(default=None) enabled = BoolCol(notNone=True, default=True) subscription_right = EnumCol(enumValues=['public', 'restricted', 'private']) authorized_subscribers = SQLRelatedJoin('User') secret = StringCol(notNone=True, default=lambda: utils.generate_secret()) subscriptions = SQLMultipleJoin('Subscription') bundles = SQLRelatedJoin('ChannelBundle') def can_subscribe(self, user): """ Return whether this user has sufficient permission to be able to subscribe to this channel or not. """ return self.subscription_right == 'public' or UserPermissions.administrator in user.highest_permission_level or user in self.authorized_subscribers def safe_add_user(self, user): """ Avoid user duplication in channel authorized subscribers. """ if user not in self.authorized_subscribers: self.addUser(user) @classmethod def get_channels_authorized_subscribers_as_json(cls, channels): """ Return the string representation of a dictionary in the form { channel.id: [ user.id, ... ] } """ channels_authorized_subscribers = {} for channel in channels: channels_authorized_subscribers[channel.id] = [u.id for u in channel.authorized_subscribers] return json.dumps(channels_authorized_subscribers) @classmethod def get_visible_channels_of(cls, user): """ Returns the channels that are accessible for the user (public channels or channels with the user specified in authorized_subscribers, or all channels if the user is superadmin) :param user: The user to retrieve the accessible channels. :return: A iterable with the accessible channels (iterable of sqlobjects) """ if UserPermissions.administrator in user.highest_permission_level: return set(Channel.select()) public_channels = set(Channel.selectBy(subscription_right='public')) if UserPermissions.screen_administrator in \ user.highest_permission_level else set() return public_channels | set(Role.selectBy(user=user).throughTo.channel) | set( User.selectBy(id=user.id).throughTo.authorized_channels) @classmethod def get_screens_channels_from(cls, user): """ Return the intersection between 3 sets of channels: all the public channels, all the channel this user is authorized to subscribe and all the channel the screens this user has access are subscribed to. The resulting data type is a set of Channel instances. """ if user.super_admin: return set(Channel.select()) return set(c for c in Channel.selectBy(subscription_right='public')) | \ set(c for c in user.authorized_channels) | \ set(c for c in user.screens.throughTo.subscriptions.throughTo.channel) def get_preview_link(self): """ Returns the secret preview link of this channel. """ return '/preview/channels/' + str(self.id) + '/' + self.secret @abstractmethod def flatten(self, keep_disabled_channels=False): """ Returns all the channels contained in this channel and the channels it contains as an Iterable[Channel]""" @abstractmethod def get_type_name(self): """ Returns a string representing the name of the subtype to be used in the UI for this class. """
class Enum1(SQLObject): cl = EnumCol(enumValues=['a', 'bcd', 'e'])
class EnumUnicode(SQLObject): n = UnicodeCol() cl = EnumCol(enumValues=['a', 'b'])
class EnumWithDefaultOther(SQLObject): cl = EnumCol(enumValues=['a', 'bcd', 'e', None], default='a')
class EnumWithNone(SQLObject): cl = EnumCol(enumValues=['a', 'bcd', 'e', None])
class EnumWithDefaultNone(SQLObject): l = EnumCol(enumValues=['a', 'bcd', 'e', None], default=None)
class Screen(ICTVObject): name = StringCol(notNone=True) building = ForeignKey('Building', notNone=True, cascade=False) location = StringCol(default=None) # A free text field to precise the screen location screen_id = DatabaseIndex('name', 'building', unique=True) owners = SQLRelatedJoin('User') subscriptions = SQLMultipleJoin('Subscription') secret = StringCol(notNone=True, default=utils.generate_secret) macs = SQLMultipleJoin('ScreenMac') last_ip = StringCol(default=None) last_access = DateTimeCol(default=None) shuffle = BoolCol(default=False) comment = StringCol(default=None) show_postit = BoolCol(default=False) show_slide_number = BoolCol(default=False) orientation = EnumCol(enumValues=['Landscape','Portrait'],default='Landscape') @property def subscribed_channels(self): return self.subscriptions.throughTo.channel def subscribe_to(self, user, channel, weight=1): """ Subscribes this screen to the channel. If this screen is already subscribed to the channel by the user, it changes the weight if needed. :param user: The user requesting the subscription. :param channel: The channel to subscribe this screen to. :param weight: The optional positive non null weight to give to the channel by this screen. :return: None """ if weight > 0: sub = Subscription.selectBy(screen=self, channel=channel).getOne(None) if sub is not None: if sub.weight != weight: sub.weight = weight if sub.created_by != user: sub.created_by = user else: Subscription(screen=self, channel=channel, weight=weight, created_by=user) def unsubscribe_from(self, user, channel): """ Deletes the user's subscription from this screen to the channel if one exists, otherwise do nothing. :param user: The user requesting the subscription deletion. :param channel: The channel to unsubscribe this screen from. :return: None """ sub = Subscription.selectBy(screen=self, channel=channel).getOne(None) # TODO: Commit this to user history if sub is not None: sub.destroySelf() def is_subscribed_to(self, channel): return channel in self.subscriptions.throughTo.channel @classmethod def get_visible_screens_of(cls, user): """ Returns the screens that are managed by the user (or all screens for the superadmin) :param user: The user to retrieve the managed screens. :return: A iterable with the managed screens (iterable of sqlobjects) """ if UserPermissions.administrator in user.highest_permission_level: return Screen.select() return user.screens def safe_add_user(self, user): """ Avoid user duplication in screen owners. """ if user not in self.owners: self.addUser(user) def get_view_link(self): """ Returns a relative URL to the screen rendered view. """ return '/screens/%d/view/%s' % (self.id, self.secret) def get_client_link(self): """ Returns a relative URL to web-based client for this screen. """ return '/screens/%d/client/%s' % (self.id, self.secret) def get_macs_string(self): return ';'.join(mac.get_pretty_mac() for mac in self.macs) def get_channels_content(self, app): """ Returns all the capsules provided by the channels of this screen as an Iterable[PluginCapsule] ignoring channel duplicates """ screen_capsules_iterables = [] already_added_channels = set() plugin_manager = app.plugin_manager def add_content(channel): for c in channel.flatten(): # do not add duplicates if c.id not in already_added_channels: screen_capsules_iterables.append(plugin_manager.get_plugin_content(c)) already_added_channels.add(c.id) for sub in self.subscriptions: add_content(sub.channel) screen_capsules = list(itertools.chain.from_iterable(screen_capsules_iterables)) if self.shuffle: random.shuffle(screen_capsules) return screen_capsules