From 20f9b344f65fb9215cb7cf4714e8fa1632a82804 Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Tue, 4 Jun 2024 14:34:25 +0200 Subject: [PATCH] Added is_imported flag to time entries --- app/Filament/Widgets/ActiveUserOverview.php | 2 + app/Filament/Widgets/TimeEntriesCreated.php | 6 +- app/Filament/Widgets/TimeEntriesImported.php | 77 +++++++++++++++++++ app/Filament/Widgets/UserRegistrations.php | 2 + app/Models/TimeEntry.php | 2 + app/Providers/Filament/AdminPanelProvider.php | 2 + .../Importers/ClockifyTimeEntriesImporter.php | 2 + .../Importers/TogglTimeEntriesImporter.php | 2 + database/factories/TimeEntryFactory.php | 1 + ..._imported_column_to_time_entries_table.php | 30 ++++++++ .../ClockifyTimeEntriesImporterTest.php | 4 + 11 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 app/Filament/Widgets/TimeEntriesImported.php create mode 100644 database/migrations/2024_05_30_175825_add_is_imported_column_to_time_entries_table.php diff --git a/app/Filament/Widgets/ActiveUserOverview.php b/app/Filament/Widgets/ActiveUserOverview.php index 19ce132f..71dc5487 100644 --- a/app/Filament/Widgets/ActiveUserOverview.php +++ b/app/Filament/Widgets/ActiveUserOverview.php @@ -11,6 +11,8 @@ use Illuminate\Database\Eloquent\Builder; class ActiveUserOverview extends BaseWidget { + protected static ?int $sort = 1; + protected static ?string $heading = 'A Registrations'; protected function getCards(): array diff --git a/app/Filament/Widgets/TimeEntriesCreated.php b/app/Filament/Widgets/TimeEntriesCreated.php index 7dcf03ff..260edbc3 100644 --- a/app/Filament/Widgets/TimeEntriesCreated.php +++ b/app/Filament/Widgets/TimeEntriesCreated.php @@ -15,6 +15,8 @@ class TimeEntriesCreated extends ChartWidget public ?string $filter = 'week'; + protected static ?int $sort = 3; + protected function getData(): array { $filter = $this->filter; @@ -27,7 +29,9 @@ class TimeEntriesCreated extends ChartWidget } else { $start = now()->subWeek(); } - $trend = Trend::model(TimeEntry::class) + $trend = Trend::query( + TimeEntry::query()->where('is_imported', '=', false) + ) ->between( start: $start, end: now(), diff --git a/app/Filament/Widgets/TimeEntriesImported.php b/app/Filament/Widgets/TimeEntriesImported.php new file mode 100644 index 00000000..5638b734 --- /dev/null +++ b/app/Filament/Widgets/TimeEntriesImported.php @@ -0,0 +1,77 @@ +filter; + if ($filter === 'week') { + $start = now()->subWeek(); + } elseif ($filter === 'month') { + $start = now()->subMonth(); + } elseif ($filter === 'year') { + $start = now()->subYear(); + } else { + $start = now()->subWeek(); + } + $trend = Trend::query( + TimeEntry::query()->where('is_imported', '=', true) + ) + ->between( + start: $start, + end: now(), + ) + ->perDay(); + + if ($filter === 'week') { + $trend->perDay(); + } elseif ($filter === 'month') { + $trend->perDay(); + } elseif ($filter === 'year') { + $trend->perMonth(); + } else { + $trend->perDay(); + } + + $data = $trend->count(); + + return [ + 'datasets' => [ + [ + 'label' => self::$heading, + 'data' => $data->map(fn (TrendValue $value) => $value->aggregate), + ], + ], + 'labels' => $data->map(fn (TrendValue $value) => $value->date), + ]; + } + + protected function getFilters(): ?array + { + return [ + 'week' => 'Last week', + 'month' => 'Last month', + 'year' => 'Last year', + ]; + } + + protected function getType(): string + { + return 'line'; + } +} diff --git a/app/Filament/Widgets/UserRegistrations.php b/app/Filament/Widgets/UserRegistrations.php index 4634c5dc..9a972121 100644 --- a/app/Filament/Widgets/UserRegistrations.php +++ b/app/Filament/Widgets/UserRegistrations.php @@ -15,6 +15,8 @@ class UserRegistrations extends ChartWidget public ?string $filter = 'week'; + protected static ?int $sort = 2; + protected function getData(): array { $filter = $this->filter; diff --git a/app/Models/TimeEntry.php b/app/Models/TimeEntry.php index 4ac5b84b..d730d080 100644 --- a/app/Models/TimeEntry.php +++ b/app/Models/TimeEntry.php @@ -25,6 +25,7 @@ use Korridor\LaravelComputedAttributes\ComputedAttributes; * @property array $tags * @property string $user_id * @property string $member_id + * @property bool $is_imported * @property-read User $user * @property-read Member $member * @property string $organization_id @@ -57,6 +58,7 @@ class TimeEntry extends Model 'billable' => 'bool', 'tags' => 'array', 'billable_rate' => 'int', + 'is_imported' => 'bool', ]; /** diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 193c3eb3..c50aa1fd 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -6,6 +6,7 @@ namespace App\Providers\Filament; use App\Filament\Widgets\ActiveUserOverview; use App\Filament\Widgets\TimeEntriesCreated; +use App\Filament\Widgets\TimeEntriesImported; use App\Filament\Widgets\UserRegistrations; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\DisableBladeIconComponents; @@ -46,6 +47,7 @@ class AdminPanelProvider extends PanelProvider ActiveUserOverview::class, UserRegistrations::class, TimeEntriesCreated::class, + TimeEntriesImported::class, ]) ->plugins([ EnvironmentIndicatorPlugin::make() diff --git a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php index de04a5d3..c2fc256f 100644 --- a/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php +++ b/app/Service/Import/Importers/ClockifyTimeEntriesImporter.php @@ -79,6 +79,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter ], [ 'client_id' => $clientId, 'color' => $this->colorService->getRandomColor(), + 'is_billable' => false, ]); } $taskId = null; @@ -105,6 +106,7 @@ class ClockifyTimeEntriesImporter extends DefaultImporter } $timeEntry->billable = $record['Billable'] === 'Yes'; $timeEntry->tags = $this->getTags($record['Tags']); + $timeEntry->is_imported = true; // Start try { diff --git a/app/Service/Import/Importers/TogglTimeEntriesImporter.php b/app/Service/Import/Importers/TogglTimeEntriesImporter.php index 208cca89..859dde45 100644 --- a/app/Service/Import/Importers/TogglTimeEntriesImporter.php +++ b/app/Service/Import/Importers/TogglTimeEntriesImporter.php @@ -78,6 +78,7 @@ class TogglTimeEntriesImporter extends DefaultImporter 'organization_id' => $this->organization->id, ], [ 'client_id' => $clientId, + 'is_billable' => false, 'color' => $this->colorService->getRandomColor(), ]); } @@ -102,6 +103,7 @@ class TogglTimeEntriesImporter extends DefaultImporter } $timeEntry->billable = $record['Billable'] === 'Yes'; $timeEntry->tags = $this->getTags($record['Tags']); + $timeEntry->is_imported = true; try { $start = Carbon::createFromFormat('Y-m-d H:i:s', $record['Start date'].' '.$record['Start time'], $timezone); } catch (InvalidFormatException) { diff --git a/database/factories/TimeEntryFactory.php b/database/factories/TimeEntryFactory.php index f875f25b..65c36789 100644 --- a/database/factories/TimeEntryFactory.php +++ b/database/factories/TimeEntryFactory.php @@ -33,6 +33,7 @@ class TimeEntryFactory extends Factory 'start' => $start, 'end' => $this->faker->dateTimeBetween($start, 'now'), 'billable' => $this->faker->boolean(), + 'is_imported' => false, 'tags' => [], 'user_id' => User::factory(), 'member_id' => Member::factory(), diff --git a/database/migrations/2024_05_30_175825_add_is_imported_column_to_time_entries_table.php b/database/migrations/2024_05_30_175825_add_is_imported_column_to_time_entries_table.php new file mode 100644 index 00000000..2ce29d80 --- /dev/null +++ b/database/migrations/2024_05_30_175825_add_is_imported_column_to_time_entries_table.php @@ -0,0 +1,30 @@ +boolean('is_imported')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('time_entries', function (Blueprint $table) { + $table->dropColumn('is_imported'); + }); + } +}; diff --git a/tests/Unit/Service/Import/Importer/ClockifyTimeEntriesImporterTest.php b/tests/Unit/Service/Import/Importer/ClockifyTimeEntriesImporterTest.php index 521961d0..744a1425 100644 --- a/tests/Unit/Service/Import/Importer/ClockifyTimeEntriesImporterTest.php +++ b/tests/Unit/Service/Import/Importer/ClockifyTimeEntriesImporterTest.php @@ -33,6 +33,7 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract $this->assertSame('2024-03-04 10:23:52', $timeEntry1->start->toDateTimeString()); $this->assertSame('2024-03-04 10:23:52', $timeEntry1->end->toDateTimeString()); $this->assertFalse($timeEntry1->billable); + $this->assertTrue($timeEntry1->is_imported); $this->assertSame([$testScenario->tag1->getKey(), $testScenario->tag2->getKey()], $timeEntry1->tags); $timeEntry2 = $timeEntries->firstWhere('description', 'Working hard'); $this->assertNotNull($timeEntry2); @@ -40,6 +41,7 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract $this->assertSame('2024-03-04 10:23:00', $timeEntry2->start->toDateTimeString()); $this->assertSame('2024-03-04 11:23:01', $timeEntry2->end->toDateTimeString()); $this->assertTrue($timeEntry2->billable); + $this->assertTrue($timeEntry2->is_imported); $this->assertSame([], $timeEntry2->tags); } @@ -68,6 +70,7 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract $this->assertSame('2024-03-04 10:23:52', $timeEntry1->start->toDateTimeString()); $this->assertSame('2024-03-04 10:23:52', $timeEntry1->end->toDateTimeString()); $this->assertFalse($timeEntry1->billable); + $this->assertTrue($timeEntry1->is_imported); $this->assertSame([$testScenario->tag1->getKey(), $testScenario->tag2->getKey()], $timeEntry1->tags); $timeEntry2 = $timeEntries->firstWhere('description', 'Working hard'); $this->assertNotNull($timeEntry2); @@ -75,6 +78,7 @@ class ClockifyTimeEntriesImporterTest extends ImporterTestAbstract $this->assertSame('2024-03-04 10:23:00', $timeEntry2->start->toDateTimeString()); $this->assertSame('2024-03-04 11:23:01', $timeEntry2->end->toDateTimeString()); $this->assertTrue($timeEntry2->billable); + $this->assertTrue($timeEntry2->is_imported); $this->assertSame([], $timeEntry2->tags); } }