Skip to content

Commit 7e19e24

Browse files
committed
Leaderboard fixes and improvements (#1288)
* Use map for options instead * Remove repeating fields selected from total xp subquery * Retrieve only the userids in the course * select_merge operator is not needed * Convert course userid query to pipeline form * Add filter option for including non-students * Fix submissions xp query and use pipeline format * Fix code formatting * Move anonymous function out of inline * Update testcases for new format * Fix test formatting
1 parent 525f6c7 commit 7e19e24

File tree

3 files changed

+47
-55
lines changed

3 files changed

+47
-55
lines changed

lib/cadet/assessments/assessments.ex

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -146,20 +146,23 @@ defmodule Cadet.Assessments do
146146
total_achievement_xp + total_assessment_xp
147147
end
148148

149-
def all_user_total_xp(course_id, offset \\ nil, limit \\ nil) do
149+
def all_user_total_xp(course_id, options \\ %{}) do
150+
include_admin_staff_users = fn q ->
151+
if options[:include_admin_staff],
152+
do: q,
153+
else: where(q, [_, cr], cr.role == "student")
154+
end
155+
150156
# get all users even if they have 0 xp
151-
base_user_query =
152-
from(
153-
cr in CourseRegistration,
154-
join: u in User,
155-
on: cr.user_id == u.id,
156-
where: cr.course_id == ^course_id,
157-
select: %{
158-
user_id: u.id,
159-
name: u.name,
160-
username: u.username
161-
}
162-
)
157+
course_userid_query =
158+
User
159+
|> join(:inner, [u], cr in CourseRegistration, on: cr.user_id == u.id)
160+
|> where([_, cr], cr.course_id == ^course_id)
161+
|> include_admin_staff_users.()
162+
|> select([u, cr], %{
163+
id: u.id,
164+
cr_id: cr.id
165+
})
163166

164167
achievements_xp_query =
165168
from(u in User,
@@ -175,7 +178,7 @@ defmodule Cadet.Assessments do
175178
a.course_id == ^course_id and p.completed and
176179
p.count == g.target_count,
177180
group_by: [u.id, u.name, u.username, cr.id],
178-
select_merge: %{
181+
select: %{
179182
user_id: u.id,
180183
achievements_xp:
181184
fragment(
@@ -188,42 +191,30 @@ defmodule Cadet.Assessments do
188191
)
189192

190193
submissions_xp_query =
191-
from(
192-
sub_xp in subquery(
193-
from(cr in CourseRegistration,
194-
join: u in User,
195-
on: cr.user_id == u.id,
196-
full_join: tm in TeamMember,
197-
on: cr.id == tm.student_id,
198-
join: s in Submission,
199-
on: tm.team_id == s.team_id or s.student_id == cr.id,
200-
join: a in Answer,
201-
on: s.id == a.submission_id,
202-
where: s.is_grading_published == true and cr.course_id == ^course_id,
203-
group_by: [cr.id, u.id, u.name, u.username, s.id, a.xp, a.xp_adjustment],
204-
select: %{
205-
user_id: u.id,
206-
submission_xp: a.xp + a.xp_adjustment + max(s.xp_bonus)
207-
}
208-
)
209-
),
210-
group_by: sub_xp.user_id,
211-
select: %{
212-
user_id: sub_xp.user_id,
213-
submission_xp: sum(sub_xp.submission_xp)
214-
}
215-
)
194+
course_userid_query
195+
|> subquery()
196+
|> join(:left, [u], tm in TeamMember, on: tm.student_id == u.cr_id)
197+
|> join(:left, [u, tm], s in Submission, on: s.student_id == u.cr_id or s.team_id == tm.id)
198+
|> join(:left, [u, tm, s], a in Answer, on: s.id == a.submission_id)
199+
|> where([_, _, s, _], s.is_grading_published == true)
200+
|> group_by([u, _, s, _], [u.id, s.id])
201+
|> select([u, _, s, a], %{
202+
user_id: u.id,
203+
submission_xp: sum(a.xp) + sum(a.xp_adjustment) + max(s.xp_bonus)
204+
})
216205

217206
total_xp_query =
218-
from(bu in subquery(base_user_query),
207+
from(cu in subquery(course_userid_query),
208+
inner_join: u in User,
209+
on: u.id == cu.id,
219210
left_join: ax in subquery(achievements_xp_query),
220-
on: bu.user_id == ax.user_id,
211+
on: u.id == ax.user_id,
221212
left_join: sx in subquery(submissions_xp_query),
222-
on: bu.user_id == sx.user_id,
213+
on: u.id == sx.user_id,
223214
select: %{
224-
user_id: bu.user_id,
225-
name: bu.name,
226-
username: bu.username,
215+
user_id: u.id,
216+
name: u.name,
217+
username: u.username,
227218
total_xp:
228219
fragment(
229220
"COALESCE(?, 0) + COALESCE(?, 0)",
@@ -237,15 +228,11 @@ defmodule Cadet.Assessments do
237228
# add rank index
238229
ranked_xp_query =
239230
from(t in subquery(total_xp_query),
240-
select: %{
241-
rank: fragment("RANK() OVER (ORDER BY total_xp DESC)"),
242-
user_id: t.user_id,
243-
name: t.name,
244-
username: t.username,
245-
total_xp: t.total_xp
231+
select_merge: %{
232+
rank: fragment("RANK() OVER (ORDER BY total_xp DESC)")
246233
},
247-
limit: ^limit,
248-
offset: ^offset
234+
limit: ^options[:limit],
235+
offset: ^options[:offset]
249236
)
250237

251238
count_query =

lib/cadet_web/controllers/leaderboard_controller.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ defmodule CadetWeb.LeaderboardController do
1313
def xp_paginated(conn, %{"course_id" => course_id}) do
1414
offset = String.to_integer(conn.params["offset"] || "0")
1515
page_size = String.to_integer(conn.params["page_size"] || "25")
16-
paginated_display = Assessments.all_user_total_xp(course_id, offset, page_size)
16+
17+
paginated_display =
18+
Assessments.all_user_total_xp(course_id, %{offset: offset, limit: page_size})
19+
1720
json(conn, paginated_display)
1821
end
1922

test/cadet/assessments/assessments_test.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3188,7 +3188,9 @@ defmodule Cadet.AssessmentsTest do
31883188
Enum.each(1..50, fn x ->
31893189
offset = Enum.random(0..49)
31903190
limit = Enum.random(1..50)
3191-
paginated_user_xp = Assessments.all_user_total_xp(course.id, offset, limit)
3191+
3192+
paginated_user_xp =
3193+
Assessments.all_user_total_xp(course.id, %{offset: offset, limit: limit})
31923194

31933195
expected_xp_list =
31943196
50..1

0 commit comments

Comments
 (0)