@ca-plant-list/ca-plant-list 0.4.29 → 0.4.31

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/taxa.csv CHANGED
@@ -94,7 +94,8 @@ Amsinckia grandiflora,large-flowered fiddleneck,N,13130,323,75424,202030,,Large-
94
94
  Amsinckia intermedia,fiddleneck,N,13131,11324,49141,202031,,Common Fiddleneck
95
95
  Amsinckia lunaris,bent-flower fiddleneck,N,13139,324,75425,202032,,Bent Flowered Fiddleneck,,,,,5,1B.2,,,S3,G3
96
96
  Amsinckia lycopsoides,,N,13140,325,58048,202033,,Bugloss-flowered Fiddleneck
97
- Amsinckia menziesii,rancher's fireweed,N,13145,326,55436,202034,,Menzies' Fiddleneck,,orange
97
+ Amsinckia menziesii,rancher's fireweed,N,13145,326,55436,202034,,Menzies' Fiddleneck,annual,orange
98
+ Amsinckia retrorsa,rigid fiddleneck,N,13147,11325,58049,202037,,Rigid Fiddleneck,annual,yellow,2,5
98
99
  Amsinckia tessellata var. gloriosa,,N,77019,333,80365,202042,,Carrizo Fiddleneck
99
100
  Amsinckia tessellata var. tessellata,devil's lettuce,N,77020,334,58852,202043,,Desert Fiddleneck
100
101
  Amsinckia vernicosa,,N,13155,335,58051,202027,,Green Fiddleneck
@@ -207,7 +208,8 @@ Balsamorhiza deltoidea,,N,1634,1047,70383,202414,true,Balsam Deltoid
207
208
  Balsamorhiza macrolepis,,N,1639,1051,69665,211633,true,California Balsamroot,,,,,350,1B.2,,,S2,G2
208
209
  Barbarea orthoceras,winter cress,N,15481,1057,52976,11356,true,American Wintercress,,yellow,3,7
209
210
  Bassia hyssopifolia,,X,15511,1060,58125,109432,true
210
- Bellardia trixago var. trixago,,X,8635,14706,1115038,416937,Bellardia trixago
211
+ Bellardia trixago var. trixago,Mediterranean lineseed,X,8635,14706,1115038,416937,Bellardia trixago,,annual,"white,pink",4,8
212
+ Bellardia viscosa,yellow glandweed,X,103729,14707,537967,206574,true,,annual,yellow,4,10
211
213
  Bellis perennis,English daisy,X,1652,1065,55563,202429,true
212
214
  Berberis aquifolium var. aquifolium,,N,71483,1068,126887,45198,Berberis aquifolium,Piper's Barberry
213
215
  Berberis aquifolium var. dictyota,,N,76967,1069,775436,45199,Berberis dictyota,Shining Netvein Barberry
@@ -636,7 +638,7 @@ Elymus condensatus,giant wild-rye,N,24165,11636,164673,140055,Leymus condensatus
636
638
  Elymus elymoides var. elymoides,squirreltail,N,72180,11639,58377,211223,Elymus elymoides subsp. elymoides,Squirreltail
637
639
  Elymus glaucus subsp. glaucus,blue wildrye,N,50264,2937,57171,167143,true,Western Rye Grass
638
640
  Elymus glaucus subsp. virescens,,N,50266,2939,61203,206754,true,Blue Wildrye
639
- Elymus hispidus,wheatgrass,X,24153,11640,155848,140096,Thinopyrum intermedium subsp. barbulatum
641
+ Elymus hispidus,wheatgrass,X,24153,11640,155848,140096,Thinopyrum intermedium
640
642
  Elymus mollis subsp. mollis,,N,70142,11641,118426,206755,Leymus mollis subsp. mollis,Dune Wild Rye
641
643
  Elymus multisetus,big squirreltail,N,24122,2941,57167,140134,true,Big Squirreltail
642
644
  Elymus ponticus,tall wheatgrass,X,91896,11643,1301269,206756,Thinopyrum ponticum
@@ -783,7 +785,7 @@ Festuca elmeri,Elmer fescue,N,25798,3580,64333,141011,true,Elmer Fescue
783
785
  Festuca idahoensis,Idaho fescue,N,25808,3581,61200,141076,true,Idaho Fescue
784
786
  Festuca microstachys,,N,25865,11688,77138,141123,Vulpia microstachys var. ciliata,Pacific Fescue
785
787
  Festuca myuros,zorro grass,X,25869,11689,77140,141133,Vulpia myuros
