aac-metrics 0.1.0 → 0.1.2

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
2
  SHA256:
3
- metadata.gz: b00b87081818fb1fe8ff225b6b7120cc8bb26ba162d84c5a8c74f2e2571f6e84
4
- data.tar.gz: 7115ff9b2081671fa60e3538e706b61deb3a8bb35bbf1b822c74cc90e4cd57eb
3
+ metadata.gz: 5cfa37f885dec40c12ac7cbb86c44ac49ceec1138866b3de2e52b84facdddb87
4
+ data.tar.gz: e29f98ec3c9aa3840a513787003a5db196dd09b81699c3a81b252439929e4dcd
5
5
  SHA512:
6
- metadata.gz: 93eeae92b189182a457ca487a75327cce7aff50912f08a7152aa3b9715335bba80940687916182cde9421b249425ecfcf2f086954cf8f51db7dbef31013849d5
7
- data.tar.gz: 99eab7e060552fd6d11b80016aa9f9f687c59d5a445f4eddb039e61cb39aa35b4dcb0e9307efff51c13dd249ff6996300213b4d1aec4f509a3150e708122eac2
6
+ metadata.gz: 3e54f361e67a70f8c68f85469ffcc0c9da6c8269928b707b2e6e4b5c353e5bc19f64de3d93d50c7c37304822924bf8ca862e35e3f6a4ed692ad2781c444403bb
7
+ data.tar.gz: c12274971262bf327f9d2e6c29286bd1147fed6e5cf3d7a022a869d9810669e796fff20bfabcf13b3e973984a480a936e33468c4d4b2b7ad8f82737bf4b1b33d
@@ -78,6 +78,7 @@ module AACMetrics::Loader
78
78
  words_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets', "base_words"))
79
79
  words = nil
80
80
  do_ingest = true
81
+ relations_hash = {}
81
82
 
82
83
  while paths.length > 0
83
84
  path = paths.shift
@@ -130,17 +131,7 @@ module AACMetrics::Loader
130
131
  "id" => "btn#{btn_idx}",
131
132
  "label" => (btn['vocalization'] || '').length > 0 ? btn['vocalization'] : btn['label']
132
133
  }
133
- if do_ingest && new_btn['label']
134
- str = new_btn['label'].downcase.sub(/^\s+/, '').sub(/\s+$/, '')
135
- if str.scan(/\s+/).length < 2
136
- word_hash = Digest::MD5.hexdigest(str)[0, 10]
137
- raise "collision!" if words[word_hash] && words[word_hash] != str
138
- if add_words || words[word_hash]
139
- words[word_hash] = str
140
- new_btn['label'] = "$#{word_hash}"
141
- end
142
- end
143
- end
134
+ # record load_board reference
144
135
  btn_idx += 1
145
136
  if btn['load_board']
146
137
  if btn['load_board']['path']
@@ -167,6 +158,36 @@ module AACMetrics::Loader
167
158
  # treat action buttons for metrics
168
159
  new_btn = nil
169
160
  end
161
+ # temporarily save semantic_id and possible clone_id for later use
162
+ # 1. Buttons in the same location with the same
163
+ # semantic_id should be marked in the obfset as having
164
+ # the same semantic_id
165
+ # 2. Buttons in the same location with the same label & voc
166
+ # and same load_board setting
167
+ # should be marked in the obfset as having the same clone_id
168
+ ref = "#{new_json['grid']['rows']}x#{new_json['grid']['columns']}-#{row_ids}.#{col_id}"
169
+ if btn['semantic_id']
170
+ relations_hash["s#{ref}-#{btn['semantic_id']}"] ||= []
171
+ relations_hash["s#{ref}-#{btn['semantic_id']}"] << [new_json['id'], new_btn['id']]
172
+ end
173
+ if new_btn['label']
174
+ # TODO: currently doesn't enforce same-location on links, just whether it's a linked button or not
175
+ pre = new_btn['load_board'] ? 'cl' : 'c'
176
+ relations_hash["#{pre}#{ref}-#{new_btn['label']}"] ||= []
177
+ relations_hash["#{pre}#{ref}-#{new_btn['label']}"] ||= [new_json['id'], new_btn['id']]
178
+ end
179
+ if do_ingest && new_btn['label']
180
+ str = new_btn['label'].downcase.sub(/^\s+/, '').sub(/\s+$/, '')
181
+ if str.scan(/\s+/).length < 2
182
+ word_hash = Digest::MD5.hexdigest(str)[0, 10]
183
+ raise "collision!" if words[word_hash] && words[word_hash] != str
184
+ if add_words || words[word_hash]
185
+ words[word_hash] = str
186
+ new_btn['label'] = "$#{word_hash}"
187
+ end
188
+ end
189
+ end
190
+
170
191
  end
