Vizzuality/landgriffon

View on GitHub
api/src/modules/eudr-alerts/dashboard/dashboard-utils.ts

Summary

Maintainability
B
5 hrs
Test Coverage
import { AlertsOutput } from '../dto/alerts-output.dto';

interface VolumeAndPlotByYear {
  year: number;
  volume: string;
  plotName?: string;
  geoRegionId?: string | null;
}

export interface AggregatedVoumeAndPlotByYear extends VolumeAndPlotByYear {
  percentage?: number;
}

export const aggregateAndCalculatePercentage = (
  records: any[],
): AggregatedVoumeAndPlotByYear[] => {
  const withGeoRegion: VolumeAndPlotByYear[] = records.filter(
    (record: VolumeAndPlotByYear) => record.geoRegionId !== null,
  );

  // Group and aggregate records for unknown GeoRegions
  const withoutGeoRegion: VolumeAndPlotByYear[] = records
    .filter((record: VolumeAndPlotByYear) => record.geoRegionId === null)
    .reduce<VolumeAndPlotByYear[]>(
      (acc: VolumeAndPlotByYear[], { year, volume }) => {
        const existingYearRecord: VolumeAndPlotByYear | undefined = acc.find(
          (record: VolumeAndPlotByYear) => record.year === year,
        );
        if (existingYearRecord) {
          existingYearRecord.volume = (
            parseFloat(existingYearRecord.volume) + parseFloat(volume)
          ).toString();
        } else {
          acc.push({ year, volume, plotName: 'Unknown', geoRegionId: null });
        }
        return acc;
      },
      [],
    );

  // Merge records with known and unknown GeoRegions
  const combinedRecords: VolumeAndPlotByYear[] = [
    ...withGeoRegion,
    ...withoutGeoRegion,
  ];

  // Calculate total volume per year
  const yearTotals: { [key: number]: number } = combinedRecords.reduce<{
    [key: number]: number;
  }>((acc: { [p: number]: number }, { year, volume }) => {
    acc[year] = (acc[year] || 0) + parseFloat(volume);
    return acc;
  }, {});

  return combinedRecords.map((record: VolumeAndPlotByYear) => ({
    ...record,
    percentage: (parseFloat(record.volume) / yearTotals[record.year]) * 100,
  }));
};

export const groupAlertsByDate = (
  alerts: AlertsOutput[],
  geoRegionMap: Map<string, any>,
): any[] => {
  const alertsByDate: any = alerts.reduce((acc: any, cur: AlertsOutput) => {
    const date: string = cur.alertDate.value.toString();
    if (!acc[date]) {
      acc[date] = [];
    }
    acc[date].push({
      plotName: geoRegionMap.get(cur.geoRegionId)?.plotName,
      geoRegionId: cur.geoRegionId,
      alertCount: cur.alertCount,
    });
    return acc;
  }, {});
  return Object.keys(alertsByDate).map((key) => ({
    alertDate: key,
    plots: alertsByDate[key],
  }));
};

export const groupAndFillAlertsByMonth = (
  alerts: AlertsOutput[],
  geoRegionMap: Map<string, any>,
  startDate: Date,
  endDate: Date,
): any[] => {
  const alertsByMonth: any = alerts.reduce((acc: any, cur: AlertsOutput) => {
    const date = new Date(cur.alertDate.value);
    const yearMonthKey = `${date.getFullYear()}-${(date.getMonth() + 1)
      .toString()
      .padStart(2, '0')}`;

    if (!acc[yearMonthKey]) {
      acc[yearMonthKey] = [];
    }

    acc[yearMonthKey].push({
      geoRegionId: cur.geoRegionId,
      plotName: geoRegionMap.get(cur.geoRegionId)?.plotName || 'Unknown',
      alertCount: cur.alertCount,
    });

    return acc;
  }, {});

  const allGeoRegions = Array.from(geoRegionMap.keys());

  const start = new Date(startDate);
  const end = new Date(endDate);
  const filledMonths: any[] = [];

  for (
    let month = new Date(start);
    month <= end;
    month.setMonth(month.getMonth() + 1)
  ) {
    const yearMonthKey = `${month.getFullYear()}-${(month.getMonth() + 1)
      .toString()
      .padStart(2, '0')}`;
    const monthAlerts = alertsByMonth[yearMonthKey] || [];

    const alertsMap = new Map(
      monthAlerts.map((alert: any) => [alert.geoRegionId, alert]),
    );

    const monthPlots = allGeoRegions.map((geoRegionId) => {
      return (
        alertsMap.get(geoRegionId) || {
          geoRegionId: geoRegionId,
          plotName: geoRegionMap.get(geoRegionId)?.plotName || 'Unknown',
          alertCount: 0,
        }
      );
    });

    filledMonths.push({
      alertDate: yearMonthKey,
      plots: monthPlots,
    });
  }

  return filledMonths.sort((a, b) => a.alertDate.localeCompare(b.alertDate));
};

export const findNonAlertedGeoRegions = (
  geoRegionMapBySupplier: Record<string, string[]>,
  alertMap: any,
): Record<string, string[]> => {
  const missingGeoRegionIds: Record<string, string[]> = {} as Record<
    string,
    string[]
  >;

  Object.entries(geoRegionMapBySupplier).forEach(
    ([supplierId, geoRegionIds]) => {
      const alertGeoRegions =
        alertMap.get(supplierId)?.geoRegionIdSet || new Set<string>();
      const missingIds = geoRegionIds.filter(
        (geoRegionId) => !alertGeoRegions.has(geoRegionId),
      );

      if (missingIds.length > 0) {
        missingGeoRegionIds[supplierId] = missingIds;
      }
    },
  );

  return missingGeoRegionIds;
};