Sith/core/utils.py

149 lines
4.6 KiB
Python
Raw Normal View History

#
# Copyright 2023 © AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
#
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# You can find the source code of the website at https://github.com/ae-utbm/sith3
#
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
from datetime import date
2017-05-14 01:14:38 +00:00
# Image utils
from io import BytesIO
from typing import Optional
2018-10-04 19:29:19 +00:00
2016-08-22 12:21:17 +00:00
import PIL
from django.conf import settings
2016-08-22 12:21:17 +00:00
from django.core.files.base import ContentFile
2024-07-22 07:49:08 +00:00
from django.http import HttpRequest
from django.utils import timezone
2024-06-24 11:07:36 +00:00
from PIL import ExifTags
2024-09-14 19:51:35 +00:00
from PIL.Image import Image, Resampling
def get_start_of_semester(today: Optional[date] = None) -> date:
2024-07-12 07:34:16 +00:00
"""Return the date of the start of the semester of the given date.
If no date is given, return the start date of the current semester.
The current semester is computed as follows:
- If the date is between 15/08 and 31/12 => Autumn semester.
- If the date is between 01/01 and 15/02 => Autumn semester of the previous year.
- If the date is between 15/02 and 15/08 => Spring semester
2024-07-12 07:34:16 +00:00
Args:
today: the date to use to compute the semester. If None, use today's date.
Returns:
the date of the start of the semester
"""
if today is None:
today = timezone.now().date()
autumn = date(today.year, *settings.SITH_SEMESTER_START_AUTUMN)
spring = date(today.year, *settings.SITH_SEMESTER_START_SPRING)
if today >= autumn: # between 15/08 (included) and 31/12 -> autumn semester
return autumn
if today >= spring: # between 15/02 (included) and 15/08 -> spring semester
return spring
# between 01/01 and 15/02 -> autumn semester of the previous year
return autumn.replace(year=autumn.year - 1)
def get_semester_code(d: Optional[date] = None) -> str:
2024-07-12 07:34:16 +00:00
"""Return the semester code of the given date.
If no date is given, return the semester code of the current semester.
The semester code is an upper letter (A for autumn, P for spring),
followed by the last two digits of the year.
For example, the autumn semester of 2018 is "A18".
2024-07-12 07:34:16 +00:00
Args:
d: the date to use to compute the semester. If None, use today's date.
Returns:
the semester code corresponding to the given date
"""
if d is None:
d = timezone.now().date()
2017-06-12 07:42:03 +00:00
start = get_start_of_semester(d)
if (start.month, start.day) == settings.SITH_SEMESTER_START_AUTUMN:
return "A" + str(start.year)[-2:]
return "P" + str(start.year)[-2:]
2017-06-12 07:42:03 +00:00
2024-09-14 19:51:35 +00:00
def resize_image(im: Image, edge: int, img_format: str):
"""Resize an image to fit the given edge length and format."""
(w, h) = im.size
ratio = edge / max(w, h)
(width, height) = int(w * ratio), int(h * ratio)
return resize_image_explicit(im, (width, height), img_format)
2017-06-12 07:42:03 +00:00
2024-09-14 19:51:35 +00:00
def resize_image_explicit(im: Image, size: tuple[int, int], img_format: str):
"""Resize an image to the given size and format."""
2024-09-01 17:05:54 +00:00
img_format = img_format.upper()
content = BytesIO()
# use the lanczos filter for antialiasing and discard the alpha channel
2024-09-14 19:51:35 +00:00
im = im.resize((size[0], size[1]), Resampling.LANCZOS)
2024-09-01 17:05:54 +00:00
if img_format == "JPEG":
# converting an image with an alpha channel to jpeg would cause a crash
im = im.convert("RGB")
2016-08-22 12:21:17 +00:00
try:
2018-10-04 19:29:19 +00:00
im.save(
fp=content,
2024-09-01 17:05:54 +00:00
format=img_format,
2018-10-04 19:29:19 +00:00
quality=90,
optimize=True,
progressive=True,
)
2016-08-22 12:21:17 +00:00
except IOError:
PIL.ImageFile.MAXBLOCK = im.size[0] * im.size[1]
2018-10-04 19:29:19 +00:00
im.save(
fp=content,
2024-09-01 17:05:54 +00:00
format=img_format,
2018-10-04 19:29:19 +00:00
quality=90,
optimize=True,
progressive=True,
)
return ContentFile(content.getvalue())
2016-11-20 12:39:04 +00:00
2017-06-12 07:42:03 +00:00
def exif_auto_rotate(image):
for orientation in ExifTags.TAGS.keys():
2018-10-04 19:29:19 +00:00
if ExifTags.TAGS[orientation] == "Orientation":
2017-06-12 07:42:03 +00:00
break
exif = dict(image._getexif().items())
if exif[orientation] == 3:
image = image.rotate(180, expand=True)
elif exif[orientation] == 6:
image = image.rotate(270, expand=True)
elif exif[orientation] == 8:
image = image.rotate(90, expand=True)
2016-11-20 12:39:04 +00:00
return image
2017-05-14 01:14:38 +00:00
2017-06-12 07:42:03 +00:00
2024-07-22 07:49:08 +00:00
def get_client_ip(request: HttpRequest) -> str | None:
headers = (
"X_FORWARDED_FOR", # Common header for proixes
"FORWARDED", # Standard header defined by RFC 7239.
"REMOTE_ADDR", # Default IP Address (direct connection)
)
for header in headers:
if (ip := request.META.get(header)) is not None:
return ip
return None