DataTalksClub / DataTalksClub/website

Render project-results empty state for authenticated non-submitters

Open
#241 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug courses frontend P1 security testing
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Product outcome

An authenticated learner who has no ordinary project submission can open the completed project's Results page and see the existing empty-state message instead of HTTP 500. This includes learners who only volunteered to review and staff who have no submission of their own. Submitted learners continue to see exactly their existing outcome, scores, votes, submission facts, and peer feedback.

Normative authority

  • _docs/PROCESS.md: independent engineer, tester/screenshots, PM, and release gates.
  • _docs/specs/04-courses-and-cohorts.md, preserved learner behavior: cohort-owned project submissions, peer review, scoring, results, and compatibility behavior remain intact.
  • _docs/specs/02-url-link-seo-compatibility.md: canonical and retained legacy course routes must remain deterministic.
  • _docs/specs/07-security-privacy-operations.md: learner state is private, minimized, non-cacheable, non-indexable, and must not cross object/identity boundaries.
  • _docs/specs/10-verification-strategy.md: focused Django/browser coverage and independently inspected responsive evidence.
  • #56 remains the broader project/peer-review/scoring preservation epic. #149 separately owns the system-evaluation/CMP feature decision.

Exact confirmed defect

On the historical exact origin/main state 9a491cd9baeb1e723bd05e8dacb20978fbaf9d33, courses.views.project_results._project_results_context() correctly resolves no ordinary submission as None for an authenticated user, but then passes that value into score processing, which dereferences submission.project. The response is HTTP 500 before courses/templates/projects/results.html can render its already-defined message:

You did not make a submission for this project.

The failure is present through both route families:

  • canonical: /courses/<course-slug>/<cohort-identifier>/project/<project-slug>/results;
  • retained legacy: /courses/<legacy-cohort-slug>/project/<project-slug>/results.

The confirmed reproduction found no write or disclosure and retained private/no-store/noindex response policy. Those properties are regression boundaries, not evidence that HTTP 500 is acceptable.

Scope

  1. Treat absence of an ordinary (volunteer_review_only=False) submission as a supported authenticated result state before any submission-dependent score, criterion-vote, or feedback computation.
  2. Render the existing projects/results.html empty-state branch with HTTP 200 on canonical and legacy routes. Do not add a new template, route, redirect, or copy.
  3. Apply the same state to:
    • an authenticated learner with no project submission;
    • a learner whose only project row is volunteer-review-only;
    • an authorized staff user with no ordinary submission of their own.
  4. Preserve the existing normal-submitter result path byte-for-behavior: outcome, pass/fail, total and component scores, criterion ordering and option vote counts, submission link/commit/timestamp, peer feedback, staff toolbar, and project navigation.
  5. Add focused rendered regression evidence for the empty state reached from a completed project's Results link.

Identity, cohort, and security boundaries

  • Submission resolution remains exactly scoped to the resolved route Project, the requesting authenticated user, and volunteer_review_only=False.
  • A volunteer-only row is not promoted to the learner's scored submission. A submission for another project, cohort, or account is never used as fallback.
  • Canonical resolution remains family slug + cohort identifier + project slug; legacy resolution remains legacy cohort slug + project slug. Existing wrong-family/cohort/project 404 behavior is unchanged.
  • The empty page reveals no other learner's existence, submission, score, vote, feedback, enrollment, or identifier. It does not distinguish “other learners submitted” from “none submitted.”
  • Anonymous GET remains HTTP 200 with the existing login prompt and does not resolve or serialize a learner result.
  • Every GET is read-only: it creates, updates, scores, assigns, submits, votes, or deletes nothing and schedules no job/network side effect.
  • Authenticated and anonymous responses retain the existing noindex policy and applicable private/no-store cache boundary; no result page enters sitemap/search/public serializers.

Non-goals

  • No scoring, rubric, criterion, vote, feedback, peer-assignment, volunteer-review, result-visibility, staff-access, or project-lifecycle policy change.
  • No adoption of #149 system evaluation, notification behavior, model/migration, API, or CMP source-pin change.
  • No broad #56 implementation, score repair/reconciliation, asynchronous URL validation, Studio/admin API, or notification work.
  • No empty-state copy, template layout, project navigation, design-system, authentication, canonical/legacy route, cache middleware, or SEO redesign.
  • No model/migration/data repair, production/private data access, dependency, retry/skip/xfail, or weakening of existing result assertions.

Dependencies and readiness

There is no hard product, model, or interface dependency on #56, #149, or #228. #228 and its children do not implement or verify this learner outcome. Future #56 or #149 work must preserve the supported empty state.

Delivery is nevertheless on an operational hold until the authoritative clean releasable baseline exists. Complete the external content-source repair, then the shared recovery order #261 → #253 → #279 → #223 → #270, followed by the accepted #232 → #236 fingerprint, focused commits in that order, and its exact-main green publish/deploy with healthy sealed live identity. A changed base invalidates every old plan and result.

The existing five-path candidate is only reconstruction input. It is staged and uncommitted on historical base 9a491cd9baeb1e723bd05e8dacb20978fbaf9d33; audited origin/main is face8e4808d65afbf0374d1ced7a88079950d663. The production view did not change across that raw base delta, but the results template did, and the plan, adoption ledger, browser environment, screenshots, and broad evidence are stale. Do not send the old worktree to testing, cherry-pick it as a finished patch, or reuse its engineer screenshots.

After the recovery release, follow normal P1 issue order. #230, #231, #234, and #241 have no product dependency on one another, but all update the same course-adoption ledger files. Their delivery must therefore be serialized as #230 → #231 → #234 → #241, reconstructing from the latest accepted main and recomputing the ledger hashes, verification plan, and evidence after each preceding merge. Parallel read-only preparation is allowed; overlapping uncommitted ledger candidates are not.

Priority remains P1: this is a user-visible authenticated 500 on a completed learner route, but it is bounded to non-submitters and is not the current release-gate blocker.

Acceptance criteria

Django and route behavior
  • Canonical and legacy Results routes return HTTP 200 and exact text You did not make a submission for this project. for an authenticated user with no ordinary submission.
  • The same two-route matrix passes when the user has a volunteer-review-only row and when an authorized staff user has no ordinary submission.
  • Empty-state responses do not contain the Outcome, Criteria Breakdown, Score Components, Submission, or Peer Feedback result sections and do not expose another learner's data.
  • Anonymous canonical and legacy requests remain HTTP 200 with the existing login prompt and no learner-result resolution.
  • Existing ordinary-submission tests preserve score ordering, criterion option-vote counts, outcome, components, submission facts, feedback, toolbar, and navigation on both route families.
  • A same-slug/project fixture across two cohorts and distinct learners proves route cohort plus requesting-user isolation; wrong family/cohort/project retains existing 404 behavior.
  • Database assertions prove all no-submission/volunteer/staff/anonymous GETs perform no project, submission, review, response, score, vote, or enrollment mutation and schedule no side effect.
  • Responses preserve the existing noindex markup/header and applicable private, no-store cache directives.
Browser, screenshots, and regression
  • A browser scenario signs in as a deterministic synthetic learner with no ordinary submission, follows the completed project's Results link, and sees the exact empty state without a debug/error page on the canonical URL.
  • The same scenario proves normal course/project navigation remains usable and no outcome/score/submission/feedback detail is visible.
  • Independent tester captures and opens full-page screenshots under .tmp/screenshots/issue-241/ at approximately 1440x900 and 390x844, confirming readable contained layout, visible heading/empty state/navigation, no horizontal overflow, and no PII beyond synthetic fixtures.
  • Focused project-results Django tests, the graph-selected Playwright tier, existing project/design-shell/noindex/security tests, and all versioned verification-plan components pass with no required skip or pending screenshot.
  • PM accepts the frozen candidate from the affected learner perspective.
  • [ON-CALL] The exact accepted commit's main push records all required jobs and aggregate gate green; a red/cancelled/missing result is routed through the lifecycle and not called green.

Scenarios

  1. No submission: authenticated learner follows Results from a completed project and sees the existing empty state.
  2. Volunteer only: a volunteer-review-only row exists; the learner still sees the empty state and no volunteer/peer data is treated as their result.
  3. Staff without submission: staff retains authorized toolbar navigation but sees no learner result as their own.
  4. Ordinary submission: the complete existing scored result remains unchanged.
  5. Cross-cohort/account: another cohort or learner has a scored submission; it is never selected or disclosed.
  6. Anonymous: login prompt remains the only learner-specific result state.

Lifecycle handoff

On the latest clean green base after the ordered lanes above, an engineer reconstructs the smallest view/context correction, focused Django route/security coverage, the persistent browser scenario, and exact adoption-ledger entries in a fresh isolated worktree. The old diff may guide reconstruction but supplies no reusable verification. The engineer must generate a new versioned plan/fingerprint and reach a terminal green graph-selected envelope; changing a Playwright test plus rendered route behavior requires fresh full Playwright unless the then-current authoritative graph proves a stricter broader disposition.

A separate tester recomputes the plan from the frozen candidate, validates every component disposition, reruns the focused Django and full graph-selected browser gates, and captures and opens full-page evidence under .tmp/screenshots/issue-241/ at approximately 1440x900 and 390x844. The current approved homepage headings are Events and Community knowledgebase; obsolete heading assertions are failures in the test contract, not product regressions. Engineer screenshots and every 9a491cd result remain invalid for acceptance.

PM accepts only after tester PASS with no required skip, pending screenshot, or unexplained drift. There is no HUMAN remainder: the focused commit uses Closes #241, the orchestrator merges and pushes, and on-call alone observes the exact main SHA. A red, cancelled, or missing required post-push verdict reopens/routes the issue through the lifecycle and is never closure evidence.

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

Start with courses.views.project_results._project_results_context() and the existing projects/results.html empty-state branch, using the latest clean green baseline rather than the historical worktree. Run the focused project-results Django tests and graph-selected Playwright scenario for both canonical and legacy routes. Done means authenticated non-submitters receive HTTP 200 with the existing empty state, while ordinary-submitter results, privacy boundaries, and navigation remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, playwright, python
Domain
backend, testing-qa, web-dev
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.