Spaces:
Sleeping
Sleeping
| # Importações | |
| import gradio as gr | |
| import requests | |
| from bs4 import BeautifulSoup | |
| import re | |
| import pandas as pd | |
| import validators | |
| from selenium import webdriver | |
| from webdriver_manager.chrome import ChromeDriverManager | |
| import openpyxl | |
| import googlemaps | |
| import folium | |
| from folium.plugins import MarkerCluster | |
| import webbrowser | |
| from datetime import datetime | |
| # Constantes e Dicionários | |
| # Constantes | |
| ESTADOS_BR = ["AC", "AL", "AM", "AP", "BA", "CE", "DF", "ES", "GO", "MA", "MG", "MS", "MT", "PA", "PB", "PE", "PI", "PR", "RJ", "RN", "RO", "RR", "RS", "SC", "SE", "SP", "TO"] | |
| # Dicionários | |
| dict_topo = { | |
| 'plano <5%': 1, | |
| 'aclive_leve 5% e 30%': 0.95, | |
| 'declive_leve 5% e 30%': 0.90, | |
| 'aclive_acentuado >30%': 0.85, | |
| 'declive_acentuado >30%': 0.80, | |
| '-': '-' | |
| } | |
| dict_rel = { | |
| 'plana': 1.1, | |
| 'ondulada': 1.00, | |
| 'montanhosa/acidentada': 0.80, | |
| '-': '-' | |
| } | |
| dict_sup = { | |
| 'Seca': 1.00, | |
| 'Região inundável mas não atingida': 0.90, | |
| 'Região inundável mas atingida periodicamente': 0.70, | |
| 'Alagada': 0.60, | |
| '-': '-' | |
| } | |
| dict_apr = { | |
| 'Loteamento': 1.00, | |
| 'Indústria': 0.90, | |
| 'Culturas': 0.80, | |
| '-': '-' | |
| } | |
| dict_ace = { | |
| 'Ótima': 1.00, | |
| 'Muito boa': 0.95, | |
| 'Boa': 0.90, | |
| 'Desfavorável': 0.80, | |
| 'Má': 0.75, | |
| 'Péssima': 0.70, | |
| '-': '-' | |
| } | |
| dict_ec = { | |
| 'Em construção ou na planta': 1.00, | |
| 'Bom (aparência de novo)': 0.95, | |
| 'Bom (aparência de usado)': 0.90, | |
| 'Regular (reparos simples)': 0.80, | |
| 'Regular (reparos importantes)': 0.75, | |
| 'Ruim': 0.70, | |
| '-': '-' | |
| } | |
| dict_id = { | |
| 'Na planta': 1.00, | |
| 'id <= 5': 0.95, | |
| '5 < id <= 10': 0.90, | |
| '10 < id <= 20': 0.85, | |
| '20 < id <= 50': 0.80, | |
| '50 < id <= 100': 0.75, | |
| 'id > 100': 0.70, | |
| '-': '-' | |
| } | |
| dict_pad = { | |
| 'Alto (superior, luxo)': 1.00, | |
| 'Alto (por predominância)': 0.95, | |
| 'Normal (c/ aspectos de alto)': 0.90, | |
| 'Normal (forte predominância)': 0.80, | |
| 'Normal (c/ aspectos de baixo)': 0.75, | |
| 'Baixo (selecionado)': 0.70, | |
| 'Mínimo': 0.65, | |
| '-': '-' | |
| } | |
| dict_loc = { | |
| 'Região central': 1.00, | |
| 'Região não central': 0.85, | |
| 'Ocupação periférica/suburbana': 0.75, | |
| 'Não inserido na malha urbana': 0.60, | |
| '-': '-' | |
| } | |
| # Funções auxiliares | |
| def extract_address(text): | |
| estados_regex = "|".join(ESTADOS_BR) | |
| match = re.search(rf'Endereço\s*(.*?\b({estados_regex})\b)', text, re.DOTALL) | |
| return match.group(1).strip() if match else None | |
| def extract_testada(text): | |
| match = re.search(r'(\d{1,3}(?:[.,]\d{1,2})?)m?\s*[xX]\s*\d', text) | |
| return float(match.group(1).replace(',', '.')) if match else None | |
| def clean_text_for_testada(page_text): | |
| return re.sub(r'^\s*\*?\s*\!\[Image[^\n]*\n?', '', page_text, flags=re.MULTILINE) | |
| # Função de busca | |
| def fetch_url_info(user_input): | |
| url = f"https://r.jina.ai/{user_input}" | |
| if not validators.url(url): | |
| return pd.DataFrame(), "URL inválida. Verifique e tente novamente.", None, None | |
| headers = {"User-Agent": "Mozilla/5.0"} | |
| try: | |
| response = requests.get(url, headers=headers, timeout=30) | |
| response.raise_for_status() | |
| soup = BeautifulSoup(response.text, "html.parser") | |
| def get_main_content(soup): | |
| elements = soup.find_all(["article", "main", "div"]) | |
| return max(elements, key=lambda el: len(el.get_text()), default=None) | |
| title = soup.title.string if soup.title else "" | |
| main_content = get_main_content(soup) | |
| page_text = main_content.get_text(separator='\n', strip=True) if main_content else soup.get_text(separator='\n', strip=True) | |
| cleaned_text = clean_text_for_testada(page_text) | |
| valor_match = re.search(r'R\$\s*(\d[\d\.,]*)', page_text) | |
| valor = float(valor_match.group(1).replace('.', '').replace(',', '.')) if valor_match else None | |
| area_match = re.search(r'(\d+)\s*m²', page_text) | |
| area = float(area_match.group(1)) if area_match else None | |
| dorm_match = re.search(r'(\d+)\s*(quarto|quartos|dormit[oó]rio|dormit[oó]rios)', page_text, re.IGNORECASE) | |
| dorm = int(dorm_match.group(1)) if dorm_match else None | |
| banheiro_match = re.search(r'(\d+)\s*(banheiro|banheiros)', page_text, re.IGNORECASE) | |
| banheiros = int(banheiro_match.group(1)) if banheiro_match else None | |
| vagas_match = re.search(r'(\d+)\s*(vaga|vagas)', page_text, re.IGNORECASE) | |
| vagas = int(vagas_match.group(1)) if vagas_match else None | |
| suite_match = re.search(r'(\d+)\s*(su[ií]te|su[ií]tes)', page_text, re.IGNORECASE) | |
| suites = int(suite_match.group(1)) if suite_match else None | |
| endereco = extract_address(page_text) | |
| testada = extract_testada(cleaned_text) | |
| result_text = f"**{title}**\n\n{page_text[:30000]}..." if title else f"{page_text[:30000]}..." | |
| df = pd.DataFrame([{ | |
| "Endereço": endereco, | |
| "Área": area, | |
| "Testada": testada, | |
| "Valor": valor, | |
| "Dorm": dorm, | |
| "Banheiros": banheiros, | |
| "Vagas": vagas, | |
| "Suítes": suites, | |
| "URL": url, | |
| }]) | |
| return df, result_text, endereco, valor | |
| except requests.RequestException as e: | |
| return pd.DataFrame(), f"Erro ao acessar a URL: {e}", None, None | |
| # Substitua 'YOUR_API_KEY' pela chave de API real que você obteve | |
| api_key = 'xxxxx' | |
| # Função para obter latitude e longitude a partir de um endereço | |
| def obter_coordenadas(endereco, gmaps): | |
| try: | |
| geocode_result = gmaps.geocode(endereco) | |
| if geocode_result: | |
| location = geocode_result[0]['geometry']['location'] | |
| return location['lat'], location['lng'] | |
| except Exception as e: | |
| print(f"Erro ao obter coordenadas para {endereco}: {e}") | |
| return None, None | |
| # Acumulador | |
| def adicionar_ao_acumulado(df_atual, df_acumulado, topo, rel, sup, apr, ace, pav, ec, id, pad, iso, pos, loc): | |
| gmaps = googlemaps.Client(key=api_key) | |
| if df_atual.empty: | |
| return df_acumulado, df_acumulado, "", "" | |
| df_novo = df_atual.copy() | |
| start_index = len(df_acumulado) + 1 | |
| df_novo.index = range(start_index, start_index + len(df_novo)) | |
| df_novo.index.name = "Dado" | |
| # Adicionar data atual no formato dd/mm/aa | |
| data_hoje = datetime.now().strftime('%d/%m/%y') | |
| df_novo.insert(1, "Data", data_hoje) | |
| # Adicionar valores dos dropdowns | |
| df_novo["Topografia"] = topo | |
| df_novo["Relevo"] = rel | |
| df_novo["Superfície"] = sup | |
| df_novo["Aproveitamento"] = apr | |
| df_novo["Acessibilidade"] = ace | |
| df_novo["Pavimentação"] = pav | |
| df_novo["Estado de conservação"] = ec | |
| df_novo["Idade estimada"] = id | |
| df_novo["Padrão construtivo"] = pad | |
| df_novo["Tipo de Construção"] = iso | |
| df_novo["Posição"] = pos | |
| df_novo["Localização"] = loc | |
| # Calcular VU (Valor / Área), evitando divisão por zero ou nulos | |
| df_novo["VU"] = df_novo.apply( | |
| lambda row: round(row["Valor"] / row["Área"], 2) if row["Área"] and row["Valor"] else None, | |
| axis=1) | |
| # Reordenar colunas para colocar VU depois de Valor | |
| cols = df_novo.columns.tolist() | |
| valor_index = cols.index("Valor") | |
| vu_index = cols.index("VU") | |
| cols.insert(valor_index + 1, cols.pop(vu_index)) | |
| df_novo = df_novo[cols] | |
| # Adicionar colunas lat e lon | |
| df_novo['lat'] = None | |
| df_novo['lon'] = None | |
| # Obter coordenadas para cada endereço | |
| for index, row in df_novo.iterrows(): | |
| endereco = row['Endereço'] | |
| lat, lon = obter_coordenadas(endereco, gmaps) | |
| print(f"Endereço: {endereco}, Lat: {lat}, Lon: {lon}") # Debug print | |
| df_novo.at[index, 'lat'] = lat | |
| df_novo.at[index, 'lon'] = lon | |
| df_novo.reset_index(inplace=True) | |
| df_acumulado = pd.concat([df_acumulado, df_novo], ignore_index=True) | |
| df_acumulado.to_excel("Banco de dados.xlsx", index=False) | |
| # Calcular estatísticas | |
| quantidade_dados = len(df_acumulado) | |
| valor_max = df_acumulado["Valor"].max() | |
| valor_min = df_acumulado["Valor"].min() | |
| valor_medio = df_acumulado["Valor"].mean() | |
| valor_mediana = df_acumulado["Valor"].median() | |
| vu_max = df_acumulado["VU"].max() | |
| vu_min = df_acumulado["VU"].min() | |
| vu_medio = df_acumulado["VU"].mean() | |
| vu_mediana = df_acumulado["VU"].median() | |
| # Criar texto com estatísticas | |
| def format_brl(valor): | |
| return f"R$ {valor:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".") | |
| stats_text = ( | |
| f"**Quantidade de dados:** {quantidade_dados}\n\n" | |
| f"**Valor:** Máximo: {format_brl(valor_max)}, Mínimo: {format_brl(valor_min)}, " | |
| f"Média: {format_brl(valor_medio)}, Mediana: {format_brl(valor_mediana)}\n\n" | |
| f"**VU:** Máximo: {format_brl(vu_max)}, Mínimo: {format_brl(vu_min)}, " | |
| f"Média: {format_brl(vu_medio)}, Mediana: {format_brl(vu_mediana)}" | |
| ) | |
| return df_acumulado, df_acumulado, "Banco de dados.xlsx", stats_text | |
| # Função para gerar um mapa | |
| def gerar_mapa(df_acumulado): | |
| df_acumulado['lat'] = df_acumulado['lat'].astype(str).str.replace(',', '.').astype(float) | |
| df_acumulado['lon'] = df_acumulado['lon'].astype(str).str.replace(',', '.').astype(float) | |
| df_filtrado = df_acumulado[pd.notnull(df_acumulado['lat']) & pd.notnull(df_acumulado['lon'])] | |
| if df_filtrado.empty: | |
| return "<p style='color:red;'>Nenhum ponto com coordenadas válidas.</p>" | |
| centro = [df_filtrado.iloc[0]['lat'], df_filtrado.iloc[0]['lon']] | |
| mapa = folium.Map(location=centro, zoom_start=13) | |
| marker_cluster = MarkerCluster().add_to(mapa) | |
| for _, row in df_filtrado.iterrows(): | |
| popup_text = f""" | |
| <b>Endereço:</b> {row.get('Endereço', 'N/A')}<br> | |
| <b>Área:</b> {row.get('Área', 'N/A')} m²<br> | |
| <b>Valor:</b> R$ {row.get('Valor', 'N/A')}<br> | |
| <b>URL:</b> <a href="{row.get('URL', '#')}" target="_blank">Link</a> | |
| """ | |
| folium.CircleMarker( | |
| location=[row['lat'], row['lon']], | |
| radius=7, | |
| color='#000000', | |
| fill=True, | |
| fill_color='#E0C200', | |
| fill_opacity=0.9, | |
| popup=folium.Popup(popup_text, max_width=300) | |
| ).add_to(marker_cluster) | |
| # Salva como string HTML (sem gravar em arquivo) | |
| mapa_html = mapa.get_root().render() | |
| # Retorna como iframe para renderizar no Gradio | |
| return f"<iframe srcdoc='{mapa_html}' width='100%' height='600px' style='border:none;'></iframe>" | |
| # Limpeza do anúncio atual | |
| def clear_fields(): | |
| # Define um DataFrame com uma linha em branco | |
| empty_df = pd.DataFrame({ | |
| "Endereço": [""], | |
| "Área": [None], | |
| "Testada": [None], | |
| "Valor": [None], | |
| "VU": [None], | |
| "Dorm": [None], | |
| "Banheiros": [None], | |
| "Vagas": [None], | |
| "Suítes": [None], | |
| "URL": [""], | |
| "Topografia": ["-"], | |
| "Relevo": ["-"], | |
| "Superfície": ["-"], | |
| "Aproveitamento": ["-"], | |
| "Acessibilidade": ["-"], | |
| "Pavimentação": ["-"], | |
| "Estado de conservação": ["-"], | |
| "Idade estimada": ["-"], | |
| "Padrão construtivo": ["-"], | |
| "Tipo de construção": ["-"], | |
| "Posição": ["-"], | |
| "Localização": ["-"] | |
| }) | |
| return "", empty_df, "", False, None, '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' | |
| # Função para excluir linhas com "Dado" vazio | |
| def excluir_dados_vazios(df_acumulado): | |
| # Remove linhas onde a coluna "Dado" está vazia ou contém apenas espaços em branco | |
| df_acumulado = df_acumulado[df_acumulado["Dado"].str.strip() != ""] | |
| # Reindexar o DataFrame para manter a sequência correta | |
| df_acumulado = df_acumulado.reset_index(drop=True) | |
| return df_acumulado, df_acumulado | |
| # Função para mostrar o anúncio na tela | |
| def toggle_output_text(show_text, result_text): | |
| return gr.update(visible=show_text), result_text | |
| # Função para fazer o print de tela | |
| def take_screenshot_with_api(url, api_key, filename="screenshot.png"): | |
| api_url = f"https://shot.screenshotapi.net/screenshot?token={api_key}&url={url}&output=image" | |
| response = requests.get(api_url) | |
| if response.status_code == 200: | |
| with open(filename, 'wb') as file: | |
| file.write(response.content) | |
| print(f"Screenshot salvo como {filename}") | |
| return filename | |
| else: | |
| print(f"Erro ao capturar screenshot: {response.status_code}") | |
| return None | |
| # Interface | |
| # Tema | |
| theme = gr.themes.Citrus(primary_hue="yellow") | |
| # App principal | |
| with gr.Blocks(theme=theme, css=""" | |
| @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;700&display=swap'); | |
| .small-file-upload { | |
| height: 65px; | |
| text-align: center; | |
| color: black; | |
| border: 2px solid black !important; | |
| box-sizing: border-box; | |
| } | |
| .small-file-upload span { | |
| display: none; | |
| } | |
| .small-file-upload input[type="file"] { | |
| color: black; | |
| } | |
| .small-file-upload label { | |
| color: black; | |
| } | |
| .small span { | |
| font-size: 1.2em; | |
| white-space: nowrap; | |
| width: auto; | |
| display: inline-block; | |
| } | |
| .small span dados { | |
| font-size: 0.8em; | |
| white-space: nowrap; | |
| width: auto; | |
| display: inline-block; | |
| } | |
| h1 { | |
| text-align: center; | |
| font-family: 'Quicksand', sans-serif; | |
| font-weight: 700; | |
| margin: 20px 0; | |
| color: black; | |
| } | |
| .map-container { | |
| height: 600px !important; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| """) as app: | |
| gr.Markdown( | |
| "<div style='font-size: 1.5em;'>" | |
| "<span style='color: gray;'>Pesquisa.AI - </span>" | |
| "<span style='color: gray;'>aval</span>" | |
| "<span style='color: #FFD700;'>ia</span>" | |
| "<span style='color: gray;'>.se</span>" | |
| "</div>" | |
| ) | |
| df_acumulado_state = gr.State(pd.DataFrame()) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| user_input = gr.Textbox(label="Copie a url do anúncio e cole aqui") | |
| gr.Markdown("**DADO**") | |
| submit_button = gr.Button("Carregar anúncio", variant="primary") | |
| screenshot_button = gr.Button("Print anúncio", variant="primary") | |
| clear_button = gr.Button("Limpar anúncio", variant="primary") | |
| gr.Markdown("**BANCO DE DADOS**") | |
| add_data = gr.Button("Adicionar dado", variant="primary") | |
| delete_data = gr.Button("Excluir dado", variant="primary") | |
| generate_map_button = gr.Button("Gerar Mapa", variant="primary") | |
| # Avisos com toggle de exibição | |
| with gr.Column(): | |
| disclaimer_checkbox = gr.Checkbox(label="Avisos importantes!", value=False) | |
| disclaimer_text = gr.Markdown( | |
| """ | |
| ### Avisos Importantes | |
| - **Aplicativo Online: ** | |
| Este é um aplicativo 100% online, que depende de conexão com a internet para funcionar corretamente. | |
| - **Coleta de Informações: ** | |
| O aplicativo se destina a coletar e organizar dados de sites de anúncios e imobiliárias. | |
| Não garantimos que os dados sejam verdadeiros, completos ou atualizados, pois são de responsabilidade dos sites de origem. | |
| - **Diversidade de Fontes: ** | |
| O sistema acessa vários sites diferentes para buscar informações. | |
| Por isso, pode haver erros no preenchimento automático dos campos. | |
| - ** Instabilidade dos Sites de Origem: ** | |
| Os sites de origem podem apresentar instabilidade, lentidão ou ficar temporariamente fora do ar, o que pode causar atrasos no carregamento ou falhas momentâneas na coleta de dados. | |
| - **Atenção do Usuário: ** | |
| Recomendamos que o usuário revise e corrija os dados, se necessário. | |
| Para facilitar isso, todos os campos da planilha "Banco de Dados" na interface são editáveis. | |
| - **Boas Práticas de Uso: ** | |
| Como o aplicativo é online, sujeito à instabilidade na conexão de internet ou falta de energia, é recomendável fazer o download periódico da pesquisa para garantir uma cópia segura do trabalho. | |
| """, | |
| visible=False | |
| ) | |
| disclaimer_checkbox.change( | |
| fn=lambda visible: gr.update(visible=visible), | |
| inputs=disclaimer_checkbox, | |
| outputs=disclaimer_text | |
| ) | |
| with gr.Column(scale=5): | |
| output_table = gr.Dataframe(label="Anúncio", | |
| headers=["Endereço", "Área", "Testada", "Valor", "VU", "Dorm", "Banheiros", "Vagas", "Suítes", "URL", "Topografia", "Relevo", "Superfície", "Aproveitamento", "Acessibilidade", "Pavimentação", "Estado de conservação", "Idade estimada", "Padrão construtivo", "Tipo de construção", "Posição", "Localização"], | |
| datatype=["str", "number", "number", "number", "number", "number", "number", "number", "number", "str", "str", "str", "str", "str", "str", "str", "str", "str", "str", "str", "str", "str"], | |
| column_widths=[200, 100, 120, 120, 100, 100, 120, 100, 100, 300, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100], | |
| interactive=True, | |
| max_height=250 | |
| ) | |
| with gr.Group("Características do imóvel"): | |
| with gr.Row(): | |
| topo_drop = gr.Dropdown(label="Topografia", choices=list(dict_topo.keys()), value='-', interactive=True) | |
| rel_drop = gr.Dropdown(label="Relevo", choices=list(dict_rel.keys()), value='-', interactive=True) | |
| sup_drop = gr.Dropdown(label="Superfície", choices=list(dict_sup.keys()), value='-', interactive=True) | |
| apr_drop = gr.Dropdown(label="Aproveitamento", choices=list(dict_apr.keys()), value='-', interactive=True) | |
| ace_drop = gr.Dropdown(label="Acessibilidade", choices=list(dict_ace.keys()), value='-', interactive=True) | |
| pav_drop = gr.Dropdown(label="Pavimentação", choices=["Sim", "Não", "-"], value='-', interactive=True) | |
| with gr.Row(): | |
| ec_drop = gr.Dropdown(label="Estado de conservação", choices=list(dict_ec.keys()), value='-', interactive=True) | |
| id_drop = gr.Dropdown(label="Idade estimada", choices=list(dict_id.keys()), value='-', interactive=True) | |
| pad_drop = gr.Dropdown(label="Padrão construtivo", choices=list(dict_pad.keys()), value='-', interactive=True) | |
| iso_drop = gr.Dropdown(label="Tipo de construção", choices=["Isolada", "Não isolada", "-"], value='-', interactive=True) | |
| pos_drop = gr.Dropdown(label="Posição", choices=["Meio de quadra", "Esquina", "-"], value='-', interactive=True) | |
| loc_drop = gr.Dropdown(label="Localização", choices=list(dict_loc.keys()), value='-', interactive=True) | |
| with gr.Row(): | |
| show_text_checkbox = gr.Checkbox(label="Mostrar anúncio", value=False, scale=4) | |
| screenshot_output = gr.Image(label="Print", type="filepath", height=50, scale=1) | |
| output_text = gr.Markdown(label="Resultado", visible=False) | |
| acumulado_table = gr.Dataframe( | |
| label="Banco de dados", | |
| headers=[ | |
| "Dado", "Data", "Endereço", "Área", "Testada", "Valor", "VU", "Dorm", "Banheiros", "Vagas", "Suítes", "URL", | |
| "Topografia", "Relevo", "Superfície", "Aproveitamento", "Acessibilidade", "Pavimentação", | |
| "Estado de conservação", "Idade estimada", "Padrão construtivo", "Tipo de construção", | |
| "Posição", "Localização", "lat", "lon" | |
| ], | |
| datatype=[ | |
| "str", "str", "str", "number", "number", "number", "number", "number", "number", "number", "number", "str", | |
| "str", "str", "str", "str", "str", "str", "str", "str", "str", "str", "str", "str", "number", "number" | |
| ], | |
| column_widths=[ | |
| 80, 90, 200, 100, 120, 120, 100, 100, 120, 100, 100, 300, | |
| 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 | |
| ], | |
| interactive=True, | |
| max_height=500 | |
| ) | |
| bd = gr.File(label="Exportar para excel", height=50, elem_classes=["small-file-upload"]) | |
| stats_output = gr.Markdown(label="Estatísticas dos Dados Acumulados") | |
| map_output = gr.HTML(label="Mapa") | |
| # Lógica dos botões | |
| submit_button.click(fetch_url_info, inputs=user_input, outputs=[output_table, output_text, gr.State(), gr.State()]) | |
| clear_button.click(clear_fields, outputs=[user_input, output_table, output_text, show_text_checkbox, screenshot_output, topo_drop, rel_drop, sup_drop, apr_drop, ace_drop, pav_drop, ec_drop, id_drop, pad_drop, iso_drop, pos_drop, loc_drop]) | |
| show_text_checkbox.change(toggle_output_text, inputs=[show_text_checkbox, output_text], outputs=[output_text, output_text]) | |
| screenshot_button.click( | |
| lambda url: take_screenshot_with_api(url, api_key), | |
| inputs=user_input, | |
| outputs=screenshot_output | |
| ) | |
| add_data.click(adicionar_ao_acumulado, inputs=[output_table, df_acumulado_state, topo_drop, rel_drop, sup_drop, apr_drop, ace_drop, pav_drop, ec_drop, id_drop, pad_drop, iso_drop, pos_drop, loc_drop], outputs=[df_acumulado_state, acumulado_table, bd, stats_output]) | |
| delete_data.click(excluir_dados_vazios, inputs=df_acumulado_state, outputs=[df_acumulado_state, acumulado_table]) | |
| generate_map_button.click(gerar_mapa, inputs=df_acumulado_state, outputs=map_output) | |
| app.launch(share=True) | |
| # MELHORIAS (a fazer) | |
| # Dataframes: Adicionar a origem do anúncio (Inserir uma coluna com o nome da imobiliária que originou o anúncio). | |
| # Dataframe acumulado: Excluir dado e reorganizar planilha. | |
| # Adaptar à outras imobiliárias e portais. Obs. Foram criados dois códigos alternativos ao Jina. O Primeiro com texto limpo. O segundo com os marcadores, talvez este seja o caminho. | |
| # Dataframe acumulado: Adicionar os coeficientes (utilizar os dicionários). | |
| # O print do anúncio, quando exportado, deverá ser nomeado com endereço e valor. | |
| # Testar o fluxo e estabilidade do sistema. | |
| # Quando clicar em "adicionar dado" limpar automaticamente o anúncio; | |
| # Dispor as colunas na seguinte ordem: Dado / Endereço / Área / Demais características utilizadas/ V_Unit / Valor Total / URL / Características não utilizadas; | |
| # Disclaimer (diminuir a fonte) | |
| # Retirar o jina.ai | |
| # Informação de utilização | |
| # Fale conosco |