Skip to content

Commit

Permalink
Merge pull request #108 from ciatph/dev
Browse files Browse the repository at this point in the history
v1.3.7
  • Loading branch information
ciatph authored Sep 1, 2024
2 parents 8a7df8a + 28859d8 commit c947056
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 28 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
## ph-municipalities

The **ph-municipalities** NPM package has **NPM scripts** that allow interactive querying of Philippines municipalities included in one or more provinces or from a whole region, with an option of writing them to JSON files from the command line.
The **ph-municipalities** NPM package provides **NPM scripts** that allow interactive querying of Philippine municipalities included in one or more provinces or from a whole region, with an option of writing them to JSON files from the command line through a CLI-like application and **classes** for parsing Excel file data sources.

It uses a **PAGASA 10-day weather forecast Excel** file in `/app/data/day1.xlsx` (downloaded and stored as of this 20220808) from PAGASA's [10-Day Climate Forecast web page](https://www.pagasa.dost.gov.ph/climate/climate-prediction/10-day-climate-forecast) as the default data source, syncing with the **PAGASA Seasonal weather forecast** regions and province names defined in the manually-encoded `/app/config/regions.json` file to determine the region names.
It uses a **PAGASA 10-day weather forecast Excel** file in `/app/data/day1.xlsx` (downloaded and stored as of this 20220808) from PAGASA's [10-Day Climate Forecast web page](https://www.pagasa.dost.gov.ph/climate/climate-prediction/10-day-climate-forecast) as the default data source.

> Since the **PAGASA 10-day weather forecast Excel** files do not have region names, the parser scripts sync it with the **PAGASA Seasonal weather forecast** regions and province names defined in the manually-encoded `/app/config/regions.json` file to determine the region names. The **classes** also provide an option to specify a new region name configuration file on initialization.
It also asks users to key in the file download URL of a remote PAGASA 10-Day weather forecast Excel file should they want to use another Excel file for a new and updated data source.

Expand Down
4 changes: 2 additions & 2 deletions app/__tests__/classInitialization/customSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ const REMOTE_SOURCE = {
}

/* eslint-disable no-undef */
describe('Class intialization using CUSTOM config', () => {
describe('Class intialization using CUSTOM PAGASA seasonal (regions) config', () => {
beforeAll(async () => {
return await Promise.all([
await Promise.all([
REMOTE_SOURCE.excelFile.init(),
REMOTE_SOURCE.excelFactory.init()
])
Expand Down
2 changes: 1 addition & 1 deletion app/__tests__/classInitialization/defaultSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const REMOTE_SOURCE = {
}

/* eslint-disable no-undef */
describe('Class intialization using DEFAULT config', () => {
describe('Class intialization using DEFAULT PAGASA seasonal (regions) config', () => {
beforeAll(async () => {
return await Promise.all([
REMOTE_SOURCE.excelFile.init(),
Expand Down
63 changes: 63 additions & 0 deletions app/__tests__/outputData/testOutputData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const fs = require('fs')
const path = require('path')

const ExcelFactory = require('../../src/classes/excelfactory')
const Schema = require('../../src/classes/schema')

const ColorLog = require('../../src/classes/colorlog')
const logger = new ColorLog({ color: ColorLog.COLORS.TEXT.YELLOW })

const checkClass = require('../classInitialization/checkClass')
const outputSchema = require('../../src/lib/schemas/outputSchema')
const { arrayToString } = require('../../src/lib/utils')

/* eslint-disable no-undef */
describe('Output JSON file format test', () => {
const jsonFileName = 'municipalities.json'
const pathToOutputFile = path.join(__dirname, jsonFileName)

let jsonDataContent

it('should write municipalities data to a JSON file', () => {
// ExcelFactory is a shorthand for the ExcelFile class when using only the default settings
const excelFile = new ExcelFactory()

const regions = excelFile.listRegions()
const regionName = regions[0]
const provinces = excelFile.listProvinces(regionName)

let msg = `Sample region: ${regionName}\n`
msg += `Provinces: ${arrayToString(provinces)}\n\n`
msg += `Writing municipalities list to "${jsonFileName}"...`
logger.log(msg)

// Write the municipalities of a region to a JSON file
excelFile.writeMunicipalities({
fileName: pathToOutputFile,
provinces
})

// Read the JSON file from disk
jsonDataContent = fs.readFileSync(pathToOutputFile, { encoding: 'utf8' })

logger.log(`JSON data read from the "${jsonFileName}" file:`)
logger.log(jsonDataContent)

expect(jsonDataContent).toBeDefined()

checkClass({
excelInstance: excelFile,
classType: ExcelFactory
})
})

it('should follow the defined output format', () => {
// Validate schema of the output JSON file
const schema = new Schema(
JSON.parse(jsonDataContent),
outputSchema
)

expect(schema).toBeDefined()
})
})
22 changes: 22 additions & 0 deletions app/__tests__/regions/regionsTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require('dotenv').config()
const path = require('path')

const ExcelFile = require('../../src/classes/excel')

/* eslint-disable no-undef */
describe('Regions listing test', () => {
it('should list region names from config file', () => {
const excelFile = new ExcelFile({
pathToFile: path.join(__dirname, '..', '..', 'data', 'day1.xlsx')
})

const regions = excelFile
.listRegions()
.map(region => ({ 'Region Name': region }))

console.table(regions)

expect(regions).toBeDefined()
expect(Array.isArray(regions)).toBe(true)
})
})
15 changes: 11 additions & 4 deletions app/src/classes/excelfactory/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ require('dotenv').config({
const ExcelFile = require('../excel')

/**
* Convenience factory class that creates an ExcelFile class using the default Excel data set.
* Creates an `ExcelFile` class with remote download given the URL parameter.
* Convenience factory class that creates an `ExcelFile` class.
* - Uses the default Excel data source if there are no constructor parameters.
* - Creates an `ExcelFile` class with remote Excel file download given the URL parameter,
* downloading the Excel file to a local Excel file with a random-generated filename.
* - Note: Use the `ExcelFile` class instead if there is a need to specify the download file path or filename.
*/
class ExcelFactory extends ExcelFile {
/**
* Initializes
* Initializes an `ExcelFactory` class.
* @typedef {Object} params - Constructor parameter Object
* @param {String} [params.url] - (Optional) Remote download URL of an excel file
* @param {Object} [params.settings] - (Optional) Region settings configuration object following the format of the `/app/config/regions.json` file. Defaults to the mentioned file if not provided.
*/
constructor ({ url, settings } = {}) {
if (url) {
const randomTime = new Date().getTime()
const fileName = `datasource_${randomTime}.xlsx`

// Downloads the remote Excel file to a local file with random filename.
super({
pathToFile: path.join(process.cwd(), 'datasource.xlsx'),
pathToFile: path.join(process.cwd(), fileName),
url,
settings
})
Expand Down
11 changes: 11 additions & 0 deletions app/src/classes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const ColorLog = require('./colorlog')
const ExcelFile = require('./excel')
const ExcelFactory = require('./excelfactory')
const Schema = require('./schema')

module.exports = {
ColorLog,
ExcelFile,
ExcelFactory,
Schema
}
20 changes: 10 additions & 10 deletions app/src/lib/format_display.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
/**
* Formats municipality names into a single text string.
* Attach municipalities count and total count.
* Formats municipality names for logging.
* Attaches municipalities count and total count to the return data.
* @param {Object} municipalitiesGroup - Object that have province names as keys, each containing a String[] of municipalities
* @returns {Object} { total, data }
* - total: {String} total municipalities count
* - data: {Object} mutated municipalitiesGroup. The String[] array municipalities are converted to 1 long String text
* - total: {Number} total municipalities count
* - data: {Object} mutated municipalitiesGroup. Contains the String[] municipalities array and the number of municipalities per province.
*/
const formatDisplay = (municipalitiesGroup) => {
const total = Object.keys(municipalitiesGroup).reduce((count, municipality) => {
count += municipalitiesGroup[municipality].length
const total = Object.keys(municipalitiesGroup).reduce((count, province) => {
count += municipalitiesGroup[province].length
return count
}, 0)

return {
total,
data: Object.keys(municipalitiesGroup).reduce((formatted, municipality) => ({
data: Object.keys(municipalitiesGroup).reduce((formatted, province) => ({
...formatted,
[municipality]: {
count: municipalitiesGroup[municipality].length,
municipalities: municipalitiesGroup[municipality].toString().split(',')
[province]: {
count: municipalitiesGroup[province].length,
municipalities: municipalitiesGroup[province]
}
}), {})
}
Expand Down
32 changes: 32 additions & 0 deletions app/src/lib/schemas/outputSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { z } = require('zod')

// Province name schema
const provinceSchema = z.string().max(40)

// JSON output schema
const outputSchema = z.object({
metadata: z.object({
source: z.string(
// URL link to local or remote Excel file data source website
z.string().url().max(200)
),
// Data set title
title: z.string().max(50),
// Data set description
description: z.string().max(500),
// Date the data set was first created
date_created: z.string().max(20)
}),

// Keys are province names (Strings)
data: z.record(
// Key-values (municipality names) are arrays of strings
z.array(z.string().max(40)).refine((data) => {
// Each province name-key should have max length of 40
return Object.keys(data)
.every(province => provinceSchema.safeParse(province).success)
})
)
})

module.exports = outputSchema
6 changes: 1 addition & 5 deletions app/src/lib/selector.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const path = require('path')

const {
createInstances,
updateInstances
Expand All @@ -23,8 +21,6 @@ const selectDataSource = async () => {
const askDownload = await prompt('\nWould you like to download and use a remote Excel file?\nPress enter to ignore. Press Y and enter to proceed. [n/Y]: ')

if (askDownload === 'Y') {
const pathToFile = path.join(process.cwd(), 'datasource.xlsx')

while (!url) {
url = await prompt('\nEnter the download URL of a remote Excel file: ')
}
Expand All @@ -36,7 +32,7 @@ const selectDataSource = async () => {
await ExcelHandler.init()
exit = true

console.log(`\nUsing the file downloaded to ${pathToFile}\nas data source`)
console.log(`\nUsing the file downloaded to ${ExcelHandler.pathToFile}\nas data source`)
} catch (err) {
console.log(`[ERROR] ${err.message}`)
ExcelHandler = null
Expand Down
6 changes: 4 additions & 2 deletions app/src/scripts/by_region.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ const main = async () => {

if (ExcelHandler !== null) {
// Display region abbreviations
const regionNames = ExcelHandler.listRegions()
const regionNames = ExcelHandler
.listRegions()
.map(region => ({ 'Region Name': region }))

console.log('\nREGION NAMES')
console.log(regionNames.toString().split(',').join('\n'))
console.table(regionNames)

// Prompt to ask for province name(s)
const region = await prompt('\nEnter a region name: ')
Expand Down
Binary file modified docs/diagrams/ph-municipalities-arch-90.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/diagrams/ph-municipalities-arch.drawio
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<mxfile host="Electron" modified="2024-08-28T11:45:58.373Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.3 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="QMTz06xJU2CeYxixARan" version="22.0.3" type="device">
<mxfile host="Electron" modified="2024-09-01T14:04:50.967Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.3 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="ThfDfPcefII_-JO3I4W5" version="22.0.3" type="device">
<diagram name="Page-1" id="PT_rIEPRiHDDqEcKwNWF">
<mxGraphModel dx="2268" dy="828" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
Expand Down Expand Up @@ -58,7 +58,7 @@
<mxCell id="N0Y9xDM0efUduAB5_r4A-83" value="Processed province / municipality&lt;br&gt;names" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document" parent="1" vertex="1">
<mxGeometry x="-352" y="400" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="N0Y9xDM0efUduAB5_r4A-84" value="Raw Excel data&lt;br&gt;(json)&lt;br&gt;" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document" parent="1" vertex="1">
<mxCell id="N0Y9xDM0efUduAB5_r4A-84" value="Raw Excel data&lt;br&gt;(JSON)" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document" parent="1" vertex="1">
<mxGeometry x="-352" y="470" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="N0Y9xDM0efUduAB5_r4A-85" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="N0Y9xDM0efUduAB5_r4A-62" target="N0Y9xDM0efUduAB5_r4A-80" edge="1">
Expand Down

0 comments on commit c947056

Please sign in to comment.