@ca-plant-list/ca-plant-list 0.4.19 → 0.4.20

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.
Files changed (41) hide show
  1. package/data/genera.json +36 -32
  2. package/data/synonyms.csv +2 -0
  3. package/data/taxa.csv +6 -5
  4. package/data/text/Rumex-conglomeratus.md +1 -0
  5. package/data/text/Rumex-obtusifolius.md +1 -0
  6. package/data/text/Rumex-pulcher.md +1 -0
  7. package/data/text/Rumex-salicifolius.md +1 -0
  8. package/lib/ebook/glossarypages.js +3 -3
  9. package/lib/ebook/images.js +2 -2
  10. package/lib/ebook/pages/page_list_families.js +1 -1
  11. package/lib/ebook/pages/page_list_flower_color.js +2 -2
  12. package/lib/ebook/pages/page_list_flowers.js +8 -8
  13. package/lib/ebook/pages/tocpage.js +2 -2
  14. package/lib/ebook/plantbook.js +2 -2
  15. package/lib/externalsites.js +20 -15
  16. package/lib/families.js +1 -1
  17. package/lib/flowercolor.js +2 -2
  18. package/lib/genera.js +2 -2
  19. package/lib/index.d.ts +31 -1
  20. package/lib/photo.js +1 -10
  21. package/lib/plants/glossary.js +2 -4
  22. package/lib/program.js +10 -2
  23. package/lib/sitegenerator.js +13 -3
  24. package/lib/taxa.js +14 -10
  25. package/lib/taxon.js +6 -3
  26. package/lib/tools/calflora.js +41 -8
  27. package/lib/tools/calscape.js +4 -4
  28. package/lib/tools/inat.js +7 -7
  29. package/lib/tools/jepsoneflora.js +28 -4
  30. package/lib/tools/jepsonfamilies.js +102 -0
  31. package/lib/tools/rpi.js +5 -5
  32. package/lib/tools/supplementaltext.js +43 -0
  33. package/lib/tools/taxacsv.js +2 -2
  34. package/lib/utils/inat-tools.js +39 -2
  35. package/lib/web/glossarypages.js +6 -6
  36. package/package.json +6 -7
  37. package/scripts/cpl-photos.js +2 -2
  38. package/scripts/cpl-tools.js +11 -3
  39. package/scripts/inatobsphotos.js +10 -1
  40. package/scripts/inattaxonphotos.js +45 -43
  41. package/types/classes.d.ts +0 -205
package/lib/taxa.js CHANGED
@@ -12,6 +12,10 @@ import { ErrorLog } from "./errorlog.js";
12
12
  import { Program } from "./program.js";
13
13
  import { Photo } from "./photo.js";
14
14
 
