import fs from 'fs'
import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import reverse from 'lodash-es/reverse.js'
import values from 'lodash-es/values.js'
import isbol from 'wsemi/src/isbol.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import isp0int from 'wsemi/src/isp0int.mjs'
import isfun from 'wsemi/src/isfun.mjs'
import ispm from 'wsemi/src/ispm.mjs'
import cdbl from 'wsemi/src/cdbl.mjs'
import haskey from 'wsemi/src/haskey.mjs'
import pmSeries from 'wsemi/src/pmSeries.mjs'
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
import fsCopyFile from 'wsemi/src/fsCopyFile.mjs'
import fsCleanFolder from 'wsemi/src/fsCleanFolder.mjs'
import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
import fsCopyFolder from 'wsemi/src/fsCopyFolder.mjs'
import fsDeleteFolder from 'wsemi/src/fsDeleteFolder.mjs'
import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
import fsGetFileBasicHash from 'wsemi/src/fsGetFileBasicHash.mjs'
import WDwdataBuilder from 'w-dwdata-builder/src/WDwdataBuilder.mjs'
import downloadEqs from './downloadEqs.mjs'
/**
* 基於檔案之下載台灣氣象署OpenData地震數據與任務建構器
*
* 執行階段最新hash數據放置於fdDwAttime,前次hash數據會於結束前自動備份至fdDwCurrent
*
* 執行階段最新數據放置於fdResult,前次數據會另存備份放置於fdResultTemp,於結束前會將fdResultTemp清空
*
* @param {String} token 輸入氣象署OpenData之API用token字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Boolean} [opt.keepAllData=true] 輸入是否儲存全部資料,氣象署地震資料內Intensity可能常變更而觸發事件,可設定keepAllData=false只基於重要數據進行偵測。預設true
* @param {String} [opt.fdTagRemove='./_tagRemove'] 輸入暫存標記為刪除數據資料夾字串,預設'./_tagRemove'
* @param {String} [opt.fdDwStorage='./_dwStorage'] 輸入完整下載數據資料夾字串,預設'./_dwStorage'
* @param {String} [opt.fdDwAttime='./_dwAttime'] 輸入當前下載供比對hash用之數據資料夾字串,預設'./_dwAttime'
* @param {String} [opt.fdDwCurrent='./_dwCurrent'] 輸入已下載供比對hash用之數據資料夾字串,預設'./_dwCurrent'
* @param {String} [opt.fdResultTemp=`./_resultTemp`] 輸入若有變更數據時,儲存前次已下載數據所連動生成數據資料夾字串,預設`./_resultTemp`
* @param {String} [opt.fdResult=`./_result`] 輸入已下載數據所連動生成數據資料夾字串,預設`./_result`
* @param {String} [opt.fdTaskCpActualSrc='./_taskCpActualSrc'] 輸入任務狀態之來源端完整資料夾字串,預設'./_taskCpActualSrc'
* @param {String} [opt.fdTaskCpSrc='./_taskCpSrc'] 輸入任務狀態之來源端資料夾字串,預設'./_taskCpSrc'
* @param {String} [opt.fdLog='./_logs'] 輸入儲存log資料夾字串,預設'./_logs'
* @param {Function} [opt.funDownloadEqs=null] 輸入自定義下載地震數據函數,回傳資料陣列,預設null
* @param {Function} [opt.funDownload=null] 輸入當前下載數據hash之函數,回傳資料陣列,預設null
* @param {Function} [opt.funGetCurrent=null] 輸入已下載數據hash之函數,回傳資料陣列,預設null
* @param {Function} [opt.funAdd=null] 輸入當有新資料時,需要連動處理之函數,預設null
* @param {Function} [opt.funModify=null] 輸入當有資料需更新時,需要連動處理之函數,預設null
* @param {Function} [opt.funRemove=null] 輸入當有資料需刪除時,需要連動處理之函數,預設null
* @param {Number} [opt.timeToleranceRemove=0] 輸入刪除任務之防抖時長,單位ms,預設0,代表不使用
* @returns {Object} 回傳事件物件,可呼叫函數on監聽change事件
* @example
*
* import fs from 'fs'
* import _ from 'lodash-es'
* import w from 'wsemi'
* import WDwdataTweqod from './src/WDwdataTweqod.mjs'
*
* let j = fs.readFileSync('../_data/settings.json', 'utf8')
* let st = JSON.parse(j)
* let token = _.get(st, 'token')
*
* //fdTagRemove
* let fdTagRemove = `./_tagRemove`
* w.fsCleanFolder(fdTagRemove)
*
* //fdDwStorage
* let fdDwStorage = `./_dwStorage`
* w.fsCleanFolder(fdDwStorage)
*
* //fdDwAttime
* let fdDwAttime = `./_dwAttime`
* w.fsCleanFolder(fdDwAttime)
*
* //fdDwCurrent
* let fdDwCurrent = `./_dwCurrent`
* w.fsCleanFolder(fdDwCurrent)
*
* //fdResultTemp
* let fdResultTemp = `./_resultTemp`
* w.fsCleanFolder(fdResultTemp)
*
* //fdResult
* let fdResult = `./_result`
* w.fsCleanFolder(fdResult)
*
* //fdTaskCpActualSrc
* let fdTaskCpActualSrc = `./_taskCpActualSrc`
* w.fsCleanFolder(fdTaskCpActualSrc)
*
* //fdTaskCpSrc
* let fdTaskCpSrc = `./_taskCpSrc`
* w.fsCleanFolder(fdTaskCpSrc)
*
* let opt = {
* keepAllData: false,
* fdTagRemove,
* fdDwStorage,
* fdDwAttime,
* fdDwCurrent,
* fdResultTemp,
* fdResult,
* fdTaskCpActualSrc,
* fdTaskCpSrc,
* // fdLog,
* // funDownload,
* // funGetCurrent,
* // funRemove,
* // funAdd,
* // funModify,
* }
* let ev = await WDwdataTweqod(token, opt)
* .catch((err) => {
* console.log(err)
* })
* ev.on('change', (msg) => {
* delete msg.type
* console.log('change', msg)
* })
* // change { event: 'start', msg: 'running...' }
* // change { event: 'proc-callfun-afterStart', msg: 'start...' }
* // change { event: 'proc-callfun-afterStart', msg: 'done' }
* // change { event: 'proc-callfun-download', msg: 'start...' }
* // change { event: 'proc-callfun-download', num: 2, msg: 'done' }
* // change { event: 'proc-callfun-getCurrent', msg: 'start...' }
* // change { event: 'proc-callfun-getCurrent', num: 0, msg: 'done' }
* // change { event: 'proc-compare', msg: 'start...' }
* // change { event: 'proc-compare', numRemove: 0, numAdd: 2, numModify: 0, numSame: 0, msg: 'done' }
* // change { event: 'proc-add-callfun-add', id: '114101', msg: 'start...' }
* // change { event: 'proc-add-callfun-add', id: '114101', msg: 'done' }
* // change { event: 'proc-add-callfun-add', id: '114102', msg: 'start...' }
* // change { event: 'proc-add-callfun-add', id: '114102', msg: 'done' }
* // ...
*
*/
let WDwdataTweqod = async(token, opt = {}) => {
//keepAllData
let keepAllData = get(opt, 'keepAllData')
if (!isbol(keepAllData)) {
keepAllData = true
}
//fdTagRemove, 暫存標記為刪除數據資料夾
let fdTagRemove = get(opt, 'fdTagRemove')
if (!isestr(fdTagRemove)) {
fdTagRemove = `./_tagRemove`
}
//fdDwStorage
let fdDwStorage = get(opt, 'fdDwStorage')
if (!isestr(fdDwStorage)) {
fdDwStorage = `./_dwStorage`
}
if (!fsIsFolder(fdDwStorage)) {
fsCreateFolder(fdDwStorage)
}
//fdDwAttime
let fdDwAttime = get(opt, 'fdDwAttime')
if (!isestr(fdDwAttime)) {
fdDwAttime = `./_dwAttime`
}
if (!fsIsFolder(fdDwAttime)) {
fsCreateFolder(fdDwAttime)
}
//fdDwCurrent
let fdDwCurrent = get(opt, 'fdDwCurrent')
if (!isestr(fdDwCurrent)) {
fdDwCurrent = `./_dwCurrent`
}
if (!fsIsFolder(fdDwCurrent)) {
fsCreateFolder(fdDwCurrent)
}
//fdResultTemp
let fdResultTemp = get(opt, 'fdResultTemp')
if (!isestr(fdResultTemp)) {
fdResultTemp = `./_resultTemp`
}
if (!fsIsFolder(fdResultTemp)) {
fsCreateFolder(fdResultTemp)
}
//fdResult
let fdResult = get(opt, 'fdResult')
if (!isestr(fdResult)) {
fdResult = `./_result`
}
if (!fsIsFolder(fdResult)) {
fsCreateFolder(fdResult)
}
//fdTaskCpActualSrc, 儲存完整任務狀態資料夾
let fdTaskCpActualSrc = get(opt, 'fdTaskCpActualSrc')
if (!isestr(fdTaskCpActualSrc)) {
fdTaskCpActualSrc = `./_taskCpActualSrc`
}
if (!fsIsFolder(fdTaskCpActualSrc)) {
fsCreateFolder(fdTaskCpActualSrc)
}
//fdTaskCpSrc
let fdTaskCpSrc = get(opt, 'fdTaskCpSrc')
if (!isestr(fdTaskCpSrc)) {
fdTaskCpSrc = './_taskCpSrc'
}
if (!fsIsFolder(fdTaskCpSrc)) {
fsCreateFolder(fdTaskCpSrc)
}
//fdLog
let fdLog = get(opt, 'fdLog')
if (!isestr(fdLog)) {
fdLog = './_logs'
}
if (!fsIsFolder(fdLog)) {
fsCreateFolder(fdLog)
}
//funDownloadEqs
let funDownloadEqs = get(opt, 'funDownloadEqs')
//funDownload
let funDownload = get(opt, 'funDownload')
//funGetCurrent
let funGetCurrent = get(opt, 'funGetCurrent')
//funAdd
let funAdd = get(opt, 'funAdd')
//funModify
let funModify = get(opt, 'funModify')
//funRemove
let funRemove = get(opt, 'funRemove')
//timeToleranceRemove
let timeToleranceRemove = get(opt, 'timeToleranceRemove')
if (!isp0int(timeToleranceRemove)) {
timeToleranceRemove = 0
}
timeToleranceRemove = cdbl(timeToleranceRemove)
//treeFilesAndGetHashs
let treeFilesAndGetHashs = (fd) => {
//vfps
let vfps = fsTreeFolder(fd, 1)
// console.log('vfps', vfps)
//ltdtHash
let ltdtHash = []
each(vfps, (v) => {
let j = fs.readFileSync(v.path, 'utf8')
let o = JSON.parse(j)
ltdtHash.push(o)
})
return ltdtHash
}
//funDownloadDef
let funDownloadDef = async() => {
//eqs
let eqs = []
if (isfun(funDownloadEqs)) {
eqs = funDownloadEqs()
if (ispm(eqs)) {
eqs = await eqs
}
}
else {
eqs = await downloadEqs(token, { keepAllData })
// console.log('eqs', eqs, size(eqs))
// eqs = [eqs[0], eqs[1]]
// fs.writeFileSync('./temp.json', JSON.stringify(eqs, null, 2), 'utf8')
}
//reverse
eqs = reverse(eqs)
//清空fdDwAttime
fsCleanFolder(fdDwAttime)
//複製已下載供比對hash用數據fdDwCurrent至fdDwAttime
fsCopyFolder(fdDwCurrent, fdDwAttime)
//ltdtHashOld, 數據來源為fdDwCurrent, 故為舊hash數據清單
let ltdtHashOld = treeFilesAndGetHashs(fdDwAttime)
//kpHash, 基於舊hash數據建構字典物件
let kpHash = {}
each(ltdtHashOld, (v) => {
kpHash[v.id] = v
})
//逐筆偵測與更新
await pmSeries(eqs, async(v) => {
//先儲存解析後數據至存放完整數據資料夾fdDwStorage
let fp = `${fdDwStorage}/${v.id}.json`
fs.writeFileSync(fp, JSON.stringify(v), 'utf8')
//計算檔案hash值, 為新hash
let hashNew = await fsGetFileBasicHash(fp, { type: 'md5' })
//check
if (haskey(kpHash, v.id)) {
//為已下載過之地震數據
//舊hash
let hashOld = kpHash[v.id].hash
//check
if (hashNew !== hashOld) {
//update
kpHash[v.id].hash = hashNew
//儲存更新後之新地震hash數據檔案
let fp = `${fdDwAttime}/${v.id}.json`
fs.writeFileSync(fp, JSON.stringify(kpHash[v.id]), 'utf8')
}
else {
//不用更新hash檔案
}
}
else {
//為新增之地震數據
//add
kpHash[v.id] = {
id: v.id,
hash: hashNew,
}
//儲存新地震hash數據檔案
let fp = `${fdDwAttime}/${v.id}.json`
fs.writeFileSync(fp, JSON.stringify(kpHash[v.id]), 'utf8')
}
})
//ltdtHashNew
let ltdtHashNew = values(kpHash)
return ltdtHashNew
}
if (!isfun(funDownload)) {
funDownload = funDownloadDef
}
//funGetCurrentDef
let funGetCurrentDef = async() => {
//vfps
let vfps = fsTreeFolder(fdDwCurrent, 1)
// console.log('vfps', vfps)
//ltdtHashOld
let ltdtHashOld = []
each(vfps, (v) => {
let j = fs.readFileSync(v.path, 'utf8')
let eq = JSON.parse(j)
ltdtHashOld.push(eq)
})
return ltdtHashOld
}
if (!isfun(funGetCurrent)) {
funGetCurrent = funGetCurrentDef
}
//funRemoveDef
let funRemoveDef = async(v) => {
let fd = `${fdResult}/${v.id}`
if (fsIsFolder(fd)) {
fsDeleteFolder(fd)
}
}
if (!isfun(funRemove)) {
funRemove = funRemoveDef
}
//funAddDef
let funAddDef = async(v) => {
let fd = `${fdResult}/${v.id}`
if (!fsIsFolder(fd)) {
fsCreateFolder(fd)
}
fsCleanFolder(fd)
let fpSrc = `${fdDwStorage}/${v.id}.json`
let fpTar = `${fd}/${v.id}.json`
fsCopyFile(fpSrc, fpTar)
}
if (!isfun(funAdd)) {
funAdd = funAddDef
}
//funModifyDef
let funModifyDef = async(v) => {
//複製舊資料夾(含檔案)至fdResultTemp做暫時備份, fdResultTemp會於funAfterStart清空, 於funBeforeEnd刪除
if (true) {
let fdSrc = `${fdResult}/${v.id}`
let fdTar = `${fdResultTemp}/${v.id}`
fsCopyFolder(fdSrc, fdTar)
}
//複製新檔案至fdResult
if (true) {
let fd = `${fdResult}/${v.id}`
if (!fsIsFolder(fd)) {
fsCreateFolder(fd)
}
fsCleanFolder(fd)
let fpSrc = `${fdDwStorage}/${v.id}.json`
let fpTar = `${fd}/${v.id}.json`
fsCopyFile(fpSrc, fpTar)
}
}
if (!isfun(funModify)) {
funModify = funModifyDef
}
let funAfterStart = async() => {
fsCleanFolder(fdResultTemp)
}
let funBeforeEnd = async() => {
fsCleanFolder(fdResultTemp)
}
let optBdr = {
fdTagRemove,
fdDwAttime,
fdDwCurrent,
fdResult,
fdTaskCpActualSrc,
fdTaskCpSrc,
fdLog,
funDownload,
funGetCurrent,
funRemove,
funAdd,
funModify,
funAfterStart,
funBeforeEnd,
timeToleranceRemove,
}
let ev = await WDwdataBuilder(optBdr)
return ev
}
export default WDwdataTweqod