feat: add StationTaggingService with supermarket detection
This commit is contained in:
32
app/Services/StationTaggingService.php
Normal file
32
app/Services/StationTaggingService.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\Station;
|
||||||
|
|
||||||
|
class StationTaggingService
|
||||||
|
{
|
||||||
|
/** @var array<string, string> brand keyword → normalised brand name */
|
||||||
|
private const SUPERMARKET_BRANDS = [
|
||||||
|
'tesco' => 'Tesco',
|
||||||
|
'asda' => 'Asda',
|
||||||
|
'morrisons' => 'Morrisons',
|
||||||
|
'sainsbury' => 'Sainsbury\'s',
|
||||||
|
'aldi' => 'Aldi',
|
||||||
|
'lidl' => 'Lidl',
|
||||||
|
'costco' => 'Costco',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function tag(Station $station): void
|
||||||
|
{
|
||||||
|
$name = strtolower($station->trading_name);
|
||||||
|
|
||||||
|
foreach (self::SUPERMARKET_BRANDS as $keyword => $normalisedBrand) {
|
||||||
|
if (str_contains($name, $keyword)) {
|
||||||
|
$station->is_supermarket = true;
|
||||||
|
$station->brand_name = $normalisedBrand;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,10 @@ class StationFactory extends Factory
|
|||||||
{
|
{
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
$trading = $this->faker->company();
|
$trading = 'Station ' . str()->random(8);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'node_id' => hash('sha256', $this->faker->unique()->uuid()),
|
'node_id' => (string) str()->ulid(),
|
||||||
'trading_name' => $trading,
|
'trading_name' => $trading,
|
||||||
'brand_name' => $trading,
|
'brand_name' => $trading,
|
||||||
'is_same_trading_and_brand' => true,
|
'is_same_trading_and_brand' => true,
|
||||||
@@ -24,14 +24,14 @@ class StationFactory extends Factory
|
|||||||
'permanent_closure' => false,
|
'permanent_closure' => false,
|
||||||
'permanent_closure_date' => null,
|
'permanent_closure_date' => null,
|
||||||
'public_phone_number' => null,
|
'public_phone_number' => null,
|
||||||
'address_line_1' => $this->faker->streetAddress(),
|
'address_line_1' => 'Address ' . str()->random(6),
|
||||||
'address_line_2' => null,
|
'address_line_2' => null,
|
||||||
'city' => $this->faker->city(),
|
'city' => 'City ' . str()->random(6),
|
||||||
'county' => null,
|
'county' => null,
|
||||||
'country' => 'England',
|
'country' => 'England',
|
||||||
'postcode' => strtoupper($this->faker->postcode()),
|
'postcode' => strtoupper(str()->random(6)),
|
||||||
'lat' => $this->faker->latitude(49.9, 60.9),
|
'lat' => random_int(499, 609) / 10.0,
|
||||||
'lng' => $this->faker->longitude(-8.2, 1.8),
|
'lng' => random_int(-82, 18) / 10.0,
|
||||||
'amenities' => [],
|
'amenities' => [],
|
||||||
'opening_times' => null,
|
'opening_times' => null,
|
||||||
'fuel_types' => ['E10', 'E5'],
|
'fuel_types' => ['E10', 'E5'],
|
||||||
|
|||||||
68
tests/Unit/Services/StationTaggingServiceTest.php
Normal file
68
tests/Unit/Services/StationTaggingServiceTest.php
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Station;
|
||||||
|
use App\Services\StationTaggingService;
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
$this->service = new StationTaggingService();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks tesco station as supermarket and normalises brand', function (): void {
|
||||||
|
$station = new Station([
|
||||||
|
'trading_name' => 'TESCO EXTRA',
|
||||||
|
'is_supermarket' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->service->tag($station);
|
||||||
|
|
||||||
|
expect($station->is_supermarket)->toBeTrue()
|
||||||
|
->and($station->brand_name)->toBe('Tesco');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks asda station as supermarket', function (): void {
|
||||||
|
$station = new Station([
|
||||||
|
'trading_name' => 'Asda Petrol Station',
|
||||||
|
'is_supermarket' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->service->tag($station);
|
||||||
|
|
||||||
|
expect($station->is_supermarket)->toBeTrue()
|
||||||
|
->and($station->brand_name)->toBe('Asda');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not mark independent station as supermarket', function (): void {
|
||||||
|
$station = new Station([
|
||||||
|
'trading_name' => 'Village Garage',
|
||||||
|
'is_supermarket' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->service->tag($station);
|
||||||
|
|
||||||
|
expect($station->is_supermarket)->toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles case insensitive matching', function (): void {
|
||||||
|
$station = new Station([
|
||||||
|
'trading_name' => 'morrisons petrol',
|
||||||
|
'is_supermarket' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->service->tag($station);
|
||||||
|
|
||||||
|
expect($station->is_supermarket)->toBeTrue()
|
||||||
|
->and($station->brand_name)->toBe('Morrisons');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not overwrite brand_name for non-supermarket stations', function (): void {
|
||||||
|
$station = new Station([
|
||||||
|
'trading_name' => 'Shell Garage',
|
||||||
|
'brand_name' => 'Shell',
|
||||||
|
'is_supermarket' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->service->tag($station);
|
||||||
|
|
||||||
|
expect($station->is_supermarket)->toBeFalse()
|
||||||
|
->and($station->brand_name)->toBe('Shell');
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user