aac-metrics 0.1.7 → 0.2.0

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: 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