lodash#maxBy TypeScript Examples

The following examples show how to use lodash#maxBy. 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: utils.tsx    From erda-ui with GNU Affero General Public License v3.0 7 votes vote down vote up
getTableList = (data: IPerm, scope: string, filterKey: string) => {
  let list = [] as any[];
  const countData = (curData: any, key = scope, depth = 0, prevData = {}) => {
    if (!curData) return;
    if (curData.role) {
      list.push({ ...prevData, action: { ...curData, key } });
    } else {
      const { name, ...rest } = curData;
      map(rest, (item, cKey) => {
        const curPrevData = { ...prevData, [`depth${depth}`]: { key, name } };
        countData(item, cKey, depth + 1, curPrevData);
      });
    }
  };
  countData(data);

  if (filterKey) {
    list = filter(list, (l) => some(l, (item) => item.key.includes(filterKey) || item.name.includes(filterKey)));
  }

  const maxDeepthObj = maxBy(list, (item) => Object.keys(item).length);
  const tableList = [] as any[];
  map(list, (item) => {
    const itemData = { ...item };
    map(maxDeepthObj, (val, key) => {
      if (!itemData[key]) {
        itemData[key] = {};
      }
    });
    const sortKeys = sortBy(Object.keys(itemData), (k) => (k.startsWith('depth') ? Number(k.slice(5)) : 1000));
    itemData.actionKey = map(sortKeys, (k) => itemData[k].key || '__').join('');
    tableList.push(itemData);
  });
  return map(
    sortBy(tableList, (item) => item.actionKey),
    ({ actionKey, ...rest }) => rest,
  );
}
Example #2
Source File: wallpaper_manager.ts    From SpaceEye with MIT License 6 votes vote down vote up
/**
     * Given a view, determine the optimal image needed.
     *
     * Image is selected based on the resolution of available screens.
     *
     * @param view - Satellite view
     * @param monitors - Computer monitors currently available
     * @returns Selected image
     */
    private static getOptimalImageFromView(view: SatelliteView, monitors: Display[]): ImageSource {
        // Get the best image size for each monitor
        const possibleImages = monitors.map(monitor =>
            WallpaperManager.selectImageSourceForMonitor(monitor, view.imageSources)
        )
        // Pick the biggest one
        const bestImage = maxBy(possibleImages, image => image.dimensions[0] * image.dimensions[1])!
        log.debug(`Optimal image for view "${view.id}": ${bestImage.id}`)
        return bestImage
    }
Example #3
Source File: yml-chart-utils.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getNodePostion = (data: IData[][], chartConfig: IChartConfig, external: IExternal) => {
  const { PADDING, NODE, MARGIN } = chartConfig;
  const nodeData = [] as IData[][];
  const isEdit = external && external.editing;
  const boxSizeArr = map(data, (item) => {
    return {
      width:
        PADDING.X * 2 +
        (item.length - 1) * MARGIN.X +
        sumBy(
          item,
          (subItem: IData) => (get(NODE, `${subItem[externalKey].nodeType}.WIDTH`) as unknown as number) || 0,
        ),
      height: get(NODE, `${get(item, `[0].${externalKey}.nodeType`)}.HEIGHT`) as unknown as number,
      itemWidth: get(NODE, `${get(item, `[0].${externalKey}.nodeType`)}.WIDTH`) as unknown as number,
    };
  });
  const maxWidthBox = (maxBy(boxSizeArr, (item) => item.width) as Obj) || { width: 0, height: 0 };
  const dataLen = data.length;
  let chartHeight = PADDING.Y;
  map(data, (item, index) => {
    const curBoxSize = boxSizeArr[index];
    const curData = [] as any[];
    let xDis = (curBoxSize.width < maxWidthBox.width ? (maxWidthBox.width - curBoxSize.width) / 2 : 0) + PADDING.X;
    map(item, (subItem, subIndex) => {
      const curNode = NODE[subItem[externalKey].nodeType] as any;
      curData.push({
        ...subItem,
        [externalKey]: {
          ...(subItem[externalKey] || {}),
          x: xDis + curNode.WIDTH / 2,
          y: chartHeight + curNode.HEIGHT / 2,
          nodeId: `node-${index}-${subIndex}`,
          width: curNode.WIDTH,
          height: curNode.HEIGHT,
        },
      });
      xDis += curNode.WIDTH + MARGIN.X;
    });
    chartHeight += curBoxSize.height + (dataLen === index + 1 ? 0 : MARGIN.Y);
    nodeData.push(curData);
  });
  chartHeight += PADDING.Y;
  return {
    chartHeight,
    chartWidth: isEdit ? maxWidthBox.width + maxWidthBox.itemWidth + MARGIN.X : maxWidthBox.width,
    nodeData,
  } as {
    chartHeight: number;
    chartWidth: number;
    nodeData: IExternalData[][];
  };
}
Example #4
Source File: txBuilder.ts    From nautilus-wallet with MIT License 6 votes vote down vote up
public async sign(context: SignContext): Promise<ErgoTx> {
    const sigmaRust = wasmModule.SigmaRust;
    const lastBlockHeader = maxBy(context.blockHeaders, (h) => h.height);
    const height = lastBlockHeader!.height;
    const recipient = MAINNET
      ? sigmaRust.Address.from_mainnet_str(this._to)
      : sigmaRust.Address.from_testnet_str(this._to);
    const changeAddress = MAINNET
      ? sigmaRust.Address.from_mainnet_str(context.bip32.deriveAddress(this._changeIndex).script)
      : sigmaRust.Address.from_testnet_str(context.bip32.deriveAddress(this._changeIndex).script);

    const unspentBoxes = sigmaRust.ErgoBoxes.from_boxes_json(this._boxes);
    const outputValue = this.getErgAmount();
    const tokens = this.buildTokenList();
    const txOutputs = this.buildOutputBoxes(outputValue, tokens, recipient, height);
    const fee = this.getFee();
    const boxSelector = new sigmaRust.SimpleBoxSelector();
    const targetBalance = sigmaRust.BoxValue.from_i64(
      outputValue.as_i64().checked_add(fee.as_i64())
    );
    const boxSelection = boxSelector.select(unspentBoxes, targetBalance, tokens);
    const unsigned = sigmaRust.TxBuilder.new(
      boxSelection,
      txOutputs,
      height,
      fee,
      changeAddress,
      sigmaRust.BoxValue.SAFE_USER_MIN()
    ).build();

    const signed = await this._sign(
      unsigned,
      unspentBoxes,
      sigmaRust.ErgoBoxes.from_boxes_json([]),
      context
    );

    return JSONBig.parse(signed.to_json());
  }
Example #5
Source File: siteUtils.ts    From aqualink-app with MIT License 6 votes vote down vote up
findInitialSitePosition = (
  sites: Site[],
  initialSiteId?: string
): LatLng | null => {
  const initialSite =
    (initialSiteId && findSiteById(sites, initialSiteId)) ||
    maxBy(
      sites,
      (site) =>
        `${site.collectionData?.tempWeeklyAlert || 0},${longDHW(
          site.collectionData?.dhw || null
        )}`
    );

  // If the polygon type is a Point, return its coordinates
  if (initialSite?.polygon.type === "Point") {
    return new LatLng(
      initialSite.polygon.coordinates[1],
      initialSite.polygon.coordinates[0]
    );
  }

  // If the polygon type is a Polygon, return the coordinates of its centroid
  if (initialSite?.polygon.type === "Polygon") {
    const centroidLat = meanBy(
      initialSite.polygon.coordinates[0],
      (coords) => coords[1]
    );
    const centroidLng = meanBy(
      initialSite.polygon.coordinates[0],
      (coords) => coords[0]
    );

    return new LatLng(centroidLat, centroidLng);
  }

  return null;
}
Example #6
Source File: utils.ts    From aqualink-app with MIT License 6 votes vote down vote up
calculateAxisLimits = (
  datasets: ChartProps["datasets"],
  startDate: ChartProps["startDate"],
  endDate: ChartProps["endDate"],
  temperatureThreshold: ChartProps["temperatureThreshold"]
) => {
  const timestampsToConsiderForXAxis = getDatasetsTimestamps(
    datasets?.filter((dataset) => dataset.considerForXAxisLimits)
  );
  const accumulatedYAxisData = flatten(
    map(datasets, ({ data }) =>
      map(
        filter(data, ({ value }) => !isNil(value)),
        ({ value }) => value
      )
    )
  );

  // x axis limits calculation
  const datasetsXMin = minBy(
    timestampsToConsiderForXAxis,
    (timestamp) => new Date(timestamp)
  );
  const datasetsXMax = maxBy(
    timestampsToConsiderForXAxis,
    (timestamp) => new Date(timestamp)
  );
  const xAxisMin = startDate || datasetsXMin;
  const xAxisMax = endDate || datasetsXMax;

  // y axis limits calculation
  const datasetsYMin = Math.min(...accumulatedYAxisData);
  const datasetsYMax = Math.max(...accumulatedYAxisData);
  const ySpacing = Math.ceil(
    Y_SPACING_PERCENTAGE * (datasetsYMax - datasetsYMin)
  ); // Set ySpacing as a percentage of the data range
  const yAxisMinTemp = datasetsYMin - ySpacing;
  const yAxisMaxTemp = datasetsYMax + ySpacing;
  const yAxisMin = Math.round(
    temperatureThreshold
      ? Math.min(yAxisMinTemp, temperatureThreshold - ySpacing)
      : yAxisMinTemp
  );
  const yAxisMax = Math.round(
    temperatureThreshold
      ? Math.max(yAxisMaxTemp, temperatureThreshold + ySpacing)
      : yAxisMaxTemp
  );

  return { xAxisMin, xAxisMax, yAxisMin, yAxisMax };
}
Example #7
Source File: helpers.ts    From aqualink-app with MIT License 6 votes vote down vote up
calculateCardMetrics = (
  from: string,
  to: string,
  data?: ValueWithTimestamp[],
  keyPrefix?: string
): CardColumn["rows"] => {
  const filteredData = filterSofarData(from, to)(data);

  return [
    {
      key: `${keyPrefix}-max`,
      value: filteredData?.[0]
        ? maxBy(filteredData, "value")?.value
        : undefined,
    },
    {
      key: `${keyPrefix}-mean`,
      value: filteredData?.[0] ? meanBy(filteredData, "value") : undefined,
    },
    {
      key: `${keyPrefix}-min`,
      value: filteredData?.[0]
        ? minBy(filteredData, "value")?.value
        : undefined,
    },
  ];
}
Example #8
Source File: manifest-handling.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
export function getFlexEnabledFlagForXMLFile(xmlPath: string): boolean {
  const manifestFilesForCurrentFolder = filter(
    Object.keys(manifestData),
    (manifestPath) => xmlPath.startsWith(dirname(manifestPath))
  );

  const closestManifestPath = maxBy(
    manifestFilesForCurrentFolder,
    (manifestPath) => manifestPath.length
  );

  if (closestManifestPath === undefined) {
    return false;
  }

  return manifestData[closestManifestPath].flexEnabled;
}
Example #9
Source File: github_runner.ts    From CIAnalyzer with MIT License 6 votes vote down vote up
private setRepoLastRun(repo: GithubConfigRepo, reports: WorkflowReport[]) {
    const workflowNameToReports = groupBy(reports, 'workflowName')
    for (const [workflowName, reports] of Object.entries(workflowNameToReports)) {
      const lastRunReport = maxBy(reports, 'buildNumber')
      if (lastRunReport) {
        this.store?.setLastRun(`${repo.fullname}-${workflowName}`, lastRunReport.buildNumber)
      }
    }
  }
Example #10
Source File: bitrise_analyzer.ts    From CIAnalyzer with MIT License 6 votes vote down vote up
parseBuildLog(BuildLogResponse: BuildLogResponse): StepLog[] {
    // Extract chunk that include summary table 
    const chunks = dropWhile(BuildLogResponse.log_chunks, (chunk) => !chunk.chunk.includes('bitrise summary'))
    if (!chunks) return []

    let rows = chunks.flatMap((chunk) => chunk.chunk.split('\n'))

    // Filter summary table rows only
    rows = dropWhile(rows, (row) => !row.includes('bitrise summary'))
    rows = takeWhile(rows, (row) => !row.includes('Total runtime'))

    const steps = rows
      // Filter row that include name and step
      .filter((row) => row.match(/\d+\s(sec|min)/))
      .map((row) => {
        // Step name
        const names = [...row.matchAll(/;1m(?<name>.+?)\u001b/g)].map((match) => match.groups?.name ?? '')
        const name = maxBy(names, (name) => name.length)
        // Duration
        const duration = row.match(/\d+(\.\d+)?\s(sec|min)/)

        return {
          name: name ? name.trim() : '',
          duration: duration ? duration[0].trim() : ''
        }
      })

    return steps
  }
Example #11
Source File: pivot-facet.ts    From S2 with MIT License 5 votes vote down vote up
private calculateColLeafNodesWidth(
    col: Node,
    colLeafNodes: Node[],
    rowHeaderWidth: number,
  ): number {
    const { colCfg, dataSet, filterDisplayDataItem } = this.cfg;
    // current.width =  get(colCfg, `widthByFieldValue.${current.value}`, current.width);
    const userDragWidth = get(
      get(colCfg, 'widthByFieldValue'),
      `${col.value}`,
      col.width,
    );
    if (userDragWidth) {
      return userDragWidth;
    }

    if (this.spreadsheet.getLayoutWidthType() === LayoutWidthTypes.Compact) {
      const {
        bolderText: colCellTextStyle,
        cell: colCellStyle,
        icon: colIconStyle,
      } = this.spreadsheet.theme.colCell;

      // leaf node rough width
      const cellFormatter = this.spreadsheet.dataSet.getFieldFormatter(
        col.field,
      );
      const leafNodeLabel = cellFormatter?.(col.value) ?? col.label;
      const iconWidth = this.getExpectedCellIconWidth(
        CellTypes.COL_CELL,
        this.spreadsheet.isValueInCols() &&
          this.spreadsheet.options.showDefaultHeaderActionIcon,
        colIconStyle,
      );
      const leafNodeRoughWidth =
        measureTextWidthRoughly(leafNodeLabel) + iconWidth;

      // will deal with real width calculation in multiple values render pr
      const multiData = dataSet.getMultiData(
        col.query,
        col.isTotals || col.isTotalMeasure,
      );
      const allDataLabels = multiData
        .map((data) => `${handleDataItem(data, filterDisplayDataItem)}`)
        ?.slice(0, 50);
      const maxDataLabel = maxBy(allDataLabels, (label) =>
        measureTextWidthRoughly(label),
      );

      // compare result
      const isLeafNodeWidthLonger =
        leafNodeRoughWidth > measureTextWidthRoughly(maxDataLabel);
      const maxLabel = isLeafNodeWidthLonger ? leafNodeLabel : maxDataLabel;
      const appendedWidth = isLeafNodeWidthLonger ? iconWidth : 0;

      DebuggerUtil.getInstance().logger(
        'Max Label In Col:',
        col.field,
        maxLabel,
      );

      return (
        measureTextWidth(maxLabel, colCellTextStyle) +
        colCellStyle.padding?.left +
        colCellStyle.padding?.right +
        appendedWidth
      );
    }
    // adaptive
    if (this.spreadsheet.isHierarchyTreeType()) {
      return this.getAdaptTreeColWidth(col, colLeafNodes);
    }
    return this.getAdaptGridColWidth(colLeafNodes, rowHeaderWidth);
  }
Example #12
Source File: pivot-facet.ts    From S2 with MIT License 5 votes vote down vote up
/**
   * 计算 compact 模式下 node 宽度
   *
   * |   fieldName  |
   *  _______________
   * | label - icon  | <- node
   * | label - icon  |
   * | label - icon  |
   *
   * @param node 目标节点
   * @returns 宽度
   */
  private getCompactGridRowWidth(node: Node): number {
    const { dataSet, spreadsheet } = this.cfg;
    const {
      bolderText: rowTextStyle,
      icon: rowIconStyle,
      cell: rowCellStyle,
    } = spreadsheet.theme.rowCell;
    const {
      bolderText: cornerTextStyle,
      icon: cornerIconStyle,
      cell: cornerCellStyle,
    } = spreadsheet.theme.cornerCell;
    const { field, isLeaf } = node;

    // calc rowNodeWitdh
    const rowIconWidth = this.getExpectedCellIconWidth(
      CellTypes.ROW_CELL,
      !spreadsheet.isValueInCols() &&
        isLeaf &&
        spreadsheet.options.showDefaultHeaderActionIcon,
      rowIconStyle,
    );
    const allLabels = dataSet
      .getDimensionValues(field)
      ?.slice(0, 50)
      .map(
        (dimValue) =>
          this.spreadsheet.dataSet.getFieldFormatter(field)?.(dimValue) ??
          dimValue,
      );
    const maxLabel = maxBy(allLabels, (label) => `${label}`.length);
    const rowNodeWidth =
      measureTextWidth(maxLabel, rowTextStyle) +
      rowIconWidth +
      rowCellStyle.padding.left +
      rowCellStyle.padding.right;

    // calc corner fieldNameNodeWidth
    const fieldName = dataSet.getFieldName(field);
    const cornerIconWidth = this.getExpectedCellIconWidth(
      CellTypes.CORNER_CELL,
      false,
      cornerIconStyle,
    );
    const fieldNameNodeWidth =
      measureTextWidth(fieldName, cornerTextStyle) +
      cornerIconWidth +
      cornerCellStyle.padding.left +
      cornerCellStyle.padding.right;

    DebuggerUtil.getInstance().logger(
      'Max Label In Row:',
      field,
      rowNodeWidth > fieldNameNodeWidth ? maxLabel : fieldName,
    );

    // return max
    return Math.max(rowNodeWidth, fieldNameNodeWidth);
  }
Example #13
Source File: table-facet.ts    From S2 with MIT License 5 votes vote down vote up
private calculateColLeafNodesWidth(
    col: Node,
    adaptiveColWitdth: number,
  ): number {
    const { colCfg, dataSet, spreadsheet } = this.cfg;
    const layoutWidthType = this.spreadsheet.getLayoutWidthType();

    const userDragWidth = get(
      get(colCfg, 'widthByFieldValue'),
      `${col.value}`,
      col.width,
    );
    let colWidth: number;
    if (userDragWidth) {
      colWidth = userDragWidth;
    } else {
      if (layoutWidthType === LayoutWidthTypes.Compact) {
        const datas = dataSet.getDisplayDataSet();
        const colLabel = col.label;

        const allLabels =
          datas?.map((data) => `${data[col.key]}`)?.slice(0, 50) || []; // 采样取了前50
        allLabels.push(colLabel);
        const maxLabel = maxBy(allLabels, (label) =>
          measureTextWidthRoughly(label),
        );

        const { bolderText: colCellTextStyle } = spreadsheet.theme.colCell;
        const { text: dataCellTextStyle, cell: cellStyle } =
          spreadsheet.theme.dataCell;

        DebuggerUtil.getInstance().logger(
          'Max Label In Col:',
          col.field,
          maxLabel,
        );

        // 最长的 Label 如果是列名,按列名的标准计算宽度
        if (colLabel === maxLabel) {
          colWidth =
            measureTextWidth(maxLabel, colCellTextStyle) +
            getOccupiedWidthForTableCol(
              this.spreadsheet,
              col,
              spreadsheet.theme.colCell,
            );
        } else {
          // 额外添加一像素余量,防止 maxLabel 有多个同样长度情况下,一些 label 不能展示完全
          const EXTRA_PIXEL = 1;
          colWidth =
            measureTextWidth(maxLabel, dataCellTextStyle) +
            cellStyle.padding.left +
            cellStyle.padding.right +
            EXTRA_PIXEL;
        }
      } else {
        colWidth = adaptiveColWitdth;
      }

      if (col.field === SERIES_NUMBER_FIELD) {
        colWidth = this.getSeriesNumberWidth();
      }
    }

    return colWidth;
  }
Example #14
Source File: collections.spec.ts    From aqualink-app with MIT License 5 votes vote down vote up
getLatestData = (data: DeepPartial<TimeSeries>[]) =>
  _(data)
    .groupBy((o) => o.metric)
    .mapValues((o) => maxBy(o, (obj) => obj.timestamp)?.value)
    .toJSON()
Example #15
Source File: jenkins_runner.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
private setRepoLastRun(jobname: string, reports: WorkflowReport[]) {
    const lastRunReport = maxBy(reports, 'buildNumber')
    if (lastRunReport) {
      this.store?.setLastRun(jobname, lastRunReport.buildNumber)
    }
  }
Example #16
Source File: circleci_runner_v1.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
private setRepoLastRun(reponame: string, reports: WorkflowReport[]) {
    const lastRunReport = maxBy(reports, 'buildNumber')
    if (lastRunReport) {
      this.store?.setLastRun(reponame, lastRunReport.buildNumber)
    }
  }
Example #17
Source File: BarChart.tsx    From aqualink-app with MIT License 5 votes vote down vote up
BarChart = ({ collection, classes }: BarChartProps) => {
  const theme = useTheme();
  const nLevels = 5;
  const groupedByAlert = groupBy(
    collection.sites,
    (site) => site.collectionData?.tempWeeklyAlert
  );

  const mostFrequentAlert = maxBy(Object.values(groupedByAlert), "length");

  return (
    <>
      <Box color={theme.palette.grey[500]} margin="0 0 14px 97px">
        <Typography variant="subtitle2">Sites by Alert Level</Typography>
      </Box>
      <Box flexGrow={1} width="100%">
        <Grid
          className={classes.alertsWrapper}
          container
          justify="space-between"
          direction="column"
        >
          {reverse(times(nLevels, Number)).map((level) => {
            const interval = findIntervalByLevel(level);
            return (
              <Grid item key={interval.label}>
                <Grid container alignItems="center">
                  <Grid item>
                    <Box
                      className={classes.alertLabelWrapper}
                      bgcolor={interval.color}
                    >
                      <Typography
                        className={classes.alertLabel}
                        color={
                          level === 3 || level === 4
                            ? "textPrimary"
                            : "textSecondary"
                        }
                        variant="subtitle2"
                      >
                        {interval.label}
                      </Typography>
                    </Box>
                  </Grid>
                  <Grid className={classes.barWrapper} item>
                    <Box width="100%" display="flex" alignItems="center">
                      <Box
                        width={percentageCalculator(
                          groupedByAlert?.[level]?.length || 0,
                          mostFrequentAlert?.length
                        )}
                        height="28px"
                        bgcolor={interval.color}
                      />
                      <Typography className={classes.siteCount}>
                        {groupedByAlert?.[level]?.length || 0}
                      </Typography>
                    </Box>
                  </Grid>
                </Grid>
              </Grid>
            );
          })}
        </Grid>
      </Box>
    </>
  );
}
Example #18
Source File: bitrise_runner.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
private setRepoLastRun(appSlug: string, reports: WorkflowReport[]) {
    const lastRunReport = maxBy(reports, 'buildNumber')
    if (lastRunReport) {
      this.store?.setLastRun(appSlug, lastRunReport.buildNumber)
    }
  }
Example #19
Source File: downloaded_image.ts    From SpaceEye with MIT License 5 votes vote down vote up
static async getNewestDownloadedImage(id: number): Promise<DownloadedImage | undefined> {
        const allImages = await DownloadedImage.getDownloadedImages()
        const matchingImages = allImages.filter(image => image.imageId === id)
        return maxBy(matchingImages, image => image.timestamp.valueOf())
    }
