diff --git a/src/components/project/handlers/set-department-id.handler.ts b/src/components/project/handlers/set-department-id.handler.ts index b11664f077..2fd2e7d697 100644 --- a/src/components/project/handlers/set-department-id.handler.ts +++ b/src/components/project/handlers/set-department-id.handler.ts @@ -97,15 +97,21 @@ export class SetDepartmentId implements IEventHandler { // Get used IDs .subQuery((sub) => sub - .match([ - node('', 'Project'), - relation('out', '', 'departmentId', ACTIVE), - node('deptIdNode', 'Property'), - ]) - .where({ 'deptIdNode.value': not(isNull()) }) - .return(collect('deptIdNode.value').as('used')), + .subQuery((sub2) => + sub2 + .match([ + node('', 'Project'), + relation('out', '', 'departmentId', ACTIVE), + node('deptIdNode', 'Property'), + ]) + .where({ 'deptIdNode.value': not(isNull()) }) + .return('deptIdNode.value as id') + .union() + .match(node('external', 'ExternalDepartmentId')) + .return('external.departmentId as id'), + ) + .return(collect('id').as('used')), ) - // Distill to available .with('[id in enumerated where not id in used][0] as next') // collapse cardinality to zero if none available .raw('unwind next as nextId') diff --git a/src/components/project/migrations/create-used-dept-id-list.migration.ts b/src/components/project/migrations/create-used-dept-id-list.migration.ts new file mode 100644 index 0000000000..2780316e34 --- /dev/null +++ b/src/components/project/migrations/create-used-dept-id-list.migration.ts @@ -0,0 +1,51 @@ +import { node } from 'cypher-query-builder'; +import { readFile } from 'node:fs/promises'; +import { BaseMigration, Migration } from '~/core/database'; +import { apoc, variable } from '~/core/database/query'; + +interface ExternalDepartmentId { + id: string; + name?: string; +} + +@Migration('2025-09-17T09:00:00') +export class CreateUsedDeptIdListMigration extends BaseMigration { + async up() { + const intaactFilePath = new URL( + '../../../../../.vscode/localFiles/All-Department-Ids-From-Intaact.csv', + import.meta.url, + ); + const cordFilePath = new URL( + '../../../../../.vscode/localFiles/prod_deptIds_ids_only.csv', + import.meta.url, + ); + + const intaactFileContent = await readFile(intaactFilePath, 'utf-8'); + const cordFileContent = await readFile(cordFilePath, 'utf-8'); + + const intaactRows = intaactFileContent.trim().split(/\r?\n/).slice(1); // Skip header + const cordRows = cordFileContent.trim().split(/\r?\n/).slice(1); // Skip header + + const intaactList: ExternalDepartmentId[] = intaactRows.flatMap((row) => { + const [id, name] = row.split(','); + return id ? { id, name } : []; + }); + + const prunedIntaactList = intaactList.flatMap((row) => + !cordRows.includes(row.id) ? row : [], + ); + + await this.db + .query() + .unwind(prunedIntaactList, 'dept') + .create(node('external', 'ExternalDepartmentId')) + .setValues({ + 'external.id': variable(apoc.create.uuid()), + 'external.departmentId': variable('dept.id'), + 'external.name': variable('dept.name'), + 'external.createdAt': variable('datetime()'), + }) + .return('count(external) as created') + .executeAndLogStats(); + } +} diff --git a/src/components/project/project.module.ts b/src/components/project/project.module.ts index a798a66a78..1cc0f7d400 100644 --- a/src/components/project/project.module.ts +++ b/src/components/project/project.module.ts @@ -14,6 +14,7 @@ import { ProjectEngagementConnectionResolver } from './engagement-connection.res import { FinancialApproverModule } from './financial-approver/financial-approver.module'; import * as handlers from './handlers'; import { InternshipProjectResolver } from './internship-project.resolver'; +import { CreateUsedDeptIdListMigration } from './migrations/create-used-dept-id-list.migration'; import { FixDeptIdLabelMigration } from './migrations/fix-dept-id-label.migration'; import { RenameTranslationToMomentumMigration } from './migrations/rename-translation-to-momentum.migration'; import { ProjectEngagementIdResolvers } from './project-engagement-id.resolver'; @@ -55,6 +56,7 @@ import { ProjectWorkflowModule } from './workflow/project-workflow.module'; ...Object.values(ConcreteRepos), ProjectLoader, ...Object.values(handlers), + CreateUsedDeptIdListMigration, RenameTranslationToMomentumMigration, FixDeptIdLabelMigration, ],