Update utils_gdmk.py
Browse files- utils_gdmk.py +130 -39
utils_gdmk.py
CHANGED
@@ -1,71 +1,162 @@
|
|
1 |
import networkx as nx
|
2 |
import matplotlib.pyplot as plt
|
3 |
import json
|
4 |
-
import textwrap
|
5 |
import requests
|
6 |
import pandas as pd
|
7 |
import os
|
|
|
|
|
|
|
8 |
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
|
13 |
-
|
|
|
|
|
14 |
|
15 |
-
url = f"https://api.airtable.com/v0/{
|
16 |
headers = {
|
17 |
-
"Authorization": f"Bearer {
|
18 |
"Content-Type": "application/json"
|
19 |
}
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
def cargar_desde_airtable():
|
22 |
-
response = requests.get(url, headers=headers)
|
23 |
-
|
24 |
if response.status_code != 200:
|
25 |
print(f"Error: {response.status_code} - {response.text}")
|
26 |
return pd.DataFrame(columns=["Nombre", "Conceptos"])
|
|
|
27 |
records = response.json().get("records", [])
|
28 |
-
aportes = [
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
def inicializar_grafo():
|
35 |
df = cargar_desde_airtable()
|
|
|
36 |
for _, row in df.iterrows():
|
37 |
-
nombre
|
38 |
-
|
39 |
-
if not G.has_node(termino):
|
40 |
-
G.add_node(termino, color='gray')
|
41 |
-
if not G.has_edge(nombre, termino):
|
42 |
-
G.add_edge(norma, enfoque)
|
43 |
-
|
44 |
-
def guardar_en_airtable(nombre, conceptos):
|
45 |
-
data = {"fields": {"Nombre": nombre, "Conceptos": conceptos}}
|
46 |
-
requests.post(url, headers=headers, json=data)
|
47 |
-
|
48 |
-
def agregar_aporte(nombre, enfoque, norma, texto):
|
49 |
-
textox = wrap_text(f"{nombre}: {texto}")
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
def visualizar_grafo():
|
60 |
plt.figure(figsize=(10, 6))
|
61 |
-
pos = nx.spring_layout(G)
|
62 |
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)
|
67 |
-
|
68 |
-
plt.title("Red de Aportes")
|
69 |
plt.savefig("graph.png")
|
70 |
plt.close()
|
71 |
return "graph.png"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 openai
|
9 |
+
import scipy as sp
|
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
|