Skip to content
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

Merged
merged 69 commits into from
Jun 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
a87b621
Added Question and Assessment
sreycodes May 20, 2018
971641d
Added functionality for grading
sreycodes May 20, 2018
5ca0341
Added assessments functionality
sreycodes May 20, 2018
f2a81cc
Fixed typo
sreycodes May 20, 2018
948abc7
Restructured assessments
sreycodes May 20, 2018
b030281
Added assessments context
May 23, 2018
20a25a5
Added controllers
May 24, 2018
ed5ca63
Delete duplicate file
sreycodes May 24, 2018
4319fc9
Changes to suit convention
May 24, 2018
b865a25
Fixed stuff
sreycodes May 24, 2018
b15aba4
Ran mix commands
sreycodes May 24, 2018
a185c5e
Small fixes
May 25, 2018
16df491
Fix changes
May 25, 2018
b6e06cd
Removed role validation
May 25, 2018
21168b9
Removed all compile time errors
sreycodes May 25, 2018
1d3de81
Fixed consistency issues
sreycodes May 25, 2018
f6c265d
Refactored
sreycodes May 25, 2018
7520e5b
Conversion to spaces
sreycodes May 25, 2018
e9d1e57
Merge branch 'master' into assessments
sreycodes May 26, 2018
70b9bcf
More functionality for questions
sreycodes May 26, 2018
695b99e
Merge branch 'assessments' of https://github.com/source-academy/cadet…
sreycodes May 26, 2018
d315083
Adding tests
sreycodes May 26, 2018
9b99754
Refactored
sreycodes May 26, 2018
94379c0
Added few tests for missions
sreycodes May 26, 2018
c8b5fcc
Few more tests to go
sreycodes May 26, 2018
7363ed8
Merge branch 'master' into assessments
sreycodes May 26, 2018
51a4f6a
Added more tests
sreycodes May 29, 2018
eca4d22
Delete useless migration
sreycodes May 29, 2018
6b6fe5e
Delete useless migration 2
sreycodes May 29, 2018
13e1352
Added submission test
sreycodes Jun 2, 2018
f739667
Merge branch 'assessments' of https://github.com/source-academy/cadet…
sreycodes Jun 2, 2018
f0ce3a7
Added question tests and switched controllers to new branch
sreycodes Jun 2, 2018
891879d
Added answers test
sreycodes Jun 2, 2018
19ea6a9
Remove unneccesary stuff
sreycodes Jun 2, 2018
ebcf903
Formatting
sreycodes Jun 2, 2018
2a50ca2
Fixed compile error
sreycodes Jun 2, 2018
6e8f098
Make changes according to review
sreycodes Jun 4, 2018
006052f
Fixes
sreycodes Jun 4, 2018
6181b4e
Added tests to question and answer
sreycodes Jun 5, 2018
a6a2580
Merge branch 'master' into assessments
indocomsoft Jun 8, 2018
6344375
Merge branch 'master' into assessments
sreycodes Jun 9, 2018
8f27f57
Added question types
sreycodes Jun 9, 2018
b6b08a9
Merge branch 'assessments' of https://github.com/source-academy/cadet…
sreycodes Jun 9, 2018
ed232c8
Various fixes
indocomsoft Jun 9, 2018
35c7d47
Merge branch 'assessments' of https://github.com/source-academy/cadet…
sreycodes Jun 10, 2018
2f15410
Changes according to question types
sreycodes Jun 10, 2018
21e7139
Assessments context
sreycodes Jun 13, 2018
48ec19d
Fixes
sreycodes Jun 13, 2018
4868540
Merge branch 'master' into assessments
sreycodes Jun 13, 2018
9cd468f
Removed invalid test
sreycodes Jun 13, 2018
157929f
Fix some tests
sreycodes Jun 13, 2018
2ef4900
Merge branch 'master' into assessments
sreycodes Jun 16, 2018
843bfa6
Assessments housecleaning (#51)
tuesmiddt Jun 18, 2018
d466842
Merge branch 'master' into assessments
indocomsoft Jun 18, 2018
5f49622
Fix credo
indocomsoft Jun 18, 2018
bb91e3d
Remove duplicate blank line
indocomsoft Jun 23, 2018
60e9cb6
Remove pre-commit
indocomsoft Jun 23, 2018
5d7d9f7
Refactor json and rename Mission to Assessment
indocomsoft Jun 23, 2018
ef3c47d
Merge branch 'master' into assessments
indocomsoft Jun 23, 2018
9451e3f
Fix submission model
indocomsoft Jun 23, 2018
8518c0f
Use cadet context
indocomsoft Jun 23, 2018
988b205
Fix some json tests
indocomsoft Jun 23, 2018
8c79b7a
Fix tests and logic error
indocomsoft Jun 23, 2018
f222fbc
Remove untested method and add one test
indocomsoft Jun 23, 2018
c28e0b7
Remove useless import
indocomsoft Jun 23, 2018
8783065
Add test
indocomsoft Jun 23, 2018
b6d1bfe
Fix according to code review
indocomsoft Jun 24, 2018
a6b45e4
Change to pipeline syntax
indocomsoft Jun 24, 2018
14e5ac3
Reorganise file
indocomsoft Jun 24, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ erl_crash.dump
.terraform
*.tfstate
*.backup

2 changes: 0 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,3 @@ config :guardian, Guardian.DB,
token_types: ["access"],
# default: 60 minute
sweep_interval: 60

config :pre_commit, commands: ["format --check-formatted", "test", "credo"]
61 changes: 59 additions & 2 deletions lib/cadet.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule Cadet do
use Ecto.Schema

import Ecto.Changeset
import Cadet.ModelHelper
end
end

Expand All @@ -19,10 +20,66 @@ defmodule Cadet do
alias Cadet.Repo

import Ecto.Changeset
import Cadet.ContextHelper
end
end

defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
def display do
quote do
import Cadet.DisplayHelper
end
end

def remote_assets do
quote do
use Arc.Definition
use Arc.Ecto.Definition
end
end

defp apply_single(context) do
apply(__MODULE__, context, [])
end

defp apply_multiple(contexts) do
contexts
|> Enum.filter(&is_atom/1)
|> Enum.map(&apply_single/1)
|> join_context_quotes
end

defp join_context_quotes(context_quotes) do
Enum.reduce(
context_quotes,
quote do
end,
fn context, acc ->
quote do
unquote(acc)
unquote(context)
end
end
)
end

@doc """
The `use Cadet` macro supports both single and multiple arguments i.e.:
```
use Cadet, :context

use Cadet, [:context, :model, :etc]
```
"""
defmacro __using__(opt) do
cond do
is_atom(opt) ->
apply_single(opt)

is_list(opt) ->
apply_multiple(opt)

true ->
raise "invalid arguments when using Cadet contexts"
end
end
end
32 changes: 32 additions & 0 deletions lib/cadet/assessments/answer.ex
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)

This comment was marked as resolved.

belongs_to(:question, Question)

This comment was marked as resolved.

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
5 changes: 1 addition & 4 deletions lib/cadet/assessments/answer_types/mcq_answer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ defmodule Cadet.Assessments.AnswerTypes.MCQAnswer do
The Assessments.QuestionTypes.MCQQuestion entity represents an MCQ Answer.
It comprises of one of the MCQ choices.
"""
use Ecto.Schema

import Ecto.Changeset
# TODO: use Cadet context after !34 is merged
use Cadet, :model

embedded_schema do
field(:choice_id, :integer)
Expand Down
4 changes: 1 addition & 3 deletions lib/cadet/assessments/answer_types/programming_answer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ defmodule Cadet.Assessments.AnswerTypes.ProgrammingAnswer do
@moduledoc """
The ProgrammingQuestion entity represents a Programming question.
"""
use Ecto.Schema

import Ecto.Changeset
use Cadet, :model

embedded_schema do
field(:code, :string)
Expand Down
57 changes: 57 additions & 0 deletions lib/cadet/assessments/assessment.ex
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
131 changes: 119 additions & 12 deletions lib/cadet/assessments/assessments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Copy link
Contributor

@tuesmiddt tuesmiddt Jun 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use the all_assessments(category) method above but I think just leave it for now.

In general I think we should combine this and the Enum.filter below into one query string so that the DB engine only gives us the results we want instead of fetching too much data into memory and then filtering it. Can leave that for a later refactor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll write it down as a TODO

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#58

# 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
3 changes: 1 addition & 2 deletions lib/cadet/assessments/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ defmodule Cadet.Assessments.Image do
@moduledoc """
Image assets used by the missions
"""
use Arc.Definition
use Arc.Ecto.Definition
use Cadet, :remote_assets

@versions [:original]

Expand Down
26 changes: 0 additions & 26 deletions lib/cadet/assessments/mission.ex

This file was deleted.

6 changes: 6 additions & 0 deletions lib/cadet/assessments/problem_type.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import EctoEnum

defenum(Cadet.Assessments.ProblemType, :type, [
:programming,
:multiple_choice
])
Loading