Fully working app. Interface needs work. Could use config to remember last opened location. Debug option not configured

master
Griffiths Lott 3 years ago
parent 6b375ecb72
commit 3aff9e4474
  1. 233
      ILParser.py
  2. 2
      getcol.py
  3. 246
      main.py
  4. 124
      ui.py

@ -0,0 +1,233 @@
from pandas import DataFrame
import re
from logging import debug, DEBUG, basicConfig, warn
from typing import Optional, Union
logConfig = basicConfig(filename='ILFormatter.log', encoding='utf-8', level=DEBUG, filemode='w')
CONTRACT_NO_REGEX = "\d{3}-\d{7}-\d{3}"
class Column:
def __init__(self, columnName: str, startIndex: int,
length: Optional[int] = None, endIndex: Optional[int] = None, valueRegex: Optional[str] = None) -> None:
assert length != None or endIndex != None, "You must specify either the length or endIndex of this column"
self.name = columnName
self.start = startIndex
self.end = endIndex if endIndex != None else startIndex + length
self.valueRegex = valueRegex
def __regex_check(self, value: str) -> bool:
if self.valueRegex == None: return True
return False if re.search(self.valueRegex, value) == None else True
def extract_column(self, line: str) -> tuple[str, Union[str, float]]:
debug(line)
end = self.end if self.end != -1 else len(line)
try:
dataValue: str = line[self.start : end].replace(',', '').strip()
except:
warn(f"NO DATA VALUE PRESENT ({self.name} | {self.start}-{self.end}): {line}")
if not self.__regex_check(dataValue):
warn(f"Invalid column value: Column: {self.name} value: {dataValue} regex: {self.valueRegex}")
try:
dataValue = float(dataValue)
except: pass
return self.name, dataValue
FIN_COLUMNS: list[Column] = [
Column("CUST.ID", startIndex= 0 ,endIndex = 21, valueRegex = "\d{8}"),
Column("CONTRACT.NO", startIndex= 21 ,endIndex = 37, valueRegex = "CONTRACT_NO_REGEX"),
Column("BUSINESS.TYPE", startIndex= 37 ,endIndex = 51, valueRegex = "\d{2}"),
Column("FED.ID", startIndex= 51 ,endIndex = 72, valueRegex = "\d{9}"),
Column("CUST.CREDIT.ACCT", startIndex= 72 ,endIndex = 89, valueRegex = "\d+"),
Column("CUSTOMER", startIndex= 89 ,endIndex = 120, valueRegex = None),
Column("LEASE.TYPE", startIndex= 120 ,endIndex = 131, valueRegex = None),
Column("EQUIPMENT.COST", startIndex= 131 ,endIndex = 146, valueRegex = None),
Column("CBR.", startIndex= 146 ,endIndex = 161, valueRegex = None),
Column("NET.INVESTMENT", startIndex= 161 ,endIndex = 176, valueRegex = None),
Column("ANNUAL.COMBINED.IRR", startIndex= 176 ,endIndex = 185, valueRegex = None),
Column("CONTRACT.TERM", startIndex= 185 ,endIndex = 199, valueRegex = None),
Column("INCOME.START.DATE", startIndex= 199 ,endIndex = 217, valueRegex = None),
Column("FIRST.PYMT.DATE", startIndex= 217 ,endIndex = 233, valueRegex = None),
Column("FIRST.PYMT.AMT", startIndex= 233 ,endIndex = 248, valueRegex = None),
Column("CONTRACT.PYMT.", startIndex= 248 ,endIndex = 263, valueRegex = None),
Column("INVOICE.CODE", startIndex= 263 ,endIndex = 276, valueRegex = None),
Column("INV.DAYS", startIndex= 276 ,endIndex = 285, valueRegex = None),
Column("INV.DUE.DAY", startIndex= 285 ,endIndex = 297, valueRegex = None),
Column("SEC.DEPOSIT.", startIndex= 297 ,endIndex = 312, valueRegex = None),
Column("IDC.AMOUNTS.", startIndex= 312 ,endIndex = 327, valueRegex = None),
Column("IDC.DATES.", startIndex= 327 ,endIndex = 338, valueRegex = None),
Column("RESIDUAL", startIndex= 338 ,endIndex = 353, valueRegex = None),
Column("MANAGERS.RESIDUAL", startIndex= 353 ,endIndex = 371, valueRegex = None),
Column("PROMOTION", startIndex= 371 ,endIndex = 381, valueRegex = None),
Column("PRODUCT.LINE", startIndex= 381 ,endIndex = 394, valueRegex = None),
Column("REGION", startIndex= 394 ,endIndex = 401, valueRegex = None),
Column("REGION.DESC.", startIndex= 401 ,endIndex = 432, valueRegex = None),
Column("BRANCH", startIndex= 432 ,endIndex = 439, valueRegex = None),
Column("BUSINESS.SEGMENT", startIndex= 439 ,endIndex = 456, valueRegex = None),
Column("LEAD.BANK", startIndex= 456 ,endIndex = 466, valueRegex = None),
Column("MRKTNG.REP", startIndex= 466 ,endIndex = 477, valueRegex = None),
Column("MRKTNG.REGION", startIndex= 477 ,endIndex = 491, valueRegex = None),
Column("REMIT.TO", startIndex= 491 ,endIndex = 500, valueRegex = None),
Column("PYMT.OPTION", startIndex= 500 ,endIndex = 512, valueRegex = None),
Column("BANK.CODE", startIndex= 512 ,endIndex = 522, valueRegex = None),
Column("TAPE.BANK.NUM", startIndex= 522 ,endIndex = 536, valueRegex = None),
Column("TAPE.ACCOUNT.NUM", startIndex= 536 ,endIndex = 557, valueRegex = None),
Column("TAPE.ACCT.TYPE", startIndex= 557 ,endIndex = 572, valueRegex = None),
Column("DEALER", startIndex= 572 ,endIndex = 583, valueRegex = None),
Column("PRIVATE.LABEL", startIndex= 583 ,endIndex = 597, valueRegex = None),
Column("RESID.METHOD", startIndex= 597 ,endIndex = 610, valueRegex = None),
Column("LATE.CHRG.EXMPT", startIndex= 610 ,endIndex = 626, valueRegex = None),
Column("INSURANCE.CODE", startIndex= 626 ,endIndex = 641, valueRegex = None),
Column("VARIABLE.DATE", startIndex= 641 ,endIndex = 655, valueRegex = None),
Column("VARIABLE.RATE", startIndex= 655 ,endIndex = 671, valueRegex = None),
Column("BILLING.CYCLE", startIndex= 671 ,endIndex = 685, valueRegex = None),
Column("UM.USER.DATE2", startIndex= 685 ,endIndex = 699, valueRegex = None),
Column("CR.ATTG.PHONE", startIndex= 699 ,endIndex = 715, valueRegex = None),
Column("GROSS.CONTRACT", startIndex= 715 ,endIndex = 730, valueRegex = None),
Column("ADV", startIndex= 730 ,endIndex = 734, valueRegex = None),
Column("PD.AMT.FINANCED ", startIndex= 735 ,endIndex = 751, valueRegex = None),
Column("PD.INCOME.START.DATE ", startIndex= 751 ,endIndex = 772, valueRegex = None),
Column("INVOICE.DESC", startIndex= 772 ,endIndex = 792, valueRegex = None),
Column("VARIABLE.PYMT.CODE ", startIndex= 792 ,endIndex = 811, valueRegex = None),
Column("PD.PAYMENT.AMT ", startIndex= 811 ,endIndex = 826, valueRegex = None),
Column("QUOTE.BUYOUT ", startIndex= 826 ,endIndex = 839, valueRegex = None),
Column("LATE.CHARGE.CODE ", startIndex= 839 ,endIndex = 856, valueRegex = None),
Column("LATE.CHRG.RATE ", startIndex= 856 ,endIndex = 871, valueRegex = None),
Column("M.DEF.COLLECTOR ", startIndex= 871 ,endIndex = 887, valueRegex = None),
Column("AM.ACH.LEAD.DAYS ", startIndex= 887 ,endIndex = 904, valueRegex = None),
Column("UNL POOL", startIndex= 904 ,endIndex = 915, valueRegex = None),
Column("PD RISK", startIndex= 915 ,endIndex = 926, valueRegex = None),
Column("PD RISK DATE.", startIndex= 926 ,endIndex = 940, valueRegex = None),
Column("LGD RISK", startIndex= 940 ,endIndex = 949, valueRegex = None),
Column("LGD DATE", startIndex= 949 ,endIndex = 960, valueRegex = None),
Column("Service By Others", startIndex= 960 ,endIndex = -1, valueRegex = None)
]
ASSET_COLS: list[Column] = [
Column("ASSET.#. ", startIndex= 0 ,endIndex = 9, valueRegex = None),
Column("CUST.ID. ", startIndex= 9 ,endIndex = 30, valueRegex = None),
Column("CONTRACT.NO ", startIndex= 30 ,endIndex = 46, valueRegex = None),
Column("CUST.CREDIT.ACCT ", startIndex= 46 ,endIndex = 63, valueRegex = None),
Column("CUST.NAME. ", startIndex= 63 ,endIndex = 84, valueRegex = None),
Column("EQUIP.DESC ", startIndex= 84 ,endIndex = 125, valueRegex = None),
Column("QUANTITY ", startIndex= 125 ,endIndex = 134, valueRegex = None),
Column("NEW.USED ", startIndex= 134 ,endIndex = 143, valueRegex = None),
Column("MODEL. ", startIndex= 143 ,endIndex = 164, valueRegex = None),
Column("A.MANUFACTURER.YEAR ", startIndex= 164 ,endIndex = 184, valueRegex = None),
Column("SERIAL.NUMBER. ", startIndex= 184 ,endIndex = 205, valueRegex = None),
Column("EQUIP.CODE ", startIndex= 205 ,endIndex = 216, valueRegex = None),
Column("EQUIP.CODE.DESC. ", startIndex= 216 ,endIndex = 247, valueRegex = None),
Column("ASSET.VENDOR ", startIndex= 247 ,endIndex = 260, valueRegex = None),
Column("ASSET.VENDOR.NAME. ", startIndex= 260 ,endIndex = 291, valueRegex = None),
Column("MANUFACTURER ", startIndex= 291 ,endIndex = 304, valueRegex = None),
Column("MANUFACT.NAME. ", startIndex= 304 ,endIndex = 335, valueRegex = None),
Column("UATB.EQUIP.ADDR1.45 ", startIndex= 335 ,endIndex = 381, valueRegex = None),
Column("UATB.EQUIP.ADDR2.45 ", startIndex= 381 ,endIndex = 427, valueRegex = None),
Column("EQUIP.CITY. ", startIndex= 427 ,endIndex = 453, valueRegex = None),
Column("EQUIP.STATE ", startIndex= 453 ,endIndex = 465, valueRegex = None),
Column("EQUIP.ZIP. ", startIndex= 465 ,endIndex = 476, valueRegex = None),
Column("STATE.TAX.CODE ", startIndex= 476 ,endIndex = 491, valueRegex = None),
Column("CNTY.TAX.CODE ", startIndex= 491 ,endIndex = 505, valueRegex = None),
Column("CITY.TAX.CODE ", startIndex= 505 ,endIndex = 519, valueRegex = None),
Column("PROP.STATUS ", startIndex= 519 ,endIndex = 531, valueRegex = None),
Column("EQUIP.COST ", startIndex= 531 ,endIndex = 546, valueRegex = None),
Column("EQUIP.COST.PCT ", startIndex= 546 ,endIndex = 561, valueRegex = None),
Column("PUR.OPTION ", startIndex= 561 ,endIndex = 572, valueRegex = None),
Column("PUR.OPTION. ", startIndex= 572 ,endIndex = 588, valueRegex = None),
Column("AS.RECOURSE.CODE ", startIndex= 588 ,endIndex = 605, valueRegex = None),
Column("RESID.AMT. ", startIndex= 605 ,endIndex = 620, valueRegex = None),
Column("BEG.DEPR.DATE ", startIndex= 620 ,endIndex = 634, valueRegex = None),
Column("OPER.LS.BEGIN.DATE ", startIndex= 634 ,endIndex = 653, valueRegex = None),
Column("OPER.LS.LIM ", startIndex= 653 ,endIndex = 665, valueRegex = None),
Column("OPER.LS.SALVAGE ", startIndex= 665 ,endIndex = -1, valueRegex = None)
]
CUST_COLS: list[Column] = [
Column("CONTRACT.NO ", startIndex= 0 ,endIndex = 16, valueRegex = None),
Column("CUST.CREDIT.ACCT ", startIndex= 16 ,endIndex = 33, valueRegex = None),
Column("CUST.ID. ", startIndex= 33 ,endIndex = 54, valueRegex = None),
Column("CUST.NAME. ", startIndex= 54 ,endIndex = 105, valueRegex = None),
Column("UATB.CUST.DBA. ", startIndex= 105 ,endIndex = 136, valueRegex = None),
Column("UATB.CUST.ADDRESS1.45 ", startIndex= 136 ,endIndex = 182, valueRegex = None),
Column("UATB.CUST.ADDRESS2.45 ", startIndex= 182 ,endIndex = 228, valueRegex = None),
Column("UATB.CUST.ADDRESS3.45 ", startIndex= 228 ,endIndex = 274, valueRegex = None),
Column("CUST.CITY. ", startIndex= 274 ,endIndex = 295, valueRegex = None),
Column("CUST.STATE ", startIndex= 295 ,endIndex = 306, valueRegex = None),
Column("CUST.ZIP ", startIndex= 306 ,endIndex = 317, valueRegex = None),
Column("GUAR.CODE.1 ", startIndex= 317 ,endIndex = 329, valueRegex = None),
Column("PRIN1/GUAR.NAME.1. ", startIndex= 329 ,endIndex = 365, valueRegex = None),
Column("PRIN1.ADD1. ", startIndex= 365 ,endIndex = 396, valueRegex = None),
Column("PRIN1.ADD2. ", startIndex= 396 ,endIndex = 427, valueRegex = None),
Column("PRIN1.CITY1. ", startIndex= 427 ,endIndex = 453, valueRegex = None),
Column("PRIN1.ST.1. ", startIndex= 453 ,endIndex = 464, valueRegex = None),
Column("ZIP.1. ", startIndex= 464 ,endIndex = 477, valueRegex = None),
Column("FED.ID/SS#1 ", startIndex= 477 ,endIndex = 503, valueRegex = None),
Column("GUAR.CODE.2.PRIN/GUAR.NAME.2. ", startIndex= 503 ,endIndex = 541, valueRegex = None),
Column("PRIN2.ADD2. ", startIndex= 541 ,endIndex = 572, valueRegex = None),
Column("PRIN2.ADDR2 ", startIndex= 572 ,endIndex = 603, valueRegex = None),
Column("PRIN2.CITY2. ", startIndex= 603 ,endIndex = 629, valueRegex = None),
Column("PRIN2.ST.2ZIP.2. ", startIndex= 629 ,endIndex = 653, valueRegex = None),
Column("FED.ID/SS#2 ", startIndex= 653 ,endIndex = 679, valueRegex = None),
Column("BILLING.NAME ", startIndex= 679 ,endIndex = 720, valueRegex = None),
Column("UATB.AR.ADDRESS1.45 ", startIndex= 720 ,endIndex = 766, valueRegex = None),
Column("UATB.AR.ADDRESS2.45 ", startIndex= 766 ,endIndex = 812, valueRegex = None),
Column("UATB.AR.ADDRESS3.45 ", startIndex= 812 ,endIndex = 858, valueRegex = None),
Column("AR.CITY. ", startIndex= 858 ,endIndex = 879, valueRegex = None),
Column("AR.STATE ", startIndex= 879 ,endIndex = 888, valueRegex = None),
Column("AR.ZIP ", startIndex= 888 ,endIndex = 899, valueRegex = None),
Column("AR.ATTN. ", startIndex= 899 ,endIndex = 920, valueRegex = None),
Column("UATB.CR.ATTG.NAME40. ", startIndex= 920 ,endIndex = 961, valueRegex = None),
Column("CR.SCORING ", startIndex= 961 ,endIndex = 972, valueRegex = None),
Column("FACILITY.SCORE ", startIndex= 972 ,endIndex = 988, valueRegex = None),
Column("SIC.CODE ", startIndex= 988 ,endIndex = -1, valueRegex = None),
]
DOB_COL: list[Column] = [
Column("CONTRACT.NO ", startIndex= 0 ,endIndex = 16, valueRegex = None),
Column("CUST.CREDIT.ACCT ", startIndex= 16 ,endIndex = 33, valueRegex = None),
Column("CUST.ID. ", startIndex= 33 ,endIndex = 54, valueRegex = None),
Column("GUAR.CODE.1 ", startIndex= 54 ,endIndex = 66, valueRegex = None),
Column("PRIN/GUAR.NAME.1. ", startIndex= 66 ,endIndex = 102, valueRegex = None),
Column("FED.ID/SS#1 ", startIndex= 102 ,endIndex = 128, valueRegex = None),
Column("DOB1 ", startIndex= 128 ,endIndex = 139, valueRegex = None),
Column("GUAR.CODE.2 ", startIndex= 139 ,endIndex = 151, valueRegex = None),
Column("PRIN/GUAR.NAME.2. ", startIndex= 151 ,endIndex = 177, valueRegex = None),
Column("FED.ID/SS#2 ", startIndex= 177 ,endIndex = -1, valueRegex = None)
]
def parse(ILOutput: str, columns: list[Column], dataColumnRegex: str = CONTRACT_NO_REGEX) -> DataFrame :
debug(ILOutput)
lines = ILOutput.splitlines()
dataDict = {}
for index, line in enumerate(lines):
debug(f"Index: {index} | {line}")
debug(re.search(dataColumnRegex, line))
if re.search(dataColumnRegex, line) == None: continue
for col in columns:
name, value = col.extract_column(line)
debug(f"name: {name} | value: {value}")
try:
dataDict[name].append(value)
except:
dataDict[name] = [value]
debug(dataDict)
try:
dataframe = DataFrame(dataDict)
except ValueError as ve:
debug({c: len(dataDict[c]) for c in dataDict.keys()})
debug(ve)
return dataframe
# extracts = [("FIN", FIN_COLUMNS), ("ASSET", ASSET_COLS), ("CUST", CUST_COLS), ("DOB", DOB_COL)]
# for file, columns in extracts:
# with open(f"Inputs/{file}", errors="replace") as reportFile:
# report: str = reportFile.read()
# # Removes characters that cause errors
# report: str = report.replace("^"," ")
# dataframe: DataFrame = parse(ILOutput=report, columns=columns)
# print(f"{file} dataframe: {dataframe}")

@ -10,7 +10,7 @@ for input in ["ASSET", "CUST", "DOB"]:
for line in report.splitlines():
print(line.strip())
if len(line.strip()) > 50:
matches = re.finditer('(\w|\.)+\s', line)
matches = re.finditer('(\w|\.|/|#)+\s', line)
for match in matches:
print(match)
colDict["ColName"].append(match.group())

@ -1,141 +1,115 @@
from pandas import DataFrame
import re
from ui import Ui_MainWindow
import ILParser
from PyQt5 import QtWidgets
from logging import debug, DEBUG, basicConfig, warn
from typing import Optional, Union
from sys import argv
from typing import Literal, Optional
from pandas import DataFrame, ExcelWriter
from datetime import datetime as dt
logConfig = basicConfig(filename='ILFormatter.log', encoding='utf-8', level=DEBUG, filemode='w')
logConfig = basicConfig(filename='ILFormatter.log', encoding='utf-8', level=DEBUG)
TEST_FIN_LOCATION = r"Inputs/FIN"
TEST_ASSET_LOCATION = R"Inputs\ASSET"
CONTRACT_NO_REGEX = "\d{3}-\d{7}-\d{3}"
class Column:
def __init__(self, columnName: str, startIndex: int,
length: Optional[int] = None, endIndex: Optional[int] = None, valueRegex: Optional[str] = None) -> None:
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
debug("MainWindow class init..")
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.processButton.setEnabled(False)
assert length != None or endIndex != None, "You must specify either the length or endIndex of this column"
self.name = columnName
self.start = startIndex
self.end = endIndex if endIndex != None else startIndex + length
self.valueRegex = valueRegex
def __regex_check(self, value: str) -> bool:
if self.valueRegex == None: return True
return False if re.search(self.valueRegex, value) == None else True
def extract_column(self, line: str) -> tuple[str, Union[str, float]]:
debug(line)
if self.end == -1:
end = len(line)
else:
assert len(line) >= self.end, f"Line is to short to extract value: len: {len(line)} > end : {self.end}"
end = self.end
dataValue: str = line[self.start : end].replace(',', '').strip()
if not self.__regex_check(dataValue):
warn(f"Invalid column value: Column: {self.name} value: {dataValue} regex: {self.valueRegex}")
try:
dataValue = float(dataValue)
except: pass
return self.name, dataValue
FIN_COLUMNS: list[Column] = [
Column("CUST.ID", startIndex= 0 ,endIndex = 21, valueRegex = "\d{8}"),
Column("CONTRACT.NO", startIndex= 21 ,endIndex = 37, valueRegex = "CONTRACT_NO_REGEX"),
Column("BUSINESS.TYPE", startIndex= 37 ,endIndex = 51, valueRegex = "\d{2}"),
Column("FED.ID", startIndex= 51 ,endIndex = 72, valueRegex = "\d{9}"),
Column("CUST.CREDIT.ACCT", startIndex= 72 ,endIndex = 89, valueRegex = "\d+"),
Column("CUSTOMER", startIndex= 89 ,endIndex = 120, valueRegex = None),
Column("LEASE.TYPE", startIndex= 120 ,endIndex = 131, valueRegex = None),
Column("EQUIPMENT.COST", startIndex= 131 ,endIndex = 146, valueRegex = None),
Column("CBR.", startIndex= 146 ,endIndex = 161, valueRegex = None),
Column("NET.INVESTMENT", startIndex= 161 ,endIndex = 176, valueRegex = None),
Column("ANNUAL.COMBINED.IRR", startIndex= 176 ,endIndex = 185, valueRegex = None),
Column("CONTRACT.TERM", startIndex= 185 ,endIndex = 199, valueRegex = None),
Column("INCOME.START.DATE", startIndex= 199 ,endIndex = 217, valueRegex = None),
Column("FIRST.PYMT.DATE", startIndex= 217 ,endIndex = 233, valueRegex = None),
Column("FIRST.PYMT.AMT", startIndex= 233 ,endIndex = 248, valueRegex = None),
Column("CONTRACT.PYMT.", startIndex= 248 ,endIndex = 263, valueRegex = None),
Column("INVOICE.CODE", startIndex= 263 ,endIndex = 276, valueRegex = None),
Column("INV.DAYS", startIndex= 276 ,endIndex = 285, valueRegex = None),
Column("INV.DUE.DAY", startIndex= 285 ,endIndex = 297, valueRegex = None),
Column("SEC.DEPOSIT.", startIndex= 297 ,endIndex = 312, valueRegex = None),
Column("IDC.AMOUNTS.", startIndex= 312 ,endIndex = 327, valueRegex = None),
Column("IDC.DATES.", startIndex= 327 ,endIndex = 338, valueRegex = None),
Column("RESIDUAL", startIndex= 338 ,endIndex = 353, valueRegex = None),
Column("MANAGERS.RESIDUAL", startIndex= 353 ,endIndex = 371, valueRegex = None),
Column("PROMOTION", startIndex= 371 ,endIndex = 381, valueRegex = None),
Column("PRODUCT.LINE", startIndex= 381 ,endIndex = 394, valueRegex = None),
Column("REGION", startIndex= 394 ,endIndex = 401, valueRegex = None),
Column("REGION.DESC.", startIndex= 401 ,endIndex = 432, valueRegex = None),
Column("BRANCH", startIndex= 432 ,endIndex = 439, valueRegex = None),
Column("BUSINESS.SEGMENT", startIndex= 439 ,endIndex = 456, valueRegex = None),
Column("LEAD.BANK", startIndex= 456 ,endIndex = 466, valueRegex = None),
Column("MRKTNG.REP", startIndex= 466 ,endIndex = 477, valueRegex = None),
Column("MRKTNG.REGION", startIndex= 477 ,endIndex = 491, valueRegex = None),
Column("REMIT.TO", startIndex= 491 ,endIndex = 500, valueRegex = None),
Column("PYMT.OPTION", startIndex= 500 ,endIndex = 512, valueRegex = None),
Column("BANK.CODE", startIndex= 512 ,endIndex = 522, valueRegex = None),
Column("TAPE.BANK.NUM", startIndex= 522 ,endIndex = 536, valueRegex = None),
Column("TAPE.ACCOUNT.NUM", startIndex= 536 ,endIndex = 557, valueRegex = None),
Column("TAPE.ACCT.TYPE", startIndex= 557 ,endIndex = 572, valueRegex = None),
Column("DEALER", startIndex= 572 ,endIndex = 583, valueRegex = None),
Column("PRIVATE.LABEL", startIndex= 583 ,endIndex = 597, valueRegex = None),
Column("RESID.METHOD", startIndex= 597 ,endIndex = 610, valueRegex = None),
Column("LATE.CHRG.EXMPT", startIndex= 610 ,endIndex = 626, valueRegex = None),
Column("INSURANCE.CODE", startIndex= 626 ,endIndex = 641, valueRegex = None),
Column("VARIABLE.DATE", startIndex= 641 ,endIndex = 655, valueRegex = None),
Column("VARIABLE.RATE", startIndex= 655 ,endIndex = 671, valueRegex = None),
Column("BILLING.CYCLE", startIndex= 671 ,endIndex = 685, valueRegex = None),
Column("UM.USER.DATE2", startIndex= 685 ,endIndex = 699, valueRegex = None),
Column("CR.ATTG.PHONE", startIndex= 699 ,endIndex = 715, valueRegex = None),
Column("GROSS.CONTRACT", startIndex= 715 ,endIndex = 730, valueRegex = None),
Column("ADV", startIndex= 730 ,endIndex = 734, valueRegex = None),
Column("PD.AMT.FINANCED ", startIndex= 735 ,endIndex = 751, valueRegex = None),
Column("PD.INCOME.START.DATE ", startIndex= 751 ,endIndex = 772, valueRegex = None),
Column("INVOICE.DESC", startIndex= 772 ,endIndex = 792, valueRegex = None),
Column("VARIABLE.PYMT.CODE ", startIndex= 792 ,endIndex = 811, valueRegex = None),
Column("PD.PAYMENT.AMT ", startIndex= 811 ,endIndex = 826, valueRegex = None),
Column("QUOTE.BUYOUT ", startIndex= 826 ,endIndex = 839, valueRegex = None),
Column("LATE.CHARGE.CODE ", startIndex= 839 ,endIndex = 856, valueRegex = None),
Column("LATE.CHRG.RATE ", startIndex= 856 ,endIndex = 871, valueRegex = None),
Column("M.DEF.COLLECTOR ", startIndex= 871 ,endIndex = 887, valueRegex = None),
Column("AM.ACH.LEAD.DAYS ", startIndex= 887 ,endIndex = 904, valueRegex = None),
Column("UNL POOL", startIndex= 904 ,endIndex = 915, valueRegex = None),
Column("PD RISK", startIndex= 915 ,endIndex = 926, valueRegex = None),
Column("PD RISK DATE.", startIndex= 926 ,endIndex = 940, valueRegex = None),
Column("LGD RISK", startIndex= 940 ,endIndex = 949, valueRegex = None),
Column("LGD DATE", startIndex= 949 ,endIndex = 960, valueRegex = None),
Column("Service By Others", startIndex= 960 ,endIndex = -1, valueRegex = None)
]
def parse(ILOutput: str, columns: list[Column], dataColumnRegex: str = CONTRACT_NO_REGEX) -> DataFrame :
debug(ILOutput)
lines = ILOutput.splitlines()
dataDict = {}
for index, line in enumerate(lines):
debug(f"Index: {index} | {line}")
debug(re.search(dataColumnRegex, line))
if re.search(dataColumnRegex, line) == None: continue
for col in columns:
name, value = col.extract_column(line)
try:
dataDict[name].append(value)
except:
dataDict[name] = [value]
dataframe = DataFrame(dataDict)
return dataframe
with open(TEST_FIN_LOCATION, errors="replace") as reportFile:
report: str = reportFile.read()
# Removes characters that cause errors
report: str = report.replace("^"," ")
finDataframe: DataFrame = parse(ILOutput=report, columns=FIN_COLUMNS)
print(f"FIN dataframe: {finDataframe}")
# File Locations
self.assetFile = None
self.custFile = None
self.dobFile = None
self.finFile = None
self.outputLocation = None
# Button Hooks
self.assetButton.clicked.connect(lambda: self._set_file(lineEdit= self.assetLE, selfFile="ASSET"))
self.custButton.clicked.connect(lambda: self._set_file(lineEdit= self.custLe, selfFile="CUST"))
self.dobButton.clicked.connect(lambda: self._set_file(lineEdit= self.dobLE, selfFile="DOB"))
self.finButton.clicked.connect(lambda: self._set_file(lineEdit= self.finLE, selfFile="FIN"))
self.outputButton.clicked.connect(lambda: self._set_output())
self.processButton.clicked.connect(lambda: self._process())
def _check_files(self):
debug(self.assetFile)
debug(self.custFile)
debug(self.dobFile)
debug(self.finFile)
ready = (
self.assetFile != None and
self.custFile != None and
self.dobFile != None and
self.finFile != None and
self.outputLocation != None
)
self.processButton.setEnabled(ready)
def _set_file(self, lineEdit: QtWidgets.QLineEdit, selfFile: Literal["ASSET", "CUST", "DOB", "FIN"]) -> str :
selectedFile: list[str] = QtWidgets.QFileDialog.getOpenFileName(self, "OpenFile")
debug(f"Selected file: {selectedFile}")
lineEdit.setText(selectedFile[0])
file = selectedFile[0] if selectedFile[0] != '' else None
if file != None and self.outputLocation == None:
self._auto_output_set(fileRoot='/'.join(file.split('/')[:-1]))
if selfFile == "ASSET":
self.assetFile = file
elif selfFile == "CUST":
self.custFile = file
elif selfFile == "DOB":
self.dobFile = file
elif selfFile == "FIN":
self.finFile = file
self._check_files()
def _set_output(self):
self.outputLocation = QtWidgets.QFileDialog.getSaveFileName(self, "Output file name") if QtWidgets.QFileDialog.getSaveFileName(self, "Output file name") != '' else None
self.outputLE.setText(self.outputLocation if self.outputLocation != None else '')
debug(f"Output Location: {self.outputLocation}")
def _auto_output_set(self, fileRoot):
self.outputLocation = fileRoot + f"/Portfolio Contracts - {dt.now().strftime('%Y-%m-%d')}.xlsx"
self.outputLE.setText(self.outputLocation if self.outputLocation != None else '')
debug(f"Auto set output: {self.outputLocation}")
def _setAssetFile(self):
self.assetFile = self._set_file(self.assetLE)
def _parse_file(self, filePath: str, parseColumns: list[ILParser.Column]) -> Optional[DataFrame]:
with open(filePath) as file:
report = file.read()
debug(f"Report: {report}")
data: DataFrame = ILParser.parse(report, parseColumns)
debug(f"Data: {data}")
if data.empty:
return None
else: return data
def _process(self):
assetDf: DataFrame = self._parse_file(self.assetFile, ILParser.ASSET_COLS)
debug(assetDf)
custDf: DataFrame = self._parse_file(self.custFile, ILParser.CUST_COLS)
debug(custDf)
dobDf: DataFrame = self._parse_file(self.dobFile, ILParser.DOB_COL)
debug(dobDf)
finDf: DataFrame = self._parse_file(self.finFile, ILParser.FIN_COLUMNS)
debug(finDf)
with ExcelWriter(self.outputLocation) as writer:
assetDf.to_excel(writer, sheet_name="ASSET")
custDf.to_excel(writer, sheet_name="CUST")
dobDf.to_excel(writer, sheet_name="DOB")
finDf.to_excel(writer, sheet_name="FIN")
debug("Finished writing to excel.")
# Defines the app
app = QtWidgets.QApplication(argv)
# Sets the style
app.setStyle("Fusion")
# Builds the main window
window = MainWindow()
window.setWindowTitle("IL Extract")
window.show()
# Starts the app
app.exec()

