source: src/main/webapp/newtournament.xhtml@ 27

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

New protocols Learn and APPLearn. Fixed memory leak.

File size: 21.2 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>Profiles and Domains list</title>
5<link rel="stylesheet" type="text/css" href="style.css" />
6</head>
7<body onload="init()">
8 <h1>Tournament</h1>
9
10 On this page you can configure the settings for running a new
11 tournament and start the tournament.
12 <br />
13 <br />
14
15
16 <div id="box" class="box">
17 <h3>Tournament global settings</h3>
18 <select id="tourprotocol" onchange="updateTournamentProt()">
19 <option value="AllPermutations">All Permutations Tournament Protocol
20 (APP)</option>
21 <option value="AllPermutationsLearn">APP with learning</option>
22 </select> Tournament protocol <br />
23 <div id="learntimebox">
24 <input type="number" id="learntime" name="learntime" min="1"
25 max="10000" value="30" /> Learning time (s)
26 </div>
27
28 <br /> <input type="number" id="ntournaments" min="1" max="1000000"
29 value="1" /> Number of times to repeat the entire tournament <br />
30
31 <br /> <input type="number" id="teamspersession" min="2" max="10"
32 value="2" /> Number of teams in each session <br /> <input
33 type="checkbox" id="reuseteams" /> Pick teams with return when
34 creating sessions <br />
35 </div>
36
37 <br />
38 <div id="box" class="box">
39 Session Protocol settings <br /> Session Protocol: <select
40 id="protocolselection" onchange="selectSessionProtocol()">
41 <option value="SAOP">SAOP</option>
42 <option value="MOPAC">MOPAC</option>
43 <option value="MOPAC2">MOPAC2</option>
44 <option value="AMOP">AMOP</option>
45 <option value="SHAOP">SHAOP</option>
46 </select>
47
48 <div id="votingevaluator">
49 <br /> Voting Evaluator: <select id="selectedevaluator">
50 <option value="LargestAgreement">Largest Agreement</option>
51 <option value="LargestAgreementsLoop">Largest Agreements
52 and Repeat</option>
53 </select>
54 </div>
55
56 <div id="votingevaluator2">
57 <br /> Voting Evaluator: <select id="selectedevaluator2">
58 <option value="LargestAgreementWithValue">Largest Agreement
59 with Value</option>
60 </select>
61 </div>
62
63 <br /> Deadline: <input type="number" id="deadlinevalue"
64 name="deadline" min="1" max="10000" value="10" /> <select
65 id="deadlinetype">
66 <option value="ROUNDS">rounds</option>
67 <option value="TIME">seconds</option>
68 </select>
69 </div>
70 <br />
71
72 <div id="box" class="box">
73 <h3>Profiles</h3>
74 Domain/Profile Server: <input type="url" name="url"
75 id="profilesserverurl" value="localhost:8080/profilesserver-1.3.1"
76 pattern=".*:[0-9]+/profilesserver.*" size="30"
77 onchange="connectDomain()"> </input> <br /> Domain: <select
78 id="domainselection" onchange="selectDomain()">
79 <!-- <option>Waiting for profiles server</option> -->
80 </select> <br /> <br />
81 <!-- first selected profile -->
82 Profile: <select id="profileselection1">
83 </select> Filter: <input type="text" id="filter1" value="" maxlength="40" /> <br />
84 <!-- second selected profile -->
85 <div class="onlySHAOP">
86 COB: <select id="profileselection2">
87 </select> Filter: <input type="text" id="filter2" value="" maxlength="40" />
88 <br />
89 </div>
90 <button onclick="addProfiles()">Add</button>
91 <br /> <br />
92 <table id="profiles">
93 <thead>
94 <th align="left">Selected Profiles</th>
95 </thead>
96 <tbody id="profilesList">
97 <tr>
98 </tr>
99
100 </tbody>
101 </table>
102 <br />
103 </div>
104 <br />
105 <div id="box" class="box">
106 <h3>Teams</h3>
107 <br /> Parties Server: <input type="url" name="url"
108 id="partiesserverurl" value="localhost:8080/partiesserver-1.3.1"
109 pattern=".*:[0-9]+/partiesserver.*" size="30"
110 onchange="connectParties()"> </input> <br /> <br />
111 <!-- party 1 selection -->
112 Party: <select id="partyselection">
113 </select> <br /> Parameters: {
114 <textarea id="parameters" rows="2" cols="70"
115 onchange="getParameters('parameters')" value="" />
116 } <br />
117 <!-- party 2 selection -->
118 <div class="onlySHAOP">
119 COB: <select id="partyselection2">
120 </select> <br /> Parameters: {
121 <textarea id="parameters2" rows="2" cols="70"
122 onchange="getParameters('parameters2')" value="" />
123 }
124 </div>
125 <br />
126
127
128 <button onclick="addTeam()">Add</button>
129
130 <br /> <br />
131 <table>
132 <thead>
133 <th align="left">Party</th>
134 <th align="left">Parameters</th>
135 <th align="left"><div class="onlySHAOP">Compare Bids
136 Party</div></th>
137 <th align="left"><div class="onlySHAOP">Compare Bids
138 Parameters</div></th>
139 </thead>
140 <tbody id="teamList">
141 <tr id="FIXME REMOVE">
142 </tr>
143
144 </tbody>
145 </table>
146
147 </div>
148 <br />
149 <form>
150 <input id="startbutton" type="button" value="Start Tournament"
151 onclick="start()" />
152 </form>
153 <div id="started" style="visibility: hidden">
154 Your tournament started. Click <a href="" id="logref">here</a> to view
155 the log file. <br /> <a href="" id="plotref">show results table.</a>.
156 </div>
157
158</body>
159
160<script type="application/javascript">
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 // FIXME quick and dirty code. No clean MVC
196
197
198 <![CDATA[
199 "use strict";
200
201 var domainwebsocket = null;
202 var partieswebsocket=null;
203 // currently known domains (and profiles) as coming from domainwebsocket.
204 // keys are domain names, values are list of profile names
205 var knowndomains={};
206
207 var teamslist=[];
208 var profiles=[]
209
210 /**
211 The tournament protocol was changed.
212 */
213 function updateTournamentProt() {
214 updateParameters();
215 var tourprot = document.getElementById("tourprotocol").value;
216 document.getElementById("learntimebox").style.display=(tourprot=="AllPermutationsLearn" ? 'block': 'none');
217
218 }
219
220 /**
221 Parameters are needed if protocol is APP.
222 */
223 function updateParameters() {
224 var tourprot = document.getElementById("tourprotocol").value;
225 if (tourprot=='AllPermutationsLearn') {
226 var params = {"persistentstate":createUUID() };
227 var str=JSON.stringify(params);
228 // remove outer brackets from the text in the box
229 document.getElementById("parameters").value=str.substring(1, str.length-1);
230 }
231 }
232
233
234 /**
235 called when user changes the protocol.
236 With SHAOP we show COB selectors and COB columns in the tables.
237 */
238 function selectSessionProtocol() {
239 const protocol = document.getElementById("protocolselection").value;
240 setStyleSheet('.onlySHAOP','{ display: '+(protocol=="SHAOP"? '?':'none')+'; }')
241
242 document.getElementById("votingevaluator").style.display=(protocol=="MOPAC" ? 'block': 'none');
243
244 document.getElementById("votingevaluator2").style.display=(protocol=="MOPAC2" ? 'block': 'none');
245
246 }
247
248 /**
249 Change style sheet to new value. Nothing happens if there is
250 no style sheet with given classname.
251 @param classname the name of the style definition, eg '.onlySHAOP'
252 @param style the new style for the class. eg '{ max-width: 100px; }'
253 */
254 function setStyleSheet(classname, style) {
255 for (var cssRulenr=0; cssRulenr< document.styleSheets.length; cssRulenr++) {
256 var cssRule = document.styleSheets[cssRulenr];
257 // there may be multple CSS definitions, check each.
258 var rules = cssRule.cssRules;
259 for (var rulenr=0; rulenr<rules.length; rulenr++) {
260 var rule = rules[rulenr];
261 if ('selectorText' in rule && rule.selectorText==classname) {
262 cssRule.deleteRule(rulenr);
263 cssRule.insertRule(classname+ " "+style, rulenr);
264 }
265 }
266 }
267 }
268
269 /**
270 Refresh known domains using given profilesserver URL.
271 Called when user enters URL for domain server.
272 */
273 function connectDomain() {
274 if (domainwebsocket!=null) {
275 domainwebsocket.close();
276 domainwebsocket=null;
277 }
278 var url=document.getElementById("profilesserverurl").value;
279 var target = "ws://"+url+"/websocket/liststream";
280 if ('WebSocket' in window) {
281 domainwebsocket = new WebSocket(target);
282 } else if ('MozWebSocket' in window) {
283 domainwebsocket = new MozWebSocket(target);
284 } else {
285 alert('WebSocket is not supported by this browser. Please use a newer browser');
286 return;
287 }
288 domainwebsocket.onopen = function () {
289 // whatever.
290 };
291 domainwebsocket.onmessage = function (event) {
292 updateDomainComboBox(JSON.parse(event.data));
293 };
294 domainwebsocket.onclose = function (event) {
295 alert('Info: Server closed connection. Code: ' + event.code +
296 (event.reason == "" ? "" : ", Reason: " + event.reason));
297 domainwebsocket=null;
298 updateDomainComboBox({});
299 };
300 }
301
302 /**
303 Sets a new knowndomains value and Updates the contents of the domain selector combobox.
304 @param the known domains, a map of the form {"jobs":["jobs1","jobs2"]}
305 where the keys are the names of the available domains nd the values a list of the available profiles in that domain.
306
307 */
308 function updateDomainComboBox(newdomains) {
309 knowndomains=newdomains
310 var combobox = document.getElementById("domainselection");
311 combobox.options.length=0;
312 for (var domain in knowndomains) {
313 var option = document.createElement('option');
314 option.text = option.value = domain;
315 combobox.add(option, 0);
316 }
317 selectDomain();
318 }
319 /**
320 Refresh known parties using given partiesserver URL.
321 Called when user enters URL for parties server.
322 */
323 function connectParties() {
324 if (partieswebsocket!=null) {
325 partieswebsocket.close();
326 partieswebsocket=null;
327 }
328 var url=document.getElementById("partiesserverurl").value;
329 var target = "ws://"+url+"/available";
330 if ('WebSocket' in window) {
331 partieswebsocket = new WebSocket(target);
332 } else if ('MozWebSocket' in window) {
333 partieswebsocket = new MozWebSocket(target);
334 } else {
335 alert('WebSocket is not supported by this browser. Please use a newer browser');
336 return;
337 }
338 partieswebsocket.onopen = function () {
339 // whatever.
340 };
341 partieswebsocket.onmessage = function (event) {
342 updateParties(JSON.parse(event.data));
343 };
344 partieswebsocket.onclose = function (event) {
345 alert('Info: Server closed connection. Code: ' + event.code +
346 (event.reason == "" ? "" : ", Reason: " + event.reason));
347 partieswebsocket=null;
348 updateParties({});
349 };
350 }
351
352 /**
353 @param parties a new list of parties available on the parties server
354 */
355 function updateParties(parties) {
356 updatePartyCombo(document.getElementById("partyselection"), parties, 'shaop');
357 updatePartyCombo(document.getElementById("partyselection2"), parties, 'comparebids');
358 }
359
360 /**
361 put the available parties in the party comboboxes.
362 @param combobox a document <select> element
363 @param parties a dict of party information coming from the partiesserver
364 @param select a string that may be a substring of the part names.
365 @return the combobox value that contains 'select' as substring, or undefined
366 */
367 function updatePartyCombo(combobox, parties, select) {
368 var selection=undefined;
369 combobox.options.length=0;
370 for (var party in parties) {
371 var option = document.createElement('option');
372 option.text = option.value = parties[party].uri;
373 if (option.text.includes(select) ) option.selected=true;
374 combobox.add(option, 0);
375 }
376 return selection;
377 }
378
379
380
381 /**
382 @param elementname eg "parameters", the ID of the element in the <input> element
383 @return the JSON parsed object, or {} if empty or parsing fails.
384 */
385 function getParameters(elementname) {
386 var text="{"+document.getElementById(elementname).value+"}";
387 try {
388 return JSON.parse(text);
389 } catch(e) {
390 alert("Parameters can not be parsed. Make sure you write correct JSON here."+e);
391 }
392 return {};
393 }
394
395 /**
396 Called when the selected domain changes. Assumes knowndomains has been set.
397 Updates the available profiles in the profile combobox.
398 @param selection the name of the selected domain.
399 */
400 function selectDomain() {
401 // determined current selection
402 var domaincombobox = document.getElementById("domainselection");
403 if (domaincombobox.options.length==0) return; // fixme clean profiles options?
404 var domain = domaincombobox.options[domaincombobox.selectedIndex].value;
405 updateProfiles(knowndomains[domain])
406 }
407
408 function updateProfiles(profileslist) {
409 updateProfileCombo(document.getElementById("profileselection1"),profileslist);
410 updateProfileCombo(document.getElementById("profileselection2"),profileslist);
411 }
412
413 /**
414 @param combobox the <select> element that should be udpated
415 @param profileslist the list of profile names to be put in the combbo
416 */
417 function updateProfileCombo(profilecombo, profileslist) {
418 profilecombo.options.length=0;
419 for (var profile in profileslist) {
420 var option = document.createElement('option');
421 option.text = option.value = profileslist[profile];
422 profilecombo.add(option, 0);
423 }
424 }
425
426
427 /**
428 Called when user clicks "Add"
429 */
430 function addTeam() {
431 var partycombo = document.getElementById("partyselection");
432 var partycombo2 = document.getElementById("partyselection2");
433 var param = getParameters("parameters");
434 var param2 = getParameters("parameters2");
435
436
437 if (partycombo.options.length==0) {
438 alert("Please set partier server and select a party");
439 return;
440 }
441 var team = [];
442 team.push({"partyref":partycombo.options[partycombo.selectedIndex].value, "parameters":param});
443 team.push({"partyref":partycombo2.options[partycombo2.selectedIndex].value, "parameters":param2});
444
445 teamslist.push(team);
446 updateTeamsTable();
447 updateParameters(); // generate new UUID for next party
448 }
449
450 /** updates the teams table, to match the #partyprofiles list. */
451 function updateTeamsTable() {
452 var table = document.getElementById("teamList");
453 table.innerHTML = ""; // clear table
454 for ( const team of teamslist) {
455 var row = table.insertRow(-1);
456 for (const col in team) {
457 const party = team[col]
458 var cell1 = row.insertCell(-1);
459 var cell2 = row.insertCell(-1);
460 cell1.innerHTML = party["partyref"];
461 cell2.innerHTML = JSON.stringify(party["parameters"]).replace(/,/g,", ");;
462 cell2.setAttribute("style","overflow-wrap: anywhere;");
463 if (col>0) {
464 cell1.className = cell2.className = "onlySHAOP";
465 }
466
467 }
468 }
469
470 selectSessionProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
471 }
472
473 /**
474 @param profilenr the profile nr, 1 for normal , 2 for cob profile
475 */
476 function getProfile(profilenr) {
477 var profilecombo = document.getElementById("profileselection"+profilenr);
478 if (profilecombo.options.length==0) {
479 alert("Please set domain/profile server and select a domain and a profile");
480 throw "blabla";
481 }
482 var filteroptions = document.getElementById("filter"+profilenr).value;
483 if (filteroptions!="") {
484 filteroptions="?"+filteroptions;
485 }
486
487 //return profilecombo.options[profilecombo.selectedIndex].value + filteroptions
488 return profilecombo.value + filteroptions
489 }
490
491 /**
492 Called when user clicks "Add" to add a profile set
493 */
494 function addProfiles() {
495 profiles.push([getProfile(1), getProfile(2)]);
496 updateProfileTable(); // what, MVC?
497 }
498
499 /** updates the party table, to match the #partyprofiles list. */
500 function updateProfileTable() {
501 var table = document.getElementById("profilesList");
502 table.innerHTML = ""; // clear table
503 for ( const profileset of profiles) {
504 var row = table.insertRow(-1);
505 for (var col in profileset) {
506 var profile = profileset[col];
507 var cell1 = row.insertCell(-1);
508 cell1.innerHTML = profile;
509 if (col>0) cell1.className = "onlySHAOP"
510 }
511 }
512 selectSessionProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
513 }
514
515 /**
516 start the tournament as currently set on this page.
517 We need to send a TournamentSettings object to the server, which typically looks like this with SAOP
518 <code>
519 {"AllPermutationsSettings":{"parties":["party1","party2"],
520 "profiles":["profile1","profile2","profile3"],
521 "reuseParties":false,
522 "partiesPerSession":2,
523 "sessionsettings":{"SAOPSettings":{"participants":[],"deadline":{"deadlinetime":{"durationms":10}}}}}}
524 </code>
525 participants are already in the global partyprofiles dictionary
526
527 With SHAOP, it looks like this
528 <code>
529 {
530 "AllPermutationsSettings": {
531 "parties": [ { "partyref": "classpath:geniusweb.exampleparties.simpleshaop.ShaopParty", "parameters": { } },
532 { "partyref": "classpath:geniusweb.exampleparties.randomparty.RandomParty", "parameters": { }}
533 ],
534 "reuseParties": false,
535 "profiles": [ "prof1?partial=10", "prof2?partial=15"],
536 "partiesPerSession": 2,
537 "sessionsettings": { "SHAOPSettings": {
538 "participants": [ ],
539 "deadline": { "deadlinerounds": { "rounds": 10, "durationms": 10000 } } } } } }
540 </code>
541 */
542 function start() {
543 const npersession = document.getElementById("teamspersession").value;
544
545 if (teamslist.length < npersession) {
546 alert("At least "+npersession+" teams are needed.");
547 return;
548 }
549
550 if (profiles.length < npersession) {
551 alert("At least "+npersession+" profilesets are needed.");
552 return;
553 }
554
555 // see https://www.w3schools.com/xml/dom_httprequest.asp
556 var xmlHttp = new XMLHttpRequest();
557 xmlHttp.onreadystatechange = function() {
558 if (this.readyState == 4) {
559 if (this.status == 200) {
560 document.getElementById("startbutton").disabled=true;
561 document.getElementById("started").setAttribute("style","");
562 document.getElementById("logref").href="log/"+this.responseText+".json";
563 document.getElementById("plotref").href="utilstable.xhtml?"+this.responseText;
564 } else
565 alert("request failed:"+this.statusText);
566 }
567 }
568 xmlHttp.open("POST", "run", true);
569 xmlHttp.send(makeRequest());
570 }
571
572 /**
573 @return a json request package containing a AllPermutationsSettings.
574 It assumes that global vars parties and sessionSettings have been set properly.
575 */
576 function makeRequest() {
577 const npersession = document.getElementById("teamspersession").value;
578 const ntournaments = document.getElementById("ntournaments").value;
579 const reuseTeams = document.getElementById("reuseteams").checked;
580 const protocolcombobox = document.getElementById("protocolselection");
581 const protocol = protocolcombobox.options[protocolcombobox.selectedIndex].value;
582 const header=protocol+"Settings";
583
584 var deadline={};
585 const value = document.getElementById("deadlinevalue").value;
586 const dtypecombo = document.getElementById("deadlinetype");
587 if (dtypecombo.options[dtypecombo.selectedIndex].value=="TIME") {
588 deadline["deadlinetime"] = { "durationms": 1000*value};
589 } else {
590 // ROUNDS
591 deadline["deadlinerounds"] = {"rounds": value, "durationms": 10000};
592 }
593
594 // create S(H)AOPSettings. [header] is a weird ECMA script workaround for javascript issue.
595 var settingvalues={"participants":[],"deadline":deadline};
596 if (protocol=="MOPAC") {
597 var combo = document.getElementById("selectedevaluator");
598 var evaluator = combo.options[combo.selectedIndex].value;
599 settingvalues['votingevaluator'] = { [evaluator]: {} };
600 }
601 if (protocol=="MOPAC2") {
602 var combo = document.getElementById("selectedevaluator2");
603 var evaluator = combo.options[combo.selectedIndex].value;
604 settingvalues['votingevaluator'] = { [evaluator]: {} };
605 }
606
607 const sessionSettings = { [header]:settingvalues};
608
609 var teamsFull = [];
610 for (const team of teamslist) {
611 if (protocol=="SHAOP")
612 teamsFull.push({"Team":team});
613 else
614 teamsFull.push({"Team":[team[0]]}); // most protocols have just 1 member per team
615 }
616 var profilesFull = [] ;
617 for (const profileset of profiles) {
618 if (protocol=="SHAOP") // only first member of team is used.
619 profilesFull.push({"ProfileList":profileset});
620 else
621 profilesFull.push({"ProfileList":[profileset[0]]});
622 }
623
624 var settings={"teams":teamsFull,"profileslists":profilesFull,
625 "reuseTeams":reuseTeams,"teamsPerSession":npersession,
626 "sessionsettings":sessionSettings,
627 "numberTournaments":ntournaments};
628
629 // add the Learn settings if needed
630 var tourprot = document.getElementById("tourprotocol").value;
631 var learntime = 1000*document.getElementById("learntime").value;
632 if (tourprot=='AllPermutationsLearn') {
633 settings["learnSettings"]={
634 "LearnSettings":{
635 "participants":[],
636 "deadline": {"deadlinetime": { "durationms": learntime }}
637 }
638 } ;
639 }
640
641 var mainheader=tourprot+"Settings";
642 return JSON.stringify({[mainheader]:settings});
643 }
644
645
646 /**
647 @return a random UUID.
648 Note − This should not be used in production as GUID or UUID
649 generated by Math.Random() may not be unique.
650 */
651 function createUUID() {
652 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
653 var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
654 return v.toString(16);
655 });
656 }
657
658 /**
659 Initialize the page after html is loaded.
660 */
661 function init() {
662 document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.6.0";
663 document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.6.0";
664
665 selectSessionProtocol();
666 connectDomain();
667 connectParties();
668 updateTournamentProt();
669 }
670
671
672 ]]>
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693</script>
694
695</html>
Note: See TracBrowser for help on using the repository browser.