debugAlwaysArchive = Zend_Registry::get('config')->Debug->always_archive_data; } /** * Returns the Piwik_ArchiveProcessing_Day or Piwik_ArchiveProcessing_Period object * depending on $name period string * * @param string $name day|week|month|year * @return Piwik_ArchiveProcessing Piwik_ArchiveProcessing_Day|Piwik_ArchiveProcessing_Period */ static function factory($name ) { switch($name) { case 'day': require_once 'ArchiveProcessing/Day.php'; $process = new Piwik_ArchiveProcessing_Day; break; case 'week': case 'month': case 'year': require_once 'ArchiveProcessing/Period.php'; $process = new Piwik_ArchiveProcessing_Period; break; default: throw new Exception("Unknown period specified $name"); break; } return $process; } /** * Inits the object * * @return void */ protected function loadArchiveProperties() { $this->idsite = $this->site->getId(); $this->periodId = $this->period->getId(); $this->dateStart = $this->period->getDateStart(); $this->dateEnd = $this->period->getDateEnd(); $this->tableArchiveNumeric = new Piwik_TablePartitioning_Monthly('archive_numeric'); $this->tableArchiveNumeric->setTimestamp($this->dateStart->get()); $this->tableArchiveBlob = new Piwik_TablePartitioning_Monthly('archive_blob'); $this->tableArchiveBlob->setTimestamp($this->dateStart->get()); $this->strDateStart = $this->dateStart->toString(); $this->strDateEnd = $this->dateEnd->toString(); // if the current archive is a DAY and if it's today, // we set this maxTimestampArchive that defines the lifetime value of today's archive $this->maxTimestampArchive = 0; if( $this->period->getNumberOfSubperiods() == 0 && $this->period->toString() == date("Y-m-d") ) { //TODO this TIMESTAMP should be a mysql NOW()!!!! $this->maxTimestampArchive = time() - Zend_Registry::get('config')->General->time_before_archive_considered_outdated; } // either // - if the period we're looking for is finished, we look for a ts_archived that // is greater than the last day of the archive // - if the period we're looking for is not finished, we look for a recent enough archive // recent enough means maxTimestampArchive = 00:00:01 this morning else { if($this->period->isFinished()) { // echo "
date end = ".$this->period->getDateEnd(); $this->maxTimestampArchive = $this->period->getDateEnd()->setTime('00:00:00')->addDay(1)->getTimestamp(); // echo "
max = ". date("Y-m-d H:i:s",$this->maxTimestampArchive); } else { $this->maxTimestampArchive = Piwik_Date::today()->getTimestamp(); } } } /** * This method returns the idArchive ; if necessary, it triggers the archiving process. * * If the archive was not processed yet, it will launch the archiving process. * If the current archive needs sub-archives (eg. a month archive needs all the days archive) * it will recursively launch the archiving (using this loadArchive() on the sub-periods) * * @return int The idarchive of the archive */ public function loadArchive() { $this->loadArchiveProperties(); $this->idArchive = $this->isArchived(); if($this->idArchive === false || $this->debugAlwaysArchive) { // Piwik::printMemoryUsage('Before loading subperiods'); $this->archivesSubperiods = $this->loadSubperiodsArchive(); // Piwik::printMemoryUsage('After loading subperiods'); $this->initCompute(); // Piwik::printMemoryUsage('After init compute'); $this->compute(); // Piwik::printMemoryUsage('After compute'); $this->postCompute(); // Piwik::printMemoryUsage('After post compute'); // we execute again the isArchived that does some initialization work $this->idArchive = $this->isArchived(); // Piwik::log("New archive computed, id = {$this->idArchive}"); } else { //Piwik::log("Archive already available, id = {$this->idArchive}"); $this->isThereSomeVisits = true; } return $this->idArchive; } /** * This methods reads the subperiods if necessary, * and computes the archive of the current period. */ abstract protected function compute(); /** * Init the object before launching the real archive processing * * @return void */ protected function initCompute() { $this->loadNextIdarchive(); $record = new Piwik_ArchiveProcessing_Record_Numeric('done', Piwik_ArchiveProcessing::DONE_ERROR); $this->insertRecord( $record); $record->delete(); $this->logTable = Piwik::prefixTable('log_visit'); $this->logVisitActionTable = Piwik::prefixTable('log_link_visit_action'); $this->logActionTable = Piwik::prefixTable('log_action'); } /** * Post processing called at the end of the main archive processing. * Makes sure the new archive is marked as "successful" in the DB * * We also try to delete some stuff from memory but really there is still a lot... * * @return void */ protected function postCompute() { // echo "
".Piwik_ArchiveProcessing_Record_Manager::getInstance()->toString(); // delete the first done = ERROR Zend_Registry::get('db')->query(" DELETE FROM ".$this->tableArchiveNumeric->getTableName()." WHERE idarchive = ? AND name = 'done'", array($this->idArchive) ); $record = new Piwik_ArchiveProcessing_Record_Numeric('done', Piwik_ArchiveProcessing::DONE_OK); // save in the database the records $records = Piwik_ArchiveProcessing_Record_Manager::getInstance()->getRecords(); foreach($records as $record) { $this->insertRecord( $record); } // delete the records from the global manager foreach($records as $record) { $record->delete(); } unset($records); // we delete all tables from the table register Piwik_ArchiveProcessing_Record_Manager::getInstance()->deleteAll(); } /** * Returns the name of the numeric table where the archive numeric values are stored * * @return string */ public function getTableArchiveNumericName() { return $this->tableArchiveNumeric->getTableName(); } /** * Returns the name of the blob table where the archive blob values are stored * * @return string */ public function getTableArchiveBlobName() { return $this->tableArchiveBlob->getTableName(); } /** * Set the period * * @param Piwik_Period $period */ public function setPeriod( Piwik_Period $period ) { $this->period = $period; } /** * Set the site * * @param Piwik_Site $site */ public function setSite( Piwik_Site $site ) { $this->site = $site; } /** * Returns the timestamp of the first date of the period * * @return int */ public function getTimestampStartDate() { // case when archive processing is in the past or the future, the starting date has not been set or processed yet if(is_null($this->timestampDateStart)) { return Piwik_Date::factory($this->strDateStart)->getTimestamp(); } return $this->timestampDateStart; } /** * Returns the idArchive we will use for the current archive * * @return int IdArchive to use when saving the current Archive */ protected function loadNextIdarchive() { $db = Zend_Registry::get('db'); $id = $db->fetchOne("SELECT max(idarchive) FROM ".$this->tableArchiveNumeric->getTableName()); if(empty($id)) { $id = 0; } $this->idArchive = $id + 1; } /** * Inserts a record in the good table (either NUMERIC or BLOB) * * @param unknown_type $record */ protected function insertRecord($record) { // table to use to save the data if(Piwik::isNumeric($record->value)) { $table = $this->tableArchiveNumeric; } else { $table = $this->tableArchiveBlob; } $query = "INSERT INTO ".$table->getTableName()." (idarchive, idsite, date1, date2, period, ts_archived, name, value) VALUES (?,?,?,?,?,?,?,?)"; Zend_Registry::get('db')->query($query, array( $this->idArchive, $this->idsite, $this->strDateStart, $this->strDateEnd, $this->periodId, date("Y-m-d H:i:s"), $record->name, $record->value, ) ); } /** * Returns the ID of the archived subperiods. * * @return array Array of the idArchive of the subperiods */ protected function loadSubperiodsArchive() { $periods = array(); // we first compute every subperiod of the archive foreach($this->period->getSubperiods() as $period) { $archivePeriod = new Piwik_Archive_Single; $archivePeriod->setSite( $this->site ); $archivePeriod->setPeriod( $period ); $archivePeriod->prepareArchive(); $periods[] = $archivePeriod; } return $periods; } /** * Returns the idArchive if the archive is available in the database. * Returns false if the archive needs to be computed. * * An archive is available if * - for today, the archive was computed less than maxTimestampArchive seconds ago * - for any other day, if the archive was computed once this day was finished * - for other periods, if the archive was computed once the period was finished * * @return int|false */ protected function isArchived() { // Piwik::log("Is archive site=$idsite for period = ".$this->period->getLabel()." for date_start = $strDateStart ?"); $bindSQL = array( $this->idsite, $this->strDateStart, $this->strDateEnd, $this->periodId, ); // echo " p = ".$this->periodId." d = ".$this->strDateStart ."," . $this->strDateEnd; $timeStampWhere = " AND UNIX_TIMESTAMP(ts_archived) >= ? "; $bindSQL[] = $this->maxTimestampArchive; $sqlQuery = " SELECT idarchive, value, name, UNIX_TIMESTAMP(date1) as timestamp FROM ".$this->tableArchiveNumeric->getTableName()." WHERE idsite = ? AND date1 = ? AND date2 = ? AND period = ? AND ( (name = 'done' AND value = ".Piwik_ArchiveProcessing::DONE_OK.") OR name = 'nb_visits') $timeStampWhere ORDER BY ts_archived DESC"; $results = Zend_Registry::get('db')->fetchAll($sqlQuery, $bindSQL ); // the archive exists in the table if(!empty($results)) { // echo $this->strDateStart . " " . $this->strDateEnd; // var_dump($results); $idarchive = false; // let's look for the more recent idarchive foreach($results as $result) { if($result['name'] == 'done') { $idarchive = $result['idarchive']; $this->timestampDateStart = $result['timestamp']; break; } } if($idarchive === false) { throw new Exception("Error during the archiving process: ". var_export($results,true)); } // let's look for the nb_visits result for this more recent archive foreach($results as $result) { if($result['name'] == 'nb_visits' && $result['idarchive'] == $idarchive) { $this->isThereSomeVisits = ($result['value'] != 0); break; } } return $idarchive; } else { // echo "no archive for ".$this->strDateStart . " " . $this->strDateEnd; exit; return false; } } }