122 lines
6.2 KiB
Python
122 lines
6.2 KiB
Python
import json, sys
|
|
sys.path.insert(0, "/Users/drjones/pest-nocturne")
|
|
from wa_exam_data import WAEXAM
|
|
|
|
p = "/Users/drjones/pest-nocturne/index.html"
|
|
s = open(p).read()
|
|
|
|
# 1. Add WA Prep tab to VIEWS
|
|
old_views = 'const VIEWS=[["home","Suite"],["field","Field Guide"],["keys","Keys"],["house","22 Points"],["cards","Flashcards"],["exam","Exam"],["gloss","Glossary"],["wa","WA Cert"]];'
|
|
new_views = 'const VIEWS=[["home","Suite"],["field","Field Guide"],["keys","Keys"],["house","22 Points"],["cards","Flashcards"],["exam","Exam"],["gloss","Glossary"],["wa","WA Cert"],["waprep","WA Prep"]];'
|
|
assert old_views in s, "VIEWS not found"
|
|
s = s.replace(old_views, new_views)
|
|
|
|
# 2. Add section
|
|
old_sec = '<section class="view" id="v-wa"></section>'
|
|
new_sec = old_sec + '\n <section class="view" id="v-waprep"></section>'
|
|
assert old_sec in s, "v-wa section not found"
|
|
s = s.replace(old_sec, new_sec)
|
|
|
|
# 3. Build the WA Prep view + quiz (self-contained)
|
|
wa_data_json = json.dumps(WAEXAM) # [{cat,q,a:[...],c,x},...]
|
|
|
|
RENDER = r'''
|
|
<script>
|
|
/* ==== WA PREP — original practice question bank ==== */
|
|
const WAEXAM = __WADATA__;
|
|
|
|
(function(){
|
|
let curCat = 'All', qi = 0, score = 0, answered = 0, qlist = [];
|
|
const CATS = ['All','Core','Structural','Ornamental & Turf','Agricultural'];
|
|
|
|
function buildList(){
|
|
qlist = curCat === 'All' ? WAEXAM.slice() : WAEXAM.filter(q => q.cat === curCat);
|
|
// shuffle deterministically-ish
|
|
qlist = qlist.map((q,i)=>({...q, _r: Math.random()})).sort((a,b)=>a._r-b._r);
|
|
qi = 0; score = 0; answered = 0;
|
|
}
|
|
|
|
function render(){
|
|
const root = document.getElementById('v-waprep');
|
|
const cats = CATS.map(c=>`<button class="wac${c===curCat?' on':''}" data-wacat="${c}">${c}</button>`).join('');
|
|
const total = qlist.length;
|
|
const q = qlist[qi];
|
|
let body = '';
|
|
if (total === 0) {
|
|
body = `<p class="lede">No questions in this category yet.</p>`;
|
|
} else {
|
|
const done = answered >= total;
|
|
body = `
|
|
<div class="wa-prog"><span>${Math.min(answered+1, total)} / ${total}</span><span class="wa-score">Score ${score}/${answered||1}</span></div>
|
|
<div class="wa-q">${q.q}</div>
|
|
<div class="wa-opts">${q.a.map((opt, i)=>`<button class="wa-opt" data-i="${i}">${String.fromCharCode(65+i)}. ${opt}</button>`).join('')}</div>
|
|
<div class="wa-expl" id="wa-expl" style="display:none"></div>
|
|
<div class="wa-nav" style="display:none">
|
|
<button class="btn" id="wa-next">${qi+1 < total ? 'Next →' : 'Restart'}</button>
|
|
</div>`;
|
|
}
|
|
root.innerHTML = `
|
|
<section class="hero" style="padding-top:26px">
|
|
<div class="eyebrow">Original question bank · not scraped, written for you</div>
|
|
<h1><span class="lo">Pass the license</span>WA<br><em>Exam Prep</em></h1>
|
|
<p class="lede">${WAEXAM.length} original Washington pesticide-applicator questions across Core, Structural, Ornamental & Turf, and Agricultural. Answer, read why, and track your score.</p>
|
|
<div class="wa-cats">${cats}</div>
|
|
</section>
|
|
<section class="section" style="padding-top:10px">
|
|
<div class="wa-card">${body}</div>
|
|
</section>`;
|
|
|
|
// wire
|
|
root.querySelectorAll('[data-wacat]').forEach(b=>b.addEventListener('click', ()=>{ curCat = b.dataset.wacat; buildList(); render(); }));
|
|
root.querySelectorAll('.wa-opt').forEach(b=>b.addEventListener('click', ()=>{
|
|
const i = +b.dataset.i;
|
|
const correct = i === q.c;
|
|
if (correct) score++;
|
|
answered++;
|
|
root.querySelectorAll('.wa-opt').forEach(o=>{
|
|
o.disabled = true;
|
|
if (+o.dataset.i === q.c) o.classList.add('right');
|
|
});
|
|
if (!correct) b.classList.add('wrong');
|
|
const expl = root.querySelector('#wa-expl');
|
|
expl.style.display = 'block';
|
|
expl.innerHTML = `<strong>${correct ? 'Correct' : 'Incorrect'}.</strong> ${q.x}`;
|
|
root.querySelector('.wa-nav').style.display = 'block';
|
|
root.querySelector('.wa-score').textContent = `Score ${score}/${answered}`;
|
|
}));
|
|
const nx = root.querySelector('#wa-next');
|
|
if (nx) nx.addEventListener('click', ()=>{ if (qi+1 < total) { qi++; } else { buildList(); } render(); });
|
|
}
|
|
|
|
// render once when the view is first opened
|
|
const origGo = window.go;
|
|
window.go = function(v){
|
|
if (v === 'waprep') { buildList(); render(); }
|
|
origGo(v);
|
|
};
|
|
})();
|
|
</script>
|
|
<style>
|
|
.wa-cats{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
|
|
.wa-cats .wac{padding:8px 16px;border-radius:999px;border:1px solid var(--line,#2C3455);background:var(--surface,#141A30);color:var(--text,#EDE7D6);cursor:pointer;font-size:.85rem}
|
|
.wa-cats .wac.on{background:var(--brass,#E3A53F);color:#1a1206;border-color:var(--brass,#E3A53F);font-weight:700}
|
|
.wa-card{background:var(--surface,#141A30);border:1px solid var(--line,#2C3455);border-radius:14px;padding:24px;max-width:760px;margin:0 auto}
|
|
.wa-prog{display:flex;justify-content:space-between;font-family:"JetBrains Mono",monospace;font-size:.78rem;color:var(--text-faint,#6E7688);margin-bottom:16px}
|
|
.wa-score{color:var(--verdigris,#54BFA9)}
|
|
.wa-q{font-family:Fraunces,serif;font-size:1.3rem;line-height:1.4;color:var(--text,#EDE7D6);margin-bottom:18px}
|
|
.wa-opts{display:flex;flex-direction:column;gap:10px}
|
|
.wa-opt{text-align:left;padding:12px 16px;border-radius:10px;border:1px solid var(--line,#2C3455);background:var(--surface-2,#1B2239);color:var(--text,#EDE7D6);cursor:pointer;font-size:.95rem;line-height:1.4}
|
|
.wa-opt:hover:not(:disabled){border-color:var(--brass,#E3A53F)}
|
|
.wa-opt.right{background:color-mix(in srgb,var(--verdigris,#54BFA9) 22%,transparent);border-color:var(--verdigris,#54BFA9)}
|
|
.wa-opt.wrong{background:color-mix(in srgb,var(--crimson,#DE5F45) 22%,transparent);border-color:var(--crimson,#DE5F45)}
|
|
.wa-opt:disabled{cursor:default;opacity:.9}
|
|
.wa-expl{margin-top:16px;padding:12px 16px;border-left:3px solid var(--verdigris,#54BFA9);background:color-mix(in srgb,var(--verdigris,#54BFA9) 9%,transparent);border-radius:8px;font-size:.9rem;line-height:1.5;color:var(--text,#EDE7D6)}
|
|
.wa-nav{margin-top:16px;text-align:right}
|
|
</style>
|
|
'''.replace('__WADATA__', wa_data_json)
|
|
|
|
s = s.rstrip() + '\n' + RENDER + '\n'
|
|
|
|
open(p, "w").write(s)
|
|
print(f"injected WA Prep view with {len(WAEXAM)} questions; size {len(s)}")
|