add page to plan a pitstop
This commit is contained in:
		
							parent
							
								
									28990f27fa
								
							
						
					
					
						commit
						9427ed50ad
					
				@ -190,6 +190,11 @@ class FillingStation(db.Model):
 | 
			
		||||
    brand = db.Column(db.Text(), nullable=False)
 | 
			
		||||
    lat = db.Column(db.Numeric(2, 5), nullable=False)
 | 
			
		||||
    lng = db.Column(db.Numeric(2, 5), nullable=False)
 | 
			
		||||
    last_update = db.Column(db.DateTime)
 | 
			
		||||
    diesel = db.Column(db.Numeric(10, 3), default=0)
 | 
			
		||||
    e5 = db.Column(db.Numeric(10, 3), default=0)
 | 
			
		||||
    e10 = db.Column(db.Numeric(10, 3), default=0)
 | 
			
		||||
    open = db.Column(db.Boolean())
 | 
			
		||||
 | 
			
		||||
    def as_dict(self):
 | 
			
		||||
        res = {}
 | 
			
		||||
 | 
			
		||||
@ -3,3 +3,4 @@ from .admin import *
 | 
			
		||||
from .misc import *
 | 
			
		||||
from .pitstop import *
 | 
			
		||||
from .service import *
 | 
			
		||||
from .filling_stations import *
 | 
			
		||||
@ -6,9 +6,8 @@ from datetime import date
 | 
			
		||||
 | 
			
		||||
from ..entities import Vehicle, Consumable, Pitstop
 | 
			
		||||
from ..forms import SelectVehicleForm, SelectConsumableForm, CreatePitstopForm, EditPitstopForm, DeletePitStopForm
 | 
			
		||||
from ..tools import db_log_update, db_log_delete, db_log_add, get_latest_pitstop_for_vehicle, \
 | 
			
		||||
    get_latest_pitstop_for_vehicle_and_consumable, compute_lower_limits_for_new_pitstop, pitstop_service_key, \
 | 
			
		||||
    get_event_line_for_vehicle
 | 
			
		||||
from ..tools import db_log_update, db_log_delete, db_log_add, pitstop_service_key, \
 | 
			
		||||
    get_event_line_for_vehicle, update_filling_station_prices
 | 
			
		||||
from .. import app, db
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,6 +49,27 @@ def select_consumable_for_new_pitstop(vid):
 | 
			
		||||
    return render_template('selectConsumableForVehicle.html', vehicle=vehicle, form=form)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.route('/pitstops/vehicle/<int:vid>/consumable/<int:cid>/plan', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
def plan_pit_stop_form(vid, cid):
 | 
			
		||||
    vehicle = Vehicle.query.get(vid)
 | 
			
		||||
    if vehicle is None or vehicle not in current_user.vehicles:
 | 
			
		||||
        return redirect(url_for('select_vehicle_for_new_pitstop'))
 | 
			
		||||
 | 
			
		||||
    consumable = Consumable.query.get(cid)
 | 
			
		||||
    if consumable not in vehicle.consumables:
 | 
			
		||||
        return redirect(url_for('select_consumable_for_new_pitstop', vid=vid))
 | 
			
		||||
 | 
			
		||||
    update_filling_station_prices([x.id for x in current_user.favourite_filling_stations])
 | 
			
		||||
 | 
			
		||||
    offers = []
 | 
			
		||||
    for fs in current_user.favourite_filling_stations:
 | 
			
		||||
        if fs.open:
 | 
			
		||||
            offers.append((fs, getattr(fs, consumable.ext_id),))
 | 
			
		||||
 | 
			
		||||
    return render_template('planPitStopForm.html', vehicle=vehicle, consumable=consumable, offers=offers)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.route('/pitstops/vehicle/<int:vid>/consumable/<int:cid>/create', methods=['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
def create_pit_stop_form(vid, cid):
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								app/templates/planPitStopForm.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								app/templates/planPitStopForm.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
{% extends "layout.html" %}
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
<div class="col-md-2" ></div>
 | 
			
		||||
<div class="col-md-8">
 | 
			
		||||
    <div class="panel panel-default">
 | 
			
		||||
        <div class="panel-body">
 | 
			
		||||
            <h3>Plan Pitstop for '{{ vehicle.name }}'</h3>
 | 
			
		||||
            Price comparision for {{ consumable.name }}:
 | 
			
		||||
            <div class="table-responsive">
 | 
			
		||||
                <table id="compare" class="table table-striped table-bordered table-condensed tablesorter">
 | 
			
		||||
                    <thead>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <th>Filling Station</th>
 | 
			
		||||
                            <th>Price/{{ consumable.unit }}</th>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tbody>
 | 
			
		||||
                        {% for offer in offers %}
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <div class="row filling_station_info" style="border: 0px">
 | 
			
		||||
                                        <div class="col-md-8">
 | 
			
		||||
                                            <div>{{ offer[0].name }}</div>
 | 
			
		||||
                                            <div>{{ offer[0].street }} {{ offer[0].houseNumber }}</div>
 | 
			
		||||
                                            <div>{{ offer[0].postCode }} {{ offer[0].place }}</div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                        <div class="col-md-4" style="height: 60px;">
 | 
			
		||||
                                            <img src="/static/logos/{{ offer[0].brand|lower }}.png">
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </td>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    {{ offer[1] }} €/{{ consumable.unit }}
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </tbody>
 | 
			
		||||
                </table>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<script>
 | 
			
		||||
    $(document).ready(function() {
 | 
			
		||||
        $("#compare").tablesorter({sortList: [[1,0]]});
 | 
			
		||||
    });
 | 
			
		||||
</script>
 | 
			
		||||
<div class="col-md-2" ></div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										83
									
								
								app/tools.py
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								app/tools.py
									
									
									
									
									
								
							@ -1,7 +1,10 @@
 | 
			
		||||
from sqlalchemy import or_
 | 
			
		||||
import requests
 | 
			
		||||
import logging
 | 
			
		||||
from datetime import date
 | 
			
		||||
from datetime import date, datetime, timedelta
 | 
			
		||||
 | 
			
		||||
from .entities import Pitstop
 | 
			
		||||
from .entities import Pitstop, FillingStation
 | 
			
		||||
from . import db, app
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConsumableStats:
 | 
			
		||||
@ -34,7 +37,7 @@ class ConsumableStats:
 | 
			
		||||
                self.average_amount.append(
 | 
			
		||||
                    StatsEvent(
 | 
			
		||||
                        current_ps.date,
 | 
			
		||||
                        round(100 * current_ps.amount/(current_ps.odometer - last_ps.odometer), 2)))
 | 
			
		||||
                        round(100 * current_ps.amount / (current_ps.odometer - last_ps.odometer), 2)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VehicleStats:
 | 
			
		||||
@ -104,9 +107,9 @@ def get_latest_pitstop_for_vehicle(vehicle_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())\
 | 
			
		||||
    latest_pitstop = Pitstop.query \
 | 
			
		||||
        .filter(Pitstop.vehicle_id == vehicle_id) \
 | 
			
		||||
        .order_by(Pitstop.id.desc()) \
 | 
			
		||||
        .first()
 | 
			
		||||
    return latest_pitstop
 | 
			
		||||
 | 
			
		||||
@ -118,10 +121,10 @@ def get_latest_pitstop_for_vehicle_and_consumable(vehicle_id, consumable_id):
 | 
			
		||||
    :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())\
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@ -150,33 +153,6 @@ def compute_lower_limits_for_new_pitstop(latest_pitstop, last_pitstop_consumable
 | 
			
		||||
    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,
 | 
			
		||||
    #                            last_pitstop_consumable.overall_amount,
 | 
			
		||||
    #                            latest_pitstop.date,
 | 
			
		||||
    #                            last_pitstop_consumable.costs,
 | 
			
		||||
    #                            consumable_id)
 | 
			
		||||
    #         else:
 | 
			
		||||
    #             return Pitstop(last_pitstop_consumable.odometer,
 | 
			
		||||
    #                            last_pitstop_consumable.overall_amount,
 | 
			
		||||
    #                            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:
 | 
			
		||||
    #             litres = latest_pitstop.overall_amount
 | 
			
		||||
    #             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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pitstop_service_key(x):
 | 
			
		||||
    return x.odometer, x.date
 | 
			
		||||
 | 
			
		||||
@ -188,4 +164,35 @@ def get_event_line_for_vehicle(vehicle):
 | 
			
		||||
    for service in vehicle.services:
 | 
			
		||||
        data.append(service)
 | 
			
		||||
    data.sort(key=pitstop_service_key)
 | 
			
		||||
    return data
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_filling_station_prices(ids):
 | 
			
		||||
    max_age = (datetime.now() - timedelta(minutes=15)).strftime('%Y-%m-%d %H:%M')
 | 
			
		||||
 | 
			
		||||
    res = db.session. \
 | 
			
		||||
        query(FillingStation). \
 | 
			
		||||
        filter(FillingStation.id.in_(ids)). \
 | 
			
		||||
        filter(or_(FillingStation.last_update==None, FillingStation.last_update < max_age)).\
 | 
			
		||||
        all()
 | 
			
		||||
 | 
			
		||||
    if len(res) > 0:
 | 
			
		||||
        map = {x.id:x for x in res}
 | 
			
		||||
        query_ids = [x.id for x in res]
 | 
			
		||||
        api_key = app.config['TANKERKOENIG_API_KEY']
 | 
			
		||||
        url = 'https://creativecommons.tankerkoenig.de/json/prices.php'
 | 
			
		||||
        params = {
 | 
			
		||||
            'apikey': api_key, 'ids': ','.join(query_ids)
 | 
			
		||||
        }
 | 
			
		||||
        response = requests.get(url, params=params)
 | 
			
		||||
        prices = response.json()['prices']
 | 
			
		||||
        for price in prices:
 | 
			
		||||
            id = price
 | 
			
		||||
            station_status = prices[id]
 | 
			
		||||
            print(id, station_status)
 | 
			
		||||
            map[id].diesel = station_status['diesel']
 | 
			
		||||
            map[id].e10 = station_status['e10']
 | 
			
		||||
            map[id].e5 = station_status['e5']
 | 
			
		||||
            map[id].open = station_status['status'] == 'open'
 | 
			
		||||
            map[id].last_update = datetime.now()
 | 
			
		||||
        db.session.commit()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user