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

Last change on this file since 13 was 12, checked in by bart, 5 years ago

Release 1.4.0

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