Skip to content

Commit

Permalink
Find duplicates: prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
Ruben committed Dec 9, 2024
1 parent ab0dd6c commit 075b36f
Show file tree
Hide file tree
Showing 20 changed files with 713 additions and 277 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,82 @@
[sizeMd]="5"
[sizeLg]="4"
>
<ion-card>
<ion-card-header>
<ion-card-title>Duplicate Registrations</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-grid>
<ion-row>
<ion-col>Duplicate Registration ID</ion-col>
<ion-col>Value</ion-col>
<ion-col>Attribute Name</ion-col>
<ion-col>Action</ion-col>
</ion-row>
<ng-container *ngFor="let duplicate of duplicates">
<ion-row>
<ion-col>
<a
[href]="
'http://localhost:8888/program/' +
program.id +
'/registration/' +
duplicate.duplicateRegistrationProgramId
"
>
{{ duplicate.duplicateRegistrationProgramId }}
</a>
</ion-col>
<ion-col>{{ duplicate.value }}</ion-col>
<ion-col>{{ duplicate.name }}</ion-col>
<ion-col>
<ion-button
(click)="postUnique(duplicate.duplicateReferenceId)"
>
Mark as Unique
</ion-button>
</ion-col>
</ion-row>
</ng-container>
</ion-grid>
</ion-card-content>
</ion-card>

<ion-card>
<ion-card-header>
<ion-card-title>Fuzzy Duplicate Registrations</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-grid>
<ion-row>
<ion-col>Fuzzy Match Score</ion-col>
<ion-col>Duplicate Registration ID</ion-col>
<ion-col>Value</ion-col>
<ion-col>Attribute Name</ion-col>
</ion-row>
<ng-container *ngFor="let fuzzyDuplicate of fuzzyDuplicates">
<ion-row>
<ion-col>{{ fuzzyDuplicate.fuzzyMatchScore }}</ion-col>
<ion-col>
<a
[href]="
'http://localhost:8888/program/' +
program.id +
'/registration/' +
fuzzyDuplicate.duplicateRegistrationProgramId
"
>
{{ fuzzyDuplicate.duplicateRegistrationProgramId }}
</a>
</ion-col>
<ion-col>{{ fuzzyDuplicate.value }}</ion-col>
<ion-col>{{ fuzzyDuplicate.name }}</ion-col>
</ion-row>
</ng-container>
</ion-grid>
</ion-card-content>
</ion-card>

<app-registration-personal-information
data-testid="registration-personal-information-data"
[person]="person"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export class RegistrationProfileComponent implements OnInit {

public lastRegistrationStatusChangeEvent: Event;

public duplicates: any[];
public fuzzyDuplicates: any[];

constructor(
private authService: AuthService,
private programsService: ProgramsServiceApiService,
Expand All @@ -54,6 +57,16 @@ export class RegistrationProfileComponent implements OnInit {
this.lastRegistrationStatusChangeEvent = this.events.find(
(event) => event.type === EventEnum.registrationStatusChange,
);
const duplicateObject = await this.programsService.getRegistrationDuplicate(
this.program.id,
this.person.referenceId,
);
this.duplicates = duplicateObject.duplicates;
this.fuzzyDuplicates = duplicateObject.fuzzyDuplicates;
console.log(
'🚀 ~ RegistrationProfileComponent ~ ngOnInit ~ this.duplicates:',
this.duplicates,
);
}

public canViewPhysicalCards(programId: number): boolean {
Expand All @@ -64,6 +77,15 @@ export class RegistrationProfileComponent implements OnInit {
]);
}

public async postUnique(referenceIdDuplicate: string) {
await this.programsService.postUnique(
this.program.id,
this.person.referenceId,
referenceIdDuplicate,
);
window.location.reload();
}

public fspHasPhysicalCardSupport(
fspName: Person['financialServiceProviderName'],
): boolean {
Expand Down
24 changes: 24 additions & 0 deletions interfaces/Portal/src/app/services/programs-service-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,30 @@ export class ProgramsServiceApiService {
);
}

public getRegistrationDuplicate(
programId: number,
referenceId: string,
): Promise<any> {
return this.apiService.get(
environment.url_121_service_api,
`/programs/${programId}/registrations/${referenceId}/duplicates`,
);
}

public postUnique(
programId: number,
referenceId1: string,
referenceId2: string,
): Promise<Note> {
return this.apiService.post(
environment.url_121_service_api,
`/programs/${programId}/registrations/${referenceId1}/uniques`,
{
referenceId: referenceId2,
},
);
}

public async getUpdateWalletAndCards(
programId: number,
referenceId: string,
Expand Down
60 changes: 60 additions & 0 deletions services/121-service/src/migration/1733229093775-duplicate-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class DuplicateView1733229093775 implements MigrationInterface {
name = 'DuplicateView1733229093775';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`,
['VIEW', 'registration_view', '121-service'],
);
await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm`); // For similarity postgres function
await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;`); // For levenshtein distance
await queryRunner.query(`DROP VIEW "121-service"."registration_view"`);
await queryRunner.query(`CREATE VIEW "121-service"."registration_view" AS SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "programFinancialServiceProviderConfigurationLabel", CAST(CONCAT('PA #',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",'yyyy-mm-dd') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || ': ' || "message"."status",'no messages yet') AS "lastMessageStatus", (CASE
WHEN "registration"."id" IN (
SELECT
d1."registrationId"
FROM
"121-service".registration_attribute_data d1
JOIN (
SELECT
"programRegistrationAttributeId",
value
FROM
"121-service".registration_attribute_data rad
LEFT JOIN
"121-service".program_registration_attribute pra ON pra.id = rad."programRegistrationAttributeId"
WHERE
value != '' AND pra."duplicateCheck"
GROUP BY
"programRegistrationAttributeId",
value
HAVING
COUNT(*) > 1
) d2
ON
d1."programRegistrationAttributeId" = d2."programRegistrationAttributeId"
AND d1.value = d2.value
) THEN TRUE
ELSE FALSE
END) AS "isDuplicate" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC`);
await queryRunner.query(
`INSERT INTO "121-service"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`,
[
'121-service',
'VIEW',
'registration_view',
'SELECT "registration"."id" AS "id", "registration"."created" AS "registrationCreated", "registration"."programId" AS "programId", "registration"."registrationStatus" AS "status", "registration"."referenceId" AS "referenceId", "registration"."phoneNumber" AS "phoneNumber", "registration"."preferredLanguage" AS "preferredLanguage", "registration"."inclusionScore" AS "inclusionScore", "registration"."paymentAmountMultiplier" AS "paymentAmountMultiplier", "registration"."maxPayments" AS "maxPayments", "registration"."paymentCount" AS "paymentCount", "registration"."scope" AS "scope", "fspconfig"."label" AS "programFinancialServiceProviderConfigurationLabel", CAST(CONCAT(\'PA #\',registration."registrationProgramId") as VARCHAR) AS "personAffectedSequence", registration."registrationProgramId" AS "registrationProgramId", TO_CHAR("registration"."created",\'yyyy-mm-dd\') AS "registrationCreatedDate", fspconfig."name" AS "programFinancialServiceProviderConfigurationName", fspconfig."id" AS "programFinancialServiceProviderConfigurationId", fspconfig."financialServiceProviderName" AS "financialServiceProviderName", "registration"."maxPayments" - "registration"."paymentCount" AS "paymentCountRemaining", COALESCE("message"."type" || \': \' || "message"."status",\'no messages yet\') AS "lastMessageStatus", (CASE\n WHEN "registration"."id" IN (\n SELECT\n d1."registrationId"\n FROM\n "121-service".registration_attribute_data d1\n JOIN (\n SELECT\n "programRegistrationAttributeId",\n value\n FROM\n "121-service".registration_attribute_data rad\n LEFT JOIN\n "121-service".program_registration_attribute pra ON pra.id = rad.id\n WHERE\n value != \'\' AND pra."duplicateCheck"\n GROUP BY\n "programRegistrationAttributeId",\n value\n HAVING\n COUNT(*) > 1\n ) d2\n ON\n d1."programRegistrationAttributeId" = d2."programRegistrationAttributeId"\n AND d1.value = d2.value\n ) THEN TRUE\n ELSE FALSE\n END) AS "isDuplicate" FROM "121-service"."registration" "registration" LEFT JOIN "121-service"."program_financial_service_provider_configuration" "fspconfig" ON "fspconfig"."id"="registration"."programFinancialServiceProviderConfigurationId" LEFT JOIN "121-service"."latest_message" "latestMessage" ON "latestMessage"."registrationId"="registration"."id" LEFT JOIN "121-service"."twilio_message" "message" ON "message"."id"="latestMessage"."messageId" ORDER BY "registration"."registrationProgramId" ASC',
],
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`,
['VIEW', 'registration_view', '121-service'],
);
await queryRunner.query(`DROP VIEW "121-service"."registration_view"`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RegistrationUniquePairs1733401228093
implements MigrationInterface
{
name = 'RegistrationUniquePairs1733401228093';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "121-service"."registration_unique_pairs" ("id" SERIAL NOT NULL, "created" TIMESTAMP NOT NULL DEFAULT now(), "updated" TIMESTAMP NOT NULL DEFAULT now(), "registrationSmallerId" integer NOT NULL, "registrationLargerId" integer NOT NULL, CONSTRAINT "UQ_baca30bd87b6df409d332675954" UNIQUE ("registrationSmallerId", "registrationLargerId"), CONSTRAINT "PK_953444295386f708a5f10d4a2df" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_093e666fd0931ed9dadbe3b81d" ON "121-service"."registration_unique_pairs" ("created") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_027ea55217d7fe593fba98fcd1" ON "121-service"."registration_unique_pairs" ("registrationSmallerId") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_835621467e670d1d4e51d7a850" ON "121-service"."registration_unique_pairs" ("registrationLargerId") `,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DELETE FROM "121-service"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`,
['VIEW', 'registration_view', '121-service'],
);
await queryRunner.query(`DROP VIEW "121-service"."registration_view"`);
await queryRunner.query(
`DROP INDEX "121-service"."IDX_835621467e670d1d4e51d7a850"`,
);
await queryRunner.query(
`DROP INDEX "121-service"."IDX_027ea55217d7fe593fba98fcd1"`,
);
await queryRunner.query(
`DROP INDEX "121-service"."IDX_093e666fd0931ed9dadbe3b81d"`,
);
await queryRunner.query(
`DROP TABLE "121-service"."registration_unique_pairs"`,
);
await queryRunner.query(
`CREATE INDEX "registration_attribute_data_value_idx" ON "121-service"."registration_attribute_data" ("value") `,
);
}
}
Loading

0 comments on commit 075b36f

Please sign in to comment.