lodash#countBy TypeScript Examples

The following examples show how to use lodash#countBy. 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: turn.ts    From fishbowl with MIT License 6 votes vote down vote up
export function drawableCards(
  turns: CurrentGameSubscription["games"][0]["turns"],
  cards: CurrentGameSubscription["games"][0]["cards"]
) {
  const allCompletedCardIds = flatMap(turns, (turn) => turn.completed_card_ids)

  const maxCount = max(values(countBy(allCompletedCardIds)))

  let completedCardIdsForRound = filter(
    groupBy(allCompletedCardIds),
    (arr) => arr.length === maxCount
  ).map((arr) => arr[0])

  const remainingIdsForRound = difference(
    cards.map((card) => card.id),
    completedCardIdsForRound
  )

  if (remainingIdsForRound.length === 0) {
    return cards
  } else {
    return filter(cards, (card) => remainingIdsForRound.includes(card.id))
  }
}
Example #2
Source File: daily-summary.ts    From backstage with Apache License 2.0 6 votes vote down vote up
function countStatusesPerDay(builds: ReadonlyArray<Build>) {
  const days = groupBy(builds, value => startOfDay(value.requestedAt));

  const foundStatuses = new Set<string>();

  const values = Object.entries(days).map(([epoch, buildsThisDay]) => {
    const byStatus = countBy(buildsThisDay, 'status');

    const value: Epoch & StatusesDatapoint = {
      __epoch: parseInt(epoch, 10),
      ...byStatus,
    };

    Object.keys(byStatus).forEach(status => {
      foundStatuses.add(status);
    });

    return value;
  });

  return {
    statuses: sortStatuses([...foundStatuses]),
    values,
  };
}
Example #3
Source File: logs.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
export function calculateFieldStats(rows: LogRowModel[], extractor: RegExp): LogLabelStatsModel[] {
  // Consider only rows that satisfy the matcher
  const rowsWithField = rows.filter(row => extractor.test(row.entry));
  const rowCount = rowsWithField.length;

  // Get field value counts for eligible rows
  const countsByValue = countBy(rowsWithField, r => {
    const row: LogRowModel = r;
    const match = row.entry.match(extractor);

    return match ? match[1] : null;
  });
  return getSortedCounts(countsByValue, rowCount);
}
Example #4
Source File: logs.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
export function calculateLogsLabelStats(rows: LogRowModel[], label: string): LogLabelStatsModel[] {
  // Consider only rows that have the given label
  const rowsWithLabel = rows.filter(row => row.labels[label] !== undefined);
  const rowCount = rowsWithLabel.length;

  // Get label value counts for eligible rows
  const countsByValue = countBy(rowsWithLabel, row => (row as LogRowModel).labels[label]);
  return getSortedCounts(countsByValue, rowCount);
}
Example #5
Source File: logs.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
export function calculateStats(values: any[]): LogLabelStatsModel[] {
  const nonEmptyValues = values.filter(value => value !== undefined && value !== null);
  const countsByValue = countBy(nonEmptyValues);
  return getSortedCounts(countsByValue, nonEmptyValues.length);
}
Example #6
Source File: get-graphql-input-type.ts    From prisma-nestjs-graphql with MIT License 5 votes vote down vote up
/**
 * Find input type for graphql field decorator.
 */
