Objetivo prático da aula

Setup mínimo no Colab

!pip install gradio pandas numpy plotly openpyxl -q

import pandas as pd
import numpy as np
import plotly.express as px
import gradio as gr

Boa prática: versão de dependências no topo da primeira célula e imports agrupados.

App mínimo: gr.Interface

def calcular_simples(investimento, taxa, fluxo_anual, anos):
    fcls = [fluxo_anual] * int(anos)
    vpl = -investimento + sum(fc / ((1 + taxa) ** (i + 1))
                              for i, fc in enumerate(fcls))
    return f"VPL: R$ {vpl:,.2f}"

app = gr.Interface(
    fn=calcular_simples,
    inputs=[
        gr.Number(label="Investimento inicial"),
        gr.Slider(0.01, 0.5, value=0.14, step=0.01, label="Taxa de desconto"),
        gr.Number(label="Fluxo anual"),
        gr.Slider(1, 10, value=5, step=1, label="Anos")
    ],
    outputs=gr.Textbox(label="Resultado")
)
app.launch()

Arquitetura em 3 camadas

io_data.py — leitura de CSV/XLSX e limpeza de dados
service_finance.py — regras de negócio: VPL, TIR, Payback
ui_gradio.py — layout Gradio: inputs, eventos, outputs

Regra: a função de negócio não importa Gradio. Ela recebe dados Python e retorna dados Python.

Upload de arquivos com tratamento de erros

def carregar_arquivo(file_obj):
    if file_obj is None:
        raise ValueError("Envie um arquivo CSV ou XLSX.")

    nome = file_obj.name.lower()
    if nome.endswith(".csv"):
        df = pd.read_csv(file_obj.name)
    elif nome.endswith(".xlsx"):
        df = pd.read_excel(file_obj.name)
    else:
        raise ValueError("Formato invalido. Use CSV ou XLSX.")

    # Padronizar nomes de colunas
    df.columns = [c.strip().lower() for c in df.columns]

    # Validar colunas obrigatórias
    obrigatorias = ["ano", "receita", "custos", "investimento"]
    faltantes = [c for c in obrigatorias if c not in df.columns]
    if faltantes:
        raise ValueError(f"Colunas ausentes: {faltantes}")

    return df

App completo com gr.Blocks

def processar(file_obj, taxa_pct):
    df = carregar_arquivo(file_obj)
    taxa = float(taxa_pct) / 100
    # ... calcular VPL, TIR, montar tabela e gráfico ...
    fig = px.bar(df, x="ano", y="vp_fcl", title="Valor presente por ano")
    return mensagem, df_resultado, fig

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# Analise de Investimento — Projeto 5")
    with gr.Row():
        arquivo_input = gr.File(label="Envie CSV ou XLSX", file_types=[".csv", ".xlsx"])
        taxa_input = gr.Number(label="Taxa de Desconto (%)", value=14)
    with gr.Row():
        botao_calcular = gr.Button("Calcular", variant="primary")
        botao_limpar = gr.Button("Limpar")
    mensagem_out = gr.Textbox(label="Resultado")
    tabela_out = gr.Dataframe(label="Detalhamento")
    grafico_out = gr.Plot(label="Visualizacao")

    botao_calcular.click(
        fn=processar,
        inputs=[arquivo_input, taxa_input],
        outputs=[mensagem_out, tabela_out, grafico_out]
    )
    botao_limpar.click(
        fn=lambda: ("", None, None),
        inputs=[],
        outputs=[mensagem_out, tabela_out, grafico_out]
    )

demo.queue(max_size=20)
demo.launch(share=True)

5 ajustes de alto impacto no UX

  1. Títulos de campos orientados a negócio, não a código ("Taxa de Desconto (%)" em vez de "taxa").
  2. Valores padrão realistas para reduzir erro de input.
  3. Textos curtos explicando cada output.
  4. Botão "Limpar" com estado inicial previsível.
  5. Limite de tamanho de arquivo aceito para evitar travamentos.

Checklist de entrega