3 pontos por GN⁺ 2024-03-04 | 1 comentários | Compartilhar no WhatsApp
  • Observable Framework é uma ferramenta open source que vai além do modelo de notebooks, forte para exploração ad hoc de dados, e permite publicar data apps, dashboards e relatórios de carregamento rápido como sites estáticos
  • Blocos de código js e expressões inline dentro do Markdown são executados no navegador, e quando valores reativos como now mudam, as exibições relacionadas também são atualizadas automaticamente
  • O Framework mantém a reatividade dos Observable Notebooks, mas oferece um único arquivo Markdown, JavaScript padrão e um fluxo de trabalho amigável ao Git
  • Bibliotecas como Inputs, d3 e Plot são carregadas sob demanda durante o desenvolvimento e, no build e na implantação, apenas o código referenciado é carregado automaticamente a partir da CDN jsdelivr
  • Com um Data loader, é possível preparar dados em qualquer linguagem no momento do build e empacotá-los como arquivos estáticos, como JSON ou CSV, permitindo publicar dashboards com menos dependência de backend

Gerador de sites estáticos para data apps

  • Observable Framework é um gerador de sites estáticos que compila Markdown, JavaScript e, se necessário, até outras linguagens em páginas interativas
  • Ele inclui um servidor de hot reload completo, de modo que, ao editar e salvar um arquivo no editor, as mudanças aparecem imediatamente no navegador
  • Ao terminar o trabalho, é possível criar um conjunto de arquivos estáticos com o comando de build
    • Esses arquivos podem ser publicados em um servidor
    • Também é possível publicar diretamente na plataforma de compartilhamento autenticada da Observable com npm run deploy

JavaScript executado dentro do Markdown

  • O design central do Framework é criar documentos interativos inserindo JavaScript dentro de documentos Markdown
  • Blocos de código Markdown marcados como js são executados como JavaScript no navegador do usuário
  • Também é possível usar expressões inline, exibindo a hora atual como string com algo como ${new Date(now)}
  • now é uma variável especial que fornece a hora atual em milissegundos desde a epoch e é atualizada continuamente
    • Quando now muda, as células e expressões inline que a referenciam também são atualizadas
  • Nos Observable Notebooks, código e Markdown são escritos em células separadas, mas no Framework ambos ficam em um único documento de texto
  • Expressões inline e blocos js podem ter formas diferentes de exibição
    • Expressões inline usam a representação em string padrão dos objetos JavaScript
    • Blocos js usam a função display() da Observable, e as regras de exibição estão em inspect/src/inspect.js

Manutenção do modelo de execução reativo

  • A reatividade, recurso central dos Observable Notebooks, também é mantida nos documentos JavaScript Markdown do Framework
  • Quando uma célula muda, outras células que dependem dela são reavaliadas automaticamente
  • Essa abordagem é uma grande diferença em relação aos Jupyter notebooks e também é um recurso marcante da ferramenta de notebooks Python marimo
  • O efeito é grande quando usada com entradas de formulário
    • Ao adicionar uma entrada à página e referenciar seu valor em outras partes do documento, é fácil criar interação em tempo real

Exemplo de dashboard de downloads do PyPI

  • O dashboard de exemplo mostra estatísticas de downloads do PyPI por pacote Python, e a versão em Observable Framework é composta por um documento Markdown de 57 linhas
  • O usuário escolhe um pacote no array packages com Inputs.select()
    • Inputs.select() é um método incluído no Framework e pode ser consultado na documentação de Observable Inputs
    • A função view() é um recurso recém-adicionado no Framework e faz com que mudanças na seleção da entrada sejam refletidas em outros blocos de código do documento
  • packageName é definido como const e pode ser usado em outros blocos js da página
  • Os dados são obtidos com d3.json()
    • No Framework, é possível usar todo o D3
    • A URL inclui o nome do pacote selecionado
    • A fonte de dados é a API JSON do Datasette
  • A tabela SQLite está em datasette.io/content/stats e é atualizada uma vez por dia com estatísticas recentes de pacotes do PyPI
    • O workflow relacionado do GitHub Actions foi abordado no post anterior sobre baked data
  • Ao acrescentar .json à URL, ela retorna JSON
    • Solicita apenas as linhas de um pacote específico
    • Ordena por data em ordem decrescente
    • Recebe no máximo 1.000 linhas como um array de objetos
  • Datas em string do SQLite são convertidas em objetos JavaScript Date com d3.timeParse("%Y-%m-%d")
  • O gráfico é renderizado com Observable Plot, empacotado junto com o Framework
  • A lista de pacotes é obtida executando uma consulta SQL diretamente no banco /content do Datasette
    • A consulta é select package from stats group by package order by max(downloads) desc
    • _shape=arrayfirst é uma forma abreviada de receber a primeira coluna das linhas de resultado como um array JSON

