<commit_before>import React from "react";
import cx from "classnames";
import { find, maxBy, orderBy, sumBy } from "lodash/fp";
import moment from "moment";

import { logAnalyticsEvent } from "~/api/analytics";
import PropTypes from "~/components/utils/propTypes";
import { Accordion } from "~/components/layout";
import BasicPopup from "~/components/BasicPopup";

import cs from "./discovery_sidebar.scss";

export default class DiscoverySidebar extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      stats: {
        numSamples: "",
        numProjects: "",
        avgTotalReads: "",
        avgAdjustedRemainingReads: ""
      },
      metadata: {
        host: [],
        tissue: [],
        time: [],
        location: []
      },
      expandedMetadataGroups: new Set()
    };
  }

  static getDerivedStateFromProps(newProps, prevState) {
    const {
      currentTab,
      loading,
      projectDimensions,
      projects,
      sampleDimensions,
      sampleStats
    } = newProps;
    if (loading) return prevState;
    const dimensions =
      currentTab === "projects" ? projectDimensions : sampleDimensions;

    return {
      stats: {
        numSamples: DiscoverySidebar.formatNumber((sampleStats || {}).count),
        numProjects: DiscoverySidebar.formatNumber(projects.length),
        avgTotalReads: DiscoverySidebar.formatNumber(
          (sampleStats || {}).avgTotalReads
        ),
        avgAdjustedRemainingReads: DiscoverySidebar.formatNumber(
          (sampleStats || {}).avgAdjustedRemainingReads
        )
      },
      metadata: {
        host: DiscoverySidebar.loadDimension(dimensions, "host"),
        tissue: DiscoverySidebar.loadDimension(dimensions, "tissue"),
        location: DiscoverySidebar.loadDimension(dimensions, "location"),
        time: DiscoverySidebar.loadDimension(dimensions, "time_bins")
      }
    };
  }

  static loadDimension(dimensions, dimensionKey) {
    return (find({ dimension: dimensionKey }, dimensions) || {}).values || [];
  }

  static formatDate(date) {
    return moment(date).format("YYYY-MM-DD");
  }

  static formatNumber(value) {
    return (Math.round(value) || "").toLocaleString();
  }

  buildDateHistogram(field) {
    const { onFilterClick } = this.props;
    const { metadata } = this.state;

    let dates = metadata[field];
    const total = (maxBy("count", dates) || {}).count;
    const isIntervalBased = !!dates.length && dates[0].interval;
    const firstDate = dates.length
      ? isIntervalBased
        ? dates[0].interval.start
        : dates[0].value
      : null;
    const lastDate = dates.length
      ? isIntervalBased
        ? dates[dates.length - 1].interval.end
        : dates[0].value
      : null;
    const formattedFirstDate = DiscoverySidebar.formatDate(firstDate);
    const formattedLastDate = DiscoverySidebar.formatDate(lastDate);

    // Special case design to show 1 or 2 bars just side-by-side.
    const realBars = dates.filter(d => d.count > 0);
    if (realBars.length < 3) dates = realBars;
    return (
      <div className={cs.histogramContainer}>
        <div className={cs.dateHistogram}>
          {dates.map(entry => {
            const percent = Math.round(100 * entry.count / total, 0);
            const element = (
              <div
                className={cs.bar}
                key={entry.value}
                style={{ height: percent + "px" }}
                onClick={() => {
                  onFilterClick && onFilterClick(entry);
                  logAnalyticsEvent("DiscoverySidebar_date-filter_clicked", {
                    dateValue: entry.value,
                    dates: dates.length,
                    count: entry.count,
                    percent
                  });
                }}
              >
                &nbsp;
              </div>
            );
            const text = entry.interval
              ? `${DiscoverySidebar.formatDate(
                  entry.interval.start
                )} - ${DiscoverySidebar.formatDate(entry.interval.end)}`
              : `${DiscoverySidebar.formatDate(entry.value)}`;
            const tooltipMessage = (
              <span>
                <span className={cs.boldText}>Date:</span> {text}
                <br />
                <span className={cs.boldText}>{`Sample${
                  entry.count > 1 ? "s" : ""
                }: `}</span>
                {entry.count}
              </span>
            );
            return (
              <BasicPopup
                key={entry.value}<commit_msg>Fix histogram tooltip (#2260)<commit_after>import React from "react";
import cx from "classnames";
import { find, maxBy, orderBy, sumBy } from "lodash/fp";
import moment from "moment";

import { logAnalyticsEvent } from "~/api/analytics";
import PropTypes from "~/components/utils/propTypes";
import { Accordion } from "~/components/layout";
import BasicPopup from "~/components/BasicPopup";

import cs from "./discovery_sidebar.scss";

export default class DiscoverySidebar extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      stats: {
        numSamples: "",
        numProjects: "",
        avgTotalReads: "",
        avgAdjustedRemainingReads: ""
      },
      metadata: {
        host: [],
        tissue: [],
        time: [],
        location: []
      },
      expandedMetadataGroups: new Set()
    };
  }

  static getDerivedStateFromProps(newProps, prevState) {
    const {
      currentTab,
      loading,
      projectDimensions,
      projects,
      sampleDimensions,
      sampleStats
    } = newProps;
    if (loading) return prevState;
    const dimensions =
      currentTab === "projects" ? projectDimensions : sampleDimensions;

    return {
      stats: {
        numSamples: DiscoverySidebar.formatNumber((sampleStats || {}).count),
        numProjects: DiscoverySidebar.formatNumber(projects.length),
        avgTotalReads: DiscoverySidebar.formatNumber(
          (sampleStats || {}).avgTotalReads
        ),
        avgAdjustedRemainingReads: DiscoverySidebar.formatNumber(
          (sampleStats || {}).avgAdjustedRemainingReads
        )
      },
      metadata: {
        host: DiscoverySidebar.loadDimension(dimensions, "host"),
        tissue: DiscoverySidebar.loadDimension(dimensions, "tissue"),
        location: DiscoverySidebar.loadDimension(dimensions, "location"),
        time: DiscoverySidebar.loadDimension(dimensions, "time_bins")
      }
    };
  }

  static loadDimension(dimensions, dimensionKey) {
    return (find({ dimension: dimensionKey }, dimensions) || {}).values || [];
  }

  static formatDate(date) {
    return moment(date).format("YYYY-MM-DD");
  }

  static formatNumber(value) {
    return (Math.round(value) || "").toLocaleString();
  }

  buildDateHistogram(field) {
    const { onFilterClick, currentTab } = this.props;
    const { metadata } = this.state;

    let dates = metadata[field];
    const total = (maxBy("count", dates) || {}).count;
    const isIntervalBased = !!dates.length && dates[0].interval;
    const firstDate = dates.length
      ? isIntervalBased
        ? dates[0].interval.start
        : dates[0].value
      : null;
    const lastDate = dates.length
      ? isIntervalBased
        ? dates[dates.length - 1].interval.end
        : dates[0].value
      : null;
    const formattedFirstDate = DiscoverySidebar.formatDate(firstDate);
    const formattedLastDate = DiscoverySidebar.formatDate(lastDate);

    // Special case design to show 1 or 2 bars just side-by-side.
    const realBars = dates.filter(d => d.count > 0);
    if (realBars.length < 3) dates = realBars;
    return (
      <div className={cs.histogramContainer}>
        <div className={cs.dateHistogram}>
          {dates.map(entry => {
            const percent = Math.round(100 * entry.count / total, 0);
            const element = (
              <div
                className={cs.bar}
                key={entry.value}
                style={{ height: percent + "px" }}
                onClick={() => {
                  onFilterClick && onFilterClick(entry);
                  logAnalyticsEvent("DiscoverySidebar_date-filter_clicked", {
                    dateValue: entry.value,
                    dates: dates.length,
                    count: entry.count,
                    percent
                  });
                }}
              >
                &nbsp;
              </div>
            );
            const text = entry.interval
              ? `${DiscoverySidebar.formatDate(
                  entry.interval.start
                )} - ${DiscoverySidebar.formatDate(entry.interval.end)}`
              : `${DiscoverySidebar.formatDate(entry.value)}`;
            const tooltipMessage = (
              <span>
                <span className={cs.boldText}>Date:</span> {text}
                <br />
                <span className={cs.boldText}>{`${
                  currentTab === "samples" ? "Sample" : "Project"
                }${entry.count > 1 ? "s" : ""}: `}</span>
                {entry.count}
              </span>
            );
            return (
              <BasicPopup
                key={entry.value}
{'fraction_non_alphanumeric': 0.12212081703607128, 'fraction_numerical': 0.003476749239461104, 'mean_word_length': 2.0490228552500827, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1790948', 'n_tokens_mistral': 2591, 'n_tokens_neox': 2481, 'n_words': 503}