diff --git a/.circleci/config.yml b/.circleci/config.yml index 562481b..a10c67e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,7 +64,7 @@ workflows: branches: only: - develop - - PM-4961 + - PM-4931 # Production builds are exectuted only on tagged commits to the # master branch. diff --git a/src/reports/member/dto/member-search-response.dto.ts b/src/reports/member/dto/member-search-response.dto.ts index 3e15d33..0141341 100644 --- a/src/reports/member/dto/member-search-response.dto.ts +++ b/src/reports/member/dto/member-search-response.dto.ts @@ -9,7 +9,7 @@ export class MatchedSkillDto { @ApiProperty({ description: - "True when the member has at least one challenge-win event for this skill.", + "True for platform-backed skill activity (wins and/or skill events). Matched skills only include these, not self-attested-only skills.", }) isVerified!: boolean; diff --git a/src/reports/member/member-search.service.spec.ts b/src/reports/member/member-search.service.spec.ts index 6f662fa..661ca28 100644 --- a/src/reports/member/member-search.service.spec.ts +++ b/src/reports/member/member-search.service.spec.ts @@ -142,7 +142,6 @@ describe("MemberSearchService", () => { expect(dataSql).not.toContain( 'EXISTS (SELECT 1 FROM recently_active ra WHERE ra.user_id = m."userId")', ); - expect(dataSql).not.toContain("COALESCE(m.verified, false) = true"); }); it("adds profileComplete CTE/join only when enabled and keeps count params free of pagination", async () => { @@ -226,7 +225,11 @@ describe("MemberSearchService", () => { expect(validationParams).toEqual([[skillA, skillB]]); expect(dataSql).toContain("requested_skills AS"); + expect(dataSql).toContain("FILTER (WHERE usd.wins > 0 OR usd.submitted > 0)"); expect(dataSql).toContain("INNER JOIN user_match_data umd"); + expect(dataSql).toContain("THEN COUNT(DISTINCT CASE"); + expect(dataSql).toContain("ELSE COUNT(DISTINCT CASE"); + expect(dataSql).toContain("(usd.wins >= rs.min_wins OR usd.submitted > 0)"); expect(dataParams).toContainEqual([skillA, skillB]); expect(dataParams).toContainEqual([5, 0]); expect(dataParams).toContain("AND"); diff --git a/src/reports/member/member-search.service.ts b/src/reports/member/member-search.service.ts index d760b7b..92bf277 100644 --- a/src/reports/member/member-search.service.ts +++ b/src/reports/member/member-search.service.ts @@ -141,31 +141,41 @@ qualifying_users AS ( GROUP BY usd.user_id HAVING CASE WHEN ${pSearchType} = 'AND' - THEN COUNT(DISTINCT CASE WHEN usd.wins >= rs.min_wins THEN usd.skill_id END) + THEN COUNT(DISTINCT CASE + WHEN (usd.wins >= rs.min_wins OR usd.submitted > 0) + THEN usd.skill_id END) = ${pNumSkills}::integer - ELSE COUNT(DISTINCT CASE WHEN usd.wins >= rs.min_wins THEN usd.skill_id END) >= 1 + ELSE COUNT(DISTINCT CASE + WHEN (usd.wins >= rs.min_wins OR usd.submitted > 0) + THEN usd.skill_id END) >= 1 END ), user_match_data AS ( SELECT usd.user_id, - SUM( - 1.0 - + LEAST(usd.wins::float / 100.0, 0.5) - + CASE WHEN usd.submitted > 0 - THEN (usd.wins::float / usd.submitted::float) * 0.5 - ELSE 0.0 - END + COALESCE( + SUM( + 1.0 + + LEAST(usd.wins::float / 100.0, 0.5) + + CASE WHEN usd.submitted > 0 + THEN (usd.wins::float / usd.submitted::float) * 0.5 + ELSE 0.0 + END + ) FILTER (WHERE usd.wins > 0 OR usd.submitted > 0), + 0.0 ) AS total_skill_points, - jsonb_agg( - jsonb_build_object( - 'id', usd.skill_id::text, - 'name', usd.skill_name, - 'isVerified', usd.wins > 0, - 'wins', usd.wins, - 'submitted', usd.submitted - ) - ORDER BY usd.skill_name + COALESCE( + jsonb_agg( + jsonb_build_object( + 'id', usd.skill_id::text, + 'name', usd.skill_name, + 'isVerified', (usd.wins > 0 OR usd.submitted > 0), + 'wins', usd.wins, + 'submitted', usd.submitted + ) + ORDER BY usd.skill_name + ) FILTER (WHERE usd.wins > 0 OR usd.submitted > 0), + '[]'::jsonb ) AS matched_skills FROM user_skill_data usd WHERE usd.user_id IN (SELECT user_id FROM qualifying_users)