15
+ /**
16
+ * @typedef {{Current: string;Former: string;Type: string;}} SynonymData
17
+ */
18
+
15
19
  const FLOWER_COLORS = [
16
20
  { name: "white", color: "white" },
17
21
  { name: "red", color: "red" },
@@ -36,11 +40,11 @@ class Taxa {
36
40
  #isSubset;
37
41
 
38
42
  /**
39
- * @param {Object<string,TaxonData>|true} inclusionList
43
+ * @param {Object<string,import("./index.js").TaxonData>|true} inclusionList
40
44
  * @param {ErrorLog} errorLog
41
45
  * @param {boolean} showFlowerErrors
42
- * @param {function(TaxonData,Genera):Taxon} taxonFactory
43
- * @param {TaxonData[]} [extraTaxa=[]]
46
+ * @param {function(import("./index.js").TaxonData,Genera):Taxon} taxonFactory
47
+ * @param {import("./index.js").TaxonData[]} [extraTaxa=[]]
44
48
  * @param {SynonymData[]} [extraSynonyms=[]]
45
49
  * @param {boolean} includePhotos
46
50
  */
@@ -110,9 +114,9 @@ class Taxa {
110
114
  */
111
115
  #loadPhotosFromFile(dataDir, filename) {
112
116
  if (!fs.existsSync(path.join(dataDir, filename))) return;
113
- /** @type {InatCsvPhoto[]} */
117
+ /** @type {import("./utils/inat-tools.js").InatCsvPhoto[]} */
114
118
  const csvPhotos = CSV.parseFile(dataDir, filename).map((row) => {
115
- /** @type {InatLicenseCode} */
119
+ /** @type {import("./utils/inat-tools.js").InatLicenseCode} */
116
120
  let licenseCode = "cc-by";
117
121
  if (row.licenseCode === "cc-by-nc-sa") licenseCode = "cc-by-nc-sa";
118
122
  else if (row.licenseCode === "cc-by-nc") licenseCode = "cc-by-nc";
@@ -220,7 +224,7 @@ class Taxa {
220
224
 
221
225
  /**
222
226
  * @param {SynonymData[]} synCSV
223
- * @param {Object<string,TaxonData>|boolean} inclusionList
227
+ * @param {Object<string,import("./index.js").TaxonData>|boolean} inclusionList
224
228
  */
225
229
  #loadSyns(synCSV, inclusionList) {
226
230
  for (const syn of synCSV) {
@@ -241,9 +245,9 @@ class Taxa {
241
245
  }
242
246
 
243
247
  /**
244
- * @param {TaxonData[]} taxaCSV
245
- * @param {Object<string,TaxonData>|true} inclusionList
246
- * @param {function(TaxonData,Genera):Taxon} taxonFactory
248
+ * @param {import("./index.js").TaxonData[]} taxaCSV
249
+ * @param {Object<string,import("./index.js").TaxonData>|true} inclusionList
250
+ * @param {function(import("./index.js").TaxonData,Genera):Taxon} taxonFactory
247
251
  * @param {Genera} genera
248
252
  * @param {boolean} showFlowerErrors
249
253
  */
@@ -251,7 +255,7 @@ class Taxa {
251
255
  for (const row of taxaCSV) {
252
256
  const name = row["taxon_name"];
253
257
 
254
- /** @type {TaxonData|{status?:StatusCode}} */
258
+ /** @type {import("./index.js").TaxonData|{status?:import("./taxon.js").StatusCode}} */
255
259
  let taxon_overrides = {};
256
260
  if (inclusionList !== true) {
257
261
  taxon_overrides = inclusionList[name];
package/lib/taxon.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import { HTML } from "./html.js";
2
2
  import { RarePlants } from "./rareplants.js";
3
3
 
4
+ /**
5
+ * @typedef {"N" | "NC" | "U" | "X"} StatusCode
6
+ */
7
+
4
8
  const TAXA_COLNAMES = {
5
9
  BLOOM_START: "bloom_start",
6
10
  BLOOM_END: "bloom_end",
@@ -9,7 +13,6 @@ const TAXA_COLNAMES = {
9
13
  };
10
14
 
11
15
  class Taxon {
12
- /** @type {Genera} */
13
16
  #genera;
14
17
  #name;
15
18
  #genus;
@@ -39,7 +42,7 @@ class Taxon {
39
42
  #photos = [];
40
43
 
41
44
  /**
42
- * @param {TaxonData} data
45
+ * @param {import("./index.js").TaxonData} data
43
46
  * @param {import("./genera.js").Genera} genera
44
47
  */
45
48
  constructor(data, genera) {
@@ -328,7 +331,7 @@ class Taxon {
328
331
  }
329
332
 
330
333
  /**
331
- * @param {Config} config
334
+ * @param {import("./config.js").Config} config
332
335
  * @returns {string}
333
336
  */
334
337
  getStatusDescription(config) {
@@ -1,6 +1,7 @@
1
1
  import * as path from "path";
2
2
  import { CSV } from "../csv.js";
3
3
  import { Files } from "../files.js";
4
+ import { TaxaCSV } from "./taxacsv.js";
4
5
 
5
6
  const CALFLORA_URL_ALL =
6
7
  "https://www.calflora.org/app/downtext?xun=117493&table=species&format=Tab&cols=0,1,4,5,8,38,41,43&psp=lifeform::grass,Tree,Herb,Fern,Shrub,Vine!!&par=f&active=";
@@ -17,18 +18,27 @@ const CALFLORA_URL_COUNTY =
17
18
  * }} CalfloraData
18
19
  */
19
20
 
20
- class Calflora {
21
+ export class Calflora {
21
22
  /** @type {Object<string,CalfloraData>} */
22
23
  static #taxa = {};
23
24
 
24
25
  /**
25
26
  *
26
27
  * @param {string} toolsDataDir
27
- * @param {Taxa} taxa
28
+ * @param {string} dataDir
29
+ * @param {import("../taxa.js").Taxa} taxa
28
30
  * @param {import("../exceptions.js").Exceptions} exceptions
29
- * @param {ErrorLog} errorLog
31
+ * @param {import("../errorlog.js").ErrorLog} errorLog
32
+ * @param {boolean} update
30
33
  */
31
- static async analyze(toolsDataDir, taxa, exceptions, errorLog) {
34
+ static async analyze(
35
+ toolsDataDir,
36
+ dataDir,
37
+ taxa,
38
+ exceptions,
39
+ errorLog,
40
+ update,
41
+ ) {
32
42
  /**
33
43
  * @param {string} url
34
44
  * @param {string} targetFile
@@ -78,6 +88,8 @@ class Calflora {
78
88
  this.#taxa[row["Taxon"]] = row;
79
89
  }
80
90
 
91
+ const idsToUpdate = new Map();
92
+
81
93
  for (const taxon of taxa.getTaxonList()) {
82
94
  const name = taxon.getName();
83
95
  if (name.includes(" unknown")) {
@@ -152,16 +164,21 @@ class Calflora {
152
164
  cfID,
153
165
  taxon.getCalfloraID(),
154
166
  );
167
+ idsToUpdate.set(name, cfID);
155
168
  }
156
169
  }
157
170
 
158
171
  this.#checkExceptions(taxa, exceptions, errorLog);
172
+
173
+ if (update) {
174
+ this.#updateIds(dataDir, idsToUpdate);
175
+ }
159
176
  }
160
177
 
161
178
  /**
162
- * @param {Taxa} taxa
179
+ * @param {import("../taxa.js").Taxa} taxa
163
180
  * @param {import("../exceptions.js").Exceptions} exceptions
164
- * @param {ErrorLog} errorLog
181
+ * @param {import("../errorlog.js").ErrorLog} errorLog
165
182
  */
166
183
  static #checkExceptions(taxa, exceptions, errorLog) {
167
184
  // Check the Calflora exceptions and make sure they still apply.
@@ -220,6 +237,22 @@ class Calflora {
220
237
  }
221
238
  }
222
239
  }
223
- }
224
240
 
225
- export { Calflora };
241
+ /**
242
+ * @param {string} dataDir
243
+ * @param {Map<string,string>} idsToUpdate
244
+ */
245
+ static #updateIds(dataDir, idsToUpdate) {
246
+ const taxa = new TaxaCSV(dataDir);
247
+
248
+ for (const taxonData of taxa.getTaxa()) {
249
+ const id = idsToUpdate.get(taxonData.taxon_name);
250
+ if (!id) {
251
+ continue;
252
+ }
253
+ taxonData["calrecnum"] = id;
254
+ }
255
+
256
+ taxa.write();
257
+ }
258
+ }
@@ -8,9 +8,9 @@ export class Calscape {
8
8
  /**
9
9
  * @param {string} toolsDataDir
10
10
  * @param {string} dataDir
11
- * @param {Taxa} taxa
11
+ * @param {import("../taxa.js").Taxa} taxa
12
12
  * @param {import("../exceptions.js").Exceptions} exceptions
13
- * @param {ErrorLog} errorLog
13
+ * @param {import("../errorlog.js").ErrorLog} errorLog
14
14
  * @param {boolean} update
15
15
  */
16
16
  static async analyze(
@@ -58,9 +58,9 @@ export class Calscape {
58
58
  }
59
59
 
60
60
  /**
61
- * @param {Taxa} taxa
61
+ * @param {import("../taxa.js").Taxa} taxa
62
62
  * @param {import("../exceptions.js").Exceptions} exceptions
63
- * @param {ErrorLog} errorLog
63
+ * @param {import("../errorlog.js").ErrorLog} errorLog
64
64
  */
65
65
  function checkExceptions(taxa, exceptions, errorLog) {
66
66
  // Check the Calscape exceptions and make sure they still apply.
package/lib/tools/inat.js CHANGED
@@ -21,9 +21,9 @@ export class INat {
21
21
  /**
22
22
  * @param {string} toolsDataDir
23
23
  * @param {string} dataDir
24
- * @param {Taxa} taxa
24
+ * @param {import("../taxa.js").Taxa} taxa
25
25
  * @param {import("../exceptions.js").Exceptions} exceptions
26
- * @param {ErrorLog} errorLog
26
+ * @param {import("../errorlog.js").ErrorLog} errorLog
27
27
  * @param {string} csvFileName
28
28
  * @param {boolean} update
29
29
  */
@@ -115,9 +115,9 @@ export class INat {
115
115
 
116
116
  /**
117
117
  *
118
- * @param {Taxa} taxa
118
+ * @param {import("../taxa.js").Taxa} taxa
119
119
  * @param {import("../exceptions.js").Exceptions} exceptions
120
- * @param {ErrorLog} errorLog
120
+ * @param {import("../errorlog.js").ErrorLog} errorLog
121
121
  */
122
122
  static #checkExceptions(taxa, exceptions, errorLog) {
123
123
  // Check the iNat exceptions and make sure they still apply.
@@ -168,9 +168,9 @@ export class INat {
168
168
 
169
169
  /**
170
170
  *
171
- * @param {Taxa} taxa
171
+ * @param {import("../taxa.js").Taxa} taxa
172
172
  * @param {import("../exceptions.js").Exceptions} exceptions
173
- * @param {ErrorLog} errorLog
173
+ * @param {import("../errorlog.js").ErrorLog} errorLog
174
174
  * @param {string} name
175
175
  * @param {string} iNatName
176
176
  */
@@ -255,7 +255,7 @@ export class INat {
255
255
 
256
256
  /**
257
257
  * @param {{name:string,rank:string}} iNatResult
258
- * @param {ErrorLog} errorLog
258
+ * @param {import("../errorlog.js").ErrorLog} errorLog
259
259
  */
260
260
  static makeSynonymName(iNatResult, errorLog) {
261
261
  const synParts = iNatResult.name.split(" ");
@@ -1,6 +1,7 @@
1
1
  import { scrape } from "@htmltools/scrape";
2
2
  import { Files } from "../files.js";
3
3
  import { SynCSV } from "./syncsv.js";
4
+ import { TaxaCSV } from "./taxacsv.js";
4
5
 
5
6
  /**
6
7
  * @typedef {{
@@ -53,8 +54,8 @@ export class JepsonEFlora {
53
54
 
54
55
  /**
55
56
  * @param {string} toolsDataDir
56
- * @param {Taxa} taxa
57
- * @param {ErrorLog} errorLog
57
+ * @param {import("../taxa.js").Taxa} taxa
58
+ * @param {import("../errorlog.js").ErrorLog} errorLog
58
59
  */
59
60
  constructor(toolsDataDir, taxa, errorLog) {
60
61
  this.#toolsDataPath = toolsDataDir + "/jepson-eflora";
@@ -63,16 +64,19 @@ export class JepsonEFlora {
63
64
  }
64
65
 
65
66
  /**
67
+ * @param {string} dataDir
66
68
  * @param {import("../exceptions.js").Exceptions} exceptions
67
69
  * @param {boolean} update
68
70
  */
69
- async analyze(exceptions, update) {
71
+ async analyze(dataDir, exceptions, update) {
70
72
  // Create data directory if it's not there.
71
73
  Files.mkdir(this.#toolsDataPath);
72
74
 
73
75
  // Retrieve all Jepson indexes.
74
76
  await this.#loadIndexPages();
75
77
 
78
+ const idsToUpdate = new Map();
79
+
76
80
  for (const taxon of this.#taxa.getTaxonList()) {
77
81
  const name = taxon.getName();
78
82
  if (name.includes(" unknown")) {
@@ -95,6 +99,7 @@ export class JepsonEFlora {
95
99
  taxon.getJepsonID(),
96
100
  jepsInfo.id,
97
101
  );
102
+ idsToUpdate.set(name, jepsInfo.id);
98
103
  }
99
104
 
100
105
  const efStatus = this.#getStatusCode(jepsInfo);
@@ -116,6 +121,7 @@ export class JepsonEFlora {
116
121
  this.#checkExceptions(exceptions);
117
122
 
118
123
  if (update) {
124
+ this.#updateIds(dataDir, idsToUpdate);
119
125
  this.#updateSynCSV();
120
126
  }
121
127
  }
@@ -222,7 +228,7 @@ export class JepsonEFlora {
222
228
 
223
229
  /**
224
230
  * @param {JepsonTaxon} jepsInfo
225
- * @returns {StatusCode|undefined}
231
+ * @returns {import("../taxon.js").StatusCode|undefined}
226
232
  */
227
233
  #getStatusCode(jepsInfo) {
228
234
  switch (jepsInfo.type) {
@@ -396,6 +402,24 @@ export class JepsonEFlora {
396
402
  }
397
403
  }
398
404
 
405
+ /**
406
+ * @param {string} dataDir
407
+ * @param {Map<string,string>} idsToUpdate
408
+ */
409
+ #updateIds(dataDir, idsToUpdate) {
410
+ const taxa = new TaxaCSV(dataDir);
411
+
412
+ for (const taxonData of taxa.getTaxa()) {
413
+ const id = idsToUpdate.get(taxonData.taxon_name);
414
+ if (!id) {
415
+ continue;
416
+ }
417
+ taxonData["jepson id"] = id;
418
+ }
419
+
420
+ taxa.write();
421
+ }
422
+
399
423
  #updateSynCSV() {
400
424
  const csv = new SynCSV("./data");
401
425
  const data = csv.getData();
@@ -0,0 +1,102 @@
1
+ import path from "node:path";
2
+ import { Files } from "../files.js";
3
+ import { scrape } from "@htmltools/scrape";
4
+
5
+ export class JepsonFamilies {
6
+ /**
7
+ * @param {string} toolsDataDir
8
+ * @param {string} outputdir
9
+ */
10
+ static async build(toolsDataDir, outputdir) {
11
+ const url = "https://ucjeps.berkeley.edu/eflora/toc.html";
12
+ const indexFileName = path.basename(url);
13
+ const toolsDataPath = toolsDataDir + "/jepsonfam";
14
+ const indexFilePath = toolsDataPath + "/" + indexFileName;
15
+
16
+ // Create data directory if it's not there.
17
+ Files.mkdir(toolsDataPath);
18
+
19
+ // Download the data file if it doesn't exist.
20
+ if (!Files.exists(indexFilePath)) {
21
+ console.log("retrieving Jepson family index");
22
+ await Files.fetch(url, indexFilePath);
23
+ }
24
+
25
+ const document = scrape.parseFile(indexFilePath);
26
+
27
+ const body = scrape.getSubtree(document, (t) => t.tagName === "body");
28
+ if (!body) {
29
+ throw new Error();
30
+ }
31
+ const contentDiv = scrape.getSubtree(
32
+ body,
33
+ (t) => scrape.getAttr(t, "id") === "content",
34
+ );
35
+ if (!contentDiv) {
36
+ throw new Error();
37
+ }
38
+ const rows = scrape.getSubtrees(contentDiv, (t) => t.tagName === "tr");
39
+
40
+ this.#parseRows(outputdir, rows);
41
+ }
42
+
43
+ /**
44
+ * @param {string} toolsDataPath
45
+ * @param {import("@htmltools/scrape").Element[]} rows
46
+ */
47
+ static #parseRows(toolsDataPath, rows) {
48
+ /** @type {Object<string,{section:string,id:string}>} */
49
+ const families = {};
50
+ /** @type {Object<string,{family:string,id:string}>} */
51
+ const genera = {};
52
+
53
+ for (const row of rows) {
54
+ const cols = scrape.getSubtrees(row, (t) => t.tagName === "td");
55
+ if (!cols || cols.length < 3) {
56
+ continue;
57
+ }
58
+
59
+ // Find the section.
60
+ const section = scrape.getTextContent(cols[0].children[0]);
61
+
62
+ // Find the family name and ID.
63
+ const familyLink = cols[1].children[0];
64
+ if (familyLink.type !== "element") {
65
+ throw new Error();
66
+ }
67
+ const familyTarget = scrape.getAttr(familyLink, "href");
68
+ if (!familyTarget) {
69
+ throw new Error();
70
+ }
71
+ const familyID = familyTarget.split("=")[1];
72
+ const familyName = scrape.getTextContent(familyLink.children[0]);
73
+ families[familyName] = { section: section, id: familyID };
74
+
75
+ // Find all the genera.
76
+ const genusLinks = scrape.getSubtrees(
77
+ cols[2],
78
+ (t) => t.tagName === "a",
79
+ );
80
+ for (const genusLink of genusLinks) {
81
+ const genusTarget = scrape.getAttr(genusLink, "href");
82
+ if (!genusTarget) {
83
+ throw new Error();
84
+ }
85
+ const genusID = genusTarget.split("=")[1];
86
+ const genusName = scrape.getTextContent(genusLink.children[0]);
87
+ genera[genusName] = { family: familyName, id: genusID };
88
+ }
89
+ }
90
+
91
+ Files.write(
92
+ toolsDataPath + "/families.json",
93
+ JSON.stringify(families, undefined, 4),
94
+ true,
95
+ );
96
+ Files.write(
97
+ toolsDataPath + "/genera.json",
98
+ JSON.stringify(genera, undefined, 4),
99
+ true,
100
+ );
101
+ }
102
+ }
package/lib/tools/rpi.js CHANGED
@@ -13,9 +13,9 @@ class RPI {
13
13
  /**
14
14
  * @param {string} toolsDataDir
15
15
  * @param {import("../taxa.js").Taxa} taxa
16
- * @param {Config} config
16
+ * @param {import("../config.js").Config} config
17
17
  * @param {import("../exceptions.js").Exceptions} exceptions
18
- * @param {ErrorLog} errorLog
18
+ * @param {import("../errorlog.js").ErrorLog} errorLog
19
19
  */
20
20
  static async analyze(toolsDataDir, taxa, config, exceptions, errorLog) {
21
21
  /**
@@ -203,9 +203,9 @@ class RPI {
203
203
 
204
204
  /**
205
205
  * @param {import("../taxa.js").Taxa} taxa
206
- * @param {Config} config
206
+ * @param {import("../config.js").Config} config
207
207
  * @param {import("../exceptions.js").Exceptions} exceptions
208
- * @param {ErrorLog} errorLog
208
+ * @param {import("../errorlog.js").ErrorLog} errorLog
209
209
  */
210
210
  static #checkExceptions(taxa, config, exceptions, errorLog) {
211
211
  const countyCodes = config.getCountyCodes();
@@ -368,7 +368,7 @@ class RPI {
368
368
  * @param {string} toolsDataDir
369
369
  * @param {import("../taxa.js").Taxa} taxa
370
370
  * @param {import("../exceptions.js").Exceptions} exceptions
371
- * @param {ErrorLog} errorLog
371
+ * @param {import("../errorlog.js").ErrorLog} errorLog
372
372
  */
373
373
  static async #scrape(toolsDataDir, taxa, exceptions, errorLog) {
374
374
  const toolsDataPath = toolsDataDir + "/rpi";
@@ -0,0 +1,43 @@
1
+ import { Files } from "../files.js";
2
+
3
+ const VALID_EXTENSIONS = new Set(["md", "footer.md"]);
4
+
5
+ export class SupplementalText {
6
+ /**
7
+ *
8
+ * @param {import("../taxa.js").Taxa} taxa
9
+ * @param {import("../errorlog.js").ErrorLog} errorLog
10
+ */
11
+ static analyze(taxa, errorLog) {
12
+ /**
13
+ * @param {string} fileName
14
+ */
15
+ function fileNameToTaxonName(fileName) {
16
+ const parts = fileName.split(".");
17
+ const ext = parts.slice(1).join(".");
18
+ const taxonName = parts[0]
19
+ .replace("-", " ")
20
+ .replace("-var-", " var. ")
21
+ .replace("-subsp-", " subsp. ");
22
+ return { taxonName: taxonName, ext: ext };
23
+ }
24
+
25
+ const dirName = "data/text";
26
+
27
+ if (!Files.isDir(dirName)) {
28
+ return;
29
+ }
30
+
31
+ const entries = Files.getDirEntries(dirName);
32
+ for (const entry of entries) {
33
+ const parsed = fileNameToTaxonName(entry);
34
+ const taxon = taxa.getTaxon(parsed.taxonName);
35
+ if (!taxon) {
36
+ errorLog.log(dirName + "/" + entry, "not found in taxa.csv");
37
+ }
38
+ if (!VALID_EXTENSIONS.has(parsed.ext)) {
39
+ errorLog.log(dirName + "/" + entry, "has invalid extension");
40
+ }
41
+ }
42
+ }
43
+ }
@@ -4,7 +4,7 @@ import { CSV } from "../csv.js";
4
4
  export class TaxaCSV {
5
5
  #filePath;
6
6
  #headers;
7
- /** @type {TaxonData[]} */
7
+ /** @type {import("../index.js").TaxonData[]} */
8
8
  #taxa;
9
9
 
10
10
  /**
@@ -19,7 +19,7 @@ export class TaxaCSV {
19
19
  }
20
20
 
21
21
  /**
22
- * @returns {TaxonData[]}
22
+ * @returns {import("../index.js").TaxonData[]}
23
23
  */
24
24
  getTaxa() {
25
25
  return this.#taxa;
@@ -1,10 +1,47 @@
1
1
  import { ProgressMeter } from "../progressmeter.js";
2
2
  import { chunk, sleep } from "../util.js";
3
3
 
4
+ /**
5
+ @typedef {"cc-by-nc-sa"
6
+ | "cc-by-nc"
7
+ | "cc-by-nc-nd"
8
+ | "cc-by"
9
+ | "cc-by-sa"
10
+ | "cc-by-nd"
11
+ | "pd"
12
+ | "gdfl"
13
+ | "cc0"} InatLicenseCode
14
+ @typedef {{
15
+ id: string;
16
+ ext: string;
17
+ licenseCode: string;
18
+ attrName: string | undefined;
19
+ }} InatPhotoInfo
20
+ @typedef {{
21
+ id: number;
22
+ attribution: string;
23
+ license_code: InatLicenseCode;
24
+ medium_url?: string;
25
+ url?: string;
26
+ }} InatApiPhoto
27
+ @typedef {{
28
+ id: number;
29
+ taxon_photos: {
30
+ photo: InatApiPhoto;
31
+ }[];
32
+ }} InatApiTaxon
33
+ @typedef {{
34
+ name: string;
35
+ id: number;
36
+ ext: string;
37
+ licenseCode: InatLicenseCode;
38
+ attrName: string;
39
+ }} InatCsvPhoto
40
+ */
4
41
  const ALLOWED_LICENSE_CODES = ["cc0", "cc-by", "cc-by-nc"];
5
42
 
6
43
  /**
7
- * @param {Taxon[]} taxa
44
+ * @param {import("../taxon.js").Taxon[]} taxa
8
45
  * @return {Promise<InatApiTaxon[]>}
9
46
  */
10
47
  async function fetchInatTaxa(taxa) {
@@ -20,7 +57,7 @@ async function fetchInatTaxa(taxa) {
20
57
  }
21
58
 
22
59
  /**
23
- * @param {Taxon[]} taxaToUpdate
60
+ * @param {import("../taxon.js").Taxon[]} taxaToUpdate
24
61
  * @returns {Promise<Map<string,InatPhotoInfo[]>>}
25
62
  */
26
63
  export async function getTaxonPhotos(taxaToUpdate) {
@@ -10,7 +10,7 @@ class GlossaryPages {
10
10
  #glossary;
11
11
 
12
12
  /**
13
- * @param {SiteGenerator} siteGenerator
13
+ * @param {import("../sitegenerator.js").SiteGenerator} siteGenerator
14
14
  */
15
15
  constructor(siteGenerator) {
16
16
  this.#siteGenerator = siteGenerator;
@@ -18,7 +18,7 @@ class GlossaryPages {
18
18
  }
19
19
 
20
20
  /**
21
- * @param {GlossaryEntry} entry
21
+ * @param {import("../plants/glossary.js").GlossaryEntry} entry
22
22
  */
23
23
  #generateEntryPage(entry) {
24
24
  const title = entry.getTermName();
@@ -29,7 +29,7 @@ class GlossaryPages {
29
29
  this.#siteGenerator.writeTemplate(
30
30
  html,
31
31
  { title: title },
32
- Files.join(ENTRY_DIR, title + ".html")
32
+ Files.join(ENTRY_DIR, title + ".html"),
33
33
  );
34
34
  }
35
35
 
@@ -50,8 +50,8 @@ class GlossaryPages {
50
50
  links.push(
51
51
  HTML.getLink(
52
52
  Files.join(ENTRY_DIR, entry.getHTMLFileName()),
53
- entry.getTermName()
54
- )
53
+ entry.getTermName(),
54
+ ),
55
55
  );
56
56
  }
57
57
  let html = HTML.wrap("h1", "Glossary");
@@ -59,7 +59,7 @@ class GlossaryPages {
59
59
  this.#siteGenerator.writeTemplate(
60
60
  html,
61
61
  { title: "Glossary" },
62
- "glossary.html"
62
+ "glossary.html",
63
63
  );
64
64
  }
65
65