Spaces:
Sleeping
Sleeping
| from pypdf import PdfReader | |
| import re | |
| from datetime import datetime | |
| from openpyxl import Workbook | |
| from openpyxl.styles import Font, PatternFill, Alignment | |
| import requests | |
| from io import BytesIO | |
| import gradio as gr | |
| import tempfile | |
| import os | |
| # URL da página do CUB-RS | |
| CUB_PAGE_URL = "https://sinduscon-rs.com.br/cub-rs/" | |
| LINHAS = [ | |
| "R 1-B (Res. Unifamiliar)", | |
| "R 1-N (Res. Unifamiliar)", | |
| "R 1-A (Res. Unifamiliar)", | |
| "PP 4-B (Prédio Popular)", | |
| "PP 4-N (Prédio Popular)", | |
| "R 8-B (Res. Multifamiliar)", | |
| "R 8-N (Res. Multifamiliar)", | |
| "R 8-A (Res. Multifamiliar)", | |
| "R 16-N (Res. Multifamiliar)", | |
| "R 16-A (Res. Multifamiliar)", | |
| "PIS (Projeto Inter. Social)", | |
| "RP1Q (Residência Popular)", | |
| "CAL 8-N (Com. Andar Livres)", | |
| "CAL 8-A (Com. Andar Livres)", | |
| "CSL 8-N (Com.Salas e Lojas)", | |
| "CSL 8-A (Com.Salas e Lojas)", | |
| "CSL 16-N (Com.Salas e Lojas)", | |
| "CSL 16-A (Com.Salas e Lojas)", | |
| "GI (Galpão Industrial)" | |
| ] | |
| NUM_LINHAS = len(LINHAS) | |
| DATA_MINIMA = datetime(2007, 2, 1) | |
| MESES_PT = { | |
| 1: "Janeiro", 2: "Fevereiro", 3: "Março", 4: "Abril", | |
| 5: "Maio", 6: "Junho", 7: "Julho", 8: "Agosto", | |
| 9: "Setembro", 10: "Outubro", 11: "Novembro", 12: "Dezembro" | |
| } | |
| GRUPOS_LINHAS = [ | |
| (2, 4), (5, 6), (7, 9), (10, 11), (12, 12), | |
| (13, 13), (14, 15), (16, 17), (18, 19), (20, 20), | |
| ] | |
| CINZA = PatternFill(start_color="D9D9D9", end_color="D9D9D9", fill_type="solid") | |
| def fetch_pdf_url(): | |
| response = requests.get(CUB_PAGE_URL, timeout=30) | |
| response.raise_for_status() | |
| pattern = r'href="([^"]+)"[^>]*>\s*Série Histórica [–-] Valor\s*</a>' | |
| match = re.search(pattern, response.text, re.IGNORECASE) | |
| if not match: | |
| raise ValueError("Link do PDF não encontrado.") | |
| return match.group(1) | |
| def download_pdf(url): | |
| response = requests.get(url, timeout=60) | |
| response.raise_for_status() | |
| return BytesIO(response.content) | |
| def br_to_float(v): | |
| return float(v.replace(".", "").replace(",", ".")) | |
| def extract_numbers(text): | |
| nums = re.findall(r"\d{1,3}(?:\.\d{3})*,\d{2}", text) | |
| return [br_to_float(n) for n in nums] | |
| def extract_year_from_page(text): | |
| match = re.search(r"EVOLUÇÃO\s*%?/?\s*(20\d{2})", text) | |
| if match: | |
| return int(match.group(1)) | |
| years = re.findall(r"(20\d{2})", text) | |
| return max(map(int, years)) if years else None | |
| def detect_start_month(num_meses, ano, ano_mais_recente): | |
| if num_meses == 12 or ano == ano_mais_recente: | |
| return 1 | |
| return 12 - num_meses + 1 | |
| def format_month_header(col_name): | |
| ano, mes = col_name.split("-") | |
| return f"{MESES_PT[int(mes)]}/{ano}" | |
| def apply_formatting(ws, num_colunas): | |
| ws.column_dimensions['A'].width = 30 | |
| for col_idx in range(2, num_colunas + 1): | |
| ws.column_dimensions[ws.cell(1, col_idx).column_letter].width = 15 | |
| for col_idx in range(1, num_colunas + 1): | |
| cell = ws.cell(row=1, column=col_idx) | |
| cell.font = Font(bold=True) | |
| cell.alignment = Alignment(horizontal='center') | |
| for grupo_idx, (ini, fim) in enumerate(GRUPOS_LINHAS): | |
| if grupo_idx % 2 == 0: | |
| for r in range(ini, fim + 1): | |
| for c in range(1, num_colunas + 1): | |
| ws.cell(r, c).fill = CINZA | |
| def gerar_excel(): | |
| log = [] | |
| pdf_url = fetch_pdf_url() | |
| log.append(f"PDF encontrado: {pdf_url}") | |
| pdf_data = download_pdf(pdf_url) | |
| reader = PdfReader(pdf_data) | |
| ano_mais_recente = extract_year_from_page(reader.pages[0].extract_text()) | |
| dados = {} | |
| for page in reader.pages: | |
| text = page.extract_text() | |
| ano = extract_year_from_page(text) | |
| if not ano: | |
| continue | |
| valores = extract_numbers(text) | |
| num_meses = len(valores) // NUM_LINHAS | |
| mes_inicial = detect_start_month(num_meses, ano, ano_mais_recente) | |
| for linha_idx in range(NUM_LINHAS): | |
| for mes_idx in range(num_meses): | |
| pos = linha_idx * num_meses + mes_idx | |
| if pos >= len(valores): | |
| break | |
| data = datetime(ano, mes_inicial + mes_idx, 1) | |
| if data < DATA_MINIMA: | |
| continue | |
| dados[(linha_idx, data.strftime("%Y-%m"))] = valores[pos] | |
| colunas = sorted(set(c for _, c in dados.keys())) | |
| wb = Workbook() | |
| ws = wb.active | |
| ws.title = "CUB Histórico" | |
| ws.append(["CÓDIGO"] + [format_month_header(c) for c in colunas]) | |
| for i, codigo in enumerate(LINHAS): | |
| row = [codigo] + [dados.get((i, c)) for c in colunas] | |
| ws.append(row) | |
| apply_formatting(ws, len(colunas) + 1) | |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") | |
| wb.save(temp_file.name) | |
| log.append("Excel gerado com sucesso.") | |
| return temp_file.name, "\n".join(log) | |
| # Interface Gradio | |
| interface = gr.Interface( | |
| fn=gerar_excel, | |
| inputs=[], | |
| outputs=[ | |
| gr.File(label="📥 Baixar Excel"), | |
| gr.Textbox(label="Log de execução") | |
| ], | |
| title="CUB/RS – Série Histórica", | |
| description="Baixa automaticamente o PDF do SINDUSCON-RS e gera o Excel da série histórica." | |
| ) | |
| if __name__ == "__main__": | |
| interface.launch() | |