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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -84
app.py CHANGED
@@ -2,7 +2,6 @@ import re
2
  import os
3
  import tempfile
4
  from datetime import datetime
5
- from io import BytesIO
6
  from pypdf import PdfReader
7
  from openpyxl import Workbook
8
  from openpyxl.styles import Font, PatternFill, Alignment
@@ -19,131 +18,120 @@ LINHAS = [
19
  "GI (Galpão Industrial)"
20
  ]
21
  NUM_LINHAS = len(LINHAS)
22
- MESES_PT = {
23
- 1: "Jan", 2: "Fev", 3: "Mar", 4: "Abr", 5: "Mai", 6: "Jun",
24
- 7: "Jul", 8: "Ago", 9: "Set", 10: "Out", 11: "Nov", 12: "Dez"
25
- }
26
  CINZA = PatternFill(start_color="D9D9D9", end_color="D9D9D9", fill_type="solid")
27
 
28
- # --- FUNÇÕES DE APOIO ---
29
  def br_to_float(v):
30
  return float(v.replace(".", "").replace(",", "."))
31
 
32
- def extract_numbers(text):
33
- return [br_to_float(n) for n in re.findall(r"\d{1,3}(?:\.\d{3})*,\d{2}", text)]
 
 
34
 
35
- def extract_year_from_page(text):
36
- match = re.search(r"EVOLUÇÃO\s*%?/?\s*(20\d{2})", text)
37
- if match: return int(match.group(1))
38
- years = re.findall(r"(20\d{2})", text)
39
- return max(map(int, years)) if years else None
40
-
41
- def format_month_header(col_name):
42
- ano, mes = col_name.split("-")
43
- return f"{MESES_PT[int(mes)]}/{ano}"
44
-
45
- # --- PROCESSAMENTO PRINCIPAL ---
46
  def processar_pdf_personalizado(arquivo_pdf, anos_retroativos):
47
  if arquivo_pdf is None:
48
- return None, "Aguardando upload do arquivo..."
49
 
50
  log = []
 
 
51
  try:
52
  reader = PdfReader(arquivo_pdf.name)
53
- ano_atual = datetime.now().year
54
- ano_limite = ano_atual - anos_retroativos
55
-
56
- # 1. Identificar o ano mais recente no documento para lógica de meses
57
- texto_capa = reader.pages[0].extract_text()
58
- ano_documento = extract_year_from_page(texto_capa) or ano_atual
59
 
60
- dados = {}
61
-
62
- for page in reader.pages:
63
  text = page.extract_text()
64
- ano_pag = extract_year_from_page(text)
65
 
 
66
  if not ano_pag or ano_pag < ano_limite:
67
  continue
68
-
69
- valores = extract_numbers(text)
70
- if not valores: continue
71
 
72
- # Cálculo de quantos meses existem nesta página
73
- num_meses = len(valores) // NUM_LINHAS
74
- # Se for o ano atual/mais recente, começa em Janeiro.
75
- # Se for ano antigo com menos de 12 meses, assume-se que termina em Dezembro.
76
- mes_inicial = 1 if (num_meses == 12 or ano_pag == ano_documento) else (12 - num_meses + 1)
 
 
 
 
 
 
 
 
 
77
 
 
 
 
78
  for linha_idx in range(NUM_LINHAS):
79
- for mes_idx in range(num_meses):
80
- pos = linha_idx * num_meses + mes_idx
81
- if pos >= len(valores): break
82
-
83
- mes_atual = mes_inicial + mes_idx
84
- if mes_atual > 12: break
85
-
86
- data_str = f"{ano_pag}-{mes_atual:02d}"
87
- dados[(linha_idx, data_str)] = valores[pos]
 
88
 
89
- if not dados:
90
- return None, f"Nenhum dado encontrado para o período de {anos_retroativos} anos."
91
 
92
- # 2. Criar Excel
93
- colunas_ordenadas = sorted(set(c for _, c in dados.keys()))
94
  wb = Workbook()
95
  ws = wb.active
96
- ws.title = "CUB Histórico"
97
 
98
- # Cabeçalho
99
- ws.append(["PROJETO-PADRÃO"] + [format_month_header(c) for c in colunas_ordenadas])
 
100
 
101
- # Dados
102
- for i, nome_linha in enumerate(LINHAS):
103
- row = [nome_linha] + [dados.get((i, c)) for c in colunas_ordenadas]
 
104
  ws.append(row)
105
 
106
- # Formatação zebrada por grupos originais do Sinduscon
107
- cor_atual = CINZA if (i // 3) % 2 == 0 else None
108
- if cor_atual:
109
- for col_idx in range(1, len(colunas_ordenadas) + 2):
110
- ws.cell(row=ws.max_row, column=col_idx).fill = cor_atual
111
 
112
- # Ajustes de Estilo
113
  for cell in ws[1]:
114
  cell.font = Font(bold=True)
115
  cell.alignment = Alignment(horizontal='center')
116
- ws.column_dimensions['A'].width = 35
117
-
118
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
119
  wb.save(temp_file.name)
120
 
121
- log.append(f"Sucesso! Foram extraídos {len(colunas_ordenadas)} meses de dados.")
122
- return temp_file.name, "\n".join(log)
123
-
124
  except Exception as e:
125
- return None, f"Erro: {str(e)}"
126
 
127
- # --- INTERFACE GRADIO ---
128
- with gr.Blocks(title="Extrator CUB-RS") as demo:
129
- gr.Markdown("# 🏗️ Extrator de Série Histórica CUB-RS")
130
- gr.Markdown("Transforme o PDF do Sinduscon em uma planilha Excel organizada por períodos.")
131
 
132
  with gr.Row():
133
  with gr.Column():
134
- input_pdf = gr.File(label="1. Suba o PDF da Série Histórica", file_types=[".pdf"])
135
- input_anos = gr.Slider(minimum=1, maximum=20, value=5, step=1, label="2. Quantos anos deseja extrair?")
136
- btn = gr.Button("Gerar Excel", variant="primary")
137
-
138
  with gr.Column():
139
- output_file = gr.File(label="3. Baixe seu Excel aqui")
140
- output_log = gr.Textbox(label="Log")
141
 
142
- btn.click(
143
- fn=processar_pdf_personalizado,
144
- inputs=[input_pdf, input_anos],
145
- outputs=[output_file, output_log]
146
- )
147
 
148
  if __name__ == "__main__":
149
  demo.launch()
 
2
  import os
3
  import tempfile
4
  from datetime import datetime
 
5
  from pypdf import PdfReader
6
  from openpyxl import Workbook
7
  from openpyxl.styles import Font, PatternFill, Alignment
 
18
  "GI (Galpão Industrial)"
19
  ]
20
  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 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()