@ca-plant-list/ca-plant-list 0.4.8 → 0.4.10
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/.github/workflows/main.yml +20 -0
- package/.vscode/settings.json +7 -2
- package/data/glossary/perianth.md +1 -0
- package/data/inattaxonphotos.csv +68 -4
- package/data/taxa.csv +5 -3
- package/data/text/Toxicoscordion-fremontii.md +1 -0
- package/data/text/Toxicoscordion-paniculatum.md +1 -0
- package/data/text/Toxicoscordion-venenosum-var-venenosum.md +1 -0
- package/ebook/css/main.css +9 -1
- package/eslint.config.mjs +9 -9
- package/lib/csv.js +27 -5
- package/lib/ebook/images.js +79 -52
- package/lib/ebook/pages/taxonpage.js +25 -27
- package/lib/ebook/plantbook.js +147 -139
- package/lib/htmltaxon.js +124 -117
- package/lib/inat_photo.js +15 -15
- package/lib/progressmeter.js +29 -0
- package/lib/taxa.js +23 -22
- package/lib/util.js +4 -3
- package/lib/utils/inat-tools.js +90 -0
- package/lib/web/pagetaxon.js +199 -190
- package/package.json +4 -3
- package/scripts/cpl-photos.js +179 -0
- package/tmp/config.json +21 -0
- package/tmp/exceptions.json +93 -0
- package/tmp/families.json +790 -0
- package/tmp/genera.json +5566 -0
- package/tmp/inattaxonphotos.csv +6059 -0
- package/tmp/synonyms.csv +2141 -0
- package/tmp/taxa_include.csv +19 -0
- package/types/classes.d.ts +21 -20
- package/data/photos.csv +0 -9
- package/lib/ebook/taxonimage.js +0 -23
package/lib/web/pagetaxon.js
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
145
|
+
#getSynonyms() {
|
146
|
+
return this.#taxon.getSynonyms();
|
147
|
+
}
|
139
148
|
|
140
|
-
|
141
|
-
|
149
|
+
render() {
|
150
|
+
let html = this.getFrontMatter();
|
142
151
|
|
143
|
-
|
152
|
+
html += '<div class="wrapper">';
|
144
153
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
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
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
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.
|
3
|
+
"version": "0.4.10",
|
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
|
+
"cpl-photos": "scripts/cpl-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.
|
29
|
+
"sharp": "^0.33.5",
|
29
30
|
"svgo-ll": "^5.6.0",
|
30
31
|
"unzipper": "^0.10.11"
|
31
32
|
},
|
@@ -0,0 +1,179 @@
|
|
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
|
+
import { getTaxonPhotos } from "../lib/utils/inat-tools.js";
|
8
|
+
import { existsSync } from "fs";
|
9
|
+
import { CSV } from "../lib/csv.js";
|
10
|
+
|
11
|
+
const PHOTO_FILE_NAME = "inattaxonphotos.csv";
|
12
|
+
|
13
|
+
const OPT_LOADER = "loader";
|
14
|
+
|
15
|
+
const MAX_PHOTOS = 5;
|
16
|
+
|
17
|
+
/**
|
18
|
+
* @param {import("commander").OptionValues} options
|
19
|
+
*/
|
20
|
+
async function addMissingPhotos(options) {
|
21
|
+
const taxaMissingPhotos = [];
|
22
|
+
|
23
|
+
const taxa = await getTaxa(options);
|
24
|
+
const errorLog = new ErrorLog(options.outputdir + "/log.tsv", true);
|
25
|
+
|
26
|
+
for (const taxon of taxa.getTaxonList()) {
|
27
|
+
const photos = taxon.getPhotos();
|
28
|
+
if (photos.length < MAX_PHOTOS) {
|
29
|
+
taxaMissingPhotos.push(taxon);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
const newPhotos = await getTaxonPhotos(taxaMissingPhotos);
|
34
|
+
const currentTaxaPhotos = readPhotos();
|
35
|
+
|
36
|
+
for (const [taxonName, photos] of newPhotos) {
|
37
|
+
let currentPhotos = currentTaxaPhotos.get(taxonName);
|
38
|
+
if (!currentPhotos) {
|
39
|
+
currentPhotos = [];
|
40
|
+
currentTaxaPhotos.set(taxonName, currentPhotos);
|
41
|
+
}
|
42
|
+
for (const photo of photos) {
|
43
|
+
if (currentPhotos.length === MAX_PHOTOS) {
|
44
|
+
break;
|
45
|
+
}
|
46
|
+
if (
|
47
|
+
currentPhotos.some(
|
48
|
+
(currentPhoto) => currentPhoto.id === photo.id,
|
49
|
+
)
|
50
|
+
) {
|
51
|
+
continue;
|
52
|
+
}
|
53
|
+
currentPhotos.push(photo);
|
54
|
+
errorLog.log("adding photo", taxonName, photo.id);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
errorLog.write();
|
59
|
+
|
60
|
+
// 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.writeFile(`${options.outputdir}/${PHOTO_FILE_NAME}`, data, headers);
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* @param {import("commander").OptionValues} options
|
82
|
+
* @param {import("commander").OptionValues} commandOptions
|
83
|
+
*/
|
84
|
+
async function checkmissing(options, commandOptions) {
|
85
|
+
const taxa = await getTaxa(options);
|
86
|
+
const errorLog = new ErrorLog(options.outputdir + "/log.tsv", true);
|
87
|
+
|
88
|
+
const minPhotos = commandOptions.minphotos;
|
89
|
+
for (const taxon of taxa.getTaxonList()) {
|
90
|
+
const photos = taxon.getPhotos();
|
91
|
+
if (
|
92
|
+
minPhotos === undefined
|
93
|
+
? photos.length !== MAX_PHOTOS
|
94
|
+
: photos.length < minPhotos
|
95
|
+
) {
|
96
|
+
errorLog.log(taxon.getName(), photos.length.toString());
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
errorLog.write();
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* @param {import("commander").OptionValues} options
|
105
|
+
* @return {Promise<Taxa>}
|
106
|
+
*/
|
107
|
+
async function getTaxa(options) {
|
108
|
+
const errorLog = new ErrorLog(options.outputdir + "/errors.tsv", true);
|
109
|
+
|
110
|
+
const loader = options[OPT_LOADER];
|
111
|
+
let taxa;
|
112
|
+
if (loader) {
|
113
|
+
const taxaLoaderClass = await import("file:" + path.resolve(loader));
|
114
|
+
taxa = await taxaLoaderClass.TaxaLoader.loadTaxa(options, errorLog);
|
115
|
+
} else {
|
116
|
+
taxa = new Taxa(
|
117
|
+
Program.getIncludeList(options.datadir),
|
118
|
+
errorLog,
|
119
|
+
options.showFlowerErrors,
|
120
|
+
);
|
121
|
+
}
|
122
|
+
|
123
|
+
errorLog.write();
|
124
|
+
return taxa;
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* @returns {Map<string,InatPhotoInfo[]>}
|
129
|
+
*/
|
130
|
+
function readPhotos() {
|
131
|
+
const photosFileName = `./data/${PHOTO_FILE_NAME}`;
|
132
|
+
if (!existsSync(photosFileName)) {
|
133
|
+
return new Map();
|
134
|
+
}
|
135
|
+
|
136
|
+
/** @type {Map<string,{id:string,ext:string,licenseCode:string,attrName:string}[]>} */
|
137
|
+
const taxonPhotos = new Map();
|
138
|
+
|
139
|
+
/** @type {InatCsvPhoto[]} */
|
140
|
+
const csvPhotos = CSV.readFile(photosFileName);
|
141
|
+
for (const csvPhoto of csvPhotos) {
|
142
|
+
const taxonName = csvPhoto.name;
|
143
|
+
let photos = taxonPhotos.get(taxonName);
|
144
|
+
if (!photos) {
|
145
|
+
photos = [];
|
146
|
+
taxonPhotos.set(taxonName, photos);
|
147
|
+
}
|
148
|
+
photos.push({
|
149
|
+
id: csvPhoto.id.toString(),
|
150
|
+
ext: csvPhoto.ext,
|
151
|
+
licenseCode: csvPhoto.licenseCode,
|
152
|
+
attrName: csvPhoto.attrName,
|
153
|
+
});
|
154
|
+
}
|
155
|
+
|
156
|
+
return taxonPhotos;
|
157
|
+
}
|
158
|
+
|
159
|
+
const program = Program.getProgram();
|
160
|
+
program
|
161
|
+
.command("checkmissing")
|
162
|
+
.description("List taxa with less than the maximum number of photos")
|
163
|
+
.option(
|
164
|
+
"--minphotos <number>",
|
165
|
+
"Minimum number of photos. Taxa with fewer than this number will be listed.",
|
166
|
+
)
|
167
|
+
.action((options) => checkmissing(program.opts(), options));
|
168
|
+
if (process.env.npm_package_name === "@ca-plant-list/ca-plant-list") {
|
169
|
+
// Only allow updates in ca-plant-list.
|
170
|
+
program
|
171
|
+
.command("addmissing")
|
172
|
+
.description("Add photos to taxa with fewer than the maximum")
|
173
|
+
.action(() => addMissingPhotos(program.opts()));
|
174
|
+
}
|
175
|
+
program.option(
|
176
|
+
"--loader <path>",
|
177
|
+
"The path (relative to the current directory) of the JavaScript file containing the TaxaLoader class. If not provided, the default TaxaLoader will be used.",
|
178
|
+
);
|
179
|
+
await program.parseAsync();
|
package/tmp/config.json
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"calflora": {
|
3
|
+
"counties": [
|
4
|
+
"ALA",
|
5
|
+
"CCA"
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"inat": {
|
9
|
+
"project": "ebcnps"
|
10
|
+
},
|
11
|
+
"labels": {
|
12
|
+
"introduced": "Introduced to the East Bay",
|
13
|
+
"native": "Native to the East Bay",
|
14
|
+
"status-NC": "California native introduced to the East Bay"
|
15
|
+
},
|
16
|
+
"ebook": {
|
17
|
+
"filename": "ebplants",
|
18
|
+
"pub_id": "ebplants",
|
19
|
+
"title": "East Bay Plants"
|
20
|
+
}
|
21
|
+
}
|