Exemplo n.º 1
0
 /**
  * @param Campaign $campaign
  * @param          $lead
  */
 public function removeScheduledEvents($campaign, $lead)
 {
     $this->em->getRepository('MauticCampaignBundle:LeadEventLog')->removeScheduledEvents($campaign->getId(), $lead->getId());
 }
Exemplo n.º 2
0
 /**
  * Find and trigger the negative events, i.e. the events with a no decision path
  *
  * @param Campaign        $campaign
  * @param int             $totalEventCount
  * @param int             $limit
  * @param bool            $max
  * @param OutputInterface $output
  * @param bool|false      $returnCounts If true, returns array of counters
  *
  * @return int
  */
 public function triggerNegativeEvents($campaign, &$totalEventCount = 0, $limit = 100, $max = false, OutputInterface $output = null, $returnCounts = false)
 {
     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();
     $actionEvents = array();
     foreach ($campaignEvents as $id => $e) {
         if (!empty($e['decisionPath']) && !empty($e['parent_id']) && $campaignEvents[$e['parent_id']]['eventType'] != 'condition') {
             if ($e['decisionPath'] == 'no') {
                 $nonActionEvents[$e['parent_id']][$id] = $e;
             } elseif ($e['decisionPath'] == 'yes') {
                 $actionEvents[$e['parent_id']][] = $id;
             }
         }
     }
     $logger->debug('CAMPAIGN: Processing the children of the following events: ' . implode(', ', array_keys($nonActionEvents)));
     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 = $leadProcessedCount = $lastRoundPercentage = $executedEventCount = $evaluatedEventCount = $negativeExecutedCount = $negativeEvaluatedCount = 0;
     $nonActionEventCount = $leadCount * count($nonActionEvents);
     $eventSettings = $campaignModel->getEvents();
     $maxCount = $max ? $max : $nonActionEventCount;
     // 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;
         $batchDebugCounter = 1;
         while ($start <= $leadCount) {
             $logger->debug('CAMPAIGN: Batch #' . $batchDebugCounter);
             // Get batched campaign ids
             $campaignLeads = $campaignRepo->getCampaignLeads($campaignId, $start, $limit, array('cl.lead_id, cl.date_added'));
             $campaignLeadIds = array();
             $campaignLeadDates = array();
             foreach ($campaignLeads as $r) {
                 $campaignLeadIds[] = $r['lead_id'];
                 $campaignLeadDates[$r['lead_id']] = $r['date_added'];
             }
             unset($campaignLeads);
             $logger->debug('CAMPAIGN: Processing the following contacts: ' . implode(', ', $campaignLeadIds));
             foreach ($nonActionEvents as $parentId => $events) {
                 // Just a check to ensure this is an appropriate action
                 if ($campaignEvents[$parentId]['eventType'] == 'action') {
                     $logger->debug('CAMPAIGN: Parent event ID #' . $parentId . ' is an action.');
                     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
                 if ($grandParentId) {
                     $logger->debug('CAMPAIGN: Checking for contacts based on grand parent execution.');
                     $leadLog = $repo->getEventLog($campaignId, $campaignLeadIds, array($grandParentId), array_keys($events), true);
                     $applicableLeads = array_keys($leadLog);
                 } else {
                     $logger->debug('CAMPAIGN: Checking for contacts based on exclusion due to being at root level');
                     // The event has no grandparent (likely because the decision is first in the campaign) so find leads that HAVE
                     // already executed the events in the root level and exclude them
                     $havingEvents = isset($actionEvents[$parentId]) ? array_merge($actionEvents[$parentId], array_keys($events)) : array_keys($events);
                     $leadLog = $repo->getEventLog($campaignId, $campaignLeadIds, $havingEvents);
                     $unapplicableLeads = array_keys($leadLog);
                     // Only use leads that are not applicable
                     $applicableLeads = array_diff($campaignLeadIds, $unapplicableLeads);
                     unset($unapplicableLeads);
                 }
                 if (empty($applicableLeads)) {
                     $logger->debug('CAMPAIGN: No events are applicable');
                     continue;
                 }
                 $logger->debug('CAMPAIGN: These contacts have have not gone down the positive path: ' . implode(', ', $applicableLeads));
                 // 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
                     $logger->debug('CAMPAIGN: No contact entities found.');
                     continue;
                 }
                 // Loop over the non-actions and determine if it has been processed for this lead
                 $leadDebugCounter = 1;
                 /** @var \Mautic\LeadBundle\Entity\Lead $lead */
                 foreach ($leads as $lead) {
                     $negativeEvaluatedCount++;
                     // Set lead for listeners
                     $leadModel->setSystemCurrentLead($lead);
                     $logger->debug('CAMPAIGN: contact ID #' . $lead->getId() . '; #' . $leadDebugCounter . ' in batch #' . $batchDebugCounter);
                     // Prevent path if lead has already gone down this path
                     if (!isset($leadLog[$lead->getId()]) || !array_key_exists($parentId, $leadLog[$lead->getId()])) {
                         // Get date to compare against
                         $utcDateString = $grandParentId ? $leadLog[$lead->getId()][$grandParentId]['date_triggered'] : $campaignLeadDates[$lead->getId()];
                         // 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
                                 $this->batchSleep();
                                 $sleepBatchCount = 0;
                             } else {
                                 $sleepBatchCount++;
                             }
                             if (isset($leadLog[$lead->getId()]) && array_key_exists($id, $leadLog[$lead->getId()])) {
                                 $logger->debug('CAMPAIGN: Event (ID #' . $id . ') has already been executed');
                                 unset($e);
                                 continue;
                             }
                             if (!isset($eventSettings[$e['eventType']][$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) {
                             $negativeEvaluatedCount += count($nonActionEvents);
                             // Timing is not appropriate so move on to next lead
                             unset($eventTiming);
                             continue;
                         }
                         if ($max && $totalEventCount + count($nonActionEvents) >= $max) {
                             // Hit the max or will hit the max while mid-process for the lead
                             if ($output) {
                                 $progress->finish();
                                 $output->writeln('');
                             }
                             $counts = array('events' => $nonActionEventCount, 'evaluated' => $negativeEvaluatedCount, 'executed' => $negativeExecutedCount, 'totalEvaluated' => $evaluatedEventCount, 'totalExecuted' => $executedEventCount);
                             $logger->debug('CAMPAIGN: Counts - ' . var_export($counts, true));
                             return $returnCounts ? $counts : $executedEventCount;
                         }
                         $decisionLogged = false;
                         // Execute or schedule events
                         $logger->debug('CAMPAIGN: Processing the following events for contact ID# ' . $lead->getId() . ': ' . implode(', ', array_keys($eventTiming)));
                         foreach ($eventTiming as $id => $eventTriggerDate) {
                             // Set event
                             $event = $events[$id];
                             $event['campaign'] = array('id' => $campaignId, 'name' => $campaignName);
                             // Set lead in case this is triggered by the system
                             $leadModel->setSystemCurrentLead($lead);
                             if ($this->executeEvent($event, $campaign, $lead, $eventSettings, false, null, $eventTriggerDate, false, $evaluatedEventCount, $executedEventCount, $totalEventCount)) {
                                 if (!$decisionLogged) {
                                     // Log the decision
                                     $log = $this->getLogEntity($parentId, $campaign, $lead, null, true);
                                     $log->setDateTriggered(new \DateTime());
                                     $log->setNonActionPathTaken(true);
                                     $repo->saveEntity($log);
                                     $this->em->detach($log);
                                     unset($log);
                                     $decisionLogged = true;
                                 }
                                 $negativeExecutedCount++;
                             }
                             unset($utcDateString, $grandParentDate);
                         }
                     } else {
                         $logger->debug('CAMPAIGN: Decision has already been executed.');
                     }
                     $currentCount = $max ? $totalEventCount : $negativeEvaluatedCount;
                     if ($output && $currentCount < $maxCount) {
                         $progress->setProgress($currentCount);
                     }
                     $leadDebugCounter++;
                     // Save RAM
                     $this->em->detach($lead);
                     unset($lead);
                 }
             }
             // Next batch
             $start += $limit;
             // Save RAM
             $this->em->clear('Mautic\\LeadBundle\\Entity\\Lead');
             $this->em->clear('Mautic\\UserBundle\\Entity\\User');
             unset($leads, $campaignLeadIds, $leadLog);
             $currentCount = $max ? $totalEventCount : $negativeEvaluatedCount;
             if ($output && $currentCount < $maxCount) {
                 $progress->setProgress($currentCount);
             }
             // Free some memory
             gc_collect_cycles();
             $batchDebugCounter++;
         }
         if ($output) {
             $progress->finish();
             $output->writeln('');
         }
     }
     $counts = array('events' => $nonActionEventCount, 'evaluated' => $negativeEvaluatedCount, 'executed' => $negativeExecutedCount, 'totalEvaluated' => $evaluatedEventCount, 'totalExecuted' => $executedEventCount);
     $logger->debug('CAMPAIGN: Counts - ' . var_export($counts, true));
     return $returnCounts ? $counts : $executedEventCount;
 }
 /**
  * {@inheritDoc}
  */
 public function __toString()
 {
     $this->__initializer__ && $this->__initializer__->__invoke($this, '__toString', array());
     return parent::__toString();
 }