@ca-plant-list/ca-plant-list 0.2.15 → 0.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/glossary/peduncle.md +3 -1
- package/data/illustrations/inkscape/peduncle.svg +170 -0
- package/data/illustrations/optimized/peduncle.svg +21 -0
- package/data/synonyms.csv +2 -2
- package/data/taxa.csv +7 -7
- package/jekyll/name_search.html +4 -4
- package/lib/basepagerenderer.js +1 -1
- package/lib/commandandtaxaprocessor.js +37 -0
- package/lib/commandprocessor.js +108 -0
- package/lib/dataloader.js +15 -2
- package/lib/ebook/pages/page_list_flowers.js +1 -1
- package/lib/ebook/pages/taxonpage.js +4 -2
- package/lib/ebook/plantbook.js +2 -2
- package/lib/errorlog.js +14 -4
- package/lib/generictaxaloader.js +40 -0
- package/lib/index.d.ts +54 -21
- package/lib/index.js +3 -3
- package/lib/pagerenderer.js +1 -1
- package/lib/taxa.js +22 -15
- package/lib/taxaloader.js +35 -0
- package/lib/taxaprocessor.js +40 -0
- package/lib/taxon.js +17 -0
- package/package.json +1 -1
- package/scripts/build-ebook.js +42 -43
- package/scripts/build-site.js +10 -33
- package/lib/commandrunner.js +0 -63
@@ -0,0 +1,170 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
3
|
+
|
4
|
+
<svg
|
5
|
+
width="102.55142"
|
6
|
+
height="60"
|
7
|
+
viewBox="0 0 102.55142 60"
|
8
|
+
version="1.1"
|
9
|
+
id="svg1"
|
10
|
+
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
11
|
+
sodipodi:docname="peduncle.svg"
|
12
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
13
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
14
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
15
|
+
xmlns="http://www.w3.org/2000/svg"
|
16
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
17
|
+
<sodipodi:namedview
|
18
|
+
id="namedview1"
|
19
|
+
pagecolor="#ffffff"
|
20
|
+
bordercolor="#000000"
|
21
|
+
borderopacity="0.25"
|
22
|
+
inkscape:showpageshadow="2"
|
23
|
+
inkscape:pageopacity="0.0"
|
24
|
+
inkscape:pagecheckerboard="0"
|
25
|
+
inkscape:deskcolor="#d1d1d1"
|
26
|
+
inkscape:document-units="px"
|
27
|
+
showgrid="true"
|
28
|
+
inkscape:zoom="4"
|
29
|
+
inkscape:cx="50.625"
|
30
|
+
inkscape:cy="59.875"
|
31
|
+
inkscape:window-width="1920"
|
32
|
+
inkscape:window-height="991"
|
33
|
+
inkscape:window-x="-9"
|
34
|
+
inkscape:window-y="-9"
|
35
|
+
inkscape:window-maximized="1"
|
36
|
+
inkscape:current-layer="layer1">
|
37
|
+
<inkscape:grid
|
38
|
+
id="grid1"
|
39
|
+
units="px"
|
40
|
+
originx="0"
|
41
|
+
originy="0"
|
42
|
+
spacingx="1"
|
43
|
+
spacingy="1"
|
44
|
+
empcolor="#0099e5"
|
45
|
+
empopacity="0.30196078"
|
46
|
+
color="#0099e5"
|
47
|
+
opacity="0.14901961"
|
48
|
+
empspacing="5"
|
49
|
+
dotted="false"
|
50
|
+
gridanglex="30"
|
51
|
+
gridanglez="30"
|
52
|
+
visible="true" />
|
53
|
+
</sodipodi:namedview>
|
54
|
+
<defs
|
55
|
+
id="defs1">
|
56
|
+
<marker
|
57
|
+
style="overflow:visible"
|
58
|
+
id="Triangle"
|
59
|
+
refX="0"
|
60
|
+
refY="0"
|
61
|
+
orient="auto-start-reverse"
|
62
|
+
inkscape:stockid="Triangle arrow"
|
63
|
+
markerWidth="1"
|
64
|
+
markerHeight="1"
|
65
|
+
viewBox="0 0 1 1"
|
66
|
+
inkscape:isstock="true"
|
67
|
+
inkscape:collect="always"
|
68
|
+
preserveAspectRatio="xMidYMid">
|
69
|
+
<path
|
70
|
+
transform="scale(0.5)"
|
71
|
+
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
|
72
|
+
d="M 5.77,0 -2.88,5 V -5 Z"
|
73
|
+
id="path135" />
|
74
|
+
</marker>
|
75
|
+
</defs>
|
76
|
+
<g
|
77
|
+
inkscape:label="Layer 1"
|
78
|
+
inkscape:groupmode="layer"
|
79
|
+
id="layer1">
|
80
|
+
<path
|
81
|
+
style="fill:#14140c;fill-opacity:1;stroke:#160707;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
|
82
|
+
d="M 15,60 V 15"
|
83
|
+
id="path2" />
|
84
|
+
<use
|
85
|
+
x="0"
|
86
|
+
y="0"
|
87
|
+
inkscape:tiled-clone-of="#path1"
|
88
|
+
xlink:href="#path1"
|
89
|
+
id="use1" />
|
90
|
+
<use
|
91
|
+
x="0"
|
92
|
+
y="0"
|
93
|
+
inkscape:tiled-clone-of="#path1"
|
94
|
+
xlink:href="#path1"
|
95
|
+
transform="rotate(30,15,15)"
|
96
|
+
id="use2" />
|
97
|
+
<use
|
98
|
+
x="0"
|
99
|
+
y="0"
|
100
|
+
inkscape:tiled-clone-of="#path1"
|
101
|
+
xlink:href="#path1"
|
102
|
+
transform="rotate(60,15,15)"
|
103
|
+
id="use3" />
|
104
|
+
<use
|
105
|
+
x="0"
|
106
|
+
y="0"
|
107
|
+
inkscape:tiled-clone-of="#path1"
|
108
|
+
xlink:href="#path1"
|
109
|
+
transform="rotate(90,15,15)"
|
110
|
+
id="use4" />
|
111
|
+
<use
|
112
|
+
x="0"
|
113
|
+
y="0"
|
114
|
+
inkscape:tiled-clone-of="#path1"
|
115
|
+
xlink:href="#path1"
|
116
|
+
transform="rotate(120,15,15)"
|
117
|
+
id="use5" />
|
118
|
+
<use
|
119
|
+
x="0"
|
120
|
+
y="0"
|
121
|
+
inkscape:tiled-clone-of="#path1"
|
122
|
+
xlink:href="#path1"
|
123
|
+
transform="rotate(150,15,15)"
|
124
|
+
id="use6" />
|
125
|
+
<ellipse
|
126
|
+
style="fill:#ffff00;stroke-width:0"
|
127
|
+
id="path1"
|
128
|
+
cx="15"
|
129
|
+
cy="15"
|
130
|
+
rx="2.5"
|
131
|
+
ry="15"
|
132
|
+
inkscape:tile-cx="15"
|
133
|
+
inkscape:tile-cy="15"
|
134
|
+
inkscape:tile-w="5"
|
135
|
+
inkscape:tile-h="30"
|
136
|
+
inkscape:tile-x0="12.5"
|
137
|
+
inkscape:tile-y0="0" />
|
138
|
+
<path
|
139
|
+
style="fill:#14140c;fill-opacity:1;stroke:#160707;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Triangle)"
|
140
|
+
d="M 20,45 H 45"
|
141
|
+
id="path3" />
|
142
|
+
<text
|
143
|
+
xml:space="preserve"
|
144
|
+
style="font-size:8px;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0.5px;opacity:0.558428;fill:#14140c;fill-opacity:1;stroke:#160707;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
|
145
|
+
x="50"
|
146
|
+
y="47.068359"
|
147
|
+
id="text3"><tspan
|
148
|
+
sodipodi:role="line"
|
149
|
+
id="tspan3"
|
150
|
+
x="50"
|
151
|
+
y="47.068359"
|
152
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0.5px;stroke-width:0.1;stroke-dasharray:none">peduncle</tspan></text>
|
153
|
+
<path
|
154
|
+
style="fill:#14140c;fill-opacity:1;stroke:#160707;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Triangle)"
|
155
|
+
d="M 35,15 H 45"
|
156
|
+
id="path4"
|
157
|
+
sodipodi:nodetypes="cc" />
|
158
|
+
<text
|
159
|
+
xml:space="preserve"
|
160
|
+
style="font-size:8px;font-family:Arial;-inkscape-font-specification:Arial;letter-spacing:0.5px;opacity:0.558428;fill:#14140c;fill-opacity:1;stroke:#160707;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
|
161
|
+
x="49.696735"
|
162
|
+
y="17.068359"
|
163
|
+
id="text4"><tspan
|
164
|
+
sodipodi:role="line"
|
165
|
+
id="tspan4"
|
166
|
+
x="49.696735"
|
167
|
+
y="17.068359"
|
168
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0.5px;stroke-width:0.1;stroke-dasharray:none">inflorescence</tspan></text>
|
169
|
+
</g>
|
170
|
+
</svg>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<svg version="1.1" viewBox="0 0 102.55 60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
2
|
+
<defs>
|
3
|
+
<marker id="b" overflow="visible" markerHeight="1" markerWidth="1" orient="auto-start-reverse" preserveAspectRatio="xMidYMid" viewBox="0 0 1 1">
|
4
|
+
<path transform="scale(.5)" d="m5.77 0-8.65 5v-10z" fill="context-stroke" fill-rule="evenodd" stroke="context-stroke" stroke-width="1pt"/>
|
5
|
+
</marker>
|
6
|
+
</defs>
|
7
|
+
<path d="m15 60v-45" fill="#14140c" stroke="#160707"/>
|
8
|
+
<use xlink:href="#a"/>
|
9
|
+
<use transform="rotate(30,15,15)" xlink:href="#a"/>
|
10
|
+
<use transform="rotate(60,15,15)" xlink:href="#a"/>
|
11
|
+
<use transform="rotate(90,15,15)" xlink:href="#a"/>
|
12
|
+
<use transform="rotate(120,15,15)" xlink:href="#a"/>
|
13
|
+
<use transform="rotate(150,15,15)" xlink:href="#a"/>
|
14
|
+
<ellipse id="a" cx="15" cy="15" rx="2.5" ry="15" fill="#ff0" stroke-width="0"/>
|
15
|
+
<g fill="#14140c" stroke="#160707">
|
16
|
+
<path d="m20 45h25" marker-start="url(#b)" stroke-width=".5"/>
|
17
|
+
<text x="50" y="47.068359" font-family="Arial" font-size="8px" letter-spacing=".5px" opacity=".55843" stroke-width=".1" xml:space="preserve"><tspan x="50" y="47.068359" font-family="Arial" font-size="8px" letter-spacing=".5px" stroke-width=".1" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">peduncle</tspan></text>
|
18
|
+
<path d="m35 15h10" marker-start="url(#b)" stroke-width=".5"/>
|
19
|
+
<text x="49.696735" y="17.068359" font-family="Arial" font-size="8px" letter-spacing=".5px" opacity=".55843" stroke-width=".1" xml:space="preserve"><tspan x="49.696735" y="17.068359" font-family="Arial" font-size="8px" letter-spacing=".5px" stroke-width=".1" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">inflorescence</tspan></text>
|
20
|
+
</g>
|
21
|
+
</svg>
|
package/data/synonyms.csv
CHANGED
@@ -811,7 +811,6 @@ Helianthemum scoparium var. vulgare,Crocanthemum scoparium var. vulgare
|
|
811
811
|
Helianthus annuus subsp. jaegeri,Helianthus annuus
|
812
812
|
Helianthus annuus subsp. lenticularis,Helianthus annuus
|
813
813
|
Helianthus annuus var. macrocarpus,Helianthus annuus
|
814
|
-
Helichrysum luteoalbum,Pseudognaphalium luteoalbum,INAT
|
815
814
|
Heliotropium oculatum,Heliotropium curassavicum var. oculatum
|
816
815
|
Heliotropium spathulatum subsp. oculatum,Heliotropium curassavicum var. oculatum
|
817
816
|
Helxine soleirolii,Soleirolia soleirolii
|
@@ -979,7 +978,6 @@ Lessingia filaginifolia var. filaginifolia,Corethrogyne filaginifolia
|
|
979
978
|
Lessingia germanorum var. parvula,Lessingia tenuis
|
980
979
|
Lessingia germanorum var. tenuis,Lessingia tenuis
|
981
980
|
Lessingia nemaclada var. albiflora,Lessingia nemaclada
|
982
|
-
Leuzea repens,Rhaponticum repens,INAT
|
983
981
|
Lewisia alba,Lewisia rediviva var. rediviva
|
984
982
|
Leymus ×multiflorus,Elymus ×gouldii,INAT
|
985
983
|
Leymus mollis subsp. mollis,Elymus mollis subsp. mollis,INAT
|
@@ -1538,6 +1536,7 @@ Prunella vulgaris var. atropurpurea,Prunella vulgaris var. lanceolata
|
|
1538
1536
|
Prunella vulgaris var. parviflora,Prunella vulgaris var. vulgaris
|
1539
1537
|
Prunus amygdalus,Prunus dulcis,INAT
|
1540
1538
|
Pseudognaphalium canescens subsp. beneolens,Pseudognaphalium beneolens
|
1539
|
+
Pseudognaphalium luteoalbum,Pseudognaphalium luteoalbum,INAT
|
1541
1540
|
Pseudognaphalium microcephalum var. thermale,Pseudognaphalium thermale
|
1542
1541
|
Psilocarphus tenellus var. globiferus,Psilocarphus chilensis
|
1543
1542
|
Psilocarphus tenellus var. tenellus,Psilocarphus tenellus
|
@@ -1590,6 +1589,7 @@ Rhamnus tomentella subsp. tomentella,Frangula californica subsp. tomentella
|
|
1590
1589
|
Rhamnus tomentella,Frangula californica subsp. tomentella
|
1591
1590
|
Rhamphospermum arvense,Sinapis arvensis,INAT
|
1592
1591
|
Rhamphospermum nigrum,Brassica nigra,INAT
|
1592
|
+
Rhaponticum repens,Rhaponticum repens,INAT
|
1593
1593
|
Rhus aromatica subsp. trilobata,Rhus aromatica
|
1594
1594
|
Rhus aromatica var. simplicifolia,Rhus aromatica
|
1595
1595
|
Rhus aromatica var. trilobata,Rhus aromatica
|
package/data/taxa.csv
CHANGED
@@ -248,7 +248,7 @@ Bromus sitchensis var. maritimus,maritime brome,N,108591,13625,71142
|
|
248
248
|
Bromus sterilis,poverty brome,X,16288,1216,57163
|
249
249
|
Bromus tectorum,cheat grass,X,16290,1218,58369
|
250
250
|
Buddleja davidii,butterfly bush,X,16349,1222,75916
|
251
|
-
Cabomba caroliniana,fanwort,X,16450,8513,75921
|
251
|
+
Cabomba caroliniana,fanwort,X,16450,8513,75921,white,5,9
|
252
252
|
Cakile edentula,sea rocket,X,16523,1232,52162
|
253
253
|
Cakile maritima,sea rocket,X,16531,1233,60982
|
254
254
|
Calamagrostis koelerioides,,N,16557,1240,75927
|
@@ -267,7 +267,7 @@ Calochortus albus,white globe lily,N,16710,1264,51315,white
|
|
267
267
|
Calochortus argillosus,clay mariposa lily,N,76542,1268,64330,"white,pink"
|
268
268
|
Calochortus clavatus var. pallidus,,N,55455,1275,58360
|
269
269
|
Calochortus invenustus,,N,16733,1284,64432
|
270
|
-
Calochortus leichtlinii
|
270
|
+
Calochortus leichtlinii,,N,16735,1288,52542
|
271
271
|
Calochortus luteus,yellow mariposa lily,N,16737,1290,51077,yellow
|
272
272
|
Calochortus pulchellus,Mt. Diablo fairy lantern,N,16755,1303,47326,,,,50,1B.2,,,S2,G2
|
273
273
|
Calochortus splendens,,N,16759,1306,58361
|
@@ -362,7 +362,7 @@ Centromadia pungens subsp. pungens,common spikeweed,N,80173,9544,64196
|
|
362
362
|
Cephalanthus occidentalis,button bush,N,18612,9794,52763
|
363
363
|
Cerastium fontanum subsp. vulgare,,X,49818,1872,1005385
|
364
364
|
Cerastium glomeratum,mouse-ear chickweed,X,18675,1873,52973
|
365
|
-
Ceratophyllum demersum,hornwort,N,18711,1874,60997
|
365
|
+
Ceratophyllum demersum,hornwort,N,18711,1874,60997,,6,8
|
366
366
|
Cercis occidentalis,western redbud,N,18736,1877,59556
|
367
367
|
Cercocarpus betuloides var. betuloides,mountain-mahogany,N,71734,1879,59001
|
368
368
|
Cestrum parqui,Chilean jessamine,X,18790,8746,76226
|
@@ -907,7 +907,7 @@ Impatiens balfourii,touch-me-not,X,28954,4320,77490
|
|
907
907
|
Iris douglasiana,Douglas' iris,N,29188,4346,50854
|
908
908
|
Iris foetidissima,stinking iris,X,29193,8604,64331
|
909
909
|
Iris longipetala,central coast iris,N,29293,4354,67703,,,,3169,4.2,,,S3,G3
|
910
|
-
Iris macrosiphon,,N,29289
|
910
|
+
Iris macrosiphon,,N,29289,4355,54057
|
911
911
|
Iris pseudacorus,yellow flag,X,29301,4358,47779
|
912
912
|
Isocoma arguta,Carquinez goldenbush,N,3633,4369,77510,,,,1264,1B.1,,,S1,G1
|
913
913
|
Isoetes howellii,Howell's quillwort,N,29392,4379,63823
|
@@ -1216,7 +1216,7 @@ Navarretia squarrosa,skunkweed,N,34475,5807,53157
|
|
1216
1216
|
Navarretia tagetina,,N,34477,5809,60226
|
1217
1217
|
Navarretia viscidula,sticky navarretia,N,34478,5810,78195
|
1218
1218
|
Nemophila heterophylla,,N,34539,5834,53158
|
1219
|
-
Nemophila menziesii var. atomaria,,N,62279
|
1219
|
+
Nemophila menziesii var. atomaria,,N,62279,5837,50647,white,2,6
|
1220
1220
|
Nemophila menziesii var. menziesii,baby blue-eyes,N,62285,5839,50650
|
1221
1221
|
Nemophila parviflora var. parviflora,,N,62288,5842,58921
|
1222
1222
|
Nemophila pedunculata,,N,34544,5844,55461
|
@@ -1425,7 +1425,7 @@ Prunus virginiana var. demissa,western choke-cherry,N,64546,6898,63900
|
|
1425
1425
|
Pseudognaphalium beneolens,,N,81674,11047,64303
|
1426
1426
|
Pseudognaphalium biolettii,,N,81666,11955,58829
|
1427
1427
|
Pseudognaphalium californicum,,N,80569,11956,53077
|
1428
|
-
Pseudognaphalium luteoalbum,cudweed,X,101867,11957,
|
1428
|
+
Pseudognaphalium luteoalbum,cudweed,X,101867,11957,53083
|
1429
1429
|
Pseudognaphalium microcephalum,,N,81265,11048,78763
|
1430
1430
|
Pseudognaphalium ramosissimum,,N,80579,11959,53088
|
1431
1431
|
Pseudognaphalium stramineum,,N,40045,11960,53089
|
@@ -1480,7 +1480,7 @@ Raphanus sativus,wild radish,X,40992,7064,55410
|
|
1480
1480
|
Rhamnus alaternus,Italian buckthorn,X,81104,9447,82856
|
1481
1481
|
Rhamnus crocea,spiny redberry,N,41069,7075,57243
|
1482
1482
|
Rhamnus ilicifolia,holly-leaf redberry,N,41070,7076,58308
|
1483
|
-
Rhaponticum repens,Russian knapweed,X,103802,13621,
|
1483
|
+
Rhaponticum repens,Russian knapweed,X,103802,13621,339502
|
1484
1484
|
Rhinotropis californica,California milkwort,N,107535,14636,876453
|
1485
1485
|
Rhus aromatica,skunkbrush,N,41175,11056,58738
|
1486
1486
|
Ribes amarum,bitter gooseberry,N,41372,7099,53433
|
package/jekyll/name_search.html
CHANGED
@@ -4,14 +4,14 @@ js: name_search.js
|
|
4
4
|
---
|
5
5
|
|
6
6
|
<script>
|
7
|
-
const NAMES = {% include names.json%};
|
7
|
+
const NAMES = {% include names.json %};
|
8
8
|
</script>
|
9
9
|
|
10
10
|
<form id="search_form">
|
11
|
-
<input type="text" id="name"
|
11
|
+
<input type="text" id="name">
|
12
|
+
<label for="name">Search for scientific name, common name, or synonym.</label>
|
12
13
|
</form>
|
13
14
|
|
14
15
|
<div class="section" id="message"></div>
|
15
16
|
|
16
|
-
<table id="results">
|
17
|
-
</table>
|
17
|
+
<table id="results"></table>
|
package/lib/basepagerenderer.js
CHANGED
@@ -27,7 +27,7 @@ class BasePageRenderer {
|
|
27
27
|
static renderTools( outputDir, taxa ) {
|
28
28
|
|
29
29
|
const names = [];
|
30
|
-
for ( const taxon of taxa.
|
30
|
+
for ( const taxon of taxa.getTaxonList() ) {
|
31
31
|
const row = [];
|
32
32
|
row.push( taxon.getName() );
|
33
33
|
const cn = taxon.getCommonNames().join( ", " );
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import { CommandProcessor } from "./commandprocessor.js";
|
2
|
+
import { TaxaProcessor } from "./taxaprocessor.js";
|
3
|
+
|
4
|
+
class GenericTaxaProcessor extends TaxaProcessor {
|
5
|
+
|
6
|
+
#fnCustomProcess;
|
7
|
+
|
8
|
+
constructor( fnCustomProcess, options, taxaLoaderClass ) {
|
9
|
+
super( options, taxaLoaderClass );
|
10
|
+
this.#fnCustomProcess = fnCustomProcess;
|
11
|
+
}
|
12
|
+
|
13
|
+
async customProcess() {
|
14
|
+
await this.#fnCustomProcess( this );
|
15
|
+
}
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
class CommandAndTaxaProcessor extends CommandProcessor {
|
20
|
+
|
21
|
+
#taxaProcessor;
|
22
|
+
|
23
|
+
constructor( commandName, commandDesc, fnCustomProcess, optionDefs = [], optionHelp = [], commandHelp = [], taxaLoaderClass ) {
|
24
|
+
super( commandName, commandDesc, optionDefs, optionHelp, commandHelp );
|
25
|
+
this.#taxaProcessor = new GenericTaxaProcessor( fnCustomProcess, this.getOptions(), taxaLoaderClass );
|
26
|
+
}
|
27
|
+
|
28
|
+
async process() {
|
29
|
+
if ( this.helpShown() ) {
|
30
|
+
return;
|
31
|
+
}
|
32
|
+
await this.#taxaProcessor.process();
|
33
|
+
}
|
34
|
+
|
35
|
+
}
|
36
|
+
|
37
|
+
export { CommandAndTaxaProcessor };
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import commandLineArgs from "command-line-args";
|
2
|
+
import commandLineUsage from "command-line-usage";
|
3
|
+
|
4
|
+
const OPTION_DEFS = [
|
5
|
+
{ name: "datadir", type: String, defaultValue: "./data" },
|
6
|
+
{ name: "help", type: Boolean },
|
7
|
+
{ name: "outputdir", type: String, defaultValue: "./output" },
|
8
|
+
{ name: "show-flower-errors", type: Boolean },
|
9
|
+
];
|
10
|
+
|
11
|
+
const OPTION_HELP = [
|
12
|
+
{
|
13
|
+
name: "datadir",
|
14
|
+
type: String,
|
15
|
+
typeLabel: "{underline path}",
|
16
|
+
description: "The directory in which the data files for the local plant list are located. Defaults to {bold ./data}."
|
17
|
+
|
18
|
+
},
|
19
|
+
{
|
20
|
+
name: "outputdir",
|
21
|
+
type: String,
|
22
|
+
typeLabel: "{underline path}",
|
23
|
+
description: "The directory in which the output files should be written. Any files or subdirectories originally" +
|
24
|
+
" in this directory will be deleted. Defaults to {bold ./output}."
|
25
|
+
|
26
|
+
},
|
27
|
+
{
|
28
|
+
name: "show-flower-errors",
|
29
|
+
type: Boolean,
|
30
|
+
description: "Include flower color and flowering time errors in errors.tsv."
|
31
|
+
},
|
32
|
+
{
|
33
|
+
name: "help",
|
34
|
+
type: Boolean,
|
35
|
+
description: "Print this usage guide."
|
36
|
+
},
|
37
|
+
];
|
38
|
+
|
39
|
+
class CommandProcessor {
|
40
|
+
|
41
|
+
#commandName;
|
42
|
+
#commandDesc;
|
43
|
+
|
44
|
+
#optionHelp;
|
45
|
+
#commandHelp;
|
46
|
+
|
47
|
+
#options;
|
48
|
+
|
49
|
+
#helpShown = false;
|
50
|
+
|
51
|
+
/**
|
52
|
+
* @param {String} commandName
|
53
|
+
* @param {String} commandDesc
|
54
|
+
* @param {*} optionDefs An array of command line options to be added to the standard options.
|
55
|
+
* @param {*} optionHelp An array of help descriptions to be added to the standard options in the first section.
|
56
|
+
* @param {*} commandHelp An array of help sections to be appended to the first option section in the help display.
|
57
|
+
*/
|
58
|
+
constructor( commandName, commandDesc, optionDefs = [], optionHelp = [], commandHelp = [] ) {
|
59
|
+
|
60
|
+
this.#commandName = commandName;
|
61
|
+
this.#commandDesc = commandDesc;
|
62
|
+
|
63
|
+
this.#optionHelp = optionHelp;
|
64
|
+
this.#commandHelp = commandHelp;
|
65
|
+
|
66
|
+
this.#options = commandLineArgs( OPTION_DEFS.concat( optionDefs ) );
|
67
|
+
|
68
|
+
if ( this.#options.help ) {
|
69
|
+
this.showHelp();
|
70
|
+
}
|
71
|
+
|
72
|
+
}
|
73
|
+
|
74
|
+
getOptions() {
|
75
|
+
return this.#options;
|
76
|
+
}
|
77
|
+
|
78
|
+
helpShown() {
|
79
|
+
return this.#helpShown;
|
80
|
+
}
|
81
|
+
|
82
|
+
showHelp() {
|
83
|
+
|
84
|
+
if ( this.#helpShown ) {
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
this.#helpShown = true;
|
89
|
+
|
90
|
+
const mainOptions = OPTION_HELP.concat( this.#optionHelp );
|
91
|
+
mainOptions.sort( ( a, b ) => a.name.localeCompare( b.name ) );
|
92
|
+
|
93
|
+
const help = [
|
94
|
+
{
|
95
|
+
header: this.#commandName,
|
96
|
+
content: this.#commandDesc
|
97
|
+
},
|
98
|
+
{
|
99
|
+
header: "Options",
|
100
|
+
optionList: mainOptions
|
101
|
+
}
|
102
|
+
];
|
103
|
+
console.log( commandLineUsage( help.concat( this.#commandHelp ) ) );
|
104
|
+
}
|
105
|
+
|
106
|
+
}
|
107
|
+
|
108
|
+
export { CommandProcessor };
|
package/lib/dataloader.js
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
import { CSV } from "./csv.js";
|
2
2
|
import { Taxa } from "./taxa.js";
|
3
3
|
import { Files } from "./files.js";
|
4
|
+
import { ErrorLog } from "./errorlog.js";
|
4
5
|
|
5
6
|
class DataLoader {
|
6
7
|
|
7
|
-
static
|
8
|
+
static #errorLog = new ErrorLog();
|
9
|
+
|
10
|
+
static getErrorLog() {
|
11
|
+
return DataLoader.#errorLog;
|
12
|
+
}
|
13
|
+
|
14
|
+
static load( options ) {
|
8
15
|
|
9
16
|
function getIncludeList( dataDir ) {
|
10
17
|
// Read inclusion list.
|
@@ -22,10 +29,16 @@ class DataLoader {
|
|
22
29
|
return include;
|
23
30
|
}
|
24
31
|
|
32
|
+
const showFlowerErrors = options[ "show-flower-errors" ];
|
33
|
+
|
25
34
|
console.log( "loading data" );
|
26
35
|
|
27
|
-
return new Taxa( getIncludeList(
|
36
|
+
return new Taxa( getIncludeList( options.datadir ), this.getErrorLog(), showFlowerErrors );
|
37
|
+
|
38
|
+
}
|
28
39
|
|
40
|
+
static writeErrorLog() {
|
41
|
+
DataLoader.#errorLog.write();
|
29
42
|
}
|
30
43
|
|
31
44
|
}
|
@@ -93,7 +93,7 @@ class PageListFlowerTime extends EBookPage {
|
|
93
93
|
|
94
94
|
const range = [ this.#m1, this.#m2 ];
|
95
95
|
const links = [];
|
96
|
-
for ( const taxon of this.#taxa.
|
96
|
+
for ( const taxon of this.#taxa.getTaxonList() ) {
|
97
97
|
const m1 = taxon.getBloomStart();
|
98
98
|
const m2 = taxon.getBloomEnd();
|
99
99
|
if ( m1 && DateUtils.monthRangesOverlap( range, [ m1, m2 ] ) ) {
|
@@ -28,8 +28,10 @@ class TaxonPage extends EBookPage {
|
|
28
28
|
return "";
|
29
29
|
}
|
30
30
|
let html = "";
|
31
|
-
|
32
|
-
|
31
|
+
if ( colors ) {
|
32
|
+
for ( const color of colors ) {
|
33
|
+
html += XHTML.textElement( "img", "", { src: "./i/f-" + color + ".svg", class: "flr" } );
|
34
|
+
}
|
33
35
|
}
|
34
36
|
if ( monthStart ) {
|
35
37
|
html += XHTML.textElement( "div", DateUtils.getMonthName( monthStart ) + "-" + DateUtils.getMonthName( monthEnd ) );
|
package/lib/ebook/plantbook.js
CHANGED
@@ -36,7 +36,7 @@ class PlantBook extends EBook {
|
|
36
36
|
const contentDir = this.getContentDir();
|
37
37
|
|
38
38
|
console.log( "creating taxon pages" );
|
39
|
-
const taxonList = this.#taxa.
|
39
|
+
const taxonList = this.#taxa.getTaxonList();
|
40
40
|
for ( const taxon of taxonList ) {
|
41
41
|
const name = taxon.getName();
|
42
42
|
new TaxonPage( contentDir, taxon, this.#images[ name ] ).create();
|
@@ -124,7 +124,7 @@ class PlantBook extends EBook {
|
|
124
124
|
}
|
125
125
|
|
126
126
|
// Add taxa.
|
127
|
-
for ( let index = 0; index < this.#taxa.
|
127
|
+
for ( let index = 0; index < this.#taxa.getTaxonList().length; index++ ) {
|
128
128
|
xml += "<itemref idref=\"t" + index + "\"/>";
|
129
129
|
}
|
130
130
|
|
package/lib/errorlog.js
CHANGED
@@ -2,14 +2,24 @@ import * as fs from "node:fs";
|
|
2
2
|
|
3
3
|
class ErrorLog {
|
4
4
|
|
5
|
-
|
5
|
+
#fileName;
|
6
|
+
#echo;
|
7
|
+
#errors = [];
|
6
8
|
|
7
|
-
|
9
|
+
constructor( fileName, echo = false ) {
|
10
|
+
this.#fileName = fileName;
|
11
|
+
this.#echo = echo;
|
12
|
+
}
|
13
|
+
|
14
|
+
log( ...args ) {
|
15
|
+
if ( this.#echo ) {
|
16
|
+
console.log( args.join() );
|
17
|
+
}
|
8
18
|
this.#errors.push( args.join( "\t" ) );
|
9
19
|
}
|
10
20
|
|
11
|
-
|
12
|
-
fs.writeFileSync( fileName, this.#errors.join( "\n" ) );
|
21
|
+
write() {
|
22
|
+
fs.writeFileSync( this.#fileName, this.#errors.join( "\n" ) );
|
13
23
|
}
|
14
24
|
|
15
25
|
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { ErrorLog } from "./errorlog.js";
|
2
|
+
|
3
|
+
class GenericTaxaLoader {
|
4
|
+
|
5
|
+
#options;
|
6
|
+
#errorLog;
|
7
|
+
#taxa;
|
8
|
+
|
9
|
+
constructor( options ) {
|
10
|
+
this.#options = options;
|
11
|
+
this.#errorLog = new ErrorLog( options.outputdir + "/errors.tsv" );
|
12
|
+
}
|
13
|
+
|
14
|
+
getErrorLog() {
|
15
|
+
return this.#errorLog;
|
16
|
+
}
|
17
|
+
|
18
|
+
getOptions() {
|
19
|
+
return this.#options;
|
20
|
+
}
|
21
|
+
|
22
|
+
getTaxa() {
|
23
|
+
return this.#taxa;
|
24
|
+
}
|
25
|
+
|
26
|
+
async load() {
|
27
|
+
this.#taxa = await this.loadTaxa();
|
28
|
+
}
|
29
|
+
|
30
|
+
async loadTaxa() {
|
31
|
+
throw new Error( "must be implemented by subclass" );
|
32
|
+
}
|
33
|
+
|
34
|
+
writeErrorLog() {
|
35
|
+
this.#errorLog.write();
|
36
|
+
}
|
37
|
+
|
38
|
+
}
|
39
|
+
|
40
|
+
export { GenericTaxaLoader };
|
package/lib/index.d.ts
CHANGED
@@ -2,9 +2,23 @@ export class BasePageRenderer {
|
|
2
2
|
static render(outputDir: any, taxa: any, familyCols: any): void;
|
3
3
|
static renderTools(outputDir: any, taxa: any): void;
|
4
4
|
}
|
5
|
-
export class
|
6
|
-
constructor(commandName: any, commandDesc: any, optionDefs: any, optionHelp: any,
|
7
|
-
|
5
|
+
export class CommandAndTaxaProcessor extends CommandProcessor {
|
6
|
+
constructor(commandName: any, commandDesc: any, fnCustomProcess: any, optionDefs: any[], optionHelp: any[], commandHelp: any[], taxaLoaderClass: any);
|
7
|
+
process(): Promise<void>;
|
8
|
+
#private;
|
9
|
+
}
|
10
|
+
import { CommandProcessor } from "./commandprocessor.js";
|
11
|
+
export class CommandProcessor {
|
12
|
+
/**
|
13
|
+
* @param {String} commandName
|
14
|
+
* @param {String} commandDesc
|
15
|
+
* @param {*} optionDefs An array of command line options to be added to the standard options.
|
16
|
+
* @param {*} optionHelp An array of help descriptions to be added to the standard options in the first section.
|
17
|
+
* @param {*} commandHelp An array of help sections to be appended to the first option section in the help display.
|
18
|
+
*/
|
19
|
+
constructor(commandName: string, commandDesc: string, optionDefs?: any, optionHelp?: any, commandHelp?: any);
|
20
|
+
getOptions(): any;
|
21
|
+
helpShown(): boolean;
|
8
22
|
showHelp(): void;
|
9
23
|
#private;
|
10
24
|
}
|
@@ -19,24 +33,21 @@ export class Config {
|
|
19
33
|
}
|
20
34
|
export class CSV {
|
21
35
|
static getMap(dir: any, fileName: any): {};
|
22
|
-
static "__#
|
36
|
+
static "__#8@#getOptions"(fileName: any, columns: any, delimiter: any): {
|
23
37
|
relax_column_count_less: boolean;
|
24
38
|
};
|
25
39
|
static parseFile(dir: any, fileName: any, columns: boolean, delimiter: any): any;
|
26
40
|
static parseStream(dir: any, fileName: any, columns: boolean, delimiter: any, callback: any): Promise<void>;
|
27
41
|
}
|
28
|
-
export class DataLoader {
|
29
|
-
static load(dataDir: any): Taxa;
|
30
|
-
}
|
31
|
-
import { Taxa } from "./taxa.js";
|
32
42
|
export class DateUtils {
|
33
43
|
static getMonthName(monthNum: any): string;
|
34
44
|
static monthRangesOverlap(r1: any, r2: any): any;
|
35
45
|
}
|
36
46
|
export class ErrorLog {
|
37
|
-
|
38
|
-
|
39
|
-
|
47
|
+
constructor(fileName: any, echo?: boolean);
|
48
|
+
log(...args: any[]): void;
|
49
|
+
write(): void;
|
50
|
+
#private;
|
40
51
|
}
|
41
52
|
export class Exceptions {
|
42
53
|
constructor(dir: any);
|
@@ -46,7 +57,7 @@ export class Exceptions {
|
|
46
57
|
#private;
|
47
58
|
}
|
48
59
|
export class Families {
|
49
|
-
static "__#
|
60
|
+
static "__#12@#families": any;
|
50
61
|
static getFamilies(): any[];
|
51
62
|
static getFamily(familyName: any): any;
|
52
63
|
static renderPages(outputDir: any, taxaColumns: any): void;
|
@@ -94,6 +105,17 @@ export class GenericPage {
|
|
94
105
|
writeFile(html: any): void;
|
95
106
|
#private;
|
96
107
|
}
|
108
|
+
export class GenericTaxaLoader {
|
109
|
+
constructor(options: any);
|
110
|
+
getErrorLog(): ErrorLog;
|
111
|
+
getOptions(): any;
|
112
|
+
getTaxa(): any;
|
113
|
+
load(): Promise<void>;
|
114
|
+
loadTaxa(): Promise<void>;
|
115
|
+
writeErrorLog(): void;
|
116
|
+
#private;
|
117
|
+
}
|
118
|
+
import { ErrorLog } from "./errorlog.js";
|
97
119
|
export namespace HTML_OPTIONS {
|
98
120
|
const OPEN_NEW: number;
|
99
121
|
const NO_ESCAPE: number;
|
@@ -134,21 +156,21 @@ export class HTML {
|
|
134
156
|
static wrap(elName: any, text: any, attributes?: {}): string;
|
135
157
|
}
|
136
158
|
import { BasePageRenderer } from "./basepagerenderer.js";
|
137
|
-
import {
|
159
|
+
import { CommandAndTaxaProcessor } from "./commandandtaxaprocessor.js";
|
138
160
|
import { Config } from "./config.js";
|
139
161
|
import { CSV } from "./csv.js";
|
140
|
-
import { DataLoader } from "./dataloader.js";
|
141
162
|
import { ErrorLog } from "./errorlog.js";
|
142
163
|
import { Exceptions } from "./exceptions.js";
|
143
164
|
import { Families } from "./families.js";
|
144
165
|
import { Files } from "./files.js";
|
166
|
+
import { GenericTaxaLoader } from "./generictaxaloader.js";
|
145
167
|
import { HTML } from "./html.js";
|
146
168
|
import { Jekyll } from "./jekyll.js";
|
147
169
|
import { PlantBook } from "./ebook/plantbook.js";
|
148
170
|
import { Taxa } from "./taxa.js";
|
149
171
|
import { TAXA_COLNAMES } from "./taxon.js";
|
150
172
|
import { Taxon } from "./taxon.js";
|
151
|
-
export { BasePageRenderer,
|
173
|
+
export { BasePageRenderer, CommandAndTaxaProcessor, Config, CSV, ErrorLog, Exceptions, Families, Files, GenericTaxaLoader, HTML, Jekyll, PlantBook, Taxa, TAXA_COLNAMES, Taxon };
|
152
174
|
export class Jekyll {
|
153
175
|
static getFrontMatter(atts: any): string;
|
154
176
|
static hasInclude(baseDir: any, path: any): boolean;
|
@@ -175,13 +197,8 @@ export class Taxa {
|
|
175
197
|
title: string;
|
176
198
|
data: (t: any) => any;
|
177
199
|
}[]): string;
|
178
|
-
constructor(inclusionList: any, taxaMeta?: {}, taxonClass?: typeof Taxon, extraTaxa?: any[], extraSynonyms?: any[]);
|
200
|
+
constructor(inclusionList: any, errorLog: any, showFlowerErrors: any, taxaMeta?: {}, taxonClass?: typeof Taxon, extraTaxa?: any[], extraSynonyms?: any[]);
|
179
201
|
getFlowerColor(name: any): any;
|
180
|
-
/**
|
181
|
-
*
|
182
|
-
* @deprecated
|
183
|
-
*/
|
184
|
-
getTaxa(): any[];
|
185
202
|
getTaxon(name: any): any;
|
186
203
|
getTaxonList(): any[];
|
187
204
|
#private;
|
@@ -223,6 +240,21 @@ export namespace TAXA_LIST_COLS {
|
|
223
240
|
}
|
224
241
|
}
|
225
242
|
import { Taxon } from "./taxon.js";
|
243
|
+
export class TaxaLoader extends GenericTaxaLoader {
|
244
|
+
loadTaxa(): Promise<Taxa>;
|
245
|
+
}
|
246
|
+
import { GenericTaxaLoader } from "./generictaxaloader.js";
|
247
|
+
import { Taxa } from "./taxa.js";
|
248
|
+
export class TaxaProcessor {
|
249
|
+
constructor(options: any, taxaLoaderClass?: typeof TaxaLoader);
|
250
|
+
customProcess(): Promise<void>;
|
251
|
+
getErrorLog(): any;
|
252
|
+
getOptions(): any;
|
253
|
+
getTaxa(): any;
|
254
|
+
process(): Promise<void>;
|
255
|
+
#private;
|
256
|
+
}
|
257
|
+
import { TaxaLoader } from "./taxaloader.js";
|
226
258
|
export namespace TAXA_COLNAMES {
|
227
259
|
const BLOOM_START: string;
|
228
260
|
const BLOOM_END: string;
|
@@ -273,5 +305,6 @@ export class Taxon {
|
|
273
305
|
*/
|
274
306
|
isNative(): boolean;
|
275
307
|
isRare(): boolean;
|
308
|
+
shouldHaveFlowers(): boolean;
|
276
309
|
#private;
|
277
310
|
}
|
package/lib/index.js
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
import { BasePageRenderer } from "./basepagerenderer.js";
|
2
|
-
import {
|
2
|
+
import { CommandAndTaxaProcessor } from "./commandandtaxaprocessor.js";
|
3
3
|
import { Config } from "./config.js";
|
4
4
|
import { CSV } from "./csv.js";
|
5
|
-
import { DataLoader } from "./dataloader.js";
|
6
5
|
import { ErrorLog } from "./errorlog.js";
|
7
6
|
import { Exceptions } from "./exceptions.js";
|
8
7
|
import { Families } from "./families.js";
|
9
8
|
import { Files } from "./files.js";
|
9
|
+
import { GenericTaxaLoader } from "./generictaxaloader.js";
|
10
10
|
import { HTML } from "./html.js";
|
11
11
|
import { Jekyll } from "./jekyll.js";
|
12
12
|
import { PlantBook } from "./ebook/plantbook.js";
|
13
13
|
import { Taxa } from "./taxa.js";
|
14
14
|
import { Taxon, TAXA_COLNAMES } from "./taxon.js";
|
15
15
|
|
16
|
-
export { BasePageRenderer,
|
16
|
+
export { BasePageRenderer, CommandAndTaxaProcessor, Config, CSV, ErrorLog, Exceptions, Families, Files, GenericTaxaLoader, HTML, Jekyll, PlantBook, Taxa, TAXA_COLNAMES, Taxon };
|
package/lib/pagerenderer.js
CHANGED
@@ -32,7 +32,7 @@ class PageRenderer extends BasePageRenderer {
|
|
32
32
|
const listTaxa = [];
|
33
33
|
const calfloraTaxa = [];
|
34
34
|
const iNatTaxa = [];
|
35
|
-
for ( const taxon of taxa.
|
35
|
+
for ( const taxon of taxa.getTaxonList() ) {
|
36
36
|
if ( list.include( taxon ) ) {
|
37
37
|
listTaxa.push( taxon );
|
38
38
|
calfloraTaxa.push( taxon.getCalfloraName() );
|
package/lib/taxa.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import { Config } from "./config.js";
|
2
2
|
import { Taxon } from "./taxon.js";
|
3
|
-
import { ErrorLog } from "./errorlog.js";
|
4
3
|
import { HTML } from "./html.js";
|
5
4
|
import { CSV } from "./csv.js";
|
6
5
|
import { RarePlants } from "./rareplants.js";
|
@@ -65,11 +64,15 @@ class FlowerColor {
|
|
65
64
|
|
66
65
|
class Taxa {
|
67
66
|
|
67
|
+
#errorLog;
|
68
68
|
#taxa = {};
|
69
69
|
#flower_colors = {};
|
70
70
|
#sortedTaxa;
|
71
71
|
|
72
|
-
constructor( inclusionList, taxaMeta = {}, taxonClass = Taxon, extraTaxa = [], extraSynonyms = [] ) {
|
72
|
+
constructor( inclusionList, errorLog, showFlowerErrors, taxaMeta = {}, taxonClass = Taxon, extraTaxa = [], extraSynonyms = [] ) {
|
73
|
+
|
74
|
+
this.#errorLog = errorLog;
|
75
|
+
|
73
76
|
for ( const color of FLOWER_COLOR_NAMES ) {
|
74
77
|
this.#flower_colors[ color ] = new FlowerColor( color );
|
75
78
|
}
|
@@ -77,13 +80,13 @@ class Taxa {
|
|
77
80
|
const dataDir = Config.getPackageDir() + "/data";
|
78
81
|
|
79
82
|
const taxaCSV = CSV.parseFile( dataDir, "taxa.csv" );
|
80
|
-
this.#loadTaxa( taxaCSV, inclusionList, taxaMeta, taxonClass );
|
81
|
-
this.#loadTaxa( extraTaxa, inclusionList, taxaMeta, taxonClass );
|
83
|
+
this.#loadTaxa( taxaCSV, inclusionList, taxaMeta, taxonClass, showFlowerErrors );
|
84
|
+
this.#loadTaxa( extraTaxa, inclusionList, taxaMeta, taxonClass, showFlowerErrors );
|
82
85
|
|
83
86
|
// Make sure everything in the inclusionList has been loaded.
|
84
87
|
for ( const name of Object.keys( inclusionList ) ) {
|
85
88
|
if ( !this.getTaxon( name ) ) {
|
86
|
-
|
89
|
+
this.#errorLog.log( name, "not found in taxon list" );
|
87
90
|
}
|
88
91
|
}
|
89
92
|
|
@@ -127,14 +130,6 @@ class Taxa {
|
|
127
130
|
return this.#flower_colors[ name ];
|
128
131
|
}
|
129
132
|
|
130
|
-
/**
|
131
|
-
*
|
132
|
-
* @deprecated
|
133
|
-
*/
|
134
|
-
getTaxa() {
|
135
|
-
return this.#sortedTaxa;
|
136
|
-
}
|
137
|
-
|
138
133
|
getTaxon( name ) {
|
139
134
|
return this.#taxa[ name ];
|
140
135
|
}
|
@@ -158,7 +153,7 @@ class Taxa {
|
|
158
153
|
}
|
159
154
|
}
|
160
155
|
|
161
|
-
#loadTaxa( taxaCSV, inclusionList, taxaMeta, taxonClass ) {
|
156
|
+
#loadTaxa( taxaCSV, inclusionList, taxaMeta, taxonClass, showFlowerErrors ) {
|
162
157
|
for ( const row of taxaCSV ) {
|
163
158
|
|
164
159
|
const name = row[ "taxon_name" ];
|
@@ -172,7 +167,7 @@ class Taxa {
|
|
172
167
|
}
|
173
168
|
|
174
169
|
if ( this.#taxa[ name ] ) {
|
175
|
-
|
170
|
+
this.#errorLog.log( name, "has multiple entries" );
|
176
171
|
}
|
177
172
|
|
178
173
|
const status = taxon_overrides[ "status" ];
|
@@ -192,6 +187,18 @@ class Taxa {
|
|
192
187
|
}
|
193
188
|
}
|
194
189
|
|
190
|
+
if ( showFlowerErrors ) {
|
191
|
+
// Make sure flower colors/bloom times are present or not depending on taxon.
|
192
|
+
if ( taxon.shouldHaveFlowers() ) {
|
193
|
+
if ( !colors || !taxon.getBloomStart() || !taxon.getBloomEnd() ) {
|
194
|
+
this.#errorLog.log( name, "does not have all flower info" );
|
195
|
+
}
|
196
|
+
} else {
|
197
|
+
if ( colors || taxon.getBloomStart() || taxon.getBloomEnd() ) {
|
198
|
+
this.#errorLog.log( name, "should not have flower info" );
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
195
202
|
}
|
196
203
|
|
197
204
|
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { CSV } from "./csv.js";
|
2
|
+
import { Files } from "./files.js";
|
3
|
+
import { GenericTaxaLoader } from "./generictaxaloader.js";
|
4
|
+
import { Taxa } from "./taxa.js";
|
5
|
+
|
6
|
+
class TaxaLoader extends GenericTaxaLoader {
|
7
|
+
|
8
|
+
constructor( options ) {
|
9
|
+
super( options );
|
10
|
+
}
|
11
|
+
|
12
|
+
async loadTaxa() {
|
13
|
+
function getIncludeList( dataDir ) {
|
14
|
+
// Read inclusion list.
|
15
|
+
const includeFileName = "taxa_include.csv";
|
16
|
+
const includeFilePath = dataDir + "/" + includeFileName;
|
17
|
+
if ( !Files.exists( includeFilePath ) ) {
|
18
|
+
console.log( includeFilePath + " not found; loading all taxa" );
|
19
|
+
return true;
|
20
|
+
}
|
21
|
+
const includeCSV = CSV.parseFile( dataDir, includeFileName );
|
22
|
+
const include = {};
|
23
|
+
for ( const row of includeCSV ) {
|
24
|
+
include[ row[ "taxon_name" ] ] = row;
|
25
|
+
}
|
26
|
+
return include;
|
27
|
+
}
|
28
|
+
|
29
|
+
const options = this.getOptions();
|
30
|
+
return new Taxa( getIncludeList( options.datadir ), this.getErrorLog(), options[ "show-flower-errors" ] );
|
31
|
+
}
|
32
|
+
|
33
|
+
}
|
34
|
+
|
35
|
+
export { TaxaLoader };
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { TaxaLoader } from "./taxaloader.js";
|
2
|
+
|
3
|
+
class TaxaProcessor {
|
4
|
+
|
5
|
+
#options;
|
6
|
+
#taxaLoaderClass;
|
7
|
+
#taxaLoader;
|
8
|
+
|
9
|
+
constructor( options, taxaLoaderClass = TaxaLoader ) {
|
10
|
+
this.#options = options;
|
11
|
+
this.#taxaLoaderClass = taxaLoaderClass;
|
12
|
+
}
|
13
|
+
|
14
|
+
async customProcess() {
|
15
|
+
throw new Error( "must be implemented by subclass" );
|
16
|
+
}
|
17
|
+
|
18
|
+
getErrorLog() {
|
19
|
+
return this.#taxaLoader.getErrorLog();
|
20
|
+
}
|
21
|
+
|
22
|
+
getOptions() {
|
23
|
+
return this.#options;
|
24
|
+
}
|
25
|
+
|
26
|
+
getTaxa() {
|
27
|
+
return this.#taxaLoader.getTaxa();
|
28
|
+
}
|
29
|
+
|
30
|
+
async process() {
|
31
|
+
console.log( "loading data" );
|
32
|
+
this.#taxaLoader = new this.#taxaLoaderClass( this.#options );
|
33
|
+
await this.#taxaLoader.load();
|
34
|
+
await this.customProcess();
|
35
|
+
this.#taxaLoader.writeErrorLog();
|
36
|
+
}
|
37
|
+
|
38
|
+
}
|
39
|
+
|
40
|
+
export { TaxaProcessor };
|
package/lib/taxon.js
CHANGED
@@ -255,6 +255,23 @@ class Taxon {
|
|
255
255
|
return this.getRPIRank() !== undefined;
|
256
256
|
}
|
257
257
|
|
258
|
+
shouldHaveFlowers() {
|
259
|
+
const sectionName = this.getFamily().getSectionName();
|
260
|
+
switch ( sectionName ) {
|
261
|
+
case "Ceratophyllales":
|
262
|
+
case "Eudicots":
|
263
|
+
case "Magnoliids":
|
264
|
+
case "Monocots":
|
265
|
+
case "Nymphaeales":
|
266
|
+
return true;
|
267
|
+
case "Ferns":
|
268
|
+
case "Gymnosperms":
|
269
|
+
case "Lycophytes":
|
270
|
+
return false;
|
271
|
+
default:
|
272
|
+
throw new Error( sectionName );
|
273
|
+
}
|
274
|
+
}
|
258
275
|
}
|
259
276
|
|
260
277
|
export { TAXA_COLNAMES, Taxon };
|
package/package.json
CHANGED
package/scripts/build-ebook.js
CHANGED
@@ -1,74 +1,73 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
|
3
|
-
import {
|
4
|
-
import { DataLoader } from "../lib/dataloader.js";
|
3
|
+
import { PlantBook } from "@ca-plant-list/ca-plant-list";
|
5
4
|
import { Config } from "../lib/config.js";
|
6
5
|
import { Files } from "../lib/files.js";
|
7
|
-
import {
|
8
|
-
|
9
|
-
const OUTPUT_DIR = "./output";
|
10
|
-
const LOC_DIR = "./locations";
|
6
|
+
import { TaxaProcessor } from "../lib/taxaprocessor.js";
|
7
|
+
import { CommandProcessor } from "../lib/commandprocessor.js";
|
11
8
|
|
12
9
|
const OPTION_DEFS = [
|
13
|
-
{ name: "
|
10
|
+
{ name: "locationsdir", type: String },
|
14
11
|
];
|
15
12
|
|
16
13
|
const OPTION_HELP = [
|
17
14
|
{
|
18
|
-
name: "
|
15
|
+
name: "locationsdir",
|
19
16
|
type: String,
|
20
17
|
typeLabel: "{underline path}",
|
21
|
-
description: "
|
22
|
-
+ "
|
23
|
-
+ "
|
24
|
-
+ "- otherwise {bold ./data} will be used as the {bold datadir}"
|
18
|
+
description: "If this option is specified, multiple ebooks will be generated. {bold locationsdir} must be a subdirectory"
|
19
|
+
+ " of the current directory, and each subdirectory of {bold locationsdir} is processed in turn to generate an ebook."
|
20
|
+
+ " Each ebook is placed in a subdirectory of {bold outputdir}."
|
25
21
|
},
|
26
22
|
];
|
27
23
|
|
28
|
-
|
29
|
-
"ca-plant-book",
|
30
|
-
"A tool to generate an ebook with local plant data.",
|
31
|
-
OPTION_DEFS,
|
32
|
-
OPTION_HELP,
|
33
|
-
undefined,
|
34
|
-
generateEBooks,
|
35
|
-
);
|
36
|
-
await cr.processCommandLine();
|
24
|
+
class BookCommand extends CommandProcessor {
|
37
25
|
|
38
|
-
|
26
|
+
constructor() {
|
27
|
+
super( "ca-plant-book", "A tool to generate an ebook with local plant data.", OPTION_DEFS, OPTION_HELP );
|
28
|
+
}
|
39
29
|
|
40
|
-
|
30
|
+
}
|
41
31
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
32
|
+
class BookGenerator extends TaxaProcessor {
|
33
|
+
|
34
|
+
async customProcess() {
|
35
|
+
const options = this.getOptions();
|
36
|
+
const ebook = new PlantBook( options.outputdir, new Config( options.datadir ), this.getTaxa() );
|
37
|
+
await ebook.create();
|
46
38
|
}
|
47
39
|
|
40
|
+
}
|
41
|
+
|
42
|
+
async function generateEBooks( options ) {
|
43
|
+
const locationsDir = options.locationsdir;
|
44
|
+
|
48
45
|
// If there is a "locations" directory, generate a book for all subdirectories.
|
49
|
-
|
50
|
-
if ( hasLocations ) {
|
46
|
+
if ( locationsDir ) {
|
51
47
|
// Generate ebook for each location.
|
52
|
-
const
|
48
|
+
const outputBase = options.outputdir;
|
49
|
+
const subdirs = Files.getDirEntries( locationsDir );
|
53
50
|
for ( const subdir of subdirs ) {
|
51
|
+
console.log( "Generating " + subdir );
|
54
52
|
const suffix = "/" + subdir;
|
55
|
-
const path =
|
53
|
+
const path = locationsDir + suffix;
|
56
54
|
if ( Files.isDir( path ) ) {
|
57
|
-
|
55
|
+
options.datadir = path;
|
56
|
+
options.outputdir = outputBase + suffix;
|
57
|
+
const gen = new BookGenerator( options );
|
58
|
+
await gen.process( options );
|
58
59
|
}
|
59
60
|
}
|
60
|
-
|
61
|
+
} else {
|
62
|
+
// Otherwise use the default directory.
|
63
|
+
const gen = new BookGenerator( options );
|
64
|
+
await gen.process( options );
|
61
65
|
}
|
62
66
|
|
63
|
-
// Otherwise use the default directory.
|
64
|
-
await generateEBook( "./data" );
|
65
|
-
|
66
|
-
ErrorLog.write( OUTPUT_DIR + "/errors.tsv" );
|
67
|
-
|
68
|
-
}
|
69
|
-
|
70
|
-
async function generateEBook( dataDir, outputSuffix = "" ) {
|
71
|
-
const ebook = new PlantBook( OUTPUT_DIR + outputSuffix, new Config( dataDir ), DataLoader.load( dataDir ) );
|
72
|
-
await ebook.create();
|
73
67
|
}
|
74
68
|
|
69
|
+
const cmd = new BookCommand();
|
70
|
+
const options = cmd.getOptions();
|
71
|
+
if ( !options.help ) {
|
72
|
+
generateEBooks( options );
|
73
|
+
}
|
package/scripts/build-site.js
CHANGED
@@ -4,26 +4,8 @@ import * as child_process from "node:child_process";
|
|
4
4
|
import * as path from "node:path";
|
5
5
|
import { Files } from "@ca-plant-list/ca-plant-list";
|
6
6
|
import { Config } from "../lib/config.js";
|
7
|
-
import { DataLoader } from "../lib/dataloader.js";
|
8
7
|
import { PageRenderer } from "../lib/pagerenderer.js";
|
9
|
-
import {
|
10
|
-
import { ErrorLog } from "../lib/errorlog.js";
|
11
|
-
|
12
|
-
const OUTPUT_DIR = "./output";
|
13
|
-
|
14
|
-
const OPTION_DEFS = [
|
15
|
-
{ name: "datadir", type: String, defaultValue: "./data" },
|
16
|
-
];
|
17
|
-
|
18
|
-
const OPTION_HELP = [
|
19
|
-
{
|
20
|
-
name: "datadir",
|
21
|
-
type: String,
|
22
|
-
typeLabel: "{underline path}",
|
23
|
-
description: "The directory in which the data files for the local plant list are located. Defaults to {bold ./data}."
|
24
|
-
|
25
|
-
},
|
26
|
-
];
|
8
|
+
import { CommandAndTaxaProcessor } from "../lib/commandandtaxaprocessor.js";
|
27
9
|
|
28
10
|
class JekyllRenderer {
|
29
11
|
|
@@ -57,22 +39,17 @@ class JekyllRenderer {
|
|
57
39
|
|
58
40
|
}
|
59
41
|
|
60
|
-
|
61
|
-
"ca-plant-list",
|
62
|
-
"A tool to generate a website with local plant data.",
|
63
|
-
OPTION_DEFS,
|
64
|
-
OPTION_HELP,
|
65
|
-
undefined,
|
66
|
-
generateSite,
|
67
|
-
);
|
68
|
-
await cr.processCommandLine();
|
69
|
-
|
70
|
-
async function generateSite( options ) {
|
71
|
-
const dataDir = options.datadir;
|
72
|
-
PageRenderer.render( OUTPUT_DIR, new Config( dataDir ), DataLoader.load( dataDir ) );
|
73
|
-
ErrorLog.write( OUTPUT_DIR + "/errors.tsv" );
|
42
|
+
async function generateSite( taxaProcessor ) {
|
74
43
|
|
44
|
+
const options = taxaProcessor.getOptions();
|
45
|
+
|
46
|
+
PageRenderer.render( options.outputdir, new Config( options.datadir ), taxaProcessor.getTaxa() );
|
47
|
+
|
48
|
+
console.log( "generating site" );
|
75
49
|
const r = new JekyllRenderer();
|
76
50
|
await r.renderPages();
|
51
|
+
|
77
52
|
}
|
78
53
|
|
54
|
+
const gen = new CommandAndTaxaProcessor( "ca-plant-list", "A tool to generate a website with local plant data.", generateSite );
|
55
|
+
await gen.process();
|
package/lib/commandrunner.js
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
import commandLineArgs from "command-line-args";
|
2
|
-
import commandLineUsage from "command-line-usage";
|
3
|
-
|
4
|
-
const OPTION_DEFS = [
|
5
|
-
{ name: "help", type: Boolean },
|
6
|
-
];
|
7
|
-
|
8
|
-
class CommandRunner {
|
9
|
-
|
10
|
-
#commandName;
|
11
|
-
#commandDesc;
|
12
|
-
#optionDefs;
|
13
|
-
#optionHelp;
|
14
|
-
#additionalHelp;
|
15
|
-
#commandFunc;
|
16
|
-
|
17
|
-
constructor( commandName, commandDesc, optionDefs, optionHelp, additionalHelp = [], commandFunc ) {
|
18
|
-
this.#commandName = commandName;
|
19
|
-
this.#commandDesc = commandDesc;
|
20
|
-
this.#optionDefs = optionDefs;
|
21
|
-
this.#optionHelp = optionHelp;
|
22
|
-
this.#additionalHelp = additionalHelp;
|
23
|
-
this.#commandFunc = commandFunc;
|
24
|
-
}
|
25
|
-
|
26
|
-
async processCommandLine() {
|
27
|
-
|
28
|
-
const options = commandLineArgs( this.#optionDefs.concat( OPTION_DEFS ) );
|
29
|
-
|
30
|
-
if ( options.help ) {
|
31
|
-
this.showHelp();
|
32
|
-
return;
|
33
|
-
}
|
34
|
-
|
35
|
-
console.log( "Use --help to see all options" );
|
36
|
-
await this.#commandFunc( options );
|
37
|
-
|
38
|
-
}
|
39
|
-
|
40
|
-
showHelp() {
|
41
|
-
const help = [
|
42
|
-
{
|
43
|
-
header: this.#commandName,
|
44
|
-
content: this.#commandDesc
|
45
|
-
},
|
46
|
-
{
|
47
|
-
header: "Options",
|
48
|
-
optionList: [
|
49
|
-
{
|
50
|
-
name: "help",
|
51
|
-
type: Boolean,
|
52
|
-
description: "Print this usage guide."
|
53
|
-
},
|
54
|
-
]
|
55
|
-
}
|
56
|
-
];
|
57
|
-
help[ 1 ].optionList = help[ 1 ].optionList.concat( this.#optionHelp );
|
58
|
-
console.log( commandLineUsage( help.concat( this.#additionalHelp ) ) );
|
59
|
-
}
|
60
|
-
|
61
|
-
}
|
62
|
-
|
63
|
-
export { CommandRunner };
|