@ca-plant-list/ca-plant-list 0.4.6 → 0.4.9

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.
@@ -8,197 +8,206 @@ import { Markdown } from "../markdown.js";
8
8
  import { Config } from "../config.js";
9
9
 
10
10
  class PageTaxon extends GenericPage {
11
- #config;
12
- #taxon;
13
-
14
- /**
15
- * @param {string} outputDir
16
- * @param {Config} config
17
- * @param {Taxon} taxon
18
- */
19
- constructor(outputDir, config, taxon) {
20
- super(outputDir, taxon.getName(), taxon.getBaseFileName());
21
- this.#config = config;
22
- this.#taxon = taxon;
23
- }
24
-
25
- #getInfoLinks() {
26
- const links = [];
27
- const jepsonID = this.#taxon.getJepsonID();
28
- if (jepsonID) {
29
- links.push(Jepson.getEFloraLink(jepsonID));
11
+ #config;
12
+ #taxon;
13
+
14
+ /**
15
+ * @param {string} outputDir
16
+ * @param {Config} config
17
+ * @param {Taxon} taxon
18
+ */
19
+ constructor(outputDir, config, taxon) {
20
+ super(outputDir, taxon.getName(), taxon.getBaseFileName());
21
+ this.#config = config;
22
+ this.#taxon = taxon;
30
23
  }
31
- const cfLink = this.#taxon.getCalfloraTaxonLink();
32
- if (cfLink) {
33
- links.push(cfLink);
34
- }
35
- const iNatLink = this.#taxon.getINatTaxonLink();
36
- if (iNatLink) {
37
- links.push(iNatLink);
38
- }
39
- const rpiLink = this.#taxon.getRPITaxonLink();
40
- if (rpiLink) {
41
- links.push(rpiLink);
42
- }
43
- return links;
44
- }
45
-
46
- #getObsLinks() {
47
- const links = [];
48
- links.push(
49
- HTML.getLink(
50
- "https://www.calflora.org/entry/observ.html?track=m#srch=t&grezc=5&cols=b&lpcli=t&cc=" +
51
- this.#config.getCountyCodes().join("!") +
52
- "&incobs=f&taxon=" +
53
- this.#taxon.getCalfloraName().replaceAll(" ", "+"),
54
- "Calflora",
55
- {},
56
- true
57
- )
58
- );
59
- const iNatID = this.#taxon.getINatID();
60
- if (iNatID) {
61
- const projectId = links.push(
62
- HTML.getLink(
63
- ExternalSites.getInatObsLink({
64
- project_id: this.#config.getConfigValue("inat", "project_id"),
65
- subview: "map",
66
- taxon_id: iNatID,
67
- }),
68
- "iNaturalist",
69
- {},
70
- true
71
- )
72
- );
24
+
25
+ #getInfoLinks() {
26
+ const links = [];
27
+ const jepsonID = this.#taxon.getJepsonID();
28
+ if (jepsonID) {
29
+ links.push(Jepson.getEFloraLink(jepsonID));
30
+ }
31
+ const cfLink = this.#taxon.getCalfloraTaxonLink();
32
+ if (cfLink) {
33
+ links.push(cfLink);
34
+ }
35
+ const iNatLink = this.#taxon.getINatTaxonLink();
36
+ if (iNatLink) {
37
+ links.push(iNatLink);
38
+ }
39
+ const rpiLink = this.#taxon.getRPITaxonLink();
40
+ if (rpiLink) {
41
+ links.push(rpiLink);
42
+ }
43
+ return links;
73
44
  }
74
45
 
75
- return links;
76
- }
77
-
78
- /**
79
- * @param {string[]} list
80
- * @param {string} header
81
- * @param {string} className
82
- */
83
- #getListSectionHTML(list, header, className) {
84
- let html = "";
85
- if (list.length > 0) {
86
- html += '<div class="section nobullet ' + className + '">';
87
- html += HTML.textElement("h2", header);
88
- html += "<ul>";
89
- html += HTML.arrayToLI(list);
90
- html += "</ul>";
91
- html += "</div>";
46
+ #getObsLinks() {
47
+ const links = [];
48
+ links.push(
49
+ HTML.getLink(
50
+ "https://www.calflora.org/entry/observ.html?track=m#srch=t&grezc=5&cols=b&lpcli=t&cc=" +
51
+ this.#config.getCountyCodes().join("!") +
52
+ "&incobs=f&taxon=" +
53
+ this.#taxon.getCalfloraName().replaceAll(" ", "+"),
54
+ "Calflora",
55
+ {},
56
+ true,
57
+ ),
58
+ );
59
+ const iNatID = this.#taxon.getINatID();
60
+ if (iNatID) {
61
+ links.push(
62
+ HTML.getLink(
63
+ ExternalSites.getInatObsLink({
64
+ project_id: this.#config.getConfigValue(
65
+ "inat",
66
+ "project_id",
67
+ ),
68
+ subview: "map",
69
+ taxon_id: iNatID,
70
+ }),
71
+ "iNaturalist",
72
+ {},
73
+ true,
74
+ ),
75
+ );
76
+ }
77
+
78
+ return links;
92
79
  }
93
- return html;
94
- }
95
80
 
96
- #getRarityInfo() {
97
- const cnpsRank = this.#taxon.getRPIRankAndThreat();
98
- if (!cnpsRank) {
99
- return "";
81
+ /**
82
+ * @param {string[]} list
83
+ * @param {string} header
84
+ * @param {string} className
85
+ */
86
+ #getListSectionHTML(list, header, className) {
87
+ let html = "";
88
+ if (list.length > 0) {
89
+ html += '<div class="section nobullet ' + className + '">';
90
+ html += HTML.textElement("h2", header);
91
+ html += "<ul>";
92
+ html += HTML.arrayToLI(list);
93
+ html += "</ul>";
94
+ html += "</div>";
95
+ }
96
+ return html;
100
97
  }
101
- const ranks = [];
102
-
103
- ranks.push(
104
- HTML.textElement("span", "CNPS Rare Plant Rank:", {
105
- class: "label",
106
- }) + HTML.getToolTip(cnpsRank, this.#taxon.getRPIRankAndThreatTooltip())
107
- );
108
- if (this.#taxon.getCESA()) {
109
- ranks.push(
110
- HTML.textElement("span", "CESA:", { class: "label" }) +
111
- RarePlants.getCESADescription(this.#taxon.getCESA())
112
- );
98
+
99
+ #getRarityInfo() {
100
+ const cnpsRank = this.#taxon.getRPIRankAndThreat();
101
+ if (!cnpsRank) {
102
+ return "";
103
+ }
104
+ const ranks = [];
105
+
106
+ ranks.push(
107
+ HTML.textElement("span", "CNPS Rare Plant Rank:", {
108
+ class: "label",
109
+ }) +
110
+ HTML.getToolTip(
111
+ cnpsRank,
112
+ this.#taxon.getRPIRankAndThreatTooltip(),
113
+ ),
114
+ );
115
+ if (this.#taxon.getCESA()) {
116
+ ranks.push(
117
+ HTML.textElement("span", "CESA:", { class: "label" }) +
118
+ RarePlants.getCESADescription(this.#taxon.getCESA()),
119
+ );
120
+ }
121
+
122
+ return HTML.wrap("div", "<ul>" + HTML.arrayToLI(ranks) + "</ul>", {
123
+ class: "section",
124
+ });
113
125
  }
114
126
 
115
- return HTML.wrap("div", "<ul>" + HTML.arrayToLI(ranks) + "</ul>", {
116
- class: "section",
117
- });
118
- }
119
-
120
- #getRelatedTaxaLinks() {
121
- const links = [];
122
- const genus = this.#taxon.getGenus();
123
- if (genus) {
124
- const taxa = genus.getTaxa();
125
- if (taxa.length > 1) {
126
- for (const taxon of taxa) {
127
- links.push(
128
- taxon.getHTMLLink(taxon.getName() !== this.#taxon.getName())
129
- );
127
+ #getRelatedTaxaLinks() {
128
+ const links = [];
129
+ const genus = this.#taxon.getGenus();
130
+ if (genus) {
131
+ const taxa = genus.getTaxa();
132
+ if (taxa.length > 1) {
133
+ for (const taxon of taxa) {
134
+ links.push(
135
+ taxon.getHTMLLink(
136
+ taxon.getName() !== this.#taxon.getName(),
137
+ ),
138
+ );
139
+ }
140
+ }
130
141
  }
131
- }
142
+ return links;
132
143
  }
133
- return links;
134
- }
135
144
 
136
- #getSynonyms() {
137
- return this.#taxon.getSynonyms();
138
- }
145
+ #getSynonyms() {
146
+ return this.#taxon.getSynonyms();
147
+ }
139
148
 
140
- render() {
141
- let html = this.getFrontMatter();
149
+ render() {
150
+ let html = this.getFrontMatter();
142
151
 
143
- html += '<div class="wrapper">';
152
+ html += '<div class="wrapper">';
144
153
 
145
- const cn = this.#taxon.getCommonNames();
146
- if (cn.length > 0) {
147
- html += HTML.textElement("div", cn.join(", "), {
148
- class: "section common-names",
149
- });
150
- }
154
+ const cn = this.#taxon.getCommonNames();
155
+ if (cn.length > 0) {
156
+ html += HTML.textElement("div", cn.join(", "), {
157
+ class: "section common-names",
158
+ });
159
+ }
151
160
 
152
- html += HTML.textElement(
153
- "div",
154
- this.#taxon.getStatusDescription(this.#config),
155
- { class: "section native-status" }
156
- );
157
-
158
- const family = this.#taxon.getFamily();
159
- html += HTML.wrap(
160
- "div",
161
- HTML.textElement("span", "Family:", { class: "label" }) +
162
- HTML.getLink("./" + family.getFileName(), family.getName()),
163
- { class: "section" }
164
- );
165
-
166
- html += this.#getRarityInfo();
167
-
168
- html += "</div>";
169
-
170
- html += HTMLTaxon.getFlowerInfo(this.#taxon, undefined, false);
171
-
172
- html += this.getMarkdown();
173
-
174
- html += '<div class="grid borders">';
175
- html += this.#getListSectionHTML(
176
- this.#getInfoLinks(),
177
- "References",
178
- "info"
179
- );
180
- html += this.#getListSectionHTML(
181
- this.#getObsLinks(),
182
- "Observations",
183
- "obs"
184
- );
185
- html += this.#getListSectionHTML(
186
- this.#getRelatedTaxaLinks(),
187
- "Related Species",
188
- "rel-taxa"
189
- );
190
- html += this.#getListSectionHTML(
191
- this.#getSynonyms(),
192
- "Synonyms",
193
- "synonyms"
194
- );
195
- html += "</div>";
196
-
197
- const photos = this.#taxon.getPhotos( );
198
- if ( photos.length > 0 ) {
199
- let photosHtml = "";
200
- for ( const photo of photos ) {
201
- photosHtml += `
161
+ html += HTML.textElement(
162
+ "div",
163
+ this.#taxon.getStatusDescription(this.#config),
164
+ { class: "section native-status" },
165
+ );
166
+
167
+ const family = this.#taxon.getFamily();
168
+ html += HTML.wrap(
169
+ "div",
170
+ HTML.textElement("span", "Family:", { class: "label" }) +
171
+ HTML.getLink("./" + family.getFileName(), family.getName()),
172
+ { class: "section" },
173
+ );
174
+
175
+ html += this.#getRarityInfo();
176
+
177
+ html += "</div>";
178
+
179
+ html += HTMLTaxon.getFlowerInfo(this.#taxon, undefined, false);
180
+
181
+ html += this.getMarkdown();
182
+
183
+ html += '<div class="grid borders">';
184
+ html += this.#getListSectionHTML(
185
+ this.#getInfoLinks(),
186
+ "References",
187
+ "info",
188
+ );
189
+ html += this.#getListSectionHTML(
190
+ this.#getObsLinks(),
191
+ "Observations",
192
+ "obs",
193
+ );
194
+ html += this.#getListSectionHTML(
195
+ this.#getRelatedTaxaLinks(),
196
+ "Related Species",
197
+ "rel-taxa",
198
+ );
199
+ html += this.#getListSectionHTML(
200
+ this.#getSynonyms(),
201
+ "Synonyms",
202
+ "synonyms",
203
+ );
204
+ html += "</div>";
205
+
206
+ const photos = this.#taxon.getPhotos();
207
+ if (photos.length > 0) {
208
+ let photosHtml = "";
209
+ for (const photo of photos) {
210
+ photosHtml += `
202
211
  <figure class="col">
203
212
  <a href="${photo.getSourceUrl()}">
204
213
  <img
@@ -213,27 +222,27 @@ class PageTaxon extends GenericPage {
213
222
  </figcaption>
214
223
  </figure>
215
224
  `;
216
- }
217
- html += `
225
+ }
226
+ html += `
218
227
  <h2>Photos</h2>
219
228
  <div class="row">
220
229
  ${photosHtml}
221
230
  </div>
222
231
  `;
223
- }
232
+ }
224
233
 
