A PyQT GUI application for converting InfoLease report outputs into Excel files. Handles parsing and summarizing. Learns where files are meant to be store and compiles monthly and yearly summaries.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
InfoLeaseExtract/main.py

407 lines
20 KiB

from ILE_MainWindow import Ui_MainWindow
from defLocs_Window2 import Ui_defaultLocationDiag
import sys
import os
import pandas as pd
import json
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow, QPushButton
from datetime import datetime as dt
import ILExtract as ilx
print("Starting GUI... will anything else print?")
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.setAcceptDrops(True)
self.inputFile = ""
self.outputFile = ""
self.inputCustomized = False
with open("settings.json") as s:
self.settings = json.loads(s.read())
self.curReportType = "ach"
self.extract_function = ilx.ach
cbs = self.copyButton.objectName()
self.report_type_change()
# Actions
self.inputFileButton.clicked.connect(self.getfile)
self.outputFileButton.clicked.connect(self.setOutput)
self.processReportButton.clicked.connect(self.process_selection)
self.copyButton.clicked.connect(self.to_clipboard)
self.reportTypeCB.currentTextChanged.connect(self.report_type_change)
self.openFolderButton.clicked.connect(lambda: self.openWithDefaultApp(self.outputFile.removesuffix(self.outputFile.split('/')[-1])))
self.openExcelButton.clicked.connect(lambda: self.openWithDefaultApp(self.outputFile))
self.action_Default_Locations.triggered.connect(self.df)
self.inputFilePreview.setText("Drag & Drop Input file here!")
def set_input(self, ifile):
with open(ifile, errors="replace") as inF:
txt = inF.read()
self.inputFilePreview.setText(txt)
self.inputFileLE.setText(ifile)
self.inputFile = ifile
# This gets the actual file name
inFileEnd = ifile.split('/')[-1]
# Takes just the root of the input file
outputRoot = self.inputFile.removesuffix(inFileEnd)
# Automatically sets output to be in the same file as input, with a naming scheme
# The report type selected in the combo box will dictate the naming
if self.reportTypeCB.currentText() == "Minv_C":
self.outputFile = f"{outputRoot}{self.reportTypeCB.currentText()}_{dt.now().strftime('%Y%m%d_%H%M')}.txt"
else:
self.outputFile = f"{outputRoot}{self.reportTypeCB.currentText()}_{dt.now().strftime('%Y%m%d_%H%M')}.xlsx"
self.outputFileLE.setText(self.outputFile)
self.inputCustomized = True
self.openExcelButton.setEnabled(False)
self.copyButton.setEnabled(False)
self.openFolderButton.setEnabled(True)
# Enables the process button
self.check_ready_to_process()
def getfile(self):
inFile = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file')
if inFile[0] == '':
return 1
else:
self.set_input(inFile[0])
def df(self):
dlg = defLocWindow(self)
dlg.exec()
with open("settings.json") as s:
self.settings = json.loads(s.read())
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
file = [u.toLocalFile() for u in event.mimeData().urls()][0]
print(file)
self.set_input(file)
def setOutput(self):
# This allows the user to change the automatic naming and location
outFile = QtWidgets.QFileDialog.getSaveFileName(self, "Output file name")
if outFile[0] == '': return ''
if self.reportTypeCB.currentText() == "Minv_C":
self.outputFileLE.setText(f"{outFile[0]}__{dt.now().strftime('%Y%m%d_%H_%M')}.txt")
else:
self.outputFileLE.setText(f"{outFile[0]}__{dt.now().strftime('%Y%m%d_%H_%M')}.xlsx")
print(f"Output: {outFile}")
self.outputFile = f"{outFile[0]}__{dt.now().strftime('%Y%m%d_%H_%M')}.xlsx"
self.check_ready_to_process()
def check_ready_to_process(self):
# Makes sure there is an input and output selected before allowing processing
rtp = True if ((self.inputFile != "") & (self.outputFile != "")) else False
if rtp :
self.processReportButton.setEnabled(True)
if self.outputFile == "":
self.openFolderButton.setEnabled(False)
def process_selection(self):
print("Processing selection...")
self.inputFilePreview.setText("Processing file...")
# If only this was python 3.10 and we could use switch statments
# but this should get the job done
try:
# This is where the actual processing happens
# We create an ILReport object and pass in the nessecary information
print(self.inputFile)
print(self.outputFile)
dataframe = ilx.ILReport(
location= self.inputFile,
extraction_function=self.extract_function,
output_location=self.outputFile,
).process()
# The text preview box can have trouble loading the larger dataframes so
# they are trimmed to 500 so that the users can see if anything got messed up
smallDF = dataframe.iloc[0:500,:]
self.inputFilePreview.setText(smallDF.to_html(index=False))
except:
error = QtWidgets.QMessageBox()
error.setWindowTitle('Error Processing File!')
error.setText(f"Unable to process {self.inputFile}!\nPlease check input file!")
self.openExcelButton.setEnabled(True)
self.copyButton.setEnabled(True)
self.checked_for_saved()
self.openFolderButton.setEnabled(True)
self.inputCustomized = False
def preview_report(self):
df = pd.read_excel(self.outputFile)
self.inputFilePreview.setText(df.to_html())
def to_clipboard(self):
df = pd.read_excel(self.outputFile)
df.to_clipboard(excel=True)
def openFolder(self):
outputRoot = self.outputFile.removesuffix(self.outputFile.split('/')[-1])
os.startfile(outputRoot)
def openWithDefaultApp(self, item):
print(item)
outputRoot = self.outputFile.removesuffix(self.outputFile.split('/')[-1])
os.startfile(item)
def checked_for_saved(self):
if self.settings["defaultLocations"][self.curReportType]["dir"] == '':
self.settings["defaultLocations"][self.curReportType]["dir"] = ('/').join(self.inputFile.split('/')[:-1])
with open('settings.json', 'w') as s:
json.dump(self.settings, s)
def report_type_change(self):
# Adjust the report type according to the combo box
# This will be used in settings the the extract function and determining file locations
self.processReportButton.setEnabled(False)
if self.reportTypeCB.currentText() == "ACH":
self.curReportType = "ach"
self.extract_function = ilx.ach
elif self.reportTypeCB.currentText() == "Disposition":
self.curReportType = "disp"
self.extract_function = ilx.disposition
elif self.reportTypeCB.currentText() == "Gain Loss":
self.curReportType = "gl"
self.extract_function = ilx.gainloss
elif self.reportTypeCB.currentText() == "Lock Box":
self.curReportType = "lb"
self.extract_function = ilx.lockbox
elif self.reportTypeCB.currentText() == "Minv_C":
self.curReportType = "minv"
self.extract_function = ilx.minv
elif self.reportTypeCB.currentText() == "Net Inv. Loans":
self.curReportType = "niv"
self.extract_function = ilx.net_invest_trial_balance
elif self.reportTypeCB.currentText() == "NI Renewal":
self.curReportType = "ren"
self.extract_function = ilx.renewal_net_invest_trial_balance
elif self.reportTypeCB.currentText() == "NIV After":
self.curReportType = "niv"
self.extract_function = ilx.net_invest_trial_balance
elif self.reportTypeCB.currentText() == "PBP Epay":
self.curReportType = "pymt"
self.extract_function = ilx.payment_transactions
elif self.reportTypeCB.currentText() == "Unapplied":
self.curReportType = "uap"
self.extract_function = ilx.unapplied
elif self.reportTypeCB.currentText() == "VMCC":
self.curReportType = "pymt"
self.extract_function = ilx.payment_transactions
elif self.reportTypeCB.currentText() == "Wires":
self.curReportType = "pymt"
self.extract_function = ilx.payment_transactions
elif self.reportTypeCB.currentText() == "Returned Check":
self.curReportType = "pymt"
self.extract_function = ilx.payment_transactions
inputRoot = self.settings["defaultLocations"][self.curReportType]["dir"]\
.replace("{dd}",dt.now().strftime('%d'))\
.replace("{mm}",dt.now().strftime('%m'))\
.replace("{yyyy}",dt.now().strftime('%Y')).replace("{yy}",dt.now().strftime('%y'))
inputFile = self.settings["defaultLocations"][self.curReportType]["fn"]\
.replace("{dd}",dt.now().strftime('%d'))\
.replace("{mm}",dt.now().strftime('%m'))\
.replace("{yyyy}",dt.now().strftime('%Y')).replace("{yy}",dt.now().strftime('%y'))
self.inputFile = f"{inputRoot}/{inputFile}" if (inputFile != '') else inputRoot
self.inputFileLE.setText(self.inputFile)
# Automatically sets output to be in the same file as input, with a naming scheme
# The report type selected in the combo box will dictate the naming
if self.inputFile == "":
outputroot = ('/').join(self.inputFileLE.text().split('/')[:-1])
else:
outputroot = inputRoot + '/'
if self.curReportType == "minv":
self.outputFile = f"{outputroot}{self.reportTypeCB.currentText()}_{dt.now().strftime('%Y%m%d_%H%M')}.txt"
else:
self.outputFile = f"{outputroot}{self.reportTypeCB.currentText()}_{dt.now().strftime('%Y%m%d_%H%M')}.xlsx"
self.outputFileLE.setText(self.outputFile)
self.openExcelButton.setEnabled(False)
self.copyButton.setEnabled(False)
self.openFolderButton.setEnabled(True)
print(self.inputFile)
print(self.outputFile)
self.check_ready_to_process()
class defLocWindow(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# Create an instance of the GUI
self.ui = Ui_defaultLocationDiag()
# Run the .setupUi() method to show the GUI
self.ui.setupUi(self)
with open("settings.json") as s:
self.settings = json.loads(s.read())
self.load_user_settings()
# Add dest folder buttons
# Lambdas are used to create anon functions to that they don't execute at init
self.ui.ach_B.clicked.connect(lambda : self.add_defloc(self.ui.ach_B.objectName(), True))
self.ui.disp_B.clicked.connect(lambda : self.add_defloc(self.ui.disp_B.objectName(), True))
self.ui.gl_B.clicked.connect(lambda : self.add_defloc(self.ui.gl_B.objectName(), True))
self.ui.lb_B.clicked.connect(lambda : self.add_defloc(self.ui.lb_B.objectName(), True))
self.ui.minv_B.clicked.connect(lambda : self.add_defloc(self.ui.minv_B.objectName(), True))
self.ui.niv_B.clicked.connect(lambda : self.add_defloc(self.ui.niv_B.objectName(), True))
self.ui.ren_B.clicked.connect(lambda : self.add_defloc(self.ui.ren_B.objectName(), True))
self.ui.pymt_B.clicked.connect(lambda : self.add_defloc(self.ui.pymt_B.objectName(), True))
self.ui.uap_B.clicked.connect(lambda : self.add_defloc(self.ui.uap_B.objectName(), True))
# Text Changes
self.ui.ach_LE.textChanged.connect(lambda : self.add_defloc(self.ui.ach_B.objectName(), False))
self.ui.disp_LE.textChanged.connect(lambda : self.add_defloc(self.ui.disp_B.objectName(), False))
self.ui.gl_LE.textChanged.connect(lambda : self.add_defloc(self.ui.gl_B.objectName(), False))
self.ui.lb_LE.textChanged.connect(lambda : self.add_defloc(self.ui.lb_B.objectName(), False))
self.ui.minv_LE.textChanged.connect(lambda : self.add_defloc(self.ui.minv_B.objectName(), False))
self.ui.niv_LE.textChanged.connect(lambda : self.add_defloc(self.ui.niv_B.objectName(), False))
self.ui.ren_LE.textChanged.connect(lambda : self.add_defloc(self.ui.ren_B.objectName(), False))
self.ui.pymt_LE.textChanged.connect(lambda : self.add_defloc(self.ui.pymt_B.objectName(), False))
self.ui.uap_LE.textChanged.connect(lambda : self.add_defloc(self.ui.uap_B.objectName(), False))
self.ui.ach_FN.textChanged.connect(lambda : self.add_fn(self.ui.ach_FN.objectName()))
self.ui.disp_FN.textChanged.connect(lambda : self.add_fn(self.ui.disp_FN.objectName()))
self.ui.gl_FN.textChanged.connect(lambda : self.add_fn(self.ui.gl_FN.objectName()))
self.ui.lb_FN.textChanged.connect(lambda : self.add_fn(self.ui.lb_FN.objectName()))
self.ui.minv_FN.textChanged.connect(lambda : self.add_fn(self.ui.minv_FN.objectName()))
self.ui.niv_FN.textChanged.connect(lambda : self.add_fn(self.ui.niv_FN.objectName()))
self.ui.ren_FN.textChanged.connect(lambda : self.add_fn(self.ui.ren_FN.objectName()))
self.ui.pymt_FN.textChanged.connect(lambda : self.add_fn(self.ui.pymt_FN.objectName()))
self.ui.uap_FN.textChanged.connect(lambda : self.add_fn(self.ui.uap_FN.objectName()))
self.ui.optionBBox.accepted.connect(self.save_changes)
def add_defloc(self, button_name, use_filebrowser: bool):
folder = QtWidgets.QFileDialog.getExistingDirectory() if use_filebrowser else None
if folder == '':
use_filebrowser = False
report_type = button_name.split('_')[0]
if report_type == "ach":
if use_filebrowser:
self.ui.ach_LE.setText(folder)
else:
folder = self.ui.ach_LE.text()
elif report_type == "disp":
if use_filebrowser:
self.ui.disp_LE.setText(folder)
else:
folder = self.ui.disp_LE.text()
elif report_type == "gl":
if use_filebrowser:
self.ui.gl_LE.setText(folder)
else:
folder = self.ui.gl_LE.text()
elif report_type == "lb":
if use_filebrowser:
self.ui.lb_LE.setText(folder)
else:
folder = self.ui.lb_LE.text()
elif report_type == "minv":
if use_filebrowser:
self.ui.minv_LE.setText(folder)
else:
folder = self.ui.minv_LE.text()
elif report_type == "niv":
if use_filebrowser:
self.ui.niv_LE.setText(folder)
else:
folder = self.ui.niv_LE.text()
elif report_type == "ren":
if use_filebrowser:
self.ui.ren_LE.setText(folder)
else:
folder = self.ui.ren_LE.text()
elif report_type == "pymt":
if use_filebrowser:
self.ui.pymt_LE.setText(folder)
else:
folder = self.ui.pymt_LE.text()
elif report_type == "uap":
if use_filebrowser:
self.ui.uap_LE.setText(folder)
else:
folder = self.ui.uap_LE.text()
if folder != "":
self.settings["defaultLocations"][report_type]["dir"] = folder
def chg_folder(self, button_name):
report_type = button_name.split('_')[0]
if report_type == "ach":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.ach_FN.text()
elif report_type == "disp":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.disp_FN.text()
elif report_type == "gl":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.gl_FN.text()
elif report_type == "lb":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.lb_FN.text()
elif report_type == "minv":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.minv_FN.text()
elif report_type == "niv":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.niv_FN.text()
elif report_type == "ren":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.ren_FN.text()
elif report_type == "pymt":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.pymt_FN.text()
elif report_type == "uap":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.uap_FN.text()
def add_fn(self, button_name):
report_type = button_name.split('_')[0]
if report_type == "ach":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.ach_FN.text()
elif report_type == "disp":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.disp_FN.text()
elif report_type == "gl":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.gl_FN.text()
elif report_type == "lb":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.lb_FN.text()
elif report_type == "minv":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.minv_FN.text()
elif report_type == "niv":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.niv_FN.text()
elif report_type == "ren":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.ren_FN.text()
elif report_type == "pymt":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.pymt_FN.text()
elif report_type == "uap":
self.settings["defaultLocations"][report_type]["fn"] = self.ui.uap_FN.text()
def load_user_settings(self):
self.ui.ach_LE.setText(self.settings["defaultLocations"]["ach"]["dir"])
self.ui.ach_FN.setText(self.settings["defaultLocations"]["ach"]["fn"])
self.ui.disp_LE.setText(self.settings["defaultLocations"]["disp"]["dir"])
self.ui.disp_FN.setText(self.settings["defaultLocations"]["disp"]["fn"])
self.ui.gl_LE.setText(self.settings["defaultLocations"]["gl"]["dir"])
self.ui.gl_FN.setText(self.settings["defaultLocations"]["gl"]["fn"])
self.ui.lb_LE.setText(self.settings["defaultLocations"]["lb"]["dir"])
self.ui.lb_FN.setText(self.settings["defaultLocations"]["lb"]["fn"])
self.ui.minv_LE.setText(self.settings["defaultLocations"]["minv"]["dir"])
self.ui.minv_FN.setText(self.settings["defaultLocations"]["minv"]["fn"])
self.ui.niv_LE.setText(self.settings["defaultLocations"]["niv"]["dir"])
self.ui.niv_FN.setText(self.settings["defaultLocations"]["niv"]["fn"])
self.ui.ren_LE.setText(self.settings["defaultLocations"]["ren"]["dir"])
self.ui.ren_FN.setText(self.settings["defaultLocations"]["ren"]["fn"])
self.ui.pymt_LE.setText(self.settings["defaultLocations"]["pymt"]["dir"])
self.ui.pymt_FN.setText(self.settings["defaultLocations"]["pymt"]["fn"])
self.ui.uap_LE.setText(self.settings["defaultLocations"]["uap"]["dir"])
self.ui.uap_FN.setText(self.settings["defaultLocations"]["uap"]["fn"])
def save_changes(self):
print(self.settings)
with open('settings.json', 'w') as s:
json.dump(self.settings, s)
print(self.settings)
app = QtWidgets.QApplication(sys.argv)
app.setStyle("Fusion")
window = MainWindow()
window.setWindowTitle("IL Extract")
window.show()
app.exec()