Packages
kino
0.2.3
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.3
0.15.2
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.2
0.6.1
0.6.0
retired
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Interactive widgets for Livebook
Current section
Files
Jump to
Current section
Files
lib/kino/utils/table.ex
defmodule Kino.Utils.Table do
@moduledoc false
# Common functions for handling various Elixir
# terms as table records.
@doc """
Computes table columns that accomodate for all the given records.
"""
def columns_for_records(records) do
case Enum.at(records, 0) do
nil ->
[]
first_record ->
first_record_columns = columns_for_record(first_record)
all_columns =
records
|> Enum.reduce(MapSet.new(), fn record, columns ->
record
|> columns_for_record()
|> MapSet.new()
|> MapSet.union(columns)
end)
|> MapSet.to_list()
|> Enum.sort_by(& &1.key)
# If all records have the same structure, keep the order,
# otherwise return the sorted accumulated columns
if length(first_record_columns) == length(all_columns) do
first_record_columns
else
all_columns
end
end
end
defp columns_for_record(record) when is_tuple(record) do
record
|> Tuple.to_list()
|> Enum.with_index()
|> Enum.map(&elem(&1, 1))
|> keys_to_columns()
end
defp columns_for_record(record) when is_map(record) do
if schema = ecto_schema(record) do
schema.__schema__(:fields)
else
record |> Map.keys() |> Enum.sort()
end
|> keys_to_columns()
end
defp columns_for_record(record) when is_list(record) do
record
|> Keyword.keys()
|> keys_to_columns()
end
defp columns_for_record(_record) do
# If the record is neither of the expected enumerables,
# we treat it as a single column value
keys_to_columns([:item])
end
@doc """
Converts keys to column specifications.
"""
def keys_to_columns(keys) do
Enum.map(keys, fn key -> %{key: key, label: inspect(key)} end)
end
@doc """
Looks up record field value by key.
"""
def get_field(record, key)
def get_field(record, key) when is_tuple(record) do
if key < tuple_size(record) do
elem(record, key)
else
nil
end
end
def get_field(record, key) when is_list(record) do
record[key]
end
def get_field(record, key) when is_map(record) do
Map.get(record, key)
end
def get_field(record, :item) do
record
end
@doc """
Converts a record to row specification given a list
of desired keys.
"""
def record_to_row(record, keys) do
fields =
Map.new(keys, fn key ->
value = get_field(record, key)
{key, inspect(value)}
end)
# Note: id is opaque to the client, and we don't need it for now
%{id: nil, fields: fields}
end
@doc """
Extracts schema module from the given struct or queryable.
If no schema found, `nil` is returned.
"""
def ecto_schema(queryable)
def ecto_schema(%{from: %{source: {_source, schema}}}) do
schema
end
def ecto_schema(queryable) when is_atom(queryable) do
if Code.ensure_loaded?(queryable) and function_exported?(queryable, :__schema__, 1) do
queryable
else
nil
end
end
def ecto_schema(struct) when is_struct(struct) do
ecto_schema(struct.__struct__)
end
def ecto_schema(_queryable), do: nil
end