Add real pest photos (25 Wikimedia) + WA State field notes

This commit is contained in:
drjones
2026-09-01 08:20:17 -07:00
parent 2e9f78e3a9
commit 23a6023114
28 changed files with 101 additions and 2 deletions

4
app.py
View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

BIN
images/bat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
images/bedbug.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
images/blackWidow.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
images/brownband.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
images/buprestid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

BIN
images/carpAnt.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

BIN
images/carpetLarva.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
images/clothesMoth.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
images/clusterFly.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
images/dampwood.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
images/drugstore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 KiB

BIN
images/flea.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
images/german.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
images/hobo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
images/indianmeal.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/moleGopher.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

BIN
images/mosquito.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
images/mothFly.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
images/mound.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
images/notchAnt.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
images/oriental.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
images/sawtooth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

BIN
images/subter.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
images/tick.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -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 &mdash; a study suite for WSU EM019 &middot; <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
View 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)}")