Skip to content

Commit 40f0dc9

Browse files
committed
Add popular skills
1 parent 1637b40 commit 40f0dc9

File tree

5 files changed

+103
-8
lines changed

5 files changed

+103
-8
lines changed

lib/code_corps/model/skill.ex

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ defmodule CodeCorps.Skill do
44
@type t :: %__MODULE__{}
55

66
schema "skills" do
7-
field :title, :string
87
field :description, :string
98
field :original_row, :integer
10-
11-
has_many :role_skills, CodeCorps.RoleSkill
12-
has_many :roles, through: [:role_skills, :role]
9+
field :title, :string
1310

1411
has_many :project_skills, CodeCorps.ProjectSkill
1512
has_many :projects, through: [:project_skills, :project]
1613

14+
has_many :role_skills, CodeCorps.RoleSkill
15+
has_many :roles, through: [:role_skills, :role]
16+
1717
timestamps()
1818
end
1919

@@ -23,7 +23,7 @@ defmodule CodeCorps.Skill do
2323
@spec changeset(CodeCorps.Skill.t, map) :: Ecto.Changeset.t
2424
def changeset(struct, params \\ %{}) do
2525
struct
26-
|> cast(params, [:title, :description, :original_row])
26+
|> cast(params, [:description, :original_row, :title])
2727
|> validate_required([:title])
2828
|> unique_constraint(:title)
2929
end

lib/code_corps/skills/skills.ex

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule CodeCorps.Skills do
2+
@moduledoc ~S"""
3+
Work with skills.
4+
"""
5+
6+
alias CodeCorps.{
7+
Repo,
8+
Skill,
9+
UserSkill
10+
}
11+
12+
import Ecto.Query
13+
14+
@doc """
15+
Find the most popular skills, in order, with a limit.
16+
"""
17+
@spec popular(map) :: [Skill.t]
18+
def popular(params \\ %{})
19+
def popular(%{"limit" => limit}), do: limit |> Integer.parse() |> apply_limit()
20+
def popular(_), do: do_popular()
21+
22+
defp apply_limit({limit, _rem}) when limit <= 100, do: do_popular(limit)
23+
defp apply_limit(_), do: do_popular()
24+
25+
@spec do_popular(pos_integer) :: [Skill.t]
26+
def do_popular(limit \\ 10) do
27+
query =
28+
from s in Skill,
29+
join: us in UserSkill,
30+
on: s.id == us.skill_id,
31+
group_by: s.id,
32+
order_by: [desc: count(us.skill_id)],
33+
limit: ^limit
34+
35+
query
36+
|> Repo.all()
37+
end
38+
end

lib/code_corps_web/controllers/skill_controller.ex

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ defmodule CodeCorpsWeb.SkillController do
22
@moduledoc false
33
use CodeCorpsWeb, :controller
44

5-
alias CodeCorps.{Skill, User, Helpers.Query}
5+
alias CodeCorps.{Helpers.Query, Skill, Skills, User}
66

77
action_fallback CodeCorpsWeb.FallbackController
88
plug CodeCorpsWeb.Plug.DataToAttributes
@@ -22,7 +22,7 @@ defmodule CodeCorpsWeb.SkillController do
2222
end
2323
end
2424

25-
@spec create(Plug.Conn.t, map) :: Conn.t
25+
@spec create(Conn.t, map) :: Conn.t
2626
def create(%Conn{} = conn, %{} = params) do
2727
with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource,
2828
{:ok, :authorized} <- current_user |> Policy.authorize(:create, %Skill{}, params),
@@ -34,12 +34,17 @@ defmodule CodeCorpsWeb.SkillController do
3434
end
3535

3636
@spec load_skills(map) :: list(Skill.t)
37+
defp load_skills(%{"popular" => "true"} = params) do
38+
params
39+
|> Skills.popular()
40+
|> preload()
41+
end
3742
defp load_skills(%{} = params) do
3843
Skill
3944
|> Query.id_filter(params)
4045
|> Query.title_filter(params)
4146
|> Query.limit_filter(params)
42-
|> Repo.all
47+
|> Repo.all()
4348
end
4449

4550
@preloads [:role_skills]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule CodeCorps.AccountsTest do
2+
@moduledoc false
3+
4+
use CodeCorps.DbAccessCase
5+
6+
alias CodeCorps.Skills
7+
8+
describe "popular/1" do
9+
test "returns popular skills in order with a limit" do
10+
[least_popular, somewhat_popular, most_popular] = insert_list(3, :skill)
11+
insert_list(3, :user_skill, skill: most_popular)
12+
insert_list(2, :user_skill, skill: somewhat_popular)
13+
insert_list(1, :user_skill, skill: least_popular)
14+
15+
[first_result, last_result] = Skills.popular(%{"limit" => "2"})
16+
17+
assert first_result == most_popular
18+
assert last_result == somewhat_popular
19+
end
20+
21+
test "defaults limit to 10" do
22+
skills = insert_list(11, :skill)
23+
skills |> Enum.each(fn skill -> insert(:user_skill, skill: skill) end)
24+
25+
results = Skills.popular()
26+
27+
assert results |> Enum.count() == 10
28+
end
29+
30+
test "ignores non-number limits" do
31+
insert(:user_skill)
32+
33+
results = Skills.popular(%{"limit" => "apples"})
34+
35+
assert results |> Enum.count() == 1
36+
end
37+
end
38+
end

test/lib/code_corps_web/controllers/skill_controller_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ defmodule CodeCorpsWeb.SkillControllerTest do
5353
returned_skills_length = json["data"] |> length
5454
assert returned_skills_length == 5
5555
end
56+
57+
test "lists popular skills", %{conn: conn} do
58+
[skill_1, skill_2] = insert_pair(:skill)
59+
insert(:user_skill, skill: skill_1)
60+
insert_list(2, :user_skill, skill: skill_2)
61+
62+
params = %{"popular" => "true"}
63+
path = conn |> skill_path(:index, params)
64+
65+
conn
66+
|> get(path)
67+
|> json_response(200)
68+
|> assert_ids_from_response([skill_2.id, skill_1.id])
69+
end
5670
end
5771

5872
describe "show" do

0 commit comments

Comments
 (0)