Skip to content

Commit

Permalink
#22 Request fare class boundaries on init to avoid "undefined" issues.
Browse files Browse the repository at this point in the history
Also made an explicit exception to handle insufficient data in the backend,
and setup the frontend component so that the page will scroll to reveal the results.
  • Loading branch information
dmnisson committed Feb 20, 2019
1 parent 382e407 commit fb6c46f
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 57 deletions.
2 changes: 2 additions & 0 deletions DriverTracker/Controllers/AnalysisApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ public async Task<double[]> GetPickupProb(double startlat, double startlon, doub
await TrainPickupPrediction();

return _pickupPrediction.GetPickupProbabilities(


new double[] { startlat, startlon },
new double[] { endlat, endlon },
delay, duration, fare, interval
Expand Down
6 changes: 3 additions & 3 deletions DriverTracker/Controllers/LegsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,14 @@ public async Task<IActionResult> Edit(int? id, int? driver)
return NotFound();
}

var leg = await _context.Legs.FindAsync(id);
var leg = await _context.Legs.Include(l => l.Driver)
.FirstOrDefaultAsync(l => l.LegID == id);
if (leg == null)
{
return NotFound();
}

var driverObj = await _context.Drivers.FirstOrDefaultAsync(m => m.DriverID == id);
var driverObj = leg.Driver;
var authResult = await _authorizationService.AuthorizeAsync(User, driverObj, "DriverInfoPolicy");

if (!authResult.Succeeded)
Expand All @@ -176,7 +177,6 @@ public async Task<IActionResult> Edit(int? id, int? driver)
// POST: Legs/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
// TODO troubleshoot why access is denied to admin users
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, int? driver, [Bind("LegID,DriverID,StartAddress,PickupRequestTime,StartTime,DestinationAddress,ArrivalTime,Distance,Fare,NumOfPassengersAboard,NumOfPassengersPickedUp,FuelCost")] Leg leg)
Expand Down
6 changes: 6 additions & 0 deletions DriverTracker/Domain/PickupPrediction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ public async Task LearnFromDates(DateTime from, DateTime to)
{
double[][] dataSubset = dataset.Where((dp, k) => pickupNumbers[k] == n).ToArray();
int[] fareClassesSubset = fareClasses.Where((fc, k) => pickupNumbers[k] == n).ToArray();

if (dataSubset.Length == 0)
{
throw new ApplicationException("Insufficient data to make a reliable prediction");
}

// for each fare class interval boundary
for (int j = 0; j < NumberOfFareClassIntervals; j++)
{
Expand Down
Binary file modified DriverTracker/DriverTracker.db
Binary file not shown.
1 change: 1 addition & 0 deletions DriverTracker/Views/Shared/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<li class="nav-item active"><a class="nav-link" asp-area="" asp-controller="Home" asp-action="About">About</a></li>
<li class="nav-item active"><a class="nav-link" asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
<li class="nav-item active"><a class="nav-link" asp-area="" asp-controller="Drivers" asp-action="Index">Driver Statistics</a></li>
<li class="nav-item active"><a class="nav-link" asp-area="" asp-controller="PickupPredictor" asp-action="Index">Pickup Predictions</a></li>
</ul>
</div>
</nav>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<h2>Pickup predictions</h2>
<p>
Predict probability of completing given leg and collecting fares.
</p>

<h2>Pickup predictions</h2>
<p>
Predict probability of completing given leg and collecting fares.
</p>

<form (ngSubmit)="onSubmit()" #pickupPredictionForm="ngForm">
<h3>Leg information</h3>
<div class="m-5">
Expand Down Expand Up @@ -43,35 +43,35 @@ <h3>Pickup probabilities</h3>
</div>
<button type="button" class="btn btn-success" (click)="predictPickupProbabilities()">Predict pickup probabilities</button>
</div>
</form>

<h2>Results</h2>
<ng-template [ngIf]="fareClassProbabilities != null">
<h3>Fare class probabilities</h3>
<div class="row" *ngFor="let i of fareClassIndices">
<div class="col">
<p class="pl-5">${{i == 0 ? 0 : fareClassIntervalBoundaries[i-1]}} to ${{fareClassIntervalBoundaries[i]}}:</p>
</div>
<div class="col">
<p class="pr-5">{{fareClassProbabilities[i] * 100}}%</p>
</div>
</div>
</form>

<h2>Results</h2>
<ng-template [ngIf]="fareClassProbabilities != null">
<h3>Fare class probabilities</h3>
<div class="row" *ngFor="let i of fareClassIndices">
<div class="col">
<p class="pl-5">${{i == 0 ? 0 : fareClassIntervalBoundaries[i-1]}} to ${{fareClassIntervalBoundaries[i]}}:</p>
</div>
<div class="col">
<p class="pr-5">{{fareClassProbabilities[i] * 100}}%</p>
</div>
</div>
<div class="row" *ngIf="fareClassIntervalBoundaries.length > 0">
<div class="col">
<p class="pl-5">${{fareClassIntervalBoundaries[fareClassIntervalBoundaries.length - 1]}}+:</p>
</div>
<div class="col">
<p class="pr-5">{{fareClassProbabilities[fareClassIntervalBoundaries.length] * 100}}%</p>
</div>
</div>
</ng-template>
</div>
</ng-template>
<ng-template [ngIf]="pickupProbabilities != null">
<h3>Pickup probabilities</h3>
<div class="row" *ngFor="let i of pickupProbabilityIndices">
<div class="col">
<p class="pl-5" *ngIf="i == 0">
Probability of failure to collect:
</p>
<p class="pl-5" *ngIf="i == 0">
Probability of failure to collect:
</p>
<p class="pl-5" *ngIf="i > 0">
Probability of collecting {{i}} passenger<ng-template [ngIf]="i != 1">s</ng-template>:
</p>
Expand All @@ -80,4 +80,7 @@ <h3>Pickup probabilities</h3>
<p class="pr-5">{{pickupProbabilities[i] * 100}}%</p>
</div>
</div>
</ng-template>
</ng-template>
<div class="row" id="pickupPredictionResults">
<div class="col"></div>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, AfterViewChecked } from '@angular/core';
import { ViewportScroller } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { PickupPredictorService } from '../pickup-predictor.service';
import { GeocodingService } from '../geocoding.service';
Expand All @@ -10,10 +11,11 @@ import { switchMap } from 'rxjs/operators';
templateUrl: './pickup-predictor.component.html',
styleUrls: ['./pickup-predictor.component.scss']
})
export class PickupPredictorComponent implements OnInit {
export class PickupPredictorComponent implements OnInit, AfterViewChecked {
constructor(private pickupPredictorService: PickupPredictorService,
private geocodingService: GeocodingService,
private aRoute: ActivatedRoute) { }
private aRoute: ActivatedRoute,
private viewportScroller: ViewportScroller) { }

startAddress: string;
startCoords: number[];
Expand All @@ -33,6 +35,9 @@ export class PickupPredictorComponent implements OnInit {
fareClassIntervalBoundaries: number[];
fareClassIndices: number[];

// so AfterViewChecked knows when to scroll
resultsJustComputed: boolean;

geocodeInputAddresses(geoDependent: ((_1: number[], _2: number[]) => Observable<any>)): Observable<any> {
const startCoords$ = this.geocodingService.getAddressCoordinates(this.startAddress);
const endCoords$ = this.geocodingService.getAddressCoordinates(this.endAddress);
Expand All @@ -50,24 +55,43 @@ export class PickupPredictorComponent implements OnInit {
this.geocodeInputAddresses((s, e) => this.pickupPredictorService.getFareClassProbabilities(
s, e, this.delay,
this.duration, this.pickups, this.interval))
.subscribe(probs => this.fareClassProbabilities = probs);
.subscribe(probs => {
this.resultsJustComputed = true;
this.fareClassProbabilities = probs;
});
}

predictPickupProbabilities(): void {
this.geocodeInputAddresses((s, e) => this.pickupPredictorService.getPickupProbabilities(
s, e, this.delay,
this.duration, this.fare, this.interval))
.subscribe(probs => {
this.resultsJustComputed = true;
this.pickupProbabilities = probs;
this.pickupProbabilityIndices = (new Array(probs.length)).fill(0).map((x,i)=>i);
});
}

ngOnInit() {
this.resultsJustComputed = false;
this.pickupPredictorService.getFareClassIntervals()
.subscribe(bounds => this.fareClassIntervalBoundaries = bounds);
this.fareClassIndices = (new Array(this.fareClassIntervalBoundaries.length))
.subscribe(bounds => {
this.fareClassIntervalBoundaries = bounds;
this.fareClassIndices = (new Array(this.fareClassIntervalBoundaries.length))
.fill(0).map((x,i)=>i);
});

}

ngAfterViewChecked() {
if (this.resultsJustComputed) {
this.scrollToResults();
this.resultsJustComputed = false;
}
}

scrollToResults() {
this.viewportScroller.scrollToAnchor("pickupPredictionResults");
}

}
Loading

0 comments on commit fb6c46f

Please sign in to comment.