// Requires const fs = require('fs'); const process = require('process'); const pathtools = require('path'); const express = require('express'); const fileUpload = require('express-fileupload'); const { spawn } = require('child_process'); const pug = require('pug'); const unzip = require('unzip'); const touch = require('touch'); // Consts const port = 8000; const pythonPath = '/home/pytron/miniconda3/envs/pytron/bin/python' const aisPath = 'pytron_run/ai_manager/' const aisPathOld = 'pytron_run/ai_manager_old' // Create the directories to store the files try { fs.mkdirSync(aisPath); } catch { } try { fs.mkdirSync(aisPathOld); } catch { } let pythonRunning = false; let pythonShouldRun = false; function runPython() { process.stdout.write('Requested to run python'); if (pythonRunning) { process.stdout.write(' buy python is already running'); pythonShouldRun = true; } else { pythonRunning = true; } process.stdout.write('\n'); let p = spawn(pythonPath, ['run.py'], {cwd: 'pytron_run'}); p.stdout.on('data', (data) => { let content = data.toString().split('\n'); for (let line of content) { console.log('run.py: ' + line); } }); p.stderr.on('data', (data) => { let content = data.toString().split('\n'); for (let line of content) { console.log('run.py: ' + line); } }); p.on('close', (code) => { process.stdout.write('Python finished executing'); if (pythonShouldRun) { process.stdout.write(' but another request arrive, so we will relaunch it\n'); pythonShouldRun = false; pythonRunning = false; runPython(); } else { process.stdout.write('\n'); pythonRunning = false; } }); } function parse(data) { let content = JSON.parse(data); let parsed = {ais: [], battles: {}}; for (let ai of content.ais) { parsed.ais.push({ name: ai, victories: 0, defeats: 0, nulls: 0, score: 0, }); } for (let key in content.battles) { let battlers = key.split('/'); let ai1 = battlers[0]; let ai2 = battlers[1]; parsed.battles[ai1 + '/' + ai2] = content.battles[key][0]; parsed.battles[ai2 + '/' + ai1] = content.battles[key][1]; let realAi1 = parsed.ais.find((x) => x.name == ai1); let realAi2 = parsed.ais.find((x) => x.name == ai2); realAi1.victories += content.battles[key][0]; realAi1.defeats += content.battles[key][1]; realAi2.victories += content.battles[key][1]; realAi2.defeats += content.battles[key][0]; realAi1.nulls += content.battles[key][2]; realAi2.nulls += content.battles[key][2]; } parsed.sortedAis = parsed.ais.slice(0); for (let ai of parsed.sortedAis) { ai.score = ai.victories * 3 + ai.nulls; } parsed.sortedAis.sort((a, b) => b.score - a.score); parsed.sortedAis[0].rank = 1; parsed.sortedAis[1].rank = 2; parsed.sortedAis[2].rank = 3; return parsed; } function startServer() { const app = express(); app.set('view engine', 'pug'); app.use(fileUpload()); app.get('/', (req, res) => { fs.readFile('pytron_run/assets/data.json', 'utf-8', (err, data) => { if (err != null) { console.log(err); } let parsed = parse(data); res.render('index', { data: parsed, running: pythonRunning, }); }); }); app.get('/upload', (req, res) => { res.render('upload', {}); }); app.post('/upload-target', (req, res) => { if (!req.body.name) { res.render('error', {message: "You have to enter a name in the form"}); return; } if (req.body.name.indexOf('.') !== -1) { res.render('error', {message: "The name of your AI can't contain dots"}); } if (!req.files.archive) { res.render('error', {message: "You have to send a ZIP archive"}); return; } let path = pathtools.join(aisPath, req.body.name) try { if (fs.statSync(path).isDirectory()) { // Move it to old let version = 0; for(;;) { let movePath = pathtools.join(aisPathOld, req.body.name + '.' + version); try { fs.statSync(movePath); // If the sync succeded, it means that the directory already exists version++; } catch { // If we're here, it means that we found the right path. We // will move the old one to the old directories, and save // the new one fs.renameSync(path, movePath); break; } } } } catch { // Nothing to do here } fs.mkdirSync(path); let zipfile = pathtools.join(path, 'archive.zip'); req.files.archive.mv(zipfile, (err) => { if (err != null) { console.log(err); res.render('error', {message: 'Unable to save the ZIP archive to the server'}); return; } fs.createReadStream(zipfile) .pipe(unzip.Extract({path})) .on('close', () => { // Touch __init__.py touch(pathtools.join(path, '__init__.py'), () => { // Trigger python_run runPython(); res.redirect('/'); }); }) .on('error', () => { res.render('error', {message: 'Failed to unzip the archive'}); }); }); }); app.use('/static', express.static('static')); app.listen(port, () => { console.log(`Server listening on port ${port}!`) }) } function main() { switch (process.argv[2]) { case 'start': startServer(); break; case 'python': runPython(); break; default: console.log('Unknown option: ' + process.argv[2]); console.log('Usage: node server.js start|python') process.exit(1); break; } } main();