@ca-plant-list/ca-plant-list 0.4.21 → 0.4.23
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 +22 -1
- package/data/synonyms.csv +2 -0
- package/data/taxa.csv +1754 -1753
- package/lib/basepagerenderer.js +10 -4
- package/lib/ebook/images.js +3 -3
- package/lib/ebook/pages/{page_list_families.js → pageListFamilies.js} +1 -1
- package/lib/ebook/pages/{page_list_flowers.js → pageListFlowers.js} +2 -2
- package/lib/ebook/pages/page_list_species.js +1 -1
- package/lib/ebook/pages/taxonpage.js +1 -1
- package/lib/ebook/pages/tocpage.js +2 -2
- package/lib/ebook/plantbook.js +3 -3
- package/lib/errorlog.js +1 -1
- package/lib/externalsites.js +113 -35
- package/lib/files.js +3 -5
- package/lib/flowercolor.js +2 -2
- package/lib/genera.js +4 -4
- package/lib/html.js +7 -8
- package/lib/htmltaxon.js +122 -33
- package/lib/index.d.ts +72 -27
- package/lib/index.js +3 -3
- package/lib/pagerenderer.js +6 -6
- package/lib/taxonomy/families.js +104 -0
- package/lib/{taxa.js → taxonomy/taxa.js} +18 -18
- package/lib/{taxon.js → taxonomy/taxon.js} +41 -111
- package/lib/taxonomy/taxonomy.js +17 -0
- package/lib/tools/calflora.js +2 -2
- package/lib/tools/calscape.js +3 -3
- package/lib/tools/cch2.js +128 -10
- package/lib/tools/fna.js +163 -0
- package/lib/tools/inat.js +3 -3
- package/lib/tools/jepsoneflora.js +21 -2
- package/lib/tools/rpi.js +5 -5
- package/lib/tools/supplementaltext.js +1 -1
- package/lib/tools/taxacsv.js +23 -4
- package/lib/types.js +10 -0
- package/lib/utils/inat-tools.js +2 -2
- package/lib/web/pageFamily.js +146 -0
- package/lib/web/pagetaxon.js +21 -63
- package/package.json +2 -1
- package/scripts/build-ebook.js +4 -4
- package/scripts/build-site.js +3 -3
- package/scripts/cpl-photos.js +1 -1
- package/scripts/cpl-tools.js +18 -2
- package/scripts/inatobsphotos.js +2 -2
- package/scripts/inattaxonphotos.js +2 -2
- package/lib/families.js +0 -243
- package/lib/jepson.js +0 -17
@@ -1,20 +1,17 @@
|
|
1
|
-
import {
|
2
|
-
import { RarePlants } from "./rareplants.js";
|
1
|
+
import { Taxonomy } from "./taxonomy.js";
|
3
2
|
|
4
|
-
class Taxon {
|
3
|
+
class Taxon extends Taxonomy {
|
5
4
|
#genera;
|
6
5
|
#name;
|
7
6
|
#genus;
|
8
7
|
#commonNames;
|
9
8
|
#status;
|
10
|
-
#jepsonID;
|
11
9
|
#calRecNum;
|
12
|
-
/**@type {string|undefined} */
|
13
|
-
#cfSyn;
|
14
10
|
#iNatID;
|
15
11
|
/**@type {string|undefined} */
|
16
12
|
#iNatSyn;
|
17
13
|
#cch2id;
|
14
|
+
#fnaName;
|
18
15
|
#calscapeCN;
|
19
16
|
#lifeCycle;
|
20
17
|
#flowerColors;
|
@@ -28,14 +25,15 @@ class Taxon {
|
|
28
25
|
#rankGlobal;
|
29
26
|
/** @type {string[]} */
|
30
27
|
#synonyms = [];
|
31
|
-
/** @type {import("
|
28
|
+
/** @type {import("../photo.js").Photo[]}*/
|
32
29
|
#photos = [];
|
33
30
|
|
34
31
|
/**
|
35
|
-
* @param {import("
|
36
|
-
* @param {import("
|
32
|
+
* @param {import("../index.js").TaxonData} data
|
33
|
+
* @param {import("../genera.js").Genera} genera
|
37
34
|
*/
|
38
35
|
constructor(data, genera) {
|
36
|
+
super(data);
|
39
37
|
this.#genera = genera;
|
40
38
|
const name = data["taxon_name"];
|
41
39
|
const commonNames = data["common name"];
|
@@ -49,10 +47,10 @@ class Taxon {
|
|
49
47
|
? commonNames.split(",").map((t) => t.trim())
|
50
48
|
: [];
|
51
49
|
this.#status = data["status"];
|
52
|
-
this.#jepsonID = data["jepson id"];
|
53
50
|
this.#calRecNum = data["calrecnum"];
|
54
51
|
this.#iNatID = data["inat id"];
|
55
52
|
this.#cch2id = data.cch2_id;
|
53
|
+
this.#fnaName = data.fna ?? "";
|
56
54
|
this.#calscapeCN =
|
57
55
|
data.calscape_cn === "" ? undefined : data.calscape_cn;
|
58
56
|
this.#lifeCycle = data.life_cycle;
|
@@ -80,10 +78,6 @@ class Taxon {
|
|
80
78
|
addSynonym(syn, type) {
|
81
79
|
this.#synonyms.push(syn);
|
82
80
|
switch (type) {
|
83
|
-
case "CF":
|
84
|
-
// Synonym is in Calflora format.
|
85
|
-
this.#cfSyn = syn;
|
86
|
-
break;
|
87
81
|
case "INAT":
|
88
82
|
// Synonyms should be in Jepson format, but store iNatName in iNat format (no var or subsp, space after x).
|
89
83
|
this.#iNatSyn = syn;
|
@@ -92,14 +86,14 @@ class Taxon {
|
|
92
86
|
}
|
93
87
|
|
94
88
|
/**
|
95
|
-
* @param {import("
|
89
|
+
* @param {import("../photo.js").Photo} photo
|
96
90
|
*/
|
97
91
|
addPhoto(photo) {
|
98
92
|
this.#photos = this.#photos.concat([photo]);
|
99
93
|
}
|
100
94
|
|
101
95
|
/**
|
102
|
-
* @returns {import("
|
96
|
+
* @returns {import("../photo.js").Photo[]}
|
103
97
|
*/
|
104
98
|
getPhotos() {
|
105
99
|
return this.#photos;
|
@@ -125,9 +119,6 @@ class Taxon {
|
|
125
119
|
}
|
126
120
|
|
127
121
|
getCalfloraName() {
|
128
|
-
if (this.#cfSyn) {
|
129
|
-
return this.#cfSyn;
|
130
|
-
}
|
131
122
|
return this.getName().replace(" subsp.", " ssp.").replace("×", "X");
|
132
123
|
}
|
133
124
|
|
@@ -135,20 +126,6 @@ class Taxon {
|
|
135
126
|
return this.#calRecNum;
|
136
127
|
}
|
137
128
|
|
138
|
-
getCalfloraTaxonLink() {
|
139
|
-
const calfloraID = this.getCalfloraID();
|
140
|
-
if (!calfloraID) {
|
141
|
-
return;
|
142
|
-
}
|
143
|
-
const link = HTML.getLink(
|
144
|
-
"https://www.calflora.org/app/taxon?crn=" + calfloraID,
|
145
|
-
"Calflora",
|
146
|
-
{},
|
147
|
-
true,
|
148
|
-
);
|
149
|
-
return this.#cfSyn ? link + " (" + this.#cfSyn + ")" : link;
|
150
|
-
}
|
151
|
-
|
152
129
|
getCalscapeCommonName() {
|
153
130
|
return this.#calscapeCN;
|
154
131
|
}
|
@@ -171,12 +148,18 @@ class Taxon {
|
|
171
148
|
return this.#cch2id;
|
172
149
|
}
|
173
150
|
|
151
|
+
/**
|
152
|
+
* @returns {string}
|
153
|
+
*/
|
174
154
|
getCESA() {
|
175
|
-
return this.#cesa;
|
155
|
+
return this.#cesa ?? "";
|
176
156
|
}
|
177
157
|
|
158
|
+
/**
|
159
|
+
* @returns {string}
|
160
|
+
*/
|
178
161
|
getCNDDBRank() {
|
179
|
-
return this.#rankCNDDB;
|
162
|
+
return this.#rankCNDDB ?? "";
|
180
163
|
}
|
181
164
|
|
182
165
|
getCommonNames() {
|
@@ -187,14 +170,27 @@ class Taxon {
|
|
187
170
|
return this.getGenus().getFamily();
|
188
171
|
}
|
189
172
|
|
173
|
+
/**
|
174
|
+
* @returns {string}
|
175
|
+
*/
|
190
176
|
getFESA() {
|
191
|
-
return this.#fesa;
|
177
|
+
return this.#fesa ?? "";
|
192
178
|
}
|
193
179
|
|
194
180
|
getFileName(ext = "html") {
|
195
181
|
return this.getBaseFileName() + "." + ext;
|
196
182
|
}
|
197
183
|
|
184
|
+
/**
|
185
|
+
* @returns {string}
|
186
|
+
*/
|
187
|
+
getFNAName() {
|
188
|
+
if (this.#fnaName === "true") {
|
189
|
+
return this.getName();
|
190
|
+
}
|
191
|
+
return this.#fnaName;
|
192
|
+
}
|
193
|
+
|
198
194
|
getFlowerColors() {
|
199
195
|
return this.#flowerColors;
|
200
196
|
}
|
@@ -207,35 +203,11 @@ class Taxon {
|
|
207
203
|
return this.#genus;
|
208
204
|
}
|
209
205
|
|
210
|
-
getGlobalRank() {
|
211
|
-
return this.#rankGlobal;
|
212
|
-
}
|
213
|
-
|
214
206
|
/**
|
215
|
-
*
|
216
|
-
* @param {boolean|string|undefined} href
|
217
|
-
* @param {boolean} includeRPI
|
207
|
+
* @returns {string}
|
218
208
|
*/
|
219
|
-
|
220
|
-
|
221
|
-
let className = this.isNative() ? "native" : "non-native";
|
222
|
-
let isRare = false;
|
223
|
-
if (includeRPI && this.isRare()) {
|
224
|
-
isRare = true;
|
225
|
-
className += " rare";
|
226
|
-
}
|
227
|
-
const attributes = { class: className };
|
228
|
-
const link = HTML.wrap(
|
229
|
-
"span",
|
230
|
-
HTML.getLink(href, this.getName()),
|
231
|
-
attributes,
|
232
|
-
);
|
233
|
-
if (isRare) {
|
234
|
-
return HTML.getToolTip(link, this.getRPIRankAndThreatTooltip(), {
|
235
|
-
icon: false,
|
236
|
-
});
|
237
|
-
}
|
238
|
-
return link;
|
209
|
+
getGlobalRank() {
|
210
|
+
return this.#rankGlobal ?? "";
|
239
211
|
}
|
240
212
|
|
241
213
|
getINatID() {
|
@@ -254,27 +226,6 @@ class Taxon {
|
|
254
226
|
return this.#iNatSyn;
|
255
227
|
}
|
256
228
|
|
257
|
-
getINatTaxonLink() {
|
258
|
-
const iNatID = this.getINatID();
|
259
|
-
if (!iNatID) {
|
260
|
-
return "";
|
261
|
-
}
|
262
|
-
const link = HTML.getLink(
|
263
|
-
"https://www.inaturalist.org/taxa/" + iNatID,
|
264
|
-
"iNaturalist",
|
265
|
-
{},
|
266
|
-
true,
|
267
|
-
);
|
268
|
-
return this.#iNatSyn ? link + " (" + this.#iNatSyn + ")" : link;
|
269
|
-
}
|
270
|
-
|
271
|
-
/**
|
272
|
-
* @returns {string}
|
273
|
-
*/
|
274
|
-
getJepsonID() {
|
275
|
-
return this.#jepsonID;
|
276
|
-
}
|
277
|
-
|
278
229
|
getLifeCycle() {
|
279
230
|
return this.#lifeCycle;
|
280
231
|
}
|
@@ -287,6 +238,9 @@ class Taxon {
|
|
287
238
|
return this.#rpiID;
|
288
239
|
}
|
289
240
|
|
241
|
+
/**
|
242
|
+
* @returns {string}
|
243
|
+
*/
|
290
244
|
getRPIRank() {
|
291
245
|
if (!this.#rankRPI) {
|
292
246
|
return this.#rankRPI;
|
@@ -301,38 +255,12 @@ class Taxon {
|
|
301
255
|
return this.#rankRPI;
|
302
256
|
}
|
303
257
|
|
304
|
-
/**
|
305
|
-
* @deprecated
|
306
|
-
*/
|
307
|
-
getRPIRankAndThreatTooltip() {
|
308
|
-
return RarePlants.getRPIRankAndThreatDescriptions(
|
309
|
-
this.getRPIRankAndThreat(),
|
310
|
-
).join("<br>");
|
311
|
-
}
|
312
|
-
|
313
|
-
/**
|
314
|
-
* @deprecated
|
315
|
-
*/
|
316
|
-
getRPITaxonLink() {
|
317
|
-
const rpiID = this.getRPIID();
|
318
|
-
if (!rpiID) {
|
319
|
-
return "";
|
320
|
-
}
|
321
|
-
const link = HTML.getLink(
|
322
|
-
"https://rareplants.cnps.org/Plants/Details/" + rpiID,
|
323
|
-
"CNPS Rare Plant Inventory",
|
324
|
-
{},
|
325
|
-
true,
|
326
|
-
);
|
327
|
-
return link;
|
328
|
-
}
|
329
|
-
|
330
258
|
getStatus() {
|
331
259
|
return this.#status;
|
332
260
|
}
|
333
261
|
|
334
262
|
/**
|
335
|
-
* @param {import("
|
263
|
+
* @param {import("../config.js").Config} config
|
336
264
|
* @returns {string}
|
337
265
|
*/
|
338
266
|
getStatusDescription(config) {
|
@@ -341,6 +269,8 @@ class Taxon {
|
|
341
269
|
return "Native";
|
342
270
|
case "NC":
|
343
271
|
return config.getLabel("status-NC", "Introduced");
|
272
|
+
case "U":
|
273
|
+
return "Nativity Uncertain";
|
344
274
|
case "X":
|
345
275
|
return "Introduced";
|
346
276
|
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
export class Taxonomy {
|
2
|
+
#data;
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @param {import("../index.js").TaxonomyData} data
|
6
|
+
*/
|
7
|
+
constructor(data) {
|
8
|
+
this.#data = data;
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* @returns {string}
|
13
|
+
*/
|
14
|
+
getJepsonID() {
|
15
|
+
return this.#data["jepson id"];
|
16
|
+
}
|
17
|
+
}
|
package/lib/tools/calflora.js
CHANGED
@@ -25,7 +25,7 @@ export class Calflora {
|
|
25
25
|
/**
|
26
26
|
* @param {string} toolsDataDir
|
27
27
|
* @param {string} dataDir
|
28
|
-
* @param {import("../
|
28
|
+
* @param {import("../types.js").Taxa} taxa
|
29
29
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
30
30
|
* @param {import("../errorlog.js").ErrorLog} errorLog
|
31
31
|
* @param {boolean} update
|
@@ -175,7 +175,7 @@ export class Calflora {
|
|
175
175
|
}
|
176
176
|
|
177
177
|
/**
|
178
|
-
* @param {import("../
|
178
|
+
* @param {import("../types.js").Taxa} taxa
|
179
179
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
180
180
|
* @param {import("../errorlog.js").ErrorLog} errorLog
|
181
181
|
*/
|
package/lib/tools/calscape.js
CHANGED
@@ -2,13 +2,13 @@ import path from "node:path";
|
|
2
2
|
import xlsx from "exceljs";
|
3
3
|
import { Files } from "../files.js";
|
4
4
|
import { TaxaCSV } from "./taxacsv.js";
|
5
|
-
import { Taxon } from "../taxon.js";
|
5
|
+
import { Taxon } from "../taxonomy/taxon.js";
|
6
6
|
|
7
7
|
export class Calscape {
|
8
8
|
/**
|
9
9
|
* @param {string} toolsDataDir
|
10
10
|
* @param {string} dataDir
|
11
|
-
* @param {import("../
|
11
|
+
* @param {import("../types.js").Taxa} taxa
|
12
12
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
13
13
|
* @param {import("../errorlog.js").ErrorLog} errorLog
|
14
14
|
* @param {boolean} update
|
@@ -58,7 +58,7 @@ export class Calscape {
|
|
58
58
|
}
|
59
59
|
|
60
60
|
/**
|
61
|
-
* @param {import("../
|
61
|
+
* @param {import("../types.js").Taxa} taxa
|
62
62
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
63
63
|
* @param {import("../errorlog.js").ErrorLog} errorLog
|
64
64
|
*/
|
package/lib/tools/cch2.js
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
import path from "node:path";
|
2
2
|
import { CSV } from "../csv.js";
|
3
3
|
import { TaxaCSV } from "./taxacsv.js";
|
4
|
+
import { Files } from "../files.js";
|
5
|
+
import puppeteer from "puppeteer";
|
6
|
+
import { renameSync } from "node:fs";
|
4
7
|
|
5
8
|
/**
|
6
9
|
* @typedef {{id:string}} CCHTaxon
|
@@ -11,42 +14,99 @@ export class CCH2 {
|
|
11
14
|
/**
|
12
15
|
* @param {string} toolsDataDir
|
13
16
|
* @param {string} dataDir
|
14
|
-
* @param {import("../
|
17
|
+
* @param {import("../exceptions.js").Exceptions} exceptions
|
18
|
+
* @param {import("../types.js").Taxa} taxa
|
15
19
|
* @param {import("../errorlog.js").ErrorLog} errorLog
|
16
20
|
* @param {boolean} update
|
17
21
|
*/
|
18
|
-
static async analyze(
|
22
|
+
static async analyze(
|
23
|
+
toolsDataDir,
|
24
|
+
dataDir,
|
25
|
+
exceptions,
|
26
|
+
taxa,
|
27
|
+
errorLog,
|
28
|
+
update,
|
29
|
+
) {
|
19
30
|
const toolsDataPath = path.join(toolsDataDir, "cch2");
|
20
31
|
|
21
32
|
const cchTaxa = await getCCHTaxa(toolsDataPath, taxa);
|
22
33
|
|
23
34
|
const idsToUpdate = new Map();
|
24
35
|
for (const taxon of taxa.getTaxonList()) {
|
25
|
-
const
|
36
|
+
const name = taxon.getName();
|
37
|
+
const cchTaxon = cchTaxa.get(name);
|
26
38
|
if (!cchTaxon) {
|
27
|
-
|
39
|
+
if (!exceptions.hasException(name, "cch", "notincch")) {
|
40
|
+
errorLog.log(name, "not found in CCH data");
|
41
|
+
}
|
28
42
|
continue;
|
29
43
|
}
|
30
44
|
if (cchTaxon.id !== taxon.getCCH2ID()) {
|
31
45
|
errorLog.log(
|
32
|
-
|
46
|
+
name,
|
33
47
|
"id in CCH data does not match id in taxa.csv",
|
34
48
|
cchTaxon.id,
|
35
49
|
taxon.getCCH2ID(),
|
36
50
|
);
|
37
|
-
idsToUpdate.set(
|
51
|
+
idsToUpdate.set(name, cchTaxon.id);
|
38
52
|
}
|
39
53
|
}
|
40
54
|
|
55
|
+
this.#checkExceptions(exceptions, taxa, errorLog, cchTaxa);
|
56
|
+
|
41
57
|
if (update) {
|
42
58
|
updateTaxaCSV(dataDir, idsToUpdate);
|
43
59
|
}
|
44
60
|
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* @param {import("../exceptions.js").Exceptions} exceptions
|
64
|
+
* @param {import("../types.js").Taxa} taxa
|
65
|
+
* @param {import("../errorlog.js").ErrorLog} errorLog
|
66
|
+
* @param {CCHTaxa} cchTaxa
|
67
|
+
*/
|
68
|
+
static #checkExceptions(exceptions, taxa, errorLog, cchTaxa) {
|
69
|
+
// Check the CCH exceptions and make sure they still apply.
|
70
|
+
for (const [name, v] of exceptions.getExceptions()) {
|
71
|
+
const exceptions = v.cch;
|
72
|
+
if (!exceptions) {
|
73
|
+
continue;
|
74
|
+
}
|
75
|
+
|
76
|
+
// Make sure the taxon is still in our list.
|
77
|
+
const taxon = taxa.getTaxon(name);
|
78
|
+
if (!taxon) {
|
79
|
+
// Don't process global exceptions if taxon is not in local list.
|
80
|
+
if (taxa.isSubset() && !v.local) {
|
81
|
+
continue;
|
82
|
+
}
|
83
|
+
errorLog.log(name, "has CCH exceptions but is not in taxa.tsv");
|
84
|
+
continue;
|
85
|
+
}
|
86
|
+
|
87
|
+
for (const [k] of Object.entries(exceptions)) {
|
88
|
+
const jepsonData = cchTaxa.get(name);
|
89
|
+
switch (k) {
|
90
|
+
case "notincch":
|
91
|
+
// Make sure it is really not in CCH data.
|
92
|
+
if (jepsonData) {
|
93
|
+
errorLog.log(
|
94
|
+
name,
|
95
|
+
"has CCH notincch exception but is in CCH data",
|
96
|
+
);
|
97
|
+
}
|
98
|
+
break;
|
99
|
+
default:
|
100
|
+
errorLog.log(name, "unrecognized CCH exception", k);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
45
105
|
}
|
46
106
|
|
47
107
|
/**
|
48
108
|
* @param {string} toolsDataPath
|
49
|
-
* @param {import("../
|
109
|
+
* @param {import("../types.js").Taxa} taxa
|
50
110
|
* @returns {Promise<CCHTaxa>}
|
51
111
|
*/
|
52
112
|
async function getCCHTaxa(toolsDataPath, taxa) {
|
@@ -58,17 +118,22 @@ async function getCCHTaxa(toolsDataPath, taxa) {
|
|
58
118
|
// Ignore ranks above species.
|
59
119
|
return;
|
60
120
|
}
|
61
|
-
if (record.acceptance !== "1") {
|
62
|
-
return;
|
63
|
-
}
|
64
121
|
if (!taxa.getTaxon(record.scientificName)) {
|
65
122
|
// If we're not tracking the taxon, ignore it.
|
66
123
|
return;
|
67
124
|
}
|
125
|
+
if (record.acceptance !== "1" && data.has(record.scientificName)) {
|
126
|
+
// Only add the synonym if there is no main entry.
|
127
|
+
return;
|
128
|
+
}
|
68
129
|
data.set(record.scientificName, { id: record.acceptedTaxonID });
|
69
130
|
}
|
70
131
|
|
71
132
|
const fileName = path.join(toolsDataPath, "taxa.csv");
|
133
|
+
if (!Files.exists(fileName)) {
|
134
|
+
await retrieveDataFile(toolsDataPath);
|
135
|
+
}
|
136
|
+
|
72
137
|
const data = new Map();
|
73
138
|
|
74
139
|
await CSV.parseFileStream(fileName, callback);
|
@@ -76,6 +141,41 @@ async function getCCHTaxa(toolsDataPath, taxa) {
|
|
76
141
|
return data;
|
77
142
|
}
|
78
143
|
|
144
|
+
/**
|
145
|
+
* @param {string} toolsDataPath
|
146
|
+
*/
|
147
|
+
async function retrieveDataFile(toolsDataPath) {
|
148
|
+
const url =
|
149
|
+
"https://www.cch2.org/portal/taxa/taxonomy/taxonomydynamicdisplay.php";
|
150
|
+
console.log(`retrieving file from ${url}`);
|
151
|
+
|
152
|
+
const browser = await puppeteer.launch({ headless: true });
|
153
|
+
|
154
|
+
const page = await browser.newPage();
|
155
|
+
|
156
|
+
await page.goto(url);
|
157
|
+
await page.locator("#taxontarget").fill("Tracheophyta");
|
158
|
+
|
159
|
+
// See https://stackoverflow.com/questions/53471235/how-to-wait-for-all-downloads-to-complete-with-puppeteer
|
160
|
+
const session = await browser.target().createCDPSession();
|
161
|
+
await session.send("Browser.setDownloadBehavior", {
|
162
|
+
behavior: "allowAndName",
|
163
|
+
downloadPath: path.resolve(toolsDataPath),
|
164
|
+
eventsEnabled: true,
|
165
|
+
});
|
166
|
+
|
167
|
+
await page.locator('button[value="exportTaxonTree"]').click();
|
168
|
+
|
169
|
+
const filename = await waitUntilDownload(session);
|
170
|
+
// Download file name is the guid; rename it to taxa.csv.
|
171
|
+
renameSync(
|
172
|
+
path.join(toolsDataPath, filename),
|
173
|
+
path.join(toolsDataPath, "taxa.csv"),
|
174
|
+
);
|
175
|
+
|
176
|
+
await browser.close();
|
177
|
+
}
|
178
|
+
|
79
179
|
/**
|
80
180
|
* @param {string} dataDir
|
81
181
|
* @param {Map<string,string>} idsToUpdate
|
@@ -93,3 +193,21 @@ function updateTaxaCSV(dataDir, idsToUpdate) {
|
|
93
193
|
|
94
194
|
taxa.write();
|
95
195
|
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* @param {import("puppeteer").CDPSession} session
|
199
|
+
* @returns {Promise<string>}
|
200
|
+
* @see https://stackoverflow.com/questions/53471235/how-to-wait-for-all-downloads-to-complete-with-puppeteer
|
201
|
+
* @see https://scrapeops.io/puppeteer-web-scraping-playbook/nodejs-puppeteer-downloading-a-file/#setting-a-custom-download-behaviour
|
202
|
+
*/
|
203
|
+
async function waitUntilDownload(session) {
|
204
|
+
return new Promise((resolve, reject) => {
|
205
|
+
session.on("Browser.downloadProgress", (e) => {
|
206
|
+
if (e.state === "completed") {
|
207
|
+
resolve(e.guid);
|
208
|
+
} else if (e.state === "canceled") {
|
209
|
+
reject();
|
210
|
+
}
|
211
|
+
});
|
212
|
+
});
|
213
|
+
}
|