Create graph for overlaps #409

This commit is contained in:
Kendrick Chan 2020-06-03 01:18:12 +08:00
parent 711082cf23
commit 0911a1787e
2 changed files with 128 additions and 7 deletions

View file

@ -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;
}
}

View file

@ -38,6 +38,9 @@
<div id="month-weekday-chart-{{ hub.id }}" style="height: 400px;">
</div>
<div id="month-weekday-chart-rv-{{ hub.id }}" style="height: 400px;">
</div>
</div>
</div>
</div>
@ -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 %}
</script>