Metrics: Use buckets for work, worklog and vouchers
This commit is contained in:
parent
138ee996b0
commit
e4247cd0bd
|
@ -179,6 +179,12 @@ return [
|
|||
'4XL' => '4XLarge Straight-Cut',
|
||||
],
|
||||
|
||||
'metrics' => [
|
||||
// User work buckets in seconds
|
||||
'work' => [1 * 60 * 60, 1.5 * 60 * 60, 2 * 60 * 60, 3 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 20 * 60 * 60],
|
||||
'voucher' => [0, 1, 2, 3, 5, 10, 15, 20],
|
||||
],
|
||||
|
||||
// Shifts overview
|
||||
// Set max number of hours that can be shown at once
|
||||
'filter_max_duration' => 0,
|
||||
|
|
|
@ -62,6 +62,11 @@ class Controller extends BaseController
|
|||
{
|
||||
$now = microtime(true);
|
||||
$this->checkAuth();
|
||||
$metrics = $this->config->get('metrics');
|
||||
foreach (['work', 'voucher'] as $type) {
|
||||
sort($metrics[$type]);
|
||||
$metrics[$type] = array_merge($metrics[$type], ['+Inf']);
|
||||
}
|
||||
|
||||
$userTshirtSizes = $this->formatStats($this->stats->tshirtSizes(), 'tshirt_sizes', 'shirt_size', 'size');
|
||||
$userLocales = $this->formatStats($this->stats->languages(), 'locales', 'language', 'locale');
|
||||
|
@ -103,13 +108,32 @@ class Controller extends BaseController
|
|||
['labels' => ['freeloader' => true], $this->stats->currentlyWorkingUsers(true)],
|
||||
],
|
||||
'work_seconds' => [
|
||||
'type' => 'gauge',
|
||||
['labels' => ['state' => 'done'], 'value' => $this->stats->workSeconds(true, false)],
|
||||
['labels' => ['state' => 'planned'], 'value' => $this->stats->workSeconds(false, false)],
|
||||
['labels' => ['state' => 'freeloaded'], 'value' => $this->stats->workSeconds(null, true)],
|
||||
'help' => 'Working users',
|
||||
'type' => 'histogram',
|
||||
[
|
||||
'labels' => ['state' => 'done'],
|
||||
'value' => $this->stats->workBuckets($metrics['work'], true, false),
|
||||
'sum' => $this->stats->workSeconds(true, false),
|
||||
],
|
||||
[
|
||||
'labels' => ['state' => 'planned'],
|
||||
'value' => $this->stats->workBuckets($metrics['work'], false, false),
|
||||
'sum' => $this->stats->workSeconds(false, false),
|
||||
],
|
||||
[
|
||||
'labels' => ['state' => 'freeloaded'],
|
||||
'value' => $this->stats->workBuckets($metrics['work'], null, true),
|
||||
'sum' => $this->stats->workSeconds(null, true),
|
||||
],
|
||||
],
|
||||
'worklog_seconds' => [
|
||||
'type' => 'histogram',
|
||||
$this->stats->worklogBuckets($metrics['work']) + ['sum' => $this->stats->worklogSeconds()],
|
||||
],
|
||||
'vouchers' => [
|
||||
'type' => 'histogram',
|
||||
$this->stats->vouchersBuckets($metrics['voucher']) + ['sum' => $this->stats->vouchers()],
|
||||
],
|
||||
'worklog_seconds' => ['type' => 'gauge', $this->stats->worklogSeconds()],
|
||||
'vouchers' => ['type' => 'counter', $this->stats->vouchers()],
|
||||
'tshirts_issued' => ['type' => 'counter', 'help' => 'Issued T-Shirts', $this->stats->tshirts()],
|
||||
'tshirt_sizes' => [
|
||||
'type' => 'gauge',
|
||||
|
|
|
@ -110,12 +110,41 @@ class Stats
|
|||
return $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
protected function vouchersQuery()
|
||||
{
|
||||
return State::query();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function vouchers(): int
|
||||
{
|
||||
return (int)State::query()->sum('got_voucher');
|
||||
return (int)$this->vouchersQuery()->sum('got_voucher');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $buckets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function vouchersBuckets(array $buckets): array
|
||||
{
|
||||
$return = [];
|
||||
foreach ($buckets as $bucket) {
|
||||
$query = $this->vouchersQuery();
|
||||
|
||||
if ($bucket !== '+Inf') {
|
||||
$query->where('got_voucher', '<=', $bucket);
|
||||
}
|
||||
|
||||
$return[$bucket] = $query->count('got_voucher');
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,10 +219,11 @@ class Stats
|
|||
*
|
||||
* @param bool|null $done
|
||||
* @param bool|null $freeloaded
|
||||
* @return int
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function workSeconds(bool $done = null, bool $freeloaded = null): int
|
||||
protected function workSecondsQuery(bool $done = null, bool $freeloaded = null): QueryBuilder
|
||||
{
|
||||
$query = $this
|
||||
->getQuery('ShiftEntry')
|
||||
|
@ -207,20 +237,111 @@ class Stats
|
|||
$query->where('end', ($done == true ? '<' : '>='), time());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of worked shifts
|
||||
*
|
||||
* @param bool|null $done
|
||||
* @param bool|null $freeloaded
|
||||
*
|
||||
* @return int
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function workSeconds(bool $done = null, bool $freeloaded = null): int
|
||||
{
|
||||
$query = $this->workSecondsQuery($done, $freeloaded);
|
||||
|
||||
return (int)$query->sum($this->raw('end - start'));
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of worked shifts
|
||||
*
|
||||
* @param array $buckets
|
||||
* @param bool|null $done
|
||||
* @param bool|null $freeloaded
|
||||
*
|
||||
* @return array
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function workBuckets(array $buckets, bool $done = null, bool $freeloaded = null): array
|
||||
{
|
||||
return $this->getBuckets(
|
||||
$buckets,
|
||||
$this->workSecondsQuery($done, $freeloaded),
|
||||
'UID',
|
||||
'SUM(end - start)',
|
||||
'end - start'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $buckets
|
||||
* @param QueryBuilder $basicQuery
|
||||
* @param string $groupBy
|
||||
* @param string $having
|
||||
* @param string $count
|
||||
*
|
||||
* @return array
|
||||
* @codeCoverageIgnore As long as its only used for old tables
|
||||
*/
|
||||
protected function getBuckets(array $buckets, $basicQuery, string $groupBy, string $having, string $count): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
foreach ($buckets as $bucket) {
|
||||
$query = clone $basicQuery;
|
||||
$query->groupBy($groupBy);
|
||||
|
||||
if ($bucket !== '+Inf') {
|
||||
$query->having($this->raw($having), '<=', $bucket);
|
||||
}
|
||||
|
||||
$return[$bucket] = $query->count($this->raw($count));
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryBuilder
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected function worklogSecondsQuery()
|
||||
{
|
||||
return $this
|
||||
->getQuery('UserWorkLog');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function worklogSeconds(): int
|
||||
{
|
||||
return (int)$this
|
||||
->getQuery('UserWorkLog')
|
||||
return (int)$this->worklogSecondsQuery()
|
||||
->sum($this->raw('work_hours * 60 * 60'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $buckets
|
||||
*
|
||||
* @return array
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function worklogBuckets(array $buckets): array
|
||||
{
|
||||
return $this->getBuckets(
|
||||
$buckets,
|
||||
$this->worklogSecondsQuery(),
|
||||
'user_id',
|
||||
'SUM(work_hours * 60 * 60)',
|
||||
'work_hours * 60 * 60'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @codeCoverageIgnore
|
||||
|
|
|
@ -12,7 +12,7 @@ class ControllerTest extends ApplicationFeatureTest
|
|||
*/
|
||||
public function testMetrics()
|
||||
{
|
||||
config(['api_key' => null]);
|
||||
config(['api_key' => null, 'metrics' => ['work' => [60 * 60], 'voucher' => [1]]]);
|
||||
|
||||
/** @var Controller $controller */
|
||||
$controller = app()->make(Controller::class);
|
||||
|
|
|
@ -151,6 +151,10 @@ class ControllerTest extends TestCase
|
|||
'0' => 'Nothing',
|
||||
'1' => 'Testing',
|
||||
]);
|
||||
$config->set('metrics', [
|
||||
'work' => [60 * 60],
|
||||
'voucher' => [1]
|
||||
]);
|
||||
|
||||
$this->setExpects($version, 'getVersion', [], '0.42.42');
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class StatsTest extends TestCase
|
|||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Metrics\Stats::vouchers
|
||||
* @covers \Engelsystem\Controllers\Metrics\Stats::vouchersQuery
|
||||
*/
|
||||
public function testVouchers()
|
||||
{
|
||||
|
@ -45,6 +46,17 @@ class StatsTest extends TestCase
|
|||
$this->assertEquals(14, $stats->vouchers());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Metrics\Stats::vouchersBuckets
|
||||
*/
|
||||
public function testVouchersBuckets()
|
||||
{
|
||||
$this->addUsers();
|
||||
|
||||
$stats = new Stats($this->database);
|
||||
$this->assertEquals([1 => 6, 3 => 8, '+Inf' => 9], $stats->vouchersBuckets([1, 3, '+Inf']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Metrics\Stats::tshirts
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue