''' Created on Jun 24, 2015 If no arguments supplied script looks for CSV's in the folder specified by PATH. Can be run for single file by supplying file path as arg. This is the preferred way of doing things if the filewatcher powershell script is enabled to avoid collisions. Creates various inventory transactions needed to keep Jomar inventory up to date with what is entering shipping dept from the AS400 Transaction data is written to TXRYBW10 and processed immediatley. See 'Inventory Control' -> 'Visibility' -> 'Inventory Errors' for transactions that failed to process TEST_MODE, when True: * Prevents files being moved to the PROCESSED folder. * Will _NOT_ e-mail Aaron * Does not commit any transactions to the database @author: Wes ''' import ConfigParser import csv, os, smtplib from email import Encoders from email.MIMEBase import MIMEBase from email.MIMEImage import MIMEImage from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from email.Utils import COMMASPACE, formatdate from glob import glob import logging from operator import itemgetter import sys, sqlite3, re from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker TEST_MODE = False LOG_LEVEL = logging.DEBUG PATH = 'D:\\INTERFACE\\' PULL_ORDERS = ['U17435', 'U32614', 'U37151', 'U38029', 'U39702', 'U38664', 'U30473', 'U61478', 'U64631', 'U74634', 'U89496'] IN_FILE_FORMAT = "[\S\s]+as400_jsi_(?P[\d][\d][\d][\d][\d]).csv" ROLL_ERROR_RECIPIENTS = ['user1@example.com', 'user2@example.com'] TX_ERROR_RECIPIENTS = ['admin@example.com', 'user1@example.com'] ORDER = 0 LINE = 1 TICKET = 2 FINISHED_YARDS = 3 INVOICED_YARDS = 4 BIN = 5 WAREHOUSE = 6 WEIGHT = 7 SPAT = 8 SCOL = 9 #This script must be run as SUNBURYTEXTILE\Administrator #engine = create_engine("mssql+pyodbc://jsisbprod", echo=False) engine = create_engine("mssql+pyodbc://{}:{}@jsisbprod".format( os.environ['JOMAR_DB_UID'], os.environ['JOMAR_DB_PWD']), echo=False) Base = declarative_base(engine) metadata = Base.metadata class TXRYBW10(Base): __tablename__ = 'TXRYBW10' #Finished good interface table __table_args__ = {'autoload':True} class TXUYUV00(Base): __tablename__ = 'TXUYUV00' #Order Header __table_args__ = {'autoload':True} class TXUYUF01(Base): __tablename__ = 'TXUYUF01' #Order Detail __table_args__ = {'autoload':True} class TXRYRT00(Base): __tablename__ = 'TXRYRT00' #Roll/tag master __table_args__ = {'autoload':True} class ItemInfo(Base): __tablename__ = 'TXMYAT03' __table_args__ = {'autoload':True} ''' MODIFIED - 01/04/2018 - SB-20 This table is refreshed from the AS400 every hour. Need this to determine NAFTA qualification ''' class HTSCDSFL(Base): __tablename__ = 'as400_HTSCDSFL' __table_args__ = {'autoload':True} def load_email_credentials(): vault = sqlite3.connect('D:\\FTP\\ftp.db') cursor = vault.execute("SELECT * from accounts where code = 'DNR'") for row in cursor: return [row[1], row[2]] ''' I found the bulk of this function already written on someone's blog for sending mail via outlook 365. I made a few modifications for our purposes. ''' def send_mail(send_from, send_to, subject, text, files=None, data_attachments=None, server="smtp.example.com", port=25, tls=False, html=False, images=None, username=None, password=None, config_file=None, config=None): if files is None: files = [] if images is None: images = [] if data_attachments is None: data_attachments = [] if config_file is not None: config = ConfigParser.ConfigParser() config.read(config_file) if config is not None: server = config.get('smtp', 'server') port = config.get('smtp', 'port') tls = config.get('smtp', 'tls').lower() in ('true', 'yes', 'y') username = config.get('smtp', 'username') password = config.get('smtp', 'password') msg = MIMEMultipart('related') msg['From'] = send_from msg['To'] = send_to if isinstance(send_to, basestring) else COMMASPACE.join(send_to) msg['Date'] = formatdate(localtime=True) msg['Subject'] = subject msg.attach( MIMEText(text, 'html' if html else 'plain') ) for f in files: part = MIMEBase('application', "octet-stream") part.set_payload( open(f,"rb").read() ) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f)) msg.attach(part) for f in data_attachments: part = MIMEBase('application', "octet-stream") part.set_payload( f['data'] ) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % f['filename']) msg.attach(part) for (n, i) in enumerate(images): fp = open(i, 'rb') msgImage = MIMEImage(fp.read()) fp.close() msgImage.add_header('Content-ID', ''.format(str(n+1))) msg.attach(msgImage) smtp = smtplib.SMTP(server, int(port)) if tls: smtp.starttls() #if username is not None: # smtp.login(username, password) smtp.sendmail(send_from, send_to, msg.as_string()) smtp.close() ''' Opens connection to database returning a session object. ''' def loadSession(): global engine Session = sessionmaker(bind=engine) session = Session() return session def initializeLogger(name='log'): global logger logger = logging.getLogger(__name__) logger.setLevel(LOG_LEVEL) handler = logging.FileHandler('D:\\INTERFACE\\' + name + '.log') handler.setLevel(LOG_LEVEL) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) #Also send logs > DEBUG to stdout ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) ''' MODIFIED - 01/04/2018 - SB-20 This function is the brains of the operation regarding the NAFTA changes. Simple lookup ''' def NAFTA(session, pattern, color): logger.debug('Attempting lookup in HTSCDSFL to determine NAFTA qualification') logger.debug('SELECT * FROM as400_HTSCDSFL WHERE as400_HTSCDSFL.HTSPAT = \'%s\' AND as400_HTSCDSFL.HTSCLR = \'%s\';', pattern, color) p = session.query(HTSCDSFL).filter(HTSCDSFL.HTSPAT == pattern, HTSCDSFL.HTSCLR == color) for r in p: logger.debug('Record found in HTSCDSFL. Value of HTSNQC is \'%s\'', r.HTSNQC) if r.HTSNQC == 'X': return 'Y' else: return 'N' logger.debug('No record found in HTSCDSFL!') return 'N' if __name__ == '__main__': os.chdir('D:\INTERFACE') initializeLogger('Finished_Goods_Interface') logger.info('********************************************************************************') if(len(sys.argv) < 2): logger.info('No args supplied, processing all files in folder...') logger.debug('Obtaining list of CSV files in ' + PATH + '\\') files = glob(PATH + '\*.csv') # Verify file name and check for any previous transactions hung up in folder elif re.match(IN_FILE_FORMAT, sys.argv[1], re.IGNORECASE): logger.info('Processing single file: ' + sys.argv[1]) files = [sys.argv[1]] in_file_tx = int(re.match(IN_FILE_FORMAT, sys.argv[1], re.IGNORECASE).group('txnum')) #parse transaction num from file name and cast to int ls = glob(PATH + '\*.csv') for f in ls: test = int(re.match(IN_FILE_FORMAT, f, re.IGNORECASE).group('txnum')) if test < (in_file_tx - 1): logger.error('Trying to process: %s but %s is still in folder.', sys.argv[1], f) try: if not TEST_MODE: logger.info('Alerting relevant people to this situation...') msg = 'For whatever reason ' + f + ' has not finished processing before ' + sys.argv[1] + ' was submitted.' dnr_account = load_email_credentials() send_mail('donotreply@example.com', TX_ERROR_RECIPIENTS, 'TRANSACTION HUNG UP IN FGI!', msg, username=dnr_account[0], password=dnr_account[1], html=True) except: logger.error('Could not send e-mail. Check internet connection.') logger.error('THERE ARE OLDER TRANSACTIONS THAT HAVE NOT PROCESSED!! Exting...') sys.exit() # Invalid file specified. print error msg and exit. else: logger.error('File name: %s does not conform to as400_jsi_#####.csv', sys.argv[1]) sys.exit() session = loadSession() ''' Process each CSV file as400_jsi_00001.csv ''' for f in files: if not('as400_jsi' in f): logger.debug('Skipping ' + f + ' wrong file name...') continue rows = [] eRows = [] jLot = f.split('_')[2].split('.')[0] ERRORS = False #This can be set True in a number of places, This conditions whether or not an e-mail is sent logger.info('****************************************') logger.info('Loading file ' + f) #Read file fully into rows with open(f, 'rb') as g: reader = csv.reader(g) for row in reader: rows.append(row) #rows = sorted(rows, key=itemgetter(0, 1, 2)) #sort rows read by Order,Line,Piece since AS400 seems to supply data in random order logger.info(str(len(rows)) + ' records read.') tickets_processed = [] ''' Process each record in file ''' for row in reversed(rows): #print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN] if row[TICKET] in tickets_processed: logger.warn('Encountered ticket: ' + row[TICKET] + ' more than once in single transmission!') row.append('DUPLICATE TICKET ROW. DOUBLE CHECK YARDAGE/WEIGHT IN JOMAR!!!') eRows.append(row) ERRORS = True continue jOrder = 'null' jLine = -1 jItem = -1 #if(row[ORDER] in PULL_ORDERS): #logger.warn(row[ORDER] + ' is a pull order. Ignoring for now! Ticket: ' + row[TICKET]) #row.append('**IGNORING PULL ORDERS FOR NOW**') #eRows.append(row) #ERRORS = True #continue #Neil says no pieces in HLD bin should make it this far; just in case... if(row[BIN] == 'HLD' or (row[WAREHOUSE] == 'SH' and row[BIN] == ' ')): #print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN] logger.warn(row[WAREHOUSE] + '-' + row[BIN] + ' is not a valid bin! Ticket: ' + row[TICKET]) row.append('INVALID BIN') eRows.append(row) ERRORS = True continue if(float(row[FINISHED_YARDS]) <= 0): #print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN] logger.warn('Finished Yards must be > 0. Ticket: ' + row[TICKET]) row.append('Finished Yards must be > 0') eRows.append(row) ERRORS = True continue if(float(row[INVOICED_YARDS]) <= 0): #print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN] logger.warn('Invoiced Yards must be > 0. Ticket: ' + row[TICKET]) row.append('Invoiced Yards must be > 0') eRows.append(row) ERRORS = True continue #Get Jomar order number logger.info('Querying order header table for Jomar order number...') logger.debug('SELECT * FROM TXUYUV00 WHERE TXUYUV00.UBNR = \'001\' AND TXUYUV00.UPLT = \'00\' AND TXUYUV00.UBLA = \'SO\' AND (TXUYUV00.URBPO = \'' + row[ORDER] + '\' OR TXUYUV00.URBPO = \'' + row[ORDER][1:] + '\');') q = session.query(TXUYUV00).order_by(TXUYUV00.UBLN).filter(TXUYUV00.UBNR == '001', TXUYUV00.UPLT == '00', TXUYUV00.UBLA == 'SO', ((TXUYUV00.URBPO == row[ORDER]) | (TXUYUV00.URBPO == row[ORDER][1:]))) if(q.count() != 1 and row[ORDER]): #print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN] logger.warn('Cannot locate order (' + row[ORDER] + ') in Jomar system. Ticket: ' + row[TICKET]) row.append('Cannot locate order in Jomar system.') eRows.append(row) ERRORS = True continue for r in q: jOrder = r.UBLN logger.info('Jomar order: ' + jOrder + ' Ticket: ' + row[TICKET]) #print 'Jomar Order:', jOrder, 'Matches Sunbury Order:', row[ORDER] #Get Jomar Line Number logger.info('Querying order detail table for Jomar line number...') logger.debug('SELECT VLFN, VANR, XFLX33, XFLX34 FROM TXUYUF01 LEFT JOIN TXMYAT03 ON TXUYUF01.VBNR = TXMYAT03.XBNR AND TXUYUF01.VPLT = TXMYAT03.XPLT AND TXUYUF01.VANR = TXMYAT03.XANR WHERE TXUYUF01.VBNR = \'001\' AND TXUYUF01.VPLT = \'00\' AND TXUYUF01.VBLA = \'SO\' AND TXUYUF01.VBLN = \'' + jOrder + '\' AND TXUYUF01.VPLIN = ' + str(int(row[LINE])) + ';') q = session.query(TXUYUF01,ItemInfo).order_by(TXUYUF01.VBLN).filter(TXUYUF01.VBNR == ItemInfo.XBNR, TXUYUF01.VPLT == ItemInfo.XPLT, TXUYUF01.VANR == ItemInfo.XANR).filter(TXUYUF01.VBNR == '001', TXUYUF01.VPLT == '00', TXUYUF01.VBLA == 'SO', TXUYUF01.VBLN == jOrder, TXUYUF01.VPLIN == int(row[LINE])) if(q.count() != 1 and row[ORDER]): #print row[ORDER], row[LINE], row[TICKET], row[FINISHED_YARDS], row[INVOICED_YARDS], row[BIN] logger.warn('AS400 Line: ' + row[LINE] + ' does not exist on Jomar order: ' + jOrder) row.append('Jomar Order: ' + jOrder + ' - There are no lines on this order where AS400 Line Number = ' + row[LINE] + ' Ticket: ' + row[TICKET]) eRows.append(row) ERRORS = True continue skip = False #Verify pattern and color match b/t AS400 and Jomar for r in q: if(r[1].XFLX33.strip().upper() != row[SPAT].strip() and row[ORDER]): skip = True ERRORS = True logger.warn('Pattern Mismatch! Jomar: ' + r[1].XFLX33 + ' AS400: ' + row[SPAT].strip() + ' Ticket: ' + row[TICKET]) row.append('Pattern Mismatch! Jomar: ' + r[1].XFLX33 + ' AS400: ' + row[SPAT].strip()) eRows.append(row) if(int(r[1].XFLX34) != int(row[SCOL].strip()) and row[ORDER]): skip = True ERRORS = True logger.warn('Color Mismatch! Jomar: ' + r[1].XFLX34 + ' AS400: ' + row[SCOL].strip() + ' Ticket: ' + row[TICKET]) row.append('Color Mismatch! Jomar: ' + r[1].XFLX34 + ' AS400: ' + row[SCOL].strip()) eRows.append(row) if(skip == False): jLine = r[0].VLFN jItem = r[0].VANR logger.info('Jomar line: ' + str(jLine) + ' Ticket: ' + row[TICKET]) logger.info('Jomar item: ' + str(jItem) + ' Ticket: ' + row[TICKET]) #print ' Jomar Line:', jLine, 'Matches Sunbury Line:', row[LINE] if(skip): continue ''' Not doing this anymore for obvious reasons... Wish I would've gotten the information concerning the other possible inventory transactions before 04/20/16...... ''' #If a roll with this ticket number exists in the inventory, get rid of it #session.query(TXRYRT00).filter(TXRYRT00.RTRNR == row[TICKET]).delete() tickets_processed.append(row[TICKET]) #Made it past all of the error checks, add this ticket to processed list. logger.info('Querying Roll/Tag master to see if this ticket exists in Jomar already. Ticket: ' + row[TICKET]) logger.debug('SELECT COUNT(*) FROM TXRYRT00 WHERE TXRYRT00.RTRNR = ' + row[TICKET] + ';') q = session.query(TXRYRT00).filter(TXRYRT00.RTRNR == row[TICKET]).count() if(q == 0): #Do an 08 transaction... logger.info('No previous record of roll. Adding production receipt (08) transaction. Ticket: ' + row[TICKET]) i = TXRYBW10() i.RWAPL = '00' i.RWSPL = '00' i.RWSLA = 'SO' i.RWPLT = '00' i.RWBNR = '001' i.RWALA = 'SO' i.RWBEW = '08' ''' MODIFIED - 01/04/2018 - SB-20 Set RTGRML to 'US' for all rolls ''' i.RWGRML = 'US' if(row[ORDER] not in PULL_ORDERS): i.RWALN = jOrder i.RWSLN = jOrder i.RWAFN = jLine i.RWSFN = jLine else: i.RWALN = None i.RWSLN = None i.RWAFN = None i.RWSFN = None i.RWANR = jItem i.RWCHA = jLot i.RWRNR = row[TICKET] i.RWPGLG = row[FINISHED_YARDS] i.RWPLG = row[INVOICED_YARDS] if(row[BIN] == 'BAG'): logger.debug('BAG bin = BG Warehouse. Ticket: ' + row[TICKET]) i.RWLOC = 'BG' i.RWLPL = '' elif(row[BIN].strip() == '08'): logger.debug('08 bin = B8 Warehouse. Ticket: ' + row[TICKET]) i.RWLOC = 'B8' i.RWLPL = '' else: if(row[WAREHOUSE] == 'SH'): i.RWLPL = row[BIN] else: i.RWLPL = '' i.RWLOC = row[WAREHOUSE] i.RWPWG = int(float(row[WEIGHT])) #i.RWTWG = int(float(row[WEIGHT])) #This was screwing up proformas. This is a tare weight field and so it should be zero for our purposes i.RWTWG = 0 i.RWQAL = '01' ''' MODIFIED - 01/04/2018 - SB-20 Determine if roll is NAFTA qualified. ''' i.RWORCD = NAFTA(session, row[SPAT].strip(), str(int(row[SCOL].strip()))) session.add(i) else: logger.debug('Roll already exists. Querying roll/tag master again to determine what to do...') logger.debug('SELECT * FROM TXRYRT00 WHERE TXRYRT00.RTRNR = ' + row[TICKET] + ';') p = session.query(TXRYRT00).filter(TXRYRT00.RTRNR == row[TICKET]) for r in p: ''' MODIFIED 01/23/17 Ran across a scenario where rolls already shipped in Jomar came through the interface again. Now checking for shipped yardage here so the script can kick out an error instead of compounding issue. ''' if float(r.RTSLG) > 0: tickets_processed.remove(row[TICKET]) #Remove this ticket from processed list logger.warn('Shipped yards greater than zero! Ticket: %s', row[TICKET]) row.append('Shipped yards > 0') eRows.append(row) ERRORS = True continue current_lot = r.RTCHA current_inv_yards = r.RTPLG current_fin_yards = r.RTPGLG current_inv_lbs = r.RTPWG current_bin = r.RTLPL current_warehouse = r.RTLOC dest_bin = row[BIN] dest_warehouse = row[WAREHOUSE] #Adjust bins and warehouses for bagging machine nonsense.... if(row[BIN].strip() == '08'): dest_bin = '' dest_warehouse = 'B8' if(row[BIN].strip() == 'BAG'): dest_bin = '' dest_warehouse = 'BG' if((float(current_inv_yards) != float(row[INVOICED_YARDS])) or (int(current_inv_lbs) != int(float(row[WEIGHT]))) or (float(current_fin_yards) != float(row[FINISHED_YARDS]))): logger.info('Roll exists but length/weight differ. Adjusting via a Production Receipt transaction (08) Ticket: ' + row[TICKET]) if (float(current_inv_yards) != float(row[INVOICED_YARDS])): logger.debug('Current Invoiced Yards = %s Transmitted = %s Adjustment = %s', current_inv_yards, row[INVOICED_YARDS], str(float(row[INVOICED_YARDS]) - float(current_inv_yards))) if (int(current_inv_lbs) != int(float(row[WEIGHT]))): logger.debug('Current Weight = %s Transmitted = %s Adjustment = %s', current_inv_lbs, row[WEIGHT], (int(float(row[WEIGHT])) - int(current_inv_lbs))) if (float(current_fin_yards) != float(row[FINISHED_YARDS])): logger.debug('Current Finished Yards = %s Transmitted = %s Adjustment = %s', current_fin_yards, row[FINISHED_YARDS], str(float(row[FINISHED_YARDS]) - float(current_fin_yards))) i = TXRYBW10() i.RWAPL = '00' i.RWSPL = '00' i.RWSLA = 'SO' i.RWPLT = '00' i.RWBNR = '001' i.RWALA = 'SO' i.RWBEW = '08' if(row[ORDER] not in PULL_ORDERS): i.RWALN = jOrder i.RWSLN = jOrder i.RWAFN = jLine i.RWSFN = jLine else: i.RWALN = None i.RWSLN = None i.RWAFN = None i.RWSFN = None i.RWANR = jItem i.RWCHA = current_lot i.RWRNR = row[TICKET] i.RWPGLG = str(float(row[FINISHED_YARDS]) - float(current_fin_yards)) i.RWPLG = str(float(row[INVOICED_YARDS]) - float(current_inv_yards)) i.RWLOC = current_warehouse.strip() i.RWLPL = current_bin.strip() i.RWPWG = (int(float(row[WEIGHT])) - int(current_inv_lbs)) #i.RWTWG = (int(float(row[WEIGHT])) - int(current_inv_lbs)) i.RWTWG = 0 i.RWQAL = '01' session.add(i) if((current_bin.strip() != dest_bin.strip()) or (current_warehouse.strip() != dest_warehouse.strip())): logger.info('Roll exists but bins are different. Adding a movement (06) transaction...' + current_warehouse + '-' + current_bin + ' --> ' + dest_warehouse + '-' + dest_bin + ' Ticket: ' + row[TICKET]) i = TXRYBW10() i.RWAPL = '00' i.RWSPL = '00' i.RWSLA = 'SO' i.RWPLT = '00' i.RWBNR = '001' i.RWALA = 'SO' i.RWBEW = '06' if(row[ORDER] not in PULL_ORDERS): i.RWALN = jOrder i.RWSLN = jOrder i.RWAFN = jLine i.RWSFN = jLine else: i.RWALN = None i.RWSLN = None i.RWAFN = None i.RWSFN = None i.RWANR = jItem i.RWCHA = current_lot i.RWRNR = row[TICKET] i.RWPGLG = row[FINISHED_YARDS] i.RWPLG = row[INVOICED_YARDS] i.RWLOC = dest_warehouse.strip() #Xfer to warehouse i.RWLPL = dest_bin.strip() #Xfer to bin i.RWLO2 = current_warehouse.strip() #Xfer from warehouse i.RWLP2 = current_bin.strip() #Xfer from bin i.RWPL2 = '00' #Xfer from plant i.RWPWG = int(float(row[WEIGHT])) #i.RWTWG = int(float(row[WEIGHT])) i.RWTWG = 0 i.RWQAL = '01' session.add(i) if( (float(current_inv_yards) == float(row[INVOICED_YARDS])) and (int(current_inv_lbs) == int(float(row[WEIGHT]))) and (current_bin.strip() == dest_bin.strip()) and (current_warehouse.strip() == dest_warehouse.strip()) and (float(current_fin_yards) == float(row[FINISHED_YARDS]))): logger.info('Nothing appears to be different. Ignoring. Ticket: ' + row[TICKET]) ''' End record processing ''' #If errors were encountered during processing, e-mail the suspect rows to Aaron in a csv if ERRORS: #Dump eRows to errors.csv logger.debug("Dumping error rows to: " + PATH + "ERRORS\\ERRORS_" + jLot + ".csv") with open(PATH + "ERRORS\\ERRORS_" + jLot + ".csv", "wb") as g: writer = csv.writer(g) writer.writerows(eRows) try: if not TEST_MODE: logger.info('E-mailing Aaron list of error rows...') dnr_account = load_email_credentials() send_mail('donotreply@example.com', ROLL_ERROR_RECIPIENTS, 'AS400 -> Jomar Inventory Errors', 'See Attachment.', username=dnr_account[0], password=dnr_account[1], files=[PATH + "ERRORS\\ERRORS_" + jLot + ".csv"]) except: logger.error('Could not send e-mail. Check internet connection.') if not TEST_MODE: logger.debug("Moving " + f + " to " + "PROCESSED\\" + f.split("\\")[2]) os.rename(f, PATH + "PROCESSED\\" + f.split("\\")[2]) ''' End file processing ''' #When all files have been processed commit new records to interface table. if(TEST_MODE): logger.warn('**TEST MODE ENABLED. ROLLING BACK DATABASE TRANSACTIONS**') session.rollback() else: logger.debug('Committing database transactions...') session.commit() logger.debug('Closing database connection.') session.close()