Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ workflows:
- develop
- pm-1127_1
- PM-4305
- PM-4490
- PM-4491-fix
- PM-3497_talent-search

Expand Down
15 changes: 15 additions & 0 deletions sql/reports/identity/users-by-handles.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ WITH input_handles AS (
SELECT
ih.handle_input AS "handle",
u.user_id AS "userId",
NULLIF(TRIM(mem."firstName"), '') AS "firstName",
NULLIF(TRIM(mem."lastName"), '') AS "lastName",
NULLIF(TRIM(ph.phone_number::text), '') AS "contactNumber",
pe.address AS "email",
COALESCE(
NULLIF(BTRIM(mem."competitionCountryCode"), ''),
Expand All @@ -24,6 +27,18 @@ LEFT JOIN LATERAL (
LIMIT 1
) AS pe
ON TRUE
LEFT JOIN LATERAL (
SELECT p."number" AS phone_number
FROM members."memberPhone" AS p
WHERE p."userId" = u.user_id
AND NULLIF(TRIM(p."number"::text), '') IS NOT NULL
ORDER BY
(p."type" = 'HOME') DESC,
p."createdAt" DESC NULLS LAST,
p."id" ASC
LIMIT 1
) AS ph
ON TRUE
LEFT JOIN members."member" AS mem
ON mem."userId" = u.user_id
ORDER BY ih.ordinality;
3 changes: 3 additions & 0 deletions src/reports/identity/dtos/identity-users.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export class UsersByGroupQueryDto {
export interface IdentityUserDto {
userId: number | null;
handle: string;
firstName: string | null;
lastName: string | null;
contactNumber: string | null;
email: string | null;
country: string | null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/reports/identity/identity-reports.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class IdentityReportsController {
@ApiBearerAuth()
@ApiOperation({
summary:
"Export user details (ID, handle, email, country) for a list of handles",
"Export user details (ID, handle, firstName, lastName, contactNumber, email, country) for a list of handles",
})
@ApiConsumes("application/json", "multipart/form-data")
@ApiBody({
Expand Down
6 changes: 6 additions & 0 deletions src/reports/identity/identity-reports.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const SUPPORTED_HANDLES_UPLOAD_EXTENSIONS = new Set([".txt", ".csv"]);
type UsersByHandlesRow = {
userId: number | null;
handle: string;
firstName: string | null;
lastName: string | null;
contactNumber: string | null;
email: string | null;
country: string | null;
};
Expand Down Expand Up @@ -114,6 +117,9 @@ export class IdentityReportsService {
return results.map((row) => ({
userId: row.userId,
handle: row.handle,
firstName: row.firstName,
lastName: row.lastName,
contactNumber: row.contactNumber,
email: row.email,
country: alpha3ToCountryName(row.country) ?? row.country,
}));
Expand Down
16 changes: 14 additions & 2 deletions src/reports/member/member-search.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import { Body, Controller, HttpCode, HttpStatus, Post, UseGuards } from "@nestjs/common";
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import {
Body,
Controller,
HttpCode,
HttpStatus,
Post,
UseGuards,
} from "@nestjs/common";
import {
ApiBearerAuth,
ApiOperation,
ApiResponse,
ApiTags,
} from "@nestjs/swagger";
import { MemberSearchBodyDto } from "./dto/member-search.dto";
import { MemberSearchResponseDto } from "./dto/member-search-response.dto";
import { MemberSearchService } from "./member-search.service";
Expand Down
12 changes: 9 additions & 3 deletions src/reports/member/member-search.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ describe("MemberSearchService", () => {

expect(dataSql).toContain("WITH recently_active AS");
expect(dataSql).not.toContain("requested_skills AS");
expect(dataSql).toContain('ORDER BY "matchIndex" DESC NULLS LAST, m.handle ASC');
expect(dataSql).toContain(
'ORDER BY "matchIndex" DESC NULLS LAST, m.handle ASC',
);

expect(countSql).toContain("SELECT COUNT(*)::integer AS total");

Expand Down Expand Up @@ -85,7 +87,9 @@ describe("MemberSearchService", () => {
});

it("uses filter params for count query but excludes pagination params", async () => {
mockDbService.query.mockResolvedValueOnce([]).mockResolvedValueOnce([{ total: 0 }]);
mockDbService.query
.mockResolvedValueOnce([])
.mockResolvedValueOnce([{ total: 0 }]);

await service.search({
country: "us",
Expand All @@ -99,7 +103,9 @@ describe("MemberSearchService", () => {
const dataParams = mockDbService.query.mock.calls[0][1] as unknown[];
const countParams = mockDbService.query.mock.calls[1][1] as unknown[];

expect(dataSql).toContain('ORDER BY m.handle ASC, "matchIndex" DESC NULLS LAST');
expect(dataSql).toContain(
'ORDER BY m.handle ASC, "matchIndex" DESC NULLS LAST',
);
expect(dataParams).toEqual(["us", 5, 5]);
expect(countParams).toEqual(["us"]);
});
Expand Down
13 changes: 8 additions & 5 deletions src/reports/member/member-search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,13 @@ skill_event_stats AS (
SELECT
se.user_id,
se.skill_id,
COUNT(*) FILTER (WHERE LOWER(set_t.name) = 'challenge_win') AS wins,
COUNT(*) FILTER (
WHERE LOWER(set_t.name) IN ('challenge_win', 'challenge_2nd_place', 'challenge_3rd_place', 'gig_completion') OR sest.name='engagement'
) AS wins,
COUNT(*) AS submitted
FROM skills.skill_event se
JOIN skills.skill_event_type set_t ON set_t.id = se.skill_event_type_id
JOIN skills.source_type sest ON sest.id = se.source_type_id
WHERE se.skill_id = ANY(${pSkillIds}::uuid[])
GROUP BY se.user_id, se.skill_id
),
Expand Down Expand Up @@ -182,17 +185,17 @@ member_address AS (
// ------------------------------------------------- dynamic WHERE
const where: string[] = [`m.status = 'ACTIVE'`];

if (openToWork === true) {
if (typeof openToWork === "boolean") {
where.push(`m."availableForGigs" = true`);
}

if (recentlyActive === true) {
if (typeof recentlyActive === "boolean") {
where.push(
`EXISTS (SELECT 1 FROM recently_active ra WHERE ra.user_id = m."userId")`,
);
}

if (verifiedProfile === true) {
if (typeof verifiedProfile === "boolean") {
where.push(
`(COALESCE(m.verified, false) = true OR EXISTS (SELECT 1 FROM verified_via_trolley vt WHERE vt.user_id = m."userId"))`,
);
Expand Down Expand Up @@ -235,7 +238,7 @@ SELECT
COALESCE(m."availableForGigs", false) AS "openToWork",
TRIM(
COALESCE(maddr.city || ' ', '') ||
COALESCE(m.country, COALESCE(m."competitionCountryCode", COALESCE(m."homeCountryCode", '')))
COALESCE(m."homeCountryCode", COALESCE(m.country, COALESCE(m."competitionCountryCode", '')))
) AS location,
${matchedSkillsExpr} AS "matchedSkills",
${matchIndexExpr} AS "matchIndex"
Expand Down
2 changes: 1 addition & 1 deletion src/reports/report-directory.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ const REGISTERED_REPORTS_DIRECTORY: RegisteredReportsDirectory = {
identityPostReport(
"Users by Handles",
"/identity/users-by-handles",
"Export user ID, handle, email, and country for each supplied handle; unknown handles return empty fields",
"Export user ID, handle, firstName, lastName, contactNumber, email, and country for each supplied handle; unknown handles return empty fields",
AppScopes.Identity.UsersByHandles,
[handlesBodyParam],
),
Expand Down
Loading