diff --git a/backend/donations/views/dashboard/admin_dashboard.py b/backend/donations/views/dashboard/admin_dashboard.py index d0a43ebc..58997893 100644 --- a/backend/donations/views/dashboard/admin_dashboard.py +++ b/backend/donations/views/dashboard/admin_dashboard.py @@ -7,10 +7,16 @@ from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ -from donations.models.ngos import Ngo -from redirectioneaza.common.cache import cache_decorator +from donations.views.dashboard.stats_helpers.chart import donors_for_month +from donations.views.dashboard.stats_helpers.metrics import ( + all_active_ngos, + all_redirections, + current_year_redirections, + ngos_active_in_current_year, + ngos_with_ngo_hub, +) +from donations.views.dashboard.stats_helpers.yearly import get_stats_for_year -from ...models.donors import Donor from .helpers import ( generate_donations_per_month_chart, get_current_year_range, @@ -27,7 +33,6 @@ def callback(request, context) -> Dict: return context -@cache_decorator(timeout=settings.TIMEOUT_CACHE_NORMAL, cache_key=ADMIN_DASHBOARD_STATS_CACHE_KEY) def _get_admin_stats() -> Dict: today = now() years_range_ascending = get_current_year_range() @@ -55,43 +60,43 @@ def _get_header_stats(today) -> List[List[Dict[str, Union[str, int | datetime]]] { "title": _("Donations this year"), "icon": "edit_document", - "metric": Donor.available.filter(date_created__year=current_year).count(), + "metric": current_year_redirections()["metric"], "footer": _create_stat_link( - url=f'{reverse("admin:donations_donor_changelist")}?{current_year_range}', text=_("View all") + url=f"{reverse('admin:donations_donor_changelist')}?{current_year_range}", text=_("View all") ), - "timestamp": now(), + "timestamp": current_year_redirections()["timestamp"], }, { "title": _("Donations all-time"), "icon": "edit_document", - "metric": Donor.available.count(), + "metric": all_redirections()["metric"], "footer": _create_stat_link(url=reverse("admin:donations_donor_changelist"), text=_("View all")), - "timestamp": now(), + "timestamp": all_redirections()["timestamp"], }, { "title": _("NGOs registered"), "icon": "foundation", - "metric": Ngo.active.count(), + "metric": all_active_ngos()["metric"], "footer": _create_stat_link( - url=f'{reverse("admin:donations_ngo_changelist")}?is_active=1', text=_("View all") + url=f"{reverse('admin:donations_ngo_changelist')}?is_active=1", text=_("View all") ), - "timestamp": now(), + "timestamp": all_active_ngos()["timestamp"], }, { "title": _("Functioning NGOs"), "icon": "foundation", - "metric": Ngo.with_forms_this_year.count(), - "footer": _create_stat_link(url=f'{reverse("admin:donations_ngo_changelist")}', text=_("View all")), - "timestamp": now(), + "metric": ngos_active_in_current_year()["metric"], + "footer": _create_stat_link(url=f"{reverse('admin:donations_ngo_changelist')}", text=_("View all")), + "timestamp": ngos_active_in_current_year()["timestamp"], }, { "title": _("NGOs from NGO Hub"), "icon": "foundation", - "metric": Ngo.ngo_hub.count(), + "metric": ngos_with_ngo_hub()["metric"], "footer": _create_stat_link( - url=f'{reverse("admin:donations_ngo_changelist")}?is_active=1&has_ngohub=1', text=_("View all") + url=f"{reverse('admin:donations_ngo_changelist')}?is_active=1&has_ngohub=1", text=_("View all") ), - "timestamp": now(), + "timestamp": ngos_with_ngo_hub()["timestamp"], }, ] ] @@ -99,9 +104,10 @@ def _get_header_stats(today) -> List[List[Dict[str, Union[str, int | datetime]]] def _create_chart_statistics() -> Dict[str, str]: default_border_width: int = 3 + current_year = now().year donations_per_month_queryset = [ - Donor.available.filter(date_created__month=month) for month in range(1, settings.DONATIONS_LIMIT.month + 1) + donors_for_month(month, current_year)["metric"] for month in range(1, settings.DONATIONS_LIMIT.month + 1) ] forms_per_month_chart = generate_donations_per_month_chart(default_border_width, donations_per_month_queryset) @@ -110,7 +116,7 @@ def _create_chart_statistics() -> Dict[str, str]: def _get_yearly_stats(years_range_ascending) -> List[Dict[str, Union[int, List[Dict]]]]: - statistics = [_get_stats_for_year(year) for year in years_range_ascending] + statistics = [get_stats_for_year(year) for year in years_range_ascending] for index, statistic in enumerate(statistics): if index == 0: @@ -129,24 +135,6 @@ def _get_yearly_stats(years_range_ascending) -> List[Dict[str, Union[int, List[D return sorted(final_statistics, key=lambda x: x["year"], reverse=True) -# TODO: This cache seems useless because we already cache the entire dashboard stats -@cache_decorator(timeout=settings.TIMEOUT_CACHE_NORMAL, cache_key_prefix=ADMIN_DASHBOARD_CACHE_KEY) -def _get_stats_for_year(year: int) -> Dict[str, int | datetime]: - donations: int = Donor.available.filter(date_created__year=year).count() - ngos_registered: int = Ngo.objects.filter(date_created__year=year).count() - ngos_with_forms: int = Donor.available.filter(date_created__year=year).values("ngo_id").distinct().count() - - statistic = { - "year": year, - "donations": donations, - "ngos_registered": ngos_registered, - "ngos_with_forms": ngos_with_forms, - "timestamp": now(), - } - - return statistic - - def _format_yearly_stats(statistics) -> List[Dict[str, Union[int, List[Dict]]]]: return [ { @@ -158,7 +146,7 @@ def _format_yearly_stats(statistics) -> List[Dict[str, Union[int, List[Dict]]]]: "metric": statistic["donations"], "label": statistic.get("donations_difference"), "footer": _create_stat_link( - url=f'{reverse("admin:donations_donor_changelist")}?date_created__year={statistic["year"]}', + url=f"{reverse('admin:donations_donor_changelist')}?date_created__year={statistic['year']}", text=_("View all"), ), "timestamp": statistic["timestamp"], @@ -169,7 +157,7 @@ def _format_yearly_stats(statistics) -> List[Dict[str, Union[int, List[Dict]]]]: "metric": statistic["ngos_registered"], "label": statistic.get("ngos_registered_difference"), "footer": _create_stat_link( - url=f'{reverse("admin:donations_ngo_changelist")}?date_created__year={statistic["year"]}', + url=f"{reverse('admin:donations_ngo_changelist')}?date_created__year={statistic['year']}", text=_("View all"), ), "timestamp": statistic["timestamp"], @@ -180,7 +168,7 @@ def _format_yearly_stats(statistics) -> List[Dict[str, Union[int, List[Dict]]]]: "metric": statistic["ngos_with_forms"], "label": statistic.get("ngos_with_forms_difference"), "footer": _create_stat_link( - url=f'{reverse("admin:donations_ngo_changelist")}?has_forms=1&date_created__year={statistic["year"]}', + url=f"{reverse('admin:donations_ngo_changelist')}?has_forms=1&date_created__year={statistic['year']}", text=_("View all"), ), "timestamp": statistic["timestamp"], diff --git a/backend/donations/views/dashboard/stats_helpers/__init__.py b/backend/donations/views/dashboard/stats_helpers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/donations/views/dashboard/stats_helpers/chart.py b/backend/donations/views/dashboard/stats_helpers/chart.py new file mode 100644 index 00000000..b853ff47 --- /dev/null +++ b/backend/donations/views/dashboard/stats_helpers/chart.py @@ -0,0 +1,75 @@ +from typing import Dict, Any + +from django.conf import settings +from django.utils.timezone import now + +from donations.models import Donor +from donations.views.dashboard.stats_helpers.utils import cache_set +from redirectioneaza.common.async_cache import async_cache_decorator, is_cache_valid_for_past_date + +STATS_FOR_MONTH_CACHE_PREFIX = "STATS_FOR_MONTH_" + + +@async_cache_decorator( + cache_key_custom=f"{STATS_FOR_MONTH_CACHE_PREFIX}{{1}}_{{{0}}}", + placeholder_value={ + "metric": -2, + "year": 0, + "month": 0, + }, + validation_func=is_cache_valid_for_past_date, + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def donors_for_month(month: int, year: int = None) -> Dict[str, Any]: + """ + Determines the number of donors for a specified month and year. + + This function retrieves the number of donors for a specific month and year + from the cache if available and valid. If the cache is invalid or absent, + it initiates an asynchronous task to update the stats and returns a default + statistic. If the year parameter is not provided, it defaults to the + current year. + + Parameters: + month (int): The month for which donor statistics are requested. + year (int, optional): The year for which donor statistics are required or current year. + + Returns: + Dict[str, Any]: A dictionary containing the number of donors for the specified month and year. + """ + if year is None: + year = now().year + + # This function is decorated with async_cache_decorator, which handles caching and async tasks. + # The actual implementation is in _update_stats_for_month, which is called by the decorator. + return _update_stats_for_month(month, year) + + +def _update_stats_for_month(month: int, year: int, _cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the number of donors for a specific month and year, and caches the result. + + Parameters: + month (int): The month for which to compute donor statistics. + year (int): The year for which to compute donor statistics. + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing the number of donors for the specified month and year. + """ + donors_count = Donor.objects.filter(date_created__year=year, date_created__month=month).count() + + stat = { + "metric": donors_count, + "year": year, + "month": month, + "timestamp": now(), + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, stat, timeout=_timeout) + + return stat diff --git a/backend/donations/views/dashboard/stats_helpers/metrics.py b/backend/donations/views/dashboard/stats_helpers/metrics.py new file mode 100644 index 00000000..9d4441be --- /dev/null +++ b/backend/donations/views/dashboard/stats_helpers/metrics.py @@ -0,0 +1,208 @@ +from typing import Dict, Any + +from django.conf import settings +from django.utils.timezone import now + +from donations.models import Donor, Ngo +from donations.views.dashboard.stats_helpers.utils import cache_set +from redirectioneaza.common.async_cache import async_cache_decorator + +# Cache key prefix for metrics +METRIC_CACHE_PREFIX = "METRIC_" + + +def _cache_key_for_metric(metric_name: str) -> str: + """ + Generates a cache key for the given metric name. + """ + return f"{METRIC_CACHE_PREFIX}{metric_name.upper()}" + + +@async_cache_decorator( + cache_key=_cache_key_for_metric("current_year_redirections"), + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def current_year_redirections() -> Dict[str, Any]: + """ + Returns the number of redirections (donations) for the current year. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + return _update_current_year_redirections() + + +def _update_current_year_redirections(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the number of redirections for the current year and caches the result. + + Parameters: + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + result = { + "metric": Donor.available.filter(date_created__year=now().year).count(), + "timestamp": now(), + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, result, timeout=_timeout) + + return result + + +@async_cache_decorator( + cache_key=_cache_key_for_metric("all_redirections"), + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def all_redirections() -> Dict[str, Any]: + """ + Returns the total number of redirections (donations) across all years. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + return _update_all_redirections() + + +def _update_all_redirections(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the total number of redirections and caches the result. + + Parameters: + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + result = { + "metric": Donor.available.count(), + "timestamp": now(), + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, result, timeout=_timeout) + + return result + + +@async_cache_decorator( + cache_key=_cache_key_for_metric("all_active_ngos"), + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def all_active_ngos() -> Dict[str, Any]: + """ + Returns the total number of active NGOs. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + return _update_all_active_ngos() + + +def _update_all_active_ngos(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the total number of active NGOs and caches the result. + + Parameters: + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + result = { + "metric": Ngo.active.count(), + "timestamp": now(), + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, result, timeout=_timeout) + + return result + + +@async_cache_decorator( + cache_key=_cache_key_for_metric("ngos_active_in_current_year"), + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def ngos_active_in_current_year() -> Dict[str, Any]: + """ + Returns the number of NGOs that are active in the current year. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + return _update_ngos_active_in_current_year() + + +def _update_ngos_active_in_current_year(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the number of NGOs active in the current year and caches the result. + + Parameters: + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + result = { + "metric": Ngo.with_forms_this_year.count(), + "timestamp": now(), + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, result, timeout=_timeout) + + return result + + +@async_cache_decorator( + cache_key=_cache_key_for_metric("ngos_with_ngo_hub"), + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def ngos_with_ngo_hub() -> Dict[str, Any]: + """ + Returns the number of NGOs that are part of the NGO Hub. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + return _update_ngos_with_ngo_hub() + + +def _update_ngos_with_ngo_hub(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the number of NGOs with NGO Hub and caches the result. + + Parameters: + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing the metric and timestamp. + """ + result = { + "metric": Ngo.ngo_hub.count(), + "timestamp": now(), + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, result, timeout=_timeout) + + return result diff --git a/backend/donations/views/dashboard/stats_helpers/utils.py b/backend/donations/views/dashboard/stats_helpers/utils.py new file mode 100644 index 00000000..bc5551c7 --- /dev/null +++ b/backend/donations/views/dashboard/stats_helpers/utils.py @@ -0,0 +1,14 @@ +from django.core.cache import cache + + +def cache_set(key: str, value: dict, timeout: int = None) -> None: + """ + Sets a value in the cache with a specified key. + + Parameters: + key (str): The cache key. + value (dict): The value to cache. + timeout (int, optional): The cache timeout in seconds. If None, the cache will not expire. + Defaults to None. + """ + cache.set(key, value, timeout=timeout) diff --git a/backend/donations/views/dashboard/stats_helpers/yearly.py b/backend/donations/views/dashboard/stats_helpers/yearly.py new file mode 100644 index 00000000..a1da1b80 --- /dev/null +++ b/backend/donations/views/dashboard/stats_helpers/yearly.py @@ -0,0 +1,83 @@ +from datetime import datetime +from typing import Dict, Any + +from django.conf import settings +from django.utils.timezone import now + +from donations.models import Donor, Ngo +from donations.views.dashboard.stats_helpers.utils import cache_set +from redirectioneaza.common.async_cache import async_cache_decorator, is_cache_valid_for_past_date + +STATS_FOR_YEAR_CACHE_PREFIX = "STATS_FOR_YEAR_" + + +@async_cache_decorator( + cache_key_custom=f"{STATS_FOR_YEAR_CACHE_PREFIX}{{0}}", + placeholder_value={ + "year": 0, + "donations": -2, + "ngos_registered": -2, + "ngos_with_forms": -2, + }, + validation_func=is_cache_valid_for_past_date, + timeout=settings.TIMEOUT_CACHE_NORMAL, +) +def get_stats_for_year(year: int) -> Dict[str, Any]: + """ + Fetches and returns statistics for a given year. + + This function attempts to retrieve cached statistics for the specified year from the + cache. If the cache is valid, the cached statistics are returned. Otherwise, the cache + is cleared, and default placeholder statistics are returned. Additionally, an + asynchronous task is triggered to update the statistics for the given year and store + them in the cache. + + Parameters: + year (int): The year for which to retrieve statistics. + + Returns: + Dict[str, Any]: A dictionary containing statistics for the specified year. + The dictionary includes: + - "year" (int): The year of the statistics. + - "donations" (int): The number of donations. + - "ngos_registered" (int): The number of NGOs registered. + - "ngos_with_forms" (int): The number of NGOs with forms. + - "timestamp" (datetime): The timestamp indicating when the statistics were computed. + """ + # This function is decorated with async_cache_decorator, which handles caching and async tasks. + # The actual implementation is in _update_stats_for_year, which is called by the decorator. + return _update_stats_for_year(year) + + +def _update_stats_for_year(year: int, _cache_key: str = None, _timeout: int = None) -> Dict[str, Any]: + """ + Updates the statistics for a given year and caches the result. + + Parameters: + year (int): The year for which to compute statistics. + _cache_key (str, optional): The cache key to use when storing the result. + This is passed by the async_cache_decorator. + _timeout (int, optional): The cache timeout in seconds. + This is passed by the async_cache_decorator. + + Returns: + Dict[str, Any]: A dictionary containing statistics for the specified year. + """ + current_time: datetime = now() + + donations: int = Donor.available.filter(date_created__year=year).count() + ngos_registered: int = Ngo.objects.filter(date_created__year=year).count() + ngos_with_forms: int = Donor.available.filter(date_created__year=year).values("ngo_id").distinct().count() + + statistic = { + "year": year, + "donations": donations, + "ngos_registered": ngos_registered, + "ngos_with_forms": ngos_with_forms, + "timestamp": current_time, + } + + if _cache_key and _timeout is not None: + cache_set(_cache_key, statistic, timeout=_timeout) + + return statistic diff --git a/backend/redirectioneaza/common/async_cache.py b/backend/redirectioneaza/common/async_cache.py new file mode 100644 index 00000000..7d3aa057 --- /dev/null +++ b/backend/redirectioneaza/common/async_cache.py @@ -0,0 +1,151 @@ +from datetime import datetime +from functools import wraps +from typing import Any, Callable, Dict, Optional, TypeVar, cast + +from django.conf import settings +from django.core.cache import cache +from django.utils.timezone import now +from django_q.tasks import async_task + +T = TypeVar('T') + + +def async_cache_decorator( + *, + timeout: int = settings.TIMEOUT_CACHE_NORMAL, + cache_key: str = None, + cache_key_prefix: str = None, + cache_key_custom: str = None, + placeholder_value: Any = -2, + include_timestamp: bool = True, + validation_func: Callable[[Dict[str, Any]], bool] = None, +): + """ + A decorator that combines caching with asynchronous task execution. + + This decorator first checks if a valid cached result exists. If it does, the cached + result is returned. If not, a placeholder result is returned and an asynchronous + task is triggered to compute the actual result and update the cache. + + Parameters: + timeout (int): The cache timeout in seconds. Defaults to settings.TIMEOUT_CACHE_NORMAL. + cache_key (str, optional): A fixed cache key to use. + cache_key_prefix (str, optional): A prefix for the cache key. The full key will be + generated by combining this prefix with a hash of the function name and arguments. + cache_key_custom (str, optional): A format string for the cache key. The format string + can include placeholders for function arguments. + placeholder_value (Any, optional): The value to return when the cache is invalid and + an async task is triggered. Defaults to -2. + include_timestamp (bool, optional): Whether to include a timestamp in the cached result. + Defaults to True. + validation_func (Callable, optional): A function that takes the cached result and + returns a boolean indicating whether the cache is valid. If not provided, the + cache is considered valid if it exists. + + Returns: + A decorator function. + + Raises: + ValueError: If none of cache_key, cache_key_prefix, or cache_key_custom is provided. + """ + def decorator(func: Callable[..., T]) -> Callable[..., T]: + @wraps(func) + def wrapper(*args, **kwargs) -> T: + if not cache_key and not cache_key_prefix and not cache_key_custom: + raise ValueError("Either cache_key, cache_key_prefix, or cache_key_custom must be provided") + + # Generate the cache key + if cache_key: + _cache_key: str = cache_key + elif cache_key_custom: + # noinspection StrFormat + _cache_key: str = cache_key_custom.format(*args, **kwargs) + else: + cache_suffix = ( + hash(f"{func.__name__}__{str(args)}_{str(kwargs)}") + .to_bytes(length=8, byteorder="big", signed=True) + .hex() + ) + _cache_key: str = f"{cache_key_prefix}__{cache_suffix}" + + # Check if the cache is enabled + if settings.ENABLE_CACHE: + # Try to get the cached value + cached_value = cache.get(_cache_key) + + # If the cache exists, check if it's valid + if cached_value is not None: + # If a validation function is provided, use it to check if the cache is valid + if validation_func is None or validation_func(cached_value): + return cast(T, cached_value) + + # If the cache is invalid, delete it + cache.delete(_cache_key) + + # Create a default result with the current timestamp + current_time = now() + if isinstance(placeholder_value, dict) and include_timestamp: + default_result = placeholder_value.copy() + default_result["timestamp"] = current_time + elif include_timestamp: + default_result = { + "metric": placeholder_value, + "timestamp": current_time, + } + else: + default_result = placeholder_value + + # Trigger an async task to compute the actual result and update the cache + async_task( + func.__name__, + *args, + **kwargs, + _cache_key=_cache_key, + _timeout=timeout, + ) + + return cast(T, default_result) + + return wrapper + + return decorator + + +def is_cache_valid_for_past_date(cached_value: Dict[str, Any], current_time: Optional[datetime] = None) -> bool: + """ + Validates if a cached value for a past date is still valid. + + For past dates (previous years or months), the cache is always valid. + For the current year/month, the cache is valid if the current time is after + the donation period limit. + + Parameters: + cached_value (Dict[str, Any]): The cached value to validate. + current_time (datetime, optional): The current time. Defaults to now(). + + Returns: + bool: True if the cache is valid, False otherwise. + """ + if current_time is None: + current_time = now() + + cache_timestamp = cached_value.get("timestamp") + if not cache_timestamp: + return False + + year = cached_value.get("year") + month = cached_value.get("month") + + # If the year is in the past, the cache is valid + if year and year < current_time.year: + return True + + # If the month is in the past (in the current year), the cache is valid + if year and month and year == current_time.year and month < current_time.month: + return True + + # If we're past the donation period limit, the cache is valid + if cache_timestamp > settings.DONATIONS_LIMIT: + return True + + return False diff --git a/backend/redirectioneaza/common/cache.py b/backend/redirectioneaza/common/cache.py index d01acfac..94b042db 100644 --- a/backend/redirectioneaza/common/cache.py +++ b/backend/redirectioneaza/common/cache.py @@ -36,7 +36,7 @@ def wrapper(*args, **kwargs): return_value = func(*args, **kwargs) if settings.ENABLE_CACHE: - cache.set(cache_key, return_value, timeout=timeout) + cache.set(_cache_key, return_value, timeout=timeout) return return_value diff --git a/backend/redirectioneaza/settings/cache.py b/backend/redirectioneaza/settings/cache.py index 6d245ff4..674af2ca 100644 --- a/backend/redirectioneaza/settings/cache.py +++ b/backend/redirectioneaza/settings/cache.py @@ -12,7 +12,8 @@ "default": { "BACKEND": "django.core.cache.backends.db.DatabaseCache", "LOCATION": "redirect_cache_default", - "TIMEOUT": TIMEOUT_CACHE_NORMAL, # default cache timeout in seconds + "TIMEOUT": TIMEOUT_CACHE_NORMAL, + "OPTIONS": {"MAX_ENTRIES": 1000}, } } else: diff --git a/backend/redirectioneaza/urls.py b/backend/redirectioneaza/urls.py index 1ae2f5e1..80217e86 100644 --- a/backend/redirectioneaza/urls.py +++ b/backend/redirectioneaza/urls.py @@ -20,6 +20,7 @@ from django.contrib import admin from django.urls import include, path, re_path, reverse from django.views.generic import RedirectView + from donations.views.account_management import ( ForgotPasswordView, LoginView, @@ -151,7 +152,7 @@ path( "allauth/login/", RedirectView.as_view( - url=f'/allauth{reverse("amazon_cognito_login", urlconf="allauth.urls")}', + url=f"/allauth{reverse('amazon_cognito_login', urlconf='allauth.urls')}", permanent=True, ), name="allauth-login",