From 6d8b04a6ec2ebad33ddf2576d5b287f4728565ed Mon Sep 17 00:00:00 2001 From: himaniraghav3 Date: Wed, 16 Sep 2026 10:42:47 +0530 Subject: [PATCH] PM-6332 Blacklist tcwebservice to show up on skill stastics --- .circleci/config.yml | 1 - .../expert-skills/category-members.sql | 1 + .../expert-skills/category-stats.sql | 2 + .../expert-skills-statistics.service.spec.ts | 12 +++++ .../expert-skills-statistics.service.ts | 44 ++++++++++++++++++- .../statistics-expert-skills.sql.spec.ts | 3 ++ 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7f67e8d..0adcae0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -65,7 +65,6 @@ workflows: only: - develop - PM-4931 - - skill-statistics tags: only: /^dev-.*/ diff --git a/sql/reports/statistics/expert-skills/category-members.sql b/sql/reports/statistics/expert-skills/category-members.sql index 458069d..0f94522 100644 --- a/sql/reports/statistics/expert-skills/category-members.sql +++ b/sql/reports/statistics/expert-skills/category-members.sql @@ -11,6 +11,7 @@ WITH category_wins AS ( JOIN skills.source_type sest ON sest.id = se.source_type_id WHERE sk.category_id = $1::uuid + AND NOT (se.user_id::text = ANY($3)) AND ( LOWER(set_t.name) IN ( 'challenge_win', diff --git a/sql/reports/statistics/expert-skills/category-stats.sql b/sql/reports/statistics/expert-skills/category-stats.sql index 7cfedd6..089a1b6 100644 --- a/sql/reports/statistics/expert-skills/category-stats.sql +++ b/sql/reports/statistics/expert-skills/category-stats.sql @@ -20,6 +20,7 @@ member_counts AS ( ON sk.id = us.skill_id AND sk.deleted_at IS NULL WHERE sk.category_id = ANY($1::uuid[]) + AND NOT (us.user_id::text = ANY($2)) GROUP BY sk.category_id ), win_events AS ( @@ -37,6 +38,7 @@ win_events AS ( JOIN skills.source_type sest ON sest.id = se.source_type_id WHERE sk.category_id = ANY($1::uuid[]) + AND NOT (se.user_id::text = ANY($2)) AND ( LOWER(set_t.name) IN ( 'challenge_win', diff --git a/src/statistics/expert-skills-statistics.service.spec.ts b/src/statistics/expert-skills-statistics.service.spec.ts index 3cc8b1f..da0d58b 100644 --- a/src/statistics/expert-skills-statistics.service.spec.ts +++ b/src/statistics/expert-skills-statistics.service.spec.ts @@ -1,4 +1,5 @@ import { NotFoundException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { DbService } from "../db/db.service"; import { SqlLoaderService } from "../common/sql-loader.service"; import { ExpertSkillsStatisticsService } from "./expert-skills-statistics.service"; @@ -10,9 +11,18 @@ describe("ExpertSkillsStatisticsService", () => { const sql = { load: jest.fn().mockReturnValue("SELECT expert skills"), }; + const config = { + get: jest.fn((key: string, defaultValue?: string) => { + if (key === "REPORTS_EXCLUDED_USER_IDS") { + return '["22838965", "8547899"]'; + } + return defaultValue; + }), + }; const service = new ExpertSkillsStatisticsService( db as unknown as DbService, sql as unknown as SqlLoaderService, + config as unknown as ConfigService, ); beforeEach(() => { @@ -63,6 +73,7 @@ describe("ExpertSkillsStatisticsService", () => { "481b5ebc-2fe6-45ed-a90c-736936d458d7", "1f5ed3e8-8d22-44ea-b75d-ea85147a04da", ], + ["22838965", "8547899"], ]); expect(result[0]).toEqual( expect.objectContaining({ @@ -126,6 +137,7 @@ describe("ExpertSkillsStatisticsService", () => { expect(db.query).toHaveBeenNthCalledWith(2, "SELECT expert skills", [ "481b5ebc-2fe6-45ed-a90c-736936d458d7", 100, + ["22838965", "8547899"], ]); expect(result).toEqual([ { diff --git a/src/statistics/expert-skills-statistics.service.ts b/src/statistics/expert-skills-statistics.service.ts index 49f86ec..b04503c 100644 --- a/src/statistics/expert-skills-statistics.service.ts +++ b/src/statistics/expert-skills-statistics.service.ts @@ -1,4 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { alpha3ToCountryName, toAlpha2CountryCode, @@ -76,10 +77,45 @@ function formatMemberName( @Injectable() export class ExpertSkillsStatisticsService { + private readonly excludedUserIds: string[]; + constructor( private readonly db: DbService, private readonly sql: SqlLoaderService, - ) {} + private readonly config: ConfigService, + ) { + this.excludedUserIds = this.parseListConfig( + "REPORTS_EXCLUDED_USER_IDS", + "[]", + ); + } + + // Accepts either a JSON array string ('["1","2"]') or a comma-separated list. + private parseListConfig(key: string, defaultValue: string): string[] { + const raw = (this.config.get(key, defaultValue) ?? "").trim(); + + if (!raw) { + return []; + } + + try { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed)) { + return parsed + .map((item) => + typeof item === "number" ? String(item) : String(item ?? "").trim(), + ) + .filter(Boolean); + } + } catch { + // ignore JSON parse failure and fall back to comma-separated values + } + + return raw + .split(",") + .map((item) => item.trim()) + .filter(Boolean); + } async getCategories() { const categories = await this.loadCategories(); @@ -122,6 +158,7 @@ export class ExpertSkillsStatisticsService { const rows = await this.db.query(q, [ category.id, MEMBERS_LIMIT, + this.excludedUserIds, ]); return rows.map((row) => { @@ -180,7 +217,10 @@ export class ExpertSkillsStatisticsService { const q = this.sql.load( "reports/statistics/expert-skills/category-stats.sql", ); - const rows = await this.db.query(q, [categoryIds]); + const rows = await this.db.query(q, [ + categoryIds, + this.excludedUserIds, + ]); return new Map(rows.map((row) => [row.id, row])); } diff --git a/src/statistics/statistics-expert-skills.sql.spec.ts b/src/statistics/statistics-expert-skills.sql.spec.ts index 0e9a0ee..d1d5026 100644 --- a/src/statistics/statistics-expert-skills.sql.spec.ts +++ b/src/statistics/statistics-expert-skills.sql.spec.ts @@ -25,6 +25,8 @@ describe("Expert skills statistics SQL", () => { expect(sql).toContain("challenge_win"); expect(sql).toContain("gig_completion"); expect(sql).toContain("ts.rn <= 3"); + expect(sql).toContain("NOT (us.user_id::text = ANY($2))"); + expect(sql).toContain("NOT (se.user_id::text = ANY($2))"); expect(sql).not.toContain("NOT ILIKE 'Test Cat%'"); }); @@ -37,6 +39,7 @@ describe("Expert skills statistics SQL", () => { expect(sql).toContain("JOIN members.member m"); expect(sql).toContain('members."memberMaxRating"'); expect(sql).toContain("ORDER BY cw.wins DESC, m.handle ASC"); + expect(sql).toContain("NOT (se.user_id::text = ANY($3))"); expect(sql).toContain("LIMIT $2"); }); });