Skip to content

Commit 431a058

Browse files
committed
Added GroundControl
1 parent 8654b13 commit 431a058

File tree

5 files changed

+292
-91
lines changed

5 files changed

+292
-91
lines changed

lib/cadet/assessments/assessments.ex

Lines changed: 148 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,56 @@ defmodule Cadet.Assessments do
1616
@xp_early_submission_max_bonus 100
1717
@xp_bonus_assessment_type ~w(mission sidequest)a
1818
@submit_answer_roles ~w(student)a
19+
@change_dates_assessment_role ~w(staff admin)a
20+
@delete_assessment_role ~w(staff admin)a
21+
@publish_assessment_role ~w(staff admin)a
1922
@unsubmit_assessment_role ~w(staff admin)a
2023
@grading_roles ~w()a
2124
@see_all_submissions_roles ~w(staff admin)a
2225
@open_all_assessment_roles ~w(staff admin)a
2326

27+
def change_dates_assessment(_user = %User{role: role}, id, close_at, open_at) do
28+
if role in @change_dates_assessment_role do
29+
assessment = Repo.get(Assessment, id)
30+
previous_open_time = assessment.open_at
31+
cond do
32+
Timex.before?(close_at, open_at) ->
33+
{:error, {:bad_request, "New end date should occur after new opening date"}}
34+
35+
Timex.before?(close_at, Timex.now()) ->
36+
{:error, {:bad_request, "New end date should occur after current time"}}
37+
38+
Timex.equal?(previous_open_time, open_at) or Timex.after?(previous_open_time, Timex.now()) ->
39+
update_assessment(id, %{close_at: close_at, open_at: open_at})
40+
41+
Timex.before?(open_at, Timex.now()) ->
42+
{:error, {:bad_request, "New Opening date should occur after current time"}}
43+
44+
true ->
45+
{:error, {:forbidden, "Assessment is already opened"}}
46+
end
47+
else
48+
{:error, {:forbidden, "User is not permitted to edit"}}
49+
end
50+
end
51+
52+
def toggle_publish_assessment(_publisher = %User{role: role}, id, bool) do
53+
if role in @publish_assessment_role do
54+
update_assessment(id, %{is_published: bool})
55+
else
56+
{:error, {:forbidden, "User is not permitted to publish"}}
57+
end
58+
end
59+
60+
def delete_assessment(_deleter = %User{role: role}, id) do
61+
if role in @delete_assessment_role do
62+
assessment = Repo.get(Assessment, id)
63+
Repo.delete(assessment)
64+
else
65+
{:error, {:forbidden, "User is not permitted to delete"}}
66+
end
67+
end
68+
2469
@spec user_total_xp(%User{}) :: integer()
2570
def user_total_xp(%User{id: user_id}) when is_ecto_id(user_id) do
2671
total_xp_bonus =
@@ -163,11 +208,18 @@ defmodule Cadet.Assessments do
163208

164209
def assessment_with_questions_and_answers(id, user = %User{}, password)
165210
when is_ecto_id(id) do
211+
role = user.role
166212
assessment =
167-
Assessment
168-
|> where(id: ^id)
169-
|> where(is_published: true)
170-
|> Repo.one()
213+
if role in @open_all_assessment_roles do
214+
Assessment
215+
|> where(id: ^id)
216+
|> Repo.one()
217+
else
218+
Assessment
219+
|> where(id: ^id)
220+
|> where(is_published: true)
221+
|> Repo.one()
222+
end
171223

172224
if assessment do
173225
assessment_with_questions_and_answers(assessment, user, password)
@@ -206,11 +258,7 @@ defmodule Cadet.Assessments do
206258
assessment_with_questions_and_answers(id, user, nil)
207259
end
208260

209-
@doc """
210-
Returns a list of assessments with all fields and an indicator showing whether it has been attempted
211-
by the supplied user
212-
"""
213-
def all_published_assessments(user = %User{}) do
261+
def all_assessments(user = %User{}) do
214262
assessments =
215263
Query.all_assessments_with_max_xp_and_grade()
216264
|> subquery()
@@ -240,7 +288,7 @@ defmodule Cadet.Assessments do
240288
question_count: q_count.count,
241289
graded_count: a_count.count
242290
})
243-
|> where(is_published: true)
291+
|> filter_published_assessments(user)
244292
|> order_by(:open_at)
245293
|> Repo.all()
246294
|> Enum.map(fn assessment = %Assessment{} ->
@@ -259,6 +307,14 @@ defmodule Cadet.Assessments do
259307
{:ok, assessments}
260308
end
261309

310+
def filter_published_assessments(assessments, user) do
311+
role = user.role
312+
case role do
313+
:student -> where(assessments, is_published: true)
314+
_ -> assessments
315+
end
316+
end
317+
262318
defp build_grading_status(submission_status, a_type, q_count, g_count) do
263319
case a_type do
264320
type when type in [:mission, :sidequest] ->
@@ -283,53 +339,108 @@ defmodule Cadet.Assessments do
283339
@doc """
284340
The main function that inserts or updates assessments from the XML Parser
285341
"""
286-
@spec insert_or_update_assessments_and_questions(map(), [map()]) ::
342+
@spec insert_or_update_assessments_and_questions(map(), [map()], boolean()) ::
287343
{:ok, any()}
288344
| {:error, Ecto.Multi.name(), any(), %{optional(Ecto.Multi.name()) => any()}}
289-
def insert_or_update_assessments_and_questions(assessment_params, questions_params) do
345+
def insert_or_update_assessments_and_questions(assessment_params, questions_params, force_update) do
290346
assessment_multi =
291347
Multi.insert_or_update(
292348
Multi.new(),
293349
:assessment,
294-
insert_or_update_assessment_changeset(assessment_params)
350+
insert_or_update_assessment_changeset(assessment_params, force_update)
295351
)
296352

297-
questions_params
298-
|> Enum.with_index(1)
299-
|> Enum.reduce(assessment_multi, fn {question_params, index}, multi ->
300-
Multi.run(multi, String.to_atom("question#{index}"), fn _repo,
301-
%{assessment: %Assessment{id: id}} ->
302-
question_params
303-
|> Map.put(:display_order, index)
304-
|> build_question_changeset_for_assessment_id(id)
305-
|> Repo.insert()
353+
if force_update and check_question_count(assessment_multi, questions_params) do
354+
{:error, "Question count is different"}
355+
else
356+
questions_params
357+
|> Enum.with_index(1)
358+
|> Enum.reduce(assessment_multi, fn {question_params, index}, multi ->
359+
Multi.run(multi, String.to_atom("question#{index}"), fn _repo,
360+
%{assessment: %Assessment{id: id}} ->
361+
question_exists =
362+
Repo.exists?(where(Question, [q], q.assessment_id == ^id and q.display_order == ^index))
363+
if !force_update or !question_exists do
364+
question_params
365+
|> Map.put(:display_order, index)
366+
|> build_question_changeset_for_assessment_id(id)
367+
|> Repo.insert()
368+
else
369+
params =
370+
(if !question_params.max_xp do
371+
question_params
372+
|> Map.put(:max_xp, 0)
373+
else
374+
question_params
375+
end)
376+
|> Map.put(:display_order, index)
377+
378+
%{id: question_id} =
379+
where(Question, [q], q.display_order == ^index and q.assessment_id == ^id)
380+
|> Repo.one()
381+
382+
changeset = Question.changeset(%Question{assessment_id: id, id: question_id}, params)
383+
Repo.update(changeset)
384+
end
385+
end)
306386
end)
307-
end)
308-
|> Repo.transaction()
387+
|> Repo.transaction()
388+
end
309389
end
310390

311-
@spec insert_or_update_assessment_changeset(map()) :: Ecto.Changeset.t()
312-
defp insert_or_update_assessment_changeset(params = %{number: number}) do
391+
defp check_question_count(assessment_multi, questions_params) do
392+
assessment_id =
393+
(assessment_multi.operations
394+
|> List.first()
395+
|> elem(1)
396+
|> elem(1)).data.id
397+
398+
if !assessment_id do
399+
false
400+
else
401+
existing_questions_count =
402+
where(Question, [q], q.assessment_id == ^assessment_id)
403+
|> Repo.all()
404+
|> Enum.count
405+
406+
new_questions_count = Enum.count(questions_params)
407+
408+
existing_questions_count != new_questions_count
409+
end
410+
end
411+
412+
@spec insert_or_update_assessment_changeset(map(), boolean()) :: Ecto.Changeset.t()
413+
defp insert_or_update_assessment_changeset(params = %{number: number}, force_update) do
313414
Assessment
314415
|> where(number: ^number)
315416
|> Repo.one()
316417
|> case do
317418
nil ->
318419
Assessment.changeset(%Assessment{}, params)
319-
320420
assessment ->
321-
if Timex.after?(assessment.open_at, Timex.now()) do
322-
# Delete all existing questions
323-
%{id: assessment_id} = assessment
421+
cond do
422+
Timex.after?(assessment.open_at, Timex.now()) ->
423+
# Delete all existing questions
424+
%{id: assessment_id} = assessment
324425

325-
Question
326-
|> where(assessment_id: ^assessment_id)
327-
|> Repo.delete_all()
426+
Question
427+
|> where(assessment_id: ^assessment_id)
428+
|> Repo.delete_all()
328429

329-
Assessment.changeset(assessment, params)
330-
else
331-
# if the assessment is already open, don't mess with it
332-
create_invalid_changeset_with_error(:assessment, "is already open")
430+
Assessment.changeset(assessment, params)
431+
432+
force_update ->
433+
# Maintain the same open/close date when force updating an assessment
434+
new_params =
435+
params
436+
|> Map.delete(:open_at)
437+
|> Map.delete(:close_at)
438+
439+
Assessment.changeset(assessment, new_params)
440+
441+
true ->
442+
# if the assessment is already open, don't mess with it
443+
create_invalid_changeset_with_error(:assessment, "is already open")
333444
end
334445
end
335446
end

0 commit comments

Comments
 (0)