diff --git a/VERSIONS_HOTLINE/181094 b/VERSIONS_HOTLINE/181094 new file mode 100644 index 0000000000000000000000000000000000000000..d0106253104be711899baabd4e4a0517990d2f22 --- /dev/null +++ b/VERSIONS_HOTLINE/181094 @@ -0,0 +1 @@ + - correctif #181094 : Centre interet en double, création d'un script qui supprimes les doublons et met à jours les facettes et domaines liés \ No newline at end of file diff --git a/library/Class/Migration/DeduplCentreInteret.php b/library/Class/Migration/DeduplCentreInteret.php new file mode 100644 index 0000000000000000000000000000000000000000..2f0b23593e754cd1370dc7acc895f70aa9b3e2b2 --- /dev/null +++ b/library/Class/Migration/DeduplCentreInteret.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright (c) 2012-2023, 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_Migration_DeduplCentreInteret { + + protected $_sql; + protected array $_log = []; + + public function run() : string { + $this->_sql = Zend_Registry::get('sql'); + $row_results = $this->_sql->fetchAll('select libelle, count(*) as grp_count from codif_interet group by libelle having grp_count > 1'); + + foreach ($row_results as $row) { + $label = $row['libelle']; + $interets = Class_CodifCentreInteret::findAllBy(['libelle' => $label]); + $code_alpha = Class_Indexation::getInstance()->alphaMaj($label); + $this->_dedupl($interets, $code_alpha); + } + + return implode("\n", $this->_log); + } + + + protected function _dedupl(array $interets, string $code_alpha) : self { + $collection = new Storm_Collection($interets); + + if ( ! ($keep = $collection + ->detect(fn($interet) => $code_alpha === $interet->getCodeAlpha()))) { + $keep = $collection->first(); + $keep->setCodeAlpha($code_alpha)->save(); + } + + $collection->select(fn($interet) => $keep->getId() !== $interet->getId()) + ->eachDo(fn($interet) => $this->_updateRecords($keep->asFacet(), $interet) + ->_updateDomains($keep->getId(), $interet)); + + return $this; + } + + + protected function _updateRecords(string $keep_facet, Class_CodifCentreInteret $interet) : self { + $facet = $interet->asFacet(); + + $query = 'UPDATE `notices` SET `facettes` = REGEXP_REPLACE(`facettes`, "' + . Class_CodifThesaurus_Loader::WORD_BOUNDARY + . $facet + . Class_CodifThesaurus_Loader::WORD_BOUNDARY + . '", "' . $keep_facet . '")' + . " WHERE MATCH(`facettes`) AGAINST('" . $facet . "' IN BOOLEAN MODE)"; + + $this->_log [] = ' - CentreInteret deleted id: ' . $interet->getId() + . ', label: ' . $interet->getLibelle() + . ', code_alpha: ' . $interet->getCodeAlpha(); + $this->_log [] = ' -> Record facettes change from: ' . $facet . ', to: ' . $keep_facet; + + $this->_sql->query($query); + $interet->delete(); + + return $this; + } + + + protected function _updateDomains(int $id, Class_CodifCentreInteret $interet) : self { + $domains = Class_Catalogue::query() + ->like('interet', '%' . $interet->getId() . '%') + ->fetchAll(); + + foreach ($domains as $domain) + $this->_updateOneDomain($domain, $interet->getId(), $id); + + return $this; + } + + + protected function _updateOneDomain(Class_Catalogue $domain, + int $replace_id, + int $new_id) : self { + $interets = []; + $updated = false; + foreach (explode(';', $domain->getInteret()) as $interet) { + $domain_updated = $replace_id == $interet; + $updated |= $domain_updated; + $interets [] = $domain_updated + ? $new_id + : $interet; + } + + if ($updated) { + $new_interests = implode(';', array_unique($interets)); + $this->_log [] = ' -> Domain id: ' . $domain->getId() + . ', change interet from: ' . $domain->getInteret() + . ', to: ' . $new_interests; + $domain->setInteret($new_interests)->save(); + } + + return $this; + } +} diff --git a/scripts/dedupl_interet.php b/scripts/dedupl_interet.php new file mode 100644 index 0000000000000000000000000000000000000000..01320ba38f1fcda3ac40af08821babcbb3c71a24 --- /dev/null +++ b/scripts/dedupl_interet.php @@ -0,0 +1,6 @@ +<?php +error_reporting(E_ALL^E_DEPRECATED); +require __DIR__ . '/../console.php'; + +echo (new Class_Migration_DeduplCentreInteret)->run(); +echo "\n\n"; diff --git a/tests/library/Class/DeduplCentreInteretTest.php b/tests/library/Class/DeduplCentreInteretTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0a52fd27bf5d9a328996c105e94b76ac8bcede93 --- /dev/null +++ b/tests/library/Class/DeduplCentreInteretTest.php @@ -0,0 +1,231 @@ +<?php +/** + * Copyright (c) 2012-2023, 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 DeduplCentreInteretTest extends ModelTestCase { + + public function setUp() { + parent::setUp(); + + Zend_Registry::set('sql', $this->mock() + + ->whenCalled('fetchAll') + ->with('select libelle, count(*) as grp_count from codif_interet group by libelle having grp_count > 1') + ->answers([['libelle' => 'age : a partir de 10 ans', 'grp_count' => 2], + ['libelle' => 'Musique & Variétées', 'grp_count' => 2], + ['libelle' => 'Thriller / Romans', 'grp_count' => 2], + ['libelle' => 'Voyage, vacances', 'grp_count' => 2]]) + + ->whenCalled('query') + ->with('UPDATE `notices` SET `facettes` = REGEXP_REPLACE(`facettes`, "\\\\bF2\\\\b", "F3") WHERE MATCH(`facettes`) AGAINST(\'F2\' IN BOOLEAN MODE)') + ->willDo(function() { + Class_Notice::find(111)->setFacettes('T1 F3 G22')->save(); + Class_Notice::find(114)->setFacettes('F3 T1 F5 G22 F8 HCCFF2001 F7') + ->save(); + }) + + ->whenCalled('query') + ->with('UPDATE `notices` SET `facettes` = REGEXP_REPLACE(`facettes`, "\\\\bF4\\\\b", "F5") WHERE MATCH(`facettes`) AGAINST(\'F4\' IN BOOLEAN MODE)') + ->willDo(fn() => Class_Notice::find(112)->setFacettes('T1 F5')->save()) + + ->whenCalled('query') + ->with('UPDATE `notices` SET `facettes` = REGEXP_REPLACE(`facettes`, "\\\\bF6\\\\b", "F7") WHERE MATCH(`facettes`) AGAINST(\'F6\' IN BOOLEAN MODE)') + ->willDo(function() { + Class_Notice::find(113)->setFacettes('F7 G22')->save(); + Class_Notice::find(114)->setFacettes('F3 T1 F5 G22 F8 HCCFF2001 F7') + ->save(); + }) + + ->whenCalled('query') + ->with('UPDATE `notices` SET `facettes` = REGEXP_REPLACE(`facettes`, "\\\\bF9\\\\b", "F8") WHERE MATCH(`facettes`) AGAINST(\'F9\' IN BOOLEAN MODE)') + ->willDo(fn() => Class_Notice::find(114) + ->setFacettes('F3 T1 F5 G22 F8 HCCFF2001 F7')->save()) + + ->whenCalled('query') + ->with('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bHCCCC0001\\\\b", "")) where type_doc not in ("8", "9", "10") and type=1 and match(facettes) against("+HCCCC0001" in boolean mode)') + ->answers(1) + + ->whenCalled('query') + ->with('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bHCCCC0002\\\\b", "")) where type_doc not in ("8", "9", "10") and type=1 and match(facettes) against("+HCCCC0002" in boolean mode)') + ->answers(1) + + ->whenCalled('query') + ->with('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bHCCCC0003\\\\b", "")) where type_doc not in ("8", "9", "10") and type=1 and match(facettes) against("+HCCCC0003" in boolean mode)') + ->answers(1) + + ->whenCalled('query') + ->with('update notices set facettes = clean_spaces(REGEXP_REPLACE(facettes, "\\\\bHCCCC0004\\\\b", "")) where type_doc not in ("8", "9", "10") and type=1 and match(facettes) against("+HCCCC0004" in boolean mode)') + ->answers(1) + + ->beStrict()); + + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 1, + 'libelle' => 'lib uniq', + 'code_alpha' => 'LIB UNIQ']); + + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 2, + 'libelle' => 'age : a partir de 10 ans', + 'code_alpha' => 'AGE A PARTIR DE 10 ANS']); + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 3, + 'libelle' => 'age : a partir de 10 ans', + 'code_alpha' => 'AGE A PARTIR DE 10 ANS']); + + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 4, + 'libelle' => 'Musique & Variétées', + 'code_alpha' => 'MUSIQUE VARIETEES']); + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 5, + 'libelle' => 'Musique & Variétées', + 'code_alpha' => 'MUSIQUE VARIETEES']); + + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 6, + 'libelle' => 'Thriller / Romans', + 'code_alpha' => 'THRILLER ROMANS']); + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 7, + 'libelle' => 'Thriller / Romans', + 'code_alpha' => 'THRILLER ROMANS']); + + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 8, + 'libelle' => 'Voyage, vacances', + 'code_alpha' => 'VOYAGE VACANCES']); + $this->fixture(Class_CodifCentreInteret::class, + ['id' => 9, + 'libelle' => 'Voyage, vacances', + 'code_alpha' => 'VOYAGE VACANCES']); + + $this->fixture(Class_Notice::class, + ['id' => 111, + 'facettes' => 'T1 F2 G22']); + $this->fixture(Class_Notice::class, + ['id' => 112, + 'facettes' => 'T1 F4']); + $this->fixture(Class_Notice::class, + ['id' => 113, + 'facettes' => 'F6 G22']); + $this->fixture(Class_Notice::class, + ['id' => 114, + 'facettes' => 'F2 T1 F5 G22 F9 HCCFF2001 F6']); + + $this->fixture(Class_Catalogue::class, + ['id' => 1111, + 'libelle' => 'Domain label 1111', + 'interet' => '2']); + $this->fixture(Class_Catalogue::class, + ['id' => 1112, + 'libelle' => 'Domain label 1112', + 'interet' => '44;4']); + $this->fixture(Class_Catalogue::class, + ['id' => 1113, + 'libelle' => 'Domain label 1113', + 'interet' => '6;66']); + $this->fixture(Class_Catalogue::class, + ['id' => 1114, + 'libelle' => 'Domain label 1114', + 'interet' => '2;5;46;9']); + + (new Class_Migration_DeduplCentreInteret)->run(); + Class_CodifCentreInteret::clearCache(); + } + + + public function keepedCentreInteret() : array { + return [[1, 'lib uniq', 'LIB UNIQ'], + [3, 'age : a partir de 10 ans', 'AGE A PARTIR DE 10 ANS'], + [5, 'Musique & Variétées', 'MUSIQUE VARIETEES'], + [7, 'Thriller / Romans', 'THRILLER ROMANS'], + [8, 'Voyage, vacances', 'VOYAGE VACANCES'] + ]; + } + + + public function deletedCentreInteret() : array { + return [[2], + [4], + [6], + [9] + ]; + } + + + public function recordsUpdated() : array { + return [[111, 'T1 F3 G22'], + [112, 'T1 F5'], + [113, 'F7 G22'], + [114, 'F3 T1 F5 G22 F8 HCCFF2001 F7'] + ]; + } + + + public function domainsUpdated() : array { + return [[1111, '3'], + [1112, '44;5'], + [1113, '7;66'], + [1114, '3;5;46;8'] + ]; + } + + + /** + * @test + * @dataProvider keepedCentreInteret + */ + public function keepedShouldNotBeUpdated(int $id, string $label, string $code_alpha) { + $interest = Class_CodifCentreInteret::find($id); + $this->assertEquals([$label, $code_alpha], [$interest->getLibelle(), + $interest->getCodeAlpha()]); + } + + + /** + * @test + * @dataProvider deletedCentreInteret + */ + public function deletedShouldNotExists(int $id) { + $this->assertNull(Class_CodifCentreInteret::find($id)); + } + + + /** + * @test + * @dataProvider recordsUpdated + */ + public function recordsFacettesShouldHaveBeenUpdated(int $id, string $facet) { + $record = Class_Notice::find($id); + $this->assertEquals($facet, $record->getFacettes()); + } + + + /** + * @test + * @dataProvider domainsUpdated + */ + public function domainsInteretsShouldHaveBeenUpdated(int $id, string $interets) { + $domain = Class_Catalogue::find($id); + $this->assertEquals($interets, $domain->getInteret()); + } +}