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