ca-mfe-page-scoring-ui
Version:
This is a react shell for the SPGI Platform MFE implementation
460 lines (399 loc) • 16.2 kB
text/typescript
import moment from 'moment';
import {
platforms, noDataCaseColorMapping, dateFormat, modelFamily,
stepIds, trackerStatus, financialsType, PRIMARY_SCORE_TYPE, letterGradeScores,
scalesNeedingModification, proSpreadScaleId, proSpreadPeriodMapping,
extractedDataFromPage
} from '@root/constants';
import { constants } from '@spglobal/ca-mfe-package-utils';
export const formatDate = (date, dateFormat) =>
{
if(date!== ''){
return (moment(date).format(dateFormat));
}
return date;
};
export const isFutureDate = date => date > new Date();
export const selectedFormData = (formdata, target) => {
const result = formdata.find(data => target in data);
return result ? result[target] : [];
};
export const selectedFormField = (formdata, target) => {
const result = formdata.find(item => item.name === target);
return result ? result.value : '';
};
export const checkValidFormInput = updatedFormData => {
const { modalFamily, companyType, industry, countryRegion } = updatedFormData;
let isModalFamily = false;
let isValidCompanyType = false;
let isValidIndustry = false;
let isValidCountryRegion = false;
if (modalFamily.value !== '' && modalFamily.value !== undefined) {
isModalFamily = true;
}
if (companyType.value !== '' && companyType.value !== undefined) {
isValidCompanyType = true;
}
if (countryRegion.value !== '' && countryRegion.value !== undefined) {
isValidCountryRegion = true;
}
if (industry.value !== '' && industry.value !== undefined) {
isValidIndustry = true;
}
return {
isModalFamily,
isValidCompanyType,
isValidIndustry,
isValidCountryRegion
};
};
export const stringformat = (s, replacedText) =>
s.toString().replace('{0}', replacedText);
export const generateFormInputList = formData => {
const inputList = [];
for (const key in formData) {
if (formData.hasOwnProperty(key)) {
inputList.push({
name: key,
value: formData[key]
});
}
}
return inputList;
};
export const sortFinancialStatement = list =>
list.sort((a, b) => b.fiscalYear - a.fiscalYear || b.fiscalQuarter - a.fiscalQuarter);
export const sortSelectPeriod = list => list.sort((a, b) => b.fiscalYear - a.fiscalYear);
export const checkPrivateCompanyType = companyTypeId => ['2', '4'].includes(companyTypeId);
export const checkPrivateCorporateOnly = companyTypeId => companyTypeId === '2';
export const getCompanyID = () => {
const href = window.location.search.toLowerCase();
const params = new URLSearchParams(href);
const company: any = {};
if (href.indexOf('companyid') > -1) {
company.companyID = params.get('companyid');
} else if (href.indexOf('alternateid') > -1) {
company.companyID = params.get('alternateid').toUpperCase();
company.isUnlinkCompany = true;
}
return company;
};
const isLocalhost = window.location.hostname === 'localhost';
export const apiBase = isLocalhost ? 'https://www.testciq.com' : '';
export const getFormattedDate = formatDate(new Date(),dateFormat);
export const getCaHeader = (isProp = false) => constants.isCiqPlatform || isLocalhost ?
(isProp ? platforms.CIQPROP :platforms.CIQ) : platforms.CIQPro;
export const getDataSourceHeader = (datasource = platforms.CIQ) =>
({ 'ca-datasource': (constants.isCiqPlatform || isLocalhost) ? datasource : platforms.CIQPro });
export const formatDateValue = val => {
const newDate = new Date(val);
const toPadStr = val => val.toString().padStart(2, '0');
const getMonth = toPadStr(newDate.getMonth()+1);
const getDate = toPadStr(newDate.getDate());
const getYear = newDate.getFullYear();
return `${getYear}-${getMonth}-${getDate}`;
};
const formatWithIntl = (num: number) => new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 20
}).format(num);
export const formatNumericValue = (val, decimals = 2, addComma = true) => {
const [integerPart, decimalPart = ''] = String(val).split('.');
const totalLength = integerPart.replace(/,/g, '').length + decimalPart.length;
const addDecimal = num => {
if (totalLength >= 16) {
return formatWithIntl(num);
}
const maxAllowedDecimals = Math.max(0, 16 - integerPart.length);
const paddedDecimal = decimalPart.length > decimals
? decimalPart.slice(0, decimals)
: String(decimalPart).padEnd(Math.min(decimals, maxAllowedDecimals), '0');
return decimals === 0 ? `${integerPart}` : `${integerPart}.${paddedDecimal}`;
};
const separateWithComma = num => {
if (totalLength >= 16) {
return formatWithIntl(num);
}
const maxAllowedDecimals = Math.max(0, 16 - integerPart.length);
const formattedNum = Math.floor(num).toLocaleString();
const paddedDecimal = decimalPart.length > decimals
? decimalPart.slice(0, decimals)
: String(decimalPart).padEnd(Math.min(decimals, maxAllowedDecimals), '0');
return decimals === 0 ? `${formattedNum}` : `${formattedNum}.${paddedDecimal}`;
};
if (val < 0) {
const absValue = Math.abs(val);
return `(${addComma ? separateWithComma(absValue) : addDecimal(absValue)})`;
}
return val !== null
? (addComma ? separateWithComma(Number(val)) : addDecimal(Number(val)))
: val;
};
const padValue = (value, max, pad = 0.99) => value === max ? max : value + pad;
const getRanges = (clr, primaryScoreType, pdConfig: any = {}) => {
if (primaryScoreType === PRIMARY_SCORE_TYPE.PD_PERCENT) {
const { index, bandCount } = pdConfig;
return {
fromPd: clr.fromPd,
toPd: clr.toPd,
from: ((bandCount - 1 - index) * (100 / bandCount)),
to: ((bandCount - 1 - index + 1) * (100 / bandCount))
};
}
/*Note: For letterGrade, chart will be plotted based on oneToHundred score*/
// else if (primaryScoreType === PRIMARY_SCORE_TYPE.LETTER_GRADE) {
// return {
// from: clr.fromLetterGrade,
// to: padValue(clr.toLetterGrade, 21)
// };
// }
return {
from: clr.from === 0 ? clr.from - 0.99 : clr.from,
to: padValue(Number(clr.to), 100)
};
};
export const getGreyedoutBandColor = rgType => noDataCaseColorMapping.find(ob => ob.risktype === rgType)?.color;
export const getRgScoreMapColorBands = ({ plotBandColors, thickness = 20, primaryScoreType, noDataCase = false }) =>
plotBandColors?.map((clr, index) => ({
rgType: clr.rgType,
color: noDataCase ? getGreyedoutBandColor(clr.rgType) : clr.color,
...getRanges(clr, primaryScoreType, { index, bandCount: plotBandColors?.length }),
thickness
}));
export const getTimestamp = data => {
const date = new Date(data);
return date.getTime();
};
export const getPercentageRounding = numbers => {
const fixedTo10Nums = numbers.map(x => parseFloat(parseFloat(x).toFixed(10)));
const total = Math.round(fixedTo10Nums.reduce((sum, e) => sum + e, 0));
const remainder = total - fixedTo10Nums.reduce((sum, x) => sum + Math.floor(x), 0);
const values = fixedTo10Nums
.map((value, index) => ({
index,
value,
remainder: Math.round((value % 1) * 100)
}))
.sort((a, b) => b.remainder - a.remainder)
.map(({value, index}, idx)=>({
index,
value: (idx<remainder) ? Math.ceil(value): Math.floor(value)
}))
.sort((a, b) => a.index - b.index)
.map(({value})=>value);
return values;
};
export const calculateRevenueFromAssets = (assetVal, usdConvRate, decimals=2) => {
if ((typeof assetVal === 'number') && usdConvRate) {
const result = (2.5981 * (Math.pow(parseFloat((assetVal * usdConvRate).toString()), 0.7571))) / usdConvRate;
return (truncateDecimal(result, decimals) ?? null);
}
return null;
};
export const truncateDecimal = (val, decimalPts = 1) => {
if (typeof val === 'number') {
const decimalFactor = Math.pow(10, decimalPts);
return Math.floor(val * decimalFactor) / decimalFactor;
}
return null;
};
export const getDifference = (arr1 = [], arr2 = []) => [
...arr1.filter(x => !arr2.includes(x)),
...arr2.filter(x => !arr1.includes(x))
];
export const extractIndustry = (metaData, industryCode) => {
const industriesData = metaData?.industries?.find(
c => c.modelFamily === modelFamily.Risk_Gauge
);
const industries = industriesData?.industryTypes?.find(c =>
c.options?.find(x => x.id.toString() === industryCode)
);
return {
...industries?.options?.find(x => x.id.toString() === industryCode),
...(industries?.type && { type: industries.type })
};
};
export const extractCountry = (metaData, countryCode) => {
const countriesData = metaData?.countries?.find(
c => c.modelFamily === modelFamily.Risk_Gauge
);
const validCountry = countriesData?.options?.find(c => c.code === countryCode);
return validCountry || {};
};
export const validateCountryIndustry = (metaData, industryCode, countryCode) => {
const validIndustry = extractIndustry(metaData, industryCode);
const validCountry = extractCountry(metaData, countryCode);
return Boolean(Object.keys(validCountry).length) && Boolean(Object.keys(validIndustry).length);
};
export const getEnabledAdjStepsIds = steppers => {
const adjSteps = steppers.find(steps => steps.stepId === stepIds.ADJUSTMENTS);
return adjSteps?.nestedSteps.filter(({ status }) =>
(status === trackerStatus.SELECTED || status === trackerStatus.ACTIVE || status === trackerStatus.COMPLETED))
.map(({ stepId }) => stepId);
};
export const checkAdjEnabled = (steppers = [], adjId = '') => {
const adjustments = steppers?.find(adj => adj.stepId === stepIds.ADJUSTMENTS);
return adjustments?.nestedSteps?.find(ns => ns.stepId === adjId)?.showStep;
};
export const checkEpdCompany = (companyType, financialType) => companyType === '2'
&& financialType === financialsType.WithoutFinancials;
export const checkIsNotValidInput = (analyticsName, val) => {
if(isNaN(val)){
return true;
}
const numVal = Number(val);
switch(analyticsName) {
case 'GOVERNMENT_OWNERSHIP':
case 'STANDALONE_PD_PARENT': {
return Boolean(numVal < 0 || numVal > 100);
}
case 'TOTAL_EMPLOYEES': {
return Boolean(numVal <= 0);
}
case 'ONLY_POSITIVE': {
return Boolean(numVal > 0);
}
default:{
return false;
}
}
};
export const rawPdFormatter = (rawPd, decimalPts = 4) => {
if (typeof rawPd === 'number') {
const tenPow = Math.pow(10, decimalPts);
return Math.floor(rawPd * tenPow * 100) / tenPow;
}
return null;
};
export const displayScore = (value, scoreScaleType) => {
if (scoreScaleType === PRIMARY_SCORE_TYPE.PD_PERCENT) {
return `${value?.toFixed(4)}%`;
} else if (scoreScaleType === PRIMARY_SCORE_TYPE.LETTER_GRADE) {
return letterGradeScores[value];
}
return `${value}`;
};
export const getPrimaryAndSecondaryScore = (scoreScaleType, scoreObj) => {
const scores: any = {};
if (scoreScaleType === PRIMARY_SCORE_TYPE.LETTER_GRADE) {
scores.primaryScore = scoreObj?.letterGradeNotch;
/*Note: For letterGrade also chart will be plotted based on oneToHundred score*/
scores.oneToHundred = scoreObj?.oneToHundred;
scores.secondaryScore = rawPdFormatter(scoreObj?.rawPd);
scores.secondaryScoreType = PRIMARY_SCORE_TYPE.PD_PERCENT;
} else if (scoreScaleType === PRIMARY_SCORE_TYPE.PD_PERCENT) {
scores.primaryScore = rawPdFormatter(scoreObj?.rawPd);
scores.secondaryScore = scoreObj?.letterGradeNotch;
scores.secondaryScoreType = PRIMARY_SCORE_TYPE.LETTER_GRADE;
scores.oneToHundred = scoreObj?.oneToHundred;
} else if (scoreScaleType === PRIMARY_SCORE_TYPE.ONE_TO_HUNDRED) {
scores.primaryScore = scoreObj?.oneToHundred;
scores.secondaryScore = scoreObj?.letterGradeNotch;
scores.secondaryScoreType = PRIMARY_SCORE_TYPE.LETTER_GRADE;
scores.oneToHundred = scoreObj?.oneToHundred;
}
return scores;
};
export const processProSpreadData = proSpreadData => {
const { scaleId, instances = [], companyType = '', gicsCode, currencyName = '', compFinancial='',
fromPage = '', isLinkedCompany = false, unlinkedSource = '' } = proSpreadData;
proSpreadData.fromPage = fromPage;
proSpreadData.companyType = companyType?.toUpperCase()?.indexOf('PUBLIC') > -1
? 'PUBLIC'
: (fromPage === extractedDataFromPage)
? companyType: 'PRIVATE';
proSpreadData.gicsCode = gicsCode ? `${gicsCode}` : gicsCode;
proSpreadData.currencyName = currencyName.replace(/(^.*\[|\].*$)/g, '');
proSpreadData.compFinancial = compFinancial;
proSpreadData.isLinkedCompany = isLinkedCompany;
proSpreadData.unlinkedSource = unlinkedSource;
instances.forEach(currInstance => {
const periodType = proSpreadPeriodMapping[currInstance.Period];
currInstance.Period = periodType;
currInstance.FQ = periodType === 'FY' ? 4 : currInstance.FQ;
if (scalesNeedingModification.includes(scaleId)) {
currInstance.Values.forEach(item => {
switch (scaleId) {
case proSpreadScaleId.thousands:
item.Value /= 1000;
break;
case proSpreadScaleId.lac:
item.Value /= 10;
break;
case proSpreadScaleId.billions:
item.Value *= 1000;
break;
case proSpreadScaleId.absolute:
item.Value /= 1000000;
break;
default:
item.Value *= 1;
}
});
}
});
return proSpreadData;
};
export const flatGroupsAndMetrics = (finData = []) => (
finData?.reduce((acc, curr)=>{
const newMetrics = curr.metrics.map(metric=>({
...metric,
groupName: curr.groupName
}));
acc.push(...newMetrics);
return acc;
}, [])
);
export const parseSizeAdjustmentNumber = (val, decimals) => {
const formattedValue = formatNumericValue(truncateDecimal(val, decimals), decimals);
return formattedValue;
};
export const getValueFormat = (numValue, decimals = 2) => {
const strNum = numValue?.toString().replaceAll(',', '');
const [restrictedLength, displayDecimals] = [18, decimals];
if (strNum?.length > restrictedLength) {
const truncatedValue = addDecimalBasedOnValue(numValue, displayDecimals);
return truncatedValue;
}
return numValue !== null ? formatNumericValue(numValue, displayDecimals) : numValue;
};
export const addDecimalBasedOnValue = (num, decimals = 2) => {
const [integerPart, decimalPart = ''] = String(num).split('.');
const totalLength = integerPart.length + decimalPart.length;
if (num === '' || num === null) {
return '';
}
if (totalLength >= 16) {
return num.toString();
}
const maxAllowedDecimals = Math.max(0, 16 - integerPart.length);
const paddedDecimal = decimalPart.length > decimals
? decimalPart.slice(0, decimals)
: String(decimalPart).padEnd(Math.min(decimals, maxAllowedDecimals), '0');
return decimals === 0 ? `${integerPart}` : `${integerPart}.${paddedDecimal}`;
};
export const trimValueToLength = (value, valDecLength = 18, valNumLength = 17) => {
if (value) {
const hasDecimal = value.includes('.');
const maxLength = hasDecimal ? valDecLength : valNumLength;
if (value.length >= maxLength) {
const trimmedValue = value.slice(0, maxLength);
return Number(trimmedValue);
}
}
return undefined;
};
export const convertToNumber = value => {
if (typeof value === 'number') {
return value;
} else if (typeof value === 'string' && !isNaN(value)) {
return Number(value);
}
return null;
};
export const setDecimalLength = (decimalPart, numValue, decimalValue = 2) => {
if (numValue !== null && numValue !== undefined && numValue !== '' && !isNaN(numValue)) {
return decimalPart === '' ? 0 : decimalPart.length;
}
return decimalPart === '' ? decimalValue : decimalPart.length;
};