-
Notifications
You must be signed in to change notification settings - Fork 50
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
Add Publish Grading Feature #965
Add Publish Grading Feature #965
Conversation
Current submissions will have the is_grading_published field populated as `true` if all answers have been graded i.e. have a grader_id, while the rest will default to `false`. Future submissions will have the field with default value `false` upon creation.
- Update existing endpoints and queries related to assessment retrieval - Add new routes and endpoints to handle new publish grading feature - Add notification for new feature
Hi! Please fix the format and CI and tag me again when the PR is ready. Thank you! |
@YaleChen299 |
No worries! Can you check the CI again? I will approve the CI run when I see then. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @emptygx, I have fixed the formatting and credo issues. However, a few tests are not passing. I suspect that the behaviour of user_total_xp
is affected by the behaviour of published/unpublished grades.
What is the expected behaviour for the published/unpublished grades? Is it that users will not see their grades if unpublished?
Could you look into these test cases and fix them? Please document the correct behaviours in terms of the test cases, i.e. for any behaviours that may be affected by the publish grading feature, create at least one positive and one negative test case.
Thank you so much for wrapping up this properly! I think this feature would make the grading activities much more user-friendly!
lib/cadet/assessments/assessments.ex
Outdated
@@ -298,6 +308,11 @@ defmodule Cadet.Assessments do | |||
|> where([s], s.student_id == ^cr.id) | |||
|> select([s], [:assessment_id, :status]) | |||
|
|||
grading_published_status = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this subquery can be merged with the previous one.
lib/cadet/assessments/assessments.ex
Outdated
@@ -309,11 +324,15 @@ defmodule Cadet.Assessments do | |||
on: a.id == sa.assessment_id | |||
) | |||
|> join(:left, [a, _], s in subquery(submission_status), on: a.id == s.assessment_id) | |||
|> select([a, sa, s], %{ | |||
|> join(:left, [a, _, _], gp in subquery(grading_published_status), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similarly here, this can be merged with the other query.
lib/cadet/assessments/assessments.ex
Outdated
a | ||
| xp: sa.xp, | ||
| xp: fragment("CASE WHEN ? THEN ? ELSE 0 END", gp.is_grading_published, sa.xp), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally feel that this hiding logic should be done at the Controller level, before sending the response to the front end, since technically the xp is not zero to the backend's knowledge(this will not affect the current behaviours but this will make the code more maintainable in the future as other feature might need to use this xp value).
lib/cadet/assessments/assessments.ex
Outdated
# and return all answers | ||
|> select([a, s], %{ | ||
a | ||
| xp: fragment("CASE WHEN ? THEN ? ELSE 0 END", s.is_grading_published, a.xp), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment. I think the hiding logic should be done at the controller layer as other logic might use the xp value. However, I am okay with this logic at the moment since our models are already filled with a lot of controller logic, and this is a bit troublesome to do at the controller level. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this to be the easier and more straightforward way to filter the data, but if maintainability may be an issue, then doing it at the controller level might be better like you said. I am not too sure how to go about that though and may need some guidance!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that making this filtering at the controller level is a bit more troublesome. I think what we can do is at least separate this filtering from the main subquery selection. If you can make it into a function to be piped through with sematic name like filter_published_xp
, I will be happy to accept that.
3ce9105
to
cf98cff
Compare
lib/cadet/assessments/assessments.ex
Outdated
role == :admin or bypass or | ||
Cadet.Accounts.Query.avenger_of?(cr, submission.student_id)} do | ||
Multi.new() | ||
|> Multi.run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is no need to use multi and transaction here since there is only a single update. May II ask what was the reason for this?
Re-thinking about the problem that we are trying to solve, I think we need to auto un-publish the grades when we unsubmit. |
Yes, I agree that should be part of the feature, which has already been implemented as part of the logic on the front end, which auto-unpublishes grades whenever an assessment is unsubmitted. Sorry for not mentioning that in the PR description, will make sure to add it into future documentation! Should I add it to the PR description as well? Oh, thanks for the clarification! In that case what if the unsubmit went through successfully but the unpublish request failed? Will it be better for that to be handled in one single request? @RichDom2185 |
This commit prevents admin/staff from individually publishing/unpublishing grades for students.
Ensures total ordering of migration files are preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for working on this! Just some comments below, thanks!
answers_query = | ||
Answer | ||
|> group_by([ans], ans.submission_id) | ||
|> select([ans], %{ | ||
submission_id: ans.submission_id, | ||
graded_count: filter(count(ans.id), not is_nil(ans.grader_id)), | ||
autograded_count: filter(count(ans.id), ans.autograding_status == :success) | ||
}) | ||
|
||
question_query = | ||
Question | ||
|> group_by([q], q.assessment_id) | ||
|> join(:inner, [q], a in Assessment, on: q.assessment_id == a.id) | ||
|> select([q, a], %{ | ||
assessment_id: q.assessment_id, | ||
question_count: count(q.id) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I'm wrong, but since these are subqueries, they'll run first in entirety before any downstream filtering.
So in order to avoid the ~30s query from the joining of the large table, should we filter by course ID here first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I managed to filter the Question table subquery but the Answer table subquery requires quite a few joins to obtain the course_id to filter by. I was wondering if it is worth the multiple joins since I'm not too sure about costs in SQL.
Joins:
Answer -> Question -> Assessment (course_id)
I also thought of combining the queries together since I already use Question -> Assessment but you cant use aggregate functions e.g. filter and count
@@ -258,6 +260,14 @@ defmodule Cadet.Autograder.GradingJob do | |||
) | |||
end | |||
|
|||
defp grade_submission_question_answer_lists(_, [], [], _, _) do | |||
defp grade_submission_question_answer_lists(submission_id, [], [], _, _) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to query as follows?
submission =
Submission
|> join(:inner, [s], a in assoc(s, :assessment))
|> join(:inner, [_, a], c in assoc(a, :config))
|> preload([_, a, c], assessment: {a, config: c})
|> Repo.get(submission_id)
is_grading_auto_published = submission.assessment.config.is_grading_auto_published
|> Answer.autograding_changeset(changes) | ||
|> Repo.update() | ||
|
||
submission = Repo.get(Submission, submission_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Let's merge this first to not block the other team's PRs. As to your question, let's discuss it after this is merged.
Thanks for the work!
To be merged with Frontend #2856
For implementation decisions, see Frontend PR.
Changes
success
autograding_status