public function getUrlParameterDateString($idSite, $period, $date, $segment) { $segmentCreatedTime = $this->getCreatedTimeOfSegment($idSite, $segment); $oldestDateToProcessForNewSegment = $this->getOldestDateToProcessForNewSegment($segmentCreatedTime); if (empty($oldestDateToProcessForNewSegment)) { return $date; } // if the start date for the archiving request is before the minimum date allowed for processing this segment, // use the minimum allowed date as the start date $periodObj = PeriodFactory::build($period, $date); if ($periodObj->getDateStart()->getTimestamp() < $oldestDateToProcessForNewSegment->getTimestamp()) { $this->logger->debug("Start date of archiving request period ({start}) is older than configured oldest date to process for the segment.", array('start' => $periodObj->getDateStart())); $endDate = $periodObj->getDateEnd(); // if the creation time of a segment is older than the end date of the archiving request range, we cannot // blindly rewrite the date string, since the resulting range would be incorrect. instead we make the // start date equal to the end date, so less archiving occurs, and no fatal error occurs. if ($oldestDateToProcessForNewSegment->getTimestamp() > $endDate->getTimestamp()) { $this->logger->debug("Oldest date to process is greater than end date of archiving request period ({end}), so setting oldest date to end date.", array('end' => $endDate)); $oldestDateToProcessForNewSegment = $endDate; } $date = $oldestDateToProcessForNewSegment->toString() . ',' . $endDate; $this->logger->debug("Archiving request date range changed to {date} w/ period {period}.", array('date' => $date, 'period' => $period)); } return $date; }
public function testFactoryInvalid() { try { Period\Factory::build('inValid', Date::today()); } catch (\Exception $e) { return; } $this->fail('Expected Exception not raised'); }
/** * Creates a new ArchiveProcessor object * * @param string $periodLabel * @param string $dateLabel * @param string $siteTimezone * @return \Core_ArchiveProcessorTest */ private function _createArchiveProcessor($periodLabel, $dateLabel, $siteTimezone) { $site = $this->_createWebsite($siteTimezone); $date = Date::factory($dateLabel); $period = Period\Factory::build($periodLabel, $date); $segment = new Segment('', $site->getId()); $params = new ArchiveProcessor\Parameters($site, $period, $segment); return new \Core_ArchiveProcessorTest($params); }
/** * @param DataTable $table * @param int $timezone The timezone of the current selected site / the timezone of the labels * @param string $period The requested period and date is needed to respect daylight saving etc. * @param string $date */ public function __construct($table, $timezone, $period, $date) { $this->timezone = $timezone; $this->date = Period\Factory::build($period, $date)->getDateEnd(); $self = $this; parent::__construct($table, function ($label) use($self) { $hour = str_pad($label, 2, 0, STR_PAD_LEFT); return $self->convertHourToUtc($hour); }); }
/** * @group Benchmarks */ public function testArchivingProcess() { if ($this->archivingLaunched) { echo "NOTE: Had to archive data, memory results will not be accurate. Run again for better results."; } Rules::$archivingDisabledByTests = true; $period = Period\Factory::build(self::$fixture->period, Date::factory(self::$fixture->date)); $dateRange = $period->getDateStart() . ',' . $period->getDateEnd(); API::getInstance()->get(self::$fixture->idSite, 'day', $dateRange); }
/** * Return the number of visitors that visited every site in the given list for the * given date range. Includes the number of visitors that visited at least one site * and the number that visited every site. * * This data is calculated on demand, and for very large tables can take a long time * to run. * * See {@link Model\DistinctMetricsAggregator} for more information. * * @param string $idSite comma separated list of site IDs, ie, `"1,2,3"` * @param string $period * @param string $date * @return array Metrics **nb_total_visitors** and **nb_shared_visitors**. * @throws Exception if $idSite references zero sites or just one site. */ public function getCommonVisitors($idSite, $period, $date, $segment = false) { if (empty($idSite)) { throw new Exception("No sites to get common visitors for."); } $idSites = Site::getIdSitesFromIdSitesString($idSite); Piwik::checkUserHasViewAccess($idSites); $segment = new Segment($segment, $idSites); $period = PeriodFactory::build($period, $date); return $this->distinctMetricsAggregator->getCommonVisitorCount($idSites, $period->getDateStart(), $period->getDateEnd(), $segment); }
public function getTriggeredAlertsForPeriod($period, $date) { $piwikDate = Date::factory($date); $date = Period\Factory::build($period, $piwikDate); $db = $this->getDb(); $sql = $this->getTriggeredAlertsSelectPart() . " WHERE period = ? AND ts_triggered BETWEEN ? AND ?"; $values = array($period, $date->getDateStart()->getDateStartUTC(), $date->getDateEnd()->getDateEndUTC()); $alerts = $db->fetchAll($sql, $values); $alerts = $this->completeAlerts($alerts); return $alerts; }
public function setUp() { parent::setUp(); $idSite = 1; if (!Fixture::siteCreated($idSite)) { Fixture::createWebsite('2014-01-01 00:00:00'); } $site = new Site($idSite); $date = Date::factory('2012-01-01'); $period = Period\Factory::build('month', $date); $segment = new Segment('', array($site->getId())); $params = new Parameters($site, $period, $segment); $this->logAggregator = new LogAggregator($params); }
private function createCollection($onlyOnePeriod = false, $onlyOneSite = false) { $periods = array(Period\Factory::build('day', '2012-12-12'), Period\Factory::build('day', '2012-12-13')); $dataType = 'numeric'; $siteIds = array($this->site1, $this->site2); $dataNames = array('Name1', 'Name2'); $defaultRow = array('default' => 1); if ($onlyOnePeriod) { $periods = array($periods[0]); } if ($onlyOneSite) { $siteIds = array($siteIds[0]); } return new DataCollection($dataNames, $dataType, $siteIds, $periods, $defaultRow); }
/** * General method to get transitions for an action * * @param $actionName * @param $actionType "url"|"title" * @param $idSite * @param $period * @param $date * @param bool $segment * @param bool $limitBeforeGrouping * @param string $parts * @return array * @throws Exception */ public function getTransitionsForAction($actionName, $actionType, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = false, $parts = 'all') { Piwik::checkUserHasViewAccess($idSite); // get idaction of the requested action $idaction = $this->deriveIdAction($actionName, $actionType); if ($idaction < 0) { throw new Exception('NoDataForAction'); } // prepare log aggregator $segment = new Segment($segment, $idSite); $site = new Site($idSite); $period = Period\Factory::build($period, $date); $params = new ArchiveProcessor\Parameters($site, $period, $segment); $logAggregator = new LogAggregator($params); // prepare the report $report = array('date' => Period\Factory::build($period->getLabel(), $date)->getLocalizedShortString()); $partsArray = explode(',', $parts); if ($parts == 'all' || in_array('internalReferrers', $partsArray)) { $this->addInternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping); } if ($parts == 'all' || in_array('followingActions', $partsArray)) { $includeLoops = $parts != 'all' && !in_array('internalReferrers', $partsArray); $this->addFollowingActions($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops); } if ($parts == 'all' || in_array('externalReferrers', $partsArray)) { $this->addExternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping); } // derive the number of exits from the other metrics if ($parts == 'all') { $report['pageMetrics']['exits'] = $report['pageMetrics']['pageviews'] - $this->getTotalTransitionsToFollowingActions() - $report['pageMetrics']['loops']; } // replace column names in the data tables $reportNames = array('previousPages' => true, 'previousSiteSearches' => false, 'followingPages' => true, 'followingSiteSearches' => false, 'outlinks' => true, 'downloads' => true); foreach ($reportNames as $reportName => $replaceLabel) { if (isset($report[$reportName])) { $columnNames = array(Metrics::INDEX_NB_ACTIONS => 'referrals'); if ($replaceLabel) { $columnNames[Metrics::INDEX_NB_ACTIONS] = 'referrals'; } $report[$reportName]->filter('ReplaceColumnNames', array($columnNames)); } } return $report; }
/** * @param string $whereClause * @param array $bindIdSites * @param $idSite * @param $period * @param $date * @param $visitorId * @param $minTimestamp * @return array * @throws Exception */ private function getWhereClauseAndBind($whereClause, $bindIdSites, $idSite, $period, $date, $visitorId, $minTimestamp) { $where = array(); $where[] = $whereClause; $whereBind = $bindIdSites; if (!empty($visitorId)) { $where[] = "log_visit.idvisitor = ? "; $whereBind[] = @Common::hex2bin($visitorId); } if (!empty($minTimestamp)) { $where[] = "log_visit.visit_last_action_time > ? "; $whereBind[] = date("Y-m-d H:i:s", $minTimestamp); } // SQL Filter with provided period if (!empty($period) && !empty($date)) { $currentSite = $this->makeSite($idSite); $currentTimezone = $currentSite->getTimezone(); $dateString = $date; if ($period == 'range') { $processedPeriod = new Range('range', $date); if ($parsedDate = Range::parseDateRange($date)) { $dateString = $parsedDate[2]; } } else { $processedDate = Date::factory($date); $processedPeriod = Period\Factory::build($period, $processedDate); } $dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone); $where[] = "log_visit.visit_last_action_time >= ?"; $whereBind[] = $dateStart->toString('Y-m-d H:i:s'); if (!in_array($date, array('now', 'today', 'yesterdaySameTime')) && strpos($date, 'last') === false && strpos($date, 'previous') === false && Date::factory($dateString)->toString('Y-m-d') != Date::factory('now', $currentTimezone)->toString()) { $dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone); $where[] = " log_visit.visit_last_action_time <= ?"; $dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s'); $whereBind[] = $dateEndString; } } if (count($where) > 0) { $where = join("\n\t\t\t\tAND ", $where); } else { $where = false; } return array($whereBind, $where); }
/** * Creates a Period instance using a period, date and timezone. * * @param string $timezone The timezone of the date. Only used if `$date` is `'now'`, `'today'`, * `'yesterday'` or `'yesterdaySameTime'`. * @param string $period The period string: `"day"`, `"week"`, `"month"`, `"year"`, `"range"`. * @param string $date The date or date range string. Can be a special value including * `'now'`, `'today'`, `'yesterday'`, `'yesterdaySameTime'`. * @return \Piwik\Period */ public static function makePeriodFromQueryParams($timezone, $period, $date) { if (empty($timezone)) { $timezone = 'UTC'; } if ($period == 'range') { self::checkPeriodIsEnabled('range'); $oPeriod = new Range('range', $date, $timezone, Date::factory('today', $timezone)); } else { if (!$date instanceof Date) { if ($date == 'now' || $date == 'today') { $date = date('Y-m-d', Date::factory('now', $timezone)->getTimestamp()); } elseif ($date == 'yesterday' || $date == 'yesterdaySameTime') { $date = date('Y-m-d', Date::factory('now', $timezone)->subDay(1)->getTimestamp()); } $date = Date::factory($date); } $oPeriod = Factory::build($period, $date); } return $oPeriod; }
/** * @deprecated Use Factory::build instead * @param $period * @param $date * @return Period */ public static function factory($period, $date) { return Factory::build($period, $date); }
/** * Sends the http headers for csv file */ protected function renderHeader() { $fileName = 'Piwik ' . Piwik::translate('General_Export'); $period = Common::getRequestVar('period', false); $date = Common::getRequestVar('date', false); if ($period || $date) { if ($period == 'range') { $period = new Range($period, $date); } else { if (strpos($date, ',') !== false) { $period = new Range('range', $date); } else { $period = Period\Factory::build($period, Date::factory($date)); } } $prettyDate = $period->getLocalizedLongString(); $meta = $this->getApiMetaData(); $fileName .= ' _ ' . $meta['name'] . ' _ ' . $prettyDate . '.csv'; } // silent fail otherwise unit tests fail Common::sendHeader('Content-Disposition: attachment; filename="' . $fileName . '"', true); ProxyHttp::overrideCacheControlHeaders(); }
/** * @param array $reports * @param array $info * @return mixed */ public function getReportMetadata(&$reports, $info) { $idSites = $info['idSites']; // If only one website is selected, we add the Graph URL if (count($idSites) != 1) { return; } $idSite = reset($idSites); // in case API.getReportMetadata was not called with date/period we use sane defaults if (empty($info['period'])) { $info['period'] = 'day'; } if (empty($info['date'])) { $info['date'] = 'today'; } // need two sets of period & date, one for single period graphs, one for multiple periods graphs if (Period::isMultiplePeriod($info['date'], $info['period'])) { $periodForMultiplePeriodGraph = $info['period']; $dateForMultiplePeriodGraph = $info['date']; $periodForSinglePeriodGraph = 'range'; $dateForSinglePeriodGraph = $info['date']; } else { $periodForSinglePeriodGraph = $info['period']; $dateForSinglePeriodGraph = $info['date']; $piwikSite = new Site($idSite); if ($periodForSinglePeriodGraph == 'range') { // for period=range, show the configured sub-periods $periodForMultiplePeriodGraph = Config::getInstance()->General['graphs_default_period_to_plot_when_period_range']; $dateForMultiplePeriodGraph = $dateForSinglePeriodGraph; } else { if ($info['period'] == 'day' || !Config::getInstance()->General['graphs_show_evolution_within_selected_period']) { // for period=day, always show the last n days // if graphs_show_evolution_within_selected_period=false, show the last n periods $periodForMultiplePeriodGraph = $periodForSinglePeriodGraph; $dateForMultiplePeriodGraph = Range::getRelativeToEndDate($periodForSinglePeriodGraph, 'last' . self::GRAPH_EVOLUTION_LAST_PERIODS, $dateForSinglePeriodGraph, $piwikSite); } else { // if graphs_show_evolution_within_selected_period=true, show the days within the period // (except if the period is day, see above) $periodForMultiplePeriodGraph = 'day'; $period = PeriodFactory::build($info['period'], $info['date']); $start = $period->getDateStart()->toString(); $end = $period->getDateEnd()->toString(); $dateForMultiplePeriodGraph = $start . ',' . $end; } } } $token_auth = Common::getRequestVar('token_auth', false); $segment = Request::getRawSegmentFromRequest(); /** @var Scheduler $scheduler */ $scheduler = StaticContainer::getContainer()->get('Piwik\\Scheduler\\Scheduler'); $isRunningTask = $scheduler->isRunningTask(); // add the idSubtable if it exists $idSubtable = Common::getRequestVar('idSubtable', false); $urlPrefix = "index.php?"; foreach ($reports as &$report) { $reportModule = $report['module']; $reportAction = $report['action']; $reportUniqueId = $reportModule . '_' . $reportAction; $parameters = array(); $parameters['module'] = 'API'; $parameters['method'] = 'ImageGraph.get'; $parameters['idSite'] = $idSite; $parameters['apiModule'] = $reportModule; $parameters['apiAction'] = $reportAction; if (!empty($token_auth)) { $parameters['token_auth'] = $token_auth; } // Forward custom Report parameters to the graph URL if (!empty($report['parameters'])) { $parameters = array_merge($parameters, $report['parameters']); } if (empty($report['dimension'])) { $parameters['period'] = $periodForMultiplePeriodGraph; $parameters['date'] = $dateForMultiplePeriodGraph; } else { $parameters['period'] = $periodForSinglePeriodGraph; $parameters['date'] = $dateForSinglePeriodGraph; } if ($idSubtable !== false) { $parameters['idSubtable'] = $idSubtable; } if (!empty($_GET['_restrictSitesToLogin']) && $isRunningTask) { $parameters['_restrictSitesToLogin'] = $_GET['_restrictSitesToLogin']; } if (!empty($segment)) { $parameters['segment'] = $segment; } $report['imageGraphUrl'] = $urlPrefix . Url::getQueryStringFromParameters($parameters); // thanks to API.getRowEvolution, reports with dimensions can now be plotted using an evolution graph // however, most reports with a fixed set of dimension values are excluded // this is done so Piwik Mobile and Scheduled Reports do not display them $reportWithDimensionsSupportsEvolution = empty($report['constantRowsCount']) || in_array($reportUniqueId, self::$CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS); $reportSupportsEvolution = !in_array($reportUniqueId, self::$REPORTS_DISABLED_EVOLUTION_GRAPH); if ($reportSupportsEvolution && $reportWithDimensionsSupportsEvolution) { $parameters['period'] = $periodForMultiplePeriodGraph; $parameters['date'] = $dateForMultiplePeriodGraph; $report['imageGraphEvolutionUrl'] = $urlPrefix . Url::getQueryStringFromParameters($parameters); } } }
/** * Returns the pretty date representation * * @param $date string * @param $period string * @return string Pretty date */ public static function getPrettyDate($date, $period) { return self::getCalendarPrettyDate(Period\Factory::build($period, Date::factory($date))); }
protected function addFilter_prettyDate() { $prettyDate = new Twig_SimpleFilter('prettyDate', function ($dateString, $period) { return Period\Factory::build($period, $dateString)->getLocalizedShortString(); }); $this->twig->addFilter($prettyDate); }
private function loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment = false, $countVisitorsToFetch = 100, $visitorId = false, $minTimestamp = false, $filterSortOrder = false) { $where = $whereBind = array(); list($whereClause, $idSites) = $this->getIdSitesWhereClause($idSite); $where[] = $whereClause; $whereBind = $idSites; if (strtolower($filterSortOrder) !== 'asc') { $filterSortOrder = 'DESC'; } $orderBy = "idsite, visit_last_action_time " . $filterSortOrder; $orderByParent = "sub.visit_last_action_time " . $filterSortOrder; if (!empty($visitorId)) { $where[] = "log_visit.idvisitor = ? "; $whereBind[] = @Common::hex2bin($visitorId); } if (!empty($minTimestamp)) { $where[] = "log_visit.visit_last_action_time > ? "; $whereBind[] = date("Y-m-d H:i:s", $minTimestamp); } // If no other filter, only look at the last 24 hours of stats if (empty($visitorId) && empty($countVisitorsToFetch) && empty($period) && empty($date)) { $period = 'day'; $date = 'yesterdaySameTime'; } // SQL Filter with provided period if (!empty($period) && !empty($date)) { $currentSite = new Site($idSite); $currentTimezone = $currentSite->getTimezone(); $dateString = $date; if ($period == 'range') { $processedPeriod = new Range('range', $date); if ($parsedDate = Range::parseDateRange($date)) { $dateString = $parsedDate[2]; } } else { $processedDate = Date::factory($date); if ($date == 'today' || $date == 'now' || $processedDate->toString() == Date::factory('now', $currentTimezone)->toString()) { $processedDate = $processedDate->subDay(1); } $processedPeriod = Period\Factory::build($period, $processedDate); } $dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone); $where[] = "log_visit.visit_last_action_time >= ?"; $whereBind[] = $dateStart->toString('Y-m-d H:i:s'); if (!in_array($date, array('now', 'today', 'yesterdaySameTime')) && strpos($date, 'last') === false && strpos($date, 'previous') === false && Date::factory($dateString)->toString('Y-m-d') != Date::factory('now', $currentTimezone)->toString()) { $dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone); $where[] = " log_visit.visit_last_action_time <= ?"; $dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s'); $whereBind[] = $dateEndString; } } if (count($where) > 0) { $where = join("\n\t\t\t\tAND ", $where); } else { $where = false; } $segment = new Segment($segment, $idSite); // Subquery to use the indexes for ORDER BY $select = "log_visit.*"; $from = "log_visit"; $subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy); $sqlLimit = $countVisitorsToFetch >= 1 ? " LIMIT 0, " . (int) $countVisitorsToFetch : ""; // Group by idvisit so that a visitor converting 2 goals only appears once $sql = "\n\t\t\tSELECT sub.* FROM (\n\t\t\t\t" . $subQuery['sql'] . "\n\t\t\t\t{$sqlLimit}\n\t\t\t) AS sub\n\t\t\tGROUP BY sub.idvisit\n\t\t\tORDER BY {$orderByParent}\n\t\t"; try { $data = Db::fetchAll($sql, $subQuery['bind']); } catch (Exception $e) { echo $e->getMessage(); exit; } $dataTable = new DataTable(); $dataTable->addRowsFromSimpleArray($data); // $dataTable->disableFilter('Truncate'); if (!empty($data[0])) { $columnsToNotAggregate = array_map(function () { return 'skip'; }, $data[0]); $dataTable->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, $columnsToNotAggregate); } return $dataTable; }
private function setSitesTable($numSites) { $sites = new DataTable(); $sites->addRowsFromSimpleArray($this->buildSitesArray(range(1, $numSites))); $sites->setMetadata('last_period_date', Period\Factory::build('day', '2012-12-12')); $this->dashboard->setSitesTable($sites); return $sites; }
private function createFactory($resultIndices) { $periods = array($this->date1range => PeriodFactory::build('day', $this->date1), $this->date2range => PeriodFactory::build('day', $this->date2)); $dataType = 'numeric'; $siteIds = array($this->site1, $this->site2); $dataNames = array('nb_visits', 'nb_pageviews'); $defaultRow = $this->defaultRow; if (!array_key_exists(DataTableFactory::TABLE_METADATA_PERIOD_INDEX, $resultIndices)) { $periods = array($periods[$this->date1range]); } if (!array_key_exists(DataTableFactory::TABLE_METADATA_SITE_INDEX, $resultIndices)) { $siteIds = array($siteIds[0]); } return new DataTableFactory($dataNames, $dataType, $siteIds, $periods, $defaultRow); }
/** * Adds new subperiods * * @param Date $startDate * @param Date $endDate * @param string $period */ protected function fillArraySubPeriods($startDate, $endDate, $period) { $arrayPeriods = array(); $endSubperiod = Period\Factory::build($period, $endDate); $arrayPeriods[] = $endSubperiod; // set end date to start of end period since we're comparing against start date. $endDate = $endSubperiod->getDateStart(); while ($endDate->isLater($startDate)) { $endDate = $endDate->addPeriod(-1, $period); $subPeriod = Period\Factory::build($period, $endDate); $arrayPeriods[] = $subPeriod; } $arrayPeriods = array_reverse($arrayPeriods); foreach ($arrayPeriods as $period) { $this->addSubperiod($period); } }
public function sendReport($idReport, $period = false, $date = false, $force = false) { Piwik::checkUserIsNotAnonymous(); $reports = $this->getReports($idSite = false, false, $idReport); $report = reset($reports); if ($report['period'] == 'never') { $report['period'] = 'day'; } if (!empty($period)) { $report['period'] = $period; } if (empty($date)) { $date = Date::now()->subPeriod(1, $report['period'])->toString(); } $language = \Piwik\Plugins\LanguagesManager\API::getInstance()->getLanguageForUser($report['login']); // generate report list($outputFilename, $prettyDate, $reportSubject, $reportTitle, $additionalFiles) = $this->generateReport($idReport, $date, $language, self::OUTPUT_SAVE_ON_DISK, $report['period']); if (!file_exists($outputFilename)) { throw new Exception("The report file wasn't found in {$outputFilename}"); } $contents = file_get_contents($outputFilename); if (empty($contents)) { Log::warning("Scheduled report file '%s' exists but is empty!", $outputFilename); } /** * Triggered when sending scheduled reports. * * Plugins that provide new scheduled report transport mediums should use this event to * send the scheduled report. * * @param string $reportType A string ID describing how the report is sent, eg, * `'sms'` or `'email'`. * @param array $report An array describing the scheduled report that is being * generated. * @param string $contents The contents of the scheduled report that was generated * and now should be sent. * @param string $filename The path to the file where the scheduled report has * been saved. * @param string $prettyDate A prettified date string for the data within the * scheduled report. * @param string $reportSubject A string describing what's in the scheduled * report. * @param string $reportTitle The scheduled report's given title (given by a Piwik user). * @param array $additionalFiles The list of additional files that should be * sent with this report. * @param \Piwik\Period $period The period for which the report has been generated. * @param boolean $force A report can only be sent once per period. Setting this to true * will force to send the report even if it has already been sent. */ Piwik::postEvent(self::SEND_REPORT_EVENT, array($report['type'], $report, $contents, $filename = basename($outputFilename), $prettyDate, $reportSubject, $reportTitle, $additionalFiles, \Piwik\Period\Factory::build($report['period'], $date), $force)); // Update flag in DB $now = Date::now()->getDatetime(); $this->getModel()->updateReport($report['idreport'], array('ts_last_sent' => $now)); // If running from piwik.php with debug, do not delete the PDF after sending the email if (!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) { @chmod($outputFilename, 0600); } }
private function getPrettyDateForSite($datetime, $period, $timezone) { $date = Date::factory($datetime, $timezone); // we ran the alerts for the period before... $date = $date->subPeriod(1, $period); $period = Period\Factory::build($period, $date); $prettyDate = $period->getLocalizedShortString(); return $prettyDate; }
/** * Returns start & end dates for the range described by a period and optional lastN * argument. * * @param string|bool $date The start date of the period (or the date range of a range * period). * @param string $period The period type ('day', 'week', 'month', 'year' or 'range'). * @param bool|int $lastN Whether to include the last N periods in the range or not. * Ignored if period == range. * * @return Date[] array of Date objects or array(false, false) * @ignore */ public static function getDateRangeForPeriod($date, $period, $lastN = false) { if ($date === false) { return array(false, false); } // if the range is just a normal period (or the period is a range in which case lastN is ignored) if ($lastN === false || $period == 'range') { if ($period == 'range') { $oPeriod = new Range('day', $date); } else { $oPeriod = Period\Factory::build($period, Date::factory($date)); } $startDate = $oPeriod->getDateStart(); $endDate = $oPeriod->getDateEnd(); } else { list($date, $lastN) = EvolutionViz::getDateRangeAndLastN($period, $date, $lastN); list($startDate, $endDate) = explode(',', $date); $startDate = Date::factory($startDate); $endDate = Date::factory($endDate); } return array($startDate, $endDate); }
/** * 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 data that is found in query parameters, so the parameters to this * function can be string values. * * If you want to create an Archive instance with an array of Period instances, use * {@link Archive::factory()}. * * @param string|int|array $idSites A single ID (eg, `'1'`), multiple IDs (eg, `'1,2,3'` or `array(1, 2, 3)`), * or `'all'`. * @param string $period 'day', `'week'`, `'month'`, `'year'` or `'range'` * @param Date|string $strDate 'YYYY-MM-DD', magic keywords (ie, 'today'; {@link Date::factory()} * 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 */ public static function build($idSites, $period, $strDate, $segment = false, $_restrictSitesToLogin = false, $skipAggregationOfSubTables = false) { $websiteIds = Site::getIdSitesFromIdSitesString($idSites, $_restrictSitesToLogin); if (Period::isMultiplePeriod($strDate, $period)) { $oPeriod = Factory::build($period, $strDate); $allPeriods = $oPeriod->getSubperiods(); } else { $timezone = count($websiteIds) == 1 ? Site::getTimezoneFor($websiteIds[0]) : false; $oPeriod = Factory::makePeriodFromQueryParams($timezone, $period, $strDate); $allPeriods = array($oPeriod); } $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); }
/** * @param $period * @return bool|int */ private function getPeriodId($period) { if (!empty($period)) { $period = Period\Factory::build($period, Date::today()); } $invalidateForPeriod = $period ? $period->getId() : false; return $invalidateForPeriod; }
/** * 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/ * * @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 * @throws Exception * @return array */ public function invalidateArchivedReports($idSites, $dates) { $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } Piwik::checkUserHasAdminAccess($idSites); // Ensure the specified dates are valid $toInvalidate = $invalidDates = array(); $dates = explode(',', trim($dates)); $dates = array_unique($dates); foreach ($dates as $theDate) { $theDate = trim($theDate); try { $date = Date::factory($theDate); } catch (Exception $e) { $invalidDates[] = $theDate; continue; } if ($date->toString() == $theDate) { $toInvalidate[] = $date; } else { $invalidDates[] = $theDate; } } // If using the feature "Delete logs older than N days"... $purgeDataSettings = PrivacyManager::getPurgeDataSettings(); $logsAreDeletedBeforeThisDate = $purgeDataSettings['delete_logs_schedule_lowest_interval']; $logsDeleteEnabled = $purgeDataSettings['delete_logs_enable']; $minimumDateWithLogs = false; if ($logsDeleteEnabled && $logsAreDeletedBeforeThisDate) { $minimumDateWithLogs = Date::factory('today')->subDay($logsAreDeletedBeforeThisDate); } // Given the list of dates, process which tables they should be deleted from $minDate = false; $warningDates = $processedDates = array(); /* @var $date Date */ foreach ($toInvalidate as $date) { // we should only delete reports for dates that are more recent than N days if ($minimumDateWithLogs && $date->isEarlier($minimumDateWithLogs)) { $warningDates[] = $date->toString(); } else { $processedDates[] = $date->toString(); } $month = $date->toString('Y_m'); // For a given date, we must invalidate in the monthly archive table $datesByMonth[$month][] = $date->toString(); // But also the year stored in January $year = $date->toString('Y_01'); $datesByMonth[$year][] = $date->toString(); // but also weeks overlapping several months stored in the month where the week is starting /* @var $week Week */ $week = Period\Factory::build('week', $date); $weekAsString = $week->getDateStart()->toString('Y_m'); $datesByMonth[$weekAsString][] = $date->toString(); // Keep track of the minimum date for each website if ($minDate === false || $date->isEarlier($minDate)) { $minDate = $date; } } if (empty($minDate)) { throw new Exception("Check the 'dates' parameter is a valid date."); } // In each table, invalidate day/week/month/year containing this date $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled(); foreach ($archiveTables as $table) { // Extract Y_m from table name $suffix = ArchiveTableCreator::getDateFromTableName($table); if (!isset($datesByMonth[$suffix])) { continue; } // Dates which are to be deleted from this table $datesToDeleteInTable = $datesByMonth[$suffix]; // Build one statement to delete all dates from the given table $sql = $bind = array(); $datesToDeleteInTable = array_unique($datesToDeleteInTable); foreach ($datesToDeleteInTable as $dateToDelete) { $sql[] = '(date1 <= ? AND ? <= date2)'; $bind[] = $dateToDelete; $bind[] = $dateToDelete; } $sql = implode(" OR ", $sql); $query = "DELETE FROM {$table} " . " WHERE ( {$sql} ) " . " AND idsite IN (" . implode(",", $idSites) . ")"; Db::query($query, $bind); } \Piwik\Plugins\SitesManager\API::getInstance()->updateSiteCreatedTime($idSites, $minDate); // Force to re-process data for these websites in the next cron core:archive command run $invalidatedIdSites = self::getWebsiteIdsToInvalidate(); $invalidatedIdSites = array_merge($invalidatedIdSites, $idSites); $invalidatedIdSites = array_unique($invalidatedIdSites); $invalidatedIdSites = array_values($invalidatedIdSites); Option::set(self::OPTION_INVALIDATED_IDSITES, serialize($invalidatedIdSites)); Site::clearCache(); $output = array(); // output logs if ($warningDates) { $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: ' . implode(", ", $warningDates) . "\n The last day with logs is " . $minimumDateWithLogs . ". " . "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'."; } $output[] = "Success. The following dates were invalidated successfully: " . implode(", ", $processedDates); return $output; }
private function insertArchiveRow($idSite, $date, $periodLabel) { $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; $periodId = Piwik::$idPeriods[$periodLabel]; $doneFlag = 'done'; if ($idArchive % 5 == 1) { $doneFlag = Rules::getDoneFlagArchiveContainsAllPlugins(self::$segment1); } else { if ($idArchive % 5 == 2) { $doneFlag .= '.VisitsSummary'; } else { if ($idArchive % 5 == 3) { $doneFlag = Rules::getDoneFlagArchiveContainsOnePlugin(self::$segment1, 'UserCountry'); } else { if ($idArchive % 5 == 4) { $doneFlag = Rules::getDoneFlagArchiveContainsAllPlugins(self::$segment2); } } } } $sql = "INSERT INTO {$table} (idarchive, name, idsite, date1, date2, period, ts_archived)\n VALUES ({$idArchive}, 'nb_visits', {$idSite}, '{$dateStart}', '{$dateEnd}', {$periodId}, NOW()),\n ({$idArchive}, '{$doneFlag}', {$idSite}, '{$dateStart}', '{$dateEnd}', {$periodId}, NOW())"; Db::query($sql); }
public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, $apiParameters = false, $idGoal = false, $language = false, $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false, $formatMetrics = null) { $timer = new Timer(); if (empty($apiParameters)) { $apiParameters = array(); } if (!empty($idGoal) && empty($apiParameters['idGoal'])) { $apiParameters['idGoal'] = $idGoal; } // Is this report found in the Metadata available reports? $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc, $showSubtableReports = true); if (empty($reportMetadata)) { throw new Exception("Requested report {$apiModule}.{$apiAction} for Website id={$idSite} not found in the list of available reports. \n"); } $reportMetadata = reset($reportMetadata); // Generate Api call URL passing custom parameters $parameters = array_merge($apiParameters, array('method' => $apiModule . '.' . $apiAction, 'idSite' => $idSite, 'period' => $period, 'date' => $date, 'format' => 'original', 'serialize' => '0', 'language' => $language, 'idSubtable' => $idSubtable)); if (!empty($segment)) { $parameters['segment'] = $segment; } if (!empty($reportMetadata['processedMetrics']) && !empty($reportMetadata['metrics']['nb_visits']) && @$reportMetadata['category'] != Piwik::translate('Goals_Ecommerce') && $apiModule !== 'MultiSites') { $deleteRowsWithNoVisits = empty($reportMetadata['constantRowsCount']) ? '1' : '0'; $parameters['filter_add_columns_when_show_all_columns'] = $deleteRowsWithNoVisits; } $url = Url::getQueryStringFromParameters($parameters); $request = new Request($url); try { /** @var DataTable */ $dataTable = $request->process(); } catch (Exception $e) { throw new Exception("API returned an error: " . $e->getMessage() . " at " . basename($e->getFile()) . ":" . $e->getLine() . "\n"); } list($newReport, $columns, $rowsMetadata, $totals) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, $showRawMetrics, $formatMetrics); foreach ($columns as &$name) { $name = ucfirst($name); } $website = new Site($idSite); $period = Period\Factory::build($period, $date); $period = $period->getLocalizedLongString(); $return = array('website' => $website->getName(), 'prettyDate' => $period, 'metadata' => $reportMetadata, 'columns' => $columns, 'reportData' => $newReport, 'reportMetadata' => $rowsMetadata, 'reportTotal' => $totals); if ($showTimer) { $return['timerMillis'] = $timer->getTimeMs(0); } return $return; }
private function getAllOverlappingChildPeriodsInRange(Date $dateStart, Date $dateEnd) { $result = array(); $childPeriodType = $this->getImmediateChildPeriodLabel(); if (empty($childPeriodType)) { return $result; } $childPeriods = Factory::build($childPeriodType, $dateStart->toString() . ',' . $dateEnd->toString()); return array_merge($childPeriods->getSubperiods(), $childPeriods->getAllOverlappingChildPeriodsInRange($dateStart, $dateEnd)); }