lodash#chunk TypeScript Examples

The following examples show how to use lodash#chunk. 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: util.ts    From erda-ui with GNU Affero General Public License v3.0 7 votes vote down vote up
getIPItemOption = (subnet: string, _toMask = 32) => {
  const ipNum = getSubnetNum(subnet);
  if (!validIp(ipNum.slice(0, 4))) return Array(4).fill([0]);
  const mask = +(ipNum.pop() as number); // 网段掩码
  const toMask = _toMask > 32 ? 32 : _toMask;
  const itemRange = [] as number[][];
  if (toMask >= mask && validIp(ipNum)) {
    const [fixLen, floatLen] = [mask, toMask];
    const binaryArr = ip2Binary(ipNum);
    const flatBinArr = binaryArr.join('').split('');
    const newBinArr = [] as number[][];
    map(flatBinArr, (item, index) => {
      if (fixLen >= index + 1) {
        // 固定位
        newBinArr.push([+item]);
      } else if (floatLen >= index + 1) {
        // 浮动位
        newBinArr.push([0, 1]);
      } else {
        // 末尾固定位0
        newBinArr.push([0]);
      }
    });
    map(chunk(newBinArr, 8), (item, index) => {
      itemRange[index] = sortBy(map(serialArray(item), (bin) => parseInt(bin, 2)));
    });
  }
  return itemRange;
}
Example #2
Source File: utils.tsx    From next-basics with GNU General Public License v3.0 7 votes vote down vote up
getCalendarData = (
  mode: ModeType,
  year: number,
  month: number,
  day?: number
): any[] => {
  if (mode === "month") {
    const monthData = [];
    const curMonthDays = getCurrentMonthDays(year, month);
    const startDate = getDate(year, month, 1).startOf("week");
    const endDate = getDate(year, month, curMonthDays).endOf("week");
    do {
      const curDate = moment(startDate);
      monthData.push({
        inView: curDate.isSame(`${year}-${month}`, "month"),
        date: curDate,
      });
    } while (startDate.add(1, "d").isSameOrBefore(endDate));
    return chunk(monthData, 7);
  } else if (mode === "week") {
    const weekData = [];
    const startDate = getDate(year, month, day).startOf("week");
    const endDate = getDate(year, month, day).endOf("week");
    do {
      weekData.push({ inView: true, date: moment(startDate) });
    } while (startDate.add(1, "d").isSameOrBefore(endDate));
    return [weekData];
  }
}
Example #3
Source File: sqs.ts    From MDDL with MIT License 6 votes vote down vote up
putMessages = async <T = any>(
  messages: Message<T>[],
  queueUrl: string,
) => {
  const groups = chunk(messages, 10)
  for (const group of groups) {
    const entries = group.map(
      ({ Id, Data, MessageDeduplicationId, MessageGroupId }) => ({
        Id,
        MessageBody: JSON.stringify(Data),
        MessageDeduplicationId,
        MessageGroupId,
      }),
    )
    const result = await sqsCLient
      .sendMessageBatch(
        {
          QueueUrl: queueUrl,
          Entries: entries,
        },
        undefined,
      )
      .promise()
    if (result.Failed.length) {
      throw new Error(
        'An error occurred while putting audit log message batch ' +
          result.Failed.map(
            (f) => `${f.Code} - ${f.Message || ''} (${f.Id})`,
          ).join(' '),
      )
    }
  }
}
Example #4
Source File: singlePubkeyRegistered.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public async initialSync(
        startBlock: number,
        endBlock: number
    ): Promise<void> {
        const events = await this.getEvents(startBlock, endBlock);
        console.info(
            `Block ${startBlock} -- ${endBlock}\t${events.length} new single public key registrations`
        );
        await chunk(events, 10).reduce(async (prev, eventsChunk) => {
            await prev;
            await Promise.all(
                eventsChunk.map(e => this.handleSinglePubkeyRegistered(e))
            );
            await this.commitUpdate();
        }, Promise.resolve());
    }
Example #5
Source File: batchPubkeyRegistered.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public async initialSync(
        startBlock: number,
        endBlock: number
    ): Promise<void> {
        const events = await this.getEvents(startBlock, endBlock);
        console.info(
            `Block ${startBlock} -- ${endBlock}\t${events.length} new batch public key registrations`
        );

        await chunk(events, 10).reduce(async (prev, eventsChunk) => {
            await prev;
            await Promise.all(
                eventsChunk.map(e => this.handleBatchPubkeyRegistered(e))
            );
            await this.commitUpdate();
        }, Promise.resolve());
    }
Example #6
Source File: eosBancorCalc.ts    From webapp with MIT License 6 votes vote down vote up
export function poolsInMemo(receiver: string): StringPool[] {
  const splitted = chunk(receiver.split(" "), 2).map(([pool, destSymbol]) => {
    const isMultiContract = pool.includes(":");
    if (isMultiContract) {
      const [multiContract, poolSymbol] = pool.split(":");
      return {
        pool: multiContract,
        poolToken: poolSymbol,
        destSymbol
      };
    } else {
      return {
        pool,
        destSymbol
      };
    }
  });
  return splitted;
}
Example #7
Source File: contractWrappers.ts    From webapp with MIT License 6 votes vote down vote up
mapSeries = async <T, Y>(
  arr: T[],
  count: number,
  iterator: (v: T) => Y
): Promise<(Y | undefined)[]> => {
  let res: (Y | undefined)[] = [];
  const chunked = chunk(arr, count);
  for (const chunk of chunked) {
    const response = await Promise.all(chunk.map(iterator)).catch(
      () => undefined
    );
    if (response === undefined) {
      res = [...res, undefined];
    } else {
      res = [...res, ...response];
    }
  }
  return res;
}
Example #8
Source File: zero-config-translate.ts    From attranslate with MIT License 6 votes vote down vote up
async translateStrings(args: TServiceArgs) {
    const remoteConfig = await fetchRemoteConfig();

    const clientOptions: ClientOptions = {
      credentials: {
        client_email: remoteConfig.client_email,
        private_key: remoteConfig.private_key,
      },
    };
    const client = new TranslationServiceClient(clientOptions);
    const batches: TString[][] = chunk(args.strings, 10);
    const results = await Promise.all(
      batches.map((batch: TString[]) =>
        this.translateBatch(batch, client, args, remoteConfig.project_id)
      )
    );
    return flatten(results);
  }
Example #9
Source File: google-translate.ts    From attranslate with MIT License 6 votes vote down vote up
async translateStrings(args: TServiceArgs) {
    if (!args.serviceConfig) {
      logFatal(
        "Set '--serviceConfig' to a path that points to a GCloud service account JSON file"
      );
    }
    checkNotDir(args.serviceConfig, { errorHint: "serviceConfig" });
    const keyFile = readRawJson<GCloudKeyFile>(args.serviceConfig).object;
    if (!keyFile.project_id) {
      logFatal(
        `serviceConfig ${getDebugPath(
          args.serviceConfig
        )} does not contain a project_id`
      );
    }
    const projectId: string = keyFile.project_id;

    const clientOptions: ClientOptions = {
      keyFile: args.serviceConfig,
    };
    const client = new TranslationServiceClient(clientOptions);
    const batches: TString[][] = chunk(args.strings, 10);
    const results = await Promise.all(
      batches.map((batch: TString[]) =>
        this.translateBatch(batch, client, args, projectId)
      )
    );
    return flatten(results);
  }
Example #10
Source File: explorerService.ts    From nautilus-wallet with MIT License 6 votes vote down vote up
public async getAddressesBalance(
    addresses: string[],
    options = { chunkBy: 20 }
  ): Promise<AssetBalance[]> {
    if (options.chunkBy <= 0 || options.chunkBy >= addresses.length) {
      const raw = await this.getAddressesBalanceFromChunk(addresses);
      return this._parseAddressesBalanceResponse(raw);
    }

    const chunks = chunk(addresses, options.chunkBy);
    let balances: AddressAPIResponse<ExplorerV1AddressBalanceResponse>[] = [];
    for (const c of chunks) {
      balances = balances.concat(await this.getAddressesBalanceFromChunk(c));
    }

    return this._parseAddressesBalanceResponse(balances);
  }
Example #11
Source File: explorerService.ts    From nautilus-wallet with MIT License 6 votes vote down vote up
public async getUsedAddresses(addresses: string[], options = { chunkBy: 20 }): Promise<string[]> {
    if (options.chunkBy <= 0 || options.chunkBy >= addresses.length) {
      return this.getUsedAddressesFromChunk(addresses);
    }

    const chunks = chunk(addresses, options.chunkBy);
    let used: string[] = [];
    for (const c of chunks) {
      used = used.concat(await this.getUsedAddressesFromChunk(c));
    }

    return used;
  }
Example #12
Source File: azure-translator.ts    From attranslate with MIT License 6 votes vote down vote up
async translateStrings(args: TServiceArgs): Promise<TResult[]> {
    const apiKey = args.serviceConfig;
    if (!apiKey) {
      logFatal("Set '--serviceConfig' to an Azure API key");
    }
    const batches = chunk(args.strings, 50);
    const results = await Promise.all(
      batches.map((batch) => this.translateBatch(batch, args, apiKey))
    );
    return flatten(results);
  }
Example #13
Source File: search-indexing.ts    From one-platform with MIT License 6 votes vote down vote up
export default async function startIndexing() {
  const apps = await TemplateHelper.listApps();
  const searchMaps = await SearchMaps.find().exec();
  const indexedData = searchMaps.map(async (searchMap) => {
    const app = apps.filter((tapp: any) => tapp._id === searchMap.appId);

    const authHeader = `${searchMap.authorization.authType} ${searchMap.authorization.credentials}`;
    const response = await TemplateHelper.fetchApi(
      authHeader,
      searchMap.apiUrl,
      searchMap.body,
      searchMap.apiQueryParams,
      searchMap.method,
    );
    return response[Object.keys(response)[0]].map((data: any) => ({
      content_type: app.name ?? 'unknown',
      id: getValueBySelector(data, searchMap.fieldMap.id, { stringify: true, fallback: null }),
      title: getValueBySelector(data, searchMap.fieldMap.title, { stringify: true, fallback: null }),
      abstract: getValueBySelector(data, searchMap.fieldMap.abstract, { stringify: true, fallback: null }),
      description: getValueBySelector(data, searchMap.fieldMap.description, { stringify: true, fallback: null }),
      icon: getValueBySelector(data, searchMap.fieldMap.icon, { stringify: true, fallback: null }),
      uri: getValueBySelector(data, searchMap.fieldMap.uri, { stringify: true, fallback: null }),
      tags: getValueBySelector(data, searchMap.fieldMap.tags, { stringify: true, fallback: null }),
      createdBy: getValueBySelector(data, searchMap.fieldMap.createdBy, { stringify: true, fallback: null }),
      createdDate: getValueBySelector(data, searchMap.fieldMap.createdDate, { stringify: true, fallback: null }),
      lastModifiedBy: getValueBySelector(data, searchMap.fieldMap.lastModifiedBy, { stringify: true, fallback: null }),
      lastModifiedDate: getValueBySelector(data, searchMap.fieldMap.lastModifiedDate, { stringify: true, fallback: null }),
    }));
  });

  /* Create chunks of 100 indexes each and send */
  return Promise.all(chunk(indexedData, 100)
    .map((documents) => index({
      dataSource: SEARCH_DATA_SOURCE,
      documents,
    }, 3)));
}
Example #14
Source File: search-sync.ts    From crossfeed with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
handler = async (commandOptions: CommandOptions) => {
  const { organizationId, domainId } = commandOptions;

  console.log('Running searchSync');
  await connectToDatabase();

  const client = new ESClient();

  const qs = Domain.createQueryBuilder('domain')
    .leftJoinAndSelect('domain.organization', 'organization')
    .leftJoinAndSelect('domain.vulnerabilities', 'vulnerabilities')
    .leftJoinAndSelect('domain.services', 'services')
    .having('domain.syncedAt is null')
    .orHaving('domain.updatedAt > domain.syncedAt')
    .orHaving('organization.updatedAt > domain.syncedAt')
    .orHaving(
      'COUNT(CASE WHEN vulnerabilities.updatedAt > domain.syncedAt THEN 1 END) >= 1'
    )
    .orHaving(
      'COUNT(CASE WHEN services.updatedAt > domain.syncedAt THEN 1 END) >= 1'
    )
    .groupBy('domain.id, organization.id, vulnerabilities.id, services.id')
    .select(['domain.id']);

  if (organizationId) {
    // This parameter is used for testing only
    qs.where('organization.id=:org', { org: organizationId });
  }

  const domainIds = (await qs.getMany()).map((e) => e.id);
  console.log(`Got ${domainIds.length} domains.`);
  if (domainIds.length) {
    const domainIdChunks = chunk(domainIds, DOMAIN_CHUNK_SIZE);
    for (const domainIdChunk of domainIdChunks) {
      const domains = await Domain.find({
        where: { id: In(domainIdChunk) },
        relations: ['services', 'organization', 'vulnerabilities']
      });
      console.log(`Syncing ${domains.length} domains...`);
      await pRetry(() => client.updateDomains(domains), {
        retries: 3,
        randomize: true
      });

      await Domain.createQueryBuilder('domain')
        .update(Domain)
        .set({ syncedAt: new Date(Date.now()) })
        .where({ id: In(domains.map((e) => e.id)) })
        .execute();
    }
    console.log('Domain sync complete.');
  } else {
    console.log('Not syncing any domains.');
  }
}
Example #15
Source File: s3-sync.ts    From lift with MIT License 5 votes vote down vote up
/**
 * Synchronize a local folder to a S3 bucket.
 *
 * @return True if some changes were uploaded.
 */
export async function s3Sync({
    aws,
    localPath,
    targetPathPrefix,
    bucketName,
}: {
    aws: AwsProvider;
    localPath: string;
    targetPathPrefix?: string;
    bucketName: string;
}): Promise<{ hasChanges: boolean; fileChangeCount: number }> {
    let hasChanges = false;
    let fileChangeCount = 0;
    const filesToUpload: string[] = await listFilesRecursively(localPath);
    const existingS3Objects = await s3ListAll(aws, bucketName, targetPathPrefix);

    // Upload files by chunks
    let skippedFiles = 0;
    for (const batch of chunk(filesToUpload, 2)) {
        await Promise.all(
            batch.map(async (file) => {
                const targetKey = targetPathPrefix !== undefined ? path.posix.join(targetPathPrefix, file) : file;
                const fileContent = fs.readFileSync(path.posix.join(localPath, file));

                // Check that the file isn't already uploaded
                if (targetKey in existingS3Objects) {
                    const existingObject = existingS3Objects[targetKey];
                    const etag = computeS3ETag(fileContent);
                    if (etag === existingObject.ETag) {
                        skippedFiles++;

                        return;
                    }
                }

                getUtils().log.verbose(`Uploading ${file}`);
                await s3Put(aws, bucketName, targetKey, fileContent);
                hasChanges = true;
                fileChangeCount++;
            })
        );
    }
    if (skippedFiles > 0) {
        getUtils().log.verbose(`Skipped uploading ${skippedFiles} unchanged files`);
    }

    const targetKeys = filesToUpload.map((file) =>
        targetPathPrefix !== undefined ? path.posix.join(targetPathPrefix, file) : file
    );
    const keysToDelete = findKeysToDelete(Object.keys(existingS3Objects), targetKeys);
    if (keysToDelete.length > 0) {
        keysToDelete.map((key) => {
            getUtils().log.verbose(`Deleting ${key}`);
            fileChangeCount++;
        });
        await s3Delete(aws, bucketName, keysToDelete);
        hasChanges = true;
    }

    return { hasChanges, fileChangeCount };
}
Example #16
Source File: sqs.ts    From lift with MIT License 5 votes vote down vote up
export async function retryMessages(
    aws: AwsProvider,
    queueUrl: string,
    dlqUrl: string,
    messages: Message[]
): Promise<{
    numberOfMessagesRetried: number;
    numberOfMessagesNotRetried: number;
    numberOfMessagesRetriedButNotDeleted: number;
}> {
    if (messages.length === 0) {
        return {
            numberOfMessagesRetried: 0,
            numberOfMessagesNotRetried: 0,
            numberOfMessagesRetriedButNotDeleted: 0,
        };
    }

    const sendBatches = chunk(messages, 10);
    const sendResults = await Promise.all(
        sendBatches.map((batch) =>
            aws.request<SendMessageBatchRequest, SendMessageBatchResult>("SQS", "sendMessageBatch", {
                QueueUrl: queueUrl,
                Entries: batch.map((message) => {
                    if (message.MessageId === undefined) {
                        throw new Error(`Found a message with no ID`);
                    }

                    return {
                        Id: message.MessageId,
                        MessageAttributes: message.MessageAttributes,
                        MessageBody: message.Body as string,
                    };
                }),
            })
        )
    );

    const messagesToDelete = messages.filter((message) => {
        const isMessageInFailedList = sendResults.some(({ Failed }) =>
            Failed.some((failedMessage) => message.MessageId === failedMessage.Id)
        );

        return !isMessageInFailedList;
    });

    const deleteBatches = chunk(messagesToDelete, 10);
    const deletionResults = await Promise.all(
        deleteBatches.map((batch) =>
            aws.request<DeleteMessageBatchRequest, DeleteMessageBatchResult>("SQS", "deleteMessageBatch", {
                QueueUrl: dlqUrl,
                Entries: batch.map((message) => {
                    return {
                        Id: message.MessageId as string,
                        ReceiptHandle: message.ReceiptHandle as string,
                    };
                }),
            })
        )
    );

    const numberOfMessagesRetried = deletionResults.reduce((total, { Successful }) => total + Successful.length, 0);
    const numberOfMessagesNotRetried = sendResults.reduce((total, { Failed }) => total + Failed.length, 0);
    const numberOfMessagesRetriedButNotDeleted = deletionResults.reduce(
        (total, { Failed }) => total + Failed.length,
        0
    );

    if (numberOfMessagesRetriedButNotDeleted > 0) {
        getUtils().log.warning(
            `${numberOfMessagesRetriedButNotDeleted} failed messages were not successfully deleted from the dead letter queue. These messages will be retried in the main queue, but they will also still be present in the dead letter queue.`
        );
    }

    return {
        numberOfMessagesRetried,
        numberOfMessagesNotRetried,
        numberOfMessagesRetriedButNotDeleted,
    };
}
Example #17
Source File: CaaSMapper.ts    From fsxa-api with Apache License 2.0 5 votes vote down vote up
/**
   * This method will create a filter for all referenced items that are registered inside of the referencedItems
   * and fetch them in a single CaaS-Request. If remoteProjectId is set, referenced Items from the remoteProject are fetched
   * After a successful fetch all references in the json structure will be replaced with the fetched and mapped item
   */
  async resolveReferencesPerProject<T extends {}>(
    data: T,
    remoteProjectId?: string,
    filterContext?: unknown
  ): Promise<T> {
    const referencedItems = remoteProjectId
      ? this._remoteReferences[remoteProjectId]
      : this._referencedItems

    const remoteProjectKey = Object.keys(this.api.remotes || {}).find((key) => {
      return key === remoteProjectId
    })
    const locale =
      remoteProjectKey && this.api.remotes ? this.api.remotes[remoteProjectKey].locale : this.locale

    const ids = Object.keys(referencedItems)
    const idChunks = chunk(ids, REFERENCED_ITEMS_CHUNK_SIZE)
    if (ids?.length > 0) {
      const response = await Promise.all(
        idChunks.map((ids) =>
          this.api.fetchByFilter({
            filters: [
              {
                operator: ComparisonQueryOperatorEnum.IN,
                value: ids,
                field: 'identifier',
              },
            ],
            locale,
            pagesize: REFERENCED_ITEMS_CHUNK_SIZE,
            remoteProject: remoteProjectId,
            filterContext,
          })
        )
      )
      const fetchedItems = response.map(({ items }) => items).flat()
      ids.forEach((id) =>
        referencedItems[id].forEach((path) =>
          set(
            data,
            path,
            fetchedItems.find((data) => {
              const hasId = typeof data === 'object' && 'id' in (data as object)
              if (hasId) {
                return (data as { id: string }).id === id
              }
            })
          )
        )
      )
    }
    return data
  }
Example #18
Source File: scheduler.ts    From crossfeed with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
async run() {
    for (const scan of this.scans) {
      const prev_numLaunchedTasks = this.numLaunchedTasks;

      if (!SCAN_SCHEMA[scan.name]) {
        console.error('Invalid scan name ', scan.name);
        continue;
      }
      const { global } = SCAN_SCHEMA[scan.name];
      if (global) {
        // Global scans are not associated with an organization.
        if (!(await shouldRunScan({ scan }))) {
          continue;
        }
        await this.launchScanTask({ scan });
      } else {
        const organizations = scan.isGranular
          ? getScanOrganizations(scan)
          : this.organizations;
        const orgsToLaunch: Organization[] = [];
        for (const organization of organizations) {
          if (!(await shouldRunScan({ organization, scan }))) {
            continue;
          }
          orgsToLaunch.push(organization);
        }
        // Split the organizations in orgsToLaunch into chunks of size
        // this.orgsPerScanTask, then launch organizations for each one.
        for (const orgs of chunk(orgsToLaunch, this.orgsPerScanTask)) {
          await this.launchScanTask({ organizations: orgs, scan });
        }
      }
      console.log(
        'Launched',
        this.numLaunchedTasks,
        'scanTasks for scan',
        scan.name
      );
      // If at least 1 new scan task was launched for this scan, update the scan
      if (this.numLaunchedTasks > prev_numLaunchedTasks) {
        scan.lastRun = new Date();
        scan.manualRunPending = false;
        await scan.save();
      }
    }
  }
Example #19
Source File: xiaomiData.ts    From brick-design with MIT License 5 votes vote down vote up
seckillResult=chunk(seckillList,4)
Example #20
Source File: today.tsx    From office-hours with GNU General Public License v3.0 5 votes vote down vote up
collapseHeatmap = (heatmap: Heatmap): Heatmap =>
  chunk(heatmap, 4).map((hours) => {
    const filteredOfficeHours = hours.filter((v) => v !== -1);
    return filteredOfficeHours.length > 0 ? mean(filteredOfficeHours) : -1;
  })
