init
This commit is contained in:
commit
2f7c19f28e
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
data/*
|
||||||
|
mlread
|
||||||
|
|
1
data/.gitkeep
Normal file
1
data/.gitkeep
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
9
deno.jsonc
Normal file
9
deno.jsonc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"fmt": {
|
||||||
|
"indentWidth": 4
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"run": "deno run --allow-net --allow-env=PORT --allow-read=./public,./data --allow-write=./public,./data main.ts",
|
||||||
|
"compile": "deno compile --allow-net --allow-env=PORT --allow-read=./public,./data --allow-write=./public,./data --output mlread main.ts"
|
||||||
|
}
|
||||||
|
}
|
80
deno.lock
generated
Normal file
80
deno.lock
generated
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"version": "4",
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@oak/commons@1": "1.0.0",
|
||||||
|
"jsr:@oak/oak@*": "17.1.3",
|
||||||
|
"jsr:@std/assert@1": "1.0.8",
|
||||||
|
"jsr:@std/bytes@1": "1.0.4",
|
||||||
|
"jsr:@std/bytes@^1.0.2": "1.0.4",
|
||||||
|
"jsr:@std/crypto@1": "1.0.3",
|
||||||
|
"jsr:@std/encoding@1": "1.0.5",
|
||||||
|
"jsr:@std/encoding@^1.0.5": "1.0.5",
|
||||||
|
"jsr:@std/http@1": "1.0.10",
|
||||||
|
"jsr:@std/io@0.224": "0.224.9",
|
||||||
|
"jsr:@std/media-types@1": "1.1.0",
|
||||||
|
"jsr:@std/path@1": "1.0.8",
|
||||||
|
"npm:path-to-regexp@6.2.1": "6.2.1"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@oak/commons@1.0.0": {
|
||||||
|
"integrity": "49805b55603c3627a9d6235c0655aa2b6222d3036b3a13ff0380c16368f607ac",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert",
|
||||||
|
"jsr:@std/bytes@1",
|
||||||
|
"jsr:@std/crypto",
|
||||||
|
"jsr:@std/encoding@1",
|
||||||
|
"jsr:@std/http",
|
||||||
|
"jsr:@std/media-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@oak/oak@17.1.3": {
|
||||||
|
"integrity": "d89296c22db91681dd3a2a1e1fd14e258d0d5a9654de55637aee5b661c159f33",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@oak/commons",
|
||||||
|
"jsr:@std/assert",
|
||||||
|
"jsr:@std/bytes@1",
|
||||||
|
"jsr:@std/crypto",
|
||||||
|
"jsr:@std/http",
|
||||||
|
"jsr:@std/io",
|
||||||
|
"jsr:@std/media-types",
|
||||||
|
"jsr:@std/path",
|
||||||
|
"npm:path-to-regexp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/assert@1.0.8": {
|
||||||
|
"integrity": "ebe0bd7eb488ee39686f77003992f389a06c3da1bbd8022184804852b2fa641b"
|
||||||
|
},
|
||||||
|
"@std/bytes@1.0.4": {
|
||||||
|
"integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc"
|
||||||
|
},
|
||||||
|
"@std/crypto@1.0.3": {
|
||||||
|
"integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f"
|
||||||
|
},
|
||||||
|
"@std/encoding@1.0.5": {
|
||||||
|
"integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04"
|
||||||
|
},
|
||||||
|
"@std/http@1.0.10": {
|
||||||
|
"integrity": "4e32d11493ab04e3ef09f104f0cb9beb4228b1d4b47c5469573c2c294c0d3692",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/encoding@^1.0.5"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/io@0.224.9": {
|
||||||
|
"integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes@^1.0.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/media-types@1.1.0": {
|
||||||
|
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
|
||||||
|
},
|
||||||
|
"@std/path@1.0.8": {
|
||||||
|
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"path-to-regexp@6.2.1": {
|
||||||
|
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
main.ts
Normal file
50
main.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import * as oak from "jsr:@oak/oak";
|
||||||
|
|
||||||
|
const port = Number(Deno.env.get("PORT") ?? 8000);
|
||||||
|
|
||||||
|
const router = new oak.Router();
|
||||||
|
|
||||||
|
router.post("/api/log", async (ctx) => {
|
||||||
|
const data = await ctx.request.body.json();
|
||||||
|
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
|
||||||
|
const postLog = await Deno.open("./data/log.txt", {
|
||||||
|
append: true,
|
||||||
|
create: true,
|
||||||
|
});
|
||||||
|
postLog.write(
|
||||||
|
enc.encode(`${new Date().toISOString()}\t${JSON.stringify(data)}\n`),
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.response.body = { ok: true };
|
||||||
|
ctx.respond = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/api/log", async (ctx) => {
|
||||||
|
const postLog = await Deno.readTextFile("./data/log.txt");
|
||||||
|
const entries = postLog
|
||||||
|
.split("\n")
|
||||||
|
.filter((l) => l)
|
||||||
|
.map((entry) => entry.split("\t"))
|
||||||
|
.map(([timestamp, data]) => (console.log(timestamp, data), {
|
||||||
|
timestamp: new Date(timestamp),
|
||||||
|
data: JSON.parse(data),
|
||||||
|
}));
|
||||||
|
ctx.response.body = entries;
|
||||||
|
ctx.respond = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`listening at http://localhost:${port}/`);
|
||||||
|
|
||||||
|
new oak.Application()
|
||||||
|
.use(router.routes())
|
||||||
|
.use(router.allowedMethods())
|
||||||
|
.use(async (ctx) => {
|
||||||
|
const { request: { url: { pathname } } } = ctx;
|
||||||
|
await oak.send(ctx, pathname, {
|
||||||
|
root: "./public",
|
||||||
|
index: "index.html",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.listen({ port });
|
14
mlread.service
Normal file
14
mlread.service
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=mlread webservice
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=simone
|
||||||
|
ExecStart=/home/simone/mlread/mlread
|
||||||
|
WorkingDirectory=/home/simone/mlread/
|
||||||
|
Restart=on-failure
|
||||||
|
Environment="PORT=8300"
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
9
public/deno.jsonc
Normal file
9
public/deno.jsonc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"fmt": {
|
||||||
|
"indentWidth": 4
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"]
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
56
public/index.html
Normal file
56
public/index.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>philopsher's ml reading</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script src="index.js" type="module" defer></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>philopsher's ml reading group</h1>
|
||||||
|
<p>The plan is to read and discuss texts from the <b>ML test</b> materials in the <b>#test-materials</b> channels in FinBol's. (<a href="https://discord.com/channels/384015097284001792/874424650057257022" target="_blank">Link</a>)</p>
|
||||||
|
<h2>The Three Sources and Three Component Parts of Marxism by Vladimir Lenin</h2>
|
||||||
|
<p><b>To be scheduled...</b></p>
|
||||||
|
<p>This text is available both <a href="https://www.marxists.org/archive/lenin/works/1913/mar/x01.htm" target="_blank">as text at MIA</a> and <a href="https://www.youtube.com/watch?v=MOAII71GaFY" target="_blank">as audiobook by Socialism For All</a>.</p>
|
||||||
|
<p>The work can be read in 20 minutes. The plan is to start the event 20 minutes <b>before</b> the announced time, to give people time to read the work, if they haven't already. Then <b>at</b> the announced time, the presentation/discussion will start. I will go through what I think are the main points, and we will use this presentation as foundation for discussion.</p>
|
||||||
|
<p>I may put some notes here afterwards...</p>
|
||||||
|
<h1>Scheduler</h1>
|
||||||
|
<div id="scheduler">
|
||||||
|
<p>
|
||||||
|
Select as many options as possible. I will try and find the
|
||||||
|
best timeslot.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Timestamps are UTC, which is currently <b><span id="time-utc">00:00</span></b>. Compensate according to your own timezone.
|
||||||
|
</p>
|
||||||
|
<form action="#" id="schedule-form">
|
||||||
|
<div id="schedule-table-scroller">
|
||||||
|
<table id="schedule-table">
|
||||||
|
<tr><th>loading...</th></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="tail">
|
||||||
|
<div>
|
||||||
|
<label for="input-username">Discord username:</label>
|
||||||
|
<input type="text" name="username" id="input-username">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="input-note">Note (optional):</label>
|
||||||
|
<textarea name="note" id="input-note" rows="2" cols="30"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="tos">I will jack your shit.</label>
|
||||||
|
<input type="checkbox" name="tos" id="tos">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="submit-div">
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<script></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
108
public/index.js
Normal file
108
public/index.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
const timeUtcSpan = document.querySelector("#time-utc");
|
||||||
|
const scheduleTable = document.querySelector("#schedule-table");
|
||||||
|
const scheduleForm = document.querySelector("#schedule-form");
|
||||||
|
|
||||||
|
timeUtcSpan.innerText = new Date()
|
||||||
|
.toUTCString()
|
||||||
|
.slice(17, 22);
|
||||||
|
// .toLocaleTimeString(["en-UK"], { hour: "2-digit", minute: "2-digit" });
|
||||||
|
|
||||||
|
const weekDayNames = [
|
||||||
|
"Monday",
|
||||||
|
"Tuesday",
|
||||||
|
"Wednesday",
|
||||||
|
"Thursday",
|
||||||
|
"Friday",
|
||||||
|
"Saturday",
|
||||||
|
"Sunday",
|
||||||
|
];
|
||||||
|
|
||||||
|
const monthNames = [
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
];
|
||||||
|
|
||||||
|
async function makeScheduleTable() {
|
||||||
|
const timeslotData = await fetch("timeslots.json")
|
||||||
|
.then((res) => res.json());
|
||||||
|
|
||||||
|
const timeslots = timeslotData
|
||||||
|
.map(({ date, times }) => ({
|
||||||
|
dateString: date,
|
||||||
|
date: new Date(date),
|
||||||
|
times,
|
||||||
|
}))
|
||||||
|
.toSorted((a, b) => a.date.getTime() - b.date.getTime());
|
||||||
|
|
||||||
|
let tableHtml = `<tr>
|
||||||
|
${
|
||||||
|
timeslots.map((ts) =>
|
||||||
|
`<th>${weekDayNames[ts.date.getUTCDay()]} ${ts.date.getUTCDay()}. ${
|
||||||
|
monthNames[ts.date.getUTCMonth()]
|
||||||
|
}</th>`
|
||||||
|
)
|
||||||
|
.join("")
|
||||||
|
}
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
tableHtml += "<tr>";
|
||||||
|
for (const timeslot of timeslots) {
|
||||||
|
tableHtml += "<td>";
|
||||||
|
for (const time of timeslot.times) {
|
||||||
|
const name = `${timeslot.dateString}-${time}`;
|
||||||
|
tableHtml += `
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" name="${name}" id="checkbox-${name}">
|
||||||
|
<label for="checkbox-${name}">${time}</label>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
tableHtml += "</td>";
|
||||||
|
}
|
||||||
|
tableHtml += "</tr>";
|
||||||
|
|
||||||
|
scheduleTable.innerHTML = tableHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleForm.onsubmit = async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
const formdata = new FormData(ev.target);
|
||||||
|
const data = formdata.entries().reduce(
|
||||||
|
(acc, [key, val]) => ({ ...acc, [key]: val }),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
if (!data.username) {
|
||||||
|
alert("please specify username");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data.tos) {
|
||||||
|
alert("please check information agreement");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
const response = await fetch("/api/log", {
|
||||||
|
method: "post",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
alert("submitted!");
|
||||||
|
} else {
|
||||||
|
alert("error occured!");
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
makeScheduleTable();
|
69
public/style.css
Normal file
69
public/style.css
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 1000px;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-top: 1ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td, table th {
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scheduler {
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scheduler form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scheduler form #tail {
|
||||||
|
width: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scheduler #submit-div {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scheduler form input[type="submit"] {
|
||||||
|
width: 30%;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#schedule-table-scroller {
|
||||||
|
width: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#schedule-table {
|
||||||
|
width: max-content;
|
||||||
|
}
|
21
public/timeslots.json
Normal file
21
public/timeslots.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[
|
||||||
|
{ "date": "7-1-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-2-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-3-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-4-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-5-2025", "times": [] },
|
||||||
|
{ "date": "7-6-2025", "times": ["13:00", "15:00", "18:00", "21:00"] },
|
||||||
|
{ "date": "7-7-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-8-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-9-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-10-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-11-2025", "times": [] },
|
||||||
|
{
|
||||||
|
"date": "7-12-2025",
|
||||||
|
"times": ["13:00", "15:00", "18:00", "21:00", "23:00"]
|
||||||
|
},
|
||||||
|
{ "date": "7-13-2025", "times": ["13:00", "15:00", "18:00", "21:00"] },
|
||||||
|
{ "date": "7-14-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-15-2025", "times": ["18:00", "21:00"] },
|
||||||
|
{ "date": "7-16-2025", "times": ["18:00", "21:00"] }
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user