aac-metrics 0.1.1 → 0.1.3

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: 24e65891d54936aeecfeab18e673f39e27c8d85ed71131658118bf0764502dd6
4
- data.tar.gz: f3eea991fad67e0445a52462a96e5af2f7e72737464c22883e80222926f2dd16
3
+ metadata.gz: 57db00976963f424efaab981a2b5066280b2de8dd8d3b241301ba3880fe1ce5c
4
+ data.tar.gz: fc386c30321ad3f94f03b00bfde72c19ea20dfebf400f6cc8322c841c5b5950b
5
5
  SHA512:
6
- metadata.gz: 433769c391cb7039957b338c6263ad8c0c107b8e2c67f3bfe7e4e30276816290c859bad7add69bdb06ed58e88d25fc9400cd89dd9791b600d028f023483ff8f9
7
- data.tar.gz: cdb2251cfa3d2531ffb2fa8b8cff28b0dc9902b19c8f83a43c8faa641981f92ad7cb1655c86d4b6f510df907ad41546fa63bfdd0475cebb41330348110a20ca2
6
+ metadata.gz: 4b9778b4e5a1dce3c1571bdd3038025b7ca6acec1dc9359cc4a04c5183dfd6640fd11fc06651d3059a5561f20d5f22974e6fa35c72d6b6273ee326b38c09612b
7
+ data.tar.gz: a2762f3a27e48b3445495d0ff16e46da23d80bd4d8e4a08f347df957fd3b595bc93f7697c769b92ba61bd633d57ec07b8df83c0bc2a1d374b763e48be9506724
@@ -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,7 +348,31 @@ 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
 
360
+ def self.fringe_words(locale)
361
+ @@fringe_words ||= {}
362
+ return @@fringe_words[locale] if @@fringe_words[locale]
363
+ locale = locale.split(/-|_/)[0]
364
+ path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets', "fringe.#{locale}.json"))
365
+ all_words = []
366
+ list = JSON.parse(File.read(path))
367
+ list.each do |set|
368
+ set['categories'].each do |cat|
369
+ all_words += cat['words']
370
+ end
371
+ end
372
+ all_words.uniq!
373
+ @@synonyms[locale] = all_words
374
+ end
375
+
296
376
  def self.base_words(locale)
297
377
  @@base_words ||= {}
298
378
  return @@base_words[locale] if @@base_words[locale]
@@ -2,31 +2,84 @@
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
-
5
+ # TODO: manual way to flag button as conceptually
6
+ # related to the same-locaed button on the
7
+ # prior board, allowing for a discounted penalty
6
8
  module AACMetrics::Metrics
7
- def self.analyze(obfset, output=true)
9
+ # TODO:
10
+ # 1. When navigating from one board to the next, grid locations
11
+ # with the same clone_id or semantic_id should result in a
12
+ # discount to overall search based more on the number of
13
+ # uncloned/unsemantic buttons than the number of total buttons
14
+ # (perhaps also factoring in the percent of board with that
15
+ # id present in the full board set)
16
+ # 2. When selecting a button with a semantic_id or clone_id,
17
+ # a discount to both search and selection should
18
+ # be applied based on the percent of boards that
19
+ # contain the same id at that grid location
20
+ # 3.5 When selecting a button with a semantic_id or clone_id,
21
+ # if the same id was present on the previous board,
22
+ # an additional discount to search and selection should be applied
23
+ def self.analyze(obfset, output=true)
8
24
  locale = nil
9
25
  buttons = []
10
- total_boards = 1
11
-
26
+ refs = {}
27
+ grid = {}
28
+
12
29
  if obfset.is_a?(Hash) && obfset['buttons']
13
30
  locale = obfset['locale'] || 'en'
31
+ refs = obfset['reference_counts']
32
+ grid = obfset['grid']
14
33
  buttons = []
15
34
  obfset['buttons'].each do |btn|
16
35
  buttons << {
17
36
  id: btn['id'],
18
37
  label: btn['label'],
19
38
  level: btn['level'],
20
- effort: btn['effort']
39
+ effort: btn['effort'],
40
+ semantic_id: btn['semantic_id'],
41
+ clone_id: btn['clone_id']
21
42
  }
22
43
  end
23
44
  total_boards = obfset['total_boards']
24
45
  else
25
46
  visited_board_ids = {}
26
47
  to_visit = [{board: obfset[0], level: 0, entry_x: 1.0, entry_y: 1.0}]
