@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.
@@ -1 +1,3 @@
1
- A stalk supporting an inflorescence (one or more flowers).
1
+ A stalk supporting an inflorescence (one or more flowers).
2
+
3
+ ![peduncle](../i/peduncle.svg)
@@ -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,,,white,2,6
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,209711
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,1061170
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
@@ -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"><label for="name">Search for scientific name, common name, or synonym</label>
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>
@@ -27,7 +27,7 @@ class BasePageRenderer {
27
27
  static renderTools( outputDir, taxa ) {
28
28
 
29
29
  const names = [];
30
- for ( const taxon of taxa.getTaxa() ) {
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 load( dataDir ) {
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( dataDir ) );
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.getTaxa() ) {
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
- for ( const color of colors ) {
32
- html += XHTML.textElement( "img", "", { src: "./i/f-" + color + ".svg", class: "flr" } );
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 ) );
@@ -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.getTaxa();
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.getTaxa().length; index++ ) {
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
- static #errors = [];
5
+ #fileName;
6
+ #echo;
7
+ #errors = [];
6
8
 
7
- static log( ...args ) {
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
- static write( fileName ) {
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 CommandRunner {
6
- constructor(commandName: any, commandDesc: any, optionDefs: any, optionHelp: any, additionalHelp: any[], commandFunc: any);
7
- processCommandLine(): Promise<void>;
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 "__#9@#getOptions"(fileName: any, columns: any, delimiter: any): {
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
- static "__#8@#errors": any[];
38
- static log(...args: any[]): void;
39
- static write(fileName: any): void;
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 "__#13@#families": any;
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 { CommandRunner } from "./commandrunner.js";
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, CommandRunner, Config, CSV, DataLoader, ErrorLog, Exceptions, Families, Files, HTML, Jekyll, PlantBook, Taxa, TAXA_COLNAMES, Taxon };
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 { CommandRunner } from "./commandrunner.js";
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, CommandRunner, Config, CSV, DataLoader, ErrorLog, Exceptions, Families, Files, HTML, Jekyll, PlantBook, Taxa, TAXA_COLNAMES, Taxon };
16
+ export { BasePageRenderer, CommandAndTaxaProcessor, Config, CSV, ErrorLog, Exceptions, Families, Files, GenericTaxaLoader, HTML, Jekyll, PlantBook, Taxa, TAXA_COLNAMES, Taxon };
@@ -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.getTaxa() ) {
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
- ErrorLog.log( name, "not found in taxon list" );
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
- ErrorLog.log( name, "has multiple entries" );
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Tools to create Jekyll files for a website listing plants in an area of California.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,74 +1,73 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { CommandRunner, PlantBook } from "@ca-plant-list/ca-plant-list";
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 { ErrorLog } from "../lib/errorlog.js";
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: "datadir", type: String },
10
+ { name: "locationsdir", type: String },
14
11
  ];
15
12
 
16
13
  const OPTION_HELP = [
17
14
  {
18
- name: "datadir",
15
+ name: "locationsdir",
19
16
  type: String,
20
17
  typeLabel: "{underline path}",
21
- description: "The directory in which the data files for the local plant list are located. If no directory is specified:\n"
22
- + "- if there is a {bold locations} subdirectory in the current directory, each subdirectory of the {bold locations} subdirectory"
23
- + " will be used as the data directory from which to generate an ebook (multiple ebooks will be generated)\n"
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
- const cr = new CommandRunner(
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
- async function generateEBooks( options ) {
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
- const dataDir = options.datadir;
30
+ }
41
31
 
42
- // If a data directory was specified, use it.
43
- if ( dataDir ) {
44
- await generateEBook( dataDir );
45
- return;
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
- const hasLocations = Files.isDir( LOC_DIR );
50
- if ( hasLocations ) {
46
+ if ( locationsDir ) {
51
47
  // Generate ebook for each location.
52
- const subdirs = Files.getDirEntries( LOC_DIR );
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 = LOC_DIR + suffix;
53
+ const path = locationsDir + suffix;
56
54
  if ( Files.isDir( path ) ) {
57
- await generateEBook( path, suffix );
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
- return;
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
+ }
@@ -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 { CommandRunner } from "../lib/commandrunner.js";
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
- const cr = new CommandRunner(
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();
@@ -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 };