diff --git a/app/__init__.py b/app/__init__.py index 0c226bb..d9343ec 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,12 +1,13 @@ -from flask import Flask +from flask import Flask, make_response from flask import g from flask_mail import Mail from flask_security import Security, SQLAlchemyUserDatastore, user_registered -from flask_security.forms import LoginForm from flask_sqlalchemy import SQLAlchemy import os from config import config -from flask.ext.security.forms import LoginForm +from flask_security.forms import LoginForm +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address from .forms import * @@ -14,6 +15,19 @@ from .forms import * app = Flask(__name__) app.config.from_object(config[os.getenv('FLASK_CONFIG') or 'default']) +limiter = Limiter( + app, + key_func=get_remote_address, + default_limits=["200 per day", "50 per hour"] +) + + +@app.errorhandler(429) +def ratelimit_handler(e): + return make_response( + jsonify(error="ratelimit exceeded %s" % e.description) + , 429 + ) db = SQLAlchemy(app) mail = Mail(app) diff --git a/app/entities.py b/app/entities.py index d138bbf..a82e7b8 100644 --- a/app/entities.py +++ b/app/entities.py @@ -34,6 +34,9 @@ class User(db.Model, UserMixin): password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) + home_lat = db.Column(db.Numeric(2, 5), default=0) + home_long = db.Column(db.Numeric(2, 5), default=0) + home_zoom = db.Column(db.Integer(), default=0) vehicles = db.relationship( 'Vehicle' diff --git a/app/routes/account.py b/app/routes/account.py index cafb537..f48f84d 100644 --- a/app/routes/account.py +++ b/app/routes/account.py @@ -1,18 +1,21 @@ -from flask import url_for, redirect, render_template, request +from flask import url_for, redirect, render_template, request, jsonify from flask_security import login_required from flask_security.core import current_user from sqlalchemy.exc import IntegrityError +import requests from ..entities import Vehicle, Consumable from ..forms import EditVehicleForm, DeleteVehicleForm, DeleteAccountForm from ..tools import db_log_update, db_log_delete, db_log_add -from .. import app, db, user_datastore +from .. import app, db, user_datastore, limiter @app.route('/account', methods=['GET']) @login_required def get_account_page(): - return render_template('account.html') + print(current_user.home_lat, current_user.home_long) + return render_template('account.html', + map_pos=(current_user.home_lat, current_user.home_long, current_user.home_zoom)) @app.route('/account/vehicle/edit/', methods=['GET', 'POST']) @@ -37,7 +40,7 @@ def edit_vehicle(vid): vehicle.name = form.name.data # we cannot delete consumables where there are pitstops for => report error vehicle.consumables = [] - for consumable_id in form.consumables.data: + for consumable_id in form.consumables.data: consumable = Consumable.query.get(consumable_id) if consumable is not None: vehicle.consumables.append(consumable) @@ -130,19 +133,60 @@ def delete_account(): return render_template('deleteAccountForm.html', form=form) +@app.route('/account/home', methods=['GET']) +@login_required +def get_users_home(): + return jsonify( + {'lat': float(current_user.home_lat), 'long': float(current_user.home_long), 'zoom': current_user.home_zoom}) + + +@app.route('/account/home', methods=['POST']) +@login_required +def set_users_home(): + current_user.home_lat = request.json['lat'] + current_user.home_long = request.json['long'] + current_user.home_zoom = request.json['zoom'] + db.session.commit() + return jsonify({'lat': float(current_user.home_lat)}) + + @app.route('/filling_stations', methods=['GET']) -#@login_required +@login_required +@limiter.limit('1 per second') def query_filling_stations(): api_key = app.config['TANKERKOENIG_API_KEY'] - lat = request.args.get('latitude') - lon = request.args.get('longitude') - rad = request.args.get('radius', default=10) + + latitude = request.args.get('latitude') + longitude = request.args.get('longitude') + radius = request.args.get('radius', default=1.5) type = request.args.get('type', default='all') - sort = request.args.get('sort', default='price') - return lat - + sort = request.args.get('sort', default='dist') + + url = 'https://creativecommons.tankerkoenig.de/json/list.php' + params = { + 'lat': latitude, 'lng': longitude, 'rad': radius, 'apikey': api_key, 'type': type, 'sort': sort + } + response = requests.get(url, params=params) + print(response.url) + return jsonify(response.json()) + + @app.route('/filling_stations/', methods=['GET']) -#@login_required +@login_required +@limiter.limit('1 per second') def query_filling_station_details(fsid): api_key = app.config['TANKERKOENIG_API_KEY'] - return fsid + + if ',' in fsid: + # more than one id, redirect to method 2 (preisabfrage) + url = 'https://creativecommons.tankerkoenig.de/json/prices.php' + params = { + 'apikey': api_key, 'ids': fsid + } + else: + url = 'https://creativecommons.tankerkoenig.de/json/detail.php' + params = { + 'apikey': api_key, 'id': fsid + } + response = requests.get(url, params=params) + return jsonify(response.json()) diff --git a/app/static/fillingstations.js b/app/static/fillingstations.js new file mode 100644 index 0000000..adf44d2 --- /dev/null +++ b/app/static/fillingstations.js @@ -0,0 +1,162 @@ +// initially go to Brandenburger Tor +var lat = 52.516275, + lon = 13.377704, + zoom = 13; + +var map; + +var filling_stations = {}; +var filling_station_markers; + +query_location = function(updater) { + if(navigator.geolocation) { + navigator.geolocation.getCurrentPosition(function(position) { + lat = position.coords.latitude; + lon = position.coords.longitude; + if(updater){ + updater(lat, lon); + } + }); + } +} + +update_map = function() { + var lonLat = new OpenLayers.LonLat( lon, lat ) + .transform( + new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984 + map.getProjectionObject() // to Spherical Mercator Projection + ); + map.setCenter (lonLat, zoom); + //load_filling_stations(); +} + +load_filling_stations = function() { + var url = '/filling_stations?latitude=' + lat + '&longitude='+ lon + '&type=all&radius=5&sort=dist'; + $.ajax({ + type: 'GET', + url: url, + success: function(data) { + data.stations.forEach(function(station) { + if (!(station.id in filling_stations)) { + filling_stations[station.id] = station; + filling_stations[station.id].marker = false; + } + }); + update_filling_station_markers(); + } + }); +} + +clicked_on_filling_station_marker = function(station) { + return function() { + console.log(station) +/* + var desc = '
'+ + '
' + + '
' + station.brand + '
' + + '
Super:
' + + '
'+ station.e5 + '€
' + + '
' + + '
' + + '
' + station.name + '
' + + '
Super E10:
' + + '
'+ station.e10 + '€
' + + '
' + + '
' + + '
' + station.street + ' ' + station.houseNumber + '
' + + '
Diesel:
' + + '
'+ station.diesel + '€
' + + '
' + + '
' + + '
' + station.postCode + ' ' + station.place + '
' + + '
Open:
' + + '
'+ station.isOpen + '€
' + + '
' + + '
'; + $('#station_info').empty(); + $('#station_info').html(desc); +*/ + } +} + +update_filling_station_markers = function() { + for(id in filling_stations) { + var station = filling_stations[id]; + if(!station.marker) { + console.log(station.id); + var lonLat = new OpenLayers.LonLat(station.lng, station.lat) + .transform(new OpenLayers.Projection('EPSG:4326'), map.getProjectionObject()); + var marker = new OpenLayers.Marker(lonLat); + marker.events.register('click', marker, clicked_on_filling_station_marker(station)); + filling_station_markers.addMarker(marker); + station.marker = true; + } + } +} + +activate_map = function(map_div_id, button_ids, home_lat, home_long, home_zoom) { + // resize to reasonable height + $('#' + map_div_id).css('height',0.75*($('#' + map_div_id).css('width'))); + + // init map + map = new OpenLayers.Map(map_div_id); + map.addLayer(new OpenLayers.Layer.OSM()); + map.events.register('moveend', null, function(e){ + var p = e.object.center.clone(); + var p = p.transform(map.getProjectionObject(), 'EPSG:4326'); + lon = p.lon; + lat = p.lat; + zoom = e.object.zoom; + }); + filling_station_markers = new OpenLayers.Layer.Markers('Filling Stations'); + map.addLayer(filling_station_markers); + update_map(); + if ((home_lat == 0) && (home_long == 0)) { + query_location(update_map); + } else { + lat = home_lat; + lon = home_long; + zoom = home_zoom; + console.log(lat, long); + update_map(); + } + + // get button + $('#'+button_ids[0]).click(function(e){ + console.log('clicked get button'); + load_filling_stations(); + }); + // set home button + $('#'+button_ids[1]).click(function(e){ + console.log('clicked set home'); + console.log(lon); + console.log(lat); + console.log(zoom); + $.ajax({ + type: 'POST', + url: '/account/home', + data: JSON.stringify({'long': lon, 'lat': lat, 'zoom': zoom}), + dataType: 'json', + timeout: 1000, + contentType : 'application/json' + }); + }); + // go home button + $('#'+button_ids[2]).click(function(e){ + console.log('clicked the go home button'); + $.ajax({ + type: 'GET', + url: '/account/home', + dataType: 'json', + timeout: 1000, + success: function(data) { + console.log(data); + lat = data.lat; + lon = data.long; + zoom = data.zoom; + update_map(); + }, + contentType : 'application/json' + }); + }); +} \ No newline at end of file diff --git a/app/templates/account.html b/app/templates/account.html index 004a005..947d7ce 100644 --- a/app/templates/account.html +++ b/app/templates/account.html @@ -63,6 +63,37 @@ +
+
Filling Stations
+
+
+
+
+
+
+ + + + + + +
Station
+
+ +
+
+
+
+ +
Account
diff --git a/app/templates/layout.html b/app/templates/layout.html index 3420b4d..6e1962e 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -133,7 +133,9 @@ + +