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
4 changes: 3 additions & 1 deletion sql/reports/payment/member-payment-accrual.sql
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ recent_payments AS (
JOIN params pr ON TRUE
WHERE w.type = 'PAYMENT'
AND p.created_at >= pr.start_date
AND p.created_at <= pr.end_date
AND p.created_at < (
DATE_TRUNC('day', pr.end_date) + INTERVAL '1 day'
)
),
categorized_payments AS (
SELECT
Expand Down
2 changes: 1 addition & 1 deletion sql/reports/sfdc/ba-fees-monthly.sql
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ WITH filtered_payments AS (
ON w.winning_id = p.winnings_id
WHERE
($1::timestamptz IS NULL OR p.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR p.created_at <= $2::timestamptz)
AND ($2::timestamptz IS NULL OR p.created_at < (DATE_TRUNC('day', $2::timestamptz) + INTERVAL '1 day'))
AND ($3::text[] IS NULL OR p.billing_account = ANY($3::text[]))
AND ($4::text[] IS NULL OR p.billing_account != ALL($4::text[]))
),
Expand Down
2 changes: 1 addition & 1 deletion sql/reports/sfdc/ba-fees.sql
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ WITH filtered_payments AS (
ON w.winning_id = p.winnings_id
WHERE
($1::timestamptz IS NULL OR p.created_at >= $1::timestamptz)
AND ($2::timestamptz IS NULL OR p.created_at <= $2::timestamptz)
AND ($2::timestamptz IS NULL OR p.created_at < (DATE_TRUNC('day', $2::timestamptz) + INTERVAL '1 day'))
AND ($3::text[] IS NULL OR p.billing_account = ANY($3::text[]))
AND ($4::text[] IS NULL OR p.billing_account != ALL($4::text[]))
),
Expand Down
2 changes: 1 addition & 1 deletion sql/reports/sfdc/payments.sql
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ WHERE
))
AND ($6::text IS NULL OR challenge_name ILIKE '%' || $6 || '%')
AND created_at >= COALESCE($7::timestamptz, (NOW() AT TIME ZONE 'UTC') - INTERVAL '45 days')
AND ($8::timestamptz IS NULL OR created_at <= $8::timestamptz)
AND ($8::timestamptz IS NULL OR created_at < (DATE_TRUNC('day', $8::timestamptz) + INTERVAL '1 day'))
AND ($9::numeric IS NULL OR total_amount >= $9::numeric)
AND ($10::numeric IS NULL OR total_amount <= $10::numeric)
AND ($11::text[] IS NULL OR reported_challenge_status::text = ANY($11::text[]))
Expand Down
143 changes: 143 additions & 0 deletions sql/reports/statistics/general/country-member-details.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
WITH member_profiles AS (
SELECT
m."userId" AS user_id,
COALESCE(
NULLIF(TRIM(m."competitionCountryCode"), ''),
NULLIF(TRIM(m."homeCountryCode"), '')
) AS country_code,
m.handle,
m."photoURL" AS photo_url
FROM members.member m
),
country_members AS (
SELECT
country_code,
COUNT(*)::bigint AS members_count
FROM member_profiles
WHERE country_code IS NOT NULL
GROUP BY country_code
),
country_member_skills AS (
SELECT DISTINCT
members.country_code,
members.user_id,
skill.id AS skill_id,
skill.name
FROM member_profiles members
JOIN skills.user_skill user_skill
ON user_skill.user_id::bigint = members.user_id
JOIN skills.user_skill_level skill_level
ON skill_level.id = user_skill.user_skill_level_id
AND LOWER(skill_level.name) IN ('verified', 'self-declared')
JOIN skills.skill skill
ON skill.id = user_skill.skill_id
AND skill.deleted_at IS NULL
WHERE members.country_code IS NOT NULL
),
country_skill_counts AS (
SELECT
owned.country_code,
owned.skill_id,
owned.name,
COUNT(*)::bigint AS owned_count
FROM country_member_skills owned
GROUP BY owned.country_code, owned.skill_id, owned.name
),
country_skills AS (
SELECT
country_code,
JSONB_AGG(
JSONB_BUILD_OBJECT(
'name', name,
'count', owned_count,
'ownedCount', owned_count
)
ORDER BY owned_count DESC, name ASC, skill_id ASC
) AS skills
FROM country_skill_counts
GROUP BY country_code
),
history_wins AS (
SELECT
history."userId",
history."trackId",
history."typeId",
COUNT(*)::bigint AS wins
FROM members."memberStatsHistory" history
WHERE history.placement = 1
GROUP BY history."userId", history."trackId", history."typeId"
),
stats_wins AS (
SELECT
stats."userId",
SUM(COALESCE(stats.wins, history.wins, 0))::bigint AS wins
FROM members."memberStats" stats
LEFT JOIN history_wins history
ON history."userId" = stats."userId"
AND history."trackId" = stats."trackId"
AND history."typeId" = stats."typeId"
LEFT JOIN challenges."ChallengeTrack" track
ON track.id::text = stats."trackId"
WHERE stats."isPrivate" = false
AND (
UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DEVELOP%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DESIGN%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DATA%SCIENCE%'
OR UPPER(COALESCE(track.name, stats."trackId")) = 'QA'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%QUALITY%ASSURANCE%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%COPILOT%'
)
GROUP BY stats."userId"
),
winner_counts AS (
SELECT
members.country_code,
members.user_id,
members.handle,
members.photo_url,
rating.rating AS max_rating,
wins.wins
FROM stats_wins wins
JOIN member_profiles members
ON members.user_id = wins."userId"
LEFT JOIN members."memberMaxRating" rating
ON rating."userId" = members.user_id
WHERE members.country_code IS NOT NULL
AND NULLIF(TRIM(members.handle), '') IS NOT NULL
AND wins.wins > 0
),
ranked_members AS (
SELECT
winner_counts.*,
ROW_NUMBER() OVER (
PARTITION BY country_code
ORDER BY wins DESC, handle ASC, user_id ASC
) AS member_rank
FROM winner_counts
),
top_members AS (
SELECT
country_code,
JSONB_AGG(
JSONB_BUILD_OBJECT(
'handle', handle,
'wins', wins,
'photoURL', photo_url,
'maxRating', max_rating
)
ORDER BY member_rank
) FILTER (WHERE member_rank <= 3) AS top_members
FROM ranked_members
GROUP BY country_code
)
SELECT
countries.country_code,
countries.members_count AS "user.count",
COALESCE(skills.skills, '[]'::jsonb) AS skills,
COALESCE(members.top_members, '[]'::jsonb) AS top_members
FROM country_members countries
LEFT JOIN country_skills skills
ON skills.country_code = countries.country_code
LEFT JOIN top_members members
ON members.country_code = countries.country_code
ORDER BY "user.count" DESC, country_code ASC;
49 changes: 39 additions & 10 deletions sql/reports/statistics/general/first-place-by-country.sql
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
WITH winners AS (
SELECT s."memberId"::text AS member_id
FROM reviews.submission s
WHERE s.placement = 1
WITH history_wins AS (
SELECT
history."userId",
history."trackId",
history."typeId",
COUNT(*)::bigint AS wins
FROM members."memberStatsHistory" history
WHERE history.placement = 1
GROUP BY history."userId", history."trackId", history."typeId"
),
member_wins AS (
SELECT
stats."userId",
SUM(COALESCE(stats.wins, history.wins, 0))::bigint AS wins
FROM members."memberStats" stats
LEFT JOIN history_wins history
ON history."userId" = stats."userId"
AND history."trackId" = stats."trackId"
AND history."typeId" = stats."typeId"
LEFT JOIN challenges."ChallengeTrack" track
ON track.id::text = stats."trackId"
WHERE stats."isPrivate" = false
AND (
UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DEVELOP%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DESIGN%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DATA%SCIENCE%'
OR UPPER(COALESCE(track.name, stats."trackId")) = 'QA'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%QUALITY%ASSURANCE%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%COPILOT%'
)
GROUP BY stats."userId"
),
winners_country AS (
SELECT
COALESCE(
NULLIF(TRIM(m."homeCountryCode"), ''),
NULLIF(TRIM(m."competitionCountryCode"), '')
) AS country_code
FROM winners w
JOIN members.member m ON m."userId"::text = w.member_id
NULLIF(TRIM(m."competitionCountryCode"), ''),
NULLIF(TRIM(m."homeCountryCode"), '')
) AS country_code,
w.wins
FROM member_wins w
JOIN members.member m ON m."userId" = w."userId"
WHERE w.wins > 0
)
SELECT
country_code,
Expand All @@ -19,7 +48,7 @@ SELECT
FROM (
SELECT
country_code,
COUNT(*)::bigint AS first_place_count
SUM(wins)::bigint AS first_place_count
FROM winners_country
WHERE country_code IS NOT NULL
GROUP BY country_code
Expand Down
97 changes: 97 additions & 0 deletions sql/reports/statistics/general/top-winners-by-country.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
WITH history_wins AS (
SELECT
history."userId",
history."trackId",
history."typeId",
COUNT(*)::bigint AS wins
FROM members."memberStatsHistory" history
WHERE history.placement = 1
GROUP BY history."userId", history."trackId", history."typeId"
),
stats_wins AS (
SELECT
stats."userId",
SUM(COALESCE(stats.wins, history.wins, 0))::bigint AS wins
FROM members."memberStats" stats
LEFT JOIN history_wins history
ON history."userId" = stats."userId"
AND history."trackId" = stats."trackId"
AND history."typeId" = stats."typeId"
LEFT JOIN challenges."ChallengeTrack" track
ON track.id::text = stats."trackId"
WHERE stats."isPrivate" = false
AND (
UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DEVELOP%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DESIGN%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%DATA%SCIENCE%'
OR UPPER(COALESCE(track.name, stats."trackId")) = 'QA'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%QUALITY%ASSURANCE%'
OR UPPER(COALESCE(track.name, stats."trackId")) LIKE '%COPILOT%'
)
GROUP BY stats."userId"
),
winner_profiles AS (
SELECT
COALESCE(
NULLIF(TRIM(m."competitionCountryCode"), ''),
NULLIF(TRIM(m."homeCountryCode"), '')
) AS country_code,
m."userId" AS user_id,
m.handle,
m."photoURL" AS photo_url,
mmr.rating AS max_rating,
wins.wins
FROM stats_wins wins
JOIN members.member m
ON m."userId" = wins."userId"
LEFT JOIN members."memberMaxRating" mmr
ON mmr."userId" = m."userId"
WHERE COALESCE(
NULLIF(TRIM(m."competitionCountryCode"), ''),
NULLIF(TRIM(m."homeCountryCode"), '')
) IS NOT NULL
AND wins.wins > 0
),
country_totals AS (
SELECT
country_code,
SUM(wins)::bigint AS first_place_count
FROM winner_profiles
GROUP BY country_code
),
ranked_winners AS (
SELECT
winner_profiles.*,
ROW_NUMBER() OVER (
PARTITION BY country_code
ORDER BY wins DESC, handle ASC, user_id ASC
) AS winner_rank
FROM winner_profiles
WHERE NULLIF(TRIM(handle), '') IS NOT NULL
),
country_winners AS (
SELECT
country_code,
JSONB_AGG(
JSONB_BUILD_OBJECT(
'handle', handle,
'wins', wins,
'photoURL', photo_url,
'maxRating', max_rating
)
ORDER BY winner_rank
) FILTER (WHERE winner_rank <= 3) AS top_winners
FROM ranked_winners
GROUP BY country_code
)
SELECT
totals.country_code,
totals.first_place_count AS "challenge_stats.count",
DENSE_RANK() OVER (
ORDER BY totals.first_place_count DESC, totals.country_code ASC
)::int AS rank,
COALESCE(winners.top_winners, '[]'::jsonb) AS top_winners
FROM country_totals totals
LEFT JOIN country_winners winners
ON winners.country_code = totals.country_code
ORDER BY "challenge_stats.count" DESC, country_code ASC;
4 changes: 2 additions & 2 deletions src/reports/payment/dto/member-payment-accrual.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export class MemberPaymentAccrualQueryDto {

@ApiPropertyOptional({
description:
"End date (inclusive) for filtering payment creation date in ISO 8601 format",
example: "2024-01-31T23:59:59.000Z",
"End date (inclusive through the full calendar day) for filtering payment creation date in ISO 8601 format",
example: "2024-01-31",
})
@IsOptional()
@IsDateString()
Expand Down
12 changes: 12 additions & 0 deletions src/reports/payment/payment-reports.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,16 @@ describe("PaymentReportsService", () => {
["Challenge Payment"],
]);
});

it("uses the full inclusive end date in the payment SQL", () => {
const paymentSql = new SqlLoaderService().load(
"reports/payment/member-payment-accrual.sql",
);

expect(paymentSql).toContain("p.created_at >= pr.start_date");
expect(paymentSql).toContain(
"DATE_TRUNC('day', pr.end_date) + INTERVAL '1 day'",
);
expect(paymentSql).not.toContain("p.created_at <= pr.end_date");
});
});
6 changes: 6 additions & 0 deletions src/reports/report-directory.data.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ describe("getAccessibleReportsDirectory", () => {
"/payment/member-payment-accrual-task",
"/payment/member-payment-accrual-challenge",
]);
expect(directory.statistics?.reports.map((report) => report.path)).toEqual(
expect.arrayContaining([
"/statistics/general/country-member-details",
"/statistics/general/top-winners-by-country",
]),
);
});

it("returns public reports plus all challenge reports for product managers", () => {
Expand Down
Loading
Loading