328 lines
7.6 KiB
Ruby
328 lines
7.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#
|
|
# Copyright (C) 2013 - present Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
class GradeSummaryAssignmentPresenter
|
|
include TextHelper
|
|
attr_reader :assignment, :submission, :originality_reports
|
|
|
|
def initialize(summary, current_user, assignment, submission)
|
|
@summary = summary
|
|
@current_user = current_user
|
|
@assignment = assignment
|
|
@submission = submission
|
|
@originality_reports = @submission.originality_reports_for_display if @submission
|
|
end
|
|
|
|
def upload_status
|
|
return unless submission
|
|
|
|
# The sort here ensures that statuses received are in the failed,
|
|
# pending and success order. With that security we can just pluck
|
|
# first one.
|
|
submission.attachments
|
|
.map { |a| AttachmentUploadStatus.upload_status(a) }
|
|
.min
|
|
end
|
|
|
|
def originality_report?
|
|
@originality_reports.present?
|
|
end
|
|
|
|
def show_distribution_graph?
|
|
@assignment.score_statistic = @summary.assignment_stats[assignment.id] # Avoid another query
|
|
@assignment.can_view_score_statistics?(@current_user)
|
|
end
|
|
|
|
def is_unread?
|
|
(submission.present? ? @summary.unread_submission_ids.include?(submission.id) : false)
|
|
end
|
|
|
|
def hide_grade_from_student?
|
|
submission.blank? || submission.hide_grade_from_student?
|
|
end
|
|
|
|
def graded?
|
|
return false if submission.blank?
|
|
|
|
(submission.grade || submission.excused?) && !hide_grade_from_student?
|
|
end
|
|
|
|
def is_letter_graded?
|
|
assignment.grading_type == "letter_grade"
|
|
end
|
|
|
|
def is_gpa_scaled?
|
|
assignment.grading_type == "gpa_scale"
|
|
end
|
|
|
|
def points_graded?
|
|
assignment.grading_type == "points"
|
|
end
|
|
|
|
def is_letter_graded_or_gpa_scaled?
|
|
is_letter_graded? || is_gpa_scaled?
|
|
end
|
|
|
|
def is_assignment?
|
|
assignment.class.to_s == "Assignment"
|
|
end
|
|
|
|
def has_no_group_weight?
|
|
!(assignment.group_weight rescue false)
|
|
end
|
|
|
|
def has_no_score_display?
|
|
hide_grade_from_student? || submission.nil?
|
|
end
|
|
|
|
def quiz_pending_review?
|
|
submission&.submission_type == "online_quiz" && submission&.workflow_state == "pending_review"
|
|
end
|
|
|
|
def original_points
|
|
has_no_score_display? ? "" : submission.published_score
|
|
end
|
|
|
|
def unchangeable?
|
|
(!@summary.editable? || assignment.special_class)
|
|
end
|
|
|
|
def has_comments?
|
|
submission&.visible_submission_comments && !submission.visible_submission_comments.empty?
|
|
end
|
|
|
|
def has_scoring_details?
|
|
return false unless submission&.score.present? && assignment&.points_possible.present?
|
|
|
|
assignment.points_possible > 0 && !hide_grade_from_student?
|
|
end
|
|
|
|
def has_grade_distribution?
|
|
return false if assignment&.points_possible.blank?
|
|
|
|
assignment.points_possible > 0 && !hide_grade_from_student?
|
|
end
|
|
|
|
def has_rubric_assessments?
|
|
!rubric_assessments.empty?
|
|
end
|
|
|
|
def is_text_entry?
|
|
submission.submission_type == "online_text_entry"
|
|
end
|
|
|
|
def is_online_upload?
|
|
submission.submission_type == "online_upload"
|
|
end
|
|
|
|
def should_display_details?
|
|
!assignment.special_class && (has_comments? || has_scoring_details?)
|
|
end
|
|
|
|
def special_class
|
|
assignment.special_class ? ("hard_coded " + assignment.special_class) : "editable"
|
|
end
|
|
|
|
def show_submission_details?
|
|
is_assignment? && !!submission&.can_view_details?(@current_user)
|
|
end
|
|
|
|
def classes
|
|
classes = ["student_assignment"]
|
|
classes << "assignment_graded" if graded?
|
|
classes << special_class
|
|
classes << "excused" if excused?
|
|
classes << "extended" if extended?
|
|
classes.join(" ")
|
|
end
|
|
|
|
def missing?
|
|
submission.try(:missing?)
|
|
end
|
|
|
|
def late?
|
|
submission.try(:late?)
|
|
end
|
|
|
|
def excused?
|
|
submission.try(:excused?)
|
|
end
|
|
|
|
def extended?
|
|
submission.try(:extended?)
|
|
end
|
|
|
|
def deduction_present?
|
|
!!(submission&.points_deducted&.> 0)
|
|
end
|
|
|
|
def entered_grade
|
|
if is_letter_graded_or_gpa_scaled? && submission.entered_grade.present?
|
|
"(#{submission.entered_grade})"
|
|
else
|
|
""
|
|
end
|
|
end
|
|
|
|
def display_entered_score
|
|
"#{I18n.n round_if_whole(submission.entered_score)} #{entered_grade}"
|
|
end
|
|
|
|
def display_points_deducted
|
|
I18n.n round_if_whole(-submission.points_deducted)
|
|
end
|
|
|
|
def published_grade
|
|
if is_letter_graded_or_gpa_scaled? && !submission.published_grade.nil?
|
|
"(#{submission.published_grade})"
|
|
else
|
|
""
|
|
end
|
|
end
|
|
|
|
def display_score
|
|
if has_no_score_display?
|
|
""
|
|
else
|
|
"#{I18n.n round_if_whole(submission.published_score)} #{published_grade}"
|
|
end
|
|
end
|
|
|
|
def turnitin
|
|
plagiarism("turnitin")
|
|
end
|
|
|
|
def vericite
|
|
plagiarism("vericite")
|
|
end
|
|
|
|
def plagiarism(type)
|
|
plag_data = if type == "vericite"
|
|
submission.vericite_data(true)
|
|
else
|
|
submission.originality_data
|
|
end
|
|
t = if is_text_entry?
|
|
plag_data[OriginalityReport.submission_asset_key(submission)] ||
|
|
plag_data[submission.asset_string]
|
|
elsif is_online_upload? && file
|
|
plag_data[file.asset_string]
|
|
end
|
|
t.try(:[], :state) ? t : nil
|
|
end
|
|
|
|
def grade_distribution
|
|
@grade_distribution ||= if (stats = @summary.assignment_stats[assignment.id])
|
|
[stats.maximum, stats.minimum, stats.mean].map { |stat| stat.to_f.round(2) }
|
|
end
|
|
end
|
|
|
|
def graph
|
|
@graph ||= begin
|
|
high, low, mean = grade_distribution
|
|
score = submission&.score
|
|
GradeSummaryGraph.new(high, low, mean, assignment.points_possible, score)
|
|
end
|
|
end
|
|
|
|
def file
|
|
@file ||= submission.attachments.detect { |a| plagiarism_attachment?(a) }
|
|
end
|
|
|
|
def plagiarism_attachment?(a)
|
|
@originality_reports.any? { |o| o.attachment == a } ||
|
|
(submission.turnitin_data && submission.turnitin_data[a.asset_string]).present? ||
|
|
(submission.vericite_data(true) && submission.vericite_data(true)[a.asset_string]).present?
|
|
end
|
|
|
|
def comments
|
|
submission.visible_submission_comments
|
|
end
|
|
|
|
def rubric_assessments
|
|
return [] unless submission
|
|
|
|
submission.visible_rubric_assessments_for(@current_user)
|
|
end
|
|
|
|
def group
|
|
@group ||= assignment&.assignment_group
|
|
end
|
|
|
|
def viewing_fake_student?
|
|
@summary.student_enrollment.fake_student?
|
|
end
|
|
end
|
|
|
|
class GradeSummaryGraph
|
|
FULLWIDTH = 150.0
|
|
|
|
def initialize(high, low, mean, points_possible, score)
|
|
@high = high.to_f
|
|
@mean = mean.to_f
|
|
@low = low.to_f
|
|
@points_possible = points_possible.to_f
|
|
@score = score
|
|
end
|
|
|
|
def low_width
|
|
pixels_for(@low)
|
|
end
|
|
|
|
def high_left
|
|
pixels_for(@high)
|
|
end
|
|
|
|
def high_width
|
|
pixels_for(@points_possible - @high)
|
|
end
|
|
|
|
def mean_left
|
|
pixels_for(@mean)
|
|
end
|
|
|
|
def mean_low_width
|
|
pixels_for(@mean - @low)
|
|
end
|
|
|
|
def mean_high_width
|
|
pixels_for(@high - @mean)
|
|
end
|
|
|
|
def max_left
|
|
[FULLWIDTH.round, (pixels_for(@high) + 3)].max
|
|
end
|
|
|
|
def score_left
|
|
pixels_for(@score) - 5
|
|
end
|
|
|
|
def title
|
|
I18n.t("#grade_summary.graph_title", "Mean %{mean}, High %{high}, Low %{low}", {
|
|
mean: I18n.n(@mean), high: I18n.n(@high), low: I18n.n(@low)
|
|
})
|
|
end
|
|
|
|
private
|
|
|
|
def pixels_for(value)
|
|
(value.to_f / @points_possible * FULLWIDTH).round
|
|
end
|
|
end
|