Example #20
Source File: upload-sheet-data.ts    From aqualink-app with MIT License 4 votes vote down vote up
uploadTimeSeriesData = async (
  filePath: string,
  fileName: string,
  siteId: string,
  surveyPointId: string | undefined,
  sourceType: SourceType,
  repositories: Repositories,
  failOnWarning?: boolean,
  mimetype?: Mimetype,
) => {
  // // TODO
  // // - Add foreign key constraint to sources on site_id
  console.time(`Upload datafile ${fileName}`);
  const { site, surveyPoint } = surveyPointId
    ? await getSiteAndSurveyPoint(
        parseInt(siteId, 10),
        parseInt(surveyPointId, 10),
        repositories.siteRepository,
        repositories.surveyPointRepository,
      )
    : {
        site: await getSite(parseInt(siteId, 10), repositories.siteRepository),
        surveyPoint: undefined,
      };

  const existingSourceEntity = await repositories.sourcesRepository.findOne({
    relations: ['surveyPoint', 'site'],
    where: {
      site: { id: siteId },
      surveyPoint: surveyPointId || null,
      type: sourceType,
    },
  });

  const sourceEntity =
    existingSourceEntity ||
    (await repositories.sourcesRepository.save({
      type: sourceType,
      site,
      surveyPoint,
    }));

  if (
    sourceType === SourceType.SONDE ||
    sourceType === SourceType.METLOG ||
    sourceType === SourceType.HOBO
  ) {
    const workSheetsFromFile = xlsx.parse(filePath, { raw: true });
    const workSheetData = workSheetsFromFile[0]?.data;
    const { ignoredHeaders, importedHeaders } = validateHeaders(
      fileName,
      workSheetData,
      sourceType,
    );

    if (failOnWarning && ignoredHeaders.length > 0) {
      throw new BadRequestException(
        `${fileName}: The columns ${ignoredHeaders
          .map((header) => `"${header}"`)
          .join(
            ', ',
          )} are not configured for import yet and cannot be uploaded.`,
      );
    }

    const signature = await md5Fle(filePath);

    const uploadExists = await repositories.dataUploadsRepository.findOne({
      where: {
        signature,
        site,
        surveyPoint,
        sensorType: sourceType,
      },
    });

    if (uploadExists) {
      throw new ConflictException(
        `${fileName}: A file upload named '${uploadExists.file}' with the same data already exists`,
      );
    }
    console.time(`Get data from sheet ${fileName}`);
    const results = findSheetDataWithHeader(
      fileName,
      workSheetData,
      sourceType,
      mimetype,
    );
    console.timeEnd(`Get data from sheet ${fileName}`);

    console.time(`Remove duplicates and empty values ${fileName}`);
    const data = uniqBy(
      results
        .reduce((timeSeriesObjects: any[], object) => {
          const { timestamp } = object;
          return [
            ...timeSeriesObjects,
            ...Object.keys(object)
              .filter((k) => k !== 'timestamp')
              .map((key) => {
                return {
                  timestamp,
                  value: parseFloat(object[key]),
                  metric: key,
                  source: sourceEntity,
                };
              }),
          ];
        }, [])
        .filter((valueObject) => {
          if (!isNaN(parseFloat(valueObject.value))) {
            return true;
          }
          logger.log('Excluding incompatible value:');
          logger.log(valueObject);
          return false;
        }),
      ({ timestamp, metric, source }) =>
        `${timestamp}, ${metric}, ${source.id}`,
    );
    console.timeEnd(`Remove duplicates and empty values ${fileName}`);

    const minDate = get(
      minBy(data, (item) => new Date(get(item, 'timestamp')).getTime()),
      'timestamp',
    );
    const maxDate = get(
      maxBy(data, (item) => new Date(get(item, 'timestamp')).getTime()),
      'timestamp',
    );

    // Initialize google cloud service, to be used for media upload
    const googleCloudService = new GoogleCloudService();

    // Note this may fail. It would still return a location, but the file may not have been uploaded
    const fileLocation = googleCloudService.uploadFileAsync(
      filePath,
      sourceType,
      'data_uploads',
      'data_upload',
    );

    const dataUploadsFile = await repositories.dataUploadsRepository.save({
      file: fileName,
      signature,
      sensorType: sourceType,
      site,
      surveyPoint,
      minDate,
      maxDate,
      metrics: importedHeaders,
      fileLocation,
    });

    const dataAsTimeSeries = data.map((x: any) => {
      return {
        timestamp: x.timestamp,
        value: x.value,
        metric: x.metric,
        source: x.source,
        dataUpload: dataUploadsFile,
      };
    });

    // Data is too big to added with one bulk insert so we batch the upload.
    console.time(`Loading into DB ${fileName}`);
    const batchSize = 100;
    logger.log(`Saving time series data in batches of ${batchSize}`);
    const inserts = chunk(dataAsTimeSeries, batchSize).map(
      async (batch: any[]) => {
        try {
          await repositories.timeSeriesRepository
            .createQueryBuilder('time_series')
            .insert()
            .values(batch)
            // If there's a conflict, replace data with the new value.
            // onConflict is deprecated, but updating it is tricky.
            // See https://github.com/typeorm/typeorm/issues/8731?fbclid=IwAR2Obg9eObtGNRXaFrtKvkvvVSWfvjtHpFu-VEM47yg89SZcPpxEcZOmcLw
            .onConflict(
              'ON CONSTRAINT "no_duplicate_data" DO UPDATE SET "value" = excluded.value',
            )
            .execute();
        } catch {
          console.warn('The following batch failed to upload:');
          console.warn(batch);
        }
        return true;
      },
    );

    // Return insert promises and print progress updates
    const actionsLength = inserts.length;
    await Bluebird.Promise.each(inserts, (props, idx) => {
      logger.log(`Saved ${idx + 1} out of ${actionsLength} batches`);
    });
    console.timeEnd(`Loading into DB ${fileName}`);
    logger.log('loading complete');

    refreshMaterializedView(repositories);

    console.timeEnd(`Upload datafile ${fileName}`);
    return ignoredHeaders;
  }

  return [];
}
Example #21
Source File: ChartWithTooltip.tsx    From aqualink-app with MIT License 4 votes vote down vote up
function ChartWithTooltip({
  chartSettings,
  children,
  className,
  style,
  ...rest
}: PropsWithChildren<ChartWithTooltipProps>) {
  const { siteId, surveys, timeZone, startDate, endDate, datasets } = rest;
  const chartDataRef = useRef<Line>(null);

  const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
  const [tooltipData, setTooltipData] = useState<TooltipData>({
    siteId,
    date: "",
    datasets: [],
    surveyId: null,
  });
  const [showTooltip, setShowTooltip] = useState<boolean>(false);

  const customTooltip =
    (ref: React.RefObject<Line>) => (tooltipModel: ChartTooltipModel) => {
      const chart = ref.current;
      if (!chart?.chartInstance.canvas) {
        return;
      }

      const date = tooltipModel.dataPoints?.[0]?.xLabel;
      if (typeof date !== "string") return;

      const dateObject = new Date(date);

      const surveyId = findSurveyFromDate(date, surveys);

      const datasetsDates = getDatasetsTimestamps(datasets);
      const minDataDate = minBy(datasetsDates, (item) => new Date(item));
      const maxDataDate = maxBy(datasetsDates, (item) => new Date(item));

      const closestDatasetData = getTooltipClosestData(dateObject, datasets);

      const nValues = closestDatasetData
        .map(({ data }) => head(data)?.value)
        .filter(isNumber).length;

      // Chart.js displays tooltips in a parallel to the X axis preference, meaning
      // that it will appear right or left from the chart point. We want to change that,
      // and display the tooltip in a Y axis preference, and more specifically, above the chart point.
      const position = chart.chartInstance.canvas.getBoundingClientRect();

      // We center the tooltip in the X axis by subtracting half its width.
      const left = position.left + tooltipModel.caretX - TOOLTIP_WIDTH / 2;

      // We increase the tooltip's top, so that it lands above the chart point. The amount by
      // which we increase varies based on how many values we display and if there is a survey at that point,
      // as we display a `VIEW SURVEY` button.
      const top =
        position.top +
        tooltipModel.caretY -
        ((surveyId ? 30 : 0) + nValues * 20 + 50);

      // We display the tooltip only if there are data to display at this point and it lands
      // between the chart's X axis limits.
      if (
        nValues > 0 &&
        moment(date).isBetween(
          moment(startDate || minDataDate),
          moment(endDate || maxDataDate),
          undefined,
          "[]"
        )
      ) {
        setTooltipPosition({ top, left });
        setTooltipData({
          ...tooltipData,
          date,
          surveyId,
          datasets: closestDatasetData,
        });
        setShowTooltip(true);
      }
    };

  const hideTooltip = () => {
    setShowTooltip(false);
  };

  // Hide tooltip on scroll to avoid dragging it on the page.
  if (showTooltip) {
    window.addEventListener("scroll", hideTooltip);
  }

  return (
    <div className={className} style={style} onMouseLeave={hideTooltip}>
      {children}
      <Chart
        {...rest}
        chartRef={chartDataRef}
        chartSettings={{
          tooltips: {
            enabled: false,
            intersect: false,
            custom: customTooltip(chartDataRef),
          },
          legend: {
            display: false,
          },
          // we could use mergeWith here too, but currently nothing would use it.
          ...chartSettings,
        }}
      />
      {showTooltip ? (
        <div
          className="chart-tooltip"
          id="chart-tooltip"
          style={{
            position: "fixed",
            top: tooltipPosition.top,
            left: tooltipPosition.left,
          }}
        >
          <Tooltip
            {...tooltipData}
            siteTimeZone={timeZone}
            userTimeZone={Intl.DateTimeFormat().resolvedOptions().timeZone}
          />
        </div>
      ) : null}
    </div>
  );
}