added configurable formats for duration (#287)

This commit is contained in:
Kevin Papst
2018-09-01 17:54:17 +02:00
committed by GitHub
parent 55b7e192aa
commit ebe007eb78
10 changed files with 78 additions and 32 deletions

View File

@@ -45,16 +45,28 @@ kimai:
languages: languages:
de: de:
date_short: 'd.m.Y' date_short: 'd.m.Y'
duration: '%%h:%%m Stunden'
duration_short: '%%h:%%m h'
en: en:
date_short: 'Y-m-d' date_short: 'Y-m-d'
duration: '%%h:%%m hours'
duration_short: '%%h:%%m h'
it: it:
date_short: 'd.m.Y' date_short: 'd.m.Y'
duration: '%%h:%%m h'
duration_short: '%%h:%%m h'
fr: fr:
date_short: 'd/m/Y' date_short: 'd/m/Y'
duration: '%%h:%%m h'
duration_short: '%%h:%%m h'
es: es:
date_short: 'd.m.Y' date_short: 'd.m.Y'
duration: '%%h:%%m h'
duration_short: '%%h:%%m h'
ru: ru:
date_short: 'd.m.Y' date_short: 'd.m.Y'
duration: '%%h:%%m h'
duration_short: '%%h:%%m h'
# timesheet calendar configuration # timesheet calendar configuration
calendar: calendar:

View File

@@ -94,7 +94,7 @@ services:
App\Twig\Extensions: App\Twig\Extensions:
arguments: arguments:
$locales: "%app_locales%" $languages: "%kimai.languages%"
App\Twig\DateExtensions: App\Twig\DateExtensions:
arguments: arguments:

View File

@@ -156,6 +156,8 @@ class Configuration implements ConfigurationInterface
->arrayPrototype() ->arrayPrototype()
->children() ->children()
->scalarNode('date_short')->end() ->scalarNode('date_short')->end()
->scalarNode('duration')->end()
->scalarNode('duration_short')->end()
->end() ->end()
->end() ->end()
; ;

View File

@@ -67,7 +67,7 @@ class DurationType extends AbstractType
$builder->addModelTransformer(new CallbackTransformer( $builder->addModelTransformer(new CallbackTransformer(
function ($intToFormat) use ($formatter) { function ($intToFormat) use ($formatter) {
try { try {
return $formatter->format($intToFormat, true); return $formatter->format($intToFormat);
} catch (\Exception $e) { } catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage()); throw new TransformationFailedException($e->getMessage());
} }

View File

@@ -85,14 +85,13 @@ class Extensions extends \Twig_Extension
]; ];
/** /**
* Extensions constructor. * @param RequestStack $requestStack
* @param string $locales * @param array $languages
* @param string $locale
*/ */
public function __construct(RequestStack $requestStack, $locales) public function __construct(RequestStack $requestStack, array $languages)
{ {
$this->requestStack = $requestStack; $this->requestStack = $requestStack;
$this->locales = explode('|', $locales); $this->locales = $languages;
$this->durationFormatter = new Duration(); $this->durationFormatter = new Duration();
} }
@@ -157,10 +156,10 @@ class Extensions extends \Twig_Extension
* Transforms seconds into a duration string. * Transforms seconds into a duration string.
* *
* @param int|Timesheet $duration * @param int|Timesheet $duration
* @param bool $includeSeconds * @param string $format
* @return string * @return string
*/ */
public function duration($duration, $includeSeconds = false) public function duration($duration, $format = null)
{ {
$seconds = $duration; $seconds = $duration;
if ($duration instanceof Timesheet) { if ($duration instanceof Timesheet) {
@@ -170,7 +169,22 @@ class Extensions extends \Twig_Extension
} }
} }
return $this->durationFormatter->format($seconds, $includeSeconds) . ' h'; $locale = $this->getLocale();
switch ($format) {
case 'full':
$format = isset($this->locales[$locale]) ? $this->locales[$locale]['duration'] : null;
break;
case null:
case 'short':
$format = isset($this->locales[$locale]) ? $this->locales[$locale]['duration_short'] : null;
break;
}
if (null === $format) {
$format = '%h:%m h';
}
return $this->durationFormatter->format($seconds, $format);
} }
/** /**
@@ -244,7 +258,7 @@ class Extensions extends \Twig_Extension
public function getLocales() public function getLocales()
{ {
$locales = []; $locales = [];
foreach ($this->locales as $locale) { foreach (array_keys($this->locales) as $locale) {
$locales[] = ['code' => $locale, 'name' => Intl::getLocaleBundle()->getLocaleName($locale, $locale)]; $locales[] = ['code' => $locale, 'name' => Intl::getLocaleBundle()->getLocaleName($locale, $locale)];
} }

View File

@@ -18,14 +18,17 @@ class Duration
public const FORMAT_NATURAL = 'natural'; public const FORMAT_NATURAL = 'natural';
public const FORMAT_SECONDS = 'seconds'; public const FORMAT_SECONDS = 'seconds';
public const FORMAT_WITH_SECONDS = '%h:%m:%s';
public const FORMAT_NO_SECONDS = '%h:%m';
/** /**
* Transforms seconds into a duration string. * Transforms seconds into a duration string.
* *
* @param $seconds * @param $seconds
* @param bool $includeSeconds * @param string $format
* @return string * @return string
*/ */
public function format($seconds, $includeSeconds = false) public function format($seconds, $format = self::FORMAT_NO_SECONDS)
{ {
$hour = floor($seconds / 3600); $hour = floor($seconds / 3600);
$minute = floor(($seconds / 60) % 60); $minute = floor(($seconds / 60) % 60);
@@ -33,14 +36,13 @@ class Duration
$hour = $hour > 9 ? $hour : '0' . $hour; $hour = $hour > 9 ? $hour : '0' . $hour;
$minute = $minute > 9 ? $minute : '0' . $minute; $minute = $minute > 9 ? $minute : '0' . $minute;
if (!$includeSeconds) {
return $hour . ':' . $minute;
}
$second = $seconds % 60; $second = $seconds % 60;
$second = $second > 9 ? $second : '0' . $second; $second = $second > 9 ? $second : '0' . $second;
return $hour . ':' . $minute . ':' . $second; $formatted = str_replace('%h', $hour, $format);
$formatted = str_replace('%m', $minute, $formatted);
return str_replace('%s', $second, $formatted);
} }
/** /**

View File

@@ -94,7 +94,7 @@
{% for widget in section.widgets|slice(1, width) %} {% for widget in section.widgets|slice(1, width) %}
{% set data = widget.data %} {% set data = widget.data %}
{% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %} {% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %}
{% set data = widget.data|duration %} {% set data = widget.data|duration('full') %}
{% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %} {% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %}
{% set data = widget.data|money %} {% set data = widget.data|money %}
{% endif %} {% endif %}

View File

@@ -17,7 +17,7 @@
{% set data = widget.data %} {% set data = widget.data %}
{% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %} {% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %}
{% set data = widget.data|duration %} {% set data = widget.data|duration('full') %}
{% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %} {% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %}
{% set data = widget.data|money %} {% set data = widget.data|money %}
{% endif %} {% endif %}

View File

@@ -21,8 +21,13 @@ use Twig\TwigFilter;
*/ */
class ExtensionsTest extends TestCase class ExtensionsTest extends TestCase
{ {
private $localeEn = ['en' => ['date_short' => 'Y-m-d', 'duration' => '%h:%m:%s h', 'duration_short' => '%h:%m h']];
private $localeDe = ['de' => ['date_short' => 'd.m.Y', 'duration' => '%h:%m:%s Stunden', 'duration_short' => '%h:%m h']];
private $localeRu = ['ru' => ['date_short' => 'd.m.Y', 'duration' => '%h:%m:%s h', 'duration_short' => '%h:%m h']];
private $localeFake = ['XX' => ['date_short' => 'd.m.Y', 'duration' => '%h Stunden, %m Minuten und %s Sekunden', 'duration_short' => '%h - %m - %s Zeit']];
/** /**
* @param string $locales * @param array $locales
* @param string $locale * @param string $locale
* @return Extensions * @return Extensions
*/ */
@@ -39,7 +44,7 @@ class ExtensionsTest extends TestCase
public function testGetFilters() public function testGetFilters()
{ {
$filters = ['duration', 'money', 'currency', 'country', 'icon']; $filters = ['duration', 'money', 'currency', 'country', 'icon'];
$sut = $this->getSut('de'); $sut = $this->getSut($this->localeDe);
$twigFilters = $sut->getFilters(); $twigFilters = $sut->getFilters();
$this->assertCount(count($filters), $twigFilters); $this->assertCount(count($filters), $twigFilters);
$i = 0; $i = 0;
@@ -52,7 +57,7 @@ class ExtensionsTest extends TestCase
public function testGetFunctions() public function testGetFunctions()
{ {
$functions = ['locales', 'is_visible_column']; $functions = ['locales', 'is_visible_column'];
$sut = $this->getSut('de'); $sut = $this->getSut($this->localeDe);
$twigFunctions = $sut->getFunctions(); $twigFunctions = $sut->getFunctions();
$this->assertCount(count($functions), $twigFunctions); $this->assertCount(count($functions), $twigFunctions);
$i = 0; $i = 0;
@@ -70,7 +75,8 @@ class ExtensionsTest extends TestCase
['code' => 'ru', 'name' => 'русский'], ['code' => 'ru', 'name' => 'русский'],
]; ];
$sut = $this->getSut('en|de|ru'); $appLocales = array_merge($this->localeEn, $this->localeDe, $this->localeRu);
$sut = $this->getSut($appLocales);
$this->assertEquals($locales, $sut->getLocales()); $this->assertEquals($locales, $sut->getLocales());
} }
@@ -82,7 +88,7 @@ class ExtensionsTest extends TestCase
'RUB' => 'RUB', 'RUB' => 'RUB',
]; ];
$sut = $this->getSut('en'); $sut = $this->getSut($this->localeEn);
foreach ($symbols as $name => $symbol) { foreach ($symbols as $name => $symbol) {
$this->assertEquals($symbol, $sut->currency($name)); $this->assertEquals($symbol, $sut->currency($name));
} }
@@ -96,7 +102,7 @@ class ExtensionsTest extends TestCase
'ES' => 'Spain', 'ES' => 'Spain',
]; ];
$sut = $this->getSut('en'); $sut = $this->getSut($this->localeEn);
foreach ($countries as $locale => $name) { foreach ($countries as $locale => $name) {
$this->assertEquals($name, $sut->country($locale)); $this->assertEquals($name, $sut->country($locale));
} }
@@ -111,7 +117,7 @@ class ExtensionsTest extends TestCase
*/ */
public function testMoney($result, $amount, $currency, $locale) public function testMoney($result, $amount, $currency, $locale)
{ {
$sut = $this->getSut('en', $locale); $sut = $this->getSut($this->localeEn, $locale);
$this->assertEquals($result, $sut->money($amount, $currency)); $this->assertEquals($result, $sut->money($amount, $currency));
} }
@@ -137,12 +143,22 @@ class ExtensionsTest extends TestCase
{ {
$record = $this->getTimesheet(9437); $record = $this->getTimesheet(9437);
$sut = $this->getSut('en'); $sut = $this->getSut($this->localeEn);
$this->assertEquals('02:37 h', $sut->duration($record->getDuration())); $this->assertEquals('02:37 h', $sut->duration($record->getDuration()));
$this->assertEquals('02:37:17 h', $sut->duration($record->getDuration(), true)); $this->assertEquals('02:37:17 h', $sut->duration($record->getDuration(), '%h:%m:%s h'));
// test Timesheet object
$this->assertEquals('02:37 h', $sut->duration($record)); $this->assertEquals('02:37 h', $sut->duration($record));
$this->assertEquals('02:37:17 h', $sut->duration($record, true)); $this->assertEquals('02:37:17', $sut->duration($record, '%h:%m:%s'));
// test extended format
$sut = $this->getSut($this->localeFake, 'XX');
$this->assertEquals('02 - 37 - 17 Zeit', $sut->duration($record->getDuration(), 'short'));
$this->assertEquals('02 Stunden, 37 Minuten und 17 Sekunden', $sut->duration($record->getDuration(), 'full'));
// test fallback format
$sut = $this->getSut($this->localeEn, 'XX');
$this->assertEquals('02:37 h', $sut->duration($record->getDuration()));
} }
protected function getTimesheet($seconds) protected function getTimesheet($seconds)
@@ -167,7 +183,7 @@ class ExtensionsTest extends TestCase
]; ];
// test pre-defined icons // test pre-defined icons
$sut = $this->getSut('en'); $sut = $this->getSut($this->localeEn);
foreach ($icons as $icon) { foreach ($icons as $icon) {
$result = $sut->icon($icon); $result = $sut->icon($icon);
$this->assertNotEmpty($result, 'Problem with icon definition: ' . $icon); $this->assertNotEmpty($result, 'Problem with icon definition: ' . $icon);

View File

@@ -21,7 +21,7 @@ class DurationTest extends TestCase
{ {
$sut = new Duration(); $sut = new Duration();
$this->assertEquals('02:38', $sut->format(9494)); $this->assertEquals('02:38', $sut->format(9494));
$this->assertEquals('02:38:14', $sut->format(9494, true)); $this->assertEquals('02:38:14', $sut->format(9494, Duration::FORMAT_WITH_SECONDS));
} }
/** /**