growthtribe_xapi 0.0.1

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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +28 -0
  3. data/CHANGELOG.md +2 -0
  4. data/CONTRIBUTING.md +7 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +78 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +299 -0
  9. data/Rakefile +12 -0
  10. data/bin/rspec +29 -0
  11. data/lib/growthtribe_xapi.rb +2 -0
  12. data/lib/xapi.rb +224 -0
  13. data/lib/xapi/about.rb +15 -0
  14. data/lib/xapi/activity.rb +37 -0
  15. data/lib/xapi/activity_definition.rb +131 -0
  16. data/lib/xapi/agent.rb +44 -0
  17. data/lib/xapi/agent_account.rb +33 -0
  18. data/lib/xapi/attachment.rb +64 -0
  19. data/lib/xapi/context.rb +54 -0
  20. data/lib/xapi/context_activities.rb +102 -0
  21. data/lib/xapi/documents/activity_profile_document.rb +15 -0
  22. data/lib/xapi/documents/agent_profile_document.rb +15 -0
  23. data/lib/xapi/documents/document.rb +20 -0
  24. data/lib/xapi/documents/state_document.rb +15 -0
  25. data/lib/xapi/enum.rb +42 -0
  26. data/lib/xapi/errors.rb +9 -0
  27. data/lib/xapi/group.rb +37 -0
  28. data/lib/xapi/interaction_component.rb +32 -0
  29. data/lib/xapi/interaction_type.rb +58 -0
  30. data/lib/xapi/lrs_response.rb +14 -0
  31. data/lib/xapi/query_result_format.rb +6 -0
  32. data/lib/xapi/remote_lrs.rb +416 -0
  33. data/lib/xapi/result.rb +46 -0
  34. data/lib/xapi/score.rb +39 -0
  35. data/lib/xapi/statement.rb +53 -0
  36. data/lib/xapi/statement_ref.rb +31 -0
  37. data/lib/xapi/statements/statements_base.rb +70 -0
  38. data/lib/xapi/statements_query.rb +42 -0
  39. data/lib/xapi/statements_query_v095.rb +42 -0
  40. data/lib/xapi/statements_result.rb +17 -0
  41. data/lib/xapi/sub_statement.rb +19 -0
  42. data/lib/xapi/tcapi_version.rb +27 -0
  43. data/lib/xapi/team.rb +44 -0
  44. data/lib/xapi/team_analytics_query.rb +36 -0
  45. data/lib/xapi/verb.rb +35 -0
  46. data/lib/xapi/version.rb +4 -0
  47. data/spec/fixtures/about.json +10 -0
  48. data/spec/fixtures/statement.json +33 -0
  49. data/spec/spec_helper.rb +107 -0
  50. data/spec/support/helpers.rb +60 -0
  51. data/spec/xapi/activity_definition_spec.rb +37 -0
  52. data/spec/xapi/activity_spec.rb +23 -0
  53. data/spec/xapi/agent_account_spec.rb +13 -0
  54. data/spec/xapi/agent_spec.rb +24 -0
  55. data/spec/xapi/attachment_spec.rb +26 -0
  56. data/spec/xapi/context_activities_spec.rb +57 -0
  57. data/spec/xapi/context_spec.rb +32 -0
  58. data/spec/xapi/group_spec.rb +15 -0
  59. data/spec/xapi/interaction_component_spec.rb +18 -0
  60. data/spec/xapi/remote_lrs_spec.rb +46 -0
  61. data/spec/xapi/result_spec.rb +24 -0
  62. data/spec/xapi/score_spec.rb +12 -0
  63. data/spec/xapi/statement_ref_spec.rb +19 -0
  64. data/spec/xapi/statement_spec.rb +73 -0
  65. data/spec/xapi/sub_statement_spec.rb +30 -0
  66. data/spec/xapi/verb_spec.rb +17 -0
  67. data/xapi.gemspec +30 -0
  68. metadata +244 -0
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ module Xapi
3
+ # InteractionComponent Class Description
4
+ class InteractionComponent
5
+
6
+ attr_accessor :id, :description
7
+
8
+ def initialize(options={}, &block)
9
+ json = options.fetch(:json, nil)
10
+ if json
11
+ attributes = JSON.parse(json)
12
+ self.id = attributes['id'] if attributes['id']
13
+ self.description = attributes['description'] if attributes['description']
14
+ else
15
+ self.id = options.fetch(:id, nil)
16
+ self.description = options.fetch(:description, nil)
17
+
18
+ if block_given?
19
+ block[self]
20
+ end
21
+ end
22
+ end
23
+
24
+ def serialize(version)
25
+ node = {}
26
+ node['id'] = id if id
27
+ node['description'] = description if description
28
+ node
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ module Xapi
3
+ # Possible interaction types
4
+ module InteractionType
5
+
6
+ CHOICE = {
7
+ to_s: 'choice',
8
+ get_value: proc{'choice'}
9
+ }
10
+
11
+ SEQUENCING = {
12
+ to_s: 'sequencing',
13
+ get_value: proc{'sequencing'}
14
+ }
15
+
16
+ LIKERT = {
17
+ to_s: 'likert',
18
+ get_value: proc{'likert'}
19
+ }
20
+
21
+ MATCHING = {
22
+ to_s: 'matching',
23
+ get_value: proc{'matching'}
24
+ }
25
+
26
+ PERFORMANCE = {
27
+ to_s: 'performance',
28
+ get_value: proc{'performance'}
29
+ }
30
+
31
+ TRUE_FALSE = {
32
+ to_s: 'true-false',
33
+ get_value: proc{'true-false'}
34
+ }
35
+
36
+ FILL_IN = {
37
+ to_s: 'fill-in',
38
+ get_value: proc{'fill-in'}
39
+ }
40
+
41
+ NUMERIC = {
42
+ to_s: 'numeric',
43
+ get_value: proc{'numeric'}
44
+ }
45
+
46
+ OTHER = {
47
+ to_s: 'other',
48
+ get_value: proc{'other'}
49
+ }
50
+
51
+ def type_by_string(type)
52
+ values.select{|v| v.to_s == type}.first
53
+ end
54
+
55
+ extend Xapi::Enum
56
+
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ module Xapi
3
+ class LrsResponse
4
+ attr_accessor :success, :error_message, :content, :status
5
+
6
+ def initialize(&block)
7
+ if block_given?
8
+ block[self]
9
+ end
10
+ end
11
+
12
+
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+ module Xapi
3
+ module QueryResultFormat
4
+
5
+ end
6
+ end
@@ -0,0 +1,416 @@
1
+ # encoding: utf-8
2
+ require 'faraday'
3
+ require 'addressable/uri'
4
+ require 'xapi/tcapi_version'
5
+ module Xapi
6
+ # Class used to communicate with a TCAPI endpoint synchronously
7
+ class RemoteLRS
8
+ include Xapi::TCAPIVersion
9
+
10
+ VALID_PARAMS = %w(end_point user_name password version)
11
+
12
+ attr_accessor :end_point, :user_name, :password, :version
13
+
14
+ def initialize(options={}, &block)
15
+ setup_options(options)
16
+ yield_or_eval(&block) if block_given?
17
+ @version ||= latest_version
18
+ end
19
+
20
+ def about
21
+ response = connection.get("#{path}about")
22
+ LrsResponse.new do |lrs|
23
+ lrs.status = response.status
24
+ if response.status== 200
25
+ lrs.content = About.new(json: response.body)
26
+ lrs.success = true
27
+ else
28
+ lrs.success = false
29
+ end
30
+ end
31
+ end
32
+
33
+ def save_statement(statement)
34
+ # TODO: Complete this
35
+ if statement.id
36
+ response = connection.put do |req|
37
+ req.url("#{path}statements")
38
+ req.headers['Content-Type'] = 'application/json'
39
+ req.params.merge!({'statementId' => statement.id})
40
+ req.body = statement.serialize(latest_version).to_json
41
+ end
42
+ else
43
+ response = connection.post do |req|
44
+ req.url("#{path}statements")
45
+ req.headers['Content-Type'] = 'application/json'
46
+ req.body = statement.serialize(latest_version).to_json
47
+ end
48
+ end
49
+ LrsResponse.new do |lrs|
50
+ lrs.status = response.status
51
+ lrs.content = statement
52
+ if response.status == 200
53
+ statement.id = JSON.parse(response.body).first
54
+ lrs.success = true
55
+ elsif response.status == 204
56
+ lrs.success = true
57
+ else
58
+ lrs.success = false
59
+ end
60
+ end
61
+ end
62
+
63
+ def save_statements(statements)
64
+ # TODO: Complete this
65
+ if statements.empty?
66
+ return LrsResponse.new do |lrs|
67
+ lrs.success = true
68
+ end
69
+ end
70
+ response = connection.post do |req|
71
+ req.url("#{path}statements")
72
+ req.headers['Content-Type'] = 'application/json'
73
+ req.body = statements.map {|s| s.serialize(latest_version)}.to_json
74
+ end
75
+ LrsResponse.new do |lrs|
76
+ lrs.status = response.status
77
+ if response.status == 200
78
+ lrs.content = statements
79
+ ids = JSON.parse(response.body)
80
+ statements.each_with_index do |statement, index|
81
+ statement.id = ids[index]
82
+ end
83
+ lrs.success = true
84
+ else
85
+ lrs.success = false
86
+ end
87
+ end
88
+ end
89
+
90
+ def retrieve_statement(id)
91
+ get_statement(id, 'statementId')
92
+ end
93
+
94
+ def retrieve_voided_statement(id)
95
+ param_name = version == TCAPIVersion.V095 ? 'statementId' : 'voidedStatementId'
96
+ get_statement(id, param_name)
97
+ end
98
+
99
+ def query_statements(statement_query)
100
+ # TODO: Complete this
101
+ query = statement_query
102
+ unless query
103
+ query = version == TCAPIVersion::V095 ? StatementsQueryV095.new : StatementsQuery.new
104
+ end
105
+ # Error if the query parameters don't match the LRS version
106
+ raise Errors::IncompatibleTCAPIVersion, "Attempted to issue #{version} query using a #{query.version} set of query parameters." unless version == query.version
107
+
108
+ response = connection.get do |req|
109
+ req.url("#{path}statements")
110
+ req.params.merge!(query.parameter_map)
111
+ end
112
+ LrsResponse.new do |lrs|
113
+ lrs.status = response.status
114
+ if response.status == 200
115
+ # TODO: FIX THIS
116
+ lrs.success = true
117
+ lrs.content = StatementsResult.new(json: JSON.parse(response.body))
118
+ else
119
+ lrs.success = false
120
+ end
121
+ end
122
+ end
123
+
124
+ def query_team_analytics(team_analytics_query)
125
+ # TODO: Complete this
126
+ query = team_analytics_query
127
+ query = TeamAnalyticsQuery.new unless query
128
+
129
+ response = connection.get do |req|
130
+ req.url("#{path}team_analytics")
131
+ req.params.merge!(query.parameter_map)
132
+ end
133
+ LrsResponse.new do |lrs|
134
+ lrs.status = response.status
135
+ if response.status == 200
136
+ lrs.success = true
137
+ lrs.content = JSON.parse(response.body)
138
+ else
139
+ lrs.success = false
140
+ end
141
+ end
142
+ end
143
+
144
+ def more_statements(more_url)
145
+ # TODO: Complete this
146
+ # more_url is relative to the endpoint's server root
147
+ response = connection.get do |req|
148
+ req.url("#{path}#{more_url}")
149
+ end
150
+ LrsResponse.new do |lrs|
151
+ lrs.status = response.status
152
+ if response.status == 200
153
+ # TODO: FIX THIS
154
+ lrs.success = true
155
+ lrs.content = StatementsResult.new(json: response.body)
156
+ else
157
+ lrs.success = false
158
+ end
159
+ end
160
+ end
161
+
162
+ def retrieve_state_ids(activity, agent, registration)
163
+ # TODO: Complete this
164
+ query_params = {
165
+ 'activityId' => activity.id,
166
+ 'agent' => agent.serialize(version).to_json
167
+ }
168
+ query_params['registration'] = registration if registration
169
+ get_profile_keys('activities/state', query_params)
170
+ end
171
+
172
+ def retrieve_state(id, activity, agent, registration)
173
+ # TODO: Complete this
174
+ query_params = {
175
+ 'stateId' => id,
176
+ 'activityId' => activity.id,
177
+ 'agent' => agent.serialize(version).to_json
178
+ }
179
+ document = StateDocument.new do |sd|
180
+ sd.id = id
181
+ sd.activity = activity
182
+ sd.agent = agent
183
+ end
184
+ lrs_response = get_document('activities/state', query_params, document)
185
+ if lrs_response.status == 200
186
+ lrs_response.content = document
187
+ end
188
+ lrs_response
189
+ end
190
+
191
+ def save_state(state)
192
+ # TODO: Complete this
193
+ query_params = {
194
+ 'stateId' => state.id,
195
+ 'activityId' => state.activity.id,
196
+ 'agent' => state.agent.serialize(version).to_json
197
+ }
198
+ save_document('activities/state', query_params, state)
199
+ end
200
+
201
+ def delete_state(state)
202
+ # TODO: Complete this
203
+ query_params = {
204
+ 'stateId' => state.id,
205
+ 'activityId' => state.activity.id,
206
+ 'agent' => state.agent.serialize(version).to_json
207
+ }
208
+ query_params['registration'] = state.registration if state.registration
209
+ delete_document('activities/state', query_params)
210
+ end
211
+
212
+ def clear_state(activity, agent, registration)
213
+ # TODO: Complete this
214
+ query_params = {
215
+ 'activityId' => activity.id,
216
+ 'agent' => agent.serialize(version).to_json
217
+ }
218
+ query_params['registration'] = registration if registration
219
+ delete_document('activities/state', query_params)
220
+ end
221
+
222
+ def retrieve_activity_profile_ids(activity)
223
+ # TODO: Complete this
224
+ query_params = {
225
+ 'activityId' => activity.id
226
+ }
227
+ get_profile_keys('activities/profile', query_params)
228
+ end
229
+
230
+ def retrieve_activity_profile(id, activity)
231
+ # TODO: Complete this
232
+ query_params = {
233
+ 'profileId' => id,
234
+ 'activityId' => activity.id,
235
+ }
236
+ document = Documents::ActivityProfileDocument.new do |apd|
237
+ apd.id = id
238
+ apd.activity = activity
239
+ end
240
+ get_document('activities/profile', query_params, document)
241
+ end
242
+
243
+ def save_activity_profile(profile)
244
+ # TODO: Complete this
245
+ query_params = {
246
+ 'profileId' => profile.id,
247
+ 'activityId' => profile.activity.id,
248
+ }
249
+ save_document('activities/profile', query_params, profile)
250
+ end
251
+
252
+ def delete_activity_profile(profile)
253
+ # TODO: Complete this
254
+ query_params = {
255
+ 'profileId' => profile.id,
256
+ 'activityId' => profile.activity.id,
257
+ }
258
+ delete_document('activities/profile', query_params)
259
+ end
260
+
261
+ def retrieve_agent_profile_ids(agent)
262
+ # TODO: Complete this
263
+ query_params = {
264
+ 'agent' => agent.serialize(version).to_json
265
+ }
266
+ get_profile_keys('agents/profile', query_params)
267
+ end
268
+
269
+ def retrieve_agent_profile(id, agent)
270
+ # TODO: Complete this
271
+ query_params = {
272
+ 'profileId' => id,
273
+ 'agent' => agent.serialize(version).to_json
274
+ }
275
+ document = Documents::AgentProfileDocument.new do |apd|
276
+ apd.id = id
277
+ apd.agent = agent
278
+ end
279
+ get_document('agents/profile', query_params, document)
280
+ end
281
+
282
+ def save_agent_profile(profile)
283
+ # TODO: Complete this
284
+ query_params = {
285
+ 'profileId' => profile.id,
286
+ 'agent' => profile.agent.serialize(version).to_json
287
+ }
288
+ save_document('agents/profile', query_params, profile)
289
+ end
290
+
291
+ def delete_agent_profile(profile)
292
+ # TODO: Complete this
293
+ query_params = {
294
+ 'profileId' => profile.id,
295
+ 'agent' => profile.agent.serialize(version).to_json
296
+ }
297
+ delete_document('agents/profile', query_params)
298
+ end
299
+
300
+ private
301
+
302
+ def setup_options(options={})
303
+ options.each_pair do |key, value|
304
+ if value && VALID_PARAMS.include?(key.to_s)
305
+ instance_variable_set("@#{key}", value)
306
+ end
307
+ end
308
+ end
309
+
310
+ def yield_or_eval(&block)
311
+ return unless block
312
+ block.arity < 1 ? instance_eval(&block) : block[self]
313
+ end
314
+
315
+ def uri
316
+ @uri ||= Addressable::URI.parse(end_point)
317
+ end
318
+
319
+ def path
320
+ @path ||= uri.path
321
+ @path += '/' unless @path.end_with?('/')
322
+ @path
323
+ end
324
+
325
+ def connection
326
+ base_url = "#{uri.scheme}://#{uri.host}"
327
+ base_url = "#{base_url}:#{uri.port}" if uri.port
328
+ @connection ||= Faraday.new(:url => base_url) do |faraday|
329
+ faraday.request :url_encoded # form-encode POST params
330
+ faraday.response :logger # log requests to STDOUT
331
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
332
+ faraday.headers['X-Experience-API-Version'] = version.to_s
333
+ faraday.basic_auth(user_name, password)
334
+ end
335
+ end
336
+
337
+ def get_statement(id, parameter)
338
+ response = connection.get do |req|
339
+ req.url("#{path}statements")
340
+ req.headers['Content-Type'] = 'application/json'
341
+ req.params.merge!({parameter => id})
342
+ end
343
+ LrsResponse.new do |lrs|
344
+ lrs.status = response.status
345
+ if response.status== 200
346
+ lrs.content = Statement.new(json: response.body)
347
+ lrs.success = true
348
+ else
349
+ lrs.success = false
350
+ end
351
+ end
352
+ end
353
+
354
+ def get_document(resource, params, document)
355
+ response = connection.get do |req|
356
+ req.url("#{path}#{resource}")
357
+ req.params.merge!(params)
358
+ end
359
+ LrsResponse.new do |lrs|
360
+ lrs.status = response.status
361
+ if response.status == 200
362
+ lrs.success = true
363
+ lrs.content = JSON.parse(response.body)
364
+ # TODO FIX THIS
365
+ elsif response.status == 404
366
+ lrs.success = true
367
+ else
368
+ lrs.success = false
369
+ end
370
+ end
371
+ end
372
+
373
+ def delete_document(resource, params)
374
+ response = connection.delete do |req|
375
+ req.url("#{path}#{resource}")
376
+ req.params.merge!(params)
377
+ end
378
+ LrsResponse.new do |lrs|
379
+ lrs.status = response.status
380
+ if response.status == 204
381
+ lrs.success = true
382
+ else
383
+ lrs.success = false
384
+ end
385
+ end
386
+ end
387
+
388
+ def save_document(resource, params, document)
389
+ # TODO: Complete this
390
+ response = connection.put do |req|
391
+ req.url("#{path}#{resource}")
392
+ req.headers['Content-Type'] = 'application/json'
393
+ req.headers['If-Match'] = document.etag if document.etag
394
+ req.params.merge!(params)
395
+ req.body = document.content
396
+ end
397
+ LrsResponse.new do |lrs|
398
+ lrs.status = response.status
399
+ if response.status == 204
400
+ lrs.success = true
401
+ else
402
+ lrs.success = false
403
+ end
404
+ end
405
+ end
406
+
407
+ def get_profile_keys(resource, params)
408
+ # TODO FIX THIS
409
+ connection.get do |req|
410
+ req.url("#{path}#{resource}")
411
+ req.params.merge!(params)
412
+ end
413
+ end
414
+
415
+ end
416
+ end