option('threshold') ?? config('newsroom.merge.collection_threshold'))); $groups = $classifier->classifyAll($threshold); $counts = array_fill_keys(array_map(static fn (CompanyAddressClass $c): string => $c->value, CompanyAddressClass::cases()), 0); foreach ($groups as $group) { $counts[$group['klasse']->value]++; } if ($this->option('dry-run')) { $this->info(sprintf('[Dry-Run] %d Adressen würden klassifiziert (Schwellwert %d).', count($groups), $threshold)); $this->renderCounts($counts); return self::SUCCESS; } $now = now(); $manual = CompanyEmailGroup::query() ->whereNotNull('manual_classification') ->pluck('manual_classification', 'email'); DB::transaction(function () use ($groups, $threshold, $now): void { $seen = []; foreach (array_chunk($groups, 500) as $chunk) { $rows = []; foreach ($chunk as $group) { $seen[] = $group['email']; $rows[] = [ 'email' => $group['email'], 'classification' => $group['klasse']->value, 'companies_count' => $group['firmen'], 'distinct_names' => $group['namen_normalisiert'], 'published_companies_count' => $group['firmen_mit_pm'], 'threshold' => $threshold, 'classified_at' => $now, 'created_at' => $now, 'updated_at' => $now, ]; } // manual_classification steht bewusst NICHT in der Update-Liste: // Der Lauf berechnet, der Mensch entscheidet — und die // Entscheidung darf keine Neuberechnung überschreiben. CompanyEmailGroup::query()->upsert( $rows, ['email'], ['classification', 'companies_count', 'distinct_names', 'published_companies_count', 'threshold', 'classified_at', 'updated_at'], ); } // Adressen, unter denen inzwischen nur noch eine Firma steht, // brauchen keine Klassifizierung mehr. Von Hand gesetzte bleiben // trotzdem — die Entscheidung könnte für die nächste Firma unter // dieser Adresse wieder gelten. CompanyEmailGroup::query() ->whereNotIn('email', $seen) ->whereNull('manual_classification') ->delete(); }); $this->info(sprintf('%d Adressen klassifiziert (Schwellwert %d).', count($groups), $threshold)); $this->renderCounts($counts); if ($manual->isNotEmpty()) { $this->line(sprintf(' %d von Hand gesetzte Klassen unverändert übernommen.', $manual->count())); } return self::SUCCESS; } /** * @param array $counts */ private function renderCounts(array $counts): void { $rows = []; foreach (CompanyAddressClass::cases() as $class) { $rows[] = [$class->label(), $counts[$class->value], $class->isMergeable() ? 'ja' : 'nein']; } $this->table(['Klasse', 'Adressen', 'zusammenführbar'], $rows); } }