Packages
livebook
0.17.1
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/notebook/learn.ex
defmodule Livebook.Notebook.Learn do
defmodule NotFoundError do
defexception [:slug, plug_status: 404]
def message(%{slug: slug}) do
"could not find an example notebook matching #{inspect(slug)}"
end
end
@type notebook_info :: %{
ref: atom() | nil,
slug: String.t(),
livemd: String.t(),
title: String.t(),
files: files(),
details: details() | nil
}
@type files :: %{String.t() => binary()}
@type details :: %{
description: String.t(),
cover: image_source()
}
@type group_info :: %{
title: String.t(),
description: String.t(),
cover: image_source(),
notebook_infos: list(notebook_info())
}
@type image_source :: {:static, filename :: String.t()} | {:url, String.t()}
files_dir = Path.expand("learn/files", __DIR__)
welcome_config = %{
path: Path.join(__DIR__, "learn/intro_to_livebook.livemd"),
details: %{
description: "Get to know Livebook, see how it works, and learn its features.",
cover_filename: "logo.png"
}
}
other_configs = [
%{
path: Path.join(__DIR__, "learn/distributed_portals_with_elixir.livemd"),
file_paths: [
Path.join(files_dir, "portal-drop.jpeg"),
Path.join(files_dir, "portal-list.jpeg")
],
details: %{
description:
"A fast-paced introduction to Elixir by building distributed data-transfer portals.",
cover_filename: "elixir.png"
}
},
%{
path: Path.join(__DIR__, "learn/chat_app.livemd"),
details: %{
description: "Write and deploy a chat app with Kino control and frames.",
cover_filename: "learn-deploy.svg"
}
},
%{
path: Path.join(__DIR__, "learn/intro_to_explorer.livemd"),
details: %{
description: "Intuitive data visualizations and data pipelines on the fly.",
cover_filename: "explorer.png"
}
},
%{
path: Path.join(__DIR__, "learn/intro_to_vega_lite.livemd"),
details: %{
description: "Learn how to quickly create numerous plots for your data.",
cover_filename: "vega_lite.png"
}
},
%{
path: Path.join(__DIR__, "learn/intro_to_maplibre.livemd"),
details: %{
description: "Seamlessly plot maps using geospatial and tabular data.",
cover_filename: "maplibre.png"
}
},
%{
path: Path.join(__DIR__, "learn/github_stars.livemd"),
details: %{
description: "Build a Livebook app to visualize GitHub repository star growth over time.",
cover_filename: "github-stars.png"
}
},
%{
ref: :kino_intro,
path: Path.join(__DIR__, "learn/kino/intro_to_kino.livemd")
},
%{
ref: :kino_vm_introspection,
path: Path.join(__DIR__, "learn/kino/vm_introspection.livemd")
},
%{
ref: :kino_custom_kinos,
path: Path.join(__DIR__, "learn/kino/custom_kinos.livemd")
},
%{
ref: :kino_smart_cells,
path: Path.join(__DIR__, "learn/kino/smart_cells.livemd")
}
]
user_configs = Application.compile_env(:livebook, :learn_notebooks, [])
notebook_configs = [welcome_config] ++ user_configs ++ other_configs
notebook_infos =
for config <- notebook_configs do
path =
config[:path] ||
raise "missing required :path attribute in notebook configuration: #{inspect(config)}"
@external_resource path
markdown = File.read!(path)
# Parse the file to ensure no warnings and read the title.
# However, in the info we keep just the file contents to save on memory.
{notebook, %{warnings: warnings}} = Livebook.LiveMarkdown.notebook_from_livemd(markdown)
if warnings != [] do
items = Enum.map(warnings, &("- " <> &1))
raise "found warnings while importing #{path}:\n\n" <> Enum.join(items, "\n")
end
files =
config
|> Map.get(:file_paths, [])
|> Map.new(fn image_path ->
image_name = Path.basename(image_path)
content = File.read!(image_path)
{image_name, content}
end)
slug =
config[:slug] || path |> Path.basename() |> Path.rootname() |> String.replace("_", "-")
%{
ref: config[:ref],
slug: slug,
livemd: markdown,
title: notebook.name,
files: files,
details:
if config_details = config[:details] do
description =
config_details[:description] ||
raise "missing required :description attribute in notebook details: #{inspect(config_details)}"
cover =
case config_details do
%{cover_filename: filename} ->
{:static, filename}
%{cover_path: path} ->
{:url, Livebook.Utils.read_as_data_url!(path)}
_ ->
raise "expected either :cover_path or :cover_filename in notebooks details: #{inspect(config_details)}"
end
%{description: description, cover: cover}
end
}
end
@doc """
Returns a list of example notebooks with metadata.
"""
@spec notebook_infos() :: list(notebook_info())
def notebook_infos(), do: unquote(Macro.escape(notebook_infos))
@doc """
Same as `notebook_infos/0`, but returns only notebooks that have
additional details.
"""
@spec visible_notebook_infos() :: list(notebook_info())
def visible_notebook_infos() do
notebook_infos() |> Enum.filter(& &1.details)
end
@doc """
Finds learn notebook by slug and returns the parsed data structure.
Returns the notebook along with the files it uses as preloaded binaries.
"""
@spec notebook_by_slug!(String.t()) :: {Livebook.Notebook.t(), files()}
def notebook_by_slug!(slug) do
notebook_infos()
|> Enum.find(&(&1.slug == slug))
|> case do
nil ->
raise NotFoundError, slug: slug
notebook_info ->
{notebook, %{warnings: []}} =
Livebook.LiveMarkdown.notebook_from_livemd(notebook_info.livemd)
{notebook, notebook_info.files}
end
end
@group_configs [
%{
title: "Deep dive into Kino",
description:
"Learn more about the Kino package, including the creation of custom UI components.",
cover_filename: "kino.png",
notebook_refs: [
:kino_intro,
:kino_vm_introspection,
:kino_custom_kinos,
:kino_smart_cells
]
}
]
@doc """
Returns a list of all defined notebook groups.
"""
@spec group_infos() :: list(group_info())
def group_infos() do
notebook_infos = notebook_infos()
for config <- @group_configs do
%{
title: config.title,
description: config.description,
cover: {:static, config.cover_filename},
notebook_infos:
for(
ref <- config.notebook_refs,
info = Enum.find(notebook_infos, &(&1[:ref] == ref)),
do: info
)
}
end
end
end