786
- Festuca octoflora,six-week's fescue,N,25872,11690,77141,141148,Vulpia octoflora var. hirtella,Six Weeks Fescue
788
+ Festuca octoflora,six-week's fescue,N,25872,11690,77141,141148,Vulpia octoflora,Six Weeks Fescue
787
789
  Festuca perennis,Italian ryegrass,X,91964,11691,52801,203852,Lolium multiflorum
788
790
  Festuca rubra,red fescue,N,25825,3586,61061,141223,true,Red Fescue
789
791
  Festuca temulenta,darnel,X,93753,11692,164712,203853,Lolium temulentum
@@ -1557,7 +1559,7 @@ Romulea rosea var. australis,,X,65230,7162,68895,207249
1557
1559
  Rorippa curvisiliqua,yellow cress,N,41580,7168,58866,30089,true,Curvepod Yellowcress
1558
1560
  Rorippa palustris subsp. palustris,,N,52673,11988,64157,43562,true,Bog Yellowcress
1559
1561
  Rosa californica,California rose,N,41631,7179,53437,30158,true,California Wildrose,,pink
1560
- Rosa gymnocarpa,wood rose,N,41652,7182,53441,30212,true,Baldhip Rose,,pink
1562
+ Rosa gymnocarpa,wood rose,N,41652,7182,53441,30212,true,Wood Rose,,pink
1561
1563
  Rosa rubiginosa,sweet-brier,X,41648,11057,78886,30313,true
1562
1564
  Rosa spithamea,ground rose,N,41696,7187,53442,30338,true,Ground Rose
1563
1565
  Rubus armeniacus,Himalayan blackberry,X,87465,10319,61317,30459
@@ -1737,7 +1739,7 @@ Stephanomeria virgata subsp. pleurocarpa,,N,6227,7806,58849,206092,true,Wand Wir
1737
1739
  Stipa cernua,nodding needlegrass,N,45640,12042,165654,149288,Nassella cernua,Nodding Needle Grass
1738
1740
  Stipa lemmonii var. lemmonii,Lemmon's needlegrass,N,73899,12053,1220756,173006,Achnatherum lemmonii subsp. lemmonii
1739
1741
  Stipa lepida,foothill needlegrass,N,45641,12054,165656,149399,Nassella lepida,Foothill Needlegrass
1740
- Stipa miliacea var. miliacea,smilo grass,X,91920,12058,524183,206097,Piptatherum miliaceum subsp. miliaceum
1742
+ Stipa miliacea var. miliacea,smilo grass,X,91920,12058,524183,206097,Piptatherum miliaceum
1741
1743
  Stipa pulchra,purple needlegrass,N,45644,12067,165659,149498,Nassella pulchra,Purple Needlegrass
1742
1744
  Streptanthus albidus subsp. peramoenus,most beautiful jewelflower,N,53142,7814,58783,258754,Streptanthus glandulosus subsp. glandulosus,,,,,,1490,1B.2,,,S2,G2T2
1743
1745
  Streptanthus breweri,,N,45725,7822,79258,32606,true,Brewer's Jewelflower
package/lib/csv.js CHANGED
@@ -5,7 +5,7 @@ import { parse as parseSync } from "csv-parse/sync";
5
5
  import { parse } from "csv-parse";
6
6
  import { stringify } from "csv-stringify/sync";
7
7
 
8
- class CSV {
8
+ export class CSV {
9
9
  /**
10
10
  * @param {string} fileName
11
11
  * @param {import("csv-parse").ColumnOption[]|boolean|function (string[]):string[]} columns
@@ -95,10 +95,11 @@ class CSV {
95
95
  }
96
96
 
97
97
  /**
98
+ * @template T
98
99
  * @param {string} fileName
99
100
  * @param {boolean|import("csv-parse").ColumnOption[]|function (string[]):string[]} [columns]
100
101
  * @param {string} [delimiter]
101
- * @returns {Object<string,string>[]}
102
+ * @returns {T[]}
102
103
  */
103
104
  static readFile(fileName, columns = true, delimiter) {
104
105
  const content = fs.readFileSync(fileName);
@@ -155,5 +156,3 @@ class CSV {
155
156
  fs.writeFileSync(fileName, content.replaceAll(/,+\n/g, "\n"));
156
157
  }
157
158
  }
158
-
159
- export { CSV };
@@ -88,20 +88,26 @@ export class ExternalSites {
88
88
  }
89
89
 
90
90
  /**
91
- * @param {import("./types.js").Taxon} taxon
91
+ * @param {{taxon_id?:string,taxon_name?:string,subview?:"map"|"table",view?:"species"}} params
92
92
  * @param {import("./config.js").Config} config
93
93
  * @returns {URL|undefined}
94
94
  */
95
- static getInatObsLink(taxon, config) {
96
- const iNatID = taxon.getINatID();
97
- if (!iNatID) {
98
- return;
99
- }
100
-
95
+ static getInatObsLink(params, config) {
101
96
  const url = new URL(
102
- "https://www.inaturalist.org/observations?subview=map",
97
+ "https://www.inaturalist.org/observations?subview=map&iconic_taxa=Plantae",
103
98
  );
104
- url.searchParams.set("taxon_id", iNatID);
99
+ if (params.subview) {
100
+ url.searchParams.set("subview", params.subview);
101
+ }
102
+ if (params.taxon_id) {
103
+ url.searchParams.set("taxon_id", params.taxon_id);
104
+ }
105
+ if (params.taxon_name) {
106
+ url.searchParams.set("taxon_name", params.taxon_name);
107
+ }
108
+ if (params.view) {
109
+ url.searchParams.set("view", params.view);
110
+ }
105
111
  for (const p of ["place_id", "project_id"]) {
106
112
  const v = config.getConfigValue("inat", p);
107
113
  if (v) {
package/lib/htmltaxon.js CHANGED
@@ -60,7 +60,11 @@ const OBSLINKS = {
60
60
  },
61
61
  inat: {
62
62
  label: "iNaturalist",
63
- href: (taxon, config) => ExternalSites.getInatObsLink(taxon, config),
63
+ href: (taxon, config) =>
64
+ ExternalSites.getInatObsLink(
65
+ { taxon_id: taxon.getINatID() },
66
+ config,
67
+ ),
64
68
  },
65
69
  };
66
70
 
@@ -120,6 +124,45 @@ class HTMLTaxon {
120
124
  */
121
125
  static addObsLink(links, taxon, config, sourceCode, label) {
122
126
  const source = OBSLINKS[sourceCode];
127
+ if (sourceCode === "inat") {
128
+ const iNatName = taxon.getINatName().split(" ");
129
+ /** @type {string[]} */
130
+ const iNatLinks = [];
131
+ iNatLinks.push(
132
+ HTML.getLink(
133
+ ExternalSites.getInatObsLink(
134
+ { taxon_name: iNatName[0], view: "species" },
135
+ config,
136
+ ),
137
+ `Genus ${iNatName[0]}`,
138
+ undefined,
139
+ true,
140
+ ),
141
+ );
142
+ if (iNatName.length > 2) {
143
+ const species = `${iNatName[0]} ${iNatName[1]}`;
144
+ iNatLinks.push(
145
+ HTML.getLink(
146
+ ExternalSites.getInatObsLink(
147
+ { taxon_name: species },
148
+ config,
149
+ ),
150
+ species,
151
+ undefined,
152
+ true,
153
+ ),
154
+ );
155
+ }
156
+ this.addLink(
157
+ iNatLinks,
158
+ source.href(taxon, config),
159
+ taxon.getINatSyn() ?? taxon.getName(),
160
+ );
161
+
162
+ const html = HTML.textElement("span", label ?? source.label);
163
+ links.push(html + HTML.wrap("ul", HTML.arrayToLI(iNatLinks)));
164
+ return;
165
+ }
123
166
  this.addLink(links, source.href(taxon, config), label ?? source.label);
124
167
  }
125
168
 
package/lib/index.d.ts CHANGED
@@ -82,10 +82,7 @@ export class Config {
82
82
  }
83
83
 
84
84
  export class CSV {
85
- static readFile(
86
- fileName: string,
87
- delimeter?: string,
88
- ): Record<string, string>[];
85
+ static readFile<T>(fileName: string, delimeter?: string): T[];
89
86
  static writeFileObject(
90
87
  fileName: string,
91
88
  data: Record<string, any>[],
@@ -129,6 +126,21 @@ export class Files {
129
126
 
130
127
  export class Genera {}
131
128
 
129
+ export class GenericPage {
130
+ constructor(
131
+ siteGenerator: SiteGenerator,
132
+ title: string,
133
+ baseFileName: string,
134
+ js?: string,
135
+ );
136
+ getBaseFileName(): string;
137
+ getFrontMatter(): string;
138
+ getOutputDir(): string;
139
+ getSiteGenerator(): SiteGenerator;
140
+ getTitle(): string;
141
+ writeFile(content: string): void;
142
+ }
143
+
132
144
  export class Genus<T extends Taxon> {
133
145
  getTaxa(): T[];
134
146
  }
package/lib/index.js CHANGED
@@ -6,6 +6,7 @@ import { Exceptions } from "./exceptions.js";
6
6
  import { ExternalSites } from "./externalsites.js";
7
7
  import { Families } from "./taxonomy/families.js";
8
8
  import { Files } from "./files.js";
9
+ import { GenericPage } from "./web/pageGeneric.js";
9
10
  import { HTML } from "./html.js";
10
11
  import { HTMLFragments } from "./utils/htmlFragments.js";
11
12
  import { HTMLTaxon } from "./htmltaxon.js";
@@ -24,6 +25,7 @@ export {
24
25
  ExternalSites,
25
26
  Families,
26
27
  Files,
28
+ GenericPage,
27
29
  HTML,
28
30
  HTMLFragments,
29
31
  HTMLTaxon,
package/lib/photo.js CHANGED
@@ -60,6 +60,15 @@ export class Photo {
60
60
  * @returns {string} The URL to retrieve the image file.
61
61
  */
62
62
  getUrl() {
63
- return `https://inaturalist-open-data.s3.amazonaws.com/photos/${this.#id}/medium.${this.#ext}`;
63
+ return Photo.getUrl(this.#id, this.#ext);
64
+ }
65
+
66
+ /**
67
+ * @param {number|string} id
68
+ * @param {string} ext
69
+ * @returns {string} The URL to retrieve the image file.
70
+ */
71
+ static getUrl(id, ext) {
72
+ return `https://inaturalist-open-data.s3.amazonaws.com/photos/${id}/medium.${ext}`;
64
73
  }
65
74
  }
package/lib/tools/inat.js CHANGED
@@ -3,6 +3,7 @@ import { Files } from "../files.js";
3
3
  import { CSV } from "../csv.js";
4
4
  import { sleep } from "../util.js";
5
5
  import { TaxaCSV } from "./taxacsv.js";
6
+ import { SynCSV } from "./syncsv.js";
6
7
 
7
8
  /**
8
9
  * @typedef {{id:string,
@@ -65,8 +66,10 @@ export class INat {
65
66
 
66
67
  const missingTaxa = [];
67
68
 
68
- /**@type {Map<string,string>} */
69
+ /** @type {Map<string,string>} */
69
70
  const idsToUpdate = new Map();
71
+ /** @type {import("./syncsv.js").SynData[]} */
72
+ const synonymsToAdd = [];
70
73
 
71
74
  for (const taxon of taxa.getTaxonList()) {
72
75
  const name = taxon.getName();
@@ -103,12 +106,14 @@ export class INat {
103
106
  errorLog,
104
107
  data.name,
105
108
  data.iNatName,
109
+ synonymsToAdd,
106
110
  );
107
111
  }
108
112
 
109
113
  this.#checkExceptions(taxa, exceptions, errorLog);
110
114
 
111
115
  if (update) {
116
+ updateSynCSV(dataDir, synonymsToAdd);
112
117
  updateTaxaCSV(dataDir, idsToUpdate);
113
118
  }
114
119
  }
@@ -167,14 +172,21 @@ export class INat {
167
172
  }
168
173
 
169
174
  /**
170
- *
171
175
  * @param {import("../types.js").Taxa} taxa
172
176
  * @param {import("../exceptions.js").Exceptions} exceptions
173
177
  * @param {import("../errorlog.js").ErrorLog} errorLog
174
178
  * @param {string} name
175
179
  * @param {string} iNatName
180
+ * @param {import("./syncsv.js").SynData[]} synonymsToAdd
176
181
  */
177
- static async #findCurrentName(taxa, exceptions, errorLog, name, iNatName) {
182
+ static async #findCurrentName(
183
+ taxa,
184
+ exceptions,
185
+ errorLog,
186
+ name,
187
+ iNatName,
188
+ synonymsToAdd,
189
+ ) {
178
190
  /**
179
191
  * @param {{matched_term:string,name:string,rank:string}[]} results
180
192
  * @param {string} iNatName
@@ -242,11 +254,17 @@ export class INat {
242
254
  }
243
255
  }
244
256
  } else {
257
+ const formerName = this.makeSynonymName(result, errorLog);
245
258
  errorLog.log(
246
259
  name,
247
260
  "found iNat synonym",
248
- this.makeSynonymName(result, errorLog) + "," + name + ",INAT",
261
+ formerName + "," + name + ",INAT",
249
262
  );
263
+ synonymsToAdd.push({
264
+ Former: formerName,
265
+ Current: name,
266
+ Type: "INAT",
267
+ });
250
268
  }
251
269
 
252
270
  // Delay to throttle queries to iNat API.
@@ -297,6 +315,17 @@ class InatTaxon {
297
315
  }
298
316
  }
299
317
 
318
+ /**
319
+ * @param {string} dataDir
320
+ * @param {import("./syncsv.js").SynData[]} synonymsToAdd
321
+ */
322
+ function updateSynCSV(dataDir, synonymsToAdd) {
323
+ const csv = new SynCSV(dataDir);
324
+ const data = csv.getData();
325
+ data.push(...synonymsToAdd);
326
+ csv.write();
327
+ }
328
+
300
329
  /**
301
330
  * @param {string} dataDir
302
331
  * @param {Map<string,string>} idsToUpdate
package/lib/util.js CHANGED
@@ -1,16 +1,19 @@
1
1
  /**
2
2
  * Break an array into chunks of a desired size
3
3
  * https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore?tab=readme-ov-file#_chunk
4
- * @param {any[]} input
4
+ * @template T
5
+ * @param {T[]} input
5
6
  * @param {number} size
6
- * @returns {any[][]}
7
+ * @returns {T[][]}
7
8
  */
8
9
  export function chunk(input, size) {
10
+ /** @type {T[][]} */
11
+ const result = [];
9
12
  return input.reduce((arr, item, idx) => {
10
13
  return idx % size === 0
11
14
  ? [...arr, [item]]
12
15
  : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]];
13
- }, []);
16
+ }, result);
14
17
  }
15
18
 
16
19
  /**
@@ -0,0 +1,10 @@
1
+ export class HttpUtils {
2
+ /**
3
+ * @param {URL|string} url
4
+ * @returns {Promise<boolean>}
5
+ */
6
+ static async UrlExists(url) {
7
+ const response = await fetch(url, { method: "HEAD" });
8
+ return response.status === 200;
9
+ }
10
+ }
@@ -41,11 +41,10 @@ import { chunk, sleep } from "../util.js";
41
41
  const ALLOWED_LICENSE_CODES = ["cc0", "cc-by", "cc-by-nc"];
42
42
 
43
43
  /**
44
- * @param {import("../types.js").Taxon[]} taxa
44
+ * @param {string[]} inatTaxonIDs
45
45
  * @return {Promise<InatApiTaxon[]>}
46
46
  */
47
- async function fetchInatTaxa(taxa) {
48
- const inatTaxonIDs = taxa.map((taxon) => taxon.getINatID()).filter(Boolean);
47
+ async function fetchInatTaxa(inatTaxonIDs) {
49
48
  const url = `https://api.inaturalist.org/v2/taxa/${inatTaxonIDs.join(",")}?fields=(taxon_photos:(photo:(medium_url:!t,attribution:!t,license_code:!t)))`;
50
49
  const resp = await fetch(url);
51
50
  if (!resp.ok) {
@@ -77,13 +76,11 @@ export async function getTaxonPhotos(taxaToUpdate) {
77
76
  let taxaRetrieved = 0;
78
77
 
79
78
  for (const batch of chunk(taxaToUpdate, 30)) {
80
- const inatTaxa = await fetchInatTaxa(batch);
79
+ const inatTaxa = await fetchInatTaxa(batch.map((t) => t.getINatID()));
81
80
  for (const iNatTaxon of inatTaxa) {
82
- const iNatTaxonPhotos = iNatTaxon.taxon_photos
83
- .filter((tp) =>
84
- ALLOWED_LICENSE_CODES.includes(tp.photo.license_code),
85
- )
86
- .slice(0, 5);
81
+ const iNatTaxonPhotos = iNatTaxon.taxon_photos.filter((tp) =>
82
+ ALLOWED_LICENSE_CODES.includes(tp.photo.license_code),
83
+ );
87
84
 
88
85
  const taxonName = idMap.get(iNatTaxon.id.toString());
89
86
  if (!taxonName) {
@@ -27,8 +27,7 @@ export class GenericPage {
27
27
  }
28
28
 
29
29
  getDefaultIntro() {
30
- let html = this.getFrontMatter();
31
- return html + this.getMarkdown();
30
+ return this.getFrontMatter() + this.getMarkdown();
32
31
  }
33
32
 
34
33
  getFrontMatter() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "description": "Tools to create files for a website listing plants in an area of California.",
5
5
  "license": "MIT",
6
6
  "repository": {