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

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

Minor fixes

File size: 18.6 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="MOPAC">MOPAC</option>
39 <option value="AMOP">AMOP</option>
40 <option value="SHAOP">SHAOP</option>
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"
55 name="deadline" min="1" max="10000" value="10" /> <select
56 id="deadlinetype">
57 <option value="ROUNDS">rounds</option>
58 <option value="TIME">seconds</option>
59 </select>
60 </div>
61 <br />
62
63 <div id="box" class="box">
64 <h3>Profiles</h3>
65 Domain/Profile Server: <input type="url" name="url"
66 id="profilesserverurl" value="localhost:8080/profilesserver-1.3.1"
67 pattern=".*:[0-9]+/profilesserver" size="30"
68 onchange="connectDomain()"> </input> <br /> Domain: <select
69 id="domainselection" onchange="selectDomain()">
70 <!-- <option>Waiting for profiles server</option> -->
71 </select> <br /> <br />
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>
82 <br /> <br />
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">
97 <h3>Teams</h3>
98 <br /> Parties Server: <input type="url" name="url"
99 id="partiesserverurl" value="localhost:8080/partiesserver-1.3.1"
100 pattern=".*:[0-9]+/partiesserver" size="30"
101 onchange="connectParties()"> </input> <br /> <br />
102 <!-- party 1 selection -->
103 Party: <select id="partyselection">
104 </select> <br /> Parameters: {
105 <textarea id="parameters" rows="2" cols="70"
106 onchange="getParameters('parameters')" value="" />
107 } <br />
108 <!-- party 2 selection -->
109 <div class="onlySHAOP">
110 COB: <select id="partyselection2">
111 </select> <br /> Parameters: {
112 <textarea id="parameters2" rows="2" cols="70"
113 onchange="getParameters('parameters2')" value="" />
114 }
115 </div>
116 <br />
117
118
119 <button onclick="addTeam()">Add</button>
120
121 <br /> <br />
122 <table>
123 <thead>
124 <th align="left">Party</th>
125 <th align="left">Parameters</th>
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>
130 </thead>
131 <tbody id="teamList">
132 <tr id="FIXME REMOVE">
133 </tr>
134
135 </tbody>
136 </table>
137
138 </div>
139 <br />
140 <form>
141 <input id="startbutton" type="button" value="Start Tournament"
142 onclick="start()" />
143 </form>
144 <div id="started" style="visibility: hidden">
145 Your tournament started. Click <a href="" id="logref">here</a> to view
146 the log file. <br /> <a href="" id="plotref">show results table.</a>.
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
170
171
172
173
174
175
176
177
178
179
180
181
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
194 var teamslist=[];
195 var profiles=[]
196
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() {
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');
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
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
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 }
319
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;
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;
333 if (option.text.includes(select) ) option.selected=true;
334 combobox.add(option, 0);
335 }
336 return selection;
337 }
338
339
340
341 /**
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.
344 */
345 function getParameters(elementname) {
346 var text="{"+document.getElementById(elementname).value+"}";
347 try {
348 return JSON.parse(text);
349 } catch(e) {
350 alert("Parameters can not be parsed. Make sure you write correct JSON here."+e);
351 }
352 return {};
353 }
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;
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) {
378 profilecombo.options.length=0;
379 for (var profile in profileslist) {
380 var option = document.createElement('option');
381 option.text = option.value = profileslist[profile];
382 profilecombo.add(option, 0);
383 }
384 }
385
386
387 /**
388 Called when user clicks "Add"
389 */
390 function addTeam() {
391 var partycombo = document.getElementById("partyselection");
392 var partycombo2 = document.getElementById("partyselection2");
393 var param = getParameters("parameters");
394 var param2 = getParameters("parameters2");
395
396
397 if (partycombo.options.length==0) {
398 alert("Please set partier server and select a party");
399 return;
400 }
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();
407 }
408
409 /** updates the teams table, to match the #partyprofiles list. */
410 function updateTeamsTable() {
411 var table = document.getElementById("teamList");
412 table.innerHTML = ""; // clear table
413 for ( const team of teamslist) {
414 var row = table.insertRow(-1);
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"];
420 cell2.innerHTML = JSON.stringify(party["parameters"]).replace(/,/g,", ");;
421 cell2.setAttribute("style","overflow-wrap: anywhere;");
422 if (col>0) {
423 cell1.className = cell2.className = "onlySHAOP";
424 }
425
426 }
427 }
428
429 selectProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
430 }
431
432 /**
433 @param profilenr the profile nr, 1 for normal , 2 for cob profile
434 */
435 function getProfile(profilenr) {
436 var profilecombo = document.getElementById("profileselection"+profilenr);
437 if (profilecombo.options.length==0) {
438 alert("Please set domain/profile server and select a domain and a profile");
439 throw "blabla";
440 }
441 var filteroptions = document.getElementById("filter"+profilenr).value;
442 if (filteroptions!="") {
443 filteroptions="?"+filteroptions;
444 }
445
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)]);
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
462 for ( const profileset of profiles) {
463 var row = table.insertRow(-1);
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 }
470 }
471 selectProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
472 }
473
474 /**
475 start the tournament as currently set on this page.
476 We need to send a TournamentSettings object to the server, which typically looks like this with SAOP
477 <code>
478 {"AllPermutationsSettings":{"parties":["party1","party2"],
479 "profiles":["profile1","profile2","profile3"],
480 "reuseParties":false,
481 "partiesPerSession":2,
482 "sessionsettings":{"SAOPSettings":{"participants":[],"deadline":{"deadlinetime":{"durationms":10}}}}}}
483 </code>
484 participants are already in the global partyprofiles dictionary
485
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>
500 */
501 function start() {
502 const npersession = document.getElementById("teamspersession").value;
503
504 if (teamslist.length < npersession) {
505 alert("At least "+npersession+" teams are needed.");
506 return;
507 }
508
509 if (profiles.length < npersession) {
510 alert("At least "+npersession+" profilesets are needed.");
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";
522 document.getElementById("plotref").href="utilstable.xhtml?"+this.responseText;
523 } else
524 alert("request failed:"+this.statusText);
525 }
526 }
527 xmlHttp.open("POST", "run", true);
528 xmlHttp.send(makeRequest());
529 }
530
531 /**
532 @return a json request package containing a AllPermutationsSettings.
533 It assumes that global vars parties and sessionSettings have been set properly.
534 */
535 function makeRequest() {
536 const npersession = document.getElementById("teamspersession").value;
537 const ntournaments = document.getElementById("ntournaments").value;
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
543 var deadline={};
544 const value = document.getElementById("deadlinevalue").value;
545 const dtypecombo = document.getElementById("deadlinetype");
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
553 // create S(H)AOPSettings. [header] is a weird ECMA script workaround for javascript issue.
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
563 var teamsFull = [];
564 for (const team of teamslist) {
565 if (protocol=="SHAOP")
566 teamsFull.push({"Team":team});
567 else
568 teamsFull.push({"Team":[team[0]]}); // most protocols have just 1 member per team
569 }
570 var profilesFull = [] ;
571 for (const profileset of profiles) {
572 if (protocol=="SHAOP") // only first member of team is used.
573 profilesFull.push({"ProfileList":profileset});
574 else
575 profilesFull.push({"ProfileList":[profileset[0]]});
576 }
577
578 return JSON.stringify({"AllPermutationsSettings":{"teams":teamsFull,"profileslists":profilesFull,
579 "reuseTeams":reuseTeams,"teamsPerSession":npersession,"sessionsettings":sessionSettings,
580 "numberTournaments":ntournaments}});
581 }
582
583 /**
584 Initialize the page after html is loaded.
585 */
586 function init() {
587 document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.5.1";
588 document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.5.1";
589
590 selectProtocol();
591 connectDomain();
592 connectParties();
593 }
594
595
596 ]]>
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613</script>
614
615</html>
Note: See TracBrowser for help on using the repository browser.