Files
project-work/worker-toolkit-stocks-in-the-future/repo/docs/map/objects/gradebook/grade-entry.md

3.7 KiB
Raw Blame History

type, cluster, universe, status, entity
type cluster universe status entity
object gradebook live verified app/models/grade_entry.rb

GradeEntry

One student's row in one grade-book: two letter grades, attendance days, a perfect-attendance flag. This is where every payout amount is defined.

Verified 2026-08-16 against commit 63732df.

Why this shape

The payout table is five Ruby constants on this model, all in cents (app/models/grade_entry.rb:9-13):

Constant Value Meaning
EARNINGS_PER_DAY_ATTENDANCE 20 $0.20 per day present
EARNINGS_FOR_A_GRADE 3_00 $3.00 for any A
EARNINGS_FOR_B_GRADE 2_00 $2.00 for any B
EARNINGS_FOR_IMPROVED_GRADE 2_00 $2.00 for improving
EARNINGS_FOR_PERFECT_ATTENDANCE 1_00 $1.00 bonus

They are not configuration. Changing what a student earns is a code change and a deploy — there is no admin screen and no database row for these.

GRADE_OPTIONS is ordered best-to-worst on purpose (:15). improved_grade? compares array indices, treating a lower index as better (:64-67). Reordering or inserting into that array silently changes every improvement bonus in the app.

There are no validations on this model at all — grades are constrained only by the <select> in app/views/grade_books/_grade_entry.html.erb:8,17, and the controller permits the values straight through (app/controllers/grade_books_controller.rb:53-57). A value outside GRADE_OPTIONS saves fine, then makes improved_grade? compare nil indices and raise NoMethodError during the next quarter's payout. Grades C through F earn nothing but are legal; anything not in the list is a latent failure.

Shape

  • Table grade_entries, db/schema.rb:109-121; unique on [grade_book_id, user_id] (db/schema.rb:118) — one row per student per book
  • math_grade, reading_grade — plain strings, nullable, unvalidated
  • attendance_days — bigint, nullable; earnings_for_attendance returns 0 when blank (:17-21)
  • is_perfect_attendance — boolean, default false, null: false (db/schema.rb:113)
  • belongs_to :grade_book, belongs_to :user (:4-5) — user, not student
  • Earnings readers: earnings_for_attendance, earnings_for_math, earnings_for_reading, attendance_perfect_earnings, math_improvement_earnings, reading_improvement_earnings (:17-49)

Every earnings method is a pure reader. Nothing here writes money — DistributeEarnings calls them and creates the deposits.

Connected to

  • owns: —
  • owned-by: grade-book, user
  • joins: —
  • looks-like-but-is-not: math_grade is a letter ("A+"…"F"), unrelated to grade-level, which is 5–8.

If you change this

  • Hits: portfolio-transaction amounts — these constants are the amounts; DistributeEarnings (app/services/distribute_earnings.rb:54-73), which sums attendance + math + reading; the finalize-gradebook-earnings movement; AttendanceEntryPresenter.
  • Does not hit: already-paid deposits. Editing an entry after finalize changes nothing retroactively — the deposits are independent rows with no link back to the entry that produced them (db/schema.rb:170-179 has no grade_entry_id). Re-paying would require re-running finalize, which the completed? guard blocks.

Surfaces

Surface Role
GradeBooksController#update (+ autosave Stimulus controller) teacher writes
DistributeEarnings reads
AttendanceEntryPresenter reads

See

  • Source: app/models/grade_entry.rb, db/schema.rb:109-121
  • As-built: docs/gradebook-earnings.md