Skip to content

Commit

Permalink
Merge pull request #23 from BananeChocolat/patch-2
Browse files Browse the repository at this point in the history
Modification du menu (osef des allergenes <3)
  • Loading branch information
AiroPi committed Oct 12, 2023
2 parents f8e1c27 + a71f2c6 commit b0b11db
Showing 1 changed file with 45 additions and 71 deletions.
116 changes: 45 additions & 71 deletions src/cogs/restauration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@

from __future__ import annotations

import io
import json
import re
from os import path
from typing import TYPE_CHECKING
from zipfile import ZipFile

import discord
import httpx
from bs4 import BeautifulSoup, Tag
from discord import File, HTTPException, TextChannel
from bs4 import BeautifulSoup
from discord import HTTPException, TextChannel, app_commands
from discord.ext import tasks
from discord.ext.commands import Cog # pyright: ignore[reportMissingTypeStubs]

if TYPE_CHECKING:
from bot import MP2IBot


IMAGES_REGEX = re.compile(r"https://lycee-kleber.com.fr/wp-content/uploads/\d{4}/\d{2}/([^.]+).jpg")
RESTAURATION_PATH = "./data/restauration.json"


Expand All @@ -29,6 +31,7 @@ def __init__(self, bot: MP2IBot) -> None:
self.already_posted: list[str] = self.read_restauration_file()

async def cog_load(self) -> None:
await self.check_menu()
self.check_menu.start()

async def cog_unload(self) -> None:
Expand Down Expand Up @@ -61,88 +64,59 @@ def read_restauration_file(self) -> list[str]:
with open(RESTAURATION_PATH, "r") as f:
return json.load(f)

async def get_menu_imgs(self) -> dict[str, io.BytesIO]:
"""
This function will get the images from the page "restaurant-scolaire".
Something noticeable is that, it doesn't search for each images links in the page, but rather get the whole
page downloaded in a zip, using an export method of mbn.
The zip structure looks like :
.
├── xxx/
│ ├── photo_dddd-ddddd.jpeg
│ ├── photo_dddd-ddddd.jpeg
│ ├── v_photo_dddd-ddddd.jpeg
│ └── v_photo_dddd-ddddd.jpeg
└── Restaurant scolaire_xxx.html
So, by checking the file name in xxx/, we can check if the image was already sent.
Btw, the v_* files are just the photos in a very very low quality. So we ignore them.
By doing this, we are able to get all images in one single request.
But we *need* to fetch them all each time, ig.
Something possible is to compare the ID_METATAG, to avoid redundant requests.
Otherwise, we can look at each single photos in the page 1 by 1, to not make extra request if already fetched.
But this implies to multiply the minimal amount of request needed.
async def get_imgs(self) -> tuple[tuple[str, ...], tuple[str, ...]]:
"""Scrap the website to get the menu and allergenes images.
Returns:
A tuple with fr:MENUs, and a second tuple with fr:ALLERGENES.
"""
async with httpx.AsyncClient() as client:
result = await client.get("https://lyc-kleber.monbureaunumerique.fr/l-etablissement/restaurant-scolaire/")
result = await client.get("https://lycee-kleber.com.fr/restauration")
page = result.text

scrap = BeautifulSoup(page, "html.parser")

if not isinstance(tag := scrap.find("input", id="ID_METATAG"), Tag):
raise Exception("Could not find the tag")
if not isinstance(tag_id := tag.get("value"), str):
raise Exception("Could not find the tag id")

print(tag_id) # temp : just check if the tag change when file changes.

result = await client.get(
f"https://lyc-kleber.monbureaunumerique.fr/exportContent?&ACTION=EXPORTER&exportType=FICHE&ID_FICHE={tag_id}"
)
zip_buffer = io.BytesIO(result.read())
zip_obj = ZipFile(zip_buffer, "r")

imgs_buffers: dict[str, io.BytesIO] = {}
imgs = [obj for obj in zip_obj.infolist() if obj.filename.endswith("jpeg") and obj.file_size > 50_000]

for img in imgs:
buffer = io.BytesIO(zip_obj.read(img.filename))
buffer.seek(0)
imgs_buffers[img.filename] = buffer
scrap = BeautifulSoup(page, "html.parser")
element = scrap.find_all("a", href=lambda r: bool(IMAGES_REGEX.match(r)))
links: list[str] = [e.get("href") for e in element]

return imgs_buffers
menus: tuple[str, ...] = tuple(
l for l in links if (m := IMAGES_REGEX.match(l)) and m.group(1).lower().startswith("menu")
)
allergenes: tuple[str, ...] = tuple(
l for l in links if (m := IMAGES_REGEX.match(l)) and m.group(1).lower().startswith("allergenes")
)
return menus, allergenes

async def post_menu(self, imgs: dict[str, io.BytesIO]) -> None:
"""
This function will post the new images on Discord.
@tasks.loop(minutes=60)
async def check_menu(self) -> None:
"""Post the menu if there is a new one, checked every hour."""
menus, _ = await self.get_imgs()
menus = [m for m in menus if m not in self.already_posted] # filter with only new ones.
for img_link in menus:
self.add_restauration_file(img_link)

It will look for each channel named "menu-cantine" in all guilds the bot have.
Then it will just post all the images in one single message for all of those.
"""
channels: list[TextChannel] = [
ch for ch in self.bot.get_all_channels() if isinstance(ch, TextChannel) and ch.name == "menu-cantine"
]

for channel in channels:
files = [File(buffer, filename=filename) for filename, buffer in imgs.items()]
try:
await channel.send(files=files)
await channel.send("\n".join(menus))
except HTTPException:
pass
[buffer.seek(0) for buffer in imgs.values()]

@tasks.loop(minutes=30)
async def check_menu(self) -> None:
try:
menus = await self.get_menu_imgs()
except Exception:
return

menus = {filename: image_file for filename, image_file in menus.items() if filename not in self.already_posted}
for filename in menus:
self.add_restauration_file(filename)
await self.post_menu(menus)
@app_commands.command(name="allergenes", description="Affiche les allergènes du menu du jour.")
async def allergen(self, inter: discord.Interaction):
_, allergens = await self.get_imgs()
bn = "\n"
await inter.response.send_message(
(
"Voici les allergènes du menu du jour :\n"
f"{bn.join(allergens)}"
"\n\nS'ils ne sont pas à jour, c'est que le lycée ne les a pas publié.\n"
"Attention : les allergènes sont susceptibles d'être modifiés, merci de se référer au panneau"
" d'affichage à la restauration scolaire."
)
)


async def setup(bot: MP2IBot):
Expand Down

0 comments on commit b0b11db

Please sign in to comment.