body: summarize_job()

in src/BuildHistoryDisplay.js [463:618]


              body: summarize_job(key),
            });
          }
        });
      }
      consecutive_failure_count.forEach((v, key) => {
        // Don't produce notifications for initial failure!
        if (
          this.state.consecutive_failure_count &&
          !this.state.consecutive_failure_count.has(key)
        ) {
          // It's failed!
          new Notification("❌ " + this.props.job, {
            body: summarize_job(key),
          });
        }
      });
    }

    // TODO: This can cause spurious state updates
    this.setState(data);
  }

  render() {
    function result_icon(result) {
      if (is_success(result))
        return (
          <span role="img" style={{ color: "green" }} aria-label="passed">
            0
          </span>
        );
      if (is_failure(result))
        return (
          <span role="img" style={{ color: "red" }} aria-label="failed">
            X
          </span>
        );
      if (is_aborted(result))
        return (
          <span role="img" style={{ color: "gray" }} aria-label="cancelled">
            .
          </span>
        );
      if (is_pending(result))
        return (
          <span
            className="animate-flicker"
            role="img"
            style={{ color: "goldenrod" }}
            aria-label="in progress"
          >
            ?
          </span>
        );
      return result;
    }

    // Sigh... the place where you can get the information you're
    // interested in at the top level is NOT the same as where you get
    // it inside, because of how Jenkins handles depth (Jenkins
    // *will* give you information for everything recursively, just
    // not in the place you might expect it.
    //
    //  class: "com.tikal.jenkins.plugins.multijob.MultiJobBuild"
    //  id: "3772"
    //  subBuilds:
    //    0:
    //      jobName: "whatever"
    //      build:
    //        class: "com.tikal.jenkins.plugins.multijob.MultiJobBuild"
    //        subBuilds:

    let builds = this.state.builds;
    let consecutive_failure_count = this.state.consecutive_failure_count;

    const known_jobs = this.state.known_jobs;
    const known_jobs_head = known_jobs.map((jobName) => (
      <th className="rotate" key={jobName}>
        <div
          className={
            (jobs_on_pr.some((e) => summarize_job(jobName).startsWith(e))
              ? "pr-header"
              : "master-only-header") +
            " " +
            (consecutive_failure_count.has(jobName) ? "failing-header" : "")
          }
        >
          {jobs_on_pr.some((e) => summarize_job(jobName).startsWith(e))
            ? ""
            : "• "}
          {summarize_job(jobName)}
        </div>
      </th>
    ));
    // const known_jobs_head = known_jobs.map((jobName) =>
    //  <th key={jobName}></th>
    //);

    const durationWidth = 100;
    const durationHeight = 10;
    const durationScale = d3.scaleLinear().rangeRound([0, durationWidth]);
    durationScale.domain([0, d3.max(builds, (b) => b.duration)]);

    const seen_prs = new Set();

    const rows = builds.map((build) => {
      const sb_map = build.sb_map;

      function perf_report(sb, result) {
        return (
          <Fragment>
            <span
              className={
                is_success(result) ? "ok-duration" : "suspect-duration"
              }
            >
              {parse_duration(sb.duration) / 1000}
            </span>
            &nbsp;&nbsp;
          </Fragment>
        );
      }

      // let cumulativeMs = 0;
      let cost = 0;
      let unknownCost = false;
      let inProgressCost = false;
      let found = false;

      const status_cols = known_jobs.map((jobName) => {
        const sb = sb_map.get(jobName);
        let cell = <Fragment />;
        if (sb !== undefined) {
          found = true;
          const dur = parse_duration(sb.duration);
          // cumulativeMs += dur;
          const node = classify_job_to_node(getJobName(sb));
          let this_cost = 0;
          if (node === "unknown") {
            unknownCost = true;
          } else {
            this_cost = Math.ceil((centsPerHour[node] * dur) / 1000 / 60 / 60);
          }
          cost += this_cost;
          if (!sb.result) inProgressCost = true;
          if (this.props.mode === "perf") {
            cell = perf_report(sb, sb.result);
          } else if (this.props.mode === "cost") {
            cell = (
              <Fragment>
                {node === "unknown" ? "?" : this_cost}&nbsp;&nbsp;
              </Fragment>
            );
          } else {
            var cellHref = sb.url;
            if (/^https?:\/\//.test(cellHref)) {