From e78ef88a875c4f5c5638e239a6ecd9a518b922aa Mon Sep 17 00:00:00 2001
From: diosmosis <benaka@piwik.pro>
Date: Wed, 7 Oct 2015 07:45:02 -0700
Subject: [PATCH] Make sure archive invalidation works for range periods.

---
 core/Archive/ArchiveInvalidator.php           |  8 ++-
 plugins/CoreAdminHome/API.php                 | 32 ++++++------
 .../DataAccess/ArchiveInvalidatorTest.php     | 50 ++++++++++++++++---
 3 files changed, 63 insertions(+), 27 deletions(-)

diff --git a/core/Archive/ArchiveInvalidator.php b/core/Archive/ArchiveInvalidator.php
index e17a16311c..fa87617339 100644
--- a/core/Archive/ArchiveInvalidator.php
+++ b/core/Archive/ArchiveInvalidator.php
@@ -124,7 +124,7 @@ class ArchiveInvalidator
     /**
      * @param $idSites int[]
      * @param $dates Date[]
-     * @param $period string TODO: make multiple
+     * @param $period string
      * @param bool $cascadeDown
      * @return InvalidationResultInfo
      * @throws \Exception
@@ -162,6 +162,10 @@ class ArchiveInvalidator
         $periodsToInvalidate = array();
 
         foreach ($dates as $date) {
+            if ($periodType == 'range') {
+                $date = $date . ',' . $date;
+            }
+
             $period = Period\Factory::build($periodType, $date);
             $periodsToInvalidate[] = $period;
 
@@ -207,7 +211,7 @@ class ArchiveInvalidator
 
     /**
      * @param int[] $idSites
-     * @param Period[][] $periods
+     * @param Period[][][] $periods
      * @throws \Exception
      */
     private function markArchivesInvalidated($idSites, $periods)
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index 9d780ec876..e3791dcdac 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -55,28 +55,24 @@ class API extends \Piwik\Plugin\API
     }
 
     /**
-     * When tracking data in the past (using Tracking API), this function
-     * can be used to invalidate reports for the idSites and dates where new data
-     * was added.
-     * DEV: If you call this API, the UI should display the data correctly, but will process
-     *      in real time, which could be very slow after large data imports.
-     *      After calling this function via REST, you can manually force all data
-     *      to be reprocessed by visiting the script as the Super User:
-     *      http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE
-     * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0
-     *    We recommend to use an hourly schedule of the script.
-     *    More information: http://piwik.org/setup-auto-archiving/
+     * Invalidates report data, forcing it to be recomputed during the next archiving run.
      *
-     * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates
-     * @param string $dates Comma separated list of dates to invalidate for all these websites
-     * @param string $period If specified (one of day, week, month, year, range) it will only invalidates archives for this period.
-     *                      Note: because week, month, year, range reports aggregate day reports then you need to specifically invalidate day reports to see
-     *                      other periods reports processed..
+     * Note: This is done automatically when tracking or importing visits in the past.
+     *
+     * @param string $idSites Comma separated list of site IDs to invalidate reports for.
+     * @param string $dates Comma separated list of dates of periods to invalidate reports for.
+     * @param string|bool $period The type of period to invalidate: either 'day', 'week', 'month', 'year', 'range'.
+     *                            The command will automatically cascade up, invalidating reports for parent periods as
+     *                            well. So invalidating a day will invalidate the week it's in, the month it's in and the
+     *                            year it's in, since those periods will need to be recomputed too.
+     * @param bool $cascadeDown If true, child periods will be invalidated as well. So if it is requested to invalidate
+     *                          a month, then all the weeks and days within that month will also be invalidated. But only
+     *                          if this parameter is set.
      * @throws Exception
      * @return array
      * @hideExceptForSuperUser
      */
-    public function invalidateArchivedReports($idSites, $dates, $period = false)
+    public function invalidateArchivedReports($idSites, $dates, $period = false, $cascadeDown = false)
     {
         $idSites = Site::getIdSitesFromIdSitesString($idSites);
         if (empty($idSites)) {
@@ -87,7 +83,7 @@ class API extends \Piwik\Plugin\API
 
         list($dateObjects, $invalidDates) = $this->getDatesToInvalidateFromString($dates);
 
-        $invalidationResult = $this->invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period);
+        $invalidationResult = $this->invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period, (bool)$cascadeDown);
 
         $output = $invalidationResult->makeOutputLogs();
         if ($invalidDates) {
diff --git a/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php b/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php
index a1f09690b5..189076f572 100644
--- a/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php
+++ b/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php
@@ -284,6 +284,10 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                 'day',
                 true,
                 array(
+                    '2014_01' => array(
+                        '1.2014-01-01.2014-12-31.4.done',
+                        '2.2014-01-01.2014-12-31.4.done',
+                    ),
                     '2015_03' => array(),
                     '2015_04' => array(
                         '1.2015-04-30.2015-04-30.1.done',
@@ -327,6 +331,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                 'month',
                 false,
                 array(
+                    '2014_01' => array(),
                     '2014_12' => array(),
                     '2015_01' => array(
                         '1.2015-01-01.2015-01-31.3.done',
@@ -345,6 +350,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                 'month',
                 true,
                 array(
+                    '2014_01' => array(),
                     '2014_12' => array(
                         '1.2014-12-29.2015-01-04.2.done',
                     ),
@@ -400,6 +406,9 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                 'week',
                 true,
                 array(
+                    '2014_01' => array(
+                        '1.2014-01-01.2014-12-31.4.done',
+                    ),
                     '2014_12' => array(
                         '1.2014-12-29.2014-12-29.1.done',
                         '1.2014-12-30.2014-12-30.1.done',
@@ -430,6 +439,28 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                     '2015_05' => array(),
                 ),
             ),
+
+            // range period, one site, cascade = true
+            array(
+                array(1),
+                array('2015-01-02', '2015-03-05'),
+                'range',
+                true,
+                array(
+                    '2014_01' => array(),
+                    '2014_12' => array(),
+                    '2015_01' => array(
+                        '1.2015-01-01.2015-01-10.5.done',
+                    ),
+                    '2015_02' => array(),
+                    '2015_03' => array(
+                        '1.2015-03-04.2015-03-05.5.done',
+                        '1.2015-03-05.2015-03-10.5.done',
+                    ),
+                    '2015_04' => array(),
+                    '2015_05' => array(),
+                ),
+            ),
         );
     }
 
@@ -474,23 +505,28 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
         foreach ($periods as $periodLabel) {
             for ($date = $startDate; $date->isEarlier($endDate); $date = $date->addPeriod(1, $periodLabel)) {
                 foreach ($sites as $idSite) {
-                    $this->insertArchiveRow($idSite, $date, $periodLabel);
+                    $this->insertArchiveRow($idSite, $date->toString(), $periodLabel);
                 }
             }
         }
+
+        $rangePeriods = array('2015-03-04,2015-03-05', '2014-12-05,2015-01-01', '2015-03-05,2015-03-10', '2015-01-01,2015-01-10');
+        foreach ($rangePeriods as $dateRange) {
+            $this->insertArchiveRow($idSite = 1, $dateRange, 'range');
+        }
     }
 
-    private function insertArchiveRow($idSite, Date $date, $periodLabel)
+    private function insertArchiveRow($idSite, $date, $periodLabel)
     {
-        $table = ArchiveTableCreator::getNumericTable($date);
+        $periodObject = \Piwik\Period\Factory::build($periodLabel, $date);
+        $dateStart = $periodObject->getDateStart();
+        $dateEnd = $periodObject->getDateEnd();
+
+        $table = ArchiveTableCreator::getNumericTable($dateStart);
 
         $idArchive = (int) Db::fetchOne("SELECT MAX(idarchive) FROM $table WHERE name LIKE 'done%'");
         $idArchive = $idArchive + 1;
 
-        $periodObject = \Piwik\Period\Factory::build($periodLabel, $date->toString());
-        $dateStart = $periodObject->getDateStart();
-        $dateEnd = $periodObject->getDateEnd();
-
         $periodId = Piwik::$idPeriods[$periodLabel];
 
         $doneFlag = 'done';
-- 
GitLab