diff --git a/lib/cadet/accounts/course_registration.ex b/lib/cadet/accounts/course_registration.ex index d2f7420e2..ca2aa6c47 100644 --- a/lib/cadet/accounts/course_registration.ex +++ b/lib/cadet/accounts/course_registration.ex @@ -10,6 +10,7 @@ defmodule Cadet.Accounts.CourseRegistration do schema "course_registrations" do field(:role, Role) field(:game_states, :map) + field(:agreed_to_research, :boolean) belongs_to(:group, Group) belongs_to(:user, User) @@ -19,7 +20,7 @@ defmodule Cadet.Accounts.CourseRegistration do end @required_fields ~w(user_id course_id role)a - @optional_fields ~w(game_states group_id)a + @optional_fields ~w(game_states group_id agreed_to_research)a def changeset(course_registration, params \\ %{}) do course_registration diff --git a/lib/cadet/accounts/course_registrations.ex b/lib/cadet/accounts/course_registrations.ex index feb7f621b..19bdc3bc2 100644 --- a/lib/cadet/accounts/course_registrations.ex +++ b/lib/cadet/accounts/course_registrations.ex @@ -183,4 +183,14 @@ defmodule Cadet.Accounts.CourseRegistrations do Repo.delete(course_reg) end end + + def update_research_agreement(course_reg, agreed_to_research) do + course_reg + |> CourseRegistration.changeset(%{agreed_to_research: agreed_to_research}) + |> Repo.update() + |> case do + result = {:ok, _} -> result + {:error, changeset} -> {:error, {:bad_request, full_error_messages(changeset)}} + end + end end diff --git a/lib/cadet_web/controllers/user_controller.ex b/lib/cadet_web/controllers/user_controller.ex index e5031b863..728e946ab 100644 --- a/lib/cadet_web/controllers/user_controller.ex +++ b/lib/cadet_web/controllers/user_controller.ex @@ -103,6 +103,20 @@ defmodule CadetWeb.UserController do end end + def update_research_agreement(conn, %{"agreedToResearch" => agreed_to_research}) do + course_reg = conn.assigns[:course_reg] + + case CourseRegistrations.update_research_agreement(course_reg, agreed_to_research) do + {:ok, %{}} -> + text(conn, "OK") + + {:error, {status, message}} -> + conn + |> put_status(status) + |> text(message) + end + end + swagger_path :index do get("/v2/user") @@ -151,6 +165,27 @@ defmodule CadetWeb.UserController do response(200, "OK") end + swagger_path :update_research_agreement do + put("/v2/courses/:course_id/user/research_agreement") + summary("Update the user's agreement to the anonymized collection of programs for research") + security([%{JWT: []}]) + consumes("application/json") + + parameters do + course_id(:path, :integer, "the user's course id", required: true) + + agreedToResearch( + :body, + :boolean, + "whether the user has agreed to participate in the research", + required: true + ) + end + + response(200, "OK") + response(400, "Bad Request") + end + def swagger_definitions do %{ IndexInfo: @@ -234,6 +269,11 @@ defmodule CadetWeb.UserController do Schema.ref(:UserGameStates), "States for user's game, including users' game progress, settings and collectibles.\n" ) + + agreed_to_research( + :boolean, + "Whether the user as agreed to participate in the collection of anonymized data for research purposes." + ) end end, CourseConfiguration: diff --git a/lib/cadet_web/router.ex b/lib/cadet_web/router.ex index 8383f371b..e4edd6977 100644 --- a/lib/cadet_web/router.ex +++ b/lib/cadet_web/router.ex @@ -91,6 +91,7 @@ defmodule CadetWeb.Router do get("/user", UserController, :get_course_reg) put("/user/game_states", UserController, :update_game_states) + put("/user/research_agreement", UserController, :update_research_agreement) get("/config", CoursesController, :index) end diff --git a/lib/cadet_web/views/user_view.ex b/lib/cadet_web/views/user_view.ex index 5308c2603..06cff4af0 100644 --- a/lib/cadet_web/views/user_view.ex +++ b/lib/cadet_web/views/user_view.ex @@ -83,7 +83,8 @@ defmodule CadetWeb.UserView do story: :story, playStory: :play_story? }), - gameStates: latest.game_states + gameStates: latest.game_states, + agreedToResearch: latest.agreed_to_research } end end diff --git a/priv/repo/migrations/20210707040448_add_research_agreement_toggle.exs b/priv/repo/migrations/20210707040448_add_research_agreement_toggle.exs new file mode 100644 index 000000000..02c38bbd2 --- /dev/null +++ b/priv/repo/migrations/20210707040448_add_research_agreement_toggle.exs @@ -0,0 +1,9 @@ +defmodule Cadet.Repo.Migrations.AddResearchAgreementToggle do + use Ecto.Migration + + def change do + alter table(:course_registrations) do + add(:agreed_to_research, :boolean, null: true) + end + end +end diff --git a/test/cadet/accounts/course_registration_test.exs b/test/cadet/accounts/course_registration_test.exs index 49e07b499..9cc22598a 100644 --- a/test/cadet/accounts/course_registration_test.exs +++ b/test/cadet/accounts/course_registration_test.exs @@ -43,7 +43,11 @@ defmodule Cadet.Accounts.CourseRegistrationTest do course2: course2 } do assert_changeset(%{user_id: user1.id, course_id: course2.id, role: :admin}, :valid) - assert_changeset(%{user_id: user2.id, course_id: course1.id, role: :student}, :valid) + + assert_changeset( + %{user_id: user2.id, course_id: course1.id, role: :student, agreed_to_research: true}, + :valid + ) # assert_changeset(%{user_id: user2.id, course_id: course2.id, role: :staff, group_id: group.id}, :valid) end @@ -328,4 +332,43 @@ defmodule Cadet.Accounts.CourseRegistrationTest do CourseRegistrations.delete_course_registration(10_000) end end + + describe "update_research_agreement" do + setup do + student1 = insert(:course_registration, %{role: :student}) + student2 = insert(:course_registration, %{role: :student, agreed_to_research: false}) + + {:ok, %{student1: student1, student2: student2}} + end + + test "succeeds when field is initially nil", %{student1: student1} do + assert is_nil( + CourseRegistration + |> where(id: ^student1.id) + |> Repo.one() + |> Map.fetch!(:agreed_to_research) + ) + + CourseRegistrations.update_research_agreement(student1, true) + + assert CourseRegistration + |> where(id: ^student1.id) + |> Repo.one() + |> Map.fetch!(:agreed_to_research) == true + end + + test "succeeds when field is initially not nil", %{student2: student2} do + assert CourseRegistration + |> where(id: ^student2.id) + |> Repo.one() + |> Map.fetch!(:agreed_to_research) == false + + CourseRegistrations.update_research_agreement(student2, true) + + assert CourseRegistration + |> where(id: ^student2.id) + |> Repo.one() + |> Map.fetch!(:agreed_to_research) == true + end + end end diff --git a/test/cadet_web/controllers/user_controller_test.exs b/test/cadet_web/controllers/user_controller_test.exs index 15603e316..99ae00200 100644 --- a/test/cadet_web/controllers/user_controller_test.exs +++ b/test/cadet_web/controllers/user_controller_test.exs @@ -88,7 +88,8 @@ defmodule CadetWeb.UserControllerTest do "xp" => 110, "maxXp" => question.max_xp, "gameStates" => %{}, - "story" => nil + "story" => nil, + "agreedToResearch" => nil }, "courseConfiguration" => %{ "enableAchievements" => true, @@ -213,7 +214,8 @@ defmodule CadetWeb.UserControllerTest do "xp" => 110, "maxXp" => question.max_xp, "gameStates" => %{}, - "story" => nil + "story" => nil, + "agreedToResearch" => nil }, "courseConfiguration" => %{ "enableAchievements" => true, @@ -292,5 +294,30 @@ defmodule CadetWeb.UserControllerTest do end end + describe "PUT /v2/courses/{course_id}/user/research_agreement" do + @tag authenticate: :student + test "success, updating research agreement", %{conn: conn} do + user = conn.assigns.current_user + course_id = conn.assigns.course_id + + params = %{ + "agreedToResearch" => true + } + + assert is_nil( + CourseRegistration + |> Repo.get_by(course_id: course_id, user_id: user.id) + |> Map.fetch!(:agreed_to_research) + ) + + conn + |> put(build_url(course_id) <> "/research_agreement", params) + |> response(200) + + updated_cr = Repo.get_by(CourseRegistration, course_id: course_id, user_id: user.id) + assert updated_cr.agreed_to_research == true + end + end + defp build_url(course_id), do: "/v2/courses/#{course_id}/user" end