225
- const footerTextPath =
226
- Config.getPackageDir() +
227
- "/data/text/" +
228
- this.getBaseFileName() +
229
- ".footer.md";
230
- const footerMarkdown = Markdown.fileToHTML(footerTextPath);
231
- if (footerMarkdown) {
232
- html += HTML.wrap("div", footerMarkdown, "section");
233
- }
234
+ const footerTextPath =
235
+ Config.getPackageDir() +
236
+ "/data/text/" +
237
+ this.getBaseFileName() +
238
+ ".footer.md";
239
+ const footerMarkdown = Markdown.fileToHTML(footerTextPath);
240
+ if (footerMarkdown) {
241
+ html += HTML.wrap("div", footerMarkdown, "section");
242
+ }
234
243
 
235
- this.writeFile(html);
236
- }
244
+ this.writeFile(html);
245
+ }
237
246
  }
238
247
 
239
248
  export { PageTaxon };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.4.6",
3
+ "version": "0.4.9",
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": {
@@ -15,7 +15,8 @@
15
15
  "types": "./lib/index.d.ts",
16
16
  "bin": {
17
17
  "ca-plant-list": "scripts/build-site.js",
18
- "ca-plant-book": "scripts/build-ebook.js"
18
+ "ca-plant-book": "scripts/build-ebook.js",
19
+ "photo-tools": "scripts/photos.js"
19
20
  },
