153 lines
5.0 KiB
Python
153 lines
5.0 KiB
Python
import os
|
|
from pathlib import Path
|
|
from django.db import models
|
|
from django.db.models import signals
|
|
from django.conf import settings
|
|
from django.core.exceptions import ValidationError
|
|
from django.core.files.base import ContentFile
|
|
from django.dispatch import receiver
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from PIL import Image
|
|
from io import BytesIO
|
|
|
|
|
|
def image_upload(instance, filename):
|
|
op_id = instance.op.id if instance.op else instance.id
|
|
now = timezone.now()
|
|
now_sec = now.strftime("%s.%f")
|
|
ext = Path(filename).suffix.lower()
|
|
if ext in (".jpeg", ".jpg"):
|
|
ext = ".jpg"
|
|
|
|
if ext not in (".jpg", ".png", ".gif"):
|
|
raise Exception("File type invalid")
|
|
|
|
return f"{instance.board.url}/{now_sec}{ext}"
|
|
|
|
|
|
def thumbs_upload(instance, filename):
|
|
op_id = instance.op.id if instance.op else instance.id
|
|
now = timezone.now()
|
|
now_sec = now.strftime("%s.%f")
|
|
ext = Path(filename).suffix.lower()
|
|
return f"{instance.board.url}/{now_sec}t{ext}"
|
|
|
|
|
|
class Board(models.Model):
|
|
# The short URL name for the board
|
|
url = models.CharField(max_length=255, null=False, blank=False, unique=True)
|
|
# Human-readable name for the board
|
|
name = models.CharField(max_length=255, null=False, blank=False)
|
|
|
|
@property
|
|
def threads(self):
|
|
return Post.objects.filter(board=self, op=None)
|
|
|
|
|
|
class Post(models.Model):
|
|
# Board that this post was made on
|
|
board = models.ForeignKey("Board", on_delete=models.CASCADE)
|
|
# Thread that this is a part of
|
|
op = models.ForeignKey(
|
|
"self", null=True, on_delete=models.CASCADE, related_name="all_replies"
|
|
)
|
|
# Post that this is replying to
|
|
reply = models.ForeignKey(
|
|
"self", null=True, on_delete=models.CASCADE, related_name="replies"
|
|
)
|
|
# User's supplied name for this post
|
|
name = models.CharField(max_length=255, null=True, blank=True)
|
|
# User's supplied subject for this post
|
|
subject = models.CharField(max_length=255, null=True, blank=True)
|
|
# Text of this post
|
|
text = models.TextField(max_length=10000, null=False, blank=True)
|
|
# The IP address of the user that made this post
|
|
ip = models.GenericIPAddressField()
|
|
# Creation time
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
# Last bump time
|
|
last_bump = models.DateTimeField(auto_now_add=True)
|
|
# Image
|
|
image = models.ImageField(
|
|
upload_to=image_upload,
|
|
null=True,
|
|
blank=True,
|
|
width_field="image_width",
|
|
height_field="image_height",
|
|
)
|
|
# Thumbnail
|
|
thumbnail = models.ImageField(upload_to=thumbs_upload, editable=False, null=True)
|
|
# Original image name
|
|
original_image_name = models.CharField(max_length=255, null=True)
|
|
# Image width and height
|
|
image_width = models.IntegerField(null=True)
|
|
image_height = models.IntegerField(null=True)
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.image:
|
|
self.original_image_name = Path(self.image.name).parts[-1]
|
|
self.__make_thumbnail()
|
|
super(Post, self).save(*args, **kwargs)
|
|
|
|
def __make_thumbnail(self):
|
|
image = Image.open(self.image)
|
|
image.thumbnail(settings.THUMB_SIZE, Image.ANTIALIAS)
|
|
|
|
image_path = Path(self.image.name)
|
|
thumb_path = Path(image_path.stem + "t" + image_path.suffix)
|
|
|
|
ext = thumb_path.suffix.lower()
|
|
if ext in (".jpg", ".jpeg"):
|
|
FTYPE = "JPEG"
|
|
elif ext == ".gif":
|
|
FTYPE = "GIF"
|
|
elif ext == ".png":
|
|
FTYPE = "PNG"
|
|
else:
|
|
raise Exception("File type invalid")
|
|
|
|
thumb_temp = BytesIO()
|
|
image.save(thumb_temp, FTYPE)
|
|
thumb_temp.seek(0)
|
|
|
|
# save=False stops recursion
|
|
self.thumbnail.save(thumb_path, ContentFile(thumb_temp.read()), save=False)
|
|
thumb_temp.close()
|
|
|
|
def get_absolute_url(self):
|
|
if self.op is None:
|
|
return reverse(
|
|
"board:post_detail", kwargs={"url": self.board.url, "id": self.id}
|
|
)
|
|
else:
|
|
return (
|
|
reverse(
|
|
"board:post_detail",
|
|
kwargs={"url": self.board.url, "id": self.op.id},
|
|
)
|
|
+ f"#p{self.id}"
|
|
)
|
|
|
|
def clean(self):
|
|
if self.image and self.image.size > settings.MAX_UPLOAD_SIZE:
|
|
raise ValidationError(
|
|
"Image supplied is too large. Maximum image size is %(max)s",
|
|
params={"max": settings.MAX_UPLOAD_SIZE},
|
|
)
|
|
|
|
|
|
@receiver(signals.post_save, sender=Post)
|
|
def post_created(sender, instance, created, **kwargs):
|
|
if created and instance.op:
|
|
instance.op.last_bump = timezone.now()
|
|
instance.op.save()
|
|
|
|
|
|
@receiver(signals.post_delete, sender=Post)
|
|
def post_deleted(sender, instance, **kwargs):
|
|
if instance.image and Path(instance.image.path).is_file():
|
|
os.remove(instance.image.path)
|
|
if instance.thumbnail and Path(instance.thumbnail.path).is_file():
|
|
os.remove(instance.thumbnail.path)
|