171
192
  new_row.push(new_btn ? new_btn['id'] : nil)
172
193
  new_json['buttons'].push(new_btn) if new_btn
@@ -176,6 +197,29 @@ module AACMetrics::Loader
176
197
  boards << new_json
177
198
  end
178
199
  end
200
+ # any semantic_id or clone_id repeats must be recorded
201
+ relations_hash.each do |id, btns|
202
+ if btns && btns.length > 0
203
+ btns.each do |brd_id, btn_id|
204
+ brd = boards.detect{|b| b['id'] == brd_id }
205
+ if brd && brd['buttons']
206
+ btn = brd['buttons'].detect{|b| b['id'] == btn_id }
207
+ if btn
208
+ if id.match(/^s/)
209
+ btn['semantic_id'] = id
210
+ brd['semantic_ids'] ||= []
211
+ brd['semantic_ids'] << id
212
+ elsif id.match(/^c/)
213
+ btn['clone_id'] = id
214
+ brd['clone_ids'] ||= []
215
+ brd['clone_ids'] << id
216
+ end
217
+ end
218
+ end
219
+ end
220
+ #
221
+ end
222
+ end
179
223
  boards.each do |brd|
180
224
  brd['buttons'].each do |btn|
181
225
  if btn['load_board'] && btn['load_board']['tmp_path']
@@ -184,9 +228,21 @@ module AACMetrics::Loader
184
228
  end
185
229
  end
186
230
  end
231
+ # TODO: record whether the board set is expected to have auto-home
187
232
  {boards: boards, words: words, words_path: words_path}
188
233
  end
189
234
 
235
+ # TODO: Qualitative assessments of common vocabularies,
236
+ # gather perspectives on what makes a "good" vocabulary
237
+ # and collect reviews from field experts, also free
238
+ # response sections.
239
+ # Some criteria:
240
+ # - works well for age group X, Y, Z
241
+ # - works well for a beginning communicator
242
+ # - allows long-term growth as-is
243
+ # - comprehensive core
244
+ # -
245
+
190
246
  def self.ingest(fn, token=nil)
191
247
  output = nil
192
248
  boards = nil
@@ -292,6 +348,14 @@ module AACMetrics::Loader
292
348
  @@synonyms[locale] = res
293
349
  end
294
350
 
351
+ def self.sentences(locale)
352
+ @@sentences ||= {}
353
+ return @@sentences[locale] if @@sentences[locale]
354
+ locale = locale.split(/-|_/)[0]
355
+ path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets', "sentences.#{locale}.json"))
356
+ res = JSON.parse(File.read(path))
357
+ @@sentences[locale] = res
358
+ end
295
359
 
296
360
  def self.base_words(locale)
297
361
  @@base_words ||= {}
@@ -2,31 +2,68 @@
2
2
  # Scores for average effort level for word sets (spelling if that's th only way)
3
3
  # Effort scores for sentence corpus
4
4
  # Effort algorithms for scanning/eyes
5
-
6
5
  module AACMetrics::Metrics
7
- def self.analyze(obfset, output=true)
6
+ # TODO:
7
+ # 1. When navigating from one board to the next, grid locations
8
+ # with the same clone_id or semantic_id should result in a
9
+ # discount to overall search based more on the number of
10
+ # uncloned/unsemantic buttons than the number of total buttons
11
+ # (perhaps also factoring in the percent of board with that
12
+ # id present in the full board set)
13
+ # 2. When selecting a button with a semantic_id or clone_id,
14
+ # a discount to both search and selection should
15
+ # be applied based on the percent of boards that
16
+ # contain the same id at that grid location
17
+ # 3.5 When selecting a button with a semantic_id or clone_id,
18
+ # if the same id was present on the previous board,
19
+ # an additional discount to search and selection should be applied
20
+ def self.analyze(obfset, output=true)
8
21
  locale = nil
9
22
  buttons = []
23
+ refs = {}
10
24
  total_boards = 1
11
-
25
+
12
26
  if obfset.is_a?(Hash) && obfset['buttons']
13
27
  locale = obfset['locale'] || 'en'
28
+ refs = obfset['reference_counts']
14
29
  buttons = []
15
30
  obfset['buttons'].each do |btn|
16
31
  buttons << {
17
32
  id: btn['id'],
18
33
  label: btn['label'],
19
34
  level: btn['level'],
20
- effort: btn['effort']
35
+ effort: btn['effort'],
36
+ semantic_id: btn['semantic_id'],
37
+ clone_id: btn['clone_id']
21
38
  }
22
39
  end
23
40
  total_boards = obfset['total_boards']
24
41
  else
25
42
  visited_board_ids = {}
26
43
  to_visit = [{board: obfset[0], level: 0, entry_x: 1.0, entry_y: 1.0}]
44
+ refs = {}
45
+ obfset.each do |board|
46
+ # determine frequency within the board set
47
+ # for each semantic_id and clone_id
48
+ if board['clone_ids']
49
+ boards['clone_ids'].each do |id|
50
+ refs[id] ||= 0
51
+ refs[id] += 1
52
+ end
53
+ end
54
+ if board['semantic_ids']
55
+ boards['semantic_ids'].each do |id|
56
+ refs[id] ||= 0
57
+ refs[id] += 1
58
+ end
59
+ end
60
+ end
61
+ pcts = {}
62
+ refs.each do |id, cnt|
63
+ pcts[id] = cnt.to_f / obfset.length.to_f
64
+ end
27
65
  locale = obfset[0]['locale']
28
66
  known_buttons = {}
29
- sqrt2 = Math.sqrt(2)
30
67
  while to_visit.length > 0
31
68
  board = to_visit.shift
32
69
  visited_board_ids[board[:board]['id']] = board[:level]
@@ -35,32 +72,63 @@ module AACMetrics::Metrics
35
72
  btn_width = 1.0 / board[:board]['grid']['columns'].to_f
36
73
  board_effort = 0
37
74
  # add effort for level of complexity when new board is rendered
38
- board_effort += 0.003 * board[:board]['grid']['rows'] * board[:board]['grid']['columns']
75
+ button_size = button_size_effort(board[:board]['grid']['rows'], board[:board]['grid']['columns'])
76
+ board_effort += button_size
39
77
  # add effort for number of visible buttons
40
- board_effort += 0.007 * board[:board]['grid']['order'].flatten.length
78
+ field_size = field_size_effort(board[:board]['grid']['order'].flatten.length)
79
+ board_effort += field_size
80
+ # decrease effort here for every button on the board
81
+ # whose semantic_id or clone_id is repeated in the board set
82
+ # -0.0025 (* pct of matching boards) for semantic_id
83
+ # -0.005 (* pct of matching boards) for clone_id
84
+ board[:board]['grid']['rows'].times do |row_idx|
85
+ board[:board]['grid']['columns'].times do |col_idx|
86
+ button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
87
+ button = board[:board]['buttons'].detect{|b| b['id'] == button_id }
88
+ if button && button['clone_id'] && pcts[button['clone_id']]
89
+ board_effort -= 0.005 * pcts[button['clone_id']]
90
+ elsif button && button['semantic_id'] && pcts[button['semantic_id']]
91
+ board_effort -= 0.0025 * pcts[button['semantic_id']]
92
+ end
93
+ end
94
+ end
95
+
41
96
  prior_buttons = 0
42
97
 
43
98
  board[:board]['grid']['rows'].times do |row_idx|
44
99
  board[:board]['grid']['columns'].times do |col_idx|
45
100
  button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
46
101
  button = board[:board]['buttons'].detect{|b| b['id'] == button_id }
47
- prior_buttons += 0.1 if !button
102
+ # prior_buttons += 0.1 if !button
48
103
  next unless button
49
104
  x = (btn_width / 2) + (btn_width * col_idx)
50
105
  y = (btn_height / 2) + (btn_height * row_idx)
51
106
  # prior_buttons = (row_idx * board[:board]['grid']['columns']) + col_idx
52
107
  effort = 0
108
+ # TODO: additional discount on board search effort
109
+ # if this button's semantic_id or clone_id
110
+ # was also present on the prior board
111
+ # board_effort * 0.5 for semantic_id
112
+ # board_effort * 0.33 for clone_id
53
113
  effort += board_effort
54
114
  # add effort for percent distance from entry point
55
- distance = Math.sqrt((x - board[:entry_x]) ** 2 + (y - board[:entry_y]) ** 2) / sqrt2
115
+ distance = distance_effort(x, y, board[:entry_x], board[:entry_y])
116
+ # TODO: decrease effective distance if the semantic_id or clone_id:
117
+ # - are used on other boards in the set (semi)
118
+ # distance * 0.5 (* pct of matching boards) for semantic_id
119
+ # distance * 0.33 (* pct of matching boards) for clone_id
120
+ # - was also present on the prior board (total)
121
+ # distance * 0.5 for semantic_id
122
+ # distance * 0.33 for clone_id
56
123
  effort += distance
57
- if distance > 0.1 || (board[:entry_x] == 1.0 && board[:entry_y] == 1.0)
124
+ if distance > DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN || (board[:entry_x] == 1.0 && board[:entry_y] == 1.0)
58
125
  # add small effort for every prior (visible) button when visually scanning
59
- effort += prior_buttons * 0.001
126
+ visual_scan = visual_scan_effort(prior_buttons)
127
+ effort += visual_scan
60
128
  else
61
129
  # ..unless it's right by the previous button, then
62
130
  # add tiny effort for local scan
63
- effort += distance * 0.5
131
+ effort += distance * SKIPPED_VISUAL_SCAN_DISTANCE_MULTIPLIER
64
132
  end
65
133
  # add cumulative effort from previous sequence
66
134
  effort += board[:prior_effort] || 0
@@ -81,11 +149,12 @@ module AACMetrics::Metrics
81
149
  end
82
150
  if try_visit
83
151
  next_board = obfset.detect{|brd| brd['id'] == button['load_board']['id'] }
152
+ puts "LIKE[] #{effort}" if button['label'] == 'like'
84
153
  if next_board
85
154
  to_visit.push({
86
155
  board: next_board,
87
156
  level: board[:level] + 1,
88
- prior_effort: effort + 1.0,
157
+ prior_effort: effort + BOARD_CHANGE_PROCESSING_EFFORT,
89
158
  entry_x: x,
90
159
  entry_y: y
91
160
  })
@@ -94,7 +163,8 @@ module AACMetrics::Metrics
94
163
  else
95
164
  word = button['label']
96
165
  existing = known_buttons[word]
97
- if !existing || board[:level] < existing[:level]
166
+ if !existing || existing[:effort] < effort #board[:level] < existing[:level]
167
+ puts "LIKE #{effort}" if button['label'] == 'like'
98
168
  known_buttons[word] = {
99
169
  id: "#{button['id']}::#{board[:board]['id']}",
100
170
  label: word,
@@ -120,50 +190,94 @@ module AACMetrics::Metrics
120
190
  locale: locale,
121
191
  total_boards: total_boards,
122
192
  total_buttons: buttons.length,
193
+ reference_counts: refs,
123
194
  buttons: buttons,
124
195
  levels: clusters
125
196
  }
