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

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

Added BOA support, some bug fixes

File size: 11.9 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>
[16]31 <th align="center">party utility - penalty</th>
[9]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
[16]54
[9]55
[16]56<!-- Script to get/update the table using the query given in the URL. -->
57<script type="application/javascript">
[9]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 */
[12]77 function init() {
[9]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
[12]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);
[9]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.
[13]149 @param profurl the profile URL may have ?partial filter.
[9]150 @param callwhendone function is called (without arguments) when profiles contains no pending anymore
151 */
[13]152 function getProfile(profurl, callwhendone) {
153 profiles[profurl]='pending'; // store ORIGINAL URL
154 var profileurl = profurl.split('?',1)[0]; // hacky, remove complete query.
[9]155
[13]156 setStatus("fetching profile "+profileurl);
[9]157 var ws;
158 if ('WebSocket' in window) {
[13]159 ws = new WebSocket(profileurl);
[9]160 } else if ('MozWebSocket' in window) {
[13]161 ws = new MozWebSocket(profileurl);
[9]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) {
[13]170 setStatus('Fatal: profile '+profileurl+" does not contain a LinearAdditiveUtilitySpace.");
[9]171 return;
172 }
173
[13]174 profiles[profurl]=profile['LinearAdditiveUtilitySpace'];
[9]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
216
217 /**
218 @param row the table row object in the document
219 @param nr the row number
220 @param result a single json result from the APP results section.
221 Contains participants, agreement and error
222 */
223 function fillRow(row, nr, result) {
224 row.insertCell(0).innerHTML = nr;
225
226 if (result['error']!=null) {
[16]227 var errstr="session failed:";
228 var err=result['error'];
229 if ("geniusweb.protocol.ProtocolException" in err) {
230 errstr=errstr+err["geniusweb.protocol.ProtocolException"]['cause']['message'];
231 }
232 row.insertCell(-1).innerHTML=errstr;
[9]233 return;
234 }
235
236 var agreedbid = result['agreement'];
237 row.insertCell(-1).innerHTML = (agreedbid==null?"none":JSON.stringify(agreedbid['issuevalues']));
238 if (agreedbid==null) agreedbid = {};
239 else agreedbid = agreedbid['issuevalues'];
240
241 // fill in the columns. If SHAOP, only the even parties
242 var stepsize = SHAOP? 2:1;
243 for (var n=0; n<result['participants'].length; n=n+stepsize) {
244 var party = result['participants'][n]
245 var profile =party['profile'];
[16]246 var penalty=result['penalties'][n];
[9]247 // make short name for readability
248 var partyname = party['party']['partyref'];
249 partyname = partyname.split('/');
250 partyname = partyname[partyname.length-1];
[16]251 addUtilityCell(row.insertCell(-1), agreedbid, partyname, profile,penalty);
[9]252 }
253 }
254
255 /**
256 @param cell the table cell to put the result in
257 @param agreedbid the bid that was agreed on
258 @param partyname the short name of the party
259 @param profileurl the profile url to use for the evaluation of the bid
260 @param bid the accepted bid, not null.
[16]261 @param penalty costs made by the party, to be subtracted from total util.
262 typically these are elicitation costs.
[9]263 */
[16]264 function addUtilityCell(cell, agreedbid, partyname, profileurl, penalty) {
[9]265 var util = utility(profiles[profileurl],agreedbid);
[16]266 var rUtil = Math.round(util*1000000)/1000000;
267 var rPenalty = Math.round( (penalty )*1000000)/1000000 ;
268 cell.innerHTML = rUtil + "-" + rPenalty + " :"+partyname;
[9]269
270 }
271
272
273
274
275
276 /******************* Private support funcs****************/
277
278 /**
279 Set the progress/status text
280 */
281 function setStatus(text) {
282 document.getElementById('progress').innerHTML=text;
283 }
284
285 /*
286 @param sessions a list of IDs (Strings).
287 */
288 function update(sessions) {
289 var table = document.getElementById("sessionsTable");
290 table.innerHTML="";
291 for(var session of sessions) {
292 var row = table.insertRow(-1); //-1 = end
293 row.insertCell(0).innerHTML = session;
294 }
295
296 }
297
298
299
300
301 /**
302 Compute utilityes
[14]303 @param profile the linear additive utility space
[9]304 @param issueValues the bid containing dict with values for the issues
[14]305 @param isResBidAlternative true if the reservation bid is usable as alternative bid
306 . If true, and the issueValues list is empty/null, this returns the utility
307 of the reservation bid instead.
308 @return utility of issueValues. Returns 0 if profile =null or no bid available
309
[9]310 */
[14]311 function utility(profile, issueValues, isResBidAlternative) {
312 if (profile==null) return 0;
313
314 // check if we need the reservationbid.
315 if (issueValues==null || Object.keys(issueValues).length==0) {
316 var resBid = profile['reservationBid'];
317 if (resBid==null) return 0;
318 issueValues = resBid['issuevalues'];
319 }
320
321 if (issueValues==null) return 0;
322
[9]323 var util=0;
324 var weights = profile['issueWeights'];
325
326 for (var issue in issueValues) {
327 util = util + weights[issue] * evaluate(profile['issueUtilities'][issue], issueValues[issue] );
328 }
329 return util;
330 }
331
332 /**
333 Bit hacky, javascript version of evaluator of utility space.
334 Would be much nicer if we could use java here
335 */
336 function evaluate (utilfunc, value) {
337 if (utilfunc == undefined) {
338 return 0;
339 }
340 if (utilfunc['numberutils']!=undefined) {
341 // it's numeric issue. Compute
342 var minval = utilfunc['numberutils']['lowValue'];
343 var minutil = utilfunc['numberutils']['lowUtility'];
344 var maxval = utilfunc['numberutils']['highValue'];
345 var maxutil = utilfunc['numberutils']['highUtility'];
346
347 var alfa = (value-minval) / (maxval - minval) ;
348 alfa = Math.max(Math.min(alfa, 1),0);
349 return alfa * maxutil + (1-alfa) * minutil;
350 }
351 if (utilfunc['discreteutils']!=undefined) {
352 // it's discrete issue. Compute
[14]353 if (value in utilfunc['discreteutils']['valueUtilities'])
354 return utilfunc['discreteutils']['valueUtilities'][value];
355 return 0;
[9]356 }
357 setStatus("Unknown utility function type "+Object.keys(utilfunc));
358
359 }
360
361
362 ]]>
363
364
365
366
367 <![CDATA[
368 "use strict";
369 /*
370 This is called when the window DOM is loaded. window.onload runs BEFORE the window is loaded
371 and therefore is too early to remove the noscript message.
372 */
373 document.addEventListener("DOMContentLoaded", function() {
374 // Remove elements with "noscript" class - <noscript> is not allowed in XHTML
375 var noscripts = document.getElementsByClassName("noscript");
376 for (var i = 0; i < noscripts.length; i++) {
377 noscripts[i].parentNode.removeChild(noscripts[i]);
378 }
[12]379 init();
[9]380
381 }, false);
382 ]]>
383
384
385</script>
386
387
388</html>
Note: See TracBrowser for help on using the repository browser.