aac-metrics 0.1.7 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29c12bbac8e7d433302f16f6f7061aa66992875774c9de39374ae62d1e935c02
4
- data.tar.gz: 508c9d5ce1db72a6274f3709cadd4ebcd3e703e79e6888647dbcaadbcc672e60
3
+ metadata.gz: 62c96e82e12b2a0c9eb03ad38b65af63c348731faf58ac3920de4d87c77e2d1c
4
+ data.tar.gz: 16c049e5e33c1b18cdb374afbf9117299fd47bddc91c15042284f2f41f96dc6b
5
5
  SHA512:
6
- metadata.gz: 184f934c0be547f2e8ae6ed09060911398827eaefd32bbf3684ab6f9a1486f3961701017b7fb4271b46798efaf89a4431f43a59676401c0a1d7d5de302fda613
7
- data.tar.gz: 77b725d14521a3bb1659960b51b108326a56b44427956b0a03082312e070be8e7908f731df6dfb45f99456c09e572cbc25a16d172a672e5262d979f5fa20ec18
6
+ metadata.gz: c37bef0e90cbea57f8e0d860e2751c1508028b97b9c76e669c6a9f33d5921dc7cf34ea819249c744c7e30a0e08aa9ebb9e9c4bc19fbacc85e56f1ebd7121a9df
7
+ data.tar.gz: bf47f7d0bbe809195ad6576269532e0ed4b26cae918ae605b671e6c562fcfdf1e5fba892bd7e0c936a18082b8a9c291c0bfec75a44eb9b9ec3f320e1bec1e52f
@@ -37,6 +37,29 @@ module AACMetrics::Loader
37
37
  end
38
38
  if obfset
39
39
  json = JSON.parse(File.read(obfset))
40
+ relations_hash = {}
41
+ json.each do |board|
42
+ board['grid']['order'].each_with_index do |row, row_idx|
43
+ row.each_with_index do |id, col_idx|
44
+ button = id && board['buttons'].detect{|b| b['id'] == id }
45
+ if button && button['label'] && !button['clone_id']
46
+ ref = "#{board['grid']['rows']}x#{board['grid']['columns']}-#{row_idx}.#{col_idx}"
47
+ pre = 'c'
48
+ relations_hash["cpre#{ref}-#{button['label']}"] ||= []
49
+ relations_hash["cpre#{ref}-#{button['label']}"] << [board, button]
50
+ end
51
+ end
52
+ end
53
+ end
54
+ relations_hash.each do |id, cells|
55
+ if cells.length > 1
56
+ cells.each do |board, button|
57
+ board['clone_ids'] ||= []
58
+ board['clone_ids'] << id
59
+ button['clone_id'] ||= id
60
+ end
61
+ end
62
+ end
40
63
  elsif analysis
41
64
  json = JSON.parse(File.read(analysis))
42
65
  end
@@ -84,6 +107,7 @@ module AACMetrics::Loader
84
107
  path = paths.shift
85
108
  visited_paths[path] = idx
86
109
  new_json = {
110
+ "format" => 'obs',
87
111
  "id" => "brd#{idx}",
88
112
  "buttons" => [],
89
113
  "grid" => {},
@@ -158,28 +182,29 @@ module AACMetrics::Loader
158
182
  # treat action buttons for metrics
159
183
  new_btn = nil
160
184
  end
161
- # temporarily save semantic_id and possible clone_id for later use
185
+ # temporarily save semantic_id and possibly clone_id for later use
162
186
  # 1. Buttons in the same location with the same
163
187
  # semantic_id should be marked in the obfset as having
164
188
  # the same semantic_id
165
189
  # 2. Buttons in the same location with the same label & voc
166
190
  # and same load_board setting
167
191
  # 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}"
192
+ ref = "#{new_json['grid']['rows']}x#{new_json['grid']['columns']}-#{row_idx}.#{col_idx}"
169
193
  if btn['semantic_id']
170
194
  relations_hash["s#{ref}-#{btn['semantic_id']}"] ||= []
171
195
  relations_hash["s#{ref}-#{btn['semantic_id']}"] << [new_json['id'], new_btn['id']]
172
196
  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'
197
+ if new_btn && new_btn['label']
198
+ #pre = new_btn['load_board'] ? 'cl' : 'c'
199
+ pre = 'c'
176
200
  relations_hash["#{pre}#{ref}-#{new_btn['label']}"] ||= []
177
- relations_hash["#{pre}#{ref}-#{new_btn['label']}"] ||= [new_json['id'], new_btn['id']]
201
+ relations_hash["#{pre}#{ref}-#{new_btn['label']}"] << [new_json['id'], new_btn['id']]
178
202
  end
179
- if do_ingest && new_btn['label']
203
+ if do_ingest && new_btn && new_btn['label']
180
204
  str = new_btn['label'].downcase.sub(/^\s+/, '').sub(/\s+$/, '')
181
205
  if str.scan(/\s+/).length < 2
182
206
  word_hash = Digest::MD5.hexdigest(str)[0, 10]
207
+ # Will need to re-evaluate hash process if it ever finds a collision with an already-saved word
183
208
  raise "collision!" if words[word_hash] && words[word_hash] != str
184
209
  if add_words || words[word_hash]
185
210
  words[word_hash] = str
@@ -197,9 +222,18 @@ module AACMetrics::Loader
197
222
  boards << new_json
198
223
  end
199
224
  end
225
+ # finalize all board paths once done iterating
226
+ boards.each do |brd|
227
+ brd['buttons'].each do |btn|
228
+ if btn['load_board'] && btn['load_board']['tmp_path']
229
+ btn['load_board']['id'] = "brd#{visited_paths[btn['load_board']['tmp_path']]}" if visited_paths[btn['load_board']['tmp_path']]
230
+ btn['load_board'].delete('tmp_path')
231
+ end
232
+ end
233
+ end
200
234
  # any semantic_id or clone_id repeats must be recorded
201
235
  relations_hash.each do |id, btns|
202
- if btns && btns.length > 0
236
+ if btns && btns.length > 1
203
237
  btns.each do |brd_id, btn_id|
204
238
  brd = boards.detect{|b| b['id'] == brd_id }
205
239
  if brd && brd['buttons']
@@ -220,32 +254,18 @@ module AACMetrics::Loader
220
254
  #
221
255
  end
222
256
  end
223
- boards.each do |brd|
224
- brd['buttons'].each do |btn|
225
- if btn['load_board'] && btn['load_board']['tmp_path']
226
- btn['load_board']['id'] = "brd#{visited_paths[btn['load_board']['tmp_path']]}" if visited_paths[btn['load_board']['tmp_path']]
227
- btn['load_board'].delete('tmp_path')
228
- end
229
- end
230
- end
231
257
  # TODO: record whether the board set is expected to have auto-home
232
258
  {boards: boards, words: words, words_path: words_path}
233
259
  end
234
260
 
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
-
246
261
  def self.ingest(fn, token=nil)
247
262
  output = nil
248
263
  boards = nil
264
+ if fn.match(/manifest.json/)
265
+ json = JSON.parse(File.read(fn))
266
+ root_fn = json['root']
267
+ fn = fn.sub(/manifest.json/, root_fn)
268
+ end
249
269
  if fn.match(/\.obfset$/)
250
270
  boards = retrieve(fn, false)
251
271
  output = fn
@@ -268,7 +288,8 @@ module AACMetrics::Loader
268
288
  end
269
289
  end
270
290
  if boards
271
- analysis = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets', fn.sub(/\.obfset$/, '.analysis')))
291
+ analysis = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'sets', output.sub(/\.obfset$/, '.analysis')))
292
+ analysis = output.sub(/\.obfset$/, '.analysis')
272
293
  res = AACMetrics::Metrics.analyze(boards, false)
273
294
  f = File.open(analysis, 'w')
274
295
  f.write(JSON.pretty_generate(res))
@@ -370,9 +391,18 @@ module AACMetrics::Loader
370
391
  end
371
392
  end
372
393
  all_words.uniq!
373
- @@synonyms[locale] = all_words
394
+ @@fringe_words[locale] = all_words
374
395
  end
375
-
396
+
397
+ def self.common_fringe_words(locale)
398
+ @@common_fringe_words ||= {}
399
+ return @@common_fringe_words[locale] if @@common_fringe_words[locale]
400
+ common = self.common_words(locale)['words']
401
+ core = self.core_lists('en').map{|r| r['words'] }.flatten.compact.uniq
402
+ all_words = common - core
403
+ @@common_fringe_words[locale] = all_words
404
+ end
405
+
376
406
  def self.base_words(locale)
377
407
  @@base_words ||= {}
378
408
  return @@base_words[locale] if @@base_words[locale]
@@ -1,38 +1,42 @@
1
1
  # TODO:
2
2
  # Qualitative evaluation criteria:
3
- # - this set is easy to learn for communicators
4
- # - this set is easy to learn for supporters
5
- # - the vocabulary organization of this set makes sense
6
- # - this set provides clear opportunities for user-specific words to be added
7
- # - grammatical forms, growth over time
3
+ # - this set looks easy to learn for communicators
4
+ # - this set looks easy to learn for supporters
5
+ # - this vocabulary organization of this set makes sense
6
+ # - this set provides clear locations for user-specific words to be added
7
+ # - this set supports the use of grammatical forms (tenses and other inflections)
8
+ # - this set provides predefined simplification for beginning communicators
9
+ # - this set allows for long-term vocabulary growth over time
10
+ # - this vocabulary looks like it will work well for young users
11
+ # - this vocabulary looks like it will work well for adult users
12
+
8
13
  # Effort algorithms for scanning/eyes
9
- # TODO: manual way to flag button as conceptually
10
- # related to the same-locaed button on the
11
- # prior board, allowing for a discounted penalty
12
14
  module AACMetrics::Metrics
13
- # TODO:
14
- # 1. When navigating from one board to the next, grid locations
15
+ # A. When navigating from one board to the next, grid locations
15
16
  # with the same clone_id or semantic_id should result in a
16
17
  # discount to overall search based more on the number of
17
18
  # uncloned/unsemantic buttons than the number of total buttons
18
19
  # (perhaps also factoring in the percent of board with that
19
20
  # id present in the full board set)
20
- # 2. When selecting a button with a semantic_id or clone_id,
21
+ # B. When selecting a button with a semantic_id or clone_id,
21
22
  # a discount to both search and selection should
22
23
  # be applied based on the percent of boards that
23
24
  # contain the same id at that grid location
24
- # 3.5 When selecting a button with a semantic_id or clone_id,
25
+ # C. When selecting a button with a semantic_id or clone_id,
25
26
  # if the same id was present on the previous board,
26
27
  # an additional discount to search and selection should be applied
27
- def self.analyze(obfset, output=true, include_obfset=false)
28
+ # D When selecting a button with a semantic_id or clone_id,
29
+ # apply a steep discount to the button in the same location
30
+ # as the link used to get there if they share an id
31
+ def self.analyze(obfset, output=true, include_obfset=false)
28
32
  locale = nil
29
33
  buttons = []
30
- refs = {}
34
+ set_refs = {}
31
35
  grid = {}
32
36
 
33
37
  if obfset.is_a?(Hash) && obfset['buttons']
34
38
  locale = obfset['locale'] || 'en'
35
- refs = obfset['reference_counts']
39
+ set_refs = obfset['reference_counts']
36
40
  grid = obfset['grid']
37
41
  buttons = []
38
42
  obfset['buttons'].each do |btn|
@@ -49,13 +53,15 @@ module AACMetrics::Metrics
49
53
  else
50
54
  visited_board_ids = {}
51
55
  to_visit = [{board: obfset[0], level: 0, entry_x: 1.0, entry_y: 1.0}]
52
- refs = {}
56
+ set_refs = {}
57
+ cell_refs = {}
53
58
  rows_tally = 0.0
54
59
  cols_tally = 0.0
55
60
  root_rows = nil
56
61
  root_cols = nil
57
62
  # Gather repeated words/concepts
58
63
  obfset.each do |board|
64
+ # try to figure out the average grid size for board set
59
65
  root_rows ||= board['grid']['rows']
60
66
  root_cols ||= board['grid']['columns']
61
67
  rows_tally += board['grid']['rows']
@@ -63,25 +69,35 @@ module AACMetrics::Metrics
63
69
  # determine frequency within the board set
64
70
  # for each semantic_id and clone_id
65
71
  if board['clone_ids']
66
- boards['clone_ids'].each do |id|
67
- refs[id] ||= 0
68
- refs[id] += 1
72
+ board['clone_ids'].each do |id|
73
+ set_refs[id] ||= 0
74
+ set_refs[id] += 1
75
+ end
76
+ end
77
+ board['grid']['rows'].times do |row_idx|
78
+ board['grid']['columns'].times do |col_idx|
79
+ id = (board['grid']['order'][row_idx] || [])[col_idx]
80
+ cell_refs["#{row_idx}.#{col_idx}"] ||= 0.0
81
+ cell_refs["#{row_idx}.#{col_idx}"] += id ? 1.0 : 0.25
69
82
  end
70
83
  end
71
84
  if board['semantic_ids']
72
- boards['semantic_ids'].each do |id|
73
- refs[id] ||= 0
74
- refs[id] += 1
85
+ board['semantic_ids'].each do |id|
86
+ set_refs[id] ||= 0
87
+ set_refs[id] += 1
75
88
  end
76
89
  end
77
90
  end
91
+ # If the average grid size is much different than the root
92
+ # grid size, only then use the average as the size for this board set
78
93
  if (rows_tally / obfset.length.to_f - root_rows).abs > 3 || (cols_tally / obfset.length.to_f - root_cols).abs > 3
79
94
  root_rows = (rows_tally / obfset.length.to_f).floor
80
95
  root_cols = (cols_tally / obfset.length.to_f).floor
81
96
  end
82
- pcts = {}
83
- refs.each do |id, cnt|
84
- pcts[id] = cnt.to_f / obfset.length.to_f
97
+ set_pcts = {}
98
+ set_refs.each do |id, cnt|
99
+ loc = id.split(/-/)[1]
100
+ set_pcts[id] = cnt.to_f / (cell_refs[loc] || obfset.length).to_f
85
101
  end
86
102
  locale = obfset[0]['locale']
87
103
  known_buttons = {}
@@ -102,20 +118,56 @@ module AACMetrics::Metrics
102
118
  # whose semantic_id or clone_id is repeated in the board set
103
119
  # -0.0025 (* pct of matching boards) for semantic_id
104
120
  # -0.005 (* pct of matching boards) for clone_id
121
+ reuse_discount = 0.0
105
122
  board[:board]['grid']['rows'].times do |row_idx|
106
123
  board[:board]['grid']['columns'].times do |col_idx|
107
124
  button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
108
125
  button = board[:board]['buttons'].detect{|b| b['id'] == button_id }
109
- if button && button['clone_id'] && pcts[button['clone_id']]
110
- board_effort -= 0.005 * pcts[button['clone_id']]
111
- elsif button && button['semantic_id'] && pcts[button['semantic_id']]
112
- board_effort -= 0.0025 * pcts[button['semantic_id']]
126
+ if button && button['clone_id'] && set_pcts[button['clone_id']]
127
+ reuse_discount += REUSED_CLONE_FROM_OTHER_DISCOUNT * set_pcts[button['clone_id']]
128
+ elsif button && button['semantic_id'] && set_pcts[button['semantic_id']]
129
+ reuse_discount += REUSED_SEMANTIC_FROM_OTHER_DISCOUNT * set_pcts[button['semantic_id']]
113
130
  end
114
131
  end
115
132
  end
116
-
133
+ board_effort -= reuse_discount
117
134
  prior_buttons = 0
118
135
 
136
+ # Calculate the percent of links to this board
137
+ # that had or were linked by clone_ids or semantic_ids
138
+ board_pcts = {}
139
+ obfset.each do |brd|
140
+ brd['buttons'].each do |link_btn|
141
+ # For every board that links to this board
142
+ if link_btn['load_board'] && link_btn['load_board']['id'] == board[:board]['id']
143
+ board_pcts['all'] ||= 0
144
+ board_pcts['all'] += 1
145
+ # Count how many of those links have a clone_id or semantic_id
146
+ if link_btn['clone_id']
147
+ board_pcts[link_btn['clone_id']] ||= 0
148
+ board_pcts[link_btn['clone_id']] += 1
149
+ end
150
+ if link_btn['semantic_id']
151
+ board_pcts[link_btn['semantic_id']] ||= 0
152
+ board_pcts[link_btn['semantic_id']] += 1
153
+ end
154
+ # Also count all the clone_ids and semantic_ids
155
+ # anywhere on the boards that link to this one
156
+ (brd['clone_ids'] || []).uniq.each do |cid|
157
+ board_pcts["upstream-#{cid}"] ||= 0
158
+ board_pcts["upstream-#{cid}"] += 1
159
+ end
160
+ (brd['semantic_ids'] || []).uniq.each do |sid|
161
+ board_pcts["upstream-#{sid}"] ||= 0
162
+ board_pcts["upstream-#{sid}"] += 1
163
+ end
164
+ end
165
+ end
166
+ end
167
+ board_pcts.each do |id, cnt|
168
+ board_pcts[id] = board_pcts[id].to_f / board_pcts['all'].to_f
169
+ end
170
+
119
171
  board[:board]['grid']['rows'].times do |row_idx|
120
172
  board[:board]['grid']['columns'].times do |col_idx|
121
173
  button_id = (board[:board]['grid']['order'][row_idx] || [])[col_idx]
@@ -125,13 +177,34 @@ module AACMetrics::Metrics
125
177
  x = (btn_width / 2) + (btn_width * col_idx)
126
178
  y = (btn_height / 2) + (btn_height * row_idx)
127
179
  # prior_buttons = (row_idx * board[:board]['grid']['columns']) + col_idx
180
+ # calculate the percentage of links that point to this button
181
+ # and match on semantic_id or clone_id
128
182
  effort = 0
129
- # TODO: additional discount on board search effort
130
- # if this button's semantic_id or clone_id
131
- # was also present on the prior board
132
- # board_effort * 0.5 for semantic_id
133
- # board_effort * 0.33 for clone_id
134
- effort += board_effort
183
+ # Additional discount on board search effort,
184
+ # remember that semantic_id and clone_id are
185
+ # keyed to the same grid location, so matches only
186
+ # apply to that specific location
187
+ # - if this button's semantic_id or clone_id
188
+ # was also present anywhere on the prior board
189
+ # board_effort * 0.5 for semantic_id
190
+ # board_effort * 0.33 for clone_id
191
+ # - if this button's semantic_id or clone_id
192
+ # is directly used to navigate to this board
193
+ # board_effort * 0.1 for semantic_id
194
+ # board_effort * 0.1 for clone_id
195
+ button_effort = board_effort
196
+ if board_pcts[button['semantic_id']]
197
+ # TODO: Pull out these magic numbers
198
+ button_effort = [button_effort, button_effort * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['semantic_id']]].min
199
+ elsif board_pcts["upstream-#{button['semantic_id']}"]
200
+ button_effort = [button_effort, button_effort * RECOGNIZABLE_SEMANTIC_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['semantic_id']}"]].min
201
+ end
202
+ if board_pcts[button['clone_id']]
203
+ button_effort = [button_effort, button_effort * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['clone_id']]].min
204
+ elsif board_pcts["upstream-#{button['clone_id']}"]
205
+ button_effort = [button_effort, button_effort * RECOGNIZABLE_CLONE_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['clone_id']}"]].min
206
+ end
207
+ effort += button_effort
135
208
  # add effort for percent distance from entry point
136
209
  distance = distance_effort(x, y, board[:entry_x], board[:entry_y])
137
210
  # TODO: decrease effective distance if the semantic_id or clone_id:
@@ -141,6 +214,24 @@ module AACMetrics::Metrics
141
214
  # - was also present on the prior board (total)
142
215
  # distance * 0.5 for semantic_id
143
216
  # distance * 0.33 for clone_id
217
+ # - is directly used to navigate to this board
218
+ # distance * 0.1 * (pct of links that match) for semantic_id
219
+ # distance * 0.1 * (pct of links that match) for clone_id
220
+ if board_pcts[button['semantic_id']]
221
+ distance = [distance, distance * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['semantic_id']]].min
222
+ elsif board_pcts["upstream-#{button['semantic_id']}"]
223
+ distance = [distance, distance * RECOGNIZABLE_SEMANTIC_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['semantic_id']}"]].min
224
+ elsif set_pcts[button['semantic_id']]
225
+ distance = [distance, distance * RECOGNIZABLE_SEMANTIC_FROM_OTHER_DISCOUNT / set_pcts[button['semantic_id']]].min
226
+ end
227
+ if board_pcts[button['clone_id']]
228
+ distance = [distance, distance * SAME_LOCATION_AS_PRIOR_DISCOUNT / board_pcts[button['clone_id']]].min
229
+ elsif board_pcts["upstream-#{button['clone_id']}"]
230
+ distance = [distance, distance * RECOGNIZABLE_CLONE_FROM_PRIOR_DISCOUNT / board_pcts["upstream-#{button['clone_id']}"]].min
231
+ elsif set_pcts[button['clone_id']]
232
+ distance = [distance, distance * RECOGNIZABLE_CLONE_FROM_OTHER_DISCOUNT / set_pcts[button['clone_id']]].min
233
+ end
234
+
144
235
  effort += distance
145
236
  if distance > DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN || (board[:entry_x] == 1.0 && board[:entry_y] == 1.0)
146
237
  # add small effort for every prior (visible) button when visually scanning
@@ -170,31 +261,39 @@ module AACMetrics::Metrics
170
261
  end
171
262
  if try_visit
172
263
  next_board = obfset.detect{|brd| brd['id'] == button['load_board']['id'] }
173
- puts "LIKE[] #{effort}" if button['label'] == 'like'
264
+ change_effort = BOARD_CHANGE_PROCESSING_EFFORT
174
265
  if next_board
175
266
  to_visit.push({
176
267
  board: next_board,
177
268
  level: board[:level] + 1,
178
- prior_effort: effort + BOARD_CHANGE_PROCESSING_EFFORT,
269
+ prior_effort: effort + change_effort,
179
270
  entry_x: x,
180
- entry_y: y
271
+ entry_y: y,
272
+ entry_clone_id: button['clone_id'],
273
+ entry_semantic_id: button['semantic_id']
181
274
  })
182
275
  end
183
276
  end
184
277
  else
185
278
  word = button['label']
186
279
  existing = known_buttons[word]
187
- button['effort'] = effort
188
- if !existing || existing[:effort] < effort #board[:level] < existing[:level]
189
- puts "LIKE #{effort}" if button['label'] == 'like'
280
+ if !existing || effort < existing[:effort] #board[:level] < existing[:level]
281
+ if board_pcts[button['clone_id']]
282
+ effort -= [BOARD_CHANGE_PROCESSING_EFFORT, BOARD_CHANGE_PROCESSING_EFFORT * 0.3 / board_pcts[button['clone_id']]].min
283
+ elsif board_pcts[button['semantic_id']]
284
+ effort -= [BOARD_CHANGE_PROCESSING_EFFORT, BOARD_CHANGE_PROCESSING_EFFORT * 0.5 / board_pcts[button['semantic_id']]].min
285
+ end
286
+
190
287
  known_buttons[word] = {
191
288
  id: "#{button['id']}::#{board[:board]['id']}",
192
289
  label: word,
193
290
  level: board[:level],
194
- effort: effort
291
+ effort: effort,
195
292
  }
196
293
  end
197
294
  end
295
+ button['effort'] = effort
296
+
198
297
  end
199
298
  end
200
299
  end
@@ -212,7 +311,7 @@ module AACMetrics::Metrics
212
311
  locale: locale,
213
312
  total_boards: total_boards,
214
313
  total_buttons: buttons.length,
215
- reference_counts: refs,
314
+ reference_counts: set_refs,
216
315
  grid: {
217
316
  rows: root_rows,
218
317
  columns: root_cols
@@ -234,6 +333,13 @@ module AACMetrics::Metrics
234
333
  DISTANCE_MULTIPLIER = 0.4
235
334
  DISTANCE_THRESHOLD_TO_SKIP_VISUAL_SCAN = 0.1
236
335
  SKIPPED_VISUAL_SCAN_DISTANCE_MULTIPLIER = 0.5
336
+ SAME_LOCATION_AS_PRIOR_DISCOUNT = 0.1
337
+ RECOGNIZABLE_SEMANTIC_FROM_PRIOR_DISCOUNT = 0.5
338
+ RECOGNIZABLE_SEMANTIC_FROM_OTHER_DISCOUNT = 0.6
339
+ RECOGNIZABLE_CLONE_FROM_PRIOR_DISCOUNT = 0.33
340
+ RECOGNIZABLE_CLONE_FROM_OTHER_DISCOUNT = 0.4
341
+ REUSED_SEMANTIC_FROM_OTHER_DISCOUNT = 0.0025
342
+ REUSED_CLONE_FROM_OTHER_DISCOUNT = 0.005
237
343
 
238
344
  def self.button_size_effort(rows, cols)
239
345
  BUTTON_SIZE_MULTIPLIER * (rows + cols) / 2
@@ -310,6 +416,7 @@ module AACMetrics::Metrics
310
416
  synonyms = AACMetrics::Loader.synonyms(target[:locale])
311
417
  sentences = AACMetrics::Loader.sentences(target[:locale])
312
418
  fringe = AACMetrics::Loader.fringe_words(target[:locale])
419
+ common_fringe = AACMetrics::Loader.common_fringe_words(target[:locale])
313
420
  common_words_obj['efforts'].each{|w, e| sortable_efforts[w] ||= e }
314
421
  common_words = common_words_obj['words']
315
422
 
@@ -503,8 +610,31 @@ module AACMetrics::Metrics
503
610
  target_effort_tally += res[:fringe_words].map{|s| s[:effort] }.sum.to_f / res[:fringe_words].length.to_f * 2.0
504
611
  comp_effort_tally += res[:fringe_words].map{|s| s[:comp_effort] }.sum.to_f / res[:fringe_words].length.to_f * 2.0
505
612
 
506
- target_effort_tally += 80 # placeholder value for future added calculations
507
- comp_effort_tally += 80
613
+ res[:common_fringe_words] = []
614
+ common_fringe.each do |word|
615
+ target_effort_score = 0.0
616
+ comp_effort_score = 0.0
617
+ synonym_words = [word] + (synonyms[word] || [])
618
+ effort = target_efforts[word] || target_efforts[word.downcase]
619
+ if !effort
620
+ synonym_words.each{|w| effort ||= target_efforts[w] }
621
+ end
622
+ effort ||= spelling_effort(word)
623
+ target_effort_score += effort
624
+
625
+ effort = comp_efforts[word] || comp_efforts[word.downcase]
626
+ if !effort
627
+ synonym_words.each{|w| effort ||= comp_efforts[w] }
628
+ end
629
+ effort ||= spelling_effort(word)
630
+ comp_effort_score += effort
631
+ res[:common_fringe_words] << {word: word, effort: target_effort_score, comp_effort: comp_effort_score}
632
+ end
633
+ target_effort_tally += res[:common_fringe_words].map{|s| s[:effort] }.sum.to_f / res[:common_fringe_words].length.to_f * 1.0
634
+ comp_effort_tally += res[:common_fringe_words].map{|s| s[:comp_effort] }.sum.to_f / res[:common_fringe_words].length.to_f * 1.0
635
+
636
+ target_effort_tally += 70 # placeholder value for future added calculations
637
+ comp_effort_tally += 70
508
638
 
509
639
 
510
640