126
197
  end
127
198
 
199
+ SQRT2 = Math.sqrt(2)
200
+ BUTTON_SIZE_MULTIPLIER = 0.09
201
+ FIELD_SIZE_MULTIPLIER = 0.017
202
+ VISUAL_SCAN_MULTIPLIER = 0.02
203
+ BOARD_CHANGE_PROCESSING_EFFORT = 1.0
204
+ DISTANCE_MULTIPLIER = 0.5
205
+ DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN = 0.1
206
+ SKIPPED_VISUAL_SCAN_DISTANCE_MULTIPLIER = 0.5
207
+
208
+ def self.button_size_effort(rows, cols)
209
+ BUTTON_SIZE_MULTIPLIER * (rows + cols) / 2
210
+ end
211
+
212
+ def self.field_size_effort(button_count)
213
+ FIELD_SIZE_MULTIPLIER * button_count
214
+ end
215
+
216
+ def self.visual_scan_effort(prior_buttons)
217
+ prior_buttons * VISUAL_SCAN_MULTIPLIER
218
+ end
219
+
220
+ def self.distance_effort(x, y, entry_x, entry_y)
221
+ Math.sqrt((x - entry_x) ** 2 + (y - entry_y) ** 2) / SQRT2 * DISTANCE_MULTIPLIER
222
+ end
223
+
224
+ def self.spelling_effort(word)
225
+ 10 + (word.length * 2.5)
226
+ end
227
+
128
228
  def self.analyze_and_compare(obfset, compset)
129
229
  target = AACMetrics::Metrics.analyze(obfset, false)
130
230
  res = {}.merge(target)
131
231
 
132
232
  compare = AACMetrics::Metrics.analyze(compset, false)
233
+ res[:comp_boards] = compare[:total_boards]
234
+ res[:comp_buttons] = compare[:total_buttons]
133
235
 
134
236
  compare_words = []
135
237
  compare_buttons = {}
238
+ comp_efforts = {}
136
239
  compare[:buttons].each do |btn|
137
240
  compare_words << btn[:label]
138
241
  compare_buttons[btn[:label]] = btn
242
+ comp_efforts[btn[:label]] = btn[:effort]
139
243
  end
140
244
 
141
- efforts = {}
245
+ sortable_efforts = {}
246
+ target_efforts = {}
142
247
  target_words = []
248
+ # Track effort scores for each button in the set,
249
+ # used to sort and for assessing priority
250
+ # TODO: keep a list of expected effort scores for
251
+ # very frequent core words and use that when available
143
252
  res[:buttons].each{|b|
144
253
  target_words << b[:label]
145
- efforts[b[:label]] = b[:effort]
254
+ target_efforts[b[:label]] = b[:effort]
255
+ sortable_efforts[b[:label]] = b[:effort]
146
256
  comp = compare_buttons[b[:label]]
147
257
  if comp
148
258
  b[:comp_level] = comp[:level]
149
259
  b[:comp_effort] = comp[:effort]
150
260
  end
151
261
  }
262
+ # Effort scores are the mean of thw scores from the
263
+ # two sets, or just a singular value if in only one set
152
264
  compare[:buttons].each{|b|
153
- if efforts[b[:label]]
154
- efforts[b[:label]] += b[:effort]
155
- efforts[b[:label]] /= 2
265
+ if sortable_efforts[b[:label]]
266
+ sortable_efforts[b[:label]] += b[:effort]
267
+ sortable_efforts[b[:label]] /= 2
156
268
  else
157
- efforts[b[:label]] ||= b[:effort]
269
+ sortable_efforts[b[:label]] ||= b[:effort]
158
270
  end
159
271
  }
160
272
 
161
273
  core_lists = AACMetrics::Loader.core_lists(target[:locale])
162
274
  common_words_obj = AACMetrics::Loader.common_words(target[:locale])
163
275
  synonyms = AACMetrics::Loader.synonyms(target[:locale])
