rename UV to UE

This commit is contained in:
Thomas Girod
2025-04-08 13:48:10 +02:00
committed by imperosol
parent 32570ee03d
commit 775a3282dc
28 changed files with 783 additions and 699 deletions

View File

@@ -1,4 +1,4 @@
"""Set of functions to interact with the UTBM UV api."""
"""Set of functions to interact with the UTBM UE api."""
from typing import Iterator
@@ -6,14 +6,14 @@ import requests
from django.conf import settings
from django.utils.functional import cached_property
from pedagogy.schemas import ShortUvList, UtbmFullUvSchema, UtbmShortUvSchema, UvSchema
from pedagogy.schemas import ShortUeList, UeSchema, UtbmFullUeSchema, UtbmShortUeSchema
class UtbmApiClient(requests.Session):
"""A wrapper around `requests.Session` to perform requests to the UTBM UV API."""
"""A wrapper around `requests.Session` to perform requests to the UTBM UE API."""
BASE_URL = settings.SITH_PEDAGOGY_UTBM_API
_cache = {"short_uvs": {}}
_cache = {"short_ues": {}}
@cached_property
def current_year(self) -> int:
@@ -22,83 +22,83 @@ class UtbmApiClient(requests.Session):
response = self.get(url)
return response.json()[-1]["annee"]
def fetch_short_uvs(
def fetch_short_ues(
self, lang: str = "fr", year: int | None = None
) -> list[UtbmShortUvSchema]:
"""Get the list of UVs in their short format from the UTBM API"""
) -> list[UtbmShortUeSchema]:
"""Get the list of UEs in their short format from the UTBM API"""
if year is None:
year = self.current_year
if lang not in self._cache["short_uvs"]:
self._cache["short_uvs"][lang] = {}
if year not in self._cache["short_uvs"][lang]:
if lang not in self._cache["short_ues"]:
self._cache["short_ues"][lang] = {}
if year not in self._cache["short_ues"][lang]:
url = f"{self.BASE_URL}/uvs/{lang}/{year}"
response = self.get(url)
uvs = ShortUvList.validate_json(response.content)
self._cache["short_uvs"][lang][year] = uvs
return self._cache["short_uvs"][lang][year]
ues = ShortUeList.validate_json(response.content)
self._cache["short_ues"][lang][year] = ues
return self._cache["short_ues"][lang][year]
def fetch_uvs(
def fetch_ues(
self, lang: str = "fr", year: int | None = None
) -> Iterator[UvSchema]:
"""Fetch all UVs from the UTBM API, parsed in a format that we can use.
) -> Iterator[UeSchema]:
"""Fetch all UEs from the UTBM API, parsed in a format that we can use.
Warning:
We need infos from the full uv schema, and the UTBM UV API
We need infos from the full ue schema, and the UTBM UE API
has no route to get all of them at once.
We must do one request per UV (for a total of around 730 UVs),
We must do one request per UE (for a total of around 730 UEs),
which takes a lot of time.
Hopefully, there seems to be no rate-limit, so an error
in the middle of the process isn't likely to occur.
"""
if year is None:
year = self.current_year
shorts_uvs = self.fetch_short_uvs(lang, year)
# When UVs are common to multiple branches (like most HUMA)
shorts_ues = self.fetch_short_ues(lang, year)
# When UEs are common to multiple branches (like most HUMA)
# the UTBM API duplicates them for every branch.
# We have no way in our db to link a UV to multiple formations,
# so we just create a single UV, which formation is the one
# of the first UV found in the list.
# We have no way in our db to link a UE to multiple formations,
# so we just create a single UE, which formation is the one
# of the first UE found in the list.
# For example, if we have CC01 (TC), CC01 (IMSI) and CC01 (EDIM),
# we will only keep CC01 (TC).
unique_short_uvs = {}
for uv in shorts_uvs:
if uv.code not in unique_short_uvs:
unique_short_uvs[uv.code] = uv
for uv in unique_short_uvs.values():
uv_url = f"{self.BASE_URL}/uv/{lang}/{year}/{uv.code}/{uv.code_formation}"
response = requests.get(uv_url)
full_uv = UtbmFullUvSchema.model_validate_json(response.content)
yield make_clean_uv(uv, full_uv)
unique_short_ues = {}
for ue in shorts_ues:
if ue.code not in unique_short_ues:
unique_short_ues[ue.code] = ue
for ue in unique_short_ues.values():
ue_url = f"{self.BASE_URL}/uv/{lang}/{year}/{ue.code}/{ue.code_formation}"
response = requests.get(ue_url)
full_ue = UtbmFullUeSchema.model_validate_json(response.content)
yield make_clean_ue(ue, full_ue)
def find_uv(self, lang: str, code: str, year: int | None = None) -> UvSchema | None:
"""Find an UV from the UTBM API."""
# query the UV list
def find_uu(self, lang: str, code: str, year: int | None = None) -> UeSchema | None:
"""Find an UE from the UTBM API."""
# query the UE list
if not year:
year = self.current_year
# the UTBM API has no way to fetch a single short uv,
# and short uvs contain infos that we need and are not
# in the full uv schema, so we must fetch everything.
short_uvs = self.fetch_short_uvs(lang, year)
short_uv = next((uv for uv in short_uvs if uv.code == code), None)
if short_uv is None:
# the UTBM API has no way to fetch a single short ue,
# and short ues contain infos that we need and are not
# in the full ue schema, so we must fetch everything.
short_ues = self.fetch_short_ues(lang, year)
short_ue = next((ue for ue in short_ues if ue.code == code), None)
if short_ue is None:
return None
# get detailed information about the UV
uv_url = f"{self.BASE_URL}/uv/{lang}/{year}/{code}/{short_uv.code_formation}"
response = requests.get(uv_url)
full_uv = UtbmFullUvSchema.model_validate_json(response.content)
return make_clean_uv(short_uv, full_uv)
# get detailed information about the UE
ue_url = f"{self.BASE_URL}/uv/{lang}/{year}/{code}/{short_ue.code_formation}"
response = requests.get(ue_url)
full_ue = UtbmFullUeSchema.model_validate_json(response.content)
return make_clean_ue(short_ue, full_ue)
def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvSchema:
def make_clean_ue(short_ue: UtbmShortUeSchema, full_ue: UtbmFullUeSchema) -> UeSchema:
"""Cleans the data up so that it corresponds to our data representation.
Some of the needed information are in the short uv schema, some
other in the full uv schema.
Some of the needed information are in the short ue schema, some
other in the full ue schema.
Thus we combine those information to obtain a data schema suitable
for our needs.
"""
if full_uv.departement == "Pôle Humanités":
if full_ue.departement == "Pôle Humanités":
department = "HUMA"
else:
department = {
@@ -112,9 +112,9 @@ def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvS
"ED": "EDIM",
"AI": "GI",
"AM": "MC",
}.get(short_uv.code_formation, "NA")
}.get(short_ue.code_formation, "NA")
match short_uv.ouvert_printemps, short_uv.ouvert_automne:
match short_ue.ouvert_printemps, short_ue.ouvert_automne:
case True, True:
semester = "AUTUMN_AND_SPRING"
case True, False:
@@ -124,22 +124,22 @@ def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvS
case _:
semester = "CLOSED"
return UvSchema(
title=full_uv.libelle or "",
code=full_uv.code,
credit_type=short_uv.code_categorie or "FREE",
return UeSchema(
title=full_ue.libelle or "",
code=full_ue.code,
credit_type=short_ue.code_categorie or "FREE",
semester=semester,
language=short_uv.code_langue.upper(),
credits=full_uv.credits_ects,
language=short_ue.code_langue.upper(),
credits=full_ue.credits_ects,
department=department,
hours_THE=next((i.nbh for i in full_uv.activites if i.code == "THE"), 0) // 60,
hours_TD=next((i.nbh for i in full_uv.activites if i.code == "TD"), 0) // 60,
hours_TP=next((i.nbh for i in full_uv.activites if i.code == "TP"), 0) // 60,
hours_TE=next((i.nbh for i in full_uv.activites if i.code == "TE"), 0) // 60,
hours_CM=next((i.nbh for i in full_uv.activites if i.code == "CM"), 0) // 60,
manager=full_uv.respo_automne or full_uv.respo_printemps or "",
objectives=full_uv.objectifs or "",
program=full_uv.programme or "",
skills=full_uv.acquisition_competences or "",
key_concepts=full_uv.acquisition_notions or "",
hours_THE=next((i.nbh for i in full_ue.activites if i.code == "THE"), 0) // 60,
hours_TD=next((i.nbh for i in full_ue.activites if i.code == "TD"), 0) // 60,
hours_TP=next((i.nbh for i in full_ue.activites if i.code == "TP"), 0) // 60,
hours_TE=next((i.nbh for i in full_ue.activites if i.code == "TE"), 0) // 60,
hours_CM=next((i.nbh for i in full_ue.activites if i.code == "CM"), 0) // 60,
manager=full_ue.respo_automne or full_ue.respo_printemps or "",
objectives=full_ue.objectifs or "",
program=full_ue.programme or "",
skills=full_ue.acquisition_competences or "",
key_concepts=full_ue.acquisition_notions or "",
)