This repository has been archived on 2023-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
python-blog/src/attachments/models.py

139 lines
4.2 KiB
Python
Raw Normal View History

2021-12-31 12:08:03 +01:00
from __future__ import annotations
import json
2023-03-24 10:52:23 +01:00
import logging
2020-08-26 19:04:48 +02:00
import tempfile
from pathlib import Path
2021-12-31 12:08:03 +01:00
from typing import Any
2020-08-26 19:04:48 +02:00
import requests
from django.conf import settings
2020-08-26 19:04:48 +02:00
from django.core.files import File
2022-09-23 21:47:19 +02:00
from django.core.handlers.wsgi import WSGIRequest
2020-08-26 19:04:48 +02:00
from django.db import models
from django.db.models.fields.files import FieldFile
2022-10-01 09:16:26 +02:00
from django.urls import reverse
2020-08-26 19:04:48 +02:00
from PIL import Image
from articles.utils import build_full_absolute_url
2023-03-24 10:52:23 +01:00
logger = logging.getLogger(__name__)
class AbsoluteUrlFieldFile(FieldFile):
2022-09-23 21:47:19 +02:00
def get_full_absolute_url(self, request: WSGIRequest) -> str:
return build_full_absolute_url(request, self.url)
class AbsoluteUrlFileField(models.FileField):
attr_class = AbsoluteUrlFieldFile
2020-08-26 19:04:48 +02:00
2020-11-28 20:09:37 +01:00
class AttachmentManager(models.Manager):
2021-12-31 12:08:03 +01:00
def get_open_graph_image(self) -> Attachment | None:
2020-11-28 20:09:37 +01:00
return self.filter(open_graph_image=True).first()
2020-08-26 19:04:48 +02:00
class Attachment(models.Model):
description = models.CharField(max_length=500)
original_file = AbsoluteUrlFileField()
processed_file = AbsoluteUrlFileField(blank=True, null=True)
open_graph_image = models.BooleanField(blank=True, default=False)
2020-11-28 20:09:37 +01:00
objects = AttachmentManager()
2020-08-26 19:04:48 +02:00
2020-08-28 22:24:28 +02:00
class Meta:
ordering = ["description"]
2021-12-31 12:08:03 +01:00
def __str__(self) -> str:
2020-08-27 22:09:18 +02:00
return f"{self.description} ({self.original_file.name})"
2021-12-31 12:08:03 +01:00
def save(self, *args: Any, **kwargs: Any) -> None:
super().save(*args, **kwargs)
2020-08-26 19:04:48 +02:00
if self.processed_file:
2023-01-30 20:58:18 +01:00
return None
2020-08-26 19:04:48 +02:00
try:
2020-12-03 22:30:11 +01:00
Image.open(self.original_file.path)
except OSError:
2023-01-30 20:58:18 +01:00
return None
# Submit job to shortpixel
base_data = {
"key": settings.SHORTPIXEL_API_KEY,
"plugin_version": "gabno",
"wait": 20,
}
post_data = {
"lossy": 1,
"resize": 3,
"resize_width": settings.SHORTPIXEL_RESIZE_WIDTH,
"resize_height": settings.SHORTPIXEL_RESIZE_HEIGHT,
2020-08-29 09:55:31 +02:00
"keep_exif": 1,
"file_paths": json.dumps(
2023-03-24 11:22:32 +01:00
{"img": self.original_file.path},
),
}
data = {**base_data, **post_data}
url = "https://api.shortpixel.com/v2/post-reducer.php"
2023-01-30 20:58:18 +01:00
with Path(self.original_file.path).open("rb") as original_file:
response = requests.post(
2023-01-30 20:58:18 +01:00
url=url,
data=data,
2023-03-24 11:22:32 +01:00
files={"img": original_file},
2023-01-30 20:58:18 +01:00
timeout=10,
)
2023-03-24 10:52:23 +01:00
res = response.json()
2023-03-24 11:03:27 +01:00
if len(res) == 0 or not isinstance(res, list):
logger.error("Shortpixel response is not a non-empty list: %s", res)
2023-03-24 11:22:32 +01:00
logger.error("POST data: %s", data)
2023-03-24 10:52:23 +01:00
return super().save(*args, **kwargs)
res_data = res[0]
# Loop until it's done
post_data = {
"key": settings.SHORTPIXEL_API_KEY,
"plugin_version": "gabno",
"wait": 20,
"file_urls": json.dumps([res_data["OriginalURL"]]),
}
check_data = {**base_data, **post_data}
while res_data["Status"]["Code"] == "1":
2023-01-30 20:58:18 +01:00
response = requests.post(url=url, data=check_data, timeout=10)
res_data = response.json()[0]
# Download image
current_path = Path(self.original_file.path)
2020-08-26 19:04:48 +02:00
temp_dir = Path(tempfile.mkdtemp())
temp_path = temp_dir / (current_path.stem + "-processed" + current_path.suffix)
2023-01-30 20:58:18 +01:00
img = requests.get(res_data["LossyURL"], stream=True, timeout=10)
with Path(temp_path).open("wb") as temp_file:
for chunk in img:
temp_file.write(chunk)
# Link it to our model
2023-01-30 20:58:18 +01:00
with Path(temp_path).open("rb") as output_file:
2020-08-26 19:04:48 +02:00
f = File(output_file)
self.processed_file.save(temp_path.name, f, save=False)
2020-08-26 19:04:48 +02:00
temp_path.unlink()
temp_dir.rmdir()
return super().save(*args, **kwargs)
2023-03-29 09:44:58 +02:00
@property
def original_file_url(self) -> str:
return reverse("attachments:original", kwargs={"pk": self.pk})
@property
def processed_file_url(self) -> str | None:
if self.processed_file:
return reverse("attachments:processed", kwargs={"pk": self.pk})
return None
def reprocess(self) -> None:
self.processed_file = None # type: ignore[assignment]
self.save()