48
+ refs = {}
49
+ rows_tally = 0.0
50
+ cols_tally = 0.0
51
+ root_rows = nil
52
+ root_cols = nil
53
+ obfset.each do |board|
54
+ root_rows ||= board['grid']['rows']
55
+ root_cols ||= board['grid']['columns']
56
+ rows_tally += board['grid']['rows']
57
+ cols_tally += board['grid']['columns']
58
+ # determine frequency within the board set
59
+ # for each semantic_id and clone_id
60
+ if board['clone_ids']
61
+ boards['clone_ids'].each do |id|
62
+ refs[id] ||= 0
63
+ refs[id] += 1
64
+ end
65
+ end
66
+ if board['semantic_ids']
67
+ boards['semantic_ids'].each do |id|
68
+ refs[id] ||= 0
69
+ refs[id] += 1
70
+ end
71
+ end
72
+ end
73
+ if (rows_tally / obfset.length.to_f - root_rows).abs > 3 || (cols_tally / obfset.length.to_f - root_cols).abs > 3
74
+ root_rows = (rows_tally / obfset.length.to_f).floor
75
+ root_cols = (cols_tally / obfset.length.to_f).floor
76
+ end
77
+ pcts = {}
78
+ refs.each do |id, cnt|
79
+ pcts[id] = cnt.to_f / obfset.length.to_f
80
+ end
27
81
  locale = obfset[0]['locale']
28
82
  known_buttons = {}
29
- sqrt2 = Math.sqrt(2)
30
83
  while to_visit.length > 0
31
84
  board = to_visit.shift
32
85
  visited_board_ids[board[:board]['id']] = board[:level]
@@ -35,32 +88,63 @@ module AACMetrics::Metrics
35
88
  btn_width = 1.0 / board[:board]['grid']['columns'].to_f
36
89
  board_effort = 0
37
90
  # add effort for level of complexity when new board is rendered
38
- board_effort += 0.003 * board[:board]['grid']['rows'] * board[:board]['grid']['columns']
91
+ button_size = button_size_effort(board[:board]['grid']['rows'], board[:board]['grid']['columns'])
92
+ board_effort += button_size
39
93
  # add effort for number of visible buttons
40
- board_effort += 0.007 * board[:board]['grid']['order'].flatten.length
94
+ field_size = field_size_effort(board[:board]['grid']['order'].flatten.length)
95
+ board_effort += field_size
96
+ # decrease effort here for every button on the board
97
+ # whose semantic_id or clone_id is repeated in the board set
98
+ # -0.0025 (* pct of matching boards) for semantic_id
99
+ # -0.005 (* pct of matching boards) for clone_id
100
+ board[:board]['grid']['rows'].times do |row_idx|
101
+ board[:board]['grid']['columns'].times do |col_idx|
102
+ button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
103
+ button = board[:board]['buttons'].detect{|b| b['id'] == button_id }
104
+ if button && button['clone_id'] && pcts[button['clone_id']]
105
+ board_effort -= 0.005 * pcts[button['clone_id']]
106
+ elsif button && button['semantic_id'] && pcts[button['semantic_id']]
107
+ board_effort -= 0.0025 * pcts[button['semantic_id']]
108
+ end
109
+ end
110
+ end
111
+
41
112
  prior_buttons = 0
42
113
 
43
114
  board[:board]['grid']['rows'].times do |row_idx|
44
115
  board[:board]['grid']['columns'].times do |col_idx|
45
116
  button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
46
117
  button = board[:board]['buttons'].detect{|b| b['id'] == button_id }
47
- prior_buttons += 0.1 if !button
118
+ # prior_buttons += 0.1 if !button
48
119
  next unless button
49
120
  x = (btn_width / 2) + (btn_width * col_idx)
50
121
  y = (btn_height / 2) + (btn_height * row_idx)
51
122
  # prior_buttons = (row_idx * board[:board]['grid']['columns']) + col_idx
52
123
  effort = 0
124
+ # TODO: additional discount on board search effort
125
+ # if this button's semantic_id or clone_id
126
+ # was also present on the prior board
127
+ # board_effort * 0.5 for semantic_id
128
+ # board_effort * 0.33 for clone_id
53
129
  effort += board_effort
54
130
  # add effort for percent distance from entry point
55
- distance = Math.sqrt((x - board[:entry_x]) ** 2 + (y - board[:entry_y]) ** 2) / sqrt2
131
+ distance = distance_effort(x, y, board[:entry_x], board[:entry_y])
132
+ # TODO: decrease effective distance if the semantic_id or clone_id:
133
+ # - are used on other boards in the set (semi)
134
+ # distance * 0.5 (* pct of matching boards) for semantic_id
135
+ # distance * 0.33 (* pct of matching boards) for clone_id
136
+ # - was also present on the prior board (total)
137
+ # distance * 0.5 for semantic_id
138
+ # distance * 0.33 for clone_id
56
139
  effort += distance
