Skip to content
Commits on Source (2)
<?php
namespace Minds\Controllers\Cli;
use Minds\Core;
use Minds\Core\Monetization\Partners\Manager;
use Minds\Cli;
use Minds\Interfaces;
use Minds\Exceptions;
use Minds\Entities;
class PartnerEarnings extends Cli\Controller implements Interfaces\CliControllerInterface
{
public function __construct()
{
}
public function help($command = null)
{
$this->out('TBD');
}
public function exec()
{
$this->out('Missing subcommand');
}
public function sync()
{
error_reporting(E_ALL);
ini_set('display_errors', 1);
$daysAgo = $this->getOpt('daysAgo') ?: 0;
$from = $this->getOpt('from') ?: strtotime("midnight $daysAgo days ago");
$manager = new Manager();
$i = 0;
foreach ($manager->issueDeposits([ 'from' => $from ]) as $record) {
$this->out(++$i);
}
}
}
<?php
/**
* Earnings Dashboard
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Traits\MagicAttributes;
/**
* @method TrafficDashboard setTimespanId(string $timespanId)
* @method TrafficDashboard setFilterIds(array $filtersIds)
*/
class EarningsDashboard implements DashboardInterface
{
use MagicAttributes;
/** @var string */
private $timespanId = '30d';
/** @var string[] */
private $filterIds = [ 'platform::browser' ];
/** @var string */
private $metricId = 'active_users';
/** @var Timespans\TimespansCollection */
private $timespansCollection;
/** @var Metrics\MetricsCollection */
private $metricsCollection;
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var Visualisations\Chart\ChartSegmentsCollection */
private $chartCollection;
public function __construct(
$timespansCollection = null,
$metricsCollection = null,
$filtersCollection = null
) {
$this->timespansCollection = $timespansCollection ?? new Timespans\TimespansCollection();
$this->metricsCollection = $metricsCollection ?? new Metrics\MetricsCollection();
$this->filtersCollection = $filtersCollection ?? new Filters\FiltersCollection();
}
/**
* Build the dashboard
* @return self
*/
public function build(): self
{
$this->timespansCollection
->setSelectedId($this->timespanId)
->addTimespans(
new Timespans\TodayTimespan(),
new Timespans\_30dTimespan(),
new Timespans\_1yTimespan(),
new Timespans\MtdTimespan(),
new Timespans\YtdTimespan()
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->addFilters(
new Filters\ChannelFilter()
);
$this->metricsCollection
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->addMetrics(
new Metrics\Earnings\TotalEarningsMetric(),
new Metrics\Earnings\ViewsEarningsMetric(),
new Metrics\Earnings\ReferralsEarningsMetric(),
new Metrics\Earnings\SalesEarningsMetric()
)
->build();
return $this;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$this->build();
return [
'category' => 'earnings',
'timespan' => $this->timespansCollection->getSelected()->getId(),
'timespans' => $this->timespansCollection->export(),
'metric' => $this->metricsCollection->getSelected()->getId(),
'metrics' => $this->metricsCollection->export(),
'filter' => $this->filtersCollection->getSelectedIds(),
'filters' => $this->filtersCollection->export(),
];
}
}
...@@ -6,6 +6,7 @@ class Manager ...@@ -6,6 +6,7 @@ class Manager
const DASHBOARDS = [ const DASHBOARDS = [
'traffic' => TrafficDashboard::class, 'traffic' => TrafficDashboard::class,
'trending' => TrendingDashboard::class, 'trending' => TrendingDashboard::class,
'earnings' => EarningsDashboard::class,
]; ];
/** /**
......
...@@ -3,6 +3,7 @@ namespace Minds\Core\Analytics\Dashboards\Metrics; ...@@ -3,6 +3,7 @@ namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection; use Minds\Core\Analytics\Dashboards\Timespans\TimespansCollection;
use Minds\Core\Di\Di; use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Traits\MagicAttributes; use Minds\Traits\MagicAttributes;
/** /**
...@@ -43,6 +44,37 @@ abstract class AbstractMetric ...@@ -43,6 +44,37 @@ abstract class AbstractMetric
/** @var FiltersCollection */ /** @var FiltersCollection */
protected $filtersCollection; protected $filtersCollection;
/**
* Return the usd guid for metrics
* @return string
*/
protected function getUserGuid(): ?string
{
$filters = $this->filtersCollection->getSelected();
$channelFilter = $filters['channel'];
if (!$channelFilter) {
if (!Session::getLoggedInUserGuid()) {
throw new \Exception("You must be loggedin");
}
return Session::getLoggedInUserGuid();
}
if ($channelFilter->getSelectedOption() === 'all') {
if (Session::isAdmin()) {
return "";
}
$channelFilter->setSelectedOption('self');
}
if ($channelFilter->getSelectedOption() === 'self') {
return Session::getLoggedInUserGuid();
}
// TODO: check permissions first
return $channelFilter->getSelectedOption();
}
/** /**
* Export * Export
* @param array $extras * @param array $extras
......
...@@ -32,6 +32,10 @@ class ActiveUsersMetric extends AbstractMetric ...@@ -32,6 +32,10 @@ class ActiveUsersMetric extends AbstractMetric
*/ */
public function buildSummary(): self public function buildSummary(): self
{ {
if ($this->getUserGuid()) {
return $this;
}
$timespan = $this->timespansCollection->getSelected(); $timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected(); $filters = $this->filtersCollection->getSelected();
...@@ -118,6 +122,11 @@ class ActiveUsersMetric extends AbstractMetric ...@@ -118,6 +122,11 @@ class ActiveUsersMetric extends AbstractMetric
*/ */
public function buildVisualisation(): self public function buildVisualisation(): self
{ {
if ($this->getUserGuid()) {
$this->visualisation = (new Visualisations\ChartVisualisation());
return $this;
}
$timespan = $this->timespansCollection->getSelected(); $timespan = $this->timespansCollection->getSelected();
$xValues = []; $xValues = [];
$yValues = []; $yValues = [];
......
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Earnings;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
abstract class AbstractEarningsMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = '';
/** @var string */
protected $label = '';
/** @var string */
protected $description = '';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $unit = 'usd';
/** @var string */
protected $aggField = '';
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();
$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' => $this->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();
$filters = $this->filtersCollection->getSelected();
$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' => $this->aggField,
],
],
],
],
],
],
];
// 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;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Earnings;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class ReferralsEarningsMetric extends AbstractEarningsMetric
{
/** @var string */
protected $id = 'earnings_referrals';
/** @var string */
protected $label = 'Referrals USD';
/** @var string */
protected $description = 'Referral earnings for PRO users';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'usd_earnings::referrals';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Earnings;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class SalesEarningsMetric extends AbstractEarningsMetric
{
/** @var string */
protected $id = 'earnings_sales';
/** @var string */
protected $label = 'Sales USD';
/** @var string */
protected $description = 'Sales earnings for PRO users';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'usd_earnings::sales';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Earnings;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class TotalEarningsMetric extends AbstractEarningsMetric
{
/** @var string */
protected $id = 'earnings_total';
/** @var string */
protected $label = 'Total Earnings';
/** @var string */
protected $description = 'Total earnings for PRO users';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'usd_earnings::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Earnings;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class ViewsEarningsMetric extends AbstractEarningsMetric
{
/** @var string */
protected $id = 'earnings_views';
/** @var string */
protected $label = 'Views USD';
/** @var string */
protected $description = 'Views earnings for PRO users';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'usd_earnings::views';
}
...@@ -70,6 +70,9 @@ class MetricsCollection implements DashboardCollectionInterface ...@@ -70,6 +70,9 @@ class MetricsCollection implements DashboardCollectionInterface
*/ */
public function getSelected(): AbstractMetric public function getSelected(): AbstractMetric
{ {
if (!isset($this->metrics[$this->selectedId])) {
$this->selectedId = key($this->metrics);
}
return $this->metrics[$this->selectedId]; return $this->metrics[$this->selectedId];
} }
......
...@@ -32,6 +32,10 @@ class SignupsMetric extends AbstractMetric ...@@ -32,6 +32,10 @@ class SignupsMetric extends AbstractMetric
*/ */
public function buildSummary(): self public function buildSummary(): self
{ {
if ($this->getUserGuid()) {
return $this;
}
$timespan = $this->timespansCollection->getSelected(); $timespan = $this->timespansCollection->getSelected();
$comparisonTsMs = strtotime("-{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000; $comparisonTsMs = strtotime("-{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$currentTsMs = $timespan->getFromTsMs(); $currentTsMs = $timespan->getFromTsMs();
...@@ -95,6 +99,11 @@ class SignupsMetric extends AbstractMetric ...@@ -95,6 +99,11 @@ class SignupsMetric extends AbstractMetric
*/ */
public function buildVisualisation(): self public function buildVisualisation(): self
{ {
if ($this->getUserGuid()) {
$this->visualisation = (new Visualisations\ChartVisualisation());
return $this;
}
$timespan = $this->timespansCollection->getSelected(); $timespan = $this->timespansCollection->getSelected();
$xValues = []; $xValues = [];
$yValues = []; $yValues = [];
......
...@@ -3,7 +3,7 @@ namespace Minds\Core\Analytics\Dashboards\Metrics; ...@@ -3,7 +3,7 @@ namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di; use Minds\Core\Di\Di;
use Minds\Core\Session; use Minds\Core\Session;
use Minds\Core\Data\Elasticsearch; use Minds\Core\Data\ElasticSearch;
class ViewsMetric extends AbstractMetric class ViewsMetric extends AbstractMetric
{ {
...@@ -48,14 +48,7 @@ class ViewsMetric extends AbstractMetric ...@@ -48,14 +48,7 @@ class ViewsMetric extends AbstractMetric
$values = []; $values = [];
foreach ([ 'value' => $currentTsMs, 'comparison' => $comparisonTsMs ] as $key => $tsMs) { foreach ([ 'value' => $currentTsMs, 'comparison' => $comparisonTsMs ] as $key => $tsMs) {
$must = []; $must = [];
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
'resolution' => $timespan->getInterval(),
],
];
$must[]['range'] = [ $must[]['range'] = [
'@timestamp' => [ '@timestamp' => [
'gte' => $tsMs, 'gte' => $tsMs,
...@@ -133,13 +126,6 @@ class ViewsMetric extends AbstractMetric ...@@ -133,13 +126,6 @@ class ViewsMetric extends AbstractMetric
], ],
]; ];
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
'resolution' => $timespan->getInterval(),
],
];
if ($userGuid = $this->getUserGuid()) { if ($userGuid = $this->getUserGuid()) {
$must[] = [ $must[] = [
'term' => [ 'term' => [
...@@ -203,24 +189,4 @@ class ViewsMetric extends AbstractMetric ...@@ -203,24 +189,4 @@ class ViewsMetric extends AbstractMetric
return $this; return $this;
} }
private function getUserGuid(): ?string
{
$filters = $this->filtersCollection->getSelected();
$channelFilter = $filters['channel'];
if (!$channelFilter) {
return "";
}
if ($channelFilter->getSelectedOption() === 'self') {
return Session::getLoggedInUserGuid();
}
if ($channelFilter->getSelectedOption() === 'all') {
return "";
}
// TODO: check permissions first
return $channelFilter->getSelectedOption();
}
} }
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
namespace Minds\Core\Analytics\Dashboards\Metrics; namespace Minds\Core\Analytics\Dashboards\Metrics;
use Minds\Core\Di\Di; use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch; use Minds\Core\Data\ElasticSearch;
use Minds\Core\Entities\Resolver;
use Minds\Common\Urn;
class ViewsTableMetric extends AbstractMetric class ViewsTableMetric extends AbstractMetric
{ {
/** @var Elasticsearch\Client */ /** @var Elasticsearch\Client */
private $es; private $es;
/** @var Resolver */
private $entitiesResolver;
/** @var string */ /** @var string */
protected $id = 'views_table'; protected $id = 'views_table';
...@@ -25,6 +29,7 @@ class ViewsTableMetric extends AbstractMetric ...@@ -25,6 +29,7 @@ class ViewsTableMetric extends AbstractMetric
public function __construct($es = null) public function __construct($es = null)
{ {
$this->es = $es ?? Di::_()->get('Database\ElasticSearch'); $this->es = $es ?? Di::_()->get('Database\ElasticSearch');
$this->entitiesResolver = $entitiesResolver ?? new Resolver();
} }
/** /**
...@@ -67,11 +72,11 @@ class ViewsTableMetric extends AbstractMetric ...@@ -67,11 +72,11 @@ class ViewsTableMetric extends AbstractMetric
]; ];
// Specify the resolution to avoid duplicates // Specify the resolution to avoid duplicates
$must[] = [ // $must[] = [
'term' => [ // 'term' => [
'resolution' => $timespan->getInterval(), // 'resolution' => $timespan->getInterval(),
], // ],
]; // ];
if ($userGuid = $this->getUserGuid()) { if ($userGuid = $this->getUserGuid()) {
$must[] = [ $must[] = [
...@@ -107,10 +112,10 @@ class ViewsTableMetric extends AbstractMetric ...@@ -107,10 +112,10 @@ class ViewsTableMetric extends AbstractMetric
], ],
], ],
'views::organic' => [ 'views::organic' => [
'sum' => [ 'sum' => [
'field' => 'views::organic', 'field' => 'views::organic',
], ],
], ],
'views::single' => [ 'views::single' => [
'sum' => [ 'sum' => [
'field' => 'views::single', 'field' => 'views::single',
...@@ -126,45 +131,47 @@ class ViewsTableMetric extends AbstractMetric ...@@ -126,45 +131,47 @@ class ViewsTableMetric extends AbstractMetric
$prepared = new ElasticSearch\Prepared\Search(); $prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query); $prepared->query($query);
$response = $this->es->request($prepared); $response = $this->es->request($prepared);
$buckets = []; $buckets = [];
foreach ($response['aggregations']['1']['buckets'] as $bucket) { foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000); $date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$subBuckets = []; $entity = $this->entitiesResolver->single(new Urn($bucket['key']));
$buckets[] = [ $buckets[] = [
'key' => $bucket['key'], 'key' => $bucket['key'],
'values' => [ 'values' => [
'views::total' => $bucket['views::total']['value'], 'entity' => $entity ? $entity->export() : null,
'views::organic' => $bucket['views::organic']['value'], 'views::total' => $bucket['views::total']['value'],
'views::single' => $bucket['views::single']['value'], 'views::organic' => $bucket['views::organic']['value'],
], 'views::single' => $bucket['views::single']['value'],
],
]; ];
} }
$this->visualisation = (new Visualisations\TableVisualisation()) $this->visualisation = (new Visualisations\TableVisualisation())
->setBuckets($buckets) ->setBuckets($buckets)
->setColumns([ 'views::total', 'views::organic', 'views::single']); ->setColumns([
[
'id' => 'entity',
'label' => '',
'order' => 0,
],
[
'id' => 'views::total',
'label' => 'Total Views',
'order' => 1,
],
[
'id' => 'views::organic',
'label' => 'Organic',
'order' => 2,
],
[
'id' => 'views::single',
'label' => 'Single',
'order' => 3,
]
]);
return $this; return $this;
} }
private function getUserGuid(): ?string
{
$filters = $this->filtersCollection->getSelected();
$channelFilter = $filters['channel'];
if (!$channelFilter) {
return "";
}
if ($channelFilter->getSelectedOption() === 'self') {
return Session::getLoggedInUserGuid();
}
if ($channelFilter->getSelectedOption() === 'all') {
return "";
}
// TODO: check permissions first
return $channelFilter->getSelectedOption();
}
} }
...@@ -63,7 +63,7 @@ class TrafficDashboard implements DashboardInterface ...@@ -63,7 +63,7 @@ class TrafficDashboard implements DashboardInterface
$this->filtersCollection $this->filtersCollection
->setSelectedIds($this->filterIds) ->setSelectedIds($this->filterIds)
->addFilters( ->addFilters(
new Filters\PlatformFilter(), // new Filters\PlatformFilter(),
new Filters\ViewTypeFilter(), new Filters\ViewTypeFilter(),
new Filters\ChannelFilter() new Filters\ChannelFilter()
); );
......
...@@ -60,7 +60,7 @@ class TrendingDashboard implements DashboardInterface ...@@ -60,7 +60,7 @@ class TrendingDashboard implements DashboardInterface
$this->filtersCollection $this->filtersCollection
->setSelectedIds($this->filterIds) ->setSelectedIds($this->filterIds)
->addFilters( ->addFilters(
new Filters\PlatformFilter(), // new Filters\PlatformFilter(),
new Filters\ViewTypeFilter(), new Filters\ViewTypeFilter(),
new Filters\ChannelFilter() new Filters\ChannelFilter()
); );
......
...@@ -60,4 +60,12 @@ class EntityCentricRecord ...@@ -60,4 +60,12 @@ class EntityCentricRecord
$this->sums[$metric] = $this->sums[$metric] + $value; $this->sums[$metric] = $this->sums[$metric] + $value;
return $this; return $this;
} }
/**
* @return string
*/
public function getUrn(): string
{
return (string) implode('-', [ $this->getEntityUrn(), $this->getResolution(), $this->getTimestamp() ]);
}
} }
...@@ -13,6 +13,7 @@ class Manager ...@@ -13,6 +13,7 @@ class Manager
{ {
/** @var array */ /** @var array */
const SYNCHRONISERS = [ const SYNCHRONISERS = [
PartnerEarningsSynchroniser::class,
SignupsSynchroniser::class, SignupsSynchroniser::class,
ActiveUsersSynchroniser::class, ActiveUsersSynchroniser::class,
ViewsSynchroniser::class, ViewsSynchroniser::class,
...@@ -21,6 +22,9 @@ class Manager ...@@ -21,6 +22,9 @@ class Manager
/** @var Repository */ /** @var Repository */
protected $repository; protected $repository;
/** @var Sums */
protected $sums;
/** @var int */ /** @var int */
private $from; private $from;
...@@ -28,9 +32,11 @@ class Manager ...@@ -28,9 +32,11 @@ class Manager
private $to; private $to;
public function __construct( public function __construct(
$repository = null $repository = null,
$sums = null
) { ) {
$this->repository = $repository ?: new Repository(); $this->repository = $repository ?? new Repository();
$this->sums = $sums ?? new Sums();
} }
/** /**
...@@ -81,4 +87,13 @@ class Manager ...@@ -81,4 +87,13 @@ class Manager
public function getAggregateByQuery(array $query): array public function getAggregateByQuery(array $query): array
{ {
} }
/**
* @param array $opts
* @retun iterable
*/
public function getListAggregatedByOwner(array $opts = []): iterable
{
return $this->sums->getByOwner($opts);
}
} }
<?php
namespace Minds\Core\Analytics\EntityCentric;
use Minds\Core\Monetization\Partners\Manager as PartnersManager;
use DateTime;
use Exception;
class PartnerEarningsSynchroniser
{
/** @var PartnersManager */
private $partnersManager;
public function __construct($partnersManager = null)
{
$this->partnersManager = $partnersManager ?? new PartnersManager;
}
/**
* @param int $from
* @return self
*/
public function setFrom($from): self
{
$this->from = $from;
return $this;
}
/**
* Convert to records
* @return iterable
*/
public function toRecords(): iterable
{
$opts = [];
$opts['from'] = $this->from;
$records = [];
$i = 0;
while (true) {
$result = $this->partnersManager->getList($opts);
$opts['offset'] = $result->getPagingToken();
foreach ($result as $deposit) {
$urn = "urn:user:{$deposit->getUserGuid()}";
$record = new EntityCentricRecord();
$record->setEntityUrn($urn)
->setOwnerGuid($deposit->getUserGuid())
->setTimestamp($deposit->getTimestamp()) // TODO: confirm if this should be rounded to midnight
->setResolution('day');
// In order to increment sums, replace with what has already been seen
if (isset($records[$record->getUrn()])) {
$record = $records[$record->getUrn()];
}
$record->incrementSum('usd_earnings::total', $deposit->getAmountCents());
$record->incrementSum("usd_earnings::{$deposit->getItem()}", $deposit->getAmountCents());
$records[$record->getUrn()] = $record;
}
if ($result->isLastPage()) {
break;
}
}
foreach ($records as $record) {
var_dump($record);
yield $record;
}
}
}
<?php
/**
* EntityCentric Sums
* @author Mark
*/
namespace Minds\Core\Analytics\EntityCentric;
use DateTime;
use DateTimeZone;
use Exception;
use Minds\Common\Repository\Response;
use Minds\Core\Data\ElasticSearch\Client as ElasticClient;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Di\Di;
class Sums
{
/** @var ElasticClient */
protected $es;
/**
* Repository constructor.
* @param ElasticClient $es
*/
public function __construct(
$es = null
) {
$this->es = $es ?: Di::_()->get('Database\ElasticSearch');
}
public function getByOwner(array $opts = []): iterable
{
$opts = array_merge([
'fields' => [],
'from' => time(),
], $opts);
$must = [];
$must[] = [
'range' => [
'@timestamp' => [
'gte' => $opts['from'] * 1000,
'lt' => strtotime('+1 day', $opts['from']) * 1000,
],
],
];
$termsAgg = [];
foreach ($opts['fields'] as $field) {
$termsAgg[$field] = [
'sum' => [
'field' => $field,
],
];
$must[] = [
'exists' => [
'field' => $field,
],
];
}
$partition = 0;
$partitions = 100;
$partitionSize = 5000; // Allows for 500,000 users
while (++$partition < $partitions) {
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'terms' => [
'field' => 'owner_guid',
'min_doc_count' => 1,
'size' => $partitionSize,
'include' => [
'partition' => $partition,
'num_partitions' => $partitions,
],
],
'aggs' => $termsAgg,
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
yield $bucket;
}
}
}
}