source: src/main/webapp/plotlog.xhtml@ 17

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

Version 1.5.

File size: 11.7 KB
RevLine 
[1]1<?xml version="1.0" encoding="UTF-8"?>
2<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3<head>
4<title>Plot log results</title>
5<script src="Chart.min_2.8.0.js"></script>
[17]6<script src="utils.js"></script>
[1]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">Graph of log results</h1>
22
23 Progress:
24 <div id="progress">Waiting for log file name</div>
25 <br />
[17]26 <div>Notice, the plotted utilities do not include possible
27 elicitation costs.</div>
[4]28 <div style="width: 100%;">
[1]29 <canvas id="canvas"></canvas>
30 </div>
[17]31
32 Agreement:
33 <div id="agreement" style="display: inline;">?</div>
[1]34</body>
35
36<!-- Script to get/update the SESSIONS list and put it into the table. -->
37<script type="application/javascript">
38
39
40
41
42
[9]43
[17]44
45
[1]46
47
48
49 <![CDATA[
50 "use strict";
51
52
53
54 var ws = null;
55
56 <!-- Most functions are reactive programming written out-->
57
58 function connect() {
[17]59 var query = new URLSearchParams(location.search);
60 var id=query.get('id');
61 if (id==undefined) {
62 alert("missing ?id=<logfile> in URL");
63 return;
64 }
[1]65
66 // load the log file with given id
67 document.getElementById('header').innerHTML="Graph of log "+id;
68
69 var url = "log/"+id+".json";
70 setStatus("Downloading file "+url);
71 var request=new XMLHttpRequest();
72 request.responseType = 'text';
73 request.open('Get', url)
74 request.onload=function() {
75 if (request.status!=200) {
76 setStatus("Failed to fetch "+url+":" +request.status+" "+request.statusText)
77 return;
78 }
79 processLogFile(JSON.parse(request.response));
80 }
81 request.send();
82 }
83
84
85 /**
86 Figure out the protocol contents and parse accordingly.
87 */
88 function processLogFile(json) {
[9]89 if (json['SAOPState']!=undefined) {
90 processSAOP(json['SAOPState']);
91 } else if (json['SHAOPState']!=undefined) {
92 processSHAOP(json['SHAOPState']);
93 } else {
[1]94 setStatus("Unknown log file contents "+Object.keys(json));
[9]95 }
[1]96 }
97
98 /**
99 Handle SAOP protocol result.
100 */
101 function processSAOP(json) {
102 var partyprofiles={}
103 var profs=json['partyprofiles'];
104 for (var party in profs) {
[17]105 partyprofiles[party]=withoutPartial(profs[party]['profile']);
[1]106 }
107
108 getProfiles(json, partyprofiles);
109 }
110
[17]111
[1]112 /**
[9]113 Handle SHAOP protocol result. Get only SHAOP profiles. Remove all 'partial=XX' from relevant profiles.
114 */
115 function processSHAOP(json) {
116 var partyprofiles={}
117 var teams=json['settings']['SHAOPSettings']['participants'];
118 for (var partyid in json['partyNumbers']) {
119 var nr= json['partyNumbers'][partyid];
120 if ( (nr & 1 != 0)) continue; // skip COB parties
121 var shaop=teams[nr/2]['shaop'];
[17]122 var profile = withoutPartial(shaop['profile']); // hacky, remove complete query.
[9]123 partyprofiles[partyid]=profile;
124 }
125
126
127 getProfiles(json, partyprofiles);
128 }
129
[17]130
131 /**
132 @param uristr a string with an uri that may have ?partial=XX
133 @return the given uristr without the "partia=XXl" value
134 */
135 function withoutPartial(uristr) {
136 var profuri=new URL(uristr);
137 var searchParams = new URLSearchParams(profuri.search);
138 searchParams.delete("partial");
139 profuri.search = searchParams.toString();
140 return profuri.toString();
141
142 }
[9]143
144 /**
[1]145 Get the partyprofiles.
146 @json the json protocol result
147 @partyprofileURIs dict with the websocket addresses for each party'as profile
148 @partyprofiles dict with each party's profile. Initially should be {}
149 */
150 function getProfiles(json, partyprofileURIs, partyprofiles={}) {
151 var keys = Object.keys(partyprofileURIs);
152 if (keys.length==0) {
[14]153 plotResults(computeUtils(json, partyprofiles), isAgreement(json));
[1]154 return;
155 }
156 var party = keys[0];
157 var uri=partyprofileURIs[party];
158 delete partyprofileURIs[party];
159
160 setStatus("fetching profile "+uri);
161 if ('WebSocket' in window) {
162 ws = new WebSocket(uri);
163 } else if ('MozWebSocket' in window) {
164 ws = new MozWebSocket(uri);
165 } else {
166 setStatus('Fatal: WebSocket is not supported by this browser. Please use a newer browser');
167 return;
168 }
169 ws.onmessage = function (event) {
170 var profile = JSON.parse(event.data);
171 if (profile['LinearAdditiveUtilitySpace']==undefined) {
172 setStatus('Fatal: profile '+uri+" does not contain a LinearAdditiveUtilitySpace.");
173 return;
174 }
175 partyprofiles[party]=profile['LinearAdditiveUtilitySpace'];
176 getProfiles(json, partyprofileURIs, partyprofiles);
177 };
178 ws.onerror=function(event) {
179 setStatus('Error fetching profile '+uri+':'+event);
180 }
181 }
182
183 /**
[14]184 @return true if there is agreement
185 */
186 function isAgreement(logfile) {
187 var actions=logfile['actions'];
188 var lastact = actions[actions.length -1];
189 return 'accept' in lastact;
190 }
191 /**
[1]192 @json the log file contents
193 @param partyprofiles a dict with key=party id and value a the (linear additive) profile
194 */
195 function computeUtils(json, partyprofiles) {
196
197 setStatus("Computing utilities.")
198 var utilities={};
199 for (var party in partyprofiles) {
200 utilities[party]=[];
201 }
202 var actions = json['actions'];
203 for (var action in actions) {
204 var offer = actions[action];
205 if (offer['offer']==undefined)
206 continue;
207 var issueutilities = offer['offer']['bid']['issuevalues']
208 for (var party in partyprofiles) {
209 utilities[party].push(utility(partyprofiles[party],issueutilities));
210 }
211 }
212 return utilities;
213
214
215 }
216
217 var chartColors = [
218 'rgb(255, 99, 132)',
219 'rgb(3, 192, 12)',
220 'rgb(54, 162, 235)',
221 'rgb(153, 102, 255)',
222 'rgb(201, 203, 207)',
223 'rgb(255, 159, 64)',
224 'rgb(255, 205, 86)'
225 ];
226
227 function color(i) {
228 return chartColors[i % chartColors.length];
229 }
230
231 /**
232 @param utilities the dict with key=party id of the utilities of all steps for each party )
[14]233 @param isAgreement true iff there is agreement reached
[1]234 */
235
[14]236 function plotResults( utilities, isAgreement) {
[1]237 setStatus("Plotting" )
[14]238
239 document.getElementById('agreement').innerHTML=isAgreement?"Yes":"No";
[1]240
241 var parties=Object.keys(utilities);
242 var thelabels=[];
243 for (var i=1; i<=utilities[parties[0]].length; i++) { thelabels.push(i); }
244
245 var thedatasets=[];
246 var partynr=0;
247 for (var party in utilities) {
248 var dataset={
249 label: party,
250 lineTension:0, // straight lines between the points
251 backgroundColor: color(partynr),
252 borderColor: color(partynr),
253 data: utilities[party],
254 fill: false
255 };
256 thedatasets.push(dataset);
257 partynr++;
258 }
259
260 var config = {
261 type: 'line',
262 data: {
263 labels: thelabels,
264 datasets: thedatasets
265 },
266 options: {
267 responsive: true,
268 title: {
269 display: true,
270 text: 'Per-turn utility plot'
271 },
272 tooltips: {
273 mode: 'index',
274 intersect: false,
275 },
276 hover: {
277 mode: 'nearest',
278 intersect: true
279 },
280 scales: {
281 xAxes: [{
282 display: true,
283 scaleLabel: {
284 display: true,
285 labelString: 'Turn'
286 }
287 }],
288 yAxes: [{
289 display: true,
290 scaleLabel: {
291 display: true,
292 labelString: 'Utility'
293 }
294 }]
295 }
296 }
297 };
298
299 var ctx = document.getElementById('canvas').getContext('2d');
300 window.myLine = new Chart(ctx, config);
301
302 setStatus("Plot ready." )
303
304 }
305
306 /**
307 keys are STRING objects 0,1,2... values are utility values (double)
308 */
309 function makeJxPolyline(pointslist) {
310 var jxpoints = []
311 for (var i in pointslist) {
312 jxpoints.push(new jxPoint(20*Number(i), 100*pointslist[i]));
313 }
314 return jxpoints;
315 }
316
317 /******************* Private support funcs****************/
318
319 /**
320 Set the progress/status text
321 */
322 function setStatus(text) {
323 document.getElementById('progress').innerHTML=text;
324 }
325
326 /*
327 @param sessions a list of IDs (Strings).
328 */
329 function update(sessions) {
330 var table = document.getElementById("sessionsTable");
331 table.innerHTML="";
332 for(var session of sessions) {
333 var row = table.insertRow(-1); //-1 = end
334 row.insertCell(0).innerHTML = session;
335 }
336
337 }
338
339
340
341
342 /**
343 Compute utilityes
344 @param linadditive the linear additive utility space
345 @param issueValues the bid containing dict with values for the issues
346 @return utility of issueValues
347 */
348 function utility(profile, issueValues) {
349 var util=0;
350 var weights = profile['issueWeights'];
351
352 for (var issue in issueValues) {
353 util = util + weights[issue] * evaluate(profile['issueUtilities'][issue], issueValues[issue] );
354 }
355 return util;
356 }
357
358 /**
359 Bit hacky, javascript version of evaluator of utility space.
360 Would be much nicer if we could use java here
361 */
362 function evaluate (utilfunc, value) {
363 if (utilfunc == undefined) {
364 return 0;
365 }
366 if (utilfunc['numberutils']!=undefined) {
367 // it's numeric issue. Compute
368 var minval = utilfunc['numberutils']['lowValue'];
369 var minutil = utilfunc['numberutils']['lowUtility'];
370 var maxval = utilfunc['numberutils']['highValue'];
371 var maxutil = utilfunc['numberutils']['highUtility'];
372
373 var alfa = (value-minval) / (maxval - minval) ;
374 alfa = Math.max(Math.min(alfa, 1),0);
375 return alfa * maxutil + (1-alfa) * minutil;
376 }
377 if (utilfunc['discreteutils']!=undefined) {
378 // it's discrete issue. Compute
379 return utilfunc['discreteutils']['valueUtilities'][value];
380 }
381 setStatus("Unknown utility function type "+Object.keys(utilfunc));
382
383 }
384
385
386 ]]>
387
388
389
390
391
392
[9]393
[17]394
395
[1]396</script>
397
398
399
400<script type="application/javascript">
401
402
403
404
405
406
[9]407
[17]408
409
[1]410 <![CDATA[
411 "use strict";
412 /*
413 This is called when the window DOM is loaded. window.onload runs BEFORE the window is loaded
414 and therefore is too early to remove the noscript message.
415 */
416 document.addEventListener("DOMContentLoaded", function() {
417 // Remove elements with "noscript" class - <noscript> is not allowed in XHTML
418 var noscripts = document.getElementsByClassName("noscript");
419 for (var i = 0; i < noscripts.length; i++) {
420 noscripts[i].parentNode.removeChild(noscripts[i]);
421 }
422 connect();
423
424 }, false);
425 ]]>
426
427
428
429
430
431
432
[9]433
[17]434
435
[1]436</script>
437
438
439</html>
Note: See TracBrowser for help on using the repository browser.