From de1b79d705385c543cebeb1127c56abed4d4615e Mon Sep 17 00:00:00 2001 From: Igor Chubin Date: Sat, 7 Sep 2019 15:12:10 +0200 Subject: [PATCH] lru cache for formatted answers (fixes #346) --- lib/cache.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/spark.py | 5 ++--- lib/wttr_srv.py | 15 ++++++++++++++- 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 lib/cache.py diff --git a/lib/cache.py b/lib/cache.py new file mode 100644 index 0000000..8f923b4 --- /dev/null +++ b/lib/cache.py @@ -0,0 +1,50 @@ +""" +LRU-Cache implementation for formatted (`format=`) answers +""" + +import datetime +import re +import time +import pylru +import pytz + +CACHE_SIZE = 10000 +CACHE = pylru.lrucache(CACHE_SIZE) + +def _update_answer(answer): + def _now_in_tz(timezone): + return datetime.datetime.now(pytz.timezone(timezone)).strftime("%H:%M:%S%z") + + if "%{{NOW(" in answer: + answer = re.sub(r"%{{NOW\(([^}]*)\)}}", lambda x: _now_in_tz(x.group(1)), answer) + + return answer + +def get_signature(user_agent, query_string, client_ip_address, lang): + """ + Get cache signature based on `user_agent`, `url_string`, + `lang`, and `client_ip_address` + """ + + timestamp = int(time.time()) / 1000 + signature = "%s:%s:%s:%s:%s" % \ + (user_agent, query_string, client_ip_address, lang, timestamp) + return signature + +def get(signature): + """ + If `update_answer` is not True, return answer as it is + stored in the cache. Otherwise update it, using + the `_update_answer` function. + """ + + if signature in CACHE: + return _update_answer(CACHE[signature]) + return None + +def store(signature, value): + """ + Store in cache `value` for `signature` + """ + CACHE[signature] = value + return _update_answer(value) diff --git a/lib/spark.py b/lib/spark.py index 019e715..47c935d 100644 --- a/lib/spark.py +++ b/lib/spark.py @@ -481,13 +481,12 @@ def textual_information(data_parsed, geo_data, config): output.append('Timezone: %s' % timezone) tmp_output = [] - tmp_output.append(' Now: %s' - % datetime.datetime.now(pytz.timezone(timezone)).strftime("%H:%M:%S%z")) + tmp_output.append(' Now: %%{{NOW(%s)}}' % timezone) tmp_output.append('Dawn: %s' % str(sun['dawn'].strftime("%H:%M:%S"))) tmp_output.append('Sunrise: %s' % str(sun['sunrise'].strftime("%H:%M:%S"))) - tmp_output.append(' Noon: %s' + tmp_output.append(' Zenith: %s' % str(sun['noon'].strftime("%H:%M:%S "))) tmp_output.append('Sunset: %s' % str(sun['sunset'].strftime("%H:%M:%S"))) diff --git a/lib/wttr_srv.py b/lib/wttr_srv.py index 7c64387..e389e6d 100644 --- a/lib/wttr_srv.py +++ b/lib/wttr_srv.py @@ -25,6 +25,8 @@ from limits import Limits from wttr import get_wetter, get_moon from wttr_line import wttr_line +import cache + if not os.path.exists(os.path.dirname(LOG_FILE)): os.makedirs(os.path.dirname(LOG_FILE)) logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG, format='%(asctime)s %(message)s') @@ -211,6 +213,12 @@ def wttr(location, request): html_output = get_output_format(request, query) user_agent = request.headers.get('User-Agent', '').lower() + # generating cache signature + cache_signature = cache.get_signature(user_agent, request.url, ip_addr, lang) + answer = cache.get(cache_signature) + if answer: + return _wrap_response(answer, html_output) + if location in PLAIN_TEXT_PAGES: help_ = show_text_file(location, lang) if html_output: @@ -242,8 +250,13 @@ def wttr(location, request): # We are ready to return the answer try: if fmt or 'format' in query: + response_text = wttr_line( + location, override_location_name, full_address, query, lang, fmt) + fmt = fmt or query.get('format') + response_text = cache.store(cache_signature, response_text) + return _wrap_response( - wttr_line(location, override_location_name, full_address, query, lang, fmt), + response_text, html_output) if png_filename: