class Parameter(db.Model): __tablename__ = 'parameters' key = db.Column(db.String, primary_key=True) value = db.Column(db.String, nullable=False) cache = timestamp = None @classmethod def get_all(cls): items = Parameter.query \ .all() values = {} for item in items: values[item.key] = item.value return values @classmethod def get(cls, key, default='', cast=None): now = time.time() if not cls.cache or cls.timestamp + CACHE_DELAY > now: cls.cache = cls.get_all() cls.timestamp = now value = cls.cache.get(key, default) if cast: value = cast(value) return value @classmethod def clear_cache(cls): cls.cache = cls.timestamp = None @classmethod def set(cls, key, value): items = Parameter.query \ .filter(Parameter.key == key) for item in items: db.session.delete(item) Parameter(key=key, value=value) \ .save() def save(self): db.session.add(self) return self
class Metadata(db.Model): __tablename__ = 'metadata' id = db.Column(db.Integer, primary_key=True) slug = db.Column(db.String, nullable=False) key = db.Column(db.String, nullable=False) value = db.Column(db.String, nullable=False) structural_keys = ('previous', 'next', 'type') @classmethod def get(cls, slug, key=None, ignores=None, structural=True): items = Metadata.query \ .filter(Metadata.slug == slug) \ if key: items = items.filter(Metadata.key == key) \ .all() return [item.value for item in items] if ignores: items = items.filter(Metadata.key.notin_(ignores)) if not structural: items = items.filter(Metadata.key.notin_(Metadata.structural_keys)) return items.order_by(Metadata.key, Metadata.value) \ .all() @classmethod def search(cls, key, value): items = db.session.query(Metadata.slug) \ .filter(Metadata.key == key) \ .filter(Metadata.value == value) \ .order_by(Metadata.key, Metadata.value) return set([item[0] for item in items]) @classmethod def deactivate(cls, slug): for item in Metadata.get(slug): db.session.delete(item) def save(self): db.session.add(self) return self
class Token(db.Model): __tablename__ = 'tokens' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, nullable=False) digest = db.Column(db.String, nullable=False) payload = db.Column(db.String, nullable=False) nonce = db.Column(db.String, nullable=False) expiry = db.Column(db.DateTime, nullable=False) timestamp = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) @classmethod def get(cls, user, digest): return Token.query \ .filter(Token.user_id == user.id) \ .filter(Token.digest == digest) \ .one_or_none() @classmethod def make(cls, user, payload, duration=DEFAULT_DURATION): nonce = secret(size=10) duration = datetime.timedelta(seconds=duration) timestamp = datetime.datetime.utcnow() expiry = timestamp + duration token = Token(user_id=user.id, payload=payload, expiry=expiry, nonce=nonce, timestamp=timestamp) token.digest = token.make_digest(payload) return token def make_digest(self, payload): message = self.nonce + str(self.timestamp) + payload if isinstance(message, six.text_type): message = message.encode('utf-8') return hashlib.sha256(message) \ .hexdigest() def valid(self, payload): expired = datetime.datetime.utcnow() > self.expiry invalid = self.make_digest(self.payload) != self.make_digest(payload) return not expired and not invalid def save(self): db.session.add(self) return self
class Notification(db.Model): __tablename__ = 'notifications' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id')) slug = db.Column(db.String, nullable=False) reason = db.Column(db.String, nullable=False) active = db.Column(db.Boolean, nullable=False, default=True) timestamp = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) user = db.relationship('User') @classmethod def get(cls, id=None, slug=None, user=None, active=None): query = Notification.query \ .order_by(db.desc(Notification.timestamp)) if id is not None: return query.filter(Notification.id == id) \ .one_or_none() if active is not None: query = query.filter(Notification.active == active) if slug is not None: query = query.filter(Notification.slug == slug) if user is not None: query = query.filter(Notification.user_id == user.id) if slug and user: return query.one_or_none() return query @classmethod def exists(cls, slug, user): return Notification.query \ .filter(Notification.slug == slug) \ .filter(Notification.user_id == user.id) \ .count() != 0 @classmethod def add(cls, slug, user, reason): if Notification.exists(slug, user): return notification = Notification(slug=slug, user_id=user.id, reason=reason) notification.save() @classmethod def send(cls, page, current_user): for notification in Notification.get(slug=page.slug, active=True): if notification.user == current_user: continue if not notification.user.notifications: continue notification.user.sendmail('modified', slug=page.slug) @cached_property def ymd(self): return self.timestamp.strftime('%Y-%m-%d') if self.timestamp else None @cached_property def hm(self): return self.timestamp.strftime('%H:%M') if self.timestamp else None @cached_property def ymd_hm(self): return self.timestamp.strftime('%Y-%m-%d %H:%M') if self.timestamp \ else None @cached_property def page(self): return Document.get(self.slug) def save(self): db.session.add(self) return self
class Document(db.Model): __tablename__ = 'documents' id = db.Column(db.Integer, primary_key=True) slug = db.Column(db.String, nullable=False) title = db.Column(db.String, nullable=False) body = db.Column(db.Text, nullable=False) bytes_delta = db.Column(db.Integer, nullable=False, default=0) user_id = db.Column(db.Integer, db.ForeignKey('users.id')) comment = db.Column(db.Text) active = db.Column(db.Boolean, nullable=False, default=False) timestamp = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) user = db.relationship('User') @property def initial(self): return self.title[0].lower() if self.title else self.slug[0] @cached_property def ymd(self): return self.timestamp.strftime('%Y-%m-%d') if self.timestamp else None @cached_property def hm(self): return self.timestamp.strftime('%H:%M') if self.timestamp else None @cached_property def ymd_hm(self): return self.timestamp.strftime('%Y-%m-%d %H:%M') if self.timestamp \ else None @memoized def meta(self, key=None, ignores=None, structural=True): return Metadata.get(self.slug, key, ignores, structural) @memoized def keys(self, ignores=None, structural=True): return set([ item.key for item in self.meta(ignores=ignores, structural=structural) ]) @classmethod def count(cls): return Document.query \ .filter(Document.active == True) \ .count() @classmethod def get(cls, slug, version=None): if version: return Document.query \ .filter(Document.id == version) \ .filter(Document.slug == slug) \ .one_or_none() item = Document.query \ .filter(Document.active == True) \ .filter(Document.slug == slug) \ .one_or_none() if not item: item = Document(slug=slug, body=DEFAULT_BODY % {'slug': slug.replace('_', ' ')}) return item @classmethod def changes(cls): return Document.query \ .order_by(db.desc(Document.timestamp)) @classmethod def titles(cls): return Document.query \ .filter(Document.active == True) \ .order_by(Document.title) @classmethod def search(cls, query=None, filters=None): slugs = None if filters: filters = [Metadata.search(key, value) for key, value in filters] slugs = None for filter in filters: if slugs is None: slugs = filter else: slugs = slugs.intersection(filter) items = Document.query \ .filter(Document.active == True) \ .order_by(Document.slug) if slugs is not None: items = items.filter(Document.slug.in_(slugs)) if query: items = items.filter(Document.body.like('%' + query + '%')) return items.all() @classmethod def facets(cls, pages, ignores=None, all=False): if not pages: return {} ignores = ignores or [] ignores.extend(Metadata.structural_keys) slugs = [page.slug for page in pages] count = db.func.count items = db.session.query(Metadata.key, Metadata.value, count()) \ .filter(Metadata.slug.in_(slugs)) \ .group_by(Metadata.key, Metadata.value) \ .order_by(Metadata.key, Metadata.value) facets = {} for item in items: if item[0] in ignores: continue facets.setdefault(item[0], []) \ .append((item[1], item[2])) return facets @classmethod def deactivate(cls, slug): item = Document.get(slug) if not item.id: return item.active = False db.session.add(item) def save(self): self.active = True db.session.add(self) return self def history(self): return Document.query \ .filter(Document.slug == self.slug) \ .order_by(db.desc(Document.timestamp))
class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) email = db.Column(db.String, nullable=False) validated = db.Column(db.Boolean, nullable=False, default=False) password = db.Column(db.String, nullable=False) admin = db.Column(db.Boolean, nullable=False, default=False) notifications = db.Column(db.Boolean, nullable=False, default=True) timestamp = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) @cached_property def document(self): return Document.get(slugify(self.name)) @cached_property def ymd(self): return self.timestamp.strftime('%Y-%m-%d') if self.timestamp else None @cached_property def hm(self): return self.timestamp.strftime('%H:%M') if self.timestamp else None @cached_property def ymd_hm(self): return self.timestamp.strftime('%Y-%m-%d %H:%M') if self.timestamp \ else None @classmethod def is_first(cls): return User.query.count() == 0 @classmethod def exists(cls, email=None, name=None): if email is not None: return User.query.filter(User.email == email) \ .count() != 0 if name is not None: return User.query.filter(User.name == name) \ .count() != 0 return True @classmethod def get(cls, email=None, id=None): if email: return User.query.filter(User.email == email) \ .one_or_none() elif id: return User.query.filter(User.id == id) \ .one_or_none() else: return User.query def get_id(self): return self.id def set_password(self, password): if isinstance(password, six.text_type): password = password.encode('utf-8') self.password = bcrypt.hashpw(password, bcrypt.gensalt()) return self def check_password(self, password): self_password = self.password if isinstance(password, six.text_type): password = password.encode('utf-8') if isinstance(self_password, six.text_type): self_password = self_password.encode('utf-8') return bcrypt.checkpw(password, self_password) def sendmail(self, template, **kwargs): context = make_context(request, self) sendmail = getattr(tasks, 'send_' + template) if tasks.with_celery(): sendmail = sendmail.delay sendmail(context, **kwargs) def save(self): db.session.add(self) return self