-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Assessments #34
Assessments #34
Changes from all commits
a87b621
971641d
5ca0341
f2a81cc
948abc7
b030281
20a25a5
ed5ca63
4319fc9
b865a25
b15aba4
a185c5e
16df491
b6e06cd
21168b9
1d3de81
f6c265d
7520e5b
e9d1e57
70b9bcf
695b99e
d315083
9b99754
94379c0
c8b5fcc
7363ed8
51a4f6a
eca4d22
6b6fe5e
13e1352
f739667
f0ce3a7
891879d
19ea6a9
ebcf903
2a50ca2
6e8f098
006052f
6181b4e
a6a2580
6344375
8f27f57
b6b08a9
ed232c8
35c7d47
2f15410
21e7139
48ec19d
4868540
9cd468f
157929f
2ef4900
843bfa6
d466842
5f49622
bb91e3d
60e9cb6
5d7d9f7
ef3c47d
9451e3f
8518c0f
988b205
8c79b7a
f222fbc
c28e0b7
8783065
b6d1bfe
a6b45e4
14e5ac3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,4 @@ erl_crash.dump | |
.terraform | ||
*.tfstate | ||
*.backup | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
defmodule Cadet.Assessments.Answer do | ||
@moduledoc """ | ||
Answers model contains domain logic for answers management for | ||
programming and multiple choice questions. | ||
""" | ||
use Cadet, :model | ||
|
||
alias Cadet.Assessments.ProblemType | ||
alias Cadet.Assessments.Submission | ||
alias Cadet.Assessments.Question | ||
|
||
schema "answers" do | ||
field(:marks, :float, default: 0.0) | ||
field(:answer, :map) | ||
field(:type, ProblemType) | ||
field(:raw_answer, :string, virtual: true) | ||
belongs_to(:submission, Submission) | ||
belongs_to(:question, Question) | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
timestamps() | ||
end | ||
|
||
@required_fields ~w(answer type)a | ||
@optional_fields ~w(marks raw_answer)a | ||
|
||
def changeset(answer, params) do | ||
answer | ||
|> cast(params, @required_fields ++ @optional_fields) | ||
|> validate_required(@required_fields) | ||
|> validate_number(:marks, greater_than_or_equal_to: 0.0) | ||
|> put_json(:answer, :raw_answer) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
defmodule Cadet.Assessments.Assessment do | ||
@moduledoc """ | ||
The Assessment entity stores metadata of a students' assessment | ||
(mission, sidequest, path, and contest) | ||
""" | ||
use Cadet, :model | ||
use Arc.Ecto.Schema | ||
|
||
alias Cadet.Assessments.Category | ||
alias Cadet.Assessments.Image | ||
alias Cadet.Assessments.Question | ||
alias Cadet.Assessments.Upload | ||
|
||
schema "assessments" do | ||
field(:title, :string) | ||
field(:is_published, :boolean, default: false) | ||
field(:category, Category) | ||
field(:summary_short, :string) | ||
field(:summary_long, :string) | ||
field(:open_at, Timex.Ecto.DateTime) | ||
field(:close_at, Timex.Ecto.DateTime) | ||
field(:max_xp, :integer, default: 0) | ||
field(:cover_picture, Image.Type) | ||
field(:mission_pdf, Upload.Type) | ||
field(:order, :string, default: "") | ||
has_many(:questions, Question, on_delete: :delete_all) | ||
timestamps() | ||
end | ||
|
||
@required_fields ~w(category title open_at close_at max_xp)a | ||
@optional_fields ~w(summary_short summary_long is_published max_xp)a | ||
@optional_file_fields ~w(cover_picture mission_pdf)a | ||
|
||
def changeset(mission, params) do | ||
params = | ||
params | ||
|> convert_date(:open_at) | ||
|> convert_date(:close_at) | ||
|
||
mission | ||
|> cast(params, @required_fields ++ @optional_fields) | ||
|> validate_required(@required_fields) | ||
|> validate_number(:max_xp, greater_than_or_equal_to: 0) | ||
|> cast_attachments(params, @optional_file_fields) | ||
|> validate_open_close_date | ||
end | ||
|
||
defp validate_open_close_date(changeset) do | ||
validate_change(changeset, :open_at, fn :open_at, open_at -> | ||
if Timex.before?(open_at, get_field(changeset, :close_at)) do | ||
[] | ||
else | ||
[open_at: "Open date must be before close date"] | ||
end | ||
end) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,29 +3,136 @@ defmodule Cadet.Assessments do | |
Assessments context contains domain logic for assessments management such as | ||
missions, sidequests, paths, etc. | ||
""" | ||
use Cadet, :context | ||
use Cadet, [:context, :display] | ||
|
||
alias Cadet.Assessments.QuestionTypes.MCQQuestion | ||
alias Cadet.Assessments.QuestionTypes.ProgrammingQuestion | ||
import Ecto.Query | ||
|
||
# To be uncommented when assessments context is merged | ||
alias Timex.Duration | ||
|
||
alias Cadet.Assessments.Assessment | ||
alias Cadet.Assessments.Question | ||
|
||
def all_assessments() do | ||
Repo.all(Assessment) | ||
end | ||
|
||
def all_assessments(category) do | ||
Repo.all(from(a in Assessment, where: a.category == ^category)) | ||
end | ||
|
||
def all_open_assessments(category) do | ||
now = Timex.now() | ||
|
||
assessment_with_category = Repo.all(from(a in Assessment, where: a.category == ^category)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can use the In general I think we should combine this and the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I'll write it down as a TODO There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
# TODO: Refactor to be done on SQL instead of in-memory | ||
Enum.filter(assessment_with_category, &(&1.is_published and Timex.before?(&1.open_at, now))) | ||
end | ||
|
||
def assessments_due_soon() do | ||
now = Timex.now() | ||
week_after = Timex.add(now, Duration.from_weeks(1)) | ||
|
||
all_assessments() | ||
|> Enum.filter( | ||
&(&1.is_published and Timex.before?(&1.open_at, now) and | ||
Timex.between?(&1.close_at, now, week_after)) | ||
) | ||
end | ||
|
||
def build_assessment(params) do | ||
Assessment.changeset(%Assessment{}, params) | ||
end | ||
|
||
def build_question(params) do | ||
Question.changeset(%Question{}, params) | ||
end | ||
|
||
def create_assessment(params) do | ||
params | ||
|> build_assessment | ||
|> Repo.insert() | ||
end | ||
|
||
def update_assessment(id, params) do | ||
simple_update( | ||
Assessment, | ||
id, | ||
using: &Assessment.changeset/2, | ||
params: params | ||
) | ||
end | ||
|
||
def update_question(id, params) do | ||
simple_update( | ||
Question, | ||
id, | ||
using: &Question.changeset/2, | ||
params: params | ||
) | ||
end | ||
|
||
def publish_assessment(id) do | ||
id | ||
|> get_assessment() | ||
|> change(%{is_published: true}) | ||
|> Repo.update() | ||
end | ||
|
||
def get_question(id) do | ||
Repo.get(Question, id) | ||
end | ||
|
||
def get_assessment(id) do | ||
Repo.get(Assessment, id) | ||
end | ||
|
||
def create_question_for_assessment(params, assessment_id) | ||
when is_binary(assessment_id) or is_number(assessment_id) do | ||
assessment = get_assessment(assessment_id) | ||
create_question_for_assessment(params, assessment) | ||
end | ||
|
||
def create_question_for_assessment(params, assessment) do | ||
Repo.transaction(fn -> | ||
assessment = Repo.preload(assessment, :questions) | ||
questions = assessment.questions | ||
|
||
changeset = | ||
params | ||
|> build_question | ||
|> put_assoc(:assessment, assessment) | ||
|> put_display_order(questions) | ||
|
||
case Repo.insert(changeset) do | ||
{:ok, question} -> question | ||
{:error, changeset} -> Repo.rollback(changeset) | ||
end | ||
end) | ||
end | ||
|
||
def delete_question(id) do | ||
question = Repo.get(Question, id) | ||
Repo.delete(question) | ||
end | ||
|
||
# TODO: Decide what to do with these methods | ||
# def create_multiple_choice_question(json_attr) when is_binary(json_attr) do | ||
# %MCQQuestion{} | ||
# |> MCQQuestion.changeset(%{raw_mcqquestion: json_attr}) | ||
# %MCQQuestion{} | ||
# |> MCQQuestion.changeset(%{raw_mcqquestion: json_attr}) | ||
# end | ||
|
||
# def create_multiple_choice_question(attr = %{}) do | ||
# %MCQQuestion{} | ||
# |> MCQQuestion.changeset(attr) | ||
# %MCQQuestion{} | ||
# |> MCQQuestion.changeset(attr) | ||
# end | ||
|
||
# def create_programming_question(json_attr) when is_binary(json_attr) do | ||
# %ProgrammingQuestion{} | ||
# |> ProgrammingQuestion.changeset(%{raw_programmingquestion: json_attr}) | ||
# %ProgrammingQuestion{} | ||
# |> ProgrammingQuestion.changeset(%{raw_programmingquestion: json_attr}) | ||
# end | ||
|
||
# def create_programming_question(attr = %{}) do | ||
# %ProgrammingQuestion{} | ||
# |> ProgrammingQuestion.changeset(attr) | ||
# %ProgrammingQuestion{} | ||
# |> ProgrammingQuestion.changeset(attr) | ||
# end | ||
end |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import EctoEnum | ||
|
||
defenum(Cadet.Assessments.ProblemType, :type, [ | ||
:programming, | ||
:multiple_choice | ||
]) |
This comment was marked as resolved.
Sorry, something went wrong.