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.
303 lines
14 KiB
303 lines
14 KiB
from ILE_MainWindow import Ui_MainWindow
|
|
import sys
|
|
import os
|
|
import pandas as pd
|
|
import json
|
|
from PyQt5 import QtWidgets
|
|
from datetime import datetime as dt
|
|
import ILExtract as ilx
|
|
from logging import debug, DEBUG, basicConfig
|
|
|
|
|
|
with open("settings.json") as s:
|
|
settings = json.loads(s.read())
|
|
if settings["debug"]:
|
|
basicConfig(filename='debug.log', encoding='utf-8', level=DEBUG)
|
|
|
|
debug("\n\n\n########################### VERSION = 3.10 ###########################\n\n\n")
|
|
debug("Running main.py...")
|
|
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|
"""
|
|
This is the main window that the users see. It's where they select the report file and report type.
|
|
"""
|
|
def __init__(self, *args, obj=None, **kwargs):
|
|
debug("MainWindow class init..")
|
|
super(MainWindow, self).__init__(*args, **kwargs)
|
|
self.setupUi(self)
|
|
|
|
# Allows the window to accept drag and drop files
|
|
# See dragEnterEvent an dropEvent
|
|
self.setAcceptDrops(True)
|
|
|
|
# Will hold the inputFile that is processed to create the ouput
|
|
self.inputFile = ""
|
|
# The location that the outputfile will be saved at
|
|
self.outputFile = ""
|
|
# Load the settings.json
|
|
with open("settings.json") as s:
|
|
self.settings = json.loads(s.read())
|
|
|
|
# Set the current report type to ACH as default
|
|
self.curReportType = "ach"
|
|
# Set the extract function to match
|
|
self.extract_function = ilx.ach
|
|
# Run the report_type change to check for saved input file paths
|
|
self.report_type_change()
|
|
#
|
|
# Actions
|
|
#
|
|
# Opens the file selection window and sets the input file accordingly
|
|
self.inputFileButton.clicked.connect(self.getfile)
|
|
# Opens file location dialog and sets the ouput accordingly
|
|
self.outputFileButton.clicked.connect(self.setOutput)
|
|
# Initates the processing of the selected file
|
|
self.processReportButton.clicked.connect(self.process_selection)
|
|
# Copies the last processed dataframe into the users clipboard
|
|
self.copyButton.clicked.connect(self.to_clipboard)
|
|
# Adjusts the extract function and file paths based on new report type in combo box
|
|
self.reportTypeCB.currentTextChanged.connect(self.report_type_change)
|
|
# Opens the folder specified in output file
|
|
self.openFolderButton.clicked.connect(lambda: self.openWithDefaultApp(self.outputFile.removesuffix(self.outputFile.split('/')[-1])))
|
|
# Opens the last processed excel document
|
|
self.openExcelButton.clicked.connect(lambda: self.openWithDefaultApp(self.outputFile))
|
|
# Sets the default text of the preview box
|
|
self.inputFilePreview.setText("Drag & Drop Input file here!")
|
|
|
|
def set_input(self, ifile):
|
|
"""
|
|
Triggerd by getFile() associated with the inputFile button
|
|
Reads the input file, sets the preview box to that excel doc and sets the
|
|
line edit and inputFile
|
|
Also sets the output file to a default structure. By default the output is put in the same
|
|
folder as the input file
|
|
! If the report type is minv_C we will save as .txt not .xlsx
|
|
Disables buttons since it is assumed this file has not yet been processed
|
|
- open excel button
|
|
- open folder button
|
|
- copy button
|
|
"""
|
|
debug(f"Executing set_input | input file: {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":
|
|
debug(f"MINV_C file selected ({self.inputFile})! Using .txt for output.")
|
|
self.outputFile = f"{outputRoot}{self.reportTypeCB.currentText()}_{dt.now().strftime('%Y%m%d_%H%M')}.txt"
|
|
else:
|
|
self.outputFile = f"{outputRoot}{self.reportTypeCB.currentText().replace('/','')}_{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)
|
|
# Enables the process button
|
|
self.check_ready_to_process()
|
|
|
|
def getfile(self):
|
|
"""
|
|
Launches the file selection dialog then inits the set_input function
|
|
"""
|
|
debug("Launching getfile.")
|
|
inFile = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file',directory=self.settings["defaultLocations"][self.curReportType])
|
|
# If the user does not select a file we just exit and do not change anything
|
|
if inFile[0] == '':
|
|
debug(f"User did not select an input file! {inFile}")
|
|
return 1
|
|
else:
|
|
self.set_input(inFile[0])
|
|
|
|
def dragEnterEvent(self, event):
|
|
"""
|
|
Allow users to drag file into the application
|
|
"""
|
|
debug(f"Drag and enter event triggered: {event}.")
|
|
if event.mimeData().hasUrls():
|
|
event.accept()
|
|
else:
|
|
event.ignore()
|
|
|
|
def dropEvent(self, event):
|
|
"""
|
|
Allow users to drag file into the application
|
|
"""
|
|
file = [u.toLocalFile() for u in event.mimeData().urls()][0]
|
|
debug(f"Drop event: {event} | {file}\n Running set_input")
|
|
self.set_input(file)
|
|
|
|
def setOutput(self):
|
|
"""
|
|
Launches dialog to select output location and naming.
|
|
"""
|
|
# 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")
|
|
debug(f"setOutput: {outFile}")
|
|
self.outputFile = f"{outFile[0]}__{dt.now().strftime('%Y%m%d_%H_%M')}.xlsx"
|
|
debug(f"setOutput | output file: {self.outputFile}")
|
|
self.check_ready_to_process()
|
|
|
|
def check_ready_to_process(self):
|
|
"""
|
|
Makes sure there is an input and output selected before allowing processing
|
|
Disables the button of outputFile is blank
|
|
"""
|
|
# 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)
|
|
debug("check_ready_to_process: Process button enabled")
|
|
if self.outputFile == "":
|
|
self.openFolderButton.setEnabled(False)
|
|
debug("check_ready_to_process: Process button disabled")
|
|
|
|
def process_selection(self):
|
|
"""
|
|
Kicks off the actual processing of the selected files
|
|
"""
|
|
debug(f"""Beginning file processing:
|
|
\nInput File: {self.inputFile} | Extract Function: {self.extract_function} | Report Type: {self.curReportType}""")
|
|
self.inputFilePreview.setText("Processing file...")
|
|
|
|
# Use the ILReport class to process the file\
|
|
# need to set the input file, extract function to use, and output location
|
|
dataframe = ilx.ILReport(
|
|
location= self.inputFile,
|
|
extraction_function=self.extract_function,
|
|
output_location=self.outputFile,
|
|
).process()
|
|
debug(f"Process Selction | dataframe:\n{dataframe}")
|
|
# 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
|
|
if dataframe.empty:
|
|
self.inputFilePreview.setText("Failed to create dataframe!")
|
|
return None
|
|
smallDF = dataframe.iloc[0:500,:]
|
|
self.inputFilePreview.setText(smallDF.to_html(index=False))
|
|
# Enable the excel button so users can open the file
|
|
self.openExcelButton.setEnabled(True)
|
|
# Enable the dopy button to copy this dataframe
|
|
self.copyButton.setEnabled(True)
|
|
# Checks if this input location is saved in settings.json.
|
|
# If there is no location for this report type it will be saved in the deafult
|
|
self.checked_for_saved()
|
|
# Allows user to open the output folder
|
|
self.openFolderButton.setEnabled(True)
|
|
|
|
def to_clipboard(self):
|
|
"""
|
|
Copies the file in output to the users clipboard
|
|
"""
|
|
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):
|
|
"""
|
|
Opens the linked item with it's default application (excel)
|
|
"""
|
|
debug(f"openWithDefaultApp: {item}")
|
|
os.startfile(item)
|
|
|
|
def checked_for_saved(self):
|
|
"""
|
|
Checks if a report type already has a default location
|
|
If the default location is blank, the current inputFileLocation will be saved
|
|
"""
|
|
if self.settings["defaultLocations"][self.curReportType] == '':
|
|
self.settings["defaultLocations"][self.curReportType] = ('/').join(self.inputFile.split('/')[:-1])
|
|
debug(f"checked_for_saved: saved new deafult location | {self.curReportType} | {self.settings['defaultLocations'][self.curReportType]}")
|
|
with open('settings.json', 'w') as s:
|
|
# Save changes to the setting
|
|
json.dump(self.settings, s)
|
|
|
|
def report_type_change(self):
|
|
debug(f"Changing report type | Was: {self.curReportType} -> {self.reportTypeCB.currentText()}")
|
|
# Adjust the report type according to the combo box
|
|
# This will be used in settings the the extract function and determining file locations
|
|
# Disable the process report button since the input and output fields are now blank
|
|
self.processReportButton.setEnabled(False)
|
|
self.openExcelButton.setEnabled(False)
|
|
self.copyButton.setEnabled(False)
|
|
self.openFolderButton.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() == "Past Due":
|
|
self.curReportType = "pastdue"
|
|
self.extract_function = ilx.pastdue
|
|
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
|
|
|
|
self.inputFile = ""
|
|
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.curReportType == "minv":
|
|
self.outputFile = f"{self.reportTypeCB.currentText()}_{dt.now().strftime('%Y%m%d_%H%M')}.txt"
|
|
else:
|
|
self.outputFile = f"{self.reportTypeCB.currentText().replace('/','')}_{dt.now().strftime('%Y%m%d_%H%M')}.xlsx"
|
|
self.outputFileLE.setText(self.outputFile)
|
|
|
|
debug(f"report_type_change | inputFile: {self.inputFile}")
|
|
debug(f"report_type_change | outputFile: {self.outputFile}")
|
|
self.check_ready_to_process()
|
|
|
|
# Defines the app
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
# Sets the style
|
|
app.setStyle("Fusion")
|
|
# Builds the main window
|
|
window = MainWindow()
|
|
window.setWindowTitle("IL Extract")
|
|
window.show()
|
|
# Starts the app
|
|
app.exec()
|
|
|