Spaces:
Running
Running
Rename app.py to coach_base.py
Browse files- app.py → coach_base.py +23 -321
app.py → coach_base.py
RENAMED
@@ -1,6 +1,5 @@
|
|
1 |
import gradio as gr
|
2 |
from datetime import datetime
|
3 |
-
from sentence_transformers import SentenceTransformer
|
4 |
import numpy as np
|
5 |
from typing import Dict, List, Tuple
|
6 |
import matplotlib.pyplot as plt
|
@@ -9,9 +8,6 @@ import seaborn as sns
|
|
9 |
import pandas as pd
|
10 |
from collections import Counter
|
11 |
|
12 |
-
# Load embeddings model
|
13 |
-
model = SentenceTransformer('all-MiniLM-L6-v2')
|
14 |
-
|
15 |
# Define questions with categories and context
|
16 |
PERGUNTAS = [
|
17 |
{
|
@@ -41,7 +37,7 @@ PERGUNTAS = [
|
|
41 |
}
|
42 |
]
|
43 |
|
44 |
-
#
|
45 |
RESPOSTAS_COACH = {
|
46 |
"autoconhecimento": {
|
47 |
"positive": [
|
@@ -125,7 +121,7 @@ class EnhancedCoach:
|
|
125 |
self.inicio = datetime.now()
|
126 |
self.historico_respostas = []
|
127 |
self.tempo_ultima_resposta = datetime.now()
|
128 |
-
|
129 |
def analisar_sentimento(self, texto: str) -> str:
|
130 |
"""Analisa o sentimento geral da resposta."""
|
131 |
positive_words = ["consegui", "superei", "aprendi", "melhorei", "efetivo"]
|
@@ -148,36 +144,19 @@ class EnhancedCoach:
|
|
148 |
if any(action in sentence.lower() for action in ["eu", "minha", "realizei", "fiz"]):
|
149 |
return sentence.strip()
|
150 |
return texto.split('.')[0].strip()
|
151 |
-
|
152 |
-
def encontrar_melhor_resposta(self, texto_usuario: str, categoria: str) -> str:
|
153 |
-
sentimento = self.analisar_sentimento(texto_usuario)
|
154 |
-
acao_especifica = self.extrair_acao_especifica(texto_usuario)
|
155 |
|
156 |
-
respostas_categoria = RESPOSTAS_COACH[categoria][sentimento]
|
157 |
-
user_embedding = model.encode(texto_usuario)
|
158 |
-
|
159 |
-
melhor_resposta = None
|
160 |
-
maior_similaridade = -1
|
161 |
-
|
162 |
-
for template in respostas_categoria:
|
163 |
-
context_embedding = model.encode(template["context"])
|
164 |
-
similaridade = np.dot(user_embedding, context_embedding)
|
165 |
-
|
166 |
-
if similaridade > maior_similaridade:
|
167 |
-
maior_similaridade = similaridade
|
168 |
-
melhor_resposta = template["response"]
|
169 |
-
|
170 |
-
return melhor_resposta.format(specific_action=acao_especifica.lower())
|
171 |
-
|
172 |
def gerar_resposta(self, texto_usuario: str) -> str:
|
173 |
self.tempo_ultima_resposta = datetime.now()
|
174 |
pergunta_atual = PERGUNTAS[self.pergunta_atual]
|
175 |
self.historico_respostas.append(texto_usuario)
|
176 |
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
|
|
|
|
|
|
181 |
|
182 |
# Analisa padrões nas respostas anteriores
|
183 |
padrao_identificado = ""
|
@@ -190,9 +169,9 @@ class EnhancedCoach:
|
|
190 |
elif sentimento_atual == sentimento_anterior == "improvement":
|
191 |
padrao_identificado = "\n\n💡 Percebo que você está identificando áreas de desenvolvimento. Vamos focar em estratégias práticas para esses desafios."
|
192 |
|
193 |
-
|
194 |
|
195 |
-
{
|
196 |
|
197 |
#### Pontos para Aprofundamento:
|
198 |
1. Como essa experiência se conecta com seus valores de liderança?
|
@@ -202,7 +181,7 @@ class EnhancedCoach:
|
|
202 |
self.pergunta_atual += 1
|
203 |
if self.pergunta_atual < len(PERGUNTAS):
|
204 |
proxima = PERGUNTAS[self.pergunta_atual]
|
205 |
-
|
206 |
|
207 |
### Próxima Reflexão: {proxima['categoria'].title()} 🎯
|
208 |
|
@@ -211,19 +190,15 @@ class EnhancedCoach:
|
|
211 |
Tome um momento para refletir e conectar com suas experiências..."""
|
212 |
else:
|
213 |
tempo = (datetime.now() - self.inicio).seconds // 60
|
214 |
-
|
215 |
-
|
216 |
-
return
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
delta = datetime.now() - self.tempo_ultima_resposta
|
221 |
-
minutos = delta.seconds // 60
|
222 |
-
segundos = delta.seconds % 60
|
223 |
-
return f"{minutos}:{segundos:02d}"
|
224 |
|
225 |
def gerar_sumario_final(self, tempo: int) -> str:
|
226 |
-
# Analisa padrões gerais
|
227 |
sentimentos = [self.analisar_sentimento(resp) for resp in self.historico_respostas]
|
228 |
predominante = max(set(sentimentos), key=sentimentos.count)
|
229 |
|
@@ -252,286 +227,13 @@ Tome um momento para refletir e conectar com suas experiências..."""
|
|
252 |
Deseja iniciar uma nova jornada de desenvolvimento com outros temas?"""
|
253 |
|
254 |
def primeira_pergunta(self):
|
255 |
-
return
|
|
|
|
|
256 |
|
257 |
Vamos explorar aspectos importantes da sua liderança através de reflexões guiadas.
|
258 |
|
259 |
{PERGUNTAS[0]['pergunta']}
|
260 |
|
261 |
Tome um momento para conectar com suas experiências e compartilhe sua perspectiva..."""
|
262 |
-
|
263 |
-
def analyze_sentiment_trend(respostas: List[str]) -> plt.Figure:
|
264 |
-
"""Generate sentiment analysis plot"""
|
265 |
-
coach = EnhancedCoach()
|
266 |
-
sentimentos = [coach.analisar_sentimento(resp) for resp in respostas]
|
267 |
-
|
268 |
-
# Convert sentiments to numeric values
|
269 |
-
valor_sentimento = {
|
270 |
-
'positive': 1,
|
271 |
-
'neutral': 0,
|
272 |
-
'improvement': -1
|
273 |
-
}
|
274 |
-
valores = [valor_sentimento[s] for s in sentimentos]
|
275 |
-
|
276 |
-
# Create the plot
|
277 |
-
fig, ax = plt.subplots(figsize=(8, 4))
|
278 |
-
sns.lineplot(data=valores, marker='o', ax=ax)
|
279 |
-
|
280 |
-
ax.set_title('Tendência de Sentimento nas Respostas')
|
281 |
-
ax.set_xlabel('Número da Resposta')
|
282 |
-
ax.set_ylabel('Sentimento')
|
283 |
-
ax.set_ylim(-1.5, 1.5)
|
284 |
-
ax.grid(True)
|
285 |
-
|
286 |
-
# Add horizontal lines for reference
|
287 |
-
ax.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
|
288 |
-
|
289 |
-
return fig
|
290 |
-
|
291 |
-
def generate_word_cloud(respostas: List[str]) -> plt.Figure:
|
292 |
-
"""Generate word cloud visualization"""
|
293 |
-
# Combine all responses
|
294 |
-
texto_completo = ' '.join(respostas)
|
295 |
-
|
296 |
-
# Create word cloud
|
297 |
-
wordcloud = WordCloud(
|
298 |
-
width=800,
|
299 |
-
height=400,
|
300 |
-
background_color='white',
|
301 |
-
colormap='viridis',
|
302 |
-
max_words=100
|
303 |
-
).generate(texto_completo)
|
304 |
-
|
305 |
-
# Create the plot
|
306 |
-
fig, ax = plt.subplots(figsize=(10, 5))
|
307 |
-
ax.imshow(wordcloud, interpolation='bilinear')
|
308 |
-
ax.axis('off')
|
309 |
-
ax.set_title('Nuvem de Palavras das Reflexões')
|
310 |
-
|
311 |
-
return fig
|
312 |
-
|
313 |
-
def analyze_themes(respostas: List[str]) -> Tuple[str, str]:
|
314 |
-
"""Analyze strong themes and development areas"""
|
315 |
-
coach = EnhancedCoach()
|
316 |
-
temas_fortes = []
|
317 |
-
areas_desenvolvimento = []
|
318 |
-
|
319 |
-
for resp in respostas:
|
320 |
-
sentimento = coach.analisar_sentimento(resp)
|
321 |
-
if sentimento == "positive":
|
322 |
-
temas_fortes.append("- " + coach.extrair_acao_especifica(resp))
|
323 |
-
elif sentimento == "improvement":
|
324 |
-
areas_desenvolvimento.append("- " + coach.extrair_acao_especifica(resp))
|
325 |
-
|
326 |
-
temas_fortes_str = "\n".join(temas_fortes[:3]) if temas_fortes else "Análise em andamento..."
|
327 |
-
areas_desenvolvimento_str = "\n".join(areas_desenvolvimento[:3]) if areas_desenvolvimento else "Análise em andamento..."
|
328 |
-
|
329 |
-
return temas_fortes_str, areas_desenvolvimento_str
|
330 |
-
|
331 |
-
def criar_interface():
|
332 |
-
coach = EnhancedCoach()
|
333 |
-
|
334 |
-
with gr.Blocks(title="Coach de Liderança", theme=gr.themes.Soft()) as app:
|
335 |
-
with gr.Row():
|
336 |
-
with gr.Column(scale=2):
|
337 |
-
gr.Markdown("""
|
338 |
-
# 🚀 Coach de Liderança
|
339 |
-
|
340 |
-
Desenvolva sua liderança através de reflexão guiada e feedback personalizado.
|
341 |
-
""")
|
342 |
-
|
343 |
-
with gr.Column(scale=1):
|
344 |
-
timer = gr.Number(
|
345 |
-
value=0,
|
346 |
-
label="⏱️ Tempo de Reflexão (minutos)",
|
347 |
-
interactive=False
|
348 |
-
)
|
349 |
-
progress = gr.Slider(
|
350 |
-
value=0,
|
351 |
-
minimum=0,
|
352 |
-
maximum=len(PERGUNTAS),
|
353 |
-
step=1,
|
354 |
-
label="📊 Progresso",
|
355 |
-
interactive=False
|
356 |
-
)
|
357 |
-
|
358 |
-
with gr.Tabs() as tabs:
|
359 |
-
with gr.Tab("💭 Sessão Atual"):
|
360 |
-
chat = gr.Chatbot(
|
361 |
-
value=[[None, coach.primeira_pergunta()]],
|
362 |
-
height=500,
|
363 |
-
show_label=False,
|
364 |
-
type="messages" # Fixing the deprecation warning
|
365 |
-
)
|
366 |
-
|
367 |
-
with gr.Row():
|
368 |
-
with gr.Column(scale=4):
|
369 |
-
txt = gr.Textbox(
|
370 |
-
placeholder="Compartilhe sua reflexão aqui...",
|
371 |
-
lines=4,
|
372 |
-
label="Sua Resposta"
|
373 |
-
)
|
374 |
-
|
375 |
-
with gr.Column(scale=1, min_width=100):
|
376 |
-
with gr.Row():
|
377 |
-
btn = gr.Button("Enviar", variant="primary")
|
378 |
-
clear = gr.Button("Limpar")
|
379 |
-
|
380 |
-
with gr.Row():
|
381 |
-
tema_atual = gr.Textbox(
|
382 |
-
value="Autoconhecimento",
|
383 |
-
label="🎯 Tema Atual",
|
384 |
-
interactive=False
|
385 |
-
)
|
386 |
-
tempo_resposta = gr.Textbox(
|
387 |
-
value="0:00",
|
388 |
-
label="⏱️ Tempo nesta resposta",
|
389 |
-
interactive=False
|
390 |
-
)
|
391 |
-
|
392 |
-
with gr.Tab("📊 Insights"):
|
393 |
-
with gr.Row():
|
394 |
-
with gr.Column():
|
395 |
-
sentiment_chart = gr.Plot(label="Análise de Sentimento")
|
396 |
-
with gr.Column():
|
397 |
-
word_cloud = gr.Plot(label="Nuvem de Palavras")
|
398 |
-
|
399 |
-
with gr.Row():
|
400 |
-
temas_fortes = gr.Textbox(
|
401 |
-
label="💪 Temas com Mais Confiança",
|
402 |
-
interactive=False,
|
403 |
-
lines=3
|
404 |
-
)
|
405 |
-
areas_desenvolvimento = gr.Textbox(
|
406 |
-
label="🎯 Áreas para Desenvolvimento",
|
407 |
-
interactive=False,
|
408 |
-
lines=3
|
409 |
-
)
|
410 |
-
|
411 |
-
with gr.Tab("📝 Notas & Recursos"):
|
412 |
-
with gr.Row():
|
413 |
-
notas = gr.Textbox(
|
414 |
-
placeholder="Faça anotações durante sua jornada...",
|
415 |
-
label="📝 Minhas Notas",
|
416 |
-
lines=5
|
417 |
-
)
|
418 |
-
|
419 |
-
with gr.Row():
|
420 |
-
with gr.Accordion("📚 Recursos por Tema", open=False):
|
421 |
-
gr.Markdown("""
|
422 |
-
### 🎯 Autoconhecimento
|
423 |
-
- [Artigo] Desenvolvendo Autoconsciência na Liderança
|
424 |
-
- [Exercício] Reflexão sobre Valores e Propósito
|
425 |
-
- [Ferramenta] Template de Diário de Liderança
|
426 |
-
|
427 |
-
### 💬 Comunicação
|
428 |
-
- [Guia] Comunicação Assertiva na Liderança
|
429 |
-
- [Checklist] Preparação para Feedbacks Difíceis
|
430 |
-
- [Framework] Estrutura de Comunicação Situacional
|
431 |
-
|
432 |
-
### 🤔 Tomada de Decisão
|
433 |
-
- [Modelo] Framework para Decisões Complexas
|
434 |
-
- [Exercício] Análise de Decisões Passadas
|
435 |
-
- [Template] Documentação de Decisões Importantes
|
436 |
-
""")
|
437 |
-
|
438 |
-
def atualizar_timer():
|
439 |
-
"""Update session timer"""
|
440 |
-
tempo = int((datetime.now() - coach.inicio).total_seconds() / 60)
|
441 |
-
return tempo
|
442 |
-
|
443 |
-
def responder(mensagem, historico):
|
444 |
-
"""Process user response and update interface"""
|
445 |
-
if not mensagem.strip():
|
446 |
-
return {
|
447 |
-
txt: "",
|
448 |
-
chat: historico
|
449 |
-
}
|
450 |
-
|
451 |
-
# Convert message to the new format
|
452 |
-
nova_mensagem = {"role": "user", "content": mensagem}
|
453 |
-
|
454 |
-
# Generate response
|
455 |
-
resposta = coach.gerar_resposta(mensagem)
|
456 |
-
resposta_formatada = {"role": "assistant", "content": resposta}
|
457 |
-
|
458 |
-
# Update history in the new format
|
459 |
-
historico.append((nova_mensagem, resposta_formatada))
|
460 |
-
|
461 |
-
# Update visualizations
|
462 |
-
sentiment_analysis = None
|
463 |
-
word_cloud_plot = None
|
464 |
-
strong_themes = ""
|
465 |
-
development_areas = ""
|
466 |
-
|
467 |
-
if len(coach.historico_respostas) > 1:
|
468 |
-
sentiment_analysis = analyze_sentiment_trend(coach.historico_respostas)
|
469 |
-
word_cloud_plot = generate_word_cloud(coach.historico_respostas)
|
470 |
-
strong_themes, development_areas = analyze_themes(coach.historico_respostas)
|
471 |
-
|
472 |
-
# Update timer
|
473 |
-
tempo_atual = atualizar_timer()
|
474 |
-
|
475 |
-
return {
|
476 |
-
txt: "",
|
477 |
-
chat: historico,
|
478 |
-
timer: tempo_atual,
|
479 |
-
progress: coach.pergunta_atual,
|
480 |
-
tema_atual: PERGUNTAS[min(coach.pergunta_atual, len(PERGUNTAS)-1)]["categoria"].title(),
|
481 |
-
tempo_resposta: "0:00",
|
482 |
-
sentiment_chart: sentiment_analysis,
|
483 |
-
word_cloud: word_cloud_plot,
|
484 |
-
temas_fortes: strong_themes,
|
485 |
-
areas_desenvolvimento: development_areas
|
486 |
-
}
|
487 |
-
|
488 |
-
def limpar_chat():
|
489 |
-
"""Reset chat and all related components"""
|
490 |
-
coach.__init__() # Reset coach state
|
491 |
-
primeira_msg = {"role": "assistant", "content": coach.primeira_pergunta()}
|
492 |
-
return {
|
493 |
-
chat: [[None, primeira_msg]],
|
494 |
-
txt: "",
|
495 |
-
progress: 0,
|
496 |
-
tema_atual: "Autoconhecimento",
|
497 |
-
tempo_resposta: "0:00",
|
498 |
-
sentiment_chart: None,
|
499 |
-
word_cloud: None,
|
500 |
-
temas_fortes: "",
|
501 |
-
areas_desenvolvimento: ""
|
502 |
-
}
|
503 |
-
|
504 |
-
# Event handlers
|
505 |
-
txt.submit(
|
506 |
-
responder,
|
507 |
-
[txt, chat],
|
508 |
-
[txt, chat, timer, progress, tema_atual, tempo_resposta,
|
509 |
-
sentiment_chart, word_cloud, temas_fortes, areas_desenvolvimento]
|
510 |
-
)
|
511 |
-
|
512 |
-
btn.click(
|
513 |
-
responder,
|
514 |
-
[txt, chat],
|
515 |
-
[txt, chat, timer, progress, tema_atual, tempo_resposta,
|
516 |
-
sentiment_chart, word_cloud, temas_fortes, areas_desenvolvimento]
|
517 |
-
)
|
518 |
-
|
519 |
-
clear.click(
|
520 |
-
limpar_chat,
|
521 |
-
None,
|
522 |
-
[chat, txt, progress, tema_atual, tempo_resposta,
|
523 |
-
sentiment_chart, word_cloud, temas_fortes, areas_desenvolvimento]
|
524 |
-
)
|
525 |
-
|
526 |
-
# Periodic updates
|
527 |
-
gr.on(
|
528 |
-
triggers=[gr.EventListener(every=1)],
|
529 |
-
fn=atualizar_timer,
|
530 |
-
outputs=[timer]
|
531 |
-
)
|
532 |
-
|
533 |
-
return app
|
534 |
-
|
535 |
-
if __name__ == "__main__":
|
536 |
-
app = criar_interface()
|
537 |
-
app.launch()
|
|
|
1 |
import gradio as gr
|
2 |
from datetime import datetime
|
|
|
3 |
import numpy as np
|
4 |
from typing import Dict, List, Tuple
|
5 |
import matplotlib.pyplot as plt
|
|
|
8 |
import pandas as pd
|
9 |
from collections import Counter
|
10 |
|
|
|
|
|
|
|
11 |
# Define questions with categories and context
|
12 |
PERGUNTAS = [
|
13 |
{
|
|
|
37 |
}
|
38 |
]
|
39 |
|
40 |
+
# Response templates
|
41 |
RESPOSTAS_COACH = {
|
42 |
"autoconhecimento": {
|
43 |
"positive": [
|
|
|
121 |
self.inicio = datetime.now()
|
122 |
self.historico_respostas = []
|
123 |
self.tempo_ultima_resposta = datetime.now()
|
124 |
+
|
125 |
def analisar_sentimento(self, texto: str) -> str:
|
126 |
"""Analisa o sentimento geral da resposta."""
|
127 |
positive_words = ["consegui", "superei", "aprendi", "melhorei", "efetivo"]
|
|
|
144 |
if any(action in sentence.lower() for action in ["eu", "minha", "realizei", "fiz"]):
|
145 |
return sentence.strip()
|
146 |
return texto.split('.')[0].strip()
|
|
|
|
|
|
|
|
|
147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
def gerar_resposta(self, texto_usuario: str) -> str:
|
149 |
self.tempo_ultima_resposta = datetime.now()
|
150 |
pergunta_atual = PERGUNTAS[self.pergunta_atual]
|
151 |
self.historico_respostas.append(texto_usuario)
|
152 |
|
153 |
+
# Analisa sentimento e extrai ação
|
154 |
+
sentimento = self.analisar_sentimento(texto_usuario)
|
155 |
+
acao_especifica = self.extrair_acao_especifica(texto_usuario)
|
156 |
+
|
157 |
+
# Seleciona resposta apropriada
|
158 |
+
respostas_categoria = RESPOSTAS_COACH[pergunta_atual["categoria"]][sentimento]
|
159 |
+
resposta = respostas_categoria[0]["response"].format(specific_action=acao_especifica.lower())
|
160 |
|
161 |
# Analisa padrões nas respostas anteriores
|
162 |
padrao_identificado = ""
|
|
|
169 |
elif sentimento_atual == sentimento_anterior == "improvement":
|
170 |
padrao_identificado = "\n\n💡 Percebo que você está identificando áreas de desenvolvimento. Vamos focar em estratégias práticas para esses desafios."
|
171 |
|
172 |
+
resposta_completa = f"""### Feedback Personalizado 💭
|
173 |
|
174 |
+
{resposta}{padrao_identificado}
|
175 |
|
176 |
#### Pontos para Aprofundamento:
|
177 |
1. Como essa experiência se conecta com seus valores de liderança?
|
|
|
181 |
self.pergunta_atual += 1
|
182 |
if self.pergunta_atual < len(PERGUNTAS):
|
183 |
proxima = PERGUNTAS[self.pergunta_atual]
|
184 |
+
resposta_completa += f"""
|
185 |
|
186 |
### Próxima Reflexão: {proxima['categoria'].title()} 🎯
|
187 |
|
|
|
190 |
Tome um momento para refletir e conectar com suas experiências..."""
|
191 |
else:
|
192 |
tempo = (datetime.now() - self.inicio).seconds // 60
|
193 |
+
resposta_completa += self.gerar_sumario_final(tempo)
|
194 |
+
|
195 |
+
return {
|
196 |
+
"role": "assistant",
|
197 |
+
"content": resposta_completa
|
198 |
+
}
|
|
|
|
|
|
|
|
|
199 |
|
200 |
def gerar_sumario_final(self, tempo: int) -> str:
|
201 |
+
# Analisa padrões gerais
|
202 |
sentimentos = [self.analisar_sentimento(resp) for resp in self.historico_respostas]
|
203 |
predominante = max(set(sentimentos), key=sentimentos.count)
|
204 |
|
|
|
227 |
Deseja iniciar uma nova jornada de desenvolvimento com outros temas?"""
|
228 |
|
229 |
def primeira_pergunta(self):
|
230 |
+
return {
|
231 |
+
"role": "assistant",
|
232 |
+
"content": f"""### 👋 Bem-vindo à sua Jornada de Desenvolvimento!
|
233 |
|
234 |
Vamos explorar aspectos importantes da sua liderança através de reflexões guiadas.
|
235 |
|
236 |
{PERGUNTAS[0]['pergunta']}
|
237 |
|
238 |
Tome um momento para conectar com suas experiências e compartilhe sua perspectiva..."""
|
239 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|