Update web/core/views.py

This commit is contained in:
Joshua Laymon 2025-08-20 02:47:57 +00:00
parent d70cc99e9c
commit d4ef68ee72

View File

@ -663,4 +663,150 @@ def api_get_recent_views(request):
@login_required
def settings_home(request):
return render(request, "settings/home.html")
return render(request, "settings/home.html")
@login_required
def stats_page(request):
from collections import Counter, OrderedDict
from calendar import month_abbr
total = Entry.objects.count()
today = date.today()
last30 = Entry.objects.filter(date_added__gte=today - timedelta(days=30)).count()
last365 = Entry.objects.filter(date_added__gte=today - timedelta(days=365)).count()
# ---- Sparkline (last 12 months) ----
months = []
y = today.year
m = today.month
for i in range(12):
mm = m - i
yy = y
while mm <= 0:
mm += 12
yy -= 1
from datetime import date as _d
start = _d(yy, mm, 1)
end = _d(yy + 1, 1, 1) if mm == 12 else _d(yy, mm + 1, 1)
label = f"{month_abbr[mm]} {str(yy)[2:]}"
months.append((label, start, end))
months = list(reversed(months))
series = [
(label, Entry.objects.filter(date_added__gte=start, date_added__lt=end).count())
for label, start, end in months
]
peak = max((v for _, v in series), default=1)
heights = [
(label, value, 8 + int((value / peak) * 100) if peak else 8)
for label, value in series
]
# ---- Top subjects (existing) ----
subject_counts = Counter()
for subj in Entry.objects.exclude(subject="").values_list("subject", flat=True):
for tag in [t.strip() for t in subj.split(",") if t.strip()]:
subject_counts[tag.lower()] += 1
top_subjects = [{"name": n.title(), "count": c} for n, c in subject_counts.most_common(10)]
# ===============================
# Scripture analytics (from scripture_raw)
# ===============================
# A light normalizer so common abbreviations map to canonical book names.
BOOK_MAP = {
# OT (examples; extend as needed)
"gen":"Genesis","ge":"Genesis","gn":"Genesis",
"ex":"Exodus","exo":"Exodus",
"lev":"Leviticus","le":"Leviticus",
"num":"Numbers","nu":"Numbers",
"de":"Deuteronomy","deut":"Deuteronomy",
"jos":"Joshua","josh":"Joshua",
"jdg":"Judges","judg":"Judges",
"ru":"Ruth","rut":"Ruth",
"ps":"Psalms","psalm":"Psalms","psalms":"Psalms",
"pr":"Proverbs","pro":"Proverbs",
"ec":"Ecclesiastes","ecc":"Ecclesiastes",
"isa":"Isaiah","is":"Isaiah",
"jer":"Jeremiah","je":"Jeremiah",
"eze":"Ezekiel","ez":"Ezekiel",
"da":"Daniel","dan":"Daniel",
"ho":"Hosea","hos":"Hosea",
# NT (examples; extend as needed)
"mt":"Matthew","matt":"Matthew",
"mr":"Mark","mk":"Mark",
"lu":"Luke","lk":"Luke",
"joh":"John","john":"John","jn":"John",
"ac":"Acts","acts":"Acts",
"rom":"Romans","ro":"Romans",
"1cor":"1 Corinthians","1 co":"1 Corinthians","1 cor":"1 Corinthians",
"2cor":"2 Corinthians","2 co":"2 Corinthians","2 cor":"2 Corinthians",
}
BOOK_RE = re.compile(r"""
^\s*
(?P<book>(?:[1-3]\s*)?[A-Za-z\.]+) # optional 1/2/3 prefix + word
[\s\.]+
(?P<ref>\d+[:\.]\d+.*)? # 3:16 or 3.16 etc (optional tail)
""", re.X)
def normalize_book(raw):
b = raw.strip().lower().replace('.', '')
b = re.sub(r'\s+', '', b) # "1 john" -> "1john"
return BOOK_MAP.get(b, raw.strip().title())
def split_refs(text):
if not text:
return []
# Entries are typically separated by semicolons; allow commas too.
parts = re.split(r'[;]+', text)
return [p.strip() for p in parts if p.strip()]
def parse_piece(piece):
m = BOOK_RE.match(piece)
if not m:
return None, None
book = normalize_book(m.group('book'))
ref = (m.group('ref') or '').strip()
return book, (f"{book} {ref}" if ref else book)
book_counts = Counter()
ref_counts = Counter()
refs_per_entry = []
entries_with_script = (Entry.objects
.exclude(scripture_raw__isnull=True)
.exclude(scripture_raw__exact=""))
for e in entries_with_script.iterator():
pieces = split_refs(e.scripture_raw)
entry_ref_count = 0
for piece in pieces:
book, full = parse_piece(piece)
if not book:
continue
book_counts[book] += 1
if full and full != book:
ref_counts[full] += 1
entry_ref_count += 1
if entry_ref_count:
refs_per_entry.append(entry_ref_count)
avg_refs_per_entry = round(sum(refs_per_entry) / len(refs_per_entry), 2) if refs_per_entry else 0
top_books = list(book_counts.most_common(10))
top_refs = list(ref_counts.most_common(10))
return render(
request,
"stats.html",
{
"total": total,
"last30": last30,
"last365": last365,
"series": series,
"heights": heights,
"top_subjects": top_subjects,
# NEW:
"avg_refs_per_entry": avg_refs_per_entry,
"top_books": top_books, # iterable of (book, count)
"top_refs": top_refs, # iterable of (ref, count)
},
)