From 39a758cc12fe414c9b2b862394da718097087595 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 5 Sep 2023 05:18:40 +0100 Subject: [PATCH] remove list_items schema while maintaining all functionality #413 --- lib/app/item.ex | 6 +- lib/app/list.ex | 70 +++++++- lib/app/list_items.ex | 125 ------------- lib/app_web/live/app_live.ex | 4 +- .../20230416001029_create_lists.exs | 1 + .../20230416001045_create_list_items.exs | 17 -- priv/repo/seeds.exs | 2 +- test/app/item_test.exs | 2 +- test/app/list_items_test.exs | 170 ------------------ test/app/list_test.exs | 24 ++- test/app/stats_test.exs | 4 +- test/app_web/live/app_live_test.exs | 21 ++- 12 files changed, 112 insertions(+), 334 deletions(-) delete mode 100644 lib/app/list_items.ex delete mode 100644 priv/repo/migrations/20230416001045_create_list_items.exs delete mode 100644 test/app/list_items_test.exs diff --git a/lib/app/item.ex b/lib/app/item.ex index ad0d00e9..f99b3017 100644 --- a/lib/app/item.ex +++ b/lib/app/item.ex @@ -57,7 +57,7 @@ defmodule App.Item do %Item{} |> changeset(attrs) |> PaperTrail.insert(originator: %{id: Map.get(attrs, :person_id, 0)}) - |> App.ListItems.add_papertrail_item_to_all_list() + |> App.List.add_papertrail_item_to_all_list() end @doc """ @@ -75,7 +75,7 @@ defmodule App.Item do %Item{} |> changeset_with_tags(attrs) |> PaperTrail.insert(originator: %{id: Map.get(attrs, :person_id, 0)}) - |> App.ListItems.add_papertrail_item_to_all_list() + |> App.List.add_papertrail_item_to_all_list() end @doc """ @@ -227,7 +227,7 @@ defmodule App.Item do def items_with_timers(person_id \\ 0) do all_list = App.List.get_all_list_for_person(person_id) # dbg(all_list) - seq = App.ListItems.get_list_items(all_list.cid) + seq = App.List.get_list_seq(all_list) # dbg(seq) sql = """ diff --git a/lib/app/list.ex b/lib/app/list.ex index 9e40af4f..454c0238 100644 --- a/lib/app/list.ex +++ b/lib/app/list.ex @@ -1,4 +1,5 @@ defmodule App.List do + require Logger use Ecto.Schema import Ecto.{Changeset, Query} alias App.{Repo} @@ -9,6 +10,7 @@ defmodule App.List do field :cid, :string field :name, :string field :person_id, :integer + field :seq, :string field :sort, :integer field :status, :integer @@ -18,7 +20,7 @@ defmodule App.List do @doc false def changeset(list, attrs) do list - |> cast(attrs, [:name, :person_id, :sort, :status]) + |> cast(attrs, [:name, :person_id, :seq, :sort, :status]) |> validate_required([:name, :person_id]) |> App.Cid.put_cid() end @@ -144,7 +146,73 @@ defmodule App.List do end end + def add_item_to_list(item_cid, list_cid, person_id) do + list = get_list_by_cid!(list_cid) + # dbg(list) + prev_seq = get_list_seq(list) + seq = [item_cid | prev_seq] |> Enum.join(",") + # dbg(seq) + update_list(list, %{seq: seq, person_id: person_id}) + end + + def update_list_seq(list_cid, person_id, seq) do + list = get_list_by_cid!(list_cid) + update_list(list, %{seq: seq, person_id: person_id}) + end + + # feel free to refactor this to use pattern matching: + def add_papertrail_item_to_all_list(tuple) do + # extract the item from the tuple: + try do + {:ok, %{model: item}} = tuple + all_list = App.List.get_all_list_for_person(item.person_id) + add_item_to_list(item.cid, all_list.cid, item.person_id) + rescue + e -> + Logger.error(Exception.format(:error, e, __STACKTRACE__)) + end + + # return the original tuple as expected downstream: + tuple + end + + def get_list_seq(list) do + if is_nil(list.seq) do + [] + else + list.seq |> String.split(",") + end + end + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Below this point is Lists transition code that will be DELETED! # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + @doc """ + `add_all_items_to_all_list_for_person_id/1` does *exactly* what its' name suggests. + Adds *all* the person's `items` to the `list_items.seq`. + """ + def add_all_items_to_all_list_for_person_id(person_id) do + all_list = App.List.get_all_list_for_person(person_id) + # dbg(all_list) + all_items = App.Item.all_items_for_person(person_id) + # dbg(all_items) + prev_seq = get_list_seq(all_list) + # dbg(prev_seq) + # Add add each `item.id` to the sequence of item ids: + seq = + Enum.reduce(all_items, prev_seq, fn i, acc -> + # Avoid adding duplicates + if Enum.member?(acc, i.cid) do + acc + else + [i.cid | acc] + end + end) + |> Enum.uniq() + |> Enum.filter(fn cid -> cid != nil && cid != "" end) + |> Enum.join(",") + + update_list(all_list, %{seq: seq}) + end end diff --git a/lib/app/list_items.ex b/lib/app/list_items.ex deleted file mode 100644 index 2eb5c2a9..00000000 --- a/lib/app/list_items.ex +++ /dev/null @@ -1,125 +0,0 @@ -defmodule App.ListItems do - require Logger - use Ecto.Schema - import Ecto.Changeset - alias App.{Repo} - alias __MODULE__ - - schema "list_items" do - field :list_cid, :string - field :list_id, :id - field :person_id, :integer - field :seq, :string - - timestamps() - end - - @doc false - def changeset(list_items, attrs) do - list_items - |> cast(attrs, [:list_cid, :list_id, :person_id, :seq]) - |> validate_required([:list_cid, :person_id, :seq]) - end - - @doc """ - `create_list_items_seq/3` inserts a new `list_items` record - where the the `list_items.seq` is the latest sequence of `item ids`. - This is used when reordering using drag and drop in the frontend interface. - and `seq` is the String of ids currently visible in the interface. - e.g: "1,2,3,42,71,93". so we can easily determine the precise position of an `item_id`. - """ - def create_list_items_seq(list_cid, person_id, seq) do - # IO.inspect("create_list_item_seq(list_id: #{list_cid}, person_id: #{person_id}, seq: #{seq})") - %ListItems{} - |> changeset(%{ - list_cid: list_cid, - person_id: person_id, - seq: seq - }) - |> Repo.insert() - - # |> dbg() - end - - def list_items_seq_sql do - """ - SELECT li.seq - FROM list_items li - WHERE li.list_cid = $1 - ORDER BY li.id DESC - LIMIT 1 - """ - end - - @doc """ - `get_list_items/2` retrieves the *latest* `list_items` record for a given `list_cid`. - """ - def get_list_items(list_cid) do - # IO.puts(" = = = = = = = = = = = = = = = > get_list_items(#{list_cid})") - result = Ecto.Adapters.SQL.query!(Repo, list_items_seq_sql(), [list_cid]) - # dbg(result.rows) - if is_nil(result.rows) or result.rows == [] do - [] - else - # dbg(result.rows) - result.rows |> List.first() |> List.first() |> String.split(",") - end - end - - @doc """ - `add_list_item/3` adds an `item` to a `list` for the given `person_id`. - """ - def add_list_item(item_cid, list_cid, person_id) do - # Get latest list_items.seq for this list.id and person_id combo. - prev_seq = get_list_items(list_cid) - # dbg(prev_seq) - # Add the `item.id` to the sequence - seq = [item_cid | prev_seq] |> Enum.join(",") - # dbg(seq) - create_list_items_seq(list_cid, person_id, seq) - end - - # feel free to refactor this to use pattern matching: - def add_papertrail_item_to_all_list(tuple) do - # extract the item from the tuple: - try do - {:ok, %{model: item}} = tuple - all_list = App.List.get_all_list_for_person(item.person_id) - App.ListItems.add_list_item(item.cid, all_list.cid, item.person_id) - rescue - e -> - Logger.error(Exception.format(:error, e, __STACKTRACE__)) - end - - # return the original tuple as expected downstream: - tuple - end - - @doc """ - `add_all_items_to_all_list_for_person_id/1` does *exactly* what its' name suggests. - Adds *all* the person's `items` to the `list_items.seq`. - """ - def add_all_items_to_all_list_for_person_id(person_id) do - all_list = App.List.get_all_list_for_person(person_id) - # dbg(all_list) - all_items = App.Item.all_items_for_person(person_id) - # dbg(all_items) - # The previous sequence of items if there is any: - prev_seq = get_list_items(all_list.cid) - # Add add each `item.id` to the sequence of item ids: - seq = - Enum.reduce(all_items, prev_seq, fn i, acc -> - # Avoid adding duplicates - if Enum.member?(acc, i.cid) do - acc - else - [i.cid | acc] - end - end) - |> Enum.uniq() - |> Enum.filter(fn cid -> cid != nil && cid != "" end) - |> Enum.join(",") - - create_list_items_seq(all_list.cid, person_id, seq) - end -end diff --git a/lib/app_web/live/app_live.ex b/lib/app_web/live/app_live.ex index 321a5a89..f1c02340 100644 --- a/lib/app_web/live/app_live.ex +++ b/lib/app_web/live/app_live.ex @@ -27,7 +27,7 @@ defmodule AppWeb.AppLive do all_list = App.List.get_all_list_for_person(person_id) # dbg(all_list) # Temporary function to add All *existing* items to the "All" list: - App.ListItems.add_all_items_to_all_list_for_person_id(person_id) + App.List.add_all_items_to_all_list_for_person_id(person_id) items = Item.items_with_timers(person_id) tags = Tag.list_person_tags(person_id) @@ -316,7 +316,7 @@ defmodule AppWeb.AppLive do # "updateIndexes -> seq: #{seq} | list_cid: #{list_cid} | person_id: #{person_id}" # ) - App.ListItems.create_list_items_seq(list_cid, person_id, seq) + App.List.update_list_seq(list_cid, person_id, seq) {:noreply, socket} end diff --git a/priv/repo/migrations/20230416001029_create_lists.exs b/priv/repo/migrations/20230416001029_create_lists.exs index d913eea2..7717e773 100644 --- a/priv/repo/migrations/20230416001029_create_lists.exs +++ b/priv/repo/migrations/20230416001029_create_lists.exs @@ -6,6 +6,7 @@ defmodule App.Repo.Migrations.CreateLists do add :cid, :string add :name, :string add :person_id, :integer + add :seq, :text add :sort, :integer add :status, :integer diff --git a/priv/repo/migrations/20230416001045_create_list_items.exs b/priv/repo/migrations/20230416001045_create_list_items.exs deleted file mode 100644 index 2da0c21b..00000000 --- a/priv/repo/migrations/20230416001045_create_list_items.exs +++ /dev/null @@ -1,17 +0,0 @@ -defmodule App.Repo.Migrations.CreateListItems do - use Ecto.Migration - - def change do - create table(:list_items) do - add :list_cid, :string - add :list_id, references(:lists, on_delete: :nothing) - add :person_id, :integer - add :seq, :text - - timestamps() - end - - create index(:list_items, [:list_id]) - end - -end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 8af077b1..6600bcb3 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -81,5 +81,5 @@ if env == :test || env == :dev do App.Item.update_all_items_cid() # Add items to lists: - App.ListItems.add_all_items_to_all_list_for_person_id(person_id) + # App.ListItems.add_all_items_to_all_list_for_person_id(person_id) end diff --git a/test/app/item_test.exs b/test/app/item_test.exs index 3eddc68e..5c1cfdaa 100644 --- a/test/app/item_test.exs +++ b/test/app/item_test.exs @@ -196,7 +196,7 @@ defmodule App.ItemTest do assert NaiveDateTime.diff(timer1.start, started) == 0 # Item must be on a list ... - App.ListItems.add_all_items_to_all_list_for_person_id(item1.person_id) + App.List.add_all_items_to_all_list_for_person_id(item1.person_id) # list items with timers: item_timers = Item.items_with_timers(1) assert length(item_timers) > 0 diff --git a/test/app/list_items_test.exs b/test/app/list_items_test.exs deleted file mode 100644 index ac0d7f9f..00000000 --- a/test/app/list_items_test.exs +++ /dev/null @@ -1,170 +0,0 @@ -defmodule App.ListItemsTest do - use App.DataCase, async: true - alias App.{Item, List, ListItems} - - describe "add items to list" do - @person_id 1 - @valid_item_attrs %{text: "do 20 pushups", person_id: @person_id, status: 2} - @valid_list_attrs %{name: "Health", person_id: @person_id, sort: 1, status: 2} - - test "add_list_item/3 adds a list_item & get_list_items/1 retrieves the list of items (seq)" do - all_list = App.List.get_all_list_for_person(@person_id) - # dbg(all_list) - # No list No list_items: - assert App.ListItems.get_list_items(all_list.cid) == [] - - # Create an item - assert {:ok, %{model: item}} = Item.create_item(@valid_item_attrs) - # Create list - assert {:ok, %{model: list}} = List.create_list(@valid_list_attrs) - - # add the item to the lists_items: - ListItems.add_list_item(item.cid, list.cid, @person_id) - - # Confirm the item.id is in the list_items.seq: - seq = ListItems.get_list_items(list.cid) - # dbg(seq) - assert Enum.member?(seq, "#{item.cid}") - end - - test "add_all_items_to_all_list_for_person_id/1 adds all items to all list for person_id" do - person_id = 42 - # create an item for the person but do NOT add it to any list: - assert {:ok, %{model: item1}} = - Item.create_item(%{text: "buy land!", person_id: person_id, status: 2}) - assert {:ok, %{model: item2}} = - Item.create_item(%{text: "plant trees & food", person_id: person_id, status: 2}) - assert {:ok, %{model: item3}} = - Item.create_item(%{text: "live best life", person_id: person_id, status: 2}) - - all_list = App.List.get_all_list_for_person(person_id) - # Invoke the biz logic: - App.ListItems.add_all_items_to_all_list_for_person_id(person_id) - - # Confirm that the item.id is in the squence of item ids for the "all" list: - all_items_seq = ListItems.get_list_items(all_list.cid) - # dbg(all_items_seq) - assert Enum.member?(all_items_seq, "#{item1.cid}") - assert Enum.member?(all_items_seq, "#{item2.cid}") - assert Enum.member?(all_items_seq, "#{item3.cid}") - end - - - # test "add_list_item/3 adds an item to a list" do - # # Create an item - # assert {:ok, %{model: item, version: _version}} = - # Item.create_item(@valid_item_attrs) - - # assert item.text == @valid_item_attrs.text - - # # Create list - # assert {:ok, %{model: list, version: _version}} = - # List.create_list(@valid_list_attrs) - - # assert list.text == @valid_list_attrs.text - - # # Add the item to the list: - # assert {:ok, list_item} = ListItem.add_list_item(item, list, 1, 42.0) - # # dbg(list_item) - # assert list_item.item_id == item.id - # assert list_item.list_id == list.id - # assert list_item.person_id == item.person_id - - # # Create SECOND list to confirm that an item - # # can be added to multiple lists - # list2_data = %{text: "Second List", person_id: 1, status: 2} - - # assert {:ok, %{model: list2, version: _version}} = - # List.create_list(list2_data) - - # assert list2.text == list2_data.text - - # # Add the item to the list: - # assert {:ok, list_item2} = ListItem.add_list_item(item, list2, 1, 42.0) - # assert list_item2.list_id !== list_item.list_id - # end - - # test "remove_list_item/2 removes an item from a list" do - # # Create an item - # assert {:ok, %{model: item, version: _version}} = - # Item.create_item(@valid_item_attrs) - - # # Create list - # assert {:ok, %{model: list, version: _version}} = - # List.create_list(@valid_list_attrs) - - # assert list.text == @valid_list_attrs.text - - # # Add the item to the list: - # assert {:ok, list_item} = ListItem.add_list_item(item, list, 1, 42.0) - # # dbg(list_item) - # assert list_item.item_id == item.id - # assert list_item.list_id == list.id - - # # "REMOVE" the item from the list: - # # See: github.com/dwyl/mvp/issues/357 - # assert {:ok, list_item_removed} = ListItem.remove_list_item(item, list, 1) - # assert list_item_removed.position == 999_999.999 - # end - - # @valid_attrs %{text: "Buy Bananas", person_id: 0, status: 2} - # test "get_list_item_position/2 retrieves the position of an item in a list" do - # {:ok, %{model: item, version: _version}} = - # Item.create_item(@valid_attrs) - - # {:ok, li1} = App.ListItem.add_item_to_all_list(item) - # assert li1.position == 1.0 - - # # Note: this conversion from Int to Binary is to simulate the - # # binary that the LiveView sends to this function ... - # item_id = Integer.to_string(item.id) - # pos = App.ListItem.get_list_item_position(item_id, li1.list_id) - - # assert li1.position == pos - # end - - # test "App.ListItem.next_position_on_list/1 retrieve next position" do - # person_id = 7 - # App.List.create_list(%{text: "all", person_id: person_id}) - # all_list = App.List.get_list_by_name!(person_id, "all") - # pos_before = App.ListItem.next_position_on_list(all_list.id) - # assert pos_before == 1.0 - - # item_ids = App.ListItem.get_items_on_all_list(person_id) - # assert length(item_ids) == 0 - - # # Add an new item for the person: - # {:ok, %{model: item}} = - # %{text: "hai", person_id: person_id, status: 2} - # |> Item.create_item() - - # App.ListItem.add_item_to_all_list(item) - - # updated_item_ids = App.ListItem.get_items_on_all_list(person_id) - - # assert length(updated_item_ids) == - # length(App.Item.all_items_for_person(person_id)) - - # pos_after = App.ListItem.next_position_on_list(all_list.id) - # assert pos_before + length(updated_item_ids) == pos_after - # end - - # test "move_item/3 repositions an item in the list" do - # {:ok, %{model: item1, version: _version}} = Item.create_item(@valid_attrs) - # {:ok, %{model: item2, version: _version}} = Item.create_item(@valid_attrs) - # # Items must be on a list ... - # {:ok, li1} = App.ListItem.add_item_to_all_list(item1) - # {:ok, li2} = App.ListItem.add_item_to_all_list(item2) - - # assert li1.position < li2.position - - # # Get the "all" list for this person: - # list = App.List.get_list_by_name!(@valid_attrs.person_id, "all") - # item_list_str = "#{item2.id} #{item1.id}" - # dbg(item_list_str) - # # Move item2 to be above item1: - # {:ok, li3} = App.ListItem.move_item(item2.id, item_list_str, list.id) - # assert li3.position == 0.999999 - # end - end -end diff --git a/test/app/list_test.exs b/test/app/list_test.exs index aacb554c..29cdb21a 100644 --- a/test/app/list_test.exs +++ b/test/app/list_test.exs @@ -1,6 +1,6 @@ defmodule App.ListTest do use App.DataCase, async: true - alias App.{List} + alias App.{Item, List} describe "list" do @person_id 7 @@ -73,6 +73,28 @@ defmodule App.ListTest do assert all_list.name == "all" end + test "add_all_items_to_all_list_for_person_id/1 adds all items to all list for person_id" do + person_id = 42 + # create an item for the person but do NOT add it to any list: + assert {:ok, %{model: item1}} = + Item.create_item(%{text: "buy land!", person_id: person_id, status: 2}) + assert {:ok, %{model: item2}} = + Item.create_item(%{text: "plant trees & food", person_id: person_id, status: 2}) + assert {:ok, %{model: item3}} = + Item.create_item(%{text: "live best life", person_id: person_id, status: 2}) + + # Invoke the biz logic: + App.List.add_all_items_to_all_list_for_person_id(person_id) + + # Confirm that the item.id is in the squence of item ids for the "all" list: + all_list = App.List.get_all_list_for_person(person_id) + all_items_seq = all_list.seq |> String.split(",") + + assert Enum.member?(all_items_seq, "#{item1.cid}") + assert Enum.member?(all_items_seq, "#{item2.cid}") + assert Enum.member?(all_items_seq, "#{item3.cid}") + end + # test "create_default_lists/1 creates the default lists" do # # Should have no lists: # person_id = 5 diff --git a/test/app/stats_test.exs b/test/app/stats_test.exs index a9c659c6..122267ac 100644 --- a/test/app/stats_test.exs +++ b/test/app/stats_test.exs @@ -43,7 +43,7 @@ defmodule App.StatsTest do # item1.inserted_at # ) == :lt # assert NaiveDateTime.compare( - # first_element.last_inserted_at, + # first_element.last_inserted_at,  # item2.inserted_at # ) == :eq end @@ -80,7 +80,7 @@ defmodule App.StatsTest do assert first_element.person_id == 2 end - test "Stats.validate_sort_column/1 returns false for invalid sort_column" do + test "Stats.validate_sort_column/1 returns false for invalid sort _column" do refute Stats.validate_sort_column(:invalid) end diff --git a/test/app_web/live/app_live_test.exs b/test/app_web/live/app_live_test.exs index 0adb1bf0..0655dbd7 100644 --- a/test/app_web/live/app_live_test.exs +++ b/test/app_web/live/app_live_test.exs @@ -88,7 +88,7 @@ defmodule AppWeb.AppLiveTest do {:ok, %{model: item}} = Item.create_item(%{text: "Always Learning", person_id: 0, status: 2}) - App.ListItems.add_all_items_to_all_list_for_person_id(item.person_id) + App.List.add_all_items_to_all_list_for_person_id(item.person_id) send(view.pid, %Broadcast{ event: "update", @@ -104,7 +104,7 @@ defmodule AppWeb.AppLiveTest do {:ok, %{model: item}} = Item.create_item(%{text: "Always Learning", person_id: 0, status: 2}) - App.ListItems.add_all_items_to_all_list_for_person_id(item.person_id) + App.List.add_all_items_to_all_list_for_person_id(item.person_id) {:ok, now} = NaiveDateTime.new(Date.utc_today(), Time.utc_now()) @@ -135,7 +135,7 @@ defmodule AppWeb.AppLiveTest do {:ok, %{model: item}} = Item.create_item(%{text: "Always Learning", person_id: 0, status: 2}) - App.ListItems.add_all_items_to_all_list_for_person_id(item.person_id) + App.List.add_all_items_to_all_list_for_person_id(item.person_id) {:ok, seven_seconds_ago} = NaiveDateTime.new(Date.utc_today(), Time.add(Time.utc_now(), -7)) @@ -164,7 +164,7 @@ defmodule AppWeb.AppLiveTest do {:ok, %{model: item}} = Item.create_item(%{text: "Always Learning", person_id: 0, status: 2}) - App.ListItems.add_all_items_to_all_list_for_person_id(item.person_id) + App.List.add_all_items_to_all_list_for_person_id(item.person_id) render_click(view, "edit-item", %{"id" => Integer.to_string(item.id)}) @@ -713,7 +713,7 @@ defmodule AppWeb.AppLiveTest do # The items need to be in the latest seq to appear on the page: list = App.List.get_all_list_for_person(person_id) - App.ListItems.create_list_items_seq(list.cid, person_id, "#{item1.cid},#{item2.cid}") + App.List.update_list_seq(list.cid, person_id, "#{item1.cid},#{item2.cid}") {:ok, view, _html} = live(conn, "/?filter_by=all") @@ -836,7 +836,7 @@ defmodule AppWeb.AppLiveTest do list = App.List.get_all_list_for_person(person_id) # Add all items to "all" list: - App.ListItems.add_all_items_to_all_list_for_person_id(person_id) + App.List.add_all_items_to_all_list_for_person_id(person_id) # Render LiveView {:ok, view, _html} = live(conn, "/") @@ -860,7 +860,8 @@ defmodule AppWeb.AppLiveTest do "seq" => "#{item.cid},#{item2.cid},#{item3.cid}" }) - seq = App.ListItems.get_list_items(list.cid) + all_list = App.List.get_all_list_for_person(person_id) + seq = App.List.get_list_seq(all_list) pos1 = Enum.find_index(seq, fn x -> x == "#{item.cid}" end) pos2 = Enum.find_index(seq, fn x -> x == "#{item2.cid}" end) # IO.puts("#{pos1}: #{item.cid}") @@ -869,13 +870,11 @@ defmodule AppWeb.AppLiveTest do assert pos1 < pos2 # Update list_item.seq: - App.ListItems.create_list_items_seq(list.cid, person_id, "#{item.cid},#{item3.cid},#{item2.cid}") - new_seq = App.ListItems.get_list_items(list.cid) + {:ok, %{model: list}} = App.List.update_list_seq(list.cid, person_id, "#{item.cid},#{item3.cid},#{item2.cid}") + new_seq = list.seq |> String.split(",") # dbg(new_seq) pos2 = Enum.find_index(new_seq, fn x -> x == "#{item2.cid}" end) pos3 = Enum.find_index(new_seq, fn x -> x == "#{item3.cid}" end) - # IO.puts("#{pos2}: #{item2.cid}") - # IO.puts("#{pos3}: #{item3.cid}") assert pos3 < pos2 end