/** * @param array $data */ protected function onStatus(array $data) { if (isset($data['current'])) { $this->progress->setCurrent((int) $data['current']); unset($data['current']); } else { $this->progress->advance(); } foreach ($data as $key => $value) { $this->progress->setMessage($value, $key); } }
private function getNotificationCallback($filename) { $notificationCallback = function ($notification_code, $severity, $message, $messageCode, $bytesTransferred, $bytesMax) use($filename) { switch ($notification_code) { case STREAM_NOTIFY_FILE_SIZE_IS: $this->progress = new ProgressBar($this->output, $bytesMax); $this->progress->setFormat('%message% %final_report%' . "\n" . '%percent:3s%% of %photo_size% [%bar%] %downloaded_bytes% eta %estimated:6s%'); $this->progress->setMessage($filename); $this->progress->setMessage('', 'final_report'); $this->progress->start(); break; case STREAM_NOTIFY_PROGRESS: $this->progress->setCurrent($bytesTransferred); break; } }; return $notificationCallback; }
/** * @param OutputInterface $output * * @return resource */ protected function createStreamContext(OutputInterface $output) { $ctx = stream_context_create([], ['notification' => function ($code, $severity, $message, $message_code, $bytesTransferred, $bytesMax) use($output) { switch ($code) { case STREAM_NOTIFY_FILE_SIZE_IS: $this->progress = new ProgressBar($output, $bytesMax); $this->progress->setBarWidth(75); $this->progress->start(); break; case STREAM_NOTIFY_PROGRESS: $this->progress->setCurrent($bytesTransferred); if ($bytesTransferred == $bytesMax) { $this->progress->finish(); $output->writeln(''); } break; case STREAM_NOTIFY_COMPLETED: $this->progress->finish(); break; } }]); return $ctx; }
/** * Find and trigger the negative events, i.e. the events with a no decision path * * @param $campaign * @param int $totalEventCount * @param int $limit * @param bool $max * @param OutputInterface $output * * @return int */ public function triggerNegativeEvents($campaign, $totalEventCount = 0, $limit = 100, $max = false, OutputInterface $output = null) { defined('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED') or define('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED', 1); $logger = $this->factory->getLogger(); $logger->debug('CAMPAIGN: Triggering negative events'); $campaignId = $campaign->getId(); $campaignName = $campaign->getName(); $repo = $this->getRepository(); $campaignRepo = $this->getCampaignRepository(); /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */ $campaignModel = $this->factory->getModel('campaign'); /** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */ $leadModel = $this->factory->getModel('lead'); // Get events to avoid large number of joins $campaignEvents = $repo->getCampaignEvents($campaignId); // Get an array of events that are non-action based $nonActionEvents = array(); foreach ($campaignEvents as $id => $e) { if ($e['decisionPath'] == 'no') { $nonActionEvents[$e['parent_id']][$id] = $e; } } if (empty($nonActionEvents)) { // No non-action events associated with this campaign unset($campaignEvents); return 0; } // Get a count $leadCount = $campaignRepo->getCampaignLeadCount($campaignId); if ($output) { $output->writeln($this->translator->trans('mautic.campaign.trigger.lead_count_analyzed', array('%leads%' => $leadCount, '%batch%' => $limit))); } $start = $eventCount = $leadProcessedCount = $lastRoundPercentage = $processedCount = 0; $eventSettings = $campaignModel->getEvents(); $maxCount = $max ? $max : $leadCount; // Try to save some memory gc_enable(); if ($leadCount) { if ($output) { $progress = new ProgressBar($output, $maxCount); $progress->start(); if ($max) { $progress->advance($totalEventCount); } } $sleepBatchCount = 0; while ($start <= $leadCount) { // Get batched campaign ids $campaignLeads = $campaignRepo->getCampaignLeadIds($campaignId, $start, $limit); foreach ($nonActionEvents as $parentId => $events) { // Just a check to ensure this is an appropriate action if ($campaignEvents[$parentId]['eventType'] != 'decision') { $logger->debug('CAMPAIGN: Parent event ID #' . $parentId . ' is not a decision.'); continue; } // Get only leads who have had the action prior to the decision executed $grandParentId = $campaignEvents[$parentId]['parent_id']; // Get the lead log for this batch of leads limiting to those that have already triggered // the decision's parent and haven't executed this level in the path yet $leadLog = $repo->getEventLog($campaignId, $campaignLeads, array($grandParentId), array_keys($events)); $applicableLeads = array_keys($leadLog); if (empty($applicableLeads)) { $logger->debug('CAMPAIGN: No events are applicable'); continue; } // Get the leads $leads = $leadModel->getEntities(array('filter' => array('force' => array(array('column' => 'l.id', 'expr' => 'in', 'value' => $applicableLeads))), 'orderBy' => 'l.id', 'orderByDir' => 'asc')); if (!count($leads)) { // Just a precaution in case non-existent leads are lingering in the campaign leads table continue; } // Loop over the non-actions and determine if it has been processed for this lead foreach ($leads as $l) { // Set lead for listeners $leadModel->setSystemCurrentLead($l); $logger->debug('CAMPAIGN: Lead ID #' . $l->getId()); // Prevent path if lead has already gone down this path if (!array_key_exists($parentId, $leadLog[$l->getId()])) { // Get date to compare against $utcDateString = $leadLog[$l->getId()][$grandParentId]['date_triggered']; // Convert to local DateTime $grandParentDate = $this->factory->getDate($utcDateString, 'Y-m-d H:i:s', 'UTC')->getLocalDateTime(); // Non-decision has not taken place yet, so cycle over each associated action to see if timing is right $eventTiming = array(); $executeAction = false; foreach ($events as $id => $e) { if ($sleepBatchCount == $limit) { // Keep CPU down sleep($this->batchSleepTime); $sleepBatchCount = 0; } else { $sleepBatchCount++; } if (array_key_exists($id, $leadLog[$l->getId()])) { $logger->debug('CAMPAIGN: Event (ID #' . $id . ') has already been executed'); unset($e); continue; } if (!isset($eventSettings['action'][$e['type']])) { $logger->debug('CAMPAIGN: Event (ID #' . $id . ') no longer exists'); unset($e); continue; } // First get the timing for all the 'non-decision' actions $eventTiming[$id] = $this->checkEventTiming($e, $grandParentDate, true); if ($eventTiming[$id] === true) { // Includes events to be executed now then schedule the rest if applicable $executeAction = true; } unset($e); } if (!$executeAction) { // Timing is not appropriate so move on to next lead unset($eventTiming); continue; } $logDecision = $decisionLogged = false; // Execute or schedule events foreach ($eventTiming as $id => $timing) { // Set event $e = $events[$id]; $e['campaign'] = array('id' => $campaignId, 'name' => $campaignName); // Set lead in case this is triggered by the system $leadModel->setSystemCurrentLead($l); if ($timing instanceof \DateTime) { $processedCount++; // Schedule the action $logger->debug('CAMPAIGN: ID# ' . $e['id'] . ' timing is not appropriate and thus scheduled for ' . $timing->format('Y-m-d H:m:i T') . ''); $log = $this->getLogEntity($e['id'], $campaign, $l, null, true); $log->setLead($l); $log->setIsScheduled(true); $log->setTriggerDate($timing); $repo->saveEntity($log); $logDecision = true; } else { $processedCount++; // Save log first to prevent subsequent triggers from duplicating $log = $this->getLogEntity($e['id'], $campaign, $l, null, true); $log->setDateTriggered(new \DateTime()); $repo->saveEntity($log); $response = $this->invokeEventCallback($e, $eventSettings['action'][$e['type']], $l, null, true); if ($response === false) { $repo->deleteEntity($log); $logger->debug('CAMPAIGN: ID# ' . $e['id'] . ' execution failed.'); $logDecision = true; } else { $logger->debug('CAMPAIGN: ID# ' . $e['id'] . ' successfully executed and logged.'); if ($response !== true) { $log->setMetadata($response); $repo->saveEntity($log); } } } if (!empty($log)) { $this->em->detach($log); } unset($e, $log); if ($logDecision && !$decisionLogged) { // Log the decision $log = $this->getLogEntity($parentId, $campaign, $l, null, true); $log->setDateTriggered(new \DateTime()); $log->setNonActionPathTaken(true); $repo->saveEntity($log); $this->em->detach($log); unset($log); $decisionLogged = true; } if ($max && $totalEventCount >= $max) { // Hit the max if ($output) { $progress->finish(); $output->writeln(''); } return $eventCount; } $eventCount++; $totalEventCount++; unset($utcDateString, $grandParentDate); } } else { $logger->debug('CAMPAIGN: Decision has already been executed.'); } $currentCount = $max ? $totalEventCount : $leadProcessedCount; if ($output && $currentCount < $maxCount) { $progress->setCurrent($currentCount); } } // Save RAM $this->em->detach($l); unset($l); } // Next batch $start += $limit; $leadProcessedCount += count($campaignLeads); // Save RAM $this->em->clear('MauticLeadBundle:Lead'); $this->em->clear('MauticUserBundle:User'); unset($leads, $campaignLeads, $leadLog); $currentCount = $max ? $eventCount : $leadProcessedCount; if ($output && $currentCount < $maxCount) { $progress->setCurrent($currentCount); } // Free some memory gc_collect_cycles(); } if ($output) { $progress->finish(); $output->writeln(''); } } return $processedCount; }
/** * Rebuild lead lists * * @param LeadList $entity * @param int $limit * @param bool $maxLeads * @param OutputInterface $output * * @return int */ public function rebuildListLeads(LeadList $entity, $limit = 1000, $maxLeads = false, OutputInterface $output = null) { defined('MAUTIC_REBUILDING_LEAD_LISTS') or define('MAUTIC_REBUILDING_LEAD_LISTS', 1); $id = $entity->getId(); $list = array('id' => $id, 'filters' => $entity->getFilters()); $dtHelper = $this->factory->getDate(); $batchLimiters = array('dateTime' => $dtHelper->toUtcString()); $localDateTime = $dtHelper->getLocalDateTime(); // Get a count of leads to add $newLeadsCount = $this->getLeadsByList($list, true, array('countOnly' => true, 'newOnly' => true, 'dynamic' => true, 'includeManual' => false, 'batchLimiters' => $batchLimiters)); // Ensure the same list is used each batch $batchLimiters['maxId'] = (int) $newLeadsCount[$id]['maxId']; // Number of total leads to process $leadCount = (int) $newLeadsCount[$id]['count']; if ($output) { $output->writeln($this->translator->trans('mautic.lead.list.rebuild.to_be_added', array('%leads%' => $leadCount, '%batch%' => $limit))); } // Handle by batches $start = $lastRoundPercentage = $leadsProcessed = 0; // Try to save some memory gc_enable(); if ($leadCount) { $maxCount = $maxLeads ? $maxLeads : $leadCount; if ($output) { $progress = new ProgressBar($output, $maxCount); $progress->start(); } // Add leads while ($start < $leadCount) { // Keep CPU down for large lists; sleep per $limit batch sleep($this->batchSleepTime); $newLeadList = $this->getLeadsByList($list, true, array('dynamic' => true, 'newOnly' => true, 'includeManual' => false, 'limit' => $limit, 'batchLimiters' => $batchLimiters)); if (empty($newLeadList[$id])) { // Somehow ran out of leads so break out break; } foreach ($newLeadList[$id] as $l) { $this->addLead($l, $entity, false, true, -1, $localDateTime); unset($l); $leadsProcessed++; if ($output && $leadsProcessed < $maxCount) { $progress->setCurrent($leadsProcessed); } if ($maxLeads && $leadsProcessed >= $maxLeads) { break; } } $start += $limit; // Dispatch batch event if ($this->dispatcher->hasListeners(LeadEvents::LEAD_LIST_BATCH_CHANGE)) { $event = new ListChangeEvent($newLeadList[$id], $entity, true); $this->dispatcher->dispatch(LeadEvents::LEAD_LIST_BATCH_CHANGE, $event); unset($event); } unset($newLeadList); // Free some memory gc_collect_cycles(); if ($maxLeads && $leadsProcessed >= $maxLeads) { if ($output) { $progress->finish(); $output->writeln(''); } return $leadsProcessed; } } if ($output) { $progress->finish(); $output->writeln(''); } } $fullList = $this->getLeadsByList($list, true, array('dynamic' => true, 'existingOnly' => true, 'includeManual' => false, 'batchLimiters' => $batchLimiters)); // Get a count of leads to be removed $removeLeadCount = $this->getLeadsByList($list, true, array('countOnly' => true, 'includeManual' => false, 'filterOutIds' => $fullList[$id], 'batchLimiters' => $batchLimiters)); // Restart batching $start = $lastRoundPercentage = 0; $leadCount = $removeLeadCount[$id]['count']; if ($output) { $output->writeln($this->translator->trans('mautic.lead.list.rebuild.to_be_removed', array('%leads%' => $leadCount, '%batch%' => $limit))); } if ($leadCount) { $maxCount = $maxLeads ? $maxLeads : $leadCount; if ($output) { $progress = new ProgressBar($output, $maxCount); $progress->start(); } // Remove leads while ($start < $leadCount) { // Keep CPU down for large lists; sleep per $limit batch sleep($this->batchSleepTime); $removeLeadList = $this->getLeadsByList($list, true, array('limit' => $limit, 'filterOutIds' => $fullList[$id], 'batchLimiters' => $batchLimiters)); if (empty($removeLeadList[$id])) { // Somehow ran out of leads so break out break; } foreach ($removeLeadList[$id] as $l) { $this->removeLead($l, $entity, false, true, true); $leadsProcessed++; if ($output && $leadsProcessed < $maxCount) { $progress->setCurrent($leadsProcessed); } if ($maxLeads && $leadsProcessed >= $maxLeads) { break; } } // Dispatch batch event if ($this->dispatcher->hasListeners(LeadEvents::LEAD_LIST_BATCH_CHANGE)) { $event = new ListChangeEvent($removeLeadList[$id], $entity, false); $this->dispatcher->dispatch(LeadEvents::LEAD_LIST_BATCH_CHANGE, $event); unset($event); } $start += $limit; unset($removeLeadList); // Free some memory gc_collect_cycles(); if ($maxLeads && $leadsProcessed >= $maxLeads) { if ($output) { $progress->finish(); $output->writeln(''); } return $leadsProcessed; } } if ($output) { $progress->finish(); $output->writeln(''); } } return $leadsProcessed; }
/** * @param Driver $sourceDriver The source driver. * @param callable $output A function to call with every SQL statement. * @param string $message The message going next to the progress bar. * * @return void */ private function doDumpDatabase(Driver $sourceDriver, callable $output, string $message) { $dumper = new Dumper($sourceDriver); $progress = new ProgressBar($this->output); $progress->setMessage('Counting objects'); $progress->setFormat('%message% [%bar%] %current%'); $progress->start(); $objectCount = 0; $dumper->countObjects(function ($count) use($progress, &$objectCount) { $objectCount += $count; $progress->setCurrent($objectCount); }); $progress->finish(); $this->output->writeln(''); $progress = new ProgressBar($this->output, $objectCount); $progress->setMessage($message); $progress->setFormat('%message% [%bar%] %current%/%max% %percent:3s%%'); $progress->start(); $dumper->dumpDatabase(function ($query) use($output, $progress) { $output($query); $progress->advance(); }); $progress->finish(); $this->output->writeln(''); }
/** * @param Campaign $campaign * @param int $limit * @param bool $maxLeads * @param OutputInterface $output * * @return int */ public function rebuildCampaignLeads(Campaign $campaign, $limit = 1000, $maxLeads = false, OutputInterface $output = null) { defined('MAUTIC_REBUILDING_CAMPAIGNS') or define('MAUTIC_REBUILDING_CAMPAIGNS', 1); $repo = $this->getRepository(); // Get a list of leads for all lists associated with the campaign $lists = $this->getCampaignListIds($campaign->getId()); if (empty($lists)) { if ($output) { $output->writeln($this->translator->trans('mautic.campaign.rebuild.no_lists')); } return 0; } $batchLimiters = array('dateTime' => $this->factory->getDate()->toUtcString()); // Get a count of new leads $newLeadsCount = $repo->getCampaignLeadsFromLists($campaign->getId(), $lists, array('newOnly' => true, 'countOnly' => true, 'batchLimiters' => $batchLimiters)); // Ensure the same list is used each batch $batchLimiters['maxId'] = (int) $newLeadsCount['maxId']; // Number of total leads to process $leadCount = (int) $newLeadsCount['count']; if ($output) { $output->writeln($this->translator->trans('mautic.campaign.rebuild.to_be_added', array('%leads%' => $leadCount, '%batch%' => $limit))); } // Handle by batches $start = $leadsProcessed = 0; // Try to save some memory gc_enable(); if ($leadCount) { $maxCount = $maxLeads ? $maxLeads : $leadCount; if ($output) { $progress = new ProgressBar($output, $maxCount); $progress->start(); } // Add leads while ($start < $leadCount) { // Keep CPU down for large lists; sleep per $limit batch sleep($this->batchSleepTime); // Get a count of new leads $newLeadList = $repo->getCampaignLeadsFromLists($campaign->getId(), $lists, array('newOnly' => true, 'limit' => $limit, 'batchLimiters' => $batchLimiters)); $start += $limit; foreach ($newLeadList as $l) { $this->addLeads($campaign, array($l), false, true, -1); $leadsProcessed++; if ($output && $leadsProcessed < $maxCount) { $progress->setCurrent($leadsProcessed); } unset($l); if ($maxLeads && $leadsProcessed >= $maxLeads) { // done for this round, bye bye if ($output) { $progress->finish(); } return $leadsProcessed; } } unset($newLeadList); // Free some memory gc_collect_cycles(); } if ($output) { $progress->finish(); $output->writeln(''); } } // Get a count of leads to be removed $removeLeadCount = $repo->getCampaignOrphanLeads($campaign->getId(), $lists, array('notInLists' => true, 'countOnly' => true, 'batchLimiters' => $batchLimiters)); // Restart batching $start = $lastRoundPercentage = 0; $leadCount = $removeLeadCount['count']; if ($output) { $output->writeln($this->translator->trans('mautic.lead.list.rebuild.to_be_removed', array('%leads%' => $leadCount, '%batch%' => $limit))); } if ($leadCount) { $maxCount = $maxLeads ? $maxLeads : $leadCount; if ($output) { $progress = new ProgressBar($output, $maxCount); $progress->start(); } // Remove leads while ($start < $leadCount) { // Keep CPU down for large lists; sleep per $limit batch sleep($this->batchSleepTime); $removeLeadList = $repo->getCampaignOrphanLeads($campaign->getId(), $lists, array('limit' => $limit, 'batchLimiters' => $batchLimiters)); foreach ($removeLeadList as $l) { $this->removeLeads($campaign, array($l), false, true, true); $leadsProcessed++; if ($output && $leadsProcessed < $maxCount) { $progress->setCurrent($leadsProcessed); } if ($maxLeads && $leadsProcessed >= $maxLeads) { // done for this round, bye bye $progress->finish(); return $leadsProcessed; } } $start += $limit; unset($removeLeadList); // Free some memory gc_collect_cycles(); } if ($output) { $progress->finish(); } } return $leadsProcessed; }
public function testClear() { $bar = new ProgressBar($output = $this->getOutputStream(), 50); $bar->start(); $bar->setCurrent(25); $bar->clear(); rewind($output->getStream()); $this->assertEquals($this->generateOutput(' 0/50 [>---------------------------] 0%') . $this->generateOutput(' 25/50 [==============>-------------] 50%') . $this->generateOutput(' '), stream_get_contents($output->getStream())); }