Example #21
Source File: eosBancorCalc.ts    From webapp with MIT License 5 votes vote down vote up
export async function findNewPath<T>(
  fromId: string,
  toId: string,
  pools: T[],
  identifier: (pool: T) => Edge
) {
  const edges = _.uniqWith(pools.map(identifier), compareEdge);
  const nodes: Node[] = _.uniqWith(edges.flat(1), compareString);

  const adjacencyList = buildAdjacencyList(edges, nodes);
  const startExists = adjacencyList.get(fromId);
  const goalExists = adjacencyList.get(toId);

  if (!(startExists && goalExists))
    throw new Error(
      `Start ${fromId} or goal ${toId} does not exist in adjacency list`
    );

  const dfsResult = await dfs(fromId, toId, adjacencyList)!;
  if (!dfsResult || dfsResult.length == 0)
    throw new Error("Failed to find path");

  const hops = _.chunk(dfsResult, 2).map((tokenIds, index, arr) => {
    let searchAbleIds: string[];

    if (tokenIds.length < 2) {
      searchAbleIds = [arr[index - 1][1], tokenIds[0]];
    } else searchAbleIds = tokenIds;

    const accomodatingRelays = pools.filter(pool => {
      const ids = identifier(pool);
      return ids.every(id => searchAbleIds.some(i => id == i));
    });

    return accomodatingRelays;
  });

  return {
    path: dfsResult,
    hops
  };
}
Example #22
Source File: upload-hobo-data.ts    From aqualink-app with MIT License 5 votes vote down vote up
parseHoboData = async (
  poiEntities: SiteSurveyPoint[],
  dbIdToCSVId: Record<number, number>,
  rootPath: string,
  poiToSourceMap: Dictionary<Sources>,
  timeSeriesRepository: Repository<TimeSeries>,
) => {
  // Parse hobo data
  const parsedData = poiEntities.map((poi) => {
    const colonyId = poi.name.split(' ')[1].padStart(3, '0');
    const dataFile = COLONY_DATA_FILE.replace('{}', colonyId);
    const colonyFolder = COLONY_FOLDER_PREFIX + colonyId;
    const siteFolder = FOLDER_PREFIX + dbIdToCSVId[poi.site.id];
    const filePath = path.join(rootPath, siteFolder, colonyFolder, dataFile);
    const headers = [undefined, 'id', 'dateTime', 'bottomTemperature'];
    const castFunction = castCsvValues(
      ['id'],
      ['bottomTemperature'],
      ['dateTime'],
    );
    return parseCSV<Data>(filePath, headers, castFunction).map((data) => ({
      timestamp: data.dateTime,
      value: data.bottomTemperature,
      source: poiToSourceMap[poi.id],
      metric: Metric.BOTTOM_TEMPERATURE,
    }));
  });

  // Find the earliest date of data
  const startDates = parsedData.reduce((acc, data) => {
    const minimum = minBy(data, (o) => o.timestamp);

    if (!minimum) {
      return acc;
    }

    return acc.concat(minimum);
  }, []);

  const groupedStartedDates = keyBy(startDates, (o) => o.source.site.id);

  // Start a backfill for each site
  const siteDiffDays: [number, number][] = Object.keys(groupedStartedDates).map(
    (siteId) => {
      const startDate = groupedStartedDates[siteId];
      if (!startDate) {
        return [parseInt(siteId, 10), 0];
      }

      const start = moment(startDate.timestamp);
      const end = moment();
      const diff = Math.min(end.diff(start, 'd'), 200);

      return [startDate.source.site.id, diff];
    },
  );

  const bottomTemperatureData = parsedData.flat();

  // Data are to much to added with one bulk insert
  // So we need to break them in batches
  const batchSize = 1000;
  logger.log(`Saving time series data in batches of ${batchSize}`);
  const inserts = chunk(bottomTemperatureData, batchSize).map((batch) => {
    return timeSeriesRepository
      .createQueryBuilder('time_series')
      .insert()
      .values(batch)
      .onConflict('ON CONSTRAINT "no_duplicate_data" DO NOTHING')
      .execute();
  });

  // 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`);
  });

  return siteDiffDays;
}
Example #23
Source File: index.tsx    From nebula-studio with Apache License 2.0 5 votes vote down vote up
DocPage = () => {
  const history = useHistory();
  const docGroup = chunk(DOCS, 3);
  useEffect(() => {
    trackPageView('/doc');
  }, []);
  
  return (
    <div className={cls(styles.studioDoc, 'studioCenterLayout')}>
      <h1 className={styles.welcomeLabel}>{intl.get('doc.welcome')} <span>Nebula Studio</span></h1>
      <div className={styles.docBox}>
        <div className={styles.header}>{intl.get('doc.functionIntro')}</div>
        <div className={styles.moduleIntro}>
          {MODULES.map(module => <Col span={8} key={module.title}>
            <div className={styles.moduleItem} onClick={() => history.push(module.link)}>
              <Icon type={module.icon} />
              <span className={styles.title}>{intl.get(module.title)}</span>
              <span className={styles.tip}>{intl.get(module.tip)}</span>
            </div>
          </Col>)}
        </div>
      </div>
      <div className={styles.docBox}>
        <div className={styles.header}>{intl.get('doc.learningDoc')}</div>
        <div className={styles.docCarousel}>
          <Carousel dotPosition="bottom" lazyLoad="progressive" dots={{ className: 'btn-carousel' }}>
            {docGroup.map((group, index) => (
              <Row className={styles.docGroup} gutter={26} key={index}>
                {group.map(doc => <Col span={8} key={doc.title}>
                  <div className={styles.docItem}>
                    <div className={styles.docDesc}>
                      <p className={styles.docTitle}>{intl.get(doc.title)}</p>
                      <p className={styles.docTip}>{intl.get(doc.tip)}</p>
                    </div>
                    <Button type="primary">
                      <a href={intl.get(doc.link)} target="_blank" rel="noreferrer">
                        {intl.get('doc.start')}
                      </a>
                    </Button>
                  </div>
                </Col>)}
              </Row>
            ))}
          </Carousel>
        </div>
      </div>
    </div>
  );
}
Example #24
Source File: preset.ts    From ovineherd with Apache License 2.0 4 votes vote down vote up
getOrgRoleApis = () => {
  const orgId = getOrgId()

  const orgTeamOption = getReqOption(
    {
      apiType: relation.org.team.apiType,
      type: relation.org.team.type,
      q_relation1: orgId,
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.label,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const orgRoleOption = getReqOption(
    {
      apiType: relation.org.role.apiType,
      type: relation.org.role.type,
      relation1: orgId,
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.label,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const listRole = getReqOption({
    apiType: relation.org.role.apiType,
    type: relation.org.role.type,
    q_relation1: orgId,
    apiName: ApiName.list,
    '&': '$$',
  })

  const addRole = getReqOption({
    ...relation.org.role,
    apiName: ApiName.add,
    relation1: orgId,
    '&': '$$',
  })

  const editRole = getReqOption({
    apiType: relation.org.role.apiType,
    apiName: ApiName.edit,
    '&': '$$',
  })

  const delRole = getReqOption({
    apiType: relation.org.role.apiType,
    apiName: ApiName.del,
    id: '$id',
  })

  const listMember = getReqOption(
    {
      apiType: relation.org.user.apiType,
      type: getOrgUniType('user', orgId),
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      // onPreRequest: (option) => {
      //   const { teamIds = '', roleIds = '', ...rest } = option.data
      //   const catIds = !roleIds ? teamIds : `${teamIds},${roleIds}`
      //   option.data = rest
      //   option.data.category_ids = catIds
      //   return option
      // },
      onSuccess: (source) => {
        source.data.items = source.data.items.map((data) => {
          const {
            // eslint-disable-next-line
            password,
            relation3_data: team = {},
            relation4_data: role = {},
            ...rest
          } = data
          return {
            team,
            role,
            ...rest,
          }
        })
        return source
      },
    }
  )

  const batchSetRole = {
    url: 'fakeBatchSetRole',
    onFakeRequest: (option) => {
      const { newRoleId, ids } = option.data
      const idArr = ids.split(',')
      chunk(idArr, 4).map(async (chuckIds) => {
        await Promise.all(
          chuckIds.map((uid: string) => {
            return requestByOption({
              apiType: relation.org.user.apiType,
              apiName: ApiName.edit,
              id: uid,
              relation4: newRoleId,
            })
          })
        )
      })

      return {
        status: 0,
      }
    },
  }

  const rmUserRole = getReqOption({
    apiType: relation.org.user.apiType,
    apiName: ApiName.edit,
    id: '$id',
    relation4: '0',
  })

  // 此处所设置权限,如何对应后端接口
  const setLimit = {
    url: 'fakeSetLimit',
    onFakeRequest: async (option) => {
      const { id: roleId, limitId = '', appLimit = [], ...rest } = option.data
      const limit_raw = JSON.stringify(option.data)
      const limit_data: any = []

      const setLimitStr = (prefix, str) => {
        const valArr = str.split(',')
        valArr.forEach((v) => {
          limit_data.push(`${prefix}/${v}`)
        })
      }

      map(rest, (val, key) => {
        setLimitStr(key, val)
      })

      appLimit.forEach(({ id, limit }) => {
        setLimitStr(`app/${id}`, limit)
      })

      const limitInfo = { limit_raw, limit_data }

      if (limitId) {
        await requestByOption({
          apiType: relation.org.limit.apiType,
          apiName: ApiName.edit,
          id: limitId,
          ...limitInfo,
        })
      } else {
        const { id: addLimitId } = await requestByOption({
          ...relation.org.limit,
          ...limitInfo,
          apiName: ApiName.add,
        })
        await requestByOption({
          apiType: relation.org.role.apiType,
          relation2_type: relation.org.role.relation2_type,
          apiName: ApiName.edit,
          id: roleId,
          relation2: addLimitId,
        })
      }

      return {
        status: 0,
        msg: '保存成功',
      }
    },
  }

  const getLimit = getReqOption(
    {
      apiType: relation.org.limit.apiType,
      apiName: ApiName.one,
      sendOn: '',
      id: '$relation2',
    },
    {
      onSuccess: (source) => {
        try {
          source.data = JSON.parse(source.data.limit_raw)
        } catch (e) {
          //
        }
        return source
      },
    }
  )

  const appListOpts = getReqOption(
    {
      ...relation.app.entity,
      apiName: ApiName.list,
      q_relation2: orgId,
    },
    {
      expired: 30 * 1000,
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.relation1_data.name,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const orgRoleApis = {
    orgTeamOption,
    orgRoleOption,
    listRole,
    addRole,
    editRole,
    delRole,
    listMember,
    batchSetRole,
    rmUserRole,
    setLimit,
    getLimit,
    appListOpts,
  }

  return orgRoleApis
}
Example #25
Source File: PopularTimes.tsx    From office-hours with GNU General Public License v3.0 4 votes vote down vote up
export default function PopularTimes({ heatmap }: HeatmapProps): ReactElement {
  const [currentDayOfWeek, setCurrentDayOfWeek] = useState(new Date().getDay());
  const [firstHour, lastHour] = findWeekMinAndMax(heatmap);
  const dailyAvgWaitTimes: number[] = chunk(heatmap, 24).map((hours) => {
    const filteredOfficeHours = hours.filter((v) => v !== -1);
    return filteredOfficeHours.length > 0 ? mean(filteredOfficeHours) : -1;
  });

  return (
    <div className="hide-in-percy">
      <TitleRow>
        <h2>Wait Times on</h2>
        <Dropdown
          trigger={["click"]}
          overlay={
            <Menu>
              {DAYS_OF_WEEK.map((dayName, i) => (
                <Menu.Item key={dayName}>
                  <a onClick={() => setCurrentDayOfWeek(i)}>{dayName}</a>
                </Menu.Item>
              ))}
            </Menu>
          }
        >
          <WeekdayDropdown>
            {DAYS_OF_WEEK[currentDayOfWeek]}
            <DownOutlined />
          </WeekdayDropdown>
        </Dropdown>
      </TitleRow>
      <GraphWithArrow>
        <GraphArrowButtons
          onClick={() => setCurrentDayOfWeek((7 + currentDayOfWeek - 1) % 7)}
        >
          <LeftOutlined />
        </GraphArrowButtons>
        <GraphContainer>
          <ParentSize>
            {({ width }) => (
              <TimeGraph
                values={heatmap
                  .slice(currentDayOfWeek * 24, (currentDayOfWeek + 1) * 24 - 1)
                  .map((i) => (i < 0 ? 0 : Math.floor(i)))}
                maxTime={Math.max(...heatmap)}
                firstHour={firstHour}
                lastHour={lastHour}
                width={width}
                height={220}
              />
            )}
          </ParentSize>
        </GraphContainer>
        <GraphArrowButtons
          onClick={() => setCurrentDayOfWeek((currentDayOfWeek + 1) % 7)}
        >
          <RightOutlined />
        </GraphArrowButtons>
      </GraphWithArrow>
      {dailyAvgWaitTimes[currentDayOfWeek] >= 0 && (
        <GraphNotes>
          <ClockCircleOutlined /> {DAYS_OF_WEEK[currentDayOfWeek]}s have{" "}
          <strong>
            {generateBusyText(currentDayOfWeek, dailyAvgWaitTimes)}
          </strong>{" "}
          wait times.
        </GraphNotes>
      )}
      {new Date().getDay() === currentDayOfWeek &&
        heatmap[currentDayOfWeek * 24 + new Date().getHours()] >= 0 && (
          <GraphNotes>
            <HourglassOutlined /> At {formatDateHour(new Date().getHours())},
            people generally wait{" "}
            <strong>
              {formatWaitTime(
                heatmap[currentDayOfWeek * 24 + new Date().getHours()]
              )}
            </strong>
            .
          </GraphNotes>
        )}
    </div>
  );
}
Example #26
Source File: DesktopSlider.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function DesktopSlider(props: DesktopSliderProps): React.ReactElement {
  const enableMyDesktop = true;
  const [desktopCursor, setDesktopCursor] = React.useState(
    getRememberedDesktopCursor()
  );
  const [appCursor, setAppCursor] = React.useState(-1);
  const { columns, rows } = useLaunchpadSettingsContext();
  const { setDesktopDir } = useDesktopDirContext();
  const slideDuration = 400;

  useEffect(() => {
    enableMyDesktop && launchpadService.setMaxVisitorLength(8);
  }, [columns, enableMyDesktop]);
  const mapItemToDesktop = (apps: appItem[]): DesktopData => ({
    name: "-",
    items: apps.map((app) => {
      if (app.type === "custom") {
        return {
          id: app.id,
          name: app.name,
          url: app.homepage,
          type: "custom",
          app,
        };
      }
      return {
        type: "app",
        id: app.id,
        app,
      };
    }),
  });

  const transformCustomItem = (item: DesktopItemCustom): appItem => ({
    id: item.id,
    localeName: item.name,
    name: item.name,
    homepage: item.url,
    ...item,
    type: "custom",
  });

  let desktops: DesktopData[];
  let validItems = props.microApps;

  if (props.desktops && props.desktops.length > 0) {
    validItems = [];

    const id2app = props.microApps.reduce((acc, app) => {
      acc.set(app.id, app);
      return acc;
    }, new Map<string, appItem>());

    desktops = props.desktops
      .map((desktop) => ({
        name: desktop.name,
        items: desktop.items
          .map((item) => {
            if (item.type === "app") {
              if (id2app.has(item.id)) {
                const app = id2app.get(item.id);
                validItems.push(app);
                id2app.delete(item.id);
                return {
                  type: item.type,
                  id: item.id,
                  app,
                };
              }
              // ignore not found apps
            } else if (item.type === "dir") {
              const items = item.items
                .map((item) => {
                  if (item.type === "app") {
                    if (id2app.has(item.id)) {
                      const app = id2app.get(item.id);
                      validItems.push(app);
                      id2app.delete(item.id);
                      return {
                        type: item.type,
                        id: item.id,
                        app,
                      };
                    }
                  } else if (item.type === "custom") {
                    validItems.push(transformCustomItem(item));
                    return item;
                  }
                })
                .filter(Boolean);
              // ignore empty dirs
              if (items.length > 0) {
                return {
                  type: item.type,
                  id: item.id,
                  name: item.name,
                  items,
                };
              }
            } else if (item.type === "custom") {
              validItems.push(transformCustomItem(item));
              return item;
            }
          })
          .filter(Boolean)
          .slice(0, columns * rows),
      }))
      // ignore empty desktops
      .filter((desktop) => desktop.items.length > 0);
  } else {
    // 如果没有定义桌面列表(例如本地开发模式),则自动按数量切割。
    desktops = chunk(props.microApps, columns * rows).map(mapItemToDesktop);
  }
  let filteredDesktop: DesktopData;
  if (props.q) {
    const lowerQ = props.q.toLowerCase();
    filteredDesktop = mapItemToDesktop(
      validItems
        .filter(
          (app) =>
            app.localeName.toLowerCase().includes(lowerQ) ||
            app.name.toLowerCase().includes(lowerQ) ||
            app.id.toLowerCase().includes(lowerQ)
        )
        .slice(0, columns * rows)
    );
  }

  // When sliding desktop, reset app cursor.
  React.useEffect(() => {
    setAppCursor(-1);
  }, [desktopCursor]);

  // When making search, set app cursor to the first app.
  React.useEffect(() => {
    setAppCursor(
      props.q && filteredDesktop && filteredDesktop.items.length > 0 ? 0 : -1
    );
  }, [props.q]);

  const lockRef = React.useRef(false);

  const throttledSetDesktopCursor = (index: number): void => {
    if (lockRef.current) {
      return;
    }
    setRememberedDesktopCursor(index);
    setDesktopCursor(index);
    // 一次滑动一个屏幕,锁定期间内,不能继续滑动屏幕。
    lockRef.current = true;
    setTimeout(() => {
      lockRef.current = false;
    }, slideDuration);
  };

  const slideLeft = React.useCallback((): void => {
    if (desktopCursor > 0) {
      throttledSetDesktopCursor(desktopCursor - 1);
    }
  }, [desktopCursor]);

  const slideRight = React.useCallback((): void => {
    const length = desktops.length;
    if (desktopCursor < length) {
      throttledSetDesktopCursor(desktopCursor + 1);
    }
  }, [desktopCursor, desktops.length]);

  const handleSlideLeft = (e: React.MouseEvent): void => {
    e.stopPropagation();
    slideLeft();
  };

  const handleSlideRight = (e: React.MouseEvent): void => {
    e.stopPropagation();
    slideRight();
  };

  const handleSlideTo = (e: React.MouseEvent, index: number): void => {
    e.stopPropagation();
    if (desktopCursor !== index) {
      throttledSetDesktopCursor(index);
    }
  };

  // Press arrow key to select an app.
  React.useEffect(() => {
    const onKeydown = (event: KeyboardEvent): void => {
      // 第一栏为我的面板,须过滤掉(PS: 但是搜索时 desktopCursor 为0是可以的)
      if (enableMyDesktop && desktopCursor === 0 && !props.q) return;
      const key =
        event.key ||
        /* istanbul ignore next: compatibility */ event.keyCode ||
        /* istanbul ignore next: compatibility */ event.which;
      const currentDesktop = props.q
        ? filteredDesktop
        : desktops[desktopCursor - 1];
      if (key === "Enter" || key === 13) {
        event.preventDefault();
        if (appCursor >= 0 && appCursor < currentDesktop.items.length) {
          const cell = currentDesktop.items[appCursor];
          if (cell.type === "app") {
            launchpadService.pushVisitor("app", cell.app);
            getRuntime().resetWorkspaceStack();
            getHistory().push(cell.app.homepage);
          } else if (cell.type === "custom") {
            launchpadService.pushVisitor("custom", cell);
            window.open(cell.url);
          } else if (cell.type === "dir") {
            // Calculate the approximate coordinates of a dir.
            const x = appCursor % columns;
            const y = Math.floor(appCursor / columns);
            setDesktopDir({
              activeIndex: 0,
              dir: {
                name: cell.name,
                items: cell.items,
              },
              coordinates: {
                x: (window.innerWidth * (x + 1)) / (columns + 1),
                y: (window.innerHeight * (y + 1)) / (rows + 1),
              },
            });
          }
        }
      } else {
        let offset = 0;
        if (key === "ArrowRight" || key === 39) {
          offset = 1;
        } else if (key === "ArrowLeft" || key === 37) {
          offset = appCursor === -1 ? currentDesktop.items.length : -1;
        } else if (key === "ArrowDown" || key === 40) {
          offset = appCursor === -1 ? 1 : columns;
        } else if (key === "ArrowUp" || key === 38) {
          offset = appCursor === -1 ? currentDesktop.items.length : -columns;
        }
        if (offset !== 0) {
          event.preventDefault();
          const next = appCursor + offset;
          if (next >= 0 && next < currentDesktop.items.length) {
            setAppCursor(next);
          }
        }
      }
    };
    window.addEventListener("keydown", onKeydown);
    return () => {
      window.removeEventListener("keydown", onKeydown);
    };
  }, [desktopCursor, appCursor, props.q, columns, setDesktopDir]);

  const deltaXRef = React.useRef(0);
  const deltaYRef = React.useRef(0);
  const responsibleRef = React.useRef(true);

  const tryToSlideByWheel = (): void => {
    // Mac 的 trackpad,部分鼠标滚轮会有“拖尾效应”,拖尾期间,不再响应滚轮事件。
    if (!responsibleRef.current) {
      return;
    }
    // 取绝对值较大的方向
    const axisRef =
      Math.abs(deltaYRef.current) > Math.abs(deltaXRef.current)
        ? deltaYRef
        : deltaXRef;
    // 经测试,滚轮纵轴一次位移 4,横轴一次位移 40。
    const threshold = axisRef === deltaYRef ? 4 : 40;
    if (axisRef.current >= threshold) {
      slideRight();
    } else if (axisRef.current <= -threshold) {
      slideLeft();
    } else {
      return;
    }
    // 触发滑动后,重设 delta,拖尾期间,不再响应滚轮事件。
    deltaXRef.current = 0;
    deltaYRef.current = 0;
    responsibleRef.current = false;
  };

  const resetDeltaTimeoutRef = React.useRef<any>();

  const handleWheel = (e: React.WheelEvent): void => {
    deltaXRef.current += e.deltaX;
    deltaYRef.current += e.deltaY;
    tryToSlideByWheel();
    if (resetDeltaTimeoutRef.current) {
      clearTimeout(resetDeltaTimeoutRef.current);
    }
    // 间隔 50ms 内的连续滚轮事件被认作一次滚动。
    resetDeltaTimeoutRef.current = setTimeout(() => {
      deltaXRef.current = 0;
      deltaYRef.current = 0;
      responsibleRef.current = true;
    }, 50);
  };

  const sliderChildrenLength = desktops.length + 1;

  return (
    <div
      onWheel={handleWheel}
      className={classNames(styles.desktopSlider, {
        [styles.filtered]: props.q,
      })}
    >
      <div className={styles.desktopSelector}>
        {[...[{ name: <HomeFilled /> }], ...desktops].map((desktop, index) => (
          <React.Fragment key={index}>
            {index !== 0 && <span className={styles.selectorSeparator} />}
            <a
              className={classNames(styles.desktopName, {
                [styles.active]: desktopCursor === index,
              })}
              onClick={(e) => handleSlideTo(e, index)}
              role="button"
            >
              {desktop.name}
            </a>
          </React.Fragment>
        ))}
      </div>
      <div className={styles.scrollContainer}>
        <div
          className={styles.desktopList}
          style={{
            width: `${sliderChildrenLength * 100}%`,
            marginLeft: `${desktopCursor * -100}%`,
            transition: `margin-left ${slideDuration}ms ease-out`,
          }}
        >
          {enableMyDesktop && (
            <MyDesktop
              desktopCount={desktops.length}
              arrowWidthPercent={props.arrowWidthPercent}
            />
          )}
          {desktops.map((desktop, index) => (
            <Desktop
              key={index}
              desktop={desktop}
              desktopCount={desktops.length}
              arrowWidthPercent={props.arrowWidthPercent}
              activeIndex={desktopCursor - 1 === index ? appCursor : -1}
            />
          ))}
        </div>
        {
          // Show filtered apps as a single desktop.
          props.q && (
            <div className={styles.filteredList}>
              <Desktop
                desktop={filteredDesktop}
                desktopCount={1}
                arrowWidthPercent={props.arrowWidthPercent}
                activeIndex={appCursor}
              />
            </div>
          )
        }
      </div>
      <a
        className={classNames(styles.arrowLeft, {
          [styles.available]: desktopCursor > 0,
        })}
        style={{ width: `${props.arrowWidthPercent}%` }}
        onClick={handleSlideLeft}
        role="button"
      >
        <span className={styles.arrowButton}>
          <LeftOutlined />
        </span>
      </a>
      <a
        className={classNames(styles.arrowRight, {
          [styles.available]: desktopCursor < sliderChildrenLength - 1,
        })}
        style={{ width: `${props.arrowWidthPercent}%` }}
        onClick={handleSlideRight}
        role="button"
      >
        <span className={styles.arrowButton}>
          <RightOutlined />
        </span>
      </a>
    </div>
  );
}
Example #27
Source File: preset.ts    From ovineherd with Apache License 2.0 4 votes vote down vote up
getAppRoleApis = () => {
  // TODO: 组织使用: 假删除。 添加冻结功能
  const appId = getAppId()

  const listRole = getReqOption({
    apiType: relation.app.role.apiType,
    type: relation.app.role.type,
    q_relation1: appId,
    apiName: ApiName.list,
    '&': '$$',
  })

  const addRole = getReqOption({
    ...relation.app.role,
    apiName: ApiName.add,
    relation1: appId,
    '&': '$$',
  })

  const editRole = getReqOption({
    apiType: relation.app.role.apiType,
    apiName: ApiName.edit,
    '&': '$$',
  })

  const delRole = getReqOption({
    apiType: relation.app.role.apiType,
    apiName: ApiName.del,
    id: '$id',
  })

  const listMember = getReqOption(
    {
      apiType: relation.app.user.apiType,
      type: getAppUniType('user', appId),
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      // onPreRequest: (option) => {
      //   const { teamIds = '', roleIds = '', ...rest } = option.data
      //   const catIds = !roleIds ? teamIds : `${teamIds},${roleIds}`
      //   option.data = rest
      //   option.data.category_ids = catIds
      //   return option
      // },
      onSuccess: (source) => {
        source.data.items = source.data.items.map((data) => {
          const {
            // eslint-disable-next-line
            password,
            relation3_data: team = {},
            relation4_data: role = {},
            ...rest
          } = data
          return {
            team,
            role,
            ...rest,
          }
        })
        return source
      },
    }
  )

  const batchSetRole = {
    url: 'fakeBatchSetRole',
    onFakeRequest: (option) => {
      const { newRoleId, ids } = option.data
      const idArr = ids.split(',')
      chunk(idArr, 4).map(async (chuckIds) => {
        await Promise.all(
          chuckIds.map((uid: string) => {
            return requestByOption({
              apiType: relation.app.user.apiType,
              apiName: ApiName.edit,
              id: uid,
              relation4: newRoleId,
            })
          })
        )
      })

      return {
        status: 0,
      }
    },
  }

  const rmUserRole = getReqOption({
    apiType: relation.app.user.apiType,
    apiName: ApiName.edit,
    id: '$id',
    relation4: '0',
  })

  // 此处所设置权限,如何对应后端接口
  const setLimit = getReqOption({
    apiType: relation.app.role.apiType,
    apiName: ApiName.edit,
    id: '$id',
    limit: '$authLimit',
  })

  const appListOpts = getReqOption(
    {
      ...relation.app.entity,
      apiName: ApiName.list,
      q_relation2: appId,
    },
    {
      expired: 30 * 1000,
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.relation1_data.name,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const appRoleOption = getReqOption(
    {
      apiType: relation.app.role.apiType,
      type: relation.app.role.type,
      q_relation1: appId,
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.label,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const appRoleApis = {
    listRole,
    addRole,
    editRole,
    delRole,
    listMember,
    batchSetRole,
    rmUserRole,
    setLimit,
    appListOpts,
    appRoleOption,
  }

  return appRoleApis
}
Example #28
Source File: api.ts    From ovineherd with Apache License 2.0 4 votes vote down vote up
getOrgRoleApi = () => {
  const orgId = getOrgId()

  const orgTeamOption = getReqOption(
    {
      apiType: relation.org.team.apiType,
      type: relation.org.team.type,
      q_relation1: orgId,
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.label,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const orgRoleOption = getReqOption(
    {
      apiType: relation.org.role.apiType,
      type: relation.org.role.type,
      q_relation1: orgId,
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.label,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  const listRole = getReqOption({
    apiType: relation.org.role.apiType,
    type: relation.org.role.type,
    q_relation1: orgId,
    apiName: ApiName.list,
    '&': '$$',
  })

  const addRole = getReqOption({
    ...relation.org.role,
    apiName: ApiName.add,
    relation1: orgId,
    '&': '$$',
  })

  const editRole = getReqOption({
    apiType: relation.org.role.apiType,
    apiName: ApiName.edit,
    '&': '$$',
  })

  const delRole = getReqOption({
    apiType: relation.org.role.apiType,
    apiName: ApiName.del,
    id: '$id',
  })

  const listMember = getReqOption(
    {
      apiType: relation.org.user.apiType,
      type: getOrgUniType('user', orgId),
      apiName: ApiName.list,
      '&': '$$',
    },
    {
      // onPreRequest: (option) => {
      //   const { teamIds = '', roleIds = '', ...rest } = option.data
      //   const catIds = !roleIds ? teamIds : `${teamIds},${roleIds}`
      //   option.data = rest
      //   option.data.category_ids = catIds
      //   return option
      // },
      onSuccess: (source) => {
        source.data.items = source.data.items.map((data) => {
          const {
            // eslint-disable-next-line
            password,
            relation3_data: team = {},
            relation4_data: role = {},
            ...rest
          } = data
          return {
            team,
            role,
            ...rest,
          }
        })
        return source
      },
    }
  )

  const batchSetRole = {
    url: 'fakeBatchSetRole',
    onFakeRequest: (option) => {
      const { newRoleId, ids } = option.data
      const idArr = ids.split(',')
      chunk(idArr, 4).map(async (chuckIds) => {
        await Promise.all(
          chuckIds.map((uid: string) => {
            return requestByOption({
              apiType: relation.org.user.apiType,
              apiName: ApiName.edit,
              id: uid,
              relation4: newRoleId,
            })
          })
        )
      })

      return {
        status: 0,
      }
    },
  }

  const rmUserRole = getReqOption({
    apiType: relation.org.user.apiType,
    apiName: ApiName.edit,
    id: '$id',
    relation4: '0',
  })

  // 此处所设置权限,如何对应后端接口
  const setLimit = {
    url: 'fakeSetLimit',
    onFakeRequest: async (option) => {
      const { id: roleId, limitId = '', appLimit = [], ...rest } = option.data
      const limit_raw = JSON.stringify(option.data)
      const limit_data: any = []

      const setLimitStr = (prefix, str) => {
        const valArr = str.split(',')
        valArr.forEach((v) => {
          limit_data.push(`${prefix}/${v}`)
        })
      }

      map(omit(rest, 'relation2'), (val, key) => {
        if (val) {
          setLimitStr(key, val)
        }
      })

      appLimit.forEach(({ id, limit, ignore }) => {
        if (ignore) {
          setLimitStr(`app/${id}`, 'ignore')
        }
        if (limit) {
          setLimitStr(`app/${id}`, limit)
        }
      })

      const limitInfo = { limit_raw, limit_data }

      if (limitId) {
        await requestByOption({
          apiType: relation.org.limit.apiType,
          apiName: ApiName.edit,
          id: limitId,
          ...limitInfo,
        })
      } else {
        const { id: addLimitId } = await requestByOption({
          ...relation.org.limit,
          ...limitInfo,
          apiName: ApiName.add,
        })
        await requestByOption({
          apiType: relation.org.role.apiType,
          relation2_type: relation.org.role.relation2_type,
          apiName: ApiName.edit,
          id: roleId,
          relation2: addLimitId,
        })
      }

      return {
        status: 0,
        msg: '保存成功',
      }
    },
  }

  const getLimit = getReqOption(
    {
      apiType: relation.org.limit.apiType,
      apiName: ApiName.one,
      // sendOn: '',
      relation2: '$relation2',
    },
    {
      onPreRequest: (option) => {
        option.url = option.url.replace(
          /authorization\/(.*)$/,
          `authorization/${option.data.relation2}`
        )
        option.data = {}
        return option
      },
      onSuccess: (source) => {
        try {
          source.data = JSON.parse(source.data.limit_raw)
        } catch (e) {
          //
        }
        return source
      },
    }
  )

  const appListOpts = getReqOption(
    {
      ...relation.app.entity,
      apiName: ApiName.list,
      q_relation2: orgId,
    },
    {
      expired: 30 * 1000,
      onSuccess: (source) => {
        const options = source.data.items.map((item) => {
          return {
            value: item.id,
            label: item.relation1_data.name,
          }
        })
        source.data = { options }
        return source
      },
    }
  )

  return {
    orgTeamOption,
    orgRoleOption,
    listRole,
    addRole,
    editRole,
    delRole,
    listMember,
    batchSetRole,
    rmUserRole,
    setLimit,
    getLimit,
    appListOpts,
  }
}
Example #29
Source File: LibraryMenuItems.tsx    From excalidraw with MIT License 4 votes vote down vote up
LibraryMenuItems = ({
  isLoading,
  libraryItems,
  onRemoveFromLibrary,
  onAddToLibrary,
  onInsertLibraryItems,
  pendingElements,
  theme,
  setAppState,
  libraryReturnUrl,
  library,
  files,
  id,
  selectedItems,
  onSelectItems,
  onPublish,
  resetLibrary,
}: {
  isLoading: boolean;
  libraryItems: LibraryItems;
  pendingElements: LibraryItem["elements"];
  onRemoveFromLibrary: () => void;
  onInsertLibraryItems: (libraryItems: LibraryItems) => void;
  onAddToLibrary: (elements: LibraryItem["elements"]) => void;
  theme: AppState["theme"];
  files: BinaryFiles;
  setAppState: React.Component<any, AppState>["setState"];
  libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
  library: Library;
  id: string;
  selectedItems: LibraryItem["id"][];
  onSelectItems: (id: LibraryItem["id"][]) => void;
  onPublish: () => void;
  resetLibrary: () => void;
}) => {
  const renderRemoveLibAlert = useCallback(() => {
    const content = selectedItems.length
      ? t("alerts.removeItemsFromsLibrary", { count: selectedItems.length })
      : t("alerts.resetLibrary");
    const title = selectedItems.length
      ? t("confirmDialog.removeItemsFromLib")
      : t("confirmDialog.resetLibrary");
    return (
      <ConfirmDialog
        onConfirm={() => {
          if (selectedItems.length) {
            onRemoveFromLibrary();
          } else {
            resetLibrary();
          }
          setShowRemoveLibAlert(false);
        }}
        onCancel={() => {
          setShowRemoveLibAlert(false);
        }}
        title={title}
      >
        <p>{content}</p>
      </ConfirmDialog>
    );
  }, [selectedItems, onRemoveFromLibrary, resetLibrary]);

  const [showRemoveLibAlert, setShowRemoveLibAlert] = useState(false);

  const isMobile = useDeviceType().isMobile;

  const renderLibraryActions = () => {
    const itemsSelected = !!selectedItems.length;
    const items = itemsSelected
      ? libraryItems.filter((item) => selectedItems.includes(item.id))
      : libraryItems;
    const resetLabel = itemsSelected
      ? t("buttons.remove")
      : t("buttons.resetLibrary");
    return (
      <div className="library-actions">
        {(!itemsSelected || !isMobile) && (
          <ToolButton
            key="import"
            type="button"
            title={t("buttons.load")}
            aria-label={t("buttons.load")}
            icon={load}
            onClick={async () => {
              try {
                await library.updateLibrary({
                  libraryItems: fileOpen({
                    description: "Excalidraw library files",
                    // ToDo: Be over-permissive until https://bugs.webkit.org/show_bug.cgi?id=34442
                    // gets resolved. Else, iOS users cannot open `.excalidraw` files.
                    /*
                    extensions: [".json", ".excalidrawlib"],
                    */
                  }),
                  merge: true,
                  openLibraryMenu: true,
                });
              } catch (error: any) {
                if (error?.name === "AbortError") {
                  console.warn(error);
                  return;
                }
                setAppState({ errorMessage: t("errors.importLibraryError") });
              }
            }}
            className="library-actions--load"
          />
        )}
        {!!items.length && (
          <>
            <ToolButton
              key="export"
              type="button"
              title={t("buttons.export")}
              aria-label={t("buttons.export")}
              icon={exportToFileIcon}
              onClick={async () => {
                const libraryItems = itemsSelected
                  ? items
                  : await library.getLatestLibrary();
                saveLibraryAsJSON(libraryItems)
                  .catch(muteFSAbortError)
                  .catch((error) => {
                    setAppState({ errorMessage: error.message });
                  });
              }}
              className="library-actions--export"
            >
              {selectedItems.length > 0 && (
                <span className="library-actions-counter">
                  {selectedItems.length}
                </span>
              )}
            </ToolButton>
            <ToolButton
              key="reset"
              type="button"
              title={resetLabel}
              aria-label={resetLabel}
              icon={trash}
              onClick={() => setShowRemoveLibAlert(true)}
              className="library-actions--remove"
            >
              {selectedItems.length > 0 && (
                <span className="library-actions-counter">
                  {selectedItems.length}
                </span>
              )}
            </ToolButton>
          </>
        )}
        {itemsSelected && (
          <Tooltip label={t("hints.publishLibrary")}>
            <ToolButton
              type="button"
              aria-label={t("buttons.publishLibrary")}
              label={t("buttons.publishLibrary")}
              icon={publishIcon}
              className="library-actions--publish"
              onClick={onPublish}
            >
              {!isMobile && <label>{t("buttons.publishLibrary")}</label>}
              {selectedItems.length > 0 && (
                <span className="library-actions-counter">
                  {selectedItems.length}
                </span>
              )}
            </ToolButton>
          </Tooltip>
        )}
      </div>
    );
  };

  const CELLS_PER_ROW = isMobile ? 4 : 6;

  const referrer =
    libraryReturnUrl || window.location.origin + window.location.pathname;

  const [lastSelectedItem, setLastSelectedItem] = useState<
    LibraryItem["id"] | null
  >(null);

  const onItemSelectToggle = (
    id: LibraryItem["id"],
    event: React.MouseEvent,
  ) => {
    const shouldSelect = !selectedItems.includes(id);

    const orderedItems = [...unpublishedItems, ...publishedItems];

    if (shouldSelect) {
      if (event.shiftKey && lastSelectedItem) {
        const rangeStart = orderedItems.findIndex(
          (item) => item.id === lastSelectedItem,
        );
        const rangeEnd = orderedItems.findIndex((item) => item.id === id);

        if (rangeStart === -1 || rangeEnd === -1) {
          onSelectItems([...selectedItems, id]);
          return;
        }

        const selectedItemsMap = arrayToMap(selectedItems);
        const nextSelectedIds = orderedItems.reduce(
          (acc: LibraryItem["id"][], item, idx) => {
            if (
              (idx >= rangeStart && idx <= rangeEnd) ||
              selectedItemsMap.has(item.id)
            ) {
              acc.push(item.id);
            }
            return acc;
          },
          [],
        );

        onSelectItems(nextSelectedIds);
      } else {
        onSelectItems([...selectedItems, id]);
      }
      setLastSelectedItem(id);
    } else {
      setLastSelectedItem(null);
      onSelectItems(selectedItems.filter((_id) => _id !== id));
    }
  };

  const getInsertedElements = (id: string) => {
    let targetElements;
    if (selectedItems.includes(id)) {
      targetElements = libraryItems.filter((item) =>
        selectedItems.includes(item.id),
      );
    } else {
      targetElements = libraryItems.filter((item) => item.id === id);
    }
    return targetElements;
  };

  const createLibraryItemCompo = (params: {
    item:
      | LibraryItem
      | /* pending library item */ {
          id: null;
          elements: readonly NonDeleted<ExcalidrawElement>[];
        }
      | null;
    onClick?: () => void;
    key: string;
  }) => {
    return (
      <Stack.Col key={params.key}>
        <LibraryUnit
          elements={params.item?.elements}
          files={files}
          isPending={!params.item?.id && !!params.item?.elements}
          onClick={params.onClick || (() => {})}
          id={params.item?.id || null}
          selected={!!params.item?.id && selectedItems.includes(params.item.id)}
          onToggle={onItemSelectToggle}
          onDrag={(id, event) => {
            event.dataTransfer.setData(
              MIME_TYPES.excalidrawlib,
              serializeLibraryAsJSON(getInsertedElements(id)),
            );
          }}
        />
      </Stack.Col>
    );
  };

  const renderLibrarySection = (
    items: (
      | LibraryItem
      | /* pending library item */ {
          id: null;
          elements: readonly NonDeleted<ExcalidrawElement>[];
        }
    )[],
  ) => {
    const _items = items.map((item) => {
      if (item.id) {
        return createLibraryItemCompo({
          item,
          onClick: () => onInsertLibraryItems(getInsertedElements(item.id)),
          key: item.id,
        });
      }
      return createLibraryItemCompo({
        key: "__pending__item__",
        item,
        onClick: () => onAddToLibrary(pendingElements),
      });
    });

    // ensure we render all empty cells if no items are present
    let rows = chunk(_items, CELLS_PER_ROW);
    if (!rows.length) {
      rows = [[]];
    }

    return rows.map((rowItems, index, rows) => {
      if (index === rows.length - 1) {
        // pad row with empty cells
        rowItems = rowItems.concat(
          new Array(CELLS_PER_ROW - rowItems.length)
            .fill(null)
            .map((_, index) => {
              return createLibraryItemCompo({
                key: `empty_${index}`,
                item: null,
              });
            }),
        );
      }
      return (
        <Stack.Row align="center" gap={1} key={index}>
          {rowItems}
        </Stack.Row>
      );
    });
  };

  const unpublishedItems = libraryItems.filter(
    (item) => item.status !== "published",
  );
  const publishedItems = libraryItems.filter(
    (item) => item.status === "published",
  );

  return (
    <div className="library-menu-items-container">
      {showRemoveLibAlert && renderRemoveLibAlert()}
      <div className="layer-ui__library-header" key="library-header">
        {renderLibraryActions()}
        {isLoading ? (
          <Spinner />
        ) : (
          <a
            href={`${process.env.REACT_APP_LIBRARY_URL}?target=${
              window.name || "_blank"
            }&referrer=${referrer}&useHash=true&token=${id}&theme=${theme}&version=${
              VERSIONS.excalidrawLibrary
            }`}
            target="_excalidraw_libraries"
          >
            {t("labels.libraries")}
          </a>
        )}
      </div>
      <Stack.Col
        className="library-menu-items-container__items"
        align="start"
        gap={1}
      >
        <>
          <div className="separator">{t("labels.personalLib")}</div>
          {renderLibrarySection([
            // append pending library item
            ...(pendingElements.length
              ? [{ id: null, elements: pendingElements }]
              : []),
            ...unpublishedItems,
          ])}
        </>

        <>
          <div className="separator">{t("labels.excalidrawLib")} </div>

          {renderLibrarySection(publishedItems)}
        </>
      </Stack.Col>
    </div>
  );
}