Skip to content

Commit 8752848

Browse files
authored
Merge branch 'master' into feat/upgrade-logger
2 parents 96225a1 + 415c28e commit 8752848

23 files changed

+1175
-296
lines changed

lib/cadet/assessments/assessments.ex

Lines changed: 369 additions & 55 deletions
Large diffs are not rendered by default.

lib/cadet/assessments/question_types/voting_question.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ defmodule Cadet.Assessments.QuestionTypes.VotingQuestion do
1212
field(:contest_number, :string)
1313
field(:reveal_hours, :integer)
1414
field(:token_divider, :integer)
15+
field(:xp_values, {:array, :integer}, default: [500, 400, 300])
1516
end
1617

1718
@required_fields ~w(content contest_number reveal_hours token_divider)a
18-
@optional_fields ~w(prepend template)a
19+
@optional_fields ~w(prepend template xp_values)a
1920

2021
def changeset(question, params \\ %{}) do
2122
question

lib/cadet/courses/course.ex

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ defmodule Cadet.Courses.Course do
1212
viewable: boolean(),
1313
enable_game: boolean(),
1414
enable_achievements: boolean(),
15+
enable_overall_leaderboard: boolean(),
16+
enable_contest_leaderboard: boolean(),
17+
top_leaderboard_display: integer(),
18+
top_contest_leaderboard_display: integer(),
1519
enable_sourcecast: boolean(),
1620
enable_stories: boolean(),
1721
source_chapter: integer(),
@@ -26,6 +30,10 @@ defmodule Cadet.Courses.Course do
2630
field(:viewable, :boolean, default: true)
2731
field(:enable_game, :boolean, default: true)
2832
field(:enable_achievements, :boolean, default: true)
33+
field(:enable_overall_leaderboard, :boolean, default: true)
34+
field(:enable_contest_leaderboard, :boolean, default: true)
35+
field(:top_leaderboard_display, :integer, default: 100)
36+
field(:top_contest_leaderboard_display, :integer, default: 10)
2937
field(:enable_sourcecast, :boolean, default: true)
3038
field(:enable_stories, :boolean, default: false)
3139
field(:source_chapter, :integer)
@@ -41,7 +49,7 @@ defmodule Cadet.Courses.Course do
4149
end
4250

4351
@required_fields ~w(course_name viewable enable_game
44-
enable_achievements enable_sourcecast enable_stories source_chapter source_variant)a
52+
enable_achievements enable_overall_leaderboard enable_contest_leaderboard top_leaderboard_display top_contest_leaderboard_display enable_sourcecast enable_stories source_chapter source_variant)a
4553
@optional_fields ~w(course_short_name module_help_text)a
4654

4755
def changeset(course, params) do

lib/cadet/jobs/xml_parser.ex

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -246,22 +246,30 @@ defmodule Cadet.Updater.XMLParser do
246246
end
247247

248248
defp process_question_entity_by_type(entity, "voting") do
249-
Map.merge(
250-
entity
251-
|> xpath(
252-
~x"."e,
253-
content: ~x"./TEXT/text()" |> transform_by(&process_charlist/1),
254-
prepend: ~x"./SNIPPET/PREPEND/text()" |> transform_by(&process_charlist/1),
255-
template: ~x"./SNIPPET/TEMPLATE/text()" |> transform_by(&process_charlist/1)
256-
),
257-
entity
258-
|> xpath(
259-
~x"./VOTING"e,
260-
contest_number: ~x"./@assessment_number"s,
261-
reveal_hours: ~x"./@reveal_hours"i,
262-
token_divider: ~x"./@token_divider"i
249+
question_data =
250+
Map.merge(
251+
entity
252+
|> xpath(
253+
~x"."e,
254+
content: ~x"./TEXT/text()" |> transform_by(&process_charlist/1),
255+
prepend: ~x"./SNIPPET/PREPEND/text()" |> transform_by(&process_charlist/1),
256+
template: ~x"./SNIPPET/TEMPLATE/text()" |> transform_by(&process_charlist/1)
257+
),
258+
entity
259+
|> xpath(
260+
~x"./VOTING"e,
261+
contest_number: ~x"./@assessment_number"s,
262+
reveal_hours: ~x"./@reveal_hours"i,
263+
token_divider: ~x"./@token_divider"i
264+
)
263265
)
264-
)
266+
267+
xp_values =
268+
entity
269+
|> xpath(~x"./VOTING/XP_ARRAY/XP"el, value: ~x"./@value"i)
270+
|> Enum.map(& &1[:value])
271+
272+
if xp_values == [], do: question_data, else: Map.merge(question_data, %{xp_values: xp_values})
265273
end
266274

267275
defp process_question_entity_by_type(_, _) do

lib/cadet_web/admin_controllers/admin_assessments_controller.ex

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -135,42 +135,35 @@ defmodule CadetWeb.AdminAssessmentsController do
135135
end
136136
end
137137

138-
def get_score_leaderboard(conn, %{"assessmentid" => assessment_id, "course_id" => course_id}) do
138+
def calculate_contest_score(conn, %{"assessmentid" => assessment_id, "course_id" => course_id}) do
139139
voting_questions =
140140
Question
141141
|> where(type: :voting)
142142
|> where(assessment_id: ^assessment_id)
143143
|> Repo.one()
144144

145-
contest_id = Assessments.fetch_associated_contest_question_id(course_id, voting_questions)
146-
147-
result =
148-
contest_id
149-
|> Assessments.fetch_top_relative_score_answers(10)
150-
|> Enum.map(fn entry ->
151-
AssessmentsHelpers.build_contest_leaderboard_entry(entry)
152-
end)
153-
154-
render(conn, "leaderboard.json", leaderboard: result)
145+
if voting_questions do
146+
Assessments.compute_relative_score(voting_questions.id)
147+
text(conn, "CONTEST SCORE CALCULATED")
148+
else
149+
text(conn, "No voting questions found for the given assessment")
150+
end
155151
end
156152

157-
def get_popular_leaderboard(conn, %{"assessmentid" => assessment_id, "course_id" => course_id}) do
153+
def dispatch_contest_xp(conn, %{"assessmentid" => assessment_id, "course_id" => course_id}) do
158154
voting_questions =
159155
Question
160156
|> where(type: :voting)
161157
|> where(assessment_id: ^assessment_id)
162158
|> Repo.one()
163159

164-
contest_id = Assessments.fetch_associated_contest_question_id(course_id, voting_questions)
160+
if voting_questions do
161+
Assessments.assign_winning_contest_entries_xp(voting_questions.id)
165162

166-
result =
167-
contest_id
168-
|> Assessments.fetch_top_popular_score_answers(10)
169-
|> Enum.map(fn entry ->
170-
AssessmentsHelpers.build_popular_leaderboard_entry(entry)
171-
end)
172-
173-
render(conn, "leaderboard.json", leaderboard: result)
163+
text(conn, "XP Dispatched")
164+
else
165+
text(conn, "No voting questions found for the given assessment")
166+
end
174167
end
175168

176169
defp check_dates(open_at, close_at, assessment) do
@@ -288,7 +281,7 @@ defmodule CadetWeb.AdminAssessmentsController do
288281
swagger_path :get_score_leaderboard do
289282
get("/courses/{course_id}/admin/assessments/:assessmentid/scoreLeaderboard")
290283

291-
summary("get the top 10 contest entries based on score")
284+
summary("get the top X contest entries based on score")
292285

293286
security([%{JWT: []}])
294287

lib/cadet_web/admin_controllers/admin_courses_controller.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ defmodule CadetWeb.AdminCoursesController do
106106
viewable(:body, :boolean, "Course viewability")
107107
enable_game(:body, :boolean, "Enable game")
108108
enable_achievements(:body, :boolean, "Enable achievements")
109+
enable_overall_leaderboard(:body, :boolean, "Enable overall leaderboard")
110+
enable_contest_leaderboard(:body, :boolean, "Enable contest leaderboard")
111+
top_leaderboard_display(:body, :integer, "Top Leaderboard Display")
112+
top_contest_leaderboard_display(:body, :integer, "Top Contest Leaderboard Display")
109113
enable_sourcecast(:body, :boolean, "Enable sourcecast")
110114
enable_stories(:body, :boolean, "Enable stories")
111115
sublanguage(:body, Schema.ref(:AdminSublanguage), "sublanguage object")

lib/cadet_web/controllers/assessments_controller.ex

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ defmodule CadetWeb.AssessmentsController do
33

44
use PhoenixSwagger
55

6-
alias Cadet.Assessments
6+
import Ecto.Query, only: [where: 2]
7+
8+
alias Cadet.{Assessments, Repo}
9+
alias Cadet.Assessments.Question
10+
alias CadetWeb.AssessmentsHelpers
711

812
# These roles can save and finalise answers for closed assessments and
913
# submitted answers
@@ -67,6 +71,103 @@ defmodule CadetWeb.AssessmentsController do
6771
end
6872
end
6973

74+
def combined_total_xp_for_all_users(conn, %{"course_id" => course_id}) do
75+
users_with_xp = Assessments.all_user_total_xp(course_id)
76+
json(conn, %{users: users_with_xp.users})
77+
end
78+
79+
def paginated_total_xp_for_leaderboard_display(conn, %{"course_id" => course_id}) do
80+
offset = String.to_integer(conn.params["offset"] || "0")
81+
page_size = String.to_integer(conn.params["page_size"] || "25")
82+
paginated_display = Assessments.all_user_total_xp(course_id, offset, page_size)
83+
json(conn, paginated_display)
84+
end
85+
86+
def get_score_leaderboard(conn, %{
87+
"assessmentid" => assessment_id,
88+
"course_id" => course_id
89+
}) do
90+
visible_entries = String.to_integer(conn.params["visible_entries"] || "10")
91+
voting_id = Assessments.fetch_contest_voting_assesment_id(assessment_id)
92+
93+
voting_questions =
94+
Question
95+
|> where(type: :voting)
96+
|> where(assessment_id: ^assessment_id)
97+
|> Repo.one()
98+
|> case do
99+
nil ->
100+
Question
101+
|> where(type: :voting)
102+
|> where(assessment_id: ^voting_id)
103+
|> Repo.one()
104+
105+
question ->
106+
question
107+
end
108+
109+
contest_id = Assessments.fetch_associated_contest_question_id(course_id, voting_questions)
110+
111+
result =
112+
contest_id
113+
|> Assessments.fetch_top_relative_score_answers(visible_entries)
114+
|> Enum.map(fn entry ->
115+
updated_entry = %{
116+
entry
117+
| answer: entry.answer["code"]
118+
}
119+
120+
AssessmentsHelpers.build_contest_leaderboard_entry(updated_entry)
121+
end)
122+
123+
json(conn, %{leaderboard: result, voting_id: voting_id})
124+
end
125+
126+
def get_popular_leaderboard(conn, %{
127+
"assessmentid" => assessment_id,
128+
"course_id" => course_id
129+
}) do
130+
visible_entries = String.to_integer(conn.params["visible_entries"] || "10")
131+
voting_id = Assessments.fetch_contest_voting_assesment_id(assessment_id)
132+
133+
voting_questions =
134+
Question
135+
|> where(type: :voting)
136+
|> where(assessment_id: ^assessment_id)
137+
|> Repo.one()
138+
|> case do
139+
nil ->
140+
Question
141+
|> where(type: :voting)
142+
|> where(assessment_id: ^voting_id)
143+
|> Repo.one()
144+
145+
question ->
146+
question
147+
end
148+
149+
contest_id = Assessments.fetch_associated_contest_question_id(course_id, voting_questions)
150+
151+
result =
152+
contest_id
153+
|> Assessments.fetch_top_popular_score_answers(visible_entries)
154+
|> Enum.map(fn entry ->
155+
updated_entry = %{
156+
entry
157+
| answer: entry.answer["code"]
158+
}
159+
160+
AssessmentsHelpers.build_popular_leaderboard_entry(updated_entry)
161+
end)
162+
163+
json(conn, %{leaderboard: result, voting_id: voting_id})
164+
end
165+
166+
def get_all_contests(conn, %{"course_id" => course_id}) do
167+
contests = Assessments.fetch_all_contests(course_id)
168+
json(conn, contests)
169+
end
170+
70171
swagger_path :submit do
71172
post("/courses/{course_id}/assessments/{assessmentId}/submit")
72173
summary("Finalise submission for an assessment")

lib/cadet_web/controllers/courses_controller.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ defmodule CadetWeb.CoursesController do
5454
viewable(:body, :boolean, "Course viewability", required: true)
5555
enable_game(:body, :boolean, "Enable game", required: true)
5656
enable_achievements(:body, :boolean, "Enable achievements", required: true)
57+
enable_overall_leaderboard(:body, :boolean, "Enable overall leaderboard", required: true)
58+
enable_contest_leaderboard(:body, :boolean, "Enable contest leaderboard", required: true)
59+
top_leaderboard_display(:body, :number, "Top leaderboard display", required: true)
60+
61+
top_contest_leaderboard_display(:body, :number, "Top contest leaderboard display",
62+
required: true
63+
)
64+
5765
enable_sourcecast(:body, :boolean, "Enable sourcecast", required: true)
5866
enable_stories(:body, :boolean, "Enable stories", required: true)
5967
source_chapter(:body, :number, "Default source chapter", required: true)
@@ -95,6 +103,14 @@ defmodule CadetWeb.CoursesController do
95103
viewable(:boolean, "Course viewability", required: true)
96104
enable_game(:boolean, "Enable game", required: true)
97105
enable_achievements(:boolean, "Enable achievements", required: true)
106+
enable_overall_leaderboard(:boolean, "Enable overall leaderboard", required: true)
107+
enable_contest_leaderboard(:boolean, "Enable contest leaderboard", required: true)
108+
top_leaderboard_display(:boolean, "Top leaderboard display", required: true)
109+
110+
top_contest_leaderboard_display(:boolean, "Top contest leaderboard display",
111+
required: true
112+
)
113+
98114
enable_sourcecast(:boolean, "Enable sourcecast", required: true)
99115
enable_stories(:boolean, "Enable stories", required: true)
100116
source_chapter(:integer, "Source Chapter number from 1 to 4", required: true)
@@ -109,6 +125,10 @@ defmodule CadetWeb.CoursesController do
109125
viewable: true,
110126
enable_game: true,
111127
enable_achievements: true,
128+
enable_overall_leaderboard: true,
129+
enable_contest_leaderboard: true,
130+
top_leaderboard_display: 100,
131+
top_contest_leaderboard_display: 10,
112132
enable_sourcecast: true,
113133
enable_stories: false,
114134
source_chapter: 1,

lib/cadet_web/controllers/user_controller.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,14 @@ defmodule CadetWeb.UserController do
315315
viewable(:boolean, "Course viewability", required: true)
316316
enable_game(:boolean, "Enable game", required: true)
317317
enable_achievements(:boolean, "Enable achievements", required: true)
318+
enable_overall_leaderboard(:boolean, "Enable overall leaderboard", required: true)
319+
enable_contest_leaderboard(:boolean, "Enable contest leadeboard", required: true)
320+
top_leaderboard_display(:integer, "Top leaderboard display", required: true)
321+
322+
top_contest_leaderboard_display(:integer, "Top contest leaderboard display",
323+
required: true
324+
)
325+
318326
enable_sourcecast(:boolean, "Enable sourcecast", required: true)
319327
enable_stories(:boolean, "Enable stories", required: true)
320328
source_chapter(:integer, "Source Chapter number from 1 to 4", required: true)
@@ -330,6 +338,10 @@ defmodule CadetWeb.UserController do
330338
viewable: true,
331339
enable_game: true,
332340
enable_achievements: true,
341+
enable_overall_leaderboard: true,
342+
enable_contest_leaderboard: true,
343+
top_leaderboard_display: 100,
344+
top_contest_leaderboard_display: 10,
333345
enable_sourcecast: true,
334346
enable_stories: false,
335347
source_chapter: 1,

lib/cadet_web/helpers/assessments_helpers.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ defmodule CadetWeb.AssessmentsHelpers do
107107
transform_map_for_view(leaderboard_ans, %{
108108
submission_id: :submission_id,
109109
answer: :answer,
110-
student_name: :student_name
110+
student_name: :student_name,
111+
student_username: :student_username,
112+
rank: :rank
111113
}),
112114
"final_score",
113115
Float.round(leaderboard_ans.relative_score, 2)
@@ -119,7 +121,9 @@ defmodule CadetWeb.AssessmentsHelpers do
119121
transform_map_for_view(leaderboard_ans, %{
120122
submission_id: :submission_id,
121123
answer: :answer,
122-
student_name: :student_name
124+
student_name: :student_name,
125+
student_username: :student_username,
126+
rank: :rank
123127
}),
124128
"final_score",
125129
Float.round(leaderboard_ans.popular_score, 2)

0 commit comments

Comments
 (0)