2
0
mirror of https://expo.survex.com/repositories/troggle/.git synced 2024-11-30 22:01:53 +00:00
troggle/core/views/editor_helpers.py

213 lines
8.2 KiB
Python

import io
from pathlib import Path
import django.forms as forms
from django.http import JsonResponse
from django.shortcuts import render
from django.template import loader
from django.urls import reverse
from django.views.decorators.csrf import ensure_csrf_cookie
from PIL import Image
import piexif
import troggle.settings as settings
from troggle.core.utils import WriteAndCommitError, write_and_commit
from .auth import login_required_if_public
MAX_IMAGE_WIDTH = 1000
MAX_IMAGE_HEIGHT = 800
THUMBNAIL_WIDTH = 200
THUMBNAIL_HEIGHT = 200
def get_dir(path):
"From a path sent from urls.py, determine the directory."
if "/" in path:
return path.rsplit("/", 1)[0]
else:
return ""
def image_selector(request, path):
"""Returns available images"""
directory = get_dir(path)
thumbnailspath = Path(settings.EXPOWEB) / directory / "t"
thumbnails = []
if thumbnailspath.is_dir():
for f in thumbnailspath.iterdir():
if f.is_file():
if directory:
base = f"{directory}/"
else:
base = ""
thumbnail_url = reverse("expopage", args=[f"{base}t/{f.name}"])
name_base = f.name.rsplit(".", 1)[0]
page_path_base = Path(settings.EXPOWEB) / directory / "l"
if (page_path_base / (f"{name_base}.htm")).is_file():
page_url = reverse("expopage", args=[f"{base}l/{name_base}.htm"])
else:
page_url = reverse("expopage", args=[f"{base}l/{name_base}.html"])
thumbnails.append({"thumbnail_url": thumbnail_url, "page_url": page_url})
return render(request, "image_selector.html", {"thumbnails": thumbnails})
def reorient_image(img, exif_dict):
if piexif.ImageIFD.Orientation in exif_dict["0th"]:
print(exif_dict)
orientation = exif_dict["0th"].pop(piexif.ImageIFD.Orientation)
if orientation == 2:
img = img.transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 3:
img = img.rotate(180)
elif orientation == 4:
img = img.rotate(180).transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 5:
img = img.rotate(-90, expand=True).transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 6:
img = img.rotate(-90, expand=True)
elif orientation == 7:
img = img.rotate(90, expand=True).transpose(Image.FLIP_LEFT_RIGHT)
elif orientation == 8:
img = img.rotate(90, expand=True)
return img
@login_required_if_public
@ensure_csrf_cookie
def new_image_form(request, path):
"""Manages a form to upload new images"""
directory = get_dir(path)
if request.method == "POST":
form = NewWebImageForm(request.POST, request.FILES, directory=directory)
if form.is_valid():
f = request.FILES["file_"]
binary_data = io.BytesIO()
for chunk in f.chunks():
binary_data.write(chunk)
i = Image.open(binary_data)
if "exif" in i.info:
exif_dict = piexif.load(i.info["exif"])
i = reorient_image(i, exif_dict)
exif_dict['Exif'][41729] = b'1'
# can crash here with bad exif data
try:
exif = piexif.dump(exif_dict)
except:
exif = None
else:
exif = None
width, height = i.size
if width > MAX_IMAGE_WIDTH or height > MAX_IMAGE_HEIGHT:
scale = max(width / MAX_IMAGE_WIDTH, height / MAX_IMAGE_HEIGHT)
i = i.resize((int(width / scale), int(height / scale)), Image.ANTIALIAS)
tscale = max(width / THUMBNAIL_WIDTH, height / THUMBNAIL_HEIGHT)
thumbnail = i.resize((int(width / tscale), int(height / tscale)), Image.ANTIALIAS)
ib = io.BytesIO()
i = i.convert('RGB')
i.save(ib, format="jpeg", quality = 75)
tb = io.BytesIO()
thumbnail = thumbnail.convert('RGB')
thumbnail.save(tb, format="jpeg", quality = 70)
image_rel_path, thumb_rel_path, desc_rel_path = form.get_rel_paths()
image_page_template = loader.get_template("image_page_template.html")
image_page = image_page_template.render(
{
"header": form.cleaned_data["header"],
"description": form.cleaned_data["description"],
"photographer": form.cleaned_data["photographer"],
"year": form.cleaned_data["year"],
"filepath": f"/{image_rel_path}",
}
)
image_path, thumb_path, desc_path = form.get_full_paths()
# Create directories if required
for full_path in image_path, thumb_path, desc_path:
print(full_path, full_path.parent)
full_path.parent.mkdir(parents=True, exist_ok=True)
try:
change_message = form.cleaned_data["change_message"]
write_and_commit(
[
(desc_path, image_page, "utf-8"),
(image_path, ib.getbuffer(), False),
(thumb_path, tb.getbuffer(), False),
],
f"{change_message} - online adding of an image",
)
except WriteAndCommitError as e:
return JsonResponse({"error": e.message})
linked_image_template = loader.get_template("linked_image_template.html")
html_snippet = linked_image_template.render(
{"thumbnail_url": f"/{thumb_rel_path}", "page_url": f"/{desc_rel_path}"}, request
)
return JsonResponse({"html": html_snippet})
else:
form = NewWebImageForm(directory=directory)
template = loader.get_template("new_image_form.html")
htmlform = template.render({"form": form, "path": path}, request)
return JsonResponse({"form": htmlform})
class NewWebImageForm(forms.Form):
"""The form used by the editexpopage function"""
header = forms.CharField(
widget=forms.TextInput(
attrs={"size": "60", "placeholder": "Enter title (displayed as a header and in the tab)"}
)
)
file_ = forms.FileField()
description = forms.CharField(
widget=forms.Textarea(attrs={"cols": 80, "rows": 20, "placeholder": "Describe the photo (using HTML)"})
)
photographer = forms.CharField(
widget=forms.TextInput(attrs={"size": "60", "placeholder": "Photographers name"}), required=False
)
year = forms.CharField(
widget=forms.TextInput(attrs={"size": "60", "placeholder": "Year photo was taken"}), required=False
)
change_message = forms.CharField(
widget=forms.Textarea(attrs={"cols": 80, "rows": 3, "placeholder": "Descibe the change made (for git)"})
)
def __init__(self, *args, **kwargs):
self.directory = Path(kwargs.pop("directory"))
super(forms.Form, self).__init__(*args, **kwargs)
def get_rel_paths(self):
f = self.cleaned_data["file_"]
return [
self.directory / "i" / (f.name.rsplit(".", 1)[0] + ".jpg"),
self.directory / "t" / (f.name.rsplit(".", 1)[0] + ".jpg"),
self.directory / "l" / (f.name.rsplit(".", 1)[0] + ".html"),
]
def get_full_paths(self):
return [Path(settings.EXPOWEB) / x for x in self.get_rel_paths()]
def clean_file_(self):
for rel_path, full_path in zip(self.get_rel_paths(), self.get_full_paths()):
if full_path.exists():
raise forms.ValidationError(f"File already exists in {rel_path}")
return self.cleaned_data["file_"]
class HTMLarea(forms.Textarea):
template_name = "widgets/HTMLarea.html"
def __init__(self, *args, **kwargs):
self.preview = kwargs.pop("preview", False)
super(forms.Textarea, self).__init__(*args, **kwargs)
def get_context(self, name, value, attrs):
c = super(forms.Textarea, self).get_context(name, value, attrs)
c["preview"] = self.preview
return c