from dataclasses import dataclass
import os
import markdown
import peewee
import typing
import PIL.Image
db = peewee.SqliteDatabase("new.db")
PER_PAGE = 16
def slugify(string):
def process(char):
if char in "- \n\t\f":
return "-"
elif char.isalpha():
return char.lower()
else:
return ""
return "".join(process(c) for c in string)[:40]
class Model(peewee.Model):
class Meta:
database = db
class Category(Model):
name = peewee.TextField()
nicename = peewee.TextField()
@classmethod
def all(klass):
elements = (
{"url": f"/category/{c.name}", "name": c.nicename} for c in klass.select()
)
return {"elements": elements}
class Design(Model):
visible_id = peewee.IntegerField()
title = peewee.TextField()
description = peewee.TextField()
category = peewee.ForeignKeyField(Category, backref="designs")
def rendered(self):
return markdown.markdown(self.description)
def id_str(self):
return f"{self.visible_id:05}"
def slug(self):
return slugify(self.title)
def thumbnail(self):
photo = Photo.select().where(Photo.design == self).limit(1).execute()
if photo:
return photo[0].thumb()
else:
return None
def category_list(self):
categories = Category.select()
return [{"name": c.name, "selected": "selected='selected'" if c == self.category else ""} for c in categories]
def to_json(self):
return {
"id": self.visible_id,
"title": self.title,
"tags": [t.tag_name for t in self.tags],
"categories": self.category_list(),
"description": self.description,
"photos": [{"id": p.id, "image": p.filename} for p in self.photos],
}
def update_with(self, stuff):
# update the category
if 'title' in stuff:
self.title = stuff['title']
if 'category' in stuff:
self.category = Category.get(name=stuff['category'])
if 'contents' in stuff:
self.description = stuff['contents']
if 'tags' in stuff:
old_tags = set((t.tag_name for t in self.tags))
new_tags = set(stuff['tags'])
for to_add in new_tags - old_tags:
Tag.create(tag_name=to_add, design=self)
for to_remove in old_tags - new_tags:
Tag.get(tag_name=to_remove, design=self).delete_instance()
self.save()
@classmethod
def make_new(klass):
new_visible_id = klass.select(peewee.fn.max(klass.visible_id)).scalar() + 1
klass.create(
visible_id=new_visible_id,
title="My Great New Design",
description="A blank canvas awaits...",
category=Category.get(),
)
return new_visible_id
@classmethod
def get_all(klass, page=1):
designs = klass.select().paginate(page, PER_PAGE)
return Paginated.paginate(page, klass.select().count(), designs,)
@classmethod
def get_where(klass, *, tag=None, category=None, page=0):
query = klass.select(klass)
if tag is not None:
query = query.join(Tag).where(Tag.tag_name == tag)
if category is not None:
query = query.switch(klass).join(Category).where(Category.name == category)
query = query.group_by(klass).order_by(klass.id.desc())
designs = query.paginate(page, PER_PAGE)
return Paginated.paginate(page, query.count(), designs,)
class Photo(Model):
filename = peewee.TextField()
design = peewee.ForeignKeyField(Design, backref="photos", null=True)
def thumb(self):
return self.filename[:-4] + "_thumb" + self.filename[-4:]
@classmethod
def get_all(klass, page=1):
size = klass.select().count()
photos = klass.select().paginate(page, PER_PAGE).order_by(klass.filename.desc())
return photos, 1, (size // PER_PAGE) + 1
@classmethod
def upload(klass, upload, design_id):
last_image = klass.select().order_by(klass.filename.desc()).limit(1)
if last_image:
n = int(last_image[0].filename[:5]) + 1
else:
n = 0
ext = upload.filename[upload.filename.rindex(".")+1:]
full_name = f"{n:05}.{ext}"
thumb_name = f"{n:05}_thumb.{ext}"
img = PIL.Image.open(upload.stream)
img.thumbnail((400, 400), PIL.Image.ANTIALIAS)
img.save(os.path.join(os.getcwd(), "static", "photos", full_name))
img.thumbnail((100, 100), PIL.Image.ANTIALIAS)
img.save(os.path.join(os.getcwd(), "static", "photos", thumb_name))
if design_id != -1:
design = Design.get(visible_id=design_id)
else:
design = None
klass.create(
filename=full_name,
design=design,
)
return full_name
class Page(Model):
name = peewee.TextField()
text = peewee.TextField()
title = peewee.TextField()
def rendered(self):
return markdown.markdown(self.text)
class Tag(Model):
tag_name = peewee.TextField()
design = peewee.ForeignKeyField(Design, backref="tags")
@classmethod
def all(klass):
elements = (
{"url": f"/tag/{t.tag_name}", "name": t.tag_name}
for t in klass.select(klass.tag_name).distinct().order_by(klass.tag_name)
)
return {"elements": elements}
class PageRef(typing.NamedTuple):
page: int
def __iter__(self):
return [self]
@dataclass
class Paginated:
next_page: typing.Optional[dict]
prev_page: typing.Optional[dict]
last_page: int
contents: typing.List[Design]
@classmethod
def paginate(
cls, offset: int, total: int, contents: typing.List[Design],
) -> "Paginated":
last_page = (total // PER_PAGE) + 1
next_page = PageRef(page=offset + 1) if offset < last_page else None
prev_page = PageRef(page=offset - 1) if offset > 1 else None
return cls(
next_page=next_page,
prev_page=prev_page,
last_page=last_page,
contents=contents,
)
MODELS = [
Category,
Design,
Photo,
Page,
Tag,
]
# @dataclass
# class Paginated:
# next_page: typing.Optional[dict]
# prev_page: typing.Optional[dict]
# last_page: int
# contents: typing.List[Design]