57
- if distance > 0.1 || (board[:entry_x] == 1.0 && board[:entry_y] == 1.0)
140
+ if distance > DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN || (board[:entry_x] == 1.0 && board[:entry_y] == 1.0)
58
141
  # add small effort for every prior (visible) button when visually scanning
59
- effort += prior_buttons * 0.001
142
+ visual_scan = visual_scan_effort(prior_buttons)
143
+ effort += visual_scan
60
144
  else
61
145
  # ..unless it's right by the previous button, then
62
146
  # add tiny effort for local scan
63
- effort += distance * 0.5
147
+ effort += distance * SKIPPED_VISUAL_SCAN_DISTANCE_MULTIPLIER
64
148
  end
65
149
  # add cumulative effort from previous sequence
66
150
  effort += board[:prior_effort] || 0
@@ -81,11 +165,12 @@ module AACMetrics::Metrics
81
165
  end
82
166
  if try_visit
83
167
  next_board = obfset.detect{|brd| brd['id'] == button['load_board']['id'] }
168
+ puts "LIKE[] #{effort}" if button['label'] == 'like'
84
169
  if next_board
85
170
  to_visit.push({
86
171
  board: next_board,
87
172
  level: board[:level] + 1,
88
- prior_effort: effort + 1.0,
173
+ prior_effort: effort + BOARD_CHANGE_PROCESSING_EFFORT,
89
174
  entry_x: x,
90
175
  entry_y: y
91
176
  })
@@ -94,7 +179,8 @@ module AACMetrics::Metrics
94
179
  else
95
180
  word = button['label']
96
181
  existing = known_buttons[word]
97
- if !existing || board[:level] < existing[:level]
182
+ if !existing || existing[:effort] < effort #board[:level] < existing[:level]
183
+ puts "LIKE #{effort}" if button['label'] == 'like'
98
184
  known_buttons[word] = {
99
185
  id: "#{button['id']}::#{board[:board]['id']}",
100
186
  label: word,
@@ -120,11 +206,45 @@ module AACMetrics::Metrics
120
206
  locale: locale,
121
207
  total_boards: total_boards,
122
208
  total_buttons: buttons.length,
209
+ reference_counts: refs,
210
+ grid: {
211
+ rows: root_rows,
212
+ columns: root_cols
213
+ },
123
214
  buttons: buttons,
124
215
  levels: clusters
125
216
  }
126
217
  end
127
218
 
219
+ SQRT2 = Math.sqrt(2)
220
+ BUTTON_SIZE_MULTIPLIER = 0.09
221
+ FIELD_SIZE_MULTIPLIER = 0.017
222
+ VISUAL_SCAN_MULTIPLIER = 0.02
223
+ BOARD_CHANGE_PROCESSING_EFFORT = 1.0
224
+ DISTANCE_MULTIPLIER = 0.5
225
+ DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN = 0.1
226
+ SKIPPED_VISUAL_SCAN_DISTANCE_MULTIPLIER = 0.5
227
+
228
+ def self.button_size_effort(rows, cols)
229
+ BUTTON_SIZE_MULTIPLIER * (rows + cols) / 2
230
+ end
231
+
232
+ def self.field_size_effort(button_count)
233
+ FIELD_SIZE_MULTIPLIER * button_count
234
+ end
235
+
236
+ def self.visual_scan_effort(prior_buttons)
237
+ prior_buttons * VISUAL_SCAN_MULTIPLIER
238
+ end
239
+
240
+ def self.distance_effort(x, y, entry_x, entry_y)
241
+ Math.sqrt((x - entry_x) ** 2 + (y - entry_y) ** 2) / SQRT2 * DISTANCE_MULTIPLIER
242
+ end
243
+
244
+ def self.spelling_effort(word)
245
+ 10 + (word.length * 2.5)
246
+ end
247
+
128
248
  def self.analyze_and_compare(obfset, compset)
129
249
  target = AACMetrics::Metrics.analyze(obfset, false)
130
250
  res = {}.merge(target)
@@ -142,34 +262,43 @@ module AACMetrics::Metrics
142
262
  comp_efforts[btn[:label]] = btn[:effort]
143
263
  end
144
264
 
145
- efforts = {}
265
+ sortable_efforts = {}
146
266
  target_efforts = {}
147
267
  target_words = []
268
+ # Track effort scores for each button in the set,
269
+ # used to sort and for assessing priority
270
+ # TODO: keep a list of expected effort scores for
271
+ # very frequent core words and use that when available
148
272
  res[:buttons].each{|b|
149
273
  target_words << b[:label]
150
274
  target_efforts[b[:label]] = b[:effort]
151
- efforts[b[:label]] = b[:effort]
275
+ sortable_efforts[b[:label]] = b[:effort]
152
276
  comp = compare_buttons[b[:label]]
153
277
  if comp
154
278
  b[:comp_level] = comp[:level]
155
279
  b[:comp_effort] = comp[:effort]
156
280
  end
157
281
  }
282
+ # Effort scores are the mean of thw scores from the
283
+ # two sets, or just a singular value if in only one set
158
284
  compare[:buttons].each{|b|
159
- if efforts[b[:label]]
160
- efforts[b[:label]] += b[:effort]
161
- efforts[b[:label]] /= 2
285
+ if sortable_efforts[b[:label]]
286
+ sortable_efforts[b[:label]] += b[:effort]
287
+ sortable_efforts[b[:label]] /= 2
162
288
  else
163
- efforts[b[:label]] ||= b[:effort]
289
+ sortable_efforts[b[:label]] ||= b[:effort]
164
290
  end
165
291
  }
166
292
 
167
293
  core_lists = AACMetrics::Loader.core_lists(target[:locale])
168
294
  common_words_obj = AACMetrics::Loader.common_words(target[:locale])
169
295
  synonyms = AACMetrics::Loader.synonyms(target[:locale])
170
- common_words_obj['efforts'].each{|w, e| efforts[w] ||= e }
296
+ sentences = AACMetrics::Loader.sentences(target[:locale])
297
+ fringe = AACMetrics::Loader.fringe_words(target[:locale])
298
+ common_words_obj['efforts'].each{|w, e| sortable_efforts[w] ||= e }
171
299
  common_words = common_words_obj['words']
172
300
 
301
+ # Track which words are significantly harder or easier than expected
173
302
  too_easy = []
174
303
  too_hard = []
175
304
  target[:buttons].each do |btn|
@@ -183,11 +312,11 @@ module AACMetrics::Metrics
183
312
  end
184
313
 
185
314
 
186
- missing = (compare_words - target_words).sort_by{|w| efforts[w] }
315
+ missing = (compare_words - target_words).sort_by{|w| sortable_efforts[w] }
187
316
  missing = missing.select do |word|
188
317
  !synonyms[word] || (synonyms[word] & target_words).length == 0
189
318
  end
190
- extras = (target_words - compare_words).sort_by{|w| efforts[w] }
319
+ extras = (target_words - compare_words).sort_by{|w| sortable_efforts[w] }
191
320
  extras = extras.select do |word|
192
321
  !synonyms[word] || (synonyms[word] & compare_words).length == 0
193
322
  end
@@ -235,28 +364,42 @@ module AACMetrics::Metrics
235
364
  res[:cores] = {
236
365
  :common => {name: "Common Word List", list: common_words, average_effort: common_effort, comp_effort: comp_effort}
237
366
  }
367
+ target_effort_tally = 0.0
368
+ comp_effort_tally = 0.0
369
+ # For each core list, find any missing words, and compute
370
+ # the average level of effort for all words in the set,
371
+ # using a fallback effort metric if the word isn't in the
372
+ # board set
238
373
  # puts missing.join(' ')
239
374
  core_lists.each do |list|
240
- puts list['id']
241
375
  missing = []
376
+ comp_missing = []
242
377
  list_effort = 0
243
378
  comp_effort = 0
244
379
  list['words'].each do |word|
245
380
  words = [word] + (synonyms[word] || [])
381
+ # Check if any words from the core list are missing in the set
246
382
  if (target_words & words).length == 0
247
383
  missing << word
248
384
  end
385
+ if (compare_words & words).length == 0
386
+ comp_missing << word
387
+ end
388
+
389
+ # Calculate the effort for the target and comp sets
249
390
  effort = target_efforts[word]
250
391
  if !effort
251
392
  words.each{|w| effort ||= target_efforts[w] }
252
393
  end
253
- effort ||= 2 + (word.length * 2.5)
394
+ # Fallback penalty for missing word
395
+ effort ||= spelling_effort(word)
254
396
  list_effort += effort
397
+
255
398
  effort = comp_efforts[word]
256
399
  if !effort
257
400
  words.each{|w| effort ||= comp_efforts[w] }
258
401
  end
259
- effort ||= 2 + (word.length * 2.5)
402
+ effort ||= spelling_effort(word)
260
403
  comp_effort += effort
261
404
  end
262
405
  if missing.length > 0
@@ -266,8 +409,79 @@ module AACMetrics::Metrics
266
409
  end
267
410
  list_effort = list_effort.to_f / list['words'].length.to_f
268
411
  comp_effort = comp_effort.to_f / list['words'].length.to_f
412
+ target_effort_tally += list_effort
413
+ comp_effort_tally += comp_effort
269
414
  res[:cores][list['id']] = {name: list['name'], list: list['words'], average_effort: list_effort, comp_effort: comp_effort}
270
415
  end
416
+ target_effort_tally = (target_effort_tally / core_lists.to_a.length) * 5.0
417
+
418
+ comp_effort_tally = (comp_effort_tally / core_lists.to_a.length) * 5.0
419
+
420
+ # TODO: Assemble or allow a battery of word combinations,
421
+ # and calculate the level of effort for each sequence,
422
+ # as well as an average level of effort across combinations.
423
+ res[:sentences] = []
424
+ sentences.each do |words|
425
+ puts " #{words.join(' ')}"
426
+ BOARD_CHANGE_PROCESSING_EFFORT
427
+ target_effort_score = 0.0
428
+ comp_effort_score = 0.0
429
+ words.each_with_index do |word, idx|
430
+ synonym_words = [word] + (synonyms[word] || [])
431
+ effort = target_efforts[word] || target_efforts[word.downcase]
432
+ if !effort
433
+ synonym_words.each{|w| effort ||= target_efforts[w] }
434
+ end
435
+ effort ||= spelling_effort(word)
436
+ effort += (idx == 0) ? 0.0 : BOARD_CHANGE_PROCESSING_EFFORT
437
+ ee = effort
438
+ target_effort_score += effort
439
+
440
+ effort = comp_efforts[word] || comp_efforts[word.downcase]
441
+ if !effort
442
+ synonym_words.each{|w| effort ||= comp_efforts[w] }
443
+ end
444
+ effort ||= spelling_effort(word)
445
+ effort += (idx == 0) ? 0.0 : BOARD_CHANGE_PROCESSING_EFFORT
446
+ comp_effort_score += effort
447
+ end
448
+ target_effort_score = target_effort_score / words.length
449
+ comp_effort_score = comp_effort_score / words.length
450
+ res[:sentences] << {sentence: words.join(' '), words: words, effort: target_effort_score, comp_effort: comp_effort_score}
451
+ end
452
+ target_effort_tally += res[:sentences].map{|s| s[:effort] }.sum.to_f / res[:sentences].length.to_f * 3.0
453
+ comp_effort_tally += res[:sentences].map{|s| s[:comp_effort] }.sum.to_f / res[:sentences].length.to_f * 3.0
454
+
455
+ res[:fringe_words] = []
456
+ fringe.each do |word|
457
+ target_effort_score = 0.0
458
+ comp_effort_score = 0.0
459
+ synonym_words = [word] + (synonyms[word] || [])
460
+ effort = target_efforts[word] || target_efforts[word.downcase]
461
+ if !effort
462
+ synonym_words.each{|w| effort ||= target_efforts[w] }
463
+ end
464
+ effort ||= spelling_effort(word)
465
+ target_effort_score += effort
466
+
467
+ effort = comp_efforts[word] || comp_efforts[word.downcase]
468
+ if !effort
469
+ synonym_words.each{|w| effort ||= comp_efforts[w] }
470
+ end
471
+ effort ||= spelling_effort(word)
472
+ comp_effort_score += effort
473
+ res[:fringe_words] << {word: word, effort: target_effort_score, comp_effort: comp_effort_score}
474
+ end
475
+ target_effort_tally += res[:fringe_words].map{|s| s[:effort] }.sum.to_f / res[:fringe_words].length.to_f * 2.0
476
+ comp_effort_tally += res[:fringe_words].map{|s| s[:comp_effort] }.sum.to_f / res[:fringe_words].length.to_f * 2.0
477
+
478
+ target_effort_tally += 80 # placeholder value for future added calculations
479
+ comp_effort_tally += 80
480
+
481
+
482
+
483
+ res[:target_effort_score] = target_effort_tally
484
+ res[:comp_effort_score] = comp_effort_tally
271
485
  # puts "CONSIDER MAKING EASIER"
272
486
  res[:high_effort_words] = too_hard
273
487
  # 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: _____
@@ -43,7 +43,7 @@
43
43
  # thing explainer phrases, lists of words
44
44
 
45
45
  module AACMetrics
46
- VERSION = "0.1"
46
+ VERSION = "0.2"
47
47
  require 'aac-metrics/loader'
48
48
  require 'aac-metrics/metrics'
49
49
  end