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

source-academy / backend / 18dc689a4df4836fc6967bf0f74dc252964bd175-PR-1180

08 Sep 2024 06:14PM UTC coverage: 79.088% (-15.3%) from 94.372%
18dc689a4df4836fc6967bf0f74dc252964bd175-PR-1180

Pull #1180

github

josh1248
Change appropriate routes into admin scope
Pull Request #1180: Transfer groundControl (and admin panel) from staff to admin route

7 of 12 new or added lines in 1 file covered. (58.33%)

499 existing lines in 25 files now uncovered.

2602 of 3290 relevant lines covered (79.09%)

1023.2 hits per line

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

10.0
/lib/cadet_web/admin_controllers/admin_assessments_controller.ex
1
defmodule CadetWeb.AdminAssessmentsController do
2
  use CadetWeb, :controller
3

4
  use PhoenixSwagger
5

6
  import Ecto.Query, only: [where: 2]
7
  import Cadet.Updater.XMLParser, only: [parse_xml: 4]
8

9
  alias CadetWeb.AssessmentsHelpers
10
  alias Cadet.Assessments.{Question, Assessment}
11
  alias Cadet.{Assessments, Repo}
12
  alias Cadet.Accounts.CourseRegistration
13

14
  def index(conn, %{"course_reg_id" => course_reg_id}) do
UNCOV
15
    course_reg = Repo.get(CourseRegistration, course_reg_id)
×
UNCOV
16
    {:ok, assessments} = Assessments.all_assessments(course_reg)
×
17

UNCOV
18
    render(conn, "index.json", assessments: assessments)
×
19
  end
20

21
  def get_assessment(conn, %{"course_reg_id" => course_reg_id, "assessmentid" => assessment_id})
22
      when is_ecto_id(assessment_id) do
23
    course_reg = Repo.get(CourseRegistration, course_reg_id)
×
24

25
    case Assessments.assessment_with_questions_and_answers(assessment_id, course_reg) do
×
26
      {:ok, assessment} -> render(conn, "show.json", assessment: assessment)
×
27
      {:error, {status, message}} -> send_resp(conn, status, message)
×
28
    end
29
  end
30

31
  def create(conn, %{
32
        "course_id" => course_id,
33
        "assessment" => assessment,
34
        "forceUpdate" => force_update,
35
        "assessmentConfigId" => assessment_config_id
36
      }) do
UNCOV
37
    file =
×
UNCOV
38
      assessment["file"].path
×
39
      |> File.read!()
40

UNCOV
41
    result =
×
42
      case force_update do
UNCOV
43
        "true" -> parse_xml(file, course_id, assessment_config_id, true)
×
UNCOV
44
        "false" -> parse_xml(file, course_id, assessment_config_id, false)
×
45
      end
46

UNCOV
47
    case result do
×
48
      :ok ->
UNCOV
49
        if force_update == "true" do
×
50
          text(conn, "Force update OK")
×
51
        else
UNCOV
52
          text(conn, "OK")
×
53
        end
54

55
      {:ok, warning_message} ->
56
        text(conn, warning_message)
×
57

58
      {:error, {status, message}} ->
59
        conn
60
        |> put_status(status)
UNCOV
61
        |> text(message)
×
62
    end
63
  end
64

65
  def delete(conn, %{"course_id" => course_id, "assessmentid" => assessment_id}) do
UNCOV
66
    with {:same_course, true} <- {:same_course, is_same_course(course_id, assessment_id)},
×
UNCOV
67
         {:ok, _} <- Assessments.delete_assessment(assessment_id) do
×
UNCOV
68
      text(conn, "OK")
×
69
    else
70
      {:same_course, false} ->
71
        conn
72
        |> put_status(403)
UNCOV
73
        |> text("User not allow to delete assessments from another course")
×
74

75
      {:error, {status, message}} ->
76
        conn
77
        |> put_status(status)
78
        |> text(message)
×
79
    end
80
  end
81

82
  def update(conn, params = %{"assessmentid" => assessment_id}) when is_ecto_id(assessment_id) do
UNCOV
83
    open_at = params |> Map.get("openAt")
×
UNCOV
84
    close_at = params |> Map.get("closeAt")
×
UNCOV
85
    is_published = params |> Map.get("isPublished")
×
UNCOV
86
    max_team_size = params |> Map.get("maxTeamSize")
