import { Controller , InternalServerErrorException , Post , UseInterceptors , UploadedFile , HttpCode , Query } from "@nestjs/common" ; import { FileInterceptor } from "@nestjs/platform-express" ; import csvParse from "csv-parse" ; import { Express } from "express" ; import { ImportRowFlag , DistrictsImportApiResponse , DistrictImportField , RegionConfigId } from "../../../../shared/entities" ; import { FIPS , MAX_IMPORT_ERRORS } from "../../../../shared/constants" ; import { TopologyService } from "../services/topology.service" ; import { RegionConfigsService } from "../../region-configs/services/region-configs.service" ; @ Controller ( "api/districts" ) export class DistrictsController { constructor ( public topologyService , private readonly regionConfigService ) { } @ UseInterceptors ( FileInterceptor ( "file" ) ) @ Post ( "import/csv" ) @ HttpCode ( 200 ) async importCsv ( @ UploadedFile ( ) file , @ Query ( "regionConfigId" ) regionConfigId ? ) { const parser = csvParse ( file . buffer , { fromLine : 2 } ) ; let records = [ ] ; for await ( const record of parser ) { records . push ( record ) ; } const flaggedRows = [ ] ; function setFlag ( row , rowNumber , field , errorText ) { const flag = { rowNumber : rowNumber , errorText : errorText , rowValue : row , field : field } ; flaggedRows [ rowNumber ] = flag ; } const stateFips = records [ 0 ] [ 0 ] ?. slice ( 0 , 2 ) ; if ( ! ( stateFips in FIPS ) ) { throw new InternalServerErrorException ( ) ; } const regionCode = FIPS [ stateFips ] ; const regionConfig = await this . regionConfigService . findOne ( regionConfigId ? { id : regionConfigId , archived : false } : { regionCode , hidden : false , archived : false } ) ; const geoCollection = regionConfig && ( await this . topologyService . get ( regionConfig . s3URI ) ) ; if ( ! geoCollection ) { throw new InternalServerErrorException ( ) ; } if ( ! ( "topology" in geoCollection ) ) { throw new InternalServerErrorException ( ) ; } const blockIdCounts = { } ; records . forEach ( ( record , i ) => { const rowFips = record [ 0 ] ?. slice ( 0 , 2 ) ; const blockId = record [ 0 ] ; blockIdCounts [ blockId ] = blockIdCounts [ blockId ] ? blockIdCounts [ blockId ] + 1 : 1 ; if ( ! ( rowFips in FIPS ) ) { setFlag ( record , i , "BLOCKID" , "Invalid FIPS code" ) ; } if ( rowFips !== stateFips && ! flaggedRows [ i ] ) { setFlag ( record , i , "BLOCKID" , "All geounits in an import must be within the same state" ) ; } if ( blockIdCounts [ blockId ] > 1 && ! flaggedRows [ i ] ) { setFlag ( record , i , "BLOCKID" , "Duplicate BLOCKID included in import" ) ; } if ( isNaN ( Number ( record [ 1 ] ) ) && ! flaggedRows [ i ] ) { setFlag ( record , i , "DISTRICT" , "Invalid district ID, must be numeric" ) ; } } ) ; const unflaggedRows = records . filter ( ( record , i ) => ! flaggedRows [ i ] ) ; const baseGeoLevel = geoCollection . definition . groups . slice ( ) . reverse ( ) [ 0 ] ; const baseGeoUnitProperties = geoCollection . topologyProperties [ baseGeoLevel ] ; const allBlockIds = new Set ( baseGeoUnitProperties . map ( ( props ) => props [ baseGeoLevel ] ) ) ; const invalidRecords = records . filter ( ( record , i ) => { if ( ! allBlockIds . has ( record [ 0 ] ) && ! flaggedRows [ i ] ) { setFlag ( record , i , "BLOCKID" , "Invalid block ID" ) ; return true ; } return false ; } ) ; if ( invalidRecords . length > MAX_IMPORT_ERRORS ) { return { error : `There were ${ invalidRecords . length } invalid block IDs for ${ regionCode }, ensure the CSV uploaded is for the Census year ${ regionConfig ?. census }` } ; } const blockToDistricts = Object . fromEntries ( unflaggedRows . map ( ( [ block , district ] ) => [ block , Number ( district ) ] ) ) ; const districtsDefinition = geoCollection . importFromCSV ( blockToDistricts ) ; const maxDistrictId = Object . values ( blockToDistricts ) . reduce ( ( a , b ) => Math . max ( a , b ) , 0 ) ; const rowFlags = flaggedRows . filter ( r => ! ! r ) ; const numFlags = rowFlags . length ; return { districtsDefinition , maxDistrictId , numFlags : numFlags || undefined , rowFlags : numFlags > 0 ? rowFlags . slice ( 0 , MAX_IMPORT_ERRORS ) : undefined } ; } }