124
ui.py

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'PortfolioILFormatter.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(520, 314)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(10, 10, 491, 251))
self.widget.setObjectName("widget")
self.mainVbox = QtWidgets.QVBoxLayout(self.widget)
self.mainVbox.setContentsMargins(0, 0, 0, 0)
self.mainVbox.setObjectName("mainVbox")
self.assetHbox = QtWidgets.QHBoxLayout()
self.assetHbox.setObjectName("assetHbox")
self.assetButton = QtWidgets.QPushButton(self.widget)
self.assetButton.setMinimumSize(QtCore.QSize(99, 27))
self.assetButton.setMaximumSize(QtCore.QSize(99, 27))
self.assetButton.setObjectName("assetButton")
self.assetHbox.addWidget(self.assetButton)
self.assetLE = QtWidgets.QLineEdit(self.widget)
self.assetLE.setObjectName("assetLE")
self.assetHbox.addWidget(self.assetLE)
self.mainVbox.addLayout(self.assetHbox)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.custButton = QtWidgets.QPushButton(self.widget)
self.custButton.setMinimumSize(QtCore.QSize(99, 27))
self.custButton.setMaximumSize(QtCore.QSize(99, 27))
self.custButton.setObjectName("custButton")
self.horizontalLayout_2.addWidget(self.custButton)
self.custLe = QtWidgets.QLineEdit(self.widget)
self.custLe.setObjectName("custLe")
self.horizontalLayout_2.addWidget(self.custLe)
self.mainVbox.addLayout(self.horizontalLayout_2)
self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.dobButton = QtWidgets.QPushButton(self.widget)
self.dobButton.setMinimumSize(QtCore.QSize(99, 27))
self.dobButton.setMaximumSize(QtCore.QSize(99, 27))
self.dobButton.setObjectName("dobButton")
self.horizontalLayout_6.addWidget(self.dobButton)
self.dobLE = QtWidgets.QLineEdit(self.widget)
self.dobLE.setObjectName("dobLE")
self.horizontalLayout_6.addWidget(self.dobLE)
self.mainVbox.addLayout(self.horizontalLayout_6)
self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
self.finButton = QtWidgets.QPushButton(self.widget)
self.finButton.setMinimumSize(QtCore.QSize(99, 27))
self.finButton.setMaximumSize(QtCore.QSize(99, 27))
self.finButton.setObjectName("finButton")
self.horizontalLayout_7.addWidget(self.finButton)
self.finLE = QtWidgets.QLineEdit(self.widget)
self.finLE.setObjectName("finLE")
self.horizontalLayout_7.addWidget(self.finLE)
self.mainVbox.addLayout(self.horizontalLayout_7)
self.horizontalLayout_8 = QtWidgets.QHBoxLayout()
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.outputButton = QtWidgets.QPushButton(self.widget)
self.outputButton.setMinimumSize(QtCore.QSize(99, 27))
self.outputButton.setMaximumSize(QtCore.QSize(99, 27))
self.outputButton.setObjectName("outputButton")
self.horizontalLayout_8.addWidget(self.outputButton)
self.outputLE = QtWidgets.QLineEdit(self.widget)
self.outputLE.setObjectName("outputLE")
self.horizontalLayout_8.addWidget(self.outputLE)
self.mainVbox.addLayout(self.horizontalLayout_8)
self.processButton = QtWidgets.QPushButton(self.widget)
self.processButton.setObjectName("processButton")
self.mainVbox.addWidget(self.processButton)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 520, 24))
self.menubar.setObjectName("menubar")
self.menuSettings = QtWidgets.QMenu(self.menubar)
self.menuSettings.setObjectName("menuSettings")
self.menuLog_Level = QtWidgets.QMenu(self.menuSettings)
self.menuLog_Level.setObjectName("menuLog_Level")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionDebug_2 = QtWidgets.QAction(MainWindow)
self.actionDebug_2.setObjectName("actionDebug_2")
self.actionInfo = QtWidgets.QAction(MainWindow)
self.actionInfo.setObjectName("actionInfo")
self.actionInfo_2 = QtWidgets.QAction(MainWindow)
self.actionInfo_2.setObjectName("actionInfo_2")
self.menuLog_Level.addAction(self.actionInfo)
self.menuLog_Level.addAction(self.actionInfo_2)
self.menuLog_Level.addAction(self.actionDebug_2)
self.menuSettings.addAction(self.menuLog_Level.menuAction())
self.menubar.addAction(self.menuSettings.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Portfolio IL Output Formatter"))
self.assetButton.setText(_translate("MainWindow", "Select ASSET:"))
self.custButton.setText(_translate("MainWindow", "Select CUST:"))
self.dobButton.setText(_translate("MainWindow", "Select DOB:"))
self.finButton.setText(_translate("MainWindow", "Select FIN:"))
self.outputButton.setText(_translate("MainWindow", "Select Output"))
self.processButton.setText(_translate("MainWindow", "Process Files"))
self.menuSettings.setTitle(_translate("MainWindow", "Settings"))
self.menuLog_Level.setTitle(_translate("MainWindow", "Log Level"))
self.actionDebug_2.setText(_translate("MainWindow", "Debug"))
self.actionInfo.setText(_translate("MainWindow", "Warn"))
self.actionInfo_2.setText(_translate("MainWindow", "Info"))
Loading…
Cancel
Save