Skip to content

Commit

Permalink
feat: add endpoints for course statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuhanming committed Dec 4, 2022
1 parent 53d35ec commit 52c919d
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 1 deletion.
89 changes: 88 additions & 1 deletion app/controllers/course/statistics/aggregate_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
# frozen_string_literal: true
# This is named aggregate controller as naming this as course controller leads to name conflict issues
class Course::Statistics::AggregateController < Course::Statistics::Controller
before_action :preload_levels, only: [:all_students]
before_action :preload_levels, only: [:all_students, :course_performance]

def course_progression
@assessment_info_array = assessment_info_array
@user_submission_array = user_submission_array
end

def course_performance
@students = students
@course_videos = current_course.videos
@course_video_count = @course_videos.exists? ? @course_videos.count : 0
@correctness_hash = correctness_hash
@has_personalized_timeline = current_course.show_personalized_timeline_features?
end

def all_staff
@staff = current_course.course_users.teaching_assistant_and_manager.includes(:group_users)
Expand All @@ -15,10 +28,84 @@ def all_students

private

def assessment_info_array
@assessment_info_array ||= Course::Assessment.published.with_default_reference_time.
where.not(course_reference_times: { end_at: nil }).
where(course_id: current_course.id).
pluck(:id, :title, :start_at, :end_at)
end

def user_submission_array # rubocop:disable Metrics/AbcSize
submission_data_arr = Course::Assessment::Submission.joins(creator: :course_users).
where(assessment_id: assessment_info_array.map { |i| i[0] },
course_users: { course_id: current_course.id, role: :student }).
group(:creator_id, 'course_users.name', 'course_users.phantom').
pluck(:creator_id, 'course_users.name', 'course_users.phantom',
'json_agg(assessment_id)', 'array_agg(submitted_at)')

submission_data_arr.map do |sub_data|
assessment_to_submitted_at = sub_data[3].zip(sub_data[4]).map do |assessment_id, submitted_at|
if submitted_at.nil?
nil
else
[assessment_id, submitted_at]
end
end.compact

[sub_data[0], sub_data[1], sub_data[2], assessment_to_submitted_at] # id, name, phantom, [ass_id, sub_at]
end
end

def correctness_hash
query = CourseUser.find_by_sql(<<-SQL.squish
SELECT
id,
AVG(correctness) AS correctness
FROM (
SELECT
cu.id AS id,
SUM(caa.grade) / SUM(caq.maximum_grade) AS correctness
FROM
course_assessment_categories cat
INNER JOIN course_assessment_tabs tab
ON tab.category_id = cat.id
INNER JOIN course_assessments ca
ON ca.tab_id = tab.id
INNER JOIN course_assessment_submissions cas
ON cas.assessment_id = ca.id
INNER JOIN course_assessment_answers caa
ON caa.submission_id = cas.id
INNER JOIN course_assessment_questions caq
ON caa.question_id = caq.id
INNER JOIN course_users cu
ON cu.user_id = cas.creator_id
WHERE
cat.course_id = #{current_course.id}
AND caa.current_answer IS true
AND cas.workflow_state IN ('graded', 'published')
AND cu.course_id = #{current_course.id}
AND cu.role = 0
GROUP BY
cu.id,
cas.assessment_id
HAVING
SUM(caq.maximum_grade) > 0
) course_user_assessment_correctness
GROUP BY
id
SQL
)
query.map { |u| [u.id, u.correctness] }.to_h
end

def course_users
@course_users ||= current_course.course_users.includes(:groups)
end

def students
@students = current_course.course_users.students.with_performance_statistics
end

def group_manager_preload_service
staff = course_users.staff
Course::GroupManagerPreloadService.new(staff)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true
show_personalized_timeline = current_course.show_personalized_timeline_features?
show_video = @course_videos.exists? && can?(:analyze_videos, current_course)
is_course_gamified = current_course.gamified?

json.students @students do |student|
json.id student.id
json.name student.name
json.isPhantom student.phantom?
json.numSubmissions student.assessment_submission_count
json.correctness @correctness_hash[student.id]

json.learningRate student.latest_learning_rate if show_personalized_timeline

if is_course_gamified
json.achievementCount student.achievement_count
json.level student.level_number
json.experiencePoints student.experience_points
json.experiencePointsLink course_user_experience_points_records_path(current_course, student)
end

if show_video
json.videoSubmissionCount student.video_submission_count
json.videoPercentWatched student.video_percent_watched
json.videoSubmissionLink course_user_video_submissions_path(current_course, student)
end
end

json.hasPersonalizedTimeline @has_personalized_timeline
json.isCourseGamified is_course_gamified
json.showVideo show_video
json.courseVideoCount @course_video_count
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true
json.assessments @assessment_info_array do |assessment|
json.id assessment[0]
json.title assessment[1]
json.startAt assessment[2].iso8601
json.endAt assessment[3]&.iso8601
end

json.submissions @user_submission_array do |user|
json.id user[0]
json.name user[1]
json.isPhantom user[2]
json.submissions user[3] do |submission|
json.assessmentId submission[0]
json.submittedAt submission[1].iso8601
end
end
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@
get '/' => 'statistics#index'
get 'course/students' => 'aggregate#all_students'
get 'course/staff' => 'aggregate#all_staff'
get 'course/course/progression' => 'aggregate#course_progression'
get 'course/course/performance' => 'aggregate#course_performance'
end

scope module: :video do
Expand Down

0 comments on commit 52c919d

Please sign in to comment.