diff --git a/app/Dto/Wikidata/WikidataObject.php b/app/Dto/Wikidata/WikidataEntity.php similarity index 97% rename from app/Dto/Wikidata/WikidataObject.php rename to app/Dto/Wikidata/WikidataEntity.php index 1b28353df..df4160c41 100644 --- a/app/Dto/Wikidata/WikidataObject.php +++ b/app/Dto/Wikidata/WikidataEntity.php @@ -5,7 +5,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; use JsonException; -readonly class WikidataObject +readonly class WikidataEntity { public string $qId; diff --git a/app/Http/Controllers/Frontend/Admin/StationController.php b/app/Http/Controllers/Frontend/Admin/StationController.php index d39f9e09a..0a57c6d20 100644 --- a/app/Http/Controllers/Frontend/Admin/StationController.php +++ b/app/Http/Controllers/Frontend/Admin/StationController.php @@ -7,7 +7,10 @@ use App\Models\Station; use App\Models\StationName; use App\Objects\LineSegment; +use App\Services\Wikidata\WikidataImportService; use App\Services\Wikidata\WikidataQueryService; +use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\View\View; @@ -116,4 +119,25 @@ public function fetchWikidata(int $id): void { ]); } } + + /** + * @param Request $request + * + * @return RedirectResponse + * @throws AuthorizationException + * @todo Make this an API endpoint when it is accessible for users too + */ + public function importWikidata(Request $request): RedirectResponse { + $this->authorize('create', Station::class); + $validated = $request->validate([ + 'qId' => ['required', 'string', 'regex:/^Q\d+$/'], + ]); + try { + $station = WikidataImportService::importStation($validated['qId']); + return redirect()->route('admin.station', ['id' => $station->id])->with('alert-success', 'Station imported successfully'); + } catch (\Exception $exception) { + Log::error('Error while importing wikidata station (manually): ' . $exception->getMessage()); + return redirect()->back()->with('alert-danger', 'Error while importing station: ' . $exception->getMessage()); + } + } } diff --git a/app/Services/Wikidata/WikidataImportService.php b/app/Services/Wikidata/WikidataImportService.php new file mode 100644 index 000000000..5cd599134 --- /dev/null +++ b/app/Services/Wikidata/WikidataImportService.php @@ -0,0 +1,58 @@ +getClaims('P1448')[0]['mainsnak']['datavalue']['value']['text'] //P1448 = official name + ?? $wikidataEntity->getLabel('de') //german label + ?? $wikidataEntity->getLabel(); //english label or null if also not available + + if ($name === null) { + throw new \InvalidArgumentException('No name found for entity ' . $qId); + } + + $coordinates = $wikidataEntity->getClaims('P625')[0]['mainsnak']['datavalue']['value'] ?? null; //P625 = coordinate location + if ($coordinates === null) { + throw new \InvalidArgumentException('No coordinates found for entity ' . $qId); + } + + $latitude = $coordinates['latitude']; + $longitude = $coordinates['longitude']; + $ibnr = $wikidataEntity->getClaims('P954')[0]['mainsnak']['datavalue']['value'] ?? null; //P954 = IBNR + $rl100 = $wikidataEntity->getClaims('P8671')[0]['mainsnak']['datavalue']['value'] ?? null; //P8671 = RL100 + $ifopt = $wikidataEntity->getClaims('P12393')[0]['mainsnak']['datavalue']['value'] ?? null; //P12393 = IFOPT + if ($ifopt !== null) { + $splittedIfopt = explode(':', $ifopt); + } + + //if ibnr is already in use, we can't import the station + if ($ibnr !== null && Station::where('ibnr', $ibnr)->exists()) { + throw new \InvalidArgumentException('IBNR ' . $ibnr . ' already in use'); + } + + return Station::create( + [ + 'name' => $name, + 'latitude' => $latitude, + 'longitude' => $longitude, + 'wikidata_id' => $qId, + 'rilIdentifier' => $rl100, + 'ibnr' => $ibnr, + 'ifopt_a' => $splittedIfopt[0] ?? null, + 'ifopt_b' => $splittedIfopt[1] ?? null, + 'ifopt_c' => $splittedIfopt[2] ?? null, + 'ifopt_d' => $splittedIfopt[3] ?? null, + 'ifopt_e' => $splittedIfopt[4] ?? null, + ] + ); + } + +} diff --git a/app/Services/Wikidata/WikidataQueryService.php b/app/Services/Wikidata/WikidataQueryService.php index a85f707ec..f498ce3f8 100644 --- a/app/Services/Wikidata/WikidataQueryService.php +++ b/app/Services/Wikidata/WikidataQueryService.php @@ -2,7 +2,7 @@ namespace App\Services\Wikidata; -use App\Dto\Wikidata\WikidataObject; +use App\Dto\Wikidata\WikidataEntity; use EasyRdf\Sparql\Client; class WikidataQueryService @@ -39,7 +39,7 @@ private function parseObjects(): void { foreach ($this->results as $result) { $uri = $result->item->getUri(); $qId = substr($uri, strrpos($uri, '/') + 1); - $this->objects[] = WikidataObject::fetch($qId); + $this->objects[] = WikidataEntity::fetch($qId); } } } diff --git a/resources/views/admin/stations/list.blade.php b/resources/views/admin/stations/list.blade.php index e20d418a5..85ea4db19 100644 --- a/resources/views/admin/stations/list.blade.php +++ b/resources/views/admin/stations/list.blade.php @@ -212,6 +212,23 @@ function deleteStation(id) { + +
+
+

Import from Wikidata

+ +
+ @csrf +
+ + +
+
+ +
+
+
diff --git a/resources/views/admin/trip/show.blade.php b/resources/views/admin/trip/show.blade.php index 80eda78d1..5ed5a2c8c 100644 --- a/resources/views/admin/trip/show.blade.php +++ b/resources/views/admin/trip/show.blade.php @@ -121,12 +121,11 @@ Name TRWL-ID + Wikidata IBNR - RIL100 - Ankunft plan - real - Abfahrt plan - real + RL100 + Ankunft soll / ist + Abfahrt soll / ist @@ -138,18 +137,22 @@ {{$stopover->station?->id}} + + + {{$stopover->station?->wikidata_id}} + + {{$stopover->station?->ibnr}} {{$stopover->station?->rilIdentifier}} {{userTime($stopover->arrival_planned)}} - - + / {{userTime($stopover->arrival_real?->format('H:i'))}} {{userTime($stopover->departure_planned)}} - - + / {{userTime($stopover->departure_real)}} diff --git a/routes/web/admin.php b/routes/web/admin.php index 9e8ea99dd..5f98e5e35 100644 --- a/routes/web/admin.php +++ b/routes/web/admin.php @@ -65,6 +65,7 @@ Route::get('/{id}', [StationController::class, 'renderStation']) ->name('admin.station'); + Route::post('/wikidata/import', [StationController::class, 'importWikidata'])->name('backend.status.import.wikidata'); //TODO: Make this an API endpoint when it is accessible for users too Route::post('/{id}/wikidata', [StationController::class, 'fetchWikidata']); });