const TEST_DATA = [ { province: "Alberta", region: "Calgary", hospital: "Test Hospital Name Building", timestamp: "0:00", }, { province: "Alberta", region: "Calgary", hospital: "Test Hospital Name Building", timestamp: "4:00", }, { province: "Alberta", region: "Calgary", hospital: "Test Hospital Name Building", timestamp: "2:00", }, { province: "Alberta", region: "Calgary", hospital: "Children's Test Hospital", timestamp: "6:00", }, { province: "Alberta", region: "Calgary", hospital: "Children's Test Hospital", timestamp: "8", }, { province: "Manitoba", region: "Winnipeg", hospital: "Children's Test Hospital", timestamp: "5:15 - 5:45", }, { province: "Alberta", region: "Calgary", hospital: "Children's Test Hospital", timestamp: "12.0", }, { province: "Alberta", region: "Edmonton", hospital: "Children's Test Hospital", timestamp: "6:00", }, { province: "Alberta", region: "Edmonton", hospital: "Children's Test Hospital", timestamp: "8", }, ]; function parseData(raw) { const data = {}; raw.forEach(entry => { const { province, region, hospital, timestamp } = entry; const wait = entry["wait-time"]; if (!data[province]) { data[province] = { province: province, regions: {} }; } const dataProvince = data[province]; if (!dataProvince.regions[region]) { dataProvince.regions[region] = { region: region, hospitals: {} }; } const dataRegion = dataProvince.regions[region]; if (!dataRegion.hospitals[hospital]) { dataRegion.hospitals[hospital] = { hospital: hospital, times: [] }; } const dataHospital = dataRegion.hospitals[hospital]; dataHospital.times.push(parseTime(wait)); }); return data; } function colonTimeToDecimal(string) { const [hours, minutes] = string.split(":").map(str => parseFloat(str)); const decimalHours = hours + minutes / 60; return decimalHours; } function stringTimeToNumber(string) { if (string.includes(":")) { return colonTimeToDecimal(string); } else { return parseFloat(string); } } function rangeTimeToDecimal(string) { const range = string.split("-").map(str => str.trim()); const [start, end] = range.map(time => stringTimeToNumber(time)); const decimalHours = (start + end) / 2; // Return average for now; return decimalHours; } function parseTime(timestamp) { if (timestamp.includes("-")) { return rangeTimeToDecimal(timestamp); } else { return stringTimeToNumber(timestamp); } } function createEmbeds(data) { const embeds = document.querySelectorAll(".wait-time-chart"); embeds.forEach(embed => { const { province, title, notes, url } = embed.dataset; const regions = embed.dataset.regions.split("+").map(d => d.trim()); // const dateRange = embed.dataset.dateRange.split(",").map(d => d.trim()); const dataProvince = data[province]; const container = d3.select(embed); const provinceTitle = container.append("h2").text(title); regions.forEach(region => createRegion(container, region, dataProvince)); const link = container .append("a") .attr("href", url) .attr("target", "_blank") .text("Visit site"); const notesDiv = container.append("div").attr("class", "notes").text(notes); }); } function createRegion(container, region, dataProvince) { if (dataProvince.regions[region]) { const hospitals = dataProvince.regions[region].hospitals; const regionTitle = container .append("h3") .attr("class", "region-title") .text(region); const regionContainer = container .append("div") .attr("class", "region-container"); regionContainer.append("div").attr("class", "header").text("Hospital"); regionContainer .append("div") .attr("class", "header") .text("Recorded wait time (hours)"); regionContainer.append("div"); const chartGrid = regionContainer.append("div").attr("class", "chart-grid"); const hours = [0, 2, 4, 6, 8, 10, 12]; const hourContainer = chartGrid .selectAll("div.hour-container") .data(hours) .join("div") .attr("class", "hour-container"); hourContainer .append("div") .attr("class", "grid-hour") .text(d => d); hourContainer.append("div").attr("class", "grid-line"); for (let hospital in hospitals) { const name = hospitals[hospital].hospital; const data = hospitals[hospital].times; const nameDiv = regionContainer .append("div") .attr("class", "hospital-name") .text(name); const chartDiv = regionContainer .append("div") .attr("class", "hospital-chart"); drawLineChart(chartDiv, data); } function resizeGridHeight() { const gridHeight = regionContainer.node().getBoundingClientRect().height; hourContainer.style("height", gridHeight + "px"); } resizeGridHeight(); window.addEventListener("resize", () => { resizeGridHeight(); }); } } function drawLineChart(container, data) { const svg = container.append("svg").attr("width", "100%").attr("height", 14); const { width, height } = svg.node().getBoundingClientRect(); const radius = 7; const x = d3.scaleLinear([0, 12], [radius, width - radius]); svg .append("line") // .attr("x1", x(0)) // .attr("y1", radius) // .attr("x2", width - radius) // .attr("y2", radius) .attr("x1", 0) .attr("y1", radius) .attr("x2", "100%") .attr("y2", radius) .style("stroke-width", radius) .style("stroke", "rgb(210,210,210,0.5)") .style("stroke-linecap", "round"); svg .selectAll("circle") .data(data) .join("circle") // .attr("cx", d => x(d)) .attr("cx", d => (100 * d) / 12 + "%") .attr("cy", d => radius) .attr("r", radius) .style("fill", "rgba(78, 156, 212, 0.4)") .style("mix-blend-mode", "multiply"); } const dataURL = "/content/dam/common/exceltojson/er-wait-times.txt"; fetch(dataURL) .then(raw => raw.json()) .then(json => { const data = json.filter( d => !["", "null"].includes(d["wait-time"].trim()) ); createEmbeds(parseData(data)); });