2016-04-28 06:32:51 +02:00
|
|
|
import logging
|
2016-07-04 20:19:59 +02:00
|
|
|
from datetime import date
|
2016-04-28 06:32:51 +02:00
|
|
|
|
2016-07-16 19:29:25 +02:00
|
|
|
from .entities import Pitstop
|
2016-04-28 06:32:51 +02:00
|
|
|
|
2016-07-10 13:40:49 +02:00
|
|
|
|
|
|
|
class ConsumableStats:
|
|
|
|
def __init__(self, vehicle, consumable):
|
|
|
|
self.name = consumable.name
|
|
|
|
self.id = consumable.id
|
|
|
|
self.unit = consumable.unit
|
2016-07-11 06:59:34 +02:00
|
|
|
self.overall_amount = 0
|
2016-07-10 13:40:49 +02:00
|
|
|
self.average_distance = 0
|
|
|
|
self.average_amount_fuelled = 0
|
|
|
|
self.average_amount_used = 0
|
|
|
|
self.average_amount = []
|
2016-07-11 06:59:34 +02:00
|
|
|
self.amounts = []
|
2016-07-10 13:40:49 +02:00
|
|
|
|
|
|
|
pitstops = [stop for stop in vehicle.pitstops if stop.consumable_id == consumable.id]
|
|
|
|
pitstop_count = len(pitstops)
|
|
|
|
|
|
|
|
if pitstop_count > 0:
|
|
|
|
for pitstop in pitstops:
|
2016-07-11 06:59:34 +02:00
|
|
|
self.overall_amount += pitstop.amount
|
|
|
|
self.amounts.append(StatsEvent(pitstop.date, pitstop.amount))
|
|
|
|
self.average_amount_fuelled = self.overall_amount / pitstop_count
|
2016-07-10 13:40:49 +02:00
|
|
|
if pitstop_count > 1:
|
|
|
|
overall_distance = vehicle.pitstops[-1].odometer - vehicle.pitstops[0].odometer
|
|
|
|
self.average_distance = overall_distance / (pitstop_count - 1)
|
2016-07-11 06:59:34 +02:00
|
|
|
self.average_amount_used = 100 * (self.overall_amount - pitstops[0].amount) / overall_distance
|
2016-07-10 13:40:49 +02:00
|
|
|
for index in range(1, pitstop_count):
|
|
|
|
last_ps = pitstops[index - 1]
|
|
|
|
current_ps = pitstops[index]
|
|
|
|
self.average_amount.append(
|
|
|
|
StatsEvent(
|
|
|
|
current_ps.date,
|
|
|
|
round(100 * current_ps.amount/(current_ps.odometer - last_ps.odometer), 2)))
|
2016-04-28 06:32:51 +02:00
|
|
|
|
|
|
|
|
2016-04-25 22:28:35 +02:00
|
|
|
class VehicleStats:
|
|
|
|
def __init__(self, vehicle):
|
|
|
|
self.name = vehicle.name
|
|
|
|
self.id = vehicle.id
|
|
|
|
self.overall_distance = 0
|
2016-06-21 21:06:41 +02:00
|
|
|
self.overall_costs = 0
|
2016-07-10 13:40:49 +02:00
|
|
|
self.consumables = []
|
|
|
|
self.odometers = []
|
|
|
|
|
|
|
|
for consumable in vehicle.consumables:
|
|
|
|
self.consumables.append(ConsumableStats(vehicle, consumable))
|
|
|
|
|
2016-11-06 13:22:54 +01:00
|
|
|
events = get_event_line_for_vehicle(vehicle)
|
|
|
|
pitstop_count = len(events)
|
2016-07-10 13:40:49 +02:00
|
|
|
|
|
|
|
if pitstop_count > 0:
|
2016-11-06 13:22:54 +01:00
|
|
|
for pitstop in events:
|
|
|
|
self.odometers.append(StatsEvent(pitstop.date, pitstop.odometer))
|
|
|
|
if pitstop.costs is not None:
|
2016-07-16 12:07:01 +02:00
|
|
|
self.overall_costs += pitstop.costs
|
2016-07-10 13:40:49 +02:00
|
|
|
|
|
|
|
if pitstop_count > 1:
|
2016-11-06 13:22:54 +01:00
|
|
|
self.overall_distance = events[-1].odometer - events[0].odometer
|
2016-04-28 06:32:51 +02:00
|
|
|
|
|
|
|
|
2016-05-02 06:42:43 +02:00
|
|
|
class StatsEvent:
|
|
|
|
def __init__(self, date, value):
|
|
|
|
self.date = date
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
|
2016-04-28 06:32:51 +02:00
|
|
|
def db_log_add(entity):
|
|
|
|
logging.info('db_add: %s' % str(entity))
|
|
|
|
|
|
|
|
|
|
|
|
def db_log_delete(entity):
|
|
|
|
logging.info('db_delete: %s' % str(entity))
|
|
|
|
|
|
|
|
|
|
|
|
def db_log_update(entity):
|
|
|
|
logging.info('db_update: %s' % str(entity))
|
2016-05-16 18:46:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
def check_vehicle_name_is_unique(current_user, name_field):
|
|
|
|
"""
|
|
|
|
Checks if the vehicle name given in the name_field is unique for the vehicles of the current user. An error is added
|
|
|
|
to the field it the name is not unique.
|
|
|
|
|
|
|
|
:param current_user: the user currently logged in
|
|
|
|
:param name_field: the form field to enter the name to
|
|
|
|
:return: True if the name is unique, False otherwise.
|
|
|
|
"""
|
|
|
|
vehicle_name = name_field.data
|
|
|
|
for vehicle in current_user.vehicles:
|
|
|
|
if vehicle.name == vehicle_name:
|
|
|
|
name_field.default = vehicle_name
|
|
|
|
name_field.errors.append('Vehicle "%s" already exists.' % vehicle_name)
|
|
|
|
return False
|
2016-07-04 20:19:59 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def get_latest_pitstop_for_vehicle(vehicle_id):
|
|
|
|
"""
|
|
|
|
return the latest pit stop for the vehicle with the given id.
|
|
|
|
:param vehicle_id: the id of the vehicle
|
|
|
|
:return: the latest pitstop or None if no pitstop exists
|
|
|
|
"""
|
|
|
|
latest_pitstop = Pitstop.query\
|
|
|
|
.filter(Pitstop.vehicle_id == vehicle_id)\
|
|
|
|
.order_by(Pitstop.id.desc())\
|
|
|
|
.first()
|
|
|
|
return latest_pitstop
|
|
|
|
|
|
|
|
|
|
|
|
def get_latest_pitstop_for_vehicle_and_consumable(vehicle_id, consumable_id):
|
|
|
|
"""
|
|
|
|
return the latest pit stop for the vehicle and consumable with the given ids.
|
|
|
|
:param vehicle_id: the id of the vehicle
|
|
|
|
:param consumable_id: the id of the consumable
|
|
|
|
:return: the latest pitstop or None if no pitstop exists
|
|
|
|
"""
|
|
|
|
latest_pitstop_consumable = Pitstop.query\
|
|
|
|
.filter(Pitstop.vehicle_id == vehicle_id)\
|
|
|
|
.filter(Pitstop.consumable_id == consumable_id)\
|
|
|
|
.order_by(Pitstop.id.desc())\
|
|
|
|
.first()
|
|
|
|
return latest_pitstop_consumable
|
|
|
|
|
|
|
|
|
|
|
|
def compute_lower_limits_for_new_pitstop(latest_pitstop, last_pitstop_consumable, consumable_id):
|
|
|
|
"""
|
|
|
|
This function figures out the lower limits for date and odometer of a new pitstop.
|
|
|
|
:param latest_pitstop:
|
|
|
|
:param last_pitstop_consumable:
|
|
|
|
:param consumable_id:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
odometer = 0
|
|
|
|
date_of_pitstop = date(1970, 1, 1)
|
|
|
|
amount = 0
|
|
|
|
costs = 0
|
|
|
|
|
|
|
|
if latest_pitstop is not None:
|
|
|
|
odometer = latest_pitstop.odometer
|
|
|
|
date_of_pitstop = latest_pitstop.date
|
|
|
|
|
|
|
|
if last_pitstop_consumable is not None:
|
|
|
|
amount = last_pitstop_consumable.amount
|
|
|
|
costs = last_pitstop_consumable.costs
|
|
|
|
|
|
|
|
return Pitstop(odometer, amount, date_of_pitstop, costs, consumable_id)
|
|
|
|
|
|
|
|
|
|
|
|
# if latest_pitstop is not None:
|
|
|
|
# if last_pitstop_consumable is not None and last_pitstop_consumable != latest_pitstop:
|
|
|
|
# if latest_pitstop.id > last_pitstop_consumable.id:
|
|
|
|
# return Pitstop(latest_pitstop.odometer,
|
2016-07-11 06:59:34 +02:00
|
|
|
# last_pitstop_consumable.overall_amount,
|
2016-07-04 20:19:59 +02:00
|
|
|
# latest_pitstop.date,
|
|
|
|
# last_pitstop_consumable.costs,
|
|
|
|
# consumable_id)
|
|
|
|
# else:
|
|
|
|
# return Pitstop(last_pitstop_consumable.odometer,
|
2016-07-11 06:59:34 +02:00
|
|
|
# last_pitstop_consumable.overall_amount,
|
2016-07-04 20:19:59 +02:00
|
|
|
# last_pitstop_consumable.date,
|
|
|
|
# last_pitstop_consumable.costs,
|
|
|
|
# consumable_id)
|
|
|
|
# else:
|
|
|
|
# # either only one pitstop exists or both are the same
|
|
|
|
# litres = 0
|
|
|
|
# costs = 0
|
|
|
|
# if latest_pitstop.consumable_id == last_pitstop_consumable.consumable_id:
|
2016-07-11 06:59:34 +02:00
|
|
|
# litres = latest_pitstop.overall_amount
|
2016-07-04 20:19:59 +02:00
|
|
|
# costs = latest_pitstop.costs
|
|
|
|
# return Pitstop(latest_pitstop.odometer, litres, latest_pitstop.date, costs, consumable_id)
|
|
|
|
# else:
|
|
|
|
# # No existing pitstop at all: insert fake data
|
|
|
|
# return Pitstop(0, 0, date(1970, 1, 1), 0, None)
|
|
|
|
|
2016-11-01 19:48:24 +01:00
|
|
|
|
|
|
|
def pitstop_service_key(x):
|
|
|
|
return x.odometer, x.date
|
|
|
|
|
|
|
|
|
2016-11-06 13:22:54 +01:00
|
|
|
def get_event_line_for_vehicle(vehicle):
|
|
|
|
data = []
|
|
|
|
for pitstop in vehicle.pitstops:
|
|
|
|
data.append(pitstop)
|
|
|
|
for service in vehicle.services:
|
|
|
|
data.append(service)
|
|
|
|
data.sort(key=pitstop_service_key)
|
|
|
|
return data
|