import math EPD_REASON_MAP = { "000": "No Adverse Factors", "001": "Available Credit On Bankcard Accounts Is Too Low", "006": "Bankcard Account Balances Are Too High In Proportion To Credit Limits", "010": "Too Many Delinquencies", "016": "Too Few Satisfactory Accounts", "020": "Length Of Time Revolving Accounts Have Been Established Is Too Short", "022": "Too many inquiries", "023": "Months Since Most Recent Delinquency Is Too Short", "024": "Too Many Serious Delinquencies", "026": "Number Of Delinquent Accounts Is Too High In Proportion To Total Number Of Accounts", "029": "Retail Account Balances Are Too High In Proportion To Credit Limits", "030": "Not Enough Retail Debt Experience", "031": "Revolving Account Balances Are Too High In Proportion To Credit Limits", "035": "Length Of Time Accounts Have Been Established Is Too Short", "037": "Too Few Bankcard Accounts", "043": "Too Few Open Revolving Accounts", "061": "Too Many Recently Opened Accounts", "066": "Too Many Serious Derogatory Items", "069": "Not Enough Debt Experience", "070": "Length Of Time Since Most Recent Bankcard Account Has Been Established Is Too Short", "074": "Too Few Satisfactory Revolving Accounts", "076": "Total Amount Past Due Is Too High", "103": "Not Enough Available Credit", "105": "Too Few Revolving Accounts", "117": "Length Of Time Since Most Seriously Delinquent Account Has Been Established Is Too Short", "132": "Too Few Open Accounts", "142": "Not Enough Balance Decreases On Active Non-Mortgage Accounts", "146": "Recency Of A Balance Overlimit On A Bankcard Account", "154": "Insufficient Payment Activity Over The Last Year", "155": "Recency Of Max Aggregate Bankcard Balance Over The Last Year", "158": "Too Few Open Retail Accounts", "174": "Too Few Open Bankcard Accounts", "181": "High Recent Balance Range Relative To Previous Balance Range", "192": "Not Enough Available Credit On Revolving Accounts", "201": "Length Of Time Since Oldest Auto Account Has Been Established Is Too Short" } VANTAGE_REASON_MAP = { "10": "Too few accounts paid as agreed", "11": "Oldest account was opened too recently", "12": "Delinquent or derogatory status on accounts is too recent", "13": "Balances on delinquent or derogatory accounts are too high", "14": "Too high proportion of accounts recently opened", "15": "Lack of recently reported accounts", "16": "Total of credit limits and loan amounts is too low", "17": "No open accounts in your credit file", "18": "Lack of account information", "19": "No negative reason code", "20": "Delinquent or derogatory bankcard", "21": "Too many bankcards with a high balance", "22": "Too few bankcards with high credit limit", "23": "Too high proportion of bankcards recently opened", "24": "Too many bankcards with high balance compared to credit limit", "25": "Too high proportion of balances from bankcards", "26": "Balances on bankcards are too high", "27": "Delinquent or derogatory status on revolving accounts is too recent", "28": "Average credit limit on open bankcards is too low", "29": "Balances on bankcards are too high compared with credit limits", "30": "Too few open revolving accounts", "31": "Not enough available credit on revolving accounts", "32": "Oldest bankcard was opened too recently", "33": "Not enough balance paid down over time on bankcards", "34": "Most recently opened revolving account is too new", "35": "Lack of revolving account information", "36": "Lack of recently reported revolving accounts", "37": "No open bankcards in your credit file", "38": "Lack of bankcard account information", "39": "Balances on delinquent or derogatory bankcards are too high", "4": "Balances on accts too high compared to credit limits and loan amounts", "04": "Balances on accts too high compared to credit limits and loan amounts", "40": "Too many delinquent or derogatory revolving accounts", "41": "Average time since revolving accounts opened is too recent", "42": "Total credit limits on open revolving accounts are too low", "43": "Too many revolving accounts with high balance compared to credit limit", "44": "Balances on revolving accts are too high compared with credit limits", "45": "Not enough balance paid down over time on retail accounts", "46": "Oldest revolving account was opened too recently", "47": "No open retail accounts in your credit file", "48": "Lack of retail account information", "49": "Not enough balance paid down over time on revolving accounts", "5": "Too many recent delinquencies", "05": "Too many recent delinquencies", "50": "Balances on personal installment accts too high compared to loan amts", "51": "Too few installment accounts recently paid as agreed", "52": "Delinquent or derogatory installment account", "53": "Not enough balance paid down over time on installment accounts", "54": "Delinquent or derogatory status on installment accounts is too recent", "55": "Lack of recently reported auto accounts", "56": "Lack of recently reported installment accounts", "57": "No open installment accounts in your credit file", "58": "Lack of installment account information", "59": "Balances on retail cards are too high compared with credit limits", "6": "Too many accounts recently opened", "06": "Too many accounts recently opened", "60": "Total delinquent or derogatory balances on real estate loans too high", "61": "No open first mortgage accounts in your credit file", "62": "Lack of first mortgage account information", "63": "Delinquent or derogatory real estate secured loan", "64": "Not enough balance paid down over time on real estate secured loans", "65": "Oldest real estate secured loan was opened too recently", "66": "Delinquent or derogatory status on real estate loans is too recent", "67": "No open real estate secured loans in your credit file", "68": "Lack of real estate secured loan information", "69": "Too high proportion of balances from loans not secured by real estate", "7": "You have too many delinquent or derogatory accounts", "07": "You have too many delinquent or derogatory accounts", "70": "Too high proportion of auto accounts are delinquent or derogatory", "71": "Not enough balance paid down over time on auto accounts", "72": "Too few auto accounts paid as agreed", "73": "Delinquent or derogatory auto account", "74": "Balances on auto accounts are too high compared with loan amounts", "75": "Payments on auto accounts less than scheduled amount", "76": "Delinquent or derogatory status on auto accounts is too recent", "77": "No open auto accounts in your credit file", "78": "Lack of auto account information", "79": "No negative reason code", "8": "Too few accounts recently paid as agreed", "08": "Too few accounts recently paid as agreed", "80": "Delinquent or derogatory student loan", "81": "Not enough balance paid down over time on student loans", "82": "Lack of recently reported student loans", "83": "No negative reason code", "84": "Number of inquiries was a factor in determining the score", "85": "Too many inquiries", "86": "Derogatory public records", "87": "Unpaid collections", "88": "Bankruptcy", "89": "No negative reason code", "9": "Delinquent or derogatory account", "09": "Delinquent or derogatory account", "90": "No open revolving accounts in your credit file", "91": "Balances on delinquent or derogatory revolving accounts are too high", "92": "Delinquent or derogatory first mortgage", "93": "Not enough balance paid down over time on first mortgage accounts", "94": "No negative reason code", "95": "No negative reason code", "96": "Too few open accounts", "97": "Too few accounts" } REASON_MAP = { 'evtg04': "System Generated", 'eads66': "System Generated", 's004s': "Length of time on file is too short", 'mt34s': "Not enough balance decreases on mortgage trades in the past 12 months", 'ct320': "Insufficient payment activity", 'us21s': "Length of time since most recent installment account has been established is too short", 'utlmag02': "Revolving account balances are too high in proportion to credit limits over the last 24 months", 'trv01': "Recency of a balance overlimit on a bankcard account", 'us34s': "Not enough balance decreases on installment trades in the past 12 months" } def generate_reason_codes(score_key, factors): # fallback to 4 null rows if no factors found if not isinstance(factors, list) or len(factors) == 0: return [{"code": None, "rank": None, "description": None} for _ in range(4)] reason_map = VANTAGE_REASON_MAP if score_key == "evtg04" else EPD_REASON_MAP if score_key == "eads66" else {} reason_codes = [] for f in factors[:4]: code = f.get("code") rank = f.get("rank") description = reason_map.get(str(code), "") reason_codes.append({ "code": code, "rank": rank, "description": description }) # pad to 4 while len(reason_codes) < 4: reason_codes.append({"code": None, "rank": None, "description": None}) return reason_codes def post_processing(processing_output, record): prediction = processing_output["prediction"] shape_reasoncode = processing_output["shape_reasoncode"] # grade mapping: if prediction < 0: grade = "M14" else: m = math.ceil(prediction / 0.01) m = max(m, 1) m = min(m, 14) grade = f"M{m}" # if prediction ≤ 0.04, not declined if prediction <= 0.04: return { "grade": grade, "reason_codes": [{ "code": None, "rank": None, "description": None }] } conditions = { 'evtg04': lambda x: x < 700, 'eads66': lambda x: x < 700, 's004s': lambda x: x < 12, 'mt34s': lambda x: x > 95, 'ct320': lambda x: x <= 3, 'us21s': lambda x: x <= 3, 'utlmag02': lambda x: x > 300, 'trv01': lambda x: x <= 3, 'us34s': lambda x: x > 90 } for item in shape_reasoncode: feat = item.get("feature") val = item.get("value") cond = conditions.get(feat) if cond: try: if cond(val): # If score-type feature (evtg04 or eads66) → full factors-based reason if feat in ("evtg04", "eads66"): return { "grade": grade, "reason_codes": generate_reason_codes(feat, record.get(f"{feat}_factors", [])) } else: # Other features → only 1 reason code based on REASON_MAP return { "grade": grade, "reason_codes": [{ "code": feat, "rank": "1", "description": REASON_MAP.get(feat, "Reason not mapped") }] } except Exception: continue # Default fallback return { "grade": grade, "reason_codes": [{ "code": feat, "rank": "1", "description": "No suitable Product Offerings found" }] }