update Piwik to version 2.16 (fixes #91)
This commit is contained in:
parent
296343bf3b
commit
d885a4baa9
5833 changed files with 418860 additions and 226988 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
|
|
@ -9,57 +9,58 @@
|
|||
namespace Piwik;
|
||||
|
||||
use Piwik\Archive\Parameters;
|
||||
|
||||
use Piwik\ArchiveProcessor\Rules;
|
||||
use Piwik\Archive\ArchiveInvalidator;
|
||||
use Piwik\Container\StaticContainer;
|
||||
use Piwik\DataAccess\ArchiveSelector;
|
||||
use Piwik\Period\Range;
|
||||
use Piwik\Period\Factory as PeriodFactory;
|
||||
|
||||
/**
|
||||
* The **Archive** class is used to query cached analytics statistics
|
||||
* (termed "archive data").
|
||||
*
|
||||
*
|
||||
* You can use **Archive** instances to get data that was archived for one or more sites,
|
||||
* for one or more periods and one optional segment.
|
||||
*
|
||||
*
|
||||
* If archive data is not found, this class will initiate the archiving process. [1](#footnote-1)
|
||||
*
|
||||
*
|
||||
* **Archive** instances must be created using the {@link build()} factory method;
|
||||
* they cannot be constructed.
|
||||
*
|
||||
*
|
||||
* You can search for metrics (such as `nb_visits`) using the {@link getNumeric()} and
|
||||
* {@link getDataTableFromNumeric()} methods. You can search for
|
||||
* reports using the {@link getBlob()}, {@link getDataTable()} and {@link getDataTableExpanded()} methods.
|
||||
*
|
||||
*
|
||||
* If you're creating an API that returns report data, you may want to use the
|
||||
* {@link getDataTableFromArchive()} helper function.
|
||||
*
|
||||
*
|
||||
* ### Learn more
|
||||
*
|
||||
*
|
||||
* Learn more about _archiving_ [here](/guides/all-about-analytics-data).
|
||||
*
|
||||
*
|
||||
* ### Limitations
|
||||
*
|
||||
*
|
||||
* - You cannot get data for multiple range periods in a single query.
|
||||
* - You cannot get data for periods of different types in a single query.
|
||||
*
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
*
|
||||
* **_Querying metrics for an API method_**
|
||||
*
|
||||
*
|
||||
* // one site and one period
|
||||
* $archive = Archive::build($idSite = 1, $period = 'week', $date = '2013-03-08');
|
||||
* return $archive->getDataTableFromNumeric(array('nb_visits', 'nb_actions'));
|
||||
*
|
||||
*
|
||||
* // all sites and multiple dates
|
||||
* $archive = Archive::build($idSite = 'all', $period = 'month', $date = '2013-01-02,2013-03-08');
|
||||
* return $archive->getDataTableFromNumeric(array('nb_visits', 'nb_actions'));
|
||||
*
|
||||
*
|
||||
* **_Querying and using metrics immediately_**
|
||||
*
|
||||
*
|
||||
* // one site and one period
|
||||
* $archive = Archive::build($idSite = 1, $period = 'week', $date = '2013-03-08');
|
||||
* $data = $archive->getNumeric(array('nb_visits', 'nb_actions'));
|
||||
*
|
||||
*
|
||||
* $visits = $data['nb_visits'];
|
||||
* $actions = $data['nb_actions'];
|
||||
*
|
||||
|
|
@ -68,41 +69,40 @@ use Piwik\Period\Range;
|
|||
* // multiple sites and multiple dates
|
||||
* $archive = Archive::build($idSite = '1,2,3', $period = 'month', $date = '2013-01-02,2013-03-08');
|
||||
* $data = $archive->getNumeric('nb_visits');
|
||||
*
|
||||
*
|
||||
* $janSite1Visits = $data['1']['2013-01-01,2013-01-31']['nb_visits'];
|
||||
* $febSite1Visits = $data['1']['2013-02-01,2013-02-28']['nb_visits'];
|
||||
* // ... etc.
|
||||
*
|
||||
*
|
||||
* **_Querying for reports_**
|
||||
*
|
||||
*
|
||||
* $archive = Archive::build($idSite = 1, $period = 'week', $date = '2013-03-08');
|
||||
* $dataTable = $archive->getDataTable('MyPlugin_MyReport');
|
||||
* // ... manipulate $dataTable ...
|
||||
* return $dataTable;
|
||||
*
|
||||
*
|
||||
* **_Querying a report for an API method_**
|
||||
*
|
||||
*
|
||||
* public function getMyReport($idSite, $period, $date, $segment = false, $expanded = false)
|
||||
* {
|
||||
* $dataTable = Archive::getDataTableFromArchive('MyPlugin_MyReport', $idSite, $period, $date, $segment, $expanded);
|
||||
* $dataTable->queueFilter('ReplaceColumnNames');
|
||||
* return $dataTable;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* **_Querying data for multiple range periods_**
|
||||
*
|
||||
*
|
||||
* // get data for first range
|
||||
* $archive = Archive::build($idSite = 1, $period = 'range', $date = '2013-03-08,2013-03-12');
|
||||
* $dataTable = $archive->getDataTableFromNumeric(array('nb_visits', 'nb_actions'));
|
||||
*
|
||||
*
|
||||
* // get data for second range
|
||||
* $archive = Archive::build($idSite = 1, $period = 'range', $date = '2013-03-15,2013-03-20');
|
||||
* $dataTable = $archive->getDataTableFromNumeric(array('nb_visits', 'nb_actions'));
|
||||
*
|
||||
*
|
||||
* <a name="footnote-1"></a>
|
||||
* [1]: The archiving process will not be launched if browser archiving is disabled
|
||||
* and the current request came from a browser (and not the **archive.php** cron
|
||||
* script).
|
||||
* and the current request came from a browser.
|
||||
*
|
||||
*
|
||||
* @api
|
||||
|
|
@ -162,6 +162,16 @@ class Archive
|
|||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* @var \Piwik\Cache\Cache
|
||||
*/
|
||||
private static $cache;
|
||||
|
||||
/**
|
||||
* @var ArchiveInvalidator
|
||||
*/
|
||||
private $invalidator;
|
||||
|
||||
/**
|
||||
* @param Parameters $params
|
||||
* @param bool $forceIndexedBySite Whether to force index the result of a query by site ID.
|
||||
|
|
@ -173,6 +183,8 @@ class Archive
|
|||
$this->params = $params;
|
||||
$this->forceIndexedBySite = $forceIndexedBySite;
|
||||
$this->forceIndexedByDate = $forceIndexedByDate;
|
||||
|
||||
$this->invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -192,37 +204,42 @@ class Archive
|
|||
* or date range (ie, 'YYYY-MM-DD,YYYY-MM-DD').
|
||||
* @param bool|false|string $segment Segment definition or false if no segment should be used. {@link Piwik\Segment}
|
||||
* @param bool|false|string $_restrictSitesToLogin Used only when running as a scheduled task.
|
||||
* @param bool $skipAggregationOfSubTables Whether the archive, when it is processed, should also aggregate all sub-tables
|
||||
* @return Archive
|
||||
* @return static
|
||||
*/
|
||||
public static function build($idSites, $period, $strDate, $segment = false, $_restrictSitesToLogin = false, $skipAggregationOfSubTables = false)
|
||||
public static function build($idSites, $period, $strDate, $segment = false, $_restrictSitesToLogin = false)
|
||||
{
|
||||
$websiteIds = Site::getIdSitesFromIdSitesString($idSites, $_restrictSitesToLogin);
|
||||
|
||||
$timezone = false;
|
||||
if (count($websiteIds) == 1) {
|
||||
$timezone = Site::getTimezoneFor($websiteIds[0]);
|
||||
}
|
||||
|
||||
if (Period::isMultiplePeriod($strDate, $period)) {
|
||||
$oPeriod = new Range($period, $strDate);
|
||||
$oPeriod = PeriodFactory::build($period, $strDate, $timezone);
|
||||
$allPeriods = $oPeriod->getSubperiods();
|
||||
} else {
|
||||
$timezone = count($websiteIds) == 1 ? Site::getTimezoneFor($websiteIds[0]) : false;
|
||||
$oPeriod = Period::makePeriodFromQueryParams($timezone, $period, $strDate);
|
||||
$oPeriod = PeriodFactory::makePeriodFromQueryParams($timezone, $period, $strDate);
|
||||
$allPeriods = array($oPeriod);
|
||||
}
|
||||
$segment = new Segment($segment, $websiteIds);
|
||||
$idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG;
|
||||
|
||||
$segment = new Segment($segment, $websiteIds);
|
||||
$idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG;
|
||||
$isMultipleDate = Period::isMultiplePeriod($strDate, $period);
|
||||
return Archive::factory($segment, $allPeriods, $websiteIds, $idSiteIsAll, $isMultipleDate, $skipAggregationOfSubTables);
|
||||
|
||||
return static::factory($segment, $allPeriods, $websiteIds, $idSiteIsAll, $isMultipleDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Archive instance that will query archive data for the given set of
|
||||
* sites and periods, using an optional segment.
|
||||
*
|
||||
*
|
||||
* This method uses an array of Period instances and a Segment instance, instead of strings
|
||||
* like {@link build()}.
|
||||
*
|
||||
*
|
||||
* If you want to create an Archive instance using data found in query parameters,
|
||||
* use {@link build()}.
|
||||
*
|
||||
*
|
||||
* @param Segment $segment The segment to use. For no segment, use `new Segment('', $idSites)`.
|
||||
* @param array $periods An array of Period instances.
|
||||
* @param array $idSites An array of site IDs (eg, `array(1, 2, 3)`).
|
||||
|
|
@ -232,41 +249,42 @@ class Archive
|
|||
* @param bool $isMultipleDate Whether multiple dates are being queried or not. If true, then
|
||||
* the result of querying functions will be indexed by period,
|
||||
* regardless of whether `count($periods) == 1`.
|
||||
* @param bool $skipAggregationOfSubTables Whether the archive should skip aggregation of all sub-tables
|
||||
*
|
||||
* @return Archive
|
||||
*/
|
||||
public static function factory(Segment $segment, array $periods, array $idSites, $idSiteIsAll = false, $isMultipleDate = false, $skipAggregationOfSubTables = false)
|
||||
public static function factory(Segment $segment, array $periods, array $idSites, $idSiteIsAll = false, $isMultipleDate = false)
|
||||
{
|
||||
$forceIndexedBySite = false;
|
||||
$forceIndexedByDate = false;
|
||||
|
||||
if ($idSiteIsAll || count($idSites) > 1) {
|
||||
$forceIndexedBySite = true;
|
||||
}
|
||||
|
||||
if (count($periods) > 1 || $isMultipleDate) {
|
||||
$forceIndexedByDate = true;
|
||||
}
|
||||
|
||||
$params = new Parameters($idSites, $periods, $segment, $skipAggregationOfSubTables);
|
||||
$params = new Parameters($idSites, $periods, $segment);
|
||||
|
||||
return new Archive($params, $forceIndexedBySite, $forceIndexedByDate);
|
||||
return new static($params, $forceIndexedBySite, $forceIndexedByDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries and returns metric data in an array.
|
||||
*
|
||||
*
|
||||
* If multiple sites were requested in {@link build()} or {@link factory()} the result will
|
||||
* be indexed by site ID.
|
||||
*
|
||||
*
|
||||
* If multiple periods were requested in {@link build()} or {@link factory()} the result will
|
||||
* be indexed by period.
|
||||
*
|
||||
*
|
||||
* The site ID index is always first, so if multiple sites & periods were requested, the result
|
||||
* will be indexed by site ID first, then period.
|
||||
*
|
||||
*
|
||||
* @param string|array $names One or more archive names, eg, `'nb_visits'`, `'Referrers_distinctKeywords'`,
|
||||
* etc.
|
||||
* @return false|numeric|array `false` if there is no data to return, a single numeric value if we're not querying
|
||||
* @return false|integer|array `false` if there is no data to return, a single numeric value if we're not querying
|
||||
* for multiple sites/periods, or an array if multiple sites, periods or names are
|
||||
* queried for.
|
||||
*/
|
||||
|
|
@ -288,50 +306,22 @@ class Archive
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries and returns blob data in an array.
|
||||
*
|
||||
* Reports are stored in blobs as serialized arrays of {@link DataTable\Row} instances, but this
|
||||
* data can technically be anything. In other words, you can store whatever you want
|
||||
* as archive data blobs.
|
||||
*
|
||||
* If multiple sites were requested in {@link build()} or {@link factory()} the result will
|
||||
* be indexed by site ID.
|
||||
*
|
||||
* If multiple periods were requested in {@link build()} or {@link factory()} the result will
|
||||
* be indexed by period.
|
||||
*
|
||||
* The site ID index is always first, so if multiple sites & periods were requested, the result
|
||||
* will be indexed by site ID first, then period.
|
||||
*
|
||||
* @param string|array $names One or more archive names, eg, `'Referrers_keywordBySearchEngine'`.
|
||||
* @param null|string $idSubtable If we're returning serialized DataTable data, then this refers
|
||||
* to the subtable ID to return. If set to 'all', all subtables
|
||||
* of each requested report are returned.
|
||||
* @return array An array of appropriately indexed blob data.
|
||||
*/
|
||||
public function getBlob($names, $idSubtable = null)
|
||||
{
|
||||
$data = $this->get($names, 'blob', $idSubtable);
|
||||
return $data->getIndexedArray($this->getResultIndices());
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries and returns metric data in a DataTable instance.
|
||||
*
|
||||
*
|
||||
* If multiple sites were requested in {@link build()} or {@link factory()} the result will
|
||||
* be a DataTable\Map that is indexed by site ID.
|
||||
*
|
||||
*
|
||||
* If multiple periods were requested in {@link build()} or {@link factory()} the result will
|
||||
* be a {@link DataTable\Map} that is indexed by period.
|
||||
*
|
||||
*
|
||||
* The site ID index is always first, so if multiple sites & periods were requested, the result
|
||||
* will be a {@link DataTable\Map} indexed by site ID which contains {@link DataTable\Map} instances that are
|
||||
* indexed by period.
|
||||
*
|
||||
*
|
||||
* _Note: Every DataTable instance returned will have at most one row in it. The contents of each
|
||||
* row will be the requested metrics for the appropriate site and period._
|
||||
*
|
||||
*
|
||||
* @param string|array $names One or more archive names, eg, 'nb_visits', 'Referrers_distinctKeywords',
|
||||
* etc.
|
||||
* @return DataTable|DataTable\Map A DataTable if multiple sites and periods were not requested.
|
||||
|
|
@ -343,22 +333,40 @@ class Archive
|
|||
return $data->getDataTable($this->getResultIndices());
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link getDataTableFromNumeric()} but merges all children on the created DataTable.
|
||||
*
|
||||
* This is the same as doing `$this->getDataTableFromNumeric()->mergeChildren()` but this way it is much faster.
|
||||
*
|
||||
* @return DataTable|DataTable\Map
|
||||
*
|
||||
* @internal Currently only used by MultiSites.getAll plugin. Feel free to remove internal tag if needed somewhere
|
||||
* else. If no longer needed by MultiSites.getAll please remove this method. If you need this to work in
|
||||
* a bit different way feel free to refactor as always.
|
||||
*/
|
||||
public function getDataTableFromNumericAndMergeChildren($names)
|
||||
{
|
||||
$data = $this->get($names, 'numeric');
|
||||
$resultIndexes = $this->getResultIndices();
|
||||
return $data->getMergedDataTable($resultIndexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries and returns one or more reports as DataTable instances.
|
||||
*
|
||||
*
|
||||
* This method will query blob data that is a serialized array of of {@link DataTable\Row}'s and
|
||||
* unserialize it.
|
||||
*
|
||||
*
|
||||
* If multiple sites were requested in {@link build()} or {@link factory()} the result will
|
||||
* be a {@link DataTable\Map} that is indexed by site ID.
|
||||
*
|
||||
*
|
||||
* If multiple periods were requested in {@link build()} or {@link factory()} the result will
|
||||
* be a DataTable\Map that is indexed by period.
|
||||
*
|
||||
*
|
||||
* The site ID index is always first, so if multiple sites & periods were requested, the result
|
||||
* will be a {@link DataTable\Map} indexed by site ID which contains {@link DataTable\Map} instances that are
|
||||
* indexed by period.
|
||||
*
|
||||
*
|
||||
* @param string $name The name of the record to get. This method can only query one record at a time.
|
||||
* @param int|string|null $idSubtable The ID of the subtable to get (if any).
|
||||
* @return DataTable|DataTable\Map A DataTable if multiple sites and periods were not requested.
|
||||
|
|
@ -372,13 +380,13 @@ class Archive
|
|||
|
||||
/**
|
||||
* Queries and returns one report with all of its subtables loaded.
|
||||
*
|
||||
*
|
||||
* If multiple sites were requested in {@link build()} or {@link factory()} the result will
|
||||
* be a DataTable\Map that is indexed by site ID.
|
||||
*
|
||||
*
|
||||
* If multiple periods were requested in {@link build()} or {@link factory()} the result will
|
||||
* be a DataTable\Map that is indexed by period.
|
||||
*
|
||||
*
|
||||
* The site ID index is always first, so if multiple sites & periods were requested, the result
|
||||
* will be a {@link DataTable\Map indexed} by site ID which contains {@link DataTable\Map} instances that are
|
||||
* indexed by period.
|
||||
|
|
@ -400,16 +408,18 @@ class Archive
|
|||
|
||||
/**
|
||||
* Returns the list of plugins that archive the given reports.
|
||||
*
|
||||
*
|
||||
* @param array $archiveNames
|
||||
* @return array
|
||||
*/
|
||||
private function getRequestedPlugins($archiveNames)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($archiveNames as $name) {
|
||||
$result[] = self::getPluginForReport($name);
|
||||
}
|
||||
|
||||
return array_unique($result);
|
||||
}
|
||||
|
||||
|
|
@ -427,7 +437,7 @@ class Archive
|
|||
/**
|
||||
* Helper function that creates an Archive instance and queries for report data using
|
||||
* query parameter data. API methods can use this method to reduce code redundancy.
|
||||
*
|
||||
*
|
||||
* @param string $name The name of the report to return.
|
||||
* @param int|string|array $idSite @see {@link build()}
|
||||
* @param string $period @see {@link build()}
|
||||
|
|
@ -435,21 +445,19 @@ class Archive
|
|||
* @param string $segment @see {@link build()}
|
||||
* @param bool $expanded If true, loads all subtables. See {@link getDataTableExpanded()}
|
||||
* @param int|null $idSubtable See {@link getDataTableExpanded()}
|
||||
* @param bool $skipAggregationOfSubTables Whether or not we should skip the aggregation of all sub-tables and only aggregate parent DataTable.
|
||||
* @param int|null $depth See {@link getDataTableExpanded()}
|
||||
* @throws \Exception
|
||||
* @return DataTable|DataTable\Map See {@link getDataTable()} and
|
||||
* {@link getDataTableExpanded()} for more
|
||||
* information
|
||||
* @deprecated Since Piwik 2.12.0 Use Archive::createDataTableFromArchive() instead
|
||||
*/
|
||||
public static function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded,
|
||||
$idSubtable = null, $skipAggregationOfSubTables = false, $depth = null)
|
||||
$idSubtable = null, $depth = null)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
|
||||
if($skipAggregationOfSubTables && ($expanded || $idSubtable)) {
|
||||
throw new \Exception("Not expected to skipAggregationOfSubTables when expanded=1 or idSubtable is set.");
|
||||
}
|
||||
$archive = Archive::build($idSite, $period, $date, $segment, $_restrictSitesToLogin = false, $skipAggregationOfSubTables);
|
||||
$archive = Archive::build($idSite, $period, $date, $segment, $_restrictSitesToLogin = false);
|
||||
if ($idSubtable === false) {
|
||||
$idSubtable = null;
|
||||
}
|
||||
|
|
@ -465,9 +473,102 @@ class Archive
|
|||
return $dataTable;
|
||||
}
|
||||
|
||||
private function appendIdSubtable($recordName, $id)
|
||||
/**
|
||||
* Helper function that creates an Archive instance and queries for report data using
|
||||
* query parameter data. API methods can use this method to reduce code redundancy.
|
||||
*
|
||||
* @param string $recordName The name of the report to return.
|
||||
* @param int|string|array $idSite @see {@link build()}
|
||||
* @param string $period @see {@link build()}
|
||||
* @param string $date @see {@link build()}
|
||||
* @param string $segment @see {@link build()}
|
||||
* @param bool $expanded If true, loads all subtables. See {@link getDataTableExpanded()}
|
||||
* @param bool $flat If true, loads all subtables and disabled all recursive filters.
|
||||
* @param int|null $idSubtable See {@link getDataTableExpanded()}
|
||||
* @param int|null $depth See {@link getDataTableExpanded()}
|
||||
* @return DataTable|DataTable\Map
|
||||
*/
|
||||
public static function createDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null, $depth = null)
|
||||
{
|
||||
return $recordName . "_" . $id;
|
||||
if ($flat && !$idSubtable) {
|
||||
$expanded = true;
|
||||
}
|
||||
|
||||
$dataTable = self::getDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth);
|
||||
|
||||
$dataTable->queueFilter('ReplaceColumnNames');
|
||||
|
||||
if ($expanded) {
|
||||
$dataTable->queueFilterSubtables('ReplaceColumnNames');
|
||||
}
|
||||
|
||||
if ($flat) {
|
||||
$dataTable->disableRecursiveFilters();
|
||||
}
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
private function getSiteIdsThatAreRequestedInThisArchiveButWereNotInvalidatedYet()
|
||||
{
|
||||
if (is_null(self::$cache)) {
|
||||
self::$cache = Cache::getTransientCache();
|
||||
}
|
||||
|
||||
$id = 'Archive.SiteIdsOfRememberedReportsInvalidated';
|
||||
|
||||
if (!self::$cache->contains($id)) {
|
||||
self::$cache->save($id, array());
|
||||
}
|
||||
|
||||
$siteIdsAlreadyHandled = self::$cache->fetch($id);
|
||||
$siteIdsRequested = $this->params->getIdSites();
|
||||
|
||||
foreach ($siteIdsRequested as $index => $siteIdRequested) {
|
||||
$siteIdRequested = (int) $siteIdRequested;
|
||||
|
||||
if (in_array($siteIdRequested, $siteIdsAlreadyHandled)) {
|
||||
unset($siteIdsRequested[$index]); // was already handled previously, do not do it again
|
||||
} else {
|
||||
$siteIdsAlreadyHandled[] = $siteIdRequested; // we will handle this id this time
|
||||
}
|
||||
}
|
||||
|
||||
self::$cache->save($id, $siteIdsAlreadyHandled);
|
||||
|
||||
return $siteIdsRequested;
|
||||
}
|
||||
|
||||
private function invalidatedReportsIfNeeded()
|
||||
{
|
||||
$siteIdsRequested = $this->getSiteIdsThatAreRequestedInThisArchiveButWereNotInvalidatedYet();
|
||||
|
||||
if (empty($siteIdsRequested)) {
|
||||
return; // all requested site ids were already handled
|
||||
}
|
||||
|
||||
$sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated();
|
||||
|
||||
foreach ($sitesPerDays as $date => $siteIds) {
|
||||
if (empty($siteIds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$siteIdsToActuallyInvalidate = array_intersect($siteIds, $siteIdsRequested);
|
||||
|
||||
if (empty($siteIdsToActuallyInvalidate)) {
|
||||
continue; // all site ids that should be handled are already handled
|
||||
}
|
||||
|
||||
try {
|
||||
$this->invalidator->markArchivesAsInvalidated($siteIdsToActuallyInvalidate, array(Date::factory($date)), false);
|
||||
} catch (\Exception $e) {
|
||||
Site::clearCache();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
Site::clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -477,7 +578,7 @@ class Archive
|
|||
* @param null|int $idSubtable
|
||||
* @return Archive\DataCollection
|
||||
*/
|
||||
private function get($archiveNames, $archiveDataType, $idSubtable = null)
|
||||
protected function get($archiveNames, $archiveDataType, $idSubtable = null)
|
||||
{
|
||||
if (!is_array($archiveNames)) {
|
||||
$archiveNames = array($archiveNames);
|
||||
|
|
@ -487,35 +588,41 @@ class Archive
|
|||
if ($idSubtable !== null
|
||||
&& $idSubtable != self::ID_SUBTABLE_LOAD_ALL_SUBTABLES
|
||||
) {
|
||||
foreach ($archiveNames as &$name) {
|
||||
$name = $this->appendIdsubtable($name, $idSubtable);
|
||||
// this is also done in ArchiveSelector. It should be actually only done in ArchiveSelector but DataCollection
|
||||
// does require to have the subtableId appended. Needs to be changed in refactoring to have it only in one
|
||||
// place.
|
||||
$dataNames = array();
|
||||
foreach ($archiveNames as $name) {
|
||||
$dataNames[] = ArchiveSelector::appendIdsubtable($name, $idSubtable);
|
||||
}
|
||||
} else {
|
||||
$dataNames = $archiveNames;
|
||||
}
|
||||
|
||||
$result = new Archive\DataCollection(
|
||||
$archiveNames, $archiveDataType, $this->params->getIdSites(), $this->params->getPeriods(), $defaultRow = null);
|
||||
$dataNames, $archiveDataType, $this->params->getIdSites(), $this->params->getPeriods(), $defaultRow = null);
|
||||
|
||||
$archiveIds = $this->getArchiveIds($archiveNames);
|
||||
|
||||
if (empty($archiveIds)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$loadAllSubtables = $idSubtable == self::ID_SUBTABLE_LOAD_ALL_SUBTABLES;
|
||||
$archiveData = ArchiveSelector::getArchiveData($archiveIds, $archiveNames, $archiveDataType, $loadAllSubtables);
|
||||
$archiveData = ArchiveSelector::getArchiveData($archiveIds, $archiveNames, $archiveDataType, $idSubtable);
|
||||
|
||||
$isNumeric = $archiveDataType == 'numeric';
|
||||
|
||||
foreach ($archiveData as $row) {
|
||||
// values are grouped by idsite (site ID), date1-date2 (date range), then name (field name)
|
||||
$idSite = $row['idsite'];
|
||||
$periodStr = $row['date1'] . "," . $row['date2'];
|
||||
$periodStr = $row['date1'] . ',' . $row['date2'];
|
||||
|
||||
if ($archiveDataType == 'numeric') {
|
||||
$value = $this->formatNumericValue($row['value']);
|
||||
if ($isNumeric) {
|
||||
$row['value'] = $this->formatNumericValue($row['value']);
|
||||
} else {
|
||||
$value = $this->uncompress($row['value']);
|
||||
$result->addMetadata($idSite, $periodStr, 'ts_archived', $row['ts_archived']);
|
||||
$result->addMetadata($row['idsite'], $periodStr, DataTable::ARCHIVED_DATE_METADATA_NAME, $row['ts_archived']);
|
||||
}
|
||||
|
||||
$resultRow = & $result->get($idSite, $periodStr);
|
||||
$resultRow[$row['name']] = $value;
|
||||
$result->set($row['idsite'], $periodStr, $row['name'], $row['value']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
|
@ -526,6 +633,9 @@ class Archive
|
|||
* queried. This function will use the idarchive cache if it has the right data,
|
||||
* query archive tables for IDs w/o launching archiving, or launch archiving and
|
||||
* get the idarchive from ArchiveProcessor instances.
|
||||
*
|
||||
* @param string $archiveNames
|
||||
* @return array
|
||||
*/
|
||||
private function getArchiveIds($archiveNames)
|
||||
{
|
||||
|
|
@ -533,7 +643,7 @@ class Archive
|
|||
|
||||
// figure out which archives haven't been processed (if an archive has been processed,
|
||||
// then we have the archive IDs in $this->idarchives)
|
||||
$doneFlags = array();
|
||||
$doneFlags = array();
|
||||
$archiveGroups = array();
|
||||
foreach ($plugins as $plugin) {
|
||||
$doneFlag = $this->getDoneStringForPlugin($plugin);
|
||||
|
|
@ -542,7 +652,7 @@ class Archive
|
|||
if (!isset($this->idarchives[$doneFlag])) {
|
||||
$archiveGroup = $this->getArchiveGroupOfPlugin($plugin);
|
||||
|
||||
if($archiveGroup == self::ARCHIVE_ALL_PLUGINS_FLAG) {
|
||||
if ($archiveGroup == self::ARCHIVE_ALL_PLUGINS_FLAG) {
|
||||
$archiveGroup = reset($plugins);
|
||||
}
|
||||
$archiveGroups[] = $archiveGroup;
|
||||
|
|
@ -560,19 +670,7 @@ class Archive
|
|||
}
|
||||
}
|
||||
|
||||
// order idarchives by the table month they belong to
|
||||
$idArchivesByMonth = array();
|
||||
foreach (array_keys($doneFlags) as $doneFlag) {
|
||||
if (empty($this->idarchives[$doneFlag])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->idarchives[$doneFlag] as $dateRange => $idarchives) {
|
||||
foreach ($idarchives as $id) {
|
||||
$idArchivesByMonth[$dateRange][] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$idArchivesByMonth = $this->getIdArchivesByMonth($doneFlags);
|
||||
|
||||
return $idArchivesByMonth;
|
||||
}
|
||||
|
|
@ -587,6 +685,8 @@ class Archive
|
|||
*/
|
||||
private function cacheArchiveIdsAfterLaunching($archiveGroups, $plugins)
|
||||
{
|
||||
$this->invalidatedReportsIfNeeded();
|
||||
|
||||
$today = Date::today();
|
||||
|
||||
foreach ($this->params->getPeriods() as $period) {
|
||||
|
|
@ -600,14 +700,14 @@ class Archive
|
|||
// we already know there are no stats for this period
|
||||
// we add one day to make sure we don't miss the day of the website creation
|
||||
if ($twoDaysAfterPeriod->isEarlier($site->getCreationDate())) {
|
||||
Log::verbose("Archive site %s, %s (%s) skipped, archive is before the website was created.",
|
||||
Log::debug("Archive site %s, %s (%s) skipped, archive is before the website was created.",
|
||||
$idSite, $period->getLabel(), $period->getPrettyString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the starting date is in the future we know there is no visiidsite = ?t
|
||||
if ($twoDaysBeforePeriod->isLater($today)) {
|
||||
Log::verbose("Archive site %s, %s (%s) skipped, archive is after today.",
|
||||
Log::debug("Archive site %s, %s (%s) skipped, archive is after today.",
|
||||
$idSite, $period->getLabel(), $period->getPrettyString());
|
||||
continue;
|
||||
}
|
||||
|
|
@ -627,7 +727,7 @@ class Archive
|
|||
private function cacheArchiveIdsWithoutLaunching($plugins)
|
||||
{
|
||||
$idarchivesByReport = ArchiveSelector::getArchiveIds(
|
||||
$this->params->getIdSites(), $this->params->getPeriods(), $this->params->getSegment(), $plugins, $this->params->isSkipAggregationOfSubTables());
|
||||
$this->params->getIdSites(), $this->params->getPeriods(), $this->params->getSegment(), $plugins);
|
||||
|
||||
// initialize archive ID cache for each report
|
||||
foreach ($plugins as $plugin) {
|
||||
|
|
@ -655,8 +755,7 @@ class Archive
|
|||
$this->params->getIdSites(),
|
||||
$this->params->getSegment(),
|
||||
$this->getPeriodLabel(),
|
||||
$plugin,
|
||||
$this->params->isSkipAggregationOfSubTables()
|
||||
$plugin
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -707,11 +806,6 @@ class Archive
|
|||
return round((float)$value, 2);
|
||||
}
|
||||
|
||||
private function uncompress($data)
|
||||
{
|
||||
return @gzuncompress($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the archive ID cache ($this->idarchives) for a particular 'done' flag.
|
||||
*
|
||||
|
|
@ -723,6 +817,8 @@ class Archive
|
|||
* If this function is not called, then periods with no visits will not add
|
||||
* entries to the cache. If the archive is used again, SQL will be executed to
|
||||
* try and find the archive IDs even though we know there are none.
|
||||
*
|
||||
* @param string $doneFlag
|
||||
*/
|
||||
private function initializeArchiveIdCache($doneFlag)
|
||||
{
|
||||
|
|
@ -745,7 +841,10 @@ class Archive
|
|||
*/
|
||||
private function getArchiveGroupOfPlugin($plugin)
|
||||
{
|
||||
if ($this->getPeriodLabel() != 'range') {
|
||||
$periods = $this->params->getPeriods();
|
||||
$periodLabel = reset($periods)->getLabel();
|
||||
|
||||
if (Rules::shouldProcessReportsAllPlugins($this->params->getIdSites(), $this->params->getSegment(), $periodLabel)) {
|
||||
return self::ARCHIVE_ALL_PLUGINS_FLAG;
|
||||
}
|
||||
|
||||
|
|
@ -755,7 +854,7 @@ class Archive
|
|||
/**
|
||||
* Returns the name of the plugin that archives a given report.
|
||||
*
|
||||
* @param string $report Archive data name, eg, `'nb_visits'`, `'UserSettings_...'`, etc.
|
||||
* @param string $report Archive data name, eg, `'nb_visits'`, `'DevicesDetection_...'`, etc.
|
||||
* @return string Plugin name.
|
||||
* @throws \Exception If a plugin cannot be found or if the plugin for the report isn't
|
||||
* activated.
|
||||
|
|
@ -766,9 +865,9 @@ class Archive
|
|||
if (in_array($report, Metrics::getVisitsMetricNames())) {
|
||||
$report = 'VisitsSummary_CoreMetrics';
|
||||
} // Goal_* metrics are processed by the Goals plugin (HACK)
|
||||
else if (strpos($report, 'Goal_') === 0) {
|
||||
elseif (strpos($report, 'Goal_') === 0) {
|
||||
$report = 'Goals_Metrics';
|
||||
} else if (strrpos($report, '_returning') === strlen($report) - strlen('_returning')) { // HACK
|
||||
} elseif (strrpos($report, '_returning') === strlen($report) - strlen('_returning')) { // HACK
|
||||
$report = 'VisitFrequency_Metrics';
|
||||
}
|
||||
|
||||
|
|
@ -789,7 +888,7 @@ class Archive
|
|||
*/
|
||||
private function prepareArchive(array $archiveGroups, Site $site, Period $period)
|
||||
{
|
||||
$parameters = new ArchiveProcessor\Parameters($site, $period, $this->params->getSegment(), $this->params->isSkipAggregationOfSubTables());
|
||||
$parameters = new ArchiveProcessor\Parameters($site, $period, $this->params->getSegment());
|
||||
$archiveLoader = new ArchiveProcessor\Loader($parameters);
|
||||
|
||||
$periodString = $period->getRangeString();
|
||||
|
|
@ -801,9 +900,37 @@ class Archive
|
|||
|
||||
$idArchive = $archiveLoader->prepareArchive($plugin);
|
||||
|
||||
if($idArchive) {
|
||||
if ($idArchive) {
|
||||
$this->idarchives[$doneFlag][$periodString][] = $idArchive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getIdArchivesByMonth($doneFlags)
|
||||
{
|
||||
// order idarchives by the table month they belong to
|
||||
$idArchivesByMonth = array();
|
||||
|
||||
foreach (array_keys($doneFlags) as $doneFlag) {
|
||||
if (empty($this->idarchives[$doneFlag])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->idarchives[$doneFlag] as $dateRange => $idarchives) {
|
||||
foreach ($idarchives as $id) {
|
||||
$idArchivesByMonth[$dateRange][] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $idArchivesByMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function clearStaticCache()
|
||||
{
|
||||
self::$cache = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue