Add real pest photos (25 Wikimedia) + WA State field notes
4
app.py
@@ -56,6 +56,10 @@ def create_btcpay_invoice(order_id):
|
||||
def index():
|
||||
return send_from_directory(BASE_DIR, "index.html")
|
||||
|
||||
@app.route("/images/<path:filename>")
|
||||
def images(filename):
|
||||
return send_from_directory(os.path.join(BASE_DIR, "images"), filename)
|
||||
|
||||
@app.route("/api/status")
|
||||
def api_status():
|
||||
oid = request.args.get("order", "")
|
||||
|
||||
BIN
images/american.jpg
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
images/bat.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
images/bedbug.jpg
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
images/blackWidow.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
images/brownband.jpg
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
images/buprestid.jpg
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
images/carpAnt.jpg
Normal file
|
After Width: | Height: | Size: 644 KiB |
BIN
images/carpetLarva.jpg
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
images/clothesMoth.jpg
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
images/clusterFly.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
images/dampwood.jpg
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
images/drugstore.png
Normal file
|
After Width: | Height: | Size: 707 KiB |
BIN
images/flea.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
images/german.jpg
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
images/hobo.gif
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
images/indianmeal.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
images/moleGopher.jpg
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
images/mosquito.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
images/mothFly.jpg
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
images/mound.jpg
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
images/notchAnt.jpg
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
images/oriental.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
images/sawtooth.png
Normal file
|
After Width: | Height: | Size: 5.1 MiB |
BIN
images/subter.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
images/tick.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
25
index.html
@@ -2095,7 +2095,7 @@ $('#v-field').innerHTML=`
|
||||
function tbl(t){return `<div class="tbl-wrap"><table class="tbl"><thead><tr>${t.head.map(h=>`<th>${h}</th>`).join('')}</tr></thead><tbody>${t.rows.map(r=>`<tr>${r.map(d=>`<td>${d}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`;}
|
||||
function plateHTML(p){
|
||||
return `<article class="plate">
|
||||
<figure class="plate-art">${ART[p.art]||''}<figcaption>${p.cap||''}</figcaption></figure>
|
||||
<figure class="plate-art">${plateImg(p.art)}<figcaption>${p.cap||''}</figcaption></figure>
|
||||
<div>
|
||||
<h3>${p.h}</h3>
|
||||
${p.sci?`<span class="binom">${p.sci}</span>`:''}
|
||||
@@ -2134,7 +2134,7 @@ function renderKey(nodeId){
|
||||
const r=K.res[nodeId];
|
||||
const crumbs=`<div class="trail"><i>${K.title}</i>${trail.map(t=>`<i>${t}</i>`).join('')}</div>`;
|
||||
if(r){
|
||||
st.innerHTML=`<div class="key">${crumbs}<div class="result">${ART[r.art]||''}<h3>${r.n}</h3></div><p style="color:var(--text-dim)">${r.x}</p><div class="cta-row"><button class="btn ghost" data-restart="1">Start this key again</button></div></div>`;
|
||||
st.innerHTML=`<div class="key">${crumbs}<div class="result">${plateImg(r.art)}<h3>${r.n}</h3></div><p style="color:var(--text-dim)">${r.x}</p><div class="cta-row"><button class="btn ghost" data-restart="1">Start this key again</button></div></div>`;
|
||||
}else{
|
||||
const n=K.nodes[nodeId];
|
||||
st.innerHTML=`<div class="key">${crumbs}<div class="step">Couplet ${trail.length+1}</div><div class="prompt">${n.q}</div><div class="couplet">${n.o.map((o,i)=>`<button data-go="${o[1]}" data-lab="${esc(o[0]).slice(0,26)}">${o[0]}</button>`).join('')}</div>${trail.length?'<div class="cta-row"><button class="btn ghost" data-restart="1">Start over</button></div>':''}</div>`;
|
||||
@@ -2371,3 +2371,24 @@ drawGloss('');
|
||||
Pest Nocturne — a study suite for WSU EM019 · <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener" style="color:var(--verdigris,#54BFA9)">Buy me a coffee</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ==== REAL PHOTOS + WA FIELD NOTES (injected) ==== */
|
||||
const PHOTOS={"blackWidow":"blackWidow.jpg","dampwood":"dampwood.jpg","subter":"subter.jpg","carpAnt":"carpAnt.jpg","notchAnt":"notchAnt.jpg","mound":"mound.jpg","buprestid":"buprestid.jpg","german":"german.jpg","oriental":"oriental.jpg","american":"american.jpg","brownband":"brownband.jpg","drugstore":"drugstore.png","sawtooth":"sawtooth.png","indianmeal":"indianmeal.jpg","carpetLarva":"carpetLarva.jpg","clothesMoth":"clothesMoth.jpg","clusterFly":"clusterFly.jpg","mothFly":"mothFly.jpg","hobo":"hobo.gif","mosquito":"mosquito.jpg","flea":"flea.jpg","tick":"tick.jpg","bedbug":"bedbug.jpg","moleGopher":"moleGopher.jpg","bat":"bat.jpg"};
|
||||
const WANOTES={"blackWidow":`Washington's widow is the <em>western</em> black widow (<i>Latrodectus hesperus</i>) — most common east of the Cascades. The red hourglass is on the <strong>underside</strong> of the abdomen, never the top.`,"dampwood":`The <strong>Pacific dampwood termite</strong> is the dominant termite <em>west of the Cascades</em>. It needs moist wood but <strong>no ground contact</strong> — soldiers are large with dark, rectangular heads. Look for it in wet framing and log piles.`,"subter":`The <strong>western subterranean termite</strong> (<i>Reticulitermes hesperus</i>) is WA's main soil-touching termite. Winged swarms fly on warm, humid spring days — a swarm indoors is the classic WA alarm bell.`,"carpAnt":`Carpenter ants are the <strong>#1 wood-destroying pest in Washington</strong>. The PNW species <i>Camponotus modoc</i> has a smooth, evenly-rounded thorax. Piles of coarse sawdust ('frass') under a sill plate are the tell.`,"notchAnt":`Moisture ants (<i>Lasius</i>) are a <strong>Washington sign of decaying wood</strong> — their presence means water is already winning. The notched, uneven thorax (side view) separates them from carpenter ants.`,"mound":`The <strong>western thatching ant</strong> (<i>Formica obscuripes</i>) builds the dome mounds of grass and twigs seen across WA clear-cuts and yards — they bite, don't sting.`,"buprestid":`The <strong>golden buprestid</strong> (<i>Buprestis aurulenta</i>) is a PNW native — its larvae tunnel Douglas fir and can emerge from milled lumber years later.`,"hobo":`The <strong>hobo spider</strong> (<i>Eratigena agrestis</i>) is a Pacific Northwest native and one of the most common house spiders in WA — the old 'hobo bite is necrotic' claim is <strong>disproven</strong>; it's medically harmless.`,"tick":`The <strong>Rocky Mountain wood tick</strong> (<i>Dermacentor andersoni</i>) is the hard tick of Eastern WA rangeland — a vector of Rocky Mountain spotted fever.`,"bat":`The <strong>little brown bat</strong> (<i>Myotis lucifugus</i>) is WA's most common bat — the usual species found roosting in attics and gable vents.`,"moleGopher":`The <strong>Townsend's mole</strong> is the mole of Western WA lawns — raised surface tunnels, not the fan-shaped mounds of a gopher.`,"mosquito":`WA's most bothersome mosquitoes are <i>Culex</i> and <i>Aedes</i> species breeding in standing water — <i>Culex pipiens</i> can vector West Nile in Eastern WA.`,"bedbug":`Bed bugs are a growing WA urban issue — confirm with the <strong>cast skins and fecal spots</strong> at mattress seams, not just bites.`};
|
||||
function plateImg(art){
|
||||
if(PHOTOS[art]){
|
||||
var img='<img class="pp" src="/images/'+PHOTOS[art]+'" alt="'+art+'" loading="lazy">';
|
||||
var wa=WANOTES[art]?'<aside class="wa-note"><strong>WA field note</strong> '+WANOTES[art]+'</aside>':'';
|
||||
return img+wa;
|
||||
}
|
||||
return ART[art]||'';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.plate-art img.pp{width:100%;height:auto;border-radius:12px;border:1px solid var(--line,#2C3455);background:var(--surface-2,#1B2239);object-fit:contain}
|
||||
.plate-art .wa-note,.key .result .wa-note{margin:12px 0 0;padding:10px 14px;border-left:3px solid var(--verdigris,#54BFA9);background:color-mix(in srgb,var(--verdigris,#54BFA9) 9%,transparent);border-radius:8px;font-size:.85rem;line-height:1.5;color:var(--text,#EDE7D6)}
|
||||
.plate-art .wa-note strong,.key .result .wa-note strong{color:var(--verdigris,#54BFA9);display:block;margin-bottom:3px;font-family:"JetBrains Mono",monospace;font-size:.7rem;letter-spacing:.06em;text-transform:uppercase}
|
||||
</style>
|
||||
|
||||
|
||||
74
integrate_photos.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import io
|
||||
|
||||
p = "index.html"
|
||||
s = open(p).read()
|
||||
|
||||
PHOTOS = {
|
||||
"blackWidow":"blackWidow.jpg","dampwood":"dampwood.jpg","subter":"subter.jpg",
|
||||
"carpAnt":"carpAnt.jpg","notchAnt":"notchAnt.jpg","mound":"mound.jpg",
|
||||
"buprestid":"buprestid.jpg","german":"german.jpg","oriental":"oriental.jpg",
|
||||
"american":"american.jpg","brownband":"brownband.jpg","drugstore":"drugstore.png",
|
||||
"sawtooth":"sawtooth.png","indianmeal":"indianmeal.jpg","carpetLarva":"carpetLarva.jpg",
|
||||
"clothesMoth":"clothesMoth.jpg","clusterFly":"clusterFly.jpg","mothFly":"mothFly.jpg",
|
||||
"hobo":"hobo.gif","mosquito":"mosquito.jpg","flea":"flea.jpg","tick":"tick.jpg",
|
||||
"bedbug":"bedbug.jpg","moleGopher":"moleGopher.jpg","bat":"bat.jpg",
|
||||
}
|
||||
|
||||
WANOTES = {
|
||||
"blackWidow": "Washington's widow is the <em>western</em> black widow (<i>Latrodectus hesperus</i>) — most common east of the Cascades. The red hourglass is on the <strong>underside</strong> of the abdomen, never the top.",
|
||||
"dampwood": "The <strong>Pacific dampwood termite</strong> is the dominant termite <em>west of the Cascades</em>. It needs moist wood but <strong>no ground contact</strong> — soldiers are large with dark, rectangular heads. Look for it in wet framing and log piles.",
|
||||
"subter": "The <strong>western subterranean termite</strong> (<i>Reticulitermes hesperus</i>) is WA's main soil-touching termite. Winged swarms fly on warm, humid spring days — a swarm indoors is the classic WA alarm bell.",
|
||||
"carpAnt": "Carpenter ants are the <strong>#1 wood-destroying pest in Washington</strong>. The PNW species <i>Camponotus modoc</i> has a smooth, evenly-rounded thorax. Piles of coarse sawdust ('frass') under a sill plate are the tell.",
|
||||
"notchAnt": "Moisture ants (<i>Lasius</i>) are a <strong>Washington sign of decaying wood</strong> — their presence means water is already winning. The notched, uneven thorax (side view) separates them from carpenter ants.",
|
||||
"mound": "The <strong>western thatching ant</strong> (<i>Formica obscuripes</i>) builds the dome mounds of grass and twigs seen across WA clear-cuts and yards — they bite, don't sting.",
|
||||
"buprestid": "The <strong>golden buprestid</strong> (<i>Buprestis aurulenta</i>) is a PNW native — its larvae tunnel Douglas fir and can emerge from milled lumber years later.",
|
||||
"hobo": "The <strong>hobo spider</strong> (<i>Eratigena agrestis</i>) is a Pacific Northwest native and one of the most common house spiders in WA — the old 'hobo bite is necrotic' claim is <strong>disproven</strong>; it's medically harmless.",
|
||||
"tick": "The <strong>Rocky Mountain wood tick</strong> (<i>Dermacentor andersoni</i>) is the hard tick of Eastern WA rangeland — a vector of Rocky Mountain spotted fever.",
|
||||
"bat": "The <strong>little brown bat</strong> (<i>Myotis lucifugus</i>) is WA's most common bat — the usual species found roosting in attics and gable vents.",
|
||||
"moleGopher": "The <strong>Townsend's mole</strong> is the mole of Western WA lawns — raised surface tunnels, not the fan-shaped mounds of a gopher.",
|
||||
"mosquito": "WA's most bothersome mosquitoes are <i>Culex</i> and <i>Aedes</i> species breeding in standing water — <i>Culex pipiens</i> can vector West Nile in Eastern WA.",
|
||||
"bedbug": "Bed bugs are a growing WA urban issue — confirm with the <strong>cast skins and fecal spots</strong> at mattress seams, not just bites.",
|
||||
}
|
||||
|
||||
INJECT = r'''
|
||||
<script>
|
||||
/* ==== REAL PHOTOS + WA FIELD NOTES (injected) ==== */
|
||||
const PHOTOS={''' + ",".join(f'"{k}":"{v}"' for k,v in PHOTOS.items()) + r'''};
|
||||
const WANOTES={''' + ",".join(f'"{k}":`{v}`' for k,v in WANOTES.items()) + r'''};
|
||||
function plateImg(art){
|
||||
if(PHOTOS[art]){
|
||||
var img='<img class="pp" src="/images/'+PHOTOS[art]+'" alt="'+art+'" loading="lazy">';
|
||||
var wa=WANOTES[art]?'<aside class="wa-note"><strong>WA field note</strong> '+WANOTES[art]+'</aside>':'';
|
||||
return img+wa;
|
||||
}
|
||||
return ART[art]||'';
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
|
||||
CSS = r'''
|
||||
<style>
|
||||
.plate-art img.pp{width:100%;height:auto;border-radius:12px;border:1px solid var(--line,#2C3455);background:var(--surface-2,#1B2239);object-fit:contain}
|
||||
.plate-art .wa-note,.key .result .wa-note{margin:12px 0 0;padding:10px 14px;border-left:3px solid var(--verdigris,#54BFA9);background:color-mix(in srgb,var(--verdigris,#54BFA9) 9%,transparent);border-radius:8px;font-size:.85rem;line-height:1.5;color:var(--text,#EDE7D6)}
|
||||
.plate-art .wa-note strong,.key .result .wa-note strong{color:var(--verdigris,#54BFA9);display:block;margin-bottom:3px;font-family:"JetBrains Mono",monospace;font-size:.7rem;letter-spacing:.06em;text-transform:uppercase}
|
||||
</style>
|
||||
'''
|
||||
|
||||
# 1. swap the two render sites
|
||||
old1 = '<figure class="plate-art">${ART[p.art]||\'\'}<figcaption>${p.cap||\'\'}</figcaption></figure>'
|
||||
new1 = '<figure class="plate-art">${plateImg(p.art)}<figcaption>${p.cap||\'\'}</figcaption></figure>'
|
||||
assert old1 in s, "render site 1 not found"
|
||||
s = s.replace(old1, new1)
|
||||
|
||||
old2 = '<div class="result">${ART[r.art]||\'\'}<h3>${r.n}</h3></div>'
|
||||
new2 = '<div class="result">${plateImg(r.art)}<h3>${r.n}</h3></div>'
|
||||
assert old2 in s, "render site 2 not found"
|
||||
s = s.replace(old2, new2)
|
||||
|
||||
# 2. inject the script (PHOTOS/WANOTES/plateImg) + CSS before the first </script> that has the render, or just append before a known anchor
|
||||
# Inject right after the ART definition script block ends. Find a safe anchor: the first '</script>' after 'const ART={}'
|
||||
# Simpler: append to EOF (global scope scripts execute in order; render happens on demand later)
|
||||
s = s.rstrip() + '\n' + INJECT + '\n' + CSS + '\n'
|
||||
|
||||
open(p, 'w').write(s)
|
||||
print(f"integrated {len(PHOTOS)} photos + {len(WANOTES)} WA notes; size {len(s)}")
|
||||