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

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

Enhanced tournament runner

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