Commit 1c76db69 authored by Patrick Barroca's avatar Patrick Barroca 😁
Browse files

dev #99468 : library openings in OpenStreetMap opening_hours format

parent 7649f5a1
Pipeline #8936 passed with stage
in 43 minutes and 34 seconds
- ticket #99468 : Open Data : Ajout de la représentation des ouvertures au format OpenStreetMap opening_hours dans la liste des bibliothèques en JSON
\ No newline at end of file
......@@ -82,9 +82,10 @@ class BibController extends ZendAfi_Controller_Action {
$openings = [];
foreach($library->getOuvertures() as $ouverture)
$openings[] = $ouverture->getRawAttributes();
$fields['openings'] = $openings;
$fields['opening_hours'] = $library->getOpeningHours();
$fields['latitude'] = '';
$fields['longitude'] = '';
......@@ -165,7 +166,7 @@ class BibController extends ZendAfi_Controller_Action {
}
function mapviewAction() {
public function mapviewAction() {
if (!$library = Class_Bib::find((int)$this->_request->getParam('id_bib')))
return $this->_redirect('opac/bib/index');
......@@ -310,6 +311,18 @@ class BibController extends ZendAfi_Controller_Action {
}
public function openinghoursAction() {
if (!$library = Class_Bib::find((int)$this->_getParam('id')))
return $this->_redirect('opac/bib/index');
$this->_helper->getHelper('viewRenderer')->setNoRender(true);
if ($layout = Zend_Layout::getMvcInstance())
$layout->disableLayout();
echo $library->getOpeningHours();
}
public function enLirePlusAction() {
$this->_initLibrary();
}
......
......@@ -1024,4 +1024,9 @@ class Class_Bib extends Storm_Model_Abstract {
? USERFILESURL . static::BASE_PATH . $picture
: BASE_URL . $picture;
}
public function getOpeningHours() {
return (new Class_Bib_OpeningHours())->format($this);
}
}
<?php
/**
* Copyright (c) 2012-2019, 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_Bib_OpeningHours {
protected $_specs;
public function format($library) {
if (!$library)
return '';
$this->_specs = new Storm_Collection();
$visitor = new Class_Ouverture_Visitor;
$library->acceptOpeningsVisitor($visitor);
if (!$visitor->hasOpenings())
return '';
$this->_collect($visitor->getDefault());
if ($library->isClosedOnHolidays())
$this->_specs->append('PH off');
foreach($visitor->getPeriodical() as $grouped)
$this->_collect($grouped);
$this->_collect($visitor->getExceptional())
->_collect($visitor->getClosure());
return implode('; ', $this->_specs->getArrayCopy());
}
protected function _collect($openings) {
$specs = array_filter(array_map(function($each)
{
return $each->asOpeningHours();
},
$openings));
$this->_specs->addAll($specs);
return $this;
}
}
......@@ -324,11 +324,21 @@ class Class_Ouverture extends Storm_Model_Abstract {
public function isClosed() {
foreach(['debut_matin', 'fin_matin', 'debut_apres_midi', 'fin_apres_midi'] as $field)
if (!$this->getLoader()->isValueClosed($this->callGetterByAttributeName($field)))
return false;
return $this->isClosedAm() && $this->isClosedPm();
}
public function isClosedAm() {
$loader = $this->getLoader();
return $loader->isValueClosed($this->getDebutMatin())
&& $loader->isValueClosed($this->getFinMatin());
}
return true;
public function isClosedPm() {
$loader = $this->getLoader();
return $loader->isValueClosed($this->getDebutApresMidi())
&& $loader->isValueClosed($this->getFinApresMidi());
}
......@@ -338,4 +348,9 @@ class Class_Ouverture extends Storm_Model_Abstract {
return (new static())->updateAttributes($attributes);
}
public function asOpeningHours() {
return (new Class_Ouverture_OpeningHours)->format($this);
}
}
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2019, 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
*/
/**
* I format a Class_Ouverture in OSM opening_hours tag format
*
* https://wiki.openstreetmap.org/wiki/Key:opening_hours
*/
class Class_Ouverture_OpeningHours {
protected $_opening;
public function __call($name, $args) {
if ($this->_opening)
return call_user_func_array([$this->_opening, $name], $args);
throw new RuntimeException('Call to unknown method Class_Ouverture_OpeningHours::' . $name);
}
public function format($opening) {
if (!$opening)
return '';
$this->_opening = $opening;
return $this->_day() . ' ' . $this->_times();
}
protected function _day() {
if ($this->isExceptional())
return $this->_date($this->getJour());
return $this->hasJourSemaine()
? $this->_period() . $this->_weekday()
: '';
}
protected function _date($day) {
return date('Y M d', strtotime($day));
}
protected function _weekday() {
$map = [Class_Ouverture::LUNDI => 'Mo',
Class_Ouverture::MARDI => 'Tu',
Class_Ouverture::MERCREDI => 'We',
Class_Ouverture::JEUDI => 'Th',
Class_Ouverture::VENDREDI => 'Fr',
Class_Ouverture::SAMEDI => 'Sa',
Class_Ouverture::DIMANCHE => 'Su'];
$code = $this->getJourSemaine();
return isset($map[$code])
? $map[$code]
: '';
}
protected function _period() {
if (!$this->hasValidityRange())
return '';
if ($this->hasValidityStart()
&& $this->hasValidityEnd())
return $this->_date($this->getValidityStart())
. '-' . $this->_date($this->getValidityEnd())
. ' ';
}
protected function _times() {
$parts = [];
if (!$this->isClosedAm())
$parts[] = $this->getDebutMatin() . '-' . $this->getFinMatin();
if (!$this->isClosedPm())
$parts[] = $this->getDebutApresMidi() . '-' . $this->getFinApresMidi();
return $parts
? implode(',', $parts)
: 'off';
}
}
......@@ -110,15 +110,15 @@ class Class_Ouverture_Visitor {
if ($this->_should_keep_all)
return true;
$now = $this->getCurrentTime();
$now_plus_30 = $this->_addToTime($now, 30);
$today_at_midnight = strtotime($this->getCurrentDate());
$today_plus_30_at_midnight = $this->_addToTime($today_at_midnight, 30);
if ($opening->isExceptional()) {
$opening_day = strtotime($opening->getJour());
return $now <= $opening_day && $now_plus_30 >= $opening_day;
return $today_at_midnight <= $opening_day && $today_plus_30_at_midnight >= $opening_day;
}
return $opening->isValidDuring($now, $now_plus_30);
return $opening->isValidDuring($today_at_midnight, $today_plus_30_at_midnight);
}
......@@ -160,7 +160,7 @@ class Class_Ouverture_Visitor {
protected function _getType($type) {
return $this->_hasType($type)
? $this->_openings[$type]
: null;
: [];
}
......@@ -178,39 +178,6 @@ class Class_Ouverture_Visitor {
}
protected function _renderTimeSegment($from, $to) {
if ($from == $to)
return $this->_renderHour($from);
$from = $this->_renderHour($from);
$to = $this->_renderHour($to);
return $from && $to
? $from . ' - ' . $to
: '';
}
protected function _renderHour($hour) {
return '00:00' == $hour ? '' : str_replace(':', 'h', $hour);
}
protected function _renderTimes($am, $pm) {
if ('' != $am && '' != $pm) {
if (substr($am, -5, 5) == substr($pm, 0, 5))
return substr($am, 0, 5) . substr($pm, 5);
return $am . ' / ' . $pm;
}
if ($am)
return $am;
return $pm;
}
protected function _escapeInfo($info) {
return str_replace('BR', '<br />', urldecode(str_replace('%0D%0A','BR', $info)));
}
......
......@@ -218,9 +218,11 @@ class BibControllerIndexActionFormatJsonTest extends AbstractControllerTestCase
* @test
* @depends annecyShouldHave2CustomFields
*/
public function firstAnnecyFieldShouldBePublic($fields) {
$this->assertEquals('Public', $fields[0]->label);
$this->assertEquals('', $fields[0]->value);
public function annecyFieldPublicShouldBePresent($fields) {
$field = (new Storm_Collection($fields))
->detect(function($item) { return 'Public' == $item->label; });
$this->assertNotNull($field);
$this->assertEquals('', $field->value);
}
......@@ -228,9 +230,11 @@ class BibControllerIndexActionFormatJsonTest extends AbstractControllerTestCase
* @test
* @depends annecyShouldHave2CustomFields
*/
public function secondAnnecyFieldShouldBeServices($fields) {
$this->assertEquals('Services', $fields[1]->label);
$this->assertEquals('Wifi;Restauration', $fields[1]->value);
public function annecyFieldServicesShouldBePresent($fields) {
$field = (new Storm_Collection($fields))
->detect(function($item) { return 'Services' == $item->label; });
$this->assertNotNull($field);
$this->assertEquals('Wifi;Restauration', $field->value);
}
......@@ -238,4 +242,10 @@ class BibControllerIndexActionFormatJsonTest extends AbstractControllerTestCase
public function annecyShouldBeOpenedTuesday() {
$this->assertEquals('2', $this->_json[0]->openings[0]->jour_semaine);
}
/** @test */
public function annecyShouldHaveOpeningHoursTuesday10to12ClosedOnPublicHolidays() {
$this->assertEquals('Tu 10:00-12:00; PH off', $this->_json[0]->opening_hours);
}
}
......@@ -936,6 +936,139 @@ class BibControllerBibViewAnnecyRangeOpeningsTest extends BibControllerLibraryWi
class BibControllerOpeningHoursAnnecyTest extends BibControllerWithZoneTestCase {
public function setUp() {
parent::setUp();
Class_Ouverture_Visitor::setTimeSource(new TimeSourceForTest('2019-12-11 09:16:55'));
// open on mondays
$this->fixture('Class_Ouverture',
['id' => 33,
'id_site' => 4,
'jour_semaine' => Class_Ouverture::LUNDI,
'debut_matin' => '00:00',
'fin_matin' => '00:00',
'debut_apres_midi' => '18:00',
'fin_apres_midi' => '19:30',
]);
// open on tuesdays
$this->fixture('Class_Ouverture',
['id' => 34,
'id_site' => 4,
'jour_semaine' => Class_Ouverture::MARDI,
'debut_matin' => '09:00',
'fin_matin' => '12:00',
'debut_apres_midi' => '13:00',
'fin_apres_midi' => '19:30',
]);
// open on day
$this->fixture('Class_Ouverture',
['id' => 35,
'id_site' => 4,
'jour' => '2019-12-11',
'debut_matin' => '09:00',
'fin_matin' => '12:00',
'debut_apres_midi' => '00:00',
'fin_apres_midi' => '00:00',
]);
// open on period
$this->fixture('Class_Ouverture',
['id' => 36,
'id_site' => 4,
'jour_semaine' => Class_Ouverture::LUNDI,
'debut_matin' => '09:00',
'fin_matin' => '12:00',
'debut_apres_midi' => '00:00',
'fin_apres_midi' => '00:00',
'validity_start' => '2019-12-01',
'validity_end' => '2019-12-31'
]);
// closed on day
$this->fixture('Class_Ouverture',
['id' => 37,
'id_site' => 4,
'jour' => '2019-12-24',
'debut_matin' => '00:00',
'fin_matin' => '00:00',
'debut_apres_midi' => '00:00',
'fin_apres_midi' => '00:00',
]);
// closed on period
$this->fixture('Class_Ouverture',
['id' => 38,
'id_site' => 4,
'jour_semaine' => Class_Ouverture::LUNDI,
'debut_matin' => '00:00',
'fin_matin' => '00:00',
'debut_apres_midi' => '00:00',
'fin_apres_midi' => '00:00',
'validity_start' => '2019-12-19',
'validity_end' => '2019-12-23'
]);
Class_Bib::find(4)->setClosedOnHolidays(true)->assertSave();
$this->dispatch('bib/opening_hours/id/4', true);
}
public function tearDown() {
Class_Ouverture_Visitor::setTimeSource(null);
parent::tearDown();
}
/** @test */
public function shouldContainsEachMonday() {
$this->assertContains('Mo 18:00-19:30', $this->_response->getBody());
}
/** @test */
public function shouldContainsEachTuesday() {
$this->assertContains('Tu 09:00-12:00,13:00-19:30', $this->_response->getBody());
}
/** @test */
public function shouldContains2019_12_11() {
$this->assertContains('2019 Dec 11 09:00-12:00', $this->_response->getBody());
}
/** @test */
public function shouldContainsMondayFrom01_12To31_12() {
$this->assertContains('2019 Dec 01-2019 Dec 31 Mo 09:00-12:00', $this->_response->getBody());
}
/** @test */
public function shouldBeClosedOn2019_12_24() {
$this->assertContains('2019 Dec 24 off', $this->_response->getBody());
}
/** @test */
public function shouldBeClosedMondayFrom19_12To23_12() {
$this->assertContains('2019 Dec 19-2019 Dec 23 Mo off', $this->_response->getBody());
}
/** @test */
public function shouldBeClosedOnHolidays() {
$this->assertContains('PH off', $this->_response->getBody());
}
}
class BibControllerBibViewAnnecyWithOutdatedRangeOpeningsTest extends BibControllerBibViewTestCase {
public function setUp() {
parent::setUp();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment