diff --git a/.DS_Store b/.DS_Store index 4609d8f..9569d1a 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/imports/README.txt b/imports/README.txt index ecda437..0041a59 100644 --- a/imports/README.txt +++ b/imports/README.txt @@ -1 +1 @@ -Place illustrations_seed.csv here. \ No newline at end of file +Place your seed file as illustrations_seed.csv here if you want auto-import on first boot. \ No newline at end of file diff --git a/web/core/utils.py b/web/core/utils.py index 2c21423..415f792 100644 --- a/web/core/utils.py +++ b/web/core/utils.py @@ -1,6 +1,6 @@ import csv, io, re from dateutil import parser as dateparser -from datetime import date, timedelta +from datetime import date from .models import Entry, ScriptureRef SCR_REF_RE = re.compile(r"""^\s*([1-3]?\s*[A-Za-z\.]+)\s+(\d+)(?::(\d+))?(?:\s*[-–—]\s*(\d+)(?::(\d+))?)?\s*$""", re.VERBOSE) @@ -11,7 +11,7 @@ def normalize_book(s): return BOOK_ALIASES.get(b, s.strip()) def parse_scripture(s): - items=[]; + items=[] for p in [x.strip() for x in (s or '').split(';') if x.strip()]: m = SCR_REF_RE.match(p) if not m: items.append(None); continue @@ -69,7 +69,6 @@ def import_csv_bytes(b: bytes, dry_run=True): obj.save(); obj.scripture_refs.all().delete(); report["updated"]+=1 else: obj=Entry.objects.create(**data); report["inserted"]+=1 - from .models import ScriptureRef for it in parsed: if it: ScriptureRef.objects.create(entry=obj, **it) except Exception as e: diff --git a/web/core/views.py b/web/core/views.py index a722f59..6c6f803 100644 --- a/web/core/views.py +++ b/web/core/views.py @@ -43,7 +43,6 @@ def search_page(request): field_options=[{"name":k,"label":label,"checked":bool(selected.get(k))} for k,label in FIELD_ORDER] q=request.GET.get("q","").strip() if q: - # AND across terms, OR across selected fields per term term_list=terms(q) qs=Entry.objects.all() fields=[f for f,sel in selected.items() if sel] @@ -56,7 +55,9 @@ def search_page(request): request.session["result_ids"]=ids if ids: entry=Entry.objects.get(pk=ids[0]) - return render(request,"entry_view.html",{"entry":entry,"locked":True,"position":1,"count":len(ids),"from_search":True}) + subject_list=[t.strip() for t in (entry.subject or "").split(",") if t.strip()] + scripture_list=[t.strip() for t in (entry.scripture_raw or "").split(";") if t.strip()] + return render(request,"entry_view.html",{"entry":entry,"subject_list":subject_list,"scripture_list":scripture_list,"locked":True,"position":1,"count":len(ids),"from_search":True}) total=Entry.objects.count() return render(request,"search.html",{"q":q,"selected":selected,"field_options":field_options,"total":total}) return render(request,"search.html",{"selected":default_fields,"field_options":[{"name":k,"label":lbl,"checked":default_fields.get(k,False)} for k,lbl in FIELD_ORDER]}) @@ -67,7 +68,9 @@ def nav_next(request): if not ids: return redirect("search") idx=int(request.GET.get("i","0")); idx=min(idx+1, len(ids)-1) entry=get_object_or_404(Entry, pk=ids[idx]) - return render(request,"entry_view.html",{"entry":entry,"locked":True,"position":idx+1,"count":len(ids)}) + subject_list=[t.strip() for t in (entry.subject or "").split(",") if t.strip()] + scripture_list=[t.strip() for t in (entry.scripture_raw or "").split(";") if t.strip()] + return render(request,"entry_view.html",{"entry":entry,"subject_list":subject_list,"scripture_list":scripture_list,"locked":True,"position":idx+1,"count":len(ids)}) @login_required def nav_prev(request): @@ -75,14 +78,18 @@ def nav_prev(request): if not ids: return redirect("search") idx=int(request.GET.get("i","0")); idx=max(idx-1, 0) entry=get_object_or_404(Entry, pk=ids[idx]) - return render(request,"entry_view.html",{"entry":entry,"locked":True,"position":idx+1,"count":len(ids)}) + subject_list=[t.strip() for t in (entry.subject or "").split(",") if t.strip()] + scripture_list=[t.strip() for t in (entry.scripture_raw or "").split(";") if t.strip()] + return render(request,"entry_view.html",{"entry":entry,"subject_list":subject_list,"scripture_list":scripture_list,"locked":True,"position":idx+1,"count":len(ids)}) @login_required def entry_view(request, entry_id): entry=get_object_or_404(Entry, pk=entry_id) ids=request.session.get("result_ids",[]); count=len(ids) position=ids.index(entry.id)+1 if entry.id in ids else 1 - return render(request,"entry_view.html",{"entry":entry,"locked":True,"position":position,"count":count}) + subject_list=[t.strip() for t in (entry.subject or "").split(",") if t.strip()] + scripture_list=[t.strip() for t in (entry.scripture_raw or "").split(";") if t.strip()] + return render(request,"entry_view.html",{"entry":entry,"subject_list":subject_list,"scripture_list":scripture_list,"locked":True,"position":position,"count":count}) @login_required def entry_edit(request, entry_id): @@ -141,10 +148,12 @@ def stats_page(request): last365=Entry.objects.filter(date_added__gte=today - timedelta(days=365)).count() buckets=month_buckets_last_12(today) series=[(label, Entry.objects.filter(date_added__gte=start, date_added__lt=end).count()) for label,start,end in buckets] + 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] from collections import Counter 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()]: counts[tag.lower()]+=1 top_subjects=[{"name":n.title(),"count":c} for n,c in counts.most_common(10)] - return render(request,"stats.html",{"total":total,"last30":last30,"last365":last365,"series":series,"top_subjects":top_subjects}) + return render(request,"stats.html",{"total":total,"last30":last30,"last365":last365,"series":series,"heights":heights,"top_subjects":top_subjects}) diff --git a/web/illustrations/settings.py b/web/illustrations/settings.py index bb27151..499ffd0 100644 --- a/web/illustrations/settings.py +++ b/web/illustrations/settings.py @@ -4,7 +4,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = os.getenv("DJANGO_SECRET_KEY","dev-insecure") DEBUG = os.getenv("DJANGO_DEBUG","False") == "True" -ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS","*").split(",") +ALLOWED_HOSTS = [h.strip() for h in os.getenv("DJANGO_ALLOWED_HOSTS","*").split(",") if h.strip()] CSRF_TRUSTED_ORIGINS = [x.strip() for x in os.getenv("CSRF_TRUSTED_ORIGINS","").split(",") if x.strip()] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") diff --git a/web/templates/entry_view.html b/web/templates/entry_view.html index 2cbe177..d8c0c50 100644 --- a/web/templates/entry_view.html +++ b/web/templates/entry_view.html @@ -18,11 +18,11 @@
-
{% for t in entry.subject.split(',') %}{% if t.strip %}{{ t.strip }}{% endif %}{% endfor %}
+
{% for t in subject_list %}{{ t }}{% endfor %}
-
{% for t in entry.scripture_raw.split(';') %}{% if t.strip %}{{ t.strip }}{% endif %}{% endfor %}
+
{% for t in scripture_list %}{{ t }}{% endfor %}
diff --git a/web/templates/stats.html b/web/templates/stats.html index 63c9649..a0c289b 100644 --- a/web/templates/stats.html +++ b/web/templates/stats.html @@ -13,9 +13,8 @@
Entries per month (by Date Added)
- {% with peak=series|map:'1' %}{% endwith %} - {% for label, value in series %} -
+ {% for label, value, height in heights %} +
{% endfor %}