Commit c077027b authored by Dainis Abols's avatar Dainis Abols
Browse files

Removed import scripts, removed additional ltter search fields, changed card...

Removed import scripts, removed additional ltter search fields, changed card display, added documentation PDF

1.0.8
parent 04996833
<?php
namespace Lu\LuNames\Command;
use Lu\LuNames\Domain\Model\Person;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class ImportCommand extends Command
{
/**
* Configure the command by defining the name, options and arguments
*/
protected function configure()
{
$this->setDescription('Imports data from xlsx.')
->addArgument('xlsxFile', InputArgument::REQUIRED, 'Xlsx file.');
}
/**
* Executes the command for import
*
* @param InputInterface $input
* @param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
Bootstrap::initializeBackendAuthentication();
Bootstrap::initializeBackendUser();
$config = require '/home/portal/typo3/typo3conf/LocalConfiguration.php';
$inputFile = $input->getArgument('xlsxFile');
$storagePid = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('lu_names')['storage_page_id'];
$personModel = GeneralUtility::makeInstance(Person::class);
$db = new \PDO('mysql:host='.$config['DB']['Connections']['Default']['host'].';dbname='.$config['DB']['Connections']['Default']['dbname'], $config['DB']['Connections']['Default']['user'], $config['DB']['Connections']['Default']['password']);
$reader = new Xlsx();
$reader->setReadDataOnly(true);
$reader->setReadEmptyCells(false);
$spreadsheet = $reader->load($inputFile);
$rawData = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
// Remove title row
array_slice(array_shift($rawData), 0, 21);
$formattedData = [];
// Get only necessary columns
foreach ($rawData as $row => $value) {
$formattedData[] = array_slice($value, 0, 21);
}
$values = [];
foreach ($formattedData as $key => $row) {
$occupationId = 0;
$familyStatusId = 0;
$sources = [];
$personId = 0;
$fullDeathText = '';
foreach ($row as $cell => $data) {
// Save photo id
if ($cell == 'A') {
$personId = $data;
unset($row[$cell]);
}
// Split birth/death date
if ($cell == 'H' || $cell == 'J') {
// Default values
$date = [0, 0, 0];
$temp = [];
if(ctype_digit(str_replace(" ","",$row[$cell])) || $row[$cell] === '' || empty($row[$cell])){
if (strlen($row[$cell]) != 0) {
$temp = explode(" ", preg_replace('!\s+!', ' ', trim($row[$cell])));
}
} else {
file_put_contents('error.txt', $personId.' - invalid date - '.$row[$cell].PHP_EOL, FILE_APPEND);
}
// Save full death date text
if ($cell == 'J') {
$fullDeathText = $data ?? '';
}
unset($row[$cell]);
array_splice($date, 0, count($temp), $temp);
if ($cell == 'H') {
array_splice($row, 8, 0, $date);
} else {
array_splice($row, 10, 0, $date);
}
}
// Convert gender value to expected lower case
if ($cell == 'P') {
$row[$cell] = strtolower($data);
}
// Create and link locations
if ($cell == 'I' || $cell == 'K' || $cell == 'L' || $cell == 'M' || $cell == 'N') {
$data = trim($data, ', ');
$locations = explode(', ', $data);
$locationIds = [];
foreach ($locations as $key => $location) {
$findLocation = $db->prepare(
"SELECT uid FROM tx_lunames_domain_model_location WHERE title = :locationTitle AND deleted = 0");
$findLocation->execute(['locationTitle' => $location]);
// Add new location if it doesn't exist
if ($findLocation->rowCount() == 0 && !empty($location)) {
$locationInsert = '('.$storagePid.', '.time().', '.time().', '.'0, '.'0, ?)';
$query = "INSERT INTO tx_lunames_domain_model_location (pid, tstamp, crdate, cruser_id, deleted, title) VALUES ".$locationInsert;
$addLocation = $db->prepare(
$query
);
$addLocation->execute([$location]);
// Get new record uid
$findLocation->execute();
}
if (!empty($location)) {
$result = $findLocation->fetch();
$locationIds[] = $result['uid'];
}
}
$row[$cell] = implode(',', $locationIds);
}
// Create and link occupation
if ($cell == 'O') {
$findOccupation = $db->prepare(
"SELECT uid FROM tx_lunames_domain_model_occupation WHERE title = :occupationTitle AND deleted = 0");
$findOccupation->execute(['occupationTitle' => $data]);
// Add new occupation if it doesn't exist
if ($findOccupation->rowCount() == 0 && !empty($data)) {
$occupationInsert = '('.$storagePid.', '.time().', '.time().', '.'0, '.'0, ?)';
$query = "INSERT INTO tx_lunames_domain_model_occupation (pid, tstamp, crdate, cruser_id, deleted, title) VALUES ".$occupationInsert;
$addOccupation = $db->prepare(
$query
);
$addOccupation->execute([$data]);
// Get new record uid
$findOccupation->execute();
}
if (!empty($data)) {
$occupationId = $findOccupation->fetch()['uid'];
}
unset($row[$cell]);
}
// Create and link family status
if ($cell == 'Q') {
$findFamilyStatus = $db->prepare(
"SELECT uid FROM tx_lunames_domain_model_family_status WHERE title = :familyStatus AND deleted = 0");
$findFamilyStatus->execute(['familyStatus' => $data]);
// Add new family status if it doesn't exist
if ($findFamilyStatus->rowCount() == 0 && !empty($data)) {
$familyStatusInsert = '('.$storagePid.', '.time().', '.time().', '.'0, '.'0, ?)';
$query = "INSERT INTO tx_lunames_domain_model_family_status (pid, tstamp, crdate, cruser_id, deleted, title) VALUES ".$familyStatusInsert;
$addFamilyStatus = $db->prepare(
$query
);
$addFamilyStatus->execute([$data]);
// Get new record uid
$findFamilyStatus->execute();
}
if (!empty($data)) {
$familyStatusId = $findFamilyStatus->fetch()['uid'];
}
unset($row[$cell]);
}
// Save source ids
if ($cell == 'T') {
$sources = $data;
unset($row[$cell]);
}
}
$slug = $personModel->generateSlug($personId, $row['B'], $row['E']);
$sourcesCount = empty($sources) ? '' : count(explode(' ', $sources));
$insert = [$personId, $storagePid, time(), time(), 0, 0, $occupationId, $familyStatusId, $slug, $sourcesCount, $fullDeathText];
// Add remaining values converting null to string for MySQL strict mode
array_splice($insert, 6, 0, array_map('strval', $row));
$values[] = ['sources' => $sources, 'insert' => $insert, 'personId' => $personId];
}
// Add persons
$statement = $db->prepare(
"INSERT INTO tx_lunames_domain_model_person (uid, pid, tstamp, crdate, cruser_id, deleted, family_name,
maiden_name, other_name, given_name, father, mother, place_of_birth, year_of_birth, day_of_birth, month_of_birth,
year_of_death, day_of_death, month_of_death, place_of_death, region, prewar_residence, war_residence, gender, spouse, fate,
comments, occupation, family_status, slug, source_codes, death)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$findUid = $db->prepare(
"SELECT uid FROM tx_lunames_domain_model_person WHERE uid = :uid AND deleted = 0");
foreach ($values as $key => $value) {
$findUid->execute(['uid' => $value['personId']]);
if ($findUid->rowCount() == 0) {
$statement->execute($value['insert']);
} else {
file_put_contents('not-imported.txt', $value['personId'].' - duplicate uid'.PHP_EOL, FILE_APPEND);
unset($values[$key]);
}
}
// Link sources
$statement = $db->prepare(
'INSERT INTO tx_lunames_domain_model_related_source (uid_local, uid_foreign, sorting, sorting_foreign) VALUES (?, ?, ?, ?)'
);
foreach ($values as $value) {
$sources = explode(' ', $value['sources']);
foreach ($sources as $sourceKey => $source) {
$source = (int)ltrim(rtrim($source, ']'), '[');
$sorting = $sourceKey + 1;
$insert = [$value['personId'], $source, $sorting, 0];
$statement->execute($insert);
}
}
// Link images
$resourceFactory = ResourceFactory::getInstance();
$storage = $resourceFactory->getDefaultStorage();
$folder = $storage->getFolder('/user_upload/names/');
$files = $folder->getFiles();
$fileIds = [];
// Get file uid and corresponding person id from file name
foreach ($files as $file) {
$fileIds[] = ['uid' => $file->getUid(), 'personId' => explode('_', $file->getName())[0]];
}
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->BE_USER = $GLOBALS['BE_USER'];
$dataHandler->BE_USER->user['admin'] = 1;
$dataHandler->userid = $GLOBALS['BE_USER']->user['uid'];
$dataHandler->admin = true;
$dataHandler->bypassWorkspaceRestrictions = true;
foreach ($values as $value) {
foreach ($fileIds as $fileId) {
if ($fileId['personId'] == $value['personId']) {
$fileObject = $resourceFactory->getFileObject((int)$fileId['uid']);
$contentElement = BackendUtility::getRecord(
'tx_lunames_domain_model_person',
$value['personId']
);
$newId = 'NEWrandomstr'; // Gets replaced by new record uid
$data = [];
$data['sys_file_reference'][$newId] = [
'table_local' => 'sys_file',
'uid_local' => $fileObject->getUid(),
'tablenames' => 'tx_lunames_domain_model_person',
'uid_foreign' => $contentElement['uid'],
'fieldname' => 'photo',
'pid' => $contentElement['pid'],
];
$data['tx_lunames_domain_model_person'][$contentElement['uid']] = [
'pid' => $storagePid,
'photo' => $newId
];
$dataHandler->start($data, []);
$dataHandler->process_datamap();
}
}
}
return 0;
}
}
......@@ -251,9 +251,9 @@ class SearchController extends ActionController
// Set filters
$filters = [
'maiden_name:'.$query.'*',
'given_name:'.$query.'*',
'other_name:'.$query.'*',
// 'maiden_name:'.$query.'*',
// 'given_name:'.$query.'*',
// 'other_name:'.$query.'*',
'family_name:'.$query.'*',
];
$solr->setFilters($filters);
......
......@@ -118,9 +118,9 @@ class Person extends AbstractEntity
protected ?ObjectStorage $occupation = null;
/**
* @var int
* @var string
*/
protected int $gender = 0;
protected string $gender = '';
/**
* @var string
......@@ -321,9 +321,9 @@ class Person extends AbstractEntity
}
/**
* @return int
* @return string
*/
public function getGender(): int
public function getGender(): string
{
return $this->gender;
}
......
<?php
return [
'lu_names:import' => [
'class' => \Lu\LuNames\Command\ImportCommand::class,
'schedulable' => false,
],
];
......@@ -237,26 +237,26 @@ return [
// 'default' => 0,
// ],
// ],
'region' => [
'label' => $ll.'tx_lunames_domain_model_person.region',
'config' => [
'type' => 'group',
'internal_type' => 'db',
'allowed' => 'tx_lunames_domain_model_location',
'maxitems' => 10,
'minitems' => 0,
'size' => 2,
'default' => 0,
'fieldControl' => [
'addRecord' => [
'disabled' => false,
'options' => [
'title' => $ll.'tx_lunames_domain_model_location.add',
],
],
],
],
],
// 'region' => [
// 'label' => $ll.'tx_lunames_domain_model_person.region',
// 'config' => [
// 'type' => 'group',
// 'internal_type' => 'db',
// 'allowed' => 'tx_lunames_domain_model_location',
// 'maxitems' => 10,
// 'minitems' => 0,
// 'size' => 2,
// 'default' => 0,
// 'fieldControl' => [
// 'addRecord' => [
// 'disabled' => false,
// 'options' => [
// 'title' => $ll.'tx_lunames_domain_model_location.add',
// ],
// ],
// ],
// ],
// ],
'prewar_residence' => [
'label' => $ll.'tx_lunames_domain_model_person.prewar_residence',
'config' => [
......@@ -434,7 +434,7 @@ return [
'showitem' => 'place_of_death, death',
],
'locations' => [
'showitem' => 'region, prewar_residence, war_residence',
'showitem' => 'prewar_residence, war_residence',
],
'family_status' => [
'showitem' => 'gender, family_status, spouse',
......
......@@ -14,151 +14,176 @@
</div>
</div>
<!-- Name header -->
<div class="searchResults__resultTitle" style="color: black;">
{person.familyName} {person.givenName}
</div>
<!-- Additional data -->
<div class="searchResults__resultContent">
<div class="searchResults__resultText">
<!-- Other name -->
<f:if condition="{person.otherName}">
<p>
<strong>Other name:</strong>
{person.otherName}
</p>
</f:if>
<!-- Maiden name name -->
<f:if condition="{person.maidenName}">
<p>
<strong>Maiden name:</strong>
{person.maidenName}
</p>
</f:if>
<!-- Family status -->
<f:if condition="{person.family_status}">
<p>
<strong>Family status:</strong>
{person.family_status}
</p>
</f:if>
<!-- Spouse -->
<f:if condition="{person.spouse}">
<p>
<strong>Spouse:</strong>
{person.spouse}
</p>
</f:if>
<!-- Mother -->
<f:if condition="{person.mother}">
<p>
<strong>Mother:</strong>
{person.mother}
</p>
</f:if>
<!-- Father -->
<f:if condition="{person.father}">
<p>
<strong>Father:</strong>
{person.father}
</p>
</f:if>
<!-- Place of birth -->
<f:if condition="{person.placeOfDeath}">
<p>
<strong>Place of birth:</strong>
<f:render partial="Locations" arguments="{locations: person.placeOfDeath}"/>
</p>
</f:if>
<!-- Birth date -->
<f:if condition="{birthDate}">
<p>
<strong>Born:</strong>
{birthDate}
</p>
</f:if>
<!-- Place of death -->
<f:if condition="{person.placeOfDeath}">
<p>
<strong>Place of death</strong>
<f:render partial="Locations" arguments="{locations: person.placeOfDeath}"/>
</p>
</f:if>
<!-- Death date -->
<f:if condition="{person.death}">
<p>
<strong>Death:</strong>
{person.death}
</p>
</f:if>
<!-- Region -->
<f:if condition="{person.region}">
<p>
<strong>Region:</strong>
<f:render partial="Locations" arguments="{locations: person.region, ending: '.'}"/>
</p>
</f:if>
<!-- Pre-war residence -->
<f:if condition="{person.prewarResidence}">
<p>
<strong>Pre-war residence:</strong>
<f:render partial="Locations" arguments="{locations: person.prewarResidence, ending: '.'}"/>
</p>
</f:if>
<!-- War residence -->
<f:if condition="{person.warResidence}">
<p>
<strong>War residence:</strong>
<f:render partial="Locations" arguments="{locations: person.warResidence, ending: '.'}"/>
</p>
</f:if>
<!-- Occupation -->
<f:if condition="{person.occupation}">
<p>
<strong>Occupation:</strong>
<f:render partial="Occupations" arguments="{occupations: person.occupation, ending: '.'}"/>
</p>
</f:if>
<!-- Fate in 1941-1945 fate -->
<f:if condition="{person.fate}">
<p>
<strong>Fate in 1941-1945:</strong><br/>
{person.fate}
</p>
</f:if>
<!-- Comments -->
<f:if condition="{person.comments}">
<p>
<strong>Comments:</strong><br/>
{person.comments}
</p>
</f:if>
<!-- Sources -->
<f:if condition="{person.sourceCodes}">
<p>
<strong>Sources:</strong><br/>
<f:render partial="Sources" arguments="{sources: person.sourceCodes}"/>
</p>
</f:if>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col" style="color: black">
<strong>Family name</strong>
</th>
<th scope="col" style="color: black">
<strong>{person.familyName}</strong>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Maiden name
</td>
<td>
{person.maidenName}
</td>
</tr>
<tr>
<td>
Other family name
</td>
<td>
{person.otherName}
</td>
</tr>
<tr>
<td>
Given name
</td>
<td>
{person.givenName}
</td>
</tr>
<tr>
<td>
Father
</td>
<td>
{person.father}
</td>
</tr>
<tr>
<td>
Mother
</td>
<td>
{person.mother}