Commit 3e67fe4a authored by Mark Harding's avatar Mark Harding

(chore): add visualisation blocked to other traffic metrics too

1 merge request!343WIP: Entity Centric Metrics & Dashboard
Pipeline #84679984 passed with stages
in 10 minutes and 21 seconds
<?php
namespace Minds\Core\Analytics\Dashboards;
class Manager
{
const DASHBOARDS = [
'traffic' => TrafficDashboard::class,
];
/**
* @param string $id
* @return DashboardInterface
*/
public function getDashboardById(string $id): DashboardInterface
{
return self::DASHBOARDS[$id];
}
}
......@@ -10,6 +10,7 @@ use Minds\Traits\MagicAttributes;
* @method AbstractMetric setFiltersCollection(FiltersCollection $filtersCollection)
* @method string getId()
* @method string getLabel()
* @method MetricSummary getSummary()
*/
abstract class AbstractMetric
{
......
......@@ -6,6 +6,9 @@ use Minds\Core\Data\Elasticsearch;
class ActiveUsersMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = 'active_users';
......@@ -107,8 +110,8 @@ class ActiveUsersMetric extends AbstractMetric
$response = $this->es->request($prepared);
$this->values = new MetricSummary();
$this->values->setValue($response['aggregations']['1']['buckets'][1]['2']['value'])
$this->summary = new MetricSummary();
$this->summary->setValue($response['aggregations']['1']['buckets'][1]['2']['value'])
->setComparisonValue($response['aggregations']['1']['buckets'][0]['2']['value'])
->setComparisonInterval($timespan->getComparisonInterval());
return $this;
......
......@@ -2,9 +2,13 @@
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
use Minds\Core\Data\Elasticsearch;
class SignupsMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = 'signups';
......@@ -14,19 +18,16 @@ class SignupsMetric extends AbstractMetric
/** @var array */
protected $permissions = [ 'admin' ];
/** @var MetricValues */
protected $values;
public function __construct($entityCentricManager = null)
public function __construct($es = null)
{
$this->entityCentricManager = $entityCentricManager ?? Di::_()->get('Analytics\EntityCentric\Manager');
$this->es = $es ?? Di::_()->get('Database\ElasticSearch');
}
/**
* Build the metrics
* @return self
*/
public function build(): self
public function buildSummary(): self
{
$timespan = $this->timespansCollection->getSelected();
$comparisonTsMs = strtotime("-{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
......@@ -54,39 +55,130 @@ class SignupsMetric extends AbstractMetric
// ],
// ];
$aggType = "sum";
$aggField = "signups::total";
$response = $this->entityCentricManager->getAggregateByQuery([
'query' => [
'bool' => [
'must' => $must,
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'fixed_interval' => $timespan->getComparisonInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
'sum' => [
'field' => $aggField,
],
],
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$this->summary = new MetricSummary();
$this->summary
->setValue($response['aggregations']['1']['buckets'][1]['2']['value'])
->setComparisonValue($response['aggregations']['1']['buckets'][0]['2']['value'])
->setComparisonInterval($timespan->getComparisonInterval());
return $this;
}
/**
* Build a visualisation for the metric
* @return self
*/
public function buildVisualisation(): self
{
$timespan = $this->timespansCollection->getSelected();
$xValues = [];
$yValues = [];
// TODO: make this respect the filters
$field = "signups::total";
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $timespan->getFromTsMs(),
],
];
// Use our global metrics
$must[]['term'] = [
'entity_urn' => 'urn:metric:global'
];
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
'resolution' => $timespan->getInterval(),
],
];
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getAggInterval(),
'min_doc_count' => 1,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
'aggs' => [
'2' => [
$aggType => [
'field' => $aggField,
],
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
'sum' => [
'field' => $field,
],
],
],
],
],
],
]);
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$xValues[] = $date;
$yValues[] = $bucket['2']['value'];
}
$this->visualisation = (new Visualisations\ChartVisualisation())
->setXValues($xValues)
->setYValues($yValues)
->setXLabel('Date')
->setYLabel('Count');
$this->values = new MetricsValues();
$this->values
->setValue($response[0]['1']['2']['value'])
->setComparisonValue($response[1]['1']['2']['value'])
->setComparisonInterval($this->getComparisonInterval());
return $this;
}
}
......@@ -2,9 +2,13 @@
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
use Minds\Core\Data\Elasticsearch;
class ViewsMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = 'views';
......@@ -14,19 +18,16 @@ class ViewsMetric extends AbstractMetric
/** @var array */
protected $permissions = [ 'admin' ];
/** @var MetricSummary */
protected $summary;
public function __construct($entityCentricManager = null)
public function __construct($es = null)
{
$this->entityCentricManager = $entityCentricManager ?? Di::_()->get('Analytics\EntityCentric\Manager');
$this->es = $es ?? Di::_()->get('Database\ElasticSearch');
}
/**
* Build the metrics
* @return self
*/
public function build(): self
public function buildSummary(): self
{
$timespan = $this->timespansCollection->getSelected();
$comparisonTsMs = strtotime("-{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
......@@ -39,48 +40,137 @@ class ViewsMetric extends AbstractMetric
],
];
$aggType = "sum";
// TODO: Allow this to be changed based on supplied filters
$aggField = "views::total";
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
'resolution' => $resolution,
'resolution' => $timespan->getInterval(),
],
];
$response = $this->entityCentricManager->getAggregateByQuery([
'query' => [
'bool' => [
'must' => $must,
],
],
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getAggInterval(),
'min_doc_count' => 1,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
'aggs' => [
'2' => [
$aggType => [
'field' => $aggField,
],
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'fixed_interval' => $timespan->getComparisonInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
'sum' => [
'field' => $aggField,
],
],
],
],
],
],
]);
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$this->summary = new MetricSummary();
$this->summary
->setValue($response[0]['1']['2']['value'])
->setComparisonValue($response[1]['1']['2']['value'])
->setComparisonInterval($this->getComparisonInterval());
->setValue($response['aggregations']['1']['buckets'][1]['2']['value'])
->setComparisonValue($response['aggregations']['1']['buckets'][0]['2']['value'])
->setComparisonInterval($timespan->getComparisonInterval());
return $this;
}
/**
* Build a visualisation for the metric
* @return self
*/
public function buildVisualisation(): self
{
$timespan = $this->timespansCollection->getSelected();
$xValues = [];
$yValues = [];
// TODO: make this respect the filters
$field = "views::total";
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $timespan->getFromTsMs(),
],
];
// Use our global metrics
$must[]['term'] = [
'entity_urn' => 'urn:metric:global'
];
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
'resolution' => $timespan->getInterval(),
],
];
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
'sum' => [
'field' => $field,
],
],
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$xValues[] = $date;
$yValues[] = $bucket['2']['value'];
}
$this->visualisation = (new Visualisations\ChartVisualisation())
->setXValues($xValues)
->setYValues($yValues)
->setXLabel('Date')
->setYLabel('Count');
return $this;
}
}
......@@ -26,6 +26,7 @@ class ActiveUsersMetricSpec extends ObjectBehavior
$this->timespansCollection = $timespansCollection;
$this->filtersCollection = $filtersCollection;
}
public function it_is_initializable()
{
$this->shouldHaveType(ActiveUsersMetric::class);
......@@ -62,9 +63,9 @@ class ActiveUsersMetricSpec extends ObjectBehavior
$this->buildSummary();
$this->values->getValue()
$this->getSummary()->getValue()
->shouldBe(128);
$this->values->getComparisonValue()
$this->getSummary()->getComparisonValue()
->shouldBe(256);
}
......
<?php
namespace Spec\Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Analytics\Dashboards\Metrics\SignupsMetric;
use Minds\Core\Analytics\Dashboards\Metrics\ActiveUsersMetric;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Analytics\Dashboards\Timespans\AbstractTimespan;
use Minds\Core\Analytics\Dashboards\Filters\FiltersCollection;
use Minds\Core\Analytics\Dashboards\Filters\AbstractFilter;
use Minds\Core\Data\Elasticsearch\Client;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class SignupsMetricSpec extends ObjectBehavior
{
private $es;
private $timespansCollection;
private $filtersCollection;
public function let(Client $es, TimespansCollection $timespansCollection, FiltersCollection $filtersCollection)
{
$this->beConstructedWith($es);
$this->es = $es;
$this->setTimespansCollection($timespansCollection);
$this->setFiltersCollection($filtersCollection);
$this->timespansCollection = $timespansCollection;
$this->filtersCollection = $filtersCollection;
}
public function it_is_initializable()
{
$this->shouldHaveType(SignupsMetric::class);
}
public function it_should_build_summary(AbstractTimespan $mockTimespan, AbstractFilter $mockFilter)
{
$this->timespansCollection->getSelected()
->willReturn($mockTimespan);
$this->filtersCollection->getSelected()
->willReturn([$mockFilter]);
$this->es->request(Argument::any())
->willReturn([
'aggregations' => [
'1' => [
'buckets' => [
[
'key' => strtotime('Midnight 1st December 2018') * 1000,
'2' => [
'value' => 256,
],
],
[
'key' => strtotime('Midnight 1st December 2019') * 1000,
'2' => [
'value' => 128,
],
],
],
],
]
]);
$this->buildSummary();
$this->getSummary()->getValue()
->shouldBe(128);
$this->getSummary()->getComparisonValue()
->shouldBe(256);
}
public function it_should_build_visualisation(AbstractTimespan $mockTimespan, AbstractFilter $mockFilter)
{
$this->timespansCollection->getSelected()
->willReturn($mockTimespan);
$this->filtersCollection->getSelected()
->willReturn([$mockFilter]);
$this->es->request(Argument::any())
->willReturn([
'aggregations' => [
'1' => [
'buckets' => [
[
'key' => strtotime('Midnight 1st December 2018') * 1000,
'2' => [
'value' => 256,
],
],
[
'key' => strtotime('Midnight 1st January 2019') * 1000,
'2' => [
'value' => 128,
],
],
[
'key' => strtotime('Midnight 1st February 2019') * 1000,
'2' => [
'value' => 4,
],
],
[
'key' => strtotime('Midnight 1st March 2019') * 1000,
'2' => [
'value' => 685,
],
],
],
],
]
]);
$this->buildVisualisation();
$xValues = $this->getVisualisation()->getXValues();
$xValues[0]->shouldBe('01-12-2018');
$xValues[1]->shouldBe('01-01-2019');
$xValues[2]->shouldBe('01-02-2019');
$xValues[3]->shouldBe('01-03-2019');
$yValues = $this->getVisualisation()->getYValues();
$yValues[0]->shouldBe(256);
$yValues[1]->shouldBe(128);
$yValues[2]->shouldBe(4);
$yValues[3]->shouldBe(685);
}
}
<?php
namespace Spec\Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Analytics\Dashboards\Metrics\ViewsMetric;
use Minds\Core\Analytics\Dashboards\Metrics\ActiveUsersMetric;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Analytics\Dashboards\Timespans\AbstractTimespan;
use Minds\Core\Analytics\Dashboards\Filters\FiltersCollection;
use Minds\Core\Analytics\Dashboards\Filters\AbstractFilter;
use Minds\Core\Data\Elasticsearch\Client;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ViewsMetricSpec extends ObjectBehavior
{
private $es;
private $timespansCollection;
private $filtersCollection;
public function let(Client $es, TimespansCollection $timespansCollection, FiltersCollection $filtersCollection)
{
$this->beConstructedWith($es);
$this->es = $es;
$this->setTimespansCollection($timespansCollection);
$this->setFiltersCollection($filtersCollection);
$this->timespansCollection = $timespansCollection;
$this->filtersCollection = $filtersCollection;
}
public function it_is_initializable()
{
$this->shouldHaveType(ViewsMetric::class);
}
public function it_should_build_summary(AbstractTimespan $mockTimespan, AbstractFilter $mockFilter)
{
$this->timespansCollection->getSelected()
->willReturn($mockTimespan);
$this->filtersCollection->getSelected()
->willReturn([$mockFilter]);
$this->es->request(Argument::any())
->willReturn([
'aggregations' => [
'1' => [
'buckets' => [
[
'key' => strtotime('Midnight 1st December 2018') * 1000,
'2' => [
'value' => 256,
],
],
[
'key' => strtotime('Midnight 1st December 2019') * 1000,
'2' => [
'value' => 128,
],
],
],
],
]
]);
$this->buildSummary();
$this->getSummary()->getValue()
->shouldBe(128);
$this->getSummary()->getComparisonValue()
->shouldBe(256);
}
public function it_should_build_visualisation(AbstractTimespan $mockTimespan, AbstractFilter $mockFilter)
{
$this->timespansCollection->getSelected()
->willReturn($mockTimespan);
$this->filtersCollection->getSelected()
->willReturn([$mockFilter]);
$this->es->request(Argument::any())
->willReturn([
'aggregations' => [
'1' => [
'buckets' => [
[
'key' => strtotime('Midnight 1st December 2018') * 1000,
'2' => [
'value' => 256,
],
],
[
'key' => strtotime('Midnight 1st January 2019') * 1000,
'2' => [
'value' => 128,
],
],
[
'key' => strtotime('Midnight 1st February 2019') * 1000,
'2' => [
'value' => 4,
],
],
[
'key' => strtotime('Midnight 1st March 2019') * 1000,
'2' => [
'value' => 685,
],
],
],
],
]
]);
$this->buildVisualisation();
$xValues = $this->getVisualisation()->getXValues();
$xValues[0]->shouldBe('01-12-2018');
$xValues[1]->shouldBe('01-01-2019');
$xValues[2]->shouldBe('01-02-2019');
$xValues[3]->shouldBe('01-03-2019');
$yValues = $this->getVisualisation()->getYValues();
$yValues[0]->shouldBe(256);
$yValues[1]->shouldBe(128);
$yValues[2]->shouldBe(4);
$yValues[3]->shouldBe(685);
}
}
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