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

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

Minor fixes

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