CURD Rice ๐ :: for ricing CRUD
Sheaf ๐พ Manipulation
defmodule R do
def recompile() do
Mix.Task.reenable("app.start")
Mix.Task.reenable("compile")
Mix.Task.reenable("compile.all")
compilers = Mix.compilers()
Enum.each(compilers, &Mix.Task.reenable("compile.#{&1}"))
Mix.Task.run("compile.all")
end
end
R.recompile()
CRUD Helper โ set up a family of sheafs for discussions UI
R.recompile()
alias Vyasa.Sangh
alias Vyasa.Sangh.Sheaf
CRUD Helper Module
This has a purge function and a seed function. Intent is to use this during the UI creation phase for discussions.
R.recompile()
defmodule CRUDHelper do
alias Vyasa.Sangh
alias Vyasa.Sangh.Sheaf
alias EctoLtree
@roots [
{"Hey there, this is the start of a discussion about how to set up the discussions UI",
"freebird"},
{"Hello folks, let's talk about why fruits are absolutely the best thing on earth.",
"fruitfinder"},
{"I like cars, what about y'all?", "carboi"}
]
@second_layer [
{"What do you think about the future of discussions?", "discussion_enthusiast"},
{"Fruits are not just tasty; they're also healthy!", "health_freak"},
{"Cars have changed so much over the years, haven't they?", "car_historian"},
{"How do you feel about online discussions?", "digital_native"},
{"What's your favorite fruit and why?", "fruit_lover"},
{"What car model do you dream of owning?", "car_dreamer"},
{"What features do you think discussions should have?", "feature_fanatic"},
{"Do you prefer sweet or savory fruits?", "taste_explorer"},
{"What was your first car experience like?", "first_driver"}
]
@third_layer [
# For first root's children
[
{"I think discussions should be more interactive.", "interactivity_lover"},
{"Have you tried any new discussion platforms?", "platform_explorer"},
{"What would improve online discussions for you?", "improvement_seeker"}
],
# For second root's children
[
{"I love fruits that are both sweet and tangy!", "sweet_tangy_lover"},
{"Have you ever tried exotic fruits?", "exotic_fruit_explorer"},
{"What's your go-to fruit for a snack?", "snack_time_fruit"}
],
# For third root's children
[
{"Electric cars are the future!", "electric_car_enthusiast"},
{"What's your opinion on self-driving cars?", "self_driving_advocate"},
{"Car maintenance tips anyone?", "maintenance_master"}
]
]
@doc """
Seeds a family of sheafs. With 3 roots, 3 children per root and 3 children per 2nd level root.
"""
def seed_family(sangh_session_id) do
# Create root sheafs
created_root_sheafs =
@roots
|> Enum.map(fn {body, user_signature} ->
payload = %{
id: Ecto.UUID.generate(),
active: false,
body: body,
marks: [],
traits: ["published"],
session_id: sangh_session_id,
signature: user_signature,
# Each root has 3 children
child_count: length(@second_layer)
}
case Sangh.create_sheaf(payload) do
{:ok, sheaf} -> sheaf
{:error, reason} -> IO.puts("Error creating root sheaf: #{reason}")
end
end)
# Create second layer sheafs
created_second_layer_sheafs =
created_root_sheafs
|> Enum.flat_map(fn root ->
Enum.map(@second_layer, fn {body, user_signature} ->
payload = %{
id: Ecto.UUID.generate(),
active: false,
body: body,
marks: [],
traits: ["published"],
session_id: sangh_session_id,
signature: user_signature,
# Each second layer has 3 children
child_count: length(@third_layer)
}
case Sangh.create_child_sheaf_from_parent(root, payload) do
{:ok, child_sheaf} -> child_sheaf
{:error, reason} -> IO.puts("Error creating second layer sheaf: #{reason}")
end
end)
end)
# Create third layer sheafs
_created_third_layer_sheafs =
created_second_layer_sheafs
|> Enum.flat_map(fn child ->
Enum.flat_map(@third_layer, fn layer ->
Enum.map(layer, fn {body, user_signature} ->
payload = %{
id: Ecto.UUID.generate(),
active: false,
body: body,
marks: [],
traits: ["published"],
session_id: sangh_session_id,
signature: user_signature,
# Third layer has no children
child_count: 0
}
case Sangh.create_child_sheaf_from_parent(child, payload) do
{:ok, grandchild_sheaf} -> grandchild_sheaf
{:error, reason} -> IO.puts("Error creating third layer sheaf: #{reason}")
end
end)
end)
end)
# insert test map to one of the roots:
updated_root_sheaf_with_mark = hd(created_root_sheafs) |> Sangh.update_sheaf(%{marks: [Stubs.Vyasa.Sangh.Mark.get_dummy_mark()]})
IO.inspect(updated_root_sheaf_with_mark, label: "CHECK ME OUT")
end
@doc """
Indiscriminately purges all the sheafs in the session.
Deletes child sheafs before deleting their parents to avoid foreign key constraint errors.
Handles stale entries gracefully.
"""
def purge(sangh_session_id) do
# Retrieve all root sheafs for the session
root_sheafs = Sangh.get_root_sheafs_by_session(sangh_session_id) |> Vyasa.Repo.preload(:marks)
# _deleted_marks = root_sheafs |> Enum.map(fn s -> s |> Sangh.delete_marks_in_sheaf() end)
_second_level_deletes =
root_sheafs
|> Enum.map(fn root ->
Sangh.get_child_sheafs_by_session(sangh_session_id, Sheaf.encode_path(root.id), 2)
end)
|> Enum.map(fn sheafs -> sheafs |> Enum.map(fn s -> Sangh.delete_sheaf(s) end) end)
_first_level_deletes =
root_sheafs
|> Enum.map(fn root ->
Sangh.get_child_sheafs_by_session(sangh_session_id, Sheaf.encode_path(root.id), 1)
end)
|> Enum.map(fn sheafs -> sheafs |> Enum.map(fn s -> Sangh.delete_sheaf(s) end) end)
_root_level_deletes =
root_sheafs
|> Enum.map(fn root ->
Sangh.get_child_sheafs_by_session(sangh_session_id, Sheaf.encode_path(root.id), 0)
end)
|> Enum.map(fn sheafs -> sheafs |> Enum.map(fn s -> Sangh.delete_sheaf(s) end) end)
end
end
Creating the Family Tree
This is for some basic setup while creating the discussion view.
# set based on current session:
sangh_session_id = "164eb05d-221a-4939-b180-8394e1a5515f"
sangh_session_id = "907c4d5e-77a1-4c51-96d1-185b8f314fde"
sangh_session_id = "907c4d5e-77a1-4c51-96d1-185b8f314fde"
CRUDHelper.purge(sangh_session_id)
CRUDHelper.seed_family(sangh_session_id)
Verifying the Family Fixture
root = hd(Sangh.get_root_sheafs_by_session(sangh_session_id))
Sangh.get_child_sheafs_by_session(sangh_session_id, Sheaf.encode_path(root.id), 2)
|>Enum.count()
%{data: root_sheafs} = Sangh.get_root_sheafs_by_session(sangh_session_id, 1)
root_sheafs
Sheaf Lattice Definition
So this is how we define a flatmap (which we call :kv_verses), so our intent is to use a similar pattern for keeping state in the discussions mode
alias Vyasa.Sangh
alias Vyasa.Sangh.Sheaf
alias Vyasa.Written.{Source}
alias Vyasa.Written
default_lang = "en"
source_title = "hanuman_chalisa"
chap_no = 1
%Source{id: sid} = source = Written.get_source_by_title(source_title)
chaps = Written.list_chapters_by_source(sid, default_lang)
%{verses: verses, translations: [ts | _], title: chap_title, body: chap_body} = chap = Written.get_chapter(chap_no, sid, default_lang)
Enum.into(verses, %{}, &{&1.id, &1})
# Written.get_chapter(1, sid, @default_lang)
Defining Lattices for Discussion Context
So we will be keeping a flatmap for state. First about the sheaf states themselves.
These will be streams of their own:
A) sheafs stream: [ sheaf path label slugs ] => %Sheaf{}
B) sheaf_ui stream [ sheaf id ] => sheaf ui state
- this will have markโs info for them also ==> for now we shall also load all the marks per sheaf
now for ui state: the Marks UI state looks like this:
%VyasaWeb.Context.Components.UiState.Marks{ is_expanded_view?: false, show_sheaf_modal?: false, is_editable_marks?: false, mark_id_to_ui: %{} }
for sheaf ui do a composition: so sheaf ui state likely can just contain the marks ui state
extra info: pagination info needs to be in the ui state
Sheaf Lattice Definition
alias Vyasa.Sangh.SheafLattice
sangh_session_id = "164eb05d-221a-4939-b180-8394e1a5515f"
R.recompile()
alias Vyasa.Sangh
alias Vyasa.Sangh.Sheaf
alias Vyasa.Written.{Source}
alias Vyasa.Written
# this is the function exposed for it:
sheaf_lattice = SheafLattice.create_complete_sheaf_lattice(sangh_session_id)
# here's the manual way of doing it
# %{data: root_sheafs} = Sangh.get_root_sheafs_by_session(sangh_session_id, 1)
# levels = [0, 1, 2]
# sheaf_lattice =
# levels
# |> Enum.flat_map(fn level ->
# root_sheafs
# |> Enum.map(fn sheaf -> to_string(sheaf.path) end)
# |> Enum.flat_map(fn sheaf_id ->
# Sangh.get_child_sheafs_by_session(sangh_session_id, sheaf_id, level)
# end)
# |> Enum.map(fn s -> {s.path.labels, s} end)
# end)
# |> Enum.into(%{})
Reading a sheaf lattice
Here, Iโve put in examples of using the helper functions for reading sheafs. Test this out by setting test_input correctly to the label of a valid leaf-sheaf, this may change based on the seed function.
alias Vyasa.Sangh.SheafLattice
example_leaf_sheaf = hd(SheafLattice.read_sheaf_lattice(sheaf_lattice, 2, nil))
test_input = [a, b, c] = example_leaf_sheaf.path.labels
# OR manually set test input in this block:
# test_input = ["8955df0c", "2628a71a", "f9ce1eee"]
# fetch all sheafs in a particular level
SheafLattice.read_sheaf_lattice(sheaf_lattice)
# should return an empty array since it's beyond the max-3 depth
SheafLattice.read_sheaf_lattice(sheaf_lattice, 3, nil)
SheafLattice.read_sheaf_lattice(sheaf_lattice, 0, nil)
SheafLattice.read_sheaf_lattice(sheaf_lattice, 1, nil)
SheafLattice.read_sheaf_lattice(sheaf_lattice, 2, nil)
# ["8955df0c", "2628a71a", "f9ce1eee"],
# # # # fetch based on specific matches of a single label
SheafLattice.read_sheaf_lattice(sheaf_lattice, 0, a)
SheafLattice.read_sheaf_lattice(sheaf_lattice, 1, b)
SheafLattice.read_sheaf_lattice(sheaf_lattice, 2, c)
# # # # # fetch based on complete matches:
SheafLattice.read_sheaf_lattice(sheaf_lattice, 1, [a, b])
SheafLattice.read_sheaf_lattice(sheaf_lattice, 2, [a, b, c])
# # # # # fetch immediate children based on particular parent
# # # # # fetch immediate children of a specific level 0 node:
SheafLattice.read_sheaf_lattice(sheaf_lattice, 1, [a, nil])
# # # # # fetch immediate children of a specific level 1 node:
SheafLattice.read_sheaf_lattice(sheaf_lattice, 2, [a, b, nil])
SheafLattice.read_sheaf_lattice(sheaf_lattice, 1, ["1bebc4d1", nil])
|> Enum.map(fn s -> s.path.labels end)
# |> Enum.count()
Sheaf UI States
Intent here is to keep ui state auxilliary to the actual data state. The shape of the data and ui lattices shall be exactly the same, so that the same read_sheaf_lattice() can be used on both ui and data state lattices.
R.recompile()
# getting info about the sheaf's marks and stuff:
[ test_root_sheaf | _]= SheafLattice.read_sheaf_lattice(sheaf_lattice, 0, nil)
|> Enum.map(fn s -> s |> Vyasa.Repo.preload(:marks) end)
|> Enum.map(fn
%{marks: [_|_] = _m} = s -> s
%{marks: [] = _m} -> nil
end)
|> Enum.reject(fn s -> is_nil(s) end)
alias VyasaWeb.Context.Components.UiState.Sheaf, as: SheafUiState
SheafUiState.get_initial_ui_state(test_root_sheaf)
# sheaf_lattice
# |> Enum.map(fn {k, } end)
sheaf_ui_lattice =
sheaf_lattice
|> Enum.map(fn {k,
%Sheaf{
marks: _marks
} = sheaf} ->
sheaf_ui = sheaf |> SheafUiState.get_initial_ui_state()
{k, sheaf_ui}
end)
|> Enum.into(%{})
sheaf_lattice |> SheafLattice.read_sheaf_lattice(2, "08e53d81")
sheaf_ui_lattice |> SheafLattice.read_sheaf_lattice(2, "08e53d81")
test_root_sheaf |> SheafUiState.get_initial_ui_state()
# SheafUi
Creation Related Test Section
{:ok, %{id: s_id}} = Vyasa.Sangh.create_session()
{:ok, sangh} = Vyasa.Sangh.create_sheaf(%{id: Ecto.UUID.generate(), session_id: s_id})
{:ok, child_sangh} = Vyasa.Sangh.create_sheaf(%{id: Ecto.UUID.generate(), parent: sangh, session_id: s_id})
{:ok, grandchild_sangh} = Vyasa.Sangh.create_sheaf(%{id: Ecto.UUID.generate(), parent: child_sangh, session_id: s_id})
# sheaf_lattice = Vyasa.Sangh.get_child_sheafs_by_session("a33b4f92-90de-45a0-88b0-c19518491182", "085994cc", 2)
# |> Enum.map(fn sheaf ->
# {sheaf.path.labels, sheaf}
# end)
# |> Enum.into(%{})
# # length(sangh.path.labels)
sheaf_lattice |> Enum.map(fn {["085994cc" | [ "01a1bb12" | _]] = k , sheaf} -> sheaf
_ -> nil
end)
# |> Enum.into(%{})