Inclui apenas o código usado

  • O dashboard de exemplo usa bibliotecas adicionais como Inputs, d3 e Plot
  • No modo de desenvolvimento, é aplicado carregamento sob demanda
    • O código só é carregado quando uma célula tenta usá-lo pela primeira vez
  • Ao fazer o build e publicar a aplicação, o Framework carrega automaticamente apenas o código das bibliotecas referenciadas a partir da CDN jsdelivr

Cache de dados no momento do build

  • O Data loader do Framework é um recurso para preparar os dados do dashboard no momento do build
  • Dashboards do Framework podem usar fetch() ou seus wrappers em tempo de execução para buscar dados de qualquer lugar
    • Observable Notebooks também funcionam dessa forma
    • Nessa abordagem, o desempenho do dashboard depende do backend conectado
  • O Framework recomenda um padrão em que os dados para o dashboard são criados no momento da implantação, e apenas o subconjunto de dados necessário é empacotado como arquivos estáticos
    • Arquivos de dados estáticos podem ser servidos rapidamente pela mesma hospedagem estática do código do dashboard
  • Um Data loader é um script escrito em qualquer linguagem de programação
    • No build, o Framework executa o script
    • O resultado da saída padrão do script é salvo em um arquivo
  • O exemplo consiste em colocar curl https://earthquake.usgs.gov/earthquakes/feed/… no arquivo quakes.json.sh
    • No build, o nome do arquivo informa ao Framework que o arquivo de destino é quakes.json e que o loader a executar é .sh
  • Se for possível emitir JSON, CSV ou outro formato útil na saída padrão, qualquer tecnologia pode ser usada para buscar os dados

Diferenças em relação aos Observable Notebooks

  • O Observable Framework reutiliza muitas ideias e código dos Observable Notebooks, mas há grandes diferenças no formato de arquivo e no ambiente de execução
  • Os Observable Notebooks existentes têm as seguintes características em comparação com os Jupyter Notebooks
    • Usam JavaScript, não Python
    • O editor de notebooks em si não é open source, mas um produto hospedado oferecido em observablehq.com
    • É possível exportar notebooks como arquivos estáticos e executá-los em qualquer lugar, mas o editor é um produto proprietário
    • As células são reativas e, quando uma célula muda, outras que dependem dela são reavaliadas automaticamente, como no Excel
    • Para dar suporte ao modelo de reatividade, foi criada uma palavra-chave customizada chamada viewof, por isso a sintaxe JavaScript não é totalmente padrão
    • Notebooks editáveis usam um formato de arquivo proprietário complexo e não combinam bem com ferramentas como Git, então a Observable implementa seu próprio sistema de versionamento e colaboração
  • O Observable Framework leva esse modelo para um formato de arquivo mais simples e um ambiente de execução open source
    • O documento é um único arquivo Markdown com blocos JavaScript
    • Ele continua sendo reativo, mas pode ser editado em qualquer editor de texto e colocado no Git
    • Tudo é open source sob licença ISC, e toda a pilha de edição pode ser executada em uma máquina local
    • Usa apenas JavaScript padrão, sem sintaxe customizada

Mudança de direção da Observable

  • O Observable Framework parece representar uma mudança da Observable, que passa de uma ferramenta colaborativa centrada no editor proprietário Observable Notebook para algo mais voltado a ferramentas para desenvolvedores
  • A descrição da Observable no Twitter é “The end-to-end solution for developers who want to build and host dashboards that don’t suck”
    • Uma cópia de 3 de outubro de 2023 no Internet Archive dizia “Build data visualizations, dashboards, and data apps that impact your business — faster.”
  • O uso dos Observable Notebooks pode ser um pouco limitado pela natureza proprietária da plataforma e pelas restrições das contas gratuitas, especialmente a ausência de notebooks privados gratuitos
  • Bibliotecas open source como Observable Plot já são avaliadas como tecnologias que podem ser usadas ativamente
  • O Observable Framework reimplementa as ideias que tornavam os Observable Notebooks atraentes em um modelo open source, com JavaScript padrão, arquivo de texto único e implantação estática

