kgrift 1.3.108
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 +7 -0
- data/KGrift/Gemfile +22 -0
- data/KGrift/README.md +66 -0
- data/KGrift/bin/kgrift +11 -0
- data/KGrift/grifter.yml +224 -0
- data/KGrift/internal_test_graphs/basic_test_graph_definition.yml +2915 -0
- data/KGrift/internal_test_graphs/unicode_test_graph_definition.yml +3070 -0
- data/KGrift/knewton_grifts/analytics_grifts.rb +103 -0
- data/KGrift/knewton_grifts/async_helper_grifts.rb +63 -0
- data/KGrift/knewton_grifts/authenticator_grifts.rb +46 -0
- data/KGrift/knewton_grifts/basic_grifts.rb +29 -0
- data/KGrift/knewton_grifts/batch_grifts.rb +14 -0
- data/KGrift/knewton_grifts/content_collection_grifts.rb +204 -0
- data/KGrift/knewton_grifts/content_collection_v1_grifts.rb +521 -0
- data/KGrift/knewton_grifts/content_eid_grifts.rb +41 -0
- data/KGrift/knewton_grifts/copy_grifts.rb +151 -0
- data/KGrift/knewton_grifts/deprecated_graph_and_taxonomy_grifts.rb +353 -0
- data/KGrift/knewton_grifts/goal_grifts.rb +203 -0
- data/KGrift/knewton_grifts/graph_and_taxonomy_grifts.rb +136 -0
- data/KGrift/knewton_grifts/graph_create_grifts.rb +34 -0
- data/KGrift/knewton_grifts/graph_query_grifts.rb +448 -0
- data/KGrift/knewton_grifts/graph_tools_grifts.rb +151 -0
- data/KGrift/knewton_grifts/graph_validation_grifts.rb +447 -0
- data/KGrift/knewton_grifts/helper_grifts.rb +92 -0
- data/KGrift/knewton_grifts/jmeter_data_grifts.rb +56 -0
- data/KGrift/knewton_grifts/learning_instance_grifts.rb +46 -0
- data/KGrift/knewton_grifts/looper_grifts.rb +34 -0
- data/KGrift/knewton_grifts/moxy_grifts.rb +64 -0
- data/KGrift/knewton_grifts/oauth_grifts.rb +182 -0
- data/KGrift/knewton_grifts/partner_grifts.rb +70 -0
- data/KGrift/knewton_grifts/partner_support_grifts.rb +85 -0
- data/KGrift/knewton_grifts/recommendation_setup_grifts.rb +215 -0
- data/KGrift/knewton_grifts/registration_grifts.rb +159 -0
- data/KGrift/knewton_grifts/registration_info_grifts.rb +23 -0
- data/KGrift/knewton_grifts/report_grifts.rb +122 -0
- data/KGrift/knewton_grifts/shell_command_grifts.rb +21 -0
- data/KGrift/knewton_grifts/student_flow_grifts.rb +560 -0
- data/KGrift/knewton_grifts/tag_grifts.rb +41 -0
- data/KGrift/knewton_grifts/test_data_grifts.rb +328 -0
- data/KGrift/knewton_grifts/test_user_grifts.rb +264 -0
- data/KGrift/lib/dtrace.rb +20 -0
- data/KGrift/lib/kgrift.rb +7 -0
- data/KGrift/test_data_generators/basic_book_and_taxonomies.rb +35 -0
- data/KGrift/test_data_generators/lo_test_graph.rb +34 -0
- data/KGrift/test_data_generators/partner_owned_book_and_taxonomies.rb +28 -0
- data/KGrift/test_data_generators/partner_owned_book_and_taxonomies_unicode.rb +28 -0
- data/KGrift/test_data_generators/sandcastle_book_and_taxonomies.rb +13 -0
- data/KGrift/test_data_generators/sandcastle_book_and_taxonomies.yml +3709 -0
- data/KGrift/test_data_generators/sandcastle_graph.rb +8 -0
- data/KGrift/test_data_generators/sandcastle_graph_definition.json +4483 -0
- data/KGrift/test_data_generators/sandcastle_graph_full.rb +7 -0
- data/KGrift/test_data_generators/sandcastle_taxonomies.yml +378 -0
- data/KGrift/test_data_generators/sandcastle_with_taxons.rb +56 -0
- data/KGrift/test_data_generators/sandcastle_with_taxons.yml +3994 -0
- data/KGrift/test_data_generators/test_users_and_partners.rb +76 -0
- data/kgrift.gemspec +43 -0
- metadata +144 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
def get_goals learning_instance_id, params={}
|
4
|
+
Log.info "Getting all goals for learning instance: '#{learning_instance_id}' , with params '#{params.keys.join(', ')}'"
|
5
|
+
kapi.get "/v0/learning-instances/#{learning_instance_id}/goals?" + URI.encode_www_form(params)
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_goal learning_instance_id, goal_id
|
9
|
+
Log.info "Getting goal '#{goal_id}' for learning instance '#{learning_instance_id}'"
|
10
|
+
kapi.get "/v0/learning-instances/#{learning_instance_id}/goals/#{goal_id}"
|
11
|
+
end
|
12
|
+
alias :get_single_goal :get_goal
|
13
|
+
|
14
|
+
def get_realized_goal learning_instance_id, goal_id
|
15
|
+
Log.info "Getting realized goal '#{goal_id}' for learning instance '#{learning_instance_id}'"
|
16
|
+
kapi.get "/v0/learning-instances/#{learning_instance_id}/realized-goals/#{goal_id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_goal goal_id, learning_instance_id
|
20
|
+
Log.info "Validating a goal with goal id #{goal_id} on learning_instance #{learning_instance_id}"
|
21
|
+
kapi.get "/v0/learning-instances/#{learning_instance_id}/goals/#{goal_id}/validate"
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Updating a goal does a full over-writing PUT. Anything not included from the original goal
|
26
|
+
# will be removed
|
27
|
+
def update_goal learning_instance_id, goal_id, overrides={}, params={}
|
28
|
+
Log.info "Updating a goal for learning instance: '#{learning_instance_id}' and goal: '#{goal_id}' , with params '#{params.keys.join(', ')}'"
|
29
|
+
kapi.put "/v0/learning-instances/#{learning_instance_id}/goals/#{goal_id}?" + URI.encode_www_form(params), overrides
|
30
|
+
end
|
31
|
+
|
32
|
+
#create goal will wait twice.
|
33
|
+
#First it will wait for the goal post to succeed
|
34
|
+
#We need to do this, because a learning instance must write to kraphs
|
35
|
+
#before we can make a goal off it, and that can take up to 60 seconds
|
36
|
+
#Second it will wait for the goal to write. Again kraphs asynchronously
|
37
|
+
#writes goals, so we have to wait for that to happen before our tests can depend on it
|
38
|
+
def create_goal learning_instance_id, overrides={}, params={}
|
39
|
+
Log.info "Creating a goal for learning instance: '#{learning_instance_id}' , with params '#{params.keys.join(', ')}'"
|
40
|
+
kapi.post "/v0/learning-instances/#{learning_instance_id}/goals?" + URI.encode_www_form(params), {
|
41
|
+
'target_modules' => [],
|
42
|
+
'recommendable_modules' => [],
|
43
|
+
'max_recommendation_size' => 3,
|
44
|
+
'start_date' => '2013-04-12T17:00:00.000Z'
|
45
|
+
}.merge(overrides)
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_goal_non_waiting learning_instance_id, overrides={}, params={}
|
49
|
+
Log.info "Creating a goal for learning instance: '#{learning_instance_id}' , with params '#{params.keys.join(', ')}'"
|
50
|
+
kapi.post "/v0/learning-instances/#{learning_instance_id}/goals?" + URI.encode_www_form(params), {
|
51
|
+
'target_modules' => [],
|
52
|
+
'recommendable_modules' => [],
|
53
|
+
'max_recommendation_size' => 3,
|
54
|
+
'start_date' => '2013-04-12T17:00:00.000Z'
|
55
|
+
}.merge(overrides)
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
#this convenience method builds out the module lists from a graph
|
60
|
+
#which avoids building complicated payloads in tests that are not
|
61
|
+
# explicitly concerned with the complicated bits of the payload
|
62
|
+
def create_goal_from_graph graph, learning_instance_id=nil, overrides={}, params={}
|
63
|
+
Log.info "Making a goal with random modules picked from a graph"
|
64
|
+
nodes = graph['nodes'].clone
|
65
|
+
Log.info nodes
|
66
|
+
|
67
|
+
module_ids = Set.new find_all_assessment_atoms(graph)
|
68
|
+
modules = nodes.select{|n| module_ids.member?n['id']}
|
69
|
+
|
70
|
+
Log.info modules
|
71
|
+
payload_overrides = {
|
72
|
+
'target_modules' => [
|
73
|
+
{ "module_id" => modules.shift['id'],
|
74
|
+
"target_score" => 1.0,
|
75
|
+
"target_date" => days_from_now(1)
|
76
|
+
},
|
77
|
+
{ "module_id" => modules.shift['id'],
|
78
|
+
"target_score" => 1.0,
|
79
|
+
"target_date" => days_from_now(2)
|
80
|
+
},
|
81
|
+
],
|
82
|
+
'recommendable_modules' => modules.map{|m| { "module_id" => m['id'] } }[0,5],
|
83
|
+
'max_recommendation_size' => 2,
|
84
|
+
}.merge(overrides)
|
85
|
+
|
86
|
+
unless learning_instance_id
|
87
|
+
learning_instance_id = create_learning_instance 'graph_id' => graph['id']
|
88
|
+
end
|
89
|
+
|
90
|
+
create_goal learning_instance_id, payload_overrides, params
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_goals_from_graph_yaml yaml_file_path
|
94
|
+
#Load yaml file
|
95
|
+
file_data = YAML.load_file(yaml_file_path)
|
96
|
+
|
97
|
+
#Cache graph locally
|
98
|
+
graph = get_graph_and_cache_it file_data['graph_id']
|
99
|
+
|
100
|
+
#Loop over all goals
|
101
|
+
output = Hash.new()
|
102
|
+
goals = file_data['goals']
|
103
|
+
goals.each do |goal_name, goal_def|
|
104
|
+
Log.info "Processing goal: #{goal_name}"
|
105
|
+
|
106
|
+
#Initialize output variable
|
107
|
+
output[goal_name] = Hash.new()
|
108
|
+
|
109
|
+
#Extract input parameters
|
110
|
+
target_module_ids = goal_def.fetch('target_module_ids',[])
|
111
|
+
recommendable_taxon_ids = goal_def.fetch('recommendable_taxon_ids',[])
|
112
|
+
recommendable_module_ids = goal_def.fetch('recommendable_module_ids',[])
|
113
|
+
|
114
|
+
#Define target module
|
115
|
+
if target_module_ids.empty?
|
116
|
+
target_module_ids = []
|
117
|
+
target_concept = find_prerequisite_concept graph
|
118
|
+
target_module_ids << find_associated_assessing_bndl(graph,target_concept)
|
119
|
+
target_module_ids << find_associated_assessing_atom(graph,target_concept)
|
120
|
+
end
|
121
|
+
Log.info "Target modules: #{target_module_ids}"
|
122
|
+
|
123
|
+
#Construct target modules hash
|
124
|
+
target_modules = []
|
125
|
+
target_modules = target_module_ids.map do |mod|
|
126
|
+
{
|
127
|
+
'module_id' => mod,
|
128
|
+
'target_date' => days_from_now(30),
|
129
|
+
'target_score' => 0.9,
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
#Define recommendable taxons and modules
|
134
|
+
Log.info "Setting recommendable modules"
|
135
|
+
rec_modules = recommendable_taxon_ids.map{ |taxon_id| {'taxon_id' => taxon_id} }
|
136
|
+
rec_modules += recommendable_module_ids.map{ |module_id| {'module_id' => module_id} }
|
137
|
+
if rec_modules.empty?
|
138
|
+
Log.info "No recommendable modules or taxons define; setting all modules as recommendable"
|
139
|
+
all_modules = find_all_recommendable_module_ids graph
|
140
|
+
rec_modules = all_modules.map{|m| {'module_id' => m}}
|
141
|
+
end
|
142
|
+
|
143
|
+
#Define goal object
|
144
|
+
goal_obj = {
|
145
|
+
'target_modules' => target_modules,
|
146
|
+
'max_recommendation_size' => (rec_modules.length < 3 ? rec_modules.length : 3),
|
147
|
+
'start_date' => datetime,
|
148
|
+
'recommendable_modules' => rec_modules,
|
149
|
+
}
|
150
|
+
Log.info "Creating goal with parameters #{goal_obj}"
|
151
|
+
|
152
|
+
#Create learning instance and goal
|
153
|
+
output[goal_name]['learning_instance_id'] = (create_learning_instance 'graph_id' => graph['id'])['id']
|
154
|
+
output[goal_name]['goal_id'] = (create_goal output[goal_name]['learning_instance_id'], goal_obj)['id']
|
155
|
+
end
|
156
|
+
|
157
|
+
#Output learning instance IDs and goal IDs to screen/file
|
158
|
+
output_file = File.join(File.dirname(yaml_file_path), File.basename(yaml_file_path, '.yml')+'_output'+'.yml')
|
159
|
+
File.open(output_file, 'w') {|f| f.write(output.to_yaml) }
|
160
|
+
end
|
161
|
+
|
162
|
+
# GROUP GOALS
|
163
|
+
|
164
|
+
def create_group_goal learning_instance_id, params
|
165
|
+
Log.debug "Creating a group goal for learning instance: #{learning_instance_id}"
|
166
|
+
kapi.post "/v0/learning-instances/#{learning_instance_id}/group-goals", params
|
167
|
+
end
|
168
|
+
|
169
|
+
def update_group_goal learning_instance_id, goal_id, params
|
170
|
+
Log.debug "Updating a group goal for learning instance: #{learning_instance_id} goal #{goal_id}"
|
171
|
+
kapi.put "/v0/learning-instances/#{learning_instance_id}/group-goals/#{goal_id}", params
|
172
|
+
end
|
173
|
+
|
174
|
+
def create_random_valid_group_goal_object graph, options={}
|
175
|
+
|
176
|
+
options = {
|
177
|
+
target_type: :lref,
|
178
|
+
num_targets: 1,
|
179
|
+
}.merge(options)
|
180
|
+
|
181
|
+
# find all all the recommendable modules that have a name
|
182
|
+
potential_target_values = if options[:target_type].to_sym == :mref
|
183
|
+
modules = find_all_assessment_modules_full(graph)
|
184
|
+
# pull the mref eids out of the nodes
|
185
|
+
modules.map{|m| m['temp_id'] =~ /^.ref-/ ? m['temp_id'] : "mref-#{m['temp_id']}" }
|
186
|
+
else
|
187
|
+
los = find_all_learning_objectives(graph)
|
188
|
+
los.map{|lo| lo['temp_id'] =~ /^.ref-/ ? lo['temp_id'] : "lref-#{lo['temp_id']}" }
|
189
|
+
end
|
190
|
+
|
191
|
+
# pick a random set of target modules/learning objectives
|
192
|
+
targets = potential_target_values.shuffle[0...options[:num_targets]]
|
193
|
+
|
194
|
+
# make the goal object
|
195
|
+
goal_obj = {
|
196
|
+
"name" => "TestGroupGoal #{random_string}",
|
197
|
+
"targets" => targets,
|
198
|
+
"scope" => {},
|
199
|
+
}
|
200
|
+
|
201
|
+
goal_obj
|
202
|
+
end
|
203
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# New get taxonomies endpoint
|
4
|
+
def get_taxonomies_for_graph (graph_id, overrides={})
|
5
|
+
Log.info "Getting taxonomies for graph id: '#{graph_id}'."
|
6
|
+
resp = nil
|
7
|
+
do_as_knewton_partner overrides.delete('use_current_user') do
|
8
|
+
resp = kapi.get "/v0/taxonomies/graph-id/#{graph_id}"
|
9
|
+
end
|
10
|
+
resp
|
11
|
+
end
|
12
|
+
|
13
|
+
# get get a taxonomy by graph id + taxonomy id
|
14
|
+
def get_taxonomy_for_graph (graph_id, taxonomy_id, overrides={})
|
15
|
+
Log.info "Getting taxonomy '#{taxonomy_id}' for graph id: '#{graph_id}'."
|
16
|
+
ts = get_taxonomies_for_graph graph_id
|
17
|
+
ts.find{ |t| t['id'] == taxonomy_id}
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_graph_summary id, overrides={}
|
21
|
+
Log.info "getting graph by id: '#{id}'"
|
22
|
+
kapi.get "/v0/graphs/#{id}/summary"
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_all_graphs overrides={}
|
26
|
+
Log.info "Getting all graphs with params '#{overrides.keys.join(', ')}'"
|
27
|
+
kapi.get '/v0/graphs?' + URI.encode_www_form(overrides)
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_all_graphs_for_partner partner_id, overrides={}
|
31
|
+
overrides.merge!'partner_id' => partner_id
|
32
|
+
get_all_graphs overrides
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_internal_graph_id partner_id, graph_name
|
36
|
+
graphs = get_all_graphs_for_partner partner_id
|
37
|
+
graph = graphs.find{|g| g['name'] == graph_name}
|
38
|
+
raise "Couldn't find graph for partner #{partner_id} with name #{graph_name}" unless graph
|
39
|
+
graph['name']
|
40
|
+
end
|
41
|
+
|
42
|
+
def translate_graph_gref_into_internal_id partner_id, gref
|
43
|
+
partner_graphs = get_all_graphs_for_partner partner_id
|
44
|
+
wanted_name = gref.sub(/^gref-/,'')
|
45
|
+
found = partner_graphs.find{|g| g['name'] == wanted_name}
|
46
|
+
raise "No such graph with name '#{wanted_name}' for partner: #{partner_id}" unless found
|
47
|
+
found['id']
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_graph id, overrides={}
|
51
|
+
Log.info "getting graph by id: '#{id}'"
|
52
|
+
do_as_knewton_partner overrides.delete('use_current_user') do
|
53
|
+
kapi.get "/v0/graphs/#{id}", timeout: 18000
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
GRAPH_CACHE_DIR = File.join(Dir.home,'.kgrift/graph_cache')
|
58
|
+
def get_graph_and_cache_it id, overrides={}
|
59
|
+
FileUtils::mkdir_p GRAPH_CACHE_DIR
|
60
|
+
cache_file_name = File.join(GRAPH_CACHE_DIR, id + '.json')
|
61
|
+
if File.exist?(cache_file_name)
|
62
|
+
return JSON.parse(IO.read(cache_file_name))
|
63
|
+
else
|
64
|
+
graph = get_graph id, overrides
|
65
|
+
File.open(cache_file_name, 'w'){|f| f.write JSON.pretty_generate(graph)}
|
66
|
+
return graph
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_graphs_map_of_assessing_bundles_for_post_requisite_concepts_and_cache_it graph, overrides={}
|
71
|
+
id = graph['id']
|
72
|
+
FileUtils::mkdir_p GRAPH_CACHE_DIR
|
73
|
+
cache_file_name = File.join(GRAPH_CACHE_DIR, id + '_postreqs_to_assessing_bndls.json')
|
74
|
+
if File.exist?(cache_file_name)
|
75
|
+
return JSON.parse(IO.read(cache_file_name))
|
76
|
+
else
|
77
|
+
assessing_bundles_for_postreq = get_map_of_assessing_bundles_for_post_requisite_concepts graph
|
78
|
+
File.open(cache_file_name, 'w'){|f| f.write JSON.pretty_generate(assessing_bundles_for_postreq)}
|
79
|
+
return assessing_bundles_for_postreq
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def graph_yaml id
|
84
|
+
graph = get_graph_and_cache_it id
|
85
|
+
File.open("#{id}.yml", 'w') {|f| f.write graph.to_yaml}
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_graph_by_name name, overrides={}
|
89
|
+
Log.info "getting graph by name: '#{name}'"
|
90
|
+
do_as_knewton_partner overrides.delete('use_current_user') do
|
91
|
+
kapi.get "/v0/graphs?name=#{URI.encode name}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def graph_exists? id, overrides={}
|
96
|
+
Log.info "Checking if graph exists with ID: #{id}"
|
97
|
+
do_as_knewton_partner overrides.delete('use_current_user') do
|
98
|
+
kapi.head "/v0/graphs/#{id}"
|
99
|
+
end
|
100
|
+
return true
|
101
|
+
rescue RequestException => e
|
102
|
+
return false if e.code == 404
|
103
|
+
raise e
|
104
|
+
end
|
105
|
+
|
106
|
+
def do_as_knewton_partner use_current_user=false, &blk
|
107
|
+
if use_current_user
|
108
|
+
yield
|
109
|
+
else
|
110
|
+
as_account :knerd do
|
111
|
+
yield
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def get_node graph_id, node_id, overrides={}
|
118
|
+
Log.info "Getting a node from graph: #{graph_id} -> #{node_id}"
|
119
|
+
kapi.get "/v0/graphs/#{graph_id}/nodes/#{node_id}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_learning_objective_node graph_id, learning_objective_id, overrides={}
|
123
|
+
Log.info "Getting a learning objective node from graph: #{graph_id} -> #{learning_objective_id}"
|
124
|
+
kapi.get "/v0/graphs/#{graph_id}/learningObjectives/#{learning_objective_id}"
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_edge graph_id, start_node_id, end_node_id, edge_type, overrides={}
|
128
|
+
Log.info "Getting an edge from graph: #{graph_id} -> #{start_node_id}_#{edge_type}_#{end_node_id}"
|
129
|
+
kapi.get "/v0/graphs/#{graph_id}/edges/#{start_node_id}_#{edge_type}_#{end_node_id}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_edges graph_id, node_id, overrides={}
|
133
|
+
Log.info "Getting a edges from graph #{graph_id}, starting from #{node_id}"
|
134
|
+
kapi.get "/v0/graphs/#{graph_id}/nodes/#{node_id}/edges"
|
135
|
+
end
|
136
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
"""
|
2
|
+
This file is all about creating graphs and taxonomies.
|
3
|
+
|
4
|
+
This is based on the POST /v0/book_and_taxonomies endpoint that Bivins implements.
|
5
|
+
This allows for making a graph and taxonomies in a way that is not Klique.
|
6
|
+
This is mostly intended for use by Bivins localmode integration tests.
|
7
|
+
|
8
|
+
Generally most tests should not use the create_book_and_taxonomies method, instead
|
9
|
+
they should use the KnewtonGraph object that allows for making test graphs through
|
10
|
+
Klique, from spreadsheets.
|
11
|
+
|
12
|
+
"""
|
13
|
+
|
14
|
+
# SUPPORTED / CURRENT METHODS
|
15
|
+
|
16
|
+
def create_book_and_taxonomies book_and_taxonomies, partner_id=nil
|
17
|
+
params = partner_id.nil? ? {} : { 'partner_id' => partner_id }
|
18
|
+
kapi.post '/v0/graphs/book_and_taxonomies?' + URI.encode_www_form(params),
|
19
|
+
book_and_taxonomies
|
20
|
+
end
|
21
|
+
|
22
|
+
def basic_book_and_taxonomies_post_object unicode=false
|
23
|
+
file_base = unicode ? 'unicode_test_graph_definition.yml' : 'basic_test_graph_definition.yml'
|
24
|
+
graph_definition_file = File.join(File.dirname(File.dirname(__FILE__)), 'internal_test_graphs', file_base)
|
25
|
+
obj = YAML.load_file graph_definition_file
|
26
|
+
obj['graph']['name'] = "#{obj['graph']['name']}_#{random_string}"
|
27
|
+
obj
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_basic_book_and_taxonomies partner_id=nil, unicode=false
|
31
|
+
response = create_book_and_taxonomies basic_book_and_taxonomies_post_object(unicode), partner_id
|
32
|
+
# return the graph id
|
33
|
+
response['objectId']
|
34
|
+
end
|
@@ -0,0 +1,448 @@
|
|
1
|
+
|
2
|
+
def get_node_by_id graph, node_id
|
3
|
+
graph['nodes'].find{|n| n['id'] == node_id}
|
4
|
+
end
|
5
|
+
|
6
|
+
def get_node_by_name graph, node_name
|
7
|
+
graph['nodes'].find{|n| n['name'] == node_name}
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_nodes_by_name_expr graph, node_name_expr
|
11
|
+
graph['nodes'].select{|n| n['name'] =~ node_name_expr}
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_random_atom_id graph
|
15
|
+
graph['nodes'].select{|n| n['type']=='module' and n['subtype']=='atom'}.sample['id']
|
16
|
+
end
|
17
|
+
|
18
|
+
# this will ensure module_id is an internal id
|
19
|
+
def internalize_module_id graph, module_id
|
20
|
+
if module_id =~ /^mref-/
|
21
|
+
found = graph['nodes'].find{|n| n['temp_id'] == module_id}
|
22
|
+
raise "No module with mref '#{module_id}' in graph: #{graph['id']}" unless found
|
23
|
+
found['id']
|
24
|
+
else
|
25
|
+
module_id
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#returns one of concept, atom, bndl, pool, unknown
|
30
|
+
def get_node_type graph, module_id
|
31
|
+
node = get_node_by_id graph, module_id
|
32
|
+
node_type = case node['type']
|
33
|
+
when 'concept'
|
34
|
+
'concept'
|
35
|
+
when 'module'
|
36
|
+
node['subtype']
|
37
|
+
else
|
38
|
+
raise "Unable to determine node type for node: #{module_id} - graph: #{graph['id']}"
|
39
|
+
end
|
40
|
+
Log.debug "get_node_type: type: #{node_type} - node: #{module_id} - graph: #{graph['id']}"
|
41
|
+
return node_type
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_all_recommendable_modules graph
|
45
|
+
graph['nodes'].select{|node| node['type']=='module' and (node['subtype']=='atom' or node['subtype']=='bndl')}
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_all_recommendable_module_ids graph
|
49
|
+
find_all_recommendable_modules(graph).map{|node| node['id']}
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_all_learning_objectives graph
|
53
|
+
graph['nodes'].select{|n| n['type'] == 'learning_objective'}
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_all_module_ids_contained_by_learning_objective_list graph, lref_list
|
57
|
+
all_learning_objectives = find_all_learning_objectives(graph)
|
58
|
+
expected_mrefs = lref_list.map do |lref|
|
59
|
+
lo_id = all_learning_objectives.find{|lo| lo['temp_id'] == lref[5..-1]}['id']
|
60
|
+
atom_ids = find_atoms_contained_by_bndl @graph, lo_id
|
61
|
+
atom_mrefs = atom_ids.map{|iid| get_node_by_id(@graph, iid)['temp_id']}
|
62
|
+
atom_mrefs
|
63
|
+
end.flatten.uniq
|
64
|
+
expected_mrefs
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_all_taxon_ids graph
|
68
|
+
nodes = graph['nodes'].select{|node| node['type']=='module' and (node['subtype']=='atom' or node['subtype']=='bndl')}
|
69
|
+
nodes.map{|node| node['taxon_ids']}.flatten.uniq
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_all_assessment_atoms graph
|
73
|
+
pool_ids = Set.new(graph['edges'].select{|edge| edge['type']=='assessed_by'}.map{|edge| edge['end']})
|
74
|
+
graph['edges'].select{|edge| pool_ids.include? edge['start']}.map{|edge| edge['end']}.uniq
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_all_assessment_modules graph
|
78
|
+
@assess_mods_cache ||= Hash.new do |cache, graph|
|
79
|
+
cache[graph] = begin
|
80
|
+
Log.debug "Calculating assessment modules for graph: #{graph['id']}"
|
81
|
+
pool_ids = graph['nodes'].select{|n| n['subtype']=='pool'}.map{|n| n['id']}
|
82
|
+
assessment_pool_ids = graph['edges'].select{|e| e['type']=='assessed_by' and
|
83
|
+
pool_ids.include?(e['end'])
|
84
|
+
}.map{|e| e['end']}
|
85
|
+
assessment_atom_ids = graph['edges'].select{|e| assessment_pool_ids.include?(e['start']) and
|
86
|
+
e['type']=='contains'
|
87
|
+
}.map{|e| e['end']}.uniq
|
88
|
+
assessment_bndl_ids = graph['edges'].select{|e| assessment_atom_ids.include?(e['end']) and not
|
89
|
+
pool_ids.include?(e['start']) and
|
90
|
+
e['type']=='contains'
|
91
|
+
}.map{|e| e['start']}.uniq
|
92
|
+
# we need to filter out learning_objectives... so we do this
|
93
|
+
assessment_bndl_ids.select!{|nid| node = graph['nodes'].find{|n| n['id'] == nid}; node['subtype'] == 'bndl'}
|
94
|
+
assessment_atom_ids+assessment_bndl_ids
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@assess_mods_cache[graph]
|
98
|
+
end
|
99
|
+
|
100
|
+
# above find_all_assessment_modules returns just modules IDs
|
101
|
+
# this method returns full module objects
|
102
|
+
def find_all_assessment_modules_full graph
|
103
|
+
mod_ids = find_all_assessment_modules(graph)
|
104
|
+
mod_ids.map{|mid| graph['nodes'].find{|n| n['id'] == mid}}
|
105
|
+
end
|
106
|
+
|
107
|
+
def find_all_instructional_modules graph
|
108
|
+
pool_ids = graph['nodes'].select{|n| n['subtype']=='pool'}.map{|n| n['id']}
|
109
|
+
instructional_pool_ids = graph['edges'].select{|e| e['type']=='taught_by' and
|
110
|
+
pool_ids.include?(e['end'])
|
111
|
+
}.map{|e| e['end']}
|
112
|
+
instructional_atom_ids = graph['edges'].select{|e| instructional_pool_ids.include?(e['start']) and
|
113
|
+
e['type']=='contains'
|
114
|
+
}.map{|e| e['end']}.uniq
|
115
|
+
instructional_bndl_ids = graph['edges'].select{|e| instructional_atom_ids.include?(e['end']) and not
|
116
|
+
pool_ids.include?(e['start']) and
|
117
|
+
e['type']=='contains'
|
118
|
+
}.map{|e| e['start']}.uniq
|
119
|
+
return instructional_atom_ids+instructional_bndl_ids
|
120
|
+
end
|
121
|
+
|
122
|
+
def find_atoms_contained_by_bndl graph, bndl_id
|
123
|
+
graph['edges'].
|
124
|
+
select{|e| e['start'] == bndl_id and e['type'] == 'contains' }.
|
125
|
+
map{|e| e['end']}
|
126
|
+
end
|
127
|
+
|
128
|
+
def find_all_bndl_ids graph
|
129
|
+
graph['nodes'].select{|node| node['type']=='module' and node['subtype']=='bndl'}.map{|node| node['id']}
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_all_concept_ids graph
|
133
|
+
graph['nodes'].select{|node| node['type'] == 'concept'}.map{|node| node['id']}
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_all_atom_ids graph
|
137
|
+
graph['nodes'].select{|node| node['subtype']=='atom' }.map{|node| node['id']}
|
138
|
+
end
|
139
|
+
|
140
|
+
def find_random_concept graph
|
141
|
+
graph['nodes'].select{|n| n['type']=='concept'}.sample['id']
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def find_concept_by_name graph, name
|
146
|
+
graph['nodes'].select{|node| node['type'] == 'concept'}.select{|node| node['name'] == name}.first
|
147
|
+
end
|
148
|
+
|
149
|
+
def find_next_concept graph, concept_id
|
150
|
+
prereq_edges = graph['edges'].select{|e| e['type'] == 'prerequisite' and e['start'] == concept_id}
|
151
|
+
return nil if prereq_edges.empty?
|
152
|
+
prereq_edges.first['end']
|
153
|
+
end
|
154
|
+
|
155
|
+
def find_post_requisite_concepts graph
|
156
|
+
Log.debug "finding post requisites for graph: #{graph['id']}"
|
157
|
+
#get all concepts
|
158
|
+
concepts = graph['nodes'].select{|n| n['type'] == 'concept'}
|
159
|
+
|
160
|
+
#filter it to concepts for which no edge starts with them,
|
161
|
+
#but have edges that end with them
|
162
|
+
pre_req_edges = graph['edges'].select{|e| e['type']=='prerequisite'}
|
163
|
+
post_req_concepts = concepts.select{|c| !pre_req_edges.any?{|e| e['start']==c['id']} and pre_req_edges.any?{|e| e['end']==c['id']} }
|
164
|
+
post_req_concepts.map{|c| c['id']}
|
165
|
+
end
|
166
|
+
|
167
|
+
def find_post_requisite_concept graph
|
168
|
+
#pick a random one and return it
|
169
|
+
find_post_requisite_concepts(graph).sample
|
170
|
+
end
|
171
|
+
|
172
|
+
def find_prerequisite_concept graph
|
173
|
+
#get all concepts
|
174
|
+
concepts = graph['nodes'].select{|n| n['type'] == 'concept'}
|
175
|
+
|
176
|
+
#filter it to concepts for which no edge starts with them,
|
177
|
+
#but have edges that end with them
|
178
|
+
pre_req_edges = graph['edges'].select{|e| e['type']=='prerequisite'}
|
179
|
+
post_req_concepts = concepts.select{|c|
|
180
|
+
pre_req_edges.any?{|e| e['start']==c['id']} and
|
181
|
+
!pre_req_edges.any?{|e| e['end']==c['id']} }
|
182
|
+
#pick a random one and return it
|
183
|
+
post_req_concepts.sample['id']
|
184
|
+
end
|
185
|
+
|
186
|
+
def find_associated_assessing_bndls graph, concept_id
|
187
|
+
Log.debug "Finding assessing bundles for concept #{concept_id}"
|
188
|
+
#find the 'assessed_by' edge
|
189
|
+
assessed_by_pool_id = graph['edges'].find{|e| e['type']=='assessed_by' and e['start'] == concept_id}['end']
|
190
|
+
|
191
|
+
#find everything contained by the assessed by edge
|
192
|
+
assessing_atom_ids= graph['edges'].select{|e| e['type']=='contains' and e['start'] == assessed_by_pool_id}.map{|e| e['end']}
|
193
|
+
|
194
|
+
#find all the edges that contain an assessing atom
|
195
|
+
edges_that_end_in_assessing_atom = graph['edges'].select{|e| e['type'] == 'contains' and assessing_atom_ids.include?(e['end'])}
|
196
|
+
|
197
|
+
assessing_bndls = edges_that_end_in_assessing_atom.map{|e| e['start']}.select{|nid| graph['nodes'].find{|n| n['id']==nid}['subtype']=='bndl'}
|
198
|
+
|
199
|
+
assessing_bndls.uniq!
|
200
|
+
Log.debug "Concept: #{concept_id} has assessing bundles: #{assessing_bndls.join(', ')}"
|
201
|
+
assessing_bndls
|
202
|
+
end
|
203
|
+
|
204
|
+
def find_associated_assessing_bndl graph, concept_id
|
205
|
+
find_associated_assessing_bndls(graph, concept_id).sample
|
206
|
+
end
|
207
|
+
|
208
|
+
def get_map_of_assessing_bundles_for_post_requisite_concepts graph
|
209
|
+
Log.debug "Building map of all assessing bundles for all postrequisite concepts in #{graph['id']}"
|
210
|
+
assessing_bundles_for_postreq = {}
|
211
|
+
postreqs = find_post_requisite_concepts graph
|
212
|
+
postreqs.each do |postreq|
|
213
|
+
bundles = find_associated_assessing_bndls graph, postreq
|
214
|
+
assessing_bundles_for_postreq[postreq] = bundles
|
215
|
+
end
|
216
|
+
Log.debug "Done building map of all assessing bundles for all postrequisite concepts in #{graph['id']}"
|
217
|
+
assessing_bundles_for_postreq
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns a list containing all concepts that are attached through assessed_by or taught_by pools.
|
221
|
+
# takes atoms or bundles. when given a bundle it looks up all the atoms contained and looks for
|
222
|
+
#
|
223
|
+
def find_associated_concept_ids graph, module_id
|
224
|
+
|
225
|
+
atom_ids = case(get_node_by_id(graph, module_id)['subtype'])
|
226
|
+
when 'atom'
|
227
|
+
[module_id]
|
228
|
+
when 'bndl'
|
229
|
+
find_atoms_contained_by_bndl(graph, module_id)
|
230
|
+
else
|
231
|
+
raise "Not an atom or bndl"
|
232
|
+
end
|
233
|
+
|
234
|
+
# Get the pools that contain the atom
|
235
|
+
pool_ids = graph['edges'].select{|e| e['type'] == 'contains' and atom_ids.include?(e['end'])}.map{|e| e['start']}.uniq
|
236
|
+
|
237
|
+
# Some edges here may be nil, so make the map to start node a separate step to avoid errors.
|
238
|
+
edges_to_concepts = pool_ids.map{|p| graph['edges'].select{|e| (e['type'] == 'assessed_by' or e['type'] == 'taught_by') and e['end'] == p}}.flatten.uniq
|
239
|
+
|
240
|
+
# Get the concepts that are assessed or taught by the pools -- ignore any pools that didn't map to concepts
|
241
|
+
concepts = edges_to_concepts.select{|p| p != nil}.map{|p| p['start']}.uniq
|
242
|
+
end
|
243
|
+
|
244
|
+
#Gets full information for the first concept an atom is a member of
|
245
|
+
def find_associated_concept graph, atom_id
|
246
|
+
get_node_by_id graph, (find_associated_concept_ids graph, atom_id).first
|
247
|
+
end
|
248
|
+
|
249
|
+
def find_associated_atoms graph, concept_id
|
250
|
+
Log.info "Finding all atoms for concept #{concept_id}"
|
251
|
+
(find_associated_assessing_atoms graph, concept_id).concat(find_associated_instructional_atoms graph, concept_id)
|
252
|
+
end
|
253
|
+
|
254
|
+
def find_associated_assessing_atoms graph, concept_id
|
255
|
+
Log.info "Finding assessing atoms for concept #{concept_id}"
|
256
|
+
|
257
|
+
#find the 'assessed_by' edge
|
258
|
+
assessed_by_pool_id = graph['edges'].find{|e| e['type']=='assessed_by' and e['start'] == concept_id}['end']
|
259
|
+
|
260
|
+
#find everything contained by the assessed by edge
|
261
|
+
assessing_atom_ids= graph['edges'].select{|e| e['type']=='contains' and e['start'] == assessed_by_pool_id}.map{|e| e['end']}.uniq
|
262
|
+
end
|
263
|
+
|
264
|
+
def find_associated_assessing_atom graph, concept_id
|
265
|
+
(find_associated_assessing_atoms graph, concept_id).sample
|
266
|
+
end
|
267
|
+
|
268
|
+
def find_associated_instructional_bndls graph, concept_id
|
269
|
+
Log.info "Finding instructional bundles for concept #{concept_id}"
|
270
|
+
|
271
|
+
#find the 'taught_by' edge
|
272
|
+
taught_by_pool_id = graph['edges'].find{|e| e['type']=='taught_by' and e['start'] == concept_id}['end']
|
273
|
+
|
274
|
+
#find everything contained by the taught by edge
|
275
|
+
teaching_atom_ids= graph['edges'].select{|e| e['type']=='contains' and e['start'] == taught_by_pool_id}.map{|e| e['end']}
|
276
|
+
|
277
|
+
#find all the edges that contain an teaching atom
|
278
|
+
edges_that_end_in_teaching_atom = graph['edges'].select{|e| e['type'] == 'contains' and teaching_atom_ids.include?(e['end'])}
|
279
|
+
|
280
|
+
teaching_bndls = edges_that_end_in_teaching_atom.map{|e| e['start']}.select{|nid| graph['nodes'].find{|n| n['id']==nid}['subtype']=='bndl'}
|
281
|
+
|
282
|
+
teaching_bndls.uniq
|
283
|
+
end
|
284
|
+
|
285
|
+
def find_associated_instructional_atoms graph, concept_id
|
286
|
+
Log.info "Finding instructional atoms for concept #{concept_id}"
|
287
|
+
|
288
|
+
#find the 'taught_by' edge
|
289
|
+
taught_by_pool_id = graph['edges'].find{|e| e['type']=='taught_by' and e['start'] == concept_id}['end']
|
290
|
+
|
291
|
+
#find everything contained by the taught by edge
|
292
|
+
teaching_atom_ids= graph['edges'].select{|e| e['type']=='contains' and e['start'] == taught_by_pool_id}.map{|e| e['end']}.uniq
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
#return ['instructional'] or ['assessment'] or ['instructional','assessment']
|
297
|
+
def get_content_types graph, module_id
|
298
|
+
|
299
|
+
node_type = get_node_type graph, module_id
|
300
|
+
atom_ids = case node_type
|
301
|
+
when 'atom'
|
302
|
+
[module_id]
|
303
|
+
when 'bndl'
|
304
|
+
graph['edges'].select{|e| e['type'] == 'contains' and e['start'] == module_id}.map{|e| e['end']}
|
305
|
+
end
|
306
|
+
|
307
|
+
bndl_ids = find_all_bndl_ids graph
|
308
|
+
|
309
|
+
pool_ids = graph['edges'].select{|e| e['type'] == 'contains' and atom_ids.include?(e['end']) }.map{|e| e['start']}.select{|n| not bndl_ids.include?(n)}
|
310
|
+
|
311
|
+
types = []
|
312
|
+
if graph['edges'].any?{|e| e['type'] == 'assessed_by' and pool_ids.include?(e['end'])}
|
313
|
+
types << 'assessment'
|
314
|
+
end
|
315
|
+
if graph['edges'].any?{|e| e['type'] == 'taught_by' and pool_ids.include?(e['end'])}
|
316
|
+
types << 'instructional'
|
317
|
+
end
|
318
|
+
|
319
|
+
types
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
def build_out_paths paths, path_so_far, prereq_edges
|
324
|
+
cur_concept = path_so_far.last
|
325
|
+
next_concepts = prereq_edges.select{|e| e['start'] == cur_concept}.map{|e| e['end']}
|
326
|
+
if next_concepts.empty?
|
327
|
+
paths << path_so_far
|
328
|
+
else
|
329
|
+
next_concepts.each{|c| build_out_paths paths, path_so_far + [c], prereq_edges}
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def get_graph_concept_paths graph
|
334
|
+
#find the starting concepts
|
335
|
+
prereq_edges = graph['edges'].select{|e| e['type'] == 'prerequisite'}
|
336
|
+
concept_ids = graph['nodes'].select{|n| n['type'] == 'concept'}.map{|n| n['id']}
|
337
|
+
starts = concept_ids.select{|c| not prereq_edges.any?{|e| e['end'] == c}}
|
338
|
+
|
339
|
+
#recursively build out the paths to ending concepts
|
340
|
+
paths = []
|
341
|
+
starts.each { |start| build_out_paths paths, [start], prereq_edges}
|
342
|
+
paths
|
343
|
+
end
|
344
|
+
|
345
|
+
def get_longest_concept_path graph
|
346
|
+
paths = get_graph_concept_paths graph
|
347
|
+
paths.group_by(&:size).max.last.last
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
def find_node_by_ref_label graph, label
|
352
|
+
nodes = graph['nodes'].select{ |n| n['temp_id'].split('-')[1] == label }
|
353
|
+
nodes[0]['temp_id']
|
354
|
+
end
|
355
|
+
|
356
|
+
def humanize_recommendation graph, rec
|
357
|
+
rec['module_names'] = rec['module_ids'].map{ |mid|
|
358
|
+
graph['nodes'].find{|n| n['id'] == mid}['name']
|
359
|
+
}
|
360
|
+
rec
|
361
|
+
end
|
362
|
+
|
363
|
+
def get_node_with_concept_and_content_type graph, module_id
|
364
|
+
node = get_node_by_id graph, module_id
|
365
|
+
content_type = get_content_types graph, module_id
|
366
|
+
if content_type.length == 1
|
367
|
+
content_type = content_type.first
|
368
|
+
end
|
369
|
+
concept = find_associated_concept graph, module_id
|
370
|
+
node['concept'] = concept
|
371
|
+
node['content_type'] = content_type
|
372
|
+
|
373
|
+
node
|
374
|
+
end
|
375
|
+
|
376
|
+
#helper function for finding a concept's child atoms and bundles
|
377
|
+
def get_associated_modules_by_type graph, concept_id, content_type
|
378
|
+
#find associated pool (prioritize assessment pools if multiple)
|
379
|
+
pool_id = nil
|
380
|
+
pools = []
|
381
|
+
graph['edges'].select{|e| e['start']==concept_id}.each do |e|
|
382
|
+
if e['type']==content_type and
|
383
|
+
!graph['nodes'].select{|n| n['id']==e['end'] and n['subtype']=='bndl'}.any?
|
384
|
+
pool_id = e['end']
|
385
|
+
end
|
386
|
+
pools << e['end']
|
387
|
+
end
|
388
|
+
|
389
|
+
#find associated atoms and bundles
|
390
|
+
atoms = graph['edges'].select{|e| e['start']==pool_id}.map{|e| e['end']}
|
391
|
+
bndls = graph['edges'].select{|e| atoms.include?(e['end']) and
|
392
|
+
!pools.include?(e['start'])
|
393
|
+
}.map{|e| e['start']}.uniq
|
394
|
+
return atoms + bndls
|
395
|
+
end
|
396
|
+
|
397
|
+
def get_taxon_eids_from_taxonomy taxonomy
|
398
|
+
# find the taxon eids... which is a bit harder than modules
|
399
|
+
#tref_prefix = "tref-#{taxonomy['name']}:"
|
400
|
+
tref_prefix = "tref-"
|
401
|
+
taxon_eids = taxonomy['taxons'].map do |taxon|
|
402
|
+
tref_suffix = taxon['name']
|
403
|
+
while taxon['parent_id']
|
404
|
+
taxon = taxonomy['taxons'].find{|t| t['id'] == taxon['parent_id']}
|
405
|
+
tref_suffix = "#{taxon['name']}|#{tref_suffix}"
|
406
|
+
end
|
407
|
+
tref_prefix + tref_suffix
|
408
|
+
end
|
409
|
+
taxon_eids
|
410
|
+
end
|
411
|
+
|
412
|
+
|
413
|
+
# batch_event_payload is a helper grift for making the body
|
414
|
+
# for POST /v0/registrations/{reg id}/batch-events
|
415
|
+
# it takes a list of module IDs (iid or eid)
|
416
|
+
# it figures out for each module if a graded or ungraded event is appropriate
|
417
|
+
# and makes the list of events accordingly
|
418
|
+
def create_batch_events_payload graph, module_ids
|
419
|
+
|
420
|
+
internal_module_ids = module_ids.map {|mid| internalize_module_id graph, mid }
|
421
|
+
|
422
|
+
module_content_types = internal_module_ids.map{|iid| get_content_types graph, iid }
|
423
|
+
|
424
|
+
batch_events_payload = []
|
425
|
+
|
426
|
+
module_ids.each_with_index do |mod_id, i|
|
427
|
+
event = {
|
428
|
+
'module_id' => mod_id,
|
429
|
+
'duration' => 12345,
|
430
|
+
'is_complete' => true,
|
431
|
+
'interaction_end_time' => days_from_now(-1),
|
432
|
+
}
|
433
|
+
if module_content_types[i] == ['instructional']
|
434
|
+
event.merge!({
|
435
|
+
'type' => 'ungraded-events',
|
436
|
+
})
|
437
|
+
else
|
438
|
+
event.merge!({
|
439
|
+
'type' => 'graded-events',
|
440
|
+
'score' => 1.0,
|
441
|
+
'is_correct' => true,
|
442
|
+
})
|
443
|
+
end
|
444
|
+
batch_events_payload << event
|
445
|
+
end
|
446
|
+
|
447
|
+
batch_events_payload
|
448
|
+
end
|