Skip to content

Commit

Permalink
Merge branch 'staging'
Browse files Browse the repository at this point in the history
  • Loading branch information
jringeisen committed Feb 10, 2024
2 parents 07808d2 + 17419d8 commit 22944c5
Show file tree
Hide file tree
Showing 19 changed files with 948 additions and 191 deletions.
35 changes: 23 additions & 12 deletions app/Http/Controllers/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,41 @@

namespace App\Http\Controllers;

use App\Models\PromptAnswer;
use App\Services\PieChartService;
use App\Models\PromptQuestion;
use App\Services\ActiveTimeService;
use App\Services\Charts\Types\LineChartService;
use App\Services\WordCountService;
use Illuminate\Http\Request;
use Inertia\Inertia;

class DashboardController extends Controller
{
public function __construct(
private readonly WordCountService $wordCountService
private readonly WordCountService $wordCountService,
private readonly ActiveTimeService $activeTimeService,
private readonly LineChartService $lineChartService,
) {
}

public function __invoke(Request $request, PieChartService $pieChartService)
public function __invoke(Request $request)
{
$timeframe = $request->get('timeframe', 'yearly');

return Inertia::render('Dashboard', [
'totalQuestions' => (int) $request->user()->promptQuestions()->whereHas('promptAnswer')->count(),
'dailyQuestions' => (int) $request->user()->promptQuestions()->whereHas('promptAnswer')->filterByDate(now())->count(),
'totalWordsRead' => $this->wordCountService->calculateWordsForPromptAnswers($request->user()),
'pieChartData' => $pieChartService
->data(PromptAnswer::class, 'subject_category')
->labels('subject_category')
->series('total')
->get(),
'timeframe' => $timeframe,
'totalQuestions' => (int) PromptQuestion::query()
->whereHas('user', fn ($query) => $query->where('parent_id', $request->user()->id))
->whereHas('promptAnswer')
->filterByTimeframe($timeframe)
->count(),
'dailyQuestions' => (int) PromptQuestion::query()
->whereHas('user', fn ($query) => $query->where('parent_id', $request->user()->id))
->whereHas('promptAnswer')
->filterByDate(now())
->count(),
'totalWordsRead' => $this->wordCountService->calculateTotalWordsRead($timeframe),
'lineChartData' => $this->lineChartService->getDataForUser($timeframe),
'activeTime' => $this->activeTimeService->fetchTotalTimeForUser($timeframe),
]);
}
}
36 changes: 10 additions & 26 deletions app/Http/Controllers/Student/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,31 @@
namespace App\Http\Controllers\Student;

use App\Http\Controllers\Controller;
use App\Models\Question;
use App\Services\StudentService;
use App\Services\WordCountService;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use NumberFormatter;

class DashboardController extends Controller
{
public function __construct(
private readonly WordCountService $wordCountService
private readonly WordCountService $wordCountService,
) {
}

public function index(Request $request, StudentService $studentService): Response
public function index(Request $request): Response
{
$timeframe = $request->get('timeframe', 'yearly');
$studentService = app(StudentService::class);

return Inertia::render('Student/Dashboard', [
'totalQuestions' => $studentService->student($request->user())->totalQuestionsAsked(),
'totalQuestions' => $studentService->student($request->user())->totalQuestionsAsked($timeframe),
'dailyQuestions' => $studentService->student($request->user())->totalQuestionsAskedToday(),
'totalWordsRead' => $this->wordCountService->calculateWordsForPromptAnswers($request->user()),
'pieChartData' => $studentService->student($request->user())->pieChartData(),
'randomQuestion' => $this->queryRandomQuestion(),
'totalWordsRead' => $this->wordCountService->calculateTotalWordsRead($timeframe, $request->user()->id),
'lineChartData' => $studentService->lineChartData($timeframe),
'activeTime' => $studentService->student($request->user())->activeTime($timeframe),
'timeframe' => $timeframe,
]);
}

protected function queryRandomQuestion(): array
{
$question = Question::inRandomOrder()->first();
$numberFormatter = new NumberFormatter('en_US', NumberFormatter::ORDINAL);

if (! $question) {
return [];
}

return [
'text' => $question->text,
'category' => $question->category,
'sub_category' => $question->sub_category,
'grade' => $numberFormatter->format($question->grade),
'image' => $question->image,
];
}
}
14 changes: 10 additions & 4 deletions app/Http/Controllers/StudentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

class StudentController extends Controller
{
private StudentService $studentService;

public function __construct(
private readonly StudentService $studentService,
private readonly WordCountService $wordCountService
) {
$this->studentService = app(StudentService::class);
}

public function index(Request $request): Response
Expand Down Expand Up @@ -82,13 +84,17 @@ public function show(Request $request, User $user): Response
{
$this->authorize('view', $user);

$timeframe = $request->get('timeframe', 'yearly');

return Inertia::render('Teachers/Students/Show', [
'student' => (new StudentResource($user->load('promptQuestions')))->resolve(),
'totalQuestions' => $this->studentService->student($user)->totalQuestionsAsked(),
'totalQuestions' => $this->studentService->student($user)->totalQuestionsAsked($timeframe),
'dailyQuestions' => $this->studentService->student($user)->totalQuestionsAskedToday(),
'totalWordsRead' => $this->wordCountService->calculateWordsForPromptAnswers($user),
'totalWordsRead' => $this->wordCountService->calculateTotalWordsRead($user, $timeframe),
'categoriesWithCounts' => $this->studentService->student($user)->categoriesWithCounts(),
'pieChartData' => $this->studentService->student($user)->pieChartData(),
'lineChartData' => $this->studentService->student($user)->lineChartData($timeframe),
'activeTime' => $this->studentService->student($user)->activeTime($timeframe),
'timeframe' => $timeframe,
]);
}

Expand Down
30 changes: 30 additions & 0 deletions app/Models/ActiveTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class ActiveTime extends Model
{
Expand All @@ -21,8 +23,36 @@ class ActiveTime extends Model
'updated_at',
];

public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

protected function getTotalMinutesAttribute(): int
{
return floor($this->total_seconds / 60);
}

public function scopeFilterByTimeframe(Builder $query, string $timeframe): Builder
{
return $query->when($timeframe, function (Builder $query) use ($timeframe) {
$usersTimezone = request()->user()->timezone;

$query->when($timeframe === 'yearly', function (Builder $query) use ($usersTimezone) {
$query->whereYear('active_time.date', now($usersTimezone)->year);
})
->when($timeframe === 'monthly', function (Builder $query) use ($usersTimezone) {
$query->whereBetween('active_time.date', [
now($usersTimezone)->startOfMonth(),
now($usersTimezone)->endOfMonth(),
]);
})
->when($timeframe === 'weekly', function (Builder $query) use ($usersTimezone) {
$query->whereBetween('active_time.date', [
now($usersTimezone)->startOfWeek(),
now($usersTimezone)->endOfWeek(),
]);
});
});
}
}
23 changes: 23 additions & 0 deletions app/Models/PromptQuestion.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,27 @@ public function scopeFilterByDate(Builder $query, string $date): Builder
$query->whereBetween('prompt_questions.created_at', [$startOfDay, $endOfDay]);
});
}

public function scopeFilterByTimeframe(Builder $query, string $timeframe): Builder
{
return $query->when($timeframe, function (Builder $query) use ($timeframe) {
$usersTimezone = request()->user()->timezone;

$query->when($timeframe === 'yearly', function (Builder $query) use ($usersTimezone) {
$query->whereYear('prompt_questions.created_at', now($usersTimezone)->year);
})
->when($timeframe === 'monthly', function (Builder $query) use ($usersTimezone) {
$query->whereBetween('prompt_questions.created_at', [
now($usersTimezone)->startOfMonth(),
now($usersTimezone)->endOfMonth(),
]);
})
->when($timeframe === 'weekly', function (Builder $query) use ($usersTimezone) {
$query->whereBetween('prompt_questions.created_at', [
now($usersTimezone)->startOfWeek(),
now($usersTimezone)->endOfWeek(),
]);
});
});
}
}
32 changes: 32 additions & 0 deletions app/Providers/StudentServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Providers;

