-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathschool_classes_controller.rb
More file actions
217 lines (176 loc) · 7.37 KB
/
school_classes_controller.rb
File metadata and controls
217 lines (176 loc) · 7.37 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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# frozen_string_literal: true
module Api
class SchoolClassesController < ApiController
before_action :authorize_user
before_action :load_and_authorize_school
before_action :load_and_authorize_school_class
def index
school_classes = accessible_school_classes
school_classes = school_classes.joins(:teachers).where(teachers: { teacher_id: current_user&.id }) if params[:my_classes] == 'true'
@school_classes_with_teachers = school_classes.with_teachers
if current_user&.school_teacher?(@school) || current_user&.school_owner?(@school)
render :teacher_index, formats: [:json], status: :ok
else
render_student_index(school_classes)
end
end
def show
@school_class_with_teachers = @school_class.with_teachers
render :show, formats: [:json], status: :ok
end
def create
result = SchoolClass::Create.call(school: @school, school_class_params:, current_user:)
if result.success?
@school_class_with_teachers = result[:school_class].with_teachers
render :show, formats: [:json], status: :created
else
render json: { error: result[:error] }, status: :unprocessable_content
end
end
def import
school_class_params = import_school_class_params
school_students_params = import_school_students_params
# Find or create the school class
school_class_result = find_or_create_school_class(school_class_params)
if school_class_result.success?
school_class = school_class_result[:school_class]
@school_class_with_teachers = school_class.with_teachers
# Create students if class exists
school_students_result = create_school_students(school_students_params, school_class)
@school_students = school_students_result[:school_students]
@school_students_errors = school_students_result[:errors]
# Assign students to class
@class_members = assign_students_to_class(school_class, @school_students)
render :import, formats: [:json], status: :created
else
render json: { error: school_class_result[:error] }, status: :unprocessable_content
end
end
def update
school_class = @school.classes.find(params[:id])
result = SchoolClass::Update.call(school_class:, school_class_params:)
if result.success?
@school_class_with_teachers = result[:school_class].with_teachers
render :show, formats: [:json], status: :ok
else
render json: { error: result[:error] }, status: :unprocessable_content
end
end
def destroy
result = SchoolClass::Delete.call(school: @school, school_class_id: params[:id])
if result.success?
head :no_content
else
render json: { error: result[:error] }, status: :unprocessable_content
end
end
private
def render_student_index(school_classes)
unread_counts = calculate_unread_counts(school_classes)
@school_classes_with_teachers_and_unread_counts = @school_classes_with_teachers.zip(unread_counts)
render :student_index, formats: [:json], status: :ok
end
def calculate_unread_counts(school_classes)
class_ids = school_classes.map(&:id)
counts_by_class_id =
SchoolProject
.joins(:feedback)
.joins(project: { parent: { lesson: :school_class } })
.where(projects: { user_id: current_user.id })
.where(feedback: { read_at: nil })
.where(school_classes: { id: class_ids })
.merge(Lesson.accessible_by(current_ability))
.group('school_classes.id')
.count('DISTINCT school_projects.id') # Count distinct projects, not feedback records
school_classes.map { |school_class| counts_by_class_id[school_class.id] || 0 }
end
def find_or_create_school_class(school_class_params)
# First try and find the class (in case we're re-importing)
existing_school_class = SchoolClass.find_by(
school: @school,
import_origin: school_class_params[:import_origin],
import_id: school_class_params[:import_id]
)
if existing_school_class.present?
response = OperationResponse.new
response[:school_class] = existing_school_class
return response
end
# Create new school class if none exists
SchoolClass::Create.call(
school: @school,
school_class_params: school_class_params,
current_user:,
validate_context: :import
)
end
def accessible_school_classes
if current_user&.school_teacher?(@school) || current_user&.school_owner?(@school)
@school.classes.accessible_by(current_ability).includes(lessons: { project: { remixes: { school_project: :school_project_transitions } } })
else
@school.classes.accessible_by(current_ability)
end
end
def create_school_students(school_students_params, school_class)
return { school_students: [], errors: nil } unless school_class.present? && school_students_params.present?
school_students_result = SchoolStudent::CreateBatchSSO.call(
school: @school,
school_students_params: school_students_params,
current_user:
)
{
school_students: school_students_result[:school_students],
errors: school_students_result[:errors]
}
end
def assign_students_to_class(school_class, school_students)
return [] unless school_class.present? && school_students.present? && school_students.any?
# Extract the student objects for class member creation
students = school_students.pluck(:student)
class_members_result = ClassMember::Create.call(school_class:, students:, teachers: [])
# Put the errors in a more useful format for the response
class_members_errors = class_members_result[:errors].map do |user_id, error|
{ success: false, student_id: user_id, school_class_id: school_class.id, error: error }
end
class_members_result[:class_members] + class_members_errors
end
def load_and_authorize_school
@school = if params[:school_id].match?(/\d\d-\d\d-\d\d/)
School.find_by(code: params[:school_id])
else
School.find(params[:school_id])
end
authorize! :read, @school
end
def load_and_authorize_school_class
if %w[index create import].include?(params[:action])
authorize! params[:action].to_sym, SchoolClass
else
@school_class = if params[:id].match?(/\d\d-\d\d-\d\d/)
@school.classes.find_by(code: params[:id])
else
@school.classes.find(params[:id])
end
authorize! params[:action].to_sym, @school_class
end
end
def school_class_params
# A school teacher may only create classes they own.
params.expect(school_class: %i[name description])
end
def import_school_class_params
params.expect(school_class: %i[name description import_origin import_id])
end
def import_school_students_params
school_students_data = params[:school_students]
return [] if school_students_data.blank?
school_students_data.filter_map do |student|
next if student.blank?
student.permit(:name, :email).to_h.with_indifferent_access
end
end
def school_owner?
current_user.school_owner?(@school)
end
end
end