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

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

Version 1.5.2

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