export function getGraphqlInputType(
  inputTypes: DMMF.SchemaArgInputType[],
  pattern?: string,
) {
  let result: DMMF.SchemaArgInputType | undefined;

  inputTypes = inputTypes.filter(t => !['null', 'Null'].includes(String(t.type)));
  inputTypes = uniqWith(inputTypes, isEqual);

  if (inputTypes.length === 1) {
    return inputTypes[0];
  }

  const countTypes = countBy(inputTypes, x => x.location);
  const isOneType = Object.keys(countTypes).length === 1;

  if (isOneType) {
    result = inputTypes.find(x => x.isList);
    if (result) {
      return result;
    }
  }

  if (pattern) {
    if (pattern.startsWith('matcher:') || pattern.startsWith('match:')) {
      const { 1: patternValue } = pattern.split(':', 2);
      const isMatch = outmatch(patternValue, { separator: false });
      result = inputTypes.find(x => isMatch(String(x.type)));
      if (result) {
        return result;
      }
    }
    result = inputTypes.find(x => String(x.type).includes(pattern));
    if (result) {
      return result;
    }
  }

  result = inputTypes.find(x => x.location === 'inputObjectTypes');
  if (result) {
    return result;
  }

  if (
    countTypes.enumTypes &&
    countTypes.scalar &&
    inputTypes.some(x => x.type === 'Json' && x.location === 'scalar')
  ) {
    result = inputTypes.find(x => x.type === 'Json' && x.location === 'scalar');
    if (result) {
      return result;
    }
  }

  throw new TypeError(
    // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
    `Cannot get matching input type from ${
      inputTypes.map(x => x.type).join(', ') || 'zero length inputTypes'
    }`,
  );
}
Example #7
Source File: Read.tsx    From sc2-planner with MIT License 4 votes vote down vote up
readBuildOrderWithoutTimer(startAt = 0): void {
        // Prepare BO instructions
        const instructionList: Instruction[] = []
        const lastEvent = this.props.gamelogic.eventLog[this.props.gamelogic.eventLog.length - 1]
        let prevInstruction: Instruction | undefined
        let boEndTime = 0
        for (const event of this.props.gamelogic.eventLog) {
            const when = (event.start * 1000) / 22.4 - startAt * 1000
            if (when >= 0) {
                const isLastMessage = event === lastEvent
                boEndTime = Math.max(boEndTime, event?.end || event.start)
                if (prevInstruction && prevInstruction.when === when) {
                    prevInstruction.events.push(event)
                    prevInstruction.isLastMessage = isLastMessage
                } else {
                    prevInstruction = {
                        events: [event],
                        when,
                        ingameWhen: Math.floor(event.start / 22.4),
                        isLastMessage,
                    }
                    instructionList.push(prevInstruction)
                }
            }
        }

        const shownInstructions: JSX.Element[][] = []
        instructionList.forEach((instruction, i) => {
            const events = instruction.events
            const counts = countBy(events, "name")
            const uniqItems = uniqBy(events, "name")

            // Set voice timers
            const eventsWithPlurals = uniqItems.map((event) => {
                const name = event.name.replace(/_/g, " ")
                const count = counts[event.name]
                return count === 1 ? name : `${count} ${name}s`
            })
            let text =
                eventsWithPlurals.length === 1
                    ? eventsWithPlurals[0]
                    : `${eventsWithPlurals.slice(0, -1).join(", ")}, and ${last(eventsWithPlurals)}`
            if (instruction.isLastMessage) {
                text += ` Then the build order ends at ${this.secondsToTimestamp(
                    Math.floor(boEndTime / 22.4),
                    "seconds"
                )}`
            }

            this.speak(text, instruction.when, instruction.isLastMessage)

            // Prepare logs
            const eventElements: JSX.Element[] = uniqItems.map((event, j) => {
                const count = counts[event.name]
                const image = getImageOfItem(event)
                const element = <img key={`read_icon_${i}_${j}`} src={image} alt={event.name} />
                return count === 1 ? (
                    element
                ) : (
                    <span key={`read_group_${i}_${j}`} className={CLASSES.readIcon}>
                        {element} ✖{count}
                    </span>
                )
            })
            const elements = (
                <div key={`instruc_${i}`} className={CLASSES.readInstruction}>
                    <div className={CLASSES.readIconGroup}>{eventElements}</div>
                    <div className={CLASSES.readTime}>
                        {this.secondsToTimestamp(instruction.ingameWhen)}
                    </div>
                </div>
            )
            for (
                let ingameTime = instructionList[i - 2]?.ingameWhen || 0;
                ingameTime < instruction.ingameWhen;
                ingameTime++
            ) {
                if (shownInstructions[ingameTime] === undefined) {
                    shownInstructions[ingameTime] = []
                }
                shownInstructions[ingameTime].push(elements)
            }
        })

        // Set log timers
        this.ingameTime = startAt
        for (let ingameTime = startAt; shownInstructions[ingameTime]; ingameTime++) {
            this.lineHandlers.push(
                setTimeout(() => {
                    this.ingameTime = ingameTime
                    this.props.log({
                        element: (
                            <div key="instructions" className={CLASSES.readContainer}>
                                <div className={CLASSES.readCurrentTime}>
                                    {this.secondsToTimestamp(ingameTime)}
                                </div>
                                <div className={CLASSES.readInstructionList}>
                                    {shownInstructions[ingameTime]}
                                </div>
                            </div>
                        ),
                        hideCloseButton: true,
                    })
                }, (ingameTime - startAt) * 1000)
            )
        }
        this.lineHandlers.push(
            setTimeout(() => this.props.log(), (shownInstructions.length - startAt + 7) * 1000)
        )
    }
Example #8
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function MapView({ classes }: MapViewProps) {
  const [defaultLayerAttempted, setDefaultLayerAttempted] = useState(false);
  const selectedLayers = useSelector(layersSelector);
  const { startDate: selectedDate } = useSelector(dateRangeSelector);
  const layersLoading = useSelector(isLoading);
  const datesLoading = useSelector(areDatesLoading);
  const loading = layersLoading || datesLoading;
  const dispatch = useDispatch();
  const [isAlertFormOpen, setIsAlertFormOpen] = useState(false);
  const serverAvailableDates = useSelector(availableDatesSelector);
  const [firstSymbolId, setFirstSymbolId] = useState<string | undefined>(
    undefined,
  );
  const selectedLayersWithDateSupport = selectedLayers
    .filter((layer): layer is DateCompatibleLayer =>
      dateSupportLayerTypes.includes(layer.type),
    )
    .filter(layer => isMainLayer(layer.id, selectedLayers));

  const boundaryLayerData = useSelector(layerDataSelector(boundaryLayer.id)) as
    | LayerData<BoundaryLayerProps>
    | undefined;

  const adminBoundariesExtent = useMemo(() => {
    if (!boundaryLayerData) {
      return undefined;
    }
    return bbox(boundaryLayerData.data) as Extent; // we get extents of admin boundaries to give to the api.
  }, [boundaryLayerData]);

  const { urlParams, updateHistory } = useUrlHistory();
  // let users know if their current date doesn't exist in possible dates
  const urlDate = urlParams.get('date');
  const mapOnClick = useMapOnClick(setIsAlertFormOpen);

  useEffect(() => {
    /*
      This useEffect hook keeps track of parameters obtained from url and loads layers according
      to the hazardLayerId and baselineLayerId values. If the date field is found, the application
      status is also updated. There are guards in case the values are not valid, such as invalid
      date or layerids.
      */
    const HAZARD_LAYER_PARAM = 'hazardLayerId';
    const BASELINE_LAYER_PARAM = 'baselineLayerId';

    const hazardLayerId = urlParams.get(HAZARD_LAYER_PARAM);
    const baselineLayerId = urlParams.get(BASELINE_LAYER_PARAM);

    /*
      In case we don't have hazard or baseline layers we will use the default
      layer provided in the appConfig defined within `prism.json` file.
     */
    if (!hazardLayerId && !baselineLayerId) {
      const defaultLayer = get(appConfig, 'defaultLayer');

      if (defaultLayer) {
        if (Object.keys(LayerDefinitions).includes(defaultLayer)) {
          const layer = LayerDefinitions[defaultLayer as LayerKey];
          const urlLayerKey =
            layer.type === 'admin_level_data'
              ? BASELINE_LAYER_PARAM
              : HAZARD_LAYER_PARAM;
          updateHistory(urlLayerKey, defaultLayer);
        } else if (!defaultLayerAttempted) {
          dispatch(
            addNotification({
              message: `Invalid default layer identifier: ${defaultLayer}`,
              type: 'error',
            }),
          );
          setDefaultLayerAttempted(true);
        }
      }
    }

    if (
      (!hazardLayerId && !baselineLayerId) ||
      Object.keys(serverAvailableDates).length === 0
    ) {
      return;
    }

    const selectedLayersIds: LayerKey[] = selectedLayers.map(layer => layer.id);

    // Check for layers in url and add them.
    [hazardLayerId, baselineLayerId].forEach(id => {
      if (!id || selectedLayersIds.includes(id as LayerKey)) {
        return;
      }

      if (Object.keys(LayerDefinitions).includes(id)) {
        const layer = LayerDefinitions[id as LayerKey];
        dispatch(addLayer(layer));

        if (selectedDate && !urlDate) {
          updateHistory(
            'date',
            moment(selectedDate).format(DEFAULT_DATE_FORMAT),
          );
        }
      } else {
        dispatch(
          addNotification({
            message: `Invalid layer identifier: ${id}`,
            type: 'error',
          }),
        );
      }
    });

    if (
      urlDate &&
      moment(urlDate).valueOf() !== selectedDate &&
      selectedLayersIds.includes(hazardLayerId as LayerKey)
    ) {
      const dateInt = moment(urlDate).valueOf();
      if (Number.isNaN(dateInt)) {
        dispatch(
          addNotification({
            message: 'Invalid date found. Using most recent date',
            type: 'warning',
          }),
        );
      } else {
        dispatch(updateDateRange({ startDate: dateInt }));
      }
    }

    // Validate hazardLayer.
  }, [
    urlParams,
    urlDate,
    dispatch,
    selectedLayers,
    serverAvailableDates,
    selectedDate,
    updateHistory,
    defaultLayerAttempted,
  ]);

  useEffect(() => {
    dispatch(loadAvailableDates());
    const displayBoundaryLayers = getDisplayBoundaryLayers();

    // we must load boundary layer here for two reasons
    // 1. Stop showing two loading screens on startup - Mapbox renders its children very late, so we can't rely on BoundaryLayer to load internally
    // 2. Prevent situations where a user can toggle a layer like NSO (depends on Boundaries) before Boundaries finish loading.
    displayBoundaryLayers.map(l => dispatch(addLayer(l)));
    displayBoundaryLayers.map(l => dispatch(loadLayerData({ layer: l })));
  }, [dispatch]);

  // calculate possible dates user can pick from the currently selected layers
  const selectedLayerDates: number[] = useMemo(() => {
    if (selectedLayersWithDateSupport.length === 0) {
      return [];
    }

    /*
       takes all the dates possible for every layer and counts the amount of times each one is duplicated.
       if a date's duplicate amount is the same as the number of layers active, then this date is compatible with all layers selected.
    */
    const selectedLayerDatesDupCount = countBy(
      selectedLayersWithDateSupport
        .map(layer => getPossibleDatesForLayer(layer, serverAvailableDates))
        .filter(value => value) // null check
        .flat()
        .map(value => moment(value).format(DEFAULT_DATE_FORMAT)),
    );
    /*
      Only keep the dates which were duplicated the same amount of times as the amount of layers active...and convert back to array.
     */
    return Object.keys(
      pickBy(
        selectedLayerDatesDupCount,
        dupTimes => dupTimes >= selectedLayersWithDateSupport.length,
      ),
      // convert back to number array after using YYYY-MM-DD strings in countBy
    ).map(dateString => moment.utc(dateString).set({ hour: 12 }).valueOf());
  }, [selectedLayersWithDateSupport, serverAvailableDates]);

  // close popups and show warning notifications
  useEffect(() => {
    // when we switch layers, or change dates, close any active pop-ups
    dispatch(hidePopup());

    // let users know if the layers selected are not possible to view together.
    if (
      selectedLayerDates.length === 0 &&
      selectedLayersWithDateSupport.length !== 0
    ) {
      dispatch(
        addNotification({
          message: 'No dates overlap with the selected layers.',
          type: 'warning',
        }),
      );
    }

    if (selectedDate && urlDate && moment(urlDate).valueOf() !== selectedDate) {
      selectedLayersWithDateSupport.forEach(layer => {
        const momentSelectedDate = moment(selectedDate);

        // we convert to date strings, so hh:ss is irrelevant
        if (
          // TODO - Replace the serverAvailableDates check by a loading flag
          // to know if dates haven't been loaded yet?
          Object.keys(serverAvailableDates).length !== 0 &&
          !getPossibleDatesForLayer(layer, serverAvailableDates)
            .map(date => moment(date).format(DEFAULT_DATE_FORMAT))
            .includes(momentSelectedDate.format(DEFAULT_DATE_FORMAT))
        ) {
          const closestDate = findClosestDate(selectedDate, selectedLayerDates);

          updateHistory('date', closestDate.format(DEFAULT_DATE_FORMAT));

          dispatch(
            addNotification({
              message: `No data was found for the layer '${
                layer.title
              }' on ${momentSelectedDate.format(
                DEFAULT_DATE_FORMAT,
              )}. The closest date ${closestDate.format(
                DEFAULT_DATE_FORMAT,
              )} has been loaded instead`,
              type: 'warning',
            }),
          );
        }
      });
    }
  }, [
    dispatch,
    selectedDate,
    selectedLayerDates,
    selectedLayersWithDateSupport,
    serverAvailableDates,
    updateHistory,
    urlParams,
    urlDate,
  ]);

  const {
    map: { latitude, longitude, zoom },
  } = appConfig;
  // Saves a reference to base MapboxGL Map object in case child layers need access beyond the React wrappers.
  // Jump map to center here instead of map initial state to prevent map re-centering on layer changes
  const saveAndJumpMap = (map: Map) => {
    // Find the first symbol on the map to make sure we add layers below them.
    const { layers } = map.getStyle();
    // Find the first symbol on the map to make sure we add layers below them.
    setFirstSymbolId(layers?.find(layer => layer.type === 'symbol')?.id);
    dispatch(setMap(() => map));
    map.jumpTo({ center: [longitude, latitude], zoom });
  };

  const style = new URL(
    process.env.REACT_APP_DEFAULT_STYLE ||
      'https://api.maptiler.com/maps/0ad52f6b-ccf2-4a36-a9b8-7ebd8365e56f/style.json?key=y2DTSu9yWiu755WByJr3',
  );

  return (
    <Grid item className={classes.container}>
      {loading && (
        <div className={classes.loading}>
          <CircularProgress size={100} />
        </div>
      )}
      <MapboxMap
        // eslint-disable-next-line react/style-prop-object
        style={style.toString()}
        onStyleLoad={saveAndJumpMap}
        containerStyle={{
          height: '100%',
        }}
        onClick={mapOnClick}
      >
        {selectedLayers.map(layer => {
          const component: ComponentType<{
            layer: any;
            before?: string;
          }> = componentTypes[layer.type];
          return createElement(component, {
            key: layer.id,
            layer,
            before: firstSymbolId,
          });
        })}
        {/* These are custom layers which provide functionality and are not really controllable via JSON */}
        <AnalysisLayer before={firstSymbolId} />
        <SelectionLayer before={firstSymbolId} />
        <MapTooltip />
      </MapboxMap>
      <Grid
        container
        justify="space-between"
        className={classes.buttonContainer}
      >
        <Grid item>
          <Analyser extent={adminBoundariesExtent} />
          <GotoBoundaryDropdown />
          {appConfig.alertFormActive ? (
            <AlertForm isOpen={isAlertFormOpen} setOpen={setIsAlertFormOpen} />
          ) : null}
          <DataViewer />
        </Grid>
        <Grid item>
          <Grid container spacing={1}>
            <Download />
            <Legends layers={selectedLayers} extent={adminBoundariesExtent} />
          </Grid>
        </Grid>
      </Grid>
      {selectedLayerDates.length > 0 && (
        <DateSelector availableDates={selectedLayerDates} />
      )}
    </Grid>
  );
}
Example #9
Source File: GetStatistics.ts    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export async function GetStatistics(
  categoryGroups: CategoryGroup[] = [],
  stories: Story[] = []
): Promise<any> {
  const storyList = getAllStoryListV2(categoryGroups, stories);
  const allCommonBricks = filter(storyList, (v) => v.type === "brick");
  const allCommonTemplates = reject(storyList, (v) => v.type === "brick");

  const commonBricksName = map(allCommonBricks, "id");
  const commonTemplatesName = map(allCommonTemplates, "id");
  const bootstrap = await AuthSdk.bootstrap();
  // 不统计iframe嵌套老站点的微应用
  const microAppsWithoutLegacy = reject(bootstrap.storyboards, [
    "app.legacy",
    "iframe",
  ]);
  const microAppsBricks = microAppsWithoutLegacy.map((microApp) => {
    const allBricksName = scanBricksInStoryboard(microApp, false);
    const allTemplatesName = scanTemplatesInStoryboard(microApp, false);
    const countAndSortAllBricks = reverse(
      sortBy(
        map(
          countBy(allBricksName, (item) => {
            return item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
              type: "brick",
              isCommon: ifCommon(k, commonBricksName),
            };
          }
        ),
        "count"
      )
    );
    const countAndSortAllTemplates = reverse(
      sortBy(
        map(
          countBy(allTemplatesName, (item) => {
            return item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
              type: "template",
              isCommon: ifCommon(k, commonTemplatesName),
            };
          }
        ),
        "count"
      )
    );
    microApp.countBricksAndTemplates = reverse(
      sortBy([...countAndSortAllBricks, ...countAndSortAllTemplates], "count")
    );
    microApp.countCommonBricksAndTemplates = filter(
      [...countAndSortAllBricks, ...countAndSortAllTemplates],
      "isCommon"
    ).length;

    const statisticsAll = [
      ...map(allBricksName, (v) => {
        return {
          type: "brick",
          id: v,
          isCommon: ifCommon(v, commonBricksName),
          app: microApp.app,
        };
      }),
      ...map(allTemplatesName, (v) => ({
        type: "template",
        id: v,
        isCommon: ifCommon(v, commonTemplatesName),
        app: microApp.app,
      })),
    ];
    microApp.statisticsAll = statisticsAll;
    microApp.commonUsedRate = getPercentage(
      filter(statisticsAll, (item) => item.isCommon).length /
        statisticsAll.length
    );
    return microApp;
  });
  const microAppsStatisticsMap = keyBy(microAppsBricks, "app.id");
  const microAppsSubMenu = {
    title: "微应用列表",
    menuItems: map(microAppsBricks, (item) => {
      return {
        text: item.app.name,
        to: `/developers/statistics/micro-app-statistics/${item.app.id}`,
      };
    }),
  };
  const microAppStatisticsRedirectTo = `${microAppsSubMenu.menuItems[0].to}`;
  const allMicroAppsBricksAndTemplate = flatten(
    map(microAppsBricks, "statisticsAll")
  );
  const allMicroAppsBricks = map(
    filter(allMicroAppsBricksAndTemplate, (item) => item.type === "brick"),
    "id"
  );
  const allMicroAppsTemplates = map(
    filter(allMicroAppsBricksAndTemplate, (item) => item.type === "template"),
    "id"
  );

  // 统计所有构件
  const allBricksAndTemplateGroupBy = groupBy(
    allMicroAppsBricksAndTemplate,
    (item) => item.type + "," + item.id
  );
  const countAllBricksAndTemplate = map(
    uniqBy(allMicroAppsBricksAndTemplate, (item) => item.type + "," + item.id),
    (v) => {
      return {
        id: v.id,
        type: v.type,
        isCommon: v.isCommon,
        count: allBricksAndTemplateGroupBy[v.type + "," + v.id].length,
        appList: map(
          uniqBy(
            allBricksAndTemplateGroupBy[v.type + "," + v.id],
            (v) => v.app.id
          ),
          "app"
        ),
      };
    }
  );

  // 排名前二十的构件
  let countCommonBricksUsed: any[] = reverse(
    sortBy(
      filter(
        map(
          countBy(allMicroAppsBricks, (item) => {
            return includes(commonBricksName, item) && item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
            };
          }
        ),
        (item) => item.id !== "false" && item.id !== "div"
      ),
      "count"
    )
  ).slice(0, 20);
  countCommonBricksUsed = map(countCommonBricksUsed, (item) => {
    const found = find(allCommonBricks, ["id", item.id]);
    if (found) {
      item.author = found.subTitle;
      item.type = found.type;
      item.url = "/developers/brick-book/" + item.type + "/" + item.id;
    }
    return item;
  });
  // 排名前二十的模板
  let countCommonTemplatesUsed: any[] = reverse(
    sortBy(
      filter(
        map(
          countBy(allMicroAppsTemplates, (item) => {
            return includes(commonTemplatesName, item) && item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
            };
          }
        ),
        (item) => item.id !== "false"
      ),
      "count"
    )
  ).slice(0, 20);
  countCommonTemplatesUsed = map(countCommonTemplatesUsed, (item) => {
    const found = find(allCommonTemplates, ["id", item.id]);
    item.author = found.subTitle;
    item.type = found.type;
    item.url = "/developers/brick-book/" + item.type + "/" + item.id;
    return item;
  });
  const topBricksAndTemplates = reverse(
    sortBy([...countCommonBricksUsed, ...countCommonTemplatesUsed], "count")
  ).slice(0, 20);
  const result = {
    microAppsCount: bootstrap.storyboards.length,
    microAppsWithoutLegacyCount: microAppsWithoutLegacy.length,
    iframeMicroApps:
      bootstrap.storyboards.length - microAppsWithoutLegacy.length,
    allBricksAndTemplatesCount:
      uniq(allMicroAppsBricks).length + uniq(allMicroAppsTemplates).length,
    commonBricksAndTemplatesCount:
      commonBricksName.length + allCommonTemplates.length,
    commonBricksAndTemplatesUsedRate: getPercentage(
      (filter(allMicroAppsBricks, (item) => {
        return includes(commonBricksName, item);
      }).length +
        filter(allMicroAppsTemplates, (item) => {
          return includes(commonTemplatesName, item);
        }).length) /
        (allMicroAppsBricks.length + allMicroAppsTemplates.length)
    ),
    microAppsPieChart: {
      title: "微应用总数:" + bootstrap.storyboards.length,
      data: {
        legendList: ["全新微应用", "iframe嵌套微应用"],
        seriesData: [
          {
            name: "全新微应用",
            value: microAppsWithoutLegacy.length,
          },
          {
            name: "iframe嵌套微应用",
            value: bootstrap.storyboards.length - microAppsWithoutLegacy.length,
          },
        ],
      },
    },
    microAppsSubMenu,
    microAppStatisticsRedirectTo,
    microAppsStatisticsMap,
    topBricksAndTemplates,
    countAllBricksAndTemplate,
    packageNames: uniq(
      compact(
        [...allMicroAppsBricks, ...allMicroAppsTemplates].map(
          (v) => v.split(".")?.[0]
        )
      )
    ),
  };
  return result;
}
Example #10
Source File: LogsTable.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
function LogsTable({ data, showLogDetails }: Props) {
  const [currentView, setCurrentView] = useState(() => getInitialView())

  const [selectedSources, setSelectedSources] = useState<FilterSelection[]>([])
  const [selectedLevels, setSelectedLevels] = useState<FilterSelection[]>([])

  const [searchResults, setSearchResults] = useState<ProcessedLog[]>([])
  const [searchString, setSearchString] = useState("")

  const search = useCallback(async () => {
    const newData = data.slice(_lastNumLogs, data.length)
    _searchIndex.add(newData)
    _lastNumLogs = data.length
    const searchResults = (await _searchIndex.search(
      searchString
    )) as ProcessedLog[]
    setSearchResults(searchResults)
    // @ts-ignore
  }, [searchString, data.length]) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    if (searchString && data.length) search()
  }, [data.length, searchString, search])

  function setView(view: View) {
    setCurrentView(view)
    setUserViewPreference(view)
  }

  const unfiltered = searchString ? searchResults : data
  const sources = data.reduce((acc, curr) => acc.add(curr.name), new Set())

  let groupedData: GroupedData = {}
  logger.log("unfiltered logs", unfiltered)

  let resultData = (unfiltered || []).filter((result) =>
    applyFilters(result, {
      level: selectedLevels.map(({ value }) => value),
      name: selectedSources.map(({ value }) => value),
    })
  )

  if (currentView === "group-pod") {
    const podNames = arrayLikeToArray(sources).map(
      (name: string) => name.toLowerCase().split(POD_NAME_SPLIT_CHAR)[0]
    )
    groupedData = {}
    podNames.forEach((podName: string) => {
      const pod: Partial<Pod> = {}
      pod.data = (resultData || []).filter(
        (log) => log.name && log.name.toLowerCase().startsWith(podName)
      )

      if (!pod.data.length) return
      pod.levels = countBy(pod.data, "level") as Levels
      groupedData[podName] = pod as Pod
    })
  } else if (currentView === "group-level") {
    groupedData = {}
    LEVELS.forEach((level: string) => {
      const levelItem: Partial<Pod> = {}

      levelItem.data = (resultData || []).filter((log) => log.level === level)

      if (!levelItem.data.length) return
      groupedData[level] = levelItem as Pod
    })
  }

  const firstCol = 300
  const secondCol = 300

  return (
    <Card className="mb-4 flex-col">
      <div className="p-0">
        <div className="p-3 flex">
          <div data-name="logStreamFilters">
            <MultiFilterSelect
              clearAfter
              options={Object.values(viewOptions)}
              onFilterChange={(option) => setView(option[0].value as View)}
              className="logstream-select mb-2 mr-0 mb-md-0 mr-md-2"
              placeholder={
                currentView === "table" ? (
                  <span data-name="logStreamViewSelectedOption">
                    <i className="material-icons mr-2">table_rows</i>Table View
                  </span>
                ) : (
                  <span data-name="logStreamViewSelectedOption">
                    <i className="material-icons mr-2">view_list</i>
                    {viewOptions[currentView].label}
                  </span>
                )
              }
              isSearchable={false}
            />
            {currentView === "table" && (
              <MultiFilterSelect
                isMulti
                options={toOption(sources)}
                onFilterChange={setSelectedSources}
                className="logstream-select mb-2 mr-0 mb-md-0 mr-md-2"
                placeholder={
                  <span data-name="logStreamSourceSelectedOption">
                    <i className="material-icons mr-2">mediation</i>All Sources
                  </span>
                }
              />
            )}
            <MultiFilterSelect
              isMulti
              options={toOption(LEVELS)}
              onFilterChange={setSelectedLevels}
              className="logstream-select mb-2 mr-0 mb-md-0 mr-md-2"
              placeholder={
                <span data-name="logStreamLevelSelectedOption">
                  <i className="material-icons mr-2">bar_chart</i>All Levels
                </span>
              }
            />
            <MultiFilterSelect
              clearAfter
              options={saveOptions}
              onFilterChange={(option) => {
                saveLogData(data, option[0].value as Format)
              }}
              className="logstream-select mb-2 mr-0 mb-md-0 mr-md-2"
              placeholder={
                <span data-name="logStreamActionsSelect">
                  <i className="material-icons mr-2">download</i>Download Logs
                </span>
              }
              isSearchable={false}
            />
          </div>
          <div>
            <ExpandingSearchbar
              placeholder="search logs..."
              value={searchString}
              onChange={setSearchString}
            />
          </div>
        </div>
      </div>
      {currentView === "table" ? (
        <LogsList
          firstCol={firstCol}
          secondCol={secondCol}
          data={resultData}
          showLogDetails={showLogDetails}
          small={false}
        />
      ) : (
        <GroupedLogs
          firstCol={firstCol}
          secondCol={secondCol}
          grouping={currentView}
          groupedData={groupedData}
          showLogDetails={showLogDetails}
        />
      )}
    </Card>
  )
}