YoBatM commited on
Commit
ffb0c3a
·
verified ·
1 Parent(s): 54523af

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -145
app.py CHANGED
@@ -1,152 +1,124 @@
1
- MAX_API_RETRIES=3
2
- from pydantic import BaseModel
3
- import pydantic_core._pydantic_core as pydantic_core
4
- from fastapi import FastAPI
5
- import json,requests
6
- app = FastAPI()
7
- def morph(valor,tipo):
8
- if isinstance(valor,dict):
9
- return tipo(valor)
10
- elif isinstance(valor,list):
11
- return list(map(lambda v:morph(v,tipo),valor))
12
- else:
13
- return valor
14
- class ResponseData(BaseModel):
15
- error:bool
16
- message:str
17
- class Diccionario:
18
- def __init__(self,diccionario:dict):
19
- self.d = diccionario
20
- def __str__(self):
21
- return json.dumps(self.d)
22
- def __repr__(self):
23
- return self.__str__()
24
- def __getattr__(self,data:str):
25
- attr = self.d.get(data,None)
26
- return morph(attr,Diccionario)
27
- class Mensaje(BaseModel):
28
- content:str = ""
29
- role:str = "user"
30
- class persona(BaseModel):
31
- nombres:str
32
- apellidos:str
33
- edad:int
34
- personalidad:str
35
- biografia:str
36
- pais:str
37
- sexo:str
38
- hobbies:list[str]
39
- class personaje(BaseModel):
40
- interpretador:persona
41
- nombre:str
42
- personalidad:str
43
- apariencia:str
44
- papel:str
45
- edad:int
46
- class pelicula(BaseModel):
47
- personajes: list[personaje]
48
- titulo:str
49
- traduccion:dict[str,str]
50
- sinopsis:str
51
- duracion:int
52
- detailed_image_token_prompt_english:list[str]
53
 
54
- class trabajador(BaseModel):
55
- identidad:persona
56
- trabajo:str
57
- sueldo:int
58
- remoto:bool=False
59
- historial_sueldo:list[str]
60
- class comentario(BaseModel):
61
- mensaje:str
62
- autor:persona
63
- likes:int
64
- dislikes:int
65
- class publicacion(BaseModel):
66
- mensaje:str
67
- likes:int
68
- dislikes:int
69
- visitas:int
70
- comentarios:list[comentario]
71
- class perfil(BaseModel):
72
- data:persona
73
- amigos:list[persona]
74
- publicaciones:list[publicacion]
75
- class steamgame(BaseModel):
76
  nombre:str
77
- descripcion_corta:str
78
- descripcion_larga:str
79
- reviews:list[comentario]
80
- calificacion:int
81
- descargas:int
82
-
83
- class episodio(BaseModel):
84
- personajes_involucrados:list[personaje]
85
- dialogos:list[dict[int,dict[str,str]]]
86
- descripcion:str
87
- class serie(BaseModel):
88
  nombre:str
89
- personajes:list[personaje]
90
- clasificacion:str
91
- rating:int
92
- episodios:int
93
- lista_episodios:list[episodio]
94
- def ask(query:str,system_prompt:str)->ResponseData:
95
- msgs:list[dict[str,str]] = [
96
- Mensaje(role="system",content=system_prompt).model_dump(),
97
- Mensaje(content=query).model_dump()
98
- ]
99
- response = requests.post("https://text.pollinations.ai/openai",json={
100
- "messages":msgs,
101
- "private":True,
102
- "jsonMode":True,
103
- "model":"openai",
104
- "seed":__import__("random").randint(10000,999999)
105
- })
106
- data=response.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
- error_msg = Diccionario(data).error
109
- structured_data = ResponseData(error=True if error_msg else False,message=error_msg or Diccionario(data).choices[0].message.content)
110
- return structured_data
111
 
