Consolidate event roster/breakdown data sources into a population-parameterized service
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
Direzione di ricerca
Inizia da app/services/event_dashboard.rb, app/services/attendees_roster.rb e app/services/attendees_breakdowns.rb, quindi segui EventsController#roster e #attendees fino ai partial condivisi di roster e breakdown. Esegui le spec di request/view per il Roster del singolo evento e per gli Attendees tra eventi prima di modificare il comportamento. Il lavoro è completo quando i servizi duplicati sono rimossi, entrambi i contesti eseguono il rendering in modo identico e i nuovi servizi di population, roster e breakdown condividono un’unica implementazione.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
🤖 From Claude: filed at the maintainer's request as a follow-up to the events-IA refactor (PR #2113).
Summary
The per-event Roster page and the cross-event Attendees index render the same shared partials (app/views/events/_registrant_roster.html.erb, app/views/events/_registrant_breakdowns.html.erb) but are fed by two different data sources that each re-implement the same roster/breakdown method surface over a different population:
EventDashboard— one event's active registrants (registered + attended + incomplete_attendance + transferred_in), anchored to the event date.AttendeesRoster/AttendeesBreakdowns— the cross-event population of people who attended a facilitator training, anchored to "now".
This is the deferred "Phase 4" of the IA work. The goal is to collapse the duplicated roster/breakdown logic behind a single population-parameterized service so there's one implementation, differing only in which people, via which registrations, as of when.
Why
- Duplication / drift risk.
AttendeesRoster/AttendeesBreakdownswere deliberately written to mirrorEventDashboard's method names and return shapes so the shared partials read either one. Any change to a breakdown (a new dimension, a bug fix, a query optimization) must be made in two places or they silently diverge. The recent affiliations-N+1 fix, for example, had to be applied toEventDashboardand bothAttendees*services separately. EventDashboardis a god object (~1,300 lines, ~120 public methods) doing money, attendance, scholarships, roster maps, and breakdown datasets. The roster/breakdown concern is cleanly separable.- The "which people / as of when" difference is currently implicit and hand-rolled in each service, rather than an explicit, testable property.
Current state (files)
app/services/event_dashboard.rb— roster maps (registrants,organization_ids_by_registrant,primary_sector_names_by_registrant,scholarship_by_recipient,program_statuses_by_registrant,location_label_by_registrant,ce_registration_by_registrant, …) and breakdown datasets (primary_sectors/sector_counts,age_groups,state_counts,country_counts,program_status_counts/program_status_by_organization/program_status_registrant_ids,life_experiences,settings,registrant_city_breakdown,organizations, scholarship/CE pies, and the*_registrant_ids_by_*drill-in maps).app/services/attendees_roster.rb— the roster-map subset over apeoplecollection (attended-training registrations).app/services/attendees_breakdowns.rb— the breakdown-dataset subset over apeoplecollection.- Shared consumers:
_registrant_roster.html.erb,_registrant_breakdowns.html.erb,_breakdown_card.html.erb,_registrant_city_row.html.erb,_registrant_city_breakdown.html.erb. - Controller:
EventsController#roster(feedsEventDashboard,context: :event) andEventsController#attendees(feedsAttendees*,context: :index).
Proposed design
Introduce three POROs (names from the original plan; open to alternatives):
RegistrantPopulation— the single source of "which people, via which registrations, as of when." Constructors for each context, e.g.RegistrantPopulation.for_event(event)→ the event's active registrations, reference_date = event start.RegistrantPopulation.attended_trainings(people_scope)→ attended facilitator-training registrations, reference_date = today.
Owns the shared, date-anchored primitives currently duplicated:city_by_organization,program_status_by_organization, org/person id maps, the reference-date anchoring, and the org-affiliation preload.
RegistrantRoster.new(population)— every per-registrant lookup map_registrant_rosterreads. ReplacesAttendeesRoster+EventDashboard's roster maps.RegistrantBreakdowns.new(population)— every chart dataset +*_registrant_idsdrill-in map_registrant_breakdownsreads (including the pie denominators currently computed in ERB). ReplacesAttendeesBreakdowns+EventDashboard's breakdown methods.
EventDashboard keeps money / attendance / scholarship-money / applicants / shout-outs, composes a RegistrantPopulation.for_event, and delegates the roster/breakdown method names so dashboard.html.erb / recipients.html.erb / roster.html.erb don't change.
Tasks / scope
- Build
RegistrantPopulationwith both constructors and the shared date-anchored primitives; unit-spec the two anchorings explicitly (the reference-date behavior is currently only implicit). - Build
RegistrantRoster+RegistrantBreakdownsover a population; move the pie-denominator math out of_registrant_breakdowns.html.erbintoRegistrantBreakdowns. - Point
EventsController#attendeesat the new services; haveEventDashboardcompose + delegate. - Delete
AttendeesRoster/AttendeesBreakdowns. - Carry over the org-affiliation preload (the N+1 fix from PR #2113) into
RegistrantPopulationso it lives in one place. - Port the two deleted services' specs to the new names; add per-event-population examples (reference-date anchoring).
- Update
AGENTS.mdservices section.
Risks / notes
- Behavior-preserving refactor. The per-event Roster and cross-event Attendees request/view specs are the safety net — their output must be unchanged. This is the main reason it was deferred:
EventDashboardis large and the roster/breakdown extraction touches many methods. - Watch the subtle sourcing difference already encoded today: on the per-event Roster the scholarship/CE/org columns come from this event's registrations; on the cross-event index they come from each person's attended-training registrations. The population abstraction must make "which registrations" an explicit property so both are expressed without branching in the views.
- The
context: :eventvscontext: :indexdrill-in branching in_registrant_breakdowns.html.erbcan likely be simplified once the population owns the id maps, but that's optional polish.
Out of scope
- No DB/schema changes.
- No UI/behavior changes — pure internal consolidation.
- No policy changes.
Acceptance criteria
AttendeesRosterandAttendeesBreakdownsare gone; one roster service + one breakdown service back both the per-event Roster and the cross-event Attendees index.- The per-event Roster, cross-event Attendees, dashboard, and recipients pages render identically (existing request/view specs pass unchanged).
- Adding a new breakdown dimension requires editing exactly one place.
References
- PR #2113 (events IA refactor) — where these services were renamed and the N+1 was fixed in triplicate (the motivating pain).
- Lingua principale
- Ruby
- Stelle
- 15
- Fork
- 28
- Merge medio
- 16h 58m
- PR unite (30g)
- 291
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Include un Dockerfile o un file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di rubyforgood/awbw
-
blocked
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
rubyforgood/awbw#2824 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
rubyforgood/awbw#2822 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
rubyforgood/awbw#2160 ·
I maintainer di solito rispondono entro 1 giorno
-
Update callout faq defualt text when the new person subscription launchesForse già presa @maebeale l’ha presa 84 giorni fa. Apertablocked
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
rubyforgood/awbw#2095 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
rubyforgood/awbw#2088 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di rubyforgood/awbw
Issue simili
-
intercepted.rb crashes on an optional positional argument when the method also takes keywordsAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
yegor256/intercepted#128 ·
-
documentation good first issue
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
ysb3996-blip/oops#51 ·
I maintainer di solito rispondono entro 1 giorno
-
good first issue
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
Remove leftover Cloud66 custom_web process from ProcfileForse già presa @ShahadatTest l’ha presa oggi. ApertaDifficulty: Easy Good First Issue
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
rubyforgood/casa#7330 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
area:core-lib P2 size:S
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno