diff --git a/README.md b/README.md
index 3510489..f108c47 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# video-privacy-today
-Tell us what videos on this day require no recording and/or no streaming
\ No newline at end of file
+Tell us what events at EMF require our attention. This is a grist plugin
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..42a79df
--- /dev/null
+++ b/index.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+ EMF Video
+
+
+
+
+
+
+
+
+
Next refresh in 60
+
+
+
+
Events Happening Now
+
+
+
+ Name
+ Venue
+ Video Team Needed
+ Privacy
+ Comments
+ Ends In
+
+
+
+
+
+
+
+
+
Events Happening Next
+
+
+
+ Name
+ Venue
+ Video Team Needed
+ Privacy
+ Comments
+ Starts In
+
+
+
+
+
+
+
+
diff --git a/main.py b/main.py
deleted file mode 100644
index f8138c1..0000000
--- a/main.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from datetime import datetime
-
-import urllib.request
-import json
-
-def extract_day(datetime_str):
- dt = datetime.strptime(datetime_str.split()[0], '%Y-%m-%d')
- return(dt.date())
-
-all_stuff = None
-with urllib.request.urlopen('https://www.emfcamp.org/schedule/2024.json') as url:
- all_stuff = json.load(url)
-
-# All things happening today
-today = datetime.today().date()
-all_today = []
-for thing in all_stuff:
- day = extract_day(thing['start_date'])
-
- if day == today:
- all_today.append(thing)
-
-# Only stages
-all_stages = []
-for thing in all_today:
- if 'Stage' in thing['venue']:
- all_stages.append(thing)
-
-# Everything on stages today that's not public
-not_public = []
-for talk in all_stages:
- if talk['video_privacy'] != 'public':
- not_public.append(talk)
-
-# Ok, print it
-print('TITLE', 'START_TIME', 'END_TIME', 'VENUE', 'PRIVACY', sep=' | ')
-print('-'*50)
-not_public = sorted(not_public, key = lambda x: x['start_date'])
-for e in not_public:
- print('\n')
- start_time = e['start_date'].split()[1]
- end_time = e['end_date'].split()[1]
- privacy = 'REVIEW' if e['video_privacy'] == 'review' else 'PRIVATE'
- print(e['title'], start_time, end_time, e['venue'], privacy, sep=' | ')
diff --git a/plugin-api.js b/plugin-api.js
new file mode 100644
index 0000000..db2238a
--- /dev/null
+++ b/plugin-api.js
@@ -0,0 +1,193 @@
+const MAX_COUNTDOWN = 60;
+
+var countdown = MAX_COUNTDOWN;
+var now = Date.now();
+var eventsOnNow = [];
+var eventsOnNext = [];
+
+grist.ready({
+ requiredAccess: 'read table'
+ });
+
+grist.onRecords(function (records) {
+ eventsOnNow = [];
+ eventsOnNext = [];
+ now = Date.now();
+
+ var i = 0;
+
+ console.info(records);
+
+ for (const r of records) {
+ let privacy = r["video_privacy"];
+ let videoNeeded = r["Video_team_attending"];
+
+ if (privacy == "none" || videoNeeded == true) {
+ let e = new Event(
+ i++,
+ r["title"],
+ r["venue"],
+ videoNeeded,
+ privacy,
+ r["Comments"],
+ Date.parse(r["start_date"]),
+ Date.parse(r["end_date"])
+ );
+
+ if (e.hasStarted() && !e.hasEnded()) {
+ eventsOnNow.push(e);
+ } else if (!e.hasStarted() && !e.hasEnded()) {
+ eventsOnNext.push(e);
+ }
+ }
+ }
+
+ eventsOnNow.sort((e1, e2) => {
+ return (e1.timeUntilEnd() > e2.timeUntilEnd()) ? 1 : ((e2.timeUntilEnd() > e1.timeUntilEnd()) ? -1 : 0)
+ });
+
+ eventsOnNext.sort((e1, e2) => {
+ return (e1.timeUntilStart() > e2.timeUntilStart()) ? 1 : ((e2.timeUntilStart() > e1.timeUntilStart()) ? -1 : 0)
+ });
+
+ console.log(eventsOnNext);
+
+ populateTable("now", eventsOnNow);
+ populateTable("next", eventsOnNext);
+
+ window.setInterval(() => handleCountdown(), 1000);
+});
+
+
+grist.onOptions(function(options, interaction) {
+ console.info("Options reloaded");
+});
+
+class Event {
+ constructor(id, name, venue, videoNeeded, privacy, comments, startTime, endTime) {
+ this.id = id;
+ this.name = name;
+ this.venue = venue;
+ this.videoNeeded = videoNeeded;
+ this.privacy = privacy;
+ this.comments = comments;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ hasStarted() {
+ return this.startTime < now;
+ }
+
+ hasEnded() {
+ return this.endTime < now;
+ }
+
+ timeUntilStart() {
+ return new Date(this.startTime - now);
+ }
+
+ timeUntilEnd() {
+ return new Date(this.endTime - now);
+ }
+}
+
+function makeTextBold(text) {
+ return "" + text + " ";
+}
+
+function convertTimeToString(time) {
+ // Using getHours() is bullshit for this usage, it won't include any days in the hours
+ let hours = Math.floor(time / (1000 * 60 * 60));
+ if (hours < 10) {
+ hours = "0" + hours
+ }
+
+ let minutes = time.getMinutes();
+ if (minutes < 10) {
+ minutes = "0" + minutes
+ }
+ return hours + ":" + minutes;
+}
+
+function populateTable(elementName, items) {
+ for (const i of items) {
+ const tableRow = document.createElement("tr");
+
+ tableRow.setAttribute("id", i.id);
+
+ var name = document.createElement("td");
+ var venue = document.createElement("td");
+ var videoNeeded = document.createElement("td");
+ var privacy = document.createElement("td");
+ var comments = document.createElement("td");
+ var startsIn = document.createElement("td");
+
+ startsIn.setAttribute("id", i.id + "_time");
+
+ name.innerHTML = i.name;
+ venue.innerHTML = i.venue;
+ videoNeeded.innerHTML = i.videoNeeded;
+ privacy.innerHTML = i.privacy;
+ comments.innerHTML = i.comments;
+ startsIn.innerHTML = convertTimeToString(i.timeUntilStart());
+
+ tableRow.appendChild(name);
+ tableRow.appendChild(venue);
+ tableRow.appendChild(videoNeeded);
+ tableRow.appendChild(privacy);
+ tableRow.appendChild(comments);
+ tableRow.appendChild(startsIn);
+
+ // Colour in any rows
+ if (i.privacy != "public") {
+ privacy.innerHTML = makeTextBold(privacy.innerHTML);
+
+ if (i.privacy == "none") {
+ tableRow.setAttribute("class", "table-danger");
+ } else if (i.privacy == "review") {
+ tableRow.setAttribute("class", "table-warning");
+ }
+ }
+
+ if (i.comments != "") {
+ comments.innerHTML = makeTextBold(comments.innerHTML);
+ tableRow.setAttribute("class", "table-danger");
+ }
+
+ if (i.videoNeeded == true) {
+ videoNeeded.innerHTML = makeTextBold(videoNeeded.innerHTML);
+ tableRow.setAttribute("class", "table-danger");
+ }
+
+ document.getElementById(elementName).getElementsByTagName("tbody")[0].append(tableRow);
+ }
+}
+
+function refreshTables() {
+ var toDelete = [];
+
+ now = Date.now();
+ for (const nowItem of eventsOnNow) {
+ let timeUntilEnd = nowItem.timeUntilEnd();
+
+ let timeField = document.getElementById(nowItem.id + "_time");
+ timeField.innerHTML = convertTimeToString(nowItem.timeUntilEnd());
+ }
+
+ for (const nextItem of eventsOnNext) {
+ let timeField = document.getElementById(nextItem.id + "_time");
+ timeField.innerHTML = convertTimeToString(nextItem.timeUntilEnd());
+ }
+}
+
+function handleCountdown() {
+ countdown--;
+ if (countdown == 0) {
+ countdown = MAX_COUNTDOWN;
+ refreshTables();
+ }
+
+ let counterElement = document.getElementById("refresh_counter_text");
+ counterElement.innerHTML = "Next refresh in " + countdown;
+}