diff --git a/server/common/utils.go b/server/common/utils.go index 2be238d..ba0bb50 100644 --- a/server/common/utils.go +++ b/server/common/utils.go @@ -25,6 +25,33 @@ func RandString(n int) string { return string(b) } +// A helper to convert from litres to gallon +func LitreToGallon(litres float32) float32 { + gallonConversionFactor := 0.21997 + return litres * float32(gallonConversionFactor); +} + +// A helper to convert from gallon to litres +func GallonToLitre(gallons float32) float32 { + litreConversionFactor := 3.785412 + return gallons * float32(litreConversionFactor); +} + + +// A helper to convert from km to miles +func KmToMiles(km float32) float32 { + kmConversionFactor := 0.62137119 + return km * float32(kmConversionFactor); +} + +// A helper to convert from miles to km +func MilesToKm(miles float32) float32 { + milesConversionFactor := 1.609344 + return miles * float32(milesConversionFactor); +} + + + // A Util function to generate jwt_token which can be used in the request header func GenToken(id string, role db.Role) (string, string) { jwt_token := jwt.New(jwt.GetSigningMethod("HS256")) diff --git a/server/controllers/reports.go b/server/controllers/reports.go index 6284ffa..6db3f10 100644 --- a/server/controllers/reports.go +++ b/server/controllers/reports.go @@ -25,7 +25,7 @@ func getMileageForVehicle(c *gin.Context) { return } - fillups, err := service.GetMileageByVehicleId(searchByIdQuery.Id, model.Since) + fillups, err := service.GetMileageByVehicleId(searchByIdQuery.Id, model.Since, model.MileageOption) if err != nil { c.JSON(http.StatusUnprocessableEntity, common.NewError("getMileageForVehicle", err)) return diff --git a/server/models/report.go b/server/models/report.go index 40e5403..c436b84 100644 --- a/server/models/report.go +++ b/server/models/report.go @@ -14,7 +14,7 @@ type MileageModel struct { 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"` @@ -35,4 +35,5 @@ func (b *MileageModel) MarshalJSON() ([]byte, error) { type MileageQueryModel struct { Since time.Time `json:"since" query:"since" form:"since"` + MileageOption string `json:"mileageOption" query:"mileageOption" form:"mileageOption"` } diff --git a/server/service/reportService.go b/server/service/reportService.go index d58b9cd..7daf69b 100644 --- a/server/service/reportService.go +++ b/server/service/reportService.go @@ -4,11 +4,12 @@ import ( "sort" "time" + "github.com/akhilrex/hammond/common" "github.com/akhilrex/hammond/db" "github.com/akhilrex/hammond/models" ) -func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models.MileageModel, err error) { +func GetMileageByVehicleId(vehicleId string, since time.Time, mileageOption string) (mileage []models.MileageModel, err error) { data, err := db.GetFillupsByVehicleIdSince(vehicleId, since) if err != nil { return nil, err @@ -36,14 +37,48 @@ func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models. 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)) { - distance := float32(currentFillup.OdoReading - lastFillup.OdoReading) - mileage.Mileage = distance / currentFillup.FuelQuantity - mileage.CostPerMile = distance / currentFillup.TotalAmount + 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; } diff --git a/ui/src/components/mileageChart.vue b/ui/src/components/mileageChart.vue index f6fea40..4b37f98 100644 --- a/ui/src/components/mileageChart.vue +++ b/ui/src/components/mileageChart.vue @@ -3,9 +3,20 @@ import { Line } from 'vue-chartjs' import axios from 'axios' import { mapState } from 'vuex' +import { string } from 'yargs' export default { extends: Line, - props: { vehicle: { type: Object, required: true }, since: { type: Date, default: '' }, user: { type: Object, required: true } }, + props: { + vehicle: { type: Object, required: true }, + since: { type: Date, default: '' }, + user: { type: Object, required: true }, + mileageOption: { type: string, default: 'litre_100km' }, + }, + data: function() { + return { + chartData: [], + } + }, computed: { ...mapState('utils', ['isMobile']), }, @@ -17,20 +28,28 @@ export default { this.fetchMileage() }, }, - data: function() { - return { - chartData: [], - } - }, mounted() { this.fetchMileage() }, methods: { showChart() { + let mileageLabel = '' + switch (this.mileageOption) { + case 'litre_100km': + mileageLabel = 'L/100km' + break + case 'km_litre': + mileageLabel = 'km/L' + break + case 'mpg': + mileageLabel = 'mpg' + break + } + var labels = this.chartData.map((x) => x.date.substr(0, 10)) var dataset = { steppedLine: true, - label: `${this.$t('odometer')} (${this.$t('unit.short.' + this.user.distanceUnitDetail.key)}/${this.$t('unit.short.' + this.vehicle.fuelUnitDetail.key)})`, + label: `Mileage (${mileageLabel})`, fill: true, data: this.chartData.map((x) => x.mileage), } @@ -41,6 +60,7 @@ export default { .get(`/api/vehicles/${this.vehicle.id}/mileage`, { params: { since: this.since, + mileageOption: this.mileageOption, }, }) .then((response) => { diff --git a/ui/src/router/views/vehicle.vue b/ui/src/router/views/vehicle.vue index c2413b6..59c574c 100644 --- a/ui/src/router/views/vehicle.vue +++ b/ui/src/router/views/vehicle.vue @@ -48,6 +48,12 @@ export default { { label: this.$t('alltime'), value: 'all_time' }, ], dateRangeOption: 'past_30_days', + mileageOptions: [ + { label: 'L/100km', value: 'litre_100km' }, + { label: 'km/L', value: 'km_litre' }, + { label: 'mpg', value: 'mpg' }, + ], + mileageOption: 'litre_100km', } }, computed: { @@ -529,14 +535,25 @@ export default {