mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-30 00:23:54 +00:00 
			
		
		
		
	Custom client for UTBM UV API calls
This commit is contained in:
		| @@ -10,13 +10,13 @@ from ninja_extra.pagination import PageNumberPaginationExtra, PaginatedResponseS | |||||||
| from core.auth.api_permissions import HasPerm | from core.auth.api_permissions import HasPerm | ||||||
| from pedagogy.models import UV | from pedagogy.models import UV | ||||||
| from pedagogy.schemas import SimpleUvSchema, UvFilterSchema, UvSchema | from pedagogy.schemas import SimpleUvSchema, UvFilterSchema, UvSchema | ||||||
| from pedagogy.utbm_api import find_uv | from pedagogy.utbm_api import UtbmApiClient | ||||||
|  |  | ||||||
|  |  | ||||||
| @api_controller("/uv") | @api_controller("/uv") | ||||||
| class UvController(ControllerBase): | class UvController(ControllerBase): | ||||||
|     @route.get( |     @route.get( | ||||||
|         "/{year}/{code}", |         "/{code}", | ||||||
|         permissions=[ |         permissions=[ | ||||||
|             # this route will almost always be called in the context |             # this route will almost always be called in the context | ||||||
|             # of a UV creation/edition |             # of a UV creation/edition | ||||||
| @@ -26,10 +26,14 @@ class UvController(ControllerBase): | |||||||
|         response=UvSchema, |         response=UvSchema, | ||||||
|     ) |     ) | ||||||
|     def fetch_from_utbm_api( |     def fetch_from_utbm_api( | ||||||
|         self, year: Annotated[int, Ge(2010)], code: str, lang: Query[str] = "fr" |         self, | ||||||
|  |         code: str, | ||||||
|  |         lang: Query[str] = "fr", | ||||||
|  |         year: Query[Annotated[int, Ge(2010)] | None] = None, | ||||||
|     ): |     ): | ||||||
|         """Fetch UV data from the UTBM API and returns it after some parsing.""" |         """Fetch UV data from the UTBM API and returns it after some parsing.""" | ||||||
|         res = find_uv(lang, year, code) |         with UtbmApiClient() as client: | ||||||
|  |             res = client.find_uv(lang, code, year) | ||||||
|         if res is None: |         if res is None: | ||||||
|             raise NotFound |             raise NotFound | ||||||
|         return res |         return res | ||||||
|   | |||||||
| @@ -46,12 +46,7 @@ | |||||||
|       const codeInput = document.querySelector('input[name="code"]') |       const codeInput = document.querySelector('input[name="code"]') | ||||||
|  |  | ||||||
|       autofillBtn.addEventListener('click', () => { |       autofillBtn.addEventListener('click', () => { | ||||||
|         const today = new Date() |         const url = `/api/uv/${codeInput.value}`; | ||||||
|         let year = today.getFullYear() |  | ||||||
|         if (today.getMonth() < 7) {  // student year starts in september |  | ||||||
|           year-- |  | ||||||
|         } |  | ||||||
|         const url = `/api/uv/${year}/${codeInput.value}`; |  | ||||||
|         deleteQuickNotifs() |         deleteQuickNotifs() | ||||||
|  |  | ||||||
|         $.ajax({ |         $.ajax({ | ||||||
| @@ -70,7 +65,7 @@ | |||||||
|               .filter(([elem, _]) => !!elem)  // skip non-existing DOM elements |               .filter(([elem, _]) => !!elem)  // skip non-existing DOM elements | ||||||
|               .forEach(([elem, val]) => {  // write the value in the form field |               .forEach(([elem, val]) => {  // write the value in the form field | ||||||
|                 if (elem.tagName === 'TEXTAREA') { |                 if (elem.tagName === 'TEXTAREA') { | ||||||
|                                 // MD editor text input |                   // MD editor text input | ||||||
|                   elem.parentNode.querySelector('.CodeMirror').CodeMirror.setValue(val); |                   elem.parentNode.querySelector('.CodeMirror').CodeMirror.setValue(val); | ||||||
|                 } else { |                 } else { | ||||||
|                   elem.value = val; |                   elem.value = val; | ||||||
|   | |||||||
| @@ -2,27 +2,59 @@ | |||||||
|  |  | ||||||
| import requests | import requests | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | from django.utils.functional import cached_property | ||||||
|  |  | ||||||
| from pedagogy.schemas import ShortUvList, UtbmFullUvSchema, UtbmShortUvSchema, UvSchema | from pedagogy.schemas import ShortUvList, UtbmFullUvSchema, UtbmShortUvSchema, UvSchema | ||||||
|  |  | ||||||
|  |  | ||||||
| def find_uv(lang, year, code) -> UvSchema | None: | class UtbmApiClient(requests.Session): | ||||||
|     """Find an UV from the UTBM API.""" |     """A wrapper around `requests.Session` to perform requests to the UTBM UV API.""" | ||||||
|     # query the UV list |  | ||||||
|     base_url = settings.SITH_PEDAGOGY_UTBM_API |  | ||||||
|     uvs_url = f"{base_url}/uvs/{lang}/{year}" |  | ||||||
|     response = requests.get(uvs_url) |  | ||||||
|     uvs: list[UtbmShortUvSchema] = ShortUvList.validate_json(response.content) |  | ||||||
|  |  | ||||||
|     short_uv = next((uv for uv in uvs if uv.code == code), None) |     BASE_URL = settings.SITH_PEDAGOGY_UTBM_API | ||||||
|     if short_uv is None: |     _cache = {} | ||||||
|         return None |  | ||||||
|  |  | ||||||
|     # get detailed information about the UV |     @cached_property | ||||||
|     uv_url = f"{base_url}/uv/{lang}/{year}/{code}/{short_uv.code_formation}" |     def current_year(self) -> int: | ||||||
|     response = requests.get(uv_url) |         """Fetch from the API the latest existing year""" | ||||||
|     full_uv = UtbmFullUvSchema.model_validate_json(response.content) |         url = f"{self.BASE_URL}/guides/fr" | ||||||
|     return make_clean_uv(short_uv, full_uv) |         response = self.get(url) | ||||||
|  |         return response.json()[-1]["annee"] | ||||||
|  |  | ||||||
|  |     def fetch_short_uvs( | ||||||
|  |         self, lang: str = "fr", year: int | None = None | ||||||
|  |     ) -> list[UtbmShortUvSchema]: | ||||||
|  |         """Get the list of UVs in their short format from the UTBM API""" | ||||||
|  |         if year is None: | ||||||
|  |             year = self.current_year | ||||||
|  |         if "short_uvs" not in self._cache: | ||||||
|  |             self._cache["short_uvs"] = {} | ||||||
|  |         if lang not in self._cache["short_uvs"]: | ||||||
|  |             self._cache["short_uvs"][lang] = {} | ||||||
|  |         if year not in self._cache["short_uvs"][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] | ||||||
|  |  | ||||||
|  |     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 | ||||||
|  |         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: | ||||||
|  |             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) | ||||||
|  |  | ||||||
|  |  | ||||||
| def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvSchema: | def make_clean_uv(short_uv: UtbmShortUvSchema, full_uv: UtbmFullUvSchema) -> UvSchema: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user