Skip to content

Added Exam mode #1236

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

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fc72d29
added enable_exam_mode field to all tests, models, and controllers
iZUMi-kyouka Feb 14, 2025
2fc38ca
force student whose courses has exam mode to only access the particul…
iZUMi-kyouka Feb 15, 2025
9953b9c
fixed failure to switch course even when no exam mode is enabled for …
iZUMi-kyouka Feb 15, 2025
f450e85
fixed unintended changes to previous migration; added migration file …
iZUMi-kyouka Mar 3, 2025
0bb9857
added is_official_course field to models; added logic to prevent stud…
iZUMi-kyouka Mar 3, 2025
d7ad8b3
removed unnecessary changes in course_registrations.ex
iZUMi-kyouka Mar 4, 2025
f216d1b
removed unnecessary changes from user_controller.ex
iZUMi-kyouka Mar 4, 2025
48fff28
sync fork
iZUMi-kyouka Mar 4, 2025
b5f2722
Merge branch 'master' into exam_mode
RichDom2185 Mar 6, 2025
bc4c48c
added resume code functionality
iZUMi-kyouka Mar 11, 2025
46c2ff0
Merge remote-tracking branch 'refs/remotes/origin/exam_mode' into exa…
iZUMi-kyouka Mar 11, 2025
fe80662
added resume code checking endpoint and its handler
iZUMi-kyouka Mar 12, 2025
f3c9ea9
minor change to resume_code handler
iZUMi-kyouka Mar 12, 2025
665ad2a
restore accidental deletion of function
iZUMi-kyouka Mar 13, 2025
abb1014
renamed resume_code to check_resume_code
iZUMi-kyouka Mar 13, 2025
82afebf
added validation for enabling exam mode and setting resume code; dele…
iZUMi-kyouka Mar 13, 2025
1db4420
added is_paused column and setting functionality to mitigate students…
iZUMi-kyouka Mar 18, 2025
d88eeba
added migrations for is_paused_column
iZUMi-kyouka Mar 18, 2025
d9a97e3
Merge branch 'master' into exam_mode
GabrielCWT Mar 31, 2025
40ea386
Remove unused tree
RichDom2185 Mar 31, 2025
7ae6f14
Fix format
RichDom2185 Mar 31, 2025
e62fcef
Merge branch 'master' into exam_mode
RichDom2185 Mar 31, 2025
e0330f2
Redate migrations to maintain total ordering
RichDom2185 Mar 31, 2025
d971fcd
fixed failing tests due to missing fields in factory method, and expe…
iZUMi-kyouka Apr 1, 2025
40ce982
Merge branch 'exam_mode_user_paused_flag' into exam_mode
iZUMi-kyouka Apr 1, 2025
0302764
makes latest course retrieval logic more concise
iZUMi-kyouka Apr 1, 2025
7fcbc89
ran mix format on courses and user controller
iZUMi-kyouka Apr 1, 2025
cad39c8
added new endpoint to report lost/regain of user focus
iZUMi-kyouka Apr 2, 2025
46af8d7
removed filters for supplying exam_mode_course to renderer function
iZUMi-kyouka Apr 3, 2025
6531376
renamed check_resume_code to try_unpause_user; split up the logic int…
iZUMi-kyouka Apr 3, 2025
3dcc288
removed unnecessary admin config renderer in user_view and admin_user…
iZUMi-kyouka Apr 3, 2025
31bbf5a
excludes staff and admins from exam_mode restriction on courses retur…
iZUMi-kyouka Apr 3, 2025
48c1b10
excludes staff and admins from exam_mode restriction on courses retur…
iZUMi-kyouka Apr 3, 2025
e0758fb
Revert "excludes staff and admins from exam_mode restriction on cours…
iZUMi-kyouka Apr 3, 2025
9154177
Merge branch 'exam_mode' into exam_mode_user_focus_logging
iZUMi-kyouka Apr 4, 2025
7bd79fd
created new user_browser_focus_log; and created its type definition, …
iZUMi-kyouka Apr 4, 2025
ed16cb4
removed redundant logic from focus logging controller
iZUMi-kyouka Apr 4, 2025
22a5704
sets a default resume_code in migration; add random resume code gener…
iZUMi-kyouka Apr 5, 2025
6cba5f5
fixed resume code validation to consider whitespace; formatting
iZUMi-kyouka Apr 5, 2025
f6ab358
added default resume_code value to schema definition in course.ex; ma…
iZUMi-kyouka Apr 8, 2025
b99a82d
improved swagger help text for resume_code field; fix formatting
iZUMi-kyouka Apr 8, 2025
604b61b
fixed bug in resume_code validation; added tests for exam_mode, is_of…
iZUMi-kyouka Apr 8, 2025
ca082c9
formatting
iZUMi-kyouka Apr 9, 2025
398bee0
formatting
iZUMi-kyouka Apr 9, 2025
1c3d787
add moduledoc for focus log
iZUMi-kyouka Apr 9, 2025
434d21c
Merge branch 'master' into exam_mode
RichDom2185 Jun 13, 2025
58906d7
Merge branch 'master' into exam_mode
iZUMi-kyouka Jun 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/cadet/accounts/course_registrations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ defmodule Cadet.Accounts.CourseRegistrations do
|> Repo.all()
end

