Commit 756c5d09 authored by Mark Harding's avatar Mark Harding

(wip): structure for dashboards

1 merge request!343WIP: Entity Centric Metrics & Dashboard
Pipeline #84401097 failed with stages
in 4 minutes and 53 seconds
<?php
namespace Minds\Core\Analytics\Dashboards;
interface DashboardCollectionInterface
{
/**
* Export everything in the collection
* @param array $extras
* @return array
*/
public function export(array $extras = []): array;
}
<?php
namespace Minds\Core\Analytics\Dashboards;
interface DashboardInterface
{
/**
* Build the dashboard
* NOTE: return type not specified due to php
* having terrible typing support
* @return self
*/
public function build();
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array;
}
<?php
namespace Minds\Core\Analytics\Dashboards\Filters;
use Minds\Traits\MagicAttributes;
abstract class FilterAbstract
{
use MagicAttributes;
/** @var string */
protected $id;
/** @var string */
protected $label;
/** @var FilterOptions */
protected $options;
/** @var string */
protected $selectedOption;
/**
* Export
* @param array $extras
* @return array
*/
public function export($extras = []): array
{
return [
'id' => (string) $this->id,
'label' => (string) $this->label,
'options' => (array) $this->options->export(),
];
}
}
<?php
/**
*
*/
namespace Minds\Core\Analytics\Dashboards\Filters;
use Minds\Traits\MagicAttributes;
class FilterOptions
{
use MagicAttributes;
/** @var FilterOptionsOption[] */
private $options = [];
/**
* Set options
* @param FilterOptionsOption $options
* @return self
*/
public function setOptions(FilterOptionsOption ...$options): self
{
$this->options = $options;
return $this;
}
/**
* Export
* @param array $export
* @return array
*/
public function export(array $export = []): array
{
$options = [];
foreach ($this->options as $option) {
$options[] = $option->export();
}
return $options;
}
}
<?php
/**
*
*/
namespace Minds\Core\Analytics\Dashboards\Filters;
use Minds\Traits\MagicAttributes;
class FilterOptionsOption
{
use MagicAttributes;
/** @var string */
private $id;
/** @var string */
private $label;
/** @var bool */
private $available = true;
/** @var bool */
private $selected = false;
/**
* Export
* @param array $export
* @return array
*/
public function export(array $export = []): array
{
return [
'id' => $this->id,
'label' => $this->label,
'available' => (bool) $this->available,
'selected' => (bool) $this->selected,
];
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Filters;
use Minds\Core\Analytics\Dashboards\DashboardCollectionInterface;
class FiltersCollection implements DashboardCollectionInterface
{
/** @var FilterAbstract[] */
private $filters = [];
/** @var string[] */
private $selectedIds;
/**
* Set the selected metric id
* @param string[]
* @return self
*/
public function setSelectedIds(array $selectedIds): self
{
$this->selectedIds = $selectedIds;
return $this;
}
public function getSelected(): array
{
// Filters have scoped key pairs like
// key::value
// platform::browser
$selected = [];
foreach ($this->selectedIds as $selectedId) {
list($key, $value) = explode('::', $selectedId);
if (!isset($this->filters[$key])) {
continue;
}
$selected[$key] = $this->filters[$key];
$selected[$key]->selectOption($value);
}
return $selected;
}
/**
* Set the filters
* @param FilterAbstract[] $filters
* @return self
*/
public function addFilters(FilterAbstract ...$filters): self
{
foreach ($filters as $filter) {
$this->filters[$filter->getId()] = $filter;
}
return $this;
}
/**
* Return the set metrics
* @return FilterAbstract[]
*/
public function getFilters(): array
{
return $this->filters;
}
// public function build(): self
// {
// foreach ($this->filters as $filter) {
// $filter->build();
// }
// return $this;
// }
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$export = [];
foreach ($this->filters as $filter) {
$export[] = $filter->export();
}
return $export;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Filters;
class ViewsFilter extends FilterAbstract
{
/** @var string */
protected $id = "views";
/** @var string */
protected $label = "View types";
public function __construct()
{
$this->options = (new FilterOptions())
->setOptions(
(new FilterOptionsOption())
->setId("total")
->setLabel("Total"),
(new FilterOptionsOption())
->setId("organic")
->setLabel("Organic"),
(new FilterOptionsOption())
->setId("boosted")
->setLabel("Boosted"),
(new FilterOptionsOption())
->setId("single")
->setLabel("Single")
);
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
class ActiveUsersMetric extends MetricAbstract
{
/** @var string */
protected $id = 'active_users';
/** @var string */
protected $label = 'active users';
/** @var array */
protected $permissions = [ 'admin' ];
/** @var MetricValues */
protected $values;
public function __construct($entityCentricManager = null)
{
$this->entityCentricManager = $entityCentricManager ?? Di::_()->get('Analytics\EntityCentric\Manager');
}
/**
* Build the metrics
* @return self
*/
public function build(): self
{
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$previousTs = strtotime('-1 ' . $timespan->getAggInterval(), $timespan->getFromTsMs() / 1000) * 1000;
$currentTs = $timespan->getFromTsMs();
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $previousTs,
],
];
// Use our global metrics
$must[]['term'] = [
'entity_urn' => 'urn:metric:global'
];
// Field name to use for the aggregation
$aggField = "active::total";
// The aggregation type, this differs by resolution
$aggType = "sum";
// The resolution to use
$resolution = 'day';
switch ($timespan->getId()) {
case 'today':
$resolution = 'day';
$aggType = "sum";
break;
case 'mtd':
$resolution = 'month';
$aggType = "max";
break;
case 'ytd':
$resolution = 'month';
$aggType = "avg";
break;
}
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
'resolution' => $resolution,
],
];
$response = $this->entityCentricManager->getAggregateByQuery([
'query' => [
'bool' => [
'must' => $must,
],
],
'size' => 0,
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getAggInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
$aggType => [
'field' => $aggField,
],
],
],
],
],
]);
$this->values = new MetricsValues();
$this->values->setCurrent($response[0]['1']['2']['value'])
->setPrevious($response[1]['1']['2']['value']);
return $this;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Di\Di;
use Minds\Traits\MagicAttributes;
/**
* @method MetricAbstract setTimespansCollection(TimespansCollection $timespansCollection)
* @method string getId()
* @method string getLabel()
*/
abstract class MetricAbstract
{
use MagicAttributes;
/** @var string */
protected $id;
/** @var string */
protected $label;
/** @var string[] */
protected $permissions;
/** @var MetricValues */
protected $values;
/** @var TimespansCollection */
protected $timespansCollection;
/** @var FiltersCollection */
protected $filtersCollection;
/**
* Export
* @param array $extras
* @return array
*/
public function export($extras = []): array
{
return [
'id' => (string) $this->id,
'label' => (string) $this->label,
'permissions' => (array) $this->permissions,
'values' => $this->values ? (array) $this->values->export() : null,
];
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Traits\MagicAttributes;
class MetricValues
{
/** @var int */
private $current = 0;
/** @var int */
private $previous = 0;
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
return [
'current' => (int) $current,
'previous' => (int) $previous,
];
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Analytics\Dashboards\DashboardCollectionInterface;
class MetricsCollection implements DashboardCollectionInterface
{
/** @var MetricsAbstract[] */
private $metrics = [];
/** @var string */
private $selectedId;
/** @var TimespansCollection */
private $timespansCollection;
/** @var FiltersCollection */
private $filtersCollection;
/**
* @param TimespansCollection $timespansCollection
* @return self
*/
public function setTimespansCollection(TimespansCollection $timespansCollection): self
{
$this->timespansCollection = $timespansCollection;
return $this;
}
/**
* @param FiltersCollection $filtersCollection
* @return self
*/
public function setFiltersCollection(?FiltersCollection $filtersCollection): self
{
$this->filtersCollection = $filtersCollection;
return $this;
}
/**
* Set the selected metric id
* @param string
* @return self
*/
public function setSelectedId(string $selectedId): self
{
$this->selectedId = $selectedId;
return $this;
}
/**
* Set the metrics
* @param MetricAbstract[] $metric
* @return self
*/
public function addMetrics(MetricAbstract ...$metrics): self
{
foreach ($metrics as $metric) {
$this->metrics[$metric->getId()] = $metric;
}
return $this;
}
/**
* Return the set metrics
* @return MetricAbstract[]
*/
public function getMetrics(): array
{
return $this->metrics;
}
public function build(): self
{
foreach ($this->metrics as $metric) {
$metric
->setTimespansCollection($this->timespansCollection)
->build();
}
return $this;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$export = [];
foreach ($this->metrics as $metric) {
$export[] = $metric->export();
}
return $export;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
class SignupsMetric extends MetricAbstract
{
/** @var string */
protected $id = 'signups';
/** @var string */
protected $label = 'signups';
/** @var array */
protected $permissions = [ 'admin' ];
/** @var MetricValues */
protected $values;
public function __construct($entityCentricManager = null)
{
$this->entityCentricManager = $entityCentricManager ?? Di::_()->get('Analytics\EntityCentric\Manager');
}
/**
* Build the metrics
* @return self
*/
public function build(): self
{
$timespan = $this->timespansCollection->getSelected();
$previousTs = strtotime('-1 ' . $timespan->getAggInterval(), $timespan->getFromTsMs() / 1000) * 1000;
$currentTs = $timespan->getFromTsMs();
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $previousTs,
],
];
// Return our global metrics
$must[]['term'] = [
'entity_urn' => 'urn:metric:global',
];
// Daily resolution
// TODO: implement this to avoid duplicated
// $must[] = [
// 'term' => [
// 'resolution' => 'day',
// ],
// ];
$aggType = "sum";
$aggField = "signups::total";
$response = $this->entityCentricManager->getAggregateByQuery([
'query' => [
'bool' => [
'must' => $must,
],
],
'size' => 0,
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getAggInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
$aggType => [
'field' => $aggField,
],
],
],
],
],
]);
$this->values = new MetricsValues();
$this->values->setCurrent($response[0]['1']['2']['value'])
->setPrevious($response[1]['1']['2']['value']);
return $this;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
class ViewsMetric extends MetricAbstract
{
/** @var string */
protected $id = 'views';
/** @var string */
protected $label = 'views';
/** @var array */
protected $permissions = [ 'admin' ];
/** @var MetricValues */
protected $values;
public function __construct($entityCentricManager = null)
{
$this->entityCentricManager = $entityCentricManager ?? Di::_()->get('Analytics\EntityCentric\Manager');
}
/**
* Build the metrics
* @return self
*/
public function build(): self
{
$timespan = $this->timespansCollection->getSelected();
$previousTs = strtotime('-1 ' . $timespan->getAggInterval(), $timespan->getFromTsMs() / 1000) * 1000;
$currentTs = $timespan->getFromTsMs();
$must = [];
$must[]['range'] = [
'@timestamp' => [
'gte' => $previousTs,
],
];
$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,
],
];
$response = $this->entityCentricManager->getAggregateByQuery([
'query' => [
'bool' => [
'must' => $must,
],
],
'size' => 0,
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getAggInterval(),
'min_doc_count' => 1,
],
'aggs' => [
'2' => [
$aggType => [
'field' => $aggField,
],
],
],
],
],
]);
$this->values = new MetricsValues();
$this->values->setCurrent($response[0]['1']['2']['value'])
->setPrevious($response[1]['1']['2']['value']);
return $this;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Timespans;
class MtdTimespan extends TimespanAbstract
{
/** @var string */
protected $id = 'mtd';
/** @var string */
protected $label = 'month to date';
/** @var string */
protected $interval = 'day';
/** @var int */
protected $fromTsMs;
/** @var string */
protected $aggInterval = 'month';
public function __construct()
{
$this->fromTsMs = strtotime('midnight first day of this month') * 1000;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Timespans;
use Minds\Traits\MagicAttributes;
/**
* @method string getId()
* @method string getLabel()
* @method string getInterval()
*/
abstract class TimespanAbstract
{
use MagicAttributes;
/** @var string */
protected $id;
/** @var string */
protected $label;
/** @var string */
protected $interval;
/** @var int */
protected $fromTsMs;
/** @var int */
protected $previousFromTsMs;
/** @var string */
protected $aggInterval = 'day';
/**
* Export
* @param array $extras
* @return array
*/
public function export($extras = []): array
{
return [
'id' => (string) $this->id,
'label' => (string) $this->label,
'interval' => (string) $this->interval,
'agg_interval' => (string) $this->aggInterval,
'from_ts_ms' => (int) $this->fromTsMs,
];
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Timespans;
use Minds\Core\Analytics\Dashboards\DashboardCollectionInterface;
class TimespansCollection implements DashboardCollectionInterface
{
/** @var TimespanAbstract[] */
private $timespans = [];
/** @var string */
private $selectedId;
/**
* Set the current timespan we are using
* @param string $selectedId
* @return self
*/
public function setSelectedId(string $selectedId): self
{
$this->selectedId = $selectedId;
return $this;
}
/**
* Return the selected timespan
* @return TimespanAbstract
*/
public function getSelected(): TimespanAbstract
{
return $this->timespans[$this->selectedId];
}
/**
* Set the timespans
* @param TimespanAbstract[] $timespans
* @return self
*/
public function addTimespans(TimespanAbstract ...$timespans): self
{
foreach ($timespans as $timespan) {
$this->timespans[$timespan->getId()] = $timespan;
}
return $this;
}
/**
* Return the set timestamps
* @return TimestampAbstract[]
*/
public function getTimespans(): array
{
return $this->timespans;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$export = [];
foreach ($this->timespans as $timespan) {
$export[] = $timespan->export();
}
return $export;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Timespans;
/**
* @method string getId()
* @method string getLabel()
* @method string getInterval()
*/
class TodayTimespan extends TimespanAbstract
{
/** @var string */
protected $id = 'today';
/** @var string */
protected $label = 'today';
/** @var string */
protected $interval = 'day';
/** @var int */
protected $fromTsMs;
/** @var string */
protected $aggInterval = 'day';
public function __construct()
{
$this->fromTsMs = strtotime('midnight') * 1000;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Timespans;
class YtdTimespan extends TimespanAbstract
{
/** @var string */
protected $id = 'ytd';
/** @var string */
protected $label = 'year to date';
/** @var string */
protected $interval = 'month';
/** @var int */
protected $fromTsMs;
/** @var string */
protected $aggInterval = 'year';
public function __construct()
{
$this->fromTsMs = strtotime('midnight first day of this year') * 1000;
}
}
<?php
/**
* Traffic Dashboard
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Traits\MagicAttributes;
/**
* @method TrafficDashboard setTimespanId(string $timespanId)
* @method TrafficDashboard setFilterIds(array $filtersIds)
*/
class TrafficDashboard implements DashboardInterface
{
use MagicAttributes;
/** @var string */
private $timespanId;
/** @var string[] */
private $filterIds;
/** @var string */
private $metricId;
/** @var Timespans\TimespansCollection */
private $timespansCollection;
/** @var Metrics\MetricsCollection */
private $metricsCollection;
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var Visualisations\Charts\ChartsCollection */
private $chartsCollection;
public function __construct(
$timespansCollection = null,
$metricsCollection = null,
$filtersCollection = null,
$chartsCollection = null
) {
$this->timespansCollection = $timespansCollection ?? new Timespans\TimespansCollection();
$this->metricsCollection = $metricsCollection ?? new Metrics\MetricsCollection();
$this->filtersCollection = $filtersCollection ?? new Filters\FiltersCollection();
$this->chartsCollection = $chartsCollection ?? new Visualisations\Charts\ChartsCollection();
}
/**
* Build the dashboard
* @return self
*/
public function build(): self
{
$this->timespansCollection
->setSelectedId($this->timespanId)
->addTimespan(
new Timespans\TodayTimespan(),
new Timespans\MtdTimespan(),
new Timespans\YtdTimespan()
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->addFilters(
new Filters\PlatformFilter(),
new Filters\ViewTypeFilter()
);
$this->metricsCollection
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->addMetrics(
new Metrics\ActiveUsersMetric(),
new Metrics\SignupsMetric(),
new Metrics\ViewsMetric()
)
->build();
$this->chartsCollection
->setTimespansCollection($this->timespansCollection)
->setMetricsCollection($this->metricsCollection);
return $this;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
return [
'category' => 'traffic',
'timespan' => $this->timespansCollection->getSelected->getId(),
'timespans' => $this->timespansCollection->export(),
'metrics' => $this->metricsCollection->export(),
'charts' => $this->chartCollection->export(),
'filter' => $this->filtersCollection->getSelected->getId(),
'filters' => $this->filtersCollection->export(),
];
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Visualisations\Charts;
class ChartsCollection
{
}
<?php
/**
* EntityCentricRecord
* @author Mark
* @author Mark
*/
namespace Minds\Core\Analytics\EntityCentric;
......@@ -45,7 +45,7 @@ class EntityCentricRecord
* Increment views
* @return DownsampledView
*/
public function incrementSum($metric): EntityCentricRecord
public function incrementSum($metric): EntityCentricRecord
{
if (!isset($this->sums[$metric])) {
$this->sums[$metric] = 0;
......@@ -53,5 +53,4 @@ class EntityCentricRecord
++$this->sums[$metric];
return $this;
}
}
<?php
/**
* EntityCentric Manager
* EntityCentric Manager
* @author Mark
*/
......@@ -67,5 +67,12 @@ class Manager
return (bool) $this->repository->add($record);
}
/**
* Query aggregate
* @param array $query
* @return array
*/
public function getAggregateByQuery(array $query): array
{
}
}
......@@ -8,7 +8,7 @@ use Exception;
class ViewsSynchroniser
{
/** @var array */
private $records = [];
private $records = [];
/** @var ViewsRepository */
private $viewsRepository;
......@@ -26,7 +26,7 @@ class ViewsSynchroniser
public function toRecords()
{
$date = (new DateTime())->setTimestamp($this->from);
$date = (new DateTime())->setTimestamp($this->from);
$opts['day'] = intval($date->format('d'));
$opts['month'] = intval($date->format('m'));
......@@ -87,6 +87,4 @@ class ViewsSynchroniser
$this->records[$view->getEntityUrn()]->incrementSum('views::total');
}
}
<?php
namespace Spec\Minds\Core\Analytics\Dashboards\Filters;
use Minds\Core\Analytics\Dashboards\Filters\FiltersCollection;
use Minds\Core\Analytics\Dashboards\Filters\ViewsFilter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FiltersCollectionSpec extends ObjectBehavior
{
public function it_is_initializable()
{
$this->shouldHaveType(FiltersCollection::class);
}
public function it_should_add_filters_to_collection()
{
$this->addFilters(new ViewsFilter);
$filters = $this->getFilters();
$filters['views']->getId()
->shouldBe('views');
}
public function it_should_export_filters()
{
$this->addFilters(new ViewsFilter);
$export = $this->export();
$export[0]['id']
->shouldBe('views');
$export[0]['options']
->shouldHaveCount(4);
}
}
<?php
namespace Spec\Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Analytics\Dashboards\Metrics\MetricsCollection;
use Minds\Core\Analytics\Dashboards\Metrics\ActiveUsersMetric;
use Minds\Core\Analytics\Dashboards\Metrics\SignupsMetric;
use Minds\Core\Analytics\Dashboards\Metrics\ViewsMetric;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Analytics\Dashboards\Timespans\TodayTimespan;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class MetricsCollectionSpec extends ObjectBehavior
{
private $timespansCollection;
private $todayTimespan;
public function let(TimespansCollection $timespansCollection, TodayTimespan $todayTimespan)
{
$this->timespansCollection = $timespansCollection;
$this->todayTimespan = $todayTimespan;
$this->setTimespansCollection($timespansCollection);
$this->timespansCollection->setSelectedId('today');
}
public function it_is_initializable()
{
$this->shouldHaveType(MetricsCollection::class);
}
public function it_should_add_metrics_to_collection()
{
$this->addMetrics(
new ActiveUsersMetric,
new SignupsMetric,
new ViewsMetric
);
$metrics = $this->getMetrics();
$metrics['active_users']->getId()
->shouldBe('active_users');
}
public function it_should_export_metrics()
{
$this->addMetrics(
new ActiveUsersMetric,
new SignupsMetric,
new ViewsMetric
);
$export = $this->export();
$export[0]['id']
->shouldBe('active_users');
}
public function it_should_build_metrics(ActiveUsersMetric $activeUsersMetric)
{
$activeUsersMetric->getId()
->shouldBeCalled()
->willReturn('active_users');
$activeUsersMetric->setTimespansCollection($this->timespansCollection)
->shouldBeCalled()
->willReturn($activeUsersMetric);
$activeUsersMetric->build()
->shouldBeCalled();
$this->addMetrics($activeUsersMetric);
$this->build();
}
}
<?php
namespace Spec\Minds\Core\Analytics\Dashboards\Timespans;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Analytics\Dashboards\Timespans\TodayTimespan;
use Minds\Core\Analytics\Dashboards\Timespans\MtdTimespan;
use Minds\Core\Analytics\Dashboards\Timespans\YtdTimespan;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TimespansCollectionSpec extends ObjectBehavior
{
public function it_is_initializable()
{
$this->shouldHaveType(TimespansCollection::class);
}
public function it_should_add_timespans_to_collection()
{
$this->addTimespans(
new TodayTimespan(),
new MtdTimespan(),
new YtdTimespan()
);
$this->getTimespans()['today']->getId()
->shouldBe('today');
$this->getTimespans()['mtd']->getId()
->shouldBe('mtd');
$this->getTimespans()['ytd']->getId()
->shouldBe('ytd');
}
public function it_should_export_timestamps()
{
$this->addTimespans(
new TodayTimespan(),
new MtdTimespan(),
new YtdTimespan()
);
$exported = $this->export();
$exported[0]['id']->shouldBe('today');
$exported[0]['from_ts_ms']->shouldBe(strtotime('midnight') * 1000);
$exported[1]['id']->shouldBe('mtd');
$exported[1]['from_ts_ms']->shouldBe(strtotime('midnight first day of this month') * 1000);
$exported[2]['id']->shouldBe('ytd');
$exported[2]['from_ts_ms']->shouldBe(strtotime('midnight first day of this year') * 1000);
$exported[2]['interval']->shouldBe('month');
}
}
<?php
namespace Spec\Minds\Core\Analytics\Dashboards;
use Minds\Core\Analytics\Dashboards\TrafficDashboard;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TrafficDashboardSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(TrafficDashboard::class);
}
}
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