Files

125 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""Download official MONIKI leadership photos and normalize them to 720x960 JPEG."""
from __future__ import annotations
import io
import urllib.request
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageOps
ROOT = Path(__file__).resolve().parents[1]
OUT_DIR = ROOT / "public" / "organization" / "leaders"
TARGET_W, TARGET_H = 720, 960
RATIO = TARGET_W / TARGET_H
LEADERS = [
("sobolev", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Sobolev.JPG"),
("gameeva", "https://www.monikiweb.ru/sites/default/files/page_content_files/GameevaEV2026-07.jpg"),
("stepanova", "https://www.monikiweb.ru/sites/default/files/page_content_files/StepanovaAM.jpg"),
("kakorina", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Kakorina_E_P.jpg"),
("mozhaev", "https://www.monikiweb.ru/sites/default/files/page_content_files/MozhaevYA.jpg"),
("chernyavskaya", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Chernyavskaya_T_K.jpg"),
("kononova", "https://www.monikiweb.ru/sites/default/files/page_content_files/KononovaIV.jpg"),
("bogdan", "https://www.monikiweb.ru/sites/default/files/page_content_files/BogdanAA.png"),
("lopotovsky", "https://www.monikiweb.ru/sites/default/files/page_content_files/LopotovskyP.jpg"),
("varlamova", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Varlamova_L_S.JPG"),
("ermolaeva", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Ermolaeva_A_V.JPG"),
("shikina", "https://www.monikiweb.ru/sites/default/files/page_content_files/Shikina.JPG"),
("padalko", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Zam.dir.po_kadram-small.jpg"),
("simonenkov", "https://www.monikiweb.ru/sites/default/files/simanenkov.JPG"),
("khizgiaev", None),
("lobanova", "https://www.monikiweb.ru/sites/default/files/page_content_files/LobanovaOS.jpg.jfif"),
("zyuzina", "https://www.monikiweb.ru/sites/default/files/page_content_files/photozyzina.jpg"),
("averin", "https://www.monikiweb.ru/sites/default/files/page_content_files/AverinAA.jpg"),
("arkhipova", "https://www.monikiweb.ru/sites/default/files/page_content_files/Arkhipova.jpg"),
("babaeva", "https://www.monikiweb.ru/sites/default/files/page_content_files/BabaevaLA.jpg"),
("doronina", "https://www.monikiweb.ru/sites/default/files/page_content_files/DoroninaOV.jpg"),
("kanina", "https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Kanina_E_V.JPG"),
("kuidin", "https://www.monikiweb.ru/sites/default/files/page_content_files/KuidinDV.jpg"),
]
KHIZGIAEV_CANDIDATES = [
"https://www.monikiweb.ru/sites/default/files/page_content_files/Hizgiaev.jpg",
"https://www.monikiweb.ru/sites/default/files/page_content_files/Hizgiyaev.jpg",
"https://www.monikiweb.ru/sites/default/files/page_content_files/Khizgiaev.jpg",
"https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Hizgiaev.jpg",
"https://www.monikiweb.ru/sites/default/files/page_content_files/!!!Hizgiaev_V_I.jpg",
"https://www.monikiweb.ru/sites/default/files/page_content_files/HizgiaevVI.jpg",
]
def fetch(url: str) -> bytes | None:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
try:
with urllib.request.urlopen(req, timeout=60) as res:
if res.status != 200:
return None
data = res.read()
if not data:
return None
return data
except Exception as exc: # noqa: BLE001
print(f" skip {url}: {exc}")
return None
def normalize(image: Image.Image) -> Image.Image:
image = ImageOps.exif_transpose(image).convert("RGB")
width, height = image.size
if width / height > RATIO:
crop_w = int(height * RATIO)
left = max((width - crop_w) // 2, 0)
image = image.crop((left, 0, left + crop_w, height))
else:
crop_h = int(width / RATIO)
image = image.crop((0, 0, width, min(crop_h, height)))
return image.resize((TARGET_W, TARGET_H), Image.Resampling.LANCZOS)
def save_jpeg(image: Image.Image, path: Path) -> None:
image.save(path, "JPEG", quality=84, optimize=True, progressive=True)
def placeholder(initials: str) -> Image.Image:
image = Image.new("RGB", (TARGET_W, TARGET_H), (232, 240, 232))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
try:
font = ImageFont.truetype("/System/Library/Fonts/Supplemental/Arial Bold.ttf", 140)
except OSError:
pass
bbox = draw.textbbox((0, 0), initials, font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
draw.text(((TARGET_W - tw) / 2, (TARGET_H - th) / 2), initials, fill=(15, 81, 50), font=font)
return image
def main() -> None:
OUT_DIR.mkdir(parents=True, exist_ok=True)
for slug, url in LEADERS:
out = OUT_DIR / f"{slug}.jpg"
print(slug)
data = None
if slug == "khizgiaev":
for candidate in KHIZGIAEV_CANDIDATES:
data = fetch(candidate)
if data:
print(f" found {candidate}")
break
elif url:
data = fetch(url)
if data:
image = Image.open(io.BytesIO(data))
save_jpeg(normalize(image), out)
print(f" wrote {out.name} {out.stat().st_size} bytes")
else:
save_jpeg(placeholder("ВИХ"), out)
print(f" placeholder {out.name}")
if __name__ == "__main__":
main()