20
21
  "dependencies": {
21
22
  "archiver": "^5.3.1",
@@ -25,7 +26,7 @@
25
26
  "csv-stringify": "^6.5.1",
26
27
  "image-size": "^1.1.1",
27
28
  "markdown-it": "^14.1.0",
28
- "sharp": "^0.32.1",
29
+ "sharp": "^0.33.5",
29
30
  "svgo-ll": "^5.6.0",
30
31
  "unzipper": "^0.10.11"
31
32
  },
@@ -37,6 +38,7 @@
37
38
  "@types/unzipper": "^0.10.9",
38
39
  "ajv-cli": "^5.0.0",
39
40
  "eslint": "^9.13.0",
41
+ "prettier": "^3.3.3",
40
42
  "typescript": "^5.6.3"
41
43
  }
42
44
  }
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from "path";
4
+ import { ErrorLog } from "../lib/errorlog.js";
5
+ import { Program } from "../lib/program.js";
6
+ import { Taxa } from "../lib/taxa.js";
7
+
8
+ const OPT_LOADER = "loader";
9
+
10
+ const MAX_PHOTOS = 5;
11
+
12
+ /**
13
+ * @param {import("commander").Command} program
14
+ * @param {import("commander").OptionValues} options
15
+ */
16
+ async function photos(program, options) {
17
+ const taxa = await getTaxa(options);
18
+ const errorLog = new ErrorLog(options.outputdir + "/log.tsv", true);
19
+
20
+ auditTaxaWithoutMaxPhotos(errorLog, taxa);
21
+
22
+ errorLog.write();
23
+ }
24
+
25
+ /**
26
+ * @param {ErrorLog} errorLog
27
+ * @param {Taxa} taxa
28
+ */
29
+ function auditTaxaWithoutMaxPhotos(errorLog, taxa) {
30
+ for (const taxon of taxa.getTaxonList()) {
31
+ const photos = taxon.getPhotos();
32
+ if (photos.length !== MAX_PHOTOS) {
33
+ errorLog.log(taxon.getName(), photos.length.toString());
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @param {import("commander").OptionValues} options
40
+ * @return {Promise<Taxa>}
41
+ */
42
+ async function getTaxa(options) {
43
+ const errorLog = new ErrorLog(options.outputdir + "/errors.tsv", true);
44
+
45
+ const loader = options[OPT_LOADER];
46
+ let taxa;
47
+ if (loader) {
48
+ const taxaLoaderClass = await import("file:" + path.resolve(loader));
49
+ taxa = await taxaLoaderClass.TaxaLoader.loadTaxa(options, errorLog);
50
+ } else {
51
+ taxa = new Taxa(
52
+ Program.getIncludeList(options.datadir),
53
+ errorLog,
54
+ options.showFlowerErrors,
55
+ );
56
+ }
57
+
58
+ errorLog.write();
59
+ return taxa;
60
+ }
61
+
62
+ const program = Program.getProgram();
63
+ program.option(
64
+ "--loader <path>",
65
+ "The path (relative to the current directory) of the JavaScript file containing the TaxaLoader class. If not provided, the default TaxaLoader will be used.",
66
+ );
67
+ program.action((options) => photos(program, options));
68
+ await program.parseAsync();
@@ -9,7 +9,7 @@ declare class Config {
9
9
  prefix: string,
10
10
  name: string,
11
11
  subcategory?: string,
12
- defaultValue?: string
12
+ defaultValue?: string,
13
13
  ): string | undefined;
14
14
  getCountyCodes(): string[];
15
15
  getLabel(name: string, dflt: string): string;
@@ -55,10 +55,6 @@ declare class GlossaryEntry {
55
55
  getTermName(): string;
56
56
  }
57
57
 
58
- declare class Images {
59
- getTaxonImages(name: string): TaxonImage[];
60
- }
61
-
62
58
  declare class InatObsOptions {
63
59
  coords?: [number, number];
64
60
  project_id?: string;
@@ -72,7 +68,7 @@ declare class SiteGenerator {
72
68
  writeTemplate(
73
69
  content: string,
74
70
  attributes: Record<string, string>,
75
- filename: string
71
+ filename: string,
76
72
  ): void;
77
73
  }
78
74
 
@@ -91,7 +87,7 @@ declare class Taxa {
91
87
 
92
88
  declare class TaxaCol {
93
89
  class?: string;
94
- data: (taxon:Taxon)=>string
90
+ data: (taxon: Taxon) => string;
95
91
  title: string;
96
92
  }
97
93
 
@@ -112,7 +108,7 @@ declare class Taxon {
112
108
  getGenusName(): string;
113
109
  getHTMLLink(
114
110
  href: boolean | string | undefined,
115
- includeRPI?: boolean
111
+ includeRPI?: boolean,
116
112
  ): string;
117
113
  getINatID(): string;
118
114
  getINatName(): string;
@@ -149,19 +145,16 @@ declare class TaxonData {
149
145
  taxon_name: string;
150
146
  }
151
147
 
152
- declare class TaxonImage {
153
- getCaption(): string | undefined;
154
- getSrc(): string;
155
- }
156
-
157
- type PhotoRights = "CC0"| "CC BY"| "CC BY-NC"| "C"|null;
148
+ type PhotoRights = "CC0" | "CC BY" | "CC BY-NC" | "C" | null;
158
149
 
159
150
  declare class Photo {
160
151
  url?: string;
161
152
  rightsHolder: null | string;
162
153
  rights?: PhotoRights;
163
- getUrl: ( ) => string;
164
- getSourceUrl: ( ) => string;
154
+ getExt(): string;
155
+ getId(): number;
156
+ getUrl(): string;
157
+ getSourceUrl(): string;
165
158
  }
166
159
 
167
160
  declare class InatPhoto extends Photo {
@@ -169,7 +162,8 @@ declare class InatPhoto extends Photo {
169
162
  ext: string;
170
163
  }
171
164
 
172
- type InatLicenseCode = "cc-by-nc-sa"
165
+ type InatLicenseCode =
166
+ | "cc-by-nc-sa"
173
167
  | "cc-by-nc"
174
168
  | "cc-by-nc-nd"
175
169
  | "cc-by"
@@ -193,8 +187,8 @@ declare class InatApiTaxon {
193
187
  photo: {
194
188
  id: number;
195
189
  attribution: string;
196
- license_code: InatLicenseCode
190
+ license_code: InatLicenseCode;
197
191
  medium_url: string;
198
- }
199
- }[]
192
+ };
193
+ }[];
200
194
  }
package/data/photos.csv DELETED
@@ -1,9 +0,0 @@
1
- taxon_name,source,credit
2
- Calochortus argillosus,https://inaturalist-open-data.s3.amazonaws.com/photos/210002711/original.jpg,"(c) skfork, some rights reserved (CC BY-NC)"
3
- Calochortus luteus,https://inaturalist-open-data.s3.amazonaws.com/photos/257812293/original.jpg,"(c) eeneill, some rights reserved (CC BY-NC)"
4
- Calochortus venustus,https://inaturalist-open-data.s3.amazonaws.com/photos/268338027/original.jpg,"(c) Gena Bentall, some rights reserved (CC BY-NC)"
5
- Ceanothus cuneatus,https://inaturalist-open-data.s3.amazonaws.com/photos/266280254/original.jpg,"(c) Ixchel Gonzalez-Ramirez, some rights reserved (CC BY-NC)"
6
- Claytonia gypsophiloides,https://inaturalist-open-data.s3.amazonaws.com/photos/268277390/original.jpeg,"(c) Christian Naventi, some rights reserved (CC BY-NC)"
7
- Lithophragma heterophyllum,https://inaturalist-open-data.s3.amazonaws.com/photos/104408/medium.jpg,"(c) dloarie, some rights reserved (CC BY)"
8
- Pseudotsuga menziesii var. menziesii,https://inaturalist-open-data.s3.amazonaws.com/photos/214823452/original.jpeg
9
- Pseudotsuga menziesii var. menziesii,https://inaturalist-open-data.s3.amazonaws.com/photos/243900174/original.jpg,"(c) Alan Siegel, some rights reserved (CC BY-NC)"
@@ -1,23 +0,0 @@
1
- class TaxonImage {
2
- #src;
3
- #credit;
4
-
5
- /**
6
- * @param {string} src
7
- * @param {string} credit
8
- */
9
- constructor(src, credit) {
10
- this.#src = src;
11
- this.#credit = credit;
12
- }
13
-
14
- getCaption() {
15
- return this.#credit ? this.#credit : undefined;
16
- }
17
-
18
- getSrc() {
19
- return this.#src;
20
- }
21
- }
22
-
23
- export { TaxonImage };