842 lines
29 KiB
PHP
842 lines
29 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use Doctrine\ORM\Query;
|
|
use Doctrine\ORM\QueryBuilder;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Process\Process;
|
|
use Symfony\Component\Process\Exception\ProcessFailedException;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
|
|
|
|
use Catalyst\MenuBundle\Annotation\Menu;
|
|
|
|
use CrEOF\Spatial\PHP\Types\Geometry\Point;
|
|
use DateTime;
|
|
use DateInterval;
|
|
|
|
use App\Entity\JobOrder;
|
|
use App\Entity\Hub;
|
|
use App\Entity\Battery;
|
|
|
|
use App\Ramcar\ShiftSchedule;
|
|
|
|
class AnalyticsController extends Controller
|
|
{
|
|
protected $weekdays = [
|
|
'Monday',
|
|
'Tuesday',
|
|
'Wednesday',
|
|
'Thursday',
|
|
'Friday',
|
|
'Saturday',
|
|
'Sunday',
|
|
];
|
|
|
|
protected $day_shifts = [
|
|
['Mon - Sat', 0, 1, 2, 3, 4, 5], // Mon - Sat
|
|
['Tue - Sun', 1, 2, 3, 4, 5, 6], // Tue - Sun
|
|
['Wed - Mon', 2, 3, 4, 5, 6, 0], // Wed - Mon
|
|
['Thu - Tue', 3, 4, 5, 6, 0, 1], // Thu - Tue
|
|
['Fri - Wed', 4, 5, 6, 0, 1, 2], // Fri - Wed
|
|
['Sat - Thu', 5, 6, 0, 1, 2, 3], // Sat - Thu
|
|
['Sun - Fri', 6, 0, 1, 2, 3, 4], // Sun - Fri
|
|
];
|
|
|
|
//protected $hour_shifts = [
|
|
/*
|
|
['00:00 - 09:00', 0, 1, 2, 3, 4, 5, 6, 7, 8],
|
|
['01:00 - 10:00', 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
['02:00 - 11:00', 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
|
['03:00 - 12:00', 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
|
['04:00 - 13:00', 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
|
['05:00 - 14:00', 5, 6, 7, 8, 9, 10, 11, 12, 13],
|
|
['06:00 - 15:00', 6, 7, 8, 9, 10, 11, 12, 13, 14],
|
|
*/
|
|
//['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
//['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
/*
|
|
['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
|
['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
|
['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21],
|
|
['14:00 - 23:00', 14, 15, 16, 17, 18, 19, 20, 21, 22],
|
|
['15:00 - 00:00', 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
|
['16:00 - 01:00', 16, 17, 18, 19, 20, 21, 22, 23, 0],
|
|
['17:00 - 02:00', 17, 18, 19, 20, 21, 22, 23, 0, 1],
|
|
['18:00 - 03:00', 18, 19, 20, 21, 22, 23, 0, 1, 2],
|
|
['19:00 - 04:00', 19, 20, 21, 22, 23, 0, 1, 2, 3],
|
|
['20:00 - 05:00', 20, 21, 22, 23, 0, 1, 2, 3, 4],
|
|
['21:00 - 06:00', 21, 22, 23, 0, 1, 2, 3, 4, 5],
|
|
['22:00 - 07:00', 22, 23, 0, 1, 2, 3, 4, 5, 6],
|
|
['23:00 - 08:00', 23, 0, 1, 2, 3, 4, 5, 6, 7],
|
|
*/
|
|
//];
|
|
|
|
/**
|
|
* @Menu(selected="analytics_forecast")
|
|
* @IsGranted("analytics.forecast")
|
|
*/
|
|
public function forecastForm(EntityManagerInterface $em)
|
|
{
|
|
$hub_ids = [ 6, 4, 36, 7, 8, 126, 127, 18, 12, 9, 60, 10, 21, 135 ];
|
|
|
|
$all_hubs = $em->getRepository(Hub::class)->findAll();
|
|
|
|
$hub_list = [];
|
|
foreach ($all_hubs as $hub)
|
|
{
|
|
$hub_list[$hub->getID()] = $hub->getName() . ' - ' . $hub->getBranch();
|
|
}
|
|
|
|
$params = [
|
|
'hub_list' => $hub_list,
|
|
'default_hubs' => $hub_ids,
|
|
'shift_schedules' => ShiftSchedule::getCollection(),
|
|
];
|
|
|
|
return $this->render('analytics/forecast_form.html.twig', $params);
|
|
}
|
|
|
|
|
|
/**
|
|
* @Menu(selected="analytics_forecast")
|
|
* @IsGranted("analytics.forecast")
|
|
*/
|
|
public function forecastSubmit(EntityManagerInterface $em, Request $req)
|
|
{
|
|
$today = new DateTime();
|
|
|
|
$hub_list = $req->request->get('hub_ids', []);
|
|
$distances = $req->request->get('distances', []);
|
|
|
|
$time_start = $req->request->get('time_from');
|
|
$time_end = $req->request->get('time_to');
|
|
|
|
$date_from = DateTime::createFromFormat('d M Y', $req->request->get('date_from'));
|
|
$date_to = DateTime::createFromFormat('d M Y', $req->request->get('date_to'));
|
|
|
|
$shift = $req->request->get('shift_schedule');
|
|
|
|
// TODO: populate the hour_shift array, depending on the shift selected
|
|
$hour_shifts = $this->populateHourShift($shift);
|
|
|
|
// error_log(print_r($hour_shifts, true));
|
|
// error_log(print_r($hub_list, true));
|
|
|
|
// $hub_list = [ 6, 4, 36, 7, 8, 126, 127, 18, 12, 9, 60, 10, 21, 135 ];
|
|
|
|
$hub_data = [];
|
|
$hub_coverage = [];
|
|
$overlaps = [];
|
|
foreach ($hub_list as $key => $hub_id)
|
|
{
|
|
$dist = $distances[$key];
|
|
$hub = $em->getRepository(Hub::class)->find($hub_id);
|
|
$coords = $hub->getCoordinates();
|
|
|
|
$hub_data[$hub_id] = $this->generateHubData($em, $hub, $dist, $date_from, $date_to, $time_start, $time_end, $overlaps);
|
|
|
|
$hub_coverage[] = [
|
|
'longitude' => $coords->getLongitude(),
|
|
'latitude' => $coords->getLatitude(),
|
|
'distance' => $dist,
|
|
];
|
|
}
|
|
|
|
// init aggregate information
|
|
$agg_data = [];
|
|
for ($i = 0; $i < 7; $i++)
|
|
{
|
|
$agg_data[] = [
|
|
'label' => $this->weekdays[$i],
|
|
'total_jos' => 0,
|
|
'total_riders' => 0,
|
|
];
|
|
}
|
|
|
|
// reprocess weekday data to account for overlap
|
|
foreach ($hub_data as $hub_id => $one_hub)
|
|
{
|
|
$c_weekday = $one_hub['c_weekday'];
|
|
|
|
$chart_weekday = $this->generateWeekdayData($c_weekday, $today, $overlaps);
|
|
$chart_all_weekdays = $this->generateAllWeekData($c_weekday, $overlaps);
|
|
|
|
// figure out the rider schedules based on the max hour values
|
|
$scheduler_data = $chart_all_weekdays['scheduler_data'];
|
|
// error_log(print_r($scheduler_data, true));
|
|
unset($chart_all_weekdays['scheduler_data']);
|
|
|
|
$total_jos = 0;
|
|
|
|
// error_log(print_r($scheduler_data, true));
|
|
|
|
// run scheduler
|
|
$sched_res = $this->runScheduler($scheduler_data, $hour_shifts, $shift);
|
|
|
|
// tally total JOs for the month
|
|
foreach ($scheduler_data as $sday_data)
|
|
foreach ($sday_data as $shour_data)
|
|
$total_jos += $shour_data;
|
|
/*
|
|
else
|
|
{
|
|
$sched_res = [
|
|
'weekday_shifts' => $this->initDayData(),
|
|
'total_riders' => 0,
|
|
];
|
|
}
|
|
*/
|
|
|
|
|
|
// error_log(print_r($chart_all_weekdays, true));
|
|
// error_log(print_r($sched_res, true));
|
|
|
|
// agggregate weekday data
|
|
$i = 0;
|
|
foreach ($sched_res['weekday_shifts'] as $day_data)
|
|
{
|
|
$agg_data[$i]['total_jos'] += $day_data['total_jos'];
|
|
$agg_data[$i]['total_riders'] += $day_data['total_riders'];
|
|
$i++;
|
|
}
|
|
|
|
|
|
$hub_data[$hub_id]['data_weekday'] = $chart_weekday;
|
|
$hub_data[$hub_id]['data_all_weekdays'] = $chart_all_weekdays;
|
|
$hub_data[$hub_id]['data_shift'] = $sched_res['weekday_shifts'];
|
|
$hub_data[$hub_id]['shift_summary'] = $sched_res['shifts'];
|
|
$hub_data[$hub_id]['total_jos'] = $total_jos;
|
|
$hub_data[$hub_id]['total_riders'] = $sched_res['total_riders'];
|
|
|
|
unset($hub_data[$hub_id]['c_weekday']);
|
|
}
|
|
|
|
// error_log(print_r($overlaps, true));
|
|
|
|
// get job orders not covered by hubs
|
|
$not_covered = $this->generateNotCoveredData($em, $hub_coverage, $today);
|
|
|
|
// error_log(print_r($agg_data, true));
|
|
|
|
$params = [
|
|
'date' => $today,
|
|
'hub_list' => $hub_data,
|
|
'hub_coverage' => $hub_coverage,
|
|
'not_covered' => $not_covered,
|
|
'agg_data' => $agg_data,
|
|
];
|
|
|
|
return $this->render('analytics/forecast_submit.html.twig', $params);
|
|
}
|
|
|
|
protected function initDayData()
|
|
{
|
|
$day_data = [];
|
|
// each weekday
|
|
for ($i = 0; $i < 7; $i++)
|
|
{
|
|
$day_data[$i] = [
|
|
'weekday' => $this->weekdays[$i],
|
|
'total_jos' => 0,
|
|
'total_riders' => 0,
|
|
'shifts' => [],
|
|
];
|
|
}
|
|
|
|
return $day_data;
|
|
}
|
|
|
|
protected function runScheduler($scheduler_data, $hour_shifts, $shift)
|
|
{
|
|
// run python script to solve scheduling for riders
|
|
|
|
$python_cmd = '/usr/bin/python3';
|
|
$sched_script = __DIR__ . '/../../utils/schedule_solver/solver.py';
|
|
|
|
// go through the days
|
|
$args = [
|
|
$python_cmd,
|
|
$sched_script,
|
|
];
|
|
foreach ($scheduler_data as $weekday_data)
|
|
$args[] = implode('-', $weekday_data);
|
|
|
|
// add shift
|
|
$args[] = $shift;
|
|
|
|
// error_log(print_r($args, true));
|
|
|
|
// error_log('running...' . $sched_script);
|
|
|
|
$proc = new Process($args);
|
|
$proc->run();
|
|
|
|
error_log('getErrorOutput() ' . $proc->getErrorOutput());
|
|
|
|
if (!$proc->isSuccessful())
|
|
error_log('SCHEDULER DID NOT RUN PROPERLY');
|
|
|
|
$res = $proc->getOutput();
|
|
// error_log($res);
|
|
// returns lines with format: <day shift>-<hour shift>-<number of riders>
|
|
|
|
|
|
// segregate into weekdays
|
|
$day_data = $this->initDayData();
|
|
$i = 0;
|
|
foreach ($scheduler_data as $weekday_data)
|
|
{
|
|
$total_jos = 0;
|
|
foreach ($weekday_data as $hourly_jo)
|
|
$total_jos += $hourly_jo;
|
|
|
|
$day_data[$i]['total_jos'] = $total_jos;
|
|
|
|
$i++;
|
|
}
|
|
|
|
$shifts = [];
|
|
$res_lines = explode("\n", $res);
|
|
$total_riders = 0;
|
|
foreach ($res_lines as $line)
|
|
{
|
|
// format is day shift - hour shift - number of riders
|
|
$shift_data = explode('-', $line);
|
|
if (count($shift_data) != 3)
|
|
continue;
|
|
|
|
$day_shift_index = $shift_data[0];
|
|
$hour_shift_index = $shift_data[1];
|
|
$rider_count = $shift_data[2];
|
|
|
|
$total_riders += $rider_count;
|
|
|
|
$label = $this->day_shifts[$day_shift_index][0] . ' ' . $hour_shifts[$hour_shift_index][0];
|
|
|
|
$shifts[] = [
|
|
'label' => $label,
|
|
'count' => $rider_count,
|
|
];
|
|
|
|
// initialize hours
|
|
$rider_hours = [];
|
|
for ($i = 0; $i < 24; $i++)
|
|
$rider_hours[$i] = 0;
|
|
for ($i = 1; $i < count($hour_shifts[$hour_shift_index]); $i++)
|
|
$rider_hours[$hour_shifts[$hour_shift_index][$i]] = 1;
|
|
|
|
// error_log('allocating ' . $rider_count . ' for ' . $label);
|
|
|
|
// add shifts to the weekday
|
|
for ($i = 1; $i < count($this->day_shifts[$day_shift_index]); $i++)
|
|
{
|
|
$day = $this->day_shifts[$day_shift_index][$i];
|
|
|
|
$day_data[$day]['shifts'][] = [
|
|
'label' => $label,
|
|
'count' => $rider_count,
|
|
'hours' => $rider_hours,
|
|
];
|
|
|
|
$day_data[$day]['total_riders'] += $rider_count;
|
|
}
|
|
}
|
|
|
|
|
|
$data = [
|
|
'shifts' => $shifts,
|
|
'weekday_shifts' => $day_data,
|
|
'total_riders' => $total_riders,
|
|
];
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function generateHubData($em, $hub, $distance_limit, DateTime $date_start, DateTime $date_end, $time_start, $time_end, &$overlaps)
|
|
{
|
|
// get hub to analyze
|
|
// $hub = $em->getRepository(Hub::class)->find($hub_id);
|
|
$conn = $em->getConnection();
|
|
|
|
// get job order data (job orders within coverage area)
|
|
$jos = $this->generateJobOrderData($conn, $hub, $distance_limit, $date_start, $date_end, $time_start, $time_end);
|
|
|
|
// get most bought battery from these JOs
|
|
$batt_id = $this->getHubBattery($conn, $jos);
|
|
$batt = $em->getRepository(Battery::class)->find($batt_id);
|
|
if ($batt == null)
|
|
$batt_data = [
|
|
'mfg' => 'None',
|
|
'model' => 'None',
|
|
'size' => 'None',
|
|
];
|
|
else
|
|
$batt_data = [
|
|
'mfg' => $batt->getManufacturer()->getName(),
|
|
'model' => $batt->getModel()->getName(),
|
|
'size' => $batt->getSize()->getName(),
|
|
];
|
|
|
|
// initialize counters
|
|
$c_weekday = [];
|
|
$c_day = [];
|
|
|
|
// counter to check instances of hourly weekdays, so we can get average
|
|
$c_week_count = [];
|
|
|
|
// loop through job orders
|
|
foreach ($jos as $jo)
|
|
{
|
|
$date = DateTime::createFromFormat('Y-m-d H:i:s', $jo['date_schedule']);
|
|
$year = $date->format('Y');
|
|
$month = $date->format('m');
|
|
$day = $date->format('d');
|
|
$weekday = $date->format('l');
|
|
$hour = $date->format('H');
|
|
$week = $date->format('W');
|
|
|
|
$jo_id = $jo['id'];
|
|
$hub_id = $hub->getID();
|
|
|
|
// year day
|
|
if (!isset($c_day[$year][$month][$day]))
|
|
$c_day[$year][$month][$day] = 0;
|
|
$c_day[$year][$month][$day]++;
|
|
|
|
// weekday
|
|
if (!isset($c_weekday[$weekday][$hour]))
|
|
{
|
|
$c_weekday[$weekday][$hour]['total'] = 0;
|
|
$c_weekday[$weekday][$hour]['count'] = 0;
|
|
$c_weekday[$weekday][$hour]['jos'] = [];
|
|
}
|
|
|
|
$c_weekday[$weekday][$hour]['total']++;
|
|
$c_weekday[$weekday][$hour]['jos'][$jo_id] = $jo_id;
|
|
|
|
// make a count of number of weeks, so we can take average
|
|
if (!isset($c_week_count[$week][$weekday][$hour]))
|
|
{
|
|
// error_log('week detected - ' . $week);
|
|
$c_week_count[$week][$weekday][$hour] = 1;
|
|
$c_weekday[$weekday][$hour]['count']++;
|
|
}
|
|
|
|
// track overlaps (jo that can be handled by more than one hub)
|
|
if (!isset($overlaps[$jo_id]))
|
|
{
|
|
$overlaps[$jo_id] = [];
|
|
}
|
|
$overlaps[$jo_id][$hub_id] = $hub_id;
|
|
}
|
|
|
|
// error_log(print_r($c_weekday, true));
|
|
|
|
$chart_year = $this->generateYearData($date_start, $date_end, $c_day);
|
|
// $chart_weekday = $this->generateWeekdayData($c_weekday, $today);
|
|
|
|
// error_log(print_r($chart_weekday, true));
|
|
|
|
$params = [
|
|
'id' => $hub->getID(),
|
|
'label' => $hub->getName(),
|
|
'data_year' => $chart_year,
|
|
// 'data_weekday' => $chart_weekday,
|
|
'c_weekday' => $c_weekday, // sending raw weekday data because we need to process overlaps
|
|
'battery' => $batt_data,
|
|
// TODO: refactor this pls
|
|
];
|
|
|
|
return $params;
|
|
}
|
|
|
|
protected function getHubBattery($conn, $jos)
|
|
{
|
|
// collect ids
|
|
$ids = [];
|
|
foreach ($jos as $jo)
|
|
{
|
|
$ids[] = $jo['id'];
|
|
}
|
|
|
|
// no jos, so no battery
|
|
if (count($ids) <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// ideally we encode the ids, but right now we're assuming they'll be int
|
|
$in_text = implode(',', $ids);
|
|
|
|
// get all the batteries ordered in these JOs
|
|
$sql = 'select battery_id, count(*) as total from invoice i,invoice_item item where i.id = item.invoice_id and i.job_order_id in (' . $in_text . ') group by battery_id';
|
|
|
|
$stmt = $conn->prepare($sql);
|
|
|
|
$stmt->execute();
|
|
$batteries = $stmt->fetchAll();
|
|
|
|
// error_log(print_r($batteries, true));
|
|
|
|
|
|
// get the most ordered, skipping the null battery
|
|
$best_batt_id = 0;
|
|
$best_batt_count = 0;
|
|
foreach ($batteries as $batt)
|
|
{
|
|
if ($batt['battery_id'] == null)
|
|
continue;
|
|
|
|
// if current battery is better than our best
|
|
if ($best_batt_count < $batt['total'])
|
|
{
|
|
$best_batt_id = $batt['battery_id'];
|
|
$best_batt_count = $batt['total'];
|
|
}
|
|
}
|
|
|
|
// error_log('BEST - ' . $best_batt_id . ' - ' . $best_batt_count);
|
|
|
|
return $best_batt_id;
|
|
}
|
|
|
|
protected function generateJobOrderData($conn, $hub, $distance_limit, DateTime $date_start, DateTime $date_end, $time_start, $time_end)
|
|
{
|
|
$hub_coord = $hub->getCoordinates();
|
|
|
|
// create query
|
|
// formula to convert to km is 111195 * st_distance
|
|
$sql = "select id, round(st_distance(coordinates, Point(:lng, :lat)) * 111195) as dist, date_schedule from job_order where st_distance(coordinates, Point(:lng, :lat)) * 111195 <= :distance_limit and status <> 'cancelled' and date_schedule >= :date_start and date_schedule <= :date_end";
|
|
// check if time is specified
|
|
if (!empty($time_start))
|
|
$sql .= ' and time(date_schedule) >= :time_start';
|
|
if (!empty($time_end))
|
|
$sql .= ' and time(date_schedule) <= :time_end';
|
|
$sql .= " order by date_schedule asc";
|
|
|
|
|
|
|
|
|
|
$stmt = $conn->prepare($sql);
|
|
$stmt->bindValue('lng', $hub_coord->getLongitude());
|
|
$stmt->bindValue('lat', $hub_coord->getLatitude());
|
|
$stmt->bindValue('distance_limit', $distance_limit);
|
|
$stmt->bindValue('date_start', $date_start->format('Y-m-d H:i:s'));
|
|
$stmt->bindValue('date_end', $date_end->format('Y-m-d H:i:s'));
|
|
|
|
if (!empty($time_start))
|
|
$stmt->bindValue('time_start', $time_start);
|
|
if (!empty($time_end))
|
|
$stmt->bindValue('time_end', $time_end);
|
|
|
|
$stmt->execute();
|
|
$jos = $stmt->fetchAll();
|
|
|
|
/*
|
|
error_log(count($jos));
|
|
error_log(print_r($jos, true));
|
|
*/
|
|
|
|
return $jos;
|
|
}
|
|
|
|
protected function generateYearData($date_start, $date_end, $c_day)
|
|
{
|
|
$res_year = [];
|
|
$date_loop = clone $date_start;
|
|
for (; $date_loop <= $date_end; $date_loop->add(new DateInterval('P1D')))
|
|
{
|
|
$year = $date_loop->format('Y');
|
|
$month = $date_loop->format('m');
|
|
$day = $date_loop->format('d');
|
|
$year_field = 'y' . $date_loop->format('Y');
|
|
$id = $date_loop->format('m-d');
|
|
|
|
// NOTE: toss aside feb 29
|
|
// TODO: handle april 29
|
|
if ($id == '02-29')
|
|
continue;
|
|
|
|
if (!isset($res_year[$id][$year_field]))
|
|
{
|
|
$res_year[$id]['date'] = $date_loop->format('M j');
|
|
$res_year[$id][$year_field] = 0;
|
|
}
|
|
|
|
if (isset($c_day[$year][$month][$day]))
|
|
$res_year[$id][$year_field] = $c_day[$year][$month][$day];
|
|
}
|
|
|
|
// error_log(print_r($res_year, true));
|
|
|
|
$chart_year = [];
|
|
foreach ($res_year as $day => $day_data)
|
|
$chart_year[] = $day_data;
|
|
|
|
return $chart_year;
|
|
}
|
|
|
|
protected function generateAllWeekData($all_weekday_data, $overlaps)
|
|
{
|
|
$data = [];
|
|
|
|
// build hours
|
|
$hours = [];
|
|
for ($i = 0; $i < 24; $i++)
|
|
$hours[] = sprintf('%02d', $i);
|
|
|
|
// TODO: substitute this
|
|
$year_data = $all_weekday_data;
|
|
$scheduler_data = [];
|
|
|
|
/*
|
|
error_log('----------------------------------------------------------------------');
|
|
error_log(print_r($all_weekday_data, true));
|
|
error_log('----------------------------------------------------------------------');
|
|
*/
|
|
|
|
// gather maximum for each hour
|
|
foreach ($this->weekdays as $weekday)
|
|
{
|
|
// go through the hours
|
|
foreach ($hours as $hour)
|
|
{
|
|
$id = $hour + 0;
|
|
if (!isset($data[$id]))
|
|
$data[$id] = [
|
|
'hour' => $hour,
|
|
];
|
|
|
|
// get hour data
|
|
$prefix = $weekday;
|
|
if (isset($year_data[$weekday][$hour]))
|
|
{
|
|
// calculate the rider value for each JO and use that score as basis
|
|
$total_rv = $this->calculateTotalRiderValue($year_data[$weekday][$hour]['jos'], $overlaps);
|
|
$rv_average = ceil($total_rv / $year_data[$weekday][$hour]['count']);
|
|
|
|
$data[$id][$prefix] = $year_data[$weekday][$hour]['total'];
|
|
$data[$id][$prefix . '_count'] = $year_data[$weekday][$hour]['count'];
|
|
$data[$id][$prefix . '_average'] = ceil($year_data[$weekday][$hour]['total'] / $year_data[$weekday][$hour]['count']);
|
|
$data[$id][$prefix . '_rv_average'] = $rv_average;
|
|
|
|
// assign scheduler data
|
|
$scheduler_data[$weekday][$hour] = $rv_average;
|
|
}
|
|
else
|
|
{
|
|
if (!isset($scheduler_data[$weekday][$hour]))
|
|
{
|
|
$data[$id][$prefix . '_rv_average'] = 0;
|
|
$scheduler_data[$weekday][$hour] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
$data['scheduler_data'] = $scheduler_data;
|
|
|
|
// error_log(print_r($data, true));
|
|
return $data;
|
|
}
|
|
|
|
protected function generateWeekdayData($all_weekday_data, $today, $overlaps)
|
|
{
|
|
$data = [];
|
|
|
|
// build hours
|
|
$hours = [];
|
|
for ($i = 0; $i < 24; $i++)
|
|
$hours[] = sprintf('%02d', $i);
|
|
|
|
$month = $today->format('m');
|
|
$weekday = $today->format('l');
|
|
|
|
$year_data = [];
|
|
foreach ($all_weekday_data as $year => $year_data)
|
|
{
|
|
// go through the hours
|
|
foreach ($hours as $hour)
|
|
{
|
|
$id = $hour + 0;
|
|
if (!isset($data[$id]))
|
|
$data[$id] = [
|
|
'hour' => $hour,
|
|
];
|
|
|
|
// get hour data
|
|
if (isset($year_data[$month][$weekday][$hour]))
|
|
{
|
|
$year_id = 'y' . $year;
|
|
|
|
// calculate the rider value for each JO and use that score as basis
|
|
$total_rv = $this->calculateTotalRiderValue($year_data[$month][$weekday][$hour]['jos'], $overlaps);
|
|
|
|
$data[$id][$year_id] = $year_data[$month][$weekday][$hour]['total'];
|
|
$data[$id][$year_id . '_count'] = $year_data[$month][$weekday][$hour]['count'];
|
|
$data[$id][$year_id . '_average'] = ceil($year_data[$month][$weekday][$hour]['total'] / $year_data[$month][$weekday][$hour]['count']);
|
|
$data[$id][$year_id . '_rv_average'] = ceil($total_rv / $year_data[$month][$weekday][$hour]['count']);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function calculateTotalRiderValue($jos, $overlaps)
|
|
{
|
|
// rider value = 1 / number of hubs (overlaps) that can service JO
|
|
|
|
$sum = 0.0;
|
|
$jo_count = count($jos);
|
|
|
|
foreach ($jos as $jo_id)
|
|
{
|
|
$hub_count = count($overlaps[$jo_id]);
|
|
$rv = 1 / $hub_count;
|
|
$sum += $rv;
|
|
}
|
|
|
|
return $sum;
|
|
}
|
|
|
|
protected function generateNotCoveredData($em, $hub_coverage, $today)
|
|
{
|
|
$conn = $em->getConnection();
|
|
|
|
$month = $today->format('m');
|
|
$weekday = $today->format('N') - 1;
|
|
|
|
$wheres = [];
|
|
foreach ($hub_coverage as $hub_data)
|
|
{
|
|
$long = $hub_data['longitude'];
|
|
$lat = $hub_data['latitude'];
|
|
$dist = $hub_data['distance'];
|
|
|
|
// get areas not covered
|
|
$wheres[] = "st_distance(coordinates, Point($long, $lat)) * 111195 > $dist";
|
|
}
|
|
|
|
$where_string = implode(' and ', $wheres);
|
|
|
|
$sql = "select st_x(coordinates) as longitude, st_y(coordinates) as latitude, id, date_schedule from job_order where $where_string and status <> 'cancelled' and month(date_schedule) = $month and weekday(date_schedule) = $weekday order by date_schedule asc";
|
|
$stmt = $conn->prepare($sql);
|
|
$stmt->execute();
|
|
$jos = $stmt->fetchAll();
|
|
|
|
// error_log($sql);
|
|
// error_log(print_r($jos, true));
|
|
|
|
return $jos;
|
|
}
|
|
|
|
protected function generateRiderSchedule()
|
|
{
|
|
}
|
|
|
|
protected function solveRiderSchedule()
|
|
{
|
|
}
|
|
|
|
protected function populateHourShift($shift)
|
|
{
|
|
$hour_shift = [];
|
|
|
|
if ($shift == '24_7') {
|
|
$hour_shift = [
|
|
['00:00 - 09:00', 0, 1, 2, 3, 4, 5, 6, 7, 8],
|
|
['01:00 - 10:00', 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
|
['02:00 - 11:00', 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
|
['03:00 - 12:00', 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
|
['04:00 - 13:00', 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
|
['05:00 - 14:00', 5, 6, 7, 8, 9, 10, 11, 12, 13],
|
|
['06:00 - 15:00', 6, 7, 8, 9, 10, 11, 12, 13, 14],
|
|
['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
|
['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
|
['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21],
|
|
['14:00 - 23:00', 14, 15, 16, 17, 18, 19, 20, 21, 22],
|
|
['15:00 - 00:00', 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
|
['16:00 - 01:00', 16, 17, 18, 19, 20, 21, 22, 23, 0],
|
|
['17:00 - 02:00', 17, 18, 19, 20, 21, 22, 23, 0, 1],
|
|
['18:00 - 03:00', 18, 19, 20, 21, 22, 23, 0, 1, 2],
|
|
['19:00 - 04:00', 19, 20, 21, 22, 23, 0, 1, 2, 3],
|
|
['20:00 - 05:00', 20, 21, 22, 23, 0, 1, 2, 3, 4],
|
|
['21:00 - 06:00', 21, 22, 23, 0, 1, 2, 3, 4, 5],
|
|
['22:00 - 07:00', 22, 23, 0, 1, 2, 3, 4, 5, 6],
|
|
['23:00 - 08:00', 23, 0, 1, 2, 3, 4, 5, 6, 7],
|
|
];
|
|
}
|
|
if ($shift == '8AM_5PM') {
|
|
$hour_shift = [
|
|
['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
];
|
|
}
|
|
|
|
if ($shift == '7AM_10PM') {
|
|
$hour_shift = [
|
|
['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
|
['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
|
['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21]
|
|
];
|
|
}
|
|
|
|
if ($shift == '6AM_7PM') {
|
|
$hour_shift = [
|
|
['06:00 - 15:00', 6, 7, 8, 9, 10, 11, 12, 13, 14],
|
|
['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
|
['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18]
|
|
];
|
|
}
|
|
|
|
if ($shift == '6AM_10PM') {
|
|
$hour_shift = [
|
|
['06:00 - 15:00', 6, 7, 8, 9, 10, 11, 12, 13, 14],
|
|
['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
|
['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
|
['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21]
|
|
];
|
|
}
|
|
|
|
if ($shift == '6AM-12AM') {
|
|
$hour_shift = [
|
|
['06:00 - 15:00', 6, 7, 8, 9, 10, 11, 12, 13, 14],
|
|
['07:00 - 16:00', 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
['08:00 - 17:00', 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
|
['09:00 - 18:00', 9, 10, 11, 12, 13, 14, 15, 16, 17],
|
|
['10:00 - 19:00', 10, 11, 12, 13, 14, 15, 16, 17, 18],
|
|
['11:00 - 20:00', 11, 12, 13, 14, 15, 16, 17, 18, 19],
|
|
['12:00 - 21:00', 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
|
['13:00 - 22:00', 13, 14, 15, 16, 17, 18, 19, 20, 21],
|
|
['14:00 - 23:00', 14, 15, 16, 17, 18, 19, 20, 21, 22],
|
|
['15:00 - 00:00', 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
|
['16:00 - 01:00', 16, 17, 18, 19, 20, 21, 22, 23, 0]
|
|
];
|
|
}
|
|
|
|
return $hour_shift;
|
|
}
|
|
}
|