@ca-plant-list/ca-plant-list 0.4.17 → 0.4.18
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/exceptions.json +5 -3
- package/data/synonyms.csv +127 -122
- package/data/taxa.csv +22 -21
- package/data/text/Brassica-nigra.md +1 -0
- package/data/text/Brassica-rapa.md +1 -0
- package/data/text/Oxalis-oregana.footer.md +3 -0
- package/data/text/Oxalis-smalliana.footer.md +3 -0
- package/data/text/Rumex-acetosella.md +1 -0
- package/lib/csv.js +2 -1
- package/lib/exceptions.js +3 -0
- package/lib/index.d.ts +9 -0
- package/lib/taxon.js +6 -0
- package/lib/tools/jepsoneflora.js +117 -152
- package/lib/tools/syncsv.js +41 -0
- package/package.json +2 -2
- package/scripts/cpl-tools.js +2 -11
package/data/taxa.csv
CHANGED
@@ -227,10 +227,10 @@ Bolboschoenus maritimus subsp. paludosus,saltmarsh bulrush,N,49436,10796,79646,P
|
|
227
227
|
Bolboschoenus robustus,seacoast bulrush,N,77450,10798,75840,Big Bulrush
|
228
228
|
Bowlesia incana,,N,15999,1133,56822,Hoary Bowlesia
|
229
229
|
Brachypodium distachyon,false-brome,X,16042,1137,57158
|
230
|
-
Brassica juncea,India mustard,X,16072,1142,64241
|
231
|
-
Brassica nigra,black mustard,X,16079,1144,1555489
|
232
|
-
Brassica oleracea,cabbage,X,16080,9040,54516
|
233
|
-
Brassica rapa,field mustard,X,16081,1145,53271
|
230
|
+
Brassica juncea,India mustard,X,16072,1142,64241,,"annual,perennial",yellow,5,9
|
231
|
+
Brassica nigra,black mustard,X,16079,1144,1555489,,annual,yellow,4,9
|
232
|
+
Brassica oleracea,cabbage,X,16080,9040,54516,,"biennial,perennial",yellow,5,8
|
233
|
+
Brassica rapa,field mustard,X,16081,1145,53271,,"annual,biennial",yellow,1,5
|
234
234
|
Brickellia californica,California brickelbush,N,1794,1152,58803,Brickell Bush
|
235
235
|
Briza maxima,quaking grass,X,16135,1165,57157
|
236
236
|
Briza minor,little quaking grass,X,16137,1166,57162
|
@@ -1075,11 +1075,11 @@ Linanthus dichotomus subsp. meridianus,,N,51287,10625,80001
|
|
1075
1075
|
Linanthus pungens subsp. pulchriflorus,granite prickly phlox,N,98684,14586,861790,,perennial,"white,pink",5,8
|
1076
1076
|
Linum bienne,,X,31144,4906,55679
|
1077
1077
|
Linum lewisii var. lewisii,blue flax,N,60912,4910,60495,Prairie Flax
|
1078
|
-
Lithophragma affine,woodland star,N,31236,4922,50803,San Francisco Woodland Star
|
1079
|
-
Lithophragma bolanderi,,N,31238,4923,77787,Bolander's Woodland Star
|
1080
|
-
Lithophragma cymbalaria,,N,31241,4925,58323,Mission Woodland Star
|
1081
|
-
Lithophragma heterophyllum,hill starflower,N,31244,4927,53124,Hillside Woodland Star
|
1082
|
-
Lithophragma parviflorum var. parviflorum,,N,60952,4930,81027,Smallflower Woodland-star
|
1078
|
+
Lithophragma affine,woodland star,N,31236,4922,50803,San Francisco Woodland Star,perennial,white,3,4
|
1079
|
+
Lithophragma bolanderi,,N,31238,4923,77787,Bolander's Woodland Star,perennial,white,2,7
|
1080
|
+
Lithophragma cymbalaria,,N,31241,4925,58323,Mission Woodland Star,perennial,white,3,5
|
1081
|
+
Lithophragma heterophyllum,hill starflower,N,31244,4927,53124,Hillside Woodland Star,perennial,white,2,6
|
1082
|
+
Lithophragma parviflorum var. parviflorum,,N,60952,4930,81027,Smallflower Woodland-star,perennial,"white,pink",3,7
|
1083
1083
|
Lobularia maritima,sweet alyssum,X,31347,4939,56992
|
1084
1084
|
Logfia filaginoides,herba impia,N,81847,10960,64254,California Cottonrose,annual,red,2,5
|
1085
1085
|
Logfia gallica,,X,31363,9521,56949,,annual,"brown,yellow",3,7
|
@@ -1285,9 +1285,10 @@ Osmorhiza berteroi,wood cicely,N,35560,10164,53166,Sweet Cicely
|
|
1285
1285
|
Osmorhiza brachypoda,California cicely,N,35564,6000,58793,California Sweetcicely
|
1286
1286
|
Oxalis corniculata,creeping wood sorrel,X,35609,6010,53168,,,yellow,1,12
|
1287
1287
|
Oxalis incarnata,crimson wood sorrel,X,35636,6012,61776,,,"white,pink",3,6
|
1288
|
-
Oxalis oregana,
|
1288
|
+
Oxalis oregana,Oregon wood sorrel,N,35641,6015,47757,Redwood Sorrel,perennial,white,2,10
|
1289
1289
|
Oxalis pes-caprae,Bermuda-buttercup,X,35642,6016,53169,,,yellow,1,5
|
1290
1290
|
Oxalis pilosa,hairy wood sorrel,N,35601,11895,78300,Radishroot Woodsorrel,,yellow,2,9
|
1291
|
+
Oxalis smalliana,redwood sorrel,N,8615,14752,1464017,,perennial,"white,pink,blue",1,8
|
1291
1292
|
Packera breweri,Brewer groundsel,N,77383,9613,56983,Groundsel
|
1292
1293
|
Panicum acuminatum,Pacific panic grass,N,35923,6044,128949,Western Panicgrass
|
1293
1294
|
Panicum capillare,witchgrass,N,36094,6048,78337,Witch Grass
|
@@ -1563,17 +1564,17 @@ Rubus pensilvanicus,,X,42114,7202,78894
|
|
1563
1564
|
Rubus spectabilis,,N,42231,7203,47543,Salmon Berry
|
1564
1565
|
Rubus ulmifolius var. anoplothyrsus,,X,91783,11997,81352
|
1565
1566
|
Rubus ursinus,California blackberry,N,42267,7206,53445,California Blackberry
|
1566
|
-
Rumex acetosella,sheep sorrel,X,42362,7213,53195
|
1567
|
-
Rumex californicus,,N,42423,11058,78901,California Dock
|
1568
|
-
Rumex conglomeratus,green dock,X,42385,7214,57215
|
1569
|
-
Rumex crassus,,N,42422,11059,61087,Willow Dock
|
1570
|
-
Rumex crispus,curly dock,X,42386,7215,53197
|
1571
|
-
Rumex fueginus,golden dock,N,42405,10679,78903,Golden Dock
|
1572
|
-
Rumex obtusifolius,bitter dock,X,42409,7220,63371
|
1573
|
-
Rumex occidentalis,western dock,N,42376,7221,61086,Western Dock
|
1574
|
-
Rumex pulcher,fiddle dock,X,42419,7224,60232
|
1575
|
-
Rumex salicifolius,willow dock,N,42421,7225,60233,Willow Dock
|
1576
|
-
Rumex transitorius,,N,42429,11999,78910
|
1567
|
+
Rumex acetosella,sheep sorrel,X,42362,7213,53195,,perennial,,4,7
|
1568
|
+
Rumex californicus,,N,42423,11058,78901,California Dock,perennial,,5,9
|
1569
|
+
Rumex conglomeratus,green dock,X,42385,7214,57215,,perennial,,5,8
|
1570
|
+
Rumex crassus,,N,42422,11059,61087,Willow Dock,perennial,,2,7
|
1571
|
+
Rumex crispus,curly dock,X,42386,7215,53197,,perennial,,1,12
|
1572
|
+
Rumex fueginus,golden dock,N,42405,10679,78903,Golden Dock,annual,,5,8
|
1573
|
+
Rumex obtusifolius,bitter dock,X,42409,7220,63371,,perennial,,5,9
|
1574
|
+
Rumex occidentalis,western dock,N,42376,7221,61086,Western Dock,perennial,,5,8
|
1575
|
+
Rumex pulcher,fiddle dock,X,42419,7224,60232,,perennial,,5,9
|
1576
|
+
Rumex salicifolius,willow dock,N,42421,7225,60233,Willow Dock,perennial,,5,7
|
1577
|
+
Rumex transitorius,,N,42429,11999,78910,,perennial,,4,6
|
1577
1578
|
Rupertia physodes,California tea,N,42446,7236,53198,Common Rupertia
|
1578
1579
|
Ruppia cirrhosa,ditch-grass,N,42449,7238,61089,Spiral Ditchgrass
|
1579
1580
|
Ruppia maritima,ditch-grass,N,42451,7239,61090,Beaked Tasselweed
|
@@ -0,0 +1 @@
|
|
1
|
+
Upper leaves not clasping stem. Usually hairy.
|
@@ -0,0 +1 @@
|
|
1
|
+
Upper leaves clasping stem. Usually not hairy.
|
@@ -0,0 +1,3 @@
|
|
1
|
+
## Resources
|
2
|
+
|
3
|
+
- Brinegar, Chris. (2023). [THE TRUE REDWOOD SORREL: REINSTATEMENT OF OXALIS SMALLIANA (OXALIDACEAE) AS A SPECIES SEPARATE FROM OXALIS OREGANA](https://www.researchgate.net/publication/369602947_THE_TRUE_REDWOOD_SORREL_REINSTATEMENT_OF_OXALIS_SMALLIANA_OXALIDACEAE_AS_A_SPECIES_SEPARATE_FROM_OXALIS_OREGANA). Madroño. 69. 10.3120/0024-9637-69.4.304.
|
@@ -0,0 +1,3 @@
|
|
1
|
+
## Resources
|
2
|
+
|
3
|
+
- Brinegar, Chris. (2023). [THE TRUE REDWOOD SORREL: REINSTATEMENT OF OXALIS SMALLIANA (OXALIDACEAE) AS A SPECIES SEPARATE FROM OXALIS OREGANA](https://www.researchgate.net/publication/369602947_THE_TRUE_REDWOOD_SORREL_REINSTATEMENT_OF_OXALIS_SMALLIANA_OXALIDACEAE_AS_A_SPECIES_SEPARATE_FROM_OXALIS_OREGANA). Madroño. 69. 10.3120/0024-9637-69.4.304.
|
@@ -0,0 +1 @@
|
|
1
|
+
Leaves with lobes at base. Plants unisexual.
|
package/lib/csv.js
CHANGED
@@ -85,9 +85,10 @@ class CSV {
|
|
85
85
|
}
|
86
86
|
|
87
87
|
/**
|
88
|
+
* @template T
|
88
89
|
* @param {string} fileName
|
89
90
|
* @param {string} [delimiter]
|
90
|
-
* @returns {{headers:string[],data:
|
91
|
+
* @returns {{headers:string[],data:T[]}}
|
91
92
|
*/
|
92
93
|
static readFileAndHeaders(fileName, delimiter) {
|
93
94
|
let headers;
|
package/lib/exceptions.js
CHANGED
package/lib/index.d.ts
CHANGED
@@ -24,6 +24,13 @@ export class ErrorLog {
|
|
24
24
|
|
25
25
|
export class Exceptions {
|
26
26
|
constructor(dataDir: string);
|
27
|
+
getExceptions(): [string, Record<string, Record<string, string>>][];
|
28
|
+
getValue(
|
29
|
+
name: string,
|
30
|
+
cat: string,
|
31
|
+
subcat: string,
|
32
|
+
defaultValue?: string | undefined,
|
33
|
+
): string | undefined;
|
27
34
|
hasException(name: string, cat: string, subcat: string): boolean;
|
28
35
|
}
|
29
36
|
|
@@ -100,7 +107,9 @@ export class Taxon {
|
|
100
107
|
getGenusName(): string;
|
101
108
|
getINatID(): string;
|
102
109
|
getINatTaxonLink(): string;
|
110
|
+
getJepsonID(): string;
|
103
111
|
getName(): string;
|
112
|
+
getRPIRankAndThreat(): string;
|
104
113
|
getRPITaxonLink(): string;
|
105
114
|
getSynonyms(): string[];
|
106
115
|
}
|
package/lib/taxon.js
CHANGED
@@ -264,6 +264,9 @@ class Taxon {
|
|
264
264
|
return this.#iNatSyn ? link + " (" + this.#iNatSyn + ")" : link;
|
265
265
|
}
|
266
266
|
|
267
|
+
/**
|
268
|
+
* @returns {string}
|
269
|
+
*/
|
267
270
|
getJepsonID() {
|
268
271
|
return this.#jepsonID;
|
269
272
|
}
|
@@ -287,6 +290,9 @@ class Taxon {
|
|
287
290
|
return this.#rankRPI.split(".")[0];
|
288
291
|
}
|
289
292
|
|
293
|
+
/**
|
294
|
+
* @returns {string}
|
295
|
+
*/
|
290
296
|
getRPIRankAndThreat() {
|
291
297
|
return this.#rankRPI;
|
292
298
|
}
|
@@ -1,10 +1,17 @@
|
|
1
1
|
import { scrape } from "@htmltools/scrape";
|
2
2
|
import { Files } from "../files.js";
|
3
|
+
import { SynCSV } from "./syncsv.js";
|
3
4
|
|
4
5
|
/**
|
5
|
-
* @typedef {{
|
6
|
+
* @typedef {{
|
7
|
+
* id:string,
|
8
|
+
* name:string,
|
9
|
+
* common?:string,
|
10
|
+
* type:string,
|
11
|
+
* }} JepsonTaxon
|
6
12
|
*/
|
7
13
|
|
14
|
+
/** @type {Object<string,string>} */
|
8
15
|
const TYPES = {
|
9
16
|
EX_ALIEN: "Extirpated alien",
|
10
17
|
HYBRID_SPONT: "Spontaneous hybrid",
|
@@ -32,44 +39,47 @@ const TYPES = {
|
|
32
39
|
WEED: "* weed*",
|
33
40
|
};
|
34
41
|
|
35
|
-
class JepsonEFlora {
|
42
|
+
export class JepsonEFlora {
|
36
43
|
#toolsDataPath;
|
37
44
|
#taxa;
|
38
45
|
#errorLog;
|
39
|
-
#shouldLogNotes;
|
40
46
|
|
41
|
-
/** @type {Map<string,
|
47
|
+
/** @type {Map<string,JepsonTaxon>} */
|
42
48
|
#nameInfo = new Map();
|
43
|
-
/** @type {
|
44
|
-
#
|
49
|
+
/** @type {Map<string,string[]>} */
|
50
|
+
#synInfo = new Map();
|
51
|
+
/** @type {import("./syncsv.js").SynData[]} */
|
52
|
+
#synonymsToAdd = [];
|
45
53
|
|
46
54
|
/**
|
47
55
|
* @param {string} toolsDataDir
|
48
56
|
* @param {Taxa} taxa
|
49
57
|
* @param {ErrorLog} errorLog
|
50
|
-
* @param {boolean} shouldLogNotes
|
51
58
|
*/
|
52
|
-
constructor(toolsDataDir, taxa, errorLog
|
59
|
+
constructor(toolsDataDir, taxa, errorLog) {
|
53
60
|
this.#toolsDataPath = toolsDataDir + "/jepson-eflora";
|
54
61
|
this.#taxa = taxa;
|
55
62
|
this.#errorLog = errorLog;
|
56
|
-
this.#shouldLogNotes = shouldLogNotes;
|
57
63
|
}
|
58
64
|
|
59
65
|
/**
|
60
66
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
67
|
+
* @param {boolean} update
|
61
68
|
*/
|
62
|
-
async analyze(exceptions) {
|
69
|
+
async analyze(exceptions, update) {
|
63
70
|
// Create data directory if it's not there.
|
64
71
|
Files.mkdir(this.#toolsDataPath);
|
65
72
|
|
73
|
+
// Retrieve all Jepson indexes.
|
74
|
+
await this.#loadIndexPages();
|
75
|
+
|
66
76
|
for (const taxon of this.#taxa.getTaxonList()) {
|
67
77
|
const name = taxon.getName();
|
68
78
|
if (name.includes(" unknown")) {
|
69
79
|
continue;
|
70
80
|
}
|
71
81
|
|
72
|
-
const jepsInfo =
|
82
|
+
const jepsInfo = this.#getJepsInfo(name);
|
73
83
|
if (jepsInfo === undefined) {
|
74
84
|
// Not found in the index.
|
75
85
|
if (!exceptions.hasException(name, "jepson", "notineflora")) {
|
@@ -87,12 +97,6 @@ class JepsonEFlora {
|
|
87
97
|
);
|
88
98
|
}
|
89
99
|
|
90
|
-
if (this.#isSynonym(jepsInfo)) {
|
91
|
-
if (!exceptions.hasException(name, "jepson", "allowsynonym")) {
|
92
|
-
this.#errorLog.log(name, "is synonym for", jepsInfo.under);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
100
|
const efStatus = this.#getStatusCode(jepsInfo);
|
97
101
|
const taxonStatus = taxon.getStatus();
|
98
102
|
if (
|
@@ -108,8 +112,12 @@ class JepsonEFlora {
|
|
108
112
|
}
|
109
113
|
}
|
110
114
|
|
111
|
-
|
115
|
+
this.#checkSynonyms();
|
112
116
|
this.#checkExceptions(exceptions);
|
117
|
+
|
118
|
+
if (update) {
|
119
|
+
this.#updateSynCSV();
|
120
|
+
}
|
113
121
|
}
|
114
122
|
|
115
123
|
/**
|
@@ -140,15 +148,6 @@ class JepsonEFlora {
|
|
140
148
|
for (const [k] of Object.entries(exceptions)) {
|
141
149
|
const jepsonData = this.#nameInfo.get(name);
|
142
150
|
switch (k) {
|
143
|
-
case "allowsynonym":
|
144
|
-
// Make sure it really is a synonym.
|
145
|
-
if (!this.#isSynonym(jepsonData)) {
|
146
|
-
this.#errorLog.log(
|
147
|
-
name,
|
148
|
-
"has Jepson allowsynonym exception but is not a synonym",
|
149
|
-
);
|
150
|
-
}
|
151
|
-
break;
|
152
151
|
case "notineflora":
|
153
152
|
// Make sure it is really not in eFlora.
|
154
153
|
if (jepsonData) {
|
@@ -169,42 +168,43 @@ class JepsonEFlora {
|
|
169
168
|
}
|
170
169
|
}
|
171
170
|
|
172
|
-
|
171
|
+
#checkSynonyms() {
|
173
172
|
// Make sure all synonyms in eFlora are in our list.
|
174
|
-
for (const
|
175
|
-
|
176
|
-
|
177
|
-
|
173
|
+
for (const [synName, targetNames] of this.#synInfo.entries()) {
|
174
|
+
for (const targetName of targetNames) {
|
175
|
+
const taxon = this.#taxa.getTaxon(targetName);
|
176
|
+
if (!taxon) {
|
177
|
+
// We're not tracking the target.
|
178
|
+
continue;
|
179
|
+
}
|
178
180
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
continue;
|
184
|
-
}
|
181
|
+
if (taxon.getSynonyms().includes(synName)) {
|
182
|
+
// Already have it.
|
183
|
+
continue;
|
184
|
+
}
|
185
185
|
|
186
|
-
|
187
|
-
|
188
|
-
|
186
|
+
this.#errorLog.log(
|
187
|
+
targetName,
|
188
|
+
"does not have synonym",
|
189
|
+
synName + "," + targetName,
|
190
|
+
);
|
191
|
+
this.#synonymsToAdd.push({
|
192
|
+
Former: synName,
|
193
|
+
Current: targetName,
|
194
|
+
});
|
189
195
|
}
|
190
|
-
|
191
|
-
this.#errorLog.log(
|
192
|
-
target,
|
193
|
-
"does not have synonym",
|
194
|
-
jepsonInfo.name + "," + target,
|
195
|
-
);
|
196
196
|
}
|
197
197
|
|
198
198
|
// Make sure everything in our list is in eFlora.
|
199
199
|
for (const taxon of this.#taxa.getTaxonList()) {
|
200
200
|
for (const synonym of taxon.getSynonyms()) {
|
201
|
-
const
|
202
|
-
if (!
|
201
|
+
const synInfo = this.#synInfo.get(synonym);
|
202
|
+
if (!synInfo || !synInfo.includes(taxon.getName())) {
|
203
203
|
// Ignore iNat synonyms.
|
204
204
|
if (synonym !== taxon.getINatSyn()) {
|
205
205
|
this.#errorLog.log(
|
206
206
|
synonym,
|
207
|
-
|
207
|
+
`is in synonyms.csv but is not a synonym for ${taxon.getName()} in eFlora`,
|
208
208
|
);
|
209
209
|
}
|
210
210
|
}
|
@@ -214,31 +214,17 @@ class JepsonEFlora {
|
|
214
214
|
|
215
215
|
/**
|
216
216
|
* @param {string} name
|
217
|
-
* @returns {
|
217
|
+
* @returns {JepsonTaxon|undefined}
|
218
218
|
*/
|
219
|
-
|
220
|
-
const firstLetter = name[0];
|
221
|
-
// See if this index has been loaded.
|
222
|
-
if (!this.#loadedLetters.has(firstLetter)) {
|
223
|
-
await this.#loadNameIndex(firstLetter);
|
224
|
-
}
|
225
|
-
|
219
|
+
#getJepsInfo(name) {
|
226
220
|
return this.#nameInfo.get(name);
|
227
221
|
}
|
228
222
|
|
229
223
|
/**
|
230
|
-
* @param {
|
224
|
+
* @param {JepsonTaxon} jepsInfo
|
231
225
|
* @returns {StatusCode|undefined}
|
232
226
|
*/
|
233
227
|
#getStatusCode(jepsInfo) {
|
234
|
-
// If it's a synonym, return status of the target.
|
235
|
-
if (this.#isSynonym(jepsInfo)) {
|
236
|
-
const targetInfo = this.#nameInfo.get(jepsInfo.under);
|
237
|
-
if (!targetInfo) {
|
238
|
-
return;
|
239
|
-
}
|
240
|
-
return this.#getStatusCode(targetInfo);
|
241
|
-
}
|
242
228
|
switch (jepsInfo.type) {
|
243
229
|
case TYPES.NATIVE:
|
244
230
|
return "N";
|
@@ -249,19 +235,12 @@ class JepsonEFlora {
|
|
249
235
|
}
|
250
236
|
}
|
251
237
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
if (!jepsInfo) {
|
258
|
-
return false;
|
259
|
-
}
|
260
|
-
switch (jepsInfo.type) {
|
261
|
-
case TYPES.SYNONYM:
|
262
|
-
return true;
|
238
|
+
async #loadIndexPages() {
|
239
|
+
for (let index = 0; index < 26; index++) {
|
240
|
+
await this.#loadNameIndex(
|
241
|
+
String.fromCharCode("A".charCodeAt(0) + index),
|
242
|
+
);
|
263
243
|
}
|
264
|
-
return false;
|
265
244
|
}
|
266
245
|
|
267
246
|
/**
|
@@ -293,32 +272,6 @@ class JepsonEFlora {
|
|
293
272
|
|
294
273
|
const document = scrape.parseFile(filePath);
|
295
274
|
this.#parseIndex(document);
|
296
|
-
|
297
|
-
this.#loadedLetters.add(firstLetter);
|
298
|
-
}
|
299
|
-
|
300
|
-
/**
|
301
|
-
* @param {JepsonInfo} taxonData
|
302
|
-
*/
|
303
|
-
#logNotes(taxonData) {
|
304
|
-
// If we're tracking the source, log it.
|
305
|
-
if (this.#taxa.getTaxon(taxonData.name)) {
|
306
|
-
this.#errorLog.log(
|
307
|
-
taxonData.name,
|
308
|
-
"has eFlora note (as source)",
|
309
|
-
taxonData.type + " for",
|
310
|
-
taxonData.under,
|
311
|
-
);
|
312
|
-
}
|
313
|
-
// If we're tracking the target, log it.
|
314
|
-
if (this.#taxa.getTaxon(taxonData.under)) {
|
315
|
-
this.#errorLog.log(
|
316
|
-
taxonData.under,
|
317
|
-
"has eFlora note (as target)",
|
318
|
-
taxonData.type + " for",
|
319
|
-
taxonData.name,
|
320
|
-
);
|
321
|
-
}
|
322
275
|
}
|
323
276
|
|
324
277
|
/**
|
@@ -370,71 +323,83 @@ class JepsonEFlora {
|
|
370
323
|
);
|
371
324
|
}
|
372
325
|
|
373
|
-
const under = scrape.getTextContent(cols[0]);
|
374
326
|
const common = scrape.getTextContent(cols[1]);
|
327
|
+
const name = linkText;
|
375
328
|
|
376
|
-
/** @type {JepsonInfo} */
|
377
|
-
const taxonData = {};
|
378
|
-
|
379
|
-
taxonData.name = linkText;
|
380
329
|
const href = scrape.getAttr(links[0], "href");
|
381
330
|
if (!href) {
|
382
331
|
throw new Error();
|
383
332
|
}
|
384
|
-
|
385
|
-
taxonData.type = type;
|
386
|
-
if (taxonData.common) {
|
387
|
-
taxonData.common = common;
|
388
|
-
}
|
333
|
+
const id = href.split("=")[1];
|
389
334
|
|
390
|
-
|
391
|
-
|
335
|
+
const sciNameText = scrape.getTextContent(cols[0]);
|
336
|
+
let under;
|
337
|
+
if (sciNameText) {
|
338
|
+
const m = sciNameText.match(reUnder);
|
392
339
|
if (m) {
|
393
|
-
|
340
|
+
under = m[1];
|
394
341
|
}
|
395
342
|
}
|
396
343
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
344
|
+
switch (type) {
|
345
|
+
case TYPES.NATIVE:
|
346
|
+
case TYPES.NATIVITY_UNCERTAIN:
|
347
|
+
case TYPES.NATURALIZED:
|
348
|
+
case TYPES.NATURALIZED_UW:
|
349
|
+
case TYPES.SYNONYM:
|
350
|
+
case TYPES.WAIF:
|
351
|
+
case TYPES.WEED:
|
352
|
+
break;
|
353
|
+
default:
|
354
|
+
continue;
|
404
355
|
}
|
405
356
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
case TYPES.MISAPP_UNABRIDGED:
|
414
|
-
case TYPES.SYN_INED:
|
415
|
-
case TYPES.SYN_ORTH_VARIANT:
|
416
|
-
case TYPES.SYN_PART:
|
417
|
-
case TYPES.SYN_PART_UN:
|
418
|
-
case TYPES.MENTIONED:
|
419
|
-
// Not a valid synonym or active taxon. Log it for further investigation.
|
420
|
-
if (this.#shouldLogNotes) {
|
421
|
-
this.#logNotes(taxonData);
|
422
|
-
}
|
357
|
+
if (type === TYPES.SYNONYM) {
|
358
|
+
// Should have "under".
|
359
|
+
if (!under) {
|
360
|
+
throw new Error();
|
361
|
+
}
|
362
|
+
// If we're not tracking the target, ignore this entry.
|
363
|
+
if (!this.#taxa.getTaxon(under)) {
|
423
364
|
continue;
|
365
|
+
}
|
366
|
+
|
367
|
+
// Add to synonyms.
|
368
|
+
let targetNames = this.#synInfo.get(name);
|
369
|
+
if (!targetNames) {
|
370
|
+
targetNames = [];
|
371
|
+
this.#synInfo.set(name, targetNames);
|
372
|
+
}
|
373
|
+
targetNames.push(under);
|
374
|
+
continue;
|
424
375
|
}
|
425
376
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
377
|
+
// Not a synonym. Should not have "under".
|
378
|
+
if (under) {
|
379
|
+
throw new Error(`under = ${under} for ${name}`);
|
380
|
+
}
|
381
|
+
|
382
|
+
// If we're not tracking either the source, ignore this entry.
|
383
|
+
if (!this.#taxa.getTaxon(name)) {
|
433
384
|
continue;
|
434
385
|
}
|
435
|
-
|
386
|
+
|
387
|
+
if (this.#nameInfo.get(name)) {
|
388
|
+
throw new Error();
|
389
|
+
}
|
390
|
+
this.#nameInfo.set(name, {
|
391
|
+
id: id,
|
392
|
+
type: type,
|
393
|
+
name: name,
|
394
|
+
common: common,
|
395
|
+
});
|
436
396
|
}
|
437
397
|
}
|
438
|
-
}
|
439
398
|
|
440
|
-
|
399
|
+
#updateSynCSV() {
|
400
|
+
const csv = new SynCSV("./data");
|
401
|
+
const data = csv.getData();
|
402
|
+
data.push(...this.#synonymsToAdd);
|
403
|
+
csv.write();
|
404
|
+
}
|
405
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import path from "path";
|
2
|
+
import { CSV } from "../csv.js";
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @typedef {{Former:string,Current:string,Type?:"INAT"|undefined}} SynData
|
6
|
+
*/
|
7
|
+
|
8
|
+
export class SynCSV {
|
9
|
+
#filePath;
|
10
|
+
#headers;
|
11
|
+
/** @type {SynData[]} */
|
12
|
+
#data;
|
13
|
+
|
14
|
+
/**
|
15
|
+
* @param {string} dataDir
|
16
|
+
*/
|
17
|
+
constructor(dataDir) {
|
18
|
+
this.#filePath = path.join(dataDir, "synonyms.csv");
|
19
|
+
const csv = CSV.readFileAndHeaders(this.#filePath);
|
20
|
+
this.#data = csv.data;
|
21
|
+
this.#headers = csv.headers;
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* @returns {SynData[]}
|
26
|
+
*/
|
27
|
+
getData() {
|
28
|
+
return this.#data;
|
29
|
+
}
|
30
|
+
|
31
|
+
write() {
|
32
|
+
this.#data.sort((a, b) => {
|
33
|
+
const former = a.Former.localeCompare(b.Former);
|
34
|
+
if (former !== 0) {
|
35
|
+
return former;
|
36
|
+
}
|
37
|
+
return a.Current.localeCompare(b.Current);
|
38
|
+
});
|
39
|
+
CSV.writeFileObject(this.#filePath, this.#data, this.#headers);
|
40
|
+
}
|
41
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ca-plant-list/ca-plant-list",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.18",
|
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": {
|
@@ -35,6 +35,7 @@
|
|
35
35
|
"inatobsphotos": "scripts/inatobsphotos.js"
|
36
36
|
},
|
37
37
|
"dependencies": {
|
38
|
+
"@htmltools/scrape": "^0.1.0",
|
38
39
|
"archiver": "^5.3.1",
|
39
40
|
"cli-progress": "^3.12.0",
|
40
41
|
"commander": "^12.1.0",
|
@@ -48,7 +49,6 @@
|
|
48
49
|
"unzipper": "^0.12.3"
|
49
50
|
},
|
50
51
|
"devDependencies": {
|
51
|
-
"@htmltools/scrape": "^0.1.0",
|
52
52
|
"@types/archiver": "^6.0.2",
|
53
53
|
"@types/cli-progress": "^3.11.6",
|
54
54
|
"@types/markdown-it": "^14.1.2",
|
package/scripts/cpl-tools.js
CHANGED
@@ -85,13 +85,8 @@ async function build(program, options) {
|
|
85
85
|
);
|
86
86
|
break;
|
87
87
|
case TOOLS.JEPSON_EFLORA: {
|
88
|
-
const eflora = new JepsonEFlora(
|
89
|
-
|
90
|
-
taxa,
|
91
|
-
errorLog,
|
92
|
-
options.efLognotes,
|
93
|
-
);
|
94
|
-
await eflora.analyze(exceptions);
|
88
|
+
const eflora = new JepsonEFlora(TOOLS_DATA_DIR, taxa, errorLog);
|
89
|
+
await eflora.analyze(exceptions, !!options.update);
|
95
90
|
break;
|
96
91
|
}
|
97
92
|
case TOOLS.JEPSON_FAM:
|
@@ -130,10 +125,6 @@ program.option(
|
|
130
125
|
"The name of the file containing the iNaturalist taxa. Can be used for testing on a smaller subset of the iNaturalist data.",
|
131
126
|
"inat_taxa.csv",
|
132
127
|
);
|
133
|
-
program.option(
|
134
|
-
"--ef-lognotes",
|
135
|
-
"When running the jepson-eflora tool, include eFlora notes, invalid names, etc. in the log file.",
|
136
|
-
);
|
137
128
|
program.option("--update", "Update taxa.csv to remove errors if possible.");
|
138
129
|
program.addHelpText(
|
139
130
|
"after",
|