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

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

Fixed memory leak. MOPAC2. removed jcenter build dependencies

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