def get_exam_mode_course(%User{id: id}) do
CourseRegistration
|> where([cr], cr.user_id == ^id)
|> join(:inner, [cr], c in assoc(cr, :course), on: c.enable_exam_mode == true and c.is_official_course == true)
|> join(:left, [cr, c], ac in assoc(c, :assessment_config))
|> preload([cr, c, ac],
course: {c, assessment_config: ^from(ac in AssessmentConfig, order_by: [asc: ac.order])}
)
|> preload(:group)
|> Repo.one()
end

def get_admin_courses_count(%User{id: id}) do
CourseRegistration
|> where(user_id: ^id)
Expand Down
6 changes: 5 additions & 1 deletion lib/cadet/courses/course.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ defmodule Cadet.Courses.Course do
enable_achievements: boolean(),
enable_sourcecast: boolean(),
enable_stories: boolean(),
enable_exam_mode: boolean(),
is_official_course: boolean(),
source_chapter: integer(),
source_variant: String.t(),
module_help_text: String.t(),
Expand All @@ -28,6 +30,8 @@ defmodule Cadet.Courses.Course do
field(:enable_achievements, :boolean, default: true)
field(:enable_sourcecast, :boolean, default: true)
field(:enable_stories, :boolean, default: false)
field(:enable_exam_mode, :boolean, default: false)
field(:is_official_course, :boolean, default: false)
field(:source_chapter, :integer)
field(:source_variant, :string)
field(:module_help_text, :string)
Expand All @@ -41,7 +45,7 @@ defmodule Cadet.Courses.Course do
end

@required_fields ~w(course_name viewable enable_game
enable_achievements enable_sourcecast enable_stories source_chapter source_variant)a
enable_exam_mode enable_achievements enable_sourcecast enable_stories source_chapter source_variant)a
@optional_fields ~w(course_short_name module_help_text)a

def changeset(course, params) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ defmodule CadetWeb.AdminCoursesController do
enable_achievements(:body, :boolean, "Enable achievements")
enable_sourcecast(:body, :boolean, "Enable sourcecast")
enable_stories(:body, :boolean, "Enable stories")
enable_exam_mode(:body, :boolean, "Enable exam mode")
sublanguage(:body, Schema.ref(:AdminSublanguage), "sublanguage object")
module_help_text(:body, :string, "Module help text")
end
Expand Down
2 changes: 2 additions & 0 deletions lib/cadet_web/controllers/courses_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ defmodule CadetWeb.CoursesController do
enable_achievements(:body, :boolean, "Enable achievements", required: true)
enable_sourcecast(:body, :boolean, "Enable sourcecast", required: true)
enable_stories(:body, :boolean, "Enable stories", required: true)
enable_exam_mode(:body, :boolean, "Enable exam mode", required: true)
source_chapter(:body, :number, "Default source chapter", required: true)

source_variant(:body, Schema.ref(:SourceVariant), "Default source variant name",
Expand Down Expand Up @@ -97,6 +98,7 @@ defmodule CadetWeb.CoursesController do
enable_achievements(:boolean, "Enable achievements", required: true)
enable_sourcecast(:boolean, "Enable sourcecast", required: true)
enable_stories(:boolean, "Enable stories", required: true)
enable_exam_mode(:boolean, "Enable exam mode", required: true)
source_chapter(:integer, "Source Chapter number from 1 to 4", required: true)
source_variant(Schema.ref(:SourceVariant), "Source Variant name", required: true)
module_help_text(:string, "Module help text", required: true)
Expand Down
94 changes: 62 additions & 32 deletions lib/cadet_web/controllers/user_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,71 @@ defmodule CadetWeb.UserController do
def index(conn, _) do
user = conn.assigns.current_user
courses = CourseRegistrations.get_courses(conn.assigns.current_user)

if user.latest_viewed_course_id do
latest = CourseRegistrations.get_user_course(user.id, user.latest_viewed_course_id)
xp = Assessments.assessments_total_xp(latest)
max_xp = Assessments.user_max_xp(latest)
story = Assessments.user_current_story(latest)

render(
conn,
"index.json",
user: user,
courses: courses,
latest: latest,
max_xp: max_xp,
story: story,
xp: xp
)
else
render(conn, "index.json",
user: user,
courses: courses,
latest: nil,
max_xp: nil,
story: nil,
xp: nil
)
exam_mode_course = CourseRegistrations.get_exam_mode_course(conn.assigns.current_user)

cond do
exam_mode_course ->
IO.puts("Course #{exam_mode_course.course_id} is under exam mode.")
xp = Assessments.assessments_total_xp(exam_mode_course)
max_xp = Assessments.user_max_xp(exam_mode_course)
story = Assessments.user_current_story(exam_mode_course)

render(
conn,
"index.json",
user: user,
courses: courses |> Enum.filter(fn c -> c.course_id == exam_mode_course.course_id end),
latest: exam_mode_course,
max_xp: max_xp,
story: story,
xp: xp
)

user.latest_viewed_course_id ->
latest = CourseRegistrations.get_user_course(user.id, user.latest_viewed_course_id)
xp = Assessments.assessments_total_xp(latest)
max_xp = Assessments.user_max_xp(latest)
story = Assessments.user_current_story(latest)

render(
conn,
"index.json",
user: user,
courses: courses,
latest: latest,
max_xp: max_xp,
story: story,
xp: xp
)

true ->
render(conn, "index.json",
user: user,
courses: courses,
latest: nil,
max_xp: nil,
story: nil,
xp: nil
)
end
end

def get_latest_viewed(conn, _) do
user = conn.assigns.current_user

latest =
case user.latest_viewed_course_id do
nil -> nil
_ -> CourseRegistrations.get_user_course(user.id, user.latest_viewed_course_id)
exam_mode_course = CourseRegistrations.get_exam_mode_course(conn.assigns.current_user)
if exam_mode_course do
latest = CourseRegistrations.get_user_course(user.id, exam_mode_course.course_id)
get_course_reg_config(conn, latest)
else
latest =
case user.latest_viewed_course_id do
nil -> nil
_ -> CourseRegistrations.get_user_course(user.id, user.latest_viewed_course_id)
end

get_course_reg_config(conn, latest)
get_course_reg_config(conn, latest)
end

end

defp get_course_reg_config(conn, course_reg) when is_nil(course_reg) do
Expand Down Expand Up @@ -317,6 +343,8 @@ defmodule CadetWeb.UserController do
enable_achievements(:boolean, "Enable achievements", required: true)
enable_sourcecast(:boolean, "Enable sourcecast", required: true)
enable_stories(:boolean, "Enable stories", required: true)
enable_exam_mode(:boolean, "Enable exam mode", required: true)
is_official_course(:boolean, "Course status (official institution course)")
source_chapter(:integer, "Source Chapter number from 1 to 4", required: true)
source_variant(Schema.ref(:SourceVariant), "Source Variant name", required: true)
module_help_text(:string, "Module help text", required: true)
Expand All @@ -332,6 +360,8 @@ defmodule CadetWeb.UserController do
enable_achievements: true,
enable_sourcecast: true,
enable_stories: false,
enable_exam_mode: false,
is_official_course: true,
source_chapter: 1,
source_variant: "default",
module_help_text: "Help text",
Expand Down
2 changes: 2 additions & 0 deletions lib/cadet_web/views/courses_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ defmodule CadetWeb.CoursesView do
enableAchievements: :enable_achievements,
enableSourcecast: :enable_sourcecast,
enableStories: :enable_stories,
enableExamMode: :enable_exam_mode,
isOfficialCourse: :is_official_course,
sourceChapter: :source_chapter,
sourceVariant: :source_variant,
moduleHelpText: :module_help_text,
Expand Down
2 changes: 2 additions & 0 deletions lib/cadet_web/views/user_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ defmodule CadetWeb.UserView do
enableAchievements: :enable_achievements,
enableSourcecast: :enable_sourcecast,
enableStories: :enable_stories,
enableExamMode: :enable_exam_mode,
isOfficialCourse: :is_official_course,
sourceChapter: :source_chapter,
sourceVariant: :source_variant,
moduleHelpText: :module_help_text,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Cadet.Repo.Migrations.AlterCoursesTableAddEnableExamMode do
use Ecto.Migration

def change do
alter table(:courses) do
add(:enable_exam_mode, :boolean, null: false, default: false)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Cadet.Repo.Migrations.AlterCoursesTableAddIsOfficialCourse do
use Ecto.Migration

def change do
alter table(:courses) do
add(:is_official_course, :boolean, null: false, default: false)
end
end
end
14 changes: 14 additions & 0 deletions test/cadet/courses/courses_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ defmodule Cadet.CoursesTest do
enable_achievements: true,
enable_sourcecast: true,
enable_stories: false,
enable_exam_mode: false,
is_official_course: true,
source_chapter: 1,
source_variant: "default",
module_help_text: "Help Text"
Expand Down Expand Up @@ -57,6 +59,8 @@ defmodule Cadet.CoursesTest do
assert course.enable_achievements == true
assert course.enable_sourcecast == true
assert course.enable_stories == false
assert course.enable_exam_mode == false
assert course.is_official_course == true
assert course.source_chapter == 1
assert course.source_variant == "default"
assert course.module_help_text == "Help Text"
Expand Down Expand Up @@ -84,6 +88,8 @@ defmodule Cadet.CoursesTest do
enable_achievements: false,
enable_sourcecast: false,
enable_stories: true,
enable_exam_mode: true,
is_official_course: true,
module_help_text: ""
})

Expand All @@ -94,6 +100,8 @@ defmodule Cadet.CoursesTest do
assert updated_course.enable_achievements == false
assert updated_course.enable_sourcecast == false
assert updated_course.enable_stories == true
assert updated_course.enable_exam_mode == true
assert updated_course.is_official_course == true
assert updated_course.source_chapter == 1
assert updated_course.source_variant == "default"
assert updated_course.module_help_text == nil
Expand All @@ -112,6 +120,8 @@ defmodule Cadet.CoursesTest do
enable_achievements: false,
enable_sourcecast: false,
enable_stories: true,
enable_exam_mode: false,
is_official_course: true,
source_chapter: new_chapter,
source_variant: "default",
module_help_text: "help"
Expand All @@ -124,6 +134,8 @@ defmodule Cadet.CoursesTest do
assert updated_course.enable_achievements == false
assert updated_course.enable_sourcecast == false
assert updated_course.enable_stories == true
assert updated_course.enable_exam_mode == false
assert updated_course.is_official_course == true
assert updated_course.source_chapter == new_chapter
assert updated_course.source_variant == "default"
assert updated_course.module_help_text == "help"
Expand All @@ -142,6 +154,8 @@ defmodule Cadet.CoursesTest do
enable_achievements: false,
enable_sourcecast: false,
enable_stories: false,
enable_exam_mode: false,
is_official_course: true,
module_help_text: "help"
})

Expand Down
8 changes: 8 additions & 0 deletions test/cadet_web/controllers/courses_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ defmodule CadetWeb.CoursesControllerTest do
"enable_achievements" => "true",
"enable_sourcecast" => "true",
"enable_stories" => "true",
"enable_exam_mode" => "false",
"is_official_course" => "true",
"source_chapter" => "1",
"source_variant" => "default",
"module_help_text" => "Help Text"
Expand Down Expand Up @@ -73,6 +75,8 @@ defmodule CadetWeb.CoursesControllerTest do
"enable_achievements" => "true",
"enable_sourcecast" => "true",
"enable_stories" => "true",
"enable_exam_mode" => "false",
"is_official_course" => "true",
"source_chapter" => "1",
"source_variant" => "default",
"module_help_text" => "Help Text"
Expand All @@ -96,6 +100,8 @@ defmodule CadetWeb.CoursesControllerTest do
"enable_achievements" => "true",
"enable_sourcecast" => "true",
"enable_stories" => "true",
"enable_exam_mode" => "false",
"is_official_course" => "true",
"source_chapter" => "1",
"source_variant" => "default",
"module_help_text" => "Help Text"
Expand All @@ -120,6 +126,8 @@ defmodule CadetWeb.CoursesControllerTest do
"enable_achievements" => "true",
"enable_sourcecast" => "true",
"enable_stories" => "true",
"enable_exam_mode" => "false",
"is_official_course" => "true",
"source_chapter" => "1",
"source_variant" => "default",
"module_help_text" => "Help Text"
Expand Down
2 changes: 2 additions & 0 deletions test/factories/courses/course_factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defmodule Cadet.Courses.CourseFactory do
enable_achievements: true,
enable_sourcecast: true,
enable_stories: false,
enable_exam_mode: false,
is_official_course: true,
source_chapter: 1,
source_variant: "default",
module_help_text: "Help Text"
Expand Down
Loading