File size: 9,351 Bytes
ef6d407 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
import re
import plotly.graph_objects as go
import pandas as pd
from typing import Dict, Tuple, Optional, List
from .graph_creation import create_pie_chart, create_bar_chart, create_tree_map_chart
from .pdf_creation import generate_pdf_report_temp
# Constantes
CIF_COMPONENTS: Dict[str, str] = {
'b': 'Funções Corporais (b)',
'd': 'Atividades e Participação (d)',
'e': 'Ambiente (e)',
's': 'Estruturas Corporais (s)',
'Outros': 'Outros' # Para códigos como N.D., N.C., N/A
}
# Padrões para códigos especiais (N.D., N.C., N/A)
SPECIAL_CODE_LITERALS: List[str] = ['N.D.', 'N.C.', 'N/A']
# Regex para encontrar qualquer um dos códigos especiais individualmente
COMBINED_SPECIAL_CODES_REGEX: str = r'(?:N\.D\.|N\.C\.|N\/A)' # Não capturante, para contagem total
# Regex para capturar códigos CIF numéricos (prefixo e número)
CIF_NUMERIC_CODE_REGEX: str = r'([bdes])([0-9]+)'
# Regex para capturar códigos CIF numéricos completos (código inteiro)
CIF_FULL_NUMERIC_CODE_REGEX: str = r'([bdes][0-9]+)'
# Regex para encontrar todas as linhas de Categoria CIF
ALL_CIF_CATEGORY_LINES_REGEX: str = r"Codificação CIF: (.+)"
# --- NOVA FUNÇÃO: Processa a resposta da LLM e retorna os DataFrames ---
def process_report_data(llm_res: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame]:
"""
Processa a resposta da LLM para extrair dados e criar os DataFrames do relatório.
Args:
llm_res (str): A resposta completa da LLM contendo os dados a serem extraídos.
Returns:
Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame]: Uma tupla contendo:
- df_group (pd.DataFrame): DataFrame de frequência por grupo CIF.
- df_group_describe (pd.DataFrame): Descrição estatística de df_group.
- df_individual_treemap (pd.DataFrame): DataFrame para o treemap de códigos individuais.
- df_treemap_describe (pd.DataFrame): Descrição estatística de df_individual_treemap.
"""
print("Processando dados para DataFrames...")
# MODIFICAÇÃO: Passe llm_res diretamente para as funções de contagem.
# Elas já contêm a lógica para extrair as linhas "Codificação CIF:"
data_by_group = _count_group_frequencies(llm_res)
df_group = pd.DataFrame(list(data_by_group.items()), columns=['Componente CIF', 'Frequencia'])
data_individual_codes = _count_individual_frequencies(llm_res)
df_individual_treemap = _create_treemap_dataframe(data_individual_codes)
translation_map = {
'count': 'Contagem', 'mean': 'Média', 'std': 'Desvio Padrão',
'min': 'Mínimo', '25%': '25º Percentil', '50%': 'Mediana (50%)',
'75%': '75º Percentil', 'max': 'Máximo'
}
df_group_describe = df_group.describe().reset_index()
df_group_describe = df_group_describe.rename(columns={'index': 'Estatística'})
df_group_describe['Estatística'] = df_group_describe['Estatística'].replace(translation_map)
df_treemap_describe = df_individual_treemap.describe().reset_index()
df_treemap_describe = df_treemap_describe.rename(columns={'index': 'Estatística'})
df_treemap_describe['Estatística'] = df_treemap_describe['Estatística'].replace(translation_map)
return (df_group, df_group_describe, df_individual_treemap, df_treemap_describe)
# --- NOVA FUNÇÃO: Gera os gráficos a partir dos DataFrames ---
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.
Returns:
Tuple[go.Figure, go.Figure, go.Figure]: Uma tupla contendo:
- fig_pie (go.Figure): Figura do gráfico de pizza.
- fig_bar (go.Figure): Figura do gráfico de barras.
- fig_tree_map (go.Figure): Figura do gráfico treemap.
"""
print("Gerando gráficos...")
# As funções create_pie_chart, create_bar_chart, create_tree_map_chart
# precisam do dicionário original 'data_by_group' e 'data_individual_codes'
# ou de uma forma de recriá-los a partir dos DataFrames, ou de aceitar os DataFrames diretamente.
# Por simplicidade, vou assumir que 'data_by_group' pode ser reconstruído de 'df_group'
# e 'data_individual_codes' pode ser reconstruído de 'df_individual_treemap'
# Reconstruindo data_by_group do df_group para as funções de criação de gráfico
data_by_group = dict(zip(df_group['Componente CIF'], df_group['Frequencia']))
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)
# --- NOVA FUNÇÃO: Gera o PDF a partir de DataFrames e Figuras ---
def generate_report_pdf(llm_res: str, df_group: pd.DataFrame, df_group_describe: pd.DataFrame,
df_individual_treemap: pd.DataFrame, df_treemap_describe: pd.DataFrame,
fig_pie: go.Figure, fig_bar: go.Figure, fig_tree_map: go.Figure) -> str:
"""
Gera o arquivo PDF do relatório com base nos DataFrames e figuras Plotly.
Args:
llm_res (str): A resposta original da LLM (para incluir no PDF).
df_group, df_group_describe, df_individual_treemap, df_treemap_describe (pd.DataFrame): DataFrames do relatório.
fig_pie, fig_bar, fig_tree_map (go.Figure): Figuras Plotly dos gráficos.
Returns:
str: Caminho para o arquivo PDF gerado temporariamente.
"""
print("Gerando PDF...")
temp_file = generate_pdf_report_temp(
plotly_figs_list=[fig_pie, fig_bar, fig_tree_map],
dataframes_list=[df_group, df_group_describe, df_individual_treemap, df_treemap_describe],
text_block=llm_res,
report_title_text="Relatório de Classificação por Componentes CIF"
)
return temp_file
def _count_group_frequencies(llm_res: str) -> Dict[str, int]:
print(llm_res)
group_frequencies: Dict[str, int] = {label: 0 for label in CIF_COMPONENTS.values()}
all_cif_category_lines = re.findall(ALL_CIF_CATEGORY_LINES_REGEX, llm_res)
if not all_cif_category_lines:
return group_frequencies
for cif_category_line in all_cif_category_lines:
numeric_matches = re.findall(CIF_NUMERIC_CODE_REGEX, cif_category_line)
for prefix, _ in numeric_matches:
component_name = CIF_COMPONENTS.get(prefix.lower())
if component_name and component_name != CIF_COMPONENTS['Outros']:
group_frequencies[component_name] += 1
special_code_matches = re.findall(COMBINED_SPECIAL_CODES_REGEX, cif_category_line)
if special_code_matches:
group_frequencies[CIF_COMPONENTS['Outros']] += len(special_code_matches)
print(f"Frequências por grupo atualizadas: {group_frequencies}")
return group_frequencies
def _count_individual_frequencies(llm_res: str) -> Dict[str, int]:
individual_frequencies: Dict[str, int] = {}
all_cif_category_lines = re.findall(ALL_CIF_CATEGORY_LINES_REGEX, llm_res)
if not all_cif_category_lines:
return individual_frequencies
for cif_category_line in all_cif_category_lines:
numeric_code_matches = re.findall(CIF_FULL_NUMERIC_CODE_REGEX, cif_category_line)
for code in numeric_code_matches:
code_lower = code.lower()
individual_frequencies[code_lower] = individual_frequencies.get(code_lower, 0) + 1
for special_code_literal in SPECIAL_CODE_LITERALS:
pattern = re.escape(special_code_literal)
matches = re.findall(pattern, cif_category_line)
if matches:
individual_frequencies[special_code_literal] = \
individual_frequencies.get(special_code_literal, 0) + len(matches)
print(f"Frequências individuais atualizadas: {individual_frequencies}")
return individual_frequencies
def _create_treemap_dataframe(data_dict: dict) -> pd.DataFrame:
df = pd.DataFrame(list(data_dict.items()), columns=['Filho', 'Frequencia'])
parents = []
subparents = []
for index, row in df.iterrows():
code = str(row['Filho'])
parent = 'Outros'
subparent = 'Outros'
if code.startswith('s'):
parent = 's'
elif code.startswith('d'):
parent = 'd'
elif code.startswith('b'):
parent = 'b'
elif code.startswith('e'):
parent = 'e'
match = re.match(r'[sdbe]([0-9])', code)
if match:
subparent = parent + match.group(1)
elif parent != 'Outros' and len(code) > 1 and code[1:].isdigit():
subparent = parent + code[1]
parents.append(parent)
subparents.append(subparent)
df['Parent'] = parents
df['Subparent'] = subparents
df_treemap = df.sort_values(by=['Parent', 'Subparent', 'Filho']).reset_index(drop=True)
return df_treemap |