85 lines
2.1 KiB
Python
85 lines
2.1 KiB
Python
import logging
|
|
from datetime import timedelta
|
|
|
|
from django.utils import timezone
|
|
from trends.models import PERIOD_CHOICES, TrendResult
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PERIOD_DAYS = {
|
|
"last_30": 30,
|
|
"last_90": 90,
|
|
"last_year": 365,
|
|
}
|
|
|
|
PERIOD_LABELS = dict(PERIOD_CHOICES)
|
|
|
|
TIME_BOUND_TRENDS = {
|
|
"activity-distribution",
|
|
"concurrent-reading",
|
|
"concurrent-listening",
|
|
"mood-by-time",
|
|
"mood-distribution",
|
|
"mood-streaks",
|
|
"mood-trajectory",
|
|
"mood-weather",
|
|
"peak-hours",
|
|
"reading-pace-vs-activity",
|
|
"trending-up",
|
|
"weekly-rhythm",
|
|
}
|
|
|
|
TREND_PERIOD_OVERRIDES = {
|
|
"trending-up": ["last_30", "last_90", "last_year"],
|
|
}
|
|
|
|
|
|
def get_supported_periods(trend_slug):
|
|
if trend_slug in TREND_PERIOD_OVERRIDES:
|
|
slugs = TREND_PERIOD_OVERRIDES[trend_slug]
|
|
return {s: PERIOD_LABELS[s] for s in slugs}
|
|
return dict(PERIOD_LABELS)
|
|
|
|
|
|
def get_period_days(period):
|
|
return PERIOD_DAYS.get(period)
|
|
|
|
|
|
def get_date_range(period):
|
|
days = get_period_days(period)
|
|
if days is None:
|
|
return None, None
|
|
now = timezone.now()
|
|
return now - timedelta(days=days), now
|
|
|
|
|
|
def get_period_nav(current_period, trend_slug):
|
|
supported = get_supported_periods(trend_slug)
|
|
keys = list(supported.keys())
|
|
try:
|
|
idx = keys.index(current_period)
|
|
except ValueError:
|
|
return None, None
|
|
prev_period = keys[idx - 1] if idx > 0 else None
|
|
next_period = keys[idx + 1] if idx < len(keys) - 1 else None
|
|
return prev_period, next_period
|
|
|
|
|
|
def compute_and_save_trend(user, slug, period="last_30"):
|
|
"""Compute a single trend for a given period and persist the result.
|
|
|
|
Returns elapsed seconds on success, raises on failure.
|
|
"""
|
|
from trends.trends import TREND_REGISTRY
|
|
|
|
fn = TREND_REGISTRY[slug]
|
|
start = timezone.now()
|
|
data = fn(user, period=period)
|
|
TrendResult.objects.update_or_create(
|
|
user=user,
|
|
trend_slug=slug,
|
|
period=period,
|
|
defaults={"data": data, "computed_at": timezone.now()},
|
|
)
|
|
return (timezone.now() - start).total_seconds()
|