1 comentários

 
GN⁺ 2024-03-04
Opiniões no Hacker News
  • Em certo sentido, o Observable Framework parece o Avengers: Endgame do universo cinematográfico de Mike Bostock.
    Reúne d3, Observable, Observable Plot e HTL, e ainda acrescenta algumas ideias novas.

    • Pessoalmente, Polymaps continua sendo minha criação favorita dele.
    • Dá a sensação de estar criando algo para agentes desenvolvedores de IA “aprimorados por humanos”.
      O Observable já tem integração com IA, e isso parece um wrapper que facilita para a IA combinar e usar essas peças. A parte em que avaliaram a estratégia sem IA me pareceu um pouco estranha.
    • Eu já tinha colocado Observable e Observable Framework nos favoritos, mas não tinha olhado em detalhes.
      Hoje comecei a ver formas de hospedar um Jupyter Notebook estático ou disponibilizá-lo de forma interativa com WASM, mas acho que, para a maioria dos usos, o Observable Framework vai se encaixar melhor.
  • O problema do Observable é que, embora pareça uma galeria de exemplos de d3, o código é projetado para rodar dentro daquele framework, então não dá simplesmente para copiar e colar.
    d3 também não é exatamente fácil de usar sem exemplos, sobretudo porque muitas vezes há mudanças incompatíveis entre versões. Ainda assim, o site tem muitos gráficos impressionantes.
    [0] https://observablehq.com/@d3/gallery

    • Concordo 100%. Nunca consegui superar o fato de ser ligeiramente diferente de JavaScript real.
      É perto o bastante da linguagem base que talvez desse para simplesmente usar JavaScript, acrescentando só algumas APIs para exibição de gráficos.
    • A comunidade já publicou um material para converter JavaScript no estilo Observable em JavaScript comum.
      Na maior parte, é um trabalho de reescrever as definições de células de nível superior.
      [0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
    • Isso é extremamente frustrante. Parece um caso de dependência de plataforma do qual nenhuma empresa deveria se orgulhar.
      Essa frustração era especialmente grande com os notebooks do ObservableHQ. Eles acabavam sendo ótimos exemplos e, ao mesmo tempo, materiais inúteis. Dito isso, este Framework parece um pouco mais aberto e, no mínimo, permite hospedagem própria, então estou acompanhando.
    • Isso parece mais um problema de o d3 não oferecer exemplos que possam ser copiados e colados em outros lugares do que um problema do Observable.
      Além disso, este artigo trata do novo Observable Framework, que eliminou alguns dos problemas dos antigos notebooks Observable, então esse comentário fica um pouco desalinhado com o texto. Agora é algo como “é tudo JavaScript padrão, sem sintaxe customizada”.
  • O Framework também é muito fácil de publicar no GitHub Pages.
    Reuni as etapas e um exemplo de GitHub Action aqui: https://notes.billmill.org/programming/observable_framework/...

  • O autor acertou em cheio sobre o Framework.
    Fiz um pequeno plot interativo usando o Observable Framework (https://github.com/willmeyers/observable-ssta), e o processo de configurar e plotar os dados foi inacreditavelmente fácil. Minha única reclamação é que seria bom poder configurar os carregadores de dados em Python para usarem virtualenv.

    • Estou usando uma configuração com poetry para executar o carregador de dados em Python dentro de um virtualenv gerenciado pelo poetry.
      Depois de criar o projeto Python, ao iniciar o servidor de desenvolvimento, basta executar poetry run yarn run dev em vez de yarn run dev, e o Python roda dentro do virtualenv. Essa configuração permite definir o código reutilizável e testável por unidade do carregador de dados como um pacote Python customizado e, depois, importá-lo nos arquivos *.json.py, mantendo-os bem simples.
    • Não daria para resolver isso com o nodeenv do Python? É o que costumo fazer quando adiciono JS a um projeto.
      Ferramentas como node e npm/yarn, além do próprio JavaScript, ficam todas dentro do venv.
    • Não daria para colocar um shebang no carregador de dados .sh e apontá-lo para o caminho completo de bin/python dentro do diretório do ambiente virtual?
  • Recentemente concluí meu primeiro projeto “de verdade” com notebooks Observable.
    Isso envolveu aprender Observable Plot e Arquero, reaprender um pouco de JavaScript e integrar com o processo de geração de dados, um simulador baseado em Rust. Sinceramente, foi excelente. Exigiu bastante energia para aprender as ferramentas, e o recurso de parametrizar o gerador de dados ainda deixa a desejar, mas o notebook final é bonito e funciona bem.
    Com Markdown e reatividade, esses notebooks realmente parecem utilizáveis. O formato customizado do Jupyter dificulta bastante o controle de versão, e sem reatividade um notebook projetado de forma iterativa vira facilmente uma bagunça baseada em estado, fácil de escrever e difícil de ler. Também tentei com o Quarto e sua integração com Observable, mas pareceu uma gambiarra de peças encaixadas.
    De verdade, foi a primeira vez que escrever um notebook e compartilhá-lo com outras pessoas foi algo prazeroso e empolgante. Ainda deve haver arestas daqui para frente, mas, depois desse projeto, virou minha primeira opção entre as ferramentas de notebook.

    • Se estiver procurando uma alternativa ao Quarto, vale olhar o Living Papers, lançado recentemente, que permite escrever documentos reativos/estáticos a partir de uma única fonte.
      [0]: https://living-papers.vercel.app/
  • Se você quiser experimentar e mexer rapidamente no Framework no navegador, foi criado um Codespace devcontainer que configura automaticamente os ambientes Node e Python
    [0]: https://github.com/dleeftink/observable-codespace

  • Será que deveríamos migrar do Jupyter Notebook para o Observable? Ou será que separar os dois dessa forma já é, por si só, uma dicotomia equivocada?

    • No fim, acho que a questão central é se você é mais produtivo em Python e seu ecossistema, ou em JavaScript e seus pacotes, especialmente coisas como D3
  • Reformulando o conteúdo do texto: tudo dentro de um bloco de código com o hint de conteúdo js é executado imediatamente no navegador do usuário
    Para mostrar o código, é preciso usar o hint js echo. Pensando em compatibilidade retroativa, não teria sido melhor o contrário? Por exemplo, deixar o código executado no navegador do usuário como um hint opt-in, tipo js exec, e manter como está o hint js, amplamente usado. Com a estrutura atual, ao integrar esse renderizador a apps existentes, é preciso gerenciar separadamente onde permitir a execução

    • Há planos para permitir mudar as opções padrão dos blocos
      Isso poderá ser feito por página, no front matter, ou aplicado ao projeto inteiro nas configurações do projeto. Assim, daria para tornar js run=false o padrão e ativar código live apenas quando desejado com js run. Dito isso, nosso principal caso de uso é código live, então escolhemos isso como padrão
    • Exato. É o mesmo problema de o GitHub renderizar automaticamente diagramas Mermaid
      Agora não dá mais para simplesmente mostrar um bloco de código Mermaid sem remover a anotação de linguagem
  • Passei uma noite inteira mergulhado no Observable Framework e gostei muito
    Quase não atrapalhou, e consegui visualizar e explorar em detalhes meu histórico do Google Maps. A parte relacionada ao ambiente dos data loaders não ficou totalmente clara, mas resolveu quando rodei Python no ambiente do poetry
    Como gosto de Kotlin, também tentei criar um data loader para scripts Kotlin, mas havia algumas arestas. Kotlin espera que o arquivo de script se chame foo.main.kts, enquanto o Observable espera a extensão foo.exe para um loader executável com shebang. Então criei um script exe proxy que chama o script Kotlin, mas aí o recarregamento automático dos dados não é disparado
    Uma pequena inconveniência em comparação com marimo ou Jupyter é o uso de variáveis entre o data loader e o notebook. Por exemplo, quero mudar, com um componente de visualização para seleção de datas, o intervalo de dados que o loader busca, mas não está claro como fazer isso. Por isso, a análise exploratória fica um pouco mais lenta. Sei que isso vai contra o paradigma, mas queria apontar. No fim, ao explorar, você pode acabar movendo uma parte considerável do processamento de dados para o notebook, o que não é ideal do ponto de vista de performance
    Por fim, seria bom poder definir data loaders inline. Gosto de arquivos únicos, então se eu adicionasse um bloco de código Python e o Framework o extraísse para um arquivo, isso seria uma pequena melhoria de qualidade de vida. Ainda é cedo, mas o Framework parece promissor. Seria ótimo poder colocar todas as minhas notas em Markdown nele e criar um ambiente parecido com org-mode sem precisar ir até um Emacs completo

    • Obrigado pelo feedback. Há um PR aberto para facilitar o registro de novos interpretadores sem precisar contornar com .sh ou .exe
      Ele deverá permitir especificar o interpretador associado a determinada extensão de arquivo. Por exemplo, .kts para Kotlin. https://github.com/observablehq/framework/pull/935
      A forma de acionar data loaders por valores de entrada fica um pouco desalinhada com o fato de o Framework preferir snapshots estáticos de dados. O objetivo é que o site gerado seja autocontido e tenha boa performance. Ainda assim, uma técnica que funciona bem é fazer o data loader gerar um arquivo Parquet com o superconjunto dos dados com os quais você quer interagir e, no cliente, usar DuckDB/SQL para extrair o subconjunto a visualizar. Em geral a performance é boa, mas, claro, depende do tamanho do superconjunto com que você está lidando
  • O Observable se integra muito bem ao ClickHouse por meio de REST API
    Há um exemplo aqui: https://observablehq.com/@stas-sl/github-issues-survival-ana...
    Ainda não usei o novo Observable Framework, mas seria interessante ver um exemplo semelhante que consulta o banco de dados em tempo real. Espero que a estrutura não permita apenas pré-carregar e cachear todos os dados. Apps desse tipo precisam ser interativos; idealmente, deveriam expor a possibilidade de editar SQL ao vivo