Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalise application process #618

Merged
merged 13 commits into from
Aug 12, 2024
126 changes: 85 additions & 41 deletions app/Http/Controllers/Auth/AdmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use App\Exports\ApplicantsExport;
use App\Http\Controllers\Controller;
use App\Models\Application;
use App\Models\ApplicationWorkshop;
use App\Models\Semester;
use App\Models\SemesterStatus;
use App\Models\User;
use App\Models\RoleUser;
use App\Models\File;
Expand All @@ -15,6 +17,7 @@
use App\Utils\HasPeriodicEvent;
use Carbon\Carbon;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
Expand Down Expand Up @@ -165,54 +168,78 @@ public function updateNote(Request $request, Application $application): Redirect
return redirect()->back();
}

/**
* Show the finalize page with final names and statistics
*/
public function indexFinalize(): View
{
$this->authorize('finalize', Application::class);
if(!($this->getDeadline() < now())) {
throw new \InvalidArgumentException('The application deadline has not passed yet.');
}
[$admitted, $not_admitted, $users_to_delete] = $this->getApplications();
return view('auth.admission.finalize', [
'semester' => $this->semester(),
'admitted_applications' => $admitted,
'users_to_delete' => $users_to_delete
->with('application')
->orderBy('name')
->get()
]);
}


/**
* Accept and delete applciations.
* @return RedirectResponse
* @throws AuthorizationException
*/
public function finalize(): RedirectResponse
{
// $this->authorize('finalizeApplicationProcess', User::class);
// Cache::forget('collegists');
// $not_handled_applicants = User::query()->withoutGlobalScope('verified')
// ->where('verified', 0)
// ->whereHas('application', function ($query) {
// $query->where('submitted', true);
// })
// ->count();
// if ($not_handled_applicants > 0) {
// return redirect()->back()->with('error', 'Még vannak feldolgozatlan jelentkezések!');
// }
// DB::transaction(function () {
// User::query()->withoutGlobalScope('verified')
// ->where('verified', 0)
// ->whereHas('application', function ($query) {
// $query->where('status', Application::STATUS_ACCEPTED);
// })
// ->update(['verified' => true]);
// $usersToDelete = User::query()->withoutGlobalScope('verified')
// ->where('verified', 0)->whereHas('application');
// foreach ($usersToDelete->get() as $user) {
// if ($user->profilePicture!=null) {
// Storage::delete($user->profilePicture->path);
// $user->profilePicture()->delete();
// }
// }
// $files = File::where('application_id', '!=', null);
// foreach ($files->get() as $file) {
// Storage::delete($file->path);
// }
// $files->delete();
// Application::query()->delete();
// $usersToDelete->forceDelete();
//
// RoleUser::where('role_id', Role::get(Role::APPLICATION_COMMITTEE_MEMBER)->id)->delete();
// RoleUser::where('role_id', Role::get(Role::AGGREGATED_APPLICATION_COMMITTEE_MEMBER)->id)->delete();
// });
//
// Cache::clear();
// return back()->with('message', 'Sikeresen jóváhagyta az elfogadott jelentkezőket');
return back()->with('error', 'Még nincs implementálva.');
$this->authorize('finalize', Application::class);
kdmnk marked this conversation as resolved.
Show resolved Hide resolved
if(!($this->getDeadline() < now())) {
throw new \InvalidArgumentException('The application deadline has not passed yet.');
}
if(!$this->semester()) {
throw new \InvalidArgumentException('No semester can be retrieved from the application periodic event.');
}
DB::transaction(function () {
kdmnk marked this conversation as resolved.
Show resolved Hide resolved
[$admitted, $not_admitted, $users_to_delete] = $this->getApplications();
// admit users
foreach ($admitted as $application) {
$application->user->update(['verified' => true]);
if($application->admitted_for_resident_status) {
$application->user->setResident();
} else {
$application->user->setExtern();
}
$application->user->workshops()->sync($application->admittedWorkshops);
$application->user->setStatusFor($this->semester(), SemesterStatus::ACTIVE);
$application->user->internetAccess->extendInternetAccess($this->semester()->getStartDate()->addMonth());
}
// delete data for not admitted users
$files = File::query()
->whereIn('application_id', $not_admitted->pluck('id')) // application files
->orWhereIn('user_id', $not_admitted->pluck('user_id')); // profile pictures
foreach ($files->get() as $file) {
Storage::delete($file->path);
}
$files->delete();
// soft deletes application, keep them for future reference
// (see https://github.com/EotvosCollegium/mars/issues/332#issuecomment-2014058021)
viktorcsimma marked this conversation as resolved.
Show resolved Hide resolved
horcsinbalint marked this conversation as resolved.
Show resolved Hide resolved
Application::whereIn('id', $admitted->pluck('id'))->delete();
Application::whereNotIn('id', $admitted->pluck('id'))->forceDelete();
ApplicationWorkshop::query()->delete();

// Note: users with not submitted applications will also be deleted
$users_to_delete->forceDelete();

RoleUser::where('role_id', Role::get(Role::APPLICATION_COMMITTEE_MEMBER)->id)->delete();
RoleUser::where('role_id', Role::get(Role::AGGREGATED_APPLICATION_COMMITTEE_MEMBER)->id)->delete();
});

Cache::clear();
return back()->with('message', __('general.successful_modification'));
}

/**
Expand Down Expand Up @@ -253,4 +280,21 @@ public function getAccessibleWorkshops(User $user): Collection
}
return Workshop::all();
}

/**
* Helper function to get admittted, not admitted applications and users to delete.
* @return array
*/
private function getApplications()
{
$admitted = Application::query()->with(['user', 'applicationWorkshops'])->admitted()->get()->sortBy('user.name');
$not_admitted = Application::query()->whereNotIn('id', $admitted->pluck('id'))->get();
$users_to_delete_query = User::query()
->withoutGlobalScope('verified')
->whereIn('id', $not_admitted->pluck('user_id'))
//ignore users with any existing role
->whereDoesntHave('roles');

return [$admitted, $not_admitted, $users_to_delete_query];
}
}
54 changes: 54 additions & 0 deletions app/Livewire/ApplicationRoleStatusUpdate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace App\Livewire;

use App\Models\Application;
use App\Models\ApplicationWorkshop;
use Carbon\Carbon;
use Livewire\Component;

class ApplicationRoleStatusUpdate extends Component
{
public Application $application;
public Carbon $lastUpdated;

/**
* Mount the component
* @param Application $application
* @return void
*/
public function mount(Application $application)
{
$this->application = $application;
$this->lastUpdated = Carbon::now()->subSeconds(2);
}

/**
* Update whether the applicant has been admitted as resident or not.
* @param $workshop
*/
public function switchResidentRole()
{
$this->authorize('finalize', Application::class);
$this->application->update(['admitted_for_resident_status' => !$this->application->admitted_for_resident_status]);
$this->lastUpdated = Carbon::now();
}

/**
* $this->updated
* @return bool
*/
public function getUpdatedProperty()
{
return $this->lastUpdated > Carbon::now()->subSeconds(2);
}

/**
* Render the component
* @return \Illuminate\View\View
*/
public function render()
{
return view('auth.application.role_status_update_component');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Carbon\Carbon;
use Livewire\Component;

class ApplicationStatusUpdate extends Component
class ApplicationWorkshopStatusUpdate extends Component
{
public Application $application;
public ApplicationWorkshop $workshop;
Expand All @@ -32,6 +32,7 @@ public function mount(Application $application, ApplicationWorkshop $workshop)
*/
public function callIn($workshop)
{
$this->authorize('editStatus', [\App\Models\Application::class, $this->workshop->workshop]);
$this->application->applicationWorkshops()->where('workshop_id', $workshop)->update(['called_in' => !$this->workshop->called_in]);
$this->lastUpdated = Carbon::now();
}
Expand All @@ -42,6 +43,7 @@ public function callIn($workshop)
*/
public function admit($workshop)
{
$this->authorize('editStatus', [\App\Models\Application::class, $this->workshop->workshop]);
$this->application->applicationWorkshops()->where('workshop_id', $workshop)->update(['admitted' => !$this->workshop->admitted]);
$this->lastUpdated = Carbon::now();
}
Expand All @@ -61,6 +63,6 @@ public function getUpdatedProperty()
*/
public function render()
{
return view('auth.application.status_update_component');
return view('auth.application.workshop_status_update_component');
}
}
50 changes: 49 additions & 1 deletion app/Models/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
namespace App\Models;

use App\Utils\DataCompresser;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;

/**
Expand All @@ -18,6 +20,8 @@
* @property Collection $files
* @property boolean $submitted
* @property string $graduation_average
* @property boolean $applied_for_resident_status
* @property boolean $admitted_for_resident_status
* @property array $semester_average
* @property array $language_exam
* @property array $competition
Expand Down Expand Up @@ -63,11 +67,13 @@
class Application extends Model
{
use HasFactory;
use SoftDeletes;

protected $fillable = [
'user_id',
'submitted',
'applied_for_resident_status',
'admitted_for_resident_status',
'graduation_average',
'semester_average',
'language_exam',
Expand All @@ -85,7 +91,8 @@ class Application extends Model

protected $casts = [
'submitted' => 'bool',
'applied_for_resident_status' => 'bool'
'applied_for_resident_status' => 'bool',
'admitted_for_resident_status' => 'bool'
];

public const QUESTION_1 = [
Expand Down Expand Up @@ -125,6 +132,7 @@ public function applicationWorkshops(): HasMany
return $this->hasMany(ApplicationWorkshop::class);
}


/**
* The Workshop models that the user applied for.
* @return HasManyThrough
Expand All @@ -141,6 +149,14 @@ public function appliedWorkshops(): HasManyThrough
);
}

/**
* The Workshop models that the user admitted to.
*/
public function admittedWorkshops(): HasManyThrough
{
return $this->appliedWorkshops()->where('application_workshops.admitted', true);
}

/**
* Uploaded files
* @return HasMany
Expand All @@ -150,12 +166,44 @@ public function files(): HasMany
return $this->hasMany('App\Models\File');
}


/*
|--------------------------------------------------------------------------
| Local scopes
|--------------------------------------------------------------------------
*/

/**
* Scope a query to only include applications admitted to any workshop.
* @param Builder $query
* @return Builder
*/
public function scopeAdmitted(Builder $query): Builder
{
return $query->whereHas('applicationWorkshops', function ($query) {
$query->where('admitted', true);
});
}


/*
|--------------------------------------------------------------------------
| Accessors & Mutators
|--------------------------------------------------------------------------
*/

/**
* Get a bool whether the applicant has been admitted to any workshops.
*
* @return Attribute
*/
protected function admitted(): Attribute
{
return Attribute::make(
get: fn () => $this->applicationWorkshops()->where('admitted', true)->exists(),
);
}

/**
* Get/set the application's semester_average attribute.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->boolean('admitted_for_resident_status')->default(false)->after('applied_for_resident_status');
$table->softDeletes();
});
Schema::table('application_workshops', function (Blueprint $table) {
$table->dropForeign('application_workshops_application_id_foreign');
$table->foreign('application_id')->references('id')->on('applications')->onDelete('cascade');
});

}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->dropSoftDeletes();
});
kdmnk marked this conversation as resolved.
Show resolved Hide resolved
}
};
Loading