ExtractData / app.py
DSatishchandra's picture
Update app.py
bdf43de verified
Raw
History Blame Contribute Delete
10.6 kB
import os
import subprocess
import sys
import re
import pandas as pd
from datetime import datetime
import logging
# Setting up logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s:%(name)s:%(lineno)d:%(levelname)s:%(message)s")
file_handler = logging.FileHandler(f'parse_payslip_info_log_{datetime.now().strftime("%d_%m_%Y__%H_%M_%S")}.log')
file_handler.setFormatter(formatter)
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(formatter)
logger.addHandler(file_handler)
if len(sys.argv) > 1:
if "--verbose" in sys.argv or "-v" in sys.argv:
logger.addHandler(stdout_handler)
def convert_pdf_to_text():
'''
convert_pdf_to_text()
Converts all pdf files in the current directory to text files
Output directory: .\converted_pdfs
'''
# directory to hold converted text files
logger.info("Creating converted_pdfs directory")
if os.system("mkdir converted_pdfs") != 0:
logger.error("Failed to create converted_pdfs directory")
sys.exit()
# list of pdf files in dir/sub-dir and save them to a text file
logger.info("Gathering list of full path to pdf files in the current directory/sub-directory")
os.system("dir /s /b *.pdf > allpdf.txt")
list_fnames = []
# put \n separated file path in a list
logger.info("Saving pdf file names to a list")
try:
with open('allpdf.txt', 'r') as fh:
list_fnames = list(fh.read().split('\n'))
except FileNotFoundError:
logger.error("Unable to open file: allpdf.txt")
sys.exit()
err_count = 0
# converting files one by one
logger.info("Generating text files from pdf")
for fname in list_fnames:
if fname:
target_text_fname = f"{get_fname_without_ext(fname)}.txt"
target_text_path = os.path.join('.\converted_pdfs', target_text_fname)
ret = subprocess.run(['bin64\pdftotext.exe', fname, target_text_path], capture_output=True)
if ret.returncode != 0:
logger.error(f"Error converting: {target_text_path}")
err_count += 1
# saving list of converted text files
logger.info("Gathering list of text file names")
os.system("dir converted_pdfs\*.txt /b > alltexts.txt")
return err_count
def get_fname_without_ext(fname):
'''
get_fname_without_ext(fname)
Returns the filename (without extension) from a filepath
Ex: Return 'ebook' from d:\pdffiles\ebook.pdf
'''
match = ""
match = re.search(r'(Payslip_.+)(.pdf)', fname)
if match:
return match.group(1)
else:
return ""
def get_list_of_converted_files():
'''
get_list_of_converted_files()
Returns list of full path of converted text files
'''
list_text_fnames = []
def append_path(fname):
return os.path.join(".\converted_pdfs", fname)
logger.info("Reading alltexts.txt, appending converted_pdfs directory name")
try:
with open("alltexts.txt", 'r') as fh:
list_text_fnames = list(fh.read().split('\n'))
except FileNotFoundError:
logger.error("alltexts.txt not found")
sys.exit()
return list(map(append_path, list_text_fnames))
def format_number_str(s):
'''
format_number_str(s)
Converts number string to float
Ex: 2,345.00 to 2345.00
'''
if s != "":
return float(s.replace(",", "").replace(" ", ""))
else:
return 0
def month_no_to_name(mnum):
'''
month_no_to_name(mnum)
Returns 3 letter month name from month number
Ex: 1 -> Jan, 2 -> Feb, 12 -> Dec
'''
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
return months[mnum-1]
class Payslip:
'''
Payslip class
Contains methods to extract payslip details from a converted text file
'''
def __init__(self):
self.pay_period = ""
self.pay_date = ""
self.epf_no = ""
self.uan_no = ""
self.basic_salary = ""
self.gross_salary = ""
self.net_salary = ""
self.gross_salary_ytd = ""
self.pf_amount = ""
self.pf_ytd = ""
self.income_tax = ""
self.income_tax_ytd = ""
self.raw_payslip_text = ""
def read_text(self, fname):
try:
with open(fname, 'r') as fh:
self.raw_payslip_text = fh.read()
self.pay_period = self.get_pay_period()
self.pay_date = self.get_pay_date()
self.epf_no = self.get_epf_number()
self.uan_no = self.get_uan_number()
self.basic_salary = self.get_basic_salary()
self.gross_salary = self.get_gross_sal()
self.net_salary = self.get_net_sal()
self.gross_salary_ytd =self.get_gross_sal_ytd()
self.pf_amount =self.get_pf()
self.pf_ytd = self.get_pf_ytd()
self.income_tax = self.get_income_tax()
self.income_tax_ytd = self.get_income_tax_ytd()
except FileNotFoundError:
logger.error(f"File not found: {fname}")
def get_pay_period(self):
match = re.search(r'Pay\sPeriod\s:\s?([\d.]+[\s\-]+[\d.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_pay_date(self):
match = re.search(r'Pay\sDate\n\n:\s?([\d+.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_epf_number(self):
match = re.search(r'Emp\sPF\sNumber:\s?([\w\/]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_uan_number(self):
match = re.search(r'UAN[\n]+:\s?(\d+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_basic_salary(self):
match = re.search(r'Basic\sSalary\n+([\d,.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_gross_sal(self):
match = re.search(r'Total\sGross\n+([\d,.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_net_sal(self):
match = re.search(r'NET\sPAY\n+([\d,.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_gross_sal_ytd(self):
match = re.search(r'YTD\sGROSS\n+([\d,.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_pf(self):
match = re.search(r'Provident\sFund\n+([\d.,]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_pf_ytd(self):
match = re.search(r'YTD\sEmployee\sPF\n+([\d.,]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_income_tax(self):
match = re.search(r'Income\sTax\n+([\d,.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
def get_income_tax_ytd(self):
match = re.search(r'YTD\sTAX\n+([\d,.]+)', self.raw_payslip_text)
if match:
return match.group(1)
else:
return ""
payslip_details = {
"pay_period": [],
"pay_date": [],
"basic_salary": [],
"gross_salary": [],
"net_salary": [],
"gross_salary_ytd": [],
"pf_amount": [],
"pf_ytd": [],
"income_tax": [],
"income_tax_ytd": [],
"epf_no": [],
"uan_no": [],
}
logger.info("Process started")
logger.info("Converting pdf to text")
convert_pdf_to_text()
list_txt_fnames = get_list_of_converted_files()
pay = Payslip()
logger.info("Saving payslip details from each text file to dictionary")
# Saving payslip details from each text file to dictionary
for fname in list_txt_fnames:
pay.read_text(fname)
payslip_details["pay_period"].append(pay.pay_period)
payslip_details["pay_date"].append(pay.pay_date)
payslip_details["epf_no"].append(pay.epf_no)
payslip_details["uan_no"].append(pay.uan_no)
payslip_details["basic_salary"].append(pay.basic_salary)
payslip_details["gross_salary"].append(pay.gross_salary)
payslip_details["net_salary"].append(pay.net_salary)
payslip_details["gross_salary_ytd"].append(pay.gross_salary_ytd)
payslip_details["pf_amount"].append(pay.pf_amount)
payslip_details["pf_ytd"].append(pay.pf_ytd)
payslip_details["income_tax"].append(pay.income_tax)
payslip_details["income_tax_ytd"].append(pay.income_tax_ytd)
logger.info("Creating dataframe from dictionary")
# creating dataframe from dictionary
pay_df = pd.DataFrame.from_dict(payslip_details)
logger.info("Formatting columns containing numeric data")
# Formatting columns containing numeric data
# converting object to float
pay_df['basic_salary'] = pay_df['basic_salary'].apply(lambda x: format_number_str(x))
pay_df['net_salary'] = pay_df['net_salary'].apply(lambda x: format_number_str(x))
pay_df['gross_salary'] = pay_df['gross_salary'].apply(lambda x: format_number_str(x))
pay_df['gross_salary_ytd'] = pay_df['gross_salary_ytd'].apply(lambda x: format_number_str(x))
pay_df['pf_amount'] = pay_df['pf_amount'].apply(lambda x: format_number_str(x))
pay_df['pf_ytd'] = pay_df['pf_ytd'].apply(lambda x: format_number_str(x))
pay_df['income_tax'] = pay_df['income_tax'].apply(lambda x: format_number_str(x))
pay_df['income_tax_ytd'] = pay_df['income_tax_ytd'].apply(lambda x: format_number_str(x))
logger.info("Creating Series to hold year and month")
# series to hold month and year
years = pay_df['pay_date'].apply(lambda x: re.sub(r'\d+.\d+.(\d{4})', r'\1',x))
months = pay_df['pay_date'].apply(lambda x: month_no_to_name(int(re.sub(r'\d+.(\d+).\d{4}', r'\1',x))))
logger.info("Appending year and month column to the start")
# appending year and month to the start
pay_df.insert(0, 'year', years)
pay_df.insert(1, 'months', months)
logger.info("Exporting to Excel")
# exporting to Excel
pay_df.to_excel("payslips.xlsx", index=False)
logger.info("Process completed successfully")