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

(feat): various changes with production data and frontend changes

1 merge request!343WIP: Analytics Dashboard
Pipeline #88908458 canceled with stages
......@@ -5,13 +5,14 @@ namespace Minds\Controllers\api\v2\analytics;
use Minds\Api\Factory;
use Minds\Core;
use Minds\Core\Session;
use Minds\Core\Di\Di;
use Minds\Common\Cookie;
use Minds\Entities;
use Minds\Helpers\Counters;
use Minds\Interfaces;
class dashboards implements Interfaces\Api, Interfaces\ApiIgnorePam
class dashboards implements Interfaces\Api
{
public function get($pages)
{
......@@ -21,6 +22,8 @@ class dashboards implements Interfaces\Api, Interfaces\ApiIgnorePam
$dashboard = $dashboardsManager->getDashboardById($id);
$dashboard->setUser(Session::getLoggedInUser());
if (isset($_GET['timespan'])) {
$dashboard->setTimespanId($_GET['timespan']);
}
......
......@@ -4,11 +4,13 @@
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
/**
* @method TrafficDashboard setTimespanId(string $timespanId)
* @method TrafficDashboard setFilterIds(array $filtersIds)
* @method EarningsDashboard setTimespanId(string $timespanId)
* @method EarningsDashboard setFilterIds(array $filtersIds)
* @method EarningsDashboard setUser(User $user)
*/
class EarningsDashboard implements DashboardInterface
{
......@@ -32,8 +34,8 @@ class EarningsDashboard implements DashboardInterface
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var Visualisations\Chart\ChartSegmentsCollection */
private $chartCollection;
/** @var User */
private $user;
public function __construct(
$timespansCollection = null,
......@@ -62,6 +64,7 @@ class EarningsDashboard implements DashboardInterface
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->setUser($this->user)
->addFilters(
new Filters\ChannelFilter()
);
......@@ -69,6 +72,7 @@ class EarningsDashboard implements DashboardInterface
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->setUser($this->user)
->addMetrics(
new Metrics\Earnings\TotalEarningsMetric(),
new Metrics\Earnings\ViewsEarningsMetric(),
......@@ -90,6 +94,8 @@ class EarningsDashboard implements DashboardInterface
$this->build();
return [
'category' => 'earnings',
'label' => 'Pro Earnings',
'description' => 'Earnings for PRO members will be paid out within 30 days upon reaching a minumum balance of $100.00.',
'timespan' => $this->timespansCollection->getSelected()->getId(),
'timespans' => $this->timespansCollection->export(),
'metric' => $this->metricsCollection->getSelected()->getId(),
......
......@@ -16,6 +16,9 @@ abstract class AbstractFilter
/** @var string */
protected $description;
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var FilterOptions */
protected $options;
......
......@@ -9,6 +9,9 @@ class ChannelFilter extends AbstractFilter
/** @var string */
protected $label = "Channel";
/** @var array */
protected $permissions = [ 'admin' ];
/** @var string */
protected $description = "Filter by channels or by the full site";
......
<?php
namespace Minds\Core\Analytics\Dashboards\Filters;
use Minds\Entities\User;
use Minds\Core\Analytics\Dashboards\DashboardCollectionInterface;
class FiltersCollection implements DashboardCollectionInterface
......@@ -11,6 +12,9 @@ class FiltersCollection implements DashboardCollectionInterface
/** @var string[] */
private $selectedIds;
/** @var User */
private $user;
/**
* Set the selected metric id
* @param string[]
......@@ -31,6 +35,16 @@ class FiltersCollection implements DashboardCollectionInterface
return $this->selectedIds;
}
/**
* @param User $user
* @return self
*/
public function setUser(User $user): self
{
$this->user = $user;
return $this;
}
public function getSelected(): array
{
// Filters have scoped key pairs like
......@@ -56,6 +70,14 @@ class FiltersCollection implements DashboardCollectionInterface
public function addFilters(AbstractFilter ...$filters): self
{
foreach ($filters as $filter) {
if (
in_array('admin', $filter->getPermissions(), true)
&& !$this->user->isAdmin()
&& !in_array('user', $filter->getPermissions(), true)
) {
continue;
}
$this->filters[$filter->getId()] = $filter;
}
return $this;
......@@ -70,6 +92,13 @@ class FiltersCollection implements DashboardCollectionInterface
return $this->filters;
}
public function clear(): self
{
$this->filters = [];
$this->selectedIds = [];
return $this;
}
// public function build(): self
// {
// foreach ($this->filters as $filter) {
......
......@@ -29,9 +29,9 @@ class ViewTypeFilter extends AbstractFilter
->setLabel("Boosted")
->setDescription("Views recorded on assets that were boosted"),
(new FilterOptionsOption())
->setId("single")
->setLabel("Single")
->setDecription("Views recorded on single pages, not in feeds")
->setId("single")
->setLabel("Pageview")
->setDecription("Views recorded on single pages, not in feeds")
);
}
}
......@@ -57,6 +57,9 @@ abstract class AbstractMetric
if (!Session::getLoggedInUserGuid()) {
throw new \Exception("You must be loggedin");
}
if (Session::isAdmin()) {
return "";
}
return Session::getLoggedInUserGuid();
}
......
......@@ -14,10 +14,10 @@ class ViewsEarningsMetric extends AbstractEarningsMetric
protected $id = 'earnings_views';
/** @var string */
protected $label = 'Views USD';
protected $label = 'Pageviews USD';
/** @var string */
protected $description = 'Views earnings for PRO users';
protected $description = 'Pageview earnings for PRO users';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
......
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Entities\User;
use Minds\Core\Di\Di;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Analytics\Dashboards\Filters\FiltersCollection;
......@@ -20,6 +21,9 @@ class MetricsCollection implements DashboardCollectionInterface
/** @var FiltersCollection */
private $filtersCollection;
/** @var User */
private $user;
/**
* @param TimespansCollection $timespansCollection
* @return self
......@@ -40,6 +44,16 @@ class MetricsCollection implements DashboardCollectionInterface
return $this;
}
/**
* @param User $user
* @return self
*/
public function setUser(User $user): self
{
$this->user = $user;
return $this;
}
/**
* Set the selected metric id
* @param string
......@@ -59,6 +73,13 @@ class MetricsCollection implements DashboardCollectionInterface
public function addMetrics(AbstractMetric ...$metrics): self
{
foreach ($metrics as $metric) {
if (
in_array('admin', $metric->getPermissions(), true)
&& !$this->user->isAdmin()
&& !in_array('user', $metric->getPermissions(), true)
) {
continue;
}
$this->metrics[$metric->getId()] = $metric;
}
return $this;
......
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
class PageviewsMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = 'pageviews';
/** @var string */
protected $label = 'Pageviews';
/** @var string */
protected $description = 'Pageviews on channel assets. A pageview is visit to a single assets on either desktop or mobile.';
/** @var array */
protected $permissions = [ 'admin', 'user' ];
public function __construct($es = null)
{
$this->es = $es ?? Di::_()->get('Database\ElasticSearch');
}
/**
* Build the metrics
* @return self
*/
public function buildSummary(): self
{
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$comparisonTsMs = strtotime("midnight -{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$currentTsMs = $timespan->getFromTsMs();
// TODO: Allow this to be changed based on supplied filters
$aggField = "views::single";
$values = [];
foreach ([ 'value' => $currentTsMs, 'comparison' => $comparisonTsMs ] as $key => $tsMs) {
$must = [];
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
if ($userGuid = $this->getUserGuid()) {
$must[] = [
'term' => [
'owner_guid' => $userGuid,
],
];
}
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'sum' => [
'field' => $aggField,
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$values[$key] = $response['aggregations']['1']['value'];
}
$this->summary = new MetricSummary();
$this->summary
->setValue($values['value'])
->setComparisonValue($values['comparison'])
->setComparisonInterval($timespan->getComparisonInterval())
->setComparisonPositivity(true);
return $this;
}
/**
* Build a visualisation for the metric
* @return self
*/
public function buildVisualisation(): self
{
$timespan = $this->timespansCollection->getSelected();
$this->filtersCollection->clear();
// TODO: make this respect the filters
$field = "views::single";
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $timespan->getFromTsMs(),
],
];
if ($userGuid = $this->getUserGuid()) {
$must[] = [
'term' => [
'owner_guid' => $userGuid,
],
];
}
// 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);
$buckets = [];
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$buckets[] = [
'key' => $bucket['key'],
'date' => date('c', $bucket['key'] / 1000),
'value' => $bucket['2']['value']
];
}
$this->visualisation = (new Visualisations\ChartVisualisation())
->setXLabel('Date')
->setYLabel('Count')
->setBuckets($buckets);
return $this;
}
}
......@@ -14,13 +14,13 @@ class ViewsMetric extends AbstractMetric
protected $id = 'views';
/** @var string */
protected $label = 'Views';
protected $label = 'Impressions';
/** @var string */
protected $description = 'Views on channel assets';
protected $description = 'Impressions on channel assets. Impressions include pageviews and feed views.';
/** @var array */
protected $permissions = [ 'admin' ];
protected $permissions = [ 'admin', 'user' ];
public function __construct($es = null)
{
......@@ -171,8 +171,6 @@ class ViewsMetric extends AbstractMetric
$buckets = [];
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$xValues[] = $date;
$yValues[] = $bucket['2']['value'];
$buckets[] = [
'key' => $bucket['key'],
'date' => date('c', $bucket['key'] / 1000),
......@@ -181,8 +179,6 @@ class ViewsMetric extends AbstractMetric
}
$this->visualisation = (new Visualisations\ChartVisualisation())
->setXValues($xValues)
->setYValues($yValues)
->setXLabel('Date')
->setYLabel('Count')
->setBuckets($buckets);
......
......@@ -24,7 +24,7 @@ class ViewsTableMetric extends AbstractMetric
protected $description = 'Views by post';
/** @var array */
protected $permissions = [ 'admin' ];
protected $permissions = [ 'user', 'admin' ];
public function __construct($es = null)
{
......@@ -134,7 +134,6 @@ class ViewsTableMetric extends AbstractMetric
$buckets = [];
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$entity = $this->entitiesResolver->single(new Urn($bucket['key']));
$buckets[] = [
'key' => $bucket['key'],
......@@ -167,7 +166,7 @@ class ViewsTableMetric extends AbstractMetric
],
[
'id' => 'views::single',
'label' => 'Single',
'label' => 'Pageviews',
'order' => 3,
]
]);
......
......@@ -23,6 +23,9 @@ abstract class AbstractTimespan
/** @var string */
protected $interval;
/** @var bool */
protected $selected = false;
/** @var int */
protected $fromTsMs;
......@@ -40,6 +43,7 @@ abstract class AbstractTimespan
'id' => (string) $this->id,
'label' => (string) $this->label,
'interval' => (string) $this->interval,
'selected' => (bool) $this->selected,
'comparison_interval' => (int) $this->comparisonInterval,
'from_ts_ms' => (int) $this->fromTsMs,
'from_ts_iso' => date('c', $this->fromTsMs / 1000),
......
......@@ -62,6 +62,9 @@ class TimespansCollection implements DashboardCollectionInterface
{
$export = [];
foreach ($this->timespans as $timespan) {
if ($timespan->getId() === $this->selectedId) {
$timespan->setSelected(true);
}
$export[] = $timespan->export();
}
return $export;
......
......@@ -4,11 +4,13 @@
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
/**
* @method TrafficDashboard setTimespanId(string $timespanId)
* @method TrafficDashboard setFilterIds(array $filtersIds)
* @method TrafficDashboard setUser(User $user)
*/
class TrafficDashboard implements DashboardInterface
{
......@@ -32,8 +34,8 @@ class TrafficDashboard implements DashboardInterface
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var Visualisations\Chart\ChartSegmentsCollection */
private $chartCollection;
/** @var User */
private $user;
public function __construct(
$timespansCollection = null,
......@@ -62,6 +64,7 @@ class TrafficDashboard implements DashboardInterface
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->setUser($this->user)
->addFilters(
// new Filters\PlatformFilter(),
new Filters\ViewTypeFilter(),
......@@ -71,9 +74,11 @@ class TrafficDashboard implements DashboardInterface
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->setUser($this->user)
->addMetrics(
new Metrics\ActiveUsersMetric(),
new Metrics\SignupsMetric(),
new Metrics\PageviewsMetric(),
new Metrics\ViewsMetric()
)
->build();
......@@ -91,6 +96,8 @@ class TrafficDashboard implements DashboardInterface
$this->build();
return [
'category' => 'traffic',
'label' => 'Traffic',
'description' => null,
'timespan' => $this->timespansCollection->getSelected()->getId(),
'timespans' => $this->timespansCollection->export(),
'metric' => $this->metricsCollection->getSelected()->getId(),
......
......@@ -4,11 +4,13 @@
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
/**
* @method TrafficDashboard setTimespanId(string $timespanId)
* @method TrafficDashboard setFilterIds(array $filtersIds)
* @method TrendingDashboard setTimespanId(string $timespanId)
* @method TrendingDashboard setFilterIds(array $filtersIds)
* @method TrendingDashboard setUser(User $user)
*/
class TrendingDashboard implements DashboardInterface
{
......@@ -32,6 +34,9 @@ class TrendingDashboard implements DashboardInterface
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var User */
private $user;
public function __construct(
$timespansCollection = null,
$metricsCollection = null,
......@@ -59,6 +64,7 @@ class TrendingDashboard implements DashboardInterface
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->setUser($this->user)
->addFilters(
// new Filters\PlatformFilter(),
new Filters\ViewTypeFilter(),
......@@ -68,6 +74,7 @@ class TrendingDashboard implements DashboardInterface
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->setUser($this->user)
->addMetrics(
new Metrics\ViewsTableMetric()
)
......@@ -86,6 +93,8 @@ class TrendingDashboard implements DashboardInterface
$this->build();
return [
'category' => 'trending',
'label' => 'Trending',
'description' => null,
'timespan' => $this->timespansCollection->getSelected()->getId(),
'timespans' => $this->timespansCollection->export(),
'metric' => $this->metricsCollection->getSelected()->getId(),
......
......@@ -66,7 +66,7 @@ class Repository
$result = $this->db->request($prepared);
if (!$result || !$result[0]) {
return new Response();
return (new Response())->setLastPage(true);
}
$response = new Response();
......
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