Advertisement
rfv123

TimeSlotList (SO Question 40505794)

Nov 21st, 2016
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 8.20 KB | None | 0 0
  1. /* ---------------------------------------------------------------------------------
  2.  * Class that does all the 'houskeeping'
  3.  */
  4.  
  5. /*
  6.  *  Requirements:
  7.  *  
  8.  *    Input:  
  9.  *        1) A list of 'date ranges' that all the timeslots must fit in one of the ranges
  10.  *        2) A list of 'required' dates - these are fixed and will always be in the final list
  11.  *        3) A list of 'candidate' dates
  12.  *        
  13.  *    Output:
  14.  *      1) A 'final list of 'none-overlapping' dates
  15.  *
  16.  *         Where:
  17.  *           a) The first 'required' data is a 'start' date
  18.  *              i.e. All candidate dates must be on or after this date.
  19.  *
  20.  *           b) No date ranges must overlap.  
  21.  */  
  22.  
  23. class TimeSlotList
  24. {
  25.     /**
  26.     * A list of all the dates that:
  27.     *   1) After the 'required' start date
  28.     *   2) Do not overlap with any 'required' date
  29.     *
  30.     * @var array $candidates
  31.     */
  32.     private $candidates = array();    
  33.  
  34.     /**
  35.     * Any date record we didn't use.
  36.     *
  37.     * @var array $unused
  38.     */
  39.     private $unused = array();
  40.        
  41.     /**
  42.     * List of dates that must be included in the 'final' list.
  43.     * The earliest date is assumed to be a start date and everything must be later.
  44.     *
  45.     * @var array $required
  46.     */    
  47.     private $required = array();
  48.  
  49.     /**
  50.     * List of dates ranges that candidate dates must be between
  51.     *
  52.     * @var array $ranges
  53.     */    
  54.     private $ranges = array();
  55.  
  56.     /**
  57.     * Ordered list of `none overlapping' dates from the Required and candidates
  58.     *
  59.     * @var array $final
  60.     */    
  61.     private $final = array();
  62.    
  63.    
  64.     /**
  65.     * Current List of paths with associated score
  66.     *
  67.     * The top one is the current best one
  68.     */
  69.     private $currentPath = array();
  70.    
  71.     /**
  72.     * These are the date lists.
  73.     * They will be converted, sorted and filters as required.
  74.     *
  75.     * @param array $requiredDates
  76.     * @param array $candidateDates
  77.     * @return void
  78.     */
  79.     public function __construct(array $requiredDates = array(),
  80.                                 array $candidateDates = array(),
  81.                                 array $ranges = array())
  82.     {
  83.         if (!empty($ranges)) {
  84.             $this->setRanges($ranges);
  85.         }
  86.  
  87.         if (!empty($requiredDates)) {
  88.             $this->setRequired($requiredDates);
  89.         }
  90.        
  91.         if (!empty($candidateDates)) {
  92.             $this->setCandidates($candidateDates);
  93.         }
  94.     }
  95.  
  96.     /**
  97.     * Process each range in turn and add the timeslotes to the the final list
  98.     *
  99.     *   Known conditions:
  100.     *     o  Both lists are in start date order
  101.     *     o  No candidates overlap with any Required date
  102.     *     o  The candidates may overlap with each other - Hmm... need to check...
  103.     *
  104.     *    
  105.     * @return array
  106.     */
  107.     public function generateList()
  108.     {
  109.         $bestList = array();
  110.         foreach ($this->ranges as $range) {
  111.  
  112.             $availableList = $this->getAllDatesWithinRange($range);
  113.             $curRange = new TimeSlotRange($range,
  114.                                           $availableList['required'],
  115.                                           $availableList['candidates']);      
  116.      
  117.             $whenList = $curRange->generateList();            
  118.                      
  119.             $bestList = array_merge($bestList, $whenList);                
  120.         }
  121.            
  122.  
  123.         $this->final = $bestList;
  124.              
  125.         return $bestList;      
  126.     }
  127.  
  128.     /**
  129.     * Convert ranges to date range and sort them
  130.     *
  131.     * @param array $requiredDates
  132.     */
  133.     public function setRanges(array $dates)
  134.     {
  135.         $this->ranges = $dates;
  136.     }
  137.  
  138.     /**
  139.     * Convert Required dates to date range and sort them
  140.     *
  141.     * @param array $requiredDates
  142.     */
  143.     public function setRequired(array $dates)
  144.     {
  145.         foreach ($dates as $when) {
  146.             $when->setIsRequired();
  147.             $this->required[] = $when;
  148.         }
  149.     }
  150.  
  151.     /**
  152.     * setter for candidates - will convert to date range
  153.     *
  154.     * @param array $candidateDates
  155.     *
  156.     * @return void;
  157.     */
  158.     public function setCandidates(array $dates)
  159.     {
  160.         foreach ($dates as $when) {
  161.             if ($when->isRequired()) {
  162.                 $this->required[] = $when;
  163.  
  164.             } else {
  165.                 $this->candidates[] = $when;
  166.             }
  167.            
  168.         }
  169.     }
  170.  
  171.     /**
  172.     * Extract all timeslots within the range as two arrays
  173.     *
  174.     * @param ITimeslot $range
  175.     *
  176.     * @return array  Containing an array ('required') and array('candidates') of all the timeslots
  177.     *                
  178.     */
  179.     public function getAllDatesWithinRange(ITimeSlot $range)
  180.     {
  181.         $whenList = array('required' => array(), 'candidates' => array());
  182.        
  183.         foreach ($this->required as $when) {
  184.             if ($range->isInside($when)) {
  185.                 $whenList['required'][] = $when;
  186.             }
  187.         }    
  188.  
  189.         foreach ($this->candidates as $when) {
  190.             if ($range->isInside($when)) {
  191.                 if (! TimeSlotRange::isOverlapAny($when, $whenList['required'])) {
  192.                     $whenList['candidates'][] = $when;
  193.                 }
  194.             }
  195.         }
  196.        
  197.         return $whenList;  
  198.     }
  199.  
  200.  
  201.  
  202.     public function displayWhenList(array $whenList, $title = 'whenList', $sort = true)
  203.     {
  204.         if ($sort) {
  205.             usort($whenList,
  206.                   function ($when1, $when2) {
  207.                     return $when1['startTime'] - $when2['startTime'];
  208.                   });
  209.         }        
  210.        
  211.         echo PHP_EOL, PHP_EOL, $title;
  212.        
  213.         foreach ($whenList as $when) {
  214.             $this->displayWhen($when);
  215.         }
  216.         echo '<pre>';
  217.     }
  218.    
  219.     /**
  220.     * Show a date formatted for debugging purposes
  221.     *
  222.     * @param array $when
  223.     * @return void
  224.     */
  225.     public function displayWhen(ITimeSlot $when)
  226.     {
  227.         echo PHP_EOL, 'date: ',   $when->getStartDate()->format('Y-m-d H:i:s'),
  228.                       ' len: ',   sprintf('%4d', $when['duration']),
  229.                       ' end: ',   $when->getEndDate()->format('Y-m-d H:i:s'),
  230.                       ' start: ',  $when['startTime'],
  231.                       ' end: ',    $when['endTime'],
  232.                       ' Required? ', ($when['isRequired'] ? 'true' : 'false'),
  233.                      
  234.                       (!empty($when['fail'])
  235.                          ? ' fail: ' . $when['fail'] : '');
  236.     }
  237.  
  238.  
  239.     public function sortEndTime(&$timeSlots)
  240.     {        
  241.         // sort by end times - makes it easy to reason about
  242.         usort($timeSlots,
  243.               function ($when1, $when2) {
  244.                     $order = $when1['endTime'] - $when2['endTime'];
  245.                     if ($order === 0) { // want the largest first
  246.                         return $when2['duration'] - $when1['duration'];
  247.                     }
  248.                     return $order;
  249.               });
  250.              
  251.         return;      
  252.     }
  253.  
  254.     public function sortStartTime(&$timeSlots)
  255.     {        
  256.         // sort by end times - makes it easy to reason about
  257.         usort($timeSlots,
  258.               function ($when1, $when2) {
  259.                     $order = $when1['startTime'] - $when2['startTime'];
  260.                     if ($order === 0) { // want the largest first
  261.                         return $when2['duration'] - $when1['duration'];
  262.                     }
  263.                     return $order;
  264.               });
  265.              
  266.         return;      
  267.     }
  268.  
  269.     /*
  270.      *  `Getters` so you can see what happened
  271.      */
  272.     public function getRequired()   { return $this->required; }
  273.     public function getRanges()     { return $this->ranges; }
  274.     public function getUnused()     { return $this->unused; }
  275.     public function getCandidates() { return $this->candidates; }
  276.     public function getFinal()      { return $this->final; }
  277.    
  278.     /**
  279.     * properties - for those of us that like them
  280.     */
  281.     public function __get($name)
  282.     {
  283.         if (property_exists($this, $name)) {
  284.             return $this->$name;
  285.         }        
  286.         return null;
  287.     }
  288. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement