Skip to content

Commit

Permalink
Merge pull request #40 from traveltime-dev/throttling
Browse files Browse the repository at this point in the history
[feature] throttling
  • Loading branch information
olivierdalang authored Jun 16, 2021
2 parents 853cd79 + d491780 commit 7801910
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 4 deletions.
Binary file modified docs/images/reference/config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ The main settings dialog can be open using the ![](images/icons/settings.svg#ico

**Clear cache** : this button allows to clear the request cache. All requests are saved to a cache file, to avoid the need of hitting the API if an identical request was already made. By clearing the cache, all saved queries will be deleted. This shouldn't be necessary unless you have a very high usage of the application and the cache file gets too big.

**Throttle API calls** : when enabled, the plugin will check how many calls were made to the API during the last minute. If above the provided threshold, the plugin will wait before sending the next query to avoid hitting the limit. Use this option if you prefer to avoid API limit errors from interrupting your workflows.

**Log API calls to the message logs** : this settings makes the plugin log all requests and responses to the QGIS Message Log, allowing to inspect what's happening in case you encounter errors.

**Disable HTTPS certificate verification** : under certain circumstances (such as connection from an enterprise network), requests made from Python may fail because the SSL certificates can not be verified. If this happens, you can disable the verification by checking this box. Please be aware that this makes your requests to the API more vulnerable to interception by an attacker.
Expand Down
15 changes: 13 additions & 2 deletions travel_time_platform_plugin/algorithms/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import requests
import json
import time
import os
import collections
from qgis.PyQt.QtCore import QSettings
from qgis.PyQt.QtTest import QTest

from qgis.core import (
Qgis,
Expand Down Expand Up @@ -201,14 +203,23 @@ def processAlgorithmMakeRequest(
)
)

response = cache.instance.cached_requests.request(
request = requests.Request(
self.method,
full_url,
data=json_data,
params=params,
headers=headers,
verify=not disable_https,
).prepare()

cached = cache.instance.cached_requests.cache.has_key(
cache.instance.cached_requests.cache.create_key(request)
)
if not cached:
throttling = cache.instance.throttling_info()
log(f"Throttling query ({throttling} s)")
QTest.qWait(throttling * 1000)

response = cache.instance.cached_requests.send(request, verify=not disable_https)

try:
response_data = json.loads(response.text)
Expand Down
32 changes: 31 additions & 1 deletion travel_time_platform_plugin/cache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import timedelta, datetime
import os

from qgis.PyQt.QtCore import QCoreApplication, QStandardPaths
from qgis.PyQt.QtCore import QCoreApplication, QStandardPaths, QSettings

from .libraries import requests_cache

Expand Down Expand Up @@ -42,5 +43,34 @@ def prepare(self):
allowable_methods=("GET", "POST"),
)

def throttling_info(self):
"""
Returns how long we must wait in seconds before next request according to throttling settings
"""
throttle = QSettings().value("traveltime_platform/throttle_calls_enabled", False, type=bool)
throttle_value = QSettings().value("traveltime_platform/throttle_calls_value", 300, type=int)

if not throttle:
return False

date = datetime.utcnow() - timedelta(seconds=60)
oldest = datetime.utcnow()
count = 0
for key in self.cached_requests.cache.responses:
try:
response, created_at = self.cached_requests.cache.responses[key]
except KeyError:
continue
if created_at >= date:
oldest = min(oldest, created_at)
count += 1

if count < throttle_value:
return False

return (datetime.utcnow() - oldest).seconds




instance = Cache()
2 changes: 2 additions & 0 deletions travel_time_platform_plugin/metadata.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ changelog=- 2019-02-12 : version 0.1-alpha
- 2021-04-27 : version 1.4.0
- changed : updated tiles urls
- fix : geocoder now returns unmatched features
- xxxx-xx-xx : version master
- feature : added throttling to avoid API overflow

tags=travel,time,platform,api,distance,matrix,geocoder,geocode,geocoding,isochrone,isochrones,routing,polygon,polygons,network analysis,shortest path
homepage=https://qgis.traveltimeplatform.com/
Expand Down
13 changes: 12 additions & 1 deletion travel_time_platform_plugin/ui.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import webbrowser
from qgis.PyQt.QtCore import Qt, QSettings, QDateTime, QDate, QTime, QUrl
from qgis.PyQt.QtCore import Qt, QSettings, QDateTime, QDate, QTime, QUrl, QSettings
from qgis.PyQt.QtWidgets import QDialog, QDateTimeEdit, QWidget
from qgis.PyQt import uic

Expand Down Expand Up @@ -38,6 +38,7 @@ def __init__(self):
self.endpointResetButton.pressed.connect(self.reset_endpoint)
self.apiKeyHelpLabel.setTextInteractionFlags(Qt.TextBrowserInteraction)
self.apiKeyHelpLabel.setOpenExternalLinks(True)
self.throttleCallsCheckBox.toggled.connect(self.throttleCallsSpinBox.setEnabled)

def showEvent(self, *args, **kwargs):
super().showEvent(*args, **kwargs)
Expand All @@ -62,6 +63,13 @@ def showEvent(self, *args, **kwargs):
self.disableHttpsCheckBox.setChecked(
s.value("traveltime_platform/disable_https", False, type=bool)
)
# throttling
self.throttleCallsCheckBox.setChecked(
s.value("traveltime_platform/throttle_calls_enabled", False, type=bool)
)
self.throttleCallsSpinBox.setValue(
s.value("traveltime_platform/throttle_calls_value", 300, type=int)
)
# refresh current cache
self.refresh_cache_label()

Expand Down Expand Up @@ -109,6 +117,9 @@ def accept(self, *args, **kwargs):
s.setValue(
"traveltime_platform/disable_https", self.disableHttpsCheckBox.isChecked()
)
# throttling
s.setValue("traveltime_platform/throttle_calls_enabled", self.throttleCallsCheckBox.isChecked())
s.setValue("traveltime_platform/throttle_calls_value", self.throttleCallsSpinBox.value())
# endpoint
s.setValue("traveltime_platform/custom_endpoint", self.endpointLineEdit.text())

Expand Down
44 changes: 44 additions & 0 deletions travel_time_platform_plugin/ui/ConfigDialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,50 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QCheckBox" name="throttleCallsCheckBox">
<property name="toolTip">
<string>If checked, the plugin will throttle API calls to avoid hitting the limit</string>
</property>
<property name="text">
<string>Throttle API calls to </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="throttleCallsSpinBox">
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>300</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>per minute</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="logCallsCheckBox">
<property name="text">
Expand Down

0 comments on commit 7801910

Please sign in to comment.