Switch to grist plugin
This commit is contained in:
parent
2896ea5d49
commit
61f9d04750
@ -1,3 +1,3 @@
|
|||||||
# video-privacy-today
|
# video-privacy-today
|
||||||
|
|
||||||
Tell us what videos on this day require no recording and/or no streaming
|
Tell us what events at EMF require our attention. This is a grist plugin
|
||||||
|
|||||||
55
index.html
Normal file
55
index.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>EMF Video</title>
|
||||||
|
<script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container bg-primary text-white padding-3" id="counter">
|
||||||
|
<h2 id="refresh_counter_text">Next refresh in 60</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container" id="happeningNow">
|
||||||
|
<h2>Events Happening Now</h2>
|
||||||
|
<table class="table table-bordered" id="now">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Venue</th>
|
||||||
|
<th scope="col">Video Team Needed</th>
|
||||||
|
<th scope="col">Privacy</th>
|
||||||
|
<th scope="col">Comments</th>
|
||||||
|
<th scope="col">Ends In</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container" id="happeningNext">
|
||||||
|
<h2>Events Happening Next</h2>
|
||||||
|
<table class="table table-bordered" id="next">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Venue</th>
|
||||||
|
<th scope="col">Video Team Needed</th>
|
||||||
|
<th scope="col">Privacy</th>
|
||||||
|
<th scope="col">Comments</th>
|
||||||
|
<th scope="col">Starts In</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
44
main.py
44
main.py
@ -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=' | ')
|
|
||||||
193
plugin-api.js
Normal file
193
plugin-api.js
Normal file
@ -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 "<b>" + text + "</b>";
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user