mirror of
https://github.com/ae-utbm/sith.git
synced 2025-01-22 06:51:09 +00:00
management command to update the whole uv guide
This commit is contained in:
parent
27e3781653
commit
997641d514
0
pedagogy/management/__init__.py
Normal file
0
pedagogy/management/__init__.py
Normal file
0
pedagogy/management/commands/__init__.py
Normal file
0
pedagogy/management/commands/__init__.py
Normal file
37
pedagogy/management/commands/update_uv_guide.py
Normal file
37
pedagogy/management/commands/update_uv_guide.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.core.management import BaseCommand
|
||||||
|
|
||||||
|
from core.models import User
|
||||||
|
from pedagogy.models import UV
|
||||||
|
from pedagogy.schemas import UvSchema
|
||||||
|
from pedagogy.utbm_api import UtbmApiClient
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Update the UV guide"
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
seen_uvs: set[int] = set()
|
||||||
|
root_user = User.objects.get(pk=settings.SITH_ROOT_USER_ID)
|
||||||
|
with UtbmApiClient() as client:
|
||||||
|
self.stdout.write(
|
||||||
|
"Fetching UVs from the UTBM API.\n"
|
||||||
|
"This may take a few minutes to complete."
|
||||||
|
)
|
||||||
|
for uv in client.fetch_uvs():
|
||||||
|
db_uv = UV.objects.filter(code=uv.code).first()
|
||||||
|
if db_uv is None:
|
||||||
|
db_uv = UV(code=uv.code, author=root_user)
|
||||||
|
fields = list(UvSchema.model_fields.keys())
|
||||||
|
fields.remove("id")
|
||||||
|
fields.remove("code")
|
||||||
|
for field in fields:
|
||||||
|
setattr(db_uv, field, getattr(uv, field))
|
||||||
|
db_uv.save()
|
||||||
|
# if it's a creation, django will set the id when saving,
|
||||||
|
# so at this point, a db_uv will always have an id
|
||||||
|
seen_uvs.add(db_uv.id)
|
||||||
|
# UVs that are in database but have not been returned by the API
|
||||||
|
# are considered as closed UEs
|
||||||
|
UV.objects.exclude(id__in=seen_uvs).update(semester="CLOSED")
|
||||||
|
self.stdout.write(self.style.SUCCESS("UV guide updated successfully"))
|
@ -54,11 +54,11 @@ class UtbmFullUvSchema(Schema):
|
|||||||
|
|
||||||
code: str
|
code: str
|
||||||
departement: str = "NA"
|
departement: str = "NA"
|
||||||
libelle: str
|
libelle: str | None
|
||||||
objectifs: str
|
objectifs: str | None
|
||||||
programme: str
|
programme: str | None
|
||||||
acquisition_competences: str
|
acquisition_competences: str | None
|
||||||
acquisition_notions: str
|
acquisition_notions: str | None
|
||||||
langue: str
|
langue: str
|
||||||
code_langue: str
|
code_langue: str
|
||||||
credits_ects: int
|
credits_ects: int
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
"""Set of functions to interact with the UTBM UV api."""
|
"""Set of functions to interact with the UTBM UV api."""
|
||||||
|
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
@ -11,7 +13,7 @@ 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 UV API."""
|
||||||
|
|
||||||
BASE_URL = settings.SITH_PEDAGOGY_UTBM_API
|
BASE_URL = settings.SITH_PEDAGOGY_UTBM_API
|
||||||
_cache = {}
|
_cache = {"short_uvs": {}}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def current_year(self) -> int:
|
def current_year(self) -> int:
|
||||||
@ -26,8 +28,6 @@ class UtbmApiClient(requests.Session):
|
|||||||
"""Get the list of UVs in their short format from the UTBM API"""
|
"""Get the list of UVs in their short format from the UTBM API"""
|
||||||
if year is None:
|
if year is None:
|
||||||
year = self.current_year
|
year = self.current_year
|
||||||
if "short_uvs" not in self._cache:
|
|
||||||
self._cache["short_uvs"] = {}
|
|
||||||
if lang not in self._cache["short_uvs"]:
|
if lang not in self._cache["short_uvs"]:
|
||||||
self._cache["short_uvs"][lang] = {}
|
self._cache["short_uvs"][lang] = {}
|
||||||
if year not in self._cache["short_uvs"][lang]:
|
if year not in self._cache["short_uvs"][lang]:
|
||||||
@ -37,6 +37,39 @@ class UtbmApiClient(requests.Session):
|
|||||||
self._cache["short_uvs"][lang][year] = uvs
|
self._cache["short_uvs"][lang][year] = uvs
|
||||||
return self._cache["short_uvs"][lang][year]
|
return self._cache["short_uvs"][lang][year]
|
||||||
|
|
||||||
|
def fetch_uvs(
|
||||||
|
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.
|
||||||
|
|
||||||
|
Warning:
|
||||||
|
We need infos from the full uv schema, and the UTBM UV 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),
|
||||||
|
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)
|
||||||
|
# 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.
|
||||||
|
# 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)
|
||||||
|
|
||||||
def find_uv(self, lang: str, code: str, year: int | None = None) -> UvSchema | None:
|
def find_uv(self, lang: str, code: str, year: int | None = None) -> UvSchema | None:
|
||||||
"""Find an UV from the UTBM API."""
|
"""Find an UV from the UTBM API."""
|
||||||
# query the UV list
|
# query the UV list
|
||||||
@ -92,9 +125,9 @@ def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvS
|
|||||||
semester = "CLOSED"
|
semester = "CLOSED"
|
||||||
|
|
||||||
return UvSchema(
|
return UvSchema(
|
||||||
title=full_uv.libelle,
|
title=full_uv.libelle or "",
|
||||||
code=full_uv.code,
|
code=full_uv.code,
|
||||||
credit_type=short_uv.code_categorie,
|
credit_type=short_uv.code_categorie or "FREE",
|
||||||
semester=semester,
|
semester=semester,
|
||||||
language=short_uv.code_langue.upper(),
|
language=short_uv.code_langue.upper(),
|
||||||
credits=full_uv.credits_ects,
|
credits=full_uv.credits_ects,
|
||||||
@ -105,8 +138,8 @@ def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvS
|
|||||||
hours_TE=next((i.nbh for i in full_uv.activites if i.code == "TE"), 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,
|
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 "",
|
manager=full_uv.respo_automne or full_uv.respo_printemps or "",
|
||||||
objectives=full_uv.objectifs,
|
objectives=full_uv.objectifs or "",
|
||||||
program=full_uv.programme,
|
program=full_uv.programme or "",
|
||||||
skills=full_uv.acquisition_competences,
|
skills=full_uv.acquisition_competences or "",
|
||||||
key_concepts=full_uv.acquisition_notions,
|
key_concepts=full_uv.acquisition_notions or "",
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user