# utils/report/graph_creation.py import plotly.express as px import plotly.graph_objects as go import pandas as pd from typing import Optional, Tuple # Dict e List não são mais necessários para as constantes globais # Importa a Enum para centralizar as definições de categoria, rótulos e cores from .icf_categories import ICFComponent # Obtém as informações de ordenação e cores diretamente da Enum ICFComponent ORDERED_ICF_LABELS = ICFComponent.get_ordered_labels() ICF_COLOR_MAP_FROM_LABEL = ICFComponent.get_color_map() # Mapeia Rótulo Completo -> Cor ICF_COLOR_MAP_FROM_SHORT_CODE = { # Mapeia Código Curto -> Cor (para o Treemap) member.short_code: member.color for member in ICFComponent } def create_pie_chart( input_df: pd.DataFrame, title: str = "Distribuição da Classificação" ) -> Optional[go.Figure]: """ Gera um gráfico de pizza a partir de um DataFrame, usando cores consistentes para as categorias CIF presentes nos dados de entrada. Args: input_df (pd.DataFrame): DataFrame com colunas 'Componente CIF' (rótulos) e 'Frequencia' (valores). Espera-se que 'Componente CIF' já esteja como pd.Categorical ordenado. title (str): O título do gráfico. Returns: Optional[go.Figure]: Objeto Plotly Figure ou None se não houver dados válidos. """ if input_df.empty: print(f"Aviso: DataFrame de entrada para o gráfico de pizza '{title}' está vazio. Retornando None.") return None plot_df = input_df[input_df['Frequência'] > 0].copy() if plot_df.empty: print(f"Aviso: Nenhum dado com frequência positiva para gerar o gráfico de pizza: '{title}'. Retornando None.") return None category_order_map = {'Componente CIF': ORDERED_ICF_LABELS} figure = px.pie( plot_df, names='Componente CIF', # values='Frequencia', # ANTES values='Frequência', # DEPOIS title=title, color='Componente CIF', color_discrete_map=ICF_COLOR_MAP_FROM_LABEL, category_orders=category_order_map ) figure.update_layout(legend_title_text='Componentes') figure.update_traces( direction='clockwise', rotation=-30, textinfo="label+value+percent", textposition='outside', textfont_size=16, pull=[0.05 if val > 0 else 0 for val in plot_df['Frequência']], # DEPOIS hovertemplate="%{label}
Frequência: %{value}
Porcentagem: %{percent}" ) return figure def create_bar_chart( input_df: pd.DataFrame, title: str = "Frequência da Classificação" ) -> Optional[go.Figure]: """ Gera um gráfico de barras a partir de um DataFrame, usando cores consistentes para as categorias CIF. Args: input_df (pd.DataFrame): DataFrame com colunas 'Componente CIF' (eixo X) e 'Frequencia' (eixo Y). Espera-se que 'Componente CIF' já esteja como pd.Categorical ordenado. title (str): O título do gráfico. Returns: Optional[go.Figure]: Objeto Plotly Figure ou None se não houver dados válidos. """ if input_df.empty: print(f"Aviso: DataFrame de entrada para o gráfico de barras '{title}' está vazio. Retornando None.") return None # Para gráficos de barra, podemos plotar frequências zero, então não filtramos > 0 if not input_df['Frequência'].any(): # DEPOIS print(f"Aviso: Todos os dados em input_df têm frequência 0 para o gráfico de barras: '{title}'.") category_order_map = {'Componente CIF': ORDERED_ICF_LABELS} figure = px.bar( input_df, x='Componente CIF', y='Frequência', # DEPOIS title=title, labels={'Componente CIF': 'Componentes CIF', 'Frequência': 'Frequência'}, # DEPOIS (chave 'Frequência') color='Componente CIF', color_discrete_map=ICF_COLOR_MAP_FROM_LABEL, category_orders=category_order_map, text_auto=True ) figure.update_layout( legend_title_text='Componentes', xaxis_title="Componentes CIF", yaxis_title="Frequência", showlegend=True ) figure.update_traces( textfont_size=14, textangle=0, textposition="inside", hovertemplate="%{x}
Frequência: %{y}" ) return figure def create_tree_map_chart( tree_map_df: pd.DataFrame, title: str = "Treemap de Frequências por Hierarquia de Códigos" ) -> Optional[go.Figure]: """ Gera um gráfico Treemap a partir de um DataFrame hierárquico. As cores do nível 'Parent' são baseadas nos códigos curtos dos componentes CIF. Args: tree_map_df (pd.DataFrame): DataFrame com colunas 'Parent', 'Subparent', 'Filho' (Rótulo), e 'Frequencia'. title (str): O título do gráfico. Returns: Optional[go.Figure]: Objeto Plotly Figure ou None se o DataFrame estiver vazio. """ if tree_map_df.empty: print(f"Aviso: DataFrame vazio para gerar o Treemap: '{title}'.") return None # Filtra linhas onde 'Filho' é nulo ou vazio, pois podem causar problemas no treemap # e geralmente representam nós estruturais que não devem ser folhas. plot_df = tree_map_df.dropna(subset=['Código']) plot_df = plot_df[plot_df['Código'] != ""] if plot_df.empty: print(f"Aviso: DataFrame para Treemap não contém 'Códigos' válidos após filtragem: '{title}'.") # DEPOIS return None figure = px.treemap( plot_df, path=['Componente', 'Capítulo', 'Código'], values='Frequência', title=title, color='Componente', color_discrete_map=ICF_COLOR_MAP_FROM_SHORT_CODE, height=700 ) figure.update_traces( textinfo="label+value+percent entry", # Informações exibidas # Para hovertemplate, %{customdata} pode ser usado se você adicionar colunas extras # Exemplo: customdata=plot_df[['Parent', 'Subparent']] # hovertemplate='%{label} (%{customdata[0]} > %{customdata[1]})
Frequência: %{value}
Porcentagem da Entrada: %{percentEntry:.1%}', hovertemplate='%{label}
Frequência: %{value}
Porcentagem da Entrada: %{percentEntry:.1%}', marker_line_width=1, marker_line_color='white' # Linhas brancas para separar os blocos ) figure.update_layout( hoverlabel=dict( bgcolor="white", font_size=12, ) ) return figure def create_report_plots(df_group: pd.DataFrame, df_individual_treemap: pd.DataFrame) -> Tuple[go.Figure, go.Figure, go.Figure]: """ Cria as figuras Plotly dos gráficos a partir dos DataFrames processados. Args: df_group (pd.DataFrame): DataFrame de frequência por grupo CIF. df_individual_treemap (pd.DataFrame): DataFrame para o treemap de códigos individuais. (Esperado ter colunas: 'Filho', 'Parent', 'Frequencia') Returns: Tuple[go.Figure, go.Figure, go.Figure]: Figuras de pizza, barras e treemap. """ print("Gerando gráficos...") fig_pie = create_pie_chart(df_group, title="Distribuição da Classificação por Componentes CIF") fig_bar = create_bar_chart(df_group, title="Frequência da Classificação por Componentes CIF") fig_tree_map = create_tree_map_chart(df_individual_treemap, title="Treemap de Frequência por Código CIF") return fig_pie, fig_bar, fig_tree_map