Bug: Manually-created "All Volunteer Training" events are typed as Volunteer and display service hours on Participation History
Summary
Some events named "All Volunteer Training" were created through the generic Create Event form instead of the All Volunteer Training shortcut. Those events were saved without the training flag set, and with the service flag set instead.
Because of that, the Participation History table treats them as ordinary volunteer service events, gives them a participation type of Volunteer, and displays service hours next to them. Students who attended a training therefore appear to have earned service hours they should not have.
This is a data problem with a display symptom. The query and template logic are behaving as designed. They are being fed events whose flags describe them incorrectly.
Expected behavior
An "All Volunteer Training" event should be stored with:
| Column |
Expected value |
| isAllVolunteerTraining |
1 |
| isService |
0 |
Those events pass through the filter, get labeled Volunteer, and render service hours.
Steps to reproduce
1. Sign in to CELTS as an admin. You should land on the admin home page.
<
2. Go to the Student Search page. Search for Fritzj2 and click on Julius Fritz.
<
3. Click Participation History.
<
4. Find the row named All Volunteer Training. Its Participation Type reads Volunteer and its Service Hours column is populated, rather than the event being excluded from this table.
<

<
Root cause
How the event got into this state
Events are added through Create Event.
<
There is an All Volunteer Training shortcut at the top of that form. Selecting it applies the correct flags automatically.
<
In several past cases the shortcut was not used. The event was instead created manually, typed in by name, and attached under an unrelated program. Nothing in the form requires the training flag to be set, so the record was saved as a regular service event that merely happens to be named "All Volunteer Training".
<
Why the wrong flags produce this specific output
For anyone new to this part of the codebase, two pieces of logic matter.
First, the filter. The Participation History query excludes training events with this condition in its where clause:
Event.isAllVolunteerTraining == False
An event with isAllVolunteerTraining = 0 is not excluded by that condition, so it stays in the result set.
Second, the label. Participation Type is not a stored column. It is computed in SQL by a Case expression in the select, which checks the event's flags in order and returns the first match:
Case(None, (
((Event.isLaborOnly | Event.name.contains("Labor")) & Event.isService, "Labor & Volunteer"),
((Event.isLaborOnly | Event.name.contains("Labor")), "Labor"),
(Event.isService, "Volunteer")), "Attendee").alias("participatedType")
An event with isService = 1 falls into the third branch and is labeled Volunteer.
The template then shows hours only for rows whose type contains Volunteer:
{% if 'Volunteer' in event.participatedType %}
<td>{{ ... hoursEarned ... }}</td>
{% else %}
<td>N/A</td>
{% endif %}
So a single wrong flag at creation time cascades into a wrong label and a wrong hours display. Correctly-created training events have isService = 0, fall through to the "Attendee" default, and would show N/A even if the filter somehow missed them. That is the double protection the affected records are missing.
Evidence from the database
SELECT id, name, isAllVolunteerTraining, isService
FROM event
WHERE name LIKE '%All Volunteer%';
Most rows are correct:
| id | name | isAllVolunteerTraining | isService |
| 2 | All Volunteer Training | 1 | 0 |
| 13 | All Volunteer Training | 1 | 0 |
| 98 | All Volunteer Training | 1 | 0 |
| ... | | | |
A subset is not:
| 703 | All Volunteer Training | 0 | 0 |
| 704 | All Volunteer Training | 0 | 0 |
| 706 | All Volunteer Training | 0 | 0 |
And at least one has the service flag set, which is the record surfacing in the screenshots above (event dated 09/04/2025). The full affected set can be listed with:
SELECT id, name, startDate, isAllVolunteerTraining, isService
FROM event
WHERE name LIKE '%All Volunteer%'
AND isAllVolunteerTraining = 0;
Suggested fix
1. Identify the affected records. Run the query above and review each row by hand before changing anything. The name match is a substring search, so confirm each result really is an all-volunteer training and not a differently-scoped event that happens to share wording.
2. Correct the flags. For the confirmed rows:
UPDATE event
SET isAllVolunteerTraining = 1,
isService = 0
WHERE id IN ( ... );
Both columns need updating. Setting only isAllVolunteerTraining removes the row from Participation History but leaves isService = 1, which means the event still counts toward service hour totals anywhere else in the application that aggregates on that flag.
3. Verify. Reload Julius Fritz's Participation History. The All Volunteer Training row should no longer appear.
Follow-up worth opening separately
This will recur. Nothing in the Create Event form prevents an admin from typing "All Volunteer Training" into a regular event, so the same cleanup will be needed again next time it happens.
Two possible directions, either of which deserves its own issue rather than being bundled here:
- Validation or a warning on the Create Event form when the name matches an all-volunteer training but the shortcut was not used.
- Reconsidering whether
isAllVolunteerTraining and isService need to be maintained as two independent flags, given that they encode overlapping information and have already drifted apart on several records.
Related
Bug: Manually-created "All Volunteer Training" events are typed as
Volunteerand display service hours on Participation HistorySummary
Some events named "All Volunteer Training" were created through the generic Create Event form instead of the All Volunteer Training shortcut. Those events were saved without the training flag set, and with the service flag set instead.
Because of that, the Participation History table treats them as ordinary volunteer service events, gives them a participation type of
Volunteer, and displays service hours next to them. Students who attended a training therefore appear to have earned service hours they should not have.This is a data problem with a display symptom. The query and template logic are behaving as designed. They are being fed events whose flags describe them incorrectly.
Expected behavior
An "All Volunteer Training" event should be stored with:
Those events pass through the filter, get labeled
Volunteer, and render service hours.Steps to reproduce
1. Sign in to CELTS as an admin. You should land on the admin home page.
<2. Go to the Student Search page. Search for
<Fritzj2and click on Julius Fritz.3. Click Participation History.
<4. Find the row named All Volunteer Training. Its Participation Type reads
<Volunteerand its Service Hours column is populated, rather than the event being excluded from this table.Root cause
How the event got into this state
Events are added through Create Event.
<There is an All Volunteer Training shortcut at the top of that form. Selecting it applies the correct flags automatically.
<In several past cases the shortcut was not used. The event was instead created manually, typed in by name, and attached under an unrelated program. Nothing in the form requires the training flag to be set, so the record was saved as a regular service event that merely happens to be named "All Volunteer Training".
<Why the wrong flags produce this specific output
For anyone new to this part of the codebase, two pieces of logic matter.
First, the filter. The Participation History query excludes training events with this condition in its
whereclause:An event with
isAllVolunteerTraining = 0is not excluded by that condition, so it stays in the result set.Second, the label. Participation Type is not a stored column. It is computed in SQL by a
Caseexpression in theselect, which checks the event's flags in order and returns the first match:An event with
isService = 1falls into the third branch and is labeledVolunteer.The template then shows hours only for rows whose type contains
Volunteer:So a single wrong flag at creation time cascades into a wrong label and a wrong hours display. Correctly-created training events have
isService = 0, fall through to the"Attendee"default, and would showN/Aeven if the filter somehow missed them. That is the double protection the affected records are missing.Evidence from the database
Most rows are correct:
A subset is not:
And at least one has the service flag set, which is the record surfacing in the screenshots above (event dated
09/04/2025). The full affected set can be listed with:Suggested fix
1. Identify the affected records. Run the query above and review each row by hand before changing anything. The name match is a substring search, so confirm each result really is an all-volunteer training and not a differently-scoped event that happens to share wording.
2. Correct the flags. For the confirmed rows:
Both columns need updating. Setting only
isAllVolunteerTrainingremoves the row from Participation History but leavesisService = 1, which means the event still counts toward service hour totals anywhere else in the application that aggregates on that flag.3. Verify. Reload Julius Fritz's Participation History. The All Volunteer Training row should no longer appear.
Follow-up worth opening separately
This will recur. Nothing in the Create Event form prevents an admin from typing "All Volunteer Training" into a regular event, so the same cleanup will be needed again next time it happens.
Two possible directions, either of which deserves its own issue rather than being bundled here:
isAllVolunteerTrainingandisServiceneed to be maintained as two independent flags, given that they encode overlapping information and have already drifted apart on several records.Related