芝麻web文件管理V1.00
编辑当前文件:/home/mgatv524/public_html/mctv/lib/XTR/StatsArchiveTask.php
. */ namespace Xibo\XTR; use Carbon\Carbon; use Xibo\Entity\User; use Xibo\Factory\MediaFactory; use Xibo\Factory\UserFactory; use Xibo\Helper\DateFormatHelper; use Xibo\Helper\Random; use Xibo\Support\Exception\InvalidArgumentException; use Xibo\Support\Exception\NotFoundException; use Xibo\Support\Exception\TaskRunException; /** * Class StatsArchiveTask * @package Xibo\XTR */ class StatsArchiveTask implements TaskInterface { use TaskTrait; /** @var User */ private $archiveOwner; /** @var MediaFactory */ private $mediaFactory; /** @var UserFactory */ private $userFactory; /** @var Carbon */ private $lastArchiveDate = null; /** @var \Xibo\Helper\SanitizerService */ private $sanitizerService; /** @inheritdoc */ public function setFactories($container) { $this->userFactory = $container->get('userFactory'); $this->mediaFactory = $container->get('mediaFactory'); $this->sanitizerService = $container->get('sanitizerService'); return $this; } /** @inheritdoc */ public function run() { $this->archiveStats(); $this->tidyStats(); } public function archiveStats() { $this->log->debug('Archive Stats'); $this->runMessage = '# ' . __('Stats Archive') . PHP_EOL . PHP_EOL; if ($this->getOption('archiveStats', 'Off') == 'On') { $this->log->debug('Archive Enabled'); // Archive tasks by week. $periodSizeInDays = $this->getOption('periodSizeInDays', 7); $maxPeriods = $this->getOption('maxPeriods', 4); $periodsToKeep = $this->getOption('periodsToKeep', 1); $this->setArchiveOwner(); // Get the earliest $earliestDate = $this->timeSeriesStore->getEarliestDate(); if ($earliestDate === null) { $this->log->debug('Earliest date is null, nothing to archive.'); $this->runMessage = __('Nothing to archive'); return; } // Wind back to the start of the day $earliestDate = $earliestDate->copy()->setTime(0, 0, 0); // Take the earliest date and roll forward until the current time $now = Carbon::now()->subDays($periodSizeInDays * $periodsToKeep)->setTime(0, 0, 0); $i = 0; while ($earliestDate < $now && $i < $maxPeriods) { $i++; // Push forward $fromDt = $earliestDate->copy(); $earliestDate->addDays($periodSizeInDays); $this->log->debug('Running archive number ' . $i . 'for ' . $fromDt->toAtomString() . ' - ' . $earliestDate->toAtomString()); try { $this->exportStatsToLibrary($fromDt, $earliestDate); } catch (\Exception $exception) { $this->log->error('Export error for Archive Number ' . $i . ', e = ' . $exception->getMessage()); // Throw out to the task handler to record the error. throw $exception; } $this->store->commitIfNecessary(); $this->log->debug('Export success for Archive Number ' . $i); // Grab the last from date for use in tidy stats $this->lastArchiveDate = $fromDt; } $this->runMessage .= ' - ' . __('Done') . PHP_EOL . PHP_EOL; } else { $this->log->debug('Archive not enabled'); $this->runMessage .= ' - ' . __('Disabled') . PHP_EOL . PHP_EOL; } $this->log->debug('Finished archive stats, last archive date is ' . ($this->lastArchiveDate == null ? 'null' : $this->lastArchiveDate->toAtomString())); } /** * Export stats to the library * @param Carbon $fromDt * @param Carbon $toDt * @throws \Xibo\Support\Exception\GeneralException */ private function exportStatsToLibrary($fromDt, $toDt) { $this->log->debug('Export period: ' . $fromDt->toAtomString() . ' - ' . $toDt->toAtomString()); $this->runMessage .= ' - ' . $fromDt->format(DateFormatHelper::getSystemFormat()) . ' / ' . $toDt->format(DateFormatHelper::getSystemFormat()) . PHP_EOL; $resultSet = $this->timeSeriesStore->getStats([ 'fromDt'=> $fromDt, 'toDt'=> $toDt, ]); $this->log->debug('Get stats'); // Create a temporary file for this $fileName = tempnam(sys_get_temp_dir(), 'stats'); $out = fopen($fileName, 'w'); fputcsv($out, ['Stat Date', 'Type', 'FromDT', 'ToDT', 'Layout', 'Display', 'Media', 'Tag', 'Duration', 'Count', 'DisplayId', 'LayoutId', 'WidgetId', 'MediaId', 'Engagements']); $hasStatsToArchive = false; while ($row = $resultSet->getNextRow()) { $hasStatsToArchive = true; $sanitizedRow = $this->getSanitizer($row); if ($this->timeSeriesStore->getEngine() == 'mongodb') { $statDate = isset($row['statDate']) ? Carbon::createFromTimestamp($row['statDate']->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()) : null; $start = Carbon::createFromTimestamp($row['start']->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()); $end = Carbon::createFromTimestamp($row['end']->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()); $engagements = isset($row['engagements']) ? json_encode($row['engagements']) : '[]'; } else { $statDate = isset($row['statDate']) ? Carbon::createFromTimestamp($row['statDate'])->format(DateFormatHelper::getSystemFormat()) : null; $start = Carbon::createFromTimestamp($row['start'])->format(DateFormatHelper::getSystemFormat()); $end = Carbon::createFromTimestamp($row['end'])->format(DateFormatHelper::getSystemFormat()); $engagements = isset($row['engagements']) ? $row['engagements'] : '[]'; } // Read the columns fputcsv($out, [ $statDate, $sanitizedRow->getString('type'), $start, $end, isset($row['layout']) ? $sanitizedRow->getString('layout') :'', isset($row['display']) ? $sanitizedRow->getString('display') :'', isset($row['media']) ? $sanitizedRow->getString('media') :'', isset($row['tag']) ? $sanitizedRow->getString('tag') :'', $sanitizedRow->getInt('duration'), $sanitizedRow->getInt('count'), $sanitizedRow->getInt('displayId'), isset($row['layoutId']) ? $sanitizedRow->getInt('layoutId') :'', isset($row['widgetId']) ? $sanitizedRow->getInt('widgetId') :'', isset($row['mediaId']) ? $sanitizedRow->getInt('mediaId') :'', $engagements ]); } fclose($out); if ($hasStatsToArchive) { $this->log->debug('Temporary file written, zipping'); // Create a ZIP file and add our temporary file $zipName = $this->config->getSetting('LIBRARY_LOCATION') . 'temp/stats.csv.zip'; $zip = new \ZipArchive(); $result = $zip->open($zipName, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); if ($result !== true) { throw new InvalidArgumentException(__('Can\'t create ZIP. Error Code: %s', $result)); } $zip->addFile($fileName, 'stats.csv'); $zip->close(); $this->log->debug('Zipped to ' . $zipName); // This all might have taken a long time indeed, so lets see if we need to reconnect MySQL $this->store->select('SELECT 1', [], null, true); $this->log->debug('MySQL connection refreshed if necessary'); // Upload to the library $media = $this->mediaFactory->create( __('Stats Export %s to %s - %s', $fromDt->format('Y-m-d'), $toDt->format('Y-m-d'), Random::generateString(5)), 'stats.csv.zip', 'genericfile', $this->archiveOwner->getId() ); $media->save(); $this->log->debug('Media saved as ' . $media->name); // Commit before the delete (the delete might take a long time) $this->store->commitIfNecessary(); // Set max attempts to -1 so that we continue deleting until we've removed all of the stats that we've exported $options = [ 'maxAttempts' => -1, 'statsDeleteSleep' => 1, 'limit' => 1000 ]; $this->log->debug('Delete stats for period: ' . $fromDt->toAtomString() . ' - ' . $toDt->toAtomString()); // Delete the stats, incrementally $this->timeSeriesStore->deleteStats($toDt, $fromDt, $options); // This all might have taken a long time indeed, so lets see if we need to reconnect MySQL $this->store->select('SELECT 1', [], null, true); $this->log->debug('MySQL connection refreshed if necessary'); $this->log->debug('Delete stats completed, export period completed.'); } else { $this->log->debug('There are no stats to archive'); } // Remove the CSV file unlink($fileName); } /** * Set the archive owner * @throws TaskRunException */ private function setArchiveOwner() { $archiveOwner = $this->getOption('archiveOwner', null); if ($archiveOwner == null) { $admins = $this->userFactory->getSuperAdmins(); if (count($admins) <= 0) { throw new TaskRunException(__('No super admins to use as the archive owner, please set one in the configuration.')); } $this->archiveOwner = $admins[0]; } else { try { $this->archiveOwner = $this->userFactory->getByName($archiveOwner); } catch (NotFoundException $e) { throw new TaskRunException(__('Archive Owner not found')); } } } /** * Tidy Stats */ private function tidyStats() { $this->log->debug('Tidy stats'); $this->runMessage .= '## ' . __('Tidy Stats') . PHP_EOL; $maxAge = intval($this->config->getSetting('MAINTENANCE_STAT_MAXAGE')); if ($maxAge != 0) { $this->log->debug('Max Age is ' . $maxAge); // Set the max age to maxAgeDays from now, or if we've archived, from the archive date $maxAgeDate = ($this->lastArchiveDate === null) ? Carbon::now()->subDays($maxAge) : $this->lastArchiveDate; // Control the flow of the deletion $options = [ 'maxAttempts' => $this->getOption('statsDeleteMaxAttempts', 10), 'statsDeleteSleep' => $this->getOption('statsDeleteSleep', 3), 'limit' => 10000 // Note: for mongo we dont use $options['limit'] anymore ]; try { $this->log->debug('Calling delete stats with max age: ' . $maxAgeDate->toAtomString()); $countDeleted = $this->timeSeriesStore->deleteStats($maxAgeDate, null, $options); $this->log->debug('Delete Stats complete'); $this->runMessage .= ' - ' . sprintf(__('Done - %d deleted.'), $countDeleted) . PHP_EOL . PHP_EOL; } catch (\Exception $exception) { $this->log->error('Unexpected error running stats tidy. e = ' . $exception->getMessage()); $this->runMessage .= ' - ' . __('Error.') . PHP_EOL . PHP_EOL; } } else { $this->runMessage .= ' - ' . __('Disabled') . PHP_EOL . PHP_EOL; } $this->log->debug('Tidy stats complete'); } }