From ff46b8d7790cfe47c4424d9d910e209a0fc65c21 Mon Sep 17 00:00:00 2001 From: Josh Smith Date: Sun, 5 Nov 2017 17:44:23 -0800 Subject: [PATCH 1/5] Optimize task list performance --- .../controllers/task_list_controller.ex | 2 +- lib/code_corps_web/views/task_in_list_view.ex | 21 +++++++++++++++++++ lib/code_corps_web/views/task_list_view.ex | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 lib/code_corps_web/views/task_in_list_view.ex diff --git a/lib/code_corps_web/controllers/task_list_controller.ex b/lib/code_corps_web/controllers/task_list_controller.ex index 6fa9682b9..ab85d36e2 100644 --- a/lib/code_corps_web/controllers/task_list_controller.ex +++ b/lib/code_corps_web/controllers/task_list_controller.ex @@ -28,7 +28,7 @@ defmodule CodeCorpsWeb.TaskListController do end end - @preloads [:tasks] + @preloads [tasks: [:github_issue, :github_pull_request, :task_skills, :user, :user_task]] def preload(data) do Repo.preload(data, @preloads) diff --git a/lib/code_corps_web/views/task_in_list_view.ex b/lib/code_corps_web/views/task_in_list_view.ex new file mode 100644 index 000000000..ac6f67ac1 --- /dev/null +++ b/lib/code_corps_web/views/task_in_list_view.ex @@ -0,0 +1,21 @@ +defmodule CodeCorpsWeb.TaskInListView do + @moduledoc false + use CodeCorpsWeb, :view + use JaSerializer.PhoenixView + + def type, do: "task" + + attributes [ + :archived, :created_at, :number, :order, :status, :title + ] + + has_one :github_issue, type: "github-issue", field: :github_issue_id + has_one :github_pull_request, serializer: CodeCorpsWeb.GithubPullRequestView, identifiers: :always + has_one :github_repo, type: "github-repo", field: :github_repo_id + has_one :project, type: "project", field: :project_id + has_one :task_list, type: "task-list", field: :task_list_id + has_one :user, type: "user", field: :user_id + has_one :user_task, serializer: CodeCorpsWeb.UserTaskView, include: true + + has_many :task_skills, serializer: CodeCorpsWeb.TaskSkillView, include: true +end diff --git a/lib/code_corps_web/views/task_list_view.ex b/lib/code_corps_web/views/task_list_view.ex index 63bb18cb6..b548eec92 100644 --- a/lib/code_corps_web/views/task_list_view.ex +++ b/lib/code_corps_web/views/task_list_view.ex @@ -7,5 +7,5 @@ defmodule CodeCorpsWeb.TaskListView do has_one :project, type: "project", field: :project_id - has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always + has_many :tasks, serializer: CodeCorpsWeb.TaskInListView, include: true end From 1ac6000dc5b9da599307024fd490d9080b70cfea Mon Sep 17 00:00:00 2001 From: Josh Smith Date: Sun, 5 Nov 2017 18:39:24 -0800 Subject: [PATCH 2/5] Experiment with even more optimizations --- .../controllers/project_controller.ex | 4 +- .../controllers/task_list_controller.ex | 2 +- lib/code_corps_web/views/project_user_view.ex | 2 +- lib/code_corps_web/views/project_view.ex | 4 +- lib/code_corps_web/views/task_view.ex | 4 +- lib/code_corps_web/views/user_slim_view.ex | 57 +++++++++++++++++++ lib/code_corps_web/views/user_task_view.ex | 2 +- 7 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 lib/code_corps_web/views/user_slim_view.ex diff --git a/lib/code_corps_web/controllers/project_controller.ex b/lib/code_corps_web/controllers/project_controller.ex index a506effe9..bfc979b7b 100644 --- a/lib/code_corps_web/controllers/project_controller.ex +++ b/lib/code_corps_web/controllers/project_controller.ex @@ -45,9 +45,9 @@ defmodule CodeCorpsWeb.ProjectController do end @preloads [ - :donation_goals, [organization: :stripe_connect_account], + :donation_goals, [organization: [:organization_github_app_installations, :projects, :slugged_route, :stripe_connect_account]], :project_categories, :project_github_repos, :project_skills, - :project_users, :stripe_connect_plan, :task_lists, :tasks + [project_users: :user], :stripe_connect_plan, :task_lists, :tasks ] def preload(data) do diff --git a/lib/code_corps_web/controllers/task_list_controller.ex b/lib/code_corps_web/controllers/task_list_controller.ex index ab85d36e2..836418500 100644 --- a/lib/code_corps_web/controllers/task_list_controller.ex +++ b/lib/code_corps_web/controllers/task_list_controller.ex @@ -28,7 +28,7 @@ defmodule CodeCorpsWeb.TaskListController do end end - @preloads [tasks: [:github_issue, :github_pull_request, :task_skills, :user, :user_task]] + @preloads [tasks: [:github_issue, :github_pull_request, :task_skills, :user, [user_task: :user]]] def preload(data) do Repo.preload(data, @preloads) diff --git a/lib/code_corps_web/views/project_user_view.ex b/lib/code_corps_web/views/project_user_view.ex index 6b5bd0f26..a36d713d7 100644 --- a/lib/code_corps_web/views/project_user_view.ex +++ b/lib/code_corps_web/views/project_user_view.ex @@ -6,5 +6,5 @@ defmodule CodeCorpsWeb.ProjectUserView do attributes [:role, :inserted_at, :updated_at] has_one :project, type: "project", field: :project_id - has_one :user, type: "user", field: :user_id + has_one :user, serializer: CodeCorpsWeb.UserSlimView, include: true end diff --git a/lib/code_corps_web/views/project_view.ex b/lib/code_corps_web/views/project_view.ex index 6ce94d29b..128eb3ec2 100644 --- a/lib/code_corps_web/views/project_view.ex +++ b/lib/code_corps_web/views/project_view.ex @@ -14,14 +14,14 @@ defmodule CodeCorpsWeb.ProjectView do :total_monthly_donated, :updated_at, :website ] - has_one :organization, type: "organization", field: :organization_id + has_one :organization, serializer: CodeCorpsWeb.OrganizationView, include: true has_one :stripe_connect_plan, serializer: CodeCorpsWeb.StripeConnectPlanView has_many :donation_goals, serializer: CodeCorpsWeb.DonationGoalView, identifiers: :always has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, identifiers: :always has_many :project_github_repos, serializer: CodeCorpsWeb.ProjectGithubRepoView, identifiers: :always has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, identifiers: :always - has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, identifiers: :always + has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, include: true has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always has_many :task_lists, serializer: CodeCorpsWeb.TaskListView, identifiers: :always diff --git a/lib/code_corps_web/views/task_view.ex b/lib/code_corps_web/views/task_view.ex index 9a2a9f517..d747c987d 100644 --- a/lib/code_corps_web/views/task_view.ex +++ b/lib/code_corps_web/views/task_view.ex @@ -13,8 +13,8 @@ defmodule CodeCorpsWeb.TaskView do has_one :github_repo, type: "github-repo", field: :github_repo_id has_one :project, type: "project", field: :project_id has_one :task_list, type: "task-list", field: :task_list_id - has_one :user, type: "user", field: :user_id - has_one :user_task, serializer: CodeCorpsWeb.UserTaskView, identifiers: :always + has_one :user, serializer: CodeCorpsWeb.UserSlimView, include: true + has_one :user_task, serializer: CodeCorpsWeb.UserTaskView, include: true has_many :comments, serializer: CodeCorpsWeb.CommentView, identifiers: :always has_many :task_skills, serializer: CodeCorpsWeb.TaskSkillView, identifiers: :always diff --git a/lib/code_corps_web/views/user_slim_view.ex b/lib/code_corps_web/views/user_slim_view.ex new file mode 100644 index 000000000..ec8c72de9 --- /dev/null +++ b/lib/code_corps_web/views/user_slim_view.ex @@ -0,0 +1,57 @@ +defmodule CodeCorpsWeb.UserSlimView do + @moduledoc false + alias CodeCorps.Presenters.ImagePresenter + + use CodeCorpsWeb, :view + use JaSerializer.PhoenixView + + def type, do: "user" + + attributes [ + :biography, :cloudinary_public_id, :email, :first_name, + :github_avatar_url, :github_id, :github_username, :intercom_user_hash, + :inserted_at, :last_name, :name, :photo_large_url, :photo_thumb_url, + :sign_up_context, :state, :state_transition, :twitter, :username, + :website, :updated_at + ] + + def photo_large_url(user, _conn), do: ImagePresenter.large(user) + + def photo_thumb_url(user, _conn), do: ImagePresenter.thumbnail(user) + + @doc """ + Returns the user email or an empty string, depending on the user + being rendered is the authenticated user, or some other user. + + Users can only see their own emails. Everyone else's are private. + """ + def email(user, %Plug.Conn{assigns: %{current_user: current_user}}) do + if user.id == current_user.id, do: user.email, else: "" + end + def email(_user, _conn), do: "" + + @intercom_secret_key Application.get_env(:code_corps, :intercom_identity_secret_key) || "RANDOM_KEY" + + def intercom_user_hash(%{id: id}, _conn) when is_number(id) do + id |> Integer.to_string |> do_intercom_user_hash + end + # def intercom_user_hash(_user, _conn), do: nil + + defp do_intercom_user_hash(id_string) do + :crypto.hmac(:sha256, @intercom_secret_key, id_string) + |> Base.encode16 + |> String.downcase + end + + @doc """ + Returns the user's full name when both first and last name are present. + Returns the only user's first name or last name when the other is missing, + otherwise returns nil. + """ + def name(%{first_name: first_name, last_name: last_name}, _conn) do + "#{first_name} #{last_name}" |> String.trim |> normalize_name + end + + defp normalize_name(name) when name in ["", nil], do: nil + defp normalize_name(name), do: name +end diff --git a/lib/code_corps_web/views/user_task_view.ex b/lib/code_corps_web/views/user_task_view.ex index 80b87723c..30ad5d7f8 100644 --- a/lib/code_corps_web/views/user_task_view.ex +++ b/lib/code_corps_web/views/user_task_view.ex @@ -4,5 +4,5 @@ defmodule CodeCorpsWeb.UserTaskView do use JaSerializer.PhoenixView has_one :task, type: "task", field: :task_id - has_one :user, type: "user", field: :user_id + has_one :user, serializer: CodeCorpsWeb.UserSlimView, include: true end From 9277c0e640986b5230a743655bd1d5f3d49aeae3 Mon Sep 17 00:00:00 2001 From: Josh Smith Date: Sun, 5 Nov 2017 18:46:01 -0800 Subject: [PATCH 3/5] Experiment with faster project loading --- lib/code_corps_web/controllers/project_controller.ex | 2 +- lib/code_corps_web/views/project_skill_view.ex | 2 +- lib/code_corps_web/views/project_view.ex | 4 ++-- lib/code_corps_web/views/skill_view.ex | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/code_corps_web/controllers/project_controller.ex b/lib/code_corps_web/controllers/project_controller.ex index bfc979b7b..5199f7fbe 100644 --- a/lib/code_corps_web/controllers/project_controller.ex +++ b/lib/code_corps_web/controllers/project_controller.ex @@ -46,7 +46,7 @@ defmodule CodeCorpsWeb.ProjectController do @preloads [ :donation_goals, [organization: [:organization_github_app_installations, :projects, :slugged_route, :stripe_connect_account]], - :project_categories, :project_github_repos, :project_skills, + :project_categories, :project_github_repos, [project_skills: :skill], [project_users: :user], :stripe_connect_plan, :task_lists, :tasks ] diff --git a/lib/code_corps_web/views/project_skill_view.ex b/lib/code_corps_web/views/project_skill_view.ex index 5621d929b..2db46b2fb 100644 --- a/lib/code_corps_web/views/project_skill_view.ex +++ b/lib/code_corps_web/views/project_skill_view.ex @@ -4,5 +4,5 @@ defmodule CodeCorpsWeb.ProjectSkillView do use JaSerializer.PhoenixView has_one :project, type: "project", field: :project_id - has_one :skill, type: "skill", field: :skill_id + has_one :skill, serializer: CodeCorpsWeb.SkillView, include: true end diff --git a/lib/code_corps_web/views/project_view.ex b/lib/code_corps_web/views/project_view.ex index 128eb3ec2..2ee80294e 100644 --- a/lib/code_corps_web/views/project_view.ex +++ b/lib/code_corps_web/views/project_view.ex @@ -18,9 +18,9 @@ defmodule CodeCorpsWeb.ProjectView do has_one :stripe_connect_plan, serializer: CodeCorpsWeb.StripeConnectPlanView has_many :donation_goals, serializer: CodeCorpsWeb.DonationGoalView, identifiers: :always - has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, identifiers: :always + has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, include: true has_many :project_github_repos, serializer: CodeCorpsWeb.ProjectGithubRepoView, identifiers: :always - has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, identifiers: :always + has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, include: true has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, include: true has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always has_many :task_lists, serializer: CodeCorpsWeb.TaskListView, identifiers: :always diff --git a/lib/code_corps_web/views/skill_view.ex b/lib/code_corps_web/views/skill_view.ex index 2ce0ebb2c..47b1e38c1 100644 --- a/lib/code_corps_web/views/skill_view.ex +++ b/lib/code_corps_web/views/skill_view.ex @@ -4,6 +4,4 @@ defmodule CodeCorpsWeb.SkillView do use JaSerializer.PhoenixView attributes [:title, :description, :inserted_at, :updated_at] - - has_many :role_skills, serializer: CodeCorpsWeb.RoleSkillView, identifiers: :always end From 26ea258b165888b2dea9b03c9db4382e36f45261 Mon Sep 17 00:00:00 2001 From: Josh Smith Date: Sun, 5 Nov 2017 20:08:25 -0800 Subject: [PATCH 4/5] Further testing --- lib/code_corps_web/views/project_view.ex | 1 - lib/code_corps_web/views/task_in_list_view.ex | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/code_corps_web/views/project_view.ex b/lib/code_corps_web/views/project_view.ex index 2ee80294e..009491d1c 100644 --- a/lib/code_corps_web/views/project_view.ex +++ b/lib/code_corps_web/views/project_view.ex @@ -22,7 +22,6 @@ defmodule CodeCorpsWeb.ProjectView do has_many :project_github_repos, serializer: CodeCorpsWeb.ProjectGithubRepoView, identifiers: :always has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, include: true has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, include: true - has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always has_many :task_lists, serializer: CodeCorpsWeb.TaskListView, identifiers: :always def can_activate_donations(project, _conn) do diff --git a/lib/code_corps_web/views/task_in_list_view.ex b/lib/code_corps_web/views/task_in_list_view.ex index ac6f67ac1..5b5f5a293 100644 --- a/lib/code_corps_web/views/task_in_list_view.ex +++ b/lib/code_corps_web/views/task_in_list_view.ex @@ -9,12 +9,12 @@ defmodule CodeCorpsWeb.TaskInListView do :archived, :created_at, :number, :order, :status, :title ] - has_one :github_issue, type: "github-issue", field: :github_issue_id - has_one :github_pull_request, serializer: CodeCorpsWeb.GithubPullRequestView, identifiers: :always + has_one :github_issue, serializer: CodeCorpsWeb.GithubIssueView, include: true + has_one :github_pull_request, serializer: CodeCorpsWeb.GithubPullRequestView, include: true has_one :github_repo, type: "github-repo", field: :github_repo_id has_one :project, type: "project", field: :project_id has_one :task_list, type: "task-list", field: :task_list_id - has_one :user, type: "user", field: :user_id + has_one :user, serializer: CodeCorpsWeb.UserSlimView, include: true has_one :user_task, serializer: CodeCorpsWeb.UserTaskView, include: true has_many :task_skills, serializer: CodeCorpsWeb.TaskSkillView, include: true From 9c69e3a88ac7304e50366e075a735bdd8002213f Mon Sep 17 00:00:00 2001 From: Josh Smith Date: Sun, 5 Nov 2017 21:24:48 -0800 Subject: [PATCH 5/5] Load task lists progressively --- .../controllers/project_skill_controller.ex | 17 +++++++++++++---- .../controllers/task_controller.ex | 2 +- lib/code_corps_web/views/project_view.ex | 9 +++++---- lib/code_corps_web/views/task_list_view.ex | 2 +- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/code_corps_web/controllers/project_skill_controller.ex b/lib/code_corps_web/controllers/project_skill_controller.ex index cd0d45bf8..d02cdfbdc 100644 --- a/lib/code_corps_web/controllers/project_skill_controller.ex +++ b/lib/code_corps_web/controllers/project_skill_controller.ex @@ -10,14 +10,14 @@ defmodule CodeCorpsWeb.ProjectSkillController do @spec index(Conn.t, map) :: Conn.t def index(%Conn{} = conn, %{} = params) do - with project_skills <- ProjectSkill |> Query.id_filter(params) |> Repo.all do + with project_skills <- ProjectSkill |> Query.id_filter(params) |> Repo.all |> preload() do conn |> render("index.json-api", data: project_skills) end end @spec show(Conn.t, map) :: Conn.t def show(%Conn{} = conn, %{"id" => id}) do - with %ProjectSkill{} = project_skill <- ProjectSkill |> Repo.get(id) do + with %ProjectSkill{} = project_skill <- ProjectSkill |> Repo.get(id) |> preload() do conn |> render("show.json-api", data: project_skill) end end @@ -26,8 +26,9 @@ defmodule CodeCorpsWeb.ProjectSkillController do def create(%Conn{} = conn, %{} = params) do with %User{} = current_user <- conn |> Guardian.Plug.current_resource, {:ok, :authorized} <- current_user |> Policy.authorize(:create, %ProjectSkill{}, params), - {:ok, %ProjectSkill{} = project_skill} <- %ProjectSkill{} |> ProjectSkill.create_changeset(params) |> Repo.insert do - + {:ok, %ProjectSkill{} = project_skill} <- %ProjectSkill{} |> ProjectSkill.create_changeset(params) |> Repo.insert(), + project_skill <- preload(project_skill) + do current_user.id |> SegmentTracker.track("Added Project Skill", project_skill) conn |> put_status(:created) |> render("show.json-api", data: project_skill) end @@ -44,4 +45,12 @@ defmodule CodeCorpsWeb.ProjectSkillController do conn |> Conn.assign(:project_skill, project_skill) |> send_resp(:no_content, "") end end + + @preloads [ + :skill + ] + + def preload(data) do + Repo.preload(data, @preloads) + end end diff --git a/lib/code_corps_web/controllers/task_controller.ex b/lib/code_corps_web/controllers/task_controller.ex index e5954bbb2..d7f2712cc 100644 --- a/lib/code_corps_web/controllers/task_controller.ex +++ b/lib/code_corps_web/controllers/task_controller.ex @@ -62,7 +62,7 @@ defmodule CodeCorpsWeb.TaskController do end end - @preloads [:comments, :github_pull_request, :task_skills, :user_task] + @preloads [:comments, :github_pull_request, :task_skills, :user, [user_task: :user]] def preload(data) do timing("TaskController", "preload") do diff --git a/lib/code_corps_web/views/project_view.ex b/lib/code_corps_web/views/project_view.ex index 009491d1c..6ce94d29b 100644 --- a/lib/code_corps_web/views/project_view.ex +++ b/lib/code_corps_web/views/project_view.ex @@ -14,14 +14,15 @@ defmodule CodeCorpsWeb.ProjectView do :total_monthly_donated, :updated_at, :website ] - has_one :organization, serializer: CodeCorpsWeb.OrganizationView, include: true + has_one :organization, type: "organization", field: :organization_id has_one :stripe_connect_plan, serializer: CodeCorpsWeb.StripeConnectPlanView has_many :donation_goals, serializer: CodeCorpsWeb.DonationGoalView, identifiers: :always - has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, include: true + has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, identifiers: :always has_many :project_github_repos, serializer: CodeCorpsWeb.ProjectGithubRepoView, identifiers: :always - has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, include: true - has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, include: true + has_many :project_skills, serializer: CodeCorpsWeb.ProjectSkillView, identifiers: :always + has_many :project_users, serializer: CodeCorpsWeb.ProjectUserView, identifiers: :always + has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always has_many :task_lists, serializer: CodeCorpsWeb.TaskListView, identifiers: :always def can_activate_donations(project, _conn) do diff --git a/lib/code_corps_web/views/task_list_view.ex b/lib/code_corps_web/views/task_list_view.ex index b548eec92..63bb18cb6 100644 --- a/lib/code_corps_web/views/task_list_view.ex +++ b/lib/code_corps_web/views/task_list_view.ex @@ -7,5 +7,5 @@ defmodule CodeCorpsWeb.TaskListView do has_one :project, type: "project", field: :project_id - has_many :tasks, serializer: CodeCorpsWeb.TaskInListView, include: true + has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always end