From 7c558d115263ac850770133c1d6892454c542fc7 Mon Sep 17 00:00:00 2001 From: Yazeed Loonat Date: Mon, 28 Apr 2025 16:55:00 -0700 Subject: [PATCH 1/2] feat: building selection criteria file transfer script --- .../controllers/script-runner.controller.ts | 17 ++ api/src/services/script-runner.service.ts | 145 ++++++++++++++++++ shared-helpers/src/types/backend-swagger.ts | 28 +++- 3 files changed, 184 insertions(+), 6 deletions(-) diff --git a/api/src/controllers/script-runner.controller.ts b/api/src/controllers/script-runner.controller.ts index 4425cafc654..6e48b3ba16c 100644 --- a/api/src/controllers/script-runner.controller.ts +++ b/api/src/controllers/script-runner.controller.ts @@ -89,6 +89,23 @@ export class ScirptRunnerController { ); } + @Put('transferListingBuildingSelectionCriteriaAssetsData') + @ApiOperation({ + summary: + 'A script that pulls listing asset data from one source into the current db', + operationId: 'transferListingBuildingSelectionCriteriaAssetsData', + }) + @ApiOkResponse({ type: SuccessDTO }) + async transferListingBuildingSelectionCriteriaAssetsData( + @Body() dataTransferDTO: AssetTransferDTO, + @Request() req: ExpressRequest, + ): Promise { + return await this.scriptRunnerService.transferListingBuildingSelectionCriteriaAssetsData( + req, + dataTransferDTO, + ); + } + @Put('transferJurisdictionPartnerUserData') @ApiOperation({ summary: diff --git a/api/src/services/script-runner.service.ts b/api/src/services/script-runner.service.ts index 13d8041af05..4314e14dd88 100644 --- a/api/src/services/script-runner.service.ts +++ b/api/src/services/script-runner.service.ts @@ -1093,6 +1093,151 @@ export class ScriptRunnerService { return { success: true }; } + /** + * + * @param req incoming request object + * @param dataTransferDTO data transfer endpoint args. Should contain foreign db connection string + * @returns successDTO + * @description transfers building selection criteria assets for listings in the specified space into the new space + */ + async transferListingBuildingSelectionCriteriaAssetsData( + req: ExpressRequest, + dataTransferDTO: AssetTransferDTO, + prisma?: PrismaClient, + ): Promise { + // script runner standard start up + const requestingUser = mapTo(User, req['user']); + await this.markScriptAsRunStart( + `data transfer building selection criteria ${ + dataTransferDTO.jurisdiction + } page: ${dataTransferDTO.page || 1}`, + requestingUser, + ); + + // connect to foreign db based on incoming connection string + const client = + prisma || + new PrismaClient({ + datasources: { + db: { + url: dataTransferDTO.connectionString, + }, + }, + }); + await client.$connect(); + + const doorwayJurisdiction = await this.prisma.jurisdictions.findFirst({ + where: { name: dataTransferDTO.jurisdiction }, + }); + + if (!doorwayJurisdiction) { + throw new Error( + `${dataTransferDTO.jurisdiction} county doesn't exist in Doorway database`, + ); + } + + // get jurisdiction + const jurisdiction: { id: string }[] = + await client.$queryRaw`SELECT id, name FROM jurisdictions WHERE name = ${dataTransferDTO.jurisdiction}`; + + if (!jurisdiction) { + throw new Error( + `${dataTransferDTO.jurisdiction} county doesn't exist in foreign database`, + ); + } + + const take = calculateTake(40); + const skip = calculateSkip(take, dataTransferDTO.page || 1); + const listingTransferMap = await this.prisma.listingTransferMap.findMany({ + take, + skip, + orderBy: { + listingId: OrderByEnum.ASC, + }, + }); + console.log( + `Found ${listingTransferMap.length} listings on page ${ + dataTransferDTO.page || 1 + } out of a possible 40 for this page`, + ); + // loop over each new listing id <-> old listing id relation + for (let i = 0; i < listingTransferMap.length; i++) { + const oldAssetInfo: { + created_at: Date; + updated_at: Date; + file_id: string; + label: string; + }[] = await client.$queryRaw`SELECT + a.created_at, + a.updated_at, + a.file_id, + a.label + FROM listings l + JOIN assets a ON a.id = l.building_selection_criteria_file_id + WHERE l.id = ${listingTransferMap[i].oldId} :: UUID + AND l.building_selection_criteria_file_id IS NOT NULL`; + console.log( + `moving ${oldAssetInfo.length || 0} assets for listing: ${ + listingTransferMap[i].oldId + }:`, + ); + // loop over each listing image on the old listing + for (let j = 0; j < oldAssetInfo.length; j++) { + // pull down image from cloudinary + const image = await axios.get( + `https://res.cloudinary.com/${dataTransferDTO.cloudinaryName}/image/upload/${oldAssetInfo[j].file_id}.pdf`, + { + responseType: 'arraybuffer', + }, + ); + const newFileId = (oldAssetInfo[j].file_id as string) + .replace('housingbayarea/', '') + .replace('dev/', ''); + + // upload image to s3 + const res = await this.assetService.upload(newFileId, { + filename: null, + buffer: image.data, + fieldname: null, + originalname: `${newFileId}.pdf`, + encoding: null, + mimetype: 'application/pdf', + size: image.data.length, + destination: null, + path: null, + stream: null, + }); + + // update new listing with these assets + await this.prisma.listings.update({ + where: { + id: listingTransferMap[i].listingId, + }, + data: { + listingsBuildingSelectionCriteriaFile: { + create: { + fileId: res.url, + label: 'cloudinaryPDF', + }, + }, + }, + }); + } + } + + // disconnect from foreign db + await client.$disconnect(); + + // script runner standard spin down + await this.markScriptAsComplete( + `data transfer building selection criteria ${ + dataTransferDTO.jurisdiction + } page: ${dataTransferDTO.page || 1}`, + requestingUser, + ); + return { success: true }; + } + /** * * @param amiChartImportDTO this is a string in a very specific format like: diff --git a/shared-helpers/src/types/backend-swagger.ts b/shared-helpers/src/types/backend-swagger.ts index 73f347c6d5a..27e3c9fae22 100644 --- a/shared-helpers/src/types/backend-swagger.ts +++ b/shared-helpers/src/types/backend-swagger.ts @@ -2322,6 +2322,28 @@ export class ScriptRunnerService { axios(configs, resolve, reject) }) } + /** + * A script that pulls listing asset data from one source into the current db + */ + transferListingBuildingSelectionCriteriaAssetsData( + params: { + /** requestBody */ + body?: AssetTransferDTO + } = {} as any, + options: IRequestOptions = {} + ): Promise { + return new Promise((resolve, reject) => { + let url = basePath + "/scriptRunner/transferListingBuildingSelectionCriteriaAssetsData" + + const configs: IRequestConfig = getConfigs("put", "application/json", url, options) + + let data = params.body + + configs.data = data + + axios(configs, resolve, reject) + }) + } /** * A script that pulls partner user data from one source into the current db */ @@ -6962,12 +6984,6 @@ export enum EnumUnitGroupAmiLevelMonthlyRentDeterminationType { "flatRent" = "flatRent", "percentageOfIncome" = "percentageOfIncome", } -export enum HomeTypeEnum { - "apartment" = "apartment", - "duplex" = "duplex", - "house" = "house", - "townhome" = "townhome", -} export enum EnumUnitGroupAmiLevelCreateMonthlyRentDeterminationType { "flatRent" = "flatRent", "percentageOfIncome" = "percentageOfIncome", From d584f19be2f51da42db931f81f32960eb4a01f02 Mon Sep 17 00:00:00 2001 From: Yazeed Loonat Date: Tue, 29 Apr 2025 10:44:39 -0700 Subject: [PATCH 2/2] fix: remove pagination --- api/src/services/script-runner.service.ts | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/api/src/services/script-runner.service.ts b/api/src/services/script-runner.service.ts index 4314e14dd88..78630d1e1d4 100644 --- a/api/src/services/script-runner.service.ts +++ b/api/src/services/script-runner.service.ts @@ -1108,9 +1108,7 @@ export class ScriptRunnerService { // script runner standard start up const requestingUser = mapTo(User, req['user']); await this.markScriptAsRunStart( - `data transfer building selection criteria ${ - dataTransferDTO.jurisdiction - } page: ${dataTransferDTO.page || 1}`, + `data transfer building selection criteria ${dataTransferDTO.jurisdiction}`, requestingUser, ); @@ -1145,21 +1143,12 @@ export class ScriptRunnerService { `${dataTransferDTO.jurisdiction} county doesn't exist in foreign database`, ); } - - const take = calculateTake(40); - const skip = calculateSkip(take, dataTransferDTO.page || 1); const listingTransferMap = await this.prisma.listingTransferMap.findMany({ - take, - skip, orderBy: { listingId: OrderByEnum.ASC, }, }); - console.log( - `Found ${listingTransferMap.length} listings on page ${ - dataTransferDTO.page || 1 - } out of a possible 40 for this page`, - ); + console.log(`Found ${listingTransferMap.length} listings`); // loop over each new listing id <-> old listing id relation for (let i = 0; i < listingTransferMap.length; i++) { const oldAssetInfo: { @@ -1230,9 +1219,7 @@ export class ScriptRunnerService { // script runner standard spin down await this.markScriptAsComplete( - `data transfer building selection criteria ${ - dataTransferDTO.jurisdiction - } page: ${dataTransferDTO.page || 1}`, + `data transfer building selection criteria ${dataTransferDTO.jurisdiction}`, requestingUser, ); return { success: true };