recordings = (await FileAttachment("recordings.json").json())
.map((d, i) => ({...d, order: i, month: d.date.slice(0, 7)}))
.sort((a, b) => b.date.localeCompare(a.date) || a.order - b.order)
// "2024-03" -> "Mar 2024"
function monthLabel(month) {
const [year, m] = month.split("-");
return new Date(year, m - 1, 1).toLocaleString("en-GB", {month: "short", year: "numeric"});
}
// "95" minutes -> "1 hr 35 min"
function minutesLabel(minutes) {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return hours ? `${hours} hr${mins ? ` ${mins} min` : ""}` : `${mins} min`;
}
// A slider with two handles, for choosing a range from a list of values.
// Its value is [first value, last value], e.g. ["2024-01", "2026-10"].
function rangeSlider(values, {label, format}) {
const max = values.length - 1;
const from = html`<input type="range" min="0" max="${max}" step="1" value="0" aria-label="${label} from">`;
const to = html`<input type="range" min="0" max="${max}" step="1" value="${max}" aria-label="${label} to">`;
const output = html`<span class="recordings-range-output"></span>`;
const slider = html`<div class="recordings-range-slider">${from}${to}</div>`;
const form = html`<div class="recordings-range"><label>${label}</label>${slider}${output}</div>`;
const update = event => {
// Stop the handles crossing over
if (+from.value > +to.value) {
if (event?.target === from) from.value = to.value;
else to.value = from.value;
}
output.textContent = `${format(values[from.value])} – ${format(values[to.value])}`;
// Highlight the track between the handles
slider.style.setProperty("--from", `${100 * from.value / max}%`);
slider.style.setProperty("--to", `${100 * to.value / max}%`);
};
from.addEventListener("input", update);
to.addEventListener("input", update);
Object.defineProperty(form, "value", {get: () => [values[from.value], values[to.value]]});
update();
return form;
}
// Every month from the earliest to the latest recording
allMonths = {
const [first, last] = d3.extent(recordings, d => d.month);
const months = [];
for (let date = new Date(first + "-01"); date <= new Date(last + "-01"); date.setUTCMonth(date.getUTCMonth() + 1)) {
months.push(date.toISOString().slice(0, 7));
}
return months;
}
// Every 5 minutes from 0 to the longest recording
allDurations = {
const longest = Math.ceil(d3.max(recordings, d => d.seconds) / 300) * 5;
return d3.range(0, longest + 5, 5);
}
entryLinks = new Map(recordings.map(d => [d.collection, d.collection_url]))
function shorten(text, length = 160) {
return text.length > length ? text.slice(0, length).replace(/\s+\S*$/, "") + "…" : text;
}
// Truncated text with a toggle to show the whole description
function expandable(text) {
const short = shorten(text);
if (short === text) return text;
const content = html`<span>${short}</span>`;
const toggle = html`<button type="button" class="recordings-see-more">See more</button>`;
let expanded = false;
toggle.onclick = event => {
event.stopPropagation();
expanded = !expanded;
content.textContent = expanded ? text : short;
toggle.textContent = expanded ? "See less" : "See more";
};
return html`<span>${content} ${toggle}</span>`;
}
// A dropdown of checkboxes, for choosing any number of values
function multiselect(values, {label, all}) {
const checkboxes = Inputs.checkbox(values, {format: String});
const summary = html`<summary></summary>`;
const dropdown = html`<details>${summary}<div class="recordings-multiselect-options">${checkboxes}</div></details>`;
const form = html`<div class="recordings-multiselect"><label>${label}</label>${dropdown}</div>`;
const update = () => {
summary.textContent = checkboxes.value.length ? checkboxes.value.join(", ") : all;
};
checkboxes.addEventListener("input", update);
// Close when clicking elsewhere on the page
document.addEventListener("click", event => {
if (!dropdown.contains(event.target)) dropdown.open = false;
});
Object.defineProperty(form, "value", {get: () => checkboxes.value});
// Show only the given options, unticking any hidden ones
form.showOnly = shown => {
let changed = false;
for (const box of checkboxes.querySelectorAll("input")) {
const visible = shown.has(values[box.value]);
box.parentElement.style.display = visible ? "" : "none";
if (!visible && box.checked) {
box.checked = false;
changed = true;
}
}
if (changed) checkboxes.dispatchEvent(new Event("input", {bubbles: true}));
};
update();
return form;
}
// Smaller text, for the table's shorter columns
function small(content) {
return html`<span class="recordings-small">${content}</span>`;
}
// Unique values, sorted
function options(values) {
return Array.from(new Set(values)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
}Search hundreds of recorded conference talks, webinars, workshops, network huddles, lectures and project showcases from across the health and care analytics community, all in one place.
Where a long recording covers several talks and its YouTube description gives the timings, each talk is listed separately and its link starts the video at that talk. Each recording also links to its Atlas entry, where you can find out more about the community that published it.
Dates are when each recording was published on YouTube. This is usually within a few weeks of the event, but some older recordings were uploaded later - the Atlas entry for each collection gives more details.
Durations are the length of each video, or for a talk within a longer recording, the time until the next timestamp in its description. A few talks are listed without timestamps, so their length isn’t known - these are only shown when the duration slider covers its full range.
Sources
The Recordings Finder brings together the recordings listed in these Atlas entries:
- NHS-OA Community: Conference Recordings
- NHS-OA Community: Webinar and Talk Recordings
- NHS-OA Community: Workshop Recordings
- NHS-OA Community: NHS-R Podcast
- AphA: Webinar and Conference Recordings
- National Analyst Network (formerly Midlands Analyst Network): Huddle Recordings
- Midlands Decision Support Network: Training Recordings
- Midlands Decision Support Network: Insight Festival Recordings
- The Strategy Unit: Webinar Recordings
- New Hospital Programme Demand Model (training recordings)
- HSMA: Project Showcase Recordings
- HSMA: Lecture and Masterclass Recordings
- HACA - Health and Care Analytics Conference
- SWAIH - South West Analytics and Infrastructure in Healthcare
Know of another collection of recordings that should be included? Raise an issue on the repository to let us know.