date-fns#formatISO JavaScript Examples

The following examples show how to use date-fns#formatISO. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: monitor.js    From lnft with GNU Affero General Public License v3.0 6 votes vote down vote up
checkTransactions = async () => {
  try {
    let { transactions } = await q(getUnconfirmed);

    for (let i = 0; i < transactions.length; i++) {
      let tx = transactions[i];
      let { block_time, confirmed } = await electrs
        .url(`/tx/${tx.hash}/status`)
        .get()
        .json();

      if (confirmed) {
        let {
          update_transactions_by_pk: { artwork_id, type, bid },
        } = await q(setConfirmed, {
          id: tx.id,
        });

        if (["deposit", "withdrawal"].includes(type))
          await q(setTransactionTime, {
            id: tx.id,
            created_at: formatISO(new Date(1000 * block_time)),
          });

        if (type === "accept")
          await q(setOwner, { id: artwork_id, owner_id: bid.user_id });
      }
    }
  } catch (e) {
    console.log("problem checking transactions", e);
  }

  setTimeout(checkTransactions, 5000);
}
Example #2
Source File: commonFunctions.js    From covid19india-react with MIT License 5 votes vote down vote up
getIndiaDateISO = () => {
  return formatISO(getIndiaDate(), {representation: 'date'});
}
Example #3
Source File: commonFunctions.js    From covid19india-react with MIT License 5 votes vote down vote up
getIndiaDateYesterdayISO = () => {
  return formatISO(getIndiaDateYesterday(), {representation: 'date'});
}
Example #4
Source File: Calendar.js    From covid19india-react with MIT License 4 votes vote down vote up
function Calendar({date, dates, slider}) {
  const [view, setView] = useState('month');
  const [activeStartDate, setActiveStartDate] = useState(parseIndiaDate(date));

  const minDate = parseIndiaDate(dates[0]);
  const maxDate = parseIndiaDate(dates[dates.length - 1]);

  const isDateDisabled = ({date, view}) => {
    return (
      view === 'month' &&
      !dates.includes(formatISO(date, {representation: 'date'}))
    );
  };

  const handleCalendarClick = (value) => {
    const clickedDate = formatISO(value, {representation: 'date'});
    slider.moveToSlide(dates.indexOf(clickedDate));
  };

  const handleViewButton = ({view}) => {
    setView(view);
  };

  const handleNavigationButton = ({activeStartDate}) => {
    setActiveStartDate(activeStartDate);
  };

  const handleNavigation = (direction) => {
    const newDate = add(
      activeStartDate,
      view === 'month' ? {months: direction} : {years: direction}
    );
    const lower =
      view === 'month' ? startOfMonth(minDate) : startOfYear(minDate);
    const upper = view === 'month' ? endOfMonth(maxDate) : endOfYear(maxDate);
    if (lower <= newDate && newDate <= upper) {
      setActiveStartDate(newDate);
    }
  };

  const swipeHandlers = useSwipeable({
    onSwipedRight: handleNavigation.bind(this, -1),
    onSwipedLeft: handleNavigation.bind(this, 1),
  });

  const handleWheel = (event) => {
    if (event.deltaX !== 0) {
      handleNavigation(Math.sign(event.deltaX));
    }
  };

  return (
    <div className="Calendar" onWheel={handleWheel} {...swipeHandlers}>
      <ReactCalendar
        value={parseIndiaDate(date)}
        tileDisabled={isDateDisabled}
        {...{minDate, maxDate, activeStartDate, view}}
        onActiveStartDateChange={handleNavigationButton}
        onViewChange={handleViewButton}
        minDetail="year"
        showFixedNumberOfWeeks
        onChange={handleCalendarClick}
        prevLabel={
          <div>
            <ChevronLeft size={18} />
          </div>
        }
        nextLabel={
          <div>
            <ChevronRight size={18} />
          </div>
        }
        prev2Label={
          <div>
            <ChevronsLeft size={18} />
          </div>
        }
        next2Label={
          <div>
            <ChevronsRight size={18} />
          </div>
        }
      />
    </div>
  );
}
Example #5
Source File: Home.js    From covid19india-react with MIT License 4 votes vote down vote up
function Home() {
  const [regionHighlighted, setRegionHighlighted] = useState({
    stateCode: 'TT',
    districtName: null,
  });

  const [anchor, setAnchor] = useLocalStorage('anchor', null);
  const [expandTable, setExpandTable] = useLocalStorage('expandTable', false);
  const [mapStatistic, setMapStatistic] = useSessionStorage(
    'mapStatistic',
    'active'
  );
  const [mapView, setMapView] = useLocalStorage('mapView', MAP_VIEWS.DISTRICTS);

  const [date, setDate] = useState('');
  const location = useLocation();

  const {data: timeseries} = useStickySWR(
    `${DATA_API_ROOT}/timeseries.min.json`,
    fetcher,
    {
      revalidateOnMount: true,
      refreshInterval: API_REFRESH_INTERVAL,
    }
  );

  const {data} = useStickySWR(
    `${DATA_API_ROOT}/data${date ? `-${date}` : ''}.min.json`,
    fetcher,
    {
      revalidateOnMount: true,
      refreshInterval: API_REFRESH_INTERVAL,
    }
  );

  const homeRightElement = useRef();
  const isVisible = useIsVisible(homeRightElement);
  const {width} = useWindowSize();

  const hideDistrictData = date !== '' && date < DISTRICT_START_DATE;
  const hideDistrictTestData =
    date === '' ||
    date >
      formatISO(
        addDays(parseIndiaDate(DISTRICT_TEST_END_DATE), TESTED_EXPIRING_DAYS),
        {representation: 'date'}
      );

  const hideVaccinated =
    getStatistic(data?.['TT'], 'total', 'vaccinated') === 0;

  const lastDataDate = useMemo(() => {
    const updatedDates = [
      data?.['TT']?.meta?.date,
      data?.['TT']?.meta?.tested?.date,
      data?.['TT']?.meta?.vaccinated?.date,
    ].filter((date) => date);
    return updatedDates.length > 0
      ? formatISO(max(updatedDates.map((date) => parseIndiaDate(date))), {
          representation: 'date',
        })
      : null;
  }, [data]);

  const lastUpdatedDate = useMemo(() => {
    const updatedDates = Object.keys(data || {})
      .map((stateCode) => data?.[stateCode]?.meta?.['last_updated'])
      .filter((datetime) => datetime);
    return updatedDates.length > 0
      ? formatDateObjIndia(
          max(updatedDates.map((datetime) => parseIndiaDate(datetime)))
        )
      : null;
  }, [data]);

  const noDistrictDataStates = useMemo(
    () =>
      // Heuristic: All cases are in Unknown
      Object.entries(data || {}).reduce((res, [stateCode, stateData]) => {
        res[stateCode] = !!(
          stateData?.districts &&
          stateData.districts?.[UNKNOWN_DISTRICT_KEY] &&
          PRIMARY_STATISTICS.every(
            (statistic) =>
              getStatistic(stateData, 'total', statistic) ===
              getStatistic(
                stateData.districts[UNKNOWN_DISTRICT_KEY],
                'total',
                statistic
              )
          )
        );
        return res;
      }, {}),
    [data]
  );

  const noRegionHighlightedDistrictData =
    regionHighlighted?.stateCode &&
    regionHighlighted?.districtName &&
    regionHighlighted.districtName !== UNKNOWN_DISTRICT_KEY &&
    noDistrictDataStates[regionHighlighted.stateCode];

  return (
    <>
      <Helmet>
        <title>Coronavirus Outbreak in India - covid19india.org</title>
        <meta
          name="title"
          content="Coronavirus Outbreak in India: Latest Map and Case Count"
        />
      </Helmet>

      <div className="Home">
        <div className={classnames('home-left', {expanded: expandTable})}>
          <div className="header">
            <Suspense fallback={<div />}>
              <Search />
            </Suspense>

            {!data && !timeseries && <div style={{height: '60rem'}} />}

            <>
              {!timeseries && <div style={{minHeight: '61px'}} />}
              {timeseries && (
                <Suspense fallback={<div style={{minHeight: '61px'}} />}>
                  <Actions
                    {...{
                      date,
                      setDate,
                      dates: Object.keys(timeseries['TT']?.dates),
                      lastUpdatedDate,
                    }}
                  />
                </Suspense>
              )}
            </>
          </div>

          <div style={{position: 'relative', marginTop: '1rem'}}>
            {data && (
              <Suspense fallback={<div style={{height: '50rem'}} />}>
                {width >= 769 && !expandTable && (
                  <MapSwitcher {...{mapStatistic, setMapStatistic}} />
                )}
                <Level data={data['TT']} />
              </Suspense>
            )}

            <>
              {!timeseries && <div style={{height: '123px'}} />}
              {timeseries && (
                <Suspense fallback={<div style={{height: '123px'}} />}>
                  <Minigraphs
                    timeseries={timeseries['TT']?.dates}
                    {...{date}}
                  />
                </Suspense>
              )}
            </>
          </div>

          {!hideVaccinated && <VaccinationHeader data={data['TT']} />}

          {data && (
            <Suspense fallback={<TableLoader />}>
              <Table
                {...{
                  data,
                  regionHighlighted,
                  setRegionHighlighted,
                  expandTable,
                  setExpandTable,
                  hideDistrictData,
                  hideDistrictTestData,
                  hideVaccinated,
                  lastDataDate,
                  noDistrictDataStates,
                }}
              />
            </Suspense>
          )}
        </div>

        <div
          className={classnames('home-right', {expanded: expandTable})}
          ref={homeRightElement}
          style={{minHeight: '4rem'}}
        >
          {(isVisible || location.hash) && (
            <>
              {data && (
                <div
                  className={classnames('map-container', {
                    expanded: expandTable,
                    stickied:
                      anchor === 'mapexplorer' || (expandTable && width >= 769),
                  })}
                >
                  <Suspense fallback={<div style={{height: '50rem'}} />}>
                    <StateHeader data={data['TT']} stateCode={'TT'} />
                    <MapExplorer
                      {...{
                        stateCode: 'TT',
                        data,
                        mapStatistic,
                        setMapStatistic,
                        mapView,
                        setMapView,
                        regionHighlighted,
                        setRegionHighlighted,
                        anchor,
                        setAnchor,
                        expandTable,
                        lastDataDate,
                        hideDistrictData,
                        hideDistrictTestData,
                        hideVaccinated,
                        noRegionHighlightedDistrictData,
                      }}
                    />
                  </Suspense>
                </div>
              )}

              {timeseries && (
                <Suspense fallback={<div style={{height: '50rem'}} />}>
                  <TimeseriesExplorer
                    stateCode="TT"
                    {...{
                      timeseries,
                      date,
                      regionHighlighted,
                      setRegionHighlighted,
                      anchor,
                      setAnchor,
                      expandTable,
                      hideVaccinated,
                      noRegionHighlightedDistrictData,
                    }}
                  />
                </Suspense>
              )}
            </>
          )}
        </div>
      </div>

      {isVisible && (
        <Suspense fallback={<div />}>
          <Footer />
        </Suspense>
      )}
    </>
  );
}
Example #6
Source File: Minigraphs.js    From covid19india-react with MIT License 4 votes vote down vote up
function Minigraphs({timeseries, date: timelineDate}) {
  const refs = useRef([]);
  const endDate = timelineDate || getIndiaDateYesterdayISO();

  let [wrapperRef, {width}] = useMeasure();
  width = Math.min(width, maxWidth);

  const dates = useMemo(() => {
    const pastDates = Object.keys(timeseries || {}).filter(
      (date) => date <= endDate
    );
    const lastDate = pastDates[pastDates.length - 1];

    const cutOffDateLower = formatISO(
      subDays(parseIndiaDate(lastDate), MINIGRAPH_LOOKBACK_DAYS),
      {representation: 'date'}
    );
    return pastDates.filter((date) => date >= cutOffDateLower);
  }, [endDate, timeseries]);

  const getMinigraphStatistic = useCallback(
    (date, statistic) => {
      return getStatistic(timeseries?.[date], 'delta', statistic);
    },
    [timeseries]
  );

  useEffect(() => {
    if (!width) return;

    const T = dates.length;

    const chartRight = width - margin.right;
    const chartBottom = height - margin.bottom;

    const xScale = scaleTime()
      .clamp(true)
      .domain([
        parseIndiaDate(dates[0] || endDate),
        parseIndiaDate(dates[T - 1]) || endDate,
      ])
      .range([margin.left, chartRight]);

    refs.current.forEach((ref, index) => {
      const svg = select(ref);
      const statistic = LEVEL_STATISTICS[index];
      const color = STATISTIC_CONFIGS[statistic].color;

      const dailyMaxAbs = max(dates, (date) =>
        Math.abs(getMinigraphStatistic(date, statistic))
      );

      const yScale = scaleLinear()
        .clamp(true)
        .domain([-dailyMaxAbs, dailyMaxAbs])
        .range([chartBottom, margin.top]);

      const linePath = line()
        .curve(curveMonotoneX)
        .x((date) => xScale(parseIndiaDate(date)))
        .y((date) => yScale(getMinigraphStatistic(date, statistic)));

      let pathLength;
      svg
        .selectAll('path')
        .data(T ? [dates] : [])
        .join(
          (enter) =>
            enter
              .append('path')
              .attr('fill', 'none')
              .attr('stroke', color + '99')
              .attr('stroke-width', 2.5)
              .attr('d', linePath)
              .attr('stroke-dasharray', function () {
                return (pathLength = this.getTotalLength());
              })
              .call((enter) =>
                enter
                  .attr('stroke-dashoffset', pathLength)
                  .transition()
                  .delay(100)
                  .duration(2500)
                  .attr('stroke-dashoffset', 0)
              ),
          (update) =>
            update
              .attr('stroke-dasharray', null)
              .transition()
              .duration(500)
              .attrTween('d', function (date) {
                const previous = select(this).attr('d');
                const current = linePath(date);
                return interpolatePath(previous, current);
              })
              .selection()
        );

      svg
        .selectAll('circle')
        .data(T ? [dates[T - 1]] : [])
        .join(
          (enter) =>
            enter
              .append('circle')
              .attr('fill', color)
              .attr('r', 2.5)
              .attr('cx', (date) => xScale(parseIndiaDate(date)))
              .attr('cy', (date) =>
                yScale(getMinigraphStatistic(date, statistic))
              )
              .style('opacity', 0)
              .call((enter) =>
                enter
                  .transition()
                  .delay(2100)
                  .duration(500)
                  .style('opacity', 1)
                  .attr('cx', (date) => xScale(parseIndiaDate(date)))
                  .attr('cy', (date) =>
                    yScale(getMinigraphStatistic(date, statistic))
                  )
              ),
          (update) =>
            update
              .transition()
              .duration(500)
              .attr('cx', (date) => xScale(parseIndiaDate(date)))
              .attr('cy', (date) =>
                yScale(getMinigraphStatistic(date, statistic))
              )
              .style('opacity', 1)
              .selection()
        );
    });
  }, [endDate, dates, width, getMinigraphStatistic]);

  return (
    <div className="Minigraph">
      {LEVEL_STATISTICS.map((statistic, index) => (
        <div
          key={statistic}
          className={classnames('svg-parent')}
          ref={index === 0 ? wrapperRef : null}
          style={{width: `calc(${100 / LEVEL_STATISTICS.length}%)`}}
        >
          <svg
            ref={(el) => {
              refs.current[index] = el;
            }}
            preserveAspectRatio="xMidYMid meet"
            width={width}
            height={height}
          />
        </div>
      ))}
    </div>
  );
}
Example #7
Source File: State.js    From covid19india-react with MIT License 4 votes vote down vote up
function State() {
  const {t} = useTranslation();

  const stateCode = useParams().stateCode.toUpperCase();

  const [mapStatistic, setMapStatistic] = useSessionStorage(
    'mapStatistic',
    'active'
  );
  const [showAllDistricts, setShowAllDistricts] = useState(false);
  const [regionHighlighted, setRegionHighlighted] = useState({
    stateCode: stateCode,
    districtName: null,
  });
  const [delta7Mode, setDelta7Mode] = useState(false);

  useEffect(() => {
    if (regionHighlighted.stateCode !== stateCode) {
      setRegionHighlighted({
        stateCode: stateCode,
        districtName: null,
      });
      setShowAllDistricts(false);
    }
  }, [regionHighlighted.stateCode, stateCode]);

  const {data: timeseries, error: timeseriesResponseError} = useSWR(
    `${DATA_API_ROOT}/timeseries-${stateCode}.min.json`,
    fetcher,
    {
      revalidateOnMount: true,
      refreshInterval: 100000,
    }
  );

  const {data} = useSWR(`${DATA_API_ROOT}/data.min.json`, fetcher, {
    revalidateOnMount: true,
    refreshInterval: 100000,
  });

  const stateData = data?.[stateCode];

  const toggleShowAllDistricts = () => {
    setShowAllDistricts(!showAllDistricts);
  };

  const handleSort = (districtNameA, districtNameB) => {
    const districtA = stateData.districts[districtNameA];
    const districtB = stateData.districts[districtNameB];
    return (
      getStatistic(districtB, 'total', mapStatistic) -
      getStatistic(districtA, 'total', mapStatistic)
    );
  };

  const gridRowCount = useMemo(() => {
    if (!stateData) return;
    const gridColumnCount = window.innerWidth >= 540 ? 3 : 2;
    const districtCount = stateData?.districts
      ? Object.keys(stateData.districts).filter(
          (districtName) => districtName !== 'Unknown'
        ).length
      : 0;
    const gridRowCount = Math.ceil(districtCount / gridColumnCount);
    return gridRowCount;
  }, [stateData]);

  const stateMetaElement = useRef();
  const isStateMetaVisible = useIsVisible(stateMetaElement);

  const trail = useMemo(() => {
    const styles = [];

    [0, 0, 0, 0].map((element, index) => {
      styles.push({
        animationDelay: `${index * 250}ms`,
      });
      return null;
    });
    return styles;
  }, []);

  const lookback = showAllDistricts ? (window.innerWidth >= 540 ? 10 : 8) : 6;

  const lastDataDate = useMemo(() => {
    const updatedDates = [
      stateData?.meta?.date,
      stateData?.meta?.tested?.date,
      stateData?.meta?.vaccinated?.date,
    ].filter((date) => date);
    return updatedDates.length > 0
      ? formatISO(max(updatedDates.map((date) => parseIndiaDate(date))), {
          representation: 'date',
        })
      : null;
  }, [stateData]);

  const primaryStatistic = MAP_STATISTICS.includes(mapStatistic)
    ? mapStatistic
    : 'confirmed';

  const noDistrictData = useMemo(() => {
    // Heuristic: All cases are in Unknown
    return !!(
      stateData?.districts &&
      stateData.districts?.[UNKNOWN_DISTRICT_KEY] &&
      PRIMARY_STATISTICS.every(
        (statistic) =>
          getStatistic(stateData, 'total', statistic) ===
          getStatistic(
            stateData.districts[UNKNOWN_DISTRICT_KEY],
            'total',
            statistic
          )
      )
    );
  }, [stateData]);

  const statisticConfig = STATISTIC_CONFIGS[primaryStatistic];

  const noRegionHighlightedDistrictData =
    regionHighlighted?.districtName &&
    regionHighlighted.districtName !== UNKNOWN_DISTRICT_KEY &&
    noDistrictData;

  const districts = Object.keys(
    ((!noDistrictData || !statisticConfig.hasPrimary) &&
      stateData?.districts) ||
      {}
  );

  return (
    <>
      <Helmet>
        <title>
          Coronavirus Outbreak in {STATE_NAMES[stateCode]} - covid19india.org
        </title>
        <meta
          name="title"
          content={`Coronavirus Outbreak in ${STATE_NAMES[stateCode]}: Latest Map and Case Count`}
        />
      </Helmet>

      <div className="State">
        <div className="state-left">
          <StateHeader data={stateData} stateCode={stateCode} />

          <div style={{position: 'relative'}}>
            <MapSwitcher {...{mapStatistic, setMapStatistic}} />
            <Level data={stateData} />
            <Minigraphs
              timeseries={timeseries?.[stateCode]?.dates}
              {...{stateCode}}
              forceRender={!!timeseriesResponseError}
            />
          </div>

          {stateData?.total?.vaccinated1 && (
            <VaccinationHeader data={stateData} />
          )}

          {data && (
            <Suspense fallback={<div style={{minHeight: '50rem'}} />}>
              <MapExplorer
                {...{
                  stateCode,
                  data,
                  regionHighlighted,
                  setRegionHighlighted,
                  mapStatistic,
                  setMapStatistic,
                  lastDataDate,
                  delta7Mode,
                  setDelta7Mode,
                  noRegionHighlightedDistrictData,
                  noDistrictData,
                }}
              ></MapExplorer>
            </Suspense>
          )}

          <span ref={stateMetaElement} />

          {isStateMetaVisible && data && (
            <Suspense fallback={<div />}>
              <StateMeta
                {...{
                  stateCode,
                  data,
                }}
                timeseries={timeseries?.[stateCode]?.dates}
              />
            </Suspense>
          )}
        </div>

        <div className="state-right">
          <>
            <div className="district-bar">
              <div
                className={classnames('district-bar-top', {
                  expanded: showAllDistricts,
                })}
              >
                <div className="district-bar-left">
                  <h2
                    className={classnames(primaryStatistic, 'fadeInUp')}
                    style={trail[0]}
                  >
                    {t('Top districts')}
                  </h2>
                  <div
                    className={`districts fadeInUp ${
                      showAllDistricts ? 'is-grid' : ''
                    }`}
                    style={
                      showAllDistricts
                        ? {
                            gridTemplateRows: `repeat(${gridRowCount}, 2rem)`,
                            ...trail[1],
                          }
                        : trail[1]
                    }
                  >
                    {districts
                      .filter((districtName) => districtName !== 'Unknown')
                      .sort((a, b) => handleSort(a, b))
                      .slice(0, showAllDistricts ? undefined : 5)
                      .map((districtName) => {
                        const total = getStatistic(
                          stateData.districts[districtName],
                          'total',
                          primaryStatistic
                        );
                        const delta = getStatistic(
                          stateData.districts[districtName],
                          'delta',
                          primaryStatistic
                        );
                        return (
                          <div key={districtName} className="district">
                            <h2>{formatNumber(total)}</h2>
                            <h5>{t(districtName)}</h5>
                            {primaryStatistic !== 'active' && (
                              <div className="delta">
                                <h6 className={primaryStatistic}>
                                  {delta > 0
                                    ? '\u2191' + formatNumber(delta)
                                    : ''}
                                </h6>
                              </div>
                            )}
                          </div>
                        );
                      })}
                  </div>
                </div>

                <div className="district-bar-right fadeInUp" style={trail[2]}>
                  {timeseries &&
                    (primaryStatistic === 'confirmed' ||
                      primaryStatistic === 'deceased') && (
                      <div className="happy-sign">
                        {Object.keys(timeseries[stateCode]?.dates || {})
                          .slice(-lookback)
                          .every(
                            (date) =>
                              getStatistic(
                                timeseries[stateCode].dates[date],
                                'delta',
                                primaryStatistic
                              ) === 0
                          ) && (
                          <div
                            className={`alert ${
                              primaryStatistic === 'confirmed' ? 'is-green' : ''
                            }`}
                          >
                            <SmileyIcon />
                            <div className="alert-right">
                              No new {primaryStatistic} cases in the past five
                              days
                            </div>
                          </div>
                        )}
                      </div>
                    )}
                  <DeltaBarGraph
                    timeseries={timeseries?.[stateCode]?.dates}
                    statistic={primaryStatistic}
                    {...{stateCode, lookback}}
                    forceRender={!!timeseriesResponseError}
                  />
                </div>
              </div>

              <div className="district-bar-bottom">
                {districts.length > 5 ? (
                  <button
                    className="button fadeInUp"
                    onClick={toggleShowAllDistricts}
                    style={trail[3]}
                  >
                    <span>
                      {t(showAllDistricts ? 'View less' : 'View all')}
                    </span>
                  </button>
                ) : (
                  <div style={{height: '3.75rem', flexBasis: '15%'}} />
                )}
              </div>
            </div>

            <Suspense fallback={<div />}>
              <TimeseriesExplorer
                {...{
                  stateCode,
                  timeseries,
                  regionHighlighted,
                  setRegionHighlighted,
                  noRegionHighlightedDistrictData,
                }}
                forceRender={!!timeseriesResponseError}
              />
            </Suspense>
          </>
        </div>
      </div>

      <Footer />
    </>
  );
}
Example #8
Source File: StateMeta.js    From covid19india-react with MIT License 4 votes vote down vote up
function StateMeta({stateCode, data, timeseries}) {
  const {t} = useTranslation();

  const confirmedPerLakh = getStatistic(data[stateCode], 'total', 'confirmed', {
    normalizedByPopulationPer: 'lakh',
  });
  const testPerLakh = getStatistic(data[stateCode], 'total', 'tested', {
    normalizedByPopulationPer: 'lakh',
  });
  const totalConfirmedPerLakh = getStatistic(data['TT'], 'total', 'confirmed', {
    normalizedByPopulationPer: 'lakh',
  });

  const activePercent = getStatistic(data[stateCode], 'total', 'activeRatio');
  const recoveryPercent = getStatistic(
    data[stateCode],
    'total',
    'recoveryRatio'
  );
  const deathPercent = getStatistic(data[stateCode], 'total', 'cfr');

  // Show TPR for week preceeding last updated date
  const pastDates = Object.keys(timeseries || {}).filter(
    (date) => date <= getIndiaDateYesterdayISO()
  );
  const lastDate = pastDates[pastDates.length - 1];
  const prevWeekDate = formatISO(subDays(parseIndiaDate(lastDate), 6));

  const tprWeek = getStatistic(timeseries?.[lastDate], 'delta', 'tpr', {
    movingAverage: true,
  });

  return (
    <>
      <div className="StateMeta population">
        <div className="meta-item population">
          <h3>{t('Population')}</h3>
          <h1>{formatNumber(data[stateCode]?.meta?.population)}</h1>
        </div>
        <div className="alert">
          <Compass />
          <div className="alert-right">
            {t('Based on 2019 population projection by NCP')}
            <a
              href="https://nhm.gov.in/New_Updates_2018/Report_Population_Projection_2019.pdf"
              target="_noblank"
            >
              report
            </a>
          </div>
        </div>
      </div>

      <div className="StateMeta">
        <StateMetaCard
          className="confirmed"
          title={t('Confirmed Per Lakh')}
          statistic={formatNumber(confirmedPerLakh)}
          total={formatNumber(totalConfirmedPerLakh)}
          formula={
            <>
              {`${1e5} x `}
              <Fraction
                numerator={t('Total confirmed cases')}
                denominator={t('Total population')}
              />
            </>
          }
          description={`
            ~${formatNumber(confirmedPerLakh, 'long')} ${t(
            'out of every lakh people in'
          )} ${STATE_NAMES[stateCode]} ${t(
            'have tested positive for the virus.'
          )}
            `}
        />

        <StateMetaCard
          className="active"
          title={t('Active Ratio')}
          statistic={`${formatNumber(activePercent, '%')}`}
          formula={
            <>
              {'100 x '}
              <Fraction
                numerator={t('Total active cases right now')}
                denominator={t('Total confirmed cases')}
              />
            </>
          }
          description={
            activePercent > 0
              ? `${t('For every 100 confirmed cases')}, ~${formatNumber(
                  activePercent,
                  'long'
                )} ${t('are currently infected.')}`
              : t('Currently, there are no active cases in this state.')
          }
        />

        <StateMetaCard
          className="recovery"
          title={t('Recovery Ratio')}
          statistic={`${formatNumber(recoveryPercent, '%')}`}
          formula={
            <>
              {'100 x '}
              <Fraction
                numerator={t('Total recovered cases')}
                denominator={t('Total confirmed cases')}
              />
            </>
          }
          description={
            recoveryPercent > 0
              ? `${t('For every 100 confirmed cases')}, ~${formatNumber(
                  recoveryPercent,
                  'long'
                )} ${t('have recovered from the virus.')}`
              : t('Unfortunately, there are no recoveries in this state yet.')
          }
        />

        <StateMetaCard
          className="mortality"
          title={t('Case Fatality Ratio')}
          statistic={`${formatNumber(deathPercent, '%')}`}
          formula={
            <>
              {'100 x '}
              <Fraction
                numerator={t('Total deaths')}
                denominator={t('Total confirmed cases')}
              />
            </>
          }
          description={
            deathPercent > 0
              ? `${t('For every 100 confirmed cases')}, ~${formatNumber(
                  deathPercent,
                  'long'
                )} ${t('have unfortunately passed away from the virus.')}`
              : t(
                  'Fortunately, no one has passed away from the virus in this state.'
                )
          }
        />

        <StateMetaCard
          className="tpr"
          title={t('Test Positivity Ratio')}
          statistic={tprWeek > 0 ? `${formatNumber(tprWeek, '%')}` : '-'}
          formula={
            <>
              {'100 x '}
              <Fraction
                numerator={t('Confirmed cases last week')}
                denominator={t('Samples tested last week')}
              />
            </>
          }
          date={`${formatDate(prevWeekDate, 'dd MMM')} - ${formatDate(
            lastDate,
            'dd MMM'
          )}`}
          description={
            tprWeek > 0
              ? `${t('In the last one week,')} ${formatNumber(tprWeek, '%')}
              ${t('of samples tested came back positive.')}`
              : t('No tested sample came back positive in last one week.')
          }
        />

        <StateMetaCard
          className="tpl"
          title={t('Tests Per Lakh')}
          statistic={`${formatNumber(testPerLakh)}`}
          formula={
            <>
              {`${1e5} x `}
              <Fraction
                numerator={t('Total samples tested')}
                denominator={t('Total population')}
              />
            </>
          }
          date={
            testPerLakh && data[stateCode]?.meta?.tested?.date
              ? `${t('As of')} ${formatLastUpdated(
                  data[stateCode].meta.tested.date
                )} ${t('ago')}`
              : ''
          }
          description={
            testPerLakh > 0
              ? `${t('For every lakh people in')} ${STATE_NAMES[stateCode]},
                ~${formatNumber(testPerLakh, 'long')} ${t(
                  'samples were tested.'
                )}`
              : t('No tests have been conducted in this state yet.')
          }
        />
      </div>
    </>
  );
}
Example #9
Source File: TimeseriesBrush.js    From covid19india-react with MIT License 4 votes vote down vote up
function TimeseriesBrush({
  timeseries,
  dates,
  currentBrushSelection,
  endDate,
  lookback,
  setBrushSelectionEnd,
  setLookback,
  animationIndex,
}) {
  const chartRef = useRef();
  const [wrapperRef, {width, height}] = useMeasure();

  const endDateMin =
    lookback !== null
      ? min([
          formatISO(addDays(parseIndiaDate(dates[0]), lookback), {
            representation: 'date',
          }),
          endDate,
        ])
      : endDate;

  const xScale = useMemo(() => {
    const T = dates.length;

    // Chart extremes
    const chartRight = width - margin.right;

    return scaleTime()
      .clamp(true)
      .domain([
        parseIndiaDate(dates[0] || endDate),
        parseIndiaDate(dates[T - 1] || endDate),
      ])
      .range([margin.left, chartRight]);
  }, [width, endDate, dates]);

  useEffect(() => {
    if (!width || !height) return;

    // Chart extremes
    const chartBottom = height - margin.bottom;

    const xAxis = (g) =>
      g
        .attr('class', 'x-axis')
        .call(axisBottom(xScale).ticks(numTicksX(width)));

    // Switched to daily confirmed instead of cumulative ARD
    const timeseriesStacked = stack()
      .keys(BRUSH_STATISTICS)
      .value((date, statistic) =>
        Math.max(0, getStatistic(timeseries[date], 'delta', statistic))
      )(dates);

    const yScale = scaleLinear()
      .clamp(true)
      .domain([
        0,
        max(
          timeseriesStacked[timeseriesStacked.length - 1],
          ([, y1]) => yBufferTop * y1
        ),
      ])
      .range([chartBottom, margin.top]);

    const svg = select(chartRef.current);

    const t = svg.transition().duration(D3_TRANSITION_DURATION);

    svg
      .select('.x-axis')
      .attr('pointer-events', 'none')
      .style('transform', `translate3d(0, ${chartBottom}px, 0)`)
      .transition(t)
      .call(xAxis);

    const areaPath = area()
      .curve(curveMonotoneX)
      .x((d) => xScale(parseIndiaDate(d.data)))
      .y0((d) => yScale(d[0]))
      .y1((d) => yScale(d[1]));

    svg
      .select('.trend-areas')
      .selectAll('.trend-area')
      .data(timeseriesStacked)
      .join(
        (enter) =>
          enter
            .append('path')
            .attr('class', 'trend-area')
            .attr('fill', ({key}) => STATISTIC_CONFIGS[key].color)
            .attr('fill-opacity', 0.4)
            .attr('stroke', ({key}) => STATISTIC_CONFIGS[key].color)
            .attr('d', areaPath)
            .attr('pointer-events', 'none'),
        (update) =>
          update
            .transition(t)
            .attrTween('d', function (date) {
              const previous = select(this).attr('d');
              const current = areaPath(date);
              return interpolatePath(previous, current);
            })
            .selection()
      );
  }, [dates, width, height, xScale, timeseries]);

  const defaultSelection = currentBrushSelection.map((date) =>
    xScale(parseIndiaDate(date))
  );

  const brush = useMemo(() => {
    if (!width || !height) return;
    // Chart extremes
    const chartRight = width - margin.right;
    const chartBottom = height - margin.bottom;

    const brush = brushX()
      .extent([
        [margin.left, margin.top],
        [chartRight, chartBottom],
      ])
      .handleSize(20);
    return brush;
  }, [width, height]);

  const brushed = useCallback(
    ({sourceEvent, selection}) => {
      if (!sourceEvent) return;
      const [brushStartDate, brushEndDate] = selection.map(xScale.invert);

      ReactDOM.unstable_batchedUpdates(() => {
        setBrushSelectionEnd(formatISO(brushEndDate, {representation: 'date'}));
        setLookback(differenceInDays(brushEndDate, brushStartDate));
      });
    },
    [xScale, setBrushSelectionEnd, setLookback]
  );

  const beforebrushstarted = useCallback(
    (event) => {
      const svg = select(chartRef.current);
      const selection = brushSelection(svg.select('.brush').node());

      if (!selection) return;

      const dx = selection[1] - selection[0];
      const [[cx]] = pointers(event);
      const [x0, x1] = [cx - dx / 2, cx + dx / 2];
      const [X0, X1] = xScale.range();
      svg
        .select('.brush')
        .call(
          brush.move,
          x1 > X1 ? [X1 - dx, X1] : x0 < X0 ? [X0, X0 + dx] : [x0, x1]
        );
    },
    [brush, xScale]
  );

  const brushended = useCallback(
    ({sourceEvent, selection}) => {
      if (!sourceEvent || !selection) return;
      const domain = selection
        .map(xScale.invert)
        .map((date) => formatISO(date, {representation: 'date'}));

      const svg = select(chartRef.current);
      svg
        .select('.brush')
        .call(
          brush.move,
          domain.map((date) => xScale(parseIndiaDate(date)))
        )
        .call((g) => g.select('.overlay').attr('cursor', 'pointer'));
    },
    [brush, xScale]
  );

  useEffect(() => {
    if (!brush) return;
    brush.on('start brush', brushed).on('end', brushended);
    const svg = select(chartRef.current);
    svg
      .select('.brush')
      .call(brush)
      .call((g) =>
        g
          .select('.overlay')
          .attr('cursor', 'pointer')
          .datum({type: 'selection'})
          .on('mousedown touchstart', beforebrushstarted)
      );
  }, [brush, brushed, brushended, beforebrushstarted]);

  useEffect(() => {
    if (!brush) return;
    const svg = select(chartRef.current);
    svg.select('.brush').call(brush.move, defaultSelection);
  }, [brush, defaultSelection]);

  const handleWheel = (event) => {
    if (event.deltaX) {
      setBrushSelectionEnd(
        max([
          endDateMin,
          dates[
            Math.max(
              0,
              Math.min(
                dates.length - 1,
                dates.indexOf(currentBrushSelection[1]) +
                  Math.sign(event.deltaX) * brushWheelDelta
              )
            )
          ],
        ])
      );
    }
  };

  return (
    <div className="Timeseries">
      <div
        className={classnames('svg-parent is-brush fadeInUp')}
        ref={wrapperRef}
        onWheel={handleWheel}
        style={{animationDelay: `${animationIndex * 250}ms`}}
      >
        <svg ref={chartRef} preserveAspectRatio="xMidYMid meet">
          <defs>
            <clipPath id="clipPath">
              <rect
                x={0}
                y={`${margin.top}`}
                width={width}
                height={`${Math.max(0, height - margin.bottom)}`}
              />
            </clipPath>
            <mask id="mask">
              <rect
                x={0}
                y={`${margin.top}`}
                width={width}
                height={`${Math.max(0, height - margin.bottom)}`}
                fill="hsl(0, 0%, 40%)"
              />
              <use href="#selection" fill="white" />
            </mask>
          </defs>

          <g className="brush" clipPath="url(#clipPath)">
            <g mask="url(#mask)">
              <rect className="overlay" />
              <g className="trend-areas" />
              <rect className="selection" id="selection" />
            </g>
          </g>
          <g className="x-axis" />
        </svg>
      </div>
    </div>
  );
}
Example #10
Source File: TimeseriesExplorer.js    From covid19india-react with MIT License 4 votes vote down vote up
function TimeseriesExplorer({
  stateCode,
  timeseries,
  date: timelineDate,
  regionHighlighted,
  setRegionHighlighted,
  anchor,
  setAnchor,
  expandTable = false,
  hideVaccinated = false,
  noRegionHighlightedDistrictData,
}) {
  const {t} = useTranslation();
  const [lookback, setLookback] = useLocalStorage('timeseriesLookbackDays', 90);
  const [chartType, setChartType] = useLocalStorage('chartType', 'delta');
  const [isUniform, setIsUniform] = useLocalStorage('isUniform', false);
  const [isLog, setIsLog] = useLocalStorage('isLog', false);
  const [isMovingAverage, setIsMovingAverage] = useLocalStorage(
    'isMovingAverage',
    false
  );

  const stateCodeDateRange = Object.keys(timeseries?.[stateCode]?.dates || {});
  const beginningDate =
    stateCodeDateRange[0] || timelineDate || getIndiaDateYesterdayISO();
  const endDate = min([
    stateCodeDateRange[stateCodeDateRange.length - 1],
    timelineDate || getIndiaDateYesterdayISO(),
  ]);

  const [brushSelectionEnd, setBrushSelectionEnd] = useState(endDate);
  useEffect(() => {
    setBrushSelectionEnd(endDate);
  }, [endDate]);

  const brushSelectionStart =
    lookback !== null
      ? formatISO(subDays(parseIndiaDate(brushSelectionEnd), lookback), {
          representation: 'date',
        })
      : beginningDate;

  const explorerElement = useRef();
  const isVisible = useIsVisible(explorerElement, {once: true});
  const {width} = useWindowSize();

  const selectedRegion = useMemo(() => {
    if (timeseries?.[regionHighlighted.stateCode]?.districts) {
      return {
        stateCode: regionHighlighted.stateCode,
        districtName: regionHighlighted.districtName,
      };
    } else {
      return {
        stateCode: regionHighlighted.stateCode,
        districtName: null,
      };
    }
  }, [timeseries, regionHighlighted.stateCode, regionHighlighted.districtName]);

  const selectedTimeseries = useMemo(() => {
    if (selectedRegion.districtName) {
      return timeseries?.[selectedRegion.stateCode]?.districts?.[
        selectedRegion.districtName
      ]?.dates;
    } else {
      return timeseries?.[selectedRegion.stateCode]?.dates;
    }
  }, [timeseries, selectedRegion.stateCode, selectedRegion.districtName]);

  const regions = useMemo(() => {
    const states = Object.keys(timeseries || {})
      .filter((code) => code !== stateCode)
      .sort((code1, code2) =>
        STATE_NAMES[code1].localeCompare(STATE_NAMES[code2])
      )
      .map((code) => {
        return {
          stateCode: code,
          districtName: null,
        };
      });
    const districts = Object.keys(timeseries || {}).reduce((acc1, code) => {
      return [
        ...acc1,
        ...Object.keys(timeseries?.[code]?.districts || {}).reduce(
          (acc2, districtName) => {
            return [
              ...acc2,
              {
                stateCode: code,
                districtName: districtName,
              },
            ];
          },
          []
        ),
      ];
    }, []);

    return [
      {
        stateCode: stateCode,
        districtName: null,
      },
      ...states,
      ...districts,
    ];
  }, [timeseries, stateCode]);

  const dropdownRegions = useMemo(() => {
    if (
      regions.find(
        (region) =>
          region.stateCode === regionHighlighted.stateCode &&
          region.districtName === regionHighlighted.districtName
      )
    )
      return regions;
    return [
      ...regions,
      {
        stateCode: regionHighlighted.stateCode,
        districtName: regionHighlighted.districtName,
      },
    ];
  }, [regionHighlighted.stateCode, regionHighlighted.districtName, regions]);

  const dates = useMemo(
    () =>
      Object.keys(selectedTimeseries || {}).filter((date) => date <= endDate),
    [selectedTimeseries, endDate]
  );

  const brushSelectionDates = useMemo(
    () =>
      dates.filter(
        (date) => brushSelectionStart <= date && date <= brushSelectionEnd
      ),
    [dates, brushSelectionStart, brushSelectionEnd]
  );

  const handleChange = useCallback(
    ({target}) => {
      setRegionHighlighted(JSON.parse(target.value));
    },
    [setRegionHighlighted]
  );

  const resetDropdown = useCallback(() => {
    setRegionHighlighted({
      stateCode: stateCode,
      districtName: null,
    });
  }, [stateCode, setRegionHighlighted]);

  const statistics = useMemo(
    () =>
      TIMESERIES_STATISTICS.filter(
        (statistic) =>
          (!(STATISTIC_CONFIGS[statistic]?.category === 'vaccinated') ||
            !hideVaccinated) &&
          // (chartType === 'total' || statistic !== 'active') &&
          (chartType === 'delta' || statistic !== 'tpr')
      ),
    [chartType, hideVaccinated]
  );

  return (
    <div
      className={classnames(
        'TimeseriesExplorer fadeInUp',
        {
          stickied: anchor === 'timeseries',
        },
        {expanded: expandTable}
      )}
      style={{
        display:
          anchor && anchor !== 'timeseries' && (!expandTable || width < 769)
            ? 'none'
            : '',
      }}
      ref={explorerElement}
    >
      <div className="timeseries-header">
        <div
          className={classnames('anchor', 'fadeInUp', {
            stickied: anchor === 'timeseries',
          })}
          style={{
            display: expandTable && width >= 769 ? 'none' : '',
          }}
          onClick={
            setAnchor &&
            setAnchor.bind(this, anchor === 'timeseries' ? null : 'timeseries')
          }
        >
          <PinIcon />
        </div>

        <h1>{t('Spread Trends')}</h1>
        <div className="tabs">
          {Object.entries(TIMESERIES_CHART_TYPES).map(
            ([ctype, value], index) => (
              <div
                className={`tab ${chartType === ctype ? 'focused' : ''}`}
                key={ctype}
                onClick={setChartType.bind(this, ctype)}
              >
                <h4>{t(value)}</h4>
              </div>
            )
          )}
        </div>

        <div className="timeseries-options">
          <div className="scale-modes">
            <label className="main">{`${t('Scale Modes')}:`}</label>
            <div className="timeseries-mode">
              <label htmlFor="timeseries-mode">{t('Uniform')}</label>
              <input
                id="timeseries-mode"
                type="checkbox"
                className="switch"
                checked={isUniform}
                aria-label={t('Checked by default to scale uniformly.')}
                onChange={setIsUniform.bind(this, !isUniform)}
              />
            </div>
            <div
              className={`timeseries-mode ${
                chartType !== 'total' ? 'disabled' : ''
              }`}
            >
              <label htmlFor="timeseries-logmode">{t('Logarithmic')}</label>
              <input
                id="timeseries-logmode"
                type="checkbox"
                checked={chartType === 'total' && isLog}
                className="switch"
                disabled={chartType !== 'total'}
                onChange={setIsLog.bind(this, !isLog)}
              />
            </div>
          </div>

          <div
            className={`timeseries-mode ${
              chartType === 'total' ? 'disabled' : ''
            } moving-average`}
          >
            <label htmlFor="timeseries-moving-average">
              {t('7 day Moving Average')}
            </label>
            <input
              id="timeseries-moving-average"
              type="checkbox"
              checked={chartType === 'delta' && isMovingAverage}
              className="switch"
              disabled={chartType !== 'delta'}
              onChange={setIsMovingAverage.bind(this, !isMovingAverage)}
            />
          </div>
        </div>
      </div>
      {dropdownRegions && (
        <div className="state-selection">
          <div className="dropdown">
            <select
              value={JSON.stringify(selectedRegion)}
              onChange={handleChange}
            >
              {dropdownRegions
                .filter(
                  (region) =>
                    STATE_NAMES[region.stateCode] !== region.districtName
                )
                .map((region) => {
                  return (
                    <option
                      value={JSON.stringify(region)}
                      key={`${region.stateCode}-${region.districtName}`}
                    >
                      {region.districtName
                        ? t(region.districtName)
                        : t(STATE_NAMES[region.stateCode])}
                    </option>
                  );
                })}
            </select>
          </div>
          <div className="reset-icon" onClick={resetDropdown}>
            <ReplyIcon />
          </div>
        </div>
      )}
      {isVisible && (
        <Suspense fallback={<TimeseriesLoader />}>
          <Timeseries
            timeseries={selectedTimeseries}
            regionHighlighted={selectedRegion}
            dates={brushSelectionDates}
            {...{
              statistics,
              endDate,
              chartType,
              isUniform,
              isLog,
              isMovingAverage,
              noRegionHighlightedDistrictData,
            }}
          />
          <TimeseriesBrush
            timeseries={selectedTimeseries}
            regionHighlighted={selectedRegion}
            currentBrushSelection={[brushSelectionStart, brushSelectionEnd]}
            animationIndex={statistics.length}
            {...{dates, endDate, lookback, setBrushSelectionEnd, setLookback}}
          />
        </Suspense>
      )}
      {!isVisible && <div style={{height: '50rem'}} />}
      <div
        className="pills fadeInUp"
        style={{animationDelay: `${(1 + statistics.length) * 250}ms`}}
      >
        {TIMESERIES_LOOKBACK_DAYS.map((numDays) => (
          <button
            key={numDays}
            type="button"
            className={classnames({
              selected: numDays === lookback,
            })}
            onClick={setLookback.bind(this, numDays)}
          >
            {numDays !== null ? `${numDays} ${t('days')}` : t('Beginning')}
          </button>
        ))}
      </div>
    </div>
  );
}
Example #11
Source File: Job.jsx    From bull-master with MIT License 4 votes vote down vote up
function Job({ match, history }) {
  const classes = useStyles();
  const [job, getJob] = useResource(({ jobId, queueName }) => ({
    url: `/queues/${queueName}/jobs/${jobId}`,
    method: 'GET',
  }));
  const [, createRemoveRequest] = useRequest(({ jobId, queueName }) => ({
    url: `/queues/${queueName}/removes`,
    method: 'POST',
    data: {
      jobs: [jobId],
    },
  }));
  const [, createRetryJobRequest] = useRequest(({ jobId, queueName }) => ({
    url: `/queues/${queueName}/retries`,
    method: 'POST',
    data: {
      jobs: [jobId],
    },
  }));
  const [, createPromoteJobRequest] = useRequest(({ jobId, queueName }) => ({
    url: `/queues/${queueName}/promotes`,
    method: 'POST',
    data: {
      jobs: [jobId],
    },
  }));
  const { jobId, queueName } = match.params;
  useInterval(() => {
    getJob({ jobId, queueName });
  }, 4000);

  const stacktrace = job.data?.stacktrace || [];
  const logs = job.data?.logs || [];
  const status = job.data?.status || 'active';

  const handleRemove = () => {
    createRemoveRequest({ jobId, queueName })
      .ready()
      .then(() => history.push(`/queues/${queueName}?status=${status}`));
  };
  const handlePromote = () => {
    createPromoteJobRequest({ jobId, queueName })
      .ready()
      .then(() => getJob({ jobId, queueName }));
  };
  const handleRetry = () => {
    createRetryJobRequest({ jobId, queueName })
      .ready()
      .then(() => getJob({ jobId, queueName }));
  };

  return (
    <Grid container spacing={3}>
      <Grid item xs={12}>
        <Breadcrumbs aria-label="breadcrumb">
          <Link component={RouterLink} color="inherit" to="/">
            Dashboard
          </Link>
          <Link
            component={RouterLink}
            color="inherit"
            to={`/queues/${queueName}`}
          >
            {queueName}
          </Link>
          <Typography color="textPrimary">#{jobId}</Typography>
        </Breadcrumbs>
      </Grid>
      <Grid item xs={12} md={8}>
        <Section>
          <Grid container alignItems="center" style={{ marginBottom: 16 }}>
            <Grid item xs={6} container alignItems="center">
              <Title style={{ marginRight: 16 }}>#{jobId}</Title>
              <Status style={{ marginRight: 16 }} status={job.data?.status} />
              <CircularProgress
                style={{ display: 'inline-block' }}
                value={job.data?.progress}
              />
            </Grid>
            <Grid
              item
              container
              xs={6}
              className={classes.actions}
              justify="flex-end"
            >
              {['failed'].includes(status) && (
                <Button
                  variant="contained"
                  color="primary"
                  className={clsx(classes.retry, classes.button)}
                  onClick={handleRetry}
                >
                  Retry
                </Button>
              )}
              {['delayed', 'failed', 'completed', 'waiting', 'paused'].includes(
                status,
              ) && (
                <Button
                  className={clsx(classes.remove, classes.button)}
                  variant="contained"
                  onClick={handleRemove}
                >
                  Remove
                </Button>
              )}
              {status === 'delayed' && (
                <Button
                  variant="contained"
                  color="primary"
                  className={clsx(classes.promote, classes.button)}
                  onClick={handlePromote}
                >
                  Promote
                </Button>
              )}
            </Grid>
          </Grid>
          <Grid container style={{ marginBottom: 16 }} alignItems="center">
            <Grid item xs={4}>
              <Typography variant="body1" component="span">
                Retries Attempted
              </Typography>
            </Grid>
            <Grid item xs={4}>
              <Typography variant="body1">{job.data?.attemptsMade}</Typography>
            </Grid>
          </Grid>
          <Grid container style={{ marginBottom: 16 }} alignItems="center">
            <Grid item xs={4}>
              <Typography variant="body1" component="span">
                Total Configured Retries
              </Typography>
            </Grid>
            <Grid item xs={4}>
              <Typography variant="body1">{job.data?.attemptsTotal}</Typography>
            </Grid>
          </Grid>
          <Grid container style={{ marginBottom: 16 }} alignItems="center">
            <Grid item xs={4}>
              <Typography variant="body1" component="span">
                Job Name
              </Typography>
            </Grid>
            <Grid item xs={4}>
              <Typography variant="body1">{job.data?.name}</Typography>
            </Grid>
          </Grid>
          <Grid container style={{ marginBottom: 16 }} alignItems="center">
            <Grid item xs={4}>
              <Typography variant="body1" component="span">
                Delayed To
              </Typography>
            </Grid>
            <Grid item xs={4}>
              <Typography variant="body1">
                {job.data?.delayedTo ? formatISO(job.data?.delayedTo) : '-'}
              </Typography>
            </Grid>
          </Grid>
          <Grid container style={{ marginBottom: 16 }} alignItems="center">
            <Grid item xs={4}>
              <Typography variant="body1" component="span">
                Failed Reason
              </Typography>
            </Grid>
            <Grid item xs={4}>
              <Typography variant="body1">{job.data?.failedReason}</Typography>
            </Grid>
          </Grid>
        </Section>
      </Grid>
      <Grid item xs={12} md={4}>
        <Section>
          <Grid container alignItems="center">
            <Grid item xs={8}>
              <Title>Timeline</Title>
              <List>
                <ListItem style={{ paddingLeft: 0 }}>
                  <ListItemText
                    secondary={
                      job.data?.timestamp ? formatISO(job.data?.timestamp) : '-'
                    }
                  >
                    Started At
                  </ListItemText>
                </ListItem>
                <ListItem style={{ paddingLeft: 0 }}>
                  <ListItemText
                    secondary={
                      job.data?.processedOn
                        ? formatISO(job.data?.processedOn)
                        : '-'
                    }
                  >
                    Processed At
                  </ListItemText>
                </ListItem>
                <ListItem style={{ paddingLeft: 0 }}>
                  <ListItemText
                    secondary={
                      job.data?.finishedOn
                        ? formatISO(job.data?.finishedOn)
                        : '-'
                    }
                  >
                    Finished At
                  </ListItemText>
                </ListItem>
              </List>
            </Grid>
          </Grid>
        </Section>
      </Grid>
      <Grid item xs={12}>
        <Section>
          <Title>Data</Title>
          <pre>{JSON.stringify(job.data?.data, null, 2)}</pre>
        </Section>
      </Grid>
      {stacktrace.length > 0 && (
        <Grid item xs={12}>
          <Section>
            <Typography variant="h6">Error Stack</Typography>
            <List>
              {stacktrace.reverse().map((trace, index) => (
                /* eslint-disable-next-line */
                <ListItem key={`${trace}-${index}`}>
                  <pre>{trace}</pre>
                </ListItem>
              ))}
            </List>
          </Section>
        </Grid>
      )}
      {logs.length > 0 && (
        <Grid item xs={12}>
          <Section>
            <Typography variant="h6">Logs</Typography>
            <List>
              {logs.reverse().map((log, index) => (
                /* eslint-disable-next-line */
                <ListItem key={`${log}-${index}`}>
                  <pre style={{ margin: 0 }}>{log}</pre>
                </ListItem>
              ))}
            </List>
          </Section>
        </Grid>
      )}
      <Grid item xs={12}>
        <Section>
          <Title>Raw Data</Title>
          <pre>{JSON.stringify(job?.data, null, 2)}</pre>
        </Section>
      </Grid>
    </Grid>
  );
}
Example #12
Source File: Queue.jsx    From bull-master with MIT License 4 votes vote down vote up
function Queue({ match, history, location }) {
  const { pathname } = location || {};
  const [queue, getQueue] = useResource(() => ({
    url: `/queues/${match.params.queueName}`,
    method: 'GET',
  }));
  useInterval(getQueue, 4000);
  const [jobs, getJobs] = useResource(({ page, pageSize, status }) => ({
    url: `/queues/${match.params.queueName}/jobs?status=${status}&pageSize=${pageSize}&page=${page}`,
    method: 'GET',
  }));
  const [, retryAll] = useResource(() => ({
    url: `/queues/${match.params.queueName}/retries`,
    method: 'POST',
    data: {
      status: 'failed',
    },
  }));
  const [, cleanQueue] = useResource(() => ({
    url: `/queues/${match.params.queueName}/clean`,
    method: 'POST',
  }));
  const [, pauseQueue] = useResource(() => ({
    url: `/queues/${match.params.queueName}/pause`,
    method: 'POST',
  }));
  const [, resumeQueue] = useResource(() => ({
    url: `/queues/${match.params.queueName}/resume`,
    method: 'POST',
  }));
  const [, promoteAll] = useResource(() => ({
    url: `/queues/${match.params.queueName}/promotes`,
    method: 'POST',
    data: {
      status: 'delayed',
    },
  }));
  const [selected, setSelected] = useState([]);
  const query = new URLSearchParams(location.search);
  const status = query.get('status') || 'active';
  const page = parseInt(query.get('page') || 0, 10);
  const pageSize = parseInt(query.get('pageSize') || 5, 10);
  const refreshTable = () => {
    const newQuery = new URLSearchParams(location.search);
    const newStatus = newQuery.get('status') || 'active';
    const newPage = parseInt(newQuery.get('page') || 0, 10);
    const newPageSize = parseInt(newQuery.get('pageSize') || 5, 10);
    getJobs({
      page: newPage,
      pageSize: newPageSize,
      status: newStatus,
    });
  };
  useInterval(refreshTable, 4000);
  const handleStatusChange = (event, newValue) => {
    const newQuery = new URLSearchParams(location.search);
    newQuery.set('status', newValue);
    newQuery.set('page', 0);
    setSelected([]);
    getJobs({
      page,
      pageSize,
      status: newValue,
    });
    history.push(`${pathname}?${newQuery.toString()}`);
  };

  const handleChangePage = (e, newPage) => {
    const newQuery = new URLSearchParams(location.search);
    newQuery.set('page', newPage);
    getJobs({
      page: newPage,
      pageSize,
      status,
    });
    history.push(`${pathname}?${newQuery.toString()}`);
  };
  const handleChangeRowsPerPage = (e) => {
    const newQuery = new URLSearchParams(location.search);
    newQuery.set('pageSize', e.target.value);
    getJobs({
      page,
      pageSize: e.target.value,
      status,
    });
    history.push(`${pathname}?${newQuery.toString()}`);
  };

  const data = jobs.data?.data || [];

  const handleSelectAllClick = (event) => {
    if (event.target.checked) {
      const newSelecteds = data.map((n) => n.id);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleCellClick = (event, name) => {
    const selectedIndex = selected.indexOf(name);
    let newSelected = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, name);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
        selected.slice(0, selectedIndex),
        selected.slice(selectedIndex + 1),
      );
    }

    setSelected(newSelected);
  };

  const handleBulkRetry = () => {
    client({
      url: `/queues/${match.params.queueName}/retries`,
      method: 'POST',
      data: {
        jobs: selected,
      },
    })
      .then(() => setSelected([]))
      .then(refreshTable);
  };

  const handleBulkPromote = () => {
    client({
      url: `/queues/${match.params.queueName}/promotes`,
      method: 'POST',
      data: {
        jobs: selected,
      },
    })
      .then(() => setSelected([]))
      .then(refreshTable);
  };

  const handleBulkRemove = () => {
    client({
      url: `/queues/${match.params.queueName}/removes`,
      method: 'POST',
      data: {
        jobs: selected,
      },
    })
      .then(() => setSelected([]))
      .then(refreshTable);
  };

  const { name, counts } = queue.data || {};

  return (
    <Grid container spacing={3}>
      <Grid container item xs={12} justify="space-between">
        <Breadcrumbs aria-label="breadcrumb">
          <Link component={RouterLink} color="inherit" to="/">
            Dashboard
          </Link>
          <Typography color="textPrimary">{name}</Typography>
        </Breadcrumbs>
        <div>
          <Button variant="outlined" onClick={pauseQueue}>
            Pause Queue
          </Button>
          <Button onClick={resumeQueue}>Resume Queue</Button>
          <Button onClick={cleanQueue}>Clean Queue</Button>
        </div>
      </Grid>
      <Grid item xs={12}>
        <StatusTabs
          value={status}
          counts={counts}
          onChange={handleStatusChange}
        />
      </Grid>
      <Grid item xs={12}>
        <Table
          title={name}
          page={page}
          selected={selected}
          onChangePage={handleChangePage}
          onCellClick={handleCellClick}
          onSelectAllClick={handleSelectAllClick}
          columns={[
            { title: 'ID', field: 'id' },
            { title: 'Job Name', field: 'name' },
            {
              title: 'Created At',
              field: 'timestamp',
              render: (value) =>
                value && (
                  <Tooltip
                    placement="top"
                    title={`${formatDistanceStrict(value, Date.now())} ago`}
                  >
                    <span>{formatISO(value)}</span>
                  </Tooltip>
                ),
            },
            {
              title: 'Started At',
              field: 'processedOn',
              render: (value) =>
                value && (
                  <Tooltip placement="top" title={formatISO(value)}>
                    <span>{formatDistanceStrict(value, Date.now())} ago</span>
                  </Tooltip>
                ),
            },
            {
              title: 'Completed At',
              field: 'finishedOn',
              render: (value) =>
                value ? (
                  <Tooltip placement="top" title={formatISO(value)}>
                    <span>{formatDistanceStrict(value, Date.now())} ago</span>
                  </Tooltip>
                ) : (
                  'Not completed'
                ),
            },
            {
              title: 'Delayed To',
              field: 'delayedTo',
              render: (value) =>
                value && (
                  <Tooltip placement="top" title={formatISO(value)}>
                    <span>{formatDistanceStrict(value, Date.now())} later</span>
                  </Tooltip>
                ),
            },
            {
              title: 'Progress',
              field: 'progress',
              render: (value) => <CircularProgress size={48} value={value} />,
            },
            {
              title: 'Attempts',
              field: 'attempts',
              render: (val, field) =>
                `${field.attemptsMade}/${field.attemptsTotal}`,
            },
            {
              title: 'Actions',
              field: 'actions',
              render: (val, field) => (
                <Button
                  size="small"
                  component={RouterLink}
                  startIcon={<VisibilityIcon />}
                  to={`${pathname}/${field.id}`}
                >
                  Details
                </Button>
              ),
            },
          ].filter((column) => FIELDS[status].includes(column.field))}
          rowsPerPage={pageSize}
          pageSizeOptions={[5, 20, 50, 100]}
          onChangeRowsPerPage={handleChangeRowsPerPage}
          totalCount={jobs.data?.totalCount}
          bulkActions={
            <div>
              {status === 'delayed' && (
                <Fragment>
                  <Button onClick={handleBulkPromote}>Promote</Button>
                  <Button onClick={handleBulkRemove}>Remove</Button>
                </Fragment>
              )}
              {status === 'failed' && (
                <Button onClick={handleBulkRetry}>Retry</Button>
              )}
            </div>
          }
          actions={
            <div>
              {status === 'delayed' && (
                <Button onClick={promoteAll}>Promote All</Button>
              )}
              {status === 'failed' && (
                <Button onClick={retryAll}>Retry All</Button>
              )}
            </div>
          }
          data={data}
        />
      </Grid>
    </Grid>
  );
}
Example #13
Source File: auctions.js    From lnft with GNU Affero General Public License v3.0 4 votes vote down vote up
setInterval(async () => {
  try {
    let { artworks } = await q(getFinishedAuctions, {
      now: formatISO(new Date()),
    });

    for (let i = 0; i < artworks.length; i++) {
      let artwork = artworks[i];
      let { bid } = artwork;

      await q(closeAuction, {
        id: artwork.id,
        artwork: {
          auction_start: null,
          auction_end: null,
        },
      });

      console.log("finalizing auction for", artwork.slug);
      console.log("reserve price", artwork.reserve_price);

      try {
        if (
          !(bid && bid.psbt) ||
          compareAsc(parseISO(bid.created_at), parseISO(artwork.auction_end)) >
            0 ||
          bid.amount < artwork.reserve_price
        )
          throw new Error("no bid");

        let combined = combine(artwork.auction_tx, bid.psbt);

        await check(combined);

        let psbt = await sign(combined);

        await broadcast(psbt);

        await q(releaseToken, {
          id: artwork.id,
          owner_id: bid.user.id,
          amount: bid.amount,
          hash: psbt.extractTransaction().getId(),
          psbt: psbt.toBase64(),
          asset: artwork.asking_asset,
          bid_id: bid.id,
          type: "release",
        });

        console.log("released to high bidder");
      } catch (e) {
        console.log("couldn't release to bidder,", e.message);

        await q(cancelBids, {
          id: artwork.id,
          start: artwork.auction_start,
          end: artwork.auction_end,
        });

        if (artwork.has_royalty) continue;

        try {
          let psbt = await sign(artwork.auction_release_tx);
          await broadcast(psbt);

          console.log("released to current owner");

          await q(releaseToken, {
            id: artwork.id,
            owner_id: artwork.owner.id,
            amount: 0,
            hash: psbt.extractTransaction().getId(),
            psbt: psbt.toBase64(),
            asset: artwork.asking_asset,
            type: "return",
          });
        } catch (e) {
          console.log("problem releasing", e);
        }
      }
    }
  } catch (e) {
    console.log(e);
  }
}, 2000);
Example #14
Source File: monitor.js    From lnft with GNU Affero General Public License v3.0 4 votes vote down vote up
updateTransactions = async (address, user_id) => {
  await wait(() => !updating[address]);
  updating[address] = true;

  let { transactions } = await q(getLastTransactionsForAddress, { address });
  let txns = (
    await getTxns(
      address,
      transactions.map((tx) => tx.hash)
    )
  ).reverse();

  if (txns.length)
    console.log(`updating ${txns.length} transactions for ${address}`);

  for (let i = 0; i < txns.length; i++) {
    let { txid, vin, vout, status } = txns[i];

    let hex;
    try {
      hex =
        hexcache[txid] || (await electrs.url(`/tx/${txid}/hex`).get().text());
      hexcache[txid] = hex;
    } catch (e) {
      await sleep(3000);
      hex =
        hexcache[txid] || (await electrs.url(`/tx/${txid}/hex`).get().text());
      hexcache[txid] = hex;
    }

    let total = {};

    for (let j = 0; j < vin.length; j++) {
      let { txid: prev, vout } = vin[j];

      try {
        let tx =
          txcache[prev] || (await electrs.url(`/tx/${prev}`).get().json());
        txcache[prev] = tx;

        let { asset, value, scriptpubkey_address: a } = tx.vout[vout];
        if (address === a) total[asset] = (total[asset] || 0) - parseInt(value);
      } catch (e) {
        console.log("problem finding input", prev, e);
      }
    }

    for (let k = 0; k < vout.length; k++) {
      let { asset, value, scriptpubkey_address: a } = vout[k];
      if (address === a) total[asset] = (total[asset] || 0) + parseInt(value);
    }

    let assets = Object.keys(total);

    for (let l = 0; l < assets.length; l++) {
      let asset = assets[l];
      let type = total[asset] < 0 ? "withdrawal" : "deposit";

      if (
        transactions.find(
          (tx) =>
            tx.user_id === user_id &&
            tx.hash === txid &&
            tx.asset === asset &&
            tx.type === type
        )
      )
        continue;

      let transaction = {
        address,
        user_id,
        asset,
        type,
        amount: total[asset],
        hash: txid,
        confirmed: status.confirmed,
        hex,
        json: JSON.stringify(txns[i]),
      };

      if (status.block_time)
        transaction.created_at = formatISO(new Date(1000 * status.block_time));

      try {
        let {
          insert_transactions_one: { id },
        } = await q(createTransaction, { transaction });
        console.log("inserting transaction", type, txid);
        transactions.push(transaction);
      } catch (e) {
        console.log(e, type, txid, asset, user_id);
        continue;
      }
    }
  }

  if (txns.length) console.log("done updating", address);
  delete updating[address];
  return txns;
}