-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathcourses_controller.rb
More file actions
178 lines (143 loc) · 7.53 KB
/
courses_controller.rb
File metadata and controls
178 lines (143 loc) · 7.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
class CoursesController < ApplicationController
before_action :authenticate_user
before_action :set_course, only: %i[show edit sync_assignments sync_enrollments sync_status bulk_update_assignments enrollments delete]
before_action :set_pending_request_count
before_action :determine_user_role
def index
teacher_courses = UserToCourse.includes(:course).where(user: @user, role: UserToCourse.staff_roles)
@teacher_courses_by_semester = group_by_semester(teacher_courses)
# Only show courses to students if extensions are enabled at the course level
student_courses = UserToCourse.includes(course: :course_settings).where(user: @user, role: 'student')
visible_student_courses = student_courses.select do |utc|
course_settings = utc.course.course_settings
course_settings.nil? || course_settings.enable_extensions
end
@student_courses_by_semester = group_by_semester(visible_student_courses)
# Keep flat lists for conditional checks in the view
@teacher_courses = teacher_courses
@student_courses = visible_student_courses
end
def show
return redirect_to courses_path, alert: 'Course not found.' unless @course
return redirect_to courses_path, alert: 'No Canvas LMS data found for this course.' unless @course.has_canvas_linked?
@side_nav = 'show'
@course.regenerate_readonly_api_token_if_blank
if @role == 'student'
course_settings = @course.course_settings
return redirect_to courses_path, alert: 'Extensions are not enabled for this course.' unless course_settings&.enable_extensions
@assignments = @course.enabled_assignments
else
@assignments = @course.assignments
end
render_role_based_view
end
def new
token = @user.lms_credentials.first.token
@courses = Course.fetch_courses(token)
flash[:alert] = 'No courses found.' if @courses.empty?
@semesters = @courses.filter_map { |c| c.dig('term', 'name') }.uniq.sort
@selected_semester = params[:semester]
# TODO: Add spec for when a course is created, but the user is not enrolled in it.
# TODO: Why do some courses have empty enrollments?
existing_canvas_ids = @user.courses.pluck(:canvas_id)
@courses_teacher = filter_courses(@courses, UserToCourse.staff_roles, existing_canvas_ids)
@courses_student = filter_courses(@courses, [ UserToCourse::STUDENT_ROLE ], existing_canvas_ids)
if @selected_semester.present?
@courses_teacher = filter_by_semester(@courses_teacher, @selected_semester)
@courses_student = filter_by_semester(@courses_student, @selected_semester)
end
end
def edit
@side_nav = 'edit'
redirect_to course_path(@course.id), alert: 'You do not have access to this page.' unless @role == 'instructor'
end
def create
token = @user.lms_credentials.first.token
filter_courses(Course.fetch_courses(token), UserToCourse.staff_roles)
.select { |c| params[:courses]&.include?(c['id'].to_s) }
.each { |course_api| Course.create_or_update_from_canvas(course_api, token, @user) }
redirect_to courses_path, notice: 'Selected courses and their assignments have been imported successfully.'
end
def sync_assignments
return render json: { error: 'Course not found.' }, status: :not_found unless @course
@course.sync_assignments(@user)
render json: { message: 'Assignments synced successfully.' }, status: :ok
end
def bulk_update_assignments
return render json: { error: 'Course not found.' }, status: :not_found unless @course
return render json: { error: 'You do not have permission.' }, status: :forbidden unless @role == 'instructor'
enabled = ActiveModel::Type::Boolean.new.cast(params[:enabled])
scope = Assignment.where(course_to_lms_id: CourseToLms.where(course_id: @course.id).select(:id))
scope = scope.where.not(due_date: nil) if enabled
scope.update_all(enabled: enabled) # rubocop:disable Rails/SkipsModelValidations
render json: { success: true }, status: :ok
end
def sync_enrollments
return render json: { error: 'Course not found.' }, status: :not_found unless @course
return render json: { error: 'You do not have permission.' }, status: :forbidden unless @is_course_admin
@course.sync_all_enrollments_from_canvas(@user.id)
render json: { message: 'Users synced successfully.' }, status: :ok
end
def sync_status
return render json: { error: 'You do not have permission.' }, status: :forbidden unless @is_course_admin
course_to_lms = @course.course_to_lms(1)
return render json: { error: 'LMS connection not found.' }, status: :not_found unless course_to_lms
render json: {
roster_synced_at: course_to_lms.recent_roster_sync&.dig('synced_at'),
assignments_synced_at: course_to_lms.recent_assignment_sync&.dig('synced_at')
}, status: :ok
end
def enrollments
@side_nav = 'enrollments'
return redirect_to courses_path, alert: 'You do not have access to this page.' unless @role == 'instructor'
@enrollments = @course.user_to_courses.includes(:user)
@is_course_admin = @course.course_admin?(@user)
@approved_late_days = Request.total_approved_late_days_by_user(@course)
end
def delete
return redirect_to courses_path, alert: 'You do not have access to this page.' unless @role == 'instructor'
return redirect_to courses_path, alert: 'Extensions are enabled for this course.' if @course.course_settings&.enable_extensions
assignments = Assignment.where(course_to_lms_id: CourseToLms.where(course_id: @course.id).select(:id))
Extension.where(assignment_id: assignments.select(:id)).destroy_all
assignments.destroy_all
CourseToLms.where(course_id: @course.id).destroy_all
UserToCourse.where(course_id: @course.id).destroy_all
Request.where(course_id: @course.id).destroy_all
CourseSettings.where(course_id: @course.id).destroy_all
FormSetting.where(course_id: @course.id).destroy_all
Course.where.missing(:user_to_courses).destroy_all
redirect_to courses_path, notice: 'Course deleted successfully.'
end
private
def set_course
@course = Course.find_by(id: params[:id])
redirect_to courses_path, alert: 'Course not found.' unless @course
end
def determine_user_role
@role = @course&.user_role(@user)
@is_course_admin = @course&.course_admin?(@user) || false
end
# Groups UserToCourse records by their course's semester, sorted most-recent-first.
# Returns an array of [semester_name, [user_to_courses]] pairs.
def group_by_semester(user_to_courses)
grouped = user_to_courses.group_by { |utc| utc.course.semester }
sorted_semesters = Course.sort_semesters(grouped.keys)
sorted_semesters.map { |semester| [ semester, grouped[semester] ] }
end
def filter_by_semester(courses, semester)
courses.select { |c| c.dig('term', 'name') == semester }
end
# TODO: This should be moved to the Canvas Facade
def filter_courses(courses, roles, exclude_ids = [])
missing_enrollments = courses.select { |course| course['enrollments'].blank? }
Rails.logger.warn("Canvas API by #{current_user.id}: Courses with missing enrollments: #{missing_enrollments.pluck('id').join(', ')}") unless missing_enrollments.empty?
courses = courses - missing_enrollments - courses.select { |course| exclude_ids.include?(course['id'].to_s) }
return [] if courses.empty?
courses.select do |course|
course['enrollments'].any? { |enrollment| roles.include?(UserToCourse.role_from_canvas_enrollment(enrollment)) }
end
end
def course_data_for_sync
{ 'id' => @course.canvas_id, 'name' => @course.course_name, 'course_code' => @course.course_code }
end
end