Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#24 - add Link and BinBin providers #117

Merged
merged 13 commits into from
Aug 22, 2023
64 changes: 64 additions & 0 deletions app/Importers/BinBinDataImporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace App\Importers;

use GuzzleHttp\Exception\GuzzleException;
use Symfony\Component\DomCrawler\Crawler;

class BinBinDataImporter extends DataImporter
{
public const language = "en";
krzysztofrewak marked this conversation as resolved.
Show resolved Hide resolved

protected Crawler $sections;

public function extract(): static
{
try {
$response = $this->client->get("https://www.binbin.tech/lokasyonlar");
$html = $response->getBody()->getContents();
} catch (GuzzleException) {
$this->createImportInfoDetails("400", self::getProviderName());

$this->stopExecution = true;

return $this;
}

$crawler = new Crawler($html);
$this->sections = $crawler->filter(".location-wrapper .cities-information .div-block-35");

if (count($this->sections) === 0) {
$this->createImportInfoDetails("204", self::getProviderName());

$this->stopExecution = true;
}

return $this;
}

public function transform(): void
{
if ($this->stopExecution) {
return;
}

$existingCityProviders = [];

foreach ($this->sections as $section) {
$data = explode("|", $section->nodeValue);

if (trim($data[1]) === "Türkiye") $countryName = "Turkey"; else $countryName = $this->translate(trim($data[1]), self::language);

if (trim($data[0]) === "Uşak") $cityName = "Usak"; elseif (trim($data[0]) === "Murter") continue;
else $cityName = $this->translate(trim($data[0]), "en");
AleksandraKozubal marked this conversation as resolved.
Show resolved Hide resolved

$provider = $this->load($cityName, $countryName);

if ($provider !== "") $existingCityProviders[] = $provider;
}

$this->deleteMissingProviders(self::getProviderName(), $existingCityProviders);
}
}
15 changes: 12 additions & 3 deletions app/Importers/DataImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@
use App\Models\ImportInfoDetail;
use App\Services\MapboxGeocodingService;
use GuzzleHttp\Client;
use Stichoza\GoogleTranslate\GoogleTranslate;

abstract class DataImporter
{
protected bool $stopExecution = false;
protected int $importInfoId;
protected GoogleTranslate $translate;

public function __construct(
protected Client $client,
protected MapboxGeocodingService $mapboxService,
) {}
) {
$this->translate = new GoogleTranslate();
}

public function setImportInfo(int $importInfoId): static
{
Expand All @@ -48,6 +52,11 @@ public static function getProviderName(): string
return $classNameParts[0];
}

public function translate(string $word, $language): string
AleksandraKozubal marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->translate->setTarget($language)->translate($word);
}

protected function countryNotFound(string $cityName, string $countryName): void
{
CityWithoutAssignedCountry::query()->updateOrCreate(
Expand Down Expand Up @@ -108,7 +117,7 @@ protected function load(string $cityName, string $countryName, string $lat = "",
$this->createProvider($cityId, self::getProviderName());

return strval($cityId);
}
}
$country = Country::query()->where("name", $countryName)->orWhere("alternative_name", $countryName)->first();

if ($country) {
Expand All @@ -129,7 +138,7 @@ protected function load(string $cityName, string $countryName, string $lat = "",
$this->createProvider($city->id, self::getProviderName());

return strval($city->id);
}
}
$this->countryNotFound($cityName, $countryName);
$this->createImportInfoDetails("420", self::getProviderName());

Expand Down
86 changes: 86 additions & 0 deletions app/Importers/LinkDataImporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace App\Importers;

use GuzzleHttp\Exception\GuzzleException;
use Symfony\Component\DomCrawler\Crawler;

class LinkDataImporter extends DataImporter
{
protected Crawler $sections;
private string $countryName = "";

public function extract(): static
{
try {
$response = $this->client->get("https://superpedestrian.com/locations");
$html = $response->getBody()->getContents();
} catch (GuzzleException) {
$this->createImportInfoDetails("400", self::getProviderName());

$this->stopExecution = true;

return $this;
}

$crawler = new Crawler($html);
$this->sections = $crawler->filter(".Main-content .sqs-row.row > .col p > strong");

if (count($this->sections) === 0) {
$this->createImportInfoDetails("204", self::getProviderName());

$this->stopExecution = true;
}

return $this;
}

public function transform(): void
{
if ($this->stopExecution) {
return;
}

$existingCityProviders = [];

foreach ($this->sections as $section) {
foreach ($section->childNodes as $node) {
$countryName = trim($node->nodeValue);

foreach ($node->parentNode->parentNode->parentNode->childNodes as $i => $cityName) {
if ($i === 0 || !trim($cityName->nodeValue)) {
continue;
}

$name = $cityName->nodeValue;

$cities = [];

if (str_contains($name, "(") && str_contains($name, ")")) {
$names = explode("(", $name)[1];
$names = explode(")", $names)[0];
$names = explode(", ", $names);

foreach ($names as $name) {
$cities[] = str_replace("*", "", $name);
}
} else {
$cities[] = $name;
}

foreach ($cities as $name) {
$provider = $this->load($name, $countryName);

if ($provider !== "") {
$existingCityProviders[] = $provider;
}
}
}
}
}

$this->deleteMissingProviders(self::getProviderName(), $existingCityProviders);
}
}
15 changes: 15 additions & 0 deletions app/Jobs/BinBinDataImporterJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Jobs;

use App\Importers\BinBinDataImporter;

class BinBinDataImporterJob extends DataImporterJob
{
public function handle(BinBinDataImporter $importer): void
{
$importer->setImportInfo($this->importInfoId)->extract()->transform();
}
}
15 changes: 15 additions & 0 deletions app/Jobs/LinkDataImporterJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Jobs;

use App\Importers\LinkDataImporter;

class LinkDataImporterJob extends DataImporterJob
{
public function handle(LinkDataImporter $importer): void
{
$importer->setImportInfo($this->importInfoId)->extract()->transform();
}
}
4 changes: 4 additions & 0 deletions app/Services/DataImporterService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace App\Services;

use App\Jobs\BinBinDataImporterJob;
use App\Jobs\BirdDataImporterJob;
use App\Jobs\BitMobilityDataImporterJob;
use App\Jobs\BoltDataImporterJob;
use App\Jobs\DottDataImporterJob;
use App\Jobs\HulajDataImporterJob;
use App\Jobs\LimeDataImporterJob;
use App\Jobs\LinkDataImporterJob;
use App\Jobs\NeuronDataImporterJob;
use App\Jobs\QuickDataImporterJob;
use App\Jobs\RydeDataImporterJob;
Expand All @@ -34,12 +36,14 @@ public function run(string $whoRunsIt = "admin"): void
$this->importInfoId = $importInfo->id;

Bus::batch([
new BinBinDataImporterJob($this->importInfoId),
new BirdDataImporterJob($this->importInfoId),
new BitMobilityDataImporterJob($this->importInfoId),
new BoltDataImporterJob($this->importInfoId),
new DottDataImporterJob($this->importInfoId),
new HulajDataImporterJob($this->importInfoId),
new LimeDataImporterJob($this->importInfoId),
new LinkDataImporterJob($this->importInfoId),
new NeuronDataImporterJob($this->importInfoId),
new QuickDataImporterJob($this->importInfoId),
new RydeDataImporterJob($this->importInfoId),
Expand Down
5 changes: 2 additions & 3 deletions app/Services/MapboxGeocodingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@ public function getCoordinatesFromApi(string $cityName, string $countryName): ar
);

$coordinates = json_decode($response->getBody()->getContents(), associative: true)["features"][0]["center"];

return [$coordinates[1], $coordinates[0]];
} catch (Throwable $exception) {
throw new MapboxGeocodingServiceException(previous: $exception);
}

return [$coordinates[1] ?? null, $coordinates[0] ?? null];
}

/**
Expand Down
4 changes: 4 additions & 0 deletions database/seeders/ProviderSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace Database\Seeders;

use App\Importers\BinBinDataImporter;
use App\Importers\BirdDataImporter;
use App\Importers\BitMobilityDataImporter;
use App\Importers\BoltDataImporter;
use App\Importers\DottDataImporter;
use App\Importers\HulajDataImporter;
use App\Importers\LimeDataImporter;
use App\Importers\LinkDataImporter;
use App\Importers\NeuronDataImporter;
use App\Importers\QuickDataImporter;
use App\Importers\RydeDataImporter;
Expand All @@ -25,12 +27,14 @@ class ProviderSeeder extends Seeder
public function run(): void
{
$providers = [
["name" => BinBinDataImporter::getProviderName(), "color" => "#3dbcc8"],
["name" => BirdDataImporter::getProviderName(), "color" => "#26ccf0"],
["name" => BitMobilityDataImporter::getProviderName(), "color" => "#8da6e3"],
["name" => BoltDataImporter::getProviderName(), "color" => "#24f0a0"],
["name" => DottDataImporter::getProviderName(), "color" => "#f5c604"],
["name" => HulajDataImporter::getProviderName(), "color" => "#d6213f"],
["name" => LimeDataImporter::getProviderName(), "color" => "#00de00"],
["name" => LinkDataImporter::getProviderName(), "color" => "#def700"],
["name" => NeuronDataImporter::getProviderName(), "color" => "#445261"],
["name" => QuickDataImporter::getProviderName(), "color" => "#009ac7"],
["name" => SpinDataImporter::getProviderName(), "color" => "#ff5436"],
Expand Down
Binary file added public/providers/binbin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
## 🛴 escooters
### Available providers

1. BinBin
2. Bird
3. BitMobility
4. Bolt
5. Dott
6. Hulaj
7. Lime
8. Link
9. Neuron
10. Quick
11. Ryde
12. Spin
13. Tier
13. Urent
14. Voi
15. Zwings
krzysztofrewak marked this conversation as resolved.
Show resolved Hide resolved

### Local development
```
cp .env.example .env
Expand Down
3 changes: 3 additions & 0 deletions tests/Unit/ImporterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\MockObject\Exception;
use Stichoza\GoogleTranslate\GoogleTranslate;
use Tests\TestCase;

class ImporterTest extends TestCase
Expand All @@ -41,6 +42,8 @@ protected function setUp(): void
$handlerStack = HandlerStack::create($mockHandler);
$mockHttpClient = new Client(["handler" => $handlerStack]);

$mockGoogleTranslate = $this->createMock(GoogleTranslate::class);

$mockMapboxService = $this->createMock(MapboxGeocodingService::class);
$mockMapboxService->method("getPlaceFromApi")->willReturn(["Perth", "Australia"]);
$mockMapboxService->method("getCoordinatesFromApi")
Expand Down