#!/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()