[new] It just worked

This commit is contained in:
Wu Han 2020-12-20 19:46:01 +00:00
parent 5c3cf7e83d
commit b08090795b
9 changed files with 1686 additions and 103 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
node_modules/ node_modules/
leaderboard.json

8
output/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# .gitignore sample
###################
# Ignore all files in this dir...
*
# ... except for this one.
!.gitignore

997
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,8 +17,11 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"express": "^4.16.3", "express": "^4.16.3",
"line-reader": "^0.4.0",
"multer": "^1.3.0", "multer": "^1.3.0",
"node-docker-api": "^1.1.22", "node-docker-api": "^1.1.22",
"socket.io": "^3.0.4" "readline-sync": "^1.4.10",
"socket.io": "^3.0.4",
"strip-ansi": "^6.0.0"
} }
} }

View File

@ -52,22 +52,61 @@ body::after {
color: #000 !important; color: #000 !important;
} }
.project-tab #tabs{
background: #007b5e;
border: 1px solid transparent;
color: #eee;
}
.project-tab #tabs .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
color: #000;
/* background-color: transparent; */
/* border-color: transparent transparent #111; */
border: 1px solid;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
/* border-bottom: 3px solid !important; */
font-weight: bold;
}
.project-tab .nav-link:hover {
border-color: #111;
color: #888;
}
.project-tab .nav-link {
border: 1px solid;
border-color: #bbb;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
color: #BBB;
font-weight: 600;
}
#console-box { #console-box {
height: 50%; height: 50%;
} }
#console { #console {
list-style: none; list-style: none;
-webkit-padding-start: 0;
-webkit-margin-after: 0;
-webkit-margin-before: 1em;
border: 1px solid #000;
overflow-y: auto; overflow-y: auto;
width: 100%; width: 100%;
background: #333;
color: #fff;
height: 200px; height: 200px;
max-height: 200px; max-height: 200px;
font-family: "Ubuntu Mono", "Lucida Console";
padding-left: 15px;
padding-top: 15px;
border-top-right-radius: 1%;
border-top-left-radius: 1%;
border: 1px solid #000;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
color: #AAA;
background: #333;
} }
.box-shadow { .box-shadow {

View File

@ -14,7 +14,7 @@ $( document ).ready( () => {
} }
}); });
$("a").on('click', function(event) { $(".navbar a").on('click', function(event) {
// Make sure this.hash has a value before overriding default behavior // Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") { if (this.hash !== "") {
// Prevent default anchor click behavior // Prevent default anchor click behavior
@ -42,6 +42,56 @@ $( document ).ready( () => {
} }
}); });
$('#submission_info').hide();
$('#ulysses16_submit').hide();
$('#att48_submit').hide();
$('#st70_submit').hide();
$('#a280_submit').hide();
$('#pcb442_submit').hide();
$('#dsj1000_submit').hide();
$( "#ulysses16_submit" ).click(function() {
upload_loaderboard('ulysses16');
$('#ulysses16_submit').removeClass('btn-primary');
$('#ulysses16_submit').text('Submitted');
$('#ulysses16_submit').prop('disabled', true);
});
$( "#att48_submit" ).click(function() {
upload_loaderboard('att48');
$('#att48_submit').removeClass('btn-primary');
$('#att48_submit').text('Submitted');
$('#att48_submit').prop('disabled', true);
});
$( "#st70_submit" ).click(function() {
upload_loaderboard('st70');
$('#st70_submit').removeClass('btn-primary');
$('#st70_submit').text('Submitted');
$('#st70_submit').prop('disabled', true);
});
$( "#a280_submit" ).click(function() {
upload_loaderboard('a280');
$('#a280_submit').removeClass('btn-primary');
$('#a280_submit').text('Submitted');
$('#a280_submit').prop('disabled', true);
});
$( "#pcb442_submit" ).click(function() {
upload_loaderboard('pcb442');
$('#pcb442_submit').removeClass('btn-primary');
$('#pcb442_submit').text('Submitted');
$('#pcb442_submit').prop('disabled', true);
});
$( "#dsj1000_submit" ).click(function() {
upload_loaderboard('dsj1000');
$('#dsj1000_submit').removeClass('btn-primary');
$('#dsj1000_submit').text('Submitted');
$('#dsj1000_submit').prop('disabled', true);
});
// initialize with defaults // initialize with defaults
// $("#uploadfile").fileinput(); // $("#uploadfile").fileinput();

View File

@ -3,22 +3,122 @@ window.onbeforeunload = function(){
return "Make sure to save your graph locally before leaving"; return "Make sure to save your graph locally before leaving";
}; };
var my_res = {};
my_res.ulysses16 = {}
my_res.att48 = {}
my_res.st70 = {}
my_res.a280 = {}
my_res.pcb442 = {}
my_res.dsj1000 = {}
my_res.ulysses16.fitness = -1;
my_res.att48.fitness = -1;
my_res.st70.fitness = -1;
my_res.a280.fitness = -1;
my_res.pcb442.fitness = -1;
my_res.dsj1000.fitness = -1;
var socket = io(); var socket = io();
socket.on('connect', () => { socket.on('connect', () => {
$("#session_id").text(socket.id); $("#status").removeClass('badge-secondary');
$("#status").addClass('badge-primary');
window.id = socket.id window.id = socket.id
console.log('Session Id: ', socket.id); // an alphanumeric id... console.log('Session Id: ', socket.id); // an alphanumeric id...
}); });
socket.on('disconnect', () => {
$("#status").removeClass('badge-primary');
$("#status").addClass('badge-secondary');
$("#status").text('offline');
});
socket.on('users_count', (clients) => { socket.on('users_count', (clients) => {
$("#user_counts").text(clients); $("#status").text(clients + ' online');
}); });
socket.on('start', () => { socket.on('start', () => {
console.log('Building start'); console.log('Building start');
}); });
socket.on('leaderboard', (obj) => {
console.log(obj);
$(".leaderboard-table").find("tr:not(:first):not(:last)").remove();
obj.ulysses16.forEach(element => {
$('#ulysses16-last').before('<tr id=""><td>' + element.name + '</td><td>' + element.fitness + '</td><td>' + element.desc + '</td></tr>')
});
obj.att48.forEach(element => {
$('#att48-last').before('<tr id=""><td>' + element.name + '</td><td>' + element.fitness + '</td><td>' + element.desc + '</td></tr>')
});
obj.st70.forEach(element => {
$('#st70-last').before('<tr id=""><td>' + element.name + '</td><td>' + element.fitness + '</td><td>' + element.desc + '</td></tr>')
});
obj.a280.forEach(element => {
$('#a280-last').before('<tr id=""><td>' + element.name + '</td><td>' + element.fitness + '</td><td>' + element.desc + '</td></tr>')
});
obj.pcb442.forEach(element => {
$('#pcb442-last').before('<tr id=""><td>' + element.name + '</td><td>' + element.fitness + '</td><td>' + element.desc + '</td></tr>')
});
obj.dsj1000.forEach(element => {
$('#dsj1000-last').before('<tr id=""><td>' + element.name + '</td><td>' + element.fitness + '</td><td>' + element.desc + '</td></tr>')
});
$('#ulysses16_best').text(obj.ulysses16[0].fitness);
$('#att48_best').text(obj.att48[0].fitness);
$('#st70_best').text(obj.st70[0].fitness);
$('#a280_best').text(obj.a280[0].fitness);
$('#pcb442_best').text(obj.pcb442[0].fitness);
$('#dsj1000_best').text(obj.dsj1000[0].fitness);
});
socket.on('update', (msg) => {
// var msg = msg.replace(/\x1b[[0-9;]*[a-zA-Z]/g, '');
msg = msg.replace(/[^\x20-\x7E]/g, '')
if(msg[1] == '[') {
msg = msg.substring(1);
}
console.log(msg);
});
socket.on('result', (res) => {
// console.log(res)
res = JSON.parse(res);
$('#submission_info').show();
res.forEach(e => {
if(e.name === "ulysses16.txt") {
my_res.ulysses16.fitness = e.fitness
$("#ulysses16_your").text(e.fitness);
$('#ulysses16_submit').show();
}
if(e.name === "att48.txt") {
my_res.att48.fitness = e.fitness
$("#att48_your").text(e.fitness);
$('#att48_submit').show();
}
if(e.name === "st70.txt") {
my_res.st70.fitness = e.fitness
$("#st70_your").text(e.fitness)
$('#st70_submit').show();
}
if(e.name === "a280.txt") {
my_res.a280.fitness = e.fitness
$("#a280_your").text(e.fitness)
$('#a280_submit').show();
}
if(e.name === "pcb442.txt") {
my_res.pcb442.fitness = e.fitness
$("#pcb442_your").text(e.fitness)
$('#pcb442_submit').show();
}
if(e.name === "dsj1000.txt") {
my_res.dsj1000.fitness = e.fitness
$("#dsj1000_your").text(e.fitness)
$('#dsj1000_submit').show();
}
});
});
$(document).ready( () => { $(document).ready( () => {
$("#btnSubmit").click((event) => { $("#btnSubmit").click((event) => {
//stop submit the form, we will post it manually. //stop submit the form, we will post it manually.
@ -42,7 +142,36 @@ $(document).ready( () => {
} }
}); });
}); });
function upload_loaderboard(name) {
obj = {}
obj.name = name;
data = {}
if(name === 'ulysses16') {
data.fitness = my_res.ulysses16.fitness;
}
else if (name === 'att48') {
data.fitness = my_res.att48.fitness;
}
else if (name === 'st70') {
data.fitness = my_res.st70.fitness;
}
else if (name === 'a280') {
data.fitness = my_res.a280.fitness;
}
else if (name === 'pcb442') {
data.fitness = my_res.pcb442.fitness;
}
else if (name === 'dsj1000') {
data.fitness = my_res.dsj1000.fitness;
}
data.name = $('#submit_name').val();
data.desc = $('#submit_desc').val()
data.time = Date.now()
obj.data = data;
socket.emit('submit', obj);
}
function doAjax() { function doAjax() {
// Get form // Get form

347
server.js
View File

@ -1,72 +1,311 @@
const fs = require('fs'); const fs = require('fs')
var express = require('express'); var express = require('express')
var app = express(); var app = express()
var router = express.Router(); var router = express.Router()
var upload = require('./app/config/multer.config.js'); var upload = require('./app/config/multer.config.js')
const {Docker} = require('node-docker-api'); const {Docker} = require('node-docker-api')
const { time } = require('console')
const docker = new Docker({ socketPath: '/var/run/docker.sock' }); const lineReader = require('line-reader');
global.__basedir = __dirname; const docker = new Docker({ socketPath: '/var/run/docker.sock' })
const stripAnsi = require('strip-ansi');
app.use(express.static('resources')); global.__basedir = __dirname
var http = require('http').createServer(app); app.use(express.static('resources'))
var io = require('socket.io')(http);
var docker_build = (socket, sessionID) => { var http = require('http').createServer(app)
console.log('[server][start]', sessionID); var io = require('socket.io')(http)
socket.emit('start')
path = process.cwd() var readlineSync = require('readline-sync');
fs.mkdir(path + '/output/' + sessionID.toString(), () => {
socket.emit('update', 'Output directort created.') const path = require('path');
}) current_path = process.cwd()
docker.container.create({
Image: 'python:3.8', var leaderboard = {}
Cmd: [ '/bin/bash', '-c', 'cd /tsp && pip install -r requirements.txt && python main.py data/simple/ulysses16.tsp && cp output/* /output' ], leaderboard.ulysses16 = []
HostConfig: { leaderboard.att48 = []
Binds: [ leaderboard.st70 = []
path.toString() + '/com2014-template:/tsp', leaderboard.a280 = []
path.toString() + "/output/" + sessionID.toString() + "/output" leaderboard.pcb442 = []
] leaderboard.dsj1000 = []
}
}) var update_leaderboard = (obj) => {
.then(container => container.start()) if(obj.name === 'ulysses16') {
.then(container => container.logs({ leaderboard.ulysses16.push(obj.data)
follow: true, }
stdout: true, else if (obj.name === 'att48') {
stderr: true leaderboard.att48.push(obj.data)
})) }
.then(stream => { else if (obj.name === 'st70') {
stream.on('data', info => socket.emit('update', new Buffer.from(info).toString('ascii'))) leaderboard.st70.push(obj.data)
stream.on('error', err => console.log(err)) }
}) else if (obj.name === 'a280') {
.catch(error => console.log(error)); leaderboard.a280.push(obj.data)
}
else if (obj.name === 'pcb442') {
leaderboard.pcb442.push(obj.data)
}
else if (obj.name === 'dsj1000') {
leaderboard.dsj1000.push(obj.data)
}
leaderboard.ulysses16.sort(function(a, b){return a.fitness - b.fitness})
leaderboard.ulysses16 = leaderboard.ulysses16.slice(0,10)
leaderboard.att48.sort(function(a, b){return a.fitness - b.fitness}).slice(0,10)
leaderboard.att48 = leaderboard.att48.slice(0,10)
leaderboard.st70.sort(function(a, b){return a.fitness - b.fitness}).slice(0,10)
leaderboard.st70 = leaderboard.st70.slice(0,10)
leaderboard.a280.sort(function(a, b){return a.fitness - b.fitness})
leaderboard.a280 = leaderboard.a280.slice(0,10)
leaderboard.pcb442.sort(function(a, b){return a.fitness - b.fitness})
leaderboard.pcb442 = leaderboard.pcb442.slice(0,10)
leaderboard.dsj1000.sort(function(a, b){return a.fitness - b.fitness})
leaderboard.dsj1000 = leaderboard.dsj1000.slice(0,10)
write_leaderboard()
}
var write_leaderboard = () => {
fs.writeFile('leaderboard.json', JSON.stringify(leaderboard), (err) => {
if (err) throw err;
console.log('Data written to file');
});
}
try {
if (fs.existsSync('leaderboard.json')) {
//file exists
leaderboard = require('./leaderboard.json');
console.log(leaderboard);
}
else {
write_leaderboard();
}
}
catch(err) {
console.error(err)
}
var res = [];
var update_submission = (socket, sessionID) => {
console.log('check result')
var output_dir = current_path.toString() + '/output/' + sessionID + '/'
console.log(output_dir)
if (output_dir.length != 0)
{
res = []
socket.emit('update', 'Got output')
fs.readdir(output_dir, (err, files) => {
if (err) {
console.log(err);
return;
}
console.log('iterate')
files.forEach(file => {
console.log(file)
console.log(path.extname(file))
if (path.extname(file) === '.txt') {
var i = 0
var obj = {}
obj.name = file
console.log(output_dir + file)
try {
// read contents of the file
const data = fs.readFileSync(output_dir + file, 'UTF-8');
// split the contents by new line
const lines = data.split(/\r?\n/);
// print all lines
lines.forEach((line) => {
console.log(line)
i = i + 1
// console.log(line)
if(i == 1) {
var fitness = parseFloat(line)
if(fitness < 0) {
obj.fitness = parseInt(line)
}
else {
obj.fitness = fitness
}
}
else if (i == 2) {
if(obj.fitness > 0) {
solution_array = line.split(",");
for(var j = 0; j < solution_array.length; j++) {
solution_array[i] = parseInt(solution_array[i], 10);
}
obj.solution = solution_array
}
else {
obj.solution = []
}
}
});
} catch (err) {
console.error(err);
}
res.push(obj)
}
});
// console.log(res)
socket.emit('result', JSON.stringify(res))
});
}
else {
console.log('no result')
}
}; };
var clients = 0;
var docker_build = (socket, sessionID) => {
console.log('[server][start]', sessionID)
socket.emit('start')
fs.mkdir(current_path + '/output/' + sessionID.toString(), () => {
socket.emit('update', 'Output directort created.')
socket.emit('update', 'Test started, this may take a while.')
})
{
if (fs.existsSync(current_path.toString() + '/uploads/' + sessionID.toString() + '/requirements.txt'))
{
//file exists
docker.container.create({
Image: 'com2014-tsp',
HostConfig: {
Binds: [
current_path.toString() + '/uploads/' + sessionID.toString() + '/requirements.txt:/tsp/requirements.txt',
current_path.toString() + "/output/" + sessionID.toString() + ":/output"
]
}
})
.then( (container) => {
container_id = container.data.Id
socket.emit('update','Container Id: ' + container_id)
return container.start()
})
.then(container => container.logs({
follow: true,
stdout: true,
stderr: true
}))
.then(stream => {
stream.on('data', info => socket.emit('update', new Buffer.from(info).toString('ascii')))
stream.on('error', err => console.log(err))
})
.catch(error => console.log(error))
}
else
{
docker.container.create({
Image: 'com2014-tsp',
HostConfig: {
Binds: [
current_path.toString() + "/output/" + sessionID.toString() + ":/output"
]
}
})
.then( (container) => {
container_id = container.data.Id
socket.emit('update','Container Id: ' + container_id)
return container.start()
})
.then(container => container.logs({
follow: true,
stdout: true,
stderr: true
}))
.then(stream => {
stream.on('data', (info) => {
console.log(stripAnsi(new Buffer.from(info).toString()))
socket.emit('update', stripAnsi(new Buffer.from(info).toString('ascii')))
}),
stream.on('error', err => console.log(err))
})
.catch(error => console.log(error))
}
var intervalObj;
var timeoutObj;
intervalObj = setInterval(() => {
if(!finished)
{
docker.container.list({all:true})
// Inspect
.then((containers) => {
containers.forEach(container => {
if(container.data.Id == container_id) {
socket.emit('update', 'Container Status: ' + container.data.State)
if(container.data.State === 'exited')
{
finished = true;
clearInterval(intervalObj);
clearTimeout(timeoutObj);
socket.emit('update', 'Test end')
update_submission(socket, sessionID);
}
}
});
})
.catch(error => console.log(error))
}
}, 2000);
var finished = false
// call the rest of the code and have it execute after 3 seconds
timeoutObj = setTimeout(() => {
console.log('end')
if(!finished)
{
clearInterval(intervalObj);
socket.emit('update', 'timeout')
}
}, 20000)
}
}
var clients = 0
io.on('connection', (socket) => { io.on('connection', (socket) => {
const sessionID = socket.id; const sessionID = socket.id
console.log('[client][connection]', sessionID); console.log('[client][connection]', sessionID)
clients = clients + 1; clients = clients + 1
io.sockets.emit('users_count', clients); io.sockets.emit('users_count', clients)
console.log(leaderboard)
socket.emit('leaderboard', leaderboard)
socket.on('disconnect', () => { socket.on('disconnect', () => {
clients = clients - 1; clients = clients - 1
console.log('[client][disconnect]', sessionID) console.log('[client][disconnect]', sessionID)
io.sockets.emit('users_count', clients); io.sockets.emit('users_count', clients)
}); })
socket.on('build', () => { socket.on('build', () => {
console.log('[client][build]', sessionID); console.log('[client][build]', sessionID)
docker_build(socket, sessionID); docker_build(socket, sessionID)
}); })
}); socket.on('submit', (obj) => {
console.log('[client][submit]', sessionID)
update_leaderboard(obj)
io.emit('leaderboard', leaderboard)
})
require('./app/routers/file.router.js')(app, router, upload); })
require('./app/routers/file.router.js')(app, router, upload)
// Create a Server // Create a Server
var server = http.listen(8081, () => { var server = http.listen(8081, () => {
@ -74,5 +313,5 @@ var server = http.listen(8081, () => {
var host = server.address().address var host = server.address().address
var port = server.address().port var port = server.address().port
console.log("App listening at http://%s:%s", host, port); console.log("App listening at http://%s:%s", host, port)
}) })

View File

@ -29,6 +29,8 @@
<script src="/static/themes/fas/theme.js" type="text/javascript"></script> <script src="/static/themes/fas/theme.js" type="text/javascript"></script>
<script src="/static/themes/explorer-fas/theme.js" type="text/javascript"></script> <script src="/static/themes/explorer-fas/theme.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Ubuntu+Mono" />
<!-- My Script --> <!-- My Script -->
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<script src="/static/js/postrequest.js"></script> <script src="/static/js/postrequest.js"></script>
@ -39,7 +41,7 @@
<body data-spy="scroll" data-target="#main-nav" data-offset="0"> <body data-spy="scroll" data-target="#main-nav" data-offset="0">
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-md bg-dark navbar-dark sticky-top" id="main-nav"> <nav class="navbar navbar-expand-md bg-dark navbar-dark sticky-top" id="main-nav">
<a class="navbar-brand" href="/">TSP Workshop &nbsp;&nbsp;<span class="badge badge-primary"><span id="user_counts">0</span> Online</span></a> <a class="navbar-brand" href="/">TSP Workshop &nbsp;&nbsp;<span class="badge badge-primary" id="status"><span id="user_counts">0</span> Online</span></a>
<button class="navbar-toggler" data-toggle="collapse" data-target="#navbarCollapse"> <button class="navbar-toggler" data-toggle="collapse" data-target="#navbarCollapse">
<span class="navbar-toggler-icon "></span> <span class="navbar-toggler-icon "></span>
</button> </button>
@ -86,7 +88,7 @@
<div id="leaderboard"> <div id="leaderboard">
<section class="section" id="section0"> <!-- <section class="section" id="section0">
<div class="slide"> <div class="slide">
<div class="container"> <div class="container">
<div class="row text-center"> <div class="row text-center">
@ -100,9 +102,7 @@
<tr> <tr>
<th scope="col">#</th> <th scope="col">#</th>
<th scope="col">ulysses16.tsp</th> <th scope="col">ulysses16.tsp</th>
<th scope="col">att48.tsp</th> <th scope="colthead-dark">a280.tsp</th>
<th scope="col">st70.tsp</th>
<th scope="col">a280.tsp</th>
<th scope="col">pcb442.tsp</th> <th scope="col">pcb442.tsp</th>
<th scope="col">dsj1000.tsp</th> <th scope="col">dsj1000.tsp</th>
</tr> </tr>
@ -203,6 +203,141 @@
</div> </div>
</div> </div>
</div> </div>
</section> -->
<section id="tabs" class="section project-tab" id="section0">
<div class="slide">
<div class="container">
<div class="row text-center">
<div class="col-12">
<div class="display-3 mb-5">Leaderboard</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<nav>
<div class="nav nav-tabs nav-fill" id="nav-tab" role="tablist">
<a class="nav-item nav-link active" id="nav-ulysses16-tab" data-toggle="tab" href="#nav-ulysses16" role="tab" aria-controls="nav-ulysses16" aria-selected="true">ulysses16</a>
<a class="nav-item nav-link" id="nav-att48-tab" data-toggle="tab" href="#nav-att48" role="tab" aria-controls="nav-att48" aria-selected="false">att48</a>
<a class="nav-item nav-link" id="nav-st70-tab" data-toggle="tab" href="#nav-st70" role="tab" aria-controls="nav-st70" aria-selected="false">st70</a>
<a class="nav-item nav-link" id="nav-a280-tab" data-toggle="tab" href="#nav-a280" role="tab" aria-controls="nav-a280" aria-selected="false">a280</a>
<a class="nav-item nav-link" id="nav-pcb442-tab" data-toggle="tab" href="#nav-pcb442" role="tab" aria-controls="nav-pcb442" aria-selected="false">pcb442</a>
<a class="nav-item nav-link" id="nav-dsj1000-tab" data-toggle="tab" href="#nav-dsj1000" role="tab" aria-controls="nav-dsj1000" aria-selected="false">dsj1000</a>
</div>
</nav>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-ulysses16" role="tabpanel" aria-labelledby="nav-ulysses16-tab">
<table class="table leaderboard-table" cellspacing="0">
<thead class="thead-dark ">
<tr>
<th>Name</th>
<th>Fitness</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<tr id="ulysses16-last">
<td> - </td>
<td> - </td>
<td> - </td>
</tr>
</tbody>
</table>
</div>
<div class="tab-pane fade" id="nav-att48" role="tabpanel" aria-labelledby="nav-att48-tab">
<table class="table leaderboard-table" cellspacing="0">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th>Fitness</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<tr id="att48-last">
<td> - </td>
<td> - </td>
<td> - </td>
</tr>
</tbody>
</table>
</div>
<div class="tab-pane fade" id="nav-st70" role="tabpanel" aria-labelledby="nav-st70-tab">
<table class="table leaderboard-table" cellspacing="0">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th>Fitness</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<tr id="st70-last">
<td> - </td>
<td> - </td>
<td> - </td>
</tr>
</tbody>
</table>
</div>
<div class="tab-pane fade" id="nav-a280" role="tabpanel" aria-labelledby="nav-a280-tab">
<table class="table leaderboard-table" cellspacing="0">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th>Fitness</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<tr id="a280-last">
<td> - </td>
<td> - </td>
<td> - </td>
</tr>
</tbody>
</table>
</div>
<div class="tab-pane fade" id="nav-pcb442" role="tabpanel" aria-labelledby="nav-pcb442-tab">
<table class="table leaderboard-table" cellspacing="0">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th>Fitness</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<tr id="pcb442-last">
<td> - </td>
<td> - </td>
<td> - </td>
</tr>
</tbody>
</table>
</div>
<div class="tab-pane fade" id="nav-dsj1000" role="tabpanel" aria-labelledby="nav-dsj1000-tab">
<table class="table leaderboard-table" cellspacing="0">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th>Fitness</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
<tr id="dsj1000-last">
<td> - </td>
<td> - </td>
<td> - </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section> </section>
</div> </div>
</div> </div>
@ -232,50 +367,50 @@
<tbody> <tbody>
<tr> <tr>
<th scope="col">ulysses16.tsp</th> <th scope="col">ulysses16.tsp</th>
<td>Inf</td> <td id="ulysses16_your">Inf</td>
<td>Inf</td> <td id="ulysses16_best">Inf</td>
<td> <td>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" id="ulysses16_submit">Submit</button>
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="col">att48.tsp</th> <th scope="col">att48.tsp</th>
<td>Inf</td> <td id="att48_your">Inf</td>
<td>Inf</td> <td id="att48_best">Inf</td>
<td> <td>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" id="att48_submit">Submit</button>
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="col">st70.tsp</th> <th scope="col">st70.tsp</th>
<td>Inf</td> <td id="st70_your">Inf</td>
<td>Inf</td> <td id="st70_best">Inf</td>
<td> <td>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" id="st70_submit">Submit</button>
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="col">a280.tsp</th> <th scope="col">a280.tsp</th>
<td>Inf</td> <td id="a280_your">Inf</td>
<td>Inf</td> <td id="a280_best">Inf</td>
<td> <td>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" id="a280_submit">Submit</button>
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="col">pcb442.tsp</th> <th scope="col">pcb442.tsp</th>
<td>Inf</td> <td id="pcb442_your">Inf</td>
<td>Inf</td> <td id="pcb442_best">Inf</td>
<td> <td>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" id="pcb442_submit">Submit</button>
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="col">dsj1000.tsp</th> <th scope="col">dsj1000.tsp</th>
<td>Inf</td> <td id="dsj1000_your">Inf</td>
<td>Inf</td> <td id="dsj1000_best">Inf</td>
<td> <td>
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary" id="dsj1000_submit">Submit</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -283,14 +418,14 @@
</div> </div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3" id="submission_info">
<div class="col col-4"> <div class="col col-4">
<label for="">Your Name</label> <label for="">Your Name</label>
<input class="form-control form-control-lg" type="text" placeholder="Your Name"> <input class="form-control form-control-lg" type="text" placeholder="Your Name" id="submit_name">
</div> </div>
<div class="col col-8"> <div class="col col-8">
<label for="">Description</label> <label for="">Description</label>
<input class="form-control form-control-lg" type="text" placeholder="Precisely describe your submission."> <input class="form-control form-control-lg" type="text" placeholder="Precisely describe your submission." id="submit_desc">
</div> </div>
</div> </div>
@ -304,9 +439,13 @@
</form> </form>
</div> </div>
</div> </div>
<hr/> <hr/>
<div class="row mb-5" id="console-box"> <div class="row mb-5" id="console-box">
<ul id="console"></ul> <!-- I will hold the log messages --> <div class="col col-12">
<ul id="console"></ul> <!-- I will hold the log messages -->
</div>
</div> </div>
</div> </div>
</div> </div>