3 điểm bởi GN⁺ 2024-03-04 | 1 bình luận | Chia sẻ qua WhatsApp
  • Observable Framework là một công cụ mã nguồn mở nhằm vượt ra ngoài mô hình notebook vốn mạnh về khám phá dữ liệu tạm thời, để triển khai các ứng dụng dữ liệu, dashboard và báo cáo tải nhanh dưới dạng site tĩnh
  • Các khối mã js và biểu thức inline trong Markdown được chạy trên trình duyệt, và khi giá trị phản ứng như now thay đổi, các phần hiển thị liên quan cũng tự động được cập nhật
  • Framework giữ lại tính phản ứng của Observable Notebooks, đồng thời cung cấp một file Markdown duy nhất, JavaScript tiêu chuẩn và quy trình làm việc thân thiện với Git
  • Các thư viện như Inputs, d3, Plot được tải lười trong quá trình phát triển; khi build và triển khai, chỉ phần mã được tham chiếu mới được tự động tải từ jsdelivr CDN
  • Dùng Data loader có thể chuẩn bị dữ liệu bằng bất kỳ ngôn ngữ nào tại thời điểm build rồi đóng gói thành các file tĩnh như JSON, CSV, giúp triển khai dashboard với ít phụ thuộc backend hơn

Trình tạo site tĩnh cho ứng dụng dữ liệu

  • Observable Framework là trình tạo site tĩnh biên dịch Markdown, JavaScript và nếu cần cả các ngôn ngữ khác thành trang tương tác
  • Nó bao gồm một máy chủ hot reload đầy đủ tính năng, nên khi chỉnh sửa và lưu file trong trình soạn thảo, các thay đổi sẽ được phản ánh ngay trong trình duyệt
  • Khi hoàn tất, có thể dùng lệnh build để tạo một tập các file tĩnh
    • Các file này có thể được triển khai lên máy chủ
    • Cũng có thể triển khai trực tiếp lên nền tảng chia sẻ đã xác thực của Observable bằng npm run deploy

JavaScript chạy bên trong Markdown

  • Thiết kế cốt lõi của Framework là đưa JavaScript vào tài liệu Markdown để tạo tài liệu tương tác
  • Các khối mã Markdown được gắn nhãn js sẽ được chạy như JavaScript trong trình duyệt của người dùng
  • Cũng có thể dùng biểu thức inline, chẳng hạn hiển thị thời gian hiện tại dưới dạng chuỗi với ${new Date(now)}
  • now là biến đặc biệt cung cấp thời gian hiện tại tính bằng mili giây kể từ epoch và liên tục được cập nhật
    • Khi now thay đổi, các cell và biểu thức inline tham chiếu đến nó cũng được cập nhật theo
  • Trong Observable Notebooks, mã và Markdown được viết trong các cell riêng biệt, còn trong Framework thì cả hai nằm trong một tài liệu văn bản duy nhất
  • Biểu thức inline và khối js có thể có cách hiển thị khác nhau
    • Biểu thức inline dùng biểu diễn chuỗi mặc định của đối tượng JavaScript
    • Khối js dùng hàm display() của Observable, còn quy tắc hiển thị nằm trong inspect/src/inspect.js

Duy trì mô hình thực thi phản ứng

  • Tính phản ứng, chức năng cốt lõi của Observable Notebooks, cũng được duy trì trong tài liệu JavaScript Markdown của Framework
  • Khi một cell thay đổi, các cell khác phụ thuộc vào cell đó sẽ tự động được đánh giá lại
  • Đây là điểm khác biệt lớn so với Jupyter notebooks, và cũng là tính năng tiêu biểu của công cụ notebook Python marimo
  • Hiệu quả càng rõ khi dùng cùng các input biểu mẫu
    • Nếu thêm input vào trang và tham chiếu giá trị của nó ở phần khác trong tài liệu, có thể dễ dàng tạo tương tác thời gian thực

Ví dụ dashboard lượt tải PyPI

  • Dashboard ví dụ hiển thị thống kê lượt tải PyPI theo từng package Python; phiên bản Observable Framework gồm một tài liệu Markdown dài 57 dòng
  • Người dùng chọn package từ mảng packages bằng Inputs.select()
    • Inputs.select() là phương thức được bao gồm trong Framework và có thể xem trong tài liệu Observable Inputs
    • Hàm view() là tính năng mới được thêm vào Framework, giúp thay đổi lựa chọn input được phản ánh trong các khối mã khác của tài liệu
  • packageName được định nghĩa bằng const nên có thể dùng trong các khối js khác trên trang
  • Dữ liệu được lấy bằng d3.json()
    • Trong Framework có thể dùng toàn bộ D3
    • URL bao gồm tên package đã chọn
    • Nguồn dữ liệu là JSON API của Datasette
  • Bảng SQLite nằm tại datasette.io/content/stats, được cập nhật mỗi ngày một lần bằng thống kê package PyPI mới nhất
    • Workflow GitHub Actions liên quan đã được đề cập trong bài trước về baked data
  • Thêm .json vào URL sẽ trả về JSON
    • Chỉ yêu cầu các hàng của một package cụ thể
    • Sắp xếp theo ngày giảm dần
    • Nhận tối đa 1.000 hàng dưới dạng mảng đối tượng
  • Chuỗi ngày của SQLite được chuyển thành đối tượng JavaScript Date bằng d3.timeParse("%Y-%m-%d")
  • Biểu đồ được render bằng Observable Plot, được đóng gói cùng Framework
  • Danh sách package được lấy bằng cách chạy truy vấn SQL trực tiếp trên cơ sở dữ liệu /content của Datasette
    • Truy vấn là select package from stats group by package order by max(downloads) desc
    • _shape=arrayfirst là cách viết tắt để nhận cột đầu tiên của các hàng kết quả dưới dạng mảng JSON

Chỉ bao gồm mã được dùng

  • Dashboard ví dụ sử dụng các thư viện bổ sung như Inputs, d3, Plot
  • Ở chế độ phát triển, tải lười được áp dụng
    • Mã chỉ được tải khi lần đầu được dùng trong một cell
  • Khi build và triển khai ứng dụng, Framework tự động chỉ tải phần mã thư viện được tham chiếu từ jsdelivr CDN

Cache dữ liệu tại thời điểm build

  • Data loader của Framework là tính năng chuẩn bị dữ liệu dashboard tại thời điểm build
  • Dashboard Framework có thể dùng fetch() hoặc wrapper của nó ở runtime để lấy dữ liệu từ bất cứ đâu
    • Observable Notebooks cũng hoạt động theo cùng cách
    • Với cách này, hiệu năng dashboard phụ thuộc vào backend được kết nối
  • Framework khuyến nghị mô hình tạo dữ liệu cho dashboard tại thời điểm triển khai và chỉ đóng gói phần tập con dữ liệu cần thiết thành file tĩnh
    • File dữ liệu tĩnh có thể được phục vụ nhanh từ cùng hệ thống hosting tĩnh với mã dashboard
  • Data loader là script được viết bằng bất kỳ ngôn ngữ lập trình nào
    • Khi build, Framework chạy script đó
    • Kết quả xuất ra stdout của script được lưu thành file
  • Ví dụ là đặt curl https://earthquake.usgs.gov/earthquakes/feed/… trong file quakes.json.sh
    • Khi build, tên file cho Framework biết file đích là quakes.json và loader cần chạy là .sh
  • Miễn là có thể xuất JSON, CSV hoặc một định dạng hữu ích ra stdout, có thể lấy dữ liệu bằng bất kỳ công nghệ nào

Khác biệt với Observable Notebooks

  • Observable Framework tái sử dụng nhiều ý tưởng và mã của Observable Notebooks, nhưng khác biệt lớn ở định dạng file và môi trường thực thi
  • Observable Notebooks hiện có các đặc điểm sau khi so với Jupyter Notebooks
    • Dùng JavaScript thay vì Python
    • Bản thân trình chỉnh sửa notebook không phải mã nguồn mở, mà là sản phẩm hosted được cung cấp tại observablehq.com
    • Có thể xuất notebook thành file tĩnh để chạy ở bất cứ đâu, nhưng trình chỉnh sửa là sản phẩm độc quyền
    • Các cell có tính phản ứng; khi một cell thay đổi, các cell phụ thuộc vào nó được tự động đánh giá lại giống như Excel
    • Để hỗ trợ mô hình phản ứng, Observable tạo từ khóa tùy biến viewof, nên cú pháp JavaScript không hoàn toàn là tiêu chuẩn
    • Notebook có thể chỉnh sửa là một định dạng file độc quyền phức tạp và không phù hợp với các công cụ như Git, nên Observable triển khai hệ thống quản lý phiên bản và cộng tác riêng
  • Observable Framework chuyển mô hình này sang định dạng file đơn giản hơn và môi trường thực thi mã nguồn mở
    • Tài liệu là một file Markdown duy nhất có chứa các khối JavaScript
    • Vẫn có tính phản ứng, nhưng có thể chỉnh sửa bằng bất kỳ trình soạn thảo văn bản nào và đưa vào Git
    • Toàn bộ là mã nguồn mở theo giấy phép ISC, và có thể chạy toàn bộ stack chỉnh sửa trên máy local
    • Chỉ dùng JavaScript tiêu chuẩn, không có cú pháp tùy biến

