@ca-plant-list/ca-plant-list 0.1.13 → 0.1.15
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/build-site.js +1 -4
- package/data/genera.json +4 -0
- package/jekyll/assets/css/main.css +5 -2
- package/lib/config.js +13 -9
- package/lib/dataloader.js +1 -7
- package/lib/exceptions.js +13 -9
- package/lib/families.js +9 -1
- package/lib/files.js +20 -6
- package/lib/genera.js +2 -2
- package/lib/genericpage.js +4 -0
- package/lib/index.d.ts +9 -2
- package/lib/pagerenderer.js +11 -31
- package/lib/pagetaxon.js +1 -1
- package/lib/taxa.js +2 -1
- package/lib/taxon.js +4 -0
- package/package.json +3 -1
package/build-site.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
|
3
|
-
import { Config } from "./lib/config.js";
|
4
3
|
import { DataLoader } from "./lib/dataloader.js";
|
5
4
|
import { ErrorLog } from "./lib/errorlog.js";
|
6
5
|
import { PageRenderer } from "./lib/pagerenderer.js";
|
@@ -8,11 +7,9 @@ import commandLineArgs from "command-line-args";
|
|
8
7
|
|
9
8
|
const options = commandLineArgs( DataLoader.getOptionDefs() );
|
10
9
|
|
11
|
-
const CONFIG_DATA_DIR = "./data";
|
12
10
|
const OUTPUT_DIR = "./output";
|
13
11
|
|
14
|
-
Config.init( CONFIG_DATA_DIR );
|
15
12
|
DataLoader.load( options );
|
16
13
|
PageRenderer.render( OUTPUT_DIR );
|
17
14
|
|
18
|
-
ErrorLog.write( OUTPUT_DIR + "/errors.
|
15
|
+
ErrorLog.write( OUTPUT_DIR + "/errors.tsv" );
|
package/data/genera.json
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
/* Defaults */
|
2
2
|
|
3
3
|
:root {
|
4
|
-
|
4
|
+
/* See https://visme.co/blog/website-color-schemes/ theme 4 */
|
5
|
+
--bs-body-bg: #5cdb95;
|
6
|
+
--bs-link-color: #05386b;
|
7
|
+
--pl-navbar-bg: #379683;
|
5
8
|
}
|
6
9
|
|
7
10
|
.navbar-nav {
|
@@ -14,7 +17,7 @@
|
|
14
17
|
--bs-navbar-brand-hover-color: rgba(255, 255, 255, .5);
|
15
18
|
--bs-navbar-brand-color: white;
|
16
19
|
--bs-navbar-nav-link-padding-x: 1rem;
|
17
|
-
background-color:
|
20
|
+
background-color: var(--pl-navbar-bg);
|
18
21
|
}
|
19
22
|
|
20
23
|
span.label {
|
package/lib/config.js
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
import * as path from "node:path";
|
2
2
|
import * as url from "node:url";
|
3
|
-
import
|
3
|
+
import { Files } from "./files.js";
|
4
4
|
|
5
5
|
class Config {
|
6
6
|
|
7
7
|
static #config = {};
|
8
8
|
static #packageDir = path.dirname( path.dirname( url.fileURLToPath( import.meta.url ) ) );
|
9
9
|
|
10
|
+
static {
|
11
|
+
try {
|
12
|
+
this.#config = JSON.parse( Files.read( "./data/config.json" ) );
|
13
|
+
} catch ( e ) {
|
14
|
+
console.log( e );
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
10
18
|
static getConfigValue( prefix, name, subcat, dflt ) {
|
11
19
|
const obj = this.#config[ prefix ];
|
12
20
|
if ( obj ) {
|
@@ -26,6 +34,10 @@ class Config {
|
|
26
34
|
return dflt;
|
27
35
|
}
|
28
36
|
|
37
|
+
static getCountyCodes() {
|
38
|
+
return this.#config[ "counties" ];
|
39
|
+
}
|
40
|
+
|
29
41
|
static getLabel( name, dflt ) {
|
30
42
|
return this.getConfigValue( "labels", name, undefined, dflt );
|
31
43
|
}
|
@@ -34,14 +46,6 @@ class Config {
|
|
34
46
|
return this.#packageDir;
|
35
47
|
}
|
36
48
|
|
37
|
-
static init( dir ) {
|
38
|
-
try {
|
39
|
-
this.#config = JSON.parse( fs.readFileSync( dir + "/config.json" ) );
|
40
|
-
} catch ( e ) {
|
41
|
-
console.log( e );
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
49
|
}
|
46
50
|
|
47
51
|
export { Config };
|
package/lib/dataloader.js
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
import { Taxa } from "./taxa.js";
|
2
|
-
import { Genera } from "./genera.js";
|
3
2
|
import { Families } from "./families.js";
|
4
|
-
import { Config } from "./config.js";
|
5
3
|
import { Exceptions } from "./exceptions.js";
|
6
4
|
|
7
5
|
const OPTION_DEFS = [
|
@@ -15,12 +13,8 @@ class DataLoader {
|
|
15
13
|
}
|
16
14
|
|
17
15
|
static init( taxaDir ) {
|
18
|
-
|
19
|
-
const defaultDataDir = Config.getPackageDir() + "/data";
|
20
|
-
|
21
16
|
Exceptions.init( taxaDir );
|
22
|
-
Families.init(
|
23
|
-
Genera.init( defaultDataDir );
|
17
|
+
Families.init();
|
24
18
|
}
|
25
19
|
|
26
20
|
static load( options ) {
|
package/lib/exceptions.js
CHANGED
@@ -1,35 +1,39 @@
|
|
1
|
-
import
|
1
|
+
import { Files } from "./files.js";
|
2
2
|
|
3
3
|
class Exceptions {
|
4
4
|
|
5
5
|
static #exceptions = {};
|
6
6
|
|
7
|
-
static
|
7
|
+
static getExceptions() {
|
8
|
+
return Object.entries( this.#exceptions );
|
9
|
+
}
|
10
|
+
|
11
|
+
static getValue( name, cat, subcat, defaultValue ) {
|
8
12
|
const taxonData = this.#exceptions[ name ];
|
9
13
|
if ( taxonData ) {
|
10
14
|
const catData = taxonData[ cat ];
|
11
15
|
if ( catData ) {
|
12
|
-
|
16
|
+
const val = catData[ subcat ];
|
17
|
+
return ( val === undefined ) ? defaultValue : val;
|
13
18
|
}
|
14
19
|
}
|
15
|
-
return
|
20
|
+
return defaultValue;
|
16
21
|
}
|
17
22
|
|
18
|
-
static
|
23
|
+
static hasException( name, cat, subcat ) {
|
19
24
|
const taxonData = this.#exceptions[ name ];
|
20
25
|
if ( taxonData ) {
|
21
26
|
const catData = taxonData[ cat ];
|
22
27
|
if ( catData ) {
|
23
|
-
|
24
|
-
return ( val === undefined ) ? defaultValue : val;
|
28
|
+
return catData[ subcat ] !== undefined;
|
25
29
|
}
|
26
30
|
}
|
27
|
-
return
|
31
|
+
return false;
|
28
32
|
}
|
29
33
|
|
30
34
|
static init( dir ) {
|
31
35
|
try {
|
32
|
-
this.#exceptions = JSON.parse(
|
36
|
+
this.#exceptions = JSON.parse( Files.read( dir + "/exceptions.json" ) );
|
33
37
|
} catch ( e ) {
|
34
38
|
console.log( e );
|
35
39
|
}
|
package/lib/families.js
CHANGED
@@ -3,6 +3,8 @@ import { HTML } from "./html.js";
|
|
3
3
|
import { Jepson } from "./jepson.js";
|
4
4
|
import { Taxa } from "./taxa.js";
|
5
5
|
import { Files } from "./files.js";
|
6
|
+
import { Config } from "./config.js";
|
7
|
+
import { Genera } from "./genera.js";
|
6
8
|
|
7
9
|
class Families {
|
8
10
|
|
@@ -12,11 +14,17 @@ class Families {
|
|
12
14
|
return this.#families[ familyName ];
|
13
15
|
}
|
14
16
|
|
15
|
-
static init(
|
17
|
+
static init() {
|
18
|
+
|
19
|
+
const dataDir = Config.getPackageDir() + "/data";
|
20
|
+
|
16
21
|
this.#families = JSON.parse( Files.read( dataDir + "/families.json" ) );
|
17
22
|
for ( const [ k, v ] of Object.entries( this.#families ) ) {
|
18
23
|
this.#families[ k ] = new Family( k, { id: v } );
|
19
24
|
}
|
25
|
+
|
26
|
+
Genera.init( dataDir );
|
27
|
+
|
20
28
|
}
|
21
29
|
|
22
30
|
static renderPages( outputDir, columns ) {
|
package/lib/files.js
CHANGED
@@ -22,11 +22,25 @@ class Files {
|
|
22
22
|
return fs.existsSync( path );
|
23
23
|
}
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
/**
|
26
|
+
* Retrieve data from a URL and write it to a file. If the response status is anything other than 200, an Error is thrown.
|
27
|
+
* @param {string|URL} url
|
28
|
+
* @param {string|undefined} targetFileName If targetFileName is undefined, the data will be retrieved but not written to a file.
|
29
|
+
* @param {Object} [headers={}] Request Headers.
|
30
|
+
* @returns {Headers} The Response headers.
|
31
|
+
*/
|
32
|
+
static async fetch( url, targetFileName, headers = {} ) {
|
33
|
+
const response = await fetch( url, headers );
|
34
|
+
if ( response.status !== 200 ) {
|
35
|
+
throw new Error( response.status + " retrieving " + url );
|
36
|
+
}
|
27
37
|
const data = await response.blob();
|
28
|
-
const
|
29
|
-
|
38
|
+
const arrayBuffer = await data.arrayBuffer();
|
39
|
+
const buffer = Buffer.from( arrayBuffer );
|
40
|
+
if ( targetFileName ) {
|
41
|
+
fs.writeFileSync( targetFileName, buffer );
|
42
|
+
}
|
43
|
+
return response.headers;
|
30
44
|
}
|
31
45
|
|
32
46
|
static mkdir( path ) {
|
@@ -41,8 +55,8 @@ class Files {
|
|
41
55
|
fs.rmSync( dir, { force: true, recursive: true, maxRetries: 2, retryDelay: 1000 } );
|
42
56
|
}
|
43
57
|
|
44
|
-
static write( path, data ) {
|
45
|
-
if ( this.exists( path ) ) {
|
58
|
+
static write( path, data, overwrite = false ) {
|
59
|
+
if ( !overwrite && this.exists( path ) ) {
|
46
60
|
throw new Error( path + " already exists" );
|
47
61
|
}
|
48
62
|
fs.writeFileSync( path, data );
|
package/lib/genera.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import * as fs from "node:fs";
|
2
1
|
import { Families } from "./families.js";
|
2
|
+
import { Files } from "./files.js";
|
3
3
|
|
4
4
|
class Genera {
|
5
5
|
|
@@ -39,7 +39,7 @@ class Genera {
|
|
39
39
|
}
|
40
40
|
|
41
41
|
static init( dataDir ) {
|
42
|
-
this.#genera = JSON.parse(
|
42
|
+
this.#genera = JSON.parse( Files.read( dataDir + "/genera.json" ) );
|
43
43
|
}
|
44
44
|
|
45
45
|
}
|
package/lib/genericpage.js
CHANGED
package/lib/index.d.ts
CHANGED
@@ -2,11 +2,18 @@ export class Files {
|
|
2
2
|
static copyDir(srcDir: any, targetDir: any): void;
|
3
3
|
static createFileFromStream(fileName: any, inStream: any): Promise<any>;
|
4
4
|
static exists(path: any): boolean;
|
5
|
-
|
5
|
+
/**
|
6
|
+
* Retrieve data from a URL and write it to a file. If the response status is anything other than 200, an Error is thrown.
|
7
|
+
* @param {string|URL} url
|
8
|
+
* @param {string|undefined} targetFileName If targetFileName is undefined, the data will be retrieved but not written to a file.
|
9
|
+
* @param {Object} [headers={}] Request Headers.
|
10
|
+
* @returns {Headers} The Response headers.
|
11
|
+
*/
|
12
|
+
static fetch(url: string | URL, targetFileName: string | undefined, headers?: any): Headers;
|
6
13
|
static mkdir(path: any): void;
|
7
14
|
static read(path: any): string;
|
8
15
|
static rmDir(dir: any): void;
|
9
|
-
static write(path: any, data: any): void;
|
16
|
+
static write(path: any, data: any, overwrite?: boolean): void;
|
10
17
|
static zipFileExtract(zipFilePath: any, fileNameToUnzip: any, targetFilePath: any): Promise<void>;
|
11
18
|
}
|
12
19
|
export namespace HTML_OPTIONS {
|
package/lib/pagerenderer.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
import { HTML } from "./html.js";
|
2
2
|
import { Taxa, TAXA_LIST_COLS } from "./taxa.js";
|
3
|
-
import { HTMLPage } from "./htmlpage.js";
|
4
3
|
import { PageTaxon } from "./pagetaxon.js";
|
5
4
|
import { Config } from "./config.js";
|
6
5
|
import { RarePlants } from "./rareplants.js";
|
7
6
|
import { BasePageRenderer } from "./basepagerenderer.js";
|
8
7
|
import { Files } from "./files.js";
|
8
|
+
import { GenericPage } from "./genericpage.js";
|
9
9
|
|
10
10
|
const RPI_CESA = [ TAXA_LIST_COLS.SPECIES, TAXA_LIST_COLS.COMMON_NAME, TAXA_LIST_COLS.CESA ];
|
11
11
|
const RPI_COLUMNS = [ TAXA_LIST_COLS.SPECIES_BARE, TAXA_LIST_COLS.COMMON_NAME, TAXA_LIST_COLS.CNPS_RANK ];
|
@@ -50,7 +50,7 @@ class PageRenderer extends BasePageRenderer {
|
|
50
50
|
Files.write( outputDir + "/inat_" + list.filename + ".txt", iNatTaxa.join( "\n" ) );
|
51
51
|
|
52
52
|
const cols = columns ? columns : list.columns;
|
53
|
-
new PageTaxonList(
|
53
|
+
new PageTaxonList( outputDir, list.name, list.filename ).render( taxa, cols );
|
54
54
|
|
55
55
|
// Check for sublists.
|
56
56
|
const subListHTML = list.listInfo ? getListArray( list.listInfo, { class: "indent" }, cols ) : "";
|
@@ -128,28 +128,6 @@ class PageRenderer extends BasePageRenderer {
|
|
128
128
|
filename: "list_cesa",
|
129
129
|
include: ( t ) => t.getCESA() !== undefined,
|
130
130
|
columns: RPI_CESA,
|
131
|
-
listInfo: [
|
132
|
-
{
|
133
|
-
name: RarePlants.getCESADescription( "CE" ),
|
134
|
-
filename: "list_rpi_ce",
|
135
|
-
include: ( t ) => t.getCESA() === "CE",
|
136
|
-
},
|
137
|
-
{
|
138
|
-
name: RarePlants.getCESADescription( "CT" ),
|
139
|
-
filename: "list_rpi_ct",
|
140
|
-
include: ( t ) => t.getCESA() === "CT",
|
141
|
-
},
|
142
|
-
{
|
143
|
-
name: RarePlants.getCESADescription( "CR" ),
|
144
|
-
filename: "list_rpi_cr",
|
145
|
-
include: ( t ) => t.getCESA() === "CR",
|
146
|
-
},
|
147
|
-
{
|
148
|
-
name: RarePlants.getCESADescription( "CC" ),
|
149
|
-
filename: "list_rpi_cc",
|
150
|
-
include: ( t ) => t.getCESA() === "CC",
|
151
|
-
},
|
152
|
-
]
|
153
131
|
},
|
154
132
|
]
|
155
133
|
},
|
@@ -176,13 +154,15 @@ class PageRenderer extends BasePageRenderer {
|
|
176
154
|
|
177
155
|
}
|
178
156
|
|
179
|
-
class PageTaxonList extends
|
157
|
+
class PageTaxonList extends GenericPage {
|
180
158
|
|
181
|
-
|
159
|
+
constructor( outputDir, title, baseName ) {
|
160
|
+
super( outputDir, title, baseName );
|
161
|
+
}
|
182
162
|
|
183
|
-
|
163
|
+
render( taxa, columns ) {
|
184
164
|
|
185
|
-
html
|
165
|
+
let html = this.getDefaultIntro();
|
186
166
|
|
187
167
|
html += "<div class=\"wrapper\">";
|
188
168
|
|
@@ -193,14 +173,14 @@ class PageTaxonList extends HTMLPage {
|
|
193
173
|
html += "<div class=\"section\">";
|
194
174
|
html += HTML.textElement( "h2", "Download" );
|
195
175
|
html += "<ul>";
|
196
|
-
html += "<li>" + HTML.getLink( "./calflora_" +
|
197
|
-
html += "<li>" + HTML.getLink( "./inat_" +
|
176
|
+
html += "<li>" + HTML.getLink( "./calflora_" + this.getBaseFileName() + ".txt", "Calflora List" ) + "</li>";
|
177
|
+
html += "<li>" + HTML.getLink( "./inat_" + this.getBaseFileName() + ".txt", "iNaturalist List" ) + "</li>";
|
198
178
|
html += "</ul>";
|
199
179
|
html += "</div>";
|
200
180
|
|
201
181
|
html += "</div>";
|
202
182
|
|
203
|
-
this.writeFile(
|
183
|
+
this.writeFile( html );
|
204
184
|
|
205
185
|
}
|
206
186
|
}
|
package/lib/pagetaxon.js
CHANGED
@@ -40,7 +40,7 @@ class PageTaxon extends GenericPage {
|
|
40
40
|
links.push(
|
41
41
|
HTML.getLink(
|
42
42
|
"https://www.calflora.org/entry/observ.html?track=m#srch=t&grezc=5&cols=b&lpcli=t&cc="
|
43
|
-
+ Config.
|
43
|
+
+ Config.getCountyCodes().join( "!" ) + "&incobs=f&taxon="
|
44
44
|
+ this.#taxon.getCalfloraName().replaceAll( " ", "+" ),
|
45
45
|
"Calflora",
|
46
46
|
{},
|
package/lib/taxa.js
CHANGED
@@ -3,6 +3,7 @@ import { ErrorLog } from "./errorlog.js";
|
|
3
3
|
import { HTML } from "./html.js";
|
4
4
|
import { CSV } from "./csv.js";
|
5
5
|
import { RarePlants } from "./rareplants.js";
|
6
|
+
import { Exceptions } from "./exceptions.js";
|
6
7
|
|
7
8
|
const TAXA_LIST_COLS = {
|
8
9
|
CESA: {
|
@@ -95,7 +96,7 @@ class Taxa {
|
|
95
96
|
row[ "CRPR" ],
|
96
97
|
row[ "CESA" ]
|
97
98
|
);
|
98
|
-
if ( !jepsonID ) {
|
99
|
+
if ( !jepsonID && !Exceptions.hasException( name, "jepson", "badjepsonid" ) ) {
|
99
100
|
ErrorLog.log( name, "has no Jepson ID" );
|
100
101
|
}
|
101
102
|
break;
|
package/lib/taxon.js
CHANGED
@@ -192,6 +192,10 @@ class Taxon {
|
|
192
192
|
return this.#status === "N" || this.#status === "NC";
|
193
193
|
}
|
194
194
|
|
195
|
+
/**
|
196
|
+
* Determine whether a species is a local native.
|
197
|
+
* @returns {boolean} true if taxon is a local native; false if not a CA native, or native elsewhere in CA.
|
198
|
+
*/
|
195
199
|
isNative() {
|
196
200
|
return this.#status === "N";
|
197
201
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ca-plant-list/ca-plant-list",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.15",
|
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": {
|
@@ -11,6 +11,7 @@
|
|
11
11
|
"type": "module",
|
12
12
|
"exports": {
|
13
13
|
".": "./lib/index.js",
|
14
|
+
"./Families": "./lib/families.js",
|
14
15
|
"./Files": "./lib/files.js",
|
15
16
|
"./HTML": "./lib/html.js",
|
16
17
|
"./Jekyll": "./lib/jekyll.js"
|
@@ -21,6 +22,7 @@
|
|
21
22
|
},
|
22
23
|
"dependencies": {
|
23
24
|
"command-line-args": "^5.2.1",
|
25
|
+
"command-line-usage": "^6.1.3",
|
24
26
|
"csv-parse": "^5.3.1",
|
25
27
|
"unzipper": "^0.10.11"
|
26
28
|
},
|