import os import platform from datetime import timedelta from django import get_version as django_version from django.contrib import messages from django.contrib.auth import login from django.db.models import Avg, Count, Q, Sum from django.db.models.functions import Coalesce from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone from .forms import MomentumEntryForm, SignUpForm from .models import Category, MomentumEntry APP_NAME = "Momentum Atlas" APP_TAGLINE = "A polished personal dashboard for tracking focus, energy, and small wins." def _entries_for_request(request): entries = MomentumEntry.objects.select_related("category") if request.user.is_authenticated: return entries.filter(user=request.user) return entries.filter(user__isnull=True) def _categories_for_request(request): if request.user.is_authenticated: entry_filter = Q(entries__user=request.user) else: entry_filter = Q(entries__user__isnull=True) return Category.objects.annotate(entry_total=Count("entries", filter=entry_filter)) def _build_weekly_trend(entries): today = timezone.localdate() start_date = today - timedelta(days=6) trend_source = ( entries.filter(entry_date__gte=start_date, entry_date__lte=today) .values("entry_date") .annotate( avg_focus=Coalesce(Avg("focus_score"), 0.0), avg_energy=Coalesce(Avg("energy_score"), 0.0), total_minutes=Coalesce(Sum("deep_work_minutes"), 0), ) ) by_date = {row["entry_date"]: row for row in trend_source} trend = [] for offset in range(7): day = start_date + timedelta(days=offset) row = by_date.get(day, {}) focus = float(row.get("avg_focus") or 0) energy = float(row.get("avg_energy") or 0) minutes = int(row.get("total_minutes") or 0) focus_level = int(round((focus / 10) * 10) * 10) if focus else 0 energy_level = int(round((energy / 10) * 10) * 10) if energy else 0 trend.append( { "date": day, "label": day.strftime("%a"), "focus": round(focus, 1), "energy": round(energy, 1), "minutes": minutes, "focus_level": max(0, min(100, focus_level)), "energy_level": max(0, min(100, energy_level)), } ) return trend def _build_weekly_summary(weekly_trend): check_in_days = sum(1 for day in weekly_trend if day["focus"] or day["energy"] or day["minutes"]) total_minutes = sum(day["minutes"] for day in weekly_trend) strongest_day = max( weekly_trend, key=lambda day: (day["focus"] + day["energy"], day["minutes"]), default=None, ) strongest_has_data = bool( strongest_day and (strongest_day["focus"] or strongest_day["energy"] or strongest_day["minutes"]) ) strongest_score = round(((strongest_day["focus"] + strongest_day["energy"]) / 2), 1) if strongest_has_data else 0 return { "check_in_days": check_in_days, "total_minutes": total_minutes, "strongest_label": strongest_day["label"] if strongest_has_data else "No data yet", "strongest_score": strongest_score, } def _build_history_overview(entries): totals = entries.aggregate( total_entries=Count("id"), avg_focus=Coalesce(Avg("focus_score"), 0.0), avg_energy=Coalesce(Avg("energy_score"), 0.0), total_minutes=Coalesce(Sum("deep_work_minutes"), 0), ) avg_focus = float(totals["avg_focus"] or 0) avg_energy = float(totals["avg_energy"] or 0) avg_momentum = round((avg_focus + avg_energy) / 2, 1) if totals["total_entries"] else 0 ordered_dates = [] seen_dates = set() for entry_date in entries.values_list("entry_date", flat=True): if entry_date not in seen_dates: ordered_dates.append(entry_date) seen_dates.add(entry_date) streak = 0 previous_date = None for entry_date in ordered_dates: if previous_date is None: streak = 1 previous_date = entry_date continue if previous_date - timedelta(days=1) == entry_date: streak += 1 previous_date = entry_date continue break top_category = ( entries.values("category__name") .annotate(total=Count("id")) .order_by("-total", "category__name") .first() ) latest_entry = entries.first() return { "total_entries": totals["total_entries"], "avg_focus": round(avg_focus, 1), "avg_energy": round(avg_energy, 1), "avg_momentum": avg_momentum, "total_minutes": int(totals["total_minutes"] or 0), "streak": streak if totals["total_entries"] else 0, "latest_entry": latest_entry, "top_category": top_category["category__name"] if top_category else "No category yet", } def _build_recent_activity(entries, limit=7): recent_entries = list(entries[:limit]) if not recent_entries: return [] recent_entries.reverse() max_minutes = max((entry.deep_work_minutes for entry in recent_entries), default=0) activity = [] for entry in recent_entries: momentum = float(entry.momentum_score) minutes_width = 0 if entry.deep_work_minutes and max_minutes: minutes_width = max(14, int(round((entry.deep_work_minutes / max_minutes) * 100))) activity.append( { "entry": entry, "focus_width": entry.focus_score * 10, "energy_width": entry.energy_score * 10, "momentum_width": int(round(momentum * 10)), "minutes_width": minutes_width, } ) return activity def _build_category_breakdown(entries): grouped = list( entries.values("category__name", "category__slug", "category__accent_color") .annotate( entry_total=Count("id"), avg_focus=Coalesce(Avg("focus_score"), 0.0), avg_energy=Coalesce(Avg("energy_score"), 0.0), total_minutes=Coalesce(Sum("deep_work_minutes"), 0), ) .order_by("-entry_total", "category__name")[:4] ) total_entries = sum(item["entry_total"] for item in grouped) or 1 breakdown = [] for item in grouped: avg_momentum = round((float(item["avg_focus"] or 0) + float(item["avg_energy"] or 0)) / 2, 1) breakdown.append( { "name": item["category__name"], "slug": item["category__slug"], "accent_color": item["category__accent_color"] or "#0F766E", "entry_total": item["entry_total"], "total_minutes": int(item["total_minutes"] or 0), "avg_momentum": avg_momentum, "share_percent": max(8, int(round((item["entry_total"] / total_entries) * 100))), "momentum_width": max(8, int(round(avg_momentum * 10))) if avg_momentum else 0, } ) return breakdown def _dashboard_context(request): entries = _entries_for_request(request) recent_entries = entries[:6] last_30_days = timezone.localdate() - timedelta(days=29) stats_window = entries.filter(entry_date__gte=last_30_days) totals = stats_window.aggregate( total_entries=Count("id"), avg_focus=Coalesce(Avg("focus_score"), 0.0), avg_energy=Coalesce(Avg("energy_score"), 0.0), total_minutes=Coalesce(Sum("deep_work_minutes"), 0), ) active_days = stats_window.values("entry_date").distinct().count() top_category = ( stats_window.values("category__name") .annotate(total=Count("id")) .order_by("-total", "category__name") .first() ) weekly_trend = _build_weekly_trend(entries) focus_average = round(float(totals["avg_focus"] or 0), 1) energy_average = round(float(totals["avg_energy"] or 0), 1) if focus_average >= 8: spotlight = "Your recent focus trend is excellent—keep protecting that deep-work time." elif focus_average >= 6: spotlight = "Momentum is building. A little more consistency could turn this into a real streak." elif totals["total_entries"]: spotlight = "A reset week might help—shrink the task list and aim for one clear win each day." else: spotlight = "Your dashboard will start filling in as soon as you save the first check-in." return { "recent_entries": recent_entries, "categories": _categories_for_request(request), "weekly_trend": weekly_trend, "weekly_summary": _build_weekly_summary(weekly_trend), "is_demo_mode": not request.user.is_authenticated, "stats": { "total_entries": totals["total_entries"], "avg_focus": focus_average, "avg_energy": energy_average, "total_minutes": totals["total_minutes"], "active_days": active_days, "top_category": top_category["category__name"] if top_category else "No category yet", "spotlight": spotlight, }, } def home(request): host_name = request.get_host().lower() agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" now = timezone.now() if request.method == "POST": if not request.user.is_authenticated: messages.info(request, "Create a free account or log in to save personal check-ins.") return redirect(f"{reverse('login')}?next={reverse('home')}") form = MomentumEntryForm(request.POST) if form.is_valid(): entry = form.save(commit=False) entry.user = request.user entry.save() messages.success(request, "Momentum captured. Your new private check-in is ready.") return redirect(f"{entry.get_absolute_url()}?created=1") messages.error(request, "Please fix the form errors and try again.") else: form = MomentumEntryForm() context = { "project_name": APP_NAME, "agent_brand": agent_brand, "django_version": django_version(), "python_version": platform.python_version(), "current_time": now, "host_name": host_name, "project_description": os.getenv("PROJECT_DESCRIPTION", APP_TAGLINE), "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), "page_title": f"{APP_NAME} | Daily focus dashboard", "meta_description": "Track daily focus, energy, and deep-work minutes in a polished Python dashboard.", "form": form, **_dashboard_context(request), } return render(request, "core/index.html", context) def entry_list(request): selected_slug = request.GET.get("category", "") entries = _entries_for_request(request) if selected_slug: entries = entries.filter(category__slug=selected_slug) context = { "page_title": f"All check-ins | {APP_NAME}", "meta_description": "Browse recent check-ins and filter your momentum history by category.", "entries": entries, "categories": _categories_for_request(request), "selected_slug": selected_slug, "is_demo_mode": not request.user.is_authenticated, "history_overview": _build_history_overview(entries), "recent_activity": _build_recent_activity(entries), "category_breakdown": _build_category_breakdown(entries), } return render(request, "core/entry_list.html", context) def entry_detail(request, pk): scoped_entries = _entries_for_request(request) entry = get_object_or_404(scoped_entries, pk=pk) related_entries = scoped_entries.filter(category=entry.category).exclude(pk=entry.pk)[:3] context = { "page_title": f"{entry.title} | {APP_NAME}", "meta_description": entry.takeaway, "entry": entry, "related_entries": related_entries, "created": request.GET.get("created") == "1", "is_demo_mode": not request.user.is_authenticated, } return render(request, "core/entry_detail.html", context) def signup(request): if request.user.is_authenticated: return redirect("home") if request.method == "POST": form = SignUpForm(request.POST) if form.is_valid(): user = form.save() login(request, user) messages.success(request, "Your account is ready. You can now save private momentum entries.") return redirect(request.POST.get("next") or "home") messages.error(request, "Please fix the sign-up form and try again.") else: form = SignUpForm() context = { "page_title": f"Create account | {APP_NAME}", "meta_description": "Create a private Momentum Atlas account to save personal check-ins.", "form": form, "next_url": request.GET.get("next") or request.POST.get("next") or reverse("home"), } return render(request, "core/signup.html", context)