Visualisation Notebook with clustering and fly.io
Mix.install([
{:kino, "~> 0.12.0"},
{:kino_vega_lite, "~> 0.1.10"}
])
Setup
Livebook connected to a remote node hosted at fly.io
Checkout the link for more.
This notebook will show visualisation through live data.
Code
remote_node = String.to_atom(System.get_env("LB_REMOTE_NODE"))
cookie = String.to_atom(System.get_env("LB_COOKIE"))
Node.set_cookie(node(), cookie)
Node.connect(remote_node)
true
Node.list(:hidden)
require Kino.RPC
node = :""
Node.set_cookie(node, String.to_atom(System.fetch_env!("LB_COOKIE")))
result =
Kino.RPC.eval_string(node, ~S"Accumulator.Stats.get_all_blogs_data()", file: __ENV__.file)
[
%{
id: 19,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-12-06 19:53:23],
views: 16,
likes: 0,
slug: "blog:things-i-want-to-explore"
},
%{
id: 5,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-12-28 20:42:30],
views: 25,
likes: 1,
slug: "blog:elixir-s3-list"
},
%{
id: 4,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-05 06:43:18],
views: 106,
likes: 3,
slug: "blog:flyio-phoenix-redis-connection"
},
%{
id: 31,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-12-06 19:53:39],
views: 14,
likes: 0,
slug: "blog:distro-hop"
},
%{
id: 24,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-11-19 15:54:11],
views: 38,
likes: 3,
slug: "blog:sonicpi-pop-os-installation"
},
%{
id: 18,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-01 23:37:43],
views: 32,
likes: 0,
slug: "blog:conditional-back-nextjs"
},
%{
id: 12,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-02-06 04:04:48],
views: 21,
likes: 0,
slug: "blog:prisma-graphql-jest-test"
},
%{
id: 35,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-09-20 16:09:24],
updated_at: ~N[2024-02-21 04:35:04],
views: 39,
likes: 2,
slug: "blog:gh-action-pr"
},
%{
id: 21,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-06 19:37:05],
views: 12,
likes: 0,
slug: "blog:mongosh-docker"
},
%{
id: 20,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-20 09:15:11],
views: 189,
likes: 5,
slug: "blog:vscode-tips-tricks"
},
%{
id: 30,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-20 10:32:59],
views: 15,
likes: 0,
slug: "blog:flask-response-headers"
},
%{
id: 34,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-08-06 11:49:42],
updated_at: ~N[2024-03-15 20:54:50],
views: 190,
likes: 2,
slug: "blog:flyio-phoenix-file-serve"
},
%{
id: 13,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-23 18:40:17],
views: 149,
likes: 2,
slug: "blog:nextjs-phoenix-channels"
},
%{
id: 25,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-16 06:32:16],
views: 35,
likes: 1,
slug: "blog:postgres-docker"
},
%{
id: 3,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-05 18:15:37],
views: 11,
likes: 0,
slug: "blog:my-custom-mdx-components"
},
%{
id: 10,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-02-22 17:38:11],
views: 15,
likes: 1,
slug: "blog:hello-world"
},
%{
id: 33,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-02-22 17:39:22],
views: 98,
likes: 10,
slug: "blog:new-tech-2023"
},
%{
id: 17,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-28 19:42:19],
views: 5,
likes: 0,
slug: "blog:visitor-pattern"
},
%{
id: 6,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-12-14 15:39:49],
views: 11,
likes: 0,
slug: "blog:elixir-file-handling"
},
%{
id: 32,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-02-22 17:39:53],
views: 42,
likes: 1,
slug: "blog:tech-2022"
},
%{
id: 23,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-18 00:40:07],
views: 109,
likes: 2,
slug: "blog:markdown-next-mdx"
},
%{
id: 2,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-10 11:00:00],
views: 78,
likes: 9,
slug: "blog:notes"
},
%{
id: 8,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-02 21:55:40],
views: 13,
likes: 0,
slug: "blog:the-odin-project"
},
%{
id: 22,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-02 22:12:10],
views: 2,
likes: 0,
slug: "blog:gsoc-asyncapi-2"
},
%{
id: 29,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-02 22:15:37],
views: 15,
likes: 2,
slug: "blog:gsoc"
},
%{
id: 27,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-19 10:12:23],
views: 22,
likes: 0,
slug: "blog:graphql-typescript"
},
%{
id: 26,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-12-14 16:24:40],
views: 104,
likes: 0,
slug: "blog:livebook-secret-config"
},
%{
id: 11,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-12-12 19:26:02],
views: 12,
likes: 0,
slug: "blog:nextjs-dynamic-import"
},
%{
id: 16,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-02-13 14:53:10],
views: 46,
likes: 5,
slug: "blog:auth0-email-template-redirect"
},
%{
id: 9,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-01-16 13:06:39],
views: 4,
likes: 0,
slug: "blog:memoization-use-memo"
},
%{
id: 28,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2023-10-26 20:49:31],
views: 12,
likes: 1,
slug: "blog:gsoc-at-postman-and-asyncapi"
},
%{
id: 7,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-02-16 08:14:23],
views: 13,
likes: 0,
slug: "blog:gsoc-asyncapi-1"
},
%{
id: 14,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-12 02:15:48],
views: 276,
likes: 0,
slug: "blog:typescript-npm-publish"
},
%{
id: 15,
__struct__: Accumulator.Stats.Stat,
__meta__: %{
state: :loaded,
context: nil,
prefix: nil,
source: "stats",
__struct__: Ecto.Schema.Metadata,
schema: Accumulator.Stats.Stat
},
inserted_at: ~N[2023-07-28 14:20:37],
updated_at: ~N[2024-03-12 02:15:48],
views: 276,
likes: 0,
slug: "blog:typescript-npm-publish"
}
]
blogs_data = Enum.map(result, fn map -> %{likes: map.likes, views: map.views, slug: map.slug} end)
[
%{views: 16, likes: 0, slug: "blog:things-i-want-to-explore"},
%{views: 25, likes: 1, slug: "blog:elixir-s3-list"},
%{views: 106, likes: 3, slug: "blog:flyio-phoenix-redis-connection"},
%{views: 14, likes: 0, slug: "blog:distro-hop"},
%{views: 38, likes: 3, slug: "blog:sonicpi-pop-os-installation"},
%{views: 32, likes: 0, slug: "blog:conditional-back-nextjs"},
%{views: 21, likes: 0, slug: "blog:prisma-graphql-jest-test"},
%{views: 39, likes: 2, slug: "blog:gh-action-pr"},
%{views: 12, likes: 0, slug: "blog:mongosh-docker"},
%{views: 189, likes: 5, slug: "blog:vscode-tips-tricks"},
%{views: 15, likes: 0, slug: "blog:flask-response-headers"},
%{views: 190, likes: 2, slug: "blog:flyio-phoenix-file-serve"},
%{views: 149, likes: 2, slug: "blog:nextjs-phoenix-channels"},
%{views: 35, likes: 1, slug: "blog:postgres-docker"},
%{views: 11, likes: 0, slug: "blog:my-custom-mdx-components"},
%{views: 15, likes: 1, slug: "blog:hello-world"},
%{views: 98, likes: 10, slug: "blog:new-tech-2023"},
%{views: 5, likes: 0, slug: "blog:visitor-pattern"},
%{views: 11, likes: 0, slug: "blog:elixir-file-handling"},
%{views: 42, likes: 1, slug: "blog:tech-2022"},
%{views: 109, likes: 2, slug: "blog:markdown-next-mdx"},
%{views: 78, likes: 9, slug: "blog:notes"},
%{views: 13, likes: 0, slug: "blog:the-odin-project"},
%{views: 2, likes: 0, slug: "blog:gsoc-asyncapi-2"},
%{views: 15, likes: 2, slug: "blog:gsoc"},
%{views: 22, likes: 0, slug: "blog:graphql-typescript"},
%{views: 104, likes: 0, slug: "blog:livebook-secret-config"},
%{views: 12, likes: 0, slug: "blog:nextjs-dynamic-import"},
%{views: 46, likes: 5, slug: "blog:auth0-email-template-redirect"},
%{views: 4, likes: 0, slug: "blog:memoization-use-memo"},
%{views: 12, likes: 1, slug: "blog:gsoc-at-postman-and-asyncapi"},
%{views: 13, likes: 0, slug: "blog:gsoc-asyncapi-1"},
%{views: 276, likes: 0, slug: "blog:typescript-npm-publish"},
%{views: 276, likes: 0, slug: "blog:typescript-npm-publish"}
]
slug_to_likes = %{
slugs: Enum.map(result, &String.replace(&1.slug, "blog:", "")),
likes: Enum.map(result, & &1.likes)
}
%{
likes: [0, 1, 3, 0, 3, 0, 0, 2, 0, 5, 0, 2, 2, 1, 0, 1, 10, 0, 0, 1, 2, 9, 0, 0, 2, 0, 0, 0, 5, 0,
1, 0, 0, 0],
slugs: ["things-i-want-to-explore", "elixir-s3-list", "flyio-phoenix-redis-connection",
"distro-hop", "sonicpi-pop-os-installation", "conditional-back-nextjs",
"prisma-graphql-jest-test", "gh-action-pr", "mongosh-docker", "vscode-tips-tricks",
"flask-response-headers", "flyio-phoenix-file-serve", "nextjs-phoenix-channels",
"postgres-docker", "my-custom-mdx-components", "hello-world", "new-tech-2023", "visitor-pattern",
"elixir-file-handling", "tech-2022", "markdown-next-mdx", "notes", "the-odin-project",
"gsoc-asyncapi-2", "gsoc", "graphql-typescript", "livebook-secret-config",
"nextjs-dynamic-import", "auth0-email-template-redirect", "memoization-use-memo",
"gsoc-at-postman-and-asyncapi", "gsoc-asyncapi-1", "typescript-npm-publish",
"typescript-npm-publish"]
}
VegaLite.new(width: 600, height: 500, title: "Blog to Likes")
|> VegaLite.data_from_values(slug_to_likes, only: ["slugs", "likes"])
|> VegaLite.mark(:bar)
|> VegaLite.encode_field(:x, "slugs", type: :nominal)
|> VegaLite.encode_field(:y, "likes", type: :quantitative)
{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"likes":0,"slugs":"things-i-want-to-explore"},{"likes":1,"slugs":"elixir-s3-list"},{"likes":3,"slugs":"flyio-phoenix-redis-connection"},{"likes":0,"slugs":"distro-hop"},{"likes":3,"slugs":"sonicpi-pop-os-installation"},{"likes":0,"slugs":"conditional-back-nextjs"},{"likes":0,"slugs":"prisma-graphql-jest-test"},{"likes":2,"slugs":"gh-action-pr"},{"likes":0,"slugs":"mongosh-docker"},{"likes":5,"slugs":"vscode-tips-tricks"},{"likes":0,"slugs":"flask-response-headers"},{"likes":2,"slugs":"flyio-phoenix-file-serve"},{"likes":2,"slugs":"nextjs-phoenix-channels"},{"likes":1,"slugs":"postgres-docker"},{"likes":0,"slugs":"my-custom-mdx-components"},{"likes":1,"slugs":"hello-world"},{"likes":10,"slugs":"new-tech-2023"},{"likes":0,"slugs":"visitor-pattern"},{"likes":0,"slugs":"elixir-file-handling"},{"likes":1,"slugs":"tech-2022"},{"likes":2,"slugs":"markdown-next-mdx"},{"likes":9,"slugs":"notes"},{"likes":0,"slugs":"the-odin-project"},{"likes":0,"slugs":"gsoc-asyncapi-2"},{"likes":2,"slugs":"gsoc"},{"likes":0,"slugs":"graphql-typescript"},{"likes":0,"slugs":"livebook-secret-config"},{"likes":0,"slugs":"nextjs-dynamic-import"},{"likes":5,"slugs":"auth0-email-template-redirect"},{"likes":0,"slugs":"memoization-use-memo"},{"likes":1,"slugs":"gsoc-at-postman-and-asyncapi"},{"likes":0,"slugs":"gsoc-asyncapi-1"},{"likes":0,"slugs":"typescript-npm-publish"},{"likes":0,"slugs":"typescript-npm-publish"}]},"encoding":{"x":{"field":"slugs","type":"nominal"},"y":{"field":"likes","type":"quantitative"}},"height":500,"mark":"bar","title":"Blog to Likes","width":600}