Skip to content

Commit

Permalink
#37 - After filling test, the user should receive email about that (#126
Browse files Browse the repository at this point in the history
)

* create mail for close quiz action

* add queue:listen to makefile

* remove duplicate recipes

* split quiz controller logic into action files

* add tests for actions

* test user model

* remove randomness from test

* fix command name

* implement sending notification when quiz is closed

* fix wasClosedManually method

* prepare quizzes for testing

* fix filterArchivedQuizzes method

* add archived quiz to seeder

* fix code style

* fix no answer warning

* change number to int

* add return type

* move isClosingToday logic to sql

* revert to non-SQL version

* Revert "- Update all non-major dependencies with digest and pinDigest (#122)"

This reverts commit 1a20886.

* fix namespaces

* fix code style

* replace nunomaduro/larastan with larastan/larastan
  • Loading branch information
AmonDeShir authored Dec 13, 2024
1 parent 8ec02ac commit f79996e
Show file tree
Hide file tree
Showing 61 changed files with 1,908 additions and 813 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test-and-lint-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # https://github.com/actions/checkout
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 # https://github.com/actions/checkout

- name: Cache dependencies
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 # https://github.com/actions/cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 # https://github.com/actions/cache
with:
path: node_modules
key: ${{ runner.os }}-npm-dependencies-${{ hashFiles('package.lock') }}
restore-keys: ${{ runner.os }}-npm-dependencies

- name: Set up node
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 # https://github.com/actions/setup-node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 # https://github.com/actions/setup-node
with:
node-version: 21

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-and-lint-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ jobs:
- 5432:5432

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 # https://github.com/actions/checkout
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 # https://github.com/actions/checkout

- name: Validate composer.json and composer.lock
run: composer validate

- name: Cache dependencies
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 # https://github.com/actions/cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 # https://github.com/actions/cache
with:
path: vendor
key: ${{ runner.os }}-composer-dependencies-${{ hashFiles('composer.lock') }}
Expand Down
8 changes: 4 additions & 4 deletions app/Actions/AssignToQuizAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
class AssignToQuizAction
{
/**
* @param Collection<User> $users
* @param Collection<int> $userIds
*/
public function execute(Quiz $quiz, Collection $users): void
public function execute(Quiz $quiz, Collection $userIds): void
{
$assignedUsers = $quiz->assignedUsers;
$users = User::query()->whereIn("id", $users)->get();
$assignedUsers = $quiz->assignedUsers()->get();
$users = User::query()->whereIn("id", $userIds)->get();

$users = $users->filter(fn(User $user) => !$assignedUsers->contains($user));
$quiz->assignedUsers()->attach($users);
Expand Down
3 changes: 3 additions & 0 deletions app/Actions/CloseUserQuizAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Actions;

use App\Events\UserQuizClosed;
use App\Models\UserQuiz;
use Carbon\Carbon;

Expand All @@ -13,5 +14,7 @@ public function execute(UserQuiz $userQuiz): void
{
$userQuiz->closed_at = Carbon::now();
$userQuiz->save();

event(new UserQuizClosed($userQuiz));
}
}
22 changes: 22 additions & 0 deletions app/Actions/CreateUserQuestionAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace App\Actions;

use App\Models\Question;
use App\Models\UserQuestion;
use App\Models\UserQuiz;

class CreateUserQuestionAction
{
public function execute(Question $question, UserQuiz $userQuiz): UserQuestion
{
$userQuestion = new UserQuestion();
$userQuestion->userQuiz()->associate($userQuiz);
$userQuestion->question()->associate($question);
$userQuestion->save();

return $userQuestion;
}
}
31 changes: 31 additions & 0 deletions app/Actions/CreateUserQuizAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace App\Actions;

use App\Models\Quiz;
use App\Models\User;
use App\Models\UserQuiz;

class CreateUserQuizAction
{
public function __construct(
protected CreateUserQuestionAction $action,
) {}

public function execute(Quiz $quiz, User $user): UserQuiz
{
$userQuiz = new UserQuiz();
$userQuiz->closed_at = $quiz->closeAt;
$userQuiz->quiz()->associate($quiz);
$userQuiz->user()->associate($user);
$userQuiz->save();

foreach ($quiz->questions as $question) {
$this->action->execute($question, $userQuiz);
}

return $userQuiz;
}
}
22 changes: 22 additions & 0 deletions app/Actions/LockQuizAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace App\Actions;

use App\Jobs\CloseUserQuizJob;
use App\Models\Quiz;
use Carbon\Carbon;

class LockQuizAction
{
public function execute(Quiz $quiz): void
{
$quiz->locked_at = Carbon::now();
$quiz->save();

if ($quiz->isClosingToday()) {
CloseUserQuizJob::dispatch($quiz)->delay($quiz->closeAt);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use App\Models\User;
use Illuminate\Support\Collection;

class UnassignToQuizAction
class UnassignFromQuizAction
{
/**
* @param Collection<User> $users
Expand All @@ -17,7 +17,8 @@ public function execute(Quiz $quiz, Collection $users): void
{
$assignedUsers = $quiz->assignedUsers;
$users = User::query()->whereIn("id", $users)->get();
$users = $users->filter(fn(User $user) => $assignedUsers->contains($user));

$users = $users->filter(fn(User $user): bool => $assignedUsers->contains($user));
$quiz->assignedUsers()->detach($users);
}
}
16 changes: 16 additions & 0 deletions app/Actions/UnlockQuizAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Actions;

use App\Models\Quiz;

class UnlockQuizAction
{
public function execute(Quiz $quiz): void
{
$quiz->locked_at = null;
$quiz->save();
}
}
24 changes: 24 additions & 0 deletions app/Console/Commands/DispatchUserQuizClosedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Jobs\CloseUserQuizJob;
use App\Models\Quiz;
use Illuminate\Console\Command;

class DispatchUserQuizClosedEvent extends Command
{
protected $signature = "app:dispatch-user-quiz-closed-event";
protected $description = "Dispatches the UserQuizClosed event for quizzes that are closing today.";

public function handle(): void
{
foreach (Quiz::all() as $quiz) {
if ($quiz->isClosingToday()) {
CloseUserQuizJob::dispatch($quiz)->delay($quiz->closeAt);
}
}
}
}
23 changes: 23 additions & 0 deletions app/Events/AssignedQuizClosed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\Quiz;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class AssignedQuizClosed
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public function __construct(
public Quiz $quiz,
public User $user,
) {}
}
21 changes: 21 additions & 0 deletions app/Events/UserQuizClosed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace App\Events;

use App\Models\UserQuiz;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserQuizClosed
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public function __construct(
public UserQuiz $userQuiz,
) {}
}
4 changes: 2 additions & 2 deletions app/Http/Controllers/InviteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace App\Http\Controllers;

use App\Actions\AssignToQuizAction;
use App\Actions\UnassignToQuizAction;
use App\Actions\UnassignFromQuizAction;
use App\Helpers\SortHelper;
use App\Http\Requests\InviteQuizRequest;
use App\Http\Resources\QuizResource;
Expand Down Expand Up @@ -51,7 +51,7 @@ public function assign(Quiz $quiz, InviteQuizRequest $request, AssignToQuizActio
->with("status", "Użytkownicy zostali przypisani do quizu.");
}

public function unassign(Quiz $quiz, InviteQuizRequest $request, UnassignToQuizAction $unassignAction): RedirectResponse
public function unassign(Quiz $quiz, InviteQuizRequest $request, UnassignFromQuizAction $unassignAction): RedirectResponse
{
$this->authorize("invite", $quiz);

Expand Down
5 changes: 3 additions & 2 deletions app/Http/Controllers/QuestionAnswerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Http\Requests\AnswerRequest;
use App\Models\Answer;
use App\Models\Question;
use App\Services\QuizCloneService;
use Illuminate\Http\RedirectResponse;

class QuestionAnswerController extends Controller
Expand Down Expand Up @@ -52,9 +53,9 @@ public function destroy(Answer $answer): RedirectResponse
return redirect()->back();
}

public function clone(Answer $answer, Question $question): RedirectResponse
public function clone(QuizCloneService $service, Answer $answer, Question $question): RedirectResponse
{
$answer->cloneTo($question);
$service->cloneAnswer($answer, $question);

return redirect()
->back()
Expand Down
33 changes: 17 additions & 16 deletions app/Http/Controllers/QuizController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@

namespace App\Http\Controllers;

use App\Actions\AssignToQuizAction;
use App\Actions\CreateUserQuizAction;
use App\Actions\LockQuizAction;
use App\Actions\UnlockQuizAction;
use App\Helpers\SortHelper;
use App\Http\Requests\QuizRequest;
use App\Http\Requests\UpdateQuizRequest;
use App\Http\Resources\QuizResource;
use App\Models\Quiz;
use App\Services\QuizCloneService;
use App\Services\QuizUpdateService;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
Expand All @@ -17,6 +22,7 @@
use Inertia\Inertia;
use Inertia\Response;

use function collect;
use function redirect;

class QuizController extends Controller
Expand Down Expand Up @@ -60,48 +66,43 @@ public function destroy(Quiz $quiz): RedirectResponse
return redirect()->back();
}

public function clone(Quiz $quiz): RedirectResponse
public function clone(QuizCloneService $service, Quiz $quiz): RedirectResponse
{
$quiz->clone();
$service->cloneQuiz($quiz);

return redirect()
->back()
->with("status", "Test został skopiowany");
}

public function lock(Quiz $quiz): RedirectResponse
public function lock(LockQuizAction $action, Quiz $quiz): RedirectResponse
{
$quiz->locked_at = Carbon::now();
$quiz->save();
$action->execute($quiz);

return redirect()
->back()
->with("status", "Test oznaczony jako gotowy do publikacji");
}

public function unlock(Quiz $quiz): RedirectResponse
public function unlock(UnlockQuizAction $action, Quiz $quiz): RedirectResponse
{
$quiz->locked_at = null;
$quiz->save();
$action->execute($quiz);

return redirect()
->back()
->with("status", "Publikacja testu została wycofana");
}

public function createUserQuiz(Request $request, Quiz $quiz): RedirectResponse
public function createUserQuiz(CreateUserQuizAction $action, Request $request, Quiz $quiz): RedirectResponse
{
$user = $request->user();
$userQuiz = $quiz->createUserQuiz($user);
$userQuiz = $action->execute($quiz, $request->user());

return redirect("/quizzes/{$userQuiz->id}/");
}

public function assign(Request $request, Quiz $quiz): RedirectResponse
public function assign(AssignToQuizAction $action, Request $request, Quiz $quiz): RedirectResponse
{
$user = $request->user();
$quiz->assignedUsers()->attach($user);
$quiz->save();
$action->execute($quiz, collect([$request->user()->id]));

return redirect()
->back()
Expand All @@ -113,7 +114,7 @@ private function filterArchivedQuizzes(Builder $query, Request $request): Builde
$showArchived = $request->query("archived", "false") === "true";

if (!$showArchived) {
return $query->orWhere(fn(Builder $query) => $query->whereNull("locked_at")->orWhereDate("scheduled_at", ">", Carbon::now()));
return $query->orWhere(fn(Builder $query) => $query->whereNull("locked_at")->orWhere("scheduled_at", ">", Carbon::now()));
}

return $query;
Expand Down
Loading

0 comments on commit f79996e

Please sign in to comment.