RAG-Test / utils /report_creation.py
Nielo47's picture
Update space
ef6d407
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