Skip to content

Commit ead4c59

Browse files
committed
wip
1 parent f631c7e commit ead4c59

File tree

7 files changed

+379
-145
lines changed

7 files changed

+379
-145
lines changed
Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1-
from datetime import datetime
2-
from typing import Dict
1+
from typing import Dict, Any
32

4-
from django.core.cache import cache
3+
from django.conf import settings
54
from django.utils.timezone import now
6-
from django_q.tasks import async_task
75

86
from donations.models import Donor
97
from donations.views.dashboard.stats_helpers.utils import cache_set
8+
from redirectioneaza.common.async_cache import async_cache_decorator, is_cache_valid_for_past_date
109

1110
STATS_FOR_MONTH_CACHE_PREFIX = "STATS_FOR_MONTH_"
1211

1312

14-
def donors_for_month(month: int, year: int = None) -> Dict[str, int]:
13+
@async_cache_decorator(
14+
cache_key_custom=f"{STATS_FOR_MONTH_CACHE_PREFIX}{{1}}_{{{0}}}",
15+
placeholder_value={
16+
"metric": -2,
17+
"year": 0,
18+
"month": 0,
19+
},
20+
validation_func=is_cache_valid_for_past_date,
21+
timeout=settings.TIMEOUT_CACHE_NORMAL,
22+
)
23+
def donors_for_month(month: int, year: int = None) -> Dict[str, Any]:
1524
"""
1625
Determines the number of donors for a specified month and year.
1726
@@ -26,63 +35,41 @@ def donors_for_month(month: int, year: int = None) -> Dict[str, int]:
2635
year (int, optional): The year for which donor statistics are required or current year.
2736
2837
Returns:
29-
Dict[str, int]: A dictionary containing the number of donors for the specified month and year.
38+
Dict[str, Any]: A dictionary containing the number of donors for the specified month and year.
3039
"""
31-
current_time = now()
32-
3340
if year is None:
34-
year = current_time.year
35-
36-
cache_key = f"{STATS_FOR_MONTH_CACHE_PREFIX}{year}_{month}"
37-
38-
if cached_stats := cache.get(cache_key):
39-
if _is_cache_valid(current_time, month, year):
40-
return cached_stats
41-
42-
cache.delete(cache_key)
43-
44-
default_stat = {
45-
"metric": -2,
46-
"year": year,
47-
"month": month,
48-
}
41+
year = now().year
4942

50-
async_task(
51-
_update_stats_for_month,
52-
month,
53-
year,
54-
cache_key,
55-
)
43+
# This function is decorated with async_cache_decorator, which handles caching and async tasks.
44+
# The actual implementation is in _update_stats_for_month, which is called by the decorator.
45+
return _update_stats_for_month(month, year)
5646

57-
return cached_stats or default_stat
5847

59-
60-
def _is_cache_valid(current_time: datetime, month: int, year: int) -> bool:
61-
if not current_time:
62-
return False
63-
64-
if year < current_time.year:
65-
return True
66-
67-
if month < current_time.month:
68-
return True
69-
70-
return False
71-
72-
73-
def _update_stats_for_month(month: int, year: int, cache_key: str) -> Dict[str, int]:
48+
def _update_stats_for_month(month: int, year: int, _cache_key: str = None, _timeout: int = None) -> Dict[str, Any]:
7449
"""
7550
Updates the number of donors for a specific month and year, and caches the result.
76-
If the cache is valid, it returns the cached number of donors.
51+
52+
Parameters:
53+
month (int): The month for which to compute donor statistics.
54+
year (int): The year for which to compute donor statistics.
55+
_cache_key (str, optional): The cache key to use when storing the result.
56+
This is passed by the async_cache_decorator.
57+
_timeout (int, optional): The cache timeout in seconds.
58+
This is passed by the async_cache_decorator.
59+
60+
Returns:
61+
Dict[str, Any]: A dictionary containing the number of donors for the specified month and year.
7762
"""
7863
donors_count = Donor.objects.filter(date_created__year=year, date_created__month=month).count()
7964

8065
stat = {
8166
"metric": donors_count,
8267
"year": year,
8368
"month": month,
69+
"timestamp": now(),
8470
}
8571

86-
cache_set(cache_key, stat)
72+
if _cache_key and _timeout is not None:
73+
cache_set(_cache_key, stat, timeout=_timeout)
8774

8875
return stat
Lines changed: 146 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,208 @@
1+
from typing import Dict, Any
2+
13
from django.conf import settings
2-
from django.core.cache import cache
34
from django.utils.timezone import now
4-
from django_q.tasks import async_task
55

66
from donations.models import Donor, Ngo
77
from donations.views.dashboard.stats_helpers.utils import cache_set
8+
from redirectioneaza.common.async_cache import async_cache_decorator
9+
10+
# Cache key prefix for metrics
11+
METRIC_CACHE_PREFIX = "METRIC_"
812

913

1014
def _cache_key_for_metric(metric_name: str) -> str:
1115
"""
1216
Generates a cache key for the given metric name.
1317
"""
14-
return f"METRIC_{metric_name.upper()}"
18+
return f"{METRIC_CACHE_PREFIX}{metric_name.upper()}"
1519

1620

17-
def metrics_cache_decorator(func):
18-
"""
19-
Decorator to cache the metrics functions.
21+
@async_cache_decorator(
22+
cache_key=_cache_key_for_metric("current_year_redirections"),
23+
timeout=settings.TIMEOUT_CACHE_NORMAL,
24+
)
25+
def current_year_redirections() -> Dict[str, Any]:
2026
"""
27+
Returns the number of redirections (donations) for the current year.
2128
22-
def wrapper(*args, **kwargs):
23-
CACHE_KEY = _cache_key_for_metric(func.__name__)
24-
25-
if cached_result := cache.get(CACHE_KEY):
26-
# if the cache has expired (the timestamp is older than TIMEOUT_CACHE_NORMAL),
27-
# we delete the cache entry, trigger an async task to recalculate the metric,
28-
# and return the cached result; the updated metric will be available in the next request
29-
cache_timestamp = cached_result.get("timestamp")
30-
if cache_timestamp and (now() - cache_timestamp).total_seconds() > settings.TIMEOUT_CACHE_NORMAL:
31-
cache.delete(CACHE_KEY)
32-
async_task(func.__name__, *args, **kwargs)
33-
34-
return cached_result
35-
default_result = {
36-
"metric": -2,
37-
"timestamp": now(),
38-
}
39-
40-
async_task(func.__name__, *args, **kwargs)
29+
Returns:
30+
Dict[str, Any]: A dictionary containing the metric and timestamp.
31+
"""
32+
return _update_current_year_redirections()
4133

42-
return default_result
4334

44-
return wrapper
35+
def _update_current_year_redirections(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]:
36+
"""
37+
Updates the number of redirections for the current year and caches the result.
4538
39+
Parameters:
40+
_cache_key (str, optional): The cache key to use when storing the result.
41+
This is passed by the async_cache_decorator.
42+
_timeout (int, optional): The cache timeout in seconds.
43+
This is passed by the async_cache_decorator.
4644
47-
@metrics_cache_decorator
48-
def current_year_redirections():
45+
Returns:
46+
Dict[str, Any]: A dictionary containing the metric and timestamp.
47+
"""
4948
result = {
5049
"metric": Donor.available.filter(date_created__year=now().year).count(),
5150
"timestamp": now(),
5251
}
5352

54-
cache_set(_cache_key_for_metric("current_year_redirections"), result)
53+
if _cache_key and _timeout is not None:
54+
cache_set(_cache_key, result, timeout=_timeout)
5555

5656
return result
5757

5858

59-
@metrics_cache_decorator
60-
def all_redirections():
59+
@async_cache_decorator(
60+
cache_key=_cache_key_for_metric("all_redirections"),
61+
timeout=settings.TIMEOUT_CACHE_NORMAL,
62+
)
63+
def all_redirections() -> Dict[str, Any]:
64+
"""
65+
Returns the total number of redirections (donations) across all years.
66+
67+
Returns:
68+
Dict[str, Any]: A dictionary containing the metric and timestamp.
69+
"""
70+
return _update_all_redirections()
71+
72+
73+
def _update_all_redirections(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]:
74+
"""
75+
Updates the total number of redirections and caches the result.
76+
77+
Parameters:
78+
_cache_key (str, optional): The cache key to use when storing the result.
79+
This is passed by the async_cache_decorator.
80+
_timeout (int, optional): The cache timeout in seconds.
81+
This is passed by the async_cache_decorator.
82+
83+
Returns:
84+
Dict[str, Any]: A dictionary containing the metric and timestamp.
85+
"""
6186
result = {
6287
"metric": Donor.available.count(),
6388
"timestamp": now(),
6489
}
6590

66-
cache_set(_cache_key_for_metric("all_redirections"), result)
91+
if _cache_key and _timeout is not None:
92+
cache_set(_cache_key, result, timeout=_timeout)
6793

6894
return result
6995

7096

71-
@metrics_cache_decorator
72-
def all_active_ngos():
97+
@async_cache_decorator(
98+
cache_key=_cache_key_for_metric("all_active_ngos"),
99+
timeout=settings.TIMEOUT_CACHE_NORMAL,
100+
)
101+
def all_active_ngos() -> Dict[str, Any]:
102+
"""
103+
Returns the total number of active NGOs.
104+
105+
Returns:
106+
Dict[str, Any]: A dictionary containing the metric and timestamp.
107+
"""
108+
return _update_all_active_ngos()
109+
110+
111+
def _update_all_active_ngos(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]:
112+
"""
113+
Updates the total number of active NGOs and caches the result.
114+
115+
Parameters:
116+
_cache_key (str, optional): The cache key to use when storing the result.
117+
This is passed by the async_cache_decorator.
118+
_timeout (int, optional): The cache timeout in seconds.
119+
This is passed by the async_cache_decorator.
120+
121+
Returns:
122+
Dict[str, Any]: A dictionary containing the metric and timestamp.
123+
"""
73124
result = {
74125
"metric": Ngo.active.count(),
75126
"timestamp": now(),
76127
}
77128

78-
cache_set(_cache_key_for_metric("all_active_ngos"), result)
129+
if _cache_key and _timeout is not None:
130+
cache_set(_cache_key, result, timeout=_timeout)
79131

80132
return result
81133

82134

83-
@metrics_cache_decorator
84-
def ngos_active_in_current_year():
135+
@async_cache_decorator(
136+
cache_key=_cache_key_for_metric("ngos_active_in_current_year"),
137+
timeout=settings.TIMEOUT_CACHE_NORMAL,
138+
)
139+
def ngos_active_in_current_year() -> Dict[str, Any]:
140+
"""
141+
Returns the number of NGOs that are active in the current year.
142+
143+
Returns:
144+
Dict[str, Any]: A dictionary containing the metric and timestamp.
145+
"""
146+
return _update_ngos_active_in_current_year()
147+
148+
149+
def _update_ngos_active_in_current_year(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]:
150+
"""
151+
Updates the number of NGOs active in the current year and caches the result.
152+
153+
Parameters:
154+
_cache_key (str, optional): The cache key to use when storing the result.
155+
This is passed by the async_cache_decorator.
156+
_timeout (int, optional): The cache timeout in seconds.
157+
This is passed by the async_cache_decorator.
158+
159+
Returns:
160+
Dict[str, Any]: A dictionary containing the metric and timestamp.
161+
"""
85162
result = {
86163
"metric": Ngo.with_forms_this_year.count(),
87164
"timestamp": now(),
88165
}
89166

90-
cache_set(_cache_key_for_metric("ngos_active_in_current_year"), result)
167+
if _cache_key and _timeout is not None:
168+
cache_set(_cache_key, result, timeout=_timeout)
91169

92170
return result
93171

94172

95-
@metrics_cache_decorator
96-
def ngos_with_ngo_hub():
173+
@async_cache_decorator(
174+
cache_key=_cache_key_for_metric("ngos_with_ngo_hub"),
175+
timeout=settings.TIMEOUT_CACHE_NORMAL,
176+
)
177+
def ngos_with_ngo_hub() -> Dict[str, Any]:
178+
"""
179+
Returns the number of NGOs that are part of the NGO Hub.
180+
181+
Returns:
182+
Dict[str, Any]: A dictionary containing the metric and timestamp.
183+
"""
184+
return _update_ngos_with_ngo_hub()
185+
186+
187+
def _update_ngos_with_ngo_hub(_cache_key: str = None, _timeout: int = None) -> Dict[str, Any]:
188+
"""
189+
Updates the number of NGOs with NGO Hub and caches the result.
190+
191+
Parameters:
192+
_cache_key (str, optional): The cache key to use when storing the result.
193+
This is passed by the async_cache_decorator.
194+
_timeout (int, optional): The cache timeout in seconds.
195+
This is passed by the async_cache_decorator.
196+
197+
Returns:
198+
Dict[str, Any]: A dictionary containing the metric and timestamp.
199+
"""
97200
result = {
98201
"metric": Ngo.ngo_hub.count(),
99202
"timestamp": now(),
100203
}
101204

102-
cache_set(_cache_key_for_metric("ngos_with_ngo_hub"), result)
205+
if _cache_key and _timeout is not None:
206+
cache_set(_cache_key, result, timeout=_timeout)
103207

104208
return result
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from django.core.cache import cache
22

33

4-
def cache_set(key: str, value: dict) -> None:
4+
def cache_set(key: str, value: dict, timeout: int = None) -> None:
55
"""
66
Sets a value in the cache with a specified key.
7-
The timeout is set to None, meaning the cache will not expire.
7+
8+
Parameters:
9+
key (str): The cache key.
10+
value (dict): The value to cache.
11+
timeout (int, optional): The cache timeout in seconds. If None, the cache will not expire.
12+
Defaults to None.
813
"""
9-
cache.set(key, value, timeout=None)
14+
cache.set(key, value, timeout=timeout)

0 commit comments

Comments
 (0)