pupilfirst_xapi 0.1.0 → 0.3.2
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 +4 -4
- data/lib/pupilfirst_xapi.rb +2 -0
- data/lib/pupilfirst_xapi/lrs.rb +24 -0
- data/lib/pupilfirst_xapi/objects.rb +4 -4
- data/lib/pupilfirst_xapi/objects/course.rb +4 -1
- data/lib/pupilfirst_xapi/objects/target.rb +9 -3
- data/lib/pupilfirst_xapi/outbox.rb +2 -12
- data/lib/pupilfirst_xapi/statements/course_completed.rb +1 -1
- data/lib/pupilfirst_xapi/statements/course_registered.rb +1 -1
- data/lib/pupilfirst_xapi/statements/target_completed.rb +1 -1
- data/lib/pupilfirst_xapi/version.rb +1 -1
- data/spec/integration_spec.rb +11 -3
- data/spec/outbox_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -1
- data/spec/statements/course_completed_spec.rb +5 -1
- data/spec/statements/{course_redistered_spec.rb → course_registered_spec.rb} +17 -4
- data/spec/statements/target_completed_spec.rb +9 -3
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdb9ed6c9957df10e29f9f8fac0d5a303ac6ed5f962d78bb27af84be1255f61d
|
|
4
|
+
data.tar.gz: 361c1a5767d137ee50a99a191bb9906b1ca295bf6074e2c3568b80a5cf5121ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab2a1cba32a1cdc99afeabb7bffc99b022a695989817ac8fe27e7f11894635dedc7befa6a88308af5660ddae6cf9aec8a4d97270bf45e9fb17263eb710d8aa61
|
|
7
|
+
data.tar.gz: e58f22380039a0235534988affdaaa4f01b208b83e43857908d62e20c17d636b548b9a8da96a616e0bb9d1ed03a77b5627488d5c3d0e0bf8e560d658bcc2d24b
|
data/lib/pupilfirst_xapi.rb
CHANGED
|
@@ -2,6 +2,7 @@ require "action_controller/railtie"
|
|
|
2
2
|
require "active_job/railtie"
|
|
3
3
|
require "growthtribe_xapi"
|
|
4
4
|
require "pupilfirst_xapi/version"
|
|
5
|
+
require "pupilfirst_xapi/lrs"
|
|
5
6
|
require "pupilfirst_xapi/outbox"
|
|
6
7
|
require "pupilfirst_xapi/actors"
|
|
7
8
|
require "pupilfirst_xapi/objects"
|
|
@@ -11,6 +12,7 @@ require "pupilfirst_xapi/statements"
|
|
|
11
12
|
module PupilfirstXapi
|
|
12
13
|
mattr_accessor :uri_for
|
|
13
14
|
mattr_accessor :repository
|
|
15
|
+
mattr_accessor :lrs
|
|
14
16
|
|
|
15
17
|
Statements.subscribe do |event_type|
|
|
16
18
|
ActiveSupport::Notifications.subscribe("#{event_type}.pupilfirst") do |_name, _start, finish, _id, payload|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'xapi'
|
|
2
|
+
|
|
3
|
+
module PupilfirstXapi
|
|
4
|
+
class Lrs
|
|
5
|
+
def initialize(
|
|
6
|
+
end_point: ENV['LRS_ENDPOINT'],
|
|
7
|
+
key: ENV['LRS_KEY'],
|
|
8
|
+
secret: ENV['LRS_SECRET']
|
|
9
|
+
)
|
|
10
|
+
@lrs = end_point && key && secret &&
|
|
11
|
+
Xapi.create_remote_lrs(
|
|
12
|
+
end_point: end_point,
|
|
13
|
+
user_name: key,
|
|
14
|
+
password: secret
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(statement)
|
|
19
|
+
return unless statement && @lrs
|
|
20
|
+
|
|
21
|
+
Xapi.post_statement(remote_lrs: @lrs, statement: statement)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -4,12 +4,12 @@ require_relative 'objects/target'
|
|
|
4
4
|
|
|
5
5
|
module PupilfirstXapi
|
|
6
6
|
module Objects
|
|
7
|
-
def self.course(course,
|
|
8
|
-
Course.new.call(course,
|
|
7
|
+
def self.course(course, uri_for)
|
|
8
|
+
Course.new.call(course, uri_for)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def self.target(target,
|
|
12
|
-
Target.new.call(target,
|
|
11
|
+
def self.target(target, uri_for)
|
|
12
|
+
Target.new.call(target, uri_for)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
module PupilfirstXapi
|
|
2
2
|
module Objects
|
|
3
3
|
class Course
|
|
4
|
-
def call(course,
|
|
4
|
+
def call(course, uri_for)
|
|
5
|
+
uri = uri_for.call(course)
|
|
6
|
+
|
|
5
7
|
Builder.new(
|
|
6
8
|
id: uri,
|
|
7
9
|
type: 'http://adlnet.gov/expapi/activities/product',
|
|
8
10
|
name: course.name,
|
|
9
11
|
description: course.description
|
|
10
12
|
).tap do |obj|
|
|
13
|
+
obj.with_extension('http://id.tincanapi.com/extension/ending-position', course.targets.count)
|
|
11
14
|
if course.ends_at.present?
|
|
12
15
|
duration = ActiveSupport::Duration.build(course.ends_at - course.created_at).iso8601
|
|
13
16
|
obj.with_extension("http://id.tincanapi.com/extension/planned-duration", duration)
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
module PupilfirstXapi
|
|
2
2
|
module Objects
|
|
3
3
|
class Target
|
|
4
|
-
def call(target,
|
|
4
|
+
def call(target, uri_for)
|
|
5
|
+
course = target.course
|
|
6
|
+
target_uri = uri_for.call(target)
|
|
7
|
+
|
|
5
8
|
Builder.new(
|
|
6
|
-
id:
|
|
9
|
+
id: target_uri,
|
|
7
10
|
type: "http://activitystrea.ms/schema/1.0/task",
|
|
8
11
|
name: target.title,
|
|
9
12
|
description: target.description
|
|
10
|
-
).
|
|
13
|
+
).tap do |obj|
|
|
14
|
+
obj.with_extension('http://id.tincanapi.com/extension/host', Objects.course(course, uri_for).as_json)
|
|
15
|
+
obj.with_extension('http://id.tincanapi.com/extension/position', target.sort_index)
|
|
16
|
+
end.call
|
|
11
17
|
end
|
|
12
18
|
end
|
|
13
19
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "active_job"
|
|
2
|
-
require 'xapi'
|
|
3
2
|
|
|
4
3
|
module PupilfirstXapi
|
|
5
4
|
class Outbox
|
|
@@ -14,19 +13,11 @@ module PupilfirstXapi
|
|
|
14
13
|
|
|
15
14
|
def outbox
|
|
16
15
|
Outbox.new(
|
|
17
|
-
lrs:
|
|
16
|
+
lrs: PupilfirstXapi.lrs || PupilfirstXapi::Lrs.new,
|
|
18
17
|
repository: PupilfirstXapi.repository,
|
|
19
18
|
uri_for: PupilfirstXapi.uri_for
|
|
20
19
|
)
|
|
21
20
|
end
|
|
22
|
-
|
|
23
|
-
def remote_lrs
|
|
24
|
-
Xapi.create_remote_lrs(
|
|
25
|
-
end_point: ENV['LRS_ENDPOINT'],
|
|
26
|
-
user_name: ENV['LRS_KEY'],
|
|
27
|
-
password: ENV['LRS_SECRET']
|
|
28
|
-
)
|
|
29
|
-
end
|
|
30
21
|
end
|
|
31
22
|
|
|
32
23
|
class << self
|
|
@@ -42,8 +33,7 @@ module PupilfirstXapi
|
|
|
42
33
|
end
|
|
43
34
|
|
|
44
35
|
def call(**payload)
|
|
45
|
-
|
|
46
|
-
Xapi.post_statement(remote_lrs: @lrs, statement: statement) if statement
|
|
36
|
+
@lrs.call(statement_for(**payload))
|
|
47
37
|
end
|
|
48
38
|
|
|
49
39
|
private
|
data/spec/integration_spec.rb
CHANGED
|
@@ -15,13 +15,20 @@ RSpec.describe "#xapi", type: :job, perform_jobs: true do
|
|
|
15
15
|
description: 'These guides are designed to make you immediately productive with Rails',
|
|
16
16
|
created_at: Time.new(2021,01,01),
|
|
17
17
|
ends_at: nil,
|
|
18
|
-
uri: 'https://guides.rubyonrails.org/'
|
|
18
|
+
uri: 'https://guides.rubyonrails.org/',
|
|
19
|
+
targets: [
|
|
20
|
+
double(:target, title: '1st target', description: 'Seems easy'),
|
|
21
|
+
double(:target, title: '2nd target', description: 'Seems not easy')
|
|
22
|
+
]
|
|
23
|
+
)
|
|
19
24
|
}
|
|
20
25
|
let(:getting_started) {
|
|
21
26
|
double(:target,
|
|
22
27
|
title: 'Getting Started with Rails',
|
|
23
28
|
description: 'This guide covers getting up and running with Ruby on Rails.',
|
|
24
|
-
uri: 'https://guides.rubyonrails.org/getting_started.html'
|
|
29
|
+
uri: 'https://guides.rubyonrails.org/getting_started.html',
|
|
30
|
+
course: ror_guides
|
|
31
|
+
)
|
|
25
32
|
}
|
|
26
33
|
let(:good_one) { double(:timeline_event, target: getting_started, passed?: true) }
|
|
27
34
|
let(:bad_one) { double(:timeline_event, target: getting_started, passed?: false) }
|
|
@@ -75,6 +82,7 @@ RSpec.describe "#xapi", type: :job, perform_jobs: true do
|
|
|
75
82
|
name: {'en-US' => 'Ruby on Rails Guides'},
|
|
76
83
|
description: {'en-US' => 'These guides are designed to make you immediately productive with Rails'},
|
|
77
84
|
type: 'http://adlnet.gov/expapi/activities/product',
|
|
85
|
+
extensions: {"http://id.tincanapi.com/extension/ending-position"=>2}
|
|
78
86
|
},
|
|
79
87
|
},
|
|
80
88
|
timestamp: timestamp.iso8601,
|
|
@@ -88,7 +96,7 @@ RSpec.describe "#xapi", type: :job, perform_jobs: true do
|
|
|
88
96
|
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
|
89
97
|
'Authorization'=>'Basic a2V5OnNlY3JldA==',
|
|
90
98
|
'Content-Type'=>'application/json',
|
|
91
|
-
'User-Agent'=>'Faraday v1.3.
|
|
99
|
+
'User-Agent'=>'Faraday v1.3.1',
|
|
92
100
|
'X-Experience-Api-Version'=>'1.0.1'
|
|
93
101
|
}
|
|
94
102
|
).to_return(status: 204, body: "", headers: {})
|
data/spec/outbox_spec.rb
CHANGED
|
@@ -4,8 +4,8 @@ module PupilfirstXapi
|
|
|
4
4
|
RSpec.describe Outbox do
|
|
5
5
|
let(:timestamp) { Time.now }
|
|
6
6
|
let(:john) { double(:john, name: 'John Doe', email: 'john@doe.com') }
|
|
7
|
-
let(:course) { double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil) }
|
|
8
|
-
let(:target) { double(:target, title: '1st target', description: 'Seems easy') }
|
|
7
|
+
let(:course) { double(:course, id: 32, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil, targets: []) }
|
|
8
|
+
let(:target) { double(:target, title: '1st target', description: 'Seems easy', course: course, sort_index: 7) }
|
|
9
9
|
let(:good_one) { double(:timeline_event, target: target, passed?: true) }
|
|
10
10
|
let(:bad_one) { double(:timeline_event, target: target, passed?: false) }
|
|
11
11
|
|
data/spec/spec_helper.rb
CHANGED
|
@@ -5,7 +5,11 @@ module PupilfirstXapi
|
|
|
5
5
|
RSpec.describe CourseCompleted do
|
|
6
6
|
it do
|
|
7
7
|
john = double(:john, name: 'John Doe', email: 'john@doe.com')
|
|
8
|
-
course = double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil
|
|
8
|
+
course = double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil,
|
|
9
|
+
targets: [
|
|
10
|
+
double(:target, title: '1st target', description: 'Seems easy'),
|
|
11
|
+
double(:target, title: '2nd target', description: 'Seems not easy')
|
|
12
|
+
])
|
|
9
13
|
data = {
|
|
10
14
|
course: { 456 => course },
|
|
11
15
|
user: { 123 => john },
|
|
@@ -5,7 +5,11 @@ module PupilfirstXapi
|
|
|
5
5
|
RSpec.describe CourseRegistered do
|
|
6
6
|
it do
|
|
7
7
|
john = double(:john, name: 'John Doe', email: 'john@doe.com')
|
|
8
|
-
course = double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil
|
|
8
|
+
course = double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil,
|
|
9
|
+
targets: [
|
|
10
|
+
double(:target, title: '1st target', description: 'Seems easy'),
|
|
11
|
+
double(:target, title: '2nd target', description: 'Seems not easy')
|
|
12
|
+
])
|
|
9
13
|
data = {
|
|
10
14
|
course: { 456 => course },
|
|
11
15
|
user: { 123 => john },
|
|
@@ -25,7 +29,9 @@ module PupilfirstXapi
|
|
|
25
29
|
expect(xapi.object.definition.type).to eq 'http://adlnet.gov/expapi/activities/product'
|
|
26
30
|
expect(xapi.object.definition.name).to eq({'en-US' => 'Rails for Begginers'})
|
|
27
31
|
expect(xapi.object.definition.description).to eq({'en-US' => 'Seems easy'})
|
|
28
|
-
expect(xapi.object.definition.extensions).to eq
|
|
32
|
+
expect(xapi.object.definition.extensions).to eq ({
|
|
33
|
+
"http://id.tincanapi.com/extension/ending-position" => 2
|
|
34
|
+
})
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
it do
|
|
@@ -34,7 +40,11 @@ module PupilfirstXapi
|
|
|
34
40
|
duration = ActiveSupport::Duration.build(ends_at - starts_at).iso8601
|
|
35
41
|
|
|
36
42
|
john = double(:john, name: 'John Doe', email: 'john@doe.com')
|
|
37
|
-
course = double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: starts_at, ends_at: ends_at
|
|
43
|
+
course = double(:course, name: 'Rails for Begginers', description: 'Seems easy', created_at: starts_at, ends_at: ends_at,
|
|
44
|
+
targets: [
|
|
45
|
+
double(:target, title: '1st target', description: 'Seems easy'),
|
|
46
|
+
double(:target, title: '2nd target', description: 'Seems not easy')
|
|
47
|
+
])
|
|
38
48
|
data = {
|
|
39
49
|
course: { 456 => course },
|
|
40
50
|
user: { 123 => john },
|
|
@@ -54,7 +64,10 @@ module PupilfirstXapi
|
|
|
54
64
|
expect(xapi.object.definition.type).to eq 'http://adlnet.gov/expapi/activities/product'
|
|
55
65
|
expect(xapi.object.definition.name).to eq({'en-US' => 'Rails for Begginers'})
|
|
56
66
|
expect(xapi.object.definition.description).to eq({'en-US' => 'Seems easy'})
|
|
57
|
-
expect(xapi.object.definition.extensions).to eq({
|
|
67
|
+
expect(xapi.object.definition.extensions).to eq({
|
|
68
|
+
"http://id.tincanapi.com/extension/planned-duration" => duration,
|
|
69
|
+
"http://id.tincanapi.com/extension/ending-position" => 2
|
|
70
|
+
})
|
|
58
71
|
end
|
|
59
72
|
end
|
|
60
73
|
end
|
|
@@ -4,7 +4,12 @@ module PupilfirstXapi
|
|
|
4
4
|
module Statements
|
|
5
5
|
RSpec.describe TargetCompleted do
|
|
6
6
|
it do
|
|
7
|
-
|
|
7
|
+
course = double(:course, id: 17, name: 'Rails for Begginers', description: 'Seems easy', created_at: 1.week.ago, ends_at: nil,
|
|
8
|
+
targets: [
|
|
9
|
+
double(:target, title: '1st target', description: 'Seems easy'),
|
|
10
|
+
double(:target, title: '2nd target', description: 'Seems not easy')
|
|
11
|
+
])
|
|
12
|
+
target = double(:target, title: '1st target', course: course, description: 'Seems easy', sort_index: 5)
|
|
8
13
|
submission = double(:timeline_event, target: target, passed?: true)
|
|
9
14
|
john = double(:john, name: 'John Doe', email: 'john@doe.com')
|
|
10
15
|
data = {
|
|
@@ -12,7 +17,7 @@ module PupilfirstXapi
|
|
|
12
17
|
user: { 123 => john },
|
|
13
18
|
}
|
|
14
19
|
repository = ->(klass, resource_id) { data.dig(klass, resource_id) }
|
|
15
|
-
uri_for = ->(obj) { obj == target ? 'target-1' : nil }
|
|
20
|
+
uri_for = ->(obj) { obj == target ? 'target-1' : obj == course ? 'course-1' : nil }
|
|
16
21
|
|
|
17
22
|
xapi = TargetCompleted.new(repository, uri_for).call(actor_id: 123, resource_id: 456)
|
|
18
23
|
|
|
@@ -26,10 +31,11 @@ module PupilfirstXapi
|
|
|
26
31
|
expect(xapi.object.definition.type).to eq 'http://activitystrea.ms/schema/1.0/task'
|
|
27
32
|
expect(xapi.object.definition.name).to eq({'en-US' => '1st target'})
|
|
28
33
|
expect(xapi.object.definition.description).to eq({'en-US' => 'Seems easy'})
|
|
34
|
+
expect(xapi.object.definition.extensions['http://id.tincanapi.com/extension/host']['id']).to eq 'course-1'
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
it 'no-op when submission is not passed' do
|
|
32
|
-
target = double(:target, title: '1st target', description: 'Seems easy')
|
|
38
|
+
target = double(:target, title: '1st target', description: 'Seems easy', sort_index: 3)
|
|
33
39
|
submission = double(:timeline_event, target: target, passed?: false)
|
|
34
40
|
john = double(:john, name: 'John Doe', email: 'john@doe.com')
|
|
35
41
|
data = {
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pupilfirst_xapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GrowthTribe
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-05-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -36,14 +36,14 @@ dependencies:
|
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.0.
|
|
39
|
+
version: 0.0.2
|
|
40
40
|
type: :runtime
|
|
41
41
|
prerelease: false
|
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 0.0.
|
|
46
|
+
version: 0.0.2
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: rspec-rails
|
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -98,6 +98,7 @@ files:
|
|
|
98
98
|
- Rakefile
|
|
99
99
|
- lib/pupilfirst_xapi.rb
|
|
100
100
|
- lib/pupilfirst_xapi/actors.rb
|
|
101
|
+
- lib/pupilfirst_xapi/lrs.rb
|
|
101
102
|
- lib/pupilfirst_xapi/objects.rb
|
|
102
103
|
- lib/pupilfirst_xapi/objects/builder.rb
|
|
103
104
|
- lib/pupilfirst_xapi/objects/course.rb
|
|
@@ -116,7 +117,7 @@ files:
|
|
|
116
117
|
- spec/rails_helper.rb
|
|
117
118
|
- spec/spec_helper.rb
|
|
118
119
|
- spec/statements/course_completed_spec.rb
|
|
119
|
-
- spec/statements/
|
|
120
|
+
- spec/statements/course_registered_spec.rb
|
|
120
121
|
- spec/statements/target_completed_spec.rb
|
|
121
122
|
homepage: https://github.com/growthtribeacademy/pupilfirst-xapi
|
|
122
123
|
licenses:
|
|
@@ -152,5 +153,5 @@ test_files:
|
|
|
152
153
|
- spec/rails_helper.rb
|
|
153
154
|
- spec/spec_helper.rb
|
|
154
155
|
- spec/statements/course_completed_spec.rb
|
|
155
|
-
- spec/statements/
|
|
156
|
+
- spec/statements/course_registered_spec.rb
|
|
156
157
|
- spec/statements/target_completed_spec.rb
|