|
from fastapi import FastAPI, Query,Path |
|
from fastapi.responses import HTMLResponse |
|
from pydantic import BaseModel,Field |
|
from typing import Annotated |
|
from bs4 import BeautifulSoup,ResultSet |
|
import requests |
|
from hashlib import md5 |
|
from enum import Enum |
|
|
|
EVA_MAIN_URL:str = "https://eva.fing.edu.uy" |
|
EVA_CATEGORY_URL:str = f"{EVA_MAIN_URL}/course/index.php?categoryid={{cat_id}}" |
|
EVA_COURSE_URL:str=f"{EVA_MAIN_URL}/course/view.php?id={{course_id}}" |
|
EVA_FORUM_URL:str=f"{EVA_MAIN_URL}/mod/forum/view.php?id={{forum_id}}" |
|
EVA_RESOURCES_URL:str=f"{EVA_MAIN_URL}/course/resources.php?id={{course_id}}" |
|
app:FastAPI = FastAPI(title="EVA API(No oficial)") |
|
class Estado(Enum): |
|
ABIERTO = 1 |
|
CERRADO = 0 |
|
class TipoMaterial(Enum): |
|
PÁGINA = 0 |
|
ARCHIVO = 1 |
|
FORO = 2 |
|
CARPETA = 3 |
|
URL = 4 |
|
class Instituto(BaseModel): |
|
id:int |
|
nombre:str |
|
class Curso(BaseModel): |
|
estado:Estado = Estado.CERRADO |
|
nombre:str |
|
id:int |
|
class Material(BaseModel): |
|
tema:str |
|
tipo:TipoMaterial = TipoMaterial.PÁGINA |
|
estado:Estado = Estado.CERRADO |
|
descripcion: str | None = None |
|
|
|
def insertar_dentro(lista:list[str],opcion) -> any: |
|
return {lista.pop(0):{"info": |
|
{'url':opcion.get('value'), |
|
'id':int(opcion.get('value').split("=",1)[-1])} |
|
} if len(lista)==0 else insertar_dentro(lista,opcion)} |
|
def parse_dict(d1:dict,d2:dict)->dict: |
|
claves_repetidas =list( set(d1.keys()) & set(d2.keys())) |
|
claves_d1_unicas = list(set(d1.keys())-set(d2.keys())) |
|
claves_d2_unicas = list(set(d2.keys())-set(d1.keys())) |
|
nuevo_diccionario = {} |
|
for key in claves_repetidas: |
|
nuevo_diccionario[key] = parse_dict(d1[key],d2[key]) |
|
for key in claves_d1_unicas: |
|
nuevo_diccionario[key]= d1[key] |
|
for key in claves_d2_unicas: |
|
nuevo_diccionario[key]= d2[key] |
|
return nuevo_diccionario |
|
def obtener_institutos() -> dict: |
|
response = requests.get(EVA_CATEGORY_URL.format(cat_id=2)) |
|
scraper = BeautifulSoup(response.text,'html.parser') |
|
opciones:ResultSet = scraper.find_all("option") |
|
selecciones:dict = {} |
|
for opcion in list(opciones): |
|
carpeta:list[str]= opcion.getText().split(" / ") |
|
if len(carpeta)<=1: continue |
|
a = insertar_dentro(carpeta,opcion) |
|
selecciones = parse_dict(selecciones,a) |
|
return selecciones |
|
|
|
def obtener_cursos(category_id:str|int)->list[Curso]: |
|
def format_option(opcion): |
|
opcion = opcion.parent |
|
is_locked = len(opcion.find_all("i"))<2 |
|
nombre=opcion.find('div',class_='coursename').getText() |
|
id=int(opcion.find('a').get('href').split("=")[-1]) |
|
return Curso(**{ |
|
"estado":Estado.CERRADO if is_locked else Estado.ABIERTO, |
|
"nombre":nombre, |
|
"id":id |
|
}) |
|
|
|
|
|
|
|
|
|
|
|
response = requests.get(EVA_CATEGORY_URL.format(cat_id=category_id)) |
|
scraper = BeautifulSoup(response.text,'html.parser') |
|
opciones:ResultSet = scraper.find_all('div',class_="coursename") |
|
fixed_opciones = list(map(format_option,opciones)) |
|
return fixed_opciones |
|
def obtener_recursos(course_id:str|int)->list[Material]: |
|
def format_recourse(recurso): |
|
datos = recurso.find_all("td") |
|
tema = datos[0].getText() |
|
info = datos[1] |
|
tipo:str = info.find("img").get("alt") |
|
url = info.find("a").get("href") |
|
tema += info.find("a").getText() if tipo=="URL" else '' |
|
descripcion=datos[2].getText() |
|
return Material(**{"tema":tema.strip(), |
|
"tipo":TipoMaterial[tipo.upper()], |
|
"url":url, |
|
"descripcion":descripcion}) |
|
response = requests.get(EVA_RESOURCES_URL.format(course_id=course_id)) |
|
scraper = BeautifulSoup(response.text,'html.parser') |
|
recursos = scraper.find("table",class_="generaltable mod_index").find_all("tr")[1:] |
|
recursos_formateados = list(map(format_recourse,recursos)) |
|
return recursos_formateados |
|
def obtener_hilos(foro_id:int)->list: |
|
response = requests.get(EVA_FORUM_URL.format(forum_id=foro_id)) |
|
|
|
return HTMLResponse(content=response.text,status_code=200); |
|
return str(scraper) |
|
|
|
@app.get('/institutos',tags=["institutos"],description="Lista los institutos",response_model=dict,name="Institutos") |
|
def ob_ins(): |
|
return obtener_institutos() |
|
|
|
@app.get('/cursos/{instituto_id}',tags=["cursos"],response_model=list[Curso]) |
|
def get_courses(instituto_id:Annotated[int,Path(title="ID del instituto",description="ID DEL INSTITUTO")]): |
|
return obtener_cursos(instituto_id) |
|
@app.get('/recursos/{curso_id}',tags=["cursos","recursos"],response_model=list[Material],description="Obtiene los recursos del curso") |
|
def get_recursos(curso_id:Annotated[int,Path(title="ID del curso")]): |
|
return obtener_recursos(curso_id) |
|
@app.get('/foros/{foro_id}') |
|
def get_foro_data(foro_id:Annotated[int,Path(title="ID del foro")]): |
|
return obtener_hilos(foro_id) |