• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / backend / e0330f2cf38b2d8af12bffd20f4cac2158d607fc-PR-1236

31 Mar 2025 09:12AM UTC coverage: 19.982% (-73.6%) from 93.607%
e0330f2cf38b2d8af12bffd20f4cac2158d607fc-PR-1236

Pull #1236

github

RichDom2185
Redate migrations to maintain total ordering
Pull Request #1236: Added Exam mode

12 of 57 new or added lines in 8 files covered. (21.05%)

2430 existing lines in 97 files now uncovered.

671 of 3358 relevant lines covered (19.98%)

3.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.3
/lib/cadet_web/controllers/user_controller.ex
1
defmodule CadetWeb.UserController do
2
  @moduledoc """
3
  Provides information about a user.
4
  """
5

6
  use CadetWeb, :controller
7
  use PhoenixSwagger
8
  alias Cadet.Accounts.CourseRegistrations
9

10
  alias Cadet.{Accounts, Assessments}
11

12
  def index(conn, _) do
13
    user = conn.assigns.current_user
1✔
14
    courses = CourseRegistrations.get_courses(conn.assigns.current_user)
1✔
15
    exam_mode_course = CourseRegistrations.get_exam_mode_course(conn.assigns.current_user)
1✔
16

NEW
17
    cond do
×
18
      exam_mode_course ->
NEW
19
        IO.puts("Course #{exam_mode_course.course_id} is under exam mode.")
×
NEW
20
        xp = Assessments.assessments_total_xp(exam_mode_course)
×
NEW
21
        max_xp = Assessments.user_max_xp(exam_mode_course)
×
NEW
22
        story = Assessments.user_current_story(exam_mode_course)
×
23

NEW
24
        render(
×
25
          conn,
26
          "index.json",
27
          user: user,
NEW
28
          courses: courses |> Enum.filter(fn c -> c.course_id == exam_mode_course.course_id end),
×
29
          latest: exam_mode_course,
30
          max_xp: max_xp,
31
          story: story,
32
          xp: xp
33
        )
34

NEW
35
      user.latest_viewed_course_id ->
×
NEW
36
        latest = CourseRegistrations.get_user_course(user.id, user.latest_viewed_course_id)
×
NEW
37
        xp = Assessments.assessments_total_xp(latest)
×
NEW
38
        max_xp = Assessments.user_max_xp(latest)
×
NEW
39
        story = Assessments.user_current_story(latest)
×
40

NEW
41
        render(
×
42
          conn,
43
          "index.json",
44
          user: user,
45
          courses: courses,
46
          latest: latest,
47
          max_xp: max_xp,
48
          story: story,
49
          xp: xp
50
        )
51

NEW
52
      true ->
×
NEW
53
        render(conn, "index.json",
×
54
          user: user,
55
          courses: courses,
56
          latest: nil,
57
          max_xp: nil,
58
          story: nil,
59
          xp: nil
60
        )
61
    end
62
  end
63

64
  def get_latest_viewed(conn, _) do
65
    user = conn.assigns.current_user
1✔
66
    exam_mode_course = CourseRegistrations.get_exam_mode_course(conn.assigns.current_user)
1✔
67

NEW
68
    if exam_mode_course do
×
NEW
69
      latest = CourseRegistrations.get_user_course(user.id, exam_mode_course.course_id)
×
NEW
70
      get_course_reg_config(conn, latest)
×
71
    else
NEW
72
      latest =
×
NEW
73
        case user.latest_viewed_course_id do
×
NEW
74
          nil -> nil
×
NEW
75
          _ -> CourseRegistrations.get_user_course(user.id, user.latest_viewed_course_id)
×
76
        end
77

NEW
78
      get_course_reg_config(conn, latest)
×
79
    end
80
  end
81

82
  defp get_course_reg_config(conn, course_reg) when is_nil(course_reg) do
UNCOV
83
    render(conn, "course.json", latest: nil, story: nil, xp: nil, max_xp: nil)
×
84
  end
85

86
  defp get_course_reg_config(conn, course_reg) do
UNCOV
87
    xp = Assessments.assessments_total_xp(course_reg)
×
UNCOV
88
    max_xp = Assessments.user_max_xp(course_reg)
×
UNCOV
89
    story = Assessments.user_current_story(course_reg)
×
90

UNCOV
91
    render(
×
92
      conn,
93
      "course.json",
94
      latest: course_reg,
95
      max_xp: max_xp,
96
      story: story,
97
      xp: xp
98
    )
99
  end
100

101
  def update_latest_viewed(conn, %{"courseId" => course_id}) do
UNCOV
102
    case Accounts.update_latest_viewed(conn.assigns.current_user, course_id) do
×
103
      {:ok, %{}} ->
UNCOV
104
        text(conn, "OK")
×
105

106
      {:error, {status, message}} ->
107
        conn
108
        |> put_status(status)
UNCOV
109
        |> text(message)
×
110
    end
111
  end
112

113
  def update_game_states(conn, %{"gameStates" => new_game_states}) do
UNCOV
114
    cr = conn.assigns[:course_reg]
×
115

UNCOV
116
    case CourseRegistrations.update_game_states(cr, new_game_states) do
×
117
      {:ok, %{}} ->
UNCOV
118
        text(conn, "OK")
×
119

120
      {:error, {status, message}} ->
121
        conn
122
        |> put_status(status)
UNCOV
123
        |> text(message)
×
124
    end
125
  end
126

127
  def update_research_agreement(conn, %{"agreedToResearch" => agreed_to_research}) do
UNCOV
128
    course_reg = conn.assigns[:course_reg]
×
129

UNCOV
130
    case CourseRegistrations.update_research_agreement(course_reg, agreed_to_research) do
×
131
      {:ok, %{}} ->
UNCOV
132
        text(conn, "OK")
×
133

134
      {:error, {status, message}} ->
135
        conn
136
        |> put_status(status)
UNCOV
137
        |> text(message)
×
138
    end
139
  end
140

141
  def combined_total_xp(conn, _) do
UNCOV
142
    course_id = conn.assigns.course_reg.course_id
×
UNCOV
143
    user_id = conn.assigns.course_reg.user_id
×
UNCOV
144
    course_reg_id = conn.assigns.course_reg.id
×
145

UNCOV
146
    total_xp = Assessments.user_total_xp(course_id, user_id, course_reg_id)
×
UNCOV
147
    json(conn, %{totalXp: total_xp})
×
148
  end
149

150
  swagger_path :index do
1✔
151
    get("/user")
152

153
    summary("Get the name, and latest_viewed_course of a user")
154

155
    security([%{JWT: []}])
156
    produces("application/json")
157
    response(200, "OK", Schema.ref(:IndexInfo))
158
    response(401, "Unauthorised")
159
  end
160

161
  swagger_path :get_latest_viewed do
1✔
162
    get("/user/latest_viewed_course")
163

164
    summary("Get the latest_viewed_course of a user")
165

166
    security([%{JWT: []}])
167
    produces("application/json")
168
    response(200, "OK", Schema.ref(:LatestViewedInfo))
169
    response(401, "Unauthorised")
170
  end
171

172
  swagger_path :update_latest_viewed do
1✔
173
    put("/user/latest_viewed_course")
174
    summary("Update user's latest viewed course")
175
    security([%{JWT: []}])
176
    consumes("application/json")
177

178
    parameters do
179
      course_id(:body, :integer, "new latest viewed course", required: true)
180
    end
181

182
    response(200, "OK")
183
  end
184

185
  swagger_path :update_game_states do
1✔
186
    put("/courses/:course_id/user/game_states")
187
    summary("Update user's game states")
188
    security([%{JWT: []}])
189
    consumes("application/json")
190

191
    parameters do
192
      gameStates(:body, Schema.ref(:UserGameStates), "new game states", required: true)
193
    end
194

195
    response(200, "OK")
196
  end
197

198
  swagger_path :update_research_agreement do
1✔
199
    put("/courses/:course_id/user/research_agreement")
200
    summary("Update the user's agreement to the anonymized collection of programs for research")
201
    security([%{JWT: []}])
202
    consumes("application/json")
203

204
    parameters do
205
      course_id(:path, :integer, "course ID", required: true)
206

207
      agreedToResearch(
208
        :body,
209
        :boolean,
210
        "whether the user has agreed to participate in the research",
211
        required: true
212
      )
213
    end
214

215
    response(200, "OK")
216
    response(400, "Bad Request")
217
  end
218

219
  swagger_path :combined_total_xp do
1✔
220
    get("/courses/:course_id/user/total_xp")
221

222
    summary("Get the user's total XP from achievements and assessments")
223

224
    security([%{JWT: []}])
225
    produces("application/json")
226

227
    parameters do
228
      course_id(:path, :integer, "course ID", required: true)
229
    end
230

231
    response(200, "OK", Schema.ref(:TotalXPInfo))
232
    response(401, "Unauthorised")
233
  end
234

235
  def swagger_definitions do
236
    %{
1✔
237
      IndexInfo:
238
        swagger_schema do
1✔
239
          title("User Index")
240
          description("user, course_registration and course configuration of the latest course")
241

242
          properties do
1✔
243
            user(Schema.ref(:UserInfo), "user info")
244

245
            courseRegistration(
246
              Schema.ref(:CourseRegistration),
247
              "course registration of the latest viewed course"
248
            )
249

250
            courseConfiguration(
1✔
251
              Schema.ref(:CourseConfiguration),
252
              "course configuration of the latest viewed course"
253
            )
254
          end
255
        end,
256
      TotalXPInfo:
257
        swagger_schema do
1✔
258
          title("User Total XP")
259
          description("the user's total achievement and assessment XP")
260

261
          properties do
1✔
262
            totalXp(:integer, "total XP")
1✔
263
          end
264
        end,
265
      LatestViewedInfo:
266
        swagger_schema do
1✔
267
          title("Latest viewed course")
268
          description("course_registration and course configuration of the latest course")
269

270
          properties do
1✔
271
            courseRegistration(
272
              Schema.ref(:CourseRegistration),
273
              "course registration of the latest viewed course"
274
            )
275

276
            courseConfiguration(
1✔
277
              Schema.ref(:CourseConfiguration),
278
              "course configuration of the latest viewed course"
279
            )
280
          end
281
        end,
282
      UserInfo:
283
        swagger_schema do
1✔
284
          title("User")
285
          description("Basic information about the user")
286

287
          properties do
1✔
288
            userId(:integer, "User's ID", required: true)
289
            name(:string, "Full name of the user", required: true)
1✔
290
          end
291
        end,
292
      CourseRegistration:
293
        swagger_schema do
1✔
294
          title("CourseRegistration")
295
          description("information about the CourseRegistration")
296

297
          properties do
1✔
298
            role(
299
              :string,
300
              "Role of the user. Can be 'Student', 'Staff', or 'Admin'",
301
              required: true
302
            )
303

304
            group(
305
              :string,
306
              "Group the user belongs to. May be null if the user does not belong to any group.",
307
              required: true
308
            )
309

310
            story(Schema.ref(:UserStory), "Story to displayed to current user. ")
311

312
            maxXp(
313
              :integer,
314
              "Total maximum xp achievable based on submitted assessments. " <>
315
                "Only provided for 'Student'"
316
            )
317

318
            xp(
319
              :integer,
320
              "Amount of xp. Only provided for 'Student'. " <> "Value will be 0 for non-students."
321
            )
322

323
            game_states(
324
              Schema.ref(:UserGameStates),
325
              "States for user's game, including users' game progress, settings and collectibles.\n"
326
            )
327

328
            agreed_to_research(
1✔
329
              :boolean,
330
              "Whether the user as agreed to participate in the collection of anonymized data for research purposes."
331
            )
332
          end
333
        end,
334
      CourseConfiguration:
335
        swagger_schema do
1✔
336
          title("Course Configuration")
337

338
          properties do
1✔
339
            course_name(:string, "Course name", required: true)
340
            course_short_name(:string, "Course module code", required: true)
341
            viewable(:boolean, "Course viewability", required: true)
342
            enable_game(:boolean, "Enable game", required: true)
343
            enable_achievements(:boolean, "Enable achievements", required: true)
344
            enable_sourcecast(:boolean, "Enable sourcecast", required: true)
345
            enable_stories(:boolean, "Enable stories", required: true)
346
            enable_exam_mode(:boolean, "Enable exam mode", required: true)
347
            is_official_course(:boolean, "Course status (official institution course)")
348
            source_chapter(:integer, "Source Chapter number from 1 to 4", required: true)
349
            source_variant(Schema.ref(:SourceVariant), "Source Variant name", required: true)
350
            module_help_text(:string, "Module help text", required: true)
351
            assessment_types(:list, "Assessment Types", required: true)
352
            assets_prefix(:string, "Assets prefix, used by the game")
1✔
353
          end
354

355
          example(%{
356
            course_name: "Programming Methodology",
357
            course_short_name: "CS1101S",
358
            viewable: true,
359
            enable_game: true,
360
            enable_achievements: true,
361
            enable_sourcecast: true,
362
            enable_stories: false,
363
            enable_exam_mode: false,
364
            is_official_course: true,
365
            source_chapter: 1,
366
            source_variant: "default",
367
            module_help_text: "Help text",
368
            assessment_types: ["Missions", "Quests", "Paths", "Contests", "Others"],
369
            assets_prefix: "courses-prod/1/"
370
          })
371
        end,
372
      SourceVariant:
373
        swagger_schema do
1✔
374
          type(:string)
375
          enum([:default, :concurrent, :gpu, :lazy, "non-det", :wasm])
376
        end,
377
      UserStory:
378
        swagger_schema do
1✔
379
          properties do
1✔
380
            story(
381
              :string,
382
              "Name of story to be displayed to current user. May only be null before start of semester" <>
383
                " when no assessments are open"
384
            )
385

386
            playStory(
1✔
387
              :boolean,
388
              "Whether story should be played (false indicates story field should only be used to fetch" <>
389
                " assets, display open world view)"
390
            )
391
          end
392
        end,
393
      UserGameStates:
394
        swagger_schema do
1✔
395
        end
396
    }
397
  end
398
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc