Python Forum
Making a payroll class
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Making a payroll class
#11
deanhystad
here is why I am frustrated; I cannot use a dictionary. Why? because I can only use what I have learned, ridiculous but that is the rule. I emailed the teacher and the example he sent in response was using a list. So, that is the reason it is in a list. So, I can make an employee list, but not an employee class (because the instructions do not tell you to do so). I really am not trying to be difficult. I know I have to do the loop because it says I need to make that.

Here is what I have just for the inputData() it does work...in that I am asked the questions and I enter, when I go to exit...it brings me to the main menu. Now, is it saving to the list, I am not sure. The fact that I started learning python 9 weeks ago, and this is the first real issues I have had is not to bad...I guess.

def inputData(self):
        self.employeeInfo = []
        stay = True
        
        while stay:
            print("Would you like to")
            option = input(" <A>dd, <R>emove, or <E>xit?").upper()
            print("-----")

            if option == "E":
                stay = False

            elif option == "A":
                self.name = str(input("Enter employee's name:"))
                self.hours = int(input("Enter hours worked:"))
                self.pay = int(input("Enter pay rate:"))
                self.employeeInfo.append(self)
            elif option == "R":
                i = int(input("Enter employee's name:"))
                if len(self.employeeInfo) >= i:
                    self.employeeInfo(i)
                    print("Employee removed")
                else:
                    print("Employee not found")
            else:
                print(option, "is not a valid option")

        print("Return to main menu")
Reply
#12
If no dictionary, an Employee class becomes very attractive. In your code is inputData() a method of Employee or Payroll (what is self)? If the latter, what is this supposed to do?
Quote: elif option == "A":
self.name = str(input("Enter employee's name:"))
self.hours = int(input("Enter hours worked:"))
self.pay = int(input("Enter pay rate:"))
self.employeeInfo.append(self)

Methods should do one thing. inputData() should input data for an employee, it should not implement a menu which can input data for an employee or remove an employee.

I really think the design should have a class for Employee which has attributes for the name, pay rate and hours. It should also have a method for computing pay and for making a pretty string for when you want to see a list of employee information. It could have a method for entering information for a new employee, but often that kind of thing would be part of a class that manages the list of employees.
Reply
#13
I never felt the need for a class so I have no experience with Python classes.

If we use csv as our database for now and we want the data in the csv as:

Quote:headers = 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'

Make yourself an Excel in the form you want your data first, with the headers you want and some data, then you will get a better feel for what you are doing.

2 employees may have the same name, but individual emails are a handy form of unique ID. Or use a unique employee number.

I don't think you would normally enter the data for each employee by hand, but you can do this here. Someone would normally provide an Excel from a template for each month, from which you could read in the details and add them to your "database".

Each instance of this class is a company name, which is the name of the csv file you save the data in. Here I used:

ABC = Payroll('ABC Company Limited')
As the csv gets bigger, you can display the data for any given month and year like this: ABC.read_data('July', '2024')

from pathlib import Path
import csv
from tabulate import tabulate

class Payroll:
    """Each instance of this class is a company name
    The details of 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'
    will be saved in a csv named after the company name but with the spaces removed"""
    # can change this for an instance of Payroll
    # ABC.mydir = '/home/pedro/temp/'
    mydir = '/home/pedro/temp/company_payrolls/'
    # can change these too for a given instance of Payroll
    headers = 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'
    
    def __init__(self, company_name):
        self.company = company_name.replace(' ', '_')
        savecsv = Path(self.mydir + self.company + '.csv')
        #savepath.exists() # returns False the first time
        if not savecsv.exists():
            with open(savecsv, 'a') as nf:
                nf.write(self.headers + '\n')
        
    def add_employee2csv(self):
        path2csv = Path(self.mydir + self.company + '.csv')
        needed = self.headers.split(',')
        for data in needed:
            print(f'We need to know this: {data}')
        month = input('Enter the pay month ... ')
        year = input('Enter the year ... ')
        email = input('Enter the employee\'s email ... ')
        name = input('Enter the employee\'s name ... ')
        hours_worked = float(input(f'Enter the hours worked in this {month} ... '))
        pay_rate = float(input(f'Enter the employee\'s hourly pay rate ... '))
        overtime = float(input(f'Enter the employee\'s overtime hours or zero if no overtime in {month} ... '))
        bonus = float(input(f'Enter the employee\'s bonus in {month} ... '))    
        total_pay = hours_worked * pay_rate + overtime * (pay_rate + 0.5 * pay_rate) + bonus
        with open(path2csv, 'a') as db:
            rowwriter = csv.writer(db, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
            rowwriter.writerow([month, year, email, name, hours_worked, pay_rate, overtime, bonus, total_pay])

    def read_data(self, month, year):
        path2csv = Path(self.mydir + self.company + '.csv')
        with open(path2csv, 'r') as db:
            csv_reader = csv.reader(db, delimiter=',')
            print(f'Data for {self.company} for the month of {month} {year}')
            data_rows = []
            data_rows.append(self.headers.split(','))
            for row in csv_reader:
                if month and year in row:
                    data_rows.append(row)
            print(tabulate(data_rows,headers='firstrow',tablefmt='grid')
                

ABC = Payroll('ABC Company Limited')
ABC.__doc__ # returns the blurb at the top just under class Payroll:
ABC.add_employee2csv()
ABC.read_data('July', '2024')
I only entered 1 employee so far:

Output:
ABC.read_data('July', '2024') Data for ABC_Company_Limited for the month of July 2024 Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay ['July', '2024', '[email protected]', 'John Jones', '160.0', '16.0', '10.0', '100.0', '2800.0']
If I did this for real I would use MySQL to store the data. Create a new table for each company whose payroll you manage and whatever columns you may wish.

If you use the module tabulate you can get nice output:

Output:
ABC.read_data('July', '2024') Data for ABC_Company_Limited for the month of July 2024 +---------+--------+-------------------+---------------+----------------+------------+------------+---------+-------------+ | Month | Year | Email | Name | hours_worked | pay_rate | overtime | Bonus | Total_pay | +=========+========+===================+===============+================+============+============+=========+=============+ | July | 2024 | [email protected] | John Jones | 160 | 16 | 10 | 100 | 2900 | +---------+--------+-------------------+---------------+----------------+------------+------------+---------+-------------+ | July | 2024 | [email protected] | Jane Jones | 160 | 18 | 10 | 100 | 3250 | +---------+--------+-------------------+---------------+----------------+------------+------------+---------+-------------+ | July | 2024 | [email protected] | Jessica Jones | 120 | 15 | 0 | 0 | 1800 | +---------+--------+-------------------+---------------+----------------+------------+------------+---------+-------------+
Reply
#14
I get it...I should save this by using import of some kind. I know, that would be the simple way. But for this class, I need to save it in some way like this. (please, I have to do it this way because that is what I am told to do..I dont have a choice at this point. I cant use csv or anything else that has me using import at the start.)
I have a class....
I have things being saved to a list....
I have a program that works perfectly...to save to a list.
Now, I need it to save to a file...

Here is what I have...I know I have it ... almost ... what am I doing wrong.
 
class Payroll:
    def __init__(self):
        self.employeeData = []  # List to store all employee data

    # Read data from a file (not implemented yet)
    def readData(self):
        employee_file = open("employeeData.txt","r")
        for employee in employee_file.realines():
            print(employee)
        for hoursWk in employee_file.readlines():   #I know this is incorrect...but know that I need to save each thing because the teacher specifically says something about this.
            print(hoursWk)                                        #If he points it out in the instructions, it is key that I make sure it does it.
        for payRate in employee_file.readlines():
            print(payRate)
        employee_file.close()

    # Write data to a file (not implemented yet)
    def writeData(self):
        employee_file = open("employeeData.txt","a")
        for employee in employee_file.append:
            print(employee_file.write("\nemployee"))
        employee_file.close()
I promise..this will be the last thing I ask.
Reply
#15
Quote:I promise..this will be the last thing I ask.

I hope not, I only do this for fun, you will be taking away my fun!

open(path) has a mode 'x', for exclusive I believe. It will only open the file if it does not exist.

try: 
    with open(path, 'x') as file: 
        file.write("Hi Baby!") 
except FileExistsError: 
    print(f"The file '{path}' already exists.")
Without using even the module os, I think this works for creating a file if it does not exist, or reading it in if it exists

path2file = '/home/pedro/temp/company_payrolls/Peter_Company_Limited.txt'
headers = 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'
def open_file(path, headers):
    try:
        with open(path, 'x') as infile:
            print(f"The file {path} does not exist, so we will open it and write the headers to it ... ")
            infile.write(headers)
            with open(path, 'r') as infile:            
                data = infile.readlines()
        return data
    except FileExistsError:
        print(f"The file {path} already exists, so we will just read it in ... ")
        with open(path, 'r') as infile:
            f = open(path, 'r')
            data = f.readlines()
        return data
Now you can either create the file and write the headers to it, or open the file and read the lines.

file.readlines() returns a list of the lines in the file, including the newline character at the end of each line.

I will use the headers I think appropriate, but you can change them.

class Payroll:
    """Make a file if it doesn't exist. If it exists, read the file in"""
    # you can change these variables for an instance of Payroll
    path2file = '/home/pedro/temp/company_payrolls/'
    headers = 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'    
    
    def __init__(self, company_name):
        self.company = company_name.replace(' ', '_')
        
    def get_data(self):
        try:
            with open(self.path2file + self.company + '.txt', 'x') as infile:
                print(f"The file {self.path2file + self.company + '.txt'} does not exist, so we will open it and write the headers to it ... ")
                infile.write(self.headers)
            with open(self.path2file + self.company + '.txt', 'r') as infile:            
                self.data = infile.readlines()
        except FileExistsError:
            print(f"The file {self.path2file + self.company + '.txt'} already exists, so we will just read it in ... ")
            with open(self.path2file + self.company + '.txt', 'r') as infile:
                self.data = infile.readlines()
Now make an instance of Payroll:

p = Payroll('Peter Company Limited')
p.get_data()
Gives:
Output:
The file /home/pedro/temp/company_payrolls/Peter_Company_Limited.txt does not exist, so we will open it and write the headers to it ...
Output:
p.data ['Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay']
Reply
#16
Now add an employee:

class Payroll:
    """Make a file if it doesn't exist. If it exists, read the file in"""
    # you can change these variables for an instance of Payroll
    path2file = '/home/pedro/temp/company_payrolls/'
    headers = 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'    
    
    def __init__(self, company_name):
        self.company = company_name.replace(' ', '_')
        
    def get_data(self):
        try:
            with open(self.path2file + self.company + '.txt', 'x') as infile:
                print(f"The file {self.path2file + self.company + '.txt'} does not exist, so we will open it and write the headers to it ... ")
                infile.write(self.headers)
            with open(self.path2file + self.company + '.txt', 'r') as infile:            
                self.data = infile.readlines()
        except FileExistsError:
            print(f"The file {self.path2file + self.company + '.txt'} already exists, so we will just read it in ... ")
            with open(self.path2file + self.company + '.txt', 'r') as infile:
                self.data = infile.readlines()

    # at the moment there are no checks on the inputs
    def add_employee(self):
        needed = self.headers.split(',')
        for data in needed:
            print(f'We need to know this: {data}')
        month = input('Enter the pay month ... ')
        year = input('Enter the year ... ')
        email = input('Enter the employee\'s email ... ')
        name = input('Enter the employee\'s name ... ')
        hours_worked = float(input(f'Enter the hours worked in this {month} ... '))
        pay_rate = float(input(f'Enter the employee\'s hourly pay rate ... '))
        overtime = float(input(f'Enter the employee\'s overtime hours or zero if no overtime in {month} ... '))
        bonus = float(input(f'Enter the employee\'s bonus in {month} ... '))    
        total_pay = hours_worked * pay_rate + overtime * (pay_rate + 0.5 * pay_rate) + bonus
        mylist = [month, year, email, name, hours_worked, pay_rate, overtime, bonus, total_pay]
        mystring = ','.join(str(m) for m in mylist)
        self.data.append(mystring)
If we do:

p = Payroll('Peter Company Limited')
p.get_data()
Gives:
Output:
The file /home/pedro/temp/company_payrolls/Peter_Company_Limited.txt already exists, so we will just read it in ...
p.data
Gives
Output:
['Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay']
p.add_employee()
Gives:

Output:
We need to know this: Month We need to know this: Year We need to know this: Email We need to know this: Name We need to know this: hours_worked We need to know this: pay_rate We need to know this: overtime We need to know this: Bonus We need to know this: Total_pay Enter the pay month ... July Enter the year ... 2024 Enter the employee's email ... [email protected] Enter the employee's name ... Pedro Rodriguez Enter the hours worked in this July ... 160 Enter the employee's hourly pay rate ... 16 Enter the employee's overtime hours or zero if no overtime in July ... 6 Enter the employee's bonus in July ... 66
p.data
Gives:
Output:
['Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay', 'July,2024,[email protected],Pedro Rodriguez,160.0,16.0,6.0,66.0,2770.0']
Reply
#17
Or maybe better like this:

class Payroll:
    """Make a file if it doesn't exist. If it exists, read the file in"""
    # you can change these variables for an instance of Payroll
    path2file = '/home/pedro/temp/company_payrolls/'
    headers = 'Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay'    
    # self.data is not initialised here because it may not exist
    def __init__(self, company_name):
        self.company = company_name.replace(' ', '_')
        try:
            with open(self.path2file + self.company + '.txt', 'x') as infile:
                print(f"The file {self.path2file + self.company + '.txt'} does not exist, so we will open it and write the headers to it ... ")
                infile.write(self.headers + '\n')
            with open(self.path2file + self.company + '.txt', 'r') as infile:            
                self.data = infile.readlines()
        except FileExistsError:
            print(f"The file {self.path2file + self.company + '.txt'} already exists, so we will just read it in ... ")
            with open(self.path2file + self.company + '.txt', 'r') as infile:
                self.data = infile.readlines()        
        
    # at the moment there are no checks on the inputs    
    def add_employee(self):
        needed = self.headers.split(',')
        for data in needed:
            print(f'We need to know this: {data}')
        month = input('Enter the pay month ... ')
        year = input('Enter the year ... ')
        email = input('Enter the employee\'s email ... ')
        name = input('Enter the employee\'s name ... ')
        hours_worked = float(input(f'Enter the hours worked in this {month} ... '))
        pay_rate = float(input(f'Enter the employee\'s hourly pay rate ... '))
        overtime = float(input(f'Enter the employee\'s overtime hours or zero if no overtime in {month} ... '))
        bonus = float(input(f'Enter the employee\'s bonus in {month} ... '))    
        total_pay = hours_worked * pay_rate + overtime * (pay_rate + 0.5 * pay_rate) + bonus
        mylist = [month, year, email, name, hours_worked, pay_rate, overtime, bonus, total_pay]
        mystring = ','.join(str(m) for m in mylist)
        # add a newline character to each data set
        self.data.append(mystring + '\n')
        # directly append the new data to the file
        with open(self.path2file + self.company + '.txt', 'a') as output:
            output.write(mystring + '\n')
Output:
p.data ['Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay\n']
After running p.add_employee()

Output:
p.data ['Month,Year,Email,Name,hours_worked,pay_rate,overtime,Bonus,Total_pay\n', 'July,2024,[email protected],Pedro Rodriguez,160.0,16.0,6.0,66.0,2770.0\n']
Reply
#18
When the program starts it should read all employees into a list. If there are no employees, the list is empty.
def load_payroll(filename):
    employees = []
    try:
        with open(filename, "r") as file:
            for line in file:
                employees.append(exercise_left_to_the_student(line))
    except IOError:
        pass
    return employees
When the program quits, it writes the employee list to a file.
def save(payroll_file, employees):
    """Save payroll information to file."""
    try:
        with open(payroll_file, "w") as file:
            for emp in employees:
                exercise_left_to_the_student(emp)
    except IOError:
        print(f"Cannot open file '{payroll_file}' for writing.")
I see no reason for making an empty file unless there are no employees when the program exits.
Reply
#19
As I understand it, this is a payroll management programme for a given company, not a list of employees. Many more things could be added, pension, savings, health insurance, loans, God forbid income tax!

Thus, we really want a csv or an Excel or dataframe. A csv is a text file. To make it look correct, and to make it importable with column names, I believe it best to initialise the "database" with column names. That is what this does if the file does not exist, i.e. the first time around.

Normally, you would receive an Excel from a template, where someone, or some AI just fills in the hours on the basis of the email, which is unique. No one is going to put all the data in by hand every month, at least not if you had 1000 employees. The Excel can then be easily transferred to the "database".

Then pull out the data for any given month and year using the function: read_data(self, month, year)

Except, that function now needs changing too, because the poor girl cannot even use csv or os or pathlib. But that is easy to do, if she needs help with that.

But I know nothing about Python classes. If you can make it tidier, please do so!

Maybe she is allowed to use tabulate to display the results in a nice way! The whole exercise is not remotely real-world!
Reply
#20
Ok...I am going to send the final program to you (Pedroski55). It works. But do not want to post it here...kinda feel like someone would be cheating if I did, they could just copy and paste.

ps...I would be happy to send it to you (deanhystad) but your PM is full.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Making a Class Person ? Kessie1971 3 1,996 Apr-23-2023, 05:20 PM
Last Post: deanhystad
  Beginning Payroll code help EmRandall 6 10,966 Sep-18-2018, 01:21 AM
Last Post: EmRandall

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020