function redraw()

in prow/cmd/deck/static/prow/prow.ts [430:737]


function redraw(fz: FuzzySearch, pushState: boolean = true): void {
    const rerunStatus = getParameterByName("rerun");
    const modal = document.getElementById('rerun')!;
    const rerunCommand = document.getElementById('rerun-content')!;
    const builds = document.getElementById("builds")!.getElementsByTagName(
        "tbody")[0];
    while (builds.firstChild) {
        builds.removeChild(builds.firstChild);
    }

    const args: string[] = [];

    function getSelection(name: string): string {
        const sel = selectionText(document.getElementById(name) as HTMLSelectElement);
        if (sel && name !== 'repo' && !opts[name + 's' as keyof RepoOptions][sel]) {
            return "";
        }
        if (sel !== "") {
            args.push(`${name}=${encodeURIComponent(sel)}`);
        }
        return sel;
    }

    function getSelectionFuzzySearch(id: string, inputId: string): RegExp {
        const input = document.getElementById(inputId) as HTMLInputElement;
        const inputText = input.value;
        if (inputText === "") {
            return new RegExp('');
        }
        if (inputText !== "") {
            args.push(`${id}=${encodeURIComponent(inputText)}`);
        }
        if (inputText !== "" && opts[id + 's' as keyof RepoOptions][inputText]) {
            return new RegExp(`^${escapeRegexLiteral(inputText)}$`);
        }
        const expr = inputText.split('*').map(escapeRegexLiteral);
        return new RegExp(`^${expr.join('.*')}$`);
    }

    const repoSel = getSelection("repo");
    const opts = optionsForRepo(repoSel);

    const typeSel = getSelection("type") as ProwJobType;
    const pullSel = getSelection("pull");
    const authorSel = getSelection("author");
    const jobSel = getSelectionFuzzySearch("job", "job-input");
    const stateSel = getSelection("state");
    const clusterSel = getSelection("cluster");

    if (pushState && window.history && window.history.pushState !== undefined) {
        if (args.length > 0) {
            history.pushState(null, "", "/?" + args.join('&'));
        } else {
            history.pushState(null, "", "/");
        }
    }
    fz.setDict(Object.keys(opts.jobs));
    redrawOptions(fz, opts);

    let lastKey = '';
    const jobCountMap = new Map() as Map<ProwJobState, number>;
    const jobInterval: Array<[number, number]> = [[3600 * 3, 0], [3600 * 12, 0], [3600 * 48, 0]];
    let currentInterval = 0;
    const jobHistogram = new JobHistogram();
    const now = Date.now() / 1000;
    let totalJob = 0;
    let displayedJob = 0;

    for (let i = 0; i < allBuilds.items.length; i++) {
        const build = allBuilds.items[i];
        const {
            metadata: {
                name: prowJobName = "",
            },
            spec: {
                cluster = "",
                type = "",
                job = "",
                agent = "",
                refs: {repo_link = "", base_sha = "", base_link = "", pulls = [], base_ref = ""} = {},
                pod_spec,
            },
            status: {startTime, completionTime = "", state = "", pod_name, build_id = "", url = ""},
        } = build;

        let buildUrl = url;
        if (url.includes('/view/')) {
            buildUrl = `${window.location.origin}/${url.slice(url.indexOf('/view/') + 1)}`;
        }

        let org = "";
        let repo = "";
        if (build.spec.refs !== undefined) {
            org = build.spec.refs.org;
            repo = build.spec.refs.repo;
        } else if (build.spec.extra_refs !== undefined && build.spec.extra_refs.length > 0 ) {
            org = build.spec.extra_refs[0].org;
            repo = build.spec.extra_refs[0].repo;
        }

        if (!equalSelected(typeSel, type)) {
            continue;
        }
        if (!equalSelected(repoSel, `${org}/${repo}`)) {
            continue;
        }
        if (!equalSelected(stateSel, state)) {
            continue;
        }
        if (!equalSelected(clusterSel, cluster)) {
            continue;
        }
        if (!jobSel.test(job)) {
            continue;
        }

        if (pullSel) {
            if (!pulls.length) {
                continue;
            }

            if (!pulls.some((pull: Pull): boolean => {
                const {number: prNumber} = pull;
                return equalSelected(pullSel, prNumber.toString());
            })) {
                continue;
            }
        }

        if (authorSel) {
            if (!pulls.length) {
                continue;
            }

            if (!pulls.some((pull: Pull): boolean => {
                const {author} = pull;
                return equalSelected(authorSel, author);
            })) {
                continue;
            }
        }

        totalJob++;
        jobCountMap.set(state, (jobCountMap.get(state) || 0) + 1);

        // accumulate a count of the percentage of successful jobs over each interval
        const started = Date.parse(startTime) / 1000;
        const finished = Date.parse(completionTime) / 1000;
        // const finished = completionTime ? Date.parse(completionTime): now;

        const durationSec = completionTime ? finished - started : 0;
        const durationStr = completionTime ? formatDuration(durationSec) : "";

        if (currentInterval >= 0 && (now - started) > jobInterval[currentInterval][0]) {
            const successCount = jobCountMap.get("success") || 0;
            const failureCount = jobCountMap.get("failure") || 0;

            const total = successCount + failureCount;
            if (total > 0) {
                jobInterval[currentInterval][1] = successCount / total;
            } else {
                jobInterval[currentInterval][1] = 0;
            }
            currentInterval++;
            if (currentInterval >= jobInterval.length) {
                currentInterval = -1;
            }
        }

        if (displayedJob >= 500) {
            jobHistogram.add(new JobSample(started, durationSec, state, -1));
            continue;
        } else {
            jobHistogram.add(new JobSample(started, durationSec, state, builds.childElementCount));
        }
        displayedJob++;
        const r = document.createElement("tr");
        r.appendChild(cell.state(state));
        if ((agent === "kubernetes" && pod_name) || agent !== "kubernetes") {
            const logIcon = icon.create("description", "Build log");
            if (pod_spec == null || pod_spec.containers.length <= 1) {
                logIcon.href = `log?job=${job}&id=${build_id}`;
            } else {
                // this logic exists for legacy jobs that are configured for gubernator compatibility
                const buildIndex = buildUrl.indexOf('/build/');
                if (buildIndex !== -1) {
                    const gcsUrl = `${window.location.origin}/view/gcs/${buildUrl.substring(buildIndex + '/build/'.length)}`;
                    logIcon.href = gcsUrl;
                } else if (buildUrl.includes('/view/')) {
                    logIcon.href = buildUrl;
                } else {
                    logIcon.href = `log?job=${job}&id=${build_id}`;
                }
            }
            const c = document.createElement("td");
            c.classList.add("icon-cell");
            c.appendChild(logIcon);
            r.appendChild(c);
        } else {
            r.appendChild(cell.text(""));
        }
        r.appendChild(createRerunCell(modal, rerunCommand, prowJobName));
        r.appendChild(createViewJobCell(prowJobName));
        const key = groupKey(build);
        if (key !== lastKey) {
            // This is a different PR or commit than the previous row.
            lastKey = key;
            r.className = "changed";

            if (type === "periodic") {
                r.appendChild(cell.text(""));
            } else {
                let repoLink = repo_link;
                if (!repoLink) {
                    repoLink = `/github-link?dest=${org}/${repo}`;
                }
                r.appendChild(cell.link(`${org}/${repo}`, repoLink));
            }
            if (type === "presubmit") {
                if (pulls.length) {
                    r.appendChild(cell.prRevision(`${org}/${repo}`, pulls[0]));
                } else {
                    r.appendChild(cell.text(""));
                }
            } else if (type === "batch") {
                r.appendChild(batchRevisionCell(build));
            } else if (type === "postsubmit") {
                r.appendChild(cell.commitRevision(`${org}/${repo}`, base_ref, base_sha, base_link));
            } else if (type === "periodic") {
                r.appendChild(cell.text(""));
            }
        } else {
            // Don't render identical cells for the same PR/commit.
            r.appendChild(cell.text(""));
            r.appendChild(cell.text(""));
        }
        if (spyglass) {
            // this logic exists for legacy jobs that are configured for gubernator compatibility
            const buildIndex = buildUrl.indexOf('/build/');
            if (buildIndex !== -1) {
                const gcsUrl = `${window.location.origin}/view/gcs/${buildUrl.substring(buildIndex + '/build/'.length)}`;
                r.appendChild(createSpyglassCell(gcsUrl));
            } else if (buildUrl.includes('/view/')) {
                r.appendChild(createSpyglassCell(buildUrl));
            } else {
                r.appendChild(cell.text(''));
            }
        } else {
            r.appendChild(cell.text(''));
        }
        if (buildUrl === "") {
            r.appendChild(cell.text(job));
        } else {
            r.appendChild(cell.link(job, buildUrl));
        }

        r.appendChild(cell.time(i.toString(), moment.unix(started)));
        r.appendChild(cell.text(durationStr));
        builds.appendChild(r);
    }

    // fill out the remaining intervals if necessary
    if (currentInterval !== -1) {
        let successCount = jobCountMap.get("success");
        if (!successCount) {
            successCount = 0;
        }
        let failureCount = jobCountMap.get("failure");
        if (!failureCount) {
            failureCount = 0;
        }
        const total = successCount + failureCount;
        for (let i = currentInterval; i < jobInterval.length; i++) {
            if (total > 0) {
                jobInterval[i][1] = successCount / total;
            } else {
                jobInterval[i][1] = 0;
            }
        }
    }

    const jobSummary = document.getElementById("job-histogram-summary")!;
    const success = jobInterval.map((interval) => {
        if (interval[1] < 0.5) {
            return `${formatDuration(interval[0])}: <span class="state failure">${Math.ceil(interval[1] * 100)}%</span>`;
        }
        return `${formatDuration(interval[0])}: <span class="state success">${Math.ceil(interval[1] * 100)}%</span>`;
    }).join(", ");
    jobSummary.innerHTML = `Success rate over time: ${success}`;
    const jobCount = document.getElementById("job-count")!;
    jobCount.textContent = `Showing ${displayedJob}/${totalJob} jobs`;
    drawJobBar(totalJob, jobCountMap);

    // if we aren't filtering the output, cap the histogram y axis to 2 hours because it
    // contains the bulk of our jobs
    let max = Number.MAX_SAFE_INTEGER;
    if (totalJob === allBuilds.items.length) {
        max = 2 * 3600;
    }
    drawJobHistogram(totalJob, jobHistogram, now - (12 * 3600), now, max);
    if (rerunStatus === "gh_redirect") {
        modal.style.display = "block";
        rerunCommand.innerHTML = "Rerunning that job requires GitHub login. Now that you're logged in, try again";
    }
    // we need to upgrade DOM for new created dynamic elements
    // see https://getmdl.io/started/index.html#dynamic
    componentHandler.upgradeDom();
}