diff --git a/VERSIONS_HOTLINE/83526 b/VERSIONS_HOTLINE/83526 new file mode 100644 index 0000000000000000000000000000000000000000..559c279cacacb1625b99a451245e5a64eb579e4c --- /dev/null +++ b/VERSIONS_HOTLINE/83526 @@ -0,0 +1 @@ + - ticket #83526 : Cosmogramme : SIGB Nanook : Amélioriation de la mise à jour de l'étalon lors de la modification des libellés de sections, genres et emplacements \ No newline at end of file diff --git a/cosmogramme/sql/patch/patch_360.php b/cosmogramme/sql/patch/patch_360.php new file mode 100644 index 0000000000000000000000000000000000000000..02584669213b33c7509ca37419eb80524076d928 --- /dev/null +++ b/cosmogramme/sql/patch/patch_360.php @@ -0,0 +1,2 @@ +<?php +(new Class_Migration_SigbStandardCodifications())->run(); diff --git a/library/Class/Codification/Rule.php b/library/Class/Codification/Rule.php new file mode 100644 index 0000000000000000000000000000000000000000..4e3b73fd7cd7de297246e847f50691fd84c96efe --- /dev/null +++ b/library/Class/Codification/Rule.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright (c) 2012-2018, 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_Codification_Rule { + const VALUE_SEPARATOR = ';'; + + protected + $_zone, + $_operator, + $_values = []; + + public static function newFrom($rule) { + $zone = substr($rule, 0, 5); + $operator = substr($rule, 5, 1); + $values = array_filter(array_map( + function($value) { + return trim(str_replace(' ', '', $value)); + }, + explode(static::VALUE_SEPARATOR, substr($rule, 6)))); + + return $values + ? new static($zone, $operator, $values) + : null; + } + + + public function __construct($zone, $operator, $values) { + $this->_zone = $zone; + $this->_operator = $operator; + $this->_values = $values; + } + + + public function isSameFieldAndOperator($other) { + return ($other + && $this->_zone === $other->_zone + && $this->_operator === $other->_operator); + } + + + public function isAlreadyHandledByOtherRule($other) { + return $this->isSameFieldAndOperator($other) + && !array_diff($this->_values, $other->_values); + } + + + public function isZone($zone) { + return $this->_zone === $zone; + } + + + public function addValuesOf($other) { + foreach($other->_values as $value) + if (!in_array($value, $this->_values)) + $this->_values[] = $value; + + return $this; + } + + + public function eachValueDo($closure) { + foreach($this->_values as $value) + $closure($value); + + return $this; + } + + + public function asString() { + return $this->_zone + . $this->_operator + . implode(static::VALUE_SEPARATOR, array_unique($this->_values)); + } +} \ No newline at end of file diff --git a/library/Class/Codification/Rules.php b/library/Class/Codification/Rules.php new file mode 100644 index 0000000000000000000000000000000000000000..eda2aeca014ce16f3bb1a733e790d7236624deb1 --- /dev/null +++ b/library/Class/Codification/Rules.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright (c) 2012-2018, 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_Codification_Rules { + protected $_rules; + + + public static function newFromString($rules) { + $model = (new Class_Entity(['Regles' => $rules])) + ->whenCalledDo( + 'hasRegles', + function() use($rules) { + return $rules; + }); + + return new static($model); + } + + + protected static function _rulesFrom($model) { + if (!$model || !$model->hasRegles()) + return new Storm_Collection([]); + + $rules = []; + foreach(explode("\n", $model->getRegles()) as $rule) + $rules[] = Class_Codification_Rule::newFrom($rule); + + return new Storm_Collection(array_filter($rules)); + } + + + public function __construct($model) { + $this->_rules = static::_rulesFrom($model); + } + + + public function ensure($rule_string) { + if (!$rule = Class_Codification_Rule::newFrom($rule_string)) + return $this; + + $existing = $this->_rules + ->detect( + function($other) use($rule) { + return $rule->isSameFieldAndOperator($other); + }); + + $existing + ? $existing->addValuesOf($rule) + : $this->_rules->append($rule); + + return $this; + } + + + public function alreadyExistsIn($other_rules) { + if (!$other_rules || $other_rules->isEmpty()) + return false; + + foreach($this->_rules as $rule) + if (!$this->_ruleIsAlreadyHandledByOtherRules($rule, $other_rules)) + return false; + + return true; + } + + + protected function _ruleIsAlreadyHandledByOtherRules($rule, $other_rules) { + foreach($other_rules->_rules as $other) + if ($rule->isAlreadyHandledByOtherRule($other)) + return true; + + return false; + } + + + public function eachDo($callback) { + $this->_rules->eachDo($callback); + return $this; + } + + + public function isEmpty() { + return $this->_rules->isEmpty(); + } + + + public function asString() { + return $this->isEmpty() + ? '' + : implode("\n", $this->_rulesAsString()->getArrayCopy()); + } + + + protected function _rulesAsString() { + return $this->_rules + ->collect(function($rule) { return $rule->asString(); }); + } +} \ No newline at end of file diff --git a/library/Class/Cosmogramme/Generator/AbstractTask.php b/library/Class/Cosmogramme/Generator/AbstractTask.php index 1d7c57a8cfa5db3672cd14240df83e41400ec14d..168006e6c31fe43e3b85261e9eabfc7bc5389aca 100644 --- a/library/Class/Cosmogramme/Generator/AbstractTask.php +++ b/library/Class/Cosmogramme/Generator/AbstractTask.php @@ -94,7 +94,7 @@ abstract class Class_Cosmogramme_Generator_AbstractTask { $label = $this->getLabel($elems); $code = $this->getCode($elems); - if (!$model = $model_class::findFirstBy(['libelle' => $label])) + if (!$model = $this->_findExistingBy($code, $label)) $model = $model_class::newInstance(['libelle' => $label]); return @@ -103,6 +103,21 @@ abstract class Class_Cosmogramme_Generator_AbstractTask { } + protected function _findExistingBy($code, $label) { + $model_class = $this->_model_class; + if ($model = $model_class::findFirstBy(['libelle' => $label])) + return $model; + + foreach($model_class::findAll() as $model) { + if (!$rules = Class_Codification_Rules::newFromString($this->_getRuleFor($code))) + continue; + + if ($rules->alreadyExistsIn(new Class_Codification_Rules($model))) + return $model->setLibelle($label); + } + } + + protected function getLabel($elems) { return trim($elems[1]); } @@ -184,11 +199,18 @@ abstract class Class_Cosmogramme_Generator_AbstractTask { protected function _createOrConcatRules($code, $model) { return !$model->isNew() && $model->getDateMaj() == $this->_date - ? $model->getRegles() . ';' . $code + ? $this->_concatRulesTo($code, $model) : $this->_getRuleFor($code); } + protected function _concatRulesTo($code, $model) { + return (new Class_Codification_Rules($model)) + ->ensure($this->_getRuleFor($code)) + ->asString(); + } + + protected function _getRuleFor($code) { return ''; } diff --git a/library/Class/Migration/SigbStandardCodifications.php b/library/Class/Migration/SigbStandardCodifications.php new file mode 100644 index 0000000000000000000000000000000000000000..706ff93ae2c9d1030c54c2f1a5d7b8c95890cc3e --- /dev/null +++ b/library/Class/Migration/SigbStandardCodifications.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright (c) 2012-2018, 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_SigbStandardCodifications { + use Trait_TimeSource, Trait_MemoryCleaner; + + protected + $_sections = [], + $_locations = [], + $_kinds = []; + + + public function run() { + $generator = (new Class_Cosmogramme_Generator())->beUpdate(); + if (!$standard_dir = $generator->getUpdatableStandard()) + return $this; + + $generator->update($standard_dir); + } +} diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index 4feac4dfe60a5bf16643197223ab4113f3dbe589..130fc2256daa48b5e47f16f759c94b82722c24dc 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -2382,3 +2382,13 @@ class UpgradeDB_359_Test extends UpgradeDBTestCase { /** @test */ public function placeholderForSerialArticleMigration() {} } + + + + +class UpgradeDB_360_Test extends UpgradeDBTestCase { + public function prepare() {} + + /** @test */ + public function placeholderForSigbStandardCodificationsMigration() {} +} diff --git a/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php b/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php index 7e5cb1920eb9db2ea96be86493cdc9b27a049fcc..6eff530223fa9b45b35dc3cf6de98688b980049c 100644 --- a/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php +++ b/tests/library/Class/Cosmogramme/Integration/PhasePrepareIntegrationsTest.php @@ -386,7 +386,8 @@ class PhasePrepareIntegrationsNanookStandardTest ->whenCalled('getSectionsOf')->with('foo') ->answers(['BIB_C_SECTION|CODE|LIBELLE', - '4|Adultes']) + '4|Adultes', + '7|Jeunes']) ->whenCalled('getLocationsOf')->with('foo') ->answers(['BIB_C_EMPLACEMENT|CODE|LIBELLE', @@ -410,6 +411,18 @@ class PhasePrepareIntegrationsNanookStandardTest 'regles' => '995$9=3', 'date_maj' => '']); + $this->fixture('Class_CodifSection', + ['id' => 36, + 'libelle' => 'Adultes', + 'regles' => "995$9=4", + 'date_maj' => '']); + + $this->fixture('Class_CodifSection', + ['id' => 38, + 'libelle' => 'Jeunesse', + 'regles' => "995$9=7", + 'date_maj' => '']); + $this->fixture('Class_Notice', ['id' => 444, 'facettes' => 'S34', @@ -722,4 +735,16 @@ class PhasePrepareIntegrationsNanookStandardTest public function updatedLibraryRunQueueShouldNotBeLost() { $this->assertNotEmpty(Class_IntBib::find(2)->getIntegrations()); } + + + /** @test */ + public function existingSectionValueShouldNotBeDuplicated() { + $this->assertEquals('995$9=4', Class_CodifSection::find(36)->getRegles()); + } + + + /** @test */ + public function renamedSectionShouldBeUpdated() { + $this->assertEquals('Jeunes', Class_CodifSection::find(38)->getLibelle()); + } } \ No newline at end of file diff --git a/tests/library/Class/Migration/SigbStandardCodificationsTest.php b/tests/library/Class/Migration/SigbStandardCodificationsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..536e9aa38d062cca2b8c40688311abb4b23f52ef --- /dev/null +++ b/tests/library/Class/Migration/SigbStandardCodificationsTest.php @@ -0,0 +1,112 @@ +<?php +/** + * Copyright (c) 2012-2018, 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_SigbStandardCodificationsTest extends ModelTestCase { + public function setUp() { + parent::setUp(); + + $this->fixture('Class_CodifSection', + ['id' => 36, + 'libelle' => 'Adultes', + 'regles' => '995$9=4;4;4;4;4', + 'date_maj' => '']); + + $this->fixture('Class_CodifEmplacement', + ['id' => 33, + 'libelle' => 'Manga', + 'regles' => '995$6=108;108;108;108;108', + 'date_maj' => '']); + + $this->fixture('Class_CodifGenre', + ['id' => 64, + 'libelle' => 'Music', + 'regles' => '995$7=1;1;1;1', + 'date_maj' => '']); + + $this->fixture('Class_IntBib', ['id' => 10, 'sigb' => Class_IntBib::SIGB_NANOOK]); + $this->fixture('Class_IntMajAuto', ['id' => 42]); + + $landing_directory = $this + ->mock() + ->whenCalled('getUpdatableStandard')->answers('foo') + + ->whenCalled('getLibrariesOf')->with('foo') + ->answers(['BIB_SPS_UTT|ID_SITE|LIBELLE', + '2|Ma library', + '6|Ma bibliothèque', + '8|' . iconv('UTF-8', 'ISO-8859-1', 'Ma médiathèque')]) + + ->whenCalled('getSectionsOf')->with('foo') + ->answers(['BIB_C_SECTION|CODE|LIBELLE', + '4|Adultes']) + + ->whenCalled('getLocationsOf')->with('foo') + ->answers(['BIB_C_EMPLACEMENT|CODE|LIBELLE', + '9|Manga']) + + ->whenCalled('getKindsOf')->with('foo') + ->answers(['BIB_GENRES|SUPPORT|CODE|LIBELLE|DOC', + '0|1|Music|f', + '0|2|Bande dessinée|f', + '0|3|Bande dessinée|f', + '0|4|' . iconv('UTF-8', 'ISO-8859-1', 'Bande dessinée') . '|f',]) + + ->beStrict(); + + Class_Cosmogramme_LandingDirectory::setInstance($landing_directory); + } + + + public function tearDown() { + Class_Cosmogramme_LandingDirectory::setInstance(null); + parent::tearDown(); + } + + + /** @test */ + public function withNotSingleNanookShouldNotCleanSections() { + Class_IntBib::deleteBy([]); + (new Class_Migration_SigbStandardCodifications())->run(); + $this->assertEquals('995$9=4;4;4;4;4', Class_CodifSection::find(36)->getRegles()); + } + + + /** @test */ + public function withSingleNanookShouldCleanSections() { + (new Class_Migration_SigbStandardCodifications())->run(); + $this->assertEquals('995$9=4', Class_CodifSection::find(36)->getRegles()); + } + + + /** @test */ + public function withSingleNanookShouldCleanLocations() { + (new Class_Migration_SigbStandardCodifications())->run(); + $this->assertEquals('995$6=108;9', Class_CodifEmplacement::find(33)->getRegles()); + } + + + /** @test */ + public function withSingleNanookShouldCleanKinds() { + (new Class_Migration_SigbStandardCodifications())->run(); + $this->assertEquals('995$7=1', Class_CodifGenre::find(64)->getRegles()); + } +}