Skip to content

Purav taking over PR 2216 Core Team additional hours carryover when blue squares exceed five - #2320

Open
pixelpix13 wants to merge 3 commits into
developmentfrom
Purav-core-team-members-additional-hours-carryover
Open

Purav taking over PR 2216 Core Team additional hours carryover when blue squares exceed five#2320
pixelpix13 wants to merge 3 commits into
developmentfrom
Purav-core-team-members-additional-hours-carryover

Conversation

@pixelpix13

@pixelpix13 pixelpix13 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Description

image

Core Team members who exceed five blue squares in a rolling year must carry forward missed volunteer hours plus a +1 hour penalty for each blue square beyond the fifth. Prior work (#1612, #2216) left incorrect penalty math, double-counting in infringement descriptions/emails, a broken MongoDB $lookup in applyMissedHourForCoreTeam, and the production Bear email bug (negative penalty hours on early blue squares). This PR ports and completes that behavior on top of the refactored userHelper.js, extracts aggregation into coreTeamMissedHoursAggregation.js for test coverage, and adds unit + integration tests including Tatyana's priority scenarios.

Supersedes HGNRest #2216 (taken over on branch Purav-core-team-members-additional-hours-carryover). Builds on HGNRest #1612.

Fixes #1141
Fixes #1281

Related PRS (if any):

  • HGNRest #1083 — backend API sends missedHours to leaderboard (merged on development)
  • HighestGoodNetworkApp #2600 — frontend UI: Leaderboard tooltip + VolunteeringTimeTab "Additional Make-up Hours This Week" (merged on development)

How reviewers test this PR (backend + UI):

Repo Branch to checkout
HGNRest Purav-core-team-members-additional-hours-carryover (this PR)
HighestGoodNetworkApp development only — do not checkout the #2600 branch; UI changes are already merged

This is a backend-only PR. No new frontend PR is required.

Main changes explained:

  • userHelper.jsprocessUserForBlueSquare: Computes coreTeamExtraHour = max(0, year-filtered BS count + 1 − 5) before the time-not-met threshold. Required hours when assigning a new cron BS = weeklycommittedHours + missedHours + coreTeamExtraHour (Core Team only).
  • userHelper.jsbuildInfringementDescription: Uses prior-week missedHours and coreTeamExtraHour separately so penalty is not double-counted into the carryover line.
  • userHelper.jsgetInfringementEmailBody: Fixes the Bear bug (no more -3 hour(s) penalty text); shows correct owed-hours breakdown (commitment + carryover + penalty = total required); hides penalty line when penalty is 0.
  • userHelper.jsapplyMissedHourForCoreTeam: Fixed $lookup pipeline syntax; year-filtered infringement count; incremental penalty (max(0,count−5) − max(0,count−6)); $set in bulkWrite; skips empty bulk ops.
  • coreTeamMissedHoursAggregation.js (new): MongoDB aggregation pipeline for Core Team missedHours calculation (extracted from inline logic for unit test coverage).
  • userHelper.jsresendBlueSquareEmailsOnlyForLastWeek: Resend path includes coreTeamExtraHour and passes base weeklycommittedHours correctly for Core Team resend emails.
  • Tests: coreTeamCarryoverHelper.test.js, coreTeamMissedHoursAggregation.test.js, getInfringementEmailBody.test.js (29 unit tests — run in CI); timeNotMetCoreTeamTest.test.js12 integration tests (skipped in CI, run locally with MongoMemoryServer).

Penalty & carryover rules (reviewer reference)

Active BS count (year-filtered) Penalty on next cron failure missedHours after cron
1–5 0 penalty hours Shortfall only: max(0, commitment + prior missedHours − hours logged)
6th +1 penalty hour Shortfall + 1
7th +2 penalty hours total Shortfall + 2
nth (n > 5) +(n − 5) penalty hours Shortfall + (n − 5)

Formula when cron assigns a new blue square:
required = weeklycommittedHours + missedHours + max(0, yearFilteredCount + 1 − 5)

Leaderboard / profile UI (frontend development): displays effective required as weeklycommittedHours + missedHours. Penalty hours are rolled into missedHours by the Sunday cron — the frontend does not calculate penalty client-side.

How to test:

Reviewer quick path: Section A alone is sufficient to approve (29 unit tests in CI). Email content and penalty math are proven by unit + integration tests — do not expect inbox delivery on local npm start. Sections B–G are optional UI/cron verification.

A. Automated tests (required — no local stack needed)

  1. Check out branch Purav-core-team-members-additional-hours-carryover on HGNRest.
  2. Run npm install if needed. Use Node 20.x (nvm use 20).
  3. Unit tests (CI — must pass):
    npm test -- src/helpers/__tests__/coreTeamCarryoverHelper.test.js src/helpers/__tests__/coreTeamMissedHoursAggregation.test.js src/helpers/__tests__/getInfringementEmailBody.test.js
    Expected: 29/29 passing
  4. Bear email regression:
    npm run test:verbose -- src/helpers/__tests__/getInfringementEmailBody.test.js -t "Bear"
    Expect: no -3 hour / -3 hours in output; owed total shown correctly.
  5. Integration tests (local only — skipped in CI when CI or GITHUB_ACTIONS is set):
    npm test -- src/test/timeNotMetCoreTeamTest.test.js
    Expected: 12/12 passing, including Tatyana scenarios below.

B. Local full-stack setup (optional manual verification)

  1. HGNRest: configure .env (MongoDB, SMTP, JWT, etc.). Start Redis (redis-server; fix ECONNREFUSED 127.0.0.1:6379 if backend fails).
  2. HGNRest: npm run build then npm start.
  3. HighestGoodNetworkApp: checkout development. Point REACT_APP_APIENDPOINT to local API if not using staging (e.g. http://localhost:4500/api). npm installnpm run start:localhttp://localhost:5173
  4. Clear site data/cache if UI behaves oddly after branch switch.

C. Create a Core Team test user

  1. Log in as Admin
  2. User Management → Add New User (or edit existing). Use your first name in the account name; password 123Welcome!.
  3. Set Role = Core Team, Weekly Committed Hours = 5 (matches Tatyana test scenarios) or 10 for simpler math; ensure user is Active.
  4. Ensure the user has a valid weekly summary on file (cron skips BS assignment without one).
  5. Open profile: http://localhost:5173/userprofile/{userId}Volunteering Time tab.

D. Tatyana scenarios (mirror integration tests)

Use last completed week time entries (Sunday–Saturday PST). Log tangible time dated in that window via the UI, then trigger cron helpers locally or wait for Sunday cron.

Scenario 1 — 1st BS, no penalty:

  • Setup: 0 prior blue squares, missedHours = 0, commitment 5h.
  • Log 3h tangible time for last week.
  • After cron (assignBlueSquareForTimeNotMetapplyMissedHourForCoreTeam): expect 1 BS, missedHours = 2 (5 − 3).
  • UI: Leaderboard tooltip + Additional Make-up Hours This Week = 2. Next week effective required = 7h (5 + 2 + 0 penalty).

Scenario 2 — 6th BS, +1 penalty:

  • Setup: 5 year-filtered BS, commitment 5h, missedHours = 0.
  • Log 3h last week.
  • After cron: expect 6 BS, missedHours = 3 (2h shortfall + 1h penalty).
  • Next week effective required = 8h (5 + 3).

Scenario 3 — 7th BS, +2 penalty:

  • Setup: 6 BS, missedHours = 3, commitment 5h.
  • Log 2h last week.
  • After cron: expect 7 BS, missedHours = 7 (5h shortfall + 2h penalty).
  • Next week effective required = 12h (5 + 7).

Scenario 4 — Meets hours including penalty (no new BS):

  • Setup: 6 BS, commitment 10h.
  • Log 12h last week (meets 10 + 2 penalty).
  • After cron: still 6 BS, missedHours = 0.

E. Manual admin blue square vs cron (critical — read before testing)

Manually adding blue squares (User Profile → Blue Squares → add, requires admin permission):

What happens Manual add Cron-assigned add
Infringement count on profile ✅ Increases ✅ Increases
Email delivered to inbox Production only — generic "New Infringement Assigned" Production only — full breakdown with hours owed
Email on local npm start Not sent (see Section H) Not sent (see Section H)
Hours/penalty breakdown in email Not included (even in production) ✅ commitment + carryover + penalty = total
missedHours updated Not updated ✅ Updated by applyMissedHourForCoreTeam
Leaderboard / make-up hours UI ❌ Unchanged until cron ✅ Updates after cron

At exactly 5 manual blue squares:

  • Still zero penalty hours in required total.
  • Leaderboard shows weeklycommittedHours + missedHours only (e.g. 10 + 0 = 10).
  • Penalty starts when a 6th blue square is cron-assigned after a failed week (+1 hour), not from manual count alone.

Adding a 6th blue square manually:

  • Profile shows 6 infringements (this is expected and sufficient for count verification).
  • You will not receive an email locally — this is not a bug (Section H).
  • Even in production, manual add email is generic ("6th blue square of 5") with no "+1 hour owed" breakdown.
  • missedHours and leaderboard still unchanged until Sunday cron runs.
  • To prove penalty math for review, use Section A integration tests or cron simulation with short time entries — not manual add alone.

F. UI verification (frontend development)

  1. Dashboard → Leaderboard: hover the user's hours cell — tooltip shows committed hours + missedHours carryover (from ambika fix core team unique additions in leaderboard #1083 + #2600).
  2. User Profile → Volunteering Time: "Additional Make-up Hours This Week" matches backend missedHours after cron runs. To demo UI without waiting for cron, an admin may temporarily set missedHours on the profile and confirm the leaderboard tooltip updates.
  3. Roles: Admin can add manual BS and view any profile. Confirm Volunteer users get BS on time-not-met but missedHours stays 0 (non–Core Team path).
  4. Dark mode (optional): sanity-check leaderboard tooltip and make-up hours label on development frontend.

G. Edge cases

  1. Zero committed hours: no BS assigned, missedHours = 0.
  2. No time entries last week: BS assigned; carryover = full commitment (+ penalty if 6+ BS on cron).
  3. Year filter: BS older than 1 year do not count toward penalty threshold.

H. Email delivery on local dev (read this if you added blue squares and got no email)

This is expected behavior, not a PR failure.

When running the backend locally (npm start), emailSender.js does not send emails unless NODE_ENV=production (password-reset emails are the only exception):

if (NODE_ENV !== 'production') → returns EMAIL_SENDING_DISABLED_NON_PROD

So if you manually add 5 or 6 blue squares on a local stack:

  • ✅ Blue squares will appear on the profile
  • No email will arrive in Gmail/inbox
  • missedHours will not update (manual add never does — see Section E)

How reviewers verify email fixes without an inbox:

What to verify How
Bear bug (no -3 hours) Section A step 4 — getInfringementEmailBody.test.js -t "Bear"
Cron email with penalty breakdown Section A step 5 — integration tests, or coreTeamCarryoverHelper.test.js
Manual add generic email body coreTeamCarryoverHelper.test.jsassignBlueSquareForTimeNotMet mock assertions

The profile subscribed / unsubscribed toggle does not block infringement emails — local NODE_ENV is what prevents delivery.

###Screenshots
pr 2320_3
pr 2320_4

Note:

  • Backend-only PR. Frontend changes are already merged on HighestGoodNetworkApp development; checkout development, not PR #2600's branch.
  • Local email is disabled when NODE_ENV !== 'production'. Reviewers must use Section A tests for email proof — not their inbox.
  • Sunday cron (userProfileJobs.js, 12:00 AM PST): runs assignBlueSquareForTimeNotMet then applyMissedHourForCoreTeam. Manual BS added mid-week do not update missedHours until that job runs.
  • timeNotMetCoreTeamTest.test.js is skipped in CI (CI / GITHUB_ACTIONS). Reviewers who cannot run MongoMemoryServer locally should rely on unit tests (29/29) + UI spot-check.
  • Redis required for local backend. Node 20.x required.
  • Cron order unchanged: assign blue squares first, then apply missed hours.
  • Penalty count is year-filtered (infringements within the last calendar year).
  • Do not merge — Jae merges. Text Jae (310-755-4693) for urgent merges.

Port and complete PR #2216: include penalty hours in weekly threshold, missed-hour roll-forward, email/resend math, and integration tests for Tatyana scenarios including Bear email negative penalty bug.
@sonarqubecloud

Copy link
Copy Markdown

@one-community one-community added the High Priority - Please Review First This is an important PR we'd like to get merged as soon as possible label Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

High Priority - Please Review First This is an important PR we'd like to get merged as soon as possible

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants