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
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 <select>
16 <option value="APP">All Permutations Tournament Protocol
17 (APP)</option>
18 </select>
19 <br />
20 <input type="number" id="ntournaments" min="1" max="1000000" value="1" />
21 Number of times to repeat the entire tournament
22 <br />
23
24 <br />
25 <input type="number" id="teamspersession" min="2" max="10" value="2" />
26 Number of teams in each session
27 <br />
28
29 <input type="checkbox" id="reuseteams" /> Pick teams with return when
30 creating sessions
31 <br />
32
33 <br />
34 <div id="box" class="box">
35 Session Protocol settings <br /> Session Protocol: <select
36 id="protocolselection" onchange="selectProtocol()">
37 <option value="SAOP">SAOP</option>
38 <option value="SHAOP">SHAOP</option>
39 </select> <br /> Deadline: <input type="number" id="deadlinevalue"
40 name="deadline" min="1" max="10000" value="10" /> <select
41 id="deadlinetype">
42 <option value="ROUNDS">rounds</option>
43 <option value="TIME">seconds</option>
44 </select>
45 </div>
46 <br />
47
48 <div id="box" class="box">
49 <h3>Profiles</h3>
50 Domain/Profile Server: <input type="url" name="url"
51 id="profilesserverurl" value="localhost:8080/profilesserver-1.3.1"
52 pattern=".*:[0-9]+/profilesserver" size="30"
53 onchange="connectDomain()"> </input> <br /> Domain: <select
54 id="domainselection" onchange="selectDomain()">
55 <!-- <option>Waiting for profiles server</option> -->
56 </select> <br /> <br />
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>
67 <br /> <br />
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">
82 <h3>Teams</h3>
83 <br /> Parties Server: <input type="url" name="url"
84 id="partiesserverurl" value="localhost:8080/partiesserver-1.3.1"
85 pattern=".*:[0-9]+/partiesserver" size="30"
86 onchange="connectParties()"> </input> <br /> <br />
87 <!-- party 1 selection -->
88 Party: <select id="partyselection">
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
104 <br /> <br />
105 <table>
106 <thead>
107 <th align="left">Party</th>
108 <th align="left">Parameters</th>
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>
113 </thead>
114 <tbody id="teamList">
115 <tr id="FIXME REMOVE">
116 </tr>
117
118 </tbody>
119 </table>
120
121 </div>
122 <br />
123 <form>
124 <input id="startbutton" type="button" value="Start Tournament"
125 onclick="start()" />
126 </form>
127 <div id="started" style="visibility: hidden">
128 Your tournament started. Click <a href="" id="logref">here</a> to view
129 the log file. <br /> <a href="" id="plotref">show results table.</a>.
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
153
154
155
156
157
158
159
160
161
162
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
175 var teamslist=[];
176 var profiles=[]
177
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
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
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 }
298
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;
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;
312 if (option.text.includes(select) ) option.selected=true;
313 combobox.add(option, 0);
314 }
315 return selection;
316 }
317
318
319
320 /**
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.
323 */
324 function getParameters(elementname) {
325 var text="{"+document.getElementById(elementname).value+"}";
326 try {
327 return JSON.parse(text);
328 } catch(e) {
329 alert("Parameters can not be parsed. Make sure you write correct JSON here."+e);
330 }
331 return {};
332 }
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;
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) {
357 profilecombo.options.length=0;
358 for (var profile in profileslist) {
359 var option = document.createElement('option');
360 option.text = option.value = profileslist[profile];
361 profilecombo.add(option, 0);
362 }
363 }
364
365
366 /**
367 Called when user clicks "Add"
368 */
369 function addTeam() {
370 var partycombo = document.getElementById("partyselection");
371 var partycombo2 = document.getElementById("partyselection2");
372 var param = getParameters("parameters");
373 var param2 = getParameters("parameters2");
374
375
376 if (partycombo.options.length==0) {
377 alert("Please set partier server and select a party");
378 return;
379 }
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();
386 }
387
388 /** updates the teams table, to match the #partyprofiles list. */
389 function updateTeamsTable() {
390 var table = document.getElementById("teamList");
391 table.innerHTML = ""; // clear table
392 for ( const team of teamslist) {
393 var row = table.insertRow(-1);
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 }
405 }
406
407 selectProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
408 }
409
410 /**
411 @param profilenr the profile nr, 1 for normal , 2 for cob profile
412 */
413 function getProfile(profilenr) {
414 var profilecombo = document.getElementById("profileselection"+profilenr);
415 if (profilecombo.options.length==0) {
416 alert("Please set domain/profile server and select a domain and a profile");
417 throw "blabla";
418 }
419 var filteroptions = document.getElementById("filter"+profilenr).value;
420 if (filteroptions!="") {
421 filteroptions="?"+filteroptions;
422 }
423
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)]);
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
440 for ( const profileset of profiles) {
441 var row = table.insertRow(-1);
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 }
448 }
449 selectProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
450 }
451
452 /**
453 start the tournament as currently set on this page.
454 We need to send a TournamentSettings object to the server, which typically looks like this with SAOP
455 <code>
456 {"AllPermutationsSettings":{"parties":["party1","party2"],
457 "profiles":["profile1","profile2","profile3"],
458 "reuseParties":false,
459 "partiesPerSession":2,
460 "sessionsettings":{"SAOPSettings":{"participants":[],"deadline":{"deadlinetime":{"durationms":10}}}}}}
461 </code>
462 participants are already in the global partyprofiles dictionary
463
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>
478 */
479 function start() {
480 const npersession = document.getElementById("teamspersession").value;
481
482 if (teamslist.length < npersession) {
483 alert("At least "+npersession+" teams are needed.");
484 return;
485 }
486
487 if (profiles.length < npersession) {
488 alert("At least "+npersession+" profilesets are needed.");
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";
500 document.getElementById("plotref").href="utilstable.xhtml?"+this.responseText;
501 } else
502 alert("request failed:"+this.statusText);
503 }
504 }
505 xmlHttp.open("POST", "run", true);
506 xmlHttp.send(makeRequest());
507 }
508
509 /**
510 @return a json request package containing a AllPermutationsSettings.
511 It assumes that global vars parties and sessionSettings have been set properly.
512 */
513 function makeRequest() {
514 const npersession = document.getElementById("teamspersession").value;
515 const ntournaments = document.getElementById("ntournaments").value;
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
521 var deadline={};
522 const value = document.getElementById("deadlinevalue").value;
523 const dtypecombo = document.getElementById("deadlinetype");
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
531 // create S(H)AOPSettings. [header] is a weird ECMA script workaround for javascript issue.
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,
550 "reuseTeams":reuseTeams,"teamsPerSession":npersession,"sessionsettings":sessionSettings,
551 "numberTournaments":ntournaments}});
552 }
553
554 /**
555 Initialize the page after html is loaded.
556 */
557 function init() {
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";
560
561 selectProtocol();
562 connectDomain();
563 connectParties();
564 }
565
566
567 ]]>
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582</script>
583
584</html>
Note: See TracBrowser for help on using the repository browser.