from abc import ABCMeta, abstractmethod import datetime import mistune import peewee from typing import Optional import slugify import by.config as c import by.error as e import by.view as v class Model(peewee.Model): class Meta: database = c.db class User(Model): name = peewee.TextField(unique=True) passhash = peewee.TextField() class Contents(Model): source = peewee.TextField() class Page(Model): slug = peewee.TextField(unique=True) title = peewee.TextField() @staticmethod def new_page(title: str, contents: str) -> "Page": p = Page.create( slug=slugify.slugify(title), title=title, ) p.update_page(contents) p.update_references() return p def update_page(self, source: str): now = datetime.datetime.now() c = Contents.create(source=source) r = Revision.create( modified=now, contents=c, page=self, ) def update_references(self): Ref.delete().where(Ref.parent == self) @staticmethod def id_from_slug(slug: str) -> Optional[int]: if (p := Page.get_or_none(Page.slug == slug)): return p.id return None @staticmethod def view_from_slug(slug: str) -> v.Page: query = list(Revision.select() .join(Page, on=(Revision.page == Page.id)) .join(Contents, on=(Revision.contents == Contents.id)) .where(Page.slug == slug) .order_by(-Revision.modified) .limit(1)) if len(query) != 1: raise e.PageNotFound() q = query[0] return v.Page( title=q.page.title, slug=q.page.slug, modified=q.modified, source=q.contents.source, rendered=mistune.html(q.contents.source), ) class Revision(Model): modified = peewee.DateTimeField() contents = peewee.ForeignKeyField(Contents) page = peewee.ForeignKeyField(Page) class Ref(Model): parent = peewee.ForeignKeyField(Page) target = peewee.ForeignKeyField(Page) MODELS = [ User, Page, Ref, Revision, Contents, ] def create_tables(): c.db.create_tables(MODELS, safe=True)