DataTalksClub / DataTalksClub/website

Preserve cohort score breakdowns, leaderboards, privacy, and complaints

Open
#57 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accessibility admin courses data-migration enhancement frontend integration operations P0 security testing
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Parent epic: #5. Downstream certificate/graduate consumer: #58. Final management parity: #59. Production-like rehearsal: #60.

PM disposition

GROOMED / P0 / DEPENDENCY-BLOCKED. Do not dispatch engineering.

This issue preserves the adopted cohort scoring aggregation, leaderboard, pseudonymous visibility, score-breakdown, and complaint behavior after the Course → Cohort and homework/project service migrations. It also supplies one exact, PII-free completion/eligibility result to #58. It does not redesign homework or project scoring, issue certificates, export PII, send email, inspect protected data, or execute a production migration.

Normative authority

Adopted inputs; no second scoring algorithm

#55 owns the authoritative homework-submission component result and #56 owns the authoritative project-submission component result, passed result, volunteer-review disposition, and project/review lifecycle. #57 consumes those accepted results; it never recalculates answers, rubric responses, peer-review points, FAQ/learning-in-public points, caps, or pass/fail independently.

For one accepted Cohort and Enrollment:

  • homework_total is the sum of the accepted #55 Submission.total_score values belonging to that exact Cohort/Enrollment;
  • project_total is the sum of the accepted #56 ProjectSubmission.total_score values belonging to that exact Cohort/Enrollment where volunteer_review_only is false;
  • total_score = homework_total + project_total; missing component rows contribute zero, while an unknown, malformed, stale, duplicated, quarantined, or cross-Cohort component blocks the result rather than being guessed or moved;
  • passed_project_count counts accepted #56 non-volunteer project submissions whose accepted result is passed=true; and
  • eligible = passed_project_count >= Cohort.min_projects_to_pass. The threshold must be positive. Total points, rank, complaint state, visibility, certificate presence, and volunteer-only reviews do not independently create graduate/certificate eligibility.

The accepted #55/#56 rules for disable_learning_in_public determine their component totals. Toggling that preference does not edit a #57 total directly: it uses the owning #55/#56 services and then produces a new #57 result/recomputation revision.

Exact score, completion, and ranking result

The shared courses query/service returns one immutable, PII-free CohortCompletionResult per Enrollment with exactly:

  • schema version and stable result_key;
  • Course UUID, Cohort UUID, Enrollment UUID, and retained Enrollment ordering identity;
  • activated Cohort score revision;
  • homework_total, project_total, and total_score;
  • passed_project_count, positive required_project_count, and boolean eligible;
  • ordered #55/#56 component-result fingerprint and scoring/settings fingerprint; and
  • reconciliation disposition plus calculated timestamp as metadata, neither of which changes the logical result identity.

result_key is deterministic over the schema version, exact Course/Cohort/Enrollment identities, accepted ordered component-result identities, and the score/completion settings revision. Exact replay returns the same key and values. Any component, threshold, scope, or schema change produces a different result key and a new Cohort score revision. A stale, unactivated, quarantined, or foreign result cannot authorize a downstream action.

Leaderboard recomputation ranks every Enrollment in the Cohort, including zero-score and display_on_leaderboard=false records, by total_score descending and retained Enrollment database identity ascending. Positions are sequential 1..N; equal totals do not share a position. Migrated rows retain the source ordering identity accepted by #244, and new rows use the same final Enrollment identity order—never username, email, display name, insertion order from an unordered query, or a random tie break.

A recomputation stages one complete cohort revision and activates totals, positions, completion results, projection version, and cache-invalidation intent atomically. Readers see either the prior complete revision or the new complete revision, never mixed enrollment rows. Exact command replay returns the original operation/result; changed replay conflicts. Concurrent component mutation or settings drift makes the candidate stale and leaves the prior revision active. Long/full-cohort work is a durable bounded operation with progress, per-row safe errors, cancellation before activation, crash/resume fencing, and no request-thread bulk loop.

Preserved learner and public behavior

The final route identity comes from accepted #53/#244. Under the canonical Cohort base, preserve the leaderboard collection, one retained-Enrollment score breakdown, and authenticated complaint form; preserve every reviewed legacy alias/API route through the accepted compatibility boundary.

Visibility is exact and Cohort-specific:

  • display_name remains the generated pseudonymous leaderboard label; it is not replaced by account email/name or an editorial Person;
  • display_on_leaderboard=false excludes the Enrollment from public leaderboard collections and public YAML/data listings but does not remove it from scoring/ranking, change its stored position, or copy the choice to a sibling Cohort;
  • the adopted direct score-breakdown route remains pseudonymous, public, noindex, and zero-TTL/no-store, including for an unlisted Enrollment; display_public_profile alone controls whether the characterized public profile relationship is shown;
  • the signed-in owner may see their own unlisted status and preferences without being inserted into a public cached collection; and
  • a preference change invalidates only the affected Cohort projection. Anonymous/public output contains no email, account name, certificate name/destination, complaint text, learner-submission free text, private profile value, or cross-Cohort state.

This is preservation, not an intentional visibility change. If the accepted #244 characterization contradicts any route-level disposition above, engineering stops and returns both issues to PM; it does not silently tighten or broaden access.

Collection ordering, pagination, breakdown equations/component links, empty/unranked states, current-learner navigation, cache refresh, and configured public-profile behavior remain characterized. Reads are side-effect free and never create an Enrollment, score result, complaint, or profile relation.

Complaint lifecycle and repair boundary

A signed-in learner may create a complaint against one score-breakdown record in the resolved Cohort. Input is one exact issue type (learning_in_public, homework, project, or other) plus trimmed escaped plain-text description of 1–2,000 characters. Scope is resolved before record lookup. Anonymous, foreign-Cohort, malformed, oversized, or unavailable targets fail safely without exposing learner/account identity.

The adopted lifecycle is exactly open → resolved. Separate reports remain separate records; only an exact idempotent API replay returns the original record. There is no learner withdrawal, reopen state, deletion, or inline score mutation. Repeated resolve is allowed only as exact replay; otherwise it conflicts. Resolution requires current revision, the #52 complaint-resolution capability, a bounded reason, an explicit outcome (no_change, corrected, duplicate, or invalid), and redacted audit. corrected also records the already-completed #55/#56 repair operation/result identity; if repair fails or remains pending, the complaint stays open. Complaint resolution itself never changes a component score or #57 total.

Complaint descriptions/evidence are private learner/authorized-operator data. Public pages and exports show no complaint existence or text. Studio/admin API apply authorized Cohort scope before lookup, mask learner identity by default, and audit only safe actor/principal reference, complaint/Cohort/Enrollment UUIDs, outcome, reason code/digest, revision, request/correlation/idempotency IDs, and timestamps—not the description, evidence, email, display/profile/certificate value, submission/review content, or repair payload.

Shared management interfaces

Register at least these exact capabilities through the accepted #52 policy:

  • courses.leaderboard.view;
  • courses.leaderboard.recompute;
  • courses.completion_result.view;
  • courses.leaderboard_complaint.view; and
  • courses.leaderboard_complaint.resolve.

Studio and /api/v1/admin/ use the same courses query/command services for leaderboard/result inspection, recomputation operation creation/status, complaint list/detail, and resolution. Admin resources use UUIDs; mutations require revision/If-Match, Idempotency-Key, allowlisted bodies, generic scope-first errors, and the accepted #28/#52 confirmation/reauthentication/audit behavior where grading repair or bulk activation is high risk. No adapter, compatibility view, signal, task, or Django-admin action writes score/position/complaint state directly.

The public/learner compatibility schemas remain source-characterized. #57 does not add PII to them or turn the token-protected graduates endpoint into the target export; #58 consumes the result below through #245's masked/export boundary.

Exact handoff to #58

#58 consumes the current activated CohortCompletionResult, not a fresh adapter-side query and not certificate presence. Certificate first issue and graduate selection must verify the exact Course/Cohort/Enrollment, schema version, result_key, activated Cohort score revision, component/settings fingerprint, passed_project_count, required_project_count, and eligible=true.

#58 snapshots the accepted result_key as its eligibility evidence. A stale/foreign/quarantined result or eligible=false denies issue. A later #55/#56/#57 result change does not silently revoke, replace, or rewrite an issued certificate; any exceptional follow-up uses #58's explicit lifecycle. Graduate selection uses current accepted results and #245 authorization/masking/export controls. This handoff contains no email, real name, certificate name/destination, template, sender, preference, delivery, or provider state.

Migration, reconciliation, and evidence identity

Repository verification uses deterministic synthetic fixtures only. #60 owns any authorized protected production-like rehearsal and cutover.

The engineer handoff freezes and records:

  • full base/head SHAs and change-selective graph/plan/report digests;
  • adopted source pin 98a235283904b4ef9ad29e196298540756cf1bcc, #30 copied-file manifest digest, and integration-patch-ledger digest;
  • exact prerequisite merge SHAs, migration leaves, and schema/migration-plan fingerprint;
  • #51/#53 Course/Cohort/route/legacy-alias fingerprint and #244 Enrollment identity/visibility/preservation fingerprint;
  • #52 capability/policy/registry/OpenAPI fingerprint;
  • #55 homework-result schema plus characterized-fixture checksum and #56 project/result/pass/volunteer schema plus characterized-fixture checksum;
  • CohortCompletionResult schema/result-key policy, leaderboard projection/order/cache schema, and complaint lifecycle/reason/audit schema versions; and
  • #58 consumer-schema fingerprint current at dispatch.

Synthetic preflight/dry-run/apply/exact replay/reverse/forward reports source/target Cohort and Enrollment counts, component/result cardinalities, hidden/visible/profile combinations, total/position/tie/eligibility matrices, complaint state/outcome counts, legacy-ID→target mapping cardinalities, transformed/rejected/quarantined rows, and safe non-PII checksums. It preserves source totals, sequential positions, complaint ownership/state/timestamps, display preferences, and sibling-Cohort isolation. Unknown mapping, duplicate logical component/result, cross-Cohort relation, invalid threshold, unsupported complaint state, or unexplained score/rank/eligibility drift fails closed and leaves the prior revision active. Reports contain no reversible learner identifier, display name, complaint text, profile/certificate value, or production data.

Dependencies and readiness

Accepted baseline inputs are #23, #28, #30, and #31.

Engineering hard-depends on accepted and integrated:

  1. #52 — exact Course/Cohort authorization, assignment, object/field policy, management registry, and audit boundary;
  2. #53 — final Cohort lifecycle plus canonical/legacy route and alias identity;
  3. #244 — final cohort-isolated Enrollment identity, display/visibility/preferences, learner history/dashboard routes, and preservation/migration mapping;
  4. #55 — accepted homework component/scoring/reconciliation result; and
  5. #56 — accepted project/review component, volunteer disposition, pass/fail, and reconciliation result.

#224/#51 are transitive through #53/#244. #32/#33/#40/#288 are transitive through #52/#244. #230/#231/#234/#242/#243/#247/#248 are transitive through #244/#56. Broad parent #54 is a coordination epic, not a dependency. Open dependency state still blocks an interface; a local candidate or partial current-main model does not imply acceptance.

#58 consumes the accepted completion result and is downstream. #59 completes later broad management parity and #60 consumes migration/compatibility evidence for protected rehearsal; neither blocks this bounded source contract. #64 remains the shared legal-rights/tombstone boundary and is not reimplemented here.

Readiness remains GROOMED / DEPENDENCY-BLOCKED. After all five hard inputs are accepted and integrated on one green current-main base, PM must revalidate the frozen identities above and #58's consumer schema before an isolated engineer lane starts. Any prerequisite, schema, migration, capability, visibility, source-pin, or consumer drift returns #57 to PM.

Acceptance criteria

  • The service consumes only accepted #55/#56 component results and reproduces exact homework/project/total score, passed-project count, positive threshold, and eligibility semantics without a second scoring/pass algorithm.
  • CohortCompletionResult has the exact PII-free schema and deterministic result identity; exact replay is stable, changed/stale/foreign/quarantined input fails safely, and #58 can validate it without querying component tables directly.
  • Complete cohort recomputation is idempotent, concurrency-safe, durable when long, atomically activated, crash/resume safe, and never exposes mixed total/rank/result/cache revisions.
  • Every Enrollment is ranked by total descending then retained Enrollment identity ascending with sequential positions; zero, hidden, equal-score, new, migrated, and sibling-Cohort cases preserve exact deterministic behavior.
  • Display-name, leaderboard visibility, public-profile, owner-unlisted, breakdown, public YAML/data, cache, noindex/no-store, and sibling-Cohort behavior match the exact preserved contract without PII exposure.
  • Complaints enforce the exact four issue types, bounded safe input, open→resolved terminal lifecycle, separate-report/exact-replay behavior, repair separation, resolution outcome/reason/revision, authorization, redaction, and audit.
  • Studio/admin API/public compatibility adapters have service, capability, scope, revision, idempotency, operation, result/error, audit, OpenAPI, and cache-invalidation parity; no adapter writes domain state directly.
  • Synthetic migration/reconciliation preserves mappings, totals, rank order, visibility, complaints, eligibility, timestamps, and checksums; all named corruption/drift cases fail closed with no outbound work, while protected rehearsal remains #60.
  • Focused model/service/migration/API/security/concurrency/adoption tests plus graph-selected full verification pass on one frozen candidate; the independent tester inspects every required desktop/mobile screenshot before PM acceptance.

Required verification scenarios

  1. Zero/one/many homework and project components; volunteer-only project; passed/not-passed/boundary threshold; learning-in-public enabled/disabled; exact replay; changed component/settings; stale and cross-Cohort result.
  2. Equal totals, zero totals, null legacy position, hidden/unhidden, profile linked/unlinked, migrated/new Enrollment ordering, sibling Cohorts, concurrent score writes, worker crash before/after staging, stale activation, cancellation, cache/projection consistency.
  3. Anonymous, owner, another learner, unlisted Enrollment, configured public profile, direct breakdown, unknown/foreign IDs, public HTML/YAML/data, private Studio/API, warmed-cache canaries, and proof that reads create no state.
  4. Complaint valid/invalid issue type, empty/2,000/over-limit/Unicode/control input, separate duplicate reports, exact/changed replay, unauthorized/cross-Cohort target, resolve outcomes, missing/pending/completed repair reference, repeated resolve/reopen/delete denial, stale revision, concurrent resolution, and audit/log/export canaries.
  5. Studio/admin API allowed/denied/masked actors, stale If-Match, idempotency conflict, bulk limit/partial result, operation progress/cancel/result, service parity, private/no-store/noindex, CSRF/Bearer separation, OpenAPI/registry coverage, and no direct model mutation.
  6. Source-characterized and synthetic migration dry-run/apply twice/reverse/forward with missing/duplicate/cross-Cohort component, invalid threshold, unsupported complaint state, mapping drift, checksum drift, and all outbound Datamailer/Relay/SES/provider paths patched to fail if called.
  7. #58 contract test accepts only the current exact eligible result, rejects ineligible/stale/foreign/quarantined/schema-drifted results, snapshots the result key, and proves later score change has no implicit certificate mutation.
Independent browser evidence

Using synthetic .invalid fixtures at approximately 1440×900 and 390×844, the separate tester captures and reads:

  1. populated, tied-score, and empty leaderboard states with deterministic order/pagination/current-learner navigation;
  2. visible and unlisted owner preference states before/after save plus public collection/YAML absence for the unlisted record;
  3. pseudonymous score breakdown with component equations, configured public-profile link, no-profile state, and direct foreign-Cohort safe denial;
  4. complaint create validation/success and terminal resolved state;
  5. authorized and denied/masked Studio complaint list/detail/resolution; and
  6. recomputation preview/confirmation/progress/success, stale/conflict/failure states, and unchanged prior revision before activation.

Include keyboard-only operation, visible focus/status/error announcement, 44 CSS px targets, 200% zoom and 320 CSS px reflow, reduced motion, and supported JavaScript-disabled form/confirmation/error paths. Every screenshot must show the intended page rather than a debug/404/broken layout and contain no real or unnecessary synthetic learner identity, complaint text, profile/certificate value, credential, provider, or production data.

Explicit non-goals

  • No homework answer/rubric/peer/vote scoring formula, project pass algorithm, Course/Cohort/Enrollment identity, registration/enrollment conversion, profile schema, certificate lifecycle, generic masked export infrastructure, notification, template, sender, preference, delivery, Relay/Datamailer/provider, or production migration behavior.
  • No cross-Cohort leaderboard, shared position, score normalization, tied rank, new completion rule, complaint withdrawal/reopen/deletion, inline complaint score edit, new public identity field, learner-work disclosure, or intentional visibility change.
  • No production/protected data or credentials, PII export, live migration/import, email/network side effect, deployment, workflow dispatch, commit, merge, or push during PM/engineering/tester gates.

Lifecycle

After prerequisites integrate, one engineer leaves an uncommitted frozen candidate and complete versioned verification report. A separate tester recomputes the plan, verifies every criterion, and captures/inspects the required screenshots. PM then performs product acceptance. Only after both gates pass may the engineer create a focused Closes #57 commit; the orchestrator locally merges with --no-ff, pushes, and on-call alone observes terminal CI.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Do not start implementation: the issue is explicitly dependency-blocked and says not to dispatch engineering. First read _docs/PROCESS.md and the linked specs 04, 06, 07, 09, and 10, then review prerequisites #52, #53, #55, #56, and #244; work is done only when the shared courses services, atomic cohort revisions, privacy and complaint behavior, and exact #58 handoff are verified.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, python
Domain
api, backend, database, security, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
18/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.