ready for testing
Some checks failed
Build and deploy the backend to staging / Build and push image (pull_request) Successful in 1m58s
Run linting on the backend code / Build (pull_request) Successful in 28s
Run testing on the backend code / Build (pull_request) Failing after 13m25s
Build and deploy the backend to staging / Deploy to staging (pull_request) Successful in 26s

This commit is contained in:
2025-01-27 14:24:19 +01:00
parent 431ae7c670
commit 3605408ebb
11 changed files with 291 additions and 130 deletions

View File

@@ -1,9 +1,9 @@
"""Module defining the caching strategy for overpass requests."""
import os
import xml.etree.ElementTree as ET
import hashlib
import time
from ..constants import OSM_CACHE_DIR
from ..constants import OSM_CACHE_DIR, OSM_TYPES
def get_cache_key(query: str) -> str:
@@ -13,14 +13,9 @@ def get_cache_key(query: str) -> str:
"""
return hashlib.md5(query.encode('utf-8')).hexdigest()
class CachingStrategyBase:
"""
Base class for implementing caching strategies.
This class defines the structure for a caching strategy with basic methods
that must be implemented by subclasses. Subclasses should define how to
retrieve, store, and close the cache.
"""
def get(self, key):
"""Retrieve the cached data associated with the provided key."""
@@ -30,6 +25,15 @@ class CachingStrategyBase:
"""Store data in the cache with the specified key."""
raise NotImplementedError('Subclass should implement set')
def set_hollow(self, key, cell: tuple, osm_types: OSM_TYPES,
selector: str, conditions=[], out='center'):
"""Create a hollow (empty) cache entry with a specific key."""
raise NotImplementedError('Subclass should implement set_hollow')
def fill_hollow(self, key, value):
"""Fill in the cache for an existing hollow entry."""
raise NotImplementedError('Subclass should implement fill_hollow')
def close(self):
"""Clean up or close any resources used by the caching strategy."""
@@ -37,22 +41,10 @@ class CachingStrategyBase:
class XMLCache(CachingStrategyBase):
"""
A caching strategy that stores and retrieves data in XML format.
This class provides methods to cache data as XML files in a specified directory.
The directory is automatically suffixed with '_XML' to distinguish it from other
caching strategies. The data is stored and retrieved using XML serialization.
Args:
cache_dir (str): The base directory where XML cache files will be stored.
Defaults to 'OSM_CACHE_DIR' with a '_XML' suffix.
Methods:
get(key): Retrieve cached data from a XML file associated with the given key.
set(key, value): Store data in a XML file with the specified key.
"""
def __init__(self, cache_dir=OSM_CACHE_DIR):
# Add the class name as a suffix to the directory
self._cache_dir = f'{cache_dir}_XML'
self._cache_dir = f'{cache_dir}'
if not os.path.exists(self._cache_dir):
os.makedirs(self._cache_dir)
@@ -68,7 +60,6 @@ class XMLCache(CachingStrategyBase):
tree = ET.parse(filename)
return tree.getroot() # Return the root element of the parsed XML
except ET.ParseError:
# print(f"Error parsing cached XML file: {filename}")
return None
return None
@@ -77,25 +68,41 @@ class XMLCache(CachingStrategyBase):
filename = self._filename(key)
tree = ET.ElementTree(value) # value is expected to be an ElementTree root element
try:
# Write the XML data to a file
with open(filename, 'wb') as file:
tree.write(file, encoding='utf-8', xml_declaration=True)
except IOError as e:
raise IOError(f"Error writing to cache file: {filename} - {e}") from e
def set_hollow(self, key, cell: tuple, osm_types: OSM_TYPES,
selector: str, conditions=[], out='center'):
"""Create an empty placeholder cache entry for a future fill."""
hollow_key = f'hollow_{key}'
filename = self._filename(hollow_key)
# Create the root element <cache>
root = ET.Element("params")
# Add sub-elements with provided values
ET.SubElement(root, "key").text = key
ET.SubElement(root, "cell").text = f"({cell[0]}, {cell[1]})"
ET.SubElement(root, "osm_types").text = ','.join(osm_types)
ET.SubElement(root, "selector").text = selector
ET.SubElement(root, "conditions").text = ','.join(conditions) if conditions else "none"
ET.SubElement(root, "out").text = out
# Create an ElementTree object from the root
tree = ET.ElementTree(root)
# Write the XML to the file
with open(filename, 'wb') as file:
tree.write(file, encoding='utf-8', xml_declaration=True)
def close(self):
"""Cleanup method, if needed."""
pass
class CachingStrategy:
"""
A class to manage different caching strategies.
This class provides an interface to switch between different caching strategies
(e.g., XMLCache, JSONCache) dynamically. It allows caching data in different formats,
depending on the strategy being used. By default, it uses the XMLCache strategy.
Attributes:
__strategy (CachingStrategyBase): The currently active caching strategy.
__strategies (dict): A mapping between strategy names (as strings) and their corresponding
classes, allowing dynamic selection of caching strategies.
"""
__strategy = XMLCache() # Default caching strategy
__strategies = {
@@ -104,37 +111,31 @@ class CachingStrategy:
@classmethod
def use(cls, strategy_name='XML', **kwargs):
"""
Set the caching strategy based on the strategy_name provided.
Args:
strategy_name (str): The name of the caching strategy (e.g., 'XML').
**kwargs: Additional keyword arguments to pass when initializing the strategy.
"""
# If a previous strategy exists, close it
if cls.__strategy:
cls.__strategy.close()
# Retrieve the strategy class based on the strategy name
strategy_class = cls.__strategies.get(strategy_name)
if not strategy_class:
raise ValueError(f"Unknown caching strategy: {strategy_name}")
# Instantiate the new strategy with the provided arguments
cls.__strategy = strategy_class(**kwargs)
return cls.__strategy
@classmethod
def get(cls, key):
"""Get data from the current strategy's cache."""
if not cls.__strategy:
raise RuntimeError("Caching strategy has not been set.")
return cls.__strategy.get(key)
@classmethod
def set(cls, key, value):
"""Set data in the current strategy's cache."""
if not cls.__strategy:
raise RuntimeError("Caching strategy has not been set.")
cls.__strategy.set(key, value)
@classmethod
def set_hollow(cls, key, cell: tuple, osm_types: OSM_TYPES,
selector: str, conditions=[], out='center'):
"""Create a hollow cache entry."""
cls.__strategy.set_hollow(key, cell, osm_types, selector, conditions, out)
@classmethod
def fill_hollow(cls, key, value):
"""Fill in the hollow cache entry with actual data."""
cls.__strategy.fill_hollow(key, value)