Merge pull request #19 from AlfHou/feat/state_mileage_option
Add option in vehicle stats to choose what mileage metric to show
This commit is contained in:
@@ -25,6 +25,33 @@ func RandString(n int) string {
|
|||||||
return string(b)
|
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
|
// A Util function to generate jwt_token which can be used in the request header
|
||||||
func GenToken(id string, role db.Role) (string, string) {
|
func GenToken(id string, role db.Role) (string, string) {
|
||||||
jwt_token := jwt.New(jwt.GetSigningMethod("HS256"))
|
jwt_token := jwt.New(jwt.GetSigningMethod("HS256"))
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func getMileageForVehicle(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fillups, err := service.GetMileageByVehicleId(searchByIdQuery.Id, model.Since)
|
fillups, err := service.GetMileageByVehicleId(searchByIdQuery.Id, model.Since, model.MileageOption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusUnprocessableEntity, common.NewError("getMileageForVehicle", err))
|
c.JSON(http.StatusUnprocessableEntity, common.NewError("getMileageForVehicle", err))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type MileageModel struct {
|
|||||||
FuelQuantity float32 `form:"fuelQuantity" json:"fuelQuantity" binding:"required"`
|
FuelQuantity float32 `form:"fuelQuantity" json:"fuelQuantity" binding:"required"`
|
||||||
PerUnitPrice float32 `form:"perUnitPrice" json:"perUnitPrice" binding:"required"`
|
PerUnitPrice float32 `form:"perUnitPrice" json:"perUnitPrice" binding:"required"`
|
||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
|
DistanceUnit db.DistanceUnit `form:"distanceUnit" json:"distanceUnit"`
|
||||||
Mileage float32 `form:"mileage" json:"mileage" binding:"mileage"`
|
Mileage float32 `form:"mileage" json:"mileage" binding:"mileage"`
|
||||||
CostPerMile float32 `form:"costPerMile" json:"costPerMile" binding:"costPerMile"`
|
CostPerMile float32 `form:"costPerMile" json:"costPerMile" binding:"costPerMile"`
|
||||||
OdoReading int `form:"odoReading" json:"odoReading" binding:"odoReading"`
|
OdoReading int `form:"odoReading" json:"odoReading" binding:"odoReading"`
|
||||||
@@ -35,4 +35,5 @@ func (b *MileageModel) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
type MileageQueryModel struct {
|
type MileageQueryModel struct {
|
||||||
Since time.Time `json:"since" query:"since" form:"since"`
|
Since time.Time `json:"since" query:"since" form:"since"`
|
||||||
|
MileageOption string `json:"mileageOption" query:"mileageOption" form:"mileageOption"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/akhilrex/hammond/common"
|
||||||
"github.com/akhilrex/hammond/db"
|
"github.com/akhilrex/hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"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)
|
data, err := db.GetFillupsByVehicleIdSince(vehicleId, since)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -36,14 +37,48 @@ func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models.
|
|||||||
PerUnitPrice: currentFillup.PerUnitPrice,
|
PerUnitPrice: currentFillup.PerUnitPrice,
|
||||||
OdoReading: currentFillup.OdoReading,
|
OdoReading: currentFillup.OdoReading,
|
||||||
Currency: currentFillup.Currency,
|
Currency: currentFillup.Currency,
|
||||||
|
DistanceUnit: currentFillup.DistanceUnit,
|
||||||
Mileage: 0,
|
Mileage: 0,
|
||||||
CostPerMile: 0,
|
CostPerMile: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentFillup.IsTankFull != nil && *currentFillup.IsTankFull && (currentFillup.HasMissedFillup == nil || !(*currentFillup.HasMissedFillup)) {
|
if currentFillup.IsTankFull != nil && *currentFillup.IsTankFull && (currentFillup.HasMissedFillup == nil || !(*currentFillup.HasMissedFillup)) {
|
||||||
distance := float32(currentFillup.OdoReading - lastFillup.OdoReading)
|
currentOdoReading := float32(currentFillup.OdoReading);
|
||||||
mileage.Mileage = distance / currentFillup.FuelQuantity
|
lastFillupOdoReading := float32(lastFillup.OdoReading);
|
||||||
mileage.CostPerMile = distance / currentFillup.TotalAmount
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,20 @@ import { Line } from 'vue-chartjs'
|
|||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
import { string } from 'yargs'
|
||||||
export default {
|
export default {
|
||||||
extends: Line,
|
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: {
|
computed: {
|
||||||
...mapState('utils', ['isMobile']),
|
...mapState('utils', ['isMobile']),
|
||||||
},
|
},
|
||||||
@@ -17,20 +28,28 @@ export default {
|
|||||||
this.fetchMileage()
|
this.fetchMileage()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
chartData: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchMileage()
|
this.fetchMileage()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showChart() {
|
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 labels = this.chartData.map((x) => x.date.substr(0, 10))
|
||||||
var dataset = {
|
var dataset = {
|
||||||
steppedLine: true,
|
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,
|
fill: true,
|
||||||
data: this.chartData.map((x) => x.mileage),
|
data: this.chartData.map((x) => x.mileage),
|
||||||
}
|
}
|
||||||
@@ -41,6 +60,7 @@ export default {
|
|||||||
.get(`/api/vehicles/${this.vehicle.id}/mileage`, {
|
.get(`/api/vehicles/${this.vehicle.id}/mileage`, {
|
||||||
params: {
|
params: {
|
||||||
since: this.since,
|
since: this.since,
|
||||||
|
mileageOption: this.mileageOption,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ export default {
|
|||||||
{ label: this.$t('alltime'), value: 'all_time' },
|
{ label: this.$t('alltime'), value: 'all_time' },
|
||||||
],
|
],
|
||||||
dateRangeOption: 'past_30_days',
|
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: {
|
computed: {
|
||||||
@@ -529,14 +535,25 @@ export default {
|
|||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column" :class="isMobile ? 'has-text-centered' : ''"> <h1 class="title">{{ $t('statistics') }}</h1></div>
|
<div class="column" :class="isMobile ? 'has-text-centered' : ''"> <h1 class="title">{{ $t('statistics') }}</h1></div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<b-select v-model="dateRangeOption" class="is-pulled-right is-medium">
|
<div class="columns is-pulled-right is-medium">
|
||||||
<option v-for="option in dateRangeOptions" :key="option.value" :value="option.value">
|
<div class="column">
|
||||||
{{ option.label }}
|
<b-select v-model="mileageOption">
|
||||||
</option>
|
<option v-for="option in mileageOptions" :key="option.value" :value="option.value">
|
||||||
</b-select></div
|
{{ option.label }}
|
||||||
>
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<b-select v-model="dateRangeOption">
|
||||||
|
<option v-for="option in dateRangeOptions" :key="option.value" :value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MileageChart :vehicle="vehicle" :since="getStartDate()" :user="me" :height="300" />
|
<MileageChart :vehicle="vehicle" :since="getStartDate()" :user="me" :height="300" :mileage-option="mileageOption" />
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user