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 +4 -4
- data/lib/aac-metrics/loader.rb +60 -30
- data/lib/aac-metrics/metrics.rb +176 -46
- data/sets/base_words.en.json +894 -0
- data/sets/common_words.en.json +1426 -1424
- data/sets/fringe.en.json +12 -129
- data/sets/l84f-e9fafa55d4.common.en.analysis +27620 -27480
- data/sets/l84f-e9fafa55d4.common.en.obfset +14784 -4752
- data/sets/pc36-6616542987.obfset +207 -0
- data/sets/qc112-a67653dd81.en.obfset +4892 -2015
- data/sets/qc24-bcbee2ea83.en.obfset +1331 -481
- data/sets/qc40-5ceeb28275.obfset +11385 -0
- data/sets/qc60-61bbf6171e.common.en.obfset +2714 -1224
- data/sets/qc84-d83fa84056.obfset +24996 -0
- data/sets/sfy-c45a81c416.common.en.obfset +863 -776
- data/sets/vf112-253f46504a.obfset +28601 -0
- data/sets/vf24-9d3442b880.obfset +13055 -0
- data/sets/vf40-0355b86ede.obfset +16088 -0
- data/sets/vf60-a37d6d6577.obfset +20557 -0
- data/sets/vf84-02166358c0.obfset +25571 -0
- data/sets/wp20-163743e671.obfset +39129 -0
- data/sets/wp42-f67dc6f4a9.obfset +84948 -0
- data/sets/wp60-ce5120b0d2.obfset +106281 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62c96e82e12b2a0c9eb03ad38b65af63c348731faf58ac3920de4d87c77e2d1c
|
4
|
+
data.tar.gz: 16c049e5e33c1b18cdb374afbf9117299fd47bddc91c15042284f2f41f96dc6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c37bef0e90cbea57f8e0d860e2751c1508028b97b9c76e669c6a9f33d5921dc7cf34ea819249c744c7e30a0e08aa9ebb9e9c4bc19fbacc85e56f1ebd7121a9df
|
7
|
+
data.tar.gz: bf47f7d0bbe809195ad6576269532e0ed4b26cae918ae605b671e6c562fcfdf1e5fba892bd7e0c936a18082b8a9c291c0bfec75a44eb9b9ec3f320e1bec1e52f
|
data/lib/aac-metrics/loader.rb
CHANGED
@@ -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
|
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']}-#{
|
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
|
-
#
|
175
|
-
pre =
|
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']}"]
|
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 >
|
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',
|
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
|
-
@@
|
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]
|
data/lib/aac-metrics/metrics.rb
CHANGED
@@ -1,38 +1,42 @@
|
|
1
1
|
# TODO:
|
2
2
|
# Qualitative evaluation criteria:
|
3
|
-
# - this set
|
4
|
-
# - this set
|
5
|
-
# -
|
6
|
-
# - this set provides clear
|
7
|
-
# - grammatical forms
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
34
|
+
set_refs = {}
|
31
35
|
grid = {}
|
32
36
|
|
33
37
|
if obfset.is_a?(Hash) && obfset['buttons']
|
34
38
|
locale = obfset['locale'] || 'en'
|
35
|
-
|
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
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
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'] &&
|
110
|
-
|
111
|
-
elsif button && button['semantic_id'] &&
|
112
|
-
|
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
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
|
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
|
-
|
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 +
|
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
|
-
|
188
|
-
|
189
|
-
|
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:
|
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
|
-
|
507
|
-
|
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
|
|