ESJL commited on
Commit
10d228a
·
verified ·
1 Parent(s): cb983a8

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +146 -0
app.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 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
9
+ import gradio as gr
10
+
11
+ # Configurações de Estrutura do CUB-RS
12
+ LINHAS = [
13
+ "R 1-B (Res. Unifamiliar)", "R 1-N (Res. Unifamiliar)", "R 1-A (Res. Unifamiliar)",
14
+ "PP 4-B (Prédio Popular)", "PP 4-N (Prédio Popular)", "R 8-B (Res. Multifamiliar)",
15
+ "R 8-N (Res. Multifamiliar)", "R 8-A (Res. Multifamiliar)", "R 16-N (Res. Multifamiliar)",
16
+ "R 16-A (Res. Multifamiliar)", "PIS (Projeto Inter. Social)", "RP1Q (Residência Popular)",
17
+ "CAL 8-N (Com. Andar Livres)", "CAL 8-A (Com. Andar Livres)", "CSL 8-N (Com.Salas e Lojas)",
18
+ "CSL 8-A (Com.Salas e Lojas)", "CSL 16-N (Com.Salas e Lojas)", "CSL 16-A (Com.Salas e Lojas)",
19
+ "GI (Galpão Industrial)"
20
+ ]
21
+ NUM_LINHAS = len(LINHAS)
22
+ DATA_MINIMA = datetime(2007, 2, 1)
23
+ MESES_PT = {
24
+ 1: "Janeiro", 2: "Fevereiro", 3: "Março", 4: "Abril",
25
+ 5: "Maio", 6: "Junho", 7: "Julho", 8: "Agosto",
26
+ 9: "Setembro", 10: "Outubro", 11: "Novembro", 12: "Dezembro"
27
+ }
28
+ GRUPOS_LINHAS = [
29
+ (2, 4), (5, 6), (7, 9), (10, 11), (12, 12),
30
+ (13, 13), (14, 15), (16, 17), (18, 19), (20, 20),
31
+ ]
32
+ CINZA = PatternFill(start_color="D9D9D9", end_color="D9D9D9", fill_type="solid")
33
+
34
+ # Funções Auxiliares de Processamento
35
+ def br_to_float(v):
36
+ return float(v.replace(".", "").replace(",", "."))
37
+
38
+ def extract_numbers(text):
39
+ return [br_to_float(n) for n in re.findall(r"\d{1,3}(?:\.\d{3})*,\d{2}", text)]
40
+
41
+ def extract_year_from_page(text):
42
+ match = re.search(r"EVOLUÇÃO\s*%?/?\s*(20\d{2})", text)
43
+ if match: return int(match.group(1))
44
+ years = re.findall(r"(20\d{2})", text)
45
+ return max(map(int, years)) if years else None
46
+
47
+ def detect_start_month(num_meses, ano, ano_mais_recente):
48
+ return 1 if (num_meses == 12 or ano == ano_mais_recente) else (12 - num_meses + 1)
49
+
50
+ def format_month_header(col_name):
51
+ ano, mes = col_name.split("-")
52
+ return f"{MESES_PT[int(mes)]}/{ano}"
53
+
54
+ def apply_formatting(ws, num_colunas):
55
+ ws.column_dimensions['A'].width = 30
56
+ for col_idx in range(1, num_colunas + 1):
57
+ cell = ws.cell(row=1, column=col_idx)
58
+ cell.font = Font(bold=True)
59
+ cell.alignment = Alignment(horizontal='center')
60
+ if col_idx > 1:
61
+ ws.column_dimensions[ws.cell(1, col_idx).column_letter].width = 15
62
+
63
+ for grupo_idx, (ini, fim) in enumerate(GRUPOS_LINHAS):
64
+ if grupo_idx % 2 == 0:
65
+ for r in range(ini, fim + 1):
66
+ for c in range(1, num_colunas + 1):
67
+ ws.cell(r, c).fill = CINZA
68
+
69
+ # Função Principal
70
+ def processar_pdf_upload(arquivo_pdf):
71
+ if arquivo_pdf is None:
72
+ return None, "Por favor, faça o upload de um arquivo PDF."
73
+
74
+ log = []
75
+ try:
76
+ reader = PdfReader(arquivo_pdf.name)
77
+ log.append(f"Arquivo lido: {os.path.basename(arquivo_pdf.name)}")
78
+
79
+ # Detectar ano mais recente para lógica de meses parciais
80
+ primeira_pag_texto = reader.pages[0].extract_text()
81
+ ano_mais_recente = extract_year_from_page(primeira_pag_texto)
82
+
83
+ dados = {}
84
+ for page in reader.pages:
85
+ text = page.extract_text()
86
+ ano = extract_year_from_page(text)
87
+ if not ano: continue
88
+
89
+ valores = extract_numbers(text)
90
+ if not valores: continue
91
+
92
+ num_meses = len(valores) // NUM_LINHAS
93
+ mes_inicial = detect_start_month(num_meses, ano, ano_mais_recente)
94
+
95
+ for linha_idx in range(NUM_LINHAS):
96
+ for mes_idx in range(num_meses):
97
+ pos = linha_idx * num_meses + mes_idx
98
+ if pos >= len(valores): break
99
+
100
+ mes_atual = mes_inicial + mes_idx
101
+ if mes_atual > 12: break # Prevenção de erro de data
102
+
103
+ data = datetime(ano, mes_atual, 1)
104
+ if data < DATA_MINIMA: continue
105
+
106
+ dados[(linha_idx, data.strftime("%Y-%m"))] = valores[pos]
107
+
108
+ if not dados:
109
+ return None, "Não foi possível extrair dados válidos do PDF. Verifique o formato."
110
+
111
+ # Montar Excel
112
+ colunas = sorted(set(c for _, c in dados.keys()))
113
+ wb = Workbook()
114
+ ws = wb.active
115
+ ws.title = "CUB Histórico"
116
+
117
+ ws.append(["CÓDIGO"] + [format_month_header(c) for c in colunas])
118
+ for i, codigo in enumerate(LINHAS):
119
+ row = [codigo] + [dados.get((i, c)) for c in colunas]
120
+ ws.append(row)
121
+
122
+ apply_formatting(ws, len(colunas) + 1)
123
+
124
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
125
+ wb.save(temp_file.name)
126
+ log.append(f"Excel gerado com {len(colunas)} meses processados.")
127
+
128
+ return temp_file.name, "\n".join(log)
129
+
130
+ except Exception as e:
131
+ return None, f"Erro ao processar: {str(e)}"
132
+
133
+ # Interface Gradio
134
+ interface = gr.Interface(
135
+ fn=processar_pdf_upload,
136
+ inputs=gr.File(label="Upload do PDF (Série Histórica CUB-RS)", file_types=[".pdf"]),
137
+ outputs=[
138
+ gr.File(label="📥 Baixar Excel Gerado"),
139
+ gr.Textbox(label="Status do Processamento")
140
+ ],
141
+ title="Conversor CUB/RS: PDF ➔ Excel",
142
+ description="Faça o upload do PDF da 'Série Histórica - Valor' baixado do site do Sinduscon-RS para gerar a planilha estruturada."
143
+ )
144
+
145
+ if __name__ == "__main__":
146
+ interface.launch()