112
- def generate_model(model:BaseModel,gived_data:BaseModel=None,retries=0)->BaseModel | None:
113
- system = f"""Eres un generador de contenido avanzado(evita contenido repetido) capaz de comprender cualquier contexto, debes generar un json valido con estas propiedades, las claves son los valores de title y los valores deben ser del mismo tipo que el valor de type, el title que esta por fuera de propierties debe ser el tipo general, tipo el que contiene todas las propiedades,no realices ningun comentario o acotacion,otorga un json valido,las claves deben estar en minuscula pero los valores no necesariamente"""
114
- if gived_data:
115
- system+=f"\n Ademas tengo esta informacion: {gived_data.model_dump_json()}"
116
- resultado = ask(json.dumps(model.model_json_schema()),system)
117
- if resultado.error==True:
118
- return {"error":resultado.message}
119
- try:
120
- data = list(json.loads(resultado.message).values())[0]
121
- if retries>MAX_API_RETRIES: return {"error":"Se ha intentado, pero no hubo caso, prueba nuevamente","failed_data":data}
122
- if isinstance(data,str):
123
- retries+=1
124
- return generate_model(model,gived_data,retries)
125
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
- return model(**(data))
128
- except (pydantic_core.ValidationError, json.JSONDecodeError) as e:
129
- retries+=1
130
- return generate_model(model,gived_data,retries)
131
 
132
- models:BaseModel= [
133
- persona,
134
- pelicula,
135
- trabajador,
136
- publicacion,
137
- perfil,
138
- steamgame,
139
- serie
140
- ]
141
- def create_handler(model_class: BaseModel):
142
- def handler() -> dict:
143
- generated=generate_model(model_class)
144
-
145
- return generated if isinstance(generated,dict) else json.loads(generated.model_dump_json())
146
- handler.__name__=f"Crear {model_class.__name__}"
147
- return handler
148
- for model in models:
149
- app.add_api_route(f"/{model.__name__}",create_handler(model))
150
- @app.post("/generate_movie")
151
- def gen_movie(user:persona):
152
- return generate_model(pelicula,user)
 
1
+ from fastapi import FastAPI, Query,Path
2
+ from fastapi.responses import HTMLResponse
3
+ from pydantic import BaseModel,Field
4
+ from typing import Annotated
5
+ from bs4 import BeautifulSoup,ResultSet
6
+ import requests
7
+ from hashlib import md5
8
+ from enum import Enum
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ EVA_MAIN_URL:str = "https://eva.fing.edu.uy"
11
+ EVA_CATEGORY_URL:str = f"{EVA_MAIN_URL}/course/index.php?categoryid={{cat_id}}"
12
+ EVA_COURSE_URL:str=f"{EVA_MAIN_URL}/course/view.php?id={{course_id}}"
13
+ EVA_FORUM_URL:str=f"{EVA_MAIN_URL}/mod/forum/view.php?id={{forum_id}}"
14
+ EVA_RESOURCES_URL:str=f"{EVA_MAIN_URL}/course/resources.php?id={{course_id}}"
15
+ app:FastAPI = FastAPI(title="EVA API(No oficial)")
16
+ class Estado(Enum):
17
+ ABIERTO = 1
18
+ CERRADO = 0
19
+ class TipoMaterial(Enum):
20
+ PÁGINA = 0
21
+ ARCHIVO = 1
22
+ FORO = 2
23
+ CARPETA = 3
24
+ URL = 4
25
+ class Instituto(BaseModel):
26
+ id:int
 
 
 
 
 
27
  nombre:str
28
+ class Curso(BaseModel):
29
+ estado:Estado = Estado.CERRADO
 
 
 
 
 
 
 
 
 
30
  nombre:str
31
+ id:int
32
+ class Material(BaseModel):
33
+ tema:str
34
+ tipo:TipoMaterial = TipoMaterial.PÁGINA
35
+ estado:Estado = Estado.CERRADO
36
+ descripcion: str | None = None
37
+
38
+ def insertar_dentro(lista:list[str],opcion) -> any:
39
+ return {lista.pop(0):{"info":
40
+ {'url':opcion.get('value'),
41
+ 'id':int(opcion.get('value').split("=",1)[-1])}
42
+ } if len(lista)==0 else insertar_dentro(lista,opcion)}
43
+ def parse_dict(d1:dict,d2:dict)->dict:
44
+ claves_repetidas =list( set(d1.keys()) & set(d2.keys()))
45
+ claves_d1_unicas = list(set(d1.keys())-set(d2.keys()))
46
+ claves_d2_unicas = list(set(d2.keys())-set(d1.keys()))
47
+ nuevo_diccionario = {}
48
+ for key in claves_repetidas:
49
+ nuevo_diccionario[key] = parse_dict(d1[key],d2[key])
50
+ for key in claves_d1_unicas:
51
+ nuevo_diccionario[key]= d1[key]
52
+ for key in claves_d2_unicas:
53
+ nuevo_diccionario[key]= d2[key]
54
+ return nuevo_diccionario
55
+ def obtener_institutos() -> dict:
56
+ response = requests.get(EVA_CATEGORY_URL.format(cat_id=2))
57
+ scraper = BeautifulSoup(response.text,'html.parser')
58
+ opciones:ResultSet = scraper.find_all("option")
59
+ selecciones:dict = {}
60
+ for opcion in list(opciones):
61
+ carpeta:list[str]= opcion.getText().split(" / ")
62
+ if len(carpeta)<=1: continue
63
+ a = insertar_dentro(carpeta,opcion)
64
+ selecciones = parse_dict(selecciones,a)
65
+ return selecciones
66
+
67
+ def obtener_cursos(category_id:str|int)->list[Curso]:
68
+ def format_option(opcion):
69
+ opcion = opcion.parent
70
+ is_locked = len(opcion.find_all("i"))<2
71
+ nombre=opcion.find('div',class_='coursename').getText()
72
+ id=int(opcion.find('a').get('href').split("=")[-1])
73
+ return Curso(**{
74
+ "estado":Estado.CERRADO if is_locked else Estado.ABIERTO,
75
+ "nombre":nombre,
76
+ "id":id
77
+ })
78
+
79
+
80
+
81
 
 
 
 
82
 
83
+ response = requests.get(EVA_CATEGORY_URL.format(cat_id=category_id))
84
+ scraper = BeautifulSoup(response.text,'html.parser')
85
+ opciones:ResultSet = scraper.find_all('div',class_="coursename")
86
+ fixed_opciones = list(map(format_option,opciones))
87
+ return fixed_opciones
88
+ def obtener_recursos(course_id:str|int)->list[Material]:
89
+ def format_recourse(recurso):
90
+ datos = recurso.find_all("td")
91
+ tema = datos[0].getText()
92
+ info = datos[1]
93
+ tipo:str = info.find("img").get("alt")
94
+ url = info.find("a").get("href")
95
+ tema += info.find("a").getText() if tipo=="URL" else ''
96
+ descripcion=datos[2].getText()
97
+ return Material(**{"tema":tema.strip(),
98
+ "tipo":TipoMaterial[tipo.upper()],
99
+ "url":url,
100
+ "descripcion":descripcion})
101
+ response = requests.get(EVA_RESOURCES_URL.format(course_id=course_id))
102
+ scraper = BeautifulSoup(response.text,'html.parser')
103
+ recursos = scraper.find("table",class_="generaltable mod_index").find_all("tr")[1:]
104
+ recursos_formateados = list(map(format_recourse,recursos))
105
+ return recursos_formateados
106
+ def obtener_hilos(foro_id:int)->list:
107
+ response = requests.get(EVA_FORUM_URL.format(forum_id=foro_id))
108
+ #scraper = BeautifulSoup(response.text,'html.parser')
109
+ return HTMLResponse(content=response.text,status_code=200);
110
+ return str(scraper)
111
 
112
+ @app.get('/institutos',tags=["institutos"],description="Lista los institutos",response_model=dict,name="Institutos")
113
+ def ob_ins():
114
+ return obtener_institutos()
 
115
 
116
+ @app.get('/cursos/{instituto_id}',tags=["cursos"],response_model=list[Curso])
117
+ def get_courses(instituto_id:Annotated[int,Path(title="ID del instituto",description="ID DEL INSTITUTO")]):
118
+ return obtener_cursos(instituto_id)
119
+ @app.get('/recursos/{curso_id}',tags=["cursos","recursos"],response_model=list[Material],description="Obtiene los recursos del curso")
120
+ def get_recursos(curso_id:Annotated[int,Path(title="ID del curso")]):
121
+ return obtener_recursos(curso_id)
122
+ @app.get('/foros/{foro_id}')
123
+ def get_foro_data(foro_id:Annotated[int,Path(title="ID del foro")]):
124
+ return obtener_hilos(foro_id)