Sự thay đổi định hướng của Observable

  • Observable Framework có vẻ cho thấy Observable đang nghiêng nhiều hơn về công cụ cho developer, thay vì công cụ cộng tác xoay quanh trình chỉnh sửa Observable Notebook độc quyền trước đây
  • Dòng giới thiệu trên Twitter của Observable là “The end-to-end solution for developers who want to build and host dashboards that don’t suck”
    • Bản lưu Internet Archive ngày 3/10/2023 ghi “Build data visualizations, dashboards, and data apps that impact your business — faster.”
  • Việc sử dụng Observable Notebooks có thể bị hạn chế phần nào do tính độc quyền của nền tảng và các giới hạn của tài khoản miễn phí, đặc biệt là không có notebook riêng tư miễn phí
  • Các thư viện mã nguồn mở như Observable Plot đã được đánh giá là những công nghệ có thể dùng tích cực
  • Observable Framework triển khai lại những ý tưởng từng làm Observable Notebooks hấp dẫn theo mô hình mã nguồn mở, JavaScript tiêu chuẩn, một file văn bản duy nhất và triển khai tĩnh

1 bình luận

 
GN⁺ 2024-03-04
Ý kiến trên Hacker News
  • Theo một khía cạnh nào đó, Observable Framework giống như Avengers: Endgame của vũ trụ điện ảnh Mike Bostock vậy
    Nó gom d3, Observable, Observable Plot, HTL lại với nhau, rồi còn thêm vào khá nhiều ý tưởng mới

    • Cá nhân tôi vẫn thích Polymaps nhất trong các sáng tạo của ông ấy
    • Có cảm giác như đang tạo ra thứ dành cho agent lập trình AI “được con người tăng cường”
      Observable vốn đã có tích hợp AI, còn cái này trông giống một lớp wrapper giúp AI dễ kết hợp và tận dụng hơn. Phần đánh giá chiến lược mà không tính đến AI thì tôi thấy hơi gượng
    • Trước đây tôi từng bookmark Observable và Observable Framework nhưng chưa xem kỹ
      Hôm nay tôi bắt đầu tìm cách host Jupyter Notebook tĩnh hoặc chạy tương tác bằng WASM, nhưng có vẻ Observable Framework sẽ phù hợp hơn với phần lớn mục đích sử dụng
  • Vấn đề của Observable là tuy trông như một kho ví dụ d3, code của nó lại được thiết kế để chạy bên trong framework đó nên không thể cứ copy-paste là dùng được
    d3 vốn cũng không dễ dùng nếu thiếu ví dụ, đặc biệt là nhiều khi các thay đổi giữa các phiên bản không tương thích. Dù vậy, trên site có rất nhiều đồ họa đáng kinh ngạc
    [0] https://observablehq.com/@d3/gallery

    • Đồng ý 100%. Tôi mãi vẫn khó vượt qua được việc nó hơi khác JavaScript thật
      Nó đủ gần với ngôn ngữ gốc đến mức lẽ ra có thể chỉ dùng JavaScript bình thường, cộng thêm một chút API để hiển thị đồ họa
    • Cộng đồng từng công bố tài liệu chuyển JavaScript kiểu Observable sang JavaScript thông thường
      Chủ yếu là viết lại các định nghĩa cell ở cấp cao nhất
      [0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
    • Điểm đó cực kỳ khó chịu. Nó giống kiểu khóa chặt vào nền tảng mà chẳng công ty nào nên tự hào
      Tôi đặc biệt bực chuyện đó với notebook ObservableHQ. Chúng vừa là ví dụ tuyệt vời, vừa trở thành tài liệu vô dụng. Tuy nhiên phía Framework lần này có vẻ mở hơn một chút, ít nhất cũng có thể tự host, nên tôi đang theo dõi thêm
    • Cái này có vẻ không hẳn là vấn đề của Observable, mà gần với việc d3 không chuẩn bị các ví dụ có thể copy-paste sang nơi khác hơn
      Hơn nữa bài này nói rằng Observable Framework mới đã loại bỏ một phần các vấn đề của notebook Observable cũ, nên bình luận này hơi lệch khỏi bài viết. Giờ thì kiểu như “tất cả đều là JavaScript chuẩn, không có cú pháp tùy chỉnh”
  • Framework cũng rất dễ triển khai lên GitHub Pages
    Tôi đã tổng hợp các bước và GitHub Action mẫu ở đây: https://notes.billmill.org/programming/observable_framework/...

  • Tác giả đã chỉ ra rất đúng về Framework
    Tôi đã thử dùng Observable Framework để tạo một plot tương tác nhỏ (https://github.com/willmeyers/observable-ssta), và quá trình thiết lập rồi vẽ dữ liệu dễ đến khó tin. Phàn nàn duy nhất là tôi ước có thể cấu hình Python data loader dùng virtualenv

    • Tôi đang dùng cấu hình với poetry để chạy Python data loader bên trong virtualenv do poetry quản lý
      Sau khi tạo project Python, khi khởi động dev server, thay vì chạy yarn run dev thì chạy poetry run yarn run dev, như vậy Python sẽ chạy trong virtualenv. Cấu hình này cho phép định nghĩa phần code có thể tái sử dụng và unit test được trong data loader dưới dạng một package Python tùy chỉnh, rồi import nó trong các file *.json.py để giữ chúng cực kỳ đơn giản
    • Không thể giải quyết bằng nodeenv của Python sao? Tôi thường làm vậy khi thêm JS vào project
      Các công cụ như node và npm/yarn, cả JavaScript nữa, đều được giữ chung trong venv
    • Không phải có thể thêm shebang vào data loader .sh, rồi trỏ đến đường dẫn đầy đủ của bin/python trong thư mục môi trường ảo sao?
  • Gần đây tôi vừa hoàn thành dự án “thực chiến” đầu tiên bằng notebook Observable
    Công việc gồm học Observable Plot, Arquero, ôn lại một phần JavaScript, và tích hợp với simulator nền Rust là quy trình tạo dữ liệu. Nói thật là rất tuyệt. Tôi tốn khá nhiều năng lượng để học các công cụ, và khả năng tham số hóa bộ tạo dữ liệu vẫn còn chưa như ý, nhưng notebook cuối cùng đẹp và chạy tốt
    Dùng Markdown và tính phản ứng khiến những notebook kiểu này thực sự có cảm giác dùng được. Định dạng tùy chỉnh của Jupyter khiến việc quản lý phiên bản khó hơn rất nhiều, còn nếu thiếu tính phản ứng thì các notebook được thiết kế lặp đi lặp lại rất dễ thành một mớ trạng thái hỗn độn: dễ viết nhưng khó đọc. Tôi cũng đã thử với Quarto và tích hợp Observable của nó, nhưng cảm giác như chắp vá tạm bợ
    Thành thật mà nói, đây là lần đầu tiên tôi thấy việc viết notebook và chia sẻ với người khác vừa vui vừa đáng mong chờ. Chắc chắn về sau vẫn còn những góc cạnh sắc, nhưng sau dự án này, nó đã trở thành lựa chọn đầu tiên của tôi trong các công cụ notebook

    • Nếu đang tìm lựa chọn thay thế Quarto, cũng nên xem Living Papers mới được công bố gần đây, cho phép viết tài liệu phản ứng/tĩnh từ một nguồn duy nhất
      [0]: https://living-papers.vercel.app/
  • Nếu muốn nhanh chóng thử và nghịch Framework ngay trong trình duyệt, tôi đã tạo một Codespace devcontainer tự động cấu hình môi trường Node và Python
    [0]: https://github.com/dleeftink/observable-codespace

  • Có nên chuyển từ Jupyter Notebook sang Observable không? Hay bản thân việc chia hai thứ như vậy đã là một cách đặt vấn đề sai?

    • Cuối cùng, tôi nghĩ cốt lõi của câu hỏi là bạn làm việc năng suất hơn với Python và hệ sinh thái của nó, hay với JavaScript cùng các package của nó, đặc biệt là những thứ như D3
  • Nói cách khác về nội dung bài viết, mọi thứ trong code block có gợi ý nội dung js sẽ được chạy ngay trong trình duyệt của người dùng
    Nếu muốn hiển thị code, phải dùng gợi ý js echo. Xét về tương thích ngược, chẳng phải làm ngược lại sẽ tốt hơn sao? Ví dụ, để code chạy trong trình duyệt người dùng là một gợi ý opt-in kiểu js exec, còn gợi ý js vốn được dùng rộng rãi thì giữ nguyên. Với cấu trúc hiện tại, khi tích hợp renderer đó vào một app hiện có, bạn phải quản lý riêng xem chỗ nào được phép chạy

    • Chúng tôi có kế hoạch cho phép thay đổi tùy chọn mặc định của block
      Có thể làm theo từng trang trong front matter, hoặc áp dụng cho toàn bộ project bằng cấu hình project. Khi đó có thể đặt js run=false làm mặc định, và chỉ bật live code bằng js run khi muốn. Tuy vậy, use case chính của chúng tôi là live code nên đã chọn nó làm mặc định
    • Đúng vậy. Đây cũng là vấn đề giống như việc GitHub tự động render diagram Mermaid
      Giờ thì không thể chỉ hiển thị nguyên code block Mermaid nữa, trừ khi bỏ chú thích ngôn ngữ đi
  • Tôi đã dành cả một đêm đắm chìm trong Observable Framework và thấy rất tuyệt
    Nó gần như không gây cản trở gì, và tôi có thể trực quan hóa cũng như khám phá chi tiết lịch sử Google Maps của mình. Phần liên quan đến môi trường data loader không thật rõ ràng, nhưng chạy Python trong môi trường poetry thì giải quyết được
    Vì thích Kotlin nên tôi cũng thử tạo data loader cho Kotlin script, nhưng có vài chỗ còn thô. Kotlin kỳ vọng tên file script là foo.main.kts, còn Observable lại kỳ vọng shebang loader có thể thực thi có phần mở rộng foo.exe. Vì vậy tôi đã tạo một script exe proxy để gọi Kotlin script, nhưng như thế thì việc tự động tải lại dữ liệu không được kích hoạt
    Một điểm hơi bất tiện so với marimo hay Jupyter là phần dùng biến giữa data loader và notebook. Ví dụ, tôi muốn thay đổi phạm vi dữ liệu mà loader lấy về bằng một view component chọn ngày, nhưng không rõ phải làm thế nào. Vì vậy phân tích khám phá bị chậm đi một chút. Tôi biết điều này không hợp với paradigm, nhưng vẫn muốn nêu ra. Kết quả là trong quá trình khám phá, có thể bạn sẽ chuyển một phần đáng kể việc xử lý dữ liệu sang notebook, điều này không lý tưởng về mặt hiệu năng
    Cuối cùng, sẽ rất tốt nếu có thể định nghĩa data loader inline. Tôi thích các file đơn lẻ, nên nếu chỉ cần thêm một code block Python và Framework trích xuất nó thành file thì đó sẽ là một cải thiện nhỏ về chất lượng trải nghiệm. Dù còn sớm, Framework trông rất hứa hẹn. Tôi mong có thể đưa tất cả ghi chú Markdown của mình lên đây để tạo một môi trường kiểu org-mode mà không cần đi đến mức dùng Emacs đầy đủ

    • Cảm ơn phản hồi. Đang có một PR mở để giúp đăng ký interpreter mới dễ hơn, không cần đi đường vòng qua .sh hay .exe
      Nó sẽ cho phép chỉ định interpreter gắn với một phần mở rộng file cụ thể. Ví dụ .kts cho Kotlin. https://github.com/observablehq/framework/pull/935
      Cách điều khiển data loader bằng giá trị đầu vào hơi khác hướng, vì Framework ưu tiên snapshot dữ liệu tĩnh. Mục tiêu là để site đã build có tính tự chứa và hiệu năng tốt. Dù vậy, một kỹ thuật hoạt động tốt là tạo một file Parquet trong data loader chứa superset của dữ liệu bạn muốn tương tác, rồi ở client dùng DuckDB/SQL để lấy ra subset cần trực quan hóa. Thường thì hiệu năng tốt, nhưng tất nhiên còn tùy vào kích thước superset bạn muốn xử lý
  • Observable tích hợp rất tốt với ClickHouse thông qua REST API
    Ví dụ ở đây: https://observablehq.com/@stas-sl/github-issues-survival-ana...
    Tôi chưa thử Observable Framework mới, nhưng sẽ rất thú vị nếu thấy một ví dụ tương tự truy vấn cơ sở dữ liệu theo thời gian thực. Hy vọng nó không phải là kiến trúc chỉ cho phép tải trước và cache toàn bộ dữ liệu. Những app kiểu này cần có tính tương tác, nên lý tưởng là phải cho phép hiển thị SQL để chỉnh sửa live