164
- common_words_obj['efforts'].each{|w, e| efforts[w] ||= e }
276
+ sentences = AACMetrics::Loader.sentences(target[:locale])
277
+ common_words_obj['efforts'].each{|w, e| sortable_efforts[w] ||= e }
165
278
  common_words = common_words_obj['words']
166
279
 
280
+ # Track which words are significantly harder or easier than expected
167
281
  too_easy = []
168
282
  too_hard = []
169
283
  target[:buttons].each do |btn|
@@ -177,11 +291,11 @@ module AACMetrics::Metrics
177
291
  end
178
292
 
179
293
 
180
- missing = (compare_words - target_words).sort_by{|w| efforts[w] }
294
+ missing = (compare_words - target_words).sort_by{|w| sortable_efforts[w] }
181
295
  missing = missing.select do |word|
182
296
  !synonyms[word] || (synonyms[word] & target_words).length == 0
183
297
  end
184
- extras = (target_words - compare_words).sort_by{|w| efforts[w] }
298
+ extras = (target_words - compare_words).sort_by{|w| sortable_efforts[w] }
185
299
  extras = extras.select do |word|
186
300
  !synonyms[word] || (synonyms[word] & compare_words).length == 0
187
301
  end
@@ -199,27 +313,131 @@ module AACMetrics::Metrics
199
313
  missing = missing.select do |word|
200
314
  !synonyms[word] || (synonyms[word] & target_words).length == 0
201
315
  end
316
+ common_effort = 0
317
+ comp_effort = 0
318
+ common_words.each do |word|
319
+ effort = target_efforts[word]
320
+ if !effort && synonyms[word]
321
+ synonyms[word].each do |syn|
322
+ effort ||= target_efforts[syn]
323
+ end
324
+ end
325
+ effort ||= 2 + (word.length * 2.5)
326
+ common_effort += effort
327
+
328
+ effort = comp_efforts[word]
329
+ if !effort && synonyms[word]
330
+ synonyms[word].each do |syn|
331
+ effort ||= comp_efforts[syn]
332
+ end
333
+ end
334
+ effort ||= 2 + (word.length * 2.5)
335
+ comp_effort += effort
336
+ end
337
+ common_effort = common_effort.to_f / common_words.length.to_f
338
+ comp_effort = comp_effort.to_f / common_words.length.to_f
202
339
  # puts "MISSING FROM COMMON (#{missing.length})"
203
340
  res[:missing] = {
204
341
  :common => {name: "Common Word List", list: missing}
205
342
  }
343
+ res[:cores] = {
344
+ :common => {name: "Common Word List", list: common_words, average_effort: common_effort, comp_effort: comp_effort}
345
+ }
346
+ target_effort_tally = 0.0
347
+ comp_effort_tally = 0.0
348
+ # For each core list, find any missing words, and compute
349
+ # the average level of effort for all words in the set,
350
+ # using a fallback effort metric if the word isn't in the
351
+ # board set
206
352
  # puts missing.join(' ')
207
353
  core_lists.each do |list|
208
- puts list['id']
209
354
  missing = []
355
+ comp_missing = []
356
+ list_effort = 0
357
+ comp_effort = 0
210
358
  list['words'].each do |word|
211
359
  words = [word] + (synonyms[word] || [])
360
+ # Check if any words from the core list are missing in the set
212
361
  if (target_words & words).length == 0
213
362
  missing << word
214
- raise "orng" if word == 'orange'
215
363
  end
364
+ if (compare_words & words).length == 0
365
+ comp_missing << word
366
+ end
367
+
368
+ # Calculate the effort for the target and comp sets
369
+ effort = target_efforts[word]
370
+ if !effort
371
+ words.each{|w| effort ||= target_efforts[w] }
372
+ end
373
+ # Fallback penalty for missing word
374
+ effort ||= spelling_effort(word)
375
+ list_effort += effort
376
+
377
+ effort = comp_efforts[word]
378
+ if !effort
379
+ words.each{|w| effort ||= comp_efforts[w] }
380
+ end
381
+ effort ||= spelling_effort(word)
382
+ comp_effort += effort
216
383
  end
