diff options
| -rw-r--r-- | Cont/custom-message/index.css | 3 | ||||
| -rw-r--r-- | Cont/custom-message/index.html | 26 | ||||
| -rw-r--r-- | Cont/index.css | 9 | ||||
| -rw-r--r-- | Cont/index.html | 2 | ||||
| -rw-r--r-- | Cont/updation/index.html | 41 | ||||
| -rw-r--r-- | parseClasses.js | 61 | ||||
| -rw-r--r-- | parseExceptions.js | 66 | ||||
| -rw-r--r-- | server.js | 75 | ||||
| -rw-r--r-- | update.js | 21 | 
9 files changed, 252 insertions, 52 deletions
diff --git a/Cont/custom-message/index.css b/Cont/custom-message/index.css new file mode 100644 index 0000000..664d7fb --- /dev/null +++ b/Cont/custom-message/index.css @@ -0,0 +1,3 @@ +body { +	padding-top: 30vh; +} diff --git a/Cont/custom-message/index.html b/Cont/custom-message/index.html new file mode 100644 index 0000000..791513d --- /dev/null +++ b/Cont/custom-message/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="fi"> +       <head> +	       <meta charset="utf-8"> +	       <title>Page not found</title> +	       <link href="/index.css" rel="stylesheet" type="text/css"> +	       <link href="/non-main.css" rel="stylesheet" type="text/css"> +	       <link href="/404/index.css" rel="stylesheet" type="text/css"> +	       <meta name="viewport" content="width=device-width, initial-scale=1"> +       </head> +       <body> +	       <div class="float-block"> +			<h1>\(header\)</h1> +			<p>\(content\)</p> +	       </div> + +	       <a class="back" href="/"><img src="/Images/back.png" alt="Takaisin etusivulle"></a> + +	       <footer> +			<a href="https://www.github.com/JoelHMikael/FoodJS">Lähdekoodi</a> +			<span>|</span> +			<a href="/devs">Ota yhteyttä</a> +	       </footer> + +       </body> +</html> diff --git a/Cont/index.css b/Cont/index.css index 8df7295..102dac8 100644 --- a/Cont/index.css +++ b/Cont/index.css @@ -61,12 +61,16 @@ body {  #input[type="text"], select {  	background-color: white;  } +textarea { +	width: 80%; +}  /*** the send-button ***/  #send {  	background: blue;  	padding: .5em;  	margin: .5em; +	margin-bottom: 0;  	font-weight: bold;  	position: relative; @@ -80,6 +84,11 @@ body {  	box-shadow: .25em .25em;  } +#updationlink { +	color: #aaaaaa; +	font-size: .8rem; +} +  /*** Footer ***/  footer {  	position: fixed; diff --git a/Cont/index.html b/Cont/index.html index 694e4c9..edd72f5 100644 --- a/Cont/index.html +++ b/Cont/index.html @@ -36,6 +36,8 @@  					</select>  					<br>  					<input type="submit" id="send" class="highlight" value="Löydä vuoro"> +					<br> +					<a href="/updation" id="updationlink">Päivitä ruoat</a>  				</form>  				<br> diff --git a/Cont/updation/index.html b/Cont/updation/index.html new file mode 100644 index 0000000..49167bc --- /dev/null +++ b/Cont/updation/index.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html lang="fi"> +	<head> +		<meta charset="utf-8"> +		<title>LYLL-ruokailuvuoro</title> +		<link href="/index.css" rel="stylesheet" type="text/css"> +		<link rel="icon" type="image/x-icon" href="/Images/favicon.ico"> +		<meta name="viewport" content="width=device-width, initial-scale=1"> +	</head> +	<body> +		<header> +			<h1 class=shadow>Ruokailuvuorojen ja luokkien päivityssivu</h1> +		</header> + +		<br> + +		<main> +			<form method="POST"> +				<p class=shadow>Liitä allaolevaan tekstikenttään ruokailuvuorot (Wilma-viestin teksti):</p> +				<br> +				<textarea name="shifts" rows="24" style="text-align: left;"></textarea> +				<br> +				<p class="shadow">Kiitos paljon! Kun painat sivun lopusta 'Päivitä'-painiketta, toimii ruokailuvuorohaku (toivon mukaan) jälleen. Jos sinulla on yhtään enemmän aikaa, voit myös suorittaa allaolevan vaiheen. Sen suoritettuasi ruokailuvuoroja voi hakea opettajan ja kurssikoodin lisäksi luokkatilan perusteella.</p> +				<br> +				<p class="shadow">Liitä allaolevaan tekstikenttään kurssitarjottimista tämä jakso. Voit avata kurssitarjottimet Excelissä tai Libreoffice Calcissa, valita alueen, jossa on jakson kurssit ja CTRL+C CTRL+V tähän. Toista sama kaikille jakson alueille kaikista vuoden kurssitarjottimista.</p> +				<br> +				<textarea name="classes" rows="24" style="text-align: left;"></textarea> +				<br> +				<input type="submit" id="send" class="highlight" value="Päivitä"> +				<br> +				<p>Painikkeen painamisen jälkeen seuraavan sivun latautumisessa kestää, koska serveri käsittelee syötteesi loppuun asti ennen vastaamista, jotta se voi kertoa, onnistuiko päivitys.</p> +			</form> +		</main> + +		<footer> +			<a href="https://www.github.com/JoelHMikael/LYLLRuoka">Lähdekoodi</a> +			<span>|</span> +			<a href="/devs">Ota yhteyttä</a> +		</footer> +	</body> +</html> diff --git a/parseClasses.js b/parseClasses.js index b114f33..fd48768 100644 --- a/parseClasses.js +++ b/parseClasses.js @@ -5,46 +5,38 @@ const readline = require("readline");  const stream = require("stream");  const getIndexType = require("./dbparse.js").indexType; -// What a mess. -async function parseClassData(path, DB) +async function parseClassData(classes, DB)  {  	const separator = "\t"; -	const inStream = fs.createReadStream(path) -	const outStream = new stream(); -	const rl = readline.createInterface(inStream, outStream); - +	classes = classes.split('\n');  	let lineNum = 0;  	let courses = []; -	rl.on("line", line => -	{ +	for(let line of classes) {  		let lineList = line.split(separator); -        let type = getIndexType(lineList[0]); -        if (!((type === "class") || (type === "teacher"))) -            lineNum = 0; +		let type = getIndexType(lineList[0]); +		if (!((type === "class") || (type === "teacher"))) +			lineNum = 0; -        if (lineNum % 3 === 0) -            courses = lineList; +		if (lineNum % 3 === 0) +			courses = lineList;  		if ((lineNum % 3) === 2) -        { -            // Remove the weird "R":s in the end of the classes -            for(let i = 0; i < lineList.length; i++) -            { -                let _s = lineList[i]; -                lineList[i] = _s.substring(0, _s.length - 1); -            } +		{ +			// Remove the weird "R":s in the end of the classes +			for(let i = 0; i < lineList.length; i++) +			{ +				let _s = lineList[i]; +				lineList[i] = _s.substring(0, _s.length - 1); +			}  			addToDBFromLists(DB, courses, lineList, -                index => { return getIndexType(index) === "course"; }, -                index => { return getIndexType(index) === "class"; } -            ); -        } +				index => { return getIndexType(index) === "course"; }, +				index => { return getIndexType(index) === "class"; } +			); +		}  		lineNum++ -	}); -    rl.on("close", () => -    { -        return 0; -    }); +	} +	return 0;  }  function addToDBFromLists(DB, l1, l2, l1cond, l2cond) @@ -56,13 +48,10 @@ function addToDBFromLists(DB, l1, l2, l1cond, l2cond)  	}  } -async function parseClasses(DB, ...paths) +async function parseClasses(DB, classes)  { -    let parsed = []; -    await DB.query_raw("DELETE FROM classes"); -    for(const path of paths) -        parsed.push(parseClassData(path, DB)) -    return await Promise.all(parsed); +	await DB.query_raw("DELETE FROM classes"); +        return await parseClassData(classes, DB);  } -exports.classes = parseClasses;
\ No newline at end of file +exports.classes = parseClasses; diff --git a/parseExceptions.js b/parseExceptions.js new file mode 100644 index 0000000..63cd6d2 --- /dev/null +++ b/parseExceptions.js @@ -0,0 +1,66 @@ + +const assert = require('node:assert').strict; + +function parseLine(line) { +	const [datePart] = line.split(' ', 1); +	assert.equal(datePart !== undefined, true, `otsikkoa ei annettu rivillä ”${line}”`); +	const rest = line.substring(datePart.length + 1); +	let dateStrings = datePart.split('-'); +	if (dateStrings.length === 1) +		dateStrings.push(dateStrings[0]); +	assert.equal(dateStrings.length, 2, `päivämääräväli ${datePart} on virheellinen (ks. rivi ${line})`); +	let d1   = dateStrings[0].split('.'); +	const d2 = dateStrings[1].split('.'); +	assert.equal(d2.length, 3, `päivämäärästä ”${dateStrings[1]}”  puuttui päivä, kuukausi tai vuosi tai se on muutoin virheellinen (ks. rivi ”${line}”)`); +	while (d1.length < 3) { +		d1.push(d2[d1.length]) +	} +	for (let i = 0; i < 3; i++) { +		d1[i] = +d1[i]; +		d2[i] = +d2[i]; +		let opts = ['päivä', 'kuukausi', 'vuosi']; +		assert.ok(!isNaN(d1[i]), `syötetty ${opts[i]} ei koostunut pelkistä numeroista (ks. rivi ${line})`); +		assert.ok(!isNaN(d2[i]), `syötetty ${opts[i]} ei koostunut pelkistä numeroista (ks. rivi ${line})`); +	} +	const start = new Date(d1[2], d1[1] - 1, d1[0]); +	const end = new Date(d2[2], d2[1] - 1, d2[0]); +  +	let [header, message = ''] = rest.split('|', 2); +	assert.equal(header === undefined, false, 'otsikko täytyy antaa (ks. rivi ${line})'); +	header = header.trimEnd(); +	message = message.trimStart(); +  +	return [ +		start, +		end, +		header, +		message +	]; +} +assert.deepEqual( +	parseLine('02.06.2024-07.08.2024 Hyvää kesää LYLLin väelle! | Kesäloma 2.6.-7.8.'), +	[ +		new Date(2024, 5, 2), +		new Date(2024, 7, 7), +		'Hyvää kesää LYLLin väelle!', +		'Kesäloma 2.6.-7.8.' +	] +); + +async function updateExceptions(exceptions, DB) { +	await DB.query_raw('DELETE FROM exceptions'); +	let dbOperations = []; +	for (line of exceptions.split('\n')) { +		if (line === '' || line[0] === '#') +			continue +		const [start, end, header, message] = parseLine(line); +		dbOperations.push(DB.execute( +			'INSERT INTO exceptions VALUE (?, ?, ?, ?)', +			[start, end, header, message] +		)); +	} +	await Promise.all(dbOperations); +	return 0; +} + +exports.updateExceptions = updateExceptions; @@ -1,6 +1,5 @@ -//const http	= require("http");  const https	= require("https"); -const http = require("http"); +const http	= require("http");  const url	= require("url");  const food	= require("./food.js");  const SQL_DBS	= require("./database.js"); @@ -16,6 +15,7 @@ async function init()  	const build = {  		"./Cont/index.html": buildMain,  		"./Cont/index.css": buildDefault, +		"./Cont/updation/index.html": buildDefault,  		"./Cont/devs/index.html": buildDevs,  		"./Cont/devs/index.css": buildDefault,  		"./Cont/404/index.css": buildDefault, @@ -25,9 +25,11 @@ async function init()  		"./Cont/Images/favicon.ico": buildImage,  	};  	const errorPath = "./Cont/404/index.html"; +        const SHIFTPATH = "../Updation/shifts.txt"; +        const CLASSPATH = "../Updation/classes.txt"; -    const startDate = new Date(); -    let visitorCount = 0; +	const startDate = new Date(); +	let visitorCount = 0;  	// await for needed things in async  	let [dbcredentials, httpsKey, httpsCert] = await Promise.all([ @@ -37,7 +39,7 @@ async function init()  	]); -    // https options, you need to get a certificate in the file ../Certificate for the server to work +	// https options, you need to get a certificate in the file ../Certificate for the server to work  	const httpsOpts = {  		key: httpsKey,  		cert: httpsCert @@ -48,7 +50,7 @@ async function init()  	// Update...  	// ...shifts and classes -	await updateDB.update(SQLDB, "../Updation/shifts.txt", "../Updation/vanhalops.csv", "../Updation/uusilops.csv"); +	await updateDB.update(SQLDB, SHIFTPATH, CLASSPATH);  	// ...foods  	dateFuncs.run_at_monday_mornings(() => food.build(SQLDB));  	if ((new Date()).getDay() !== 1) // update if it's not monday. if it's monday, it has already been run by the scheduler above. @@ -56,8 +58,57 @@ async function init()  	// server code  	async function server(req, res)  	{ -        // Lightweight analytics. Don't be evil. We just want to know if anyone uses this. -        visitorCount++; +		visitorCount++; + +		// Updation panel +		if (req.method === "POST") { +			let data = ""; +			req.on("data", chunk => { +				data += chunk; +				if (data.length > 1e6) { +					res.writeHead(413); +					res.end("Liian pitkä pyyntö"); +					req.connection.destroy(); +				} +			}); +			req.on("end", async function updateandrespond() { +				let q = new URLSearchParams(data); +				let shifts = ""; +				let classes = ""; +				try { +					shifts = decodeURIComponent(q.get("shifts")).replaceAll("+", " "); +					classes = decodeURIComponent(q.get("classes")).replaceAll("+", " "); +				} catch { +					console.log("Malformed url, presumably"); +					res.writeHead(400); +					res.end(); +				} +				if (shifts === null || classes === null) { +					res.writeHead(400); +					res.end("Avainta 'shifts' ja/tai 'classes' ei löytynyt pyynnöstä."); +				} +				let shiftfile = await fs.open(SHIFTPATH, "w"); +				await shiftfile.write(shifts); +				shiftfile.close(); +				if (classes != "") { +					let classfile = await fs.open(CLASSPATH, "w"); +					await classfile.write(classes) +					classfile.close(); +				} + +				try { +					await updateDB.update(SQLDB, SHIFTPATH, CLASSPATH); +				} catch(e) { +					res.writeHead(400); +					res.end("Virhe tietojen käsittelyssä. Ota yhteys kehittäjään."); +					console.log(e); +					return; +				} +				res.writeHead(200); +				res.end("Kiitos paljon! Näyttää siltä, että tiedot saatiin päivitettyä."); +			}); +			return; +		}  		// validate inputs  		let q = url.parse(req.url, true); @@ -322,6 +373,14 @@ async function buildDevs(args)  	return build_replace(data.toString("utf-8"), {"devs": res});  } +async function buildCustomMessage(header, message) { +	let data = await open.file("./Cont/custom-message/index.html"); +	data = data +		.toString("utf-8") +		.replace("\\(header\\)", header) +		.replace("\\(content\\)", message); +	return data; +}  async function build404(args)  { @@ -3,15 +3,20 @@ const parseClasses  = require("./parseClasses.js").classes;  const parse	= require("./dbparse.js");  // Run this if you want to build the database from text files -async function buildDB(dbconnection, shiftPath, ...classfiles) -{ +async function buildDB(dbconnection, shiftPath, classPath, exceptionPath) {  	let shiftCont = await openFile(shiftPath); -	shiftCont = shiftCont.toString("utf-8").replaceAll("\r", ""); // \r because of the \r\n newline on windows which may create problems +	shiftCont = shiftCont.toString("utf-8"); + +	let exceptions = await openFile(exceptionPath); +	exceptions = exceptions.toString("utf-8"); + +	let classes = await openFile(classPath); +	classes = classes.toString('utf-8'); + +	await parseClasses(dbconnection, classes); +        await parse.build(shiftCont, dbconnection); +        await updateExceptions(exceptions, dbconnection); -	await parseClasses(dbconnection, ...classfiles), -	console.log("Classes updated."); -	await parse.build(shiftCont, dbconnection) -	console.log("Shifts updated.");  	return 0;  } @@ -22,5 +27,5 @@ const openFile = require("./Functions/open.js").file;  const database  = require("./database.js");  const dbcredentials = await openFile("../dblogin.txt");  const DB = new database.Database(JSON.parse(dbcredentials)); -await updateDB.update(dbcredentials, "./shifts.txt", "./Kurssitarjottimet/2016Classes.txt", "./Kurssitarjottimet/NewClasses.txt"); +await updateDB.update(DB, "./shifts.txt", "./Kurssitarjottimet/2016Classes.txt", "./Kurssitarjottimet/NewClasses.txt");  */  | 
