$ details = {
$ "Family": [""],
$ "Dormancy": ["Deciduous", "Semi-deciduous", "Evergreen"],
$ "Leaf Type": ["Broadleaf", "Conifer"],
$ "Ecology": ["Water Needs (PF)", "CA Native"],
$ "Height (ft)": ["Short, < 20", "Medium, 20-30", "Tall, 30-40", "Extra Tall, > 40"],
$ "Spread (ft)": ["Narrow, < 20", "Average, 20-40", "Broad, > 40"],
$ "Growth Rate (in‍/‍yr)": ["Slow, < 12", "Moderate, 12-24", "Fast, > 24"],
$ "Lifespan (yr)": ["Short, < 50", "Average, 50-100", "Long, 100-150", "Very Long, > 150"],
$ "Special Use": ["Good Under Utilities", "Bank Stabilization", "Fire Resistant", "Stormwater BMP"],
$ "Street Tree": ["Good", "Bad"],
$ "Tolerances": ["Drought", "Heat", "Wind", "Poor Drainage", "Frost", "Alkalinity", "Pests/Diseases"],
$ "Form": ["Rounded", "Oval", "Columnar", "Pyramidal", "Vase"],
$ "Bloom": ["Conspicuous Flowers", "Color", "Spring", "Summer", "Fall", "Winter"],
$ }
$# removed:
$# "Habitat Value" from "Ecology"
$# "Planting Area (ft)": ["< 4", "4-6", "6-10", "> 10"],
$# "Cautions": ["DO NOT USE in parkway strips or tree wells", "Pests/Diseases", "Produces Debris"],
$# "Notes": [""]
<style>
tr.subdetails th {
text-orientation: mixed;
transform: rotate(180deg);
writing-mode: vertical-rl;
}
th, td {
padding: 0 .25em;
width: 1em;
}
</style>
<table style=font-size:.75em>
<thead>
<tr class=details>
<th style=width:13em>Species</th>
$for detail, subdetails in details.items():
<th colspan=$len(subdetails)>$detail</th>
</tr>
<tr class=subdetails>
<th></th>
$for subdetails in details.values():
$for subdetail in subdetails:
<th>$subdetail</th>
</tr>
</thead>
<tr>
$for resource in web.tx.db.select("resources", where="json_extract(resource, '$.type[0]') = 'species'"):
$ tree = resource["resource"]
<th>$tree["latin-name"][0]<br><small style=font-weight:normal>$", ".join(tree["common-name"])</small></th>
<td>$tree["family"][0]</td>
$for dormancy in details["Dormancy"]:
<td>\
$if dormancy.lower() in tree["dormancy"]:
X
</td>
$for leaf_type in details["Leaf Type"]:
<td>\
$if leaf_type.lower() == tree["leaf"][0]:
X
</td>
<td>$tree["water"][0].upper()</td>
<td>\
$if tree["native"][0].upper():
X\
</td>
$ min_height, max_height = tree["height"][0].split("-")
$ avg_height = ((int(max_height) + int(min_height)) / 2) + int(min_height)
<td>
$if avg_height < 20: X
</td><td>
$if 20 < avg_height < 30: X
</td><td>
$if 30 < avg_height < 40: X
</td><td>
$if 40 < avg_height: X
</td>
$ min_spread, max_spread = tree["spread"][0].split("-")
$ avg_spread = ((int(max_spread) - int(min_spread)) / 2) + int(min_spread)
<td>
$if avg_spread < 20: X
</td><td>
$if 20 < avg_spread < 40: X
</td><td>
$if 40 < avg_spread: X
</td>
$ min_growth, max_growth = tree["growth-rate"][0].split("-")
$ avg_growth = ((int(max_growth) - int(min_growth)) / 2) + int(min_growth)
<td>
$if avg_growth < 12: X
</td><td>
$if 12 < avg_growth < 24: X
</td><td>
$if 24 < avg_growth: X
</td>
$ min_lifespan, max_lifespan = tree["lifespan"][0].split("-")
$ avg_lifespan = ((int(max_lifespan) - int(min_lifespan)) / 2) + int(min_lifespan)
<td>
$if avg_lifespan < 50: X
</td><td>
$if 50 < avg_lifespan < 100: X
</td><td>
$if 100 < avg_lifespan < 150: X
</td><td>
$if 150 < avg_lifespan: X
</td>
$for special_use in details["Special Use"]:
<td>\
$if special_use.lower() in tree["special-use"]:
X
</td>
$for street_tree in details["Street Tree"]:
<td>\
$if street_tree.lower() in tree["street-tree"]:
X
</td>
$for tolerance in details["Tolerances"]:
<td>\
$if tolerance.lower() in tree["tolerances"]:
X
</td>
$for form in details["Form"]:
<td>\
$if form.lower() in tree["form"]:
X
</td>
$if flower_color := details.get("flower-color"):
<td>X</td>
<td>$flower_color</td>
$else:
<td></td>
<td></td>
$for flower_season in details["Bloom"][2:]:
<td>\
$if flower_season.lower() in tree["flower-season"]:
X
</td>
</tr>
</table>
#### Sources
* https://www.southpasadenaca.gov/files/assets/public/v/1/public-works/documents/south-pasadena-tree-guide.pdf