init
This commit is contained in:
84
app/Console/Commands/ParseEntries.php
Normal file
84
app/Console/Commands/ParseEntries.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Service\Service;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ParseEntries extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:parse-entries {file}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Parse entries from json file';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$filePath = $this->argument('file');
|
||||
if(!file_exists($filePath)) {
|
||||
$this->error('File not found');
|
||||
return;
|
||||
}
|
||||
$json = file_get_contents($filePath);
|
||||
$data = json_decode($json, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$this->error('Invalid JSON file');
|
||||
Log::channel('parser')->error('Invalid JSON file '. $filePath);
|
||||
return;
|
||||
}
|
||||
$this->info('File loaded');
|
||||
|
||||
$service = new Service();
|
||||
$successCount = 0;
|
||||
$failCount = 0;
|
||||
$this->line('Start parsing...');
|
||||
Log::channel('parser')->info('Start parsing file ' . $filePath);
|
||||
|
||||
foreach ($data as $entry) {
|
||||
try {
|
||||
if($service->processEntry($entry)) $successCount++; else $failCount++;
|
||||
Log::channel('parser')->info('Processed entry ' . $entry['number']);
|
||||
} catch (\Exception $e) {
|
||||
$failCount++;
|
||||
$number = array_key_exists('number', $entry) ? $entry['number'] : '';
|
||||
$message = 'Error processing entry ' . $number . ': ' . $e->getMessage();
|
||||
$this->error($message);
|
||||
Log::channel('parser')->error('Error processing entry ', [
|
||||
'number' => $number,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->line('Parsing finished.');
|
||||
$this->info('Total entries: ' . ($successCount + $failCount));
|
||||
$this->info('Success entries: ' . $successCount);
|
||||
$this->info('Total inspections: '. $service->getInspectionsCount());
|
||||
$this->info('Total failure reports: '. $service->getFailureReportsCount());
|
||||
$this->info('Fail entries: ' . $failCount);
|
||||
Log::channel('parser')->info('Parsing finished. Total entries: ' . ($successCount + $failCount));
|
||||
Log::channel('parser')->info('Success entries: ' . $successCount);
|
||||
Log::channel('parser')->info('Total inspections: '. $service->getInspectionsCount());
|
||||
Log::channel('parser')->info('Total failure reports: '. $service->getFailureReportsCount());
|
||||
Log::channel('parser')->info('Fail entries: ' . $failCount);
|
||||
$path = base_path('./output');
|
||||
File::ensureDirectoryExists($path);
|
||||
File::put($path.'/failureReports.json', json_encode($service->getFailureReports(), JSON_PRETTY_PRINT| JSON_UNESCAPED_UNICODE));
|
||||
File::put($path.'/inspections.json', json_encode($service->getInspections(), JSON_PRETTY_PRINT| JSON_UNESCAPED_UNICODE));
|
||||
File::put($path.'/unprocessable.json', json_encode($service->getUnprocessable(), JSON_PRETTY_PRINT| JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
48
app/Models/User.php
Normal file
48
app/Models/User.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
24
app/Providers/AppServiceProvider.php
Normal file
24
app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
19
app/Service/Entities/BaseReport.php
Normal file
19
app/Service/Entities/BaseReport.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Entities;
|
||||
|
||||
use App\Service\Enums\Status;
|
||||
use App\Service\Enums\Type;
|
||||
use Carbon\Carbon;
|
||||
use DateTimeImmutable;
|
||||
|
||||
abstract class BaseReport
|
||||
{
|
||||
public Type $type;
|
||||
public string $description = '';
|
||||
public ?Carbon $serviceDate = null;
|
||||
public Status $status = Status::NEW;
|
||||
public string $serviceNotes = '';
|
||||
public ?string $contactPhone = null;
|
||||
public DateTimeImmutable $createdAt;
|
||||
}
|
||||
9
app/Service/Entities/EntityInterface.php
Normal file
9
app/Service/Entities/EntityInterface.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Entities;
|
||||
|
||||
interface EntityInterface
|
||||
{
|
||||
public function __construct(InputEntity $entity);
|
||||
public static function isValid(InputEntity $entity): bool;
|
||||
}
|
||||
66
app/Service/Entities/FailureReport.php
Normal file
66
app/Service/Entities/FailureReport.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Entities;
|
||||
|
||||
use App\Service\Enums\Priority;
|
||||
use App\Service\Enums\Status;
|
||||
use App\Service\Enums\Type;
|
||||
use DateTimeImmutable;
|
||||
|
||||
class FailureReport extends BaseReport implements EntityInterface
|
||||
{
|
||||
public Priority $priority = Priority::NORMAL;
|
||||
public string $serviceNotes = '';
|
||||
public Type $type = Type::FAILURE;
|
||||
|
||||
|
||||
public function __construct(InputEntity $entity)
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
$this->fromData($entity);
|
||||
}
|
||||
|
||||
public static function isValid(InputEntity $entity): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __toArray(): array
|
||||
{
|
||||
return [
|
||||
'description' => $this->description,
|
||||
'type' => $this->type->value,
|
||||
'priority' => $this->priority->value,
|
||||
'serviceDate' => $this->serviceDate?->format('Y-m-d') ?? '',
|
||||
'status' => $this->status->value,
|
||||
'serviceNotes' => $this->serviceNotes,
|
||||
'contactPhone' => $this->contactPhone ?? '',
|
||||
'createdAt' => $this->createdAt->format('Y-m-d')
|
||||
];
|
||||
}
|
||||
|
||||
private function fromData(InputEntity $entity): void
|
||||
{
|
||||
$this->description = $entity->description;
|
||||
$this->serviceDate = $entity->date;
|
||||
$this->contactPhone = $entity->phone;
|
||||
$this->checkPriority($entity->description);
|
||||
$this->status = $this->serviceDate ? Status::DEADLINE : Status::NEW;
|
||||
|
||||
}
|
||||
|
||||
private function checkPriority(string $description): void
|
||||
{
|
||||
$map = [
|
||||
'/bardzo pilne/im' => Priority::CRITICAL,
|
||||
'/pilne/im' => Priority::HIGH,
|
||||
];
|
||||
|
||||
foreach ($map as $pattern => $priority) {
|
||||
if (preg_match($pattern, $description)) {
|
||||
$this->priority = $priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
app/Service/Entities/InputEntity.php
Normal file
60
app/Service/Entities/InputEntity.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Entities;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
class InputEntity
|
||||
{
|
||||
public int $number;
|
||||
public string $description;
|
||||
public ?Carbon $date = null;
|
||||
public ?string $phone = null;
|
||||
|
||||
public function __construct(private readonly array $data) {
|
||||
$this->parse();
|
||||
}
|
||||
|
||||
private function parse()
|
||||
{
|
||||
$this->validateNumber();
|
||||
$this->validateDescription();
|
||||
$this->validateDate();
|
||||
$this->validatePhone();
|
||||
|
||||
}
|
||||
|
||||
private function validateNumber(): void {
|
||||
if(!array_key_exists('number', $this->data) || !preg_match('/^\d+$/', trim($this->data['number']))) throw new \InvalidArgumentException('Invalid number');
|
||||
$this->number = (int)$this->data['number'];
|
||||
}
|
||||
|
||||
private function validateDescription(): void {
|
||||
if(!array_key_exists('description', $this->data) || empty(trim($this->data['description']))) throw new \InvalidArgumentException('Empty description');
|
||||
$this->description = trim($this->data['description']);
|
||||
}
|
||||
|
||||
private function validateDate(): void {
|
||||
if(!array_key_exists('dueDate', $this->data)) return;
|
||||
$date = trim((string)$this->data['dueDate']);
|
||||
if(!strlen($date)) return;
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$/', $date)) {
|
||||
$this->date = Carbon::parse($date);
|
||||
return;
|
||||
}
|
||||
throw new \InvalidArgumentException('Invalid date');
|
||||
}
|
||||
|
||||
private function validatePhone(): void {
|
||||
if(!array_key_exists('phone', $this->data)) return;
|
||||
$rawPhone = $this->data['phone'];
|
||||
$phone = trim((string)$rawPhone);
|
||||
if(!strlen($phone)) return;
|
||||
$checkPhone = str_replace([' ', '-'], '', $phone);
|
||||
if(!preg_match('/^\+?\d{9,12}$/', $checkPhone)) {
|
||||
throw new \InvalidArgumentException('Invalid phone number');
|
||||
}
|
||||
$this->phone = $rawPhone;
|
||||
}
|
||||
}
|
||||
57
app/Service/Entities/Inspection.php
Normal file
57
app/Service/Entities/Inspection.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Entities;
|
||||
|
||||
use App\Service\Enums\Status;
|
||||
use App\Service\Enums\Type;
|
||||
use DateTimeImmutable;
|
||||
|
||||
class Inspection extends BaseReport implements EntityInterface
|
||||
{
|
||||
|
||||
public ?int $serviceWeek = null;
|
||||
public Type $type = Type::INSPECTION;
|
||||
|
||||
public function __construct(InputEntity $entity)
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
$this->fromData($entity);
|
||||
}
|
||||
|
||||
public static function isValid(InputEntity $entity): bool
|
||||
{
|
||||
return preg_match('/przegląd/mi', $entity->description);
|
||||
}
|
||||
|
||||
public function __toArray(): array
|
||||
{
|
||||
return [
|
||||
'description' => $this->description,
|
||||
'type' => $this->type->value,
|
||||
'inspectionDate' => $this->serviceDate?->format('Y-m-d') ?? '',
|
||||
'weekOfInspection' => $this->serviceWeek ?? '',
|
||||
'status' => $this->status->value,
|
||||
'recommendations' => $this->serviceNotes ?? '',
|
||||
'contactPhone' => $this->contactPhone ?? '',
|
||||
'createdAt' => $this->createdAt->format('Y-m-d')
|
||||
];
|
||||
}
|
||||
|
||||
private function fromData(InputEntity $entity): void
|
||||
{
|
||||
$this->description = $entity->description;
|
||||
$this->contactPhone = $entity->phone;
|
||||
$this->serviceDate = $entity->date;
|
||||
$this->checkWeek();
|
||||
$this->status = $this->serviceDate ? Status::PLANED : Status::NEW;
|
||||
}
|
||||
|
||||
private function checkWeek(): void
|
||||
{
|
||||
if($this->serviceDate) {
|
||||
$this->serviceWeek = $this->serviceDate->isoWeek();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
10
app/Service/Enums/Priority.php
Normal file
10
app/Service/Enums/Priority.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Enums;
|
||||
|
||||
enum Priority: string
|
||||
{
|
||||
case CRITICAL = 'krytyczny';
|
||||
case HIGH = 'wysoki';
|
||||
case NORMAL = 'normalny';
|
||||
}
|
||||
10
app/Service/Enums/Status.php
Normal file
10
app/Service/Enums/Status.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Enums;
|
||||
|
||||
enum Status: string
|
||||
{
|
||||
case PLANED = 'zaplanowany';
|
||||
case NEW = 'nowy';
|
||||
case DEADLINE = 'termin';
|
||||
}
|
||||
10
app/Service/Enums/Type.php
Normal file
10
app/Service/Enums/Type.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Enums;
|
||||
|
||||
enum Type: string
|
||||
{
|
||||
case INSPECTION = 'przegląd';
|
||||
case FAILURE = 'zgłoszenie awarii';
|
||||
case UNPROCESSABLE = 'nieprzetworzony';
|
||||
}
|
||||
81
app/Service/Service.php
Normal file
81
app/Service/Service.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Service\Entities\FailureReport;
|
||||
use App\Service\Entities\InputEntity;
|
||||
use App\Service\Entities\Inspection;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class Service
|
||||
{
|
||||
private array $unprocessable = [];
|
||||
private array $types = [Inspection::class, FailureReport::class];
|
||||
private array $entities = [];
|
||||
private array $descriptions = [];
|
||||
|
||||
public function processEntry(array $rawEntry)
|
||||
{
|
||||
try {
|
||||
$inputEntity = new InputEntity($rawEntry);
|
||||
} catch (\Exception $e) {
|
||||
$this->unprocessable[] = $rawEntry;
|
||||
throw $e;
|
||||
}
|
||||
if(in_array(trim(mb_strtolower($inputEntity->description)), $this->descriptions)) {
|
||||
$this->unprocessable[] = $rawEntry;
|
||||
throw new \InvalidArgumentException('Duplicate description');
|
||||
}
|
||||
$entity = $this->create($inputEntity);
|
||||
Log::channel('parser')->info('Created entity ', [
|
||||
'number' => $inputEntity->number,
|
||||
'type' => $entity->type->value,
|
||||
]);
|
||||
if($entity) {
|
||||
$this->entities[] = $entity;
|
||||
$this->descriptions[] = trim(mb_strtolower($inputEntity->description));
|
||||
return true;
|
||||
}
|
||||
$this->unprocessable[] = $rawEntry;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUnprocessable(): array {
|
||||
return $this->unprocessable;
|
||||
}
|
||||
|
||||
public function getInspections(): array {
|
||||
return $this->getEntities(Inspection::class)->map->__toArray()->all();
|
||||
}
|
||||
|
||||
public function getInspectionsCount(): int {
|
||||
return $this->getEntities(Inspection::class)->count();
|
||||
}
|
||||
|
||||
public function getFailureReports(): array {
|
||||
return $this->getEntities(FailureReport::class)->map->__toArray()->all();;
|
||||
}
|
||||
|
||||
public function getFailureReportsCount(): int
|
||||
{
|
||||
return $this->getEntities(FailureReport::class)->count();
|
||||
}
|
||||
|
||||
private function getEntities($type): Collection
|
||||
{
|
||||
return collect($this->entities)
|
||||
->whereInstanceOf($type)
|
||||
->values();
|
||||
}
|
||||
|
||||
private function create(InputEntity $inputEntity)
|
||||
{
|
||||
foreach ($this->types as $type) {
|
||||
if($type::isValid($inputEntity)) {
|
||||
return new $type($inputEntity);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user