8
8
//
9
9
// Output values are returned in the product object
10
10
//
11
- // - match_status: yes, no, unknown
12
- // - match_score: number (maximum depends on the preferences)
11
+ // - match_status:
12
+ // very_good_match
13
+ // good_match
14
+ // poor_match
15
+ // unknown_match
16
+ // may_not_match
17
+ // does_not_match
18
+ //
19
+ // - match_score: number from 0 to 100
20
+ //
13
21
// - match_attributes: array of arrays of attributes corresponding to the product and
14
22
// each set of preferences: mandatory, very_important, important
15
23
16
24
function match_product_to_preferences ( product , product_preferences ) {
17
25
18
26
var score = 0 ;
19
- var status = "yes" ;
20
27
var debug = "" ;
21
28
22
29
product . match_attributes = {
@@ -25,72 +32,132 @@ function match_product_to_preferences (product, product_preferences) {
25
32
"important" : [ ]
26
33
} ;
27
34
35
+ // Note: mandatory preferences is set to 0:
36
+ // The attribute is only used to check if a product is compatible or not
37
+ // It does not affect the very good / good / poor match status
38
+ // The score will be 0 if the product is not compatible
39
+ var preferences_factors = {
40
+ "mandatory" : 0 ,
41
+ "very_important" : 2 ,
42
+ "important" : 1 ,
43
+ "not_important" : 0
44
+ } ;
45
+
46
+ var sum_of_factors = 0 ;
47
+ var sum_of_factors_for_unknown_attributes = 0 ;
48
+
28
49
if ( product . attribute_groups ) {
50
+
51
+ product . attributes_for_status = { } ;
29
52
30
53
// Iterate over attribute groups
31
54
$ . each ( product . attribute_groups , function ( key , attribute_group ) {
32
55
33
56
// Iterate over attributes
34
57
35
58
$ . each ( attribute_group . attributes , function ( key , attribute ) {
59
+
60
+ var attribute_preference = product_preferences [ attribute . id ] ;
61
+ var match_status_for_attribute = "match" ;
36
62
37
- if ( ( ! product_preferences [ attribute . id ] ) || ( product_preferences [ attribute . id ] == "not_important" ) ) {
63
+ if ( ( ! attribute_preference ) || ( attribute_preference = == "not_important" ) ) {
38
64
// Ignore attribute
39
65
debug += attribute . id + " not_important" + "\n" ;
40
66
}
41
67
else {
68
+
69
+ var attribute_factor = preferences_factors [ attribute_preference ] ;
70
+ sum_of_factors += attribute_factor ;
42
71
43
- if ( attribute . status == "unknown" ) {
44
-
45
- // If the attribute is important or more, then mark the product unknown
46
- // if the attribute is unknown (unless the product is already not matching)
47
-
48
- if ( status == "yes" ) {
49
- status = "unknown" ;
72
+ if ( attribute . status === "unknown" ) {
73
+
74
+ sum_of_factors_for_unknown_attributes += attribute_factor ;
75
+
76
+ // If the attribute is mandatory and the attribute status is unknown
77
+ // then mark the product status unknown
78
+
79
+ if ( attribute_preference === "mandatory" ) {
80
+ match_status_for_attribute = "unknown_match" ;
50
81
}
51
82
}
52
83
else {
53
84
54
- debug += attribute . id + " " + product_preferences [ attribute . id ] + " - match: " + attribute . match + "\n" ;
55
-
56
- if ( product_preferences [ attribute . id ] == "important" ) {
57
-
58
- score += attribute . match ;
59
- }
60
- else if ( product_preferences [ attribute . id ] == "very_important" ) {
61
-
62
- score += attribute . match * 2 ;
63
- }
64
- else if ( product_preferences [ attribute . id ] == "mandatory" ) {
85
+ debug += attribute . id + " " + attribute_preference + " - match: " + attribute . match + "\n" ;
65
86
66
- score += attribute . match * 4 ;
67
-
68
- if ( attribute . match <= 20 ) {
69
- status = "no" ;
87
+ score += attribute . match * attribute_factor ;
88
+
89
+ if ( attribute_preference === "mandatory" ) {
90
+ if ( attribute . match <= 10 ) {
91
+ // Mandatory attribute with a very bad score (e.g. contains an allergen) -> status: does not match
92
+ match_status_for_attribute = "does_not_match" ;
93
+ }
94
+ // Mandatory attribute with a bad score (e.g. may contain traces of an allergen) -> status: may not match
95
+ else if ( attribute . match <= 50 ) {
96
+ match_status_for_attribute = "may_not_match" ;
70
97
}
71
98
}
72
99
}
73
-
74
- product . match_attributes [ product_preferences [ attribute . id ] ] . push ( attribute ) ;
100
+
101
+ if ( ! ( match_status_for_attribute in product . attributes_for_status ) ) {
102
+ product . attributes_for_status [ match_status_for_attribute ] = [ ] ;
103
+ }
104
+ product . attributes_for_status [ match_status_for_attribute ] . push ( attribute ) ;
105
+
106
+ product . match_attributes [ attribute_preference ] . push ( attribute ) ;
75
107
}
76
108
} ) ;
77
- } ) ;
109
+ } ) ;
110
+
111
+ // Normalize the score from 0 to 100
112
+ if ( sum_of_factors === 0 ) {
113
+ score /= sum_of_factors ;
114
+ } else {
115
+ score = 0 ;
116
+ }
117
+
118
+ // If one of the attributes does not match, the product does not match
119
+ if ( "does_not_match" in product . attributes_for_status ) {
120
+ // Set score to 0 for products that do not match
121
+ score = "0" ;
122
+ product . match_status = "does_not_match" ;
123
+ }
124
+ else if ( "may_not_match" in product . attributes_for_status ) {
125
+ product . match_status = "may_not_match" ;
126
+ }
127
+ // If too many attributes are unknown, set an unknown match
128
+ else if ( sum_of_factors_for_unknown_attributes >= sum_of_factors / 2 ) {
129
+ product . match_status = "unknown_match" ;
130
+ }
131
+ // If the product matches, check how well it matches user preferences
132
+ else if ( score >= 75 ) {
133
+ product . match_status = "very_good_match" ;
134
+ }
135
+ else if ( score >= 50 ) {
136
+ product . match_status = "good_match" ;
137
+ }
138
+ else {
139
+ product . match_status = "poor_match" ;
140
+ }
78
141
}
79
142
else {
80
- // the product does not have the attribute_group field
81
- status = "unknown" ;
143
+ // the product does not have the attribute_groups field
144
+ product . match_status = "unknown_match" ;
145
+ debug = "no attribute_groups" ;
82
146
}
83
147
84
- product . match_status = status ;
85
- product . match_score = score ;
148
+ product . match_score = score ;
86
149
product . match_debug = debug ;
87
150
}
88
151
152
+
89
153
// rank_products (products, product_preferences)
90
154
91
155
// keep the initial order of each result
92
156
var initial_order = 0 ;
93
157
158
+ // option to enable tabs in results to filter on product match status
159
+ var show_tabs_to_filter_by_match_status = 0 ;
160
+
94
161
function rank_products ( products , product_preferences , use_user_product_preferences_for_ranking ) {
95
162
96
163
// Score all products
@@ -109,10 +176,12 @@ function rank_products(products, product_preferences, use_user_product_preferenc
109
176
110
177
if ( use_user_product_preferences_for_ranking ) {
111
178
112
- // Rank all products, and return them in 3 arrays: "yes", "no", "unknown"
179
+ // Rank all products
113
180
114
181
products . sort ( function ( a , b ) {
115
- return ( b . match_score - a . match_score ) || ( a . initial_order - b . initial_order ) ;
182
+ return ( b . match_score - a . match_score ) // Highest score first
183
+ || ( ( b . match_status === "does_not_match" ? 0 : 1 ) - ( a . match_status === "does_not_match" ? 0 : 1 ) ) // Matching products second
184
+ || ( a . initial_order - b . initial_order ) ; // Initial order third
116
185
} ) ;
117
186
}
118
187
else {
@@ -123,14 +192,14 @@ function rank_products(products, product_preferences, use_user_product_preferenc
123
192
124
193
var product_groups = {
125
194
"all" : [ ] ,
126
- "yes" : [ ] ,
127
- "unknown" : [ ] ,
128
- "no" : [ ] ,
129
195
} ;
130
196
131
197
$ . each ( products , function ( key , product ) {
132
198
133
- if ( use_user_product_preferences_for_ranking ) {
199
+ if ( show_tabs_to_filter_by_match_status && use_user_product_preferences_for_ranking ) {
200
+ if ( ! ( product . match_status in product_groups ) ) {
201
+ product_groups [ product . match_status ] = [ ] ;
202
+ }
134
203
product_groups [ product . match_status ] . push ( product ) ;
135
204
}
136
205
product_groups . all . push ( product ) ;
@@ -161,14 +230,17 @@ function display_products(target, product_groups, user_prefs ) {
161
230
162
231
$ . each ( product_group , function ( key , product ) {
163
232
164
- var product_html = "" ;
233
+ var product_html = `<li><a href=" ${ product . url } ">` ;
165
234
166
- // Show the green / grey / colors for matching products only if we are using the user preferences
167
- let css_classes = 'list_product_a' ;
168
235
if ( user_prefs . use_ranking ) {
169
- css_classes += ' list_product_a_match_' + product . match_status ;
236
+ product_html += `<div class="list_product_banner list_product_banner_${ product . match_status } ">`
237
+ + lang ( ) [ "products_match_" + product . match_status ] + ' ' + Math . round ( product . match_score ) + '%</div>'
238
+ + '<div class="list_product_content">' ;
170
239
}
171
- product_html += `<li><a href="${ product . url } " class="${ css_classes } ">` ;
240
+ else {
241
+ product_html += '<div class="list_product_unranked">' ;
242
+ }
243
+
172
244
product_html += '<div class="list_product_img_div">' ;
173
245
174
246
const img_src =
@@ -206,7 +278,6 @@ function display_products(target, product_groups, user_prefs ) {
206
278
if ( user_prefs . display . display_barcode ) {
207
279
product_html += `<span class="list_display_barcode">${ product . code } </span>` ;
208
280
}
209
- product_html += "</a>" ;
210
281
if ( user_prefs . display . edit_link ) {
211
282
const edit_url = product_edit_url ( product ) ;
212
283
const edit_title = lang ( ) . edit_product_page ;
@@ -219,34 +290,41 @@ function display_products(target, product_groups, user_prefs ) {
219
290
</a>
220
291
` ;
221
292
}
222
- product_html += "</li>" ;
293
+ product_html += "</div></a></ li>" ;
223
294
224
295
products_html . push ( product_html ) ;
225
296
} ) ;
297
+
226
298
299
+
227
300
var active = "" ;
228
301
var text_or_icon = "" ;
229
- if ( product_group_id == "all" ) {
302
+ if ( product_group_id === "all" ) {
230
303
active = " active" ;
231
- if ( product_group . length == 1 ) {
232
- text_or_icon = lang ( ) [ "1_product" ] ;
304
+ }
305
+
306
+ if ( show_tabs_to_filter_by_match_status ) {
307
+ if ( product_group_id === "all" ) {
308
+ if ( product_group . length === 1 ) {
309
+ text_or_icon = lang ( ) [ "1_product" ] ;
310
+ }
311
+ else {
312
+ text_or_icon = product_group . length + ' ' + lang ( ) . products ;
313
+ }
233
314
}
234
315
else {
235
- text_or_icon = product_group . length + ' ' + lang ( ) . products ;
316
+ text_or_icon = '<img src="/images/attributes/match-' + product_group_id + '.svg" class="icon">'
317
+ + ' <span style="color:grey">' + product_group . length + "</span>" ;
318
+ }
319
+
320
+ if ( user_prefs . use_ranking ) {
321
+ $ ( "#products_tabs_titles" ) . append (
322
+ '<li class="tabs tab-title tab_products-title' + active + '">'
323
+ + '<a id="tab_products_' + product_group_id + '" href="#products_' + product_group_id + '" title="' + lang ( ) [ "products_match_" + product_group_id ] + '">'
324
+ + text_or_icon
325
+ + "</a></li>"
326
+ ) ;
236
327
}
237
- }
238
- else {
239
- text_or_icon = '<img src="/images/attributes/match-' + product_group_id + '.svg" class="icon">'
240
- + ' <span style="color:grey">' + product_group . length + "</span>" ;
241
- }
242
-
243
- if ( user_prefs . use_ranking ) {
244
- $ ( "#products_tabs_titles" ) . append (
245
- '<li class="tabs tab-title tab_products-title' + active + '">'
246
- + '<a id="tab_products_' + product_group_id + '" href="#products_' + product_group_id + '" title="' + lang ( ) [ "products_match_" + product_group_id ] + '">'
247
- + text_or_icon
248
- + "</a></li>"
249
- ) ;
250
328
}
251
329
252
330
$ ( "#products_tabs_content" ) . append (
@@ -282,7 +360,7 @@ function display_product_summary(target, product) {
282
360
// vary the color from green to red
283
361
var grade = "unknown" ;
284
362
285
- if ( attribute . status == "known" ) {
363
+ if ( attribute . status === "known" ) {
286
364
grade = attribute . grade ;
287
365
}
288
366
0 commit comments