Skip to content

Commit

Permalink
Merge pull request #251 from opensafely-core/cell-check
Browse files Browse the repository at this point in the history
New per-cell annotation format
  • Loading branch information
tomodwyer authored Nov 6, 2023
2 parents 53e35ba + bdc663a commit dc51a37
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 49 deletions.
25 changes: 16 additions & 9 deletions assets/src/scripts/_file-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,27 @@ import "highlight.js/styles/github.css";
import { highlightJsName } from "./_utils";

/**
* @param {Node} el
* @param {string} ext
* @param {string} url
* @param {object} outcome
* @param {Object} params
* @param {HTMLElement} params.element
* @param {string} params.fileExtension
* @param {string} params.fileUrl
* @param {object} params.outcome
* @param {string} params.fileIndex
*/
export async function createTableElement(el, ext, url, outcome) {
const data = await fileLoader(ext, url);

el.classList.add("overflow-x-auto");
export async function createTableElement({
element,
fileExtension,
fileUrl,
outcome,
fileIndex,
}) {
const data = await fileLoader(fileExtension, fileUrl);

tableBuilder({
csvString: data,
el,
el: element,
outcome,
fileIndex,
});
}

Expand Down
13 changes: 7 additions & 6 deletions assets/src/scripts/_output-click.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ export default async function outputClick({ outputName, metadata }) {
`[data-sacro-el="file-preview-template"]`
);

const filesCount = Object.keys(openOutput.value.metadata.files).length;

// only attempt to render outcome cells if there is a single output file
const outcome = filesCount === 1 ? openOutput.value.metadata.outcome : {};

Object.entries(openOutput.value.metadata.files).map(([i, filedata]) => {
const path = filedata.name;
const { url } = filedata;
Expand Down Expand Up @@ -80,7 +75,13 @@ export default async function outputClick({ outputName, metadata }) {
filePreviewLink.setAttribute("href", url);

if (isCsv(ext)) {
createTableElement(filePreviewContent, ext, url, outcome);
createTableElement({
element: filePreviewContent,
fileExtension: ext,
fileUrl: url,
outcome: filedata.cell_index,
fileIndex: i,
});
} else if (isImg(ext)) {
createImageElement(filePreviewContent, url);
} else if (isTxt(ext)) {
Expand Down
88 changes: 54 additions & 34 deletions assets/src/scripts/_table-builder.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import Papa from "papaparse";
import { html } from "./_utils";

const indexOfAll = (arr, val) =>
arr.reduce((acc, el, i) => (el !== val ? [...acc, i] : acc), []);

/**
*
* @param {string} columnName
* @param {object} columnOutcome
* @param {string[]} headings
* @returns
* @param {Object} params
* @param {Object.<string, string[]>} params.outcome
* @param {string} params.tableId
*/
function highlightFailingCells(columnName, columnOutcome, headings) {
const rows = indexOfAll(Object.values(columnOutcome), "ok");
const column = headings.findIndex((val) => val === columnName);
function highlightFailingCells({ outcome, tableId }) {
/** @type {HTMLElement|null} */
const tableBody = document.getElementById(tableId);
if (!tableBody) return;

const colData = rows.map((row) => {
const tableBody = document.getElementById("csvBody");
const tableRow = tableBody.children[row];
const tableCell = tableRow.children[column];
Object.keys(outcome).forEach((index) => {
const x = parseFloat(index.split(",")[0]) + 1;
const y = parseFloat(index.split(",")[1]) + 2;

/** @type {HTMLTableCellElement|null} */
const tableCell = tableBody.querySelector(
`tr:nth-child(${x}) > td:nth-child(${y})`
);
if (!tableCell) return;

tableCell.classList.add(
"bg-red-50",
Expand All @@ -30,28 +31,44 @@ function highlightFailingCells(columnName, columnOutcome, headings) {
"cursor-pointer"
);

const tooltipContent = Object.values(columnOutcome)
[row].split("; ")
.filter((i) => i !== "")
.map((i) => html`<span class="block">${i}</span>`)
/** @type {string} */
const tooltipContent = outcome[index]
.map(
(/** @type {string} */ str) => html`<span class="block">${str}</span>`
)
.join("");

const tooltipTemplateEl = document.getElementById(`tooltip`);
if (!(tooltipTemplateEl instanceof HTMLTemplateElement)) return;
const tooltipEl = tooltipTemplateEl?.content?.firstElementChild;
if (!tooltipEl) return;

const cellTooltip =
tooltipTemplateEl?.content?.firstElementChild.cloneNode(true);

cellTooltip.classList.add("flex", "-bottom-2");
cellTooltip.querySelector(`[data-sacro-el="tooltip-content"]`).innerHTML =
tooltipContent;
const cellTooltip = tooltipEl.cloneNode(true);
tableCell.appendChild(cellTooltip);

return tableCell;
const tooltip = tableCell.querySelector("span:first-child");
if (tooltip) {
tooltip.classList.add("flex", "-bottom-2");

const tooltipContentSpan = tooltip?.querySelector(
`[data-sacro-el="tooltip-content"]`
);
if (tooltipContentSpan) {
tooltipContentSpan.innerHTML = tooltipContent;
}
}
});
return colData;
}

function tableBuilder({ csvString, el, outcome }) {
/**
*
* @param {object} params
* @param {Object.<string, string[]>} params.outcome
* @param {HTMLElement} params.el
* @param {string} params.csvString
* @param {string} params.fileIndex
*/
function tableBuilder({ csvString, el, outcome, fileIndex }) {
const csvToJson = Papa.parse(csvString).data;

const bodyCell = (row) =>
Expand All @@ -64,9 +81,9 @@ function tableBuilder({ csvString, el, outcome }) {
: ``
);

const table = html`
const tableHtmlStr = html`
<table
id="csvTable"
id=${`csvTable${fileIndex}`}
class="min-w-full divide-y divide-gray-300 text-left text-sm text-gray-900"
>
<thead class="font-semibold bg-gray-200">
Expand All @@ -80,11 +97,14 @@ function tableBuilder({ csvString, el, outcome }) {
</table>
`;

el.innerHTML = table; // eslint-disable-line no-param-reassign
if (typeof tableHtmlStr === "string") {
el.innerHTML = tableHtmlStr; // eslint-disable-line no-param-reassign

Object.keys(outcome).map((columnName) =>
highlightFailingCells(columnName, outcome[columnName], csvToJson[0])
);
highlightFailingCells({
outcome,
tableId: `csvTable${fileIndex}`,
});
}
}

export default tableBuilder;

0 comments on commit dc51a37

Please sign in to comment.