Compare commits

..

No commits in common. "7984461d2c4ed0800f811b43fa487e27c6ae95f7" and "5c1db6f948fdc9215dc8c00a6c32a6b97e8b05c5" have entirely different histories.

3 changed files with 11 additions and 143 deletions

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260116104840 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX unique_quartal');
$this->addSql('CREATE UNIQUE INDEX unique_quartal ON contacts (phone_number, due_quartal) WHERE due_quartal IS NOT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('DROP INDEX unique_quartal');
$this->addSql('CREATE INDEX unique_quartal ON contacts (phone_number, due_quartal) WHERE (due_quartal IS NOT NULL)');
}
}

View File

@ -5,8 +5,6 @@ declare(strict_types=1);
namespace App\Command; namespace App\Command;
use App\Entity\Contacts; use App\Entity\Contacts;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use League\Csv\Reader; use League\Csv\Reader;
@ -35,9 +33,8 @@ final class CleanMobileCommand extends Command
private string $backendApiURL = 'https://umfragetool.ukbonn.de/api/api/participant/create-qm-befr-participant'; private string $backendApiURL = 'https://umfragetool.ukbonn.de/api/api/participant/create-qm-befr-participant';
public function __construct( public function __construct(
private EntityManagerInterface $em, private readonly EntityManagerInterface $em,
private readonly HttpClientInterface $http, private readonly HttpClientInterface $http
private readonly ManagerRegistry $doctrine,
) { ) {
parent::__construct(); parent::__construct();
} }
@ -222,8 +219,7 @@ final class CleanMobileCommand extends Command
// Create a Contact entity for DB insertion // Create a Contact entity for DB insertion
$contact = new Contacts(); $contact = new Contacts();
$contact->setPhoneNumber($row['HANDY_E164']); $contact->setPhoneNumber($row['HANDY_E164']);
$today = new \DateTime('today'); $dueDate = (new \DateTime('today'))->setTime(12, 0, 0);
$dueDate = ($today)->setTime(12, 0, 0);
$contact->setDueDate($dueDate); $contact->setDueDate($dueDate);
$contact->setContacted(false); $contact->setContacted(false);
$contact->setParsedFilename($inputPath); $contact->setParsedFilename($inputPath);
@ -231,10 +227,7 @@ final class CleanMobileCommand extends Command
$contact->setStudyId($study_id); $contact->setStudyId($study_id);
$contact->setParsedFileLinenum($rowCount + 1); $contact->setParsedFileLinenum($rowCount + 1);
$contact->setParsedFileLine($sanitiseUtf8(implode(';', $row))); $contact->setParsedFileLine($sanitiseUtf8(implode(';', $row)));
// $contact->setMsgContentType($rowCount % 2 ? 1 : 2); // alternativ lang oder kurz $contact->setMsgContentType($rowCount % 2 ? 1 : 2);
$contact->setMsgContentType(2); // nur kurzformat
$quartal = $today->format('Y') . ceil($today->format('n') / 3);
$contact->setDueQuartal((int) $quartal);
try { try {
$result = $this->http->request('POST', $this->backendApiURL . "/" . $study_id . "/" . $study_id_chain, [ $result = $this->http->request('POST', $this->backendApiURL . "/" . $study_id . "/" . $study_id_chain, [
@ -261,29 +254,16 @@ final class CleanMobileCommand extends Command
// 6⃣ Persist the valid contacts (batch insert) // 6⃣ Persist the valid contacts (batch insert)
// ------------------------------------------------------------- // -------------------------------------------------------------
if (\count($validContacts) > 0) { if (\count($validContacts) > 0) {
$batch = []; $batchSize = 100;
foreach ($validContacts as $i => $contact) { foreach ($validContacts as $i => $contact) {
$batch[] = $contact; $this->em->persist($contact);
if (count($batch) === 100) { if ((($i + 1) % $batchSize) === 0) {
$this->flushBatch($batch, $io); $this->em->flush();
$batch = []; $this->em->clear(); // free memory
} }
} }
if ($batch) { $this->em->flush();
$this->flushBatch($batch, $io); $this->em->clear();
}
// foreach ($validContacts as $i => $contact) {
// $this->em->persist($contact);
// try {
// $this->em->flush();
// } catch(UniqueConstraintViolationException $e) {
// $io->warning(['The number ', $contact->getPhoneNumber(), ' already contacted for quartal', $contact->getDueQuartal()]);
// }
// $this->em->clear();
// }
} }
// ------------------------------------------------------------- // -------------------------------------------------------------
@ -300,64 +280,4 @@ final class CleanMobileCommand extends Command
return Command::SUCCESS; return Command::SUCCESS;
} }
private function flushBatch(array $batch, $io): void
{
$this->em = $this->resetEntityManager();
foreach ($batch as $c) {
$this->em->persist($c);
}
try {
$this->em->flush(); // versucht, den kompletten Batch zu speichern
} catch (UniqueConstraintViolationException $e) {
$io->warning('Batch conflict falling back to elementwise');
// Transaction rollback (falls aktiv)
$conn = $this->em->getConnection();
if ($conn->isTransactionActive()) {
$conn->rollBack();
}
// Der EM ist jetzt *geschlossen* → resetten
if (!$this->em->isOpen()) {
$this->em = $this->resetEntityManager(); // hol dir einen frischen EM
}
// Einzelweise weiter versuchen, damit die „guten“ Zeilen nicht verloren gehen
$this->flushElementsIndividually($batch, $io);
return;
}
// Batch erfolgreich Speicher freigeben
$this->em->clear();
}
private function flushElementsIndividually(array $contacts, $io): void
{
$io->warning('flushing individually');
foreach ($contacts as $contact) {
$this->em->persist($contact);
$io->warning('flushing individually2');
try {
$this->em->flush();
$io->warning('flushing individually3');
} catch (UniqueConstraintViolationException $e) {
$io->warning(['The number ', $contact->getPhoneNumber(), ' already contacted for quartal', $contact->getDueQuartal()]);
// das duplizierte Objekt aus dem UnitofWork entfernen
$this->em->detach($contact);
$io->warning('flushing individually4');
if (!$this->em->isOpen()) {
$this->em = $this->resetEntityManager(); // hol dir einen frischen EM
}
}
// jedes Entity einzeln freigeben, sonst wächst der Speicher
$this->em->clear();
}
}
private function resetEntityManager(): EntityManagerInterface
{
return $this->doctrine->resetManager();
}
} }

View File

@ -6,11 +6,6 @@ use App\Repository\ContactsRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ContactsRepository::class)] #[ORM\Entity(repositoryClass: ContactsRepository::class)]
#[ORM\UniqueConstraint(
name: 'unique_quartal',
columns: ['phone_number', 'due_quartal'],
options: ['where' => "due_quartal IS NOT NULL"]
)]
class Contacts class Contacts
{ {
#[ORM\Id] #[ORM\Id]
@ -24,9 +19,6 @@ class Contacts
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
private ?\DateTime $due_date = null; private ?\DateTime $due_date = null;
#[ORM\Column(nullable: true)]
private ?int $due_quartal = null;
#[ORM\Column] #[ORM\Column]
private ?bool $contacted = null; private ?bool $contacted = null;
@ -192,14 +184,4 @@ class Contacts
{ {
$this->study_id_short = $study_id_short; $this->study_id_short = $study_id_short;
} }
public function getDueQuartal(): ?int
{
return $this->due_quartal;
}
public function setDueQuartal(?int $due_quartal): void
{
$this->due_quartal = $due_quartal;
}
} }