"""
nearby.py - the courtesy directory behind /find-a-unit.
Other Scouting units in the Continental District. Rows live in `nearby_units`
in scout73.db (schema in store.py); this module only reads and renders them.
Two things shape everything here.
It is somebody else's data. It comes from a hand-typed district document that
goes stale without telling us, so `verified_at` is shown to the reader instead
of kept as bookkeeping, and BeAScout is offered as the authoritative lookup
rather than buried. A family driving to the wrong address on a Tuesday night is
the failure this page exists to avoid.
It is not about us. Pack 73 and Troop 73 are deliberately absent from the table
and from this page. The rest of the site is our pitch; this one is a door out
for a family we are not the right fit for.
"""
import store
# unit_type -> the badge a parent actually recognises.
TYPE_LABEL = {
"pack": "Cub Scouts",
"troop": "Scouts BSA",
"ship": "Sea Scouts",
"club": "Exploring",
}
# The unit's own words for who it serves. "family" is the common case and gets
# the warmer chip; a single-gender unit is stated plainly rather than softened,
# because that is the thing a parent is scanning for.
SERVES_LABEL = {
"family": "Boys & girls",
"boys": "Boys",
"girls": "Girls",
"coed": "Co-ed",
}
BEASCOUT = "https://beascout.scouting.org"
# The district's own short link to its unit list. This is the page the district
# hands out, so it is the right thing to credit and the right thing to link.
# One URL for the whole page rather than a per-row column: when the admin panel
# exists this belongs in a settings row, not in seventeen copies.
SOURCE_NAME = "Continental District unit list"
SOURCE_URL = "https://tinyurl.com/ContinentalScouts"
MONTHS = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"]
def pretty_date(iso):
"""2026-09-04 -> September 4, 2026. A log line is not a sentence."""
try:
y, m, d = (int(x) for x in str(iso).split("-"))
return f"{MONTHS[m - 1]} {d}, {y}"
except Exception:
return str(iso)
def listing():
"""Rows grouped by area, in sort_order. Empty list if the table is unusable."""
try:
con = store.connect()
except Exception:
return []
try:
rows = con.execute(
"SELECT * FROM nearby_units WHERE active = 1 "
"ORDER BY sort_order, unit_number"
).fetchall()
except Exception:
return []
finally:
con.close()
groups, order = {}, []
for r in rows:
area = r["area"] or "Elsewhere in the district"
if area not in groups:
groups[area] = []
order.append(area)
groups[area].append(dict(r))
return [(a, groups[a]) for a in order]
def checked_on():
"""The most recent verified_at across live rows, for the honesty line."""
try:
con = store.connect()
except Exception:
return None
try:
row = con.execute(
"SELECT MAX(verified_at) AS v FROM nearby_units WHERE active = 1"
).fetchone()
return row["v"] if row else None
except Exception:
return None
finally:
con.close()
def _esc(s):
return (str(s).replace("&", "&").replace("<", "<").replace(">", ">")
if s else "")
def unit_name(u):
n = _esc(u["unit_number"])
kind = {"pack": "Pack", "troop": "Troop", "ship": "Ship", "club": "Club"}.get(
u["unit_type"], "Unit")
return f"{kind} {n}"
def card(u):
"""One unit.
The link sits on the unit's name, never printed raw. Most of these are
tinyurls and a wall of them reads like a spam folder.
"""
name = unit_name(u)
if u["link_url"]:
title = (f'{name}')
else:
title = f'{name}'
chips = [f'{TYPE_LABEL.get(u["unit_type"], "Scouting")}']
if u["serves"] and u["serves"] in SERVES_LABEL:
# Tone tracks who the unit is OPEN to, not the word it uses. Exploring
# posts and clubs are co-ed by design and have no "family" variant to
# be compared against, so colouring "coed" like a single-gender unit
# would make Club 254 look narrower than Pack 244 when it is not.
tone = "gold" if u["serves"] in ("family", "coed") else "blue"
chips.append(f'{SERVES_LABEL[u["serves"]]}')
lines = []
if u["chartered_org"]:
lines.append(_esc(u["chartered_org"]))
where = ", ".join(x for x in [_esc(u["street"]), _esc(u["town"])] if x)
if where:
lines.append(where)
body = "
".join(lines)
meets = (f'
Scouting works best close to home. If we are the wrong side of the county, or Tuesday nights are already spoken for, one of these units may fit your family better. Every one of them welcomes visitors, and none of them mind being asked.
BeAScout is Scouting America's official lookup. Put in your zip code and it will show you every pack, troop, ship and post near you, with current contact details for each one.
Search BeAScout.org