diff --git a/src/Controller/AnalyticsController.php b/src/Controller/AnalyticsController.php index 8dfbddde..140405cf 100644 --- a/src/Controller/AnalyticsController.php +++ b/src/Controller/AnalyticsController.php @@ -62,13 +62,14 @@ class AnalyticsController extends Controller $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(); - $dist = $distances[$key]; - $hub_data[$hub_id] = $this->generateHubData($em, $hub, $dist, $today); + $hub_data[$hub_id] = $this->generateHubData($em, $hub, $dist, $today, $overlaps); $hub_coverage[] = [ 'longitude' => $coords->getLongitude(), @@ -77,16 +78,32 @@ class AnalyticsController extends Controller ]; } + // 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); + $hub_data[$hub_id]['data_weekday'] = $chart_weekday; + + 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); + $params = [ 'date' => $today, 'hub_list' => $hub_data, 'hub_coverage' => $hub_coverage, + 'not_covered' => $not_covered, ]; return $this->render('analytics/forecast_submit.html.twig', $params); } - protected function generateHubData($em, $hub, $distance_limit, DateTime $today) + protected function generateHubData($em, $hub, $distance_limit, DateTime $today, &$overlaps) { $date_start = DateTime::createFromFormat('Y-m-d H:i:s', '2018-01-01 00:00:00'); $date_end = new DateTime(); @@ -95,7 +112,7 @@ class AnalyticsController extends Controller // $hub = $em->getRepository(Hub::class)->find($hub_id); $conn = $em->getConnection(); - // get job order data + // get job order data (job orders within coverage area) $jos = $this->generateJobOrderData($conn, $hub, $distance_limit); // initialize counters @@ -116,6 +133,9 @@ class AnalyticsController extends Controller $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; @@ -126,9 +146,11 @@ class AnalyticsController extends Controller { $c_weekday[$year][$month][$weekday][$hour]['total'] = 0; $c_weekday[$year][$month][$weekday][$hour]['count'] = 0; + $c_weekday[$year][$month][$weekday][$hour]['jos'] = []; } $c_weekday[$year][$month][$weekday][$hour]['total']++; + $c_weekday[$year][$month][$weekday][$hour]['jos'][$jo_id] = $jo_id; if (!isset($c_week_count[$year][$month][$week][$weekday][$hour])) { @@ -136,12 +158,19 @@ class AnalyticsController extends Controller $c_week_count[$year][$month][$week][$weekday][$hour] = 1; $c_weekday[$year][$month][$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); + // $chart_weekday = $this->generateWeekdayData($c_weekday, $today); // error_log(print_r($chart_weekday, true)); @@ -149,7 +178,9 @@ class AnalyticsController extends Controller 'id' => $hub->getID(), 'label' => $hub->getName(), 'data_year' => $chart_year, - 'data_weekday' => $chart_weekday, + // 'data_weekday' => $chart_weekday, + 'c_weekday' => $c_weekday, // sending raw weekday data because we need to process overlaps + // TODO: refactor this pls ]; return $params; @@ -212,7 +243,7 @@ class AnalyticsController extends Controller return $chart_year; } - protected function generateWeekdayData($all_weekday_data, $today) + protected function generateWeekdayData($all_weekday_data, $today, $overlaps) { $data = []; @@ -241,13 +272,65 @@ class AnalyticsController extends Controller { $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; + } } diff --git a/templates/analytics/forecast_submit.html.twig b/templates/analytics/forecast_submit.html.twig index 3951a231..c8c3d28a 100644 --- a/templates/analytics/forecast_submit.html.twig +++ b/templates/analytics/forecast_submit.html.twig @@ -38,6 +38,9 @@
+ +
+
@@ -67,6 +70,15 @@ var map = L.map('map_coverage').setView([{% trans %}default_lat{% endtrans %}, { L.circle([{{ cover.latitude }}, {{ cover.longitude }}], { radius: {{ cover.distance }} }).addTo(map); {% endfor %} +//------------------------------------------------------------------------ + +// display not covered +{% for jo_na in not_covered %} +L.marker([{{ jo_na.latitude }}, {{ jo_na.longitude }}]).addTo(map); +{% endfor %} + +//------------------------------------------------------------------------ + {% for hub in hub_list %} // create chart instance var chart = am4core.create("year-day-chart-{{ hub.id }}", am4charts.XYChart); @@ -132,6 +144,32 @@ l2019.strokeWidth = 3; l2019.dataFields.valueY = "y2019_average"; l2019.dataFields.categoryX = "hour"; +//------------------------------------------------------------------------ + +var chart2 = am4core.create("month-weekday-chart-rv-{{ hub.id }}", am4charts.XYChart); +chart2.data = {{ hub.data_weekday|json_encode|raw }}; + +var xAxis = chart2.xAxes.push(new am4charts.CategoryAxis()); +xAxis.dataFields.category = "hour"; +xAxis.title.text = "Hour"; +var yAxis = chart2.yAxes.push(new am4charts.ValueAxis()); +yAxis.title.text = "Orders"; +yAxis.maxPrecision = 0; + +var l2018 = chart2.series.push(new am4charts.LineSeries()); +l2018.name = "2018"; +l2018.stroke = am4core.color("#0000FF"); +l2018.strokeWidth = 2; +l2018.dataFields.valueY = "y2018_rv_average"; +l2018.dataFields.categoryX = "hour"; + +var l2019 = chart2.series.push(new am4charts.LineSeries()); +l2019.name = "2019"; +l2019.stroke = am4core.color("#FF0000"); +l2019.strokeWidth = 3; +l2019.dataFields.valueY = "y2019_rv_average"; +l2019.dataFields.categoryX = "hour"; + {% endfor %}