Skip to content

Conversation

simonostendorf
Copy link
Member

closes #921

@simonostendorf simonostendorf self-assigned this Oct 14, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds a CSV export for admin statistics and exposes it via the dashboard.

  • New route and controller action to stream a CSV with global, per-event, per-group, per-slot, and per-course stats.
  • Admin dashboard UI button to trigger the CSV download.
  • CSV headers and rows assembled server-side and streamed to the client.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.

File Description
routes/web.php Adds a GET route for downloading statistics, behind the view statistics ability.
resources/js/pages/Dashboard/Admin/Index.vue Adds a button that opens the download URL in a new tab.
app/Http/Controllers/DashboardAdminController.php Implements streamed CSV export with computed statistics across events, groups, slots, and courses.

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +93 to +94
$array = array_map('utf8_decode', array_values($row));
fputcsv($file, $array, ';');
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using utf8_decode here converts strings from UTF-8 to ISO-8859-1, which contradicts the declared UTF-8 charset and will corrupt non-ASCII characters. Remove utf8_decode and write UTF-8 directly; if Excel support is needed, write a UTF-8 BOM once before the first fputcsv (e.g., fputs($file, "\xEF\xBB\xBF")). Apply the same change to the later writes in this method.

Copilot uses AI. Check for mistakes.

Comment on lines +150 to +151
// add basic stats
$row['event_or_group'] = $event->name . ' - ' . $group->name;
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The logic to build $row is duplicated across event, group, and slot sections, making future changes error-prone. Extract a helper to assemble a stats row (given a registrations source and labels) to centralize the counting and formatting, then reuse it for events, groups, and slots.

Copilot uses AI. Check for mistakes.

Comment on lines 196 to 233
$row = [];

// add basic stats
$row['event_or_group'] = $event->name . ' - ' . $slot->name;
$row['registered_users'] = $slot->registrations()->count();
$row['present_users'] = $slot->registrations()->where('is_present', true)->count();
$row['not_present_users'] = $slot->registrations()->where('is_present', false)->count();
$row['present_percentage'] = ($row['registered_users'] > 0 ? round(($row['present_users'] / $row['registered_users']) * 100, 2) . '%' : '0%');

// add requirements stats if event has requirements
if ($event->has_requirements) {
$row['fulfills_requirements'] = $slot->registrations()->where('fulfils_requirements', true)->count();
$row['not_fulfills_requirements'] = $slot->registrations()->where('fulfils_requirements', false)->count();
$row['fulfills_requirements_percentage'] = ($row['registered_users'] > 0 ? round(($row['fulfills_requirements'] / $row['registered_users']) * 100, 2) . '%' : '0%');
} else {
$row['fulfills_requirements'] = 'N/A';
$row['not_fulfills_requirements'] = 'N/A';
$row['fulfills_requirements_percentage'] = 'N/A';
}

// add alcohol stats if event consider alcohol
if ($event->consider_alcohol) {
$row['drinks_alcohol'] = $slot->registrations()->where('drinks_alcohol', true)->count();
$row['drinks_no_alcohol'] = $slot->registrations()->where('drinks_alcohol', false)->count();
$row['drinks_alcohol_percentage'] = ($row['registered_users'] > 0 ? round(($row['drinks_alcohol'] / $row['registered_users']) * 100, 2) . '%' : '0%');
} else {
$row['drinks_alcohol'] = 'N/A';
$row['drinks_no_alcohol'] = 'N/A';
$row['drinks_alcohol_percentage'] = 'N/A';
}

// add course stats
foreach ($courses as $course) {
$row['course_' . $course->abbreviation] = $slot->registrations()->whereHas('user', function ($query) use ($course) {
$query->where('course_id', $course->id);
})->count();
}

Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The logic to build $row is duplicated across event, group, and slot sections, making future changes error-prone. Extract a helper to assemble a stats row (given a registrations source and labels) to centralize the counting and formatting, then reuse it for events, groups, and slots.

Suggested change
$row = [];
// add basic stats
$row['event_or_group'] = $event->name . ' - ' . $slot->name;
$row['registered_users'] = $slot->registrations()->count();
$row['present_users'] = $slot->registrations()->where('is_present', true)->count();
$row['not_present_users'] = $slot->registrations()->where('is_present', false)->count();
$row['present_percentage'] = ($row['registered_users'] > 0 ? round(($row['present_users'] / $row['registered_users']) * 100, 2) . '%' : '0%');
// add requirements stats if event has requirements
if ($event->has_requirements) {
$row['fulfills_requirements'] = $slot->registrations()->where('fulfils_requirements', true)->count();
$row['not_fulfills_requirements'] = $slot->registrations()->where('fulfils_requirements', false)->count();
$row['fulfills_requirements_percentage'] = ($row['registered_users'] > 0 ? round(($row['fulfills_requirements'] / $row['registered_users']) * 100, 2) . '%' : '0%');
} else {
$row['fulfills_requirements'] = 'N/A';
$row['not_fulfills_requirements'] = 'N/A';
$row['fulfills_requirements_percentage'] = 'N/A';
}
// add alcohol stats if event consider alcohol
if ($event->consider_alcohol) {
$row['drinks_alcohol'] = $slot->registrations()->where('drinks_alcohol', true)->count();
$row['drinks_no_alcohol'] = $slot->registrations()->where('drinks_alcohol', false)->count();
$row['drinks_alcohol_percentage'] = ($row['registered_users'] > 0 ? round(($row['drinks_alcohol'] / $row['registered_users']) * 100, 2) . '%' : '0%');
} else {
$row['drinks_alcohol'] = 'N/A';
$row['drinks_no_alcohol'] = 'N/A';
$row['drinks_alcohol_percentage'] = 'N/A';
}
// add course stats
foreach ($courses as $course) {
$row['course_' . $course->abbreviation] = $slot->registrations()->whereHas('user', function ($query) use ($course) {
$query->where('course_id', $course->id);
})->count();
}
$row = $this->buildStatsRow(
$slot->registrations(),
$event->name . ' - ' . $slot->name,
$event,
$courses
);

Copilot uses AI. Check for mistakes.

@simonostendorf simonostendorf merged commit 153c9e5 into dev Oct 17, 2025
2 checks passed
@simonostendorf simonostendorf deleted the feat/stats-csv-export branch October 17, 2025 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant