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

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

Tries harder to kill parties after deadline

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