diff --git a/app/Http/Livewire/ProfileStudents.php b/app/Http/Livewire/ProfileStudents.php index 926d176f..f8f53220 100644 --- a/app/Http/Livewire/ProfileStudents.php +++ b/app/Http/Livewire/ProfileStudents.php @@ -37,6 +37,8 @@ class ProfileStudents extends Component public $travel_other_filter = ''; public $tag_filter = ''; + + public $filing_status = ''; protected $listeners = [ 'profileStudentStatusUpdated' => 'refreshStudents' @@ -70,7 +72,7 @@ public function getStudentsProperty() ->willTravelOther($this->travel_other_filter) ->willWorkWithAnimals($this->animals_filter) ->needsResearchCredit($this->credit_filter) - ->with('user:id,email') + ->with('user:id,email', 'research_profile', 'tags') ->orderBy('last_name') ->get(); } @@ -85,6 +87,19 @@ public function refreshStudents() $this->students = $this->getStudentsProperty(); } + public function exportToCsv() + { + $students = $this->students->where('application.status', $this->filing_status); + + if ($students->isEmpty()) { + $this->emit('alert', "No records available for the filters applied", 'danger'); + } + else { + $student_apps = Student::exportStudentApps($students); + return $student_apps->toCsv('students_apps.csv'); + } + } + public function render() { return view('livewire.profile-students', [ diff --git a/app/Macros/CollectionMacros.php b/app/Macros/CollectionMacros.php new file mode 100644 index 00000000..cbc82241 --- /dev/null +++ b/app/Macros/CollectionMacros.php @@ -0,0 +1,44 @@ +streamDownload(function () use ($results) { + if ($results->isEmpty()) return; + + $titles = implode(',', array_keys((array) $results->first()->getAttributes())); + + $values = $results->map(function ($result) { + return collect($result->getAttributes())->map(function ($value) { + if (is_null($value)) return '""'; + + $value = (string) $value; + $value = str_replace(["\r", "\n", "\t"], ' ', $value); + $value = preg_replace('/\s+/', ' ', $value); + $value = str_replace('"', '""', $value); + + return "\"{$value}\""; + })->implode(','); + }); + + $values->prepend($titles); + echo $values->implode("\n"); + }, $name ?? 'export.csv'); + }); + } +} \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 899a7bd0..53469956 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Macros\CollectionMacros; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Blade; use Illuminate\Pagination\Paginator; @@ -46,6 +47,7 @@ public function boot() view()->share('settings', $settings); }); + CollectionMacros::register(); } /** diff --git a/app/Student.php b/app/Student.php index fbecf415..32cc2fff 100644 --- a/app/Student.php +++ b/app/Student.php @@ -125,6 +125,84 @@ public function updateAcceptedStats(Profile $profile, $accepted = true) } } + /** + * Transforms a collection of students with their associated 'user' and 'research_profile' + * data into a flat collection suitable for CSV export. + * + * @param \Illuminate\Database\Eloquent\Collection $students + * @return \Illuminate\Support\Collection + * + */ + public static function exportStudentApps(EloquentCollection $students) + { + $apps = $students->map(function ($student) { + + $st = clone $student; + + $st->email = $st->user->email; + $st->link = "https://profiles.utdallas.edu/students/{$st->slug}"; + $st->brief_intro = $st->research_profile->brief_intro; + $st->intro = $st->research_profile->intro; + $st->interest = $st->research_profile->interest; + $st->major = $st->research_profile->major; + $st->schools = $st->research_profile->schools; + $st->languages = $st->research_profile->languages; + $st->languages_other = $st->research_profile->language_other_name; + $st->languages_proficiency = $st->research_profile->lang_proficiency; + $st->semesters = $st->research_profile->semesters; + $st->availability = $st->research_profile->availability; + $st->earn_credit = $st->research_profile->credit; + $st->graduation_date = $st->research_profile->graduation_date; + $st->bbs_travel_centers = $st->research_profile->travel; + $st->bbs_travel_other = $st->research_profile->travel_other; + $st->bbs_comfortable_animals = $st->research_profile->animals; + $st->other_info = $st->research_profile->other_info; + + $st->earn_credit = match ($st->earn_credit) { + '1' => 'credit', + '0' => 'volunteer', + '-1' => 'no preference', + default => $st->earn_credit, + }; + + foreach ([ + 'bbs_travel_centers', + 'bbs_travel_other', + 'bbs_comfortable_animals', + ] as $yes_no_field) { + $st->$yes_no_field = match ($st->$yes_no_field) { + '1' => 'yes', + '0' => 'no', + default => $st->$yes_no_field, + }; + } + + $st->topics = $st->tags->pluck('name')->implode(", "); + + foreach($st->getAttributes() as $attr => $value) { + if (is_array($value)) { + if (array_is_list($value)) { + $st->$attr = implode(", ", $value); + } else { + $json = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE); + $json = str_replace([',', ':'], [', ', ': '], $json); + $json = str_replace(["\r", "\n"], ' ', $json); + $st->$attr = $json; + } + } + if ($value === null) { + $st->$attr = ''; + } + } + + unset($st->id, $st->user_id, $st->type, $st->application, $st->tags, $st->research_profile, $st->user); + + return $st; + }); + + return $apps; + } + /** * Increments the Student Application view count * diff --git a/resources/views/livewire/profile-students.blade.php b/resources/views/livewire/profile-students.blade.php index 5c066885..8c3288ef 100644 --- a/resources/views/livewire/profile-students.blade.php +++ b/resources/views/livewire/profile-students.blade.php @@ -1,4 +1,16 @@