|
|
"""Página de análises de tempo e movimentos do checklist""" |
|
|
|
|
|
import streamlit as st |
|
|
import pandas as pd |
|
|
import plotly.express as px |
|
|
import plotly.graph_objects as go |
|
|
from utils.database import get_checklist_analytics, get_checklist_with_items |
|
|
from datetime import datetime, timedelta |
|
|
|
|
|
st.set_page_config( |
|
|
page_title="Análises de Tempo", |
|
|
page_icon="📊", |
|
|
layout="wide" |
|
|
) |
|
|
|
|
|
def format_time(seconds): |
|
|
"""Formata segundos em formato legível""" |
|
|
if seconds is None or seconds == 0: |
|
|
return "0s" |
|
|
|
|
|
if seconds < 60: |
|
|
return f"{int(seconds)}s" |
|
|
elif seconds < 3600: |
|
|
minutes = int(seconds // 60) |
|
|
secs = int(seconds % 60) |
|
|
return f"{minutes}m {secs}s" |
|
|
else: |
|
|
hours = int(seconds // 3600) |
|
|
minutes = int((seconds % 3600) // 60) |
|
|
return f"{hours}h {minutes}m" |
|
|
|
|
|
def main(): |
|
|
if 'current_checklist_id' not in st.session_state or st.session_state.current_checklist_id is None: |
|
|
st.error("Nenhum checklist selecionado!") |
|
|
if st.button("← Voltar para Início"): |
|
|
st.switch_page("app.py") |
|
|
return |
|
|
|
|
|
checklist_id = st.session_state.current_checklist_id |
|
|
|
|
|
try: |
|
|
|
|
|
checklist = get_checklist_with_items(checklist_id) |
|
|
analytics = get_checklist_analytics(checklist_id) |
|
|
|
|
|
if not checklist: |
|
|
st.error("Checklist não encontrado!") |
|
|
return |
|
|
|
|
|
|
|
|
col1, col2 = st.columns([5, 1]) |
|
|
with col1: |
|
|
st.title(f"📊 Análises: {checklist['name']}") |
|
|
if checklist['numero_processo']: |
|
|
st.caption(f"🔢 Processo: {checklist['numero_processo']}") |
|
|
with col2: |
|
|
if st.button("← Voltar"): |
|
|
st.switch_page("pages/dashboard.py") |
|
|
|
|
|
st.markdown("---") |
|
|
|
|
|
|
|
|
stats = analytics['stats'] |
|
|
time_analysis = analytics['time_analysis'] |
|
|
|
|
|
if stats['first_interaction'] and stats['last_interaction']: |
|
|
total_time = stats['last_interaction'] - stats['first_interaction'] |
|
|
total_minutes = total_time.total_seconds() / 60 |
|
|
else: |
|
|
total_minutes = 0 |
|
|
|
|
|
col1, col2, col3, col4 = st.columns(4) |
|
|
|
|
|
with col1: |
|
|
st.metric("Total de Items", stats['total_items']) |
|
|
with col2: |
|
|
st.metric("Items Concluídos", stats['completed_items']) |
|
|
with col3: |
|
|
completion_rate = (stats['completed_items'] / stats['total_items']) * 100 if stats['total_items'] > 0 else 0 |
|
|
st.metric("Taxa de Conclusão", f"{completion_rate:.1f}%") |
|
|
with col4: |
|
|
st.metric("Tempo Total", format_time(total_minutes * 60)) |
|
|
|
|
|
st.markdown("---") |
|
|
|
|
|
|
|
|
if time_analysis: |
|
|
st.markdown("### 📈 Análise de Tempo por Item") |
|
|
|
|
|
|
|
|
df_time = pd.DataFrame(time_analysis) |
|
|
|
|
|
|
|
|
df_time['total_seconds_spent'] = pd.to_numeric(df_time['total_seconds_spent'], errors='coerce').fillna(0) |
|
|
df_time['avg_seconds_per_completion'] = pd.to_numeric(df_time['avg_seconds_per_completion'], errors='coerce').fillna(0) |
|
|
df_time['times_worked'] = pd.to_numeric(df_time['times_worked'], errors='coerce').fillna(0).astype(int) |
|
|
|
|
|
df_time['tempo_formatado'] = df_time['total_seconds_spent'].apply(lambda x: format_time(x) if x else "0s") |
|
|
df_time['tempo_medio_formatado'] = df_time['avg_seconds_per_completion'].apply(lambda x: format_time(x) if x else "0s") |
|
|
df_time['vezes_trabalhado'] = df_time['times_worked'] |
|
|
|
|
|
|
|
|
if not df_time.empty and df_time['total_seconds_spent'].sum() > 0: |
|
|
fig_time = px.bar( |
|
|
df_time.sort_values('total_seconds_spent', ascending=False), |
|
|
x='item_text', |
|
|
y='total_seconds_spent', |
|
|
title="Tempo Total Gasto por Item", |
|
|
labels={'total_seconds_spent': 'Tempo (segundos)', 'item_text': 'Item'} |
|
|
) |
|
|
fig_time.update_layout(xaxis_tickangle=-45) |
|
|
st.plotly_chart(fig_time, use_container_width=True) |
|
|
|
|
|
|
|
|
df_positive = df_time[df_time['total_seconds_spent'] > 0] |
|
|
if not df_positive.empty: |
|
|
fig_pie = px.pie( |
|
|
df_positive, |
|
|
values='total_seconds_spent', |
|
|
names='item_text', |
|
|
title="Distribuição do Tempo por Item" |
|
|
) |
|
|
st.plotly_chart(fig_pie, use_container_width=True) |
|
|
|
|
|
|
|
|
st.markdown("### 📋 Detalhes por Item") |
|
|
|
|
|
df_display = df_time[['item_text', 'vezes_trabalhado', 'tempo_formatado', 'tempo_medio_formatado']].copy() |
|
|
df_display.columns = ['Item', 'Vezes Trabalhado', 'Tempo Total', 'Tempo Médio'] |
|
|
|
|
|
|
|
|
df_display = df_display.loc[df_time.sort_values('total_seconds_spent', ascending=False).index] |
|
|
|
|
|
st.dataframe(df_display, use_container_width=True) |
|
|
|
|
|
|
|
|
st.markdown("### 🔍 Insights") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
with col1: |
|
|
st.markdown("#### Items mais demorados") |
|
|
if not df_time.empty and df_time['total_seconds_spent'].sum() > 0: |
|
|
top_slow = df_time.sort_values('total_seconds_spent', ascending=False).head(3) |
|
|
for _, item in top_slow.iterrows(): |
|
|
if item['total_seconds_spent'] > 0: |
|
|
st.write(f"• **{item['item_text']}**: {item['tempo_formatado']}") |
|
|
else: |
|
|
st.info("Nenhum dado de tempo disponível ainda.") |
|
|
|
|
|
with col2: |
|
|
st.markdown("#### Items mais retrabalhados") |
|
|
if not df_time.empty and df_time['times_worked'].sum() > 0: |
|
|
top_rework = df_time.sort_values('times_worked', ascending=False).head(3) |
|
|
for _, item in top_rework.iterrows(): |
|
|
if item['times_worked'] > 1: |
|
|
st.write(f"• **{item['item_text']}**: {item['times_worked']} vezes") |
|
|
else: |
|
|
st.info("Nenhum retrabalho identificado.") |
|
|
|
|
|
|
|
|
st.markdown("#### 💡 Recomendações") |
|
|
|
|
|
if not df_time.empty and df_time['total_seconds_spent'].sum() > 0: |
|
|
mean_time = df_time['total_seconds_spent'].mean() |
|
|
slow_items = df_time[df_time['total_seconds_spent'] > mean_time]['item_text'].tolist() |
|
|
rework_items = df_time[df_time['times_worked'] > 1]['item_text'].tolist() |
|
|
|
|
|
if slow_items: |
|
|
st.warning(f"**Items que demandam mais tempo:** {', '.join(slow_items[:3])}") |
|
|
st.write("💡 Considere revisar estes items ou dividir em subtarefas menores.") |
|
|
|
|
|
if rework_items: |
|
|
st.info(f"**Items com retrabalho:** {', '.join(rework_items[:3])}") |
|
|
st.write("💡 Analise se estes items precisam de mais clareza ou recursos adicionais.") |
|
|
|
|
|
if not slow_items and not rework_items: |
|
|
st.success("✅ Ótimo trabalho! O checklist está sendo executado de forma eficiente.") |
|
|
elif not df_time.empty: |
|
|
st.info("📊 Marque e desmarque alguns items para gerar recomendações baseadas no tempo de execução.") |
|
|
|
|
|
else: |
|
|
st.info("📊 Ainda não há dados suficientes para análise. Continue marcando os items do checklist para gerar insights.") |
|
|
|
|
|
|
|
|
st.markdown("### 📋 Status Atual dos Items") |
|
|
for item in checklist['items']: |
|
|
status = "✅" if item['is_checked'] else "⏳" |
|
|
st.write(f"{status} {item['text']}") |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Erro ao carregar análises: {str(e)}") |
|
|
if st.button("← Voltar para Dashboard"): |
|
|
st.switch_page("pages/dashboard.py") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |