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

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

Update to version 1.41

File size: 11.4 KB
RevLine 
[9]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 */
[12]79 function init() {
[9]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
[12]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);
[9]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.
[13]151 @param profurl the profile URL may have ?partial filter.
[9]152 @param callwhendone function is called (without arguments) when profiles contains no pending anymore
153 */
[13]154 function getProfile(profurl, callwhendone) {
155 profiles[profurl]='pending'; // store ORIGINAL URL
156 var profileurl = profurl.split('?',1)[0]; // hacky, remove complete query.
[9]157
[13]158 setStatus("fetching profile "+profileurl);
[9]159 var ws;
160 if ('WebSocket' in window) {
[13]161 ws = new WebSocket(profileurl);
[9]162 } else if ('MozWebSocket' in window) {
[13]163 ws = new MozWebSocket(profileurl);
[9]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) {
[13]172 setStatus('Fatal: profile '+profileurl+" does not contain a LinearAdditiveUtilitySpace.");
[9]173 return;
174 }
175
[13]176 profiles[profurl]=profile['LinearAdditiveUtilitySpace'];
[9]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 var util = utility(profiles[profileurl],agreedbid);
260 cell.innerHTML = Math.round(util*1000000)/1000000 +" :"+partyname;
261
262 }
263
264
265
266
267
268 /******************* Private support funcs****************/
269
270 /**
271 Set the progress/status text
272 */
273 function setStatus(text) {
274 document.getElementById('progress').innerHTML=text;
275 }
276
277 /*
278 @param sessions a list of IDs (Strings).
279 */
280 function update(sessions) {
281 var table = document.getElementById("sessionsTable");
282 table.innerHTML="";
283 for(var session of sessions) {
284 var row = table.insertRow(-1); //-1 = end
285 row.insertCell(0).innerHTML = session;
286 }
287
288 }
289
290
291
292
293 /**
294 Compute utilityes
[14]295 @param profile the linear additive utility space
[9]296 @param issueValues the bid containing dict with values for the issues
[14]297 @param isResBidAlternative true if the reservation bid is usable as alternative bid
298 . If true, and the issueValues list is empty/null, this returns the utility
299 of the reservation bid instead.
300 @return utility of issueValues. Returns 0 if profile =null or no bid available
301
[9]302 */
[14]303 function utility(profile, issueValues, isResBidAlternative) {
304 if (profile==null) return 0;
305
306 // check if we need the reservationbid.
307 if (issueValues==null || Object.keys(issueValues).length==0) {
308 var resBid = profile['reservationBid'];
309 if (resBid==null) return 0;
310 issueValues = resBid['issuevalues'];
311 }
312
313 if (issueValues==null) return 0;
314
[9]315 var util=0;
316 var weights = profile['issueWeights'];
317
318 for (var issue in issueValues) {
319 util = util + weights[issue] * evaluate(profile['issueUtilities'][issue], issueValues[issue] );
320 }
321 return util;
322 }
323
324 /**
325 Bit hacky, javascript version of evaluator of utility space.
326 Would be much nicer if we could use java here
327 */
328 function evaluate (utilfunc, value) {
329 if (utilfunc == undefined) {
330 return 0;
331 }
332 if (utilfunc['numberutils']!=undefined) {
333 // it's numeric issue. Compute
334 var minval = utilfunc['numberutils']['lowValue'];
335 var minutil = utilfunc['numberutils']['lowUtility'];
336 var maxval = utilfunc['numberutils']['highValue'];
337 var maxutil = utilfunc['numberutils']['highUtility'];
338
339 var alfa = (value-minval) / (maxval - minval) ;
340 alfa = Math.max(Math.min(alfa, 1),0);
341 return alfa * maxutil + (1-alfa) * minutil;
342 }
343 if (utilfunc['discreteutils']!=undefined) {
344 // it's discrete issue. Compute
[14]345 if (value in utilfunc['discreteutils']['valueUtilities'])
346 return utilfunc['discreteutils']['valueUtilities'][value];
347 return 0;
[9]348 }
349 setStatus("Unknown utility function type "+Object.keys(utilfunc));
350
351 }
352
353
354 ]]>
355
356
357
358
359 <![CDATA[
360 "use strict";
361 /*
362 This is called when the window DOM is loaded. window.onload runs BEFORE the window is loaded
363 and therefore is too early to remove the noscript message.
364 */
365 document.addEventListener("DOMContentLoaded", function() {
366 // Remove elements with "noscript" class - <noscript> is not allowed in XHTML
367 var noscripts = document.getElementsByClassName("noscript");
368 for (var i = 0; i < noscripts.length; i++) {
369 noscripts[i].parentNode.removeChild(noscripts[i]);
370 }
[12]371 init();
[9]372
373 }, false);
374 ]]>
375
376
377</script>
378
379
380</html>
Note: See TracBrowser for help on using the repository browser.