Vizzuality/landgriffon

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

Summary

Maintainability
A
1 hr
Test Coverage
import {
  BigQuery,
  Query,
  SimpleQueryRowsResponse,
} from '@google-cloud/bigquery';
import {
  Inject,
  Injectable,
  Logger,
  ServiceUnavailableException,
} from '@nestjs/common';
import { DataSource } from 'typeorm';
import { AlertsOutput } from 'modules/eudr-alerts/dto/alerts-output.dto';
import {
  AlertedGeoregionsBySupplier,
  EUDRAlertDatabaseResult,
  EUDRAlertDates,
  IEUDRAlertsRepository,
} from 'modules/eudr-alerts/eudr.repositoty.interface';
import { GetEUDRAlertsDto } from 'modules/eudr-alerts/dto/get-alerts.dto';
import {
  BigQueryAlertsQueryBuilder,
  EUDR_ALERTS_DATABASE_FIELDS,
} from 'modules/eudr-alerts/alerts-query-builder/big-query-alerts-query.builder';

const projectId: string = 'carto-dw-ac-zk2uhih6';

export enum EUDRAlertsFields {
  alertDate = 'date',
  alertConfidence = 'alertconfidence',
  year = 'year',
  alertCount = 'pixel_count',
  geoRegionId = 'georegionid',
  supplierId = 'supplierid',
}

@Injectable()
export class AlertsRepository implements IEUDRAlertsRepository {
  logger: Logger = new Logger(AlertsRepository.name);
  bigQueryClient: BigQuery;

  constructor(
    private readonly dataSource: DataSource,
    @Inject('EUDRCredentials') private credentials: string,
    @Inject('EUDRDataset') private baseDataset: string,
  ) {
    // if (!credentials) {
    //   this.logger.error('BigQuery credentials are missing');
    //   throw new ServiceUnavailableException(
    //     'EUDR Module not available. Tearing down the application',
    //   );
    // }
    this.bigQueryClient = new BigQuery({
      credentials: JSON.parse(this.credentials),
      projectId,
    });
  }

  async getAlerts(dto?: GetEUDRAlertsDto): Promise<AlertsOutput[]> {
    const queryBuilder: BigQueryAlertsQueryBuilder =
      this.createQueryBuilder(dto);
    // TODO: Make field selection dynamic
    queryBuilder.from(this.baseDataset, 'alerts');
    queryBuilder.select(EUDR_ALERTS_DATABASE_FIELDS.alertDate, 'alertDate');
    queryBuilder.addSelect(
      EUDR_ALERTS_DATABASE_FIELDS.supplierId,
      'supplierId',
    );
    queryBuilder.addSelect(
      EUDR_ALERTS_DATABASE_FIELDS.alertCount,
      'alertCount',
    );
    queryBuilder.addSelect(
      EUDR_ALERTS_DATABASE_FIELDS.geoRegionId,
      'geoRegionId',
    );
    // queryBuilder.addSelect(
    //   EUDR_ALERTS_DATABASE_FIELDS.carbonRemovals,
    //   'carbonRemovals',
    // );
    queryBuilder.orderBy(EUDR_ALERTS_DATABASE_FIELDS.alertDate, 'ASC');
    return this.query(queryBuilder);
  }

  async getAlertedGeoRegionsBySupplier(dto: {
    supplierIds: string[];
    startAlertDate: Date;
    endAlertDate: Date;
  }): Promise<AlertedGeoregionsBySupplier[]> {
    const queryBuilder: BigQueryAlertsQueryBuilder =
      this.createQueryBuilder(dto);
    queryBuilder.from(this.baseDataset, 'alerts');
    queryBuilder.select(EUDR_ALERTS_DATABASE_FIELDS.geoRegionId, 'geoRegionId');
    queryBuilder.addSelect(
      EUDR_ALERTS_DATABASE_FIELDS.supplierId,
      'supplierId',
    );
    // queryBuilder.addSelect(
    //   EUDR_ALERTS_DATABASE_FIELDS.carbonRemovals,
    //   'carbonRemovals',
    // );
    return this.query(queryBuilder);
  }

  async getDates(dto: GetEUDRAlertsDto): Promise<EUDRAlertDates[]> {
    const queryBuilder: BigQueryAlertsQueryBuilder =
      this.createQueryBuilder(dto);
    queryBuilder.from(this.baseDataset, 'alerts');
    queryBuilder.select(EUDR_ALERTS_DATABASE_FIELDS.alertDate, 'alertDate');
    queryBuilder.orderBy(EUDR_ALERTS_DATABASE_FIELDS.alertDate, 'ASC');
    queryBuilder.groupBy(EUDR_ALERTS_DATABASE_FIELDS.alertDate);
    return this.query(queryBuilder);
  }

  async getAlertSummary(dto: any): Promise<EUDRAlertDatabaseResult[]> {
    const bigQueryBuilder: BigQueryAlertsQueryBuilder =
      this.createQueryBuilder(dto);
    bigQueryBuilder
      .from(this.baseDataset, 'alerts')
      .select('supplierid', 'supplierId')
      .addSelect(
        'SUM(CASE WHEN alertcount = 0 THEN 1 ELSE 0 END)',
        'zero_alerts',
      )
      .addSelect(
        'SUM(CASE WHEN alertcount > 0 THEN 1 ELSE 0 END)',
        'nonzero_alerts',
      )
      .addSelect('COUNT(*)', 'total_geo_regions')
      .groupBy('supplierid');

    const mainQueryBuilder: BigQueryAlertsQueryBuilder =
      this.createQueryBuilder();

    mainQueryBuilder
      .select('supplierid')
      .addSelect(
        '(CAST(zero_alerts AS FLOAT64) / NULLIF(total_geo_regions, 0)) * 100',
        'dfs',
      )
      .addSelect(
        '(CAST(nonzero_alerts AS FLOAT64) / NULLIF(total_geo_regions, 0)) * 100',
        'sda',
      )
      .from('(' + bigQueryBuilder.getQuery() + ')', 'alerts_summary')
      .setParameters(bigQueryBuilder.getParameters());

    return this.query(mainQueryBuilder);
  }

  private async query(queryBuilder: BigQueryAlertsQueryBuilder): Promise<any> {
    try {
      const response: SimpleQueryRowsResponse = await this.bigQueryClient.query(
        queryBuilder.buildQuery(),
      );
      if (!response.length || 'error' in response) {
        this.logger.error('Error in query', response);
        throw new Error();
      }
      return response[0];
    } catch (e) {
      this.logger.error('Error in query', e);
      throw new ServiceUnavailableException(
        'Unable to retrieve EUDR Data. Please contact your administrator.',
      );
    }
  }

  private createQueryBuilder(
    dto?: GetEUDRAlertsDto,
  ): BigQueryAlertsQueryBuilder {
    return new BigQueryAlertsQueryBuilder(
      this.dataSource.createQueryBuilder(),
      dto,
    );
  }
}