diff --git a/VERSIONS_HOTLINE/149668b b/VERSIONS_HOTLINE/149668b new file mode 100644 index 0000000000000000000000000000000000000000..29545edbd4c294ec5f238c6a318161fad470bff7 --- /dev/null +++ b/VERSIONS_HOTLINE/149668b @@ -0,0 +1 @@ + - correctif #149668 : Intégrations : Amélioration des performances de l'indexation des domaines \ No newline at end of file diff --git a/cosmogramme/php/integration/domaines.php b/cosmogramme/php/integration/domaines.php deleted file mode 100644 index 90f52996aed298e61cd9d604aba43c28eb069e49..0000000000000000000000000000000000000000 --- a/cosmogramme/php/integration/domaines.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. - * - * BOKEH is free software; you can redistribute it and/or modify - * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by - * the Free Software Foundation. - * - * There are special exceptions to the terms and conditions of the AGPL as it - * is applied to this software (see README file). - * - * BOKEH is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE - * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -setVariable('traitement_phase', 'Indexation des domaines'); - -if ($phase==16) { - $log->log('<h4>Indexation des domaines</h4>'); - unset($phase_data); - $reprise = false; - $phase_data['nombre'] = 0; - $phase_data['nb_fic'] = 0; - $phase_data['timeStart'] = time(); - $phase_data['pointeur'] = 0; - $phase_data['domaine'] = 0; - - $phase=17; -} - -if ($phase==17) { - if ($mode_cron) { - $position_domaine = $phase_data['domaine']; - $catalogues = array_slice(Class_Catalogue::findAllCataloguesAIndexer(), - $position_domaine); - - foreach ($catalogues as $catalogue) { - $page = $phase_data['pointeur']; - $log->log('Indexation du domaine : '.$catalogue->getLibelle().'<br/>'); - $catalogue->index($page); - - $position_domaine++; - $phase_data['domaine'] = $position_domaine; - $phase_data['pointeur'] = 0; - } - - $log->log('<h4>Indexation des paniers dans les domaines</h4>'); - Class_PanierNotice::indexAll(); - - $log->log('<h4>Indexation des articles dans les domaines</h4>'); - Class_Article::indexAll(); - - $log->log('<h4>Indexation des sitothèques dans les domaines</h4>'); - Class_Sitotheque::indexAll(); - } else { - $log->log('<h4>Les indexations des domaines, des paniers, articles et sitothèques dans les domaines ne sont traitées qu\'en mode cron</h4>'); - } -} \ No newline at end of file diff --git a/cosmogramme/php/integre_traite_main.php b/cosmogramme/php/integre_traite_main.php index 62371a614e12e8a7da4a32b7edf4848d2566ae65..fb7c08a8c2e852a9c0d9020bff6c77177f675fee 100644 --- a/cosmogramme/php/integre_traite_main.php +++ b/cosmogramme/php/integre_traite_main.php @@ -303,8 +303,9 @@ if ($phase==14 or $phase==15) { // ---------------------------------------------------------------- // Indexation Domaines // ---------------------------------------------------------------- -if ($phase==16 or $phase==17) { - include("integration/domaines.php"); +if ($phase==16) { + startIntegrationPhase('Domains'); + $phase = 17; } @@ -368,108 +369,6 @@ $log_debug->close(); print(BR . BR . '</body></html>'); -// ---------------------------------------------------------------- -// Ecriture logs et affichage écran -// ---------------------------------------------------------------- -function traceTraitementNotice() -{ - global $log, $log_debug; - global $notice, $compteur, $phase_data, $nb_notices, $chrono100notices, $debug_level; - global $nom_bib, $libelle_type_operation, $ret; - - // Recup du statut - $statut = $notice->getLastStatut(); - $code_statut = $statut["statut"]; - - // Maj des compteurs - $compteur[$code_statut]++; - $compteur["nb_notices"]++; - - // logs - if ($code_statut == 0) - { - //incrementeVariable("traitement_erreurs"); - $phase_data["nb_erreurs"]++; - $phase_data["erreurs"][$statut["erreur"]][] = $ret["adresse"]; - if ($debug_level == 1 or $debug_level == 2) $log->log('<span class="rouge">Notice n° ' . $nb_notices . ' - ' . $statut["erreur"] . '</span>' . BR); - } - if (count($statut["warnings"]) > 0) - { - //incrementeVariable("traitement_warnings"); - if ($debug_level == 2) $log->log('<span class="num_notice">notice n° ' . $nb_notices . '</span>' . BR); - foreach ($statut["warnings"] as $warning) - { - if ($debug_level == 2) $log->log('<font color="purple">Anomalie : ' . $warning[0] . " » " . $warning[1] . '</font>' . BR); - if ($warning[0] == "Genre non reconnu" or $warning[0] == "Section non reconnue") continue; - $phase_data["nb_warnings"]++; - $phase_data["warnings"][$warning[0]][] = $ret["adresse"] . chr(9) . $warning[1]; - } - } - - // Affichage toutes les 100 notices - if ($nb_notices % 100 == 0) - { - $log->log("notice $nb_notices (" . $chrono100notices->tempsPasse() . " secondes)<br>"); - $chrono100notices->start(); - } - // Debug level a 1 on affiche le mode d'identificationde la notice - if ($debug_level > 1) - { - if ($nb_notices == 1) print(BR); - $log->log('<span class="num_notice">Notice n° ' . $nb_notices . '</span>' . BR); - $log->log('<span>Mode d\'identification ---> ' . $statut["identification"] . '</span>' . BR); - } - // Tout afficher si debug_level maxi - if ($debug_level > 2) - { - $detail = $notice->getNotice(); - $log->log('<div style=width:700px;margin-left:15px;margin-bottom:10px;">'); - $log->log('<table class="blank" cellspacing="0" cellpadding="5px">'); - // Statut de retour - $log->log('<tr><td class="blank">Traitement</td>'); - if ($statut["erreur"]) $erreur = '<span class="rouge"> - ' . $statut["erreur"] . '</span>'; else $erreur=""; - $log->log('<td class="blank">' . $notice->libStatut[$code_statut] . $erreur . '</td></tr>'); - // warnings - $log->log('<tr><td class="blank" style="vertical-align:top">Anomalies</td>'); - if (count($statut["warnings"]) > 0) - { - $log->log('<td class="blank"><font color="purple">'); - foreach ($statut["warnings"] as $warning) - { - $log->log($warning[0] . " » " . $warning[1] . BR); - } - $log->log('</font></td></tr>'); - } - else $log->log('<td class="blank">aucune</td></tr>'); - // Détail de l'enreg - foreach ($detail as $clef => $data) - { - if ($clef == "exemplaires") - { - for ($i = 0; $i < count($data); $i++) - { - $log->log('<tr><td class="blank" style="vertical-align:top">Exemplaire ' . ($i + 1) . '</td>'); - $log->log('<td class="blank">'); - foreach ($data[$i] as $key => $valeur) $log->log($key . " = " . $valeur . BR); - $log->log('</td></tr>'); - } - continue; - } elseif ($clef == "warnings") continue; - elseif (gettype($data) == "array") - { - $aff = ""; - foreach ($data as $key => $valeur) $aff.=$key . " = " . $valeur . BR; - $data = $aff; - } - $log->log('<tr><td class="blank" style="vertical-align:top">' . $clef . '</td>'); - if ($data == "") $data = " "; - $log->log('<td class="blank">' . $data . '</td></tr>'); - } - $log->log("</table></div>"); - } -} - - // ---------------------------------------------------------------- // Gestion du contexte pour les timeout diff --git a/library/Class/Catalogue.php b/library/Class/Catalogue.php index 1be530d74be49de77dbf8242c18277016f964dba..002bd88085c9b8e81566e50c9f3c33ac34e80669 100644 --- a/library/Class/Catalogue.php +++ b/library/Class/Catalogue.php @@ -168,29 +168,29 @@ class Class_Catalogue extends Storm_Model_Abstract { } - public function updateNoticesWithFacette($nb_par_page,$page) { - if (!$ids=$this->getAllNoticeIdsForDomaine($nb_par_page,$page)) + public function updateNoticesWithFacette($nb_par_page, $page) { + if (!$ids = $this->getAllNoticeIdsForDomaine($nb_par_page, $page)) return false; - $sql = Zend_Registry::get('sql'); - $thesaurus=Class_CodifThesaurus::findThesaurusForCatalogue($this->getId()); - if (!$thesaurus) + if (!$thesaurus = Class_CodifThesaurus::findThesaurusForCatalogue($this->getId())) { $this->getLoader()->saveThesaurus($this); - if ($thesaurus) { - if ($page==0) $this->getLoader()->deleteThesaurusInFacette($thesaurus->getIdThesaurus()); + return false; + } - $sql->query('update notices set facettes=concat(facettes," H'.$thesaurus->getIdThesaurus().'") where id_notice in ('.implode(',',$ids).')'); - return true; + if ($page == 0) + $this->getLoader()->deleteThesaurusInFacette($thesaurus); - } - return false; + Zend_Registry::get('sql') + ->query('update notices set facettes=concat(facettes," H'.$thesaurus->getIdThesaurus().'") where id_notice in ('.implode(',',$ids).')'); + + return true; } public function afterDelete() { if ($thesaurus = Class_CodifThesaurus::findThesaurusForCatalogue($this->getId())) { $thesaurus->delete(); - $this->getLoader()->deleteThesaurusInFacette($thesaurus->getIdThesaurus()); + $this->getLoader()->deleteThesaurusInFacette($thesaurus); } } diff --git a/library/Class/Catalogue/Loader.php b/library/Class/Catalogue/Loader.php index da11bb0b44e40dde815d114b193829604583cb6e..aa947e2171ad2ed616b361276c7d55174bdf9966 100644 --- a/library/Class/Catalogue/Loader.php +++ b/library/Class/Catalogue/Loader.php @@ -610,7 +610,7 @@ class Class_Catalogue_Loader extends Storm_Model_Loader { public function saveThesaurus($catalogue) { if ($thesaurus = Class_CodifThesaurus::findThesaurusForCatalogue($catalogue->getId())) { - Class_Catalogue::deleteThesaurusInFacette($thesaurus->getIdThesaurus()); + Class_Catalogue::deleteThesaurusInFacette($thesaurus); $catalogue->updateThesaurusLabel($thesaurus); } else if (!$thesaurus = $catalogue->saveThesauriParents()) return; @@ -731,21 +731,41 @@ class Class_Catalogue_Loader extends Storm_Model_Loader { } - public function deleteThesaurusInFacette($id_thesaurus) : int { - /* - * Sites, articles and RSS records are first destroyed and then get their own selection of domains. - * So therauri facettes must not be deleted. By the way, need to find a nicer implementation ... - */ - $query = - 'update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bH' - . $id_thesaurus . '\\\\b", "")) '. - 'where notices.type_doc not in (\'' - . implode('\',\'', - [Class_TypeDoc::ARTICLE, Class_TypeDoc::RSS, Class_TypeDoc::SITE]) - . '\') ' . 'and match(facettes) against("+H' - . $id_thesaurus . '" in boolean mode)'; + public function deleteThesaurusInFacette(Class_CodifThesaurus $thesaurus) : int { + $where = implode(' and ', + [$this->_notInPseudoRecords(), + 'match(facettes) against("+' . $thesaurus->getFacetCode() . '" in boolean mode)']); - return Zend_Registry::get('sql')->query($query); + return Class_CodifThesaurus::deleteFacetsInRecordsForThesaurus($thesaurus, $where); } + + public function clearThesaurusFacets() : int { + $thesaurus = Class_CodifThesaurus::findFixed('Domain')->getThesaurus(); + return Class_CodifThesaurus::deleteFacetsInRecordsForThesaurusTree($thesaurus, + $this->_notInPseudoRecords()); + } + + + public function clearDomainFacets() : int { + $pattern = '\\\\b' . Class_Catalogue::CODE_FACETTE . '[a-zA-Z0-9]*\\\\b'; + + return Class_Notice::deleteFacetsInRecordsByPattern($pattern, + $this->_notInPseudoRecords() + . ' and match(facettes) against("+' . Class_Catalogue::CODE_FACETTE . '*" in boolean mode)'); + } + + + + /** + * Class_Sitotheque, Class_Article and Class_Rss can have their own selection of domains. + * Their corresponding Class_Notice will have a mix of manual selection of domains and + * domains matching by their criterias in their facets. + * So we cannot blindly delete. + * + * @see Class_Indexation_PseudoNotice::_getFacettes() + */ + protected function _notInPseudoRecords() : string { + return 'type_doc not in (' . Class_Notice::pseudoRecordsTypes() .') and type=' . Class_Notice::TYPE_BIBLIOGRAPHIC; + } } diff --git a/library/Class/CodifThesaurus.php b/library/Class/CodifThesaurus.php index 114294f7892dc40c76b83cd857d89a74be802543..612e5d4983e0b9a21b8649d8acaeac17081d7c30 100644 --- a/library/Class/CodifThesaurus.php +++ b/library/Class/CodifThesaurus.php @@ -80,10 +80,8 @@ class CodifThesaurusLoader extends Storm_Model_Loader { } - public function ensureForModelUnderRoot($model, $definition) { - return ($root = Class_CodifThesaurus::findRootOfId($definition->getId(), - $definition->getCode(), - $definition->getLabel())) + public function ensureForModelUnderRoot($model, Class_CodifThesaurusFixed $definition) { + return ($root = $definition->getThesaurus()) ? $root->getOrCreateChild($model->getId(), $model->getLibelle()) : null; } @@ -289,8 +287,20 @@ class CodifThesaurusLoader extends Storm_Model_Loader { } - public function deleteFacetsInRecordsForThesaurusTree(Class_CodifThesaurus $thesaurus) : int { - return Zend_Registry::get('sql')->query(sprintf('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bH%s\\\\d*\\\\b", ""))', $thesaurus->getIdThesaurus())); + public function deleteFacetsInRecordsForThesaurusTree(Class_CodifThesaurus $thesaurus, string $where='') : int { + return $this->_deleteFacetsInRecordsByPattern('\\\\b' . $thesaurus->getFacetCode() . '\\\\d*\\\\b', + $where); + } + + + public function deleteFacetsInRecordsForThesaurus(Class_CodifThesaurus $thesaurus, string $where='') : int { + return $this->_deleteFacetsInRecordsByPattern('\\\\b' . $thesaurus->getFacetCode() . '\\\\b', + $where); + } + + + protected function _deleteFacetsInRecordsByPattern(string $pattern, string $where='') : int { + return Class_Notice::deleteFacetsInRecordsByPattern($pattern, $where); } diff --git a/library/Class/CodifThesaurusFixed.php b/library/Class/CodifThesaurusFixed.php index b69d58a838145db7fa11f473f6c2de7ea0f64941..4d8a26828b346bb91ef3fe416d804ea286f5195e 100644 --- a/library/Class/CodifThesaurusFixed.php +++ b/library/Class/CodifThesaurusFixed.php @@ -20,7 +20,7 @@ */ -class Class_CodifThesaurusFixed extends Class_Entity { +class Class_CodifThesaurusFixed { const CODE_DOMAIN_FACET = 'DOMAIN', CODE_UNIMARC_FACET = 'UNIMARC'; @@ -39,7 +39,12 @@ class Class_CodifThesaurusFixed extends Class_Entity { ]; - public static function getAll() { + protected string $_id; + protected string $_code; + protected string $_label; + + + public static function getAll() : array { $all = []; foreach(static::$_known as $k => $v) $all[$k] = static::newWith($v); @@ -48,7 +53,7 @@ class Class_CodifThesaurusFixed extends Class_Entity { } - public static function isFixedValue($value) { + public static function isFixedValue($value) : bool { if (!$value || Class_CodifThesaurus::CODE_FACETTE !== substr($value, 0, 1) || 1 !== (strlen($value) % Class_CodifThesaurus::ID_KEY_LENGTH)) @@ -75,10 +80,34 @@ class Class_CodifThesaurusFixed extends Class_Entity { } - public static function newWith($params) { - return (new Class_CodifThesaurusFixed()) - ->setId($params[0]) - ->setCode($params[1]) - ->setLabel($params[2]); + public static function newWith(array $params) : self { + return new static($params[0], $params[1], $params[2]); + } + + + public function __construct(string $id, string $code, string $label) { + $this->_id = $id; + $this->_code = $code; + $this->_label = $label; + } + + + public function getId() : string { + return $this->_id; + } + + + public function getCode() : string { + return $this->_code; + } + + + public function getLabel() : string { + return $this->_label; + } + + + public function getThesaurus() : Class_CodifThesaurus { + return Class_CodifThesaurus::findRootOfId($this->_id, $this->_code, $this->_label); } } diff --git a/library/Class/Cosmogramme/Integration/PhaseAbstract.php b/library/Class/Cosmogramme/Integration/PhaseAbstract.php index 28ce7718c7ea760006fd2d53c207cc96096a8ac8..0928e7c2a1dcae65221e44d461f224a29ce88d2a 100644 --- a/library/Class/Cosmogramme/Integration/PhaseAbstract.php +++ b/library/Class/Cosmogramme/Integration/PhaseAbstract.php @@ -191,25 +191,29 @@ abstract class Class_Cosmogramme_Integration_PhaseAbstract { } - protected function _logMessage($message) { - if ($this->_log) - $this->_log->log($message); + protected function _logMessage(string $message) : self { + return $this->_withLogDo(fn($log) => $log->log($message)); + } - return $this; + + protected function _logInfo(string $message) : self { + return $this->_withLogDo(fn($log) => $log->info($message)); } - protected function _logSuccess($message) { - if ($this->_log) - $this->_log->success($message); + protected function _logSuccess(string $message) : self { + return $this->_withLogDo(fn($log) => $log->success($message)); + } - return $this; + + protected function _logError(string $message) : self { + return $this->_withLogDo(fn($log) => $log->error($message)); } - protected function _logError($message) { + protected function _withLogDo(Closure $callback) : self { if ($this->_log) - $this->_log->error($message); + $callback($this->_log); return $this; } diff --git a/library/Class/Cosmogramme/Integration/PhaseDomains.php b/library/Class/Cosmogramme/Integration/PhaseDomains.php new file mode 100644 index 0000000000000000000000000000000000000000..d04b64cb2649166b8aa9a2d5cde053e2bf606432 --- /dev/null +++ b/library/Class/Cosmogramme/Integration/PhaseDomains.php @@ -0,0 +1,232 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Cosmogramme_Integration_PhaseDomains + extends Class_Cosmogramme_Integration_PhaseAbstract { + const MY_ID = 17; + + protected int $_indexed_count; + + public function __construct($phase, $log, $chrono) { + parent::__construct($phase, $log, $chrono); + $this->_label = $this->_('Indexation des domaines'); + } + + + protected function _init($phase) { + $phase->resetDatas(); + return $this; + } + + + protected function _execute() { + if (!$this->_phase->isCron()) { + $this->_logInfo($this->_('Ce traitement ne se lance qu\'en mode cron.')); + return; + } + + $this + ->_handleDomains() + ->_handlePaniers(); + } + + + protected function _handleDomains() : self { + $this->_chrono->startOnFile(); + $clear_count = Class_Catalogue::clearThesaurusFacets(); + $this->_logInfo($this->_plural($clear_count, + 'Aucune notices à désindexer', + '1 notice désindexée', + '%d notices désindexées', + $clear_count)); + + $this->_indexed_count = 0; + foreach (Class_Catalogue::findAllCataloguesAIndexer() as $domain) + $this->_runOneDomain($domain); + + $this->_summarize(); + + return $this; + } + + + protected function _runOneDomain(Class_Catalogue $domain) : self { + $this->_logInfo($this->_('Indexation du domaine : %s', $domain->getLibelle())); + + if ((!$thesaurus = Class_CodifThesaurus::findThesaurusForCatalogue($domain->getId())) + && (!$thesaurus = Class_Catalogue::saveThesaurus($domain))) { + $this->_logError($this->_('Impossible de créer le thesaurus pour ce domaine')); + return $this; + } + + if (!$where = Class_Catalogue::getQueryConditionsForDomain($domain)) { + $this->_logInfo($this->_('Impossible d\'indexer un domaine sans critère')); + return $this; + } + + $indexed_count = Zend_Registry::get('sql') + ->query(sprintf('update notices set facettes=concat(facettes, " %s") %s', + $thesaurus->getFacetCode(), + trim($where))); + + $this->_logInfo($this->_plural($indexed_count, + 'Aucune notices à indexer', + '1 notice indexée', + '%d notices indexées', + $indexed_count)); + $this->_indexed_count += $indexed_count; + + return $this; + } + + + protected function _summarize() : self { + $this->_logSuccess($this->_plural($this->_indexed_count, + 'Aucune notices traitée', + '1 notice traitée', + '%d notices traitées', + $this->_indexed_count)); + if ($this->_indexed_count) + $this->_logSuccess($this->_('Temps de traitement %s (%s)', + $this->_chrono->endFile(), + $this->_chrono->meanOnFile($this->_indexed_count, + $this->_('notices')))); + + return $this; + } + + + protected function _handlePaniers() : self { + $this->_chrono->startOnFile(); + + $this->_logMessage('<h4>' . $this->_('Indexation des paniers dans les domaines') . '</h4>'); + + $clear_count = Class_NoticeDomain::clearNotPseudoRecordsWithPanier(); + $this->_logInfo($this->_plural($clear_count, + 'Aucun lien notice/domaine à supprimer', + '1 lien notice/domaine supprimé', + '%d liens notice/domaine supprimés', + $clear_count)); + + $clear_count = Class_Catalogue::clearDomainFacets(); + $this->_logInfo($this->_plural($clear_count, + 'Aucune notices à désindexer', + '1 notice désindexée', + '%d notices désindexées', + $clear_count)); + + $this->_indexed_count = 0; + foreach (Class_PanierNotice::findAllWithCatalogue() as $panier) + $this->_runOnePanier($panier); + + $this->_summarize(); + + return $this; + } + + + protected function _runOnePanier(Class_PanierNotice $panier) : self { + $this->_logInfo($this->_('Indexation du panier : %s', $panier->getLibelle())); + + if (!$linked_domains = $panier->rawLinkedDomains()) { + $this->_logError($this->_('Impossible d\'indexer un panier qui n\'est pas rattaché à au moins un domaine')); + return $this; + } + + if (!$keys = $panier->getClesNotices()) { + $this->_logInfo($this->_('Inutile d\'indexer un panier vide')); + return $this; + } + + $domains_id = array_map(fn($row) => $row['id_catalogue'], + $linked_domains); + + return $this + ->_runOnePanierNoticeDomainLinks($panier, $domains_id, $keys) + ->_runOnePanierDomainFacets($linked_domains, $keys); + } + + + protected function _runOnePanierNoticeDomainLinks(Class_PanierNotice $panier, array $domains_id, array $keys) : self { + $inserted_count = Class_NoticeDomain::createAllForPanier($panier, $domains_id, $keys); + + $this->_logInfo($this->_plural($inserted_count, + 'Aucun lien notice/domaine créé', + '1 lien notice/domaine créé', + '%d liens notice/domaine créés', + $inserted_count)); + + return $this; + } + + + protected function _runOnePanierDomainFacets(array $linked_domains, array $keys) : self { + $facets = new Storm_Collection(array_map(fn($row) => Class_Catalogue::CODE_FACETTE.$row['id_catalogue'], + $linked_domains)); + + $facets = $this->_addDomainThesaurusFacetTo($linked_domains, $facets); + + $where = 'clef_alpha in (' + . implode(', ', + array_map(fn($key) => '"' . $key . '"', + $keys)) + . ')'; + + $indexed_count = Zend_Registry::get('sql') + ->query(sprintf('update notices set facettes=concat(facettes, " %s") where %s', + implode(' ', $facets->getArrayCopy()), + $where)); + + $this->_logInfo($this->_plural($indexed_count, + 'Aucune notices à indexer', + '1 notice indexée', + '%d notices indexées', + $indexed_count)); + + $this->_indexed_count += $indexed_count; + + return $this; + } + + + protected function _addDomainThesaurusFacetTo(array $linked_domains, Storm_Collection $facets) : Storm_Collection { + $with_thesaurus = array_map(fn($row) => $row['id_catalogue'], + array_filter($linked_domains, + fn($row) => 1 == $row['indexer'])); + if (!$with_thesaurus) + return $facets; + + $query = 'select id_thesaurus from codif_thesaurus where id_thesaurus like "CCCC%" and length(id_thesaurus)=8 and id_origine in (' + . implode(', ', + array_map(fn($id) => '"' . $id . '"', + $with_thesaurus)) + . ')'; + + if (!$ids = Zend_Registry::get('sql')->fetchAllByColumn($query)) + return $facets; + + $facets->addAll(array_map(fn($id) => Class_CodifThesaurus::CODE_FACETTE.$id, + $ids)); + + return $facets; + } +} diff --git a/library/Class/Indexation/Model/WithManyDomains.php b/library/Class/Indexation/Model/WithManyDomains.php index 3f8079f8eda1dbdb15e00b45e934985b34b305a4..d1dbe203443aeda7eb341ad36ada0127f7b4cb2c 100644 --- a/library/Class/Indexation/Model/WithManyDomains.php +++ b/library/Class/Indexation/Model/WithManyDomains.php @@ -27,54 +27,74 @@ class Class_Indexation_Model_WithManyDomains { $this->_model = $model; } - public function index() { - if (!$alpha_key = $this->_model->getAlphaKey()) - return $this; - $domains_ids = Class_Catalogue::getIds($this->_model->getDomaines()); - $existing = Class_NoticeDomain::findAllBy(['record_alpha_key' => $alpha_key]); - $existing_domains = array_filter(array_map(function ($item) { - return $item->getDomain(); - }, $existing)); + public function index() : self { + if (!$alpha_key = $this->_getAlphaKey()) + return $this; - $existing_ids = array_map(function ($item) { return $item->getId();}, $existing_domains); + $selected_domains = new Storm_Model_Collection($this->_model->getDomaines()); + $selected_domain_ids = $selected_domains->collect('id')->getArrayCopy(); - if ($to_delete = array_diff($existing_ids, $domains_ids)) - Class_NoticeDomain::deleteBy(['record_alpha_key' => $alpha_key, - 'domain_id' => $to_delete]); + $existings = Class_NoticeDomain::findAllBy(['record_alpha_key' => $alpha_key, + 'panier_id' => 0]); + $existings = new Storm_Model_Collection($existings); - $to_create = array_diff($domains_ids, $existing_ids); + // delete unselected + $existings + ->select(fn($existing) => !in_array($existing->getDomainId(), $selected_domain_ids)) + ->eachDo(fn($existing) => $existing->delete()); - foreach($to_create as $domain_id) { - $notice_domain = Class_NoticeDomain::newInstance(['domain_id' => $domain_id, - 'record_alpha_key' => $alpha_key]); - $notice_domain->updateFacette(); - $notice_domain->save(); - } + /// create newly selected + $existing_domain_ids = $existings->collect('domain_id')->getArrayCopy(); + foreach(array_diff($selected_domain_ids, $existing_domain_ids) as $domain_id) + Class_NoticeDomain::newInstance(['domain_id' => $domain_id, + 'record_alpha_key' => $alpha_key]) + ->save(); - foreach(Class_NoticeDomain::findAllBy(['record_alpha_key' => $alpha_key]) as $notice_domain) - $notice_domain->updateFacette(); + $this->_synchronizeRecordFacets($selected_domains, $alpha_key); $this->_model - ->setDomaineIds($domains_ids) + ->setDomaineIds($selected_domain_ids) ->save(); return $this; } - public function unindex() { - if (!$alpha_key = $this->getAlphaKey()) + protected function _synchronizeRecordFacets(Storm_Collection $selected_domains, string $alpha_key) : self { + $record = $this->_model->getNotice(); + $linked_by_selection = Class_NoticeDomain::findAllBy(['record_alpha_key' => $alpha_key, + 'panier_id not' => 0]); + $linked_by_selection = (new Storm_Model_Collection($linked_by_selection)) + ->collect('domain') + ->select(fn($domain) => null !== $domain); + + $facets = array_filter($record->getFacetCodes(), + fn($facet) => $facet && Class_Catalogue::CODE_FACETTE !== substr($facet, 0, 1)); + $facets = array_unique([...$facets, + ...$selected_domains->collect('facet_code')->getArrayCopy(), + ...$linked_by_selection->collect('facet_code')->getArrayCopy()]); + + $record->setFacettes($facets); + + return $this; + } + + + public function unindex() : self { + if (!$alpha_key = $this->_getAlphaKey()) return $this; - Class_NoticeDomain::deleteBy(['record_alpha_key' => $alpha_key, - 'panier_id' => 0]); + Class_NoticeDomain::basicDeleteBy(['record_alpha_key' => $alpha_key, + 'panier_id' => 0]); return $this; } - public function getAlphaKey() { - return $this->_model->getAlphaKey(); + protected function _getAlphaKey() : string { + return isset($this->_model) + ? $this->_model->getAlphaKey() + : ''; } } \ No newline at end of file diff --git a/library/Class/Notice.php b/library/Class/Notice.php index 0a5837bc1c9964c5c6482da83b2ecd07598665f3..0fc3f982f57749ec596cb9bc195c0699fcbc2575 100644 --- a/library/Class/Notice.php +++ b/library/Class/Notice.php @@ -19,190 +19,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class NoticeLoader extends Storm_Model_Loader { - use Trait_MemoryCleaner; - - public function getNoticesFromPreferences($preferences) { - $requetes = Class_Catalogue::getRequetes($preferences); - - if (!isset($requetes['req_liste'])) - return []; - - $notices = $this->findAll($requetes["req_liste"]); - - // Tirage aleatoire - if (isset($preferences['aleatoire']) - && $preferences["aleatoire"] == 1) { - shuffle($notices); - $notices = array_slice($notices, 0, $preferences["nb_notices"]); - } - - return $notices; - } - - - public function findFirstNoticeForClefChapeau($clef) { - $parts = explode('-', $clef); - $params = ['clef_chapeau' => $parts[0]]; - if (isset($parts[1])) - $params['type_doc'] = $parts[1]; - - return $this->findFirstBy($params); - } - - - public function getNoticeIdsByRequeteRecherche($req, $clear_cache = false) { - if (!$req) - return []; - - $cache = (new Storm_Cache())->useImplodeExplodeSerialization(); - $cache_key = [$req, __CLASS__, __FUNCTION__]; - - if ($clear_cache) - $cache->remove($cache_key); - - return $cache->memoize($cache_key, - function() use ($req) - { - return Zend_Registry::get('sql')->fetchAllByColumn($req); - }); - } - - - public function findAllByRequeteRecherche($req, $nb_par_page, $page_no=1 ) { - return $this->findAllByIds($this->getNoticeIdsByRequeteRecherche($req), - $nb_par_page, - $page_no); - } - - - public function findAllByIds($ids, $nb_par_page, $page_no) { - $page_no = (int)$page_no; - $nb_par_page = (int)$nb_par_page; - - if ($nb_par_page) { - $offset = ($page_no ? $page_no - 1 : 0) * $nb_par_page; - $ids = array_slice($ids, $offset, $nb_par_page); - } - - if (empty($ids)) - return []; - - return Class_Notice::getLoader()->findAllBy(['id_notice' => $ids, - 'order' => 'FIELD(id_notice, '.implode(',', $ids).')']); - } - - - public function countBySQLSelect($req) { - return fetchOne($req); - } - - - public function getNoticeByOAIIdentifier($identifier) { - $parts = explode(':', $identifier); - return Class_Notice::getLoader()->getNoticeByClefAlpha(end($parts)); - } - - - public function getNoticeByClefAlpha($clef) { - return Class_Notice::findFirstBy(['clef_alpha' => $clef]); - } - - - public function getAllNoticesByClefChapeau($clef, $conditions) { - return Class_Notice::findAllBy(array_merge($conditions, - ['clef_chapeau' => $clef, - 'order' => 'tome_alpha desc'])); - } - - - public function getAllNoticesByClefAlpha($clef) { - return Class_Notice::findAllBy(['clef_alpha' => $clef]); - } - - - public function findAllByCatalogue($catalogue) { - return Class_Catalogue::getLoader()->loadNoticesFor($catalogue); - } - - public function findByUrl($url) { - $frbr_link = new Class_FRBR_Link(); - if (!$clef_alpha = $frbr_link->extractKeyFromUrl($url)) - return; - - return Class_Notice::getNoticeByClefAlpha($clef_alpha); - } - - - public function getEarliestNotice() { - $result = $this->findAllBy(['limit' => 1, - 'created_at not' => null, - 'order' => 'created_at asc']); - if (0 == count($result)) - return null; - - return $result[0]; - } - - - public function findAllAfter($record_id, $update_date) { - $where = "id_notice > " . $record_id . " and date_maj >='" . $update_date . "'"; - return Class_Notice::findAllBy(['where' => $where, - 'order' => 'id_notice', - 'limit' => '100']); - } - - - public function indexNoveltyFacets() { - $facets = ['HNNNN*', 'HNANA*']; - if ($novelty_thesaurus = Class_CodifThesaurus::recordNoveltyFor(true)) - $facets[] = $novelty_thesaurus->getFacetCode(); - $facets = implode(' ', $facets); - - $record_id = 0; - while ($records = $this->_findNoveltyRecordsFrom($facets, $record_id)) - $record_id = $this->_updateNovelty($records); - } - - - protected function _findNoveltyRecordsFrom($facets, $record_id) { - if (null === $record_id) - return []; - - $where = sprintf('match(facettes) against("+(%s)" in boolean mode) and id_notice > %d', - $facets, $record_id); - - return Class_Notice::findAllBy(['where' => $where, - 'order' => 'id_notice', - 'limit' => 100]); - } - - - protected function _updateNovelty($records) { - $record_id = null; - foreach($records as $record) { - $record->updateNoveltyFacets(); - $record_id = $record->getId(); - } - $this->_cleanMemory(); - - return $record_id; - } - - - public function basicDeleteAllFromSigb() { - // @see Class_TypeDoc::isSigb() - $params = ['cast(type_doc as UNSIGNED) > 0', - 'type_doc < '.Class_TypeDoc::LIVRE_NUM, - 'type_doc not in (' . implode(',', [Class_TypeDoc::ARTICLE, - Class_TypeDoc::RSS, - Class_TypeDoc::SITE]) . ')']; - - return Class_Notice::basicDeleteBy(['where' => implode(' and ', $params)]); - } -} - - class Class_Notice extends Storm_Model_Abstract { use Trait_TimeSource, Trait_Translator; @@ -214,7 +30,7 @@ class Class_Notice extends Storm_Model_Abstract { TYPE_AUTHORITY_PARTIAL = 4; protected - $_loader_class = 'NoticeLoader', + $_loader_class = Class_Notice_Loader::class, $_table_name = 'notices', $_table_primary = 'id_notice', $_notice_unimarc, diff --git a/library/Class/Notice/Loader.php b/library/Class/Notice/Loader.php new file mode 100644 index 0000000000000000000000000000000000000000..e547a615a9c8b93fae636537a5031acf3f8af0fd --- /dev/null +++ b/library/Class/Notice/Loader.php @@ -0,0 +1,223 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Notice_Loader extends Storm_Model_Loader { + use Trait_MemoryCleaner; + + public function getNoticesFromPreferences($preferences) { + $requetes = Class_Catalogue::getRequetes($preferences); + + if (!isset($requetes['req_liste'])) + return []; + + $notices = $this->findAll($requetes["req_liste"]); + + // Tirage aleatoire + if (isset($preferences['aleatoire']) + && $preferences["aleatoire"] == 1) { + shuffle($notices); + $notices = array_slice($notices, 0, $preferences["nb_notices"]); + } + + return $notices; + } + + + public function findFirstNoticeForClefChapeau($clef) { + $parts = explode('-', $clef); + $params = ['clef_chapeau' => $parts[0]]; + if (isset($parts[1])) + $params['type_doc'] = $parts[1]; + + return $this->findFirstBy($params); + } + + + public function getNoticeIdsByRequeteRecherche($req, $clear_cache = false) { + if (!$req) + return []; + + $cache = (new Storm_Cache())->useImplodeExplodeSerialization(); + $cache_key = [$req, __CLASS__, __FUNCTION__]; + + if ($clear_cache) + $cache->remove($cache_key); + + return $cache->memoize($cache_key, + function() use ($req) + { + return Zend_Registry::get('sql')->fetchAllByColumn($req); + }); + } + + + public function findAllByRequeteRecherche($req, $nb_par_page, $page_no=1 ) { + return $this->findAllByIds($this->getNoticeIdsByRequeteRecherche($req), + $nb_par_page, + $page_no); + } + + + public function findAllByIds($ids, $nb_par_page, $page_no) { + $page_no = (int)$page_no; + $nb_par_page = (int)$nb_par_page; + + if ($nb_par_page) { + $offset = ($page_no ? $page_no - 1 : 0) * $nb_par_page; + $ids = array_slice($ids, $offset, $nb_par_page); + } + + if (empty($ids)) + return []; + + return Class_Notice::getLoader()->findAllBy(['id_notice' => $ids, + 'order' => 'FIELD(id_notice, '.implode(',', $ids).')']); + } + + + public function countBySQLSelect($req) { + return fetchOne($req); + } + + + public function getNoticeByOAIIdentifier($identifier) { + $parts = explode(':', $identifier); + return Class_Notice::getLoader()->getNoticeByClefAlpha(end($parts)); + } + + + public function getNoticeByClefAlpha($clef) { + return Class_Notice::findFirstBy(['clef_alpha' => $clef]); + } + + + public function getAllNoticesByClefChapeau($clef, $conditions) { + return Class_Notice::findAllBy(array_merge($conditions, + ['clef_chapeau' => $clef, + 'order' => 'tome_alpha desc'])); + } + + + public function getAllNoticesByClefAlpha($clef) { + return Class_Notice::findAllBy(['clef_alpha' => $clef]); + } + + + public function findAllByCatalogue($catalogue) { + return Class_Catalogue::getLoader()->loadNoticesFor($catalogue); + } + + public function findByUrl($url) { + $frbr_link = new Class_FRBR_Link(); + if (!$clef_alpha = $frbr_link->extractKeyFromUrl($url)) + return; + + return Class_Notice::getNoticeByClefAlpha($clef_alpha); + } + + + public function getEarliestNotice() { + $result = $this->findAllBy(['limit' => 1, + 'created_at not' => null, + 'order' => 'created_at asc']); + if (0 == count($result)) + return null; + + return $result[0]; + } + + + public function findAllAfter($record_id, $update_date) { + $where = "id_notice > " . $record_id . " and date_maj >='" . $update_date . "'"; + return Class_Notice::findAllBy(['where' => $where, + 'order' => 'id_notice', + 'limit' => '100']); + } + + + public function indexNoveltyFacets() { + $facets = ['HNNNN*', 'HNANA*']; + if ($novelty_thesaurus = Class_CodifThesaurus::recordNoveltyFor(true)) + $facets[] = $novelty_thesaurus->getFacetCode(); + $facets = implode(' ', $facets); + + $record_id = 0; + while ($records = $this->_findNoveltyRecordsFrom($facets, $record_id)) + $record_id = $this->_updateNovelty($records); + } + + + protected function _findNoveltyRecordsFrom($facets, $record_id) { + if (null === $record_id) + return []; + + $where = sprintf('match(facettes) against("+(%s)" in boolean mode) and id_notice > %d', + $facets, $record_id); + + return Class_Notice::findAllBy(['where' => $where, + 'order' => 'id_notice', + 'limit' => 100]); + } + + + protected function _updateNovelty($records) { + $record_id = null; + foreach($records as $record) { + $record->updateNoveltyFacets(); + $record_id = $record->getId(); + } + $this->_cleanMemory(); + + return $record_id; + } + + + public function basicDeleteAllFromSigb() { + // @see Class_TypeDoc::isSigb() + $params = ['cast(type_doc as UNSIGNED) > 0', + 'type_doc < '.Class_TypeDoc::LIVRE_NUM, + 'type_doc not in (' . $this->pseudoRecordsTypes() . ')']; + + return Class_Notice::basicDeleteBy(['where' => implode(' and ', $params)]); + } + + + public function deleteFacetsInRecordsByPattern(string $pattern, string $where) : int { + $query = 'update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "'. $pattern .'", ""))'; + if ($where) + $query .= ' where ' . $where; + + return Zend_Registry::get('sql')->query($query); + } + + + public function pseudoRecordsTypes(?Closure $callback=null) : string { + if (null === $callback) + $callback = fn($type) => '"' . $type. '"'; + + return implode(', ', + array_map($callback, + [Class_TypeDoc::ARTICLE, + Class_TypeDoc::RSS, + Class_TypeDoc::SITE])); + } +} diff --git a/library/Class/NoticeDomain.php b/library/Class/NoticeDomain.php index c3c3da5ed79ce16f8f23ed0698db2b632a698cea..df240abedc3247f0dc2e2e9618584a3ae376c499 100644 --- a/library/Class/NoticeDomain.php +++ b/library/Class/NoticeDomain.php @@ -100,6 +100,55 @@ class NoticeDomainLoader extends Storm_Model_Loader { $notice_domain->delete(); } } + + + /** + * Class_Sitotheque, Class_Article and Class_Rss can have their own selection of domains. + * Their corresponding Class_Notice will have a mix of manual selection of domains and + * domains matching by their criterias in their facets. + * So we cannot blindly delete. + * + * @see Class_Indexation_PseudoNotice::_getFacettes() + */ + public function clearNotPseudoRecordsWithPanier() : int { + $pseudo_records_key_ends = Class_Notice::pseudoRecordsTypes(fn($type) => '"-'. $type .'"'); + $where = + 'panier_id != 0' + . ' and substring(record_alpha_key, -2) not in ('. $pseudo_records_key_ends .')'; + + return Zend_Registry::get('sql')->query('delete from notice_domain where ' . $where); + } + + + /** + * @param $panier Class_PanierNotice + * @param $domains array<int> + * @param $clefs_alpha array<string> + */ + public function createAllForPanier(Class_PanierNotice $panier, array $domains, array $clefs_alpha) : int { + if (!$panier_id = $panier->getId()) + return 0; + + $values = new Storm_Collection; + + foreach($domains as $domain) + $values->addAll($this->_insertValuesForPanierAndDomain($panier_id, $domain, $clefs_alpha)); + + return Zend_Registry::get('sql') + ->query('insert into notice_domain (domain_id, panier_id, record_alpha_key) values ' . implode(', ', $values->getArrayCopy())); + } + + + protected function _insertValuesForPanierAndDomain(int $panier_id, int $domain_id, array $clefs_alpha) : array { + return + array_map(fn($alpha_key) => '(' . $domain_id . ', ' . $panier_id . ', "' . $alpha_key . '")', + $clefs_alpha); + } + + + protected function _insertValuesFor(int $domain_id, int $panier_id, string $record_alpha_key) : string { + return '(' . $domain_id . ', ' . $panier_id . ', "' . $record_alpha_key . '")'; + } } diff --git a/library/Class/PanierNotice.php b/library/Class/PanierNotice.php index 3f0fa9fa735869f70265be19fad8eb4066c078f5..148d5d0fee3b2a616bf2ef6a97c89cbc52bdc0dd 100644 --- a/library/Class/PanierNotice.php +++ b/library/Class/PanierNotice.php @@ -139,6 +139,16 @@ class PanierNoticeLoader extends Storm_Model_Loader { Class_PanierNotice::findFirstBy(['libelle' => $label, 'id_user' => $user->getId()]); } + + + public function rawLinkedDomainsOf(Class_PanierNotice $panier) : array { + $query = 'select c.id_catalogue, c.indexer' + .' from catalogue c' + .' inner join notices_paniers_catalogues npc' + .' on (c.id_catalogue=npc.id_catalogue and npc.id_panier=' . $panier->getId() . ')'; + + return Zend_Registry::get('sql')->fetchAll($query); + } } @@ -582,4 +592,23 @@ class Class_PanierNotice extends Storm_Model_Abstract { public function isEmpty() : bool { return ! $this->getClesNotices(); } + + + public function rawLinkedDomains() : array { + return static::getLoader()->rawLinkedDomainsOf($this); + } + + + public function getClesNoticesNotPseudoRecords() : array { + if (!$keys = $this->getClesNotices()) + return []; + + $pseudo_ends = array_map(fn($type) => '-' . $type, + [Class_TypeDoc::ARTICLE, + Class_TypeDoc::RSS, + Class_TypeDoc::SITE]); + + return array_filter($keys, + fn($key) => !in_array(substr($key, -2), $pseudo_ends)); + } } \ No newline at end of file diff --git a/library/Trait/HasManyDomaines.php b/library/Trait/HasManyDomaines.php index 452c8bc969b3e861e913c0827d6bc83ed03d6651..4a6939c5b427a22d9e5f0d581389e04c1baeefd0 100644 --- a/library/Trait/HasManyDomaines.php +++ b/library/Trait/HasManyDomaines.php @@ -23,19 +23,19 @@ trait Trait_HasManyDomaines { public function setDomaineIds($domaines) { if (is_array($domaines)) - $domaines=implode(';', array_unique($domaines)); - $domaines=str_replace('-',';',$domaines); + $domaines = implode(';', array_unique($domaines)); + $domaines = str_replace('-',';',$domaines); return $this->_set('domaine_ids',$domaines); } - public function getDomaineIdsAsArray() { + public function getDomaineIdsAsArray() : array { $domaines = $this->_get('domaine_ids') ; return array_filter(explode(';', $domaines)); } - public function getDomaines() { + public function getDomaines() : array { $domains = $this->getDomaineIdsAsArray(); return empty($domains) ? [] @@ -43,18 +43,14 @@ trait Trait_HasManyDomaines { } - public function getDomaineLibelles() { - return array_map(function($model) {return $model->getLibelle();}, + public function getDomaineLibelles() : array { + return array_map(fn($model) => $model->getLibelle(), $this->getDomaines()); } public function setDomaines($domaines) { - $ids = []; - foreach($domaines as $domaine) - $ids []= $domaine->getId(); - return $this->setDomaineIds($ids); + return $this->setDomaineIds(array_map(fn($domaine) => $domaine->getId(), + $domaines)); } } - -?> \ No newline at end of file diff --git a/tests/library/Class/Cosmogramme/Integration/PhaseDomainsTest.php b/tests/library/Class/Cosmogramme/Integration/PhaseDomainsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0b5401f09aecbff5ecf4fbdf4dd24dba76abd1df --- /dev/null +++ b/tests/library/Class/Cosmogramme/Integration/PhaseDomainsTest.php @@ -0,0 +1,189 @@ +<?php +/** + * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +abstract class PhaseDomainsTestCase extends Class_Cosmogramme_Integration_PhaseTestCase { + protected function _getPreviousPhase() { + return new Class_Cosmogramme_Integration_Phase(16); + } + + + public function setUp() { + parent::setUp(); + + $this->_phase = $this->_buildPhase('Domains') + ->setMemoryCleaner(function() {}) + ->run(); + } + + + /** @test */ + public function logShouldNotContainsError() { + $this->assertNotError(); + } + + + /** @test */ + public function logShouldContainsIndexationDesDomaines() { + $this->assertLogContains('Indexation des domaines'); + } +} + + + + +class PhaseDomainsNotCronTest extends PhaseDomainsTestCase { + /** @test */ + public function logShouldContainsQuEnModeCron() { + $this->assertLogContains('Ce traitement ne se lance qu\'en mode cron.'); + } +} + + + + +class PhaseDomainsCronTest extends PhaseDomainsTestCase { + protected function _getPreviousPhase() { + return parent::_getPreviousPhase()->beCron(); + } + + + protected function _prepareFixtures() { + $sql = $this->mock() + + ->whenCalled('query') + ->with('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bHCCCC\\\\d*\\\\b", "")) where type_doc not in ("8", "9", "10") and type=1') + ->answers(12) + + ->whenCalled('query') + ->with('update notices set facettes=concat(facettes, " HCCCC0001") Where (notices.type_doc=\'2\') and type=1') + ->answers(1) + + ->whenCalled('query') + ->with('delete from notice_domain where panier_id != 0 and substring(record_alpha_key, -2) not in ("-8", "-9", "-10")') + ->answers(8) + + ->whenCalled('query') + ->with('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bQ[a-zA-Z0-9]*\\\\b", "")) where type_doc not in ("8", "9", "10") and type=1 and match(facettes) against("+Q*" in boolean mode)') + ->answers(5) + + ->whenCalled('fetchAll') + ->with('select c.id_catalogue, c.indexer from catalogue c inner join notices_paniers_catalogues npc on (c.id_catalogue=npc.id_catalogue and npc.id_panier=348)') + ->answers([ + ['id_catalogue' => 33, 'indexer' => 1], + ['id_catalogue' => 34, 'indexer' => 0] + ]) + + ->whenCalled('query') + ->with('insert into notice_domain (domain_id, panier_id, record_alpha_key) values (33, 348, "MYSUPER-KEY----1"), (33, 348, "MYOTHER-KEY-----1"), (34, 348, "MYSUPER-KEY----1"), (34, 348, "MYOTHER-KEY-----1")') + ->answers(2) + + ->whenCalled('query') + ->with('update notices set facettes=concat(facettes, " Q33 Q34 HCCCC007Z") where clef_alpha in ("MYSUPER-KEY----1", "MYOTHER-KEY-----1")') + ->answers(2) + + ->whenCalled('fetchAllByColumn') + ->with('select id_thesaurus from codif_thesaurus where id_thesaurus like "CCCC%" and length(id_thesaurus)=8 and id_origine in ("33")') + ->answers(['CCCC007Z']) + ; + + Zend_Registry::set('sql', $sql); + + $serials = $this->fixture(Class_Catalogue::class, + ['id' => 33, + 'libelle' => 'My Domain of serials', + 'indexer' => 1, + 'type_doc' => Class_TypeDoc::PERIODIQUE]); + + $not_indexed = $this->fixture(Class_Catalogue::class, + ['id' => 34, + 'libelle' => 'Shy', + 'indexer' => 0, + 'type_doc' => Class_TypeDoc::LIVRE]); + + $this->fixture(Class_PanierNotice::class, + ['id' => 348, + 'libelle' => 'My selection', + 'notices' => 'MYSUPER-KEY----1;MYOTHER-KEY-----1', + 'catalogues' => [$serials, $not_indexed]]); + } + + + /** @test */ + public function logShouldContains12NoticesDésindexées() { + $this->assertLogContains('12 notices désindexées'); + } + + + /** @test */ + public function logShouldContainsMyDomainOfSerials() { + $this->assertLogContains('Indexation du domaine : My Domain of serials'); + } + + + /** @test */ + public function logShouldContainsUneNoticeIndexée() { + $this->assertLogContains('1 notice indexé'); + } + + + /** @test */ + public function logShouldContainsSummary() { + $this->assertLogContains('1 notice traitée'); + $this->assertLogContains('Temps de traitement'); + } + + + /** @test */ + public function logShouldContainsIndexationDesPaniers() { + $this->assertLogContains('Indexation des paniers dans les domaines'); + } + + + /** @test */ + public function logShouldContains8LiensNoticeDomaineSupprimés() { + $this->assertLogContains('8 liens notice/domaine supprimés'); + } + + + /** @test */ + public function logShouldContains5NoticesDésindexées() { + $this->assertLogContains('5 notices désindexées'); + } + + + /** @test */ + public function logShouldContainsIndexationDuPanierMySelection() { + $this->assertLogContains('Indexation du panier : My selection'); + } + + + /** @test */ + public function logShouldContains2LiensNoticeDomaineCréés() { + $this->assertLogContains('2 liens notice/domaine créés'); + } + + + /** @test */ + public function logShouldContains2NoticesIndexées() { + $this->assertLogContains('2 notices indexées'); + } +} diff --git a/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php b/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php index ad039b1d1a16b70fdae162fc963cfe4a39898b4c..f57e0de504809c99ca33a0ccca3d97e556a27c3b 100644 --- a/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php +++ b/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php @@ -855,7 +855,7 @@ class PhasePrepareIntegrationsFlushBeforeFullAndTotalRecordsTest /** @test */ public function shouldDeleteAllNoticeFromSigb() { - $this->assertEquals(['where' => 'cast(type_doc as UNSIGNED) > 0 and type_doc < 100 and type_doc not in (8,9,10)'], + $this->assertEquals(['where' => 'cast(type_doc as UNSIGNED) > 0 and type_doc < 100 and type_doc not in ("8", "9", "10")'], Class_Notice::getFirstAttributeForLastCallOn('basicDeleteBy')); }