use App\Services\ActiveTimeService;
use App\Services\Charts\Types\LineChartService;
use App\Services\StudentService;
use Illuminate\Support\ServiceProvider;

class StudentServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
$this->app->singleton(StudentService::class, function ($app) {
return new StudentService(
$app->make(ActiveTimeService::class),
$app->make(LineChartService::class)
);
});
}

/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}
28 changes: 28 additions & 0 deletions app/Services/ActiveTimeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Services;

use App\Models\ActiveTime;

class ActiveTimeService
{
public function fetchTotalTimeForUser(string $timeframe, mixed $userId = null): string
{
$totalSecondsDuringTimeframe = ActiveTime::query()
->when($userId, fn ($query) => $query->where('user_id', $userId))
->when(! $userId, fn ($query) => $query->whereHas('user', fn ($query) => $query->where('parent_id', request()->user()->id)))
->filterByTimeframe($timeframe)
->sum('total_seconds');

return $this->formatActiveTime($totalSecondsDuringTimeframe);
}

protected function formatActiveTime(int $seconds): string
{
$days = intdiv($seconds, 86400);
$hours = intdiv($seconds % 86400, 3600);
$minutes = intdiv($seconds % 3600, 60);

return $days.'d '.$hours.'h '.$minutes.'m';
}
}
16 changes: 16 additions & 0 deletions app/Services/Charts/Interfaces/TimeframeStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Services\Charts\Interfaces;

use Illuminate\Support\Collection;

interface TimeframeStrategy
{
public function fetchData(string $userId): Collection;

public function getPeriod(): array;

public function transformData(Collection $data): array;

public function buildChartData(array $data): array;
}
62 changes: 62 additions & 0 deletions app/Services/Charts/TimeframeStrategies/MonthlyStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace App\Services\Charts\TimeframeStrategies;

use App\Models\ActiveTime;
use App\Services\Charts\Interfaces\TimeframeStrategy;
use Carbon\CarbonPeriod;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;

class MonthlyStrategy implements TimeframeStrategy
{
public function fetchData(mixed $userId = null): Collection
{
return ActiveTime::query()
->when($userId, fn ($query) => $query->where('user_id', $userId))
->when(! $userId, fn ($query) => $query->whereHas('user', fn ($query) => $query->where('parent_id', request()->user()->id)))
->whereBetween('created_at', [
now(request()->user()->timezone)->startOfMonth(),
now(request()->user()->timezone)->endOfMonth(),
])
->selectRaw('DAY(created_at) as day, SUM(total_seconds) as total_seconds')
->groupBy('day')
->orderBy('day')
->get();
}

public function getPeriod(): array
{
$startOfMonth = now(request()->user()->timezone)->startOfMonth();
$endOfMonth = now(request()->user()->timezone)->endOfMonth();

$period = CarbonPeriod::create($startOfMonth, '1 day', $endOfMonth);
$days = [];

foreach ($period as $date) {
$days[$date->format('j')] = 0;
}

return $days;
}

public function transformData($data): array
{
$totalHoursPerDay = $data->reduce(function ($carry, $item) {
$day = Carbon::createFromFormat('j', $item->day)->format('j');
$carry[$day] = (int) $item->total_seconds;

return $carry;
}, []);

return array_replace($this->getPeriod(), $totalHoursPerDay);
}

public function buildChartData(array $data): array
{
return [
'labels' => array_keys($data),
'series' => array_values($data),
];
}
}
Loading

0 comments on commit 22944c5

Please sign in to comment.