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

Last change on this file since 11 was 9, checked in by bart, 5 years ago

Tournament overview table, few bug fixes

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