<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\String\Slugger\SluggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Doctrine\ORM\EntityManagerInterface;
use Eckinox\DataToEntityBundle\Controller\DataToEntityController;
use Eckinox\CoreBundle\Service\Logger;
use App\Form\Type\DataType;
use App\Entity\Graveyard;
use App\Entity\Spouse;
use App\Entity\MemorialCard;
use App\Entity\Person;
use App\Entity\Census;
use App\Entity\Wedding;
use App\Entity\ImportData;
use App\Entity\Photo;
class DataController extends DataToEntityController
{
/**
* @var Request
*/
private $request;
/**
* @var TranslatorInterface
*/
private $translator;
/**
* @var SluggerInterface
*/
private $slugger;
/**
* @var Logger
*/
private $logger;
public function __construct(
RequestStack $requestStack,
TranslatorInterface $translator,
SluggerInterface $slugger,
Logger $logger,
EntityManagerInterface $entityManager
) {
parent::__construct($entityManager);
$this->request = $requestStack->getCurrentRequest();
$this->translator = $translator;
$this->slugger = $slugger;
$this->logger = $logger;
}
/**
* @Route("/", name="dashboard")
*/
public function dashboard(): Response
{
return $this->redirectToRoute('genaise_upload_data');
}
/**
* @Route("/data/upload", name="genaise_upload_data")
*/
public function upload(): Response
{
$form = $this->createForm(DataType::class);
$date = new \DateTime();
$data = [
'memorial_card' => MemorialCard::class,
'graveyard' => Graveyard::class,
'person' => Person::class,
'census' => Census::class
];
$form->handleRequest($this->request);
if ($form->isSubmitted() && $form->isValid()) {
foreach ($data as $dataName => $entityClass) {
$file = $form->get($dataName)->get($dataName . '_file')->getData();
if ($file !== null) {
$importData = new ImportData();
$originalFileName = sprintf('%s.%s', pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME), $file->guessExtension());
$fileName = sprintf('%s_%s.%s', $dataName, $date->format('YmdHis'), $file->guessExtension());
try {
$file->move(
$this->getParameter('import_data_directory'),
$fileName
);
$importData->setFilePath($this->getParameter('import_data_directory').$fileName);
$importData->setEntityClass($entityClass);
$this->entityManager->persist($importData);
$this->entityManager->flush();
$this->addFlash('success', $this->translator->trans(
'data.messages.file_has_been_uploaded',
[ '%fileName%' => $originalFileName ]
));
} catch (FileException $e) {
$this->addFlash('error', $this->translator->trans(
'data.messages.file_upload_failed',
[ '%fileName%' => $originalFileName ]
));
}
}
}
}
return $this->render('genaise/data/form.html.twig', [
'form' => $form->createView(),
'import_data' => $this->entityManager->getRepository(ImportData::class)->findBy([ 'executedAt' => null ])
]);
}
/**
* @Route("/cron/import-data", name="genaise_cron_import_data")
*/
public function import(): Response
{
ini_set('memory_limit', '1000M');
ini_set('max_execution_time', '0');
$this->entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
$this->logger->disableOnFlushEvent();
$this->enableDetachEntity();
$importData = $this->entityManager->getRepository(ImportData::class)->findBy([ 'executedAt' => null ]);
foreach ($importData as $import) {
$timeStart = microtime(true);
$executedAt = new \DateTime('now');
if (file_exists($import->getFilePath())) {
$data = [];
$file = fopen($import->getFilePath(), 'r');
$repository = $this->entityManager->getRepository($import->getEntityClass());
$repository->deleteAll();
while (!feof($file)) {
$row = fgetcsv($file, 0, ';');
if ($row) { $data[] = $row; }
if (count($data) >= 10000 || feof($file)) {
$this->saveToEntity($import->getEntityClass(), $data);
if ($import->getEntityClass() === Person::class) {
$this->handleWeddings($data);
} else if ($import->getEntityClass() === Graveyard::class) {
$this->handleSpouses($data);
}
$data = [];
}
}
fclose($file);
// Match photos
if ($import->getEntityClass() === Graveyard::class) {
$this->matchGraveyardsPhotos();
} else if ($import->getEntityClass() === MemorialCard::class) {
$this->matchMemorialCardsPhotos();
}
}
$import->setExecutedAt($executedAt);
$import->setExecutionTime(microtime(true) - $timeStart);
$this->entityManager->persist($import);
$this->entityManager->flush();
}
$this->logger->enableOnFlushEvent();
return new Response('done');
}
/**
* @Route("/api/rest/graveyards/names", name="genaise_api_graveyards_names")
*/
public function getGraveyardsNames(): Response
{
return $this->json([
'status' => 'success',
'data' => $this->entityManager->getRepository(Graveyard::class)->findNames()
]);
}
/**
* @Route("/api/rest/census/cities", name="genaise_api_census_cities")
*/
public function getCensusCities(): Response
{
return $this->json([
'status' => 'success',
'data' => $this->entityManager->getRepository(Census::class)->findCities()
]);
}
/**
* @Route("/api/rest/census/years", name="genaise_api_census_years")
*/
public function getCensusYears(): Response
{
return $this->json([
'status' => 'success',
'data' => $this->entityManager->getRepository(Census::class)->findYears()
]);
}
public function formatData($class, $data) {
foreach ($this->mapping[$class]['fields'] as $dataField => $entityField) {
if (strpos($entityField['name'], 'date') !== false && isset($data[$dataField])) {
$data[$dataField] = $data[$dataField] ? new \DateTime($data[$dataField]) : null;
}
if ($entityField['name'] === 'death_year') {
$data[$dataField] = $data[$dataField] ? (int) $data[$dataField] : null;
}
if ($class === Census::class && in_array($entityField['name'], [ 'number', 'home', 'family', 'line', 'year' ])) {
$data[$dataField] = $data[$dataField] ? (int) $data[$dataField] : null;
}
}
return $data;
}
private function handleWeddings(array $rows) {
$data = [];
foreach ($rows as $row) {
$weddings = [];
$rowNumber = 18;
$verifiedRowNumber = 60;
// Handle the three weddings
foreach ([ 1, 2, 3 ] as $number) {
$wedding = [
'number' => $number,
'marital_status' => $row[$rowNumber++],
'date' => $row[$rowNumber++],
'place' => $row[$rowNumber++],
'spouse_first_name' => $row[$rowNumber++],
'spouse_last_name' => $row[$rowNumber++],
'spouse_father_full_name' => $row[$rowNumber++],
'spouse_mother_full_name' => $row[$rowNumber++],
'notes' => $row[$rowNumber++],
'references' => $row[$rowNumber++],
'document' => $row[$rowNumber++],
'verified' => $row[$verifiedRowNumber++],
];
if ($wedding['spouse_first_name'] || $wedding['spouse_last_name']) {
$weddings[] = $wedding;
}
}
if ($weddings) {
$person = $this->entityManager->getRepository(Person::class)->findOneBy([
'number' => $row[0],
'usualFirstName' => $row[4],
'lastName' => $row[6]
]);
foreach ($weddings as $key => $wedding) {
$weddings[$key]['person'] = $person;
$data[] = $weddings[$key];
}
}
}
$this->saveToEntity(Wedding::class, $data);
$this->detachEntity(Wedding::class);
$data = [];
return $this;
}
private function handleSpouses(array $rows) {
$data = [];
foreach ($rows as $row) {
$spouses = [];
$rowNumber = 3;
// Handle the two spouses
foreach ([ 1, 2 ] as $number) {
$spouse = [
'number' => $number,
'last_name' => $row[$rowNumber++],
'first_name' => $row[$rowNumber++],
];
if ($spouse['first_name'] && $spouse['last_name']) {
$spouses[] = $spouse;
}
}
if ($spouses) {
$graveyard = $this->entityManager->getRepository(Graveyard::class)->findOneBy([
'number' => $row[0],
'lastName' => $row[1],
'firstName' => $row[2]
]);
foreach ($spouses as $key => $spouse) {
$spouses[$key]['graveyard'] = $graveyard;
$data[] = $spouses[$key];
}
}
}
$this->saveToEntity(Spouse::class, $data);
$this->detachEntity(Spouse::class);
$data = [];
return $this;
}
private function matchGraveyardsPhotos() {
$date = (new \DateTime('now'))->setTime(0, 0);
$photoRepository = $this->entityManager->getRepository(Photo::class);
$graveyardRepository = $this->entityManager->getRepository(Graveyard::class);
$graveyardsIds = $graveyardRepository->findIds();
$count = count($graveyardsIds);
$persistedCount = 0;
foreach ($graveyardsIds as $key => $id) {
$isLast = $key == $count - 1;
// We have to refresh the graveyard entity because it was detached
$graveyard = $graveyardRepository->find($id);
if ($graveyard->getPhotoNumber()) {
$key = sprintf('cimetieres-%s-%s', $graveyard->getGraveyardName(), $graveyard->getPhotoNumber());
$slug = strtolower($this->slugger->slug($key));
$photo = $photoRepository->findOneBy([
'slug' => $slug,
'number' => $graveyard->getPhotoNumber()
]);
if ($photo) {
$graveyard->addPhoto($photo);
$this->entityManager->persist($graveyard);
$persistedCount++;
}
}
if ($persistedCount >= 200 || $isLast) {
$this->entityManager->flush();
$this->detachEntity(Graveyard::class);
$persistedCount = 0;
}
}
return $this;
}
private function matchMemorialCardsPhotos() {
$date = (new \DateTime('now'))->setTime(0, 0);
$photoRepository = $this->entityManager->getRepository(Photo::class);
$memorialCardRepository = $this->entityManager->getRepository(MemorialCard::class);
$memorialCardsIds = $memorialCardRepository->findIds();
$count = count($memorialCardsIds);
$persistedCount = 0;
foreach ($memorialCardsIds as $key => $id) {
$isLast = $key == $count - 1;
// We have to refresh the memorial card entity because it was detached
$memorialCard = $memorialCardRepository->find($id);
if ($memorialCard->getNumber()) {
$key = sprintf('cartes-mortuaires-%s-', $memorialCard->getNumber());
$slug = strtolower($this->slugger->slug($key));
$photo = $photoRepository->findOneBy([
'slug' => $slug,
'number' => $memorialCard->getNumber()
]);
if ($photo) {
$memorialCard->addPhoto($photo);
$this->entityManager->persist($memorialCard);
$persistedCount++;
}
}
if ($persistedCount >= 200 || $isLast) {
$this->entityManager->flush();
$this->detachEntity(MemorialCard::class);
$persistedCount = 0;
}
}
return $this;
}
}