Skip to content

Commit 7d1a400

Browse files
committed
Merge branch 'master' of https://github.com/source-academy/backend into pr/iZUMi-kyouka/1236
2 parents 58906d7 + 40cd12a commit 7d1a400

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2955
-497
lines changed

.credo.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
{Credo.Check.Readability.SpaceAfterCommas},
9595
{Credo.Check.Refactor.DoubleBooleanNegation},
9696
{Credo.Check.Refactor.CondStatements},
97-
{Credo.Check.Refactor.CyclomaticComplexity, max_complexity: 10},
97+
{Credo.Check.Refactor.CyclomaticComplexity, max_complexity: 11},
9898
{Credo.Check.Refactor.FunctionArity},
9999
{Credo.Check.Refactor.LongQuoteBlocks},
100100
{Credo.Check.Refactor.MatchInCondition},

config/prod.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ import Config
33
# Do not print debug messages in production
44
config :logger, level: :info
55

6+
# Add the CloudWatch logger backend in production
7+
config :logger, backends: [:console, {Cadet.Logger.CloudWatchLogger, :cloudwatch_logger}]
8+
9+
# Configure CloudWatch Logger
10+
config :logger, :cloudwatch_logger,
11+
level: :info,
12+
format: "$time $metadata[$level] $message\n",
13+
metadata: [:request_id],
14+
log_group: "cadet-logs",
15+
log_stream: "#{node()}-#{:os.system_time(:second)}"
16+
617
# ## SSL Support
718
#
819
# To get SSL working, you will need to add the `https` key

config/test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ config :cadet, CadetWeb.Endpoint,
99
config :cadet, environment: :test
1010

1111
# Print only warnings and errors during test
12-
config :logger, level: :warn, compile_time_purge_matching: [[level_lower_than: :warn]]
12+
config :logger, level: :warning, compile_time_purge_matching: [[level_lower_than: :warning]]
1313

1414
config :ex_aws,
1515
access_key_id: "hello",

lib/cadet/accounts/accounts.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Cadet.Accounts do
55
use Cadet, [:context, :display]
66

77
import Ecto.Query
8+
require Logger
89

910
alias Cadet.Accounts.{Query, User, CourseRegistration}
1011
alias Cadet.Auth.Provider
@@ -101,23 +102,29 @@ defmodule Cadet.Accounts do
101102

102103
def update_latest_viewed(user = %User{id: user_id}, latest_viewed_course_id)
103104
when is_ecto_id(latest_viewed_course_id) do
105+
Logger.info("Updating latest viewed course for user #{user_id} to #{latest_viewed_course_id}")
106+
104107
CourseRegistration
105108
|> where(user_id: ^user_id)
106109
|> where(course_id: ^latest_viewed_course_id)
107110
|> Repo.one()
108111
|> case do
109112
nil ->
113+
Logger.error("User #{user_id} is not enrolled in course #{latest_viewed_course_id}")
110114
{:error, {:bad_request, "user is not in the course"}}
111115

112116
_ ->
113117
case user
114118
|> User.changeset(%{latest_viewed_course_id: latest_viewed_course_id})
115119
|> Repo.update() do
116120
result = {:ok, _} ->
121+
Logger.info("Successfully updated latest viewed course for user")
117122
result
118123

119124
{:error, changeset} ->
120-
{:error, {:internal_server_error, full_error_messages(changeset)}}
125+
error_msg = full_error_messages(changeset)
126+
Logger.error("Failed to update latest viewed course for user: #{error_msg}")
127+
{:error, {:internal_server_error, error_msg}}
121128
end
122129
end
123130
end

lib/cadet/accounts/course_registrations.ex

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Cadet.Accounts.CourseRegistrations do
55
use Cadet, [:context, :display]
66

77
import Ecto.Query
8+
require Logger
89

910
alias Cadet.{Repo, Accounts}
1011
alias Cadet.Accounts.{User, CourseRegistration}
@@ -18,33 +19,69 @@ defmodule Cadet.Accounts.CourseRegistrations do
1819
# otherwise just use CourseRegistration
1920

2021
def get_user_record(user_id, course_id) when is_ecto_id(user_id) and is_ecto_id(course_id) do
21-
CourseRegistration
22-
|> where([cr], cr.user_id == ^user_id)
23-
|> where([cr], cr.course_id == ^course_id)
24-
|> preload(:course)
25-
|> preload(:group)
26-
|> Repo.one()
22+
Logger.info("Retrieving user record for user #{user_id} in course #{course_id}")
23+
24+
result =
25+
CourseRegistration
26+
|> where([cr], cr.user_id == ^user_id)
27+
|> where([cr], cr.course_id == ^course_id)
28+
|> preload(:course)
29+
|> preload(:group)
30+
|> Repo.one()
31+
32+
case result do
33+
nil ->
34+
Logger.error("User record not found for user #{user_id} in course #{course_id}")
35+
36+
_ ->
37+
Logger.info(
38+
"Successfully retrieved user record for user #{user_id} in course #{course_id}"
39+
)
40+
end
41+
42+
result
2743
end
2844

2945
def get_user_course(user_id, course_id) when is_ecto_id(user_id) and is_ecto_id(course_id) do
30-
CourseRegistration
31-
|> where([cr], cr.user_id == ^user_id)
32-
|> where([cr], cr.course_id == ^course_id)
33-
|> join(:inner, [cr], c in assoc(cr, :course))
34-
|> join(:left, [cr, c], ac in assoc(c, :assessment_config))
35-
|> preload([cr, c, ac],
36-
course: {c, assessment_config: ^from(ac in AssessmentConfig, order_by: [asc: ac.order])}
37-
)
38-
|> preload(:group)
39-
|> Repo.one()
46+
Logger.info("Retrieving course details for user #{user_id} in course #{course_id}")
47+
48+
result =
49+
CourseRegistration
50+
|> where([cr], cr.user_id == ^user_id)
51+
|> where([cr], cr.course_id == ^course_id)
52+
|> join(:inner, [cr], c in assoc(cr, :course))
53+
|> join(:left, [cr, c], ac in assoc(c, :assessment_config))
54+
|> preload([cr, c, ac],
55+
course: {c, assessment_config: ^from(ac in AssessmentConfig, order_by: [asc: ac.order])}
56+
)
57+
|> preload(:group)
58+
|> Repo.one()
59+
60+
case result do
61+
nil ->
62+
Logger.error("Course details not found for user #{user_id} in course #{course_id}")
63+
64+
_ ->
65+
Logger.info(
66+
"Successfully retrieved course details for user #{user_id} in course #{course_id}"
67+
)
68+
end
69+
70+
result
4071
end
4172

4273
def get_courses(%User{id: id}) do
43-
CourseRegistration
44-
|> where([cr], cr.user_id == ^id)
45-
|> join(:inner, [cr], c in assoc(cr, :course))
46-
|> preload(:course)
47-
|> Repo.all()
74+
Logger.info("Retrieving all courses for user #{id}")
75+
76+
courses =
77+
CourseRegistration
78+
|> where([cr], cr.user_id == ^id)
79+
|> join(:inner, [cr], c in assoc(cr, :course))
80+
|> preload(:course)
81+
|> Repo.all()
82+
83+
Logger.info("Retrieved #{length(courses)} courses for user #{id}")
84+
courses
4885
end
4986

5087
def get_exam_mode_course(%User{id: id}) do
@@ -132,6 +169,8 @@ defmodule Cadet.Accounts.CourseRegistrations do
132169
"""
133170
def enroll_course(params = %{user_id: user_id, course_id: course_id, role: _role})
134171
when is_ecto_id(user_id) and is_ecto_id(course_id) do
172+
Logger.info("Enrolling user #{user_id} in course #{course_id}")
173+
135174
case params |> insert_or_update_course_registration() do
136175
{:ok, _course_reg} = ok ->
137176
# Ensures that the user has a latest_viewed_course
@@ -141,9 +180,14 @@ defmodule Cadet.Accounts.CourseRegistrations do
141180
|> User.changeset(%{latest_viewed_course_id: course_id})
142181
|> Repo.update()
143182

183+
Logger.info("Successfully enrolled user #{user_id} in course #{course_id}")
144184
ok
145185

146-
{:error, _} = error ->
186+
{:error, changeset} = error ->
187+
Logger.error(
188+
"Failed to enroll user #{user_id} in course #{course_id}: #{full_error_messages(changeset)}"
189+
)
190+
147191
error
148192
end
149193
end

lib/cadet/accounts/teams.ex

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,38 @@ defmodule Cadet.Accounts.Teams do
77
use Ecto.Schema
88

99
import Ecto.{Changeset, Query}
10+
require Logger
1011

1112
alias Cadet.Repo
1213
alias Cadet.Accounts.{Team, TeamMember, Notification}
1314
alias Cadet.Assessments.{Answer, Submission}
1415

16+
@doc """
17+
Returns all teams for a given course.
18+
19+
## Parameters
20+
21+
* `course_id` - The ID of the course.
22+
23+
## Returns
24+
25+
Returns a list of teams.
26+
27+
"""
28+
def all_teams_for_course(course_id) do
29+
Logger.info("Retrieving all teams for course #{course_id}")
30+
31+
teams =
32+
Team
33+
|> join(:inner, [t], a in assoc(t, :assessment))
34+
|> where([t, a], a.course_id == ^course_id)
35+
|> Repo.all()
36+
|> Repo.preload(assessment: [:config], team_members: [student: [:user]])
37+
38+
Logger.info("Retrieved #{length(teams)} teams for course #{course_id}")
39+
teams
40+
end
41+
1542
@doc """
1643
Creates a new team and assigns an assessment and team members to it.
1744
@@ -26,44 +53,62 @@ defmodule Cadet.Accounts.Teams do
2653
"""
2754
def create_team(attrs) do
2855
assessment_id = attrs["assessment_id"]
56+
Logger.info("Creating teams for assessment #{assessment_id}")
57+
2958
teams = attrs["student_ids"]
3059
assessment = Cadet.Repo.get(Cadet.Assessments.Assessment, assessment_id)
3160

3261
cond do
3362
!all_team_within_max_size?(teams, assessment.max_team_size) ->
63+
Logger.error(
64+
"Team creation failed for assessment #{assessment_id} - teams exceed maximum size"
65+
)
66+
3467
{:error, {:conflict, "One or more teams exceed the maximum team size!"}}
3568

3669
!all_students_distinct?(teams) ->
70+
Logger.error("Team creation failed for assessment #{assessment_id} - duplicate students")
71+
3772
{:error, {:conflict, "One or more students appear multiple times in a team!"}}
3873

3974
!all_student_enrolled_in_course?(teams, assessment.course_id) ->
75+
Logger.error(
76+
"Team creation failed for assessment #{assessment_id} - students not enrolled in course"
77+
)
78+
4079
{:error, {:conflict, "One or more students not enrolled in this course!"}}
4180

4281
student_already_assigned?(teams, assessment_id) ->
82+
Logger.error(
83+
"Team creation failed for assessment #{assessment_id} - students already assigned to teams"
84+
)
85+
4386
{:error, {:conflict, "One or more students already in a team for this assessment!"}}
4487

4588
true ->
46-
Enum.reduce_while(attrs["student_ids"], {:ok, nil}, fn team_attrs, {:ok, _} ->
47-
student_ids = Enum.map(team_attrs, &Map.get(&1, "userId"))
89+
result =
90+
Enum.reduce_while(attrs["student_ids"], {:ok, nil}, fn team_attrs, {:ok, _} ->
91+
{:ok, team} =
92+
%Team{}
93+
|> Team.changeset(attrs)
94+
|> Repo.insert()
4895

49-
{:ok, team} =
50-
%Team{}
51-
|> Team.changeset(attrs)
52-
|> Repo.insert()
96+
team_id = team.id
5397

54-
team_id = team.id
98+
Enum.each(team_attrs, fn student ->
99+
student_id = Map.get(student, "userId")
100+
attributes = %{student_id: student_id, team_id: team_id}
55101

56-
Enum.each(team_attrs, fn student ->
57-
student_id = Map.get(student, "userId")
58-
attributes = %{student_id: student_id, team_id: team_id}
102+
%TeamMember{}
103+
|> cast(attributes, [:student_id, :team_id])
104+
|> Repo.insert()
105+
end)
59106

60-
%TeamMember{}
61-
|> cast(attributes, [:student_id, :team_id])
62-
|> Repo.insert()
107+
{:cont, {:ok, team}}
63108
end)
64109

65-
{:cont, {:ok, team}}
66-
end)
110+
Logger.info("Successfully created teams for assessment #{assessment_id}")
111+
result
67112
end
68113
end
69114

@@ -85,7 +130,6 @@ defmodule Cadet.Accounts.Teams do
85130
ids = Enum.map(team, &Map.get(&1, "userId"))
86131

87132
unique_ids_count = ids |> Enum.uniq() |> Enum.count()
88-
all_ids_distinct = unique_ids_count == Enum.count(ids)
89133

90134
student_already_in_team?(-1, ids, assessment_id)
91135
end)
@@ -209,7 +253,6 @@ defmodule Cadet.Accounts.Teams do
209253
210254
"""
211255
def update_team(team = %Team{}, new_assessment_id, student_ids) do
212-
old_assessment_id = team.assessment_id
213256
team_id = team.id
214257
new_student_ids = Enum.map(hd(student_ids), fn student -> Map.get(student, "userId") end)
215258

@@ -276,7 +319,10 @@ defmodule Cadet.Accounts.Teams do
276319
277320
"""
278321
def delete_team(team = %Team{}) do
322+
Logger.info("Deleting team #{team.id} for assessment #{team.assessment_id}")
323+
279324
if has_submitted_answer?(team.id) do
325+
Logger.error("Cannot delete team #{team.id} - team has submitted answers")
280326
{:error, {:conflict, "This team has submitted their answers! Unable to delete the team!"}}
281327
else
282328
submission =
@@ -299,8 +345,19 @@ defmodule Cadet.Accounts.Teams do
299345
|> Repo.delete_all()
300346
end
301347

302-
team
303-
|> Repo.delete()
348+
result =
349+
team
350+
|> Repo.delete()
351+
352+
case result do
353+
{:ok, _} ->
354+
Logger.info("Successfully deleted team #{team.id}")
355+
result
356+
357+
{:error, changeset} ->
358+
Logger.error("Failed to delete team #{team.id}: #{full_error_messages(changeset)}")
359+
result
360+
end
304361
end
305362
end
306363

0 commit comments

Comments
 (0)