source: src/main/webapp/utilstable.xhtml@ 18

Last change on this file since 18 was 18, checked in by bart, 4 years ago

Minor fixes

File size: 13.4 KB
Line 
1<?xml version="1.0" encoding="UTF-8"?>
2<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3<head>
4<title>Show tournament results table</title>
5<link rel="stylesheet" type="text/css" href="style.css" />
6</head>
7<style>
8canvas {
9 -moz-user-select: none;
10 -webkit-user-select: none;
11 -ms-user-select: none;
12}
13</style>
14<body>
15 <div class="noscript">
16 <h2 style="color: #ff0000">Seems your browser doesn't support
17 Javascript! Websockets rely on Javascript being enabled. Please
18 enable Javascript and reload this page!</h2>
19 </div>
20 <h1 id="header">Tournament results</h1>
21
22 Progress:
23 <div id="progress">Waiting for log file name</div>
24 <br />
25
26 <table id="outcomes">
27 <thead>
28 <tr>
29 <th align="center">run nr</th>
30 <th align="center">accepted bid(s)</th>
31 <th align="center">party utility - penalty</th>
32 </tr>
33 </thead>
34 <tbody id="outcomeslist">
35 <!-- one row for each session run added by script -->
36 </tbody>
37
38 </table>
39</body>
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56<!-- Script to get/update the table using the query given in the URL. -->
57<script type="application/javascript">
58
59
60
61
62 <![CDATA[
63 "use strict";
64
65
66
67 var ws = null;
68 var SHAOP=false;
69 /* dict with key=profile URL and value = the downloaded (json) profile */
70 var profiles = {};
71
72 <!-- Most functions are reactive programming written out-->
73
74 /**
75 Called when the page opens.
76 */
77 function init() {
78
79 var id=document.location.search;
80 id = location.search.split('?')[1]
81 if (id==undefined) return;
82
83 // load the log file with given id
84 document.getElementById('header').innerHTML="Tournament results table of "+id;
85
86 var url = "log/"+id+".json";
87 setStatus("Downloading file "+url);
88 var request=new XMLHttpRequest();
89 request.responseType = 'text';
90 request.open('Get', url)
91 request.onload=function() {
92 if (request.status!=200) {
93 setStatus("Failed to fetch "+url+":" +request.status+" "+request.statusText)
94 return;
95 }
96 processLogFile(JSON.parse(request.response));
97 }
98 request.send();
99 }
100
101
102 /**
103 Figure out the protocol contents and parse accordingly.
104 */
105 function processLogFile(json) {
106 if (json['AllPermutationsState']!=undefined) {
107 processAPP(json['AllPermutationsState']);
108 } else {
109 setStatus("Unknown log file contents "+Object.keys(json));
110 }
111 }
112
113 /**
114 Handle AllPermutationsState protocol result.
115 */
116 function processAPP(json) {
117 setStatus("processing the log file");
118
119 if (json['toursettings']['AllPermutationsSettings']['sessionsettings']['SHAOPSettings']!=undefined) {
120 SHAOP=true;
121 }
122
123 // collect all profiles from all profileslists
124 var profiles= [];
125 for (const profilelist of json['toursettings']['AllPermutationsSettings']['profileslists']) {
126 profiles = profiles.concat(profilelist['ProfileList']);
127 }
128 getAllProfiles(json, profiles);
129 }
130
131 /**
132 Step 1, get all profiles. Repeats recursiuvely till profileurls is empty.
133 Note just calling the function again without recurse would be better.
134 Problem is that we call this after we got the profile to ensure we
135 have all profiles at the end.
136 @param profileurls the list of profile urls that need to be fetched.
137 @param profiles a dictionary of profiles . Key is profileurl, value the profile. Should
138 be initially {}
139 returns only after all profiles have been fetched.
140 */
141 function getAllProfiles(json, profileurls) {
142 for (var n in profileurls) {
143 getProfile(profileurls[n], function() {fillRows(json)} );
144 }
145 }
146
147 /**
148 This function returns immediately and will add the profile to the profiles dictionary some time later.
149 @param profurl the profile URL may have ?partial filter.
150 @param callwhendone function is called (without arguments) when profiles contains no pending anymore
151 */
152 function getProfile(profurl, callwhendone) {
153 profiles[profurl]='pending'; // store ORIGINAL URL
154 var profileurl = profurl.split('?',1)[0]; // hacky, remove complete query.
155
156 setStatus("fetching profile "+profileurl);
157 var ws;
158 if ('WebSocket' in window) {
159 ws = new WebSocket(profileurl);
160 } else if ('MozWebSocket' in window) {
161 ws = new MozWebSocket(profileurl);
162 } else {
163 setStatus('Fatal: WebSocket is not supported by this browser. Please use a newer browser');
164 return;
165 }
166 ws.onmessage = function (event) {
167 ws.close();
168 var profile = JSON.parse(event.data);
169 if (profile['LinearAdditiveUtilitySpace']==undefined) {
170 setStatus('Fatal: profile '+profileurl+" does not contain a LinearAdditiveUtilitySpace.");
171 return;
172 }
173
174 profiles[profurl]=profile['LinearAdditiveUtilitySpace'];
175
176 checkAllProfiles(callwhendone);
177 };
178 ws.onerror=function(event) {
179 ws.close();
180 setStatus('Error fetching profile '+uri+':'+event);
181 }
182 }
183
184 /**
185 Check if there are still pending profiles. If not, callwhendone().
186 */
187 function checkAllProfiles(callwhendone) {
188 for (var key in profiles) {
189 if (profiles[key] == 'pending') return;
190 }
191 callwhendone();
192 }
193
194 /**
195 Step 2, fill the table with util values
196 @param profiles a dictionary of profiles . Key is profileurl, value the profile. Should
197 be initially {}
198 */
199 function fillRows(json)
200 {
201 setStatus("Computing utilities");
202 var table = document.getElementById("outcomeslist");
203 table.innerHTML="";
204 var results = json['results'];
205
206 for (var nr in results) {
207 var result = results[nr];
208 var row = table.insertRow(-1); //-1 = end
209 fillRow(row, nr, results[nr]);
210 }
211 setStatus("done");
212 }
213
214 /**
215 @return true if object1, object2 are 'deep equal'. That means,
216 if they are dicts then we check that the keys and their values are equal,
217 recursively.
218 */
219 function deepEqual(object1, object2) {
220 const keys1 = Object.keys(object1);
221 const keys2 = Object.keys(object2);
222
223 if (keys1.length !== keys2.length) {
224 return false;
225 }
226
227 for (const key of keys1) {
228 const val1 = object1[key];
229 const val2 = object2[key];
230 const areObjects = isObject(val1) && isObject(val2);
231 if (
232 areObjects && !deepEqual(val1, val2) || !areObjects && val1 !== val2
233 ) {
234 return false;
235 }
236 }
237
238 return true;
239 }
240
241 function isObject(object) {
242 return object != null && typeof object === 'object';
243 }
244
245 /**
246 @param list the list to add value to
247 @param value the value to add
248 @return list with the value added, but only if not already in list.
249 Uses deepEqual to check equality of list members
250 */
251 function addSet(list, value) {
252 for (const v of list) {
253 if (deepEqual(v, value))
254 return list;
255 }
256 return list.concat(value);
257 }
258
259 /**
260 @param row the table row object in the document
261 @param nr the row number
262 @param result a single json result from the APP results section.
263 Contains participants, agreements and error
264 */
265 function fillRow(row, nr, result) {
266 row.insertCell(0).innerHTML = nr;
267
268 if (result['error']!=null) {
269 var errstr="session failed:";
270 var err=result['error'];
271 if ("geniusweb.protocol.ProtocolException" in err) {
272 errstr=errstr+err["geniusweb.protocol.ProtocolException"]['cause']['message'];
273 }
274 row.insertCell(-1).innerHTML=errstr;
275 return;
276 }
277
278 // collect unique bids in agreements.
279 var agreements = result['agreements'];
280 var bids = [];
281 for (const pid in agreements) {
282 bids=addSet(bids, agreements[pid]['issuevalues']);
283 }
284
285
286 // a dict, keys are party ids.
287 // FIXME this does not work ok with MOPAC.
288 row.insertCell(-1).innerHTML = JSON.stringify(bids);
289 var agreedbid={};
290 if (bids.length>0) agreedbid = bids[0];
291
292 // fill in the columns. If SHAOP, only the even parties
293 var stepsize = SHAOP? 2:1;
294 for (var n=0; n<result['participants'].length; n=n+stepsize) {
295 var party = result['participants'][n]
296 var profile =party['profile'];
297 var penalty=result['penalties'][n];
298 // make short name for readability
299 var partyname = party['party']['partyref'];
300 partyname = partyname.split('/');
301 partyname = partyname[partyname.length-1];
302 addUtilityCell(row.insertCell(-1), agreedbid, partyname, profile,penalty);
303 }
304 }
305
306 /**
307 @param cell the table cell to put the result in
308 @param agreedbid the bid that was agreed on
309 @param partyname the short name of the party
310 @param profileurl the profile url to use for the evaluation of the bid
311 @param bid the accepted bid, not null.
312 @param penalty costs made by the party, to be subtracted from total util.
313 typically these are elicitation costs.
314 */
315 function addUtilityCell(cell, agreedbid, partyname, profileurl, penalty) {
316 var util = utility(profiles[profileurl],agreedbid);
317 var rUtil = Math.round(util*1000000)/1000000;
318 var rPenalty = Math.round( (penalty )*1000000)/1000000 ;
319 cell.innerHTML = rUtil + "-" + rPenalty + " :"+partyname;
320
321 }
322
323
324
325
326
327 /******************* Private support funcs****************/
328
329 /**
330 Set the progress/status text
331 */
332 function setStatus(text) {
333 document.getElementById('progress').innerHTML=text;
334 }
335
336 /*
337 @param sessions a list of IDs (Strings).
338 */
339 function update(sessions) {
340 var table = document.getElementById("sessionsTable");
341 table.innerHTML="";
342 for(var session of sessions) {
343 var row = table.insertRow(-1); //-1 = end
344 row.insertCell(0).innerHTML = session;
345 }
346
347 }
348
349
350
351
352 /**
353 Compute utilityes
354 @param profile the linear additive utility space
355 @param issueValues the bid containing dict with values for the issues
356 @param isResBidAlternative true if the reservation bid is usable as alternative bid
357 . If true, and the issueValues list is empty/null, this returns the utility
358 of the reservation bid instead.
359 @return utility of issueValues. Returns 0 if profile =null or no bid available
360
361 */
362 function utility(profile, issueValues, isResBidAlternative) {
363 if (profile==null) return 0;
364
365 // check if we need the reservationbid.
366 if (issueValues==null || Object.keys(issueValues).length==0) {
367 var resBid = profile['reservationBid'];
368 if (resBid==null) return 0;
369 issueValues = resBid['issuevalues'];
370 }
371
372 if (issueValues==null) return 0;
373
374 var util=0;
375 var weights = profile['issueWeights'];
376
377 for (var issue in issueValues) {
378 util = util + weights[issue] * evaluate(profile['issueUtilities'][issue], issueValues[issue] );
379 }
380 return util;
381 }
382
383 /**
384 Bit hacky, javascript version of evaluator of utility space.
385 Would be much nicer if we could use java here
386 */
387 function evaluate (utilfunc, value) {
388 if (utilfunc == undefined) {
389 return 0;
390 }
391 if (utilfunc['numberutils']!=undefined) {
392 // it's numeric issue. Compute
393 var minval = utilfunc['numberutils']['lowValue'];
394 var minutil = utilfunc['numberutils']['lowUtility'];
395 var maxval = utilfunc['numberutils']['highValue'];
396 var maxutil = utilfunc['numberutils']['highUtility'];
397
398 var alfa = (value-minval) / (maxval - minval) ;
399 alfa = Math.max(Math.min(alfa, 1),0);
400 return alfa * maxutil + (1-alfa) * minutil;
401 }
402 if (utilfunc['discreteutils']!=undefined) {
403 // it's discrete issue. Compute
404 if (value in utilfunc['discreteutils']['valueUtilities'])
405 return utilfunc['discreteutils']['valueUtilities'][value];
406 return 0;
407 }
408 setStatus("Unknown utility function type "+Object.keys(utilfunc));
409
410 }
411
412
413 ]]>
414
415
416
417
418 <![CDATA[
419 "use strict";
420 /*
421 This is called when the window DOM is loaded. window.onload runs BEFORE the window is loaded
422 and therefore is too early to remove the noscript message.
423 */
424 document.addEventListener("DOMContentLoaded", function() {
425 // Remove elements with "noscript" class - <noscript> is not allowed in XHTML
426 var noscripts = document.getElementsByClassName("noscript");
427 for (var i = 0; i < noscripts.length; i++) {
428 noscripts[i].parentNode.removeChild(noscripts[i]);
429 }
430 init();
431
432 }, false);
433 ]]>
434
435
436</script>
437
438
439</html>
Note: See TracBrowser for help on using the repository browser.