canvas_shim 0.1.6 → 0.1.7rc

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 609839eafaec6666e51e55d0cc8d6edbf03fe6b3
4
- data.tar.gz: 3bbd30dc0a3a84a7b71b41d699dfea5d94368e94
2
+ SHA256:
3
+ metadata.gz: b5d833c2d89b8226175ffb44f45af903d1ec442e841449b8fe98c113b4c0e411
4
+ data.tar.gz: 26de1db9614e6743aff4a6ecdeaae65c434cbca86c5084ab0acfe63419c1533e
5
5
  SHA512:
6
- metadata.gz: 589f68146ae0b4b3cc2d087d3ae09de8174abc5af2619fd0cd736859001aaf0b09514be2a180dc8a1cab277cb25df15f11c5785a3ad83ef16b6de460e59c0a24
7
- data.tar.gz: 9cec254fdf2b4050229c08839c29fd52bc579e30c0ed575c66849148011bef387db8ba699aaabc59f24df0617183bc6e7d758c745224d71add04d650432d8aa0
6
+ metadata.gz: 9d0eec50ee26d4e9a2ebbc20552f5417079054bf43f2a42c3f3d97d67fd9716077d2897d82b6c93ac5fe29af5e8187cc49ab8cc7ea1592abf5036d7f6c946d9f
7
+ data.tar.gz: 6bafe2b3171ff7018c5c390619fc36bfa8cf5eeb77f25783dc68dcff31bd6a7647c6a764d418a30c81cd644de83264c839415aa831102ee1e6d9806ac2b2a132
@@ -35,9 +35,7 @@ module PipelineService
35
35
  end
36
36
 
37
37
  def post_to_pipeline
38
- client.new(
39
- @args.merge(object: object)
40
- ).call
38
+ client.new(@args.merge(object: object)).call
41
39
  end
42
40
  end
43
41
  end
@@ -46,7 +46,6 @@ module PipelineService
46
46
  end
47
47
 
48
48
  def configure_dependencies
49
- @message_class = @args[:message_class] || PipelinePublisher::Message
50
49
  @fetcher = @args[:fetcher] || Serializers::Fetcher
51
50
  @logger = @args[:logger] || PipelineService::Logger
52
51
  @canvas_domain = ENV['CANVAS_DOMAIN']
@@ -58,11 +57,11 @@ module PipelineService
58
57
  end
59
58
 
60
59
  def build
61
- message_class.new(payload.to_hash)
60
+ payload.to_hash
62
61
  end
63
62
 
64
63
  def noun
65
- object.class.to_s.underscore
64
+ object.class.to_s.split('::').last.underscore
66
65
  end
67
66
 
68
67
  def id
@@ -1,16 +1,13 @@
1
1
  module PipelineService
2
2
  module Events
3
3
  class Emitter
4
- def initialize(args={})
4
+ def initialize args={}
5
5
  @object = args[:object]
6
6
  @args = args
7
- @responder = @args[:responder] || Events::Responders::SIS
8
- @event = Events::GradedOutEvent
7
+ @responder = @args[:responder] || Events::Responders::SIS
9
8
  end
10
9
 
11
10
  def call
12
- return unless object.is_a?(Enrollment)
13
- fetch_serializer
14
11
  build_message
15
12
  build_responder
16
13
  build_subscriptions
@@ -19,26 +16,49 @@ module PipelineService
19
16
 
20
17
  private
21
18
 
22
- attr_reader :subscriptions, :object, :responder, :message, :serializer, :event
19
+ attr_reader :subscriptions, :object, :responder, :message
20
+
21
+ def events
22
+ {
23
+ graded_out: Events::GradedOutEvent,
24
+ grade_changed: Events::GradeChangedEvent
25
+ }
26
+ end
23
27
 
24
28
  def build_subscriptions
25
- @subscriptions = [
26
- Events::Subscription.new(
27
- event: :graded_out,
28
- responder: responder
29
+ @subscriptions = []
30
+
31
+ @subscriptions << Events::Subscription.new(
32
+ event: :graded_out,
33
+ responder: Events::Responders::SIS.new(
34
+ object: object,
35
+ message: message
36
+ )
37
+ )
38
+
39
+ @subscriptions << Events::Subscription.new(
40
+ event: :grade_changed,
41
+ responder: Events::Responders::SISUnitGrade.new(
42
+ object: object,
43
+ message: message
29
44
  )
30
- ]
45
+ )
31
46
  end
32
47
 
33
48
  def build_responder
34
- @responder = responder.new(
49
+ @responder = Events::Responders::SIS.new(
35
50
  object: object,
36
51
  message: message
37
52
  )
38
53
  end
39
54
 
40
- def fetch_serializer
41
- @serializer = Serializers::CanvasAPIEnrollment
55
+ def serializer
56
+ case object
57
+ when PipelineService::Nouns::UnitGrades
58
+ Serializers::UnitGrades
59
+ when Enrollment
60
+ Serializers::CanvasAPIEnrollment
61
+ end
42
62
  end
43
63
 
44
64
  def build_message
@@ -47,8 +67,10 @@ module PipelineService
47
67
 
48
68
  def emit
49
69
  subscriptions.each do |subscription|
50
- next if subscription.event != :graded_out
51
- event.new(@args.merge(subscription: subscription)).emit
70
+ next unless events[subscription.event]
71
+ events[subscription.event].new(
72
+ @args.merge(subscription: subscription)
73
+ ).emit
52
74
  end
53
75
  end
54
76
  end
@@ -0,0 +1,24 @@
1
+ module PipelineService
2
+ module Events
3
+ class GradeChangedEvent
4
+ def initialize args
5
+ @args = args
6
+ @subscription = args[:subscription]
7
+ @object = args[:object]
8
+ @changes = args[:changes]
9
+ end
10
+
11
+ def emit
12
+ return unless should_trigger?
13
+ @subscription.responder.call
14
+ end
15
+
16
+ private
17
+
18
+ def should_trigger?
19
+ return false unless @changes['score']
20
+ @object.is_a?(PipelineService::Nouns::UnitGrades)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,61 @@
1
+ module PipelineService
2
+ module Events
3
+ module Responders
4
+ class SISUnitGrade
5
+ HEADERS = { 'Content-Type' => 'application/json' }
6
+
7
+ def initialize(object:, message:, args: {})
8
+ @message = message
9
+ @args = args
10
+ configure_dependencies
11
+ end
12
+
13
+ def call
14
+ raise 'Missing config' if missing_config?
15
+ queue.enqueue(self)
16
+ end
17
+
18
+ def perform
19
+ post
20
+ log
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :message, :api_key, :endpoint, :args, :queue
26
+
27
+ def configure_dependencies
28
+ @api_key = ENV['SIS_UNIT_GRADE_ENDPOINT_API_KEY']
29
+ @endpoint = ENV['SIS_UNIT_GRADE_ENDPOINT']
30
+ @queue = args[:queue] || Delayed::Job
31
+ end
32
+
33
+ def missing_config?
34
+ [@api_key, @endpoint].any?(&:nil?)
35
+ end
36
+
37
+ def log
38
+ PipelineService::Logger.new(
39
+ source: 'pipeline_event::grade_changed',
40
+ message: message,
41
+ enpdoint: build_endpoint
42
+ ).call
43
+ end
44
+
45
+ def build_endpoint
46
+ endpoint + '?apiKey=' + api_key
47
+ end
48
+
49
+ def post
50
+ return unless message
51
+
52
+ PipelineService::Events::HTTPClient.post(
53
+ build_endpoint,
54
+ body: message.to_json,
55
+ headers: HEADERS
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -11,7 +11,8 @@ module PipelineService
11
11
  end
12
12
 
13
13
  def post(args)
14
- PipelinePublisher::MessagesApi.new.messages_post(*args)
14
+ message = PipelinePublisher::Message.new(*args)
15
+ PipelinePublisher::MessagesApi.new.messages_post(message)
15
16
  end
16
17
 
17
18
  def get(endpoint, args)
@@ -0,0 +1,13 @@
1
+ module PipelineService
2
+ module Nouns
3
+ class UnitGrades
4
+ attr_reader :course, :student, :id, :changes
5
+ def initialize(submission)
6
+ @course = submission.assignment.course
7
+ @student = submission.user
8
+ @id = submission.id
9
+ @changes = submission.changes
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,6 +6,8 @@ module PipelineService
6
6
  # Maps any object with a class containing 'enrollment' to the enrollment serializer
7
7
  def self.fetch(object:)
8
8
  case object.class.to_s
9
+ when /PipelineService::Nouns::UnitGrades/
10
+ PipelineService::Serializers::UnitGrades
9
11
  when /Enrollment/
10
12
  PipelineService::Serializers::Enrollment
11
13
  else
@@ -0,0 +1,17 @@
1
+ module PipelineService
2
+ module Serializers
3
+ class UnitGrades
4
+ def initialize(object:)
5
+ @course = object.course
6
+ @student = object.student
7
+ end
8
+
9
+ def call
10
+ UnitsService::Commands::GetUnitGrades.new(
11
+ course: @course,
12
+ student: @student
13
+ ).call
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ module UnitsService
2
+ module Commands
3
+ class GetUnitGrades
4
+ def initialize(course:, student:)
5
+ @course = course
6
+ @student = student
7
+ end
8
+
9
+ def call
10
+ get_submissions
11
+ calculate_grades
12
+ payload
13
+ end
14
+
15
+ private
16
+
17
+ def get_submissions
18
+ @unit_submissions = UnitsService::Queries::GetSubmissions.new(
19
+ course: @course,
20
+ student: @student
21
+ ).query
22
+ end
23
+
24
+ def payload
25
+ {
26
+ school_domain: ENV['CANVAS_DOMAIN'],
27
+ course_id: @course.id,
28
+ student_id: @student.id,
29
+ units: @grades.map {|unit, score| {
30
+ id: unit.id,
31
+ position: unit.position,
32
+ created_at: unit.created_at,
33
+ score: score
34
+ }}
35
+ }
36
+ end
37
+
38
+ def calculate_grades
39
+ @grades = UnitsService::GradesCalculator.new(@unit_submissions, @course).call
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,39 @@
1
+ module UnitsService
2
+ class GradesCalculator
3
+ def initialize unit_submissions, course
4
+ @unit_submissions = unit_submissions
5
+ @course = course
6
+ end
7
+
8
+ def call
9
+ result = {}
10
+
11
+ @unit_submissions.each do |unit, submissions|
12
+ next if submissions.count == 0
13
+ result[unit] = weighted_average(submissions)
14
+ end
15
+
16
+ result
17
+ end
18
+
19
+ private
20
+
21
+ def weighted_average(submissions)
22
+ result = {}
23
+ submissions.group_by do |submission|
24
+ submission.assignment.assignment_group
25
+ end.each do |group, submissions|
26
+ result[group.name] = [] unless result[group]
27
+ average = submissions.sum(&:score).to_f / submissions.count
28
+ weight = category_weights[group.name] / category_weights.values.sum
29
+ result[group.name] << average * weight
30
+ end
31
+
32
+ result.sum { |r, weighted| weighted.sum }
33
+ end
34
+
35
+ def category_weights
36
+ Queries::GetCategoryWeights.new(@course).query
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ module UnitsService
2
+ module Queries
3
+ class GetCategoryWeights
4
+ def initialize(course)
5
+ @course = course
6
+ end
7
+
8
+ def query
9
+ @course.assignment_groups.map{|ag| [ag.name, ag.group_weight]}.to_h
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module UnitsService
2
+ module Queries
3
+ class GetItems
4
+ def initialize(course:)
5
+ @course = course
6
+ @context_modules = @course.context_modules
7
+ end
8
+
9
+ def query
10
+ result = {}
11
+
12
+ @context_modules.each do |context_module|
13
+ result[context_module] =
14
+ context_module.content_tags.select do |ct|
15
+ ct.content.present? &&
16
+ ct.content.respond_to?(:submissions) &&
17
+ ct.content.workflow_state == 'published'
18
+ end
19
+ end
20
+ result
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module UnitsService
2
+ module Queries
3
+ class GetSubmissions
4
+ def initialize(course:, student:)
5
+ @student = student
6
+ @course = course
7
+ end
8
+
9
+ def query
10
+ result = {}
11
+ units.each do |unit, items|
12
+ items.each do |item|
13
+ result[unit] = item.content.submissions.where(user_id: @student.id).where("excused is not true")
14
+ end
15
+ end
16
+
17
+ result
18
+ end
19
+
20
+ def units
21
+ GetItems.new(course: @course).query
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,16 +1,15 @@
1
1
  require 'csv'
2
- require "rails"
3
2
  require 'grape'
4
3
  require 'grape-swagger'
5
4
  require 'grape-swagger-ui'
6
5
  require 'aws-sdk-dynamodb'
7
- require "pipeline_publisher_ruby"
8
6
  require "business"
9
7
  require 'httparty'
10
8
  require 'aws-sdk-core'
11
9
  require 'aws-sdk-s3'
12
- require 'inst-jobs'
13
10
  require 'pg'
11
+ require "pipeline_publisher_ruby"
12
+
14
13
 
15
14
  module CanvasShim
16
15
  class Engine < ::Rails::Engine
@@ -1,3 +1,3 @@
1
1
  module CanvasShim
2
- VERSION = '0.1.6'
2
+ VERSION = '0.1.7rc'
3
3
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canvas_shim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7rc
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-13 00:00:00.000000000 Z
11
+ date: 2018-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rails
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 5.0.4
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 5.0.4
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: grape
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -123,61 +109,19 @@ dependencies:
123
109
  - !ruby/object:Gem::Version
124
110
  version: '0'
125
111
  - !ruby/object:Gem::Dependency
126
- name: aws-sdk-core
112
+ name: loofah
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
115
  - - '='
130
116
  - !ruby/object:Gem::Version
131
- version: 3.27.0
117
+ version: 2.2.3
132
118
  type: :runtime
133
119
  prerelease: false
134
120
  version_requirements: !ruby/object:Gem::Requirement
135
121
  requirements:
136
122
  - - '='
137
123
  - !ruby/object:Gem::Version
138
- version: 3.27.0
139
- - !ruby/object:Gem::Dependency
140
- name: aws-sdk-s3
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :runtime
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: inst-jobs
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :runtime
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
- - !ruby/object:Gem::Dependency
168
- name: pg
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - ">="
172
- - !ruby/object:Gem::Version
173
- version: '0'
174
- type: :runtime
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - ">="
179
- - !ruby/object:Gem::Version
180
- version: '0'
124
+ version: 2.2.3
181
125
  description: A rails engine to be integrated with Canvas
182
126
  email:
183
127
  - ''
@@ -215,12 +159,15 @@ files:
215
159
  - app/services/pipeline_service/endpoints/pipeline.rb
216
160
  - app/services/pipeline_service/endpoints/pipeline/message_builder.rb
217
161
  - app/services/pipeline_service/events/emitter.rb
162
+ - app/services/pipeline_service/events/grade_changed_event.rb
218
163
  - app/services/pipeline_service/events/graded_out_event.rb
219
164
  - app/services/pipeline_service/events/http_client.rb
220
165
  - app/services/pipeline_service/events/responders/sis.rb
166
+ - app/services/pipeline_service/events/responders/sis_unit_grade.rb
221
167
  - app/services/pipeline_service/events/subscription.rb
222
168
  - app/services/pipeline_service/http_client.rb
223
169
  - app/services/pipeline_service/logger.rb
170
+ - app/services/pipeline_service/nouns/unit_grades.rb
224
171
  - app/services/pipeline_service/pipeline_client.rb
225
172
  - app/services/pipeline_service/serializers/assignment.rb
226
173
  - app/services/pipeline_service/serializers/base_methods.rb
@@ -228,6 +175,7 @@ files:
228
175
  - app/services/pipeline_service/serializers/enrollment.rb
229
176
  - app/services/pipeline_service/serializers/fetcher.rb
230
177
  - app/services/pipeline_service/serializers/submission.rb
178
+ - app/services/pipeline_service/serializers/unit_grades.rb
231
179
  - app/services/pipeline_service/serializers/user.rb
232
180
  - app/services/settings_service.rb
233
181
  - app/services/settings_service/api_docs.yml
@@ -250,6 +198,11 @@ files:
250
198
  - app/services/settings_service/student_assignment_repository.rb
251
199
  - app/services/settings_service/submission.rb
252
200
  - app/services/settings_service/user.rb
201
+ - app/services/units_service/commands/get_unit_grades.rb
202
+ - app/services/units_service/grades_calculator.rb
203
+ - app/services/units_service/queries/get_category_weights.rb
204
+ - app/services/units_service/queries/get_items.rb
205
+ - app/services/units_service/queries/get_submissions.rb
253
206
  - app/views/layouts/canvas_shim/application.html.erb
254
207
  - config/initializers/hash.rb
255
208
  - config/initializers/string.rb
@@ -273,12 +226,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
273
226
  version: '0'
274
227
  required_rubygems_version: !ruby/object:Gem::Requirement
275
228
  requirements:
276
- - - ">="
229
+ - - ">"
277
230
  - !ruby/object:Gem::Version
278
- version: '0'
231
+ version: 1.3.1
279
232
  requirements: []
280
233
  rubyforge_project:
281
- rubygems_version: 2.6.14
234
+ rubygems_version: 2.7.7
282
235
  signing_key:
283
236
  specification_version: 4
284
237
  summary: A tool to make it easy to write code that plugs into canvas