import calendar
from collections import defaultdict
from blog.models import Post
def get_month_posts(year, month):
"""
Returns queryset of all posts in a given month.
"""
return (
Post.objects
.filter(date__year=year, date__month=month)
.order_by("date")
)
def group_posts_by_day(posts_qs):
"""
Returns: {"01": [...], "02": [...], ...}
"""
grouped = defaultdict(list)
for p in posts_qs:
key = f"{p.date.day:02d}"
grouped[key].append(p)
return grouped
def get_calendar_weeks(year, month):
"""
Returns calendar weeks layout.
"""
cal = calendar.Calendar(firstweekday=0)
return cal.monthdayscalendar(year, month)
def get_month_navigation(year, month):
"""
Returns prev/next month info + existence of posts.
"""
if month > 1:
prev_month = month - 1
prev_year = year
else:
prev_month = 12
prev_year = year - 1
if month < 12:
next_month = month + 1
next_year = year
else:
next_month = 1
next_year = year + 1
has_prev_posts = Post.objects.filter(
date__year=prev_year,
date__month=prev_month
).exists()
has_next_posts = Post.objects.filter(
date__year=next_year,
date__month=next_month
).exists()
return {
"prev_year": prev_year,
"next_year": next_year,
"prev_month": prev_month,
"next_month": next_month,
"prev_month_str": f"{prev_month:02d}",
"next_month_str": f"{next_month:02d}",
"prev_month_name": calendar.month_name[prev_month],
"next_month_name": calendar.month_name[next_month],
"has_prev_posts": has_prev_posts,
"has_next_posts": has_next_posts,
}
def build_month_context(year, month):
"""
Fully prepares the full month calendar structure
used by both archive and post detail views.
"""
posts_qs = get_month_posts(year, month)
return {
"year": year,
"month": {
"num": month,
"name": calendar.month_name[month],
"month_str": f"{month:02d}",
"posts_by_day": group_posts_by_day(posts_qs),
"weeks": get_calendar_weeks(year, month),
"count": posts_qs.count(),
"has_posts": posts_qs.exists(),
},
"posts": posts_qs,
**get_month_navigation(year, month),
}
def get_year_month_counts(year):
"""
Returns {1: count, 2: count, ... 12: count}.
"""
return {
m: Post.objects.filter(date__year=year, date__month=m).count()
for m in range(1, 13)
}
def build_year_context(year):
"""
Returns full context needed for archive_year view.
"""
year = int(year)
month_counts = get_year_month_counts(year)
months = [
{
"num": m,
"name": calendar.month_name[m],
"month_str": f"{m:02d}",
"count": month_counts[m],
"has_posts": month_counts[m] > 0,
}
for m in range(1, 13)
]
rows = [months[i:i+3] for i in range(0, 12, 3)]
prev_year = year - 1
next_year = year + 1
has_prev = Post.objects.filter(date__year=prev_year).exists()
has_next = Post.objects.filter(date__year=next_year).exists()
return {
"year": year,
"rows": rows,
"prev_year": prev_year,
"next_year": next_year,
"has_prev_year_posts": has_prev,
"has_next_year_posts": has_next,
}