×
UNCOV
87
    has_token_counter = params |> Map.get("hasTokenCounter")
×
UNCOV
88
    has_voting_features = params |> Map.get("hasVotingFeatures")
×
UNCOV
89
    assign_entries_for_voting = params |> Map.get("assignEntriesForVoting")
×
90

UNCOV
91
    updated_assessment =
×
92
      if is_nil(is_published) do
UNCOV
93
        %{}
×
94
      else
UNCOV
95
        %{:is_published => is_published}
×
96
      end
97

UNCOV
98
    updated_assessment =
×
99
      if is_nil(max_team_size) do
UNCOV
100
        updated_assessment
×
101
      else
102
        Map.put(updated_assessment, :max_team_size, max_team_size)
×
103
      end
104

UNCOV
105
    updated_assessment =
×
106
      if is_nil(has_token_counter) do
UNCOV
107
        updated_assessment
×
108
      else
UNCOV
109
        Map.put(updated_assessment, :has_token_counter, has_token_counter)
×
110
      end
111

UNCOV
112
    updated_assessment =
×
113
      if is_nil(has_voting_features) do
UNCOV
114
        updated_assessment
×
115
      else
UNCOV
116
        Map.put(updated_assessment, :has_voting_features, has_voting_features)
×
117
      end
118

UNCOV
119
    is_reassigning_voting =
×
UNCOV
120
      if is_nil(assign_entries_for_voting) do
×
121
        false
122
      else
123
        assign_entries_for_voting
×
124
      end
125

UNCOV
126
    with {:ok, assessment} <- check_dates(open_at, close_at, updated_assessment),
×
UNCOV
127
         {:ok, _nil} <- Assessments.update_assessment(assessment_id, assessment),
×
UNCOV
128
         {:ok, _nil} <- Assessments.reassign_voting(assessment_id, is_reassigning_voting) do
×
UNCOV
129
      text(conn, "OK")
×
130
    else
131
      {:error, {status, message}} ->
132
        conn
133
        |> put_status(status)
UNCOV
134
        |> text(message)
×
135
    end
136
  end
137

138
  def get_score_leaderboard(conn, %{"assessmentid" => assessment_id, "course_id" => course_id}) do
UNCOV
139
    voting_questions =
×
140
      Question
141
      |> where(type: :voting)
UNCOV
142
      |> where(assessment_id: ^assessment_id)
×
143
      |> Repo.one()
144

UNCOV
145
    contest_id = Assessments.fetch_associated_contest_question_id(course_id, voting_questions)
×
146

UNCOV
147
    result =
×
148
      contest_id
149
      |> Assessments.fetch_top_relative_score_answers(10)
150
      |> Enum.map(fn entry ->
UNCOV
151
        AssessmentsHelpers.build_contest_leaderboard_entry(entry)
×
152
      end)
153

UNCOV
154
    render(conn, "leaderboard.json", leaderboard: result)
×
155
  end
156

157
  def get_popular_leaderboard(conn, %{"assessmentid" => assessment_id, "course_id" => course_id}) do
UNCOV
158
    voting_questions =
×
159
      Question
160
      |> where(type: :voting)
UNCOV
161
      |> where(assessment_id: ^assessment_id)
×
162
      |> Repo.one()
163

UNCOV
164
    contest_id = Assessments.fetch_associated_contest_question_id(course_id, voting_questions)
×
165

UNCOV
166
    result =
×
167
      contest_id
168
      |> Assessments.fetch_top_popular_score_answers(10)
169
      |> Enum.map(fn entry ->
UNCOV
170
        AssessmentsHelpers.build_popular_leaderboard_entry(entry)
×
171
      end)
172

UNCOV
173
    render(conn, "leaderboard.json", leaderboard: result)
×
174
  end
175

176
  defp check_dates(open_at, close_at, assessment) do
UNCOV
177
    if is_nil(open_at) and is_nil(close_at) do
×
178
      {:ok, assessment}
179
    else
UNCOV
180
      formatted_open_date = elem(DateTime.from_iso8601(open_at), 1)
×
UNCOV
181
      formatted_close_date = elem(DateTime.from_iso8601(close_at), 1)
×
182

UNCOV
183
      if Timex.before?(formatted_close_date, formatted_open_date) do
×
184
        {:error, {:bad_request, "New end date should occur after new opening date"}}
185
      else
UNCOV
186
        assessment = Map.put(assessment, :open_at, formatted_open_date)
×
UNCOV
187
        assessment = Map.put(assessment, :close_at, formatted_close_date)
×
188
        {:ok, assessment}
189
      end
190
    end
191
  end
192

193
  defp is_same_course(course_id, assessment_id) do
194
    Assessment
195
    |> where(id: ^assessment_id)
UNCOV
196
    |> where(course_id: ^course_id)
×
UNCOV
197
    |> Repo.exists?()
×
198
  end
199

200
  swagger_path :index do
1✔
201
    get("/courses/{course_id}/admin/users/{courseRegId}/assessments")
202

203
    summary("Fetches assessment overviews of a user")
204

205
    security([%{JWT: []}])
206

207
    parameters do
208
      courseRegId(:path, :integer, "Course Reg ID", required: true)
209
    end
210

211
    response(200, "OK", Schema.array(:AssessmentsList))
212
    response(401, "Unauthorised")
213
    response(403, "Forbidden")
214
  end
215

216
  swagger_path :create do
1✔
217
    post("/courses/{course_id}/admin/assessments")
218

219
    summary("Creates a new assessment or updates an existing assessment")
220

221
    security([%{JWT: []}])
222

223
    consumes("multipart/form-data")
224

225
    parameters do
226
      assessment(:formData, :file, "Assessment to create or update", required: true)
227
      forceUpdate(:formData, :boolean, "Force update", required: true)
228
    end
229

230
    response(200, "OK")
231
    response(400, "XML parse error")
232
    response(403, "Forbidden")
233
  end
234

235
  swagger_path :delete do
1✔
236
    PhoenixSwagger.Path.delete("/courses/{course_id}/admin/assessments/{assessmentId}")
237

238
    summary("Deletes an assessment")
239

240
    security([%{JWT: []}])
241

242
    parameters do
243
      assessmentId(:path, :integer, "Assessment ID", required: true)
244
    end
245

246
    response(200, "OK")
247
    response(403, "Forbidden")
248
  end
249

250
  swagger_path :update do
1✔
251
    post("/courses/{course_id}/admin/assessments/{assessmentId}")
252

253
    summary("Updates an assessment")
254

255
    security([%{JWT: []}])
256

257
    consumes("application/json")
258

259
    parameters do
260
      assessmentId(:path, :integer, "Assessment ID", required: true)
261

262
      assessment(:body, Schema.ref(:AdminUpdateAssessmentPayload), "Updated assessment details",
263
        required: true
264
      )
265
    end
266

267
    response(200, "OK")
268
    response(401, "Assessment is already opened")
269
    response(403, "Forbidden")
270
  end
271

272
  swagger_path :get_popular_leaderboard do
×
273
    get("/courses/{course_id}/admin/assessments/:assessmentid/popularVoteLeaderboard")
274

275
    summary("get the top 10 contest entries based on popularity")
276

277
    security([%{JWT: []}])
278

279
    parameters do
280
      assessmentId(:path, :integer, "Assessment ID", required: true)
281
    end
282

283
    response(200, "OK", Schema.array(:Leaderboard))
284
    response(401, "Unauthorised")
285
    response(403, "Forbidden")
286
  end
287

288
  swagger_path :get_score_leaderboard do
×
289
    get("/courses/{course_id}/admin/assessments/:assessmentid/scoreLeaderboard")
290

291
    summary("get the top 10 contest entries based on score")
292

293
    security([%{JWT: []}])
294

295
    parameters do
296
      assessmentId(:path, :integer, "Assessment ID", required: true)
297
    end
298

299
    response(200, "OK", Schema.array(:Leaderboard))
300
    response(401, "Unauthorised")
301
    response(403, "Forbidden")
302
  end
303

304
  def swagger_definitions do
305
    %{
1✔
306
      # Schemas for payloads to modify data
307
      AdminUpdateAssessmentPayload:
308
        swagger_schema do
1✔
309
          properties do
1✔
310
            closeAt(:string, "Open date", required: false)
311
            openAt(:string, "Close date", required: false)
312
            isPublished(:boolean, "Whether the assessment is published", required: false)
313
            maxTeamSize(:number, "Max team size of the assessment", required: false)
1✔
314
          end
315
        end
316
    }
317
  end
318
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