@ca-plant-list/ca-plant-list 0.4.26 → 0.4.28

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 (58) hide show
  1. package/data/exceptions.json +0 -27
  2. package/data/genera.json +52 -28
  3. package/data/glossary/anther.md +3 -0
  4. package/data/glossary/filament.md +3 -0
  5. package/data/glossary/stamen.md +3 -0
  6. package/data/illustrations/inkscape/stamen.svg +132 -0
  7. package/data/inatobsphotos.csv +0 -5
  8. package/data/inattaxonphotos.csv +69 -64
  9. package/data/synonyms.csv +155 -164
  10. package/data/taxa.csv +38 -38
  11. package/data/text/Dichelostemma-congestum.md +1 -0
  12. package/data/text/Dipterostemon-capitatus-subsp-capitatus.md +1 -0
  13. package/generators/eleventy/layouts/h1.njk +6 -0
  14. package/generators/eleventy/layouts/html.njk +55 -0
  15. package/lib/basepagerenderer.js +35 -15
  16. package/lib/config.js +5 -3
  17. package/lib/ebook/ebookpage.js +1 -4
  18. package/lib/ebook/ebooksitegenerator.js +4 -12
  19. package/lib/ebook/glossarypages.js +1 -3
  20. package/lib/ebook/plantbook.js +1 -1
  21. package/lib/files.js +1 -0
  22. package/lib/htmltaxon.js +8 -19
  23. package/lib/index.d.ts +20 -8
  24. package/lib/index.js +4 -2
  25. package/lib/jekyll.js +40 -59
  26. package/lib/markdown.js +3 -5
  27. package/lib/sitegenerator.js +68 -12
  28. package/lib/taxonomy/taxon.js +5 -1
  29. package/lib/tools/cch2.js +37 -18
  30. package/lib/tools/jepsonfamilies.js +1 -1
  31. package/lib/types.js +4 -0
  32. package/lib/utils/eleventyGenerator.js +82 -0
  33. package/lib/utils/htmlFragments.js +19 -0
  34. package/lib/web/glossarypages.js +6 -10
  35. package/lib/web/pageFamily.js +14 -14
  36. package/lib/web/pageGeneric.js +78 -0
  37. package/lib/web/{pagetaxon.js → pageTaxon.js} +4 -4
  38. package/lib/web/pageTaxonList.js +53 -0
  39. package/lib/{pagerenderer.js → web/renderAllPages.js} +38 -80
  40. package/package.json +12 -10
  41. package/scripts/build-site.js +20 -52
  42. package/scripts/cpl-photos.js +61 -24
  43. package/scripts/cpl-tools.js +4 -1
  44. package/static/assets/js/nameSearchData.js +2 -0
  45. package/{jekyll → static}/assets/js/name_search.js +12 -14
  46. package/static/name_search.html +15 -0
  47. package/jekyll/_includes/glossary.html +0 -0
  48. package/jekyll/name_search.html +0 -17
  49. package/lib/genericpage.js +0 -88
  50. /package/{jekyll → generators}/_includes/analytics.html +0 -0
  51. /package/{jekyll → generators}/_includes/menu_extra.html +0 -0
  52. /package/{jekyll → generators/jekyll}/_config.yml +0 -0
  53. /package/{jekyll → generators/jekyll}/_layouts/default.html +0 -0
  54. /package/{jekyll → generators/jekyll}/_layouts/html.html +0 -0
  55. /package/{jekyll → static}/assets/css/main.css +0 -0
  56. /package/{jekyll → static}/assets/js/ui.js +0 -0
  57. /package/{jekyll → static}/assets/js/utils.js +0 -0
  58. /package/{jekyll → static}/index.md +0 -0
@@ -9,6 +9,7 @@ import { existsSync } from "fs";
9
9
  import { CSV } from "../lib/csv.js";
10
10
 
11
11
  const PHOTO_FILE_NAME = "inattaxonphotos.csv";
12
+ const PHOTO_FILE_PATH = `./data/${PHOTO_FILE_NAME}`;
12
13
 
13
14
  const OPT_LOADER = "loader";
14
15
 
@@ -58,27 +59,7 @@ async function addMissingPhotos(options) {
58
59
  errorLog.write();
59
60
 
60
61
  // Write updated photo file.
61
- const headers = ["name", "id", "ext", "licenseCode", "attrName"];
62
- /** @type {string[][]} */
63
- const data = [];
64
- for (const taxonName of [...currentTaxaPhotos.keys()].sort()) {
65
- // @ts-ignore
66
- for (const photo of currentTaxaPhotos.get(taxonName)) {
67
- data.push([
68
- taxonName,
69
- photo.id,
70
- photo.ext,
71
- photo.licenseCode,
72
- photo.attrName ?? "",
73
- ]);
74
- }
75
- }
76
-
77
- CSV.writeFileArray(
78
- `${options.outputdir}/${PHOTO_FILE_NAME}`,
79
- data,
80
- headers,
81
- );
62
+ writePhotos(currentTaxaPhotos);
82
63
  }
83
64
 
84
65
  /**
@@ -128,12 +109,39 @@ async function getTaxa(options) {
128
109
  return taxa;
129
110
  }
130
111
 
112
+ /**
113
+ * @param {{outputdir:string,update:boolean}} options
114
+ */
115
+ async function prune(options) {
116
+ const taxa = await getTaxa(options);
117
+ const errorLog = new ErrorLog(options.outputdir + "/log.tsv", true);
118
+ const currentTaxaPhotos = readPhotos();
119
+
120
+ const invalidNames = new Set();
121
+
122
+ for (const name of currentTaxaPhotos.keys()) {
123
+ const taxon = taxa.getTaxon(name);
124
+ if (!taxon) {
125
+ errorLog.log(name, `is in ${PHOTO_FILE_NAME} but not in taxa list`);
126
+ invalidNames.add(name);
127
+ }
128
+ }
129
+
130
+ if (options.update) {
131
+ for (const name of invalidNames) {
132
+ currentTaxaPhotos.delete(name);
133
+ }
134
+ writePhotos(currentTaxaPhotos);
135
+ }
136
+
137
+ errorLog.write();
138
+ }
139
+
131
140
  /**
132
141
  * @returns {Map<string,import("../lib/utils/inat-tools.js").InatPhotoInfo[]>}
133
142
  */
134
143
  function readPhotos() {
135
- const photosFileName = `./data/${PHOTO_FILE_NAME}`;
136
- if (!existsSync(photosFileName)) {
144
+ if (!existsSync(PHOTO_FILE_PATH)) {
137
145
  return new Map();
138
146
  }
139
147
 
@@ -142,7 +150,7 @@ function readPhotos() {
142
150
 
143
151
  /** @type {import("../lib/utils/inat-tools.js").InatCsvPhoto[]} */
144
152
  // @ts-ignore
145
- const csvPhotos = CSV.readFile(photosFileName);
153
+ const csvPhotos = CSV.readFile(PHOTO_FILE_PATH);
146
154
  for (const csvPhoto of csvPhotos) {
147
155
  const taxonName = csvPhoto.name;
148
156
  let photos = taxonPhotos.get(taxonName);
@@ -161,6 +169,30 @@ function readPhotos() {
161
169
  return taxonPhotos;
162
170
  }
163
171
 
172
+ /**
173
+ * @param {Map<string,import("../lib/utils/inat-tools.js").InatPhotoInfo[]>} currentTaxaPhotos
174
+ */
175
+ function writePhotos(currentTaxaPhotos) {
176
+ // Write updated photo file.
177
+ const headers = ["name", "id", "ext", "licenseCode", "attrName"];
178
+ /** @type {string[][]} */
179
+ const data = [];
180
+ for (const taxonName of [...currentTaxaPhotos.keys()].sort()) {
181
+ // @ts-ignore - should always be defined at this point
182
+ for (const photo of currentTaxaPhotos.get(taxonName)) {
183
+ data.push([
184
+ taxonName,
185
+ photo.id,
186
+ photo.ext,
187
+ photo.licenseCode,
188
+ photo.attrName ?? "",
189
+ ]);
190
+ }
191
+ }
192
+
193
+ CSV.writeFileArray(PHOTO_FILE_PATH, data, headers);
194
+ }
195
+
164
196
  const program = Program.getProgram();
165
197
  program
166
198
  .command("checkmissing")
@@ -176,9 +208,14 @@ if (process.env.npm_package_name === "@ca-plant-list/ca-plant-list") {
176
208
  .command("addmissing")
177
209
  .description("Add photos to taxa with fewer than the maximum")
178
210
  .action(() => addMissingPhotos(program.opts()));
211
+ program
212
+ .command("prune")
213
+ .description("Remove photos without valid taxon names")
214
+ .action(() => prune(program.opts()));
179
215
  }
180
216
  program.option(
181
217
  "--loader <path>",
182
218
  "The path (relative to the current directory) of the JavaScript file containing the TaxaLoader class. If not provided, the default TaxaLoader will be used.",
183
219
  );
220
+ program.option("--update", "Update the file if possible.");
184
221
  await program.parseAsync();
@@ -58,10 +58,13 @@ async function build(program, options) {
58
58
 
59
59
  const exceptions = new Exceptions(options.datadir);
60
60
  const config = new Config(options.datadir);
61
- const taxa = await Taxa.loadTaxa(options);
62
61
 
63
62
  const errorLog = new ErrorLog(options.outputdir + "/log.tsv", true);
64
63
  for (const tool of tools) {
64
+ const taxa =
65
+ tool === TOOLS.JEPSON_FAM
66
+ ? undefined
67
+ : await Taxa.loadTaxa(options);
65
68
  switch (tool) {
66
69
  case TOOLS.CALFLORA:
67
70
  await Calflora.analyze(
@@ -0,0 +1,2 @@
1
+ /** @type {import("../../../lib/types.js").NameSearchData[]} */
2
+ export const NAMES = [];
@@ -1,8 +1,8 @@
1
+ import { NAMES } from "./nameSearchData.js";
1
2
  import { Utils } from "./utils.js";
2
3
 
3
4
  /**
4
- * @typedef {[string]|[string,string]|[string,string|undefined,string[]]} RawSearchData
5
- * @typedef {{raw:RawSearchData,searchSci:string,searchCommon?:string,synonyms?:string[]}} SearchData
5
+ * @typedef {{raw:import("../../../lib/types.js").NameSearchData,searchSci:string,searchCommon?:string,synonyms?:string[]}} SearchData
6
6
  */
7
7
 
8
8
  const MIN_LEN = 2;
@@ -52,9 +52,9 @@ class Search {
52
52
  // Include any matching synonyms.
53
53
  for (const index of syns) {
54
54
  matches.push([
55
- taxon.raw[0],
56
- taxon.raw[1],
57
- taxon.raw[2] ? taxon.raw[2][index] : undefined,
55
+ taxon.raw.t,
56
+ taxon.raw.c,
57
+ taxon.raw.s ? taxon.raw.s[index] : undefined,
58
58
  ]);
59
59
  }
60
60
  } else {
@@ -62,7 +62,7 @@ class Search {
62
62
  const namesMatch =
63
63
  name.includes(value) || (cn && cn.includes(value));
64
64
  if (namesMatch) {
65
- matches.push([taxon.raw[0], taxon.raw[1]]);
65
+ matches.push([taxon.raw.t, taxon.raw.c]);
66
66
  }
67
67
  }
68
68
  }
@@ -146,23 +146,21 @@ class Search {
146
146
  /** @type {SearchData[]} */
147
147
  const searchData = [];
148
148
 
149
- /** @type {RawSearchData[]} */
150
- // @ts-ignore
151
- // eslint-disable-next-line no-undef
149
+ /** @type {import("../../../lib/types.js").NameSearchData[]} */
152
150
  const names = NAMES;
153
151
 
154
152
  for (const taxon of names) {
155
153
  /** @type {SearchData} */
156
154
  const taxonData = {
157
155
  raw: taxon,
158
- searchSci: this.#normalizeName(taxon[0]),
156
+ searchSci: this.#normalizeName(taxon.t),
159
157
  };
160
- if (taxon[1]) {
161
- taxonData.searchCommon = taxon[1].toLowerCase();
158
+ if (taxon.c) {
159
+ taxonData.searchCommon = taxon.c.toLowerCase();
162
160
  }
163
- if (taxon[2]) {
161
+ if (taxon.s) {
164
162
  const syns = [];
165
- for (const syn of taxon[2]) {
163
+ for (const syn of taxon.s) {
166
164
  syns.push(this.#normalizeName(syn));
167
165
  }
168
166
  taxonData.synonyms = syns;
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Name Search
3
+ js: name_search.js
4
+ ---
5
+
6
+ <form id="search_form">
7
+ <input type="text" id="name" />
8
+ <label for="name"
9
+ >Search for scientific name, common name, or synonym.</label
10
+ >
11
+ </form>
12
+
13
+ <div class="section" id="message"></div>
14
+
15
+ <table id="results"></table>
File without changes
@@ -1,17 +0,0 @@
1
- ---
2
- title: Name Search
3
- js: name_search.js
4
- ---
5
-
6
- <script>
7
- const NAMES = {% include names.json %};
8
- </script>
9
-
10
- <form id="search_form">
11
- <input type="text" id="name">
12
- <label for="name">Search for scientific name, common name, or synonym.</label>
13
- </form>
14
-
15
- <div class="section" id="message"></div>
16
-
17
- <table id="results"></table>
@@ -1,88 +0,0 @@
1
- import { Config } from "./config.js";
2
- import { Files } from "./files.js";
3
- import { HTML } from "./html.js";
4
- import { Jekyll } from "./jekyll.js";
5
- import { Markdown } from "./markdown.js";
6
-
7
- class GenericPage {
8
- #outputDir;
9
- #title;
10
- #baseFileName;
11
- #js;
12
-
13
- /**
14
- * @param {string} outputDir
15
- * @param {string} title
16
- * @param {string} baseFileName
17
- * @param {string} [js]
18
- */
19
- constructor(outputDir, title, baseFileName, js) {
20
- this.#outputDir = outputDir;
21
- this.#title = title;
22
- this.#baseFileName = baseFileName;
23
- this.#js = js;
24
- }
25
-
26
- getBaseFileName() {
27
- return this.#baseFileName;
28
- }
29
-
30
- getDefaultIntro() {
31
- let html = this.getFrontMatter();
32
- return html + this.getMarkdown();
33
- }
34
-
35
- getFrontMatter() {
36
- return (
37
- "---\n" +
38
- 'title: "' +
39
- this.#title +
40
- '"\n' +
41
- (this.#js ? "js: " + this.#js + "\n" : "") +
42
- "---\n"
43
- );
44
- }
45
-
46
- getMarkdown() {
47
- // Include site-specific markdown.
48
- let html = this.#getMarkdown("intros");
49
-
50
- // Include package markdown.
51
- const mdPath =
52
- Config.getPackageDir() + "/data/text/" + this.#baseFileName + ".md";
53
- const markdownHTML = Markdown.fileToHTML(mdPath);
54
- if (markdownHTML) {
55
- html += HTML.wrap("div", markdownHTML, { class: "section" });
56
- }
57
-
58
- return html;
59
- }
60
-
61
- /**
62
- * @param {string} path
63
- */
64
- #getMarkdown(path) {
65
- const textPath = path + "/" + this.#baseFileName + ".md";
66
- if (!Jekyll.hasInclude(this.#outputDir, textPath)) {
67
- return "";
68
- }
69
- return HTML.wrap("div", Jekyll.include(textPath), { class: "section" });
70
- }
71
-
72
- getOutputDir() {
73
- return this.#outputDir;
74
- }
75
-
76
- getTitle() {
77
- return this.#title;
78
- }
79
-
80
- /**
81
- * @param {string} html
82
- */
83
- writeFile(html) {
84
- Files.write(this.#outputDir + "/" + this.#baseFileName + ".html", html);
85
- }
86
- }
87
-
88
- export { GenericPage };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes