Update
This commit is contained in:
parent
3458501272
commit
6426211800
@ -1 +1 @@
|
||||
Place illustrations_seed.csv here.
|
||||
Place your seed file as illustrations_seed.csv here if you want auto-import on first boot.
|
||||
@ -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:
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -18,11 +18,11 @@
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Subject</label>
|
||||
<div class="chips">{% for t in entry.subject.split(',') %}{% if t.strip %}<span class="chip">{{ t.strip }}</span>{% endif %}{% endfor %}</div>
|
||||
<div class="chips">{% for t in subject_list %}<span class="chip">{{ t }}</span>{% endfor %}</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Scripture</label>
|
||||
<div class="chips">{% for t in entry.scripture_raw.split(';') %}{% if t.strip %}<span class="chip" style="background:#eef4ff;">{{ t.strip }}</span>{% endif %}{% endfor %}</div>
|
||||
<div class="chips">{% for t in scripture_list %}<span class="chip" style="background:#eef4ff;">{{ t }}</span>{% endfor %}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
@ -13,9 +13,8 @@
|
||||
<div class="card">
|
||||
<div class="small">Entries per month (by Date Added)</div>
|
||||
<div style="display:flex; gap:6px; align-items:flex-end; height:120px; margin-top:8px;">
|
||||
{% with peak=series|map:'1' %}{% endwith %}
|
||||
{% for label, value in series %}
|
||||
<div title="{{ label }}: {{ value }}" style="width:24px; background:#dbe7ff; border:1px solid #c8d6ff; height: {{ value|add:5 }}px;"></div>
|
||||
{% for label, value, height in heights %}
|
||||
<div title="{{ label }}: {{ value }}" style="width:24px; background:#dbe7ff; border:1px solid #c8d6ff; height: {{ height }}px;"></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="small" style="display:flex; gap:8px; flex-wrap:wrap; margin-top:6px;">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user