Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Groups #169

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
67 changes: 67 additions & 0 deletions lib/app/group.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
defmodule App.Group do
use Ecto.Schema
import Ecto.{Changeset, Query}
alias App.{List, GroupList, Person, GroupPerson, Repo}
alias __MODULE__

schema "groups" do
field :name, :string

many_to_many(:people, Person,
join_through: GroupPerson,
on_replace: :delete,
join_keys: [group_id: :id, person_id: :person_id]
)

many_to_many(:lists, List, join_through: GroupList, on_replace: :delete)

timestamps()
end

@doc false
def changeset(group, attrs \\ %{}) do
group
|> cast(attrs, [:name])
|> validate_required([:name])
end

def changeset_with_lists(group, list_ids) do
lists = Repo.all(from l in List, where: l.id in ^list_ids)

group
|> change()
|> put_assoc(:lists, lists)
end

def create_group(person_id, attrs) do
person = Person.get_person!(person_id)

%Group{}
|> changeset(attrs)
|> put_assoc(:people, [person])
|> Repo.insert()
end

def get_group!(id) do
Group
|> Repo.get!(id)
|> Repo.preload(people: from(p in Person, order_by: p.name))
|> Repo.preload(lists: from(l in List, order_by: l.name))
end

def update_group(%Group{} = group, attrs) do
group
|> Group.changeset(attrs)
|> Repo.update()
end

def update_group_with_lists(%Group{} = group, list_ids) do
group
|> Group.changeset_with_lists(list_ids)
|> Repo.update()
end

def delete_group(%Group{} = group) do
Repo.delete(group)
end
end
12 changes: 12 additions & 0 deletions lib/app/group_list.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule App.GroupList do
use Ecto.Schema
alias App.{Group, List}

@primary_key false
schema "groups_lists" do
belongs_to(:group, Group)
belongs_to(:list, List)

timestamps()
end
end
12 changes: 12 additions & 0 deletions lib/app/group_person.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule App.GroupPerson do
use Ecto.Schema
alias App.{Group, Person}

@primary_key false
schema "groups_people" do
belongs_to(:group, Group)
belongs_to :people, Person, references: :person_id, foreign_key: :person_id

timestamps()
end
end
31 changes: 26 additions & 5 deletions lib/app/item.ex
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
defmodule App.Item do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias App.{Repo, Tag, ItemTag, Person}
import Ecto.{Changeset, Query}
alias App.{Repo, Tag, ListItem, ItemTag, Person}
alias App.List, as: L
alias __MODULE__

schema "items" do
field :status, :integer
field :text, :string
field :item_lists, {:array, :string}, virtual: true

belongs_to :people, Person, references: :person_id, foreign_key: :person_id
many_to_many(:tags, Tag, join_through: ItemTag, on_replace: :delete)
many_to_many(:lists, L, join_through: ListItem, on_replace: :delete)

timestamps()
end

@doc false
def changeset(item, attrs) do
def changeset(item, attrs \\ %{}) do
item
|> cast(attrs, [:person_id, :status, :text])
|> cast(attrs, [:person_id, :status, :text, :item_lists])
|> validate_required([:text, :person_id])
end

Expand All @@ -27,6 +29,14 @@ defmodule App.Item do
|> put_assoc(:tags, Tag.parse_and_create_tags(attrs))
end

def changeset_with_lists(item, list_ids) do
lists = Repo.all(from l in L, where: l.id in ^list_ids)

item
|> change()
|> put_assoc(:lists, lists)
end

@doc """
Creates an `item`.

Expand Down Expand Up @@ -69,6 +79,7 @@ defmodule App.Item do
Item
|> Repo.get!(id)
|> Repo.preload(tags: from(t in Tag, order_by: t.text))
|> Repo.preload(lists: from(l in L, order_by: l.name))
end

@doc """
Expand All @@ -92,6 +103,7 @@ defmodule App.Item do
|> where(person_id: ^person_id)
|> Repo.all()
|> Repo.preload(tags: from(t in Tag, order_by: t.text))
|> Repo.preload(lists: from(l in L, order_by: l.name))
end

@doc """
Expand Down Expand Up @@ -121,6 +133,12 @@ defmodule App.Item do
|> Repo.update()
end

def update_item_with_lists(%Item{} = item, list_ids) do
item
|> Item.changeset_with_lists(list_ids)
|> Repo.update()
end

def delete_item(id) do
get_item!(id)
|> Item.changeset(%{status: 6})
Expand Down Expand Up @@ -163,6 +181,9 @@ defmodule App.Item do
|> Enum.map(fn t ->
Map.put(t, :tags, items_tags[t.id].tags)
end)
|> Enum.map(fn t ->
Map.put(t, :lists, items_tags[t.id].lists)
end)
end

@doc """
Expand Down
53 changes: 53 additions & 0 deletions lib/app/list.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule App.List do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias App.{Item, ListItem, Person, Repo}
alias __MODULE__

schema "lists" do
field :name, :string

belongs_to :people, Person, references: :person_id, foreign_key: :person_id
many_to_many(:items, Item, join_through: ListItem)

timestamps()
end

@doc false
def changeset(list, attrs \\ %{}) do
list
|> cast(attrs, [:person_id, :name])
|> validate_required([:person_id, :name])
|> unique_constraint([:name, :person_id], name: :lists_name_person_id_index)
end

def create_list(attrs) do
%List{}
|> changeset(attrs)
|> Repo.insert()
end

def get_list!(id), do: Repo.get!(List, id)

def get_lists_from_ids(ids) do
Repo.all(from l in List, where: l.id in ^ids)
end

def list_person_lists(person_id) do
List
|> where(person_id: ^person_id)
|> order_by(:name)
|> Repo.all()
end

def update_list(%List{} = list, attrs) do
list
|> List.changeset(attrs)
|> Repo.update()
end

def delete_list(%List{} = list) do
Repo.delete(list)
end
end
12 changes: 12 additions & 0 deletions lib/app/list_item.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule App.ListItem do
use Ecto.Schema
alias App.{Item, List}

@primary_key false
schema "lists_items" do
belongs_to(:item, Item)
belongs_to(:list, List)

timestamps()
end
end
22 changes: 20 additions & 2 deletions lib/app/person.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule App.Person do
use Ecto.Schema
import Ecto.Changeset
alias App.{Item, Repo, Tag}
import Ecto.{Changeset, Query}
alias App.{Item, Group, GroupPerson, Repo, Tag}
alias __MODULE__
# https://hexdocs.pm/phoenix/Phoenix.Param.html
@derive {Phoenix.Param, key: :person_id}
Expand All @@ -12,6 +12,12 @@ defmodule App.Person do

has_many :items, Item, foreign_key: :person_id
has_many :tags, Tag, foreign_key: :person_id

many_to_many(:groups, Group,
join_through: GroupPerson,
join_keys: [person_id: :person_id, group_id: :id]
)

timestamps()
end

Expand All @@ -30,10 +36,22 @@ defmodule App.Person do
end

def get_person!(person_id), do: Repo.get!(Person, person_id)
def get_person_by_name(name), do: Repo.get_by(Person, name: name)

def get_person_with_groups!(person_id) do
Person
|> Repo.get!(person_id)
|> Repo.preload(groups: from(g in Group, order_by: g.name))
end

def update_person(%Person{} = person, attrs) do
person
|> Person.changeset(attrs)
|> Repo.update()
end

def get_or_insert(person_id) do
Repo.get_by(Person, person_id: person_id) ||
Repo.insert!(%Person{person_id: person_id})
end
end
69 changes: 69 additions & 0 deletions lib/app_web/controllers/group_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
defmodule AppWeb.GroupController do
use AppWeb, :controller
alias App.{Group, Person}
# plug :permission_tag when action in [:edit, :update, :delete]

# Only member of the group can have persmission on the group updates
# When a new group is created the person_id is added automatically in the group
# Create the post /group/1/person/create endpoint to add a new person to the group by name

# input text with person's name: --- Add to group
# display list of existing group member
# add liveview to not have to reload the page (nice to have)

def index(conn, _params) do
person_id = conn.assigns[:person][:id] || 0
person = Person.get_person_with_groups!(person_id)

render(conn, "index.html", groups: person.groups)
end

def new(conn, _params) do
changeset = Group.changeset(%Group{})
render(conn, "new.html", changeset: changeset)
end

def create(conn, %{"group" => group_params}) do
person_id = conn.assigns[:person][:id] || 0
# add person_id to group

case Group.create_group(person_id, group_params) do
{:ok, _group} ->
conn
|> put_flash(:info, "Group created successfully.")
|> redirect(to: Routes.group_path(conn, :index))

{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end

def edit(conn, %{"id" => id}) do
group = Group.get_group!(id)
changeset = Group.changeset(group)
render(conn, "edit.html", group: group, changeset: changeset)
end

def update(conn, %{"id" => id, "group" => group_params}) do
group = Group.get_group!(id)

case Group.update_group(group, group_params) do
{:ok, _group} ->
conn
|> put_flash(:info, "Group updated successfully.")
|> redirect(to: Routes.group_path(conn, :index))

{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "edit.html", group: group, changeset: changeset)
end
end

def delete(conn, %{"id" => id}) do
group = Group.get_group!(id)
{:ok, _group} = Group.delete_group(group)

conn
|> put_flash(:info, "Group deleted successfully.")
|> redirect(to: Routes.group_path(conn, :index))
end
end
Loading