+
diff --git a/core/systems/pool/landing_page.ex b/core/systems/pool/landing_page.ex
index bfa30e309..cb18b87db 100644
--- a/core/systems/pool/landing_page.ex
+++ b/core/systems/pool/landing_page.ex
@@ -17,12 +17,6 @@ defmodule Systems.Pool.LandingPage do
{:ok, socket}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def handle_event("register", _, socket) do
{
diff --git a/core/systems/pool/participant_page.ex b/core/systems/pool/participant_page.ex
index c40001468..aa61bff5b 100644
--- a/core/systems/pool/participant_page.ex
+++ b/core/systems/pool/participant_page.ex
@@ -18,12 +18,6 @@ defmodule Systems.Pool.ParticipantPage do
{:ok, socket}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def render(assigns) do
~H"""
diff --git a/core/systems/pool/submission_page.ex b/core/systems/pool/submission_page.ex
index 537edc24b..e22436d60 100644
--- a/core/systems/pool/submission_page.ex
+++ b/core/systems/pool/submission_page.ex
@@ -25,12 +25,6 @@ defmodule Systems.Pool.SubmissionPage do
{:ok, socket}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def handle_event("publish", _params, %{assigns: %{vm: %{submission: submission}}} = socket) do
socket =
diff --git a/core/systems/project/_assembly.ex b/core/systems/project/_assembly.ex
index 9a92a9b8e..f923c45a9 100644
--- a/core/systems/project/_assembly.ex
+++ b/core/systems/project/_assembly.ex
@@ -11,6 +11,15 @@ defmodule Systems.Project.Assembly do
alias Systems.Assignment
alias Systems.Project
+ def template(%Project.ItemModel{} = item) do
+ item
+ |> Project.ItemModel.special()
+ |> template()
+ end
+
+ def template(%Assignment.Model{special: template}), do: template
+ def template(_), do: nil
+
def delete(%Project.Model{auth_node: %{id: node_id}}) do
from(ra in Authorization.RoleAssignment,
where: ra.node_id == ^node_id
@@ -41,17 +50,6 @@ defmodule Systems.Project.Assembly do
|> Repo.transaction()
end
- def create(name, user, template) do
- items = prepare_items(template)
- project = prepare_project(name, items, user)
-
- Multi.new()
- |> Multi.insert(:project, project)
- |> EctoHelper.run(:auth, &update_auth/2)
- |> EctoHelper.run(:path, &update_path/2)
- |> Repo.transaction()
- end
-
def create_item(template_or_changeset, name, %Project.NodeModel{} = node)
when is_binary(name) do
Multi.new()
@@ -79,16 +77,6 @@ defmodule Systems.Project.Assembly do
Project.Public.prepare(%{name: name}, items, user)
end
- defp prepare_items(:data_donation) do
- [prepare_item(:data_donation, "Data Donation Assignment")]
- end
-
- defp prepare_items(:benchmark) do
- [
- prepare_item(:benchmark_challenge, "Benchmark Challenge")
- ]
- end
-
defp prepare_item(:benchmark_challenge, name) do
{:ok, assignment} =
Assignment.Assembly.prepare(:benchmark_challenge, :project, nil)
diff --git a/core/systems/project/_live_hook.ex b/core/systems/project/_live_hook.ex
new file mode 100644
index 000000000..384cab62a
--- /dev/null
+++ b/core/systems/project/_live_hook.ex
@@ -0,0 +1,24 @@
+defmodule Systems.Project.LiveHook do
+ @moduledoc "A Live Hook that injects the correct Branch (Project.NodeModel) for each Leaf"
+ use Frameworks.Concept.LiveHook
+
+ alias Frameworks.Concept
+ alias Systems.Project
+
+ @impl true
+ def on_mount(_live_view_module, _params, _session, socket) do
+ branch =
+ with model <- Map.get(socket.assigns, :model, nil),
+ false <- model == nil,
+ false <- Concept.Leaf.impl_for(model) == nil,
+ item <- Project.Public.get_item_by(model),
+ false <- item == nil,
+ node <- Project.Public.get_node_by_item!(item) do
+ %Project.Branch{node_id: node.id, item_id: item.id}
+ else
+ _ -> nil
+ end
+
+ {:cont, socket |> assign(branch: branch)}
+ end
+end
diff --git a/core/systems/project/_public.ex b/core/systems/project/_public.ex
index c57ba1ece..1e1314aac 100644
--- a/core/systems/project/_public.ex
+++ b/core/systems/project/_public.ex
@@ -1,5 +1,5 @@
defmodule Systems.Project.Public do
- @behaviour Frameworks.Concept.Context.Handler
+ use CoreWeb, :verified_routes
import CoreWeb.Gettext
import Ecto.Query, warn: false
@@ -8,9 +8,10 @@ defmodule Systems.Project.Public do
alias Core.Authorization
alias Core.Repo
alias Ecto.Multi
+
alias Frameworks.Signal
- alias Systems.Account.User
+ alias Systems.Account.User
alias Systems.Advert
alias Systems.Assignment
alias Systems.Graphite
@@ -18,25 +19,6 @@ defmodule Systems.Project.Public do
alias Systems.Storage
alias Systems.Workflow
- @impl true
- def name(:parent, %Systems.Storage.EndpointModel{} = model) do
- case get_by_item_special(model) do
- %{name: name} -> {:ok, name}
- _ -> {:error, :not_found}
- end
- end
-
- @impl true
- def name(:self, %Systems.Storage.EndpointModel{} = model) do
- case get_item_by(model) do
- %{name: name} -> {:ok, name}
- _ -> {:error, :not_found}
- end
- end
-
- @impl true
- def name(_, _), do: {:error, :not_supported}
-
def get!(id, preload \\ []) do
from(project in Project.Model,
where: project.id == ^id,
@@ -105,30 +87,29 @@ defmodule Systems.Project.Public do
end
end
- def get_item_by(assignment, preload \\ [])
-
- def get_item_by(%Assignment.Model{id: assignment_id}, preload) do
- get_item_by_special(:assignment, assignment_id, preload)
+ def get_item_by(%Assignment.Model{id: assignment_id}) do
+ get_item_by_special(:assignment, assignment_id)
end
- def get_item_by(%Advert.Model{id: advert_id}, preload) do
- get_item_by_special(:advert, advert_id, preload)
+ def get_item_by(%Advert.Model{id: advert_id}) do
+ get_item_by_special(:advert, advert_id)
end
- def get_item_by(%Graphite.LeaderboardModel{id: advert_id}, preload) do
- get_item_by_special(:leaderboard, advert_id, preload)
+ def get_item_by(%Graphite.LeaderboardModel{id: advert_id}) do
+ get_item_by_special(:leaderboard, advert_id)
end
- def get_item_by(%Storage.EndpointModel{id: storage_endpoint_id}, preload) do
- get_item_by_special(:storage_endpoint, storage_endpoint_id, preload)
+ def get_item_by(%Storage.EndpointModel{id: storage_endpoint_id}) do
+ get_item_by_special(:storage_endpoint, storage_endpoint_id)
end
- defp get_item_by_special(special_name, special_id, preload) do
+ defp get_item_by_special(special_name, special_id) do
item_query_by_special(special_name, special_id)
- |> Repo.preload(preload)
|> Repo.one()
+ |> Repo.preload(Project.ItemModel.preload_graph(:down))
end
+ @spec list_owned_projects(any()) :: any()
@doc """
Returns the list of projects that are owned by the user.
"""
diff --git a/core/systems/project/branch.ex b/core/systems/project/branch.ex
new file mode 100644
index 000000000..c8e2fcef8
--- /dev/null
+++ b/core/systems/project/branch.ex
@@ -0,0 +1,58 @@
+defmodule Systems.Project.Branch do
+ use Ecto.Schema
+ @primary_key false
+
+ embedded_schema do
+ field(:node_id, :integer)
+ field(:item_id, :integer)
+ end
+end
+
+defimpl Frameworks.Concept.Branch, for: Systems.Project.Branch do
+ use CoreWeb, :verified_routes
+ import CoreWeb.Gettext
+ import Frameworks.Utility.List
+ alias Frameworks.Concept
+ alias Systems.Project
+
+ def name(%Project.Branch{node_id: node_id}, :parent) do
+ %Project.Model{name: name} =
+ node_id
+ |> Project.Public.get_node!()
+ |> Project.Public.get_by_root()
+
+ name
+ end
+
+ def name(%Project.Branch{item_id: item_id}, :self) do
+ %Project.ItemModel{name: name} = Project.Public.get_item!(item_id)
+ name
+ end
+
+ def hierarchy(%Project.Branch{node_id: node_id, item_id: item_id}) do
+ node_breadcrumb = fn ->
+ Project.Public.get_node!(node_id) |> breadcrumb()
+ end
+
+ item_breadcrumb = fn ->
+ Project.Public.get_item!(item_id, Project.ItemModel.preload_graph(:down)) |> breadcrumb()
+ end
+
+ [root_breadcrumb()]
+ |> append_if(node_breadcrumb, not is_nil(node_id))
+ |> append_if(item_breadcrumb, not is_nil(item_id))
+ end
+
+ defp breadcrumb(%Project.ItemModel{name: name} = item) do
+ %{label: name, path: "/#{Concept.Leaf.resource_id(item)}/content"}
+ end
+
+ defp breadcrumb(%Project.NodeModel{} = node) do
+ %Project.Model{name: name} = Project.Public.get_by_root(node)
+ %{label: name, path: "/project/node/#{node.id}"}
+ end
+
+ defp root_breadcrumb() do
+ %{label: dgettext("eyra-project", "first.breadcrumb.label"), path: ~p"/project"}
+ end
+end
diff --git a/core/systems/project/branch_plug.ex b/core/systems/project/branch_plug.ex
new file mode 100644
index 000000000..b689589b1
--- /dev/null
+++ b/core/systems/project/branch_plug.ex
@@ -0,0 +1,30 @@
+defmodule Systems.Project.BranchPlug do
+ @behaviour Plug
+ alias Frameworks.Concept
+ alias Systems.Project
+ alias Systems.Storage
+
+ @impl true
+ def init(opts), do: opts
+
+ @impl true
+ def call(%{request_path: request_path} = conn, _opts) do
+ branch = branch(Path.split(request_path))
+ conn |> Plug.Conn.assign(:branch, branch)
+ end
+
+ defp branch(["/", "storage", "endpoint", id | _]), do: branch(Storage.Public.get_endpoint!(id))
+
+ defp branch(%{} = leaf) do
+ with false <- Concept.Leaf.impl_for(leaf) == nil,
+ item <- Project.Public.get_item_by(leaf),
+ false <- item == nil,
+ node <- Project.Public.get_node_by_item!(item) do
+ %Project.Branch{node_id: node.id, item_id: item.id}
+ else
+ _ -> nil
+ end
+ end
+
+ defp branch(_), do: nil
+end
diff --git a/core/systems/project/create_item_popup.ex b/core/systems/project/create_item_view.ex
similarity index 88%
rename from core/systems/project/create_item_popup.ex
rename to core/systems/project/create_item_view.ex
index 80db6082c..bfef05583 100644
--- a/core/systems/project/create_item_popup.ex
+++ b/core/systems/project/create_item_view.ex
@@ -1,4 +1,4 @@
-defmodule Systems.Project.CreateItemPopup do
+defmodule Systems.Project.CreateItemView do
use CoreWeb, :live_component
import CoreWeb.UI.Dialog
@@ -58,10 +58,6 @@ defmodule Systems.Project.CreateItemPopup do
type: :primary,
label: dgettext("eyra-project", "create_item_popup.create.button")
}
- },
- %{
- action: %{type: :send, event: "cancel", target: myself},
- face: %{type: :label, label: dgettext("eyra-ui", "cancel.button")}
}
]
)
@@ -78,11 +74,6 @@ defmodule Systems.Project.CreateItemPopup do
{:noreply, socket |> send_event(:parent, "saved")}
end
- @impl true
- def handle_event("cancel", _, socket) do
- {:noreply, socket |> send_event(:parent, "cancelled")}
- end
-
@impl true
def handle_event(
"active_item_id",
diff --git a/core/systems/project/item_model.ex b/core/systems/project/item_model.ex
index 835eee96f..7e191b000 100644
--- a/core/systems/project/item_model.ex
+++ b/core/systems/project/item_model.ex
@@ -1,19 +1,23 @@
defmodule Systems.Project.ItemModel do
+ @required_fields ~w(name project_path)a
+ @fields @required_fields
+ @special_fields ~w(storage_endpoint assignment advert leaderboard)a
+
use Ecto.Schema
use Frameworks.Utility.Schema
+ use Frameworks.Concept.Special, @special_fields
import Ecto.Changeset
import CoreWeb.Gettext
- alias CoreWeb.UI.Timestamp
alias Core.ImageHelpers
+ alias Frameworks.Concept
alias Systems.Project
alias Systems.Storage
alias Systems.Assignment
alias Systems.Advert
alias Systems.Graphite
- alias Systems.Monitor
schema "project_items" do
field(:name, :string)
@@ -26,9 +30,6 @@ defmodule Systems.Project.ItemModel do
timestamps()
end
- @required_fields ~w(name project_path)a
- @fields @required_fields
-
@doc false
def changeset(project_item, attrs) do
project_item
@@ -63,8 +64,6 @@ defmodule Systems.Project.ItemModel do
def preload_graph(:storage_endpoint),
do: [storage_endpoint: Storage.EndpointModel.preload_graph(:down)]
- def special(%{assignment: %{id: _id} = special}), do: special
-
def auth_tree(%Project.ItemModel{assignment: %Ecto.Association.NotLoaded{}} = item) do
auth_tree(Repo.preload(item, :assignment))
end
@@ -102,250 +101,126 @@ defmodule Systems.Project.ItemModel do
Enum.map(items, &auth_tree/1)
end
- def tag(%{assignment: %{id: _} = assignment}), do: Assignment.Model.tag(assignment)
- def tag(%{leaderboard: %{id: _} = leaderboard}), do: Graphite.LeaderboardModel.tag(leaderboard)
- def tag(%{advert: %{id: _} = advert}), do: Advert.Model.tag(advert)
-
- def tag(%{storage_endpoint: %{id: _} = storage_endpoint}),
- do: Storage.EndpointModel.tag(storage_endpoint)
+ defimpl Frameworks.Concept.Leaf do
+ def tag(item), do: Project.ItemModel.special(item) |> Concept.Leaf.tag()
+ def resource_id(item), do: Project.ItemModel.special(item) |> Concept.Leaf.resource_id()
+ def info(item, timezone), do: Project.ItemModel.special(item) |> Concept.Leaf.info(timezone)
+ def status(item), do: Project.ItemModel.special(item) |> Concept.Leaf.status()
+ end
defimpl Frameworks.Utility.ViewModelBuilder do
use CoreWeb, :verified_routes
- def view_model(%Project.ItemModel{} = item, page, %{current_user: user, timezone: timezone}) do
- vm(item, page, user, timezone)
- end
-
- defp vm(
- %{
- id: id,
- name: name,
- assignment:
- %{
- id: assignment_id,
- status: status,
- info: %{
- image_id: image_id,
- logo_url: logo_url
- }
- } = assignment
- },
- {Project.NodePage, :item_card},
- _user,
- _timezone
- ) do
- image_info = ImageHelpers.get_image_info(image_id, 400, 200)
- tags = get_card_tags(assignment)
- path = ~p"/assignment/#{assignment_id}/content"
-
- edit = %{
- action: %{type: :send, event: "rename", item: id},
- face: %{type: :label, label: "Rename", wrap: true}
- }
-
- delete = %{
- action: %{type: :send, event: "delete", item: id},
- face: %{type: :icon, icon: :delete}
- }
-
+ def view_model(
+ %Project.ItemModel{id: id, name: name} = item,
+ {Project.NodePage, :item_card},
+ %{timezone: timezone}
+ ) do
%{
type: :secondary,
id: id,
- path: path,
- image_info: image_info,
- icon_url: logo_url,
- label: get_label(status),
title: name,
- tags: tags,
- info: get_assignment_info(assignment),
- left_actions: [edit],
- right_actions: [delete]
+ path: "/#{Concept.Leaf.resource_id(item)}/content",
+ image_info: image_info(item),
+ icon_url: logo_url(item),
+ label: label(Concept.Leaf.status(item)),
+ tags: [Concept.Leaf.tag(item)],
+ info: Concept.Leaf.info(item, timezone),
+ left_actions: left_actions(item),
+ right_actions: right_actions(item)
}
end
- defp vm(
- %{
- id: id,
- name: name,
- leaderboard: %{id: leaderboard_id, status: status} = leaderboard
- },
- {Project.NodePage, :item_card},
- _user,
- timezone
- ) do
- assignment = Assignment.Public.get_by_tool(leaderboard.tool, [:info])
- image_info = ImageHelpers.get_image_info(assignment.info.image_id, 400, 200)
- logo_url = assignment.info.logo_url
+ # ACTIONS
+ defp left_actions(%Project.ItemModel{id: id}) do
edit = %{
action: %{type: :send, event: "rename", item: id},
face: %{type: :label, label: "Rename", wrap: true}
}
- delete = %{
- action: %{type: :send, event: "delete", item: id},
- face: %{type: :icon, icon: :delete}
- }
-
- %{
- type: :secondary,
- id: id,
- path: ~p"/graphite/leaderboard/#{leaderboard_id}/content",
- image_info: image_info,
- icon_url: logo_url,
- label: get_label(status),
- title: name,
- tags: get_card_tags(leaderboard),
- info: get_leaderboard_info(leaderboard, timezone),
- left_actions: [edit],
- right_actions: [delete]
- }
+ [edit]
end
- defp vm(
- %{
- id: id,
- name: name,
- advert: %{id: advert_id, assignment: assignment, status: status} = advert
- },
- {Project.NodePage, :item_card},
- _user,
- _timezone
- ) do
- image_info = ImageHelpers.get_image_info(assignment.info.image_id, 400, 200)
- logo_url = assignment.info.logo_url
-
- edit = %{
- action: %{type: :send, event: "rename", item: id},
- face: %{type: :label, label: "Rename", wrap: true}
- }
-
+ defp right_actions(%Project.ItemModel{id: id}) do
delete = %{
action: %{type: :send, event: "delete", item: id},
face: %{type: :icon, icon: :delete}
}
- %{
- type: :secondary,
- id: id,
- path: ~p"/advert/#{advert_id}/content",
- image_info: image_info,
- icon_url: logo_url,
- label: get_label(status),
- title: name,
- tags: get_card_tags(advert),
- info: get_advert_info(advert),
- left_actions: [edit],
- right_actions: [delete]
- }
+ [delete]
end
- defp vm(
- %{
- id: id,
- name: name,
- storage_endpoint: %{id: storage_endpoint_id} = storage_endpoint
- },
- {Project.NodePage, :item_card},
- _user,
- _timezone
- ) do
- image_id =
- "raw_url=https%3A%2F%2Fimages.unsplash.com%2Fphoto-1622986819498-60765a6e52c0%3Fixid%3DM3w1MzYyOTF8MHwxfHNlYXJjaHwzOXx8Zm9sZGVyc3xlbnwwfHx8fDE3MTg3OTEyOTB8MA%26ixlib%3Drb-4.0.3&username=_sycthe_&name=Tanay+Shah&blur_hash=LCH%5En%5DActRkV%2AH%25yM%7BsD%5BBNMkYkU"
-
- image_info = ImageHelpers.get_image_info(image_id, 400, 200)
-
- edit = %{
- action: %{type: :send, event: "rename", item: id},
- face: %{type: :label, label: "Rename", wrap: true}
- }
-
- delete = %{
- action: %{type: :send, event: "delete", item: id},
- face: %{type: :icon, icon: :delete}
- }
-
- icon_url = Storage.EndpointModel.asset_image_src(storage_endpoint, :icon)
+ # IMAGE
- %{
- type: :secundary,
- id: id,
- path: ~p"/storage/#{storage_endpoint_id}/content",
- image_info: image_info,
- icon_url: icon_url,
- label: get_label(Storage.Public.status(storage_endpoint)),
- title: name,
- tags: get_card_tags(storage_endpoint),
- info: get_storage_endpoint_info(storage_endpoint),
- left_actions: [edit],
- right_actions: [delete]
- }
+ defp image_info(%Project.ItemModel{} = item) do
+ image_info(Project.ItemModel.special(item))
end
- defp get_label(:concept),
- do: %{type: :warning, text: dgettext("eyra-project", "label.concept")}
+ defp image_info(%Storage.EndpointModel{}) do
+ image_info(
+ "raw_url=https%3A%2F%2Fimages.unsplash.com%2Fphoto-1622986819498-60765a6e52c0%3Fixid%3DM3w1MzYyOTF8MHwxfHNlYXJjaHwzOXx8Zm9sZGVyc3xlbnwwfHx8fDE3MTg3OTEyOTB8MA%26ixlib%3Drb-4.0.3&username=_sycthe_&name=Tanay+Shah&blur_hash=LCH%5En%5DActRkV%2AH%25yM%7BsD%5BBNMkYkU"
+ )
+ end
- defp get_label(:online), do: %{type: :success, text: dgettext("eyra-project", "label.online")}
+ defp image_info(%Assignment.Model{info: %{image_id: image_id}}) do
+ image_info(image_id)
+ end
- defp get_label(:offline),
- do: %{type: :delete, text: dgettext("eyra-project", "label.offline")}
+ defp image_info(struct) when is_struct(struct) do
+ image_info(assignment(struct))
+ end
- defp get_label(:idle), do: %{type: :idle, text: dgettext("eyra-project", "label.idle")}
+ defp image_info(image_id) do
+ ImageHelpers.get_image_info(image_id, 400, 200)
+ end
- defp get_label(nil), do: %{type: :idle, text: "Improper label"}
+ # LOGO
- defp get_card_tags(%Assignment.Model{}) do
- ["Assignment"]
+ defp logo_url(%Project.ItemModel{} = item) do
+ logo_url(Project.ItemModel.special(item))
end
- defp get_card_tags(%Graphite.LeaderboardModel{}), do: ["Leaderboard"]
- defp get_card_tags(%Graphite.ToolModel{}), do: ["Challenge"]
- defp get_card_tags(%Advert.Model{}), do: ["Advertisement"]
- defp get_card_tags(%Storage.EndpointModel{}), do: ["Storage"]
- defp get_card_tags(_), do: []
+ defp logo_url(%Storage.EndpointModel{} = storage_endpoint) do
+ Storage.EndpointModel.asset_image_src(storage_endpoint, :icon)
+ end
- defp get_assignment_info(%Assignment.Model{info: info}) do
- subject_count = Map.get(info, :subject_count) || 0
- [dngettext("eyra-project", "1 participant", "* participants", subject_count)]
+ defp logo_url(%Assignment.Model{info: %{logo_url: logo_url}}) do
+ logo_url
end
- defp get_advert_info(%Advert.Model{submission: %{pool: %{name: pool_name}}}) do
- [pool_name]
+ defp logo_url(struct) when is_struct(struct) do
+ logo_url(assignment(struct))
end
- defp get_storage_endpoint_info(%Storage.EndpointModel{} = storage_endpoint) do
- file_count =
- Monitor.Public.event({storage_endpoint, :files})
- |> Monitor.Public.sum()
+ defp logo_url(nil), do: nil
- [dngettext("eyra-project", "1 file", "* files", file_count)]
- end
+ # LABEL
+ defp label(%Frameworks.Concept.Leaf.Status{value: status}),
+ do: label(status)
- defp get_leaderboard_info(%Graphite.LeaderboardModel{id: _id, tool: tool}, timezone) do
- deadline_str = format_datetime(tool.deadline, timezone)
- nr_submissions = Graphite.Public.get_submission_count(tool)
+ defp label(:concept),
+ do: %{type: :warning, text: dgettext("eyra-project", "label.concept")}
- submission_info = dngettext("eyra-project", "1 submission", "* submissions", nr_submissions)
+ defp label(:online), do: %{type: :success, text: dgettext("eyra-project", "label.online")}
- deadline_info =
- dgettext("eyra-project", "leaderboard.deadline.info", deadline: deadline_str)
+ defp label(:offline),
+ do: %{type: :delete, text: dgettext("eyra-project", "label.offline")}
- [
- [submission_info, deadline_info]
- |> Enum.reject(&(&1 == ""))
- |> Enum.join(" | ")
- ]
- end
+ defp label(:idle),
+ do: %{type: :idle, text: dgettext("eyra-project", "label.idle")}
- defp format_datetime(nil, _timezone),
- do: dgettext("eyra-project", "leaderboard.unspecified.deadline.label")
+ # STRUCTURE
- defp format_datetime(_, nil), do: ""
+ defp assignment(%Project.ItemModel{} = item) do
+ assignment(Project.ItemModel.special(item))
+ end
- defp format_datetime(datetime, timezone) do
- datetime
- |> Timestamp.convert(timezone)
- |> Timestamp.format!()
+ defp assignment(%Graphite.LeaderboardModel{tool: tool}) do
+ assignment(Assignment.Public.get_by_tool(tool, [:info]))
end
+
+ defp assignment(%Advert.Model{assignment: assignment}), do: assignment
+ defp assignment(%Assignment.Model{} = assignment), do: assignment
end
end
diff --git a/core/systems/project/model.ex b/core/systems/project/model.ex
index c6ce2b3fc..dd3a89e09 100644
--- a/core/systems/project/model.ex
+++ b/core/systems/project/model.ex
@@ -3,10 +3,7 @@ defmodule Systems.Project.Model do
use Frameworks.Utility.Schema
import Ecto.Changeset
-
- alias Systems.{
- Project
- }
+ alias Systems.Project
schema "projects" do
field(:name, :string)
@@ -83,7 +80,7 @@ defmodule Systems.Project.Model do
tags =
items
- |> Enum.map(&Project.ItemModel.tag/1)
+ |> Enum.map(&tag/1)
|> Enum.filter(&(&1 != nil))
|> Enum.uniq()
@@ -100,6 +97,14 @@ defmodule Systems.Project.Model do
}
end
+ defp tag(item) do
+ if template = Project.Assembly.template(item) do
+ Project.ItemTemplates.translate(template)
+ else
+ nil
+ end
+ end
+
defp info([_item]), do: "1 item"
defp info(items) when is_list(items), do: "#{Enum.count(items)} items"
end
diff --git a/core/systems/project/node_page.ex b/core/systems/project/node_page.ex
index a00d90444..c41e4c469 100644
--- a/core/systems/project/node_page.ex
+++ b/core/systems/project/node_page.ex
@@ -2,9 +2,16 @@ defmodule Systems.Project.NodePage do
use Systems.Content.Composer, :live_workspace
import Frameworks.Pixel.Empty
+ import Frameworks.Pixel.Line
alias Frameworks.Pixel.Grid
+ alias Frameworks.Pixel.Breadcrumbs
alias Systems.Project
+ @impl true
+ def get_authorization_context(params, session, socket) do
+ get_model(params, session, socket)
+ end
+
@impl true
def get_model(%{"id" => id}, _session, _socket) do
Project.Public.get_node!(String.to_integer(id), Project.NodeModel.preload_graph(:down))
@@ -26,9 +33,9 @@ defmodule Systems.Project.NodePage do
end
@impl true
- def compose(:create_item_popup, %{vm: %{node: node}}) do
+ def compose(:create_item_view, %{vm: %{node: node}}) do
%{
- module: Project.CreateItemPopup,
+ module: Project.CreateItemView,
params: %{node: node}
}
end
@@ -65,8 +72,8 @@ defmodule Systems.Project.NodePage do
{
:noreply,
socket
- |> compose_child(:create_item_popup)
- |> show_popup(:create_item_popup)
+ |> compose_child(:create_item_view)
+ |> show_modal(:create_item_view, :dialog)
}
end
@@ -86,17 +93,21 @@ defmodule Systems.Project.NodePage do
{:noreply, socket |> hide_modal(modal_view)}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def render(assigns) do
~H"""
<.live_workspace title={@vm.title} menus={@menus} modal={@modal} popup={@popup} dialog={@dialog}>
+ <:top_bar>
+
+
+
+ <.live_component id="path" module={Breadcrumbs} elements={@vm.breadcrumbs}/>
+
+
+ <.line />
+
+
<%= if Enum.count(@vm.node_cards) > 0 do %>
diff --git a/core/systems/project/node_page_builder.ex b/core/systems/project/node_page_builder.ex
index 74a669139..bf1e17ef4 100644
--- a/core/systems/project/node_page_builder.ex
+++ b/core/systems/project/node_page_builder.ex
@@ -2,10 +2,8 @@ defmodule Systems.Project.NodePageBuilder do
use Core.FeatureFlags
alias Frameworks.Utility.ViewModelBuilder
-
- alias Systems.{
- Project
- }
+ alias Frameworks.Concept
+ alias Systems.Project
def view_model(
%Project.NodeModel{
@@ -13,12 +11,15 @@ defmodule Systems.Project.NodePageBuilder do
} = node,
assigns
) do
+ branch = %Project.Branch{node_id: node.id}
+ breadcrumbs = Concept.Branch.hierarchy(branch)
item_cards = to_item_cards(node, assigns)
node_cards = to_node_cards(node, assigns)
%{
id: id,
title: node.name,
+ breadcrumbs: breadcrumbs,
active_menu_item: :projects,
node_cards: node_cards,
item_cards: item_cards,
diff --git a/core/systems/project/overview_page.ex b/core/systems/project/overview_page.ex
index 9f91cd134..71cdfb809 100644
--- a/core/systems/project/overview_page.ex
+++ b/core/systems/project/overview_page.ex
@@ -21,12 +21,8 @@ defmodule Systems.Project.OverviewPage do
{:ok, socket}
end
- @impl true
def handle_view_model_updated(socket), do: socket |> update_child(:people_page)
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def compose(:project_form, %{active_project: project_id, vm: %{projects: projects}}) do
project = Enum.find(projects, &(&1.id == String.to_integer(project_id)))
diff --git a/core/systems/promotion/landing_page.ex b/core/systems/promotion/landing_page.ex
index 490b0993e..ede471136 100644
--- a/core/systems/promotion/landing_page.ex
+++ b/core/systems/promotion/landing_page.ex
@@ -3,7 +3,9 @@ defmodule Systems.Promotion.LandingPage do
The public promotion screen.
"""
use Systems.Content.Composer, :live_website
- use CoreWeb.UI.Responsive.Viewport
+
+ on_mount({CoreWeb.Live.Hook.Base, __MODULE__})
+ on_mount({CoreWeb.Live.Hook.Viewport, __MODULE__})
import Systems.Promotion.BannerView
@@ -42,14 +44,10 @@ defmodule Systems.Promotion.LandingPage do
}
end
- @impl true
def handle_view_model_updated(socket) do
update_image_info(socket)
end
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def handle_resize(socket) do
update_image_info(socket)
@@ -117,7 +115,7 @@ defmodule Systems.Promotion.LandingPage do
@impl true
def render(assigns) do
~H"""
-
+
<.live_website user={@current_user} user_agent={Browser.Ua.to_ua(@socket)} menus={@menus} modal={@modal} popup={@popup} dialog={@dialog}>
<:hero>
diff --git a/core/systems/storage/_public.ex b/core/systems/storage/_public.ex
index dc01f48be..1a27c3ee2 100644
--- a/core/systems/storage/_public.ex
+++ b/core/systems/storage/_public.ex
@@ -24,6 +24,7 @@ defmodule Systems.Storage.Public do
special_changeset = prepare_endpoint_special(special_type, attrs)
%Storage.EndpointModel{}
+ |> Storage.EndpointModel.changeset(%{})
|> Storage.EndpointModel.change_special(special_type, special_changeset)
|> Changeset.put_assoc(:auth_node, Authorization.prepare_node())
end
@@ -117,26 +118,6 @@ defmodule Systems.Storage.Public do
connected?
end
- def status(%Storage.EndpointModel{} = endpoint) do
- status(Storage.EndpointModel.special(endpoint))
- end
-
- def status(%Storage.BuiltIn.EndpointModel{}), do: :online
- def status(%Storage.Centerdata.EndpointModel{}), do: :online
-
- def status(special) do
- sum =
- {special, :connected}
- |> Monitor.Public.event()
- |> Monitor.Public.sum()
-
- if sum <= 0 do
- :concept
- else
- :online
- end
- end
-
defp apply_on_special_backend(endpoint, function_name) when is_atom(function_name) do
special = Storage.EndpointModel.special(endpoint)
{_, backend} = Storage.Private.special_info(special)
diff --git a/core/systems/storage/_routes.ex b/core/systems/storage/_routes.ex
index 6370486cb..62de555f8 100644
--- a/core/systems/storage/_routes.ex
+++ b/core/systems/storage/_routes.ex
@@ -3,8 +3,8 @@ defmodule Systems.Storage.Routes do
quote do
scope "/", Systems.Storage do
pipe_through([:browser, :require_authenticated_user])
- live("/storage/:id/content", EndpointContentPage)
- get("/storage/:id/export", Controller, :export)
+ live("/storage/endpoint/:id/content", EndpointContentPage)
+ get("/storage/endpoint/:id/export", Controller, :export)
end
end
end
diff --git a/core/systems/storage/controller.ex b/core/systems/storage/controller.ex
index eedff60be..8166e0b71 100644
--- a/core/systems/storage/controller.ex
+++ b/core/systems/storage/controller.ex
@@ -2,21 +2,21 @@ defmodule Systems.Storage.Controller do
alias CoreWeb.UI.Timestamp
use CoreWeb, :controller
- alias Frameworks.Concept.Context
+ alias Frameworks.Concept
alias Systems.Storage
alias Systems.Rate
- def export(conn, %{"id" => id}) do
+ def export(%{assigns: %{branch: branch}} = conn, %{"id" => id}) do
if endpoint =
Storage.Public.get_endpoint!(
String.to_integer(id),
Storage.EndpointModel.preload_graph(:down)
) do
special = Storage.EndpointModel.special(endpoint)
- context_name = Context.name(:parent, endpoint, "export")
+ branch_name = Concept.Branch.name(branch, :parent)
- export(conn, special, context_name)
+ export(conn, special, branch_name)
else
service_unavailable(conn)
end
@@ -25,12 +25,12 @@ defmodule Systems.Storage.Controller do
def export(
%{remote_ip: remote_ip} = conn,
%Storage.BuiltIn.EndpointModel{} = builtin,
- context_name
+ branch_name
) do
date = Timestamp.now() |> Timestamp.format_date_short!()
export_name =
- [date, context_name]
+ [date, branch_name]
|> Enum.join(" ")
|> Slug.slugify(separator: ?_)
diff --git a/core/systems/storage/delivery.ex b/core/systems/storage/delivery.ex
index e2d36d795..8fdb77287 100644
--- a/core/systems/storage/delivery.ex
+++ b/core/systems/storage/delivery.ex
@@ -28,7 +28,9 @@ defmodule Systems.Storage.Delivery do
end
def deliver(backend, endpoint, data, meta_data) do
- Logger.notice("[Storage.Delivery] deliver", ansi_color: :light_magenta)
+ Logger.notice("[Storage.Delivery] deliver #{byte_size(data)} bytes",
+ ansi_color: :light_magenta
+ )
try do
backend.store(endpoint, data, meta_data)
diff --git a/core/systems/storage/empty_confirmation_view.ex b/core/systems/storage/empty_confirmation_view.ex
index 43586fea8..a0b1d570d 100644
--- a/core/systems/storage/empty_confirmation_view.ex
+++ b/core/systems/storage/empty_confirmation_view.ex
@@ -4,7 +4,7 @@ defmodule Systems.Storage.EmptyConfirmationView do
import Frameworks.Pixel.Form
@impl true
- def update(%{context_name: context_name}, socket) do
+ def update(%{branch_name: branch_name}, socket) do
loading = Map.get(socket.assigns, :loading, false)
submit_enabled = Map.get(socket.assigns, :submit_enabled, false)
confirm_name = Map.get(socket.assigns, :confirm_name, "")
@@ -13,7 +13,7 @@ defmodule Systems.Storage.EmptyConfirmationView do
:ok,
socket
|> assign(
- context_name: context_name,
+ branch_name: branch_name,
confirm_name: confirm_name,
loading: loading,
submit_enabled: submit_enabled
@@ -47,9 +47,9 @@ defmodule Systems.Storage.EmptyConfirmationView do
def handle_event(
"change",
%{"confirm_name" => confirm_name},
- %{assigns: %{context_name: context_name}} = socket
+ %{assigns: %{branch_name: branch_name}} = socket
) do
- submit_enabled = confirm_name == context_name
+ submit_enabled = confirm_name == branch_name
{:noreply, socket |> assign(confirm_name: confirm_name, submit_enabled: submit_enabled)}
end
@@ -76,7 +76,7 @@ defmodule Systems.Storage.EmptyConfirmationView do
<.form id={@id} for={@form} phx-change="change" phx-submit="submit" phx-target={@myself}>
<%= dgettext("eyra-storage", "empty_confirmation_view.title") %>
-
<%= raw(dgettext("eyra-storage", "empty_confirmation_view.body", name: @context_name)) %>
+
<%= raw(dgettext("eyra-storage", "empty_confirmation_view.body", name: @branch_name)) %>
<.text_input form={@form} field={:confirm_name} debounce="0" placeholder={dgettext("eyra-storage", "empty_confirmation_view.placeholder")} reserve_error_space={false} />
<.wrap>
diff --git a/core/systems/storage/endpoint_content_page.ex b/core/systems/storage/endpoint_content_page.ex
index fe6023208..d906d1b24 100644
--- a/core/systems/storage/endpoint_content_page.ex
+++ b/core/systems/storage/endpoint_content_page.ex
@@ -28,7 +28,6 @@ defmodule Systems.Storage.EndpointContentPage do
}
end
- @impl true
def handle_view_model_updated(%{assigns: %{vm: vm}} = socket) do
if tab = Enum.find(vm.tabs, &(&1.id == :data_view)) do
Fabric.send_event(tab.child.ref, %{name: "update_files", payload: %{}})
@@ -37,9 +36,6 @@ defmodule Systems.Storage.EndpointContentPage do
socket
end
- @impl true
- def handle_resize(socket), do: socket
-
@impl true
def handle_uri(socket), do: update_view_model(socket)
@@ -48,6 +44,7 @@ defmodule Systems.Storage.EndpointContentPage do
~H"""
<.management_page
title={@vm.title}
+ breadcrumbs={@vm.breadcrumbs}
tabs={@vm.tabs}
show_errors={@vm.show_errors}
actions={@actions}
diff --git a/core/systems/storage/endpoint_content_page_builder.ex b/core/systems/storage/endpoint_content_page_builder.ex
index 9a8664fc6..b0c6cc467 100644
--- a/core/systems/storage/endpoint_content_page_builder.ex
+++ b/core/systems/storage/endpoint_content_page_builder.ex
@@ -2,21 +2,23 @@ defmodule Systems.Storage.EndpointContentPageBuilder do
import CoreWeb.Gettext
import Frameworks.Utility.List
- alias Frameworks.Concept.Context
+ alias Frameworks.Concept
alias Systems.Storage
alias Systems.Monitor
def view_model(
%{id: id} = endpoint,
- assigns
+ %{branch: branch} = assigns
) do
show_errors = true
+ breadcrumbs = Concept.Branch.hierarchy(branch)
tabs = create_tabs(endpoint, show_errors, assigns)
- title = Context.name(:self, endpoint, "Data")
+ title = Concept.Branch.name(branch, :self)
%{
id: id,
title: title,
+ breadcrumbs: breadcrumbs,
tabs: tabs,
actions: [],
show_errors: show_errors,
@@ -70,15 +72,15 @@ defmodule Systems.Storage.EndpointContentPageBuilder do
:data,
endpoint,
show_errors,
- %{fabric: fabric, timezone: timezone} = _assigns
+ %{branch: branch, fabric: fabric, timezone: timezone} = _assigns
) do
ready? = true
- context_name = Context.name(:parent, endpoint, "Current")
+ branch_name = Concept.Branch.name(branch, :parent)
child =
Fabric.prepare_child(fabric, :data_view, Storage.EndpointDataView, %{
endpoint: endpoint,
- context_name: context_name,
+ branch_name: branch_name,
timezone: timezone
})
diff --git a/core/systems/storage/endpoint_data_view.ex b/core/systems/storage/endpoint_data_view.ex
index dc3d4f486..2e4766f9b 100644
--- a/core/systems/storage/endpoint_data_view.ex
+++ b/core/systems/storage/endpoint_data_view.ex
@@ -6,7 +6,7 @@ defmodule Systems.Storage.EndpointDataView do
@impl true
def update(
- %{endpoint: endpoint, context_name: context_name, timezone: timezone},
+ %{endpoint: endpoint, branch_name: branch_name, timezone: timezone},
%{assigns: %{}} = socket
) do
total_count = Map.get(socket.assigns, :total_count, nil)
@@ -19,7 +19,7 @@ defmodule Systems.Storage.EndpointDataView do
socket
|> assign(
endpoint: endpoint,
- context_name: context_name,
+ branch_name: branch_name,
timezone: timezone,
total_count: total_count,
visible_count: visible_count,
@@ -45,11 +45,11 @@ defmodule Systems.Storage.EndpointDataView do
}
end
- def compose(:empty_confirmation, %{context_name: context_name}) do
+ def compose(:empty_confirmation, %{branch_name: branch_name}) do
%{
module: Storage.EmptyConfirmationView,
params: %{
- context_name: context_name
+ branch_name: branch_name
}
}
end
@@ -74,7 +74,7 @@ defmodule Systems.Storage.EndpointDataView do
export_button = %{
action: %{
type: :http_download,
- to: ~p"/storage/#{id}/export"
+ to: ~p"/storage/endpoint/#{id}/export"
},
face: %{
type: :label,
diff --git a/core/systems/storage/endpoint_form.ex b/core/systems/storage/endpoint_form.ex
index c67116564..1bd471ce4 100644
--- a/core/systems/storage/endpoint_form.ex
+++ b/core/systems/storage/endpoint_form.ex
@@ -103,7 +103,10 @@ defmodule Systems.Storage.EndpointForm do
}
} = socket
) do
- changeset = Storage.EndpointModel.change_special(endpoint, special_type, special_changeset)
+ changeset =
+ endpoint
+ |> Storage.EndpointModel.changeset(%{})
+ |> Storage.EndpointModel.change_special(special_type, special_changeset)
socket
|> send_event(:parent, "update", %{changeset: changeset})
diff --git a/core/systems/storage/endpoint_model.ex b/core/systems/storage/endpoint_model.ex
index aacbaf3e9..fc405556a 100644
--- a/core/systems/storage/endpoint_model.ex
+++ b/core/systems/storage/endpoint_model.ex
@@ -1,16 +1,21 @@
defmodule Systems.Storage.EndpointModel do
+ @fields ~w()a
+ @required_fields @fields
+ @special_fields ~w(builtin yoda centerdata aws azure)a
+
use Ecto.Schema
- alias Frameworks.Utility.Assets
+
use Frameworks.Utility.Schema
+ use Frameworks.Concept.Special, @special_fields
import Ecto.Changeset
import CoreWeb.Gettext
alias Frameworks.Concept
+ alias Frameworks.Utility.Assets
- alias Systems.{
- Storage
- }
+ alias Systems.Storage
+ alias Systems.Monitor
require Storage.ServiceIds
@@ -26,10 +31,6 @@ defmodule Systems.Storage.EndpointModel do
timestamps()
end
- @fields ~w()a
- @required_fields @fields
- @special_fields ~w(builtin yoda centerdata aws azure)a
-
@spec changeset(
{map(), map()}
| %{
@@ -52,58 +53,6 @@ defmodule Systems.Storage.EndpointModel do
def auth_tree(%{auth_node: auth_node}), do: auth_node
- def tag(_) do
- dgettext("eyra-storage", "project.item.tag")
- end
-
- def change_special(endpoint, special_field, special) when is_atom(special_field) do
- specials =
- Enum.map(
- @special_fields,
- &{&1,
- if &1 == special_field do
- special
- else
- nil
- end}
- )
-
- changeset(endpoint, %{})
- |> then(
- &Enum.reduce(specials, &1, fn {field, value}, changeset ->
- put_assoc(changeset, field, value)
- end)
- )
- end
-
- def special(endpoint) do
- if field = special_field(endpoint) do
- Map.get(endpoint, field)
- else
- nil
- end
- end
-
- def special_field_id(endpoint) do
- if field = special_field(endpoint) do
- map_to_field_id(field)
- else
- nil
- end
- end
-
- def special_field(endpoint) do
- Enum.reduce(@special_fields, nil, fn field, acc ->
- field_id = map_to_field_id(field)
-
- if Map.get(endpoint, field_id) != nil do
- field
- else
- acc
- end
- end)
- end
-
def ready?(endpoint) do
if special = special(endpoint) do
Concept.ContentModel.ready?(special)
@@ -119,8 +68,6 @@ defmodule Systems.Storage.EndpointModel do
def asset_image_src(:builtin, type), do: Assets.image_src("next", type)
def asset_image_src(special, type), do: Assets.image_src("#{special}", type)
- defp map_to_field_id(field), do: String.to_existing_atom("#{field}_id")
-
defimpl Frameworks.GreenLight.AuthorizationNode do
def id(endpoint), do: endpoint.auth_node_id
end
@@ -130,4 +77,39 @@ defmodule Systems.Storage.EndpointModel do
def form(_), do: Storage.EndpointForm
def ready?(endpoint), do: Storage.EndpointModel.ready?(endpoint)
end
+
+ defimpl Frameworks.Concept.Leaf do
+ alias Frameworks.Concept
+
+ def resource_id(%{id: id}), do: "storage/endpoint/#{id}"
+ def tag(_), do: dgettext("eyra-storage", "atom.tag")
+
+ def info(storage_endpoint, _timezone) do
+ file_count =
+ Monitor.Public.event({storage_endpoint, :files})
+ |> Monitor.Public.sum()
+
+ [dngettext("eyra-storage", "1 file", "* files", file_count)]
+ end
+
+ def status(%Storage.EndpointModel{} = endpoint) do
+ status(Storage.EndpointModel.special(endpoint))
+ end
+
+ def status(%Storage.BuiltIn.EndpointModel{}), do: %Concept.Leaf.Status{value: :online}
+ def status(%Storage.Centerdata.EndpointModel{}), do: %Concept.Leaf.Status{value: :online}
+
+ def status(special) do
+ sum =
+ {special, :connected}
+ |> Monitor.Public.event()
+ |> Monitor.Public.sum()
+
+ if sum <= 0 do
+ %Concept.Leaf.Status{value: :concept}
+ else
+ %Concept.Leaf.Status{value: :online}
+ end
+ end
+ end
end
diff --git a/core/systems/support/helpdesk_page.ex b/core/systems/support/helpdesk_page.ex
index cafa8cea0..b84b74b46 100644
--- a/core/systems/support/helpdesk_page.ex
+++ b/core/systems/support/helpdesk_page.ex
@@ -6,6 +6,7 @@ defmodule Systems.Support.HelpdeskPage do
user
end
+ @impl true
def mount(_params, _session, socket) do
{:ok, socket |> compose_child(:helpdesk_form)}
end
@@ -18,12 +19,6 @@ defmodule Systems.Support.HelpdeskPage do
}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def render(assigns) do
~H"""
diff --git a/core/systems/support/overview_page.ex b/core/systems/support/overview_page.ex
index 3452594eb..36daaac01 100644
--- a/core/systems/support/overview_page.ex
+++ b/core/systems/support/overview_page.ex
@@ -17,12 +17,6 @@ defmodule Systems.Support.OverviewPage do
}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def render(assigns) do
~H"""
diff --git a/core/systems/support/ticket_page.ex b/core/systems/support/ticket_page.ex
index 738e53d36..1c901416d 100644
--- a/core/systems/support/ticket_page.ex
+++ b/core/systems/support/ticket_page.ex
@@ -12,6 +12,7 @@ defmodule Systems.Support.TicketPage do
Support.Public.get_ticket!(id)
end
+ @impl true
def mount(%{"id" => id}, _session, socket) do
{
:ok,
@@ -20,12 +21,6 @@ defmodule Systems.Support.TicketPage do
}
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
-
@impl true
def handle_event("close_ticket", _params, %{assigns: %{id: id}} = socket) do
Support.Public.close_ticket_by_id(id)
diff --git a/core/systems/test/page.ex b/core/systems/test/page.ex
index 8d9db4fb8..b410dfc59 100644
--- a/core/systems/test/page.ex
+++ b/core/systems/test/page.ex
@@ -2,7 +2,11 @@ defmodule Systems.Test.Page do
@moduledoc """
The page for testing the view model observations
"""
- use Systems.Content.Composer, :live_workspace
+ use CoreWeb, :live_view
+
+ on_mount({CoreWeb.Live.Hook.Base, __MODULE__})
+ on_mount({CoreWeb.Live.Hook.Model, __MODULE__})
+ on_mount({Systems.Observatory.LiveHook, __MODULE__})
alias Systems.Test
@@ -13,14 +17,19 @@ defmodule Systems.Test.Page do
@impl true
def mount(_params, _session, socket) do
- {:ok, socket |> assign(active_menu_item: nil)}
+ {
+ :ok,
+ socket
+ |> assign(
+ view_model_updated: 0,
+ active_menu_item: nil
+ )
+ }
end
- @impl true
- def handle_view_model_updated(socket), do: socket
-
- @impl true
- def handle_uri(socket), do: socket
+ def handle_view_model_updated(%{assigns: %{view_model_updated: view_model_updated}} = socket) do
+ socket |> assign(view_model_updated: "#{view_model_updated + 1}")
+ end
# data(model, :map)
@impl true
@@ -28,6 +37,7 @@ defmodule Systems.Test.Page do
~H"""
<%= @vm.title %>
<%= @vm.subtitle %>
+
view_model_updated: <%= @view_model_updated %>
"""
end
end
diff --git a/core/test/core_web/ui/timestamp_test.exs b/core/test/core_web/ui/timestamp_test.exs
index 44e3ad4b3..0c74262a5 100644
--- a/core/test/core_web/ui/timestamp_test.exs
+++ b/core/test/core_web/ui/timestamp_test.exs
@@ -110,6 +110,6 @@ defmodule CoreWeb.Ui.TimestampTest do
end
def apply_offset(hour, offset) do
- Integer.mod(hour + offset, 23)
+ Integer.mod(hour + offset, 24)
end
end
diff --git a/core/test/google_sign_in/plug_test.exs b/core/test/google_sign_in/plug_test.exs
index b214ace60..8a9c6c057 100644
--- a/core/test/google_sign_in/plug_test.exs
+++ b/core/test/google_sign_in/plug_test.exs
@@ -50,7 +50,7 @@ defmodule GoogleSignIn.AuthorizePlug.Test do
{:ok, domain: domain}
end
- test "redirects to SurfConext login page" do
+ test "redirects to Google login page" do
conn =
conn(:get, "/google")
|> init_test_session(%{})
@@ -118,22 +118,26 @@ defmodule GoogleSignIn.CallbackPlug.Test do
given_name = Faker.Person.first_name()
family_name = Faker.Person.last_name()
- GoogleSignIn.register_user(%{
- "sub" => GoogleSignIn.FakeGoogle.sub(),
- "name" => "#{given_name} #{family_name}",
- "email" => email,
- "email_verified" => true,
- "given_name" => given_name,
- "family_name" => family_name,
- "picture" => Faker.Internet.url(),
- "locale" => "en"
- })
+ GoogleSignIn.register_user(
+ %{
+ "sub" => GoogleSignIn.FakeGoogle.sub(),
+ "name" => "#{given_name} #{family_name}",
+ "email" => email,
+ "email_verified" => true,
+ "given_name" => given_name,
+ "family_name" => family_name,
+ "picture" => Faker.Internet.url(),
+ "locale" => "en"
+ },
+ true
+ )
user =
conn(:get, "/google")
|> init_test_session(%{})
|> CallbackPlug.call(:test)
+ assert user.creator == true
assert user.email == email
end
diff --git a/core/test/google_sign_in_test.exs b/core/test/google_sign_in_test.exs
index a6f68d80b..707fb5082 100644
--- a/core/test/google_sign_in_test.exs
+++ b/core/test/google_sign_in_test.exs
@@ -30,12 +30,13 @@ defmodule GoogleSignIn.Test do
"locale" => "en"
}
- {:ok, google_user} = GoogleSignIn.register_user(sso_info)
+ {:ok, google_user} = GoogleSignIn.register_user(sso_info, false)
for key <- Map.keys(sso_info) do
assert Map.get(google_user, String.to_atom(key)) == Map.get(sso_info, key)
end
+ assert google_user.user.creator == false
assert google_user.user.email == Map.get(sso_info, "email")
assert google_user.user.displayname == "#{given_name}"
assert google_user.user.profile.fullname == "#{given_name} #{family_name}"
diff --git a/core/test/systems/advert/content_page_test.exs b/core/test/systems/advert/content_page_test.exs
index 001c726f6..c2879e248 100644
--- a/core/test/systems/advert/content_page_test.exs
+++ b/core/test/systems/advert/content_page_test.exs
@@ -2,8 +2,10 @@ defmodule Systems.Advert.ContentPageTest do
use CoreWeb.ConnCase, async: true
import Phoenix.ConnTest
import Phoenix.LiveViewTest
+ import Phoenix.Component, only: [assign: 2]
import ExUnit.Assertions
+ import Mock
alias Systems.Advert
@@ -11,18 +13,27 @@ defmodule Systems.Advert.ContentPageTest do
setup [:login_as_creator]
test "Default", %{conn: %{assigns: %{current_user: researcher}} = conn} do
- %{id: id} = Advert.Factories.create_advert(researcher, :accepted, 1)
+ branch =
+ Promox.new()
+ |> Promox.stub(Frameworks.Concept.Branch, :hierarchy, fn _ -> [] end)
- {:ok, _view, html} = live(conn, ~p"/advert/#{id}/content")
+ with_mock Systems.Project.LiveHook,
+ on_mount: fn _live_view_module, _params, _session, socket ->
+ {:cont, socket |> assign(branch: branch)}
+ end do
+ %{id: id} = Advert.Factories.create_advert(researcher, :accepted, 1)
- assert html =~ "
"
- assert html =~ "Settings"
+ {:ok, _view, html} = live(conn, ~p"/advert/#{id}/content")
- assert html =~ "
"
- assert html =~ "Criteria"
+ assert html =~ "
"
+ assert html =~ "Settings"
- assert html =~ "
"
- assert html =~ "Monitor"
+ assert html =~ "
"
+ assert html =~ "Criteria"
+
+ assert html =~ "
"
+ assert html =~ "Monitor"
+ end
end
end
end
diff --git a/core/test/systems/observatory/view_model_observe_test.exs b/core/test/systems/observatory/view_model_observe_test.exs
index 669191568..4b485b4ab 100644
--- a/core/test/systems/observatory/view_model_observe_test.exs
+++ b/core/test/systems/observatory/view_model_observe_test.exs
@@ -4,9 +4,7 @@ defmodule Systems.Observatory.ViewModelObserveTest do
import Phoenix.LiveViewTest
import Core.AuthTestHelpers
- alias Systems.{
- Test
- }
+ alias Systems.Test
describe "View model roundtrip" do
setup [:login_as_member]
@@ -16,6 +14,7 @@ defmodule Systems.Observatory.ViewModelObserveTest do
assert html =~ "John Doe"
assert html =~ "Age: 56 - Works at: The Basement"
+ assert html =~ "view_model_updated: 0"
end
test "View model update", %{conn: conn} do
@@ -24,7 +23,10 @@ defmodule Systems.Observatory.ViewModelObserveTest do
model = Test.Public.get(1)
Test.Public.update(model, %{age: 57})
- assert render(view) =~ "Age: 57 - Works at: The Basement"
+ html = render(view)
+
+ assert html =~ "Age: 57 - Works at: The Basement"
+ assert html =~ "view_model_updated: 1"
end
end
end
diff --git a/core/test/systems/support/overview_page_test.exs b/core/test/systems/support/overview_page_test.exs
index 4b394585b..d99cbb195 100644
--- a/core/test/systems/support/overview_page_test.exs
+++ b/core/test/systems/support/overview_page_test.exs
@@ -7,9 +7,7 @@ defmodule Systems.Support.OverviewPageTest do
setup [:login_as_member]
test "deny access to non-admin", %{conn: conn} do
- assert_error_sent(403, fn ->
- live(conn, ~p"/support/ticket")
- end)
+ assert {:error, {:redirect, %{to: "/access_denied"}}} = live(conn, ~p"/support/ticket")
end
end
diff --git a/core/test/systems/support/ticket_page_test.exs b/core/test/systems/support/ticket_page_test.exs
index b3d427371..73ce53102 100644
--- a/core/test/systems/support/ticket_page_test.exs
+++ b/core/test/systems/support/ticket_page_test.exs
@@ -9,9 +9,8 @@ defmodule Systems.Support.TicketPageTest do
test "deny access to non-admin", %{conn: conn} do
ticket = Factories.insert!(:helpdesk_ticket)
- assert_error_sent(403, fn ->
- live(conn, ~p"/support/ticket/#{ticket.id}")
- end)
+ assert {:error, {:redirect, %{to: "/access_denied"}}} =
+ live(conn, ~p"/support/ticket/#{ticket.id}")
end
end
diff --git a/core/test/test_helper.exs b/core/test/test_helper.exs
index 43a493d6b..0345f6488 100644
--- a/core/test/test_helper.exs
+++ b/core/test/test_helper.exs
@@ -1,3 +1,7 @@
+require Promox
+
+Promox.defmock(for: Frameworks.Concept.Branch)
+
ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(Core.Repo, :manual)
diff --git a/setup b/setup
index 0633d5dfe..b6227136e 100755
--- a/setup
+++ b/setup
@@ -15,4 +15,6 @@ cd ..
asdf exec mix deps.get
+pre-commit install
+
echo "All done. Reload your shell to enable the new commands."
diff --git a/setup.sh b/setup.sh
index 0633d5dfe..b6227136e 100644
--- a/setup.sh
+++ b/setup.sh
@@ -15,4 +15,6 @@ cd ..
asdf exec mix deps.get
+pre-commit install
+
echo "All done. Reload your shell to enable the new commands."