hkopendata
Version:
Access different Opendata API and data in Hong Kong
366 lines (360 loc) • 17.2 kB
JavaScript
const cmn = require("../common");
const moment = require("../moment");
const Coordinate = require("../_class").Coordinate;
function PreprocessFields(data) {
const FIELDS = {
regex: {
"product([a-z]{2,})$f-i": "$1",
"suminsured": "sumInsured",
"(yearly|annual)premium(hkd)?$": "annualPremium",
"monthlypremium(hkd)?$": "monthlyPremium",
"benefitpremium": "BenefitPremium",
"premium(hkd)?$": "Premium",
"(premium|insured)type": "$1Type",
"additionalcardannual": "additionalCardAnnual",
"foreigncurrencytxnfee": "foreignCurrencyTxnFee",
"minrepaymentamt|paypercent": "minRepay",
"^retailspending": "retailSpending",
"^cashadvance": "cashAdvance",
"retailspending": "RetailSpending",
"cashadvance": "CashAdvance",
"giftpoint": "giftPoint",
"permile": "asiaMiles",
"overduemincharge": "overdueminfeesCharge"
},
text: {
"ATM": "atm",
"Area": "region",
"BankSell": "sellRate",
"BankBuy": "buyRate",
"LastUpdateTime": "lastUpdate",
"ATMWithRMBWithdraw": "aTMwithRMB",
"customerservicehotline": "tel",
"investmentservicehotline": "tel",
"Telephone": "tel",
"BranchName": "name",
"SecuritiesTrading": "securitiesServices",
"SelfServiceBankingCentre": "automatedBankingCentre",
"WealthManagementCentre": "wealthManagement",
"InvestmentService": "investment",
"ForeignCcyNoteReservation": "foreignCurrencyReservable",
"ForeignBanknoteService": "foreignCurrencyAvailable",
"BochkIService": "iservice",
"EnrichBankingPriorityCounter": "priorityCounters",
"CapitalInvestEntrantSchemeACService": "captialInvestment",
"CorporateBankingCentre": "corporateCentre",
"BusinessAccountOpeningBranch": "businessAccountOpening",
"applicationchannel": "accessChannel",
"applicationmethod": "accessChannel",
"accountopenchannel": "accessChannel",
"interestcalculationmethodology": "compoundingFrequency",
"interestdepositfrequency": "interestFrequency",
"fees": "serviceCharge",
"servicefee": "serviceCharge",
"feesofchequebooks": "chequeBookFee",
"feesofreturnedcheques": "returnedChequeCharge",
"earlyupliftfee": "earlyUpliftFee",
"applicationappointmenturl": "urlAppoint",
"eligibilityofapplicant": "eligibility",
"remark": "remarks",
"moreinfo": "remarks",
"handlingfee": "handlingFee",
"loanamount": "loanAmount",
"annualisedpercentagerate": "repAPR",
"annualizedinterestrate": "repAPR",
"ratetableid": "rateId",
"repaymentperiod": "loanTerm",
"applicationformdownloadurl": "applyForm",
"reminderandremark": "remarks",
"cashrebates": "cashRebate",
"targetcustomer": "customerType",
"settlementdate": "settlementDate",
"workprocedure": "procedure",
"tenor": "investTenor",
"principalamount": "principalAmount",
"investmentamount": "principalAmount",
"tradingchannel": "tradingChannel",
"tradinghours": "tradingHour",
"currencypair": "currencyPair",
"ExchangeCode": "currencyPair",
"referenceassettype": "referenceAsset",
"investmentcurrency": "currency",
"policycurrency": "currency",
"premiumcurrency": "currency",
"securitymarket": "securityMarket",
"securitytrading": "securityTrading",
"securitymargintrading": "securityMarginTrading",
"monthlystocksavingplan": "monthlyStockSavingPlan",
"iposubscription": "ipoSubscription",
"ipofinancing": "ipoFinancing",
"insurer": "insuranceCompany",
"benefittype": "coverageType",
"lang": "language",
"basicplanenrollmentrequired": "standaloneBasis",
"premiumpaymentperiod": "premiumPaymentPeriod",
"coverageperiod": "coveragePeriod",
"dayofinsurance": "coverageDay",
"regioncountry": "coverageRegion",
"statementavailability": "statement",
"issuinginstitutions": "issuingInstitution",
"cardimgurl": "cardimg",
"YellowFormApplicationEndDate": "yellowFormEndDate"
},
number: {
"interestfreerepaymentperiod": "maxInterestFreeDay",
}
}
const FIELDS2 = {
regex: {
"^summary$$f-i": "description",
},
text: {
"subtype": "subType",
"instructions": "description",
"shortdescription": "description",
"leafleturl": "website",
"fee": "serviceCharge",
"cashAdvancehandlingfee": "cashAdvanceHandlingFee"
},
}
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 = {};
data = cmn.RenameFields(data, FIELDS);
data = cmn.RenameFields(data, FIELDS2);
for (let key in data) {
let ckey = key.toCamelCase();
if (ckey != "metadata") {
if (Array.isArray(data[key])) {
temp[ckey] = data[key].map(v => PreprocessFields(v));
} else if (typeof data[key] === "object" || typeof data[key] === "string") {
temp[ckey] = PreprocessFields(data[key]);
} else {
temp[ckey] = data[key]
}
}
}
return temp;
} else if (typeof data === "string") {
return data == "N/A" ? "" : data.replace(/''/g, "\"").trimRightChar("#").breakline();
}
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 = {};
for (let key in data) {
let m;
if (/^(Y|N)$/.test(data[key])) {
if (!("services" in temp)) temp.services = {};
temp.services[key] = data[key] == "Y";
} else if (/^(notice|rule)$/.test(key)) {
if (!("safeDepositBoxTC" in temp)) temp.safeDepositBoxTC = [];
temp.safeDepositBoxTC.push(data[key])
} else if (/^(latitude|longitude)$/.test(key)) {
if (!("coordinate" in temp)) temp.coordinate = {};
temp.coordinate[key] = data[key];
} else if (m = key.match(/^(retailSpending|cashAdvance)apr$/)) {
if (!("loanInterest" in temp)) temp.loanInterest = {
loanInterestTierBandSet: [{
tierBand: [{}]
}]
};
if (!("repAPR" in temp.loanInterest.loanInterestTierBandSet[0].tierBand[0])) temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].repAPR = {};
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].repAPR[m[1]] = data[key];
} else if (m = key.match(/^overdue(.+)$/)) {
if (!("loanInterest" in temp)) temp.loanInterest = {
loanInterestTierBandSet: [{
tierBand: [{}]
}]
};
if (!("overdue" in temp.loanInterest.loanInterestTierBandSet[0].tierBand[0])) temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue = {};
if (/^min/.test(m[1])) {
let o, n = m[1].match(/^min(.+)/);
if (o = n[1].match(/(.+)(hkd|rmb)/)) {
if (!(o[1] in temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue)) temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue[o[1]] = [];
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue[o[1]].push({
currency: o[2].toUpperCase(),
min: data[key].parseNumber()
});
} else {
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue[n[1]] = data[key];
}
} else {
let n;
if (n = m[1].match(/(.+)apr/)) {
if (!("repAPR" in temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue)) temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue.repAPR = {};
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue.repAPR[n[1].toCamelCase()] = data[key];
} else {
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].overdue[m[1].toCamelCase()] = data[key];
}
}
} else if (/(loanAmount|repAPR|rateId|cashRebate)$/.test(key)) {
if (!("loanInterest" in temp)) temp.loanInterest = {
loanInterestTierBandSet: [{
tierBand: [{}]
}]
};
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0][key] = data[key];
} else if (m = key.match(/loanTerm$|^minRepay(hkd|rmb)/)) {
if (!("loanInterest" in temp)) temp.loanInterest = {
loanInterestTierBandSet: [{
tierBand: [{}]
}]
};
if (!("referenceLoanRepayment" in temp.loanInterest.loanInterestTierBandSet[0].tierBand[0])) temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].referenceLoanRepayment = [{}]
if (/minRepay/.test(key)) {
if (!("minRepay" in temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].referenceLoanRepayment[0])) temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].referenceLoanRepayment[0].minRepay = [];
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].referenceLoanRepayment[0].minRepay.push({
currency: m[1].toUpperCase(),
amount: data[key].parseNumber()
});
} else {
temp.loanInterest.loanInterestTierBandSet[0].tierBand[0].referenceLoanRepayment[0][key] = data[key];
}
} else if (m = key.match(/(enh|cruise)BenefitPremium$/)) {
if (!("optionalBenefit" in temp)) temp.optionalBenefit = {};
temp.optionalBenefit[`${m[1] == "enh" ? "enhance": m[1]}Benefit`] = data[key];
} else if (/Frequency$/.test(key)) {
if (!("creditInterest" in temp)) temp.creditInterest = {
tierBand: [{}]
};
temp.creditInterest.tierBand[0][key] = data[key];
} else if (m = key.match(/^(min)?(monthly|annual)?[Pp]remium$/)) {
let c = `${m[2] ? m[2] : ""}Premium`.toCamelCase();
if (!("feesCharge" in temp)) temp.feesCharge = {};
if (!(c in temp.feesCharge)) temp.feesCharge[c] = {};
temp.feesCharge[c][m[1] ? m[1] : "amount"] = data[key].parseNumber();
} else if (m = key.match(/^card(name|type|id|img)$/)) {
if (!("card" in temp)) temp.card = {};
temp.card[m[1]] = data[key];
} else if (m = key.match(/^scheme(name|class|id)$/)) {
if (!("scheme" in temp)) temp.scheme = {};
temp.scheme[m[1] == "id" ? `_${m[1]}` : m[1]] = data[key];
} else if (m = key.match(/^stock(name|code)$/i)) {
if (!("stock" in temp)) temp.stock = {};
temp.stock[m[1].toCamelCase()] = data[key];
} else if (m = key.match(/^class(name|id)$/)) {
if (!("class" in temp)) temp.class = {};
temp.class[m[1] == "id" ? `_${m[1]}` : m[1]] = data[key];
} else if (m = key.match(/^(min)?(area|sumInsured|age)(fr|to)?$/)) {
if (!(data[key] == "N/A" || data[key] == "")) {
if (!(m[2] in temp)) temp[m[2]] = {};
temp[m[2]][m[1] == "min" || m[3] == "fr" ? "min" : "max"] = data[key].parseNumber();
}
} else if (m = key.match(/(local|oversea|os)spending(giftPoint|cashback|asiaMiles)/)) {
if (!("spendingRewards" in temp)) temp.spendingRewards = {};
if (!(m[2] in temp.spendingRewards)) temp.spendingRewards[m[2]] = {};
temp.spendingRewards[m[2]][m[1] == "os" ? "oversea" : m[1]] = data[key];
} else if (m = key.match(/min(accopenbalance|agereq|balancereq|annualsalaryreq)/)) {
let t;
switch (m[1]) {
case "accopenbalance":
t = "initialBalance";
break;
case "agereq":
t = "age";
break;
case "balancereq":
t = "balance";
break;
case "annualsalaryreq":
t = "annualSalary";
break;
}
if (!("eligibility" in temp)) temp.eligibility = {};
temp.eligibility[t] = {
min: data[key].parseNumber(),
};
} else if (/(highlight(long)?|preferselected|promotionoffer|benefit\d)$/.test(key) || /^welcominggift/.test(key)) {
if (!("featureBenefit" in temp)) temp.featureBenefit = [];
let val = data[key],
valid = true;
if (Array.isArray(val)) {
val = val.map(v => v.breakline()).filter(v => v.length != 0);
if (val.length == 0) valid = false;
} else if (typeof val === "string") {
val = val.breakline();
if (val.length == 0) valid = false;
}
if (valid) temp.featureBenefit.push(Array.isArray(val) ? val : [val]);
} else if (/(Charge|Fee|commission)$/.test(key)) {
if (!("feesCharge" in temp)) temp.feesCharge = {};
if (/commission/.test(key)) {
if (!("commissionFee" in temp.feesCharge)) temp.feesCharge.commissionFee = {}
temp.feesCharge.commissionFee[/^min/.test(key) ? "min" : "amount"] = data[key].parseNumber();
} else {
temp.feesCharge[key] = Array.isArray(data[key]) ? data[key] : data[key].breakline();
}
} else if (m = key.match(/(.+)fee(hkd|rmb)$/)) {
let fee = `${m[1]}Fee`;
if (!("feesCharge" in temp)) temp.feesCharge = {};
if (!(fee in temp.feesCharge)) temp.feesCharge[fee] = [];
temp.feesCharge[fee].push({
currency: m[2].toUpperCase(),
amount: data[key].parseNumber()
})
} else if (m = key.match(/^openingHours(.+)/)) {
if (!("opening" in temp)) temp.opening = {
mon: false,
tue: false,
wed: false,
thu: false,
fri: false,
sat: false,
sun: false,
ph: false,
};
if (/\d:\d+/.test(data[key])) {
let times = data[key].match(/\d+:\d+/g)
.reduce((p, c, i, l) => p.concat(i % 2 == 0 ? [l.slice(i, i + 2)] : []), [])
.map(v => v.join("-"));
if (m[1].indexOf("Sat") != -1) {
temp.opening.sat = times;
} else if (m[1].indexOf("Mon") != -1) {
Object.keys(temp.opening).filter(v => !/sat|sun|ph/.test(v)).map(v => temp.opening[v] = times);
} else {
temp.opening.sun = times;
temp.opening.ph = times;
}
}
} else if (/(buy|sell)Rate/.test(key)) {
temp[key] = data[key].parseNumber(2);
} else if (/(tel|fax)/.test(key)) {
let val = data[key];
if (typeof val === "string") {
val = val.match(/[0-9() +-]+/g);
if (val.length == 1) val = val[1];
}
temp[key] = val;
} else if (key == "yellowFormEndDate") {
temp[key] = moment(data[key], "YYYY/MM/DD").format("YYYY-MM-DD")
} else if (key == "lastUpdate") {
temp[key] = moment(data[key], "YYYY/MM/DD HH:mm:ss").format("YYYY-MM-DD HH:mm:ss")
} else if (!/hashtag|language|sortingid/.test(key)) {
let val = data[key];
if (typeof val === "string") {
if (key == "currency") {
val = val.split(",").map(v => v.breakline());
if (val.length == 1) val = val[0];
} else {
val = val.breakline();
}
}
temp[key] = val;
}
}
for (let key in temp) {
if (key == "coordinate") {
temp[key] = new Coordinate(temp[key]);
}
}
return temp;
}
module.exports = {
PreprocessFields,
ParseItem,
}