217
384
  if missing.length > 0
218
385
  # puts "MISSING FROM #{list['id']} (#{missing.length}):"
219
- res[:missing][list['id']] = {name: list['name'], list: missing}
386
+ res[:missing][list['id']] = {name: list['name'], list: missing, average_effort: list_effort}
220
387
  # puts missing.join(' ')
221
388
  end
389
+ list_effort = list_effort.to_f / list['words'].length.to_f
390
+ comp_effort = comp_effort.to_f / list['words'].length.to_f
391
+ target_effort_tally += list_effort
392
+ comp_effort_tally += comp_effort
393
+ res[:cores][list['id']] = {name: list['name'], list: list['words'], average_effort: list_effort, comp_effort: comp_effort}
394
+ end
395
+ target_effort_tally = (target_effort_tally / core_lists.to_a.length) * 5.0
396
+
397
+ comp_effort_tally = (comp_effort_tally / core_lists.to_a.length) * 5.0
398
+
399
+ # TODO: Assemble or allow a battery of word combinations,
400
+ # and calculate the level of effort for each sequence,
401
+ # as well as an average level of effort across combinations.
402
+ res[:sentences] = []
403
+ sentences.each do |words|
404
+ puts " #{words.join(' ')}"
405
+ BOARD_CHANGE_PROCESSING_EFFORT
406
+ target_effort_score = 0.0
407
+ comp_effort_score = 0.0
408
+ words.each_with_index do |word, idx|
409
+ synonym_words = [word] + (synonyms[word] || [])
410
+ effort = target_efforts[word] || target_efforts[word.downcase]
411
+ if !effort
412
+ synonym_words.each{|w| effort ||= target_efforts[w] }
413
+ end
414
+ effort ||= spelling_effort(word)
415
+ effort += (idx == 0) ? 0.0 : BOARD_CHANGE_PROCESSING_EFFORT
416
+ ee = effort
417
+ target_effort_score += effort
418
+
419
+ effort = comp_efforts[word] || comp_efforts[word.downcase]
420
+ if !effort
421
+ synonym_words.each{|w| effort ||= comp_efforts[w] }
422
+ end
423
+ effort ||= spelling_effort(word)
424
+ effort += (idx == 0) ? 0.0 : BOARD_CHANGE_PROCESSING_EFFORT
425
+ comp_effort_score += effort
426
+ puts " #{word} #{ee} #{effort}"
427
+ end
428
+ target_effort_score = target_effort_score / words.length
429
+ comp_effort_score = comp_effort_score / words.length
430
+ res[:sentences] << {sentence: words.join(' '), words: words, effort: target_effort_score, comp_effort: comp_effort_score}
222
431
  end
432
+ target_effort_tally += res[:sentences].map{|s| s[:effort] }.sum.to_f / res[:sentences].length.to_f * 3.0
433
+ comp_effort_tally += res[:sentences].map{|s| s[:comp_effort] }.sum.to_f / res[:sentences].length.to_f * 3.0
434
+ target_effort_tally += 100 # placeholder value for future added calculations
435
+ comp_effort_tally += 100
436
+
437
+
438
+
439
+ res[:target_effort_score] = target_effort_tally
440
+ res[:comp_effort_score] = comp_effort_tally
223
441
  # puts "CONSIDER MAKING EASIER"
224
442
  res[:high_effort_words] = too_hard
225
443
  # puts too_hard.join(' ')
data/lib/aac-metrics.rb CHANGED
@@ -8,7 +8,7 @@
8
8
  # Are grammar inflections available?
9
9
  # Does this support auto-home, or other motor planning supports?
10
10
  # Are other language options available?
11
- # How is thee vocabulary organized?
11
+ # How is the vocabulary organized?
12
12
  # What platforms are supported?
13
13
  # Access to keyboard w/ prediction? Numbers?
14
14
  # How easy is it to say these personalized sentences: _____