New file |
0,0 → 1,375 |
<?php |
/** |
* Exemple lancement: |
* /opt/lampp/bin/php -d memory_limit=3500M cli.php import -n |
*/ |
class Import extends Script { |
private $bdd = null; |
private $auteurs = array(); |
protected $parametres_autorises = array( |
'-n' => array(true, false, 'Nom du fichier à importer.')); |
|
protected $table_publication = null; |
protected $table_auteur = null; |
protected $table_publication_a_auteur = null; |
|
protected $id_role_auteur = null; |
|
protected $colonnes_obligatoires = array(); |
protected $colonnes_acceptees = array(); |
|
protected $cache_auteur = array(); |
protected $auteurs_a_publis = array(); |
protected $publis_a_auteurs = array(); |
|
public function __construct($script_nom, $parametres) { |
parent::__construct($script_nom, $parametres); |
$this->bdd = new Bdd(); |
} |
|
public function executer() { |
try { |
$this->initialiserScript(); |
$cmd = $this->getParametre('a'); |
$fichier = $this->getParametre('n'); |
switch ($cmd) { |
case "import" : |
$resultat = $this->charger($fichier); break; |
default : |
$this->traiterErreur('Erreur : la commande "%s" n\'existe pas!', array($cmd)); |
} |
} catch (Exception $erreur) { |
$this->traiterErreur($erreur->getMessage()); |
} |
|
// renvoi du résultat vers la sortie php |
// car le script est surtout destiné à être appelé par un web service |
echo $resultat; |
} |
|
private function initialiserScript() { |
$fichierIni = $this->getScriptChemin().'import.ini'; |
if (file_exists($fichierIni)) { |
Config::charger($fichierIni); |
} else { |
$erreur = "Veuillez configurer le projet en créant le fichier 'import.ini' ". |
"dans le dossier du module de script du projet à partir du fichier 'import.defaut.ini'."; |
throw new Exception($erreur); |
} |
|
$this->colonnes_obligatoires= Config::get('champs_obligatoires'); |
$this->colonnes_acceptees = Config::get('champs'); |
|
$tables = Config::get('tables'); |
$this->table_publication = $tables['publication']; |
$this->table_auteur = $tables['auteur']; |
$this->table_publication_a_auteur = $tables['publication_a_auteur']; |
|
$this->id_role_auteur = $tables['id_role_auteur']; |
} |
|
private function charger($fichier) { |
|
// vérification existence fichier |
if(!file_exists(Config::get('dossiertmp').$fichier)) { |
$erreur = "Le fichier ".Config::get('dossiertmp').$fichier." n'existe pas."; |
throw new Exception($erreur); |
} |
|
$pointeur = fopen(Config::get('dossiertmp').$fichier, "r"); |
// Chargement de la première colonne (qui contient les noms de colonnes à importer |
$colonnes = fgetcsv($pointeur, 0, ";"); |
|
// Vérification des colonnes obligatoires et en même temps du séparateur |
if(count($colonnes) < 2 || !array_intersect(array_keys($this->colonnes_obligatoires), $colonnes)) { |
$erreur = "Le fichier ne contient pas les colonnes obligatoires : ".implode(',', array_keys($this->colonnes_obligatoires))."\n"; |
$erreur .= "ou bien n'est pas séparé par le caractère ';' "; |
throw new Exception($erreur); |
} |
|
|
//TODO: vérifier si les deux index ci dessous ne font pas double emploi |
|
// Création d'un index des colonnes qui seront importées dans la fiche en les associant |
// au numéro de colonne dans la feuille csv |
$index_colonnes_importees = array_intersect(array_keys($this->colonnes_acceptees), $colonnes); |
$colonnes_importees = array(); |
foreach($index_colonnes_importees as $index_colonne_importee) { |
$colonnes_importees[] = $this->colonnes_acceptees[$index_colonne_importee]; |
} |
|
$index_colonnes_refusees = array_keys(array_diff($colonnes, array_keys($this->colonnes_acceptees))); |
// Création d'un index associant chaque numéro de colonne importée à son rôle |
// pour y apporter des traitement spécifiques |
$index_colonnes_importees = $colonnes; |
foreach($index_colonnes_refusees as $colonne_refusee) { |
unset($index_colonnes_importees[$colonne_refusee]); |
} |
|
$roles_a_num_colonnes = array_flip($index_colonnes_importees); |
|
$lignes = array(); |
while($ligne = fgetcsv($pointeur, 0, ";")) { |
$ligne_inseree = array(); |
foreach($ligne as $index => &$valeur) { |
if(!in_array($index, $index_colonnes_refusees)) { |
$ligne_inseree[] = $this->traiterChamp($valeur, $index_colonnes_importees[$index]); |
} |
} |
// Ajout du nom complet formaté de la publication |
$nom_complet = $this->bdd->proteger($this->formaterNomComplet($ligne, $index_colonnes_importees)); |
$ligne_inseree[] = $nom_complet; |
$lignes[] = "(".implode(",", $ligne_inseree).")"; |
|
$this->stockerAuteursPourLiaisons($ligne[$roles_a_num_colonnes['auteur']], $nom_complet); |
} |
|
// Ajout de la colonne nom complet aux champs de la requête |
$colonnes_importees[] = 'cpu_fmt_nom_complet'; |
|
// le script renvoie le nombre de publications importées |
$nb_publis_inserees = $this->insererPublications($colonnes_importees, $lignes); |
$nb_auteurs_lies = $this->insererLiensPublicationsAuteurs(); |
|
return $nb_publis_inserees; |
} |
|
private function stockerAuteursPourLiaisons($infos_auteurs, $nom_complet_publication) { |
// Découpage des différents auteurs |
$auteurs_tab = explode(',', $infos_auteurs); |
foreach($auteurs_tab as $auteur) { |
// si les infos auteurs sont un tableau dans le cache alors son id existe bien en base de données |
if(is_array($this->cache_auteur[$auteur])) { |
// ajout d'une correspondance entre le nom complet de la publi et l'auteur (pour liaison ultérieure) |
// et vice versa, ce qui permet facilement de retrouver à quel auteur est lié quelle publi dans les deux sens |
$this->auteurs_a_publi[$auteur][] = $nom_complet_publication; |
$this->publis_a_auteurs[$nom_complet_publication][] = $auteur; |
} |
} |
} |
|
protected function insererLiensPublicationsAuteurs() { |
|
$champs = array('cpuap_id_publication', 'cpuap_id_personne', 'cpuap_id_role', 'cpuap_ce_truk_type', 'cpuap_ordre', 'cpuap_ce_meta'); |
$requete = "INSERT IGNORE INTO ".$this->table_publication_a_auteur." ". |
"(".implode(",", $champs).") VALUES "; |
|
foreach($this->auteurs_a_publi as $auteur => $publis) { |
|
$id_auteur = $this->cache_auteur[$auteur]['cp_id_personne']; |
|
// Pour chaque publication importée à laquelle est liée l'auteur |
// il peut avoir un ordre différent |
foreach($publis as $publi) { |
|
// l'ordre des auteurs commence à 1, les auteurs sont stockés dans l'ordre dans publis_a_auteurs |
$ordre_auteur = array_search($auteur, $this->publis_a_auteurs[$publi]) + 1; |
|
// Le nom complet de la publication est normalement unique et donc peut servir à retrouver |
// les publications qui ont été insérées par lot (sans en récupérer l'id) |
$sous_requete_id_publi = "SELECT cpu_id_publication FROM coel_publication ". |
"WHERE cpu_fmt_nom_complet IN (".$publi.") "; |
|
$requete_insertion = $requete." (". |
" (".$sous_requete_id_publi."), ". |
$this->bdd->proteger($id_auteur).", ". |
$this->id_role_auteur.", ". |
"NULL ,". |
$ordre_auteur.", ". |
"NULL". |
") "; |
$insertion = $this->bdd->executer($requete_insertion); |
} |
} |
// TODO: quoi faire en ce qui concerne l'insertion des auteurs ? |
// et le nombre inséré, les éventuels échecs de la requête ? |
} |
|
protected function formaterNomComplet($ligne, $roles) { |
|
$roles = array_flip($roles); |
|
// Intitulé de la publication complet : fmt_auteur, date_parution(année). titre. Editeur (nom), collection, fascicule, indication_nvt. pages. |
// indication_nvt = serie_tome |
$champs_nom_complet = array('auteur', 'annee', 'titre', 'editeur', 'intitule_revue', 'fascicule', 'serie_tome', 'pages'); |
$champs_fmt = array(); |
|
$fmt_nom_complet = ""; |
|
foreach($champs_nom_complet as $champ) { |
$index = $roles[$champ]; |
if(!isset($ligne[$index])) { |
$champs_fmt[$champ] = ""; |
} else { |
$champs_fmt[$champ] = $ligne[$index]; |
} |
} |
|
$indication_nvt_pages = array($champs_fmt['serie_tome'], $champs_fmt['pages']); |
$indication_nvt_pages = array_filter($indication_nvt_pages, 'strlen'); |
$indication_nvt_pages = trim(implode(". ", $indication_nvt_pages)); |
$indication_nvt_pages .= !empty($indication_nvt_pages) ? "." : ""; |
|
$annee_titre_editeur = array($champs_fmt['annee'], $champs_fmt['titre'], $champs_fmt['editeur']); |
$annee_titre_editeur = array_filter($annee_titre_editeur, 'strlen'); |
$annee_titre_editeur = trim(implode(". ", $annee_titre_editeur)); |
|
$fmt_nom_complet = array($champs_fmt['auteur'], |
$annee_titre_editeur, |
$champs_fmt['intitule_revue'], |
$champs_fmt['fascicule'], |
$indication_nvt_pages); |
|
$fmt_nom_complet = array_filter($fmt_nom_complet, 'strlen'); |
$fmt_nom_complet = implode(", ", $fmt_nom_complet); |
|
return $fmt_nom_complet; |
} |
|
private function insererPublications(&$colonnes, &$publications) { |
$requete = "INSERT INTO ".$this->table_publication." ". |
"(".implode(',', $colonnes).") ". |
"VALUES ".implode(","."\n", $publications)."\n"; |
|
return $this->bdd->executer($requete); |
} |
|
private function traiterChamp($valeur, $role) { |
switch($role) { |
case 'auteur': |
$valeur = $this->traiterChampAuteur($valeur); |
break; |
case 'editeur': |
$valeur = 'AUTRE##'.$valeur; |
break; |
} |
|
return $this->bdd->proteger($valeur); |
} |
|
private function traiterChampAuteur($auteur_intitule) { |
$auteurs_fmt = array(); |
$auteurs_tab = explode(',', $auteur_intitule); |
foreach($auteurs_tab as $auteur) { |
$auteurs_fmt[] = $this->obtenirInfosAuteur($auteur); |
} |
|
return implode(', ', $auteurs_fmt); |
} |
|
private function obtenirInfosAuteur($auteur_intitule) { |
$retour = $auteur_intitule; |
// Si on déjà recherché et trouvé (ou non l'auteur) |
if(isset($this->cache_auteur[$auteur_intitule])) { |
// On renvoie son nom complet formatée s'il existe en bdd ou sinon |
// le nom qui a été donné dans la feuille csv |
$retour = !empty($this->cache_auteur[$auteur_intitule]['cp_id_personne']) ? |
$this->cache_auteur[$auteur_intitule]['cp_id_personne'] : |
$this->cache_auteur[$auteur_intitule]; |
} else { |
// Recherche de l'auteur sur un maximum du combinaisons possible des variations |
// de son nom et prénom |
$auteur = $this->fabriquerCombinaisonsAuteur($auteur_intitule); |
$auteur_req = "(".implode(', ', $auteur).")"; |
|
$requete = "SELECT cp_id_personne, cp_fmt_nom_complet FROM ".$this->table_auteur." WHERE cp_fmt_nom_complet IN ".$auteur_req; |
$infos_auteur = $this->bdd->recupererTous($requete); |
|
// Stockage en cache de la ligne de bdd si trouvé, |
// de la valeur brute sinon |
if(!empty($infos_auteur)) { |
$retour = $infos_auteur[0]['cp_fmt_nom_complet']; |
$this->cache_auteur[$auteur_intitule] = $infos_auteur[0]; |
} else { |
$retour = $auteur_intitule; |
$this->cache_auteur[$auteur_intitule] = $auteur_intitule; |
} |
} |
|
return $retour; |
} |
|
private function fabriquerCombinaisonsAuteur($auteur) { |
|
$auteur = trim($auteur); |
$auteurs_formates = array($this->bdd->proteger($auteur)); |
|
// Séparation des élements de l'auteur des espaces pour obtenir |
// les noms et prénoms (Ex. "Carl Von Linné" => "Carl", "Von", "Linné") |
$auteur_tab = explode(' ', $auteur); |
$auteur_tab = array_filter($auteur_tab); |
|
$combinaisons = array(); |
foreach($auteur_tab as &$element_auteur) { |
$combinaisons[] = array( |
// élement tel quel (Ex: "Linné") |
$element_auteur, |
// possibilité de l'initiale seule (Ex: "L") |
substr($element_auteur, 0, 1), |
// possibilité de l'initiale avec un point du genre (Ex: "L.") |
substr($element_auteur, 0, 1)."." |
); |
} |
|
// Fabrication de toutes les combinaisons possibles des parties de noms ci-dessus |
// pour deux et trois élements en faisant plusieurs produits cartésiens |
// car le nom peut être combiné différement de celui en base de données |
// (Ex. : "Carl Von Linné" VS "C. Von Linné" ou "C. V. Linné" ou encore "Von Linné C." etc...) |
|
// C'est moche et pas très élégant mais bon ça marche dans 90% des cas |
// On ne teste pas plus que 3 combinaisons, car ça serait très très couteux |
// TODO: faire mieux et factoriser les appels ci dessous |
if(count($auteur_tab) > 2) { |
// Cas du nom à trois parties (genre "Carl Von Linné") |
$possibilites_auteurs = $this->cartesian(array($combinaisons[0], $combinaisons[1], $combinaisons[2])); |
$possibilites_auteurs = array_merge($possibilites_auteurs, $this->cartesian(array($combinaisons[0], $combinaisons[2], $combinaisons[1]))); |
$possibilites_auteurs = array_merge($possibilites_auteurs, $this->cartesian(array($combinaisons[1], $combinaisons[2], $combinaisons[0]))); |
$possibilites_auteurs = array_merge($possibilites_auteurs, $this->cartesian(array($combinaisons[1], $combinaisons[0], $combinaisons[2]))); |
$possibilites_auteurs = array_merge($possibilites_auteurs, $this->cartesian(array($combinaisons[2], $combinaisons[1], $combinaisons[0]))); |
$possibilites_auteurs = array_merge($possibilites_auteurs, $this->cartesian(array($combinaisons[2], $combinaisons[0], $combinaisons[1]))); |
} else { |
// Cas du nom à trois parties (genre "Carl Linné") |
$possibilites_auteurs = $this->cartesian(array($combinaisons[0], $combinaisons[1])); |
$possibilites_auteurs = array_merge($possibilites_auteurs, $this->cartesian(array($combinaisons[1], $combinaisons[0]))); |
} |
|
$auteurs_formates = array(); |
foreach($possibilites_auteurs as &$possibilite) { |
$initiales = true; |
// Suppression des possibilités ne contenant que des initiales avec ou sans point |
foreach($possibilite as &$chars) { |
$initiales = $initiales && mb_strlen($chars) <= 2; |
} |
|
if(!$initiales) { |
$auteurs_formates[] = $this->bdd->proteger(implode(" ", $possibilite)); |
} |
} |
|
return $auteurs_formates; |
} |
|
// http://stackoverflow.com/questions/6311779/finding-cartesian-product-with-php-associative-arrays |
function cartesian($input) { |
// filter out empty values |
$input = array_filter($input); |
|
$result = array(array()); |
|
foreach ($input as $key => $values) { |
$append = array(); |
|
foreach($result as $product) { |
foreach($values as $item) { |
$product[$key] = $item; |
$append[] = $product; |
} |
} |
|
$result = $append; |
} |
|
return $result; |
} |
} |
?> |