netgescon-day0/app/Console/Commands/GesconBackfillRubricaLegacyIdentityCommand.php

240 lines
7.6 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\RubricaUniversale;
use App\Models\Titolo;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Schema;
class GesconBackfillRubricaLegacyIdentityCommand extends Command
{
protected $signature = 'gescon:backfill-rubrica-legacy-identity
{--apply : Applica realmente le modifiche}
{--limit=5000 : Numero massimo di record da analizzare}
{--id=* : Limita l\'analisi a specifici ID rubrica}';
protected $description = 'Sposta i dati anagrafici legacy dalle note ai campi strutturati della rubrica_universale.';
public function handle(): int
{
if (! Schema::hasTable('rubrica_universale')) {
$this->error('Tabella rubrica_universale non disponibile.');
return self::FAILURE;
}
$apply = (bool) $this->option('apply');
$limit = max(1, min((int) $this->option('limit'), 50000));
$ids = array_values(array_filter(array_map(fn($value) => is_numeric($value) ? (int) $value : 0, (array) $this->option('id'))));
$query = RubricaUniversale::query()
->where('note', 'like', '%Dati anagrafici legacy:%')
->orderBy('id')
->limit($limit);
if ($ids !== []) {
$query->whereIn('id', $ids);
}
$rows = $query->get();
if ($rows->isEmpty()) {
$this->info('Nessun record da bonificare.');
return self::SUCCESS;
}
$processed = 0;
$updated = 0;
foreach ($rows as $rubrica) {
$processed++;
$legacy = $this->extractLegacyIdentityFromNote($rubrica->note);
$changes = [];
if (! $rubrica->titolo_id && ! empty($legacy['titolo_id'])) {
$changes['titolo_id'] = (int) $legacy['titolo_id'];
}
if (! $rubrica->sesso && ! empty($legacy['sesso'])) {
$changes['sesso'] = $legacy['sesso'];
}
if ((! $rubrica->data_nascita || (int) optional($rubrica->data_nascita)->format('Y') > (int) now()->format('Y') + 1) && ! empty($legacy['data_nascita'])) {
$changes['data_nascita'] = $legacy['data_nascita'];
}
if (! $rubrica->luogo_nascita && ! empty($legacy['luogo_nascita'])) {
$changes['luogo_nascita'] = $legacy['luogo_nascita'];
}
if (! $rubrica->provincia_nascita && ! empty($legacy['provincia_nascita'])) {
$changes['provincia_nascita'] = $legacy['provincia_nascita'];
}
$cleanNote = $legacy['note_clean'] ?? null;
if (($rubrica->note ?? null) !== $cleanNote) {
$changes['note'] = $cleanNote;
}
if ($changes === []) {
continue;
}
$updated++;
if ($apply) {
$rubrica->fill($changes);
$rubrica->save();
}
}
$this->info(($apply ? 'Bonifica eseguita' : 'Dry-run bonifica') . ": processati {$processed}, aggiornabili {$updated}");
return self::SUCCESS;
}
/** @return array<string, mixed> */
private function extractLegacyIdentityFromNote(mixed $note): array
{
$raw = trim((string) ($note ?? ''));
if ($raw === '') {
return [
'note_clean' => null,
'titolo_id' => null,
'sesso' => null,
'data_nascita' => null,
'luogo_nascita' => null,
'provincia_nascita' => null,
];
}
$parsed = [
'titolo_id' => null,
'sesso' => null,
'data_nascita' => null,
'luogo_nascita' => null,
'provincia_nascita' => null,
];
$cleanLines = [];
foreach (preg_split('/\r\n|\r|\n/', $raw) ?: [] as $line) {
$trimmed = trim($line);
if (! str_starts_with($trimmed, 'Dati anagrafici legacy:')) {
if ($trimmed !== '') {
$cleanLines[] = $line;
}
continue;
}
$payload = trim(substr($trimmed, strlen('Dati anagrafici legacy:')));
$bits = preg_split('/\s*\|\s*/', $payload) ?: [];
foreach ($bits as $bit) {
[$key, $value] = array_pad(explode(':', $bit, 2), 2, null);
$key = mb_strtolower(trim((string) $key));
$value = trim((string) $value);
if ($value === '') {
continue;
}
if ($key === 'titolo') {
$parsed['titolo_id'] = $this->resolveTitoloId($value);
continue;
}
if ($key === 'sesso') {
$parsed['sesso'] = $this->normalizeSessoValue($value);
continue;
}
if ($key === 'data nascita') {
$parsed['data_nascita'] = $this->normalizeLegacyDateValue($value);
continue;
}
if ($key === 'luogo nascita') {
$parsed['luogo_nascita'] = $value;
continue;
}
if ($key === 'provincia nascita') {
$parsed['provincia_nascita'] = $this->normalizeProvince($value);
}
}
}
return $parsed + [
'note_clean' => $cleanLines === [] ? null : implode("\n", $cleanLines),
];
}
private function normalizeLegacyDateValue(mixed $value): ?string
{
$raw = trim((string) ($value ?? ''));
if ($raw === '') {
return null;
}
foreach (['Y-m-d', 'd/m/Y', 'd-m-Y', 'm/d/Y', 'd.m.Y', 'd/m/y', 'd-m-y', 'm/d/y', 'd.m.y'] as $format) {
$dt = \DateTime::createFromFormat($format, $raw);
if ($dt instanceof \DateTime) {
return $this->normalizeBirthDateYear($dt);
}
}
$timestamp = strtotime($raw);
if ($timestamp === false) {
return null;
}
return $this->normalizeBirthDateYear((new \DateTime())->setTimestamp($timestamp));
}
private function normalizeBirthDateYear(\DateTime $date): ?string
{
$year = (int) $date->format('Y');
$currentYear = (int) now()->format('Y');
if ($year > $currentYear + 1) {
$date->modify('-100 years');
$year = (int) $date->format('Y');
}
if ($year < 1900 || $year > $currentYear + 1) {
return null;
}
return $date->format('Y-m-d');
}
private function normalizeSessoValue(mixed $value): ?string
{
$raw = strtoupper(trim((string) ($value ?? '')));
return match (true) {
$raw === 'M', str_starts_with($raw, 'MAS') => 'M',
$raw === 'F', str_starts_with($raw, 'FEM') => 'F',
default => null,
};
}
private function normalizeProvince(mixed $value): ?string
{
$raw = strtoupper(trim((string) ($value ?? '')));
if ($raw === '') {
return null;
}
return mb_substr($raw, 0, 2);
}
private function resolveTitoloId(string $value): ?int
{
if (! Schema::hasTable('titoli')) {
return null;
}
$key = mb_strtolower(trim($value));
if ($key === '') {
return null;
}
$titolo = Titolo::query()
->whereRaw('LOWER(sigla) = ?', [$key])
->orWhereRaw('LOWER(nome) = ?', [$key])
->first();
return $titolo?->id ? (int) $titolo->id : null;
}
}