Create ui.py
Browse files- modules/ui.py +231 -0
modules/ui.py
ADDED
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import matplotlib.pyplot as plt
|
3 |
+
import pandas as pd
|
4 |
+
import io
|
5 |
+
import base64
|
6 |
+
from spacy import displacy
|
7 |
+
import re
|
8 |
+
from .morpho_analysis import POS_COLORS, POS_TRANSLATIONS
|
9 |
+
from .auth import authenticate_user, register_user, get_user_role
|
10 |
+
from .database import get_student_data, store_analysis_result
|
11 |
+
from .morpho_analysis import get_repeated_words_colors, highlight_repeated_words
|
12 |
+
from .syntax_analysis import visualize_syntax
|
13 |
+
|
14 |
+
def login_form():
|
15 |
+
username = st.text_input("Usuario")
|
16 |
+
password = st.text_input("Contraseña", type='password')
|
17 |
+
captcha_answer = st.text_input("Captcha: ¿Cuánto es 2 + 3?")
|
18 |
+
|
19 |
+
if st.button("Iniciar Sesión"):
|
20 |
+
if captcha_answer == "5":
|
21 |
+
if authenticate_user(username, password):
|
22 |
+
st.success(f"Bienvenido, {username}!")
|
23 |
+
st.session_state.logged_in = True
|
24 |
+
st.session_state.username = username
|
25 |
+
st.session_state.role = get_user_role(username)
|
26 |
+
st.experimental_rerun()
|
27 |
+
else:
|
28 |
+
st.error("Usuario o contraseña incorrectos")
|
29 |
+
else:
|
30 |
+
st.error("Captcha incorrecto")
|
31 |
+
|
32 |
+
def register_form():
|
33 |
+
new_username = st.text_input("Nuevo Usuario")
|
34 |
+
new_password = st.text_input("Nueva Contraseña", type='password')
|
35 |
+
carrera = st.text_input("Carrera")
|
36 |
+
captcha_answer = st.text_input("Captcha: ¿Cuánto es 3 + 4?")
|
37 |
+
|
38 |
+
if st.button("Registrarse"):
|
39 |
+
if captcha_answer == "7":
|
40 |
+
additional_info = {'carrera': carrera}
|
41 |
+
if register_user(new_username, new_password, additional_info):
|
42 |
+
st.success("Registro exitoso. Por favor, inicia sesión.")
|
43 |
+
else:
|
44 |
+
st.error("El usuario ya existe o ocurrió un error durante el registro")
|
45 |
+
else:
|
46 |
+
st.error("Captcha incorrecto")
|
47 |
+
|
48 |
+
def display_chat_interface():
|
49 |
+
st.markdown("### Chat con AIdeaText")
|
50 |
+
|
51 |
+
if 'chat_history' not in st.session_state:
|
52 |
+
st.session_state.chat_history = []
|
53 |
+
|
54 |
+
for i, (role, text) in enumerate(st.session_state.chat_history):
|
55 |
+
if role == "user":
|
56 |
+
st.text_area(f"Tú:", value=text, height=50, key=f"user_message_{i}", disabled=True)
|
57 |
+
else:
|
58 |
+
st.text_area(f"AIdeaText:", value=text, height=50, key=f"bot_message_{i}", disabled=True)
|
59 |
+
|
60 |
+
user_input = st.text_input("Escribe tu mensaje aquí:")
|
61 |
+
|
62 |
+
if st.button("Enviar"):
|
63 |
+
if user_input:
|
64 |
+
st.session_state.chat_history.append(("user", user_input))
|
65 |
+
response = get_chatbot_response(user_input)
|
66 |
+
st.session_state.chat_history.append(("bot", response))
|
67 |
+
st.experimental_rerun()
|
68 |
+
|
69 |
+
def display_student_progress(username, lang_code='es'):
|
70 |
+
student_data = get_student_data(username)
|
71 |
+
|
72 |
+
if student_data is None:
|
73 |
+
st.warning("No se encontraron datos para este estudiante.")
|
74 |
+
st.info("Intenta realizar algunos análisis de texto primero.")
|
75 |
+
return
|
76 |
+
|
77 |
+
st.title(f"Progreso de {username}")
|
78 |
+
|
79 |
+
if student_data['entries_count'] > 0:
|
80 |
+
if 'word_count' in student_data and student_data['word_count']:
|
81 |
+
st.subheader("Total de palabras por categoría gramatical")
|
82 |
+
|
83 |
+
df = pd.DataFrame(list(student_data['word_count'].items()), columns=['category', 'count'])
|
84 |
+
df['label'] = df.apply(lambda x: f"{POS_TRANSLATIONS[lang_code].get(x['category'], x['category'])}", axis=1)
|
85 |
+
|
86 |
+
df = df.sort_values('count', ascending=False)
|
87 |
+
|
88 |
+
fig, ax = plt.subplots(figsize=(12, 6))
|
89 |
+
bars = ax.bar(df['label'], df['count'], color=[POS_COLORS.get(cat, '#CCCCCC') for cat in df['category']])
|
90 |
+
|
91 |
+
ax.set_xlabel('Categoría Gramatical')
|
92 |
+
ax.set_ylabel('Cantidad de Palabras')
|
93 |
+
ax.set_title('Total de palabras por categoría gramatical')
|
94 |
+
plt.xticks(rotation=45, ha='right')
|
95 |
+
|
96 |
+
for bar in bars:
|
97 |
+
height = bar.get_height()
|
98 |
+
ax.text(bar.get_x() + bar.get_width()/2., height,
|
99 |
+
f'{height}',
|
100 |
+
ha='center', va='bottom')
|
101 |
+
|
102 |
+
plt.tight_layout()
|
103 |
+
|
104 |
+
buf = io.BytesIO()
|
105 |
+
fig.savefig(buf, format='png')
|
106 |
+
buf.seek(0)
|
107 |
+
st.image(buf, use_column_width=True)
|
108 |
+
else:
|
109 |
+
st.info("No hay datos de conteo de palabras disponibles.")
|
110 |
+
|
111 |
+
st.header("Diagramas de Arco")
|
112 |
+
with st.expander("Ver todos los Diagramas de Arco"):
|
113 |
+
for i, entry in enumerate(student_data['entries']):
|
114 |
+
if 'arc_diagrams' in entry and entry['arc_diagrams']:
|
115 |
+
st.subheader(f"Entrada {i+1} - {entry['timestamp']}")
|
116 |
+
st.write(entry['arc_diagrams'][0], unsafe_allow_html=True)
|
117 |
+
|
118 |
+
st.header("Diagramas de Red")
|
119 |
+
with st.expander("Ver todos los Diagramas de Red"):
|
120 |
+
for i, entry in enumerate(student_data['entries']):
|
121 |
+
if 'network_diagram' in entry and entry['network_diagram']:
|
122 |
+
st.subheader(f"Entrada {i+1} - {entry['timestamp']}")
|
123 |
+
try:
|
124 |
+
image_bytes = base64.b64decode(entry['network_diagram'])
|
125 |
+
st.image(image_bytes)
|
126 |
+
except Exception as e:
|
127 |
+
st.error(f"Error al mostrar el diagrama de red: {str(e)}")
|
128 |
+
else:
|
129 |
+
st.warning("No se encontraron entradas para este estudiante.")
|
130 |
+
st.info("Intenta realizar algunos análisis de texto primero.")
|
131 |
+
|
132 |
+
def display_text_analysis_interface(nlp_models, lang_code):
|
133 |
+
translations = {
|
134 |
+
'es': {
|
135 |
+
'title': "AIdeaText - Análisis morfológico y sintáctico",
|
136 |
+
'input_label': "Ingrese un texto para analizar (máx. 5,000 palabras):",
|
137 |
+
'input_placeholder': "El objetivo de esta aplicación es que mejore sus habilidades de redacción...",
|
138 |
+
'analyze_button': "Analizar texto",
|
139 |
+
'repeated_words': "Palabras repetidas",
|
140 |
+
'legend': "Leyenda: Categorías gramaticales",
|
141 |
+
'arc_diagram': "Análisis sintáctico: Diagrama de arco",
|
142 |
+
'network_diagram': "Análisis sintáctico: Diagrama de red",
|
143 |
+
'sentence': "Oración"
|
144 |
+
},
|
145 |
+
'en': {
|
146 |
+
'title': "AIdeaText - Morphological and Syntactic Analysis",
|
147 |
+
'input_label': "Enter a text to analyze (max 5,000 words):",
|
148 |
+
'input_placeholder': "The goal of this app is for you to improve your writing skills...",
|
149 |
+
'analyze_button': "Analyze text",
|
150 |
+
'repeated_words': "Repeated words",
|
151 |
+
'legend': "Legend: Grammatical categories",
|
152 |
+
'arc_diagram': "Syntactic analysis: Arc diagram",
|
153 |
+
'network_diagram': "Syntactic analysis: Network diagram",
|
154 |
+
'sentence': "Sentence"
|
155 |
+
},
|
156 |
+
'fr': {
|
157 |
+
'title': "AIdeaText - Analyse morphologique et syntaxique",
|
158 |
+
'input_label': "Entrez un texte à analyser (max 5 000 mots) :",
|
159 |
+
'input_placeholder': "Le but de cette application est d'améliorer vos compétences en rédaction...",
|
160 |
+
'analyze_button': "Analyser le texte",
|
161 |
+
'repeated_words': "Mots répétés",
|
162 |
+
'legend': "Légende : Catégories grammaticales",
|
163 |
+
'arc_diagram': "Analyse syntaxique : Diagramme en arc",
|
164 |
+
'network_diagram': "Analyse syntaxique : Diagramme de réseau",
|
165 |
+
'sentence': "Phrase"
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
t = translations[lang_code]
|
170 |
+
|
171 |
+
if 'input_text' not in st.session_state:
|
172 |
+
st.session_state.input_text = ""
|
173 |
+
|
174 |
+
sentence_input = st.text_area(
|
175 |
+
t['input_label'],
|
176 |
+
height=150,
|
177 |
+
placeholder=t['input_placeholder'],
|
178 |
+
value=st.session_state.input_text,
|
179 |
+
key=f"text_input_{lang_code}"
|
180 |
+
)
|
181 |
+
st.session_state.input_text = sentence_input
|
182 |
+
|
183 |
+
if st.button(t['analyze_button'], key=f"analyze_button_{lang_code}"):
|
184 |
+
if sentence_input:
|
185 |
+
doc = nlp_models[lang_code](sentence_input)
|
186 |
+
|
187 |
+
with st.expander(t['repeated_words'], expanded=True):
|
188 |
+
word_colors = get_repeated_words_colors(doc)
|
189 |
+
highlighted_text = highlight_repeated_words(doc, word_colors)
|
190 |
+
st.markdown(highlighted_text, unsafe_allow_html=True)
|
191 |
+
|
192 |
+
st.markdown(f"##### {t['legend']}")
|
193 |
+
legend_html = "<div style='display: flex; flex-wrap: wrap;'>"
|
194 |
+
for pos, color in POS_COLORS.items():
|
195 |
+
if pos in POS_TRANSLATIONS:
|
196 |
+
legend_html += f"<div style='margin-right: 10px;'><span style='background-color: {color}; padding: 2px 5px;'>{POS_TRANSLATIONS[pos]}</span></div>"
|
197 |
+
legend_html += "</div>"
|
198 |
+
st.markdown(legend_html, unsafe_allow_html=True)
|
199 |
+
|
200 |
+
with st.expander(t['arc_diagram'], expanded=True):
|
201 |
+
sentences = list(doc.sents)
|
202 |
+
arc_diagrams = []
|
203 |
+
for i, sent in enumerate(sentences):
|
204 |
+
st.subheader(f"{t['sentence']} {i+1}")
|
205 |
+
html = displacy.render(sent, style="dep", options={"distance": 100})
|
206 |
+
html = html.replace('height="375"', 'height="200"')
|
207 |
+
html = re.sub(r'<svg[^>]*>', lambda m: m.group(0).replace('height="450"', 'height="300"'), html)
|
208 |
+
html = re.sub(r'<g [^>]*transform="translate\((\d+),(\d+)\)"', lambda m: f'<g transform="translate({m.group(1)},50)"', html)
|
209 |
+
st.write(html, unsafe_allow_html=True)
|
210 |
+
arc_diagrams.append(html)
|
211 |
+
|
212 |
+
with st.expander(t['network_diagram'], expanded=True):
|
213 |
+
fig = visualize_syntax(sentence_input, nlp_models[lang_code], lang_code)
|
214 |
+
st.pyplot(fig)
|
215 |
+
|
216 |
+
if store_analysis_result(
|
217 |
+
st.session_state.username,
|
218 |
+
sentence_input,
|
219 |
+
word_colors,
|
220 |
+
arc_diagrams,
|
221 |
+
fig
|
222 |
+
):
|
223 |
+
st.success("Análisis guardado correctamente.")
|
224 |
+
else:
|
225 |
+
st.error("Hubo un problema al guardar el análisis. Por favor, inténtelo de nuevo.")
|
226 |
+
st.error(f"Falló el guardado del análisis. Username: {st.session_state.username}")
|
227 |
+
|
228 |
+
def get_chatbot_response(input_text):
|
229 |
+
# Esta función debe ser implementada o importada de otro módulo
|
230 |
+
# Por ahora, retornamos un mensaje genérico
|
231 |
+
return "Lo siento, el chatbot no está disponible en este momento."
|