ESJL commited on
Commit
1b3ebf8
·
verified ·
1 Parent(s): f527499

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -79
app.py CHANGED
@@ -21,117 +21,100 @@ NUM_LINHAS = len(LINHAS)
21
  MESES_PT = {i: m for i, m in enumerate(["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 1)}
22
  CINZA = PatternFill(start_color="D9D9D9", end_color="D9D9D9", fill_type="solid")
23
 
24
- def br_to_float(v):
25
- return float(v.replace(".", "").replace(",", "."))
26
-
27
- def extract_year(text):
28
- # Procura por "EVOLUÇÃO DO CUB/RS - 2024" ou apenas "2024" isolado
29
- match = re.search(r"(?:20)\d{2}", text)
30
- return int(match.group(0)) if match else None
31
 
32
- def processar_pdf_personalizado(arquivo_pdf, anos_retroativos):
33
- if arquivo_pdf is None:
34
- return None, "Aguardando upload..."
35
 
36
  log = []
37
  dados_globais = {}
 
38
 
39
  try:
40
  reader = PdfReader(arquivo_pdf.name)
41
- ano_atual_ref = datetime.now().year
42
- ano_limite = ano_atual_ref - anos_retroativos
43
 
44
  for i, page in enumerate(reader.pages):
45
  text = page.extract_text()
46
- ano_pag = extract_year(text)
47
 
48
- # Se não achar o ano na página ou for anterior ao limite, pula
49
- if not ano_pag or ano_pag < ano_limite:
50
- continue
 
51
 
52
- log.append(f"Processando página {i+1} (Ano: {ano_pag})")
53
 
54
- # Extrai apenas valores que pareçam moeda (ex: 2.500,00 ou 900,00)
55
- # Ignora números simples como "1", "2024", etc.
56
- valores = re.findall(r"\d{1,3}(?:\.\d{3})*,\d{2}", text)
57
- valores_float = [br_to_float(v) for v in valores]
58
 
59
- if not valores_float:
60
- continue
61
-
62
- # No PDF do Sinduscon, os valores costumam vir agrupados por projeto
63
- # Ex: Todos os meses do R 1-B, depois todos do R 1-N...
64
- # A lógica abaixo distribui os valores encontrados proporcionalmente
65
- num_meses_encontrados = len(valores_float) // NUM_LINHAS
66
 
67
- if num_meses_encontrados == 0:
68
- continue
 
 
69
 
70
  for linha_idx in range(NUM_LINHAS):
71
- for mes_offset in range(num_meses_encontrados):
72
- pos = (linha_idx * num_meses_encontrados) + mes_offset
73
  if pos < len(valores_float):
74
- # Se a página tem 12 meses, mes_idx é offset + 1
75
- # Se a página tem menos (ano corrente), precisamos inferir
76
- # Geralmente o Sinduscon preenche da esquerda para a direita (Jan -> atual)
77
- mes_idx = mes_offset + 1
78
- if mes_idx <= 12:
79
- data_key = f"{ano_pag}-{mes_idx:02d}"
80
- dados_globais[(linha_idx, data_key)] = valores_float[pos]
81
 
82
- if not dados_globais:
83
- return None, "Nenhum dado encontrado. Verifique se o PDF é o de 'Série Histórica - Valor'."
84
 
85
- # --- GERAÇÃO DO EXCEL ---
86
- colunas_ordenadas = sorted(set(c for _, c in dados_globais.keys()))
87
  wb = Workbook()
88
  ws = wb.active
89
- ws.title = "CUB RS Histórico"
90
 
91
- # Cabeçalho formatado: Jan/2024
92
- header = ["PROJETO-PADRÃO"] + [f"{MESES_PT[int(c.split('-')[1])]}/{c.split('-')[0]}" for c in colunas_ordenadas]
93
- ws.append(header)
94
 
95
- for l_idx, nome_projeto in enumerate(LINHAS):
96
- row = [nome_projeto]
97
- for data_key in colunas_ordenadas:
98
- row.append(dados_globais.get((l_idx, data_key), ""))
99
  ws.append(row)
100
-
101
- # Zebra por categoria (grupos de 3 ou conforme LINHAS)
102
  if (l_idx // 3) % 2 == 0:
103
- for c_idx in range(1, len(colunas_ordenadas) + 2):
104
  ws.cell(row=ws.max_row, column=c_idx).fill = CINZA
105
 
106
- # Estética
107
- for cell in ws[1]:
108
- cell.font = Font(bold=True)
109
- cell.alignment = Alignment(horizontal='center')
 
 
 
 
 
110
  ws.column_dimensions['A'].width = 30
111
-
112
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
113
  wb.save(temp_file.name)
114
 
115
- return temp_file.name, "\n".join(log) + f"\n\nSucesso! {len(colunas_ordenadas)} meses extraídos."
 
116
 
117
  except Exception as e:
118
- return None, f"Erro crítico: {str(e)}"
119
-
120
- # --- INTERFACE ---
121
- with gr.Blocks() as demo:
122
- gr.Markdown("## 🏗️ Extrator CUB-RS Multi-Páginas")
123
- gr.Markdown("Este extrator lê todas as páginas do PDF e filtra o período desejado.")
124
-
125
- with gr.Row():
126
- with gr.Column():
127
- f_in = gr.File(label="PDF do Sinduscon", file_types=[".pdf"])
128
- s_in = gr.Slider(1, 20, 5, step=1, label="Anos para retroceder")
129
- btn = gr.Button("Extrair Dados", variant="primary")
130
- with gr.Column():
131
- f_out = gr.File(label="Planilha Gerada")
132
- t_out = gr.Textbox(label="Relatório")
133
-
134
- btn.click(processar_pdf_personalizado, [f_in, s_in], [f_out, t_out])
135
 
136
- if __name__ == "__main__":
137
- demo.launch()
 
 
 
 
 
 
 
21
  MESES_PT = {i: m for i, m in enumerate(["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 1)}
22
  CINZA = PatternFill(start_color="D9D9D9", end_color="D9D9D9", fill_type="solid")
23
 
24
+ def limpar_e_converter(valor_str):
25
+ """Converte string '1.234,56' ou '-0,45' em float."""
26
+ try:
27
+ # Remove pontos de milhar e troca vírgula por ponto
28
+ return float(valor_str.replace(".", "").replace(",", "."))
29
+ except:
30
+ return None
31
 
32
+ def processar_pdf_universal(arquivo_pdf, anos_retroativos):
33
+ if arquivo_pdf is None: return None, "Aguardando arquivo..."
 
34
 
35
  log = []
36
  dados_globais = {}
37
+ eh_porcentagem = False # Flag para detectar tipo de dado
38
 
39
  try:
40
  reader = PdfReader(arquivo_pdf.name)
41
+ ano_limite = datetime.now().year - anos_retroativos
 
42
 
43
  for i, page in enumerate(reader.pages):
44
  text = page.extract_text()
 
45
 
46
+ # Busca o ano na página
47
+ ano_match = re.search(r"(?:20)\d{2}", text)
48
+ if not ano_match: continue
49
+ ano_pag = int(ano_match.group(0))
50
 
51
+ if ano_pag < ano_limite: continue
52
 
53
+ # Detecta se é PDF de variação (geralmente contém o símbolo % no texto)
54
+ if "%" in text: eh_porcentagem = True
 
 
55
 
56
+ # REGEX UNIVERSAL: Pega números como 2.500,00 | 900,00 | 0,45 | -0,10
57
+ # Explicação: Sinal opcional | dígitos com pontos opcionais | vírgula | 2 decimais
58
+ padrao = r"-?\d{1,3}(?:\.\d{3})*,\d{2}"
59
+ valores_encontrados = re.findall(padrao, text)
60
+ valores_float = [limpar_e_converter(v) for v in valores_encontrados]
 
 
61
 
62
+ if not valores_float: continue
63
+
64
+ num_meses = len(valores_float) // NUM_LINHAS
65
+ if num_meses == 0: continue
66
 
67
  for linha_idx in range(NUM_LINHAS):
68
+ for mes_off in range(num_meses):
69
+ pos = (linha_idx * num_meses) + mes_off
70
  if pos < len(valores_float):
71
+ mes_idx = mes_off + 1
72
+ dados_globais[(linha_idx, f"{ano_pag}-{mes_idx:02d}")] = valores_float[pos]
73
+
74
+ log.append(f"Página {i+1} (Ano {ano_pag}) processada.")
 
 
 
75
 
76
+ if not dados_globais: return None, "Nenhum dado extraído."
 
77
 
78
+ # Gerar Excel
79
+ colunas = sorted(set(c for _, c in dados_globais.keys()))
80
  wb = Workbook()
81
  ws = wb.active
82
+ ws.title = "Dados CUB-RS"
83
 
84
+ # Cabeçalho
85
+ ws.append(["PROJETO-PADRÃO"] + [f"{MESES_PT[int(c.split('-')[1])]}/{c.split('-')[0]}" for c in colunas])
 
86
 
87
+ for l_idx, nome in enumerate(LINHAS):
88
+ row = [nome] + [dados_globais.get((l_idx, c)) for c in colunas]
 
 
89
  ws.append(row)
 
 
90
  if (l_idx // 3) % 2 == 0:
91
+ for c_idx in range(1, len(colunas) + 2):
92
  ws.cell(row=ws.max_row, column=c_idx).fill = CINZA
93
 
94
+ # Formatação de Células
95
+ for r in range(2, ws.max_row + 1):
96
+ for c in range(2, ws.max_column + 1):
97
+ cell = ws.cell(r, c)
98
+ if eh_porcentagem:
99
+ cell.number_format = '0.00"%"'
100
+ else:
101
+ cell.number_format = '#,##0.00'
102
+
103
  ws.column_dimensions['A'].width = 30
 
104
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
105
  wb.save(temp_file.name)
106
 
107
+ tipo = "Variação (%)" if eh_porcentagem else "Valores (R$)"
108
+ return temp_file.name, f"Tipo detectado: {tipo}\n" + "\n".join(log)
109
 
110
  except Exception as e:
111
+ return None, f"Erro: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ # Interface Gradio permanece a mesma...
114
+ demo = gr.Interface(
115
+ fn=processar_pdf_universal,
116
+ inputs=[gr.File(label="Suba o PDF (Valores ou Variação)"), gr.Slider(1, 25, 5, label="Anos")],
117
+ outputs=[gr.File(label="Download"), gr.Textbox(label="Log")],
118
+ title="Extrator CUB-RS Universal"
119
+ )
120
+ demo.launch()