Skip to content

Commit 24cd6fa

Browse files
authored
Merge pull request #18 from ciatph/dev
v1.0.3
2 parents 2277e62 + f7fc645 commit 24cd6fa

7 files changed

Lines changed: 124 additions & 12 deletions

File tree

src/01_recommendations/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require('dotenv').config()
22
const path = require('path')
3+
const { CsvToFireStore } = require('csv-firestore')
34
const SeasonalTab = require('./src/classes/seasonaltab')
45
const TendayTab = require('./src/classes/tendaytab')
56
const SpecialTab = require('./src/classes/specialtab')
@@ -16,6 +17,9 @@ const main = async () => {
1617
// Excel file path
1718
const filePath = path.join(__dirname, process.env.EXCEL_FILENAME)
1819

20+
// Firestore documents upload handler
21+
const handler = new CsvToFireStore()
22+
1923
// Excel tabs column names definitions
2024
const excelTabs = [
2125
new SeasonalTab(),
@@ -57,7 +61,16 @@ const main = async () => {
5761
if (upload) {
5862
try {
5963
data.forEach((item, index) => {
64+
// Simple merged JSON data
6065
query.push(uploadToFirestore('n_list_crop_recommendations', item.recommendations.type, item.recommendations))
66+
67+
// Upload each recommendation row to a Document
68+
// Path: /n_list_crop_recommendations_{type}
69+
query.push(handler.firestoreUpload(
70+
`n_list_crop_recommendations_${item.recommendations.type}`,
71+
true,
72+
item.recommendations.data
73+
))
6174
})
6275

6376
// Upload data to Firestore

src/01_recommendations/src/classes/excelsheetdef.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const { CROP_STAGE_LABELS } = require('../../../02_crop_calendar/constants')
2+
13
class ExcelTabDefinition {
24
/** Excel sheet tab number */
35
excelTabNumber = -1
@@ -21,6 +23,13 @@ class ExcelTabDefinition {
2123
PRACTICE: 'practice',
2224
PRACTICE_TAGALOG: 'practice_tagalog'
2325
}
26+
27+
NORMAL_CROPSTAGE_CODES = {
28+
'Newly Planted': Object.keys(CROP_STAGE_LABELS).find(key => CROP_STAGE_LABELS[key] === 'Newly Planted'),
29+
'Vegetative/Reproductive': Object.keys(CROP_STAGE_LABELS).find(key => CROP_STAGE_LABELS[key] === 'Vegetative/Reproductive'),
30+
Maturing: Object.keys(CROP_STAGE_LABELS).find(key => CROP_STAGE_LABELS[key] === 'Maturing'),
31+
'Preparation Stage': Object.keys(CROP_STAGE_LABELS).find(key => CROP_STAGE_LABELS[key] === 'Preparation Stage')
32+
}
2433
}
2534

2635
module.exports = ExcelTabDefinition

src/01_recommendations/src/classes/seasonaltab.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ class SeasonalTab extends ExcelTabDefinition {
1818
__EMPTY_2: this.NORMAL_COLUMN_NAMES.PRACTICE,
1919
__EMPTY_3: this.NORMAL_COLUMN_NAMES.PRACTICE_TAGALOG
2020
}
21+
22+
/** Normalized weather forecast text */
23+
this.NORMAL_FORECAST_NAMES = {
24+
'WAY BELOW NORMAL': 'Way Below Normal',
25+
'BELOW NORMAL': 'Below Normal',
26+
'NEAR NORMAL': 'Near normal',
27+
'ABOVE NORMAL': 'Above Normal'
28+
}
29+
30+
/** Normalized weather forecast codes */
31+
this.NORMAL_FORECAST_CODES = {
32+
'WAY BELOW NORMAL': 'wb_normal',
33+
'BELOW NORMAL': 'b_normal',
34+
'NEAR NORMAL': 'near_normal',
35+
'ABOVE NORMAL': 'above_normal'
36+
}
2137
}
2238
}
2339

src/01_recommendations/src/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const RECOMMEDATIONS_TYPE = {
22
SEASONAL: 'seasonal',
33
TENDAY: 'tenday',
4-
SPECIAL: 'sepcial'
4+
SPECIAL: 'special'
55
}
66

77
module.exports = {

src/01_recommendations/src/extract.js

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const XLSXWrapper = require('../../lib/xlsxwrapper')
2+
const { RECOMMEDATIONS_TYPE } = require('./constants')
23

34
/**
45
* Extract normalized recommendations data and other metadata from an excel sheet tab
@@ -28,8 +29,38 @@ module.exports.extractExcelData = (ExcelTab, excelFilePath) => {
2829
const obj = {}
2930

3031
for (const key in ExcelTab.EXCEL_COLUMN_NAMES) {
31-
obj[ExcelTab.EXCEL_COLUMN_NAMES[key]] = item[key] || ''
32-
obj[ExcelTab.EXCEL_COLUMN_NAMES[key]] = obj[ExcelTab.EXCEL_COLUMN_NAMES[key]].trim()
32+
let value = item[key] || ''
33+
value = value.trim()
34+
35+
switch (ExcelTab.type) {
36+
case RECOMMEDATIONS_TYPE.SEASONAL:
37+
// Normalize the seasonal forecast text
38+
if (ExcelTab.EXCEL_COLUMN_NAMES[key] === ExcelTab.NORMAL_COLUMN_NAMES.FORECAST) {
39+
value = ExcelTab.NORMAL_FORECAST_CODES[value]
40+
}
41+
break
42+
default: break
43+
}
44+
45+
// Insert <span> in <li>
46+
switch (ExcelTab.EXCEL_COLUMN_NAMES[key]) {
47+
case ExcelTab.NORMAL_COLUMN_NAMES.PRACTICE:
48+
case ExcelTab.NORMAL_COLUMN_NAMES.PRACTICE_TAGALOG:
49+
case ExcelTab.NORMAL_COLUMN_NAMES.IMPACT:
50+
case ExcelTab.NORMAL_COLUMN_NAMES.IMPACT_TAGALOG:
51+
value = value.replace(/<li>/g, '<li><span>')
52+
value = value.replace(/<\/li>/g, '</span></li>')
53+
break
54+
default:
55+
break
56+
}
57+
58+
// Normalize the crop stage name - use crop calendar codes
59+
if (ExcelTab.EXCEL_COLUMN_NAMES[key] === ExcelTab.NORMAL_COLUMN_NAMES.CROP_STAGE) {
60+
value = ExcelTab.NORMAL_CROPSTAGE_CODES[value]
61+
}
62+
63+
obj[ExcelTab.EXCEL_COLUMN_NAMES[key]] = value
3364
}
3465

3566
list.push(obj)
@@ -38,6 +69,28 @@ module.exports.extractExcelData = (ExcelTab, excelFilePath) => {
3869
return list
3970
}, [])
4071

72+
// Normalize the special recommendations' farm operations
73+
if (ExcelTab.type === RECOMMEDATIONS_TYPE.SPECIAL) {
74+
recommendations.data = recommendations.data.reduce((list, item, index) => {
75+
const farmoperation = item[ExcelTab.NORMAL_COLUMN_NAMES.FARM_OPERATION]
76+
77+
// Expand the merged farm operations vertically
78+
if (farmoperation.includes('/') && farmoperation !== 'Planting/Transplanting') {
79+
const farmoperations = farmoperation.split('/').map(operations => operations.trim())
80+
81+
farmoperations.forEach((operation) => {
82+
const temp = { ...item }
83+
temp[ExcelTab.NORMAL_COLUMN_NAMES.FARM_OPERATION] = operation
84+
list.push(temp)
85+
})
86+
} else {
87+
list.push(item)
88+
}
89+
90+
return list
91+
}, [])
92+
}
93+
4194
// List unique crop stages
4295
const cropstages = recommendations.data.map(x => x[ExcelTab.NORMAL_COLUMN_NAMES.CROP_STAGE])
4396
.filter((x, i, a) => a.indexOf(x) === i)

src/02_crop_calendar/index.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ const path = require('path')
33
const CroppingCalendar = require('./cropping_calendar')
44
const { uploadToFirestore } = require('../lib/uploadtofirestore')
55

6-
// Path: /n_cropping_calendar_merged/{province}.data[]
6+
// Path: /n_cropping_calendar_lite/{province}.data[]
77
const main = async () => {
88
const handler = new CroppingCalendar(path.resolve(__dirname, process.env.CSV_FILENAME))
99
const upload = false
1010
const write = true
1111

1212
// Cropping Calendar-specific tables and firestore collection names
1313
const newTables = {
14-
provinces: 'n_provinces',
15-
municipalities: 'n_municipalities',
14+
// provinces: 'n_provinces',
15+
// municipalities: 'n_municipalities',
1616
crops: 'n_crops',
1717
crop_stages: 'n_crop_stages'
1818
}
@@ -30,7 +30,8 @@ const main = async () => {
3030
group[province] = []
3131
}
3232

33-
const obj = {}
33+
const obj = { province }
34+
3435
for (const key in row) {
3536
if (!['id', 'province'].includes(key)) {
3637
obj[key] = row[key].trim()
@@ -41,7 +42,7 @@ const main = async () => {
4142
return { ...group }
4243
}, {})
4344

44-
console.log('\nUploading data to firestore...')
45+
console.log('\nUploading list data to firestore...')
4546
const query = []
4647

4748
// Upload full collections
@@ -63,11 +64,29 @@ const main = async () => {
6364
logs += `${province}: ${data[province].length} items\n`
6465

6566
// Upload query
66-
query.push(uploadToFirestore('n_cropping_calendar_merged', province, { data: data[province] }))
67+
query.push(uploadToFirestore('n_cropping_calendar_lite', province, { data: data[province] }))
6768
}
6869

6970
console.log(logs)
70-
console.log('Uploading data to Firestore...')
71+
console.log('Uploading calendar data to Firestore...')
72+
73+
// Upload list data as documents
74+
console.log('Uploading lists in a single Firestore document...')
75+
76+
// Upload the list of provinces and respective municipalities for use as constant, static values
77+
query.push(uploadToFirestore('constant_data', 'provinces', {
78+
data: handler.provinces.reduce((list, province, index) => {
79+
const temp = { id: province.id, label: province.name }
80+
81+
temp.municipalities = handler.municipalities.filter((municipality) => municipality.province === province.name)
82+
.sort((a, b) => a.name > b.name ? 1 : (a.name < b.name) ? -1 : 0)
83+
.map((municipality, id) => ({ id, label: municipality.name }))
84+
85+
list.push(temp)
86+
return list
87+
}, [])
88+
}))
89+
7190
await Promise.all(query)
7291
console.log('Upload success!')
7392
}

src/lib/uploadtofirestore/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ const firestore = new FirestoreData()
99
* - {Object[]} data - crop recommendations mapped to crop stages, farm operations and other keys
1010
* - {String} type - type of recommendations. One of RECOMMEDATIONS_TYPE.
1111
* - {String} description - Brief text description describing the nature of data
12+
* @param {Object} metadata - Key-value pairs description and other information about the data
1213
* @returns {Timestamp} Firestore timestamp of successful data upload
1314
*/
14-
module.exports.uploadToFirestore = async (collectionName, docName, jsonData) => {
15+
module.exports.uploadToFirestore = async (collectionName, docName, jsonData, metadata) => {
1516
// CSV and Firestore handler
16-
jsonData.date_created = firestore.admin.firestore.Timestamp.now()
17+
jsonData.metadata = metadata ?? {}
18+
jsonData.metadata.date_created = firestore.admin.firestore.Timestamp.now()
1719

1820
try {
1921
// Upload data to Firestore

0 commit comments

Comments
 (0)