Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Holidex Livebook

holidex.livemd

Holidex Livebook

Mix.install([
  {:kino, "~> 0.12.0"},
  {:kino_vega_lite, "~> 0.1.10"},
  {:kino_explorer, "~> 0.1.11"}
])

Connection Setup

1. Run your local Holidex 🧑🏻‍💻

We need to run our holidex as a node locally:

iex --sname holidex --cookie secret -S mix phx.server

And then keep your node name (it could be something like holidex@JoseValim)

2. Smart Cell for Remote Execution 🔌

Remote Execution For connect your LiveBook to an existing Node, you’ll need a Smart Cell:

  1. Click the Smart button.
  2. Click the Remote Execution option and then you’ll have this section available on your noteook.
  3. In the first input put your Node name: holidex@JoseValim.
  4. Click the cookie input to open a modal where you need to put the name COOKIE and the secret value secret (this values comes from your phoenix node).
  5. Inside the code box you can execute code direct in your Phoenix app. But if you try to call any variable outside you’ll raise an error.
  6. To assign any data from the node, you can use the last input to set a variable to save the data and use it in other Elixir cells in your notebook.

Extract data from the Node 📍

You can execute any module from Holidex inside the Remote Execution, but to be able to export and use it in this Livebook, you’ll need to extract the data you need and assign to the variable node_info.

require Kino.RPC
node = :"holidex@MacBook-Pro-de-Carlo-Gilmar"
Node.set_cookie(node, String.to_atom(System.fetch_env!("LB_COOKIE")))

node_info =
  Kino.RPC.eval_string(
    node,
    ~S"""
    # Explore the data direct from ETS 
    data_server = :ets.tab2list(:data_server_registered_names)
    schemas_availables = Enum.map(data_server, fn {schema, _lits} -> schema end)

    db = 
    data_server 
    |> Enum.map(fn {schema, _lits} -> {schema, Holidex.DataServer.Common.list_data(schema)} end) 
    |> Map.new() 

    # Data Server Processes
    # 1. Find supervisor pid
    supervisor = :"Elixir.Holidex.DataServer.Supervisor"
    data_server_pid = Process.whereis(supervisor)
    # 2. Get child pids
    {:links, childs} = Process.info data_server_pid, :links
    # 3. Get pid information
    data_server_childs = for pid <- childs, do: Process.info(pid)



    # Supervision three of DataServer
    childs = Supervisor.which_children(data_server_pid)

    records = 
      childs
      |> Enum.map(
        fn {process_name, pid, type, _processes_list} ->
          if type == :supervisor do
            childs = Supervisor.which_children(pid)
            Enum.with_index(childs, fn {_, child_pid,_,[child_name]}, index -> 
              from = Atom.to_string(process_name)
              child_name = Atom.to_string(child_name)
              %{from: from, to: "#{child_name}-#{index}"}
            end)
            
          else
            from = Atom.to_string(supervisor)
            to = Atom.to_string(process_name)
            %{from: from, to: to}
          end
        end)
      |> List.flatten() 


    # ==============================================================
    # Map to export data from this node and use it in this livebook
    # node_info
    # ==============================================================
      %{
        db: db, 
        data_server_childs: data_server_childs,
        records: records
      }

    """,
    file: __ENV__.file
  )
# Map all users to present in a table view
# This cell execute elixir code outside the remote node

users =
  Enum.map(
    node_info.db["users-0.1.2"],
    fn user ->
      %{
        id: user.id,
        name: user.name,
        status: Atom.to_string(user.status),
        email: user.email,
        role: user.role_id
      }
    end
  )

Data Server Supervision Three

The DataServer supervisor manages the links to the processes that we’re using to store data. This section is to visualize this structure.

Every register in the visualization correspond to a record stored.

## Module to create a custom diagram
defmodule HolidexBook.Mermaid do
  def create_diagram(relations) do
    graph =
      relations
      |> Enum.map(fn %{from: from, to: to} ->
        "#{from}-->#{to};"
      end)
      |> Enum.join("\n")

    """
    graph LR;
    #{graph}
    """
  end
end

supervisor = "DataServer.Supervisor"

# We need to build the mermaid diagram from scratch  
supervisor_childs =
  Enum.map(
    node_info.data_server_childs,
    fn child ->
      child_name = Atom.to_string(child[:registered_name])
      %{from: supervisor, to: child_name}
    end
  )

mermaid_diagram = HolidexBook.Mermaid.create_diagram(supervisor_childs ++ node_info.records)

Kino.Mermaid.new(mermaid_diagram)
require Kino.RPC
node = :"holidex@MacBook-Pro-de-Carlo-Gilmar"
Node.set_cookie(node, String.to_atom(System.fetch_env!("LB_COOKIE")))

Kino.RPC.eval_string(
  node,
  ~S"""
  user = %{id: "21f139c4-8626-4b08-a6d5-fe25800125f6"}

  {:ok, resume} = Holidex.User.Resume.fetch_user_resume(user)

  Holidex.DataServer.Resume.delete(resume)
  """,
  file: __ENV__.file
)