UNPKG

hkopendata

Version:

Access different Opendata API and data in Hong Kong

384 lines (368 loc) 13.5 kB
const cmn = require("../common"); const moment = require("../moment"); const Coordinate = require("../_class").Coordinate; const CURRENCY_REGEX_ALL = /[A-Z]{3}|澳元|加幣|瑞士法郎|歐羅|英鎊|日元|紐西蘭元|美元|人民幣|港元|港幣/g; const CURRENCY_REGEX = /([A-Z]{3}|澳元|加幣|瑞士法郎|歐羅|英鎊|日元|紐西蘭元|美元|人民幣|港元|港幣)/; function PreprocessFields(data) { const FIELDS = { regex: { "(card|investment|insurance)-category": "category" }, number: { "buy-price": "buyRate", "sell-price": "sellRate", "service-fee": "serviceCharge", "tenor": "loanTenor", }, text: { "last-updated": "lastUpdate", "product-url": "product-website", "product-apply-url": "product-applyForm", "ltv": "loanToValueRatio", }, others: { "deposit-rates": "creditInterest", "types": "type", "telephone": "tel", "mortgages": "mortgage", "savings-accounts": "saving", "current-accounts": "current", "time-deposit-accounts": "timeDeposit", "credit-cards": "creditCard", "personal-loans": "loan", "investments": "investment", "title": "name", "summary-title": "name", "summary": "remarks", "details": "description", "url": "website", } } if (Array.isArray(data)) { return data.map(v => PreprocessFields(v)) } else if (typeof data === "object" && data !== null) { if (Object.keys(data) == 0) return data; let temp = {}, common = ["product", "category", "tnc", "premium"], keyJoin = common.join("|"), regex = new RegExp(keyJoin, "i"), group = {}; data = cmn.RenameFields(data, FIELDS); Object.keys(data).filter(v => regex.test(v)).map(v => { let r = new RegExp(`(${keyJoin})-([A-z-]+)`), m = v.match(r); if (m) { if (/category/.test(m[1])) { delete data[v]; } else { for (let key in data) { if (key.match(`${m[1]}(.*)`)) { let ckey = m[1].toCamelCase(), citem = key.replace(`${m[1]}-`, "").toCamelCase(); if (!(ckey in group)) group[ckey] = {} group[ckey][citem] = data[key]; delete data[key]; } } } } }) group = PreprocessFields(group); for (let name in group) { if (/^(product|tnc)$/.test(name)) { for (let key in group[name]) { if (!(key in temp)) { temp[key] = group[name][key]; delete group[name][key]; } } } else { let r = new RegExp(name); Object.keys(data).filter(v => r.test(v)).map(v => { group[name][v.replace(r, "").toCamelCase()] = data[v]; delete data[v]; }) temp[name] = group[name]; } } for (let key in data) { let ckey = key.toCamelCase(); if (Array.isArray(data[key])) { temp[ckey] = data[key].map(v => PreprocessFields(v)); } else if (typeof data[key] === "object") { temp[ckey] = PreprocessFields(data[key]); } else { temp[ckey] = data[key] } } return temp; } return data; } function ParseItem(data) { if (typeof data !== "object" || data === null) return data; else if (Array.isArray(data)) return data.map(v => ParseItem(v)); let temp = {}, opening = { mon: false, tue: false, wed: false, thu: false, fri: false, sat: false, sun: false, }, hasOpening = false; for (let key in data) { if (key == "services") { temp[key] = {}; data[key].map(v => { let ckey = v.toCamelCase(); if (ckey == "cashAndChequeDepositMachines") { temp[key].cashDepositMachine = true; temp[key].chequeDepositMachine = true; } else { temp[key][ckey] = true; } }); } else if (key == "address") { temp.address = data[key].addressLines; temp.coordinate = new Coordinate({ latitude: parseFloat(data[key].latitude), longitude: parseFloat(data[key].longitude), }) } else if (key == "is24Hours" && data[key]) { hasOpening = true; for (let k in opening) { opening[k] = "00:00-23:59"; } } else if (key == "regularHours") { hasOpening = true; data[key].map(v => { let weekday = Object.keys(opening), od = weekday[v.openDay - 1], ed = weekday[v.closeDay - 1], ot = moment(v.openTime, "HH:mm AA", "en-US").format("HH:mm"), et = moment(v.closeTime, "HH:mm AA", "en-US").format("HH:mm"); if (!opening[od]) opening[od] = []; if (od == ed) { opening[od].push(`${ot}-${et}`); } else { if (v.closeDay < v.openDay) v.closeDay += 7; opening[od].push(`${ot}-23:59`); for (let i = v.openDay; i < v.closeDay - 1; i++) { opening[weekday[i % 7]] = ["00:00-23:59"]; } opening[ed] = [`00:00-${et}`]; } }) } else if (key == "lastUpdate") { temp[key] = moment(data[key], "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss"); } else if (key == "eligibility") { if (!("eligibility" in temp)) temp.eligibility = []; data[key].map(v => { for (let k in v) { let t = { type: k }; if (/age|income/i.test(k)) { if (/income/i.test(k)) { t.type = "income"; } let r = processCurrencyAmount(v[k]); for (let k2 in r) { t[k2] = r[k2]; } } else { t.remarks = v[k]; } temp.eligibility.push(t) } }) } else if (/^(annualFee|supplementaryFee|handlingFee)$/.test(key)) { if (!("feesCharge" in temp)) temp.feesCharge = []; let m, fee = { name: key, }; if (typeof data[key] === "number") { fee.amount = data[key]; } else if (m = data[key].match(/([0-9,]+)/)) { fee.amount = parseFloat(m[1].replace(/[^0-9]/g, "")); if (m = data[key].match(CURRENCY_REGEX)) { fee.currency = m[1]; } } else { fee.remarks = data[key]; } temp.feesCharge.push(fee) } else if (key == "initialDeposit") { if (!("eligibility" in temp)) temp.eligibility = []; temp.eligibility.push({ ...{ type: key }, ...processCurrencyAmount(data[key]) }) } else if (key == "feeWaiver") { if (!("feesCharge" in temp)) temp.feesCharge = []; temp.feesCharge.push({ name: key, remarks: data[key] }) } else if (key == "loanAmount") { temp[key] = processCurrencyAmount(data[key]); } else if (key == "feesAndCharges") { if (!("feesCharge" in temp)) temp.feesCharge = []; data[key].map(v => { let t = {} for (let k in v) { if (k == "currency") { t[k] = v[k].match(CURRENCY_REGEX_ALL) if (t[k].length == 1) t[k] = t[k][0]; } else if (k == "type") { t.name = v[k]; } else if (k == "description") { t.remarks = v[k]; } else { t[k] = v[k]; } } if ("name" in t && !("remarks" in t)) { t.remarks = t.name; delete t.name; } temp.feesCharge.push(t) }) } else if (/^(features|benefits)$/.test(key)) { if (!("featureBenefit" in temp)) temp.featureBenefit = []; let feature = []; data[key].map(v => { let t = {} for (let k in v) { t[/description|data/.test(k) ? "remarks" : k] = v[k] } feature.push(t); }) temp.featureBenefit = temp.featureBenefit.concat(feature) } else if (key == "currency") { if (CURRENCY_REGEX_ALL.test(data[key])) { temp[key] = data[key].match(CURRENCY_REGEX_ALL) } else { temp[key] = data[key].split(/,|、/).map(v => v.trim()) } if (temp[key].length == 1) temp[key] = data[key]; } else if (key != "id") { temp[key] = data[key]; } } if (hasOpening) { temp.opening = opening; } if ("cardCategory" in temp) { temp.card = { type: temp.cardCategory } delete temp.cardCategory; if ("code" in temp) { temp.card.code = temp.code; delete temp.code; } if ("subCode" in temp) { temp.card.subCode = temp.subCode; delete temp.subCode; } } else if ("code" in temp && "subCode" in temp) { if (temp.subCode == temp.code) { delete temp.subCode; } } if ("coverageCharges" in temp) { temp.coverageCharges.map((v, i) => { if ("premium" in v) { for (let key in v.premium) { if (/annually|monthly|onetime/.test(key)) { temp.coverageCharges[i].premium[key] = processCurrencyAmount(v.premium[key]) } } } }) } return temp; } function DataValidate(target, queryData) { const CATEGORY = { "saving": "SA", "current": "CA", "timeDeposit": "TD", "creditCard": "CC", "loan": "PL", "mortgage": "ML", "investment": "Inv", "insurance": "Ins", } const ACCEPT_LANG = { en: "en-HK", tc: "zh-HK" } let data = true; if (target == "fx") { if (queryData.currency) { let currency = queryData.currency.split(",").map(v => { return v.length == 3 ? v + "HKD" : v; }).filter(v => v.length == 6 && /AUD(CAD|CHF|CNH|HKD|JPY|NZD|SGD|USD)|CAD(CHF|CNH|HKD|JPY|SGD)|CHF(CNH|HKD|JPY|SGD)|CNHHKD|EUR(AUD|CAD|CHF|CNH|GBP|HKD|JPY|NZD|SGD|USD)|GBP(AUD|CAD|CHF|CNH|HKD|JPY|NZD|SGD|USD)|JPY(CNH|HKD)|NZD(CAD|CHF|CNH|HKD|JPY|SGD|USD)|SGD(CNH|HKD|JPY)|USD(CAD|CHF|CNH|HKD|JPY|SGD)/.test(v)).join(","); if (currency.length == 0) data = false; else data = { currency } } else { data = false; } } else if (/atm|branch/.test(target)) { data = { type: target } } else if (target in CATEGORY) { if (!(queryData.lang in ACCEPT_LANG)) queryData.lang = "en"; data = { category: CATEGORY[target], language: ACCEPT_LANG[queryData.lang], } } return data } function processCurrencyAmount(data) { if (typeof data === "number") return data; else if (typeof data === "string") { let m = data.match(/([0-9,.-]+)/), n = data.match(CURRENCY_REGEX), result = {}; if (m) { result.amount = parseFloat(m[1].replace(/[^0-9.-]/g, "")); } if (n) { result.currency = n[1]; } if (!m && !n) return data; return result } else if (Array.isArray(data)) { return data.map(v => processCurrencyAmount(v)); } else if (typeof data === "object" && data !== null) { let temp = {}; for (let key in data) { if (/^(amount|min|max)$/.test(key)) { let r = processCurrencyAmount(data[key]); if ("amount" in r) temp[key] = r.amount; if ("currency" in r) temp.currency = r.currency; } else { temp[key] = data[key]; } } return temp; } return data; } module.exports = { PreprocessFields, ParseItem, DataValidate, }