"""Employee Personal Days === CSC148 Winter 2021 === Department of Mathematical and Computational Sciences, University of Toronto Mississauga === Module Description === This code is a cleaned up version of the code from lecture showing some possible solutions to the "Personal Days" worksheet. """ from datetime import date from typing import Dict, List class Employee: """An employee of a company. This is an abstract class. Only subclasses should be instantiated. === Attributes === id_: This employee's ID number. name: This employee's name. (only use one of these three; I'm showing all three so that you know some of the options we have) personal_days: A "record" of the personal days this employee has taken, as the number of personal days taken. personal_days2: A "record" of the personal days this employee has taken, as the list of all personal days taken. personal_days3: A "record" of the personal days this employee has taken, as a dictionary mapping year to personal days taken that year. """ id_: int name: str personal_days: int personal_days2: List[date] personal_days3: Dict[int, List[date]] def __init__(self, id_: int, name: str) -> None: """Initialize this employee. Note: This initializer is meant for internal use only; Employee is an abstract class and should not be instantiated directly. """ self.id_ = id_ self.name = name self.personal_days = 0 self.personal_days2 = [] self.personal_days3 = {} def get_monthly_payment(self) -> float: """Return the amount that this Employee should be paid in one month. Round the amount to the nearest cent. """ raise NotImplementedError def pay(self, pay_date: date) -> None: """Pay this Employee on the given date and record the payment. (Assume this is called once per month.) """ payment = self.get_monthly_payment() print(f'An employee was paid {payment} on {pay_date}.') def take_personal_day(self, day: date) -> None: """Record that a personal day is taken on the given day. Do nothing if the number of personal days taken in the given year exceeds 10. [Alternatively, could use a precondition. From lecture: Precondition: - self.personal_days < 10 - this employee has taken less than 10 personal days in the same year as day. ] """ # Using personal_days (int) if self.personal_days < 10: self.personal_days += 1 # Using personal_days2 (List[date]) num_taken_this_year = 0 for personal_day in self.personal_days2: if personal_day.year == day.year: num_taken_this_year += 1 if num_taken_this_year < 10: self.personal_days2.append(day) # Using personal_days3 (Dict[int, List[date]]) if day.year in self.personal_days3: if len(self.personal_days3[day.year]) < 10: self.personal_days3[day.year].append(day) else: self.personal_days3[day.year] = [day] class SalariedEmployee(Employee): """An employee whose pay is computed based on an annual salary. === Attributes === salary: This employee's annual salary === Representation invariants === - salary >= 0 """ id_: int name: str salary: float def __init__(self, id_: int, name: str, salary: float) -> None: """Initialize this salaried Employee. >>> e = SalariedEmployee(14, 'Fred Flintstone', 5200.0) >>> e.salary 5200.0 """ # Note that to call the superclass initializer, we need to use the # full method name '__init__'. This is the only time you should write # '__init__' explicitly. Employee.__init__(self, id_, name) self.salary = salary def get_monthly_payment(self) -> float: """Return the amount that this Employee should be paid in one month. Round the amount to the nearest cent. >>> e = SalariedEmployee(99, 'Mr Slate', 120000.0) >>> e.get_monthly_payment() 10000.0 """ return round(self.salary / 12, 2) class HourlyEmployee(Employee): """An employee whose pay is computed based on an hourly rate. === Attributes === hourly_wage: This employee's hourly rate of pay. hours_per_month: The number of hours this employee works each month. === Representation invariants === - hourly_wage >= 0 - hours_per_month >= 0 """ id_: int name: str hourly_wage: float hours_per_month: float def __init__(self, id_: int, name: str, hourly_wage: float, hours_per_month: float) -> None: """Initialize this HourlyEmployee. >>> barney = HourlyEmployee(23, 'Barney Rubble', 1.25, 50.0) >>> barney.hourly_wage 1.25 >>> barney.hours_per_month 50.0 """ Employee.__init__(self, id_, name) self.hourly_wage = hourly_wage self.hours_per_month = hours_per_month def get_monthly_payment(self) -> float: """Return the amount that this Employee should be paid in one month. Round the amount to the nearest cent. >>> e = HourlyEmployee(23, 'Barney Rubble', 1.25, 50) >>> e.get_monthly_payment() 62.5 """ return self.hours_per_month * self.hourly_wage def pay(self, pay_date: date) -> None: """Pay this HourlyEmployee on the given date and record the payment. (Assume this is called once per month.) NOTE: if we don't want to change the public interface of get_monthly_payment, we need to override pay in HourlyEmployee to calculate deductions based on personal days. We only show an implementation using personal_days2 and personal_days3. """ payment = self.get_monthly_payment() # Using personal_days # Not really possible to get the number of personal days taken # this month, given that we only keep track of a yearly tally # Using personal_days2 num_taken_this_month = 0 for personal_day in self.personal_days2: if (personal_day.year == pay_date.year and personal_day.month == pay_date.month): num_taken_this_month += 1 # Using personal_days3 num_taken_this_month2 = 0 if pay_date.year in self.personal_days3: for day in self.personal_days3[pay_date.year]: if day.month == pay_date.month: num_taken_this_month2 += 1 # use either num_taken_this_month or num_taken_this_month2 payment = max(0, payment - num_taken_this_month * 8 * self.hourly_wage) print(f'An employee was paid {payment} on {pay_date}.') class Company: """A company with employees. We use this class mainly as a client for the various Employee classes we defined in employee. === Public Attributes === employees: the employees in the company. """ employees: List[Employee] def __init__(self, employees: List[Employee]) -> None: self.employees = employees def pay_all(self, pay_date: date) -> None: """Pay all employees at this company.""" for employee in self.employees: employee.pay(pay_date) # if __name__ == '__main__': # # import python_ta # # python_ta.check_all() # # # Illustrate a small company. # my_corp = Company([ # SalariedEmployee(14, 'Fred Flintstone', 5200.0), # HourlyEmployee(23, 'Barney Rubble', 1.25, 50.0), # SalariedEmployee(99, 'Mr Slate', 120000.0) # ]) # # my_corp.pay_all(date(2017, 8, 31)) # my_corp.pay_all(date(2017, 9, 30))