Skip to content

Commit

Permalink
improve mileage algorithm for litres_100km
Browse files Browse the repository at this point in the history
  • Loading branch information
Goloso98 committed Feb 13, 2025
1 parent c167642 commit 4d06423
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 70 deletions.
23 changes: 13 additions & 10 deletions server/models/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ import (
)

type MileageModel struct {
Date time.Time `form:"date" json:"date" binding:"required" time_format:"2006-01-02"`
VehicleID string `form:"vehicleId" json:"vehicleId" binding:"required"`
FuelUnit db.FuelUnit `form:"fuelUnit" json:"fuelUnit" binding:"required"`
FuelQuantity float32 `form:"fuelQuantity" json:"fuelQuantity" binding:"required"`
PerUnitPrice float32 `form:"perUnitPrice" json:"perUnitPrice" binding:"required"`
Currency string `json:"currency"`
DistanceUnit db.DistanceUnit `form:"distanceUnit" json:"distanceUnit"`
Mileage float32 `form:"mileage" json:"mileage" binding:"mileage"`
CostPerMile float32 `form:"costPerMile" json:"costPerMile" binding:"costPerMile"`
OdoReading int `form:"odoReading" json:"odoReading" binding:"odoReading"`
Date time.Time `form:"date" json:"date" binding:"required" time_format:"2006-01-02"`
StartDate time.Time `form:"startDate" json:"startDate" binding:"required" time_format:"2006-01-02"`
EndDate time.Time `form:"endDate" json:"endDate" binding:"required" time_format:"2006-01-02"`
VehicleID string `form:"vehicleId" json:"vehicleId" binding:"required"`
FuelUnit db.FuelUnit `form:"fuelUnit" json:"fuelUnit" binding:"required"`
FuelQuantity float32 `form:"fuelQuantity" json:"fuelQuantity" binding:"required"`
PerUnitPrice float32 `form:"perUnitPrice" json:"perUnitPrice" binding:"required"`
Currency string `json:"currency"`
DistanceUnit db.DistanceUnit `form:"distanceUnit" json:"distanceUnit"`
Mileage float32 `form:"mileage" json:"mileage" binding:"mileage"`
CostPerMile float32 `form:"costPerMile" json:"costPerMile" binding:"costPerMile"`
OdoReading int `form:"odoReading" json:"odoReading" binding:"odoReading"`
DistanceTravel float32 `form:"distanceTravel" json:"distanceTravel" binding:"distanceTravel"`
}

func (v *MileageModel) FuelUnitDetail() db.EnumDetail {
Expand Down
238 changes: 178 additions & 60 deletions server/service/reportService.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,116 @@ import (
"hammond/models"
)

type Range struct {
Start int
End int
}

func ComputeMileage_litre_100km(fillups []db.Fillup, mileages *[]models.MileageModel) {
var ranges []Range

// fillups ordered by odo descending
// travel from lower odo to higher
for i := len(fillups) - 1; i >= 0; i-- {
currentFillup := fillups[i]
currTankFull := currentFillup.IsTankFull != nil && *currentFillup.IsTankFull
if !currTankFull {
continue
}

for j := i-1; j >= 0; j-- {
lastFillup := fillups[j]
lastHasMissed := lastFillup.HasMissedFillup != nil && *lastFillup.HasMissedFillup
if lastHasMissed {
i = j + 1 // so that i loop lands on j in the next iter
break
}

lastTankFull := lastFillup.IsTankFull != nil && *lastFillup.IsTankFull
if lastTankFull {
ranges = append(ranges, Range{Start: i, End: j})
i = j + 1
break
}
}
}

for _, r := range ranges {
startFillup := fillups[r.Start]
endFillup := fillups[r.End]

mileage := models.MileageModel{
Date: startFillup.Date,
StartDate: startFillup.Date,
EndDate: endFillup.Date,
VehicleID: startFillup.VehicleID,
FuelUnit: db.LITRE,
FuelQuantity: 0,
PerUnitPrice: startFillup.PerUnitPrice,
OdoReading: 0,
DistanceTravel: 0,
Currency: startFillup.Currency,
DistanceUnit: db.KILOMETERS,
Mileage: 0,
CostPerMile: 0,
}

// This computes the distance traveled
startOdo := float32(startFillup.OdoReading)
endOdo := float32(endFillup.OdoReading)

// Convert into km if needed
if startFillup.DistanceUnit != mileage.DistanceUnit {
startOdo = common.MilesToKm(startOdo)
}
if endFillup.DistanceUnit != mileage.DistanceUnit {
endOdo = common.MilesToKm(endOdo)
}
mileage.DistanceTravel = endOdo - startOdo

// This computes the spent fuel
// first we convert into Litre
// reverse order...
// also should skip start fillup
for idx := r.Start-1; idx >= r.End; idx-- {
f := fillups[idx]
if (f.FuelUnit != mileage.FuelUnit) {
f.FuelUnit = mileage.FuelUnit
f.FuelQuantity = common.GallonToLitre(f.FuelQuantity)
}
// second sum them all
mileage.FuelQuantity += f.FuelQuantity
}
// third divide
mileage.Mileage = mileage.FuelQuantity / mileage.DistanceTravel
mileage.Mileage *= 100
// forth append
// append blank
// if last date != this date
// meaning has missing fillups
mileagesSize := len(*mileages)
if mileagesSize > 0 && ((*mileages)[mileagesSize-1].EndDate != mileage.StartDate) {
blankMileage := models.MileageModel {
Date: (*mileages)[mileagesSize-1].EndDate,
StartDate: (*mileages)[mileagesSize-1].EndDate,
EndDate: mileage.StartDate,
VehicleID: startFillup.VehicleID,
FuelUnit: db.LITRE,
FuelQuantity: 0,
PerUnitPrice: startFillup.PerUnitPrice,
OdoReading: 0,
DistanceTravel: 0,
Currency: startFillup.Currency,
DistanceUnit: db.KILOMETERS,
Mileage: 0,
CostPerMile: 0,
}
*mileages = append(*mileages, blankMileage)
}
*mileages = append(*mileages, mileage)
}
}

func GetMileageByVehicleId(vehicleId string, since time.Time, mileageOption string) (mileage []models.MileageModel, err error) {
data, err := db.GetFillupsByVehicleIdSince(vehicleId, since)
if err != nil {
Expand All @@ -23,67 +133,75 @@ func GetMileageByVehicleId(vehicleId string, since time.Time, mileageOption stri

var mileages []models.MileageModel

for i := 0; i < len(fillups)-1; i++ {
last := i + 1
if mileageOption == "litre_100km" {
ComputeMileage_litre_100km(fillups, &mileages)
} else {

for i := 0; i < len(fillups)-1; i++ {
last := i + 1

currentFillup := fillups[i]
lastFillup := fillups[last]

mileage := models.MileageModel{
StartDate: currentFillup.Date,
EndDate: currentFillup.Date,
Date: currentFillup.Date,
VehicleID: currentFillup.VehicleID,
FuelUnit: currentFillup.FuelUnit,
FuelQuantity: currentFillup.FuelQuantity,
PerUnitPrice: currentFillup.PerUnitPrice,
OdoReading: currentFillup.OdoReading,
Currency: currentFillup.Currency,
DistanceUnit: currentFillup.DistanceUnit,
Mileage: 0,
CostPerMile: 0,
}

if currentFillup.IsTankFull != nil && *currentFillup.IsTankFull && (currentFillup.HasMissedFillup == nil || !(*currentFillup.HasMissedFillup)) {
currentOdoReading := float32(currentFillup.OdoReading);
lastFillupOdoReading := float32(lastFillup.OdoReading);
currentFuelQuantity := float32(currentFillup.FuelQuantity);
// If miles per gallon option and distanceUnit is km, convert from km to miles
// then check if fuel unit is litres. If it is, convert to gallons
if (mileageOption == "mpg" && mileage.DistanceUnit == db.KILOMETERS) {
currentOdoReading = common.KmToMiles(currentOdoReading);
lastFillupOdoReading = common.KmToMiles(lastFillupOdoReading);
if (mileage.FuelUnit == db.LITRE) {
currentFuelQuantity = common.LitreToGallon(currentFuelQuantity);
}
}

// If km_litre option or litre per 100km and distanceUnit is miles, convert from miles to km
// then check if fuel unit is not litres. If it isn't, convert to litres

if ((mileageOption == "km_litre" || mileageOption == "litre_100km") && mileage.DistanceUnit == db.MILES) {
currentOdoReading = common.MilesToKm(currentOdoReading);
lastFillupOdoReading = common.MilesToKm(lastFillupOdoReading);

if (mileage.FuelUnit == db.US_GALLON) {
currentFuelQuantity = common.GallonToLitre(currentFuelQuantity);
}
}




distance := float32(currentOdoReading - lastFillupOdoReading);
if (mileageOption == "litre_100km") {
mileage.Mileage = currentFuelQuantity / distance * 100;
} else {
mileage.Mileage = distance / currentFuelQuantity;
}

mileage.CostPerMile = distance / currentFillup.TotalAmount;

}

mileages = append(mileages, mileage)
}
}

currentFillup := fillups[i]
lastFillup := fillups[last]

mileage := models.MileageModel{
Date: currentFillup.Date,
VehicleID: currentFillup.VehicleID,
FuelUnit: currentFillup.FuelUnit,
FuelQuantity: currentFillup.FuelQuantity,
PerUnitPrice: currentFillup.PerUnitPrice,
OdoReading: currentFillup.OdoReading,
Currency: currentFillup.Currency,
DistanceUnit: currentFillup.DistanceUnit,
Mileage: 0,
CostPerMile: 0,
}

if currentFillup.IsTankFull != nil && *currentFillup.IsTankFull && (currentFillup.HasMissedFillup == nil || !(*currentFillup.HasMissedFillup)) {
currentOdoReading := float32(currentFillup.OdoReading);
lastFillupOdoReading := float32(lastFillup.OdoReading);
currentFuelQuantity := float32(currentFillup.FuelQuantity);
// If miles per gallon option and distanceUnit is km, convert from km to miles
// then check if fuel unit is litres. If it is, convert to gallons
if (mileageOption == "mpg" && mileage.DistanceUnit == db.KILOMETERS) {
currentOdoReading = common.KmToMiles(currentOdoReading);
lastFillupOdoReading = common.KmToMiles(lastFillupOdoReading);
if (mileage.FuelUnit == db.LITRE) {
currentFuelQuantity = common.LitreToGallon(currentFuelQuantity);
}
}

// If km_litre option or litre per 100km and distanceUnit is miles, convert from miles to km
// then check if fuel unit is not litres. If it isn't, convert to litres

if ((mileageOption == "km_litre" || mileageOption == "litre_100km") && mileage.DistanceUnit == db.MILES) {
currentOdoReading = common.MilesToKm(currentOdoReading);
lastFillupOdoReading = common.MilesToKm(lastFillupOdoReading);

if (mileage.FuelUnit == db.US_GALLON) {
currentFuelQuantity = common.GallonToLitre(currentFuelQuantity);
}
}




distance := float32(currentOdoReading - lastFillupOdoReading);
if (mileageOption == "litre_100km") {
mileage.Mileage = currentFuelQuantity / distance * 100;
} else {
mileage.Mileage = distance / currentFuelQuantity;
}

mileage.CostPerMile = distance / currentFillup.TotalAmount;

}

mileages = append(mileages, mileage)
}
if mileages == nil {
mileages = make([]models.MileageModel, 0)
}
Expand Down

0 comments on commit 4d06423

Please sign in to comment.