Search code examples
pythoncsvclassabstractabc

Assign CSV values to class attributes, process output to csv using abstract classes


This question is a bit confusing so bear with me, it also has to be done with pure Python and no third-party modules.

I can't seem to assign the correct datatype to the CSV values and assign them to the class attributes.

I've tried every way I know, looked around for a few days for an answer...

The value error when FLOAT assigned the "line[4]", and the TypeError if I don't cast the variable.

I've also tried assigning them to new variables and casting the datatype. I think it is due to the rstrip() and strip() function output.

ValueError: could not convert string to float: ''
TypeError: '>' not supported between instances of 'str' and 'int'

Expected output:

------- Resident ID: {self._id} -------
Gross: {self.gross}
Net: {self.net}
Tax: {self.tax}

------- Holiday ID: {self._id} -------
Gross: {self.gross}
Net: {self.net}
Tax: {self.tax}
Visa: {self._visa}
YTD: {self._year_to_date}
from abc import ABC, abstractmethod
from datetime import date #To assign <timestamp> for export_summary()
from typing import List

class PayRecord(ABC):
    def __init__(self, id: int, hours:float, rates:float):
        self._id = id
        self._hours = hours
        self._rates = rates

    @abstractmethod
    def get_details(self): 
        pass

    @property
    def id(self):
        return self._id

    @property
    def gross(self):
        return (self._hours * self._rates)

    @property
    @abstractmethod
    def tax(self):
        return self.tax

    @property
    def net(self):
        return (self.gross - self.tax)
        
class ResidentPayRecord(PayRecord):
    def __init__(self, id: int, hours:float, rates:float):
        super().__init__(id, hours, rates)

    def get_details(self):
        return f"------- Resident ID: {self._id} -------\n\nGross: {self.gross}\nNet: {self.net}\nTax: {self.tax}\n"

    @property
    def tax(self):
        return calc_res_tax(self.gross)

class WorkingHolidayPayRecord(PayRecord):
    def __init__(self, id: int, hours:float, rates:float, visa: str, year_to_date:float):
        super().__init__(id, hours, rates)
        self._visa = visa
        self._year_to_date = year_to_date

    @property
    def visa(self):
        return self._visa

    @property
    def year_to_date(self):
        return self._year_to_date

    @property
    def tax(self):
        return calc_wh_tax(self.gross, self._year_to_date)

    def get_details(self):
        return f"------- Holiday ID: {self._id} -------\n\nGross: {self.gross}\nNet: {self.net}\nTax: {self.tax}\nVisa: {self._visa}\nYTD: {self._year_to_date}"


def calc_res_tax(gross: float):

    A_eff = [0.19,0.2342,0.3477,0.345,0.39,0.47]
    b_Eff =[0.19,3.213,44.2476,41.7311,103.8657,352.788]
    if (gross > -1 and gross <= 72):
        resTax = A_eff[0] * gross - b_Eff[0]
        return resTax
    if (gross > 72 and gross <= 361):
        resTax = A_eff[1] * gross - b_Eff[1]
        return resTax
    if (gross > 361 and gross <= 932):
        resTax = A_eff[2] * gross - b_Eff[2]
        return resTax
    if (gross > 932 and gross <= 1380):
        resTax = A_eff[3] * gross - b_Eff[3]
        return resTax
    if (gross > 1380 and gross <= 3111):
        resTax = A_eff[4] * gross - b_Eff[4]
        return resTax
    if (gross > 3111 and gross <= 999999):
        resTax = A_eff[5] * gross - b_Eff[5]
        return resTax
    
def calc_wh_tax(gross:float, year_to_date:float):
    rate = [0.15,0.32,0.37,0.45]
    if (year_to_date > -1 and year_to_date <= 37000):
        whTax = gross * rate[0]
        return whTax
    if (year_to_date > 37000 and year_to_date <= 90000):
        whTax = gross * rate[1]
        return whTax
    if (year_to_date > 90000 and year_to_date <= 180000):
        whTax = gross * rate[2]
        return whTax
    if (year_to_date > 180000 and year_to_date <= 9999999):
        whTax = gross * rate[3]
        return whTax    

def import_pay_records(file:str):
    records = []

    with open(file,"r") as f:
        next(f)
        for line in f:
            line = line.rstrip().split(',')
            id = int(line[0])
            hours = float(line[1])
            rates = float(line[2])
            visa = str(line[3])
            year_to_date = float(line[4])
            app = id,hours,rates,visa,year_to_date
            rec = [string for string in app if string != '']
            records.append(rec)
            create_pay_record(id,hours,rates,visa,year_to_date)


        return records

def create_pay_record(id:int, hours:float, rates:float, visa:str, year_to_date:float):
    
    r: ResidentPayRecord = ResidentPayRecord(id,hours,rates)
    wh: WorkingHolidayPayRecord = WorkingHolidayPayRecord(id,hours,rates,visa,year_to_date)

    print(r.get_details())
    print(wh.get_details())

def write_summary(file:str, records:List[PayRecord], to_console:bool):
    """The function must accept a list of PayRecord objects and write the Id, Gross, Net, and Tax amounts of a pay record to a comma delimited values (.csv) file"""
    pass

def main():
    records = import_pay_records("import\\employee-payroll-data.csv")
    write_summary("export\\export-data.csv",records,True)

if __name__ == '__main__':
    main()

Import Data: There's more duplicate, but it doesn't matter. Just as an example.

EmployeeId,Hours,Rate,Visa,YearToDate
1,2,25,,
1,3,25,,
1,4,25,,
1,5,32,,
1,6,32,,
2,2,25,417,47520.0
2,2,25,417,47520.0
2,2,25,417,47520.0

Solution

  • The problems if that some fields are empty and float('') actually raise that ValueError

    So you should test for empty values before trying to convert:

    year_to_date = float(line[4]) if line[4].strip() != '' else 0.0