Katkat

Build a Progressive Employee Tracker

AnthropicSoftware Engineer · Onsite

Last updated

Design an in-memory attendance and payroll tracking system that manages shift lifecycles, deferred rate promotions, overlapping bonus windows, and duration rankings.

Problem Statement

Design an in-memory employee attendance and payroll management class, EmployeeTracker. The system tracks employee shifts, hourly rates, promotions, global bonus intervals, and total time worked.

Implement the EmployeeTracker class with the following methods:

  • register_employee(emp_id: str, initial_hourly_rate: float) -> None: Registers a new employee with a base hourly rate.

  • schedule_promotion(emp_id: str, effective_time: int, new_hourly_rate: float) -> None: Schedules a rate update for emp_id. The promotion takes effect at the employee's first clock-in at or after effective_time. An ongoing shift active across effective_time is not affected; its rate remains snapshotted at its initial clock-in rate.

  • add_bonus_window(start_time: int, end_time: int) -> None: Adds a global bonus window [start_time, end_time). Any portion of a shift overlapping with one or more bonus windows is paid at 2.0x the shift's snapshotted rate. Overlapping or contiguous bonus windows must be merged so bonus pay is applied at most once per time unit.

  • clock_in(emp_id: str, timestamp: int) -> None: Clocks in an employee. Applies any scheduled promotions whose effective_time None: Clocks out the employee, closing the shift [clock_in_time, timestamp). Accrues total completed duration and earned pay.

  • get_total_pay(emp_id: str) -> float: Returns total earnings from all closed shifts for the employee. Returns 0.0 if no shifts have been closed.

  • get_top_k_worked(k: int) -> list[str]: Returns the emp_ids of the top k employees by total closed shift duration in descending order. Break ties lexicographically by emp_id in ascending order.

All timestamps and durations are integers representing hours.

Constraints

  • Timestamps are non-negative integers (0 <= timestamp <= 10^9).
  • Calls to clock_in and clock_out for any given employee are guaranteed to be strictly valid (alternating, non-empty, and strictly increasing timestamps).
  • Bonus windows satisfy 0 <= start_time < end_time <= 10^9.
  • At most 10^5 operations across all methods.

Example

tracker = EmployeeTracker()
tracker.register_employee("E1", 20.0)
tracker.add_bonus_window(2, 5)
tracker.add_bonus_window(4, 7)  # Merged window: [2, 7)

tracker.clock_in("E1", 1)
tracker.clock_out("E1", 4)
# Shift [1, 4): duration 3 hours.
# Overlap with [2, 7) is [2, 4) (2 hrs). Normal portion: [1, 2) (1 hr).
# Pay: (1 hr * 20.0) + (2 hrs * 20.0 * 2.0) = 20.0 + 80.0 = 100.0

tracker.schedule_promotion("E1", 5, 30.0)
tracker.clock_in("E1", 6)
# Timestamp 6 >= 5, so rate is updated to 30.0 for this shift.
tracker.clock_out("E1", 9)
# Shift [6, 9): duration 3 hours.
# Overlap with [2, 7) is [6, 7) (1 hr bonus). Normal portion: [7, 9) (2 hrs).
# Pay: (1 hr * 30.0 * 2.0) + (2 hrs * 30.0) = 60.0 + 60.0 = 120.0

assert tracker.get_total_pay("E1") == 220.0
assert tracker.get_top_k_worked(1) == ["E1"]

Hints

Hint 1

Maintain bonus windows in a sorted, disjoint interval list by merging overlapping or adjacent intervals upon insertion or query.

Hint 2

Store scheduled promotions in a priority queue or sorted list per employee so they can be popped when a clock-in occurs at or after their effective times.

Hint 3

To compute the bonus overlap for a shift [start, end), use binary search on the disjoint bonus intervals rather than checking unit-by-unit.

Hint 4

For get_top_k_worked, maintain total durations per employee and either use a min-heap of size k or sort on demand depending on call frequency.

Follow-up questions

  • How would you optimize `add_bonus_window` and shift overlap calculation if timestamps were dense and calls were heavily write-heavy vs. read-heavy?
  • How would you handle retroactive shift modifications or cancellations without recomputing all pay from scratch?
  • If promotion rules depended on hours worked (e.g., automatically promoting an employee after 500 completed hours) rather than scheduled timestamps, how would you restructure the event processing?