class Revisions(BaseMixin, db.Model): """ Collection of revisions of content. """ __tablename__ = 'revisions' #: All revisions of the content history = db.relationship( ContentRevision, primaryjoin='ContentRevision.parent_id == Revisions.id', order_by=ContentRevision.id.desc, backref=db.backref('parent', cascade='all')) #: Latest published revision published_id = db.Column(db.Integer, db.ForeignKey('content_revision.id'), nullable=True) published = db.relationship(ContentRevision, post_update=True, primaryjoin=published_id == ContentRevision.id) #: Latest draft revision draft_id = db.Column(db.Integer, db.ForeignKey('content_revision.id'), nullable=True) draft = db.relationship(ContentRevision, post_update=True, primaryjoin=draft_id == ContentRevision.id)
class ContentRevision(BaseMixin, db.Model): """ A single revision of any piece of content. """ __tablename__ = 'content_revision' parent_id = db.Column(db.Integer, db.ForeignKey('revisions.id', use_alter=True, name='fk_content_revision_parent_id'), nullable=False) #: Previous revision previous_id = db.Column(db.Integer, db.ForeignKey('content_revision.id'), nullable=True) previous = db.relationship('ContentRevision', remote_side='ContentRevision.id', uselist=False) #: User who made this content revision user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False, default=default_user_id) user = db.relationship(User) #: Title of the current revision title = db.Column(db.Unicode(250), nullable=False) #: Abstract that is shown in summaries. Plain text. description = db.Column(db.UnicodeText, nullable=False, default=u'') #: Page content. Rich text. _content = db.Column('content', db.UnicodeText, nullable=False, default=u'') #: Template with which this page will be rendered template = db.Column(db.Unicode(80), nullable=False, default=u'') def __init__(self, **kwargs): super(ContentRevision, self).__init__(**kwargs) if self.previous: # Copy over content from the previous revision if not self.user: self.user = self.previous.user if not self.title: self.title = self.previous.title if not self.description: self.description = self.previous.description if not self._content: self._content = self.previous._content if not self.template: self.template = self.previous.template @property def content(self): return Markup(self._content) @content.setter def content(self, value): self._content = value content = db.synonym('_content', descriptor=content)
class Participant(BaseMixin, db.Model): __tablename__ = 'participant' #: List that this participant is in participant_list_id = db.Column(None, db.ForeignKey('participant_list.id'), nullable=False) #: Datetime when this participant's record was created upstream datetime = db.Column(db.DateTime, nullable=True) #: Ticket no, the reference key ticket = db.Column(db.Unicode(80), nullable=True, unique=True) #: Participant's name fullname = db.Column(db.Unicode(80), nullable=True) #: Unvalidated email address email = db.Column(db.Unicode(80), nullable=True) #: Unvalidated phone number phone = db.Column(db.Unicode(80), nullable=True) #: Unvalidated Twitter id twitter = db.Column(db.Unicode(80), nullable=True) #: Ticket type, if the registration system had multiple tickets ticket_type = db.Column(db.Unicode(80), nullable=True) #: Job title jobtitle = db.Column(db.Unicode(80), nullable=True) #: Company company = db.Column(db.Unicode(80), nullable=True) #: Participant's city city = db.Column(db.Unicode(80), nullable=True) #: T-shirt size tshirt_size = db.Column(db.Unicode(4), nullable=True) #: Link to the user record user_id = db.Column(None, db.ForeignKey('user.id'), nullable=True) user = db.relationship(User) #: Access key for connecting to the user record (nulled when linked) access_key = db.Column(db.Unicode(44), nullable=True, default=newsecret, unique=True) #: Is listed in the public directory is_listed = db.Column(db.Boolean, default=False, nullable=False) #: Data fields the participant has chosen to reveal in public fields_directory = db.Column(db.Unicode(250), nullable=False, default=u'fullname company') #: Data fields the participant has chosen to reveal via ContactPoint fields_contactpoint = db.Column( db.Unicode(250), nullable=False, default=u'fullname email phone twitter jobtitle company city') participant_list = db.relationship(ParticipantList, backref=db.backref( 'participants', order_by=fullname, cascade='all, delete-orphan')) parent = db.synonym('participant_list')
class Map(NodeMixin, Node): __tablename__ = 'map' items = db.relationship(MapItem, order_by=[MapItem.seq], collection_class=ordering_list('seq'), backref='map', cascade='all, delete-orphan') def as_json(self): result = super(NodeMixin, self).as_json() result['items'] = [{'name': item.name, 'title': item.title, 'url': item.url, 'latitude': item.latitude, 'longitude': item.longitude, 'zoomlevel': item.zoomlevel, 'marker': item.marker} for item in self.items] return result def populate_map(self, items): newitems = [] for itemdata in items: mitem = MapItem.get_or_new(self, name=itemdata['name']) for key in itemdata: setattr(mitem, key, itemdata[key]) newitems.append(mitem) self.items = newitems self.items.reorder() # Since self.items is a relationship, it won't update self.updated_at self.updated_at = datetime.utcnow() def import_from(self, data): super(NodeMixin, self).import_from(data) self.populate_map(data['items']) def get_by_name(self, name): if name is not None: for i in self.items: if i.name == name: return i def prev_to(self, item, items=None): items = items or self.items candidate = None for i in items: if i is item: return candidate else: candidate = i # The given item is not in this list return None def next_to(self, item): # Reverse the list and search with prev_to return self.prev_to(item, items=self.items[::-1])
class ListItem(BaseMixin, db.Model): __tablename__ = 'list_item' list_id = db.Column(None, db.ForeignKey('list.id'), nullable=False) name = db.Column(db.Unicode(80), nullable=True) title = db.Column(db.Unicode(250), nullable=True) url = db.Column(db.Unicode(250), nullable=True) node_id = db.Column(None, db.ForeignKey('node.id'), nullable=True) node = db.relationship(Node, backref=db.backref('lists', cascade='all, delete-orphan')) seq = db.Column(db.Integer, nullable=False) __table_args__ = ( db.UniqueConstraint( 'list_id', 'name'), # name, if present, must be unique in this list db.UniqueConstraint('list_id', 'node_id') # A node can only be in a list once ) @classmethod def get_or_new(cls, list, name=None, node=None): if name is None and node is None: return query = cls.query.filter_by(list=list) if name: query = query.filter_by(name=name) if node: query = query.filter_by(node=node) item = query.first() if item: return item else: item = cls(list=list, name=name, node=node) db.session.add(item) return item
def revisions(cls): return db.relationship(Revisions, uselist=False)
class List(NodeMixin, Node): __tablename__ = 'list' items = db.relationship(ListItem, order_by=[ListItem.seq], collection_class=ordering_list('seq'), backref='list', cascade='all, delete-orphan') def as_json(self): result = super(NodeMixin, self).as_json() result['items'] = [[ item.name, item.title, item.url, '%s/%s' % (item.node.folder.name, item.node.name) if item.node else '' ] for item in self.items] return result def populate_list(self, items): newitems = [] for name, title, url, path in items: name = name or None # Convert blank strings into None litem = None if path: try: foldername, nodename = path.split('/', 1) except ValueError: # No folder specified? Try loading nodes from the current folder lfolder = self.folder nodename = path else: if self.folder.name == foldername: lfolder = self.folder else: lfolder = Folder.query.filter_by( website=self.folder.website, name=foldername).first() if lfolder: lnode = Node.query.filter_by(folder=lfolder, name=nodename).first() if lnode: litem = ListItem.get_or_new(self, node=lnode) # If this item has a name, ensure the name is unique if name is not None: for existing in self.items: if existing is not litem and existing.name == name: existing.name = None litem.name = name litem.title = title litem.url = url if name and litem is None: litem = ListItem.get_or_new(self, name=name) litem.title = title litem.url = url litem.node = None if litem is None: litem = ListItem(name=name, title=title, node=None, url=url) newitems.append(litem) self.items = newitems self.items.reorder() # Since self.items is a relationship, it won't update self.updated_at self.updated_at = datetime.utcnow() def import_from(self, data): super(NodeMixin, self).import_from(data) def import_from_internal(self, data): super(NodeMixin, self).import_from_internal(data) self.populate_list(data['items']) def get_by_node(self, node): if node is not None: for i in self.items: if i.node == node: return i def prev_to(self, item, items=None): items = items or self.items candidate = None for i in items: if i == item: return candidate else: candidate = i # The given node is not in this list return None def next_to(self, item): # Reverse the list and search with prev_to return self.prev_to(item, items=self.items[::-1]) def prev_to_node(self, node, items=None): items = items or self.items candidate = None for i in items: if i.node == node: return candidate else: candidate = i # The given node is not in this list return None def next_to_node(self, node): # Reverse the list and search with prev_to return self.prev_to_node(node, items=self.items[::-1])