Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattab <matthieu.aubry@gmail.com>2013-11-05 07:30:56 +0400
committermattab <matthieu.aubry@gmail.com>2013-11-05 07:30:56 +0400
commit8a6fad6d93c5f693fd1bbb1498c731f6d5595ee6 (patch)
tree20dc56f904293f70510a20dd0f50246c6c4e9c3f
parent09b56ba3d65de601cea754941b23788f777fa794 (diff)
Refs #4278 Simplifying ArchiveProcessor: moving internal logic to ArchiveProcessor\Loader
ArchiveProcessor becomes a helper class easier to understand for plugin developers
-rw-r--r--core/Archive.php3
-rw-r--r--core/ArchiveProcessor.php485
-rw-r--r--core/ArchiveProcessor/Loader.php421
-rw-r--r--core/ArchiveProcessor/Parameters.php95
-rw-r--r--core/DataAccess/ArchiveSelector.php10
-rw-r--r--core/DataAccess/ArchiveWriter.php28
-rw-r--r--core/DataAccess/LogAggregator.php16
-rw-r--r--core/Plugin/Archiver.php2
-rw-r--r--plugins/Actions/Archiver.php2
-rw-r--r--plugins/Goals/Archiver.php4
-rw-r--r--plugins/VisitTime/Archiver.php4
11 files changed, 596 insertions, 474 deletions
diff --git a/core/Archive.php b/core/Archive.php
index 1086d9ba36..3b393c3696 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -610,7 +610,8 @@ class Archive
continue;
}
- $processing = new ArchiveProcessor($period, $site, $this->params->getSegment());
+ $parameters = new ArchiveProcessor\Parameters($period, $site, $this->params->getSegment());
+ $processing = new ArchiveProcessor\Loader($parameters);
// process for each plugin as well
foreach ($archiveGroups as $plugin) {
diff --git a/core/ArchiveProcessor.php b/core/ArchiveProcessor.php
index e0039e2bb7..9a02d8dd49 100644
--- a/core/ArchiveProcessor.php
+++ b/core/ArchiveProcessor.php
@@ -11,11 +11,12 @@
namespace Piwik;
use Exception;
+use Piwik\ArchiveProcessor\Parameters;
use Piwik\ArchiveProcessor\Rules;
use Piwik\DataAccess\ArchiveSelector;
use Piwik\DataAccess\ArchiveWriter;
-use Piwik\DataAccess\LogAggregator;
+use Piwik\DataAccess\LogAggregator;
use Piwik\DataTable\Manager;
use Piwik\Db;
use Piwik\Period;
@@ -82,35 +83,6 @@ use Piwik\Plugin\Archiver;
*/
class ArchiveProcessor
{
- /**
- * Flag stored at the end of the archiving
- *
- * @var int
- */
- const DONE_OK = 1;
-
- /**
- * Flag stored at the start of the archiving
- * When requesting an Archive, we make sure that non-finished archive are not considered valid
- *
- * @var int
- */
- const DONE_ERROR = 2;
-
- /**
- * Flag indicates the archive is over a period that is not finished, eg. the current day, current week, etc.
- * Archives flagged will be regularly purged from the DB.
- *
- * @var int
- */
- const DONE_OK_TEMPORARY = 3;
-
- /**
- * Idarchive in the DB for the requested archive
- *
- * @var int
- */
- protected $idArchive;
/**
* @var \Piwik\DataAccess\ArchiveWriter
@@ -118,115 +90,59 @@ class ArchiveProcessor
protected $archiveWriter;
/**
- * Is the current archive temporary. ie.
- * - today
- * - current week / month / year
+ * @var \Piwik\DataAccess\LogAggregator
*/
- protected $temporaryArchive;
+ protected $logAggregator;
/**
- * @var LogAggregator
+ * @var Archive
*/
- protected $logAggregator = null;
+ public $archive = null;
/**
- * @var int Cached number of visits cached
+ * @var int
*/
- protected $visitsMetricCached = false;
+ protected $numberOfVisits;
+ protected $numberOfVisitsConverted;
- /**
- * @var int Cached number of visits with conversions
- */
- protected $convertedVisitsMetricCached = false;
+ public function __construct(Parameters $params, ArchiveWriter $archiveWriter, $visits, $visitsConverted)
+ {
+ $this->params = $params;
+ $this->logAggregator = new LogAggregator($params);
+ $this->archiveWriter = $archiveWriter;
+ $this->numberOfVisits = $visits;
+ $this->numberOfVisitsConverted = $visitsConverted;
+ }
/**
- * Site of the current archive
- * Can be accessed by plugins (that is why it's public)
+ * Returns the Parameters object containing Period, Site, Segment used for this archive.
*
- * @var Site
- */
- private $site = null;
-
- /**
- * @var Period
- */
- private $period = null;
-
- /**
- * @var Segment
- */
- private $segment = null;
-
- /**
- * @var Archive
+ * @return Parameters
+ * @api
*/
- protected $archive = null;
-
- public function __construct(Period $period, Site $site, Segment $segment)
+ public function getParams()
{
- $this->period = $period;
- $this->site = $site;
- $this->segment = $segment;
-
- // If we are aggregating multiple reports: prepare the Archive object needed for aggregate* methods
- if(!$this->isDayArchive()) {
- $subPeriods = $this->getPeriod()->getSubperiods();
- $this->archive = Archive::factory($this->getSegment(), $subPeriods, array($this->getSite()->getId()));
- }
+ return $this->params;
}
/**
* Returns a [LogAggregator](#) instance for the site, period and segment this
* ArchiveProcessor will insert archive data for.
- *
+ *
* @return LogAggregator
* @api
*/
public function getLogAggregator()
{
- if (empty($this->logAggregator)) {
- $this->logAggregator = new LogAggregator($this->getPeriod()->getDateStart(), $this->getPeriod()->getDateEnd(),
- $this->getSite(), $this->getSegment());
- }
return $this->logAggregator;
}
/**
- * Returns the period we computing statistics for.
- *
- * @return Period
- * @api
- */
- public function getPeriod()
- {
- return $this->period;
- }
-
- /**
- * Returns the site we are computing statistics for.
- *
- * @return Site
- * @api
+ * @return ArchiveWriter
*/
- public function getSite()
+ public function getArchiveWriter()
{
- return $this->site;
- }
-
- /**
- * The Segment used to limit the set of visits that are being aggregated.
- *
- * @return Segment
- * @api
- */
- public function getSegment()
- {
- return $this->segment;
- }
-
- public function getNumberOfVisitsConverted()
- {
- return $this->convertedVisitsMetricCached;
+ return $this->archiveWriter;
}
/**
@@ -254,289 +170,23 @@ class ArchiveProcessor
* Numeric values are not inserted if they equal 0.
*
* @param string $name The name of the numeric value, eg, `'Referrers_distinctKeywords'`.
- * @param numeric $value The numeric value.
+ * @param float $value The numeric value.
* @api
*/
public function insertNumericRecord($name, $value)
{
$value = round($value, 2);
- $this->archiveWriter->insertRecord($name, $value);
- }
-
- public function preProcessArchive($requestedPlugin, $enforceProcessCoreMetricsOnly = false)
- {
- $this->idArchive = false;
-
- $this->setRequestedPlugin($requestedPlugin);
-
- if (!$enforceProcessCoreMetricsOnly) {
- $this->idArchive = $this->loadExistingArchiveIdFromDb($requestedPlugin);
- if ($this->isArchivingForcedToTrigger()) {
- $this->idArchive = false;
- $this->setNumberOfVisits(false);
- }
- if (!empty($this->idArchive)) {
- return $this->idArchive;
- }
-
- $visitsNotKnownYet = $this->getNumberOfVisits() === false;
-
- $createAnotherArchiveForVisitsSummary = !$this->doesRequestedPluginIncludeVisitsSummary($requestedPlugin) && $visitsNotKnownYet;
-
- if ($createAnotherArchiveForVisitsSummary) {
- // recursive archive creation in case we create another separate one, for VisitsSummary core metrics
- // We query VisitsSummary here, as it is needed in the call below ($this->getNumberOfVisits() > 0)
- $requestedPlugin = $this->getRequestedPlugin();
- $this->preProcessArchive('VisitsSummary', $pleaseProcessCoreMetricsOnly = true);
- $this->setRequestedPlugin($requestedPlugin);
- if ($this->getNumberOfVisits() === false) {
- throw new Exception("preProcessArchive() is expected to set number of visits to a numeric value.");
- }
- }
- }
-
- return $this->computeNewArchive($requestedPlugin, $enforceProcessCoreMetricsOnly);
- }
-
- protected function setRequestedPlugin($plugin)
- {
- $this->requestedPlugin = $plugin;
- }
-
- /**
- * Returns the idArchive if the archive is available in the database for the requested plugin.
- * Returns false if the archive needs to be processed.
- *
- * @param $requestedPlugin
- * @return int or false
- */
- protected function loadExistingArchiveIdFromDb($requestedPlugin)
- {
- $minDatetimeArchiveProcessedUTC = $this->getMinTimeArchiveProcessed();
- $site = $this->getSite();
- $period = $this->getPeriod();
- $segment = $this->getSegment();
-
- $idAndVisits = ArchiveSelector::getArchiveIdAndVisits($site, $period, $segment, $minDatetimeArchiveProcessedUTC, $requestedPlugin);
- if (!$idAndVisits) {
- return false;
- }
- list($idArchive, $visits, $visitsConverted) = $idAndVisits;
- $this->setNumberOfVisits($visits, $visitsConverted);
- return $idArchive;
- }
-
- protected function isArchivingForcedToTrigger()
- {
- $period = $this->getPeriod()->getLabel();
- $debugSetting = 'always_archive_data_period'; // default
- if ($period == 'day') {
- $debugSetting = 'always_archive_data_day';
- } elseif ($period == 'range') {
- $debugSetting = 'always_archive_data_range';
- }
- return Config::getInstance()->Debug[$debugSetting];
- }
-
- /**
- * A flag mechanism to store whether visits were selected from archive
- *
- * @param $visitsMetricCached
- * @param bool $convertedVisitsMetricCached
- */
- protected function setNumberOfVisits($visitsMetricCached, $convertedVisitsMetricCached = false)
- {
- if ($visitsMetricCached === false) {
- $this->visitsMetricCached = $this->convertedVisitsMetricCached = false;
- } else {
- $this->visitsMetricCached = (int)$visitsMetricCached;
- $this->convertedVisitsMetricCached = (int)$convertedVisitsMetricCached;
- }
+ $this->getArchiveWriter()->insertRecord($name, $value);
}
public function getNumberOfVisits()
{
- return $this->visitsMetricCached;
- }
-
- protected function doesRequestedPluginIncludeVisitsSummary($requestedPlugin)
- {
- $processAllReportsIncludingVisitsSummary = Rules::shouldProcessReportsAllPlugins($this->getSegment(), $this->getPeriod()->getLabel());
- $doesRequestedPluginIncludeVisitsSummary = $processAllReportsIncludingVisitsSummary || $requestedPlugin == 'VisitsSummary';
- return $doesRequestedPluginIncludeVisitsSummary;
+ return $this->numberOfVisits;
}
- protected function computeNewArchive($requestedPlugin, $enforceProcessCoreMetricsOnly)
- {
- $archiveWriter = new ArchiveWriter($this->getSite()->getId(), $this->getSegment(), $this->getPeriod(), $requestedPlugin, $this->isArchiveTemporary());
- $archiveWriter->initNewArchive();
-
- $this->archiveWriter = $archiveWriter;
-
- $visitsNotKnownYet = $this->getNumberOfVisits() === false;
- if ($visitsNotKnownYet
- || $this->doesRequestedPluginIncludeVisitsSummary($requestedPlugin)
- || $enforceProcessCoreMetricsOnly
- ) {
- if($this->isDayArchive()) {
- $metrics = $this->aggregateDayVisitsMetrics();
- } else {
- $metrics = $this->aggregateMultipleVisitMetrics();
- }
- if (empty($metrics)) {
- $this->setNumberOfVisits(false);
- } else {
- $this->setNumberOfVisits($metrics['nb_visits'], $metrics['nb_visits_converted']);
- }
- }
- $this->logStatusDebug($requestedPlugin);
-
- $isVisitsToday = $this->getNumberOfVisits() > 0;
- if ($isVisitsToday
- && !$enforceProcessCoreMetricsOnly
- ) {
- $this->compute();
- }
-
- $archiveWriter->finalizeArchive();
-
- if ($isVisitsToday && $this->period->getLabel() != 'day') {
- ArchiveSelector::purgeOutdatedArchives($this->getPeriod()->getDateStart());
- }
-
- return $archiveWriter->getIdArchive();
- }
-
- /**
- * Returns the minimum archive processed datetime to look at. Only public for tests.
- *
- * @return int|bool Datetime timestamp, or false if must look at any archive available
- */
- protected function getMinTimeArchiveProcessed()
- {
- $endDateTimestamp = self::determineIfArchivePermanent($this->getDateEnd());
- $isArchiveTemporary = ($endDateTimestamp === false);
- $this->temporaryArchive = $isArchiveTemporary;
-
- if ($endDateTimestamp) {
- // Permanent archive
- return $endDateTimestamp;
- }
- // Temporary archive
- return Rules::getMinTimeProcessedForTemporaryArchive($this->getDateStart(), $this->getPeriod(), $this->getSegment(), $this->getSite());
- }
-
- protected function isArchiveTemporary()
- {
- if (is_null($this->temporaryArchive)) {
- throw new Exception("getMinTimeArchiveProcessed() should be called prior to isArchiveTemporary()");
- }
- return $this->temporaryArchive;
- }
-
- /**
- * @param $requestedPlugin
- */
- protected function logStatusDebug($requestedPlugin)
- {
- $temporary = 'definitive archive';
- if ($this->isArchiveTemporary()) {
- $temporary = 'temporary archive';
- }
- Log::verbose(
- "'%s, idSite = %d (%s), segment '%s', report = '%s', UTC datetime [%s -> %s]",
- $this->getPeriod()->getLabel(),
- $this->getSite()->getId(),
- $temporary,
- $this->getSegment()->getString(),
- $requestedPlugin,
- $this->getDateStart()->getDateStartUTC(),
- $this->getDateEnd()->getDateEndUTC()
- );
- }
-
- /**
- * @var Archiver[] $archivers
- */
- private static $archivers = array();
-
-
- /**
- * Loads Archiver class from any plugin that defines one.
- *
- * @return Plugin\Archiver[]
- */
- protected function getPluginArchivers()
- {
- if (empty(static::$archivers)) {
- $pluginNames = Plugin\Manager::getInstance()->getLoadedPluginsName();
- $archivers = array();
- foreach ($pluginNames as $pluginName) {
- $archivers[$pluginName] = self::getPluginArchiverClass($pluginName);
- }
- static::$archivers = array_filter($archivers);
- }
- return static::$archivers;
- }
-
- private static function getPluginArchiverClass($pluginName)
- {
- $klassName = 'Piwik\\Plugins\\' . $pluginName . '\\Archiver';
- if (class_exists($klassName)
- && is_subclass_of($klassName, 'Piwik\\Plugin\\Archiver')) {
- return $klassName;
- }
- return false;
- }
-
- /**
- * This methods reads the subperiods if necessary,
- * and computes the archive of the current period.
- */
- protected function compute()
- {
- $archivers = $this->getPluginArchivers();
-
- foreach($archivers as $pluginName => $archiverClass) {
- /** @var Archiver $archiver */
- $archiver = new $archiverClass( $this );
-
- if($this->shouldProcessReportsForPlugin($pluginName)) {
- if($this->isDayArchive()) {
- $archiver->aggregateDayReport();
- } else {
- $archiver->aggregateMultipleReports();
- }
- }
- }
- }
-
- protected static function determineIfArchivePermanent(Date $dateEnd)
- {
- $now = time();
- $endTimestampUTC = strtotime($dateEnd->getDateEndUTC());
- if ($endTimestampUTC <= $now) {
- // - if the period we are looking for is finished, we look for a ts_archived that
- // is greater than the last day of the archive
- return $endTimestampUTC;
- }
- return false;
- }
-
- /**
- * @return Date
- */
- public function getDateEnd()
- {
- return $this->getPeriod()->getDateEnd()->setTimezone($this->getSite()->getTimezone());
- }
-
- /**
- * @return Date
- */
- public function getDateStart()
+ public function getNumberOfVisitsConverted()
{
- return $this->getPeriod()->getDateStart()->setTimezone($this->getSite()->getTimezone());
+ return $this->numberOfVisitsConverted;
}
/**
@@ -568,12 +218,12 @@ class ArchiveProcessor
$value = $this->compress($value);
$clean[] = array($newName, $value);
}
- $this->archiveWriter->insertBulkRecords($clean);
+ $this->getArchiveWriter()->insertBulkRecords($clean);
return;
}
$values = $this->compress($values);
- $this->archiveWriter->insertRecord($name, $values);
+ $this->getArchiveWriter()->insertRecord($name, $values);
}
protected function compress($data)
@@ -585,70 +235,6 @@ class ArchiveProcessor
}
/**
- * Whether the specified plugin's reports should be archived
- * @param string $pluginName
- * @return bool
- */
- protected function shouldProcessReportsForPlugin($pluginName)
- {
- if (Rules::shouldProcessReportsAllPlugins($this->getSegment(), $this->getPeriod()->getLabel())) {
- return true;
- }
- // If any other segment, only process if the requested report belong to this plugin
- $pluginBeingProcessed = $this->getRequestedPlugin();
- if ($pluginBeingProcessed == $pluginName) {
- return true;
- }
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginLoaded($pluginBeingProcessed)) {
- return true;
- }
- return false;
- }
-
- protected function getRequestedPlugin()
- {
- return $this->requestedPlugin;
- }
-
- /**
- * @return bool
- */
- protected function isDayArchive()
- {
- return $this->getPeriod()->getLabel() == 'day';
- }
-
-
- protected function aggregateMultipleVisitMetrics()
- {
- $toSum = Metrics::getVisitsMetricNames();
- $metrics = $this->aggregateNumericMetrics($toSum);
- return $metrics;
- }
-
-
- protected function aggregateDayVisitsMetrics()
- {
- $query = $this->getLogAggregator()->queryVisitsByDimension();
- $data = $query->fetch();
-
- $metrics = $this->convertMetricsIdToName($data);
- $this->insertNumericRecords($metrics);
- return $metrics;
- }
-
- protected function convertMetricsIdToName($data)
- {
- $metrics = array();
- foreach ($data as $metricId => $value) {
- $readableMetric = Metrics::$mappingFromIdToName[$metricId];
- $metrics[$readableMetric] = $value;
- }
- return $metrics;
- }
-
-
- /**
* Array of (column name before => column name renamed) of the columns for which sum operation is invalid.
* These columns will be renamed as per this mapping.
* @var array
@@ -739,7 +325,7 @@ class ArchiveProcessor
$this->enrichWithUniqueVisitorsMetric($results);
foreach ($results as $name => $value) {
- $this->archiveWriter->insertRecord($name, $value);
+ $this->getArchiveWriter()->insertRecord($name, $value);
}
// if asked for only one field to sum
@@ -856,7 +442,7 @@ class ArchiveProcessor
protected function enrichWithUniqueVisitorsMetric(&$results)
{
if (array_key_exists('nb_uniq_visitors', $results)) {
- if (SettingsPiwik::isUniqueVisitorsEnabled($this->getPeriod()->getLabel())) {
+ if (SettingsPiwik::isUniqueVisitorsEnabled($this->getParams()->getPeriod()->getLabel())) {
$results['nb_uniq_visitors'] = (float)$this->computeNbUniqVisitors();
} else {
unset($results['nb_uniq_visitors']);
@@ -894,3 +480,4 @@ class ArchiveProcessor
return $data[Metrics::INDEX_NB_UNIQ_VISITORS];
}
}
+
diff --git a/core/ArchiveProcessor/Loader.php b/core/ArchiveProcessor/Loader.php
new file mode 100644
index 0000000000..44508b5d53
--- /dev/null
+++ b/core/ArchiveProcessor/Loader.php
@@ -0,0 +1,421 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik\ArchiveProcessor;
+use Piwik\Archive;
+use Piwik\ArchiveProcessor;
+use Piwik\Config;
+use Piwik\DataAccess\ArchiveSelector;
+use Piwik\DataAccess\ArchiveWriter;
+use Piwik\DataAccess\LogAggregator;
+use Piwik\Date;
+use Piwik\Log;
+use Piwik\Metrics;
+use Piwik\Period;
+use Piwik\Plugin\Archiver;
+use Piwik\Segment;
+use Piwik\Site;
+
+/**
+ *
+ */
+class Loader
+{
+ /**
+ * @var LogAggregator
+ */
+ private $logAggregator = null;
+
+ /**
+ * @var int Cached number of visits cached
+ */
+ protected $visitsMetricCached = false;
+
+ /**
+ * @var int Cached number of visits with conversions
+ */
+ protected $convertedVisitsMetricCached = false;
+
+ /**
+ * @var string Plugin name which triggered this archive processor
+ */
+ protected $requestedPlugin = false;
+
+ /**
+ * Is the current archive temporary. ie.
+ * - today
+ * - current week / month / year
+ */
+ protected $temporaryArchive;
+
+ /**
+ * Idarchive in the DB for the requested archive
+ *
+ * @var int
+ */
+ protected $idArchive;
+
+ /**
+ * @var Parameters
+ */
+ protected $params;
+
+ public function __construct(Parameters $params)
+ {
+ $this->params = $params;
+ }
+
+ /**
+ * A flag mechanism to store whether visits were selected from archive
+ *
+ * @param $visitsMetricCached
+ * @param bool $convertedVisitsMetricCached
+ */
+ protected function setNumberOfVisits($visitsMetricCached, $convertedVisitsMetricCached = false)
+ {
+ if ($visitsMetricCached === false) {
+ $this->visitsMetricCached = $this->convertedVisitsMetricCached = false;
+ } else {
+ $this->visitsMetricCached = (int)$visitsMetricCached;
+ $this->convertedVisitsMetricCached = (int)$convertedVisitsMetricCached;
+ }
+ }
+
+
+ public function getNumberOfVisits()
+ {
+ return $this->visitsMetricCached;
+ }
+
+ public function getNumberOfVisitsConverted()
+ {
+ return $this->convertedVisitsMetricCached;
+ }
+
+ public function preProcessArchive($requestedPlugin, $enforceProcessCoreMetricsOnly = false)
+ {
+ $this->idArchive = false;
+
+ $this->setRequestedPlugin($requestedPlugin);
+
+ if (!$enforceProcessCoreMetricsOnly) {
+ $this->idArchive = $this->loadExistingArchiveIdFromDb($requestedPlugin);
+ if ($this->isArchivingForcedToTrigger()) {
+ $this->idArchive = false;
+ $this->setNumberOfVisits(false);
+ }
+ if (!empty($this->idArchive)) {
+ return $this->idArchive;
+ }
+
+ $visitsNotKnownYet = $this->getNumberOfVisits() === false;
+
+ $createAnotherArchiveForVisitsSummary = !$this->doesRequestedPluginIncludeVisitsSummary($requestedPlugin) && $visitsNotKnownYet;
+
+ if ($createAnotherArchiveForVisitsSummary) {
+ // recursive archive creation in case we create another separate one, for VisitsSummary core metrics
+ // We query VisitsSummary here, as it is needed in the call below ($this->getNumberOfVisits() > 0)
+ $requestedPlugin = $this->getRequestedPlugin();
+ $this->preProcessArchive('VisitsSummary', $pleaseProcessCoreMetricsOnly = true);
+ $this->setRequestedPlugin($requestedPlugin);
+ if ($this->getNumberOfVisits() === false) {
+ throw new \Exception("preProcessArchive() is expected to set number of visits to a numeric value.");
+ }
+ }
+ }
+
+ return $this->computeNewArchive($requestedPlugin, $enforceProcessCoreMetricsOnly);
+ }
+
+ protected function doesRequestedPluginIncludeVisitsSummary($requestedPlugin)
+ {
+ $processAllReportsIncludingVisitsSummary = Rules::shouldProcessReportsAllPlugins($this->params->getSegment(), $this->params->getPeriod()->getLabel());
+ $doesRequestedPluginIncludeVisitsSummary = $processAllReportsIncludingVisitsSummary || $requestedPlugin == 'VisitsSummary';
+ return $doesRequestedPluginIncludeVisitsSummary;
+ }
+
+ protected function setRequestedPlugin($plugin)
+ {
+ $this->requestedPlugin = $plugin;
+ }
+
+ protected function isArchivingForcedToTrigger()
+ {
+ $period = $this->params->getPeriod()->getLabel();
+ $debugSetting = 'always_archive_data_period'; // default
+ if ($period == 'day') {
+ $debugSetting = 'always_archive_data_day';
+ } elseif ($period == 'range') {
+ $debugSetting = 'always_archive_data_range';
+ }
+ return Config::getInstance()->Debug[$debugSetting];
+ }
+
+ /**
+ * Returns the idArchive if the archive is available in the database for the requested plugin.
+ * Returns false if the archive needs to be processed.
+ *
+ * @param $requestedPlugin
+ * @return int or false
+ */
+ protected function loadExistingArchiveIdFromDb($requestedPlugin)
+ {
+ $minDatetimeArchiveProcessedUTC = $this->getMinTimeArchiveProcessed();
+ $site = $this->params->getSite();
+ $period = $this->params->getPeriod();
+ $segment = $this->params->getSegment();
+
+ $idAndVisits = ArchiveSelector::getArchiveIdAndVisits($site, $period, $segment, $minDatetimeArchiveProcessedUTC, $requestedPlugin);
+ if (!$idAndVisits) {
+ return false;
+ }
+ list($idArchive, $visits, $visitsConverted) = $idAndVisits;
+ $this->setNumberOfVisits($visits, $visitsConverted);
+ return $idArchive;
+ }
+
+ protected function computeNewArchive($requestedPlugin, $enforceProcessCoreMetricsOnly)
+ {
+ $archiveWriter = new ArchiveWriter($this->params->getSite()->getId(), $this->params->getSegment(), $this->params->getPeriod(), $requestedPlugin, $this->isArchiveTemporary());
+ $archiveWriter->initNewArchive();
+
+ $archiveProcessor = $this->makeArchiveProcessor($archiveWriter);
+
+ $visitsNotKnownYet = $this->getNumberOfVisits() === false;
+ if ($visitsNotKnownYet
+ || $this->doesRequestedPluginIncludeVisitsSummary($requestedPlugin)
+ || $enforceProcessCoreMetricsOnly
+ ) {
+
+ if($this->isDayArchive()) {
+ $metrics = $this->aggregateDayVisitsMetrics($archiveProcessor);
+ } else {
+ $metrics = $this->aggregateMultipleVisitMetrics($archiveProcessor);
+ }
+
+ if (empty($metrics)) {
+ $this->setNumberOfVisits(false);
+ } else {
+ $this->setNumberOfVisits($metrics['nb_visits'], $metrics['nb_visits_converted']);
+ }
+ }
+ $this->logStatusDebug($requestedPlugin);
+
+ $archiveProcessor = $this->makeArchiveProcessor($archiveWriter);
+
+ $isVisitsToday = $this->getNumberOfVisits() > 0;
+ if ($isVisitsToday
+ && !$enforceProcessCoreMetricsOnly
+ ) {
+ $this->compute($archiveProcessor);
+ }
+
+ $archiveWriter->finalizeArchive();
+
+ if ($isVisitsToday && $this->params->getPeriod()->getLabel() != 'day') {
+ ArchiveSelector::purgeOutdatedArchives($this->params->getPeriod()->getDateStart());
+ }
+
+ return $archiveWriter->getIdArchive();
+ }
+
+ protected function aggregateDayVisitsMetrics(ArchiveProcessor $archiveProcessor)
+ {
+ $query = $archiveProcessor->getLogAggregator()->queryVisitsByDimension();
+ $data = $query->fetch();
+
+ $metrics = $this->convertMetricsIdToName($data);
+ $archiveProcessor->insertNumericRecords($metrics);
+ return $metrics;
+ }
+
+ protected function convertMetricsIdToName($data)
+ {
+ $metrics = array();
+ foreach ($data as $metricId => $value) {
+ $readableMetric = Metrics::$mappingFromIdToName[$metricId];
+ $metrics[$readableMetric] = $value;
+ }
+ return $metrics;
+ }
+
+ protected function aggregateMultipleVisitMetrics(ArchiveProcessor $archiveProcessor)
+ {
+ $toSum = Metrics::getVisitsMetricNames();
+ $metrics = $archiveProcessor->aggregateNumericMetrics($toSum);
+ return $metrics;
+ }
+
+ protected static function determineIfArchivePermanent(Date $dateEnd)
+ {
+ $now = time();
+ $endTimestampUTC = strtotime($dateEnd->getDateEndUTC());
+ if ($endTimestampUTC <= $now) {
+ // - if the period we are looking for is finished, we look for a ts_archived that
+ // is greater than the last day of the archive
+ return $endTimestampUTC;
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns the minimum archive processed datetime to look at. Only public for tests.
+ *
+ * @return int|bool Datetime timestamp, or false if must look at any archive available
+ */
+ protected function getMinTimeArchiveProcessed()
+ {
+ $endDateTimestamp = self::determineIfArchivePermanent($this->params->getDateEnd());
+ $isArchiveTemporary = ($endDateTimestamp === false);
+ $this->temporaryArchive = $isArchiveTemporary;
+
+ if ($endDateTimestamp) {
+ // Permanent archive
+ return $endDateTimestamp;
+ }
+ // Temporary archive
+ return Rules::getMinTimeProcessedForTemporaryArchive($this->params->getDateStart(), $this->params->getPeriod(), $this->params->getSegment(), $this->params->getSite());
+ }
+
+ protected function isArchiveTemporary()
+ {
+ if (is_null($this->temporaryArchive)) {
+ throw new \Exception("getMinTimeArchiveProcessed() should be called prior to isArchiveTemporary()");
+ }
+ return $this->temporaryArchive;
+ }
+
+ /**
+ * @param $requestedPlugin
+ */
+ protected function logStatusDebug($requestedPlugin)
+ {
+ $temporary = 'definitive archive';
+ if ($this->isArchiveTemporary()) {
+ $temporary = 'temporary archive';
+ }
+ Log::verbose(
+ "'%s, idSite = %d (%s), segment '%s', report = '%s', UTC datetime [%s -> %s]",
+ $this->params->getPeriod()->getLabel(),
+ $this->params->getSite()->getId(),
+ $temporary,
+ $this->params->getSegment()->getString(),
+ $requestedPlugin,
+ $this->params->getDateStart()->getDateStartUTC(),
+ $this->params->getDateEnd()->getDateEndUTC()
+ );
+ }
+
+ /**
+ * @var Archiver[] $archivers
+ */
+ private static $archivers = array();
+
+
+ /**
+ * Loads Archiver class from any plugin that defines one.
+ *
+ * @return \Piwik\Plugin\Archiver[]
+ */
+ protected function getPluginArchivers()
+ {
+ if (empty(static::$archivers)) {
+ $pluginNames = \Piwik\Plugin\Manager::getInstance()->getLoadedPluginsName();
+ $archivers = array();
+ foreach ($pluginNames as $pluginName) {
+ $archivers[$pluginName] = self::getPluginArchiverClass($pluginName);
+ }
+ static::$archivers = array_filter($archivers);
+ }
+ return static::$archivers;
+ }
+
+ private static function getPluginArchiverClass($pluginName)
+ {
+ $klassName = 'Piwik\\Plugins\\' . $pluginName . '\\Archiver';
+ if (class_exists($klassName)
+ && is_subclass_of($klassName, 'Piwik\\Plugin\\Archiver')) {
+ return $klassName;
+ }
+ return false;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function isDayArchive()
+ {
+ return $this->params->getPeriod()->getLabel() == 'day';
+ }
+
+ /**
+ * This methods reads the subperiods if necessary,
+ * and computes the archive of the current period.
+ */
+ protected function compute($archiveProcessor)
+ {
+ $archivers = $this->getPluginArchivers();
+
+ foreach($archivers as $pluginName => $archiverClass) {
+ /** @var Archiver $archiver */
+ $archiver = new $archiverClass($archiveProcessor);
+
+ if($this->shouldProcessReportsForPlugin($pluginName)) {
+ if($this->isDayArchive()) {
+ $archiver->aggregateDayReport();
+ } else {
+ $archiver->aggregateMultipleReports();
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether the specified plugin's reports should be archived
+ * @param string $pluginName
+ * @return bool
+ */
+ protected function shouldProcessReportsForPlugin($pluginName)
+ {
+ if (Rules::shouldProcessReportsAllPlugins($this->params->getSegment(), $this->params->getPeriod()->getLabel())) {
+ return true;
+ }
+ // If any other segment, only process if the requested report belong to this plugin
+ $pluginBeingProcessed = $this->getRequestedPlugin();
+ if ($pluginBeingProcessed == $pluginName) {
+ return true;
+ }
+ if (!\Piwik\Plugin\Manager::getInstance()->isPluginLoaded($pluginBeingProcessed)) {
+ return true;
+ }
+ return false;
+ }
+
+ protected function getRequestedPlugin()
+ {
+ return $this->requestedPlugin;
+ }
+
+ /**
+ * @param $archiveWriter
+ * @return ArchiveProcessor
+ */
+ protected function makeArchiveProcessor($archiveWriter)
+ {
+ $archiveProcessor = new ArchiveProcessor($this->params, $archiveWriter, $this->getNumberOfVisits(), $this->getNumberOfVisitsConverted());
+
+ if (!$this->isDayArchive()) {
+ $subPeriods = $this->params->getPeriod()->getSubperiods();
+ $archiveProcessor->archive = Archive::factory($this->params->getSegment(), $subPeriods, array($this->params->getSite()->getId()));
+ }
+ return $archiveProcessor;
+ }
+}
diff --git a/core/ArchiveProcessor/Parameters.php b/core/ArchiveProcessor/Parameters.php
new file mode 100644
index 0000000000..5b169ff791
--- /dev/null
+++ b/core/ArchiveProcessor/Parameters.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+
+namespace Piwik\ArchiveProcessor;
+
+use Piwik\Date;
+use Piwik\Period;
+use Piwik\Segment;
+use Piwik\Site;
+
+class Parameters
+{
+
+ /**
+ * Site of the current archive
+ * Can be accessed by plugins (that is why it's public)
+ *
+ * @var Site
+ */
+ private $site = null;
+
+ /**
+ * @var Period
+ */
+ private $period = null;
+
+ /**
+ * @var Segment
+ */
+ private $segment = null;
+
+ public function __construct(Period $period, Site $site, Segment $segment)
+ {
+ $this->period = $period;
+ $this->site = $site;
+ $this->segment = $segment;
+ }
+
+ /**
+ * Returns the period we computing statistics for.
+ *
+ * @return Period
+ * @api
+ */
+ public function getPeriod()
+ {
+ return $this->period;
+ }
+
+ /**
+ * Returns the site we are computing statistics for.
+ *
+ * @return Site
+ * @api
+ */
+ public function getSite()
+ {
+ return $this->site;
+ }
+
+ /**
+ * The Segment used to limit the set of visits that are being aggregated.
+ *
+ * @return Segment
+ * @api
+ */
+ public function getSegment()
+ {
+ return $this->segment;
+ }
+
+ /**
+ * @return Date
+ */
+ public function getDateEnd()
+ {
+ return $this->getPeriod()->getDateEnd()->setTimezone($this->getSite()->getTimezone());
+ }
+
+ /**
+ * @return Date
+ */
+ public function getDateStart()
+ {
+ return $this->getPeriod()->getDateStart()->setTimezone($this->getSite()->getTimezone());
+ }
+} \ No newline at end of file
diff --git a/core/DataAccess/ArchiveSelector.php b/core/DataAccess/ArchiveSelector.php
index 4cf01afa24..956fa60666 100644
--- a/core/DataAccess/ArchiveSelector.php
+++ b/core/DataAccess/ArchiveSelector.php
@@ -19,8 +19,8 @@ use Piwik\Db;
use Piwik\Log;
use Piwik\Period;
use Piwik\Period\Range;
-use Piwik\Piwik;
+use Piwik\Piwik;
use Piwik\Segment;
use Piwik\Site;
@@ -275,8 +275,8 @@ class ArchiveSelector
// create the SQL to find archives that are DONE
return "(name IN ($allDoneFlags)) AND " .
- " (value = '" . ArchiveProcessor::DONE_OK . "' OR " .
- " value = '" . ArchiveProcessor::DONE_OK_TEMPORARY . "')";
+ " (value = '" . ArchiveWriter::DONE_OK . "' OR " .
+ " value = '" . ArchiveWriter::DONE_OK_TEMPORARY . "')";
}
static public function purgeOutdatedArchives(Date $dateStart)
@@ -333,9 +333,9 @@ class ArchiveSelector
$query = "SELECT idarchive
FROM " . ArchiveTableCreator::getNumericTable($date) . "
WHERE name LIKE 'done%'
- AND (( value = " . ArchiveProcessor::DONE_OK_TEMPORARY . "
+ AND (( value = " . ArchiveWriter::DONE_OK_TEMPORARY . "
AND ts_archived < ?)
- OR value = " . ArchiveProcessor::DONE_ERROR . ")";
+ OR value = " . ArchiveWriter::DONE_ERROR . ")";
$result = Db::fetchAll($query, array($purgeArchivesOlderThan));
$idArchivesToDelete = array();
diff --git a/core/DataAccess/ArchiveWriter.php b/core/DataAccess/ArchiveWriter.php
index 0c4e5911bb..fdab1f08c4 100644
--- a/core/DataAccess/ArchiveWriter.php
+++ b/core/DataAccess/ArchiveWriter.php
@@ -15,9 +15,9 @@ use Piwik\ArchiveProcessor\Rules;
use Piwik\ArchiveProcessor;
use Piwik\Common;
use Piwik\Config;
+
use Piwik\Db;
use Piwik\Db\BatchInsert;
-
use Piwik\Log;
use Piwik\Period;
use Piwik\Segment;
@@ -31,6 +31,26 @@ use Piwik\SettingsPiwik;
class ArchiveWriter
{
const PREFIX_SQL_LOCK = "locked_";
+ /**
+ * Flag stored at the end of the archiving
+ *
+ * @var int
+ */
+ const DONE_OK = 1;
+ /**
+ * Flag stored at the start of the archiving
+ * When requesting an Archive, we make sure that non-finished archive are not considered valid
+ *
+ * @var int
+ */
+ const DONE_ERROR = 2;
+ /**
+ * Flag indicates the archive is over a period that is not finished, eg. the current day, current week, etc.
+ * Archives flagged will be regularly purged from the DB.
+ *
+ * @var int
+ */
+ const DONE_OK_TEMPORARY = 3;
protected $fields = array('idarchive',
'idsite',
@@ -136,7 +156,7 @@ class ArchiveWriter
protected function logArchiveStatusAsIncomplete()
{
- $statusWhileProcessing = ArchiveProcessor::DONE_ERROR;
+ $statusWhileProcessing = self::DONE_ERROR;
$this->insertRecord($this->doneFlag, $statusWhileProcessing);
}
@@ -182,9 +202,9 @@ class ArchiveWriter
protected function logArchiveStatusAsFinal()
{
- $status = ArchiveProcessor::DONE_OK;
+ $status = self::DONE_OK;
if ($this->isArchiveTemporary) {
- $status = ArchiveProcessor::DONE_OK_TEMPORARY;
+ $status = self::DONE_OK_TEMPORARY;
}
$this->insertRecord($this->doneFlag, $status);
}
diff --git a/core/DataAccess/LogAggregator.php b/core/DataAccess/LogAggregator.php
index 71597c049e..7387226785 100644
--- a/core/DataAccess/LogAggregator.php
+++ b/core/DataAccess/LogAggregator.php
@@ -11,6 +11,7 @@
namespace Piwik\DataAccess;
use PDOStatement;
+use Piwik\ArchiveProcessor\Parameters;
use Piwik\Common;
use Piwik\DataArray;
use Piwik\Date;
@@ -79,17 +80,14 @@ class LogAggregator
/**
* Constructor
- * @param Date $dateStart
- * @param Date $dateEnd
- * @param Site $site
- * @param Segment $segment
+ * @param \Piwik\ArchiveProcessor\Parameters $params
*/
- public function __construct(Date $dateStart, Date $dateEnd, Site $site, Segment $segment)
+ public function __construct(Parameters $params)
{
- $this->dateStart = $dateStart;
- $this->dateEnd = $dateEnd;
- $this->segment = $segment;
- $this->site = $site;
+ $this->dateStart = $params->getPeriod()->getDateStart();
+ $this->dateEnd = $params->getPeriod()->getDateEnd();
+ $this->segment = $params->getSegment();
+ $this->site = $params->getSite();
}
public function generateQuery($select, $from, $where, $groupBy, $orderBy)
diff --git a/core/Plugin/Archiver.php b/core/Plugin/Archiver.php
index 1e603127a5..ac97b24d83 100644
--- a/core/Plugin/Archiver.php
+++ b/core/Plugin/Archiver.php
@@ -82,7 +82,7 @@ abstract class Archiver
*/
protected function getProcessor()
{
- return $this->processor->getDateEnd();
+ return $this->processor;
}
/**
diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php
index f11ea3963a..8b3a0b8a3b 100644
--- a/plugins/Actions/Archiver.php
+++ b/plugins/Actions/Archiver.php
@@ -79,7 +79,7 @@ class Archiver extends \Piwik\Plugin\Archiver
function __construct($processor)
{
parent::__construct($processor);
- $this->isSiteSearchEnabled = $processor->getSite()->isSiteSearchEnabled();
+ $this->isSiteSearchEnabled = $processor->getParams()->getSite()->isSiteSearchEnabled();
}
/**
diff --git a/plugins/Goals/Archiver.php b/plugins/Goals/Archiver.php
index 33174d5a3e..87983faf75 100644
--- a/plugins/Goals/Archiver.php
+++ b/plugins/Goals/Archiver.php
@@ -275,7 +275,7 @@ class Archiver extends \Piwik\Plugin\Archiver
// Per item doesn't support segment
// Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
// event if it did support segment
- if (!$this->getProcessor()->getSegment()->isEmpty()) {
+ if (!$this->getProcessor()->getParams()->getSegment()->isEmpty()) {
return false;
}
return true;
@@ -395,7 +395,7 @@ class Archiver extends \Piwik\Plugin\Archiver
/*
* Archive General Goal metrics
*/
- $goalIdsToSum = GoalManager::getGoalIds($this->getProcessor()->getSite()->getId());
+ $goalIdsToSum = GoalManager::getGoalIds($this->getProcessor()->getParams()->getSite()->getId());
//Ecommerce
$goalIdsToSum[] = GoalManager::IDGOAL_ORDER;
diff --git a/plugins/VisitTime/Archiver.php b/plugins/VisitTime/Archiver.php
index 694f9eef53..58516bee7f 100644
--- a/plugins/VisitTime/Archiver.php
+++ b/plugins/VisitTime/Archiver.php
@@ -54,8 +54,8 @@ class Archiver extends \Piwik\Plugin\Archiver
protected function convertTimeToLocalTimezone(DataArray &$array)
{
- $date = Date::factory($this->getProcessor()->getDateStart()->getDateStartUTC())->toString();
- $timezone = $this->getProcessor()->getSite()->getTimezone();
+ $date = Date::factory($this->getProcessor()->getParams()->getDateStart()->getDateStartUTC())->toString();
+ $timezone = $this->getProcessor()->getParams()->getSite()->getTimezone();
$converted = array();
foreach ($array->getDataArray() as $hour => $stats) {