From a53ed79256a3dfbe0eeb82d031f934a46d684f61 Mon Sep 17 00:00:00 2001 From: Michal Date: Tue, 27 Feb 2024 21:40:23 +0000 Subject: [PATCH] feat: added meta tags support for GPX imported files --- app/Http/Controllers/MarkerController.php | 34 ++++++++++++++----- app/Parsers/Files/GPXParser.php | 36 ++++++++++++++++++-- tests/Unit/MarkerTest.php | 40 +++++++++++++++++++++++ 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/MarkerController.php b/app/Http/Controllers/MarkerController.php index 66c1b0f..9680589 100644 --- a/app/Http/Controllers/MarkerController.php +++ b/app/Http/Controllers/MarkerController.php @@ -141,9 +141,9 @@ public function storeInBulk(Request $request, Map $map) 'markers.*.description' => ['nullable', 'string', 'max:191'], 'markers.*.category_name' => ['required_without:markers.*.category', 'min:3', 'max:32', new \App\Rules\NotContainsString()], 'user_id' => 'exists:users,id', - 'markers.*.created_at' => 'nullable', - 'markers.*.updated_at' => 'nullable', - 'markers.*.expires_at' => 'nullable', + 'markers.*.created_at' => 'nullable|date', + 'markers.*.updated_at' => 'nullable|date', + 'markers.*.expires_at' => 'nullable|date', 'markers.*.link' => [Rule::requiredIf(optional($map->options)['links'] === "required")], 'markers.*.meta' => 'nullable|array|max:10', 'markers.*.meta.*' => ['nullable', 'max:255'], @@ -168,8 +168,8 @@ public function storeInBulk(Request $request, Map $map) 'markers.*.locations.*.speed' => 'nullable|numeric|between:0,100000', 'markers.*.locations.*.zoom' => 'nullable|numeric|between:0,20', 'markers.*.locations.*.elevation' => 'nullable|numeric|between:-100000,100000', - 'markers.*.locations.*.created_at' => 'nullable', - 'markers.*.locations.*.updated_at' => 'nullable', + 'markers.*.locations.*.created_at' => 'nullable|date', + 'markers.*.locations.*.updated_at' => 'nullable|date', ]); $now = Carbon::now(); @@ -179,8 +179,13 @@ public function storeInBulk(Request $request, Map $map) foreach ($validated_data['markers'] as $index => $marker) { $marker['bulk_insert_id'] = $bulkInsertId; - $marker['created_at'] = $marker['created_at'] ?? $now; - $marker['updated_at'] = $marker['updated_at'] ?? $now; + + + // The dates need to be converted to Carbon instances and then to string for insertion + $marker['created_at'] = isset($marker['created_at']) ? Carbon::parse($marker['created_at'])->toDateTimeString() : $now; + $marker['updated_at'] = isset($marker['updated_at']) ? Carbon::parse($marker['updated_at'])->toDateTimeString() : $now; + $marker['expires_at'] = isset($marker['expires_at']) ? Carbon::parse($marker['expires_at'])->toDateTimeString() : null; + $marker['token'] = Str::random(32); $marker['user_id'] = $validated_data['user_id']; $marker['map_id'] = $map->id; @@ -216,6 +221,17 @@ public function storeInBulk(Request $request, Map $map) $insertableMarker = $marker; + // If there is meta, we need to json_encode it + if (isset($insertableMarker['meta'])) { + $insertableMarker['meta'] = json_encode($insertableMarker['meta']); + } else { + $insertableMarker['meta'] = null; + } + + if (!isset($insertableMarker['link'])) { + $insertableMarker['link'] = null; + } + unset($insertableMarker['elevation']); unset($insertableMarker['current_location']); unset($insertableMarker['zoom']); @@ -268,8 +284,8 @@ public function storeInBulk(Request $request, Map $map) 'roll' => $location['roll'] ?? null, 'speed' => $location['speed'] ?? null, 'user_id' => $marker->user_id, - 'created_at' => $location['created_at'] ?? $marker->created_at ?? $now, - 'updated_at' => $location['updated_at'] ?? $marker->updated_at ?? $now, + 'created_at' => isset($location['created_at']) ? Carbon::parse($location['created_at'])->toDateTimeString() : $marker->created_at ?? $now, + 'updated_at' => isset($location['updated_at']) ? Carbon::parse($location['updated_at'])->toDateTimeString() : $marker->updated_at ?? $now, ]; } diff --git a/app/Parsers/Files/GPXParser.php b/app/Parsers/Files/GPXParser.php index 5ef8440..6eb602c 100644 --- a/app/Parsers/Files/GPXParser.php +++ b/app/Parsers/Files/GPXParser.php @@ -26,7 +26,7 @@ public function parseFile(string $filepath): array !is_string($marker['name']) || empty($marker['name']) ) { - $marker['name'] = 'Waypoint'; + $marker['name'] = $marker['sym'] ?? 'Waypoint'; } if ( @@ -37,11 +37,27 @@ public function parseFile(string $filepath): array $marker['desc'] = null; } + $markerMeta = []; + + // Add all direct children of the wpt element as metadata, except for the name and desc + foreach ($marker as $key => $value) { + if ($key === 'name' || $key === 'desc' || $key === 'ele' || $key === 'sym' || $key === 'link' || $key === '@attributes') { + continue; + } + + $markerMeta[$key] = $value; + } + $markers[] = [ 'lat' => $marker['@attributes']['lat'], 'lng' => $marker['@attributes']['lon'], - 'category_name' => $marker['name'] ?? 'Waypoint', + 'category_name' => $marker['name'], 'description' => $marker['desc'] ?? null, + 'link' => $marker['link'] ?? null, + 'elevation' => $marker['ele'] ?? null, + 'created_at' => $marker['time'] ?? null, + 'updated_at' => $marker['time'] ?? null, + 'meta' => $markerMeta, ]; } } @@ -60,6 +76,9 @@ public function parseFile(string $filepath): array $markerlocations[] = [ 'lat' => $trkpt['@attributes']['lat'], 'lng' => $trkpt['@attributes']['lon'], + 'elevation' => $trkpt['ele'] ?? null, + 'created_at' => $trkpt['time'] ?? null, + 'updated_at' => $trkpt['time'] ?? null, ]; } } @@ -83,11 +102,24 @@ public function parseFile(string $filepath): array $track['desc'] = null; } + $markerMeta = []; + + // Add all direct children of the trk element as metadata, except for the name and desc + foreach ($track as $key => $value) { + if ($key === 'name' || $key === 'desc' || $key === 'ele' || $key === 'trkseg' || $key === 'trkpt' || $key === '@attributes') { + continue; + } + + $markerMeta[$key] = $value; + } + $markers[] = [ 'description' => $track['desc'], 'locations' => $markerlocations, // The category_name is the name of the track - parsed CDATA 'category_name' => $track['name'], + // Meta needs to be encoded as JSON + 'meta' => $markerMeta, ]; } } diff --git a/tests/Unit/MarkerTest.php b/tests/Unit/MarkerTest.php index 3991081..c787f93 100644 --- a/tests/Unit/MarkerTest.php +++ b/tests/Unit/MarkerTest.php @@ -2,6 +2,8 @@ namespace Tests\Unit; +use App\Jobs\FillMissingLocationGeocodes; +use App\Jobs\FillMissingMarkerElevation; use App\Models\Category; use App\Models\Marker; use App\Models\User; @@ -214,6 +216,12 @@ public function testFailCreateMarkerInBulkUnauthorised() */ public function testCreateMarkerInBulk() { + // Skip any dispatched jobs + $this->expectsJobs([ + FillMissingMarkerElevation::class, + FillMissingLocationGeocodes::class, + ]); + // Get raw factory data $marker = Marker::factory()->make(); @@ -258,6 +266,12 @@ public function testCreateMarkerInBulk() */ public function testCreateMarkerInBulkWithAllMapOptions() { + // Skip any dispatched jobs + $this->expectsJobs([ + FillMissingMarkerElevation::class, + FillMissingLocationGeocodes::class, + ]); + $map = new \App\Models\Map(); $map->users_can_create_markers = 'yes'; $map->options = ['links' => 'optional']; @@ -306,6 +320,12 @@ public function testCreateMarkerInBulkWithAllMapOptions() */ public function testCreateMarkerInBulkWithMultipleLocations() { + // Skip any dispatched jobs + $this->expectsJobs([ + FillMissingMarkerElevation::class, + FillMissingLocationGeocodes::class, + ]); + $map = new \App\Models\Map(); $map->users_can_create_markers = 'yes'; $map->options = ['links' => 'optional']; @@ -382,6 +402,14 @@ public function testCreateMarkerInBulkWithMultipleLocations() */ public function testCreateMarkerInBulkWithGpxFile() { + $this->withoutExceptionHandling(); + + // Skip any dispatched jobs + $this->expectsJobs([ + FillMissingMarkerElevation::class, + FillMissingLocationGeocodes::class, + ]); + // We need to clean up the database before we start DB::table('markers')->delete(); DB::table('marker_locations')->delete(); @@ -412,6 +440,18 @@ public function testCreateMarkerInBulkWithGpxFile() // The DB file adds 348 trkpt, so we should have 348 locations + 25 wpt $this->assertEquals(373, $map->markerLocations()->count()); + + // 271 of the locations should have an elevation + $this->assertEquals(271, $map->markerLocations()->whereNotNull('elevation')->count()); + + // 267 should have created_at and updated_at on 2002-04-21 (any time) + $this->assertEquals(267, $map->markerLocations()->whereDate('marker_locations.created_at', '2002-04-21')->whereDate('marker_locations.updated_at', '2002-04-21')->count()); + + // The others should have todays date + $this->assertEquals(106, $map->markerLocations()->whereDate('marker_locations.created_at', now()->toDateString())->whereDate('marker_locations.updated_at', now()->toDateString())->count()); + + // There should be 11 markers with the field "number". It doesn't matter what the value is, just that it exists. This field will be a key in the JSON column called "meta" + $this->assertEquals(11, $map->markers()->whereNotNull('meta->number')->count()); } /**