canvas_shim 0.1.6 → 0.1.7rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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