Refactored the way we store metrics

This commit is contained in:
James Brooks
2016-03-02 12:09:57 +00:00
committed by James Brooks
parent 3730ca8811
commit f9bc46b460
25 changed files with 329 additions and 104 deletions

View File

@@ -69,6 +69,13 @@ final class AddMetricCommand
*/
public $default_view;
/**
* The threshold to buffer the metric points in.
*
* @var int
*/
public $threshold;
/**
* The validation rules.
*
@@ -84,6 +91,7 @@ final class AddMetricCommand
'display_chart' => 'int',
'places' => 'int|between:0,4',
'default_view' => 'int|between:0,3',
'threshold' => 'numeric|between:0,10',
];
/**
@@ -97,10 +105,11 @@ final class AddMetricCommand
* @param int $display_chart
* @param int $places
* @param int $default_view
* @param int $threshold
*
* @return void
*/
public function __construct($name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view)
public function __construct($name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view, $threshold)
{
$this->name = $name;
$this->suffix = $suffix;
@@ -110,5 +119,6 @@ final class AddMetricCommand
$this->display_chart = $display_chart;
$this->places = $places;
$this->default_view = $default_view;
$this->threshold = $threshold;
}
}

View File

@@ -78,6 +78,13 @@ final class UpdateMetricCommand
*/
public $default_view;
/**
* The threshold to buffer the metric points in.
*
* @var int
*/
public $threshold;
/**
* The validation rules.
*
@@ -93,6 +100,7 @@ final class UpdateMetricCommand
'display_chart' => 'int',
'places' => 'numeric|between:0,4',
'default_view' => 'numeric|between:0,4',
'threshold' => 'numeric|between:0,10',
];
/**
@@ -107,10 +115,11 @@ final class UpdateMetricCommand
* @param int $display_chart
* @param int $places
* @param int $default_view
* @param int $threshold
*
* @return void
*/
public function __construct(Metric $metric, $name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view)
public function __construct(Metric $metric, $name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view, $threshold)
{
$this->metric = $metric;
$this->name = $name;
@@ -121,5 +130,6 @@ final class UpdateMetricCommand
$this->display_chart = $display_chart;
$this->places = $places;
$this->default_view = $default_view;
$this->threshold = $threshold;
}
}

View File

@@ -14,6 +14,11 @@ namespace CachetHQ\Cachet\Bus\Commands\Metric;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
/**
* This is the update metric point command.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateMetricPointCommand
{
/**
@@ -33,7 +38,7 @@ final class UpdateMetricPointCommand
/**
* The metric point value.
*
* @var int
* @var float
*/
public $value;
@@ -50,7 +55,7 @@ final class UpdateMetricPointCommand
* @var string[]
*/
public $rules = [
'value' => 'int',
'value' => 'numeric',
'created_at' => 'string',
];
@@ -59,7 +64,7 @@ final class UpdateMetricPointCommand
*
* @param \CachetHQ\Cachet\Models\MetricPoint $point
* @param \CachetHQ\Cachet\Models\Metric $metric
* @param int $value
* @param float $value
* @param string $created_at
*
* @return void

View File

@@ -35,6 +35,7 @@ class AddMetricCommandHandler
'display_chart' => $command->display_chart,
'places' => $command->places,
'default_view' => $command->default_view,
'threshold' => $command->threshold,
]);
event(new MetricWasAddedEvent($metric));

View File

@@ -15,6 +15,7 @@ use CachetHQ\Cachet\Bus\Commands\Metric\AddMetricPointCommand;
use CachetHQ\Cachet\Bus\Events\Metric\MetricPointWasAddedEvent;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\MetricPoint;
use Carbon\Carbon;
class AddMetricPointCommandHandler
{
@@ -49,19 +50,35 @@ class AddMetricPointCommandHandler
$metric = $command->metric;
$createdAt = $command->created_at;
$data = [
'metric_id' => $metric->id,
'value' => $command->value,
];
// Do we have an existing point with the same value?
$point = $this->findOrCreatePoint($command);
if ($createdAt) {
$data['created_at'] = $this->dates->create('U', $createdAt)->format('Y-m-d H:i:s');
$point->increment('counter', 1);
event(new MetricPointWasAddedEvent($point));
return $point;
}
protected function findOrCreatePoint(AddMetricPointCommand $command)
{
$buffer = Carbon::now()->subMinutes($command->metric->threshold);
$point = MetricPoint::where('metric_id', $command->metric->id)->where('value', $command->value)->where('created_at', '>=', $buffer)->first();
if ($point) {
return $point;
}
$metricPoint = MetricPoint::create($data);
$data = [
'metric_id' => $command->metric->id,
'value' => $command->value,
'counter' => 0,
];
event(new MetricPointWasAddedEvent($metricPoint));
if ($command->created_at) {
$data['created_at'] = $this->dates->create('U', $command->created_at)->format('Y-m-d H:i:s');
}
return $metricPoint;
return MetricPoint::create($data);
}
}

View File

@@ -53,6 +53,7 @@ class UpdateMetricCommandHandler
'display_chart' => $command->display_chart,
'places' => $command->places,
'default_view' => $command->default_view,
'threshold' => $command->threshold,
];
return array_filter($params, function ($val) {

View File

@@ -51,7 +51,7 @@ class UpdateMetricPointCommandHandler
$data = [
'metric_id' => $metric->id,
'value' => $command->value,
'value' => (float) $command->value,
];
if ($createdAt) {

View File

@@ -84,7 +84,8 @@ class MetricController extends AbstractApiController
Binput::get('calc_type', 0),
Binput::get('display_chart', true),
Binput::get('places', 2),
Binput::get('view', 1)
Binput::get('view', 1),
Binput::get('threshold', 5)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
@@ -112,7 +113,8 @@ class MetricController extends AbstractApiController
Binput::get('calc_type'),
Binput::get('display_chart'),
Binput::get('places'),
Binput::get('view')
Binput::get('view'),
Binput::get('threshold', 5)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();

View File

@@ -48,8 +48,8 @@ class MetricPointController extends AbstractApiController
$metricPoint = dispatch(new AddMetricPointCommand(
$metric,
Binput::get('value'),
Binput::get('timestamp'))
);
Binput::get('timestamp')
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}

View File

@@ -79,7 +79,8 @@ class MetricController extends Controller
$metricData['calc_type'],
$metricData['display_chart'],
$metricData['places'],
$metricData['default_view']
$metricData['default_view'],
$metricData['threshold']
));
} catch (ValidationException $e) {
return Redirect::route('dashboard.metrics.add')
@@ -151,7 +152,8 @@ class MetricController extends Controller
Binput::get('calc_type', null, false),
Binput::get('display_chart', null, false),
Binput::get('places', null, false),
Binput::get('default_view', null, false)
Binput::get('default_view', null, false),
Binput::get('threshold', null, false)
));
} catch (ValidationException $e) {
return Redirect::route('dashboard.metrics.edit', ['id' => $metric->id])

View File

@@ -47,6 +47,7 @@ class Metric extends Model implements HasPresenter
'calc_type' => 0,
'places' => 2,
'default_view' => 1,
'threshold' => 5,
];
/**
@@ -61,6 +62,7 @@ class Metric extends Model implements HasPresenter
'calc_type' => 'int',
'places' => 'int',
'default_view' => 'int',
'threshold' => 'int',
];
/**
@@ -77,6 +79,7 @@ class Metric extends Model implements HasPresenter
'calc_type',
'places',
'default_view',
'threshold',
];
/**
@@ -91,6 +94,7 @@ class Metric extends Model implements HasPresenter
'default_value' => 'numeric',
'places' => 'numeric|between:0,4',
'default_view' => 'numeric|between:0,3',
'threshold' => 'numeric|between:0,10',
];
/**

View File

@@ -20,6 +20,16 @@ class MetricPoint extends Model implements HasPresenter
{
use ValidatingTrait;
/**
* The model's attributes.
*
* @var string[]
*/
protected $attributes = [
'value' => 0,
'counter' => 1,
];
/**
* The attributes that should be casted to native types.
*
@@ -27,7 +37,8 @@ class MetricPoint extends Model implements HasPresenter
*/
protected $casts = [
'metric_id' => 'int',
'value' => 'int',
'value' => 'float',
'counter' => 'int',
];
/**
@@ -35,7 +46,12 @@ class MetricPoint extends Model implements HasPresenter
*
* @var string[]
*/
protected $fillable = ['metric_id', 'value', 'created_at'];
protected $fillable = [
'metric_id',
'value',
'counter',
'created_at',
];
/**
* The validation rules.
@@ -53,7 +69,25 @@ class MetricPoint extends Model implements HasPresenter
*/
public function metric()
{
return $this->belongsTo(Metric::class, 'id', 'metric_id');
return $this->belongsTo(Metric::class);
}
/**
* Override the value attribute.
*
* @param mixed $value
*
* @return float
*/
public function getActiveValueAttribute($value)
{
if ($this->metric->calc_type === Metric::CALC_SUM) {
return round((float) $value * $this->counter, $this->metric->places);
} elseif ($this->metric->calc_type === Metric::CALC_AVG) {
return round((float) $value * $this->counter, $this->metric->places);
}
return round((float) $value, $this->metric->places);
}
/**

View File

@@ -19,6 +19,16 @@ class MetricPointPresenter extends BasePresenter implements Arrayable
{
use TimestampsTrait;
/**
* Show the actual calculated value; as per (value * counter).
*
* @return int
*/
public function calculated_value()
{
return $this->wrappedObject->value * $this->wrappedObject->counter;
}
/**
* Convert the presenter instance to an array.
*
@@ -27,8 +37,9 @@ class MetricPointPresenter extends BasePresenter implements Arrayable
public function toArray()
{
return array_merge($this->wrappedObject->toArray(), [
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
'calculated_value' => $this->calculated_value(),
]);
}
}

View File

@@ -105,7 +105,7 @@ class MetricRepository
$points = [];
$pointKey = $dateTime->format('jS M');
$pointKey = $dateTime->format('D jS M');
for ($i = 0; $i <= 7; $i++) {
$points[$pointKey] = $this->repository->getPointsForDayInWeek($metric, $i);

View File

@@ -32,14 +32,21 @@ class MySqlRepository implements MetricInterface
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
$timeInterval = $dateTime->format('YmdHi');
$points = $metric->points()
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d%H%i") = '.$timeInterval)
->groupBy(DB::raw('HOUR(created_at), MINUTE(created_at)'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND DATE_FORMAT(mp.`created_at`, '%Y%m%d%H%i') = :timeInterval GROUP BY HOUR(mp.`created_at`), MINUTE(mp.`created_at`)", [
'metricId' => $metric->id,
'timeInterval' => $timeInterval,
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -62,14 +69,21 @@ class MySqlRepository implements MetricInterface
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
$hourInterval = $dateTime->format('YmdH');
$points = $metric->points()
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d%H") = '.$hourInterval)
->groupBy(DB::raw('HOUR(created_at)'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND DATE_FORMAT(mp.`created_at`, '%Y%m%d%H') = :hourInterval GROUP BY HOUR(mp.`created_at`)", [
'metricId' => $metric->id,
'hourInterval' => $hourInterval,
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -90,15 +104,21 @@ class MySqlRepository implements MetricInterface
{
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
$points = $metric->points()
->whereRaw('created_at BETWEEN DATE_SUB(created_at, INTERVAL 1 WEEK) AND NOW()')
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d") = '.$dateTime->format('Ymd'))
->groupBy(DB::raw('DATE_FORMAT(created_at, "%Y%m%d")'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND mp.`created_at` BETWEEN DATE_SUB(mp.`created_at`, INTERVAL 1 WEEK) AND DATE_ADD(NOW(), INTERVAL 1 DAY) AND DATE_FORMAT(mp.`created_at`, '%Y%m%d') = :timeInterval GROUP BY DATE_FORMAT(mp.`created_at`, '%Y%m%d')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('Ymd'),
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {

View File

@@ -30,26 +30,24 @@ class PgSqlRepository implements MetricInterface
public function getPointsLastHour(Metric $metric, $hour, $minute)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
$hourInterval = $dateTime->format('YmdHi');
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$queryType = 'avg(metric_points.value)';
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$query = DB::select("select {$queryType} as aggregate FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metric_id AND to_char(metric_points.created_at, 'YYYYMMDDHH24MI') = :timestamp GROUP BY to_char(metric_points.created_at, 'HHMI')", [
'metric_id' => $metric->id,
'timestamp' => $hourInterval,
$value = 0;
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND to_char(metric_points.created_at, 'YYYYMMDDHH24MI') = :timeInterval GROUP BY to_char(metric_points.created_at, 'HHMI')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdHi'),
]);
if (isset($query[0])) {
$value = $query[0]->aggregate;
} else {
$value = 0;
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -70,26 +68,24 @@ class PgSqlRepository implements MetricInterface
public function getPointsByHour(Metric $metric, $hour)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
$hourInterval = $dateTime->format('YmdH');
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$queryType = 'avg(metric_points.value)';
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$query = DB::select("select {$queryType} as aggregate FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metric_points.metric_id = :metric_id AND to_char(metric_points.created_at, 'YYYYMMDDHH24') = :timestamp GROUP BY to_char(metric_points.created_at, 'H')", [
'metric_id' => $metric->id,
'timestamp' => $hourInterval,
$value = 0;
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metric_points.metric_id = :metricId AND to_char(metric_points.created_at, 'YYYYMMDDHH24') = :timeInterval GROUP BY to_char(metric_points.created_at, 'H')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdH'),
]);
if (isset($query[0])) {
$value = $query[0]->aggregate;
} else {
$value = 0;
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -110,15 +106,20 @@ class PgSqlRepository implements MetricInterface
{
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
$points = $metric->points()
->whereRaw('created_at BETWEEN (created_at - interval \'1 week\') AND now()')
->whereRaw('to_char(created_at, \'YYYYMMDD\') = \''.$dateTime->format('Ymd').'\'')
->groupBy(DB::raw('to_char(created_at, \'YYYYMMDD\')'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND mp.`created_at` BETWEEN (mp.`created_at` - interval '1 week') AND (now() + interval 1 day) AND to_char(mp.`created_at`, 'YYYYMMDD') = :timeInterval GROUP BY to_char(mp.`created_at`, 'YYYYMMDD')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('Ymd'),
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {

View File

@@ -30,16 +30,24 @@ class SqliteRepository implements MetricInterface
public function getPointsLastHour(Metric $metric, $hour, $minute)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
$hourInterval = $dateTime->format('YmdHi');
$points = $metric->points()
->whereRaw('strftime("%Y%m%d%H%M", created_at) = "'.$hourInterval.'"')
->groupBy(DB::raw('strftime("%H%M", created_at)'));
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$value = 0;
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND strftime('%Y%m%d%H%M', metric_points.created_at) = :timeInterval GROUP BY strftime('%H%M', metric_points.created_at)", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdHi'),
]);
if (isset($query[0])) {
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -60,16 +68,24 @@ class SqliteRepository implements MetricInterface
public function getPointsByHour(Metric $metric, $hour)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
$hourInterval = $dateTime->format('YmdH');
$points = $metric->points()
->whereRaw('strftime("%Y%m%d%H", created_at) = "'.$hourInterval.'"')
->groupBy(DB::raw('strftime("%H", created_at)'));
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$value = 0;
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND strftime('%Y%m%d%H', metric_points.created_at) = :timeInterval GROUP BY strftime('%H', metric_points.created_at)", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdH'),
]);
if (isset($query[0])) {
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -90,15 +106,23 @@ class SqliteRepository implements MetricInterface
{
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
$points = $metric->points()
->whereRaw('created_at > date("now", "-7 day")')
->whereRaw('strftime("%Y%m%d", created_at) = "'.$dateTime->format('Ymd').'"')
->groupBy(DB::raw('strftime("%Y%m%d", created_at)'));
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$value = 0;
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND metric_points.created_at > date('now', '-7 day') AND strftime('%Y%m%d', metric_points.created_at) = :timeInterval GROUP BY strftime('%Y%m%d', metric_points.created_at)", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('Ymd'),
]);
if (isset($query[0])) {
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {

View File

@@ -61,8 +61,10 @@ $factory->define(Metric::class, function ($faker) {
'suffix' => $faker->word(),
'description' => $faker->paragraph(),
'default_value' => 1,
'places' => 2,
'calc_type' => $faker->boolean(),
'display_chart' => $faker->boolean(),
'threshold' => 5,
];
});
@@ -70,6 +72,7 @@ $factory->define(MetricPoint::class, function ($faker) {
return [
'metric_id' => factory(Metric::class)->create()->id,
'value' => random_int(1, 100),
'counter' => 1,
];
});

View File

@@ -0,0 +1,49 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AlterTableMetricPointsAddCounterColumn extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('metrics', function (Blueprint $table) {
$table->integer('threshold')->unsigned()->default(5)->after('default_view');
});
Schema::table('metric_points', function (Blueprint $table) {
$table->integer('counter')->unsigned()->default(1)->after('value');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('metrics', function (Blueprint $table) {
$table->dropColumn('threshold');
});
Schema::table('metric_points', function (Blueprint $table) {
$table->dropColumn('counter');
});
}
}

View File

@@ -89,6 +89,7 @@ return [
'type_avg' => 'Average',
'places' => 'Decimal places',
'default_view' => 'Default view',
'threshold' => 'How many minutes of threshold between metric points?',
'points' => [
'value' => 'Value',

View File

@@ -55,6 +55,10 @@
<label for="metric-places">{{ trans('forms.metrics.places') }}</label>
<input type="number" min="0" max="4" class="form-control" name="metric[places]" id="metric-places" required value="{{ Binput::old('metric.places') }}">
</div>
<div class="form-group">
<label for="metric-places">{{ trans('forms.metrics.threshold') }}</label>
<input type="number" min="0" max="100" class="form-control" name="metric[threshold]" id="metric-threshold" required value="{{ Binput::old('metric.threshold') }}">
</div>
<div class="checkbox">
<label>
<input type="hidden" value="0" name="metric[display_chart]">

View File

@@ -55,6 +55,10 @@
<label for="metric-places">{{ trans('forms.metrics.places') }}</label>
<input type="number" min="0" max="4" class="form-control" name="places" id="metric-places" required value="{{ $metric->places }}">
</div>
<div class="form-group">
<label for="metric-places">{{ trans('forms.metrics.threshold') }}</label>
<input type="number" min="0" max="100" class="form-control" name="threshold" id="metric-threshold" required value="{{ $metric->threshold }}">
</div>
<div class="checkbox">
<label>
<input type="hidden" value="0" name="display_chart">

View File

@@ -29,9 +29,12 @@ class MetricPointTest extends AbstractApiTestCase
]);
$this->get("/api/v1/metrics/{$metric->id}/points");
$this->seeJson(['id' => $metricPoint[0]->id]);
$this->seeJson(['id' => $metricPoint[1]->id]);
$this->seeJson(['id' => $metricPoint[2]->id]);
$this->assertResponseOk();
}
public function testPostMetricPointUnauthorized()
@@ -55,7 +58,10 @@ class MetricPointTest extends AbstractApiTestCase
]);
$this->post("/api/v1/metrics/{$metric->id}/points", $metricPoint->toArray());
$this->seeJson(['value' => $metricPoint->value]);
$this->assertResponseOk();
}
public function testPostMetricPointTimestamp()
@@ -72,7 +78,10 @@ class MetricPointTest extends AbstractApiTestCase
$postData['timestamp'] = $timestamp;
$this->post("/api/v1/metrics/{$metric->id}/points", $postData);
$this->seeJson(['value' => $metricPoint->value, 'created_at' => $datetime]);
$this->assertResponseOk();
}
public function testPostMetricPointTimestampTimezone()
@@ -96,7 +105,10 @@ class MetricPointTest extends AbstractApiTestCase
$postData['timestamp'] = $datetime->timestamp;
$this->post("/api/v1/metrics/{$metric->id}/points", $postData, ['Time-Zone' => $timezone]);
$this->seeJson(['value' => $metricPoint->value, 'created_at' => $datetime->toDateTimeString()]);
$this->assertResponseOk();
}
public function testPutMetricPoint()
@@ -106,10 +118,14 @@ class MetricPointTest extends AbstractApiTestCase
$metricPoint = factory('CachetHQ\Cachet\Models\MetricPoint')->create([
'metric_id' => $metric->id,
]);
$this->put("/api/v1/metrics/{$metric->id}/points/{$metricPoint->id}", [
'value' => 999,
]);
$this->seeJson(['value' => 999]);
$this->assertResponseOk();
}
public function testDeleteMetricPoint()
@@ -119,7 +135,9 @@ class MetricPointTest extends AbstractApiTestCase
$metricPoint = factory('CachetHQ\Cachet\Models\MetricPoint')->create([
'metric_id' => $metric->id,
]);
$this->delete("/api/v1/metrics/{$metric->id}/points/{$metricPoint->id}");
$this->assertResponseStatus(204);
}
}

View File

@@ -29,14 +29,15 @@ class AddMetricCommandTest extends AbstractTestCase
protected function getObjectAndParams()
{
$params = [
'name' => 'Coffee',
'suffix' => 'cups',
'description' => 'Cups of coffee consumed',
'default_value' => 0,
'calc_type' => 0,
'display_chart' => 1,
'places' => 0,
'default_view' => 0,
'name' => 'Coffee',
'suffix' => 'cups',
'description' => 'Cups of coffee consumed',
'default_value' => 0,
'calc_type' => 0,
'display_chart' => 1,
'places' => 0,
'default_view' => 0,
'threshold' => 0,
];
$object = new AddMetricCommand(
@@ -47,7 +48,8 @@ class AddMetricCommandTest extends AbstractTestCase
$params['calc_type'],
$params['display_chart'],
$params['places'],
$params['default_view']
$params['default_view'],
$params['threshold']
);
return compact('params', 'object');

View File

@@ -39,6 +39,7 @@ class UpdateMetricCommandTest extends AbstractTestCase
'display_chart' => 1,
'places' => 0,
'default_view' => 0,
'threshold' => 0,
];
$object = new UpdateMetricCommand(
@@ -50,7 +51,8 @@ class UpdateMetricCommandTest extends AbstractTestCase
$params['calc_type'],
$params['display_chart'],
$params['places'],
$params['default_view']
$params['default_view'],
$params['threshold']
);
return compact('params', 'object');