Update app.py
Browse files
app.py
CHANGED
@@ -1,166 +1,13 @@
|
|
1 |
-
import networkx as nx
|
2 |
-
import matplotlib.pyplot as plt
|
3 |
-
import json
|
4 |
-
import requests
|
5 |
-
import pandas as pd
|
6 |
-
import os
|
7 |
import gradio as gr
|
8 |
-
import
|
9 |
-
|
10 |
-
|
11 |
-
# Load OpenAI API Key
|
12 |
-
API_KEY = os.getenv("OAIK")
|
13 |
-
client = openai.OpenAI(api_key=API_KEY)
|
14 |
-
|
15 |
-
api_key_airt = os.getenv("AIRT_KEY")
|
16 |
-
AIRT_DBASE = 'appUuBVTJR5ju0y6J'
|
17 |
-
AIRT_TABLE = 'foros_postdoc'
|
18 |
-
|
19 |
-
url = f"https://api.airtable.com/v0/{AIRT_DBASE}/{AIRT_TABLE}"
|
20 |
-
headers = {
|
21 |
-
"Authorization": f"Bearer {api_key_airt}",
|
22 |
-
"Content-Type": "application/json"
|
23 |
-
}
|
24 |
|
25 |
# Sample vocabulary for concept extraction
|
26 |
VOCABULARY = ["algoritmos", "inteligencia artificial", "políticas públicas",
|
27 |
"educación a distancia", "gobernanza", "educación superior"]
|
28 |
|
29 |
-
def cargar_nombres(archivo):
|
30 |
-
with open('nombres_postdoc.txt', "r", encoding="utf-8") as f:
|
31 |
-
students = [line.strip() for line in f if line.strip()] # Eliminar espacios y líneas vacías
|
32 |
-
return students
|
33 |
-
|
34 |
-
students = cargar_nombres("nombres.txt")
|
35 |
-
|
36 |
G = nx.DiGraph()
|
37 |
-
|
38 |
-
def extract_concepts(text):
|
39 |
-
|
40 |
-
instrucciones = "Eres un experto en educación superior a distancia con conocimiento de políticas públicas, \
|
41 |
-
tanto para educación superior como la adopción de inteligencia artificial"
|
42 |
-
prompt = f"""Dado el siguiente mensaje: '{text}', utilizando como base esta lista, {VOCABULARY}, genera una lista corta de los términos \
|
43 |
-
coincidentes con la lista larga separados por comas."""
|
44 |
-
|
45 |
-
version_model = 'gpt-3.5-turbo-0125'
|
46 |
-
response = client.chat.completions.create(
|
47 |
-
model=version_model,
|
48 |
-
messages=[{"role": "system", "content": instrucciones},
|
49 |
-
{"role": "user", "content": prompt}],
|
50 |
-
temperature=0.8,
|
51 |
-
max_tokens=300,
|
52 |
-
)
|
53 |
-
extract_concepts = response.choices[0].message.content.split(',')
|
54 |
-
return extract_concepts
|
55 |
-
|
56 |
-
def cargar_desde_airtable():
|
57 |
-
response = requests.get(url, headers=headers)
|
58 |
-
if response.status_code != 200:
|
59 |
-
print(f"Error: {response.status_code} - {response.text}")
|
60 |
-
return pd.DataFrame(columns=["Nombre", "Conceptos"])
|
61 |
-
|
62 |
-
records = response.json().get("records", [])
|
63 |
-
aportes = []
|
64 |
-
|
65 |
-
for record in records:
|
66 |
-
nombre = record["fields"].get("Nombre", "").strip()
|
67 |
-
conceptos = record["fields"].get("Conceptos", "").strip()
|
68 |
-
aportes.append([nombre, conceptos])
|
69 |
-
|
70 |
-
df = pd.DataFrame(aportes, columns=["Nombre", "Conceptos"])
|
71 |
-
|
72 |
-
print("Loaded Airtable Data:")
|
73 |
-
print(df) # 🔍 Debugging: Print the loaded data
|
74 |
-
return df
|
75 |
-
|
76 |
-
def inicializar_grafo():
|
77 |
-
df = cargar_desde_airtable()
|
78 |
-
|
79 |
-
for _, row in df.iterrows():
|
80 |
-
nombre = row["Nombre"].strip()
|
81 |
-
conceptos = row["Conceptos"].strip()
|
82 |
-
|
83 |
-
if not nombre or not conceptos:
|
84 |
-
continue # Skip empty rows
|
85 |
-
|
86 |
-
# ✅ Ensure name is added as a node first
|
87 |
-
if not G.has_node(nombre):
|
88 |
-
G.add_node(nombre, color='lightblue')
|
89 |
-
|
90 |
-
for termino in conceptos.split(','):
|
91 |
-
termino = termino.strip()
|
92 |
-
if termino: # ✅ Fix empty terms issue
|
93 |
-
if not G.has_node(termino):
|
94 |
-
G.add_node(termino, color='lightgreen')
|
95 |
-
if not G.has_edge(nombre, termino):
|
96 |
-
G.add_edge(nombre, termino)
|
97 |
-
|
98 |
-
# print("✅ Graph Nodes:", list(G.nodes))
|
99 |
-
# print("✅ Graph Edges:", list(G.edges))
|
100 |
-
|
101 |
-
def visualizar_grafo():
|
102 |
-
plt.figure(figsize=(10, 6))
|
103 |
-
|
104 |
-
if len(G.nodes) == 0:
|
105 |
-
print("⚠️ Warning: The graph is empty! Check if data was loaded correctly.")
|
106 |
-
plt.text(0.5, 0.5, "No data available", fontsize=12, ha='center')
|
107 |
-
plt.savefig("graph.png")
|
108 |
-
plt.close()
|
109 |
-
return "graph.png"
|
110 |
-
|
111 |
-
centrality = nx.betweenness_centrality(G)
|
112 |
-
pos = nx.kamada_kawai_layout(G)
|
113 |
-
node_colors = []
|
114 |
-
node_sizes = []
|
115 |
-
|
116 |
-
for node in G.nodes():
|
117 |
-
if node in students: # Si es un estudiante
|
118 |
-
node_colors.append('lightblue') # 🔵 Color para nombres
|
119 |
-
else:
|
120 |
-
node_colors.append('lightgreen') # 🟢 Color para conceptos
|
121 |
-
|
122 |
-
node_sizes.append(2000 + 8000 * centrality.get(node, 0))
|
123 |
-
nx.draw(
|
124 |
-
G, pos, with_labels=True, node_color=node_colors, edge_color='gray',
|
125 |
-
node_size=node_sizes, font_size=10
|
126 |
-
)
|
127 |
-
edge_labels = nx.get_edge_attributes(G, 'label')
|
128 |
-
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)
|
129 |
-
plt.title("Red de Aportes - Optimizada por Betweenness")
|
130 |
-
plt.savefig("graph.png")
|
131 |
-
plt.close()
|
132 |
-
return "graph.png"
|
133 |
-
|
134 |
-
def guardar_en_airtable(nombre, conceptos, texto):
|
135 |
-
if isinstance(conceptos, str):
|
136 |
-
conceptos = [c.strip() for c in conceptos.split(',') if c.strip()] # Ensure it's a list
|
137 |
-
data = {"fields": {"Nombre": nombre, "Conceptos": ", ".join(conceptos), "Texto_Aporte": texto}}
|
138 |
-
response = requests.post(url, headers=headers, json=data)
|
139 |
-
if response.status_code != 200:
|
140 |
-
print(f"Error saving to Airtable: {response.status_code} - {response.text}")
|
141 |
-
|
142 |
-
def agregar_aporte(nombre, texto):
|
143 |
-
conceptos = extract_concepts(texto) # ✅ Extract concepts dynamically
|
144 |
-
print(f"Extracted Concepts: {conceptos}") # 🔍 Debugging
|
145 |
-
|
146 |
-
if not G.has_node(nombre):
|
147 |
-
G.add_node(nombre, color='gray')
|
148 |
-
|
149 |
-
for termino in conceptos:
|
150 |
-
termino = termino.strip()
|
151 |
-
if not G.has_node(termino):
|
152 |
-
G.add_node(termino, color='gray')
|
153 |
-
if not G.has_edge(nombre, termino):
|
154 |
-
G.add_edge(nombre, termino)
|
155 |
-
|
156 |
-
guardar_en_airtable(nombre, conceptos, texto)
|
157 |
-
return visualizar_grafo()
|
158 |
-
|
159 |
-
def reload_data():
|
160 |
-
"""Reloads data from Airtable and updates the graph."""
|
161 |
-
inicializar_grafo() # Fetch fresh data and rebuild the graph
|
162 |
-
return visualizar_grafo() # Return updated image
|
163 |
-
|
164 |
inicializar_grafo() # Call this before displaying the graph
|
165 |
|
166 |
iface = gr.Blocks()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
+
from utils_gdmk import (
|
3 |
+
inicializar_grafo, cargar_normativas, cargar_estudiantes, mostrar_detalles,
|
4 |
+
visualizar_grafo, agregar_aporte
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
# Sample vocabulary for concept extraction
|
7 |
VOCABULARY = ["algoritmos", "inteligencia artificial", "políticas públicas",
|
8 |
"educación a distancia", "gobernanza", "educación superior"]
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
G = nx.DiGraph()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
inicializar_grafo() # Call this before displaying the graph
|
12 |
|
13 |
iface = gr.Blocks()
|