Headway Karat Phone Screen

20 min system design discussion of how to add comments to articles
30min OOD:

  1. find bug: missing float
  2. complete count_journeys
  3. complete catch_speeders

class LogEntry:
def init(self, log_line):
tokens = log_line.split(’ ')
# BUG WAS HERE: was tokens[0] stored as string
# Fix: use float() to parse timestamp as number
# Without this, timestamp subtraction returns NaN
# causing catchSpeeders() to never detect speeders
self.timestamp = float(tokens[0])
self.license_plate = tokens[1]
self.booth_type = tokens[3]
location_str = tokens[2]
self.location = int(location_str[:-1])
direction_letter = location_str[-1]
if direction_letter == ‘E’:
self.direction = ‘EAST’
elif direction_letter == ‘W’:
self.direction = ‘WEST’
else:
raise ValueError(‘Invalid direction letter’)

def str(self):
    return (f"<LogEntry timestamp: {self.timestamp} license: {self.license_plate} "
            f"location: {self.location} direction: {self.direction} "
            f"booth type: {self.booth_type}>")

class LogFile:
def init(self, file_contents):
self.log_entries =
lines = file_contents.split(‘\n’)
for line in lines:
if line.strip():
log_entry = LogEntry(line.strip())
self.log_entries.append(log_entry)

@property
def length(self):
    return len(self.log_entries)

def item(self, index):
    return self.log_entries[index]

def count_journeys(self):
    """
    TODO: Write a function named count_journeys() that returns
    how many complete journeys there are in the given LogFile.

    A complete journey consists of:

A driver entering the highway through an ENTRY toll booth
The driver passing through some number of MAINROAD toll booths (possibly 0)
The driver exiting the highway through an EXIT toll booth

    You may assume the log only contains complete journeys
    and there are no missing entries.

    Solution: Simply count ENTRY events since every
    journey starts with exactly one ENTRY.
    """
    count = 0
    for entry in self.log_entries:
        if entry.booth_type == 'ENTRY':
            count += 1
    return count

def catch_speeders(self):
    """
    TODO: Write a function named catch_speeders() in LogFile
    that returns a collection of license plates that drove
    at unsafe speeds during a journey in the LogFile.

    Unsafe speed means a driver does either of the following:

Drives 130 km/h or greater in any individual 10km segment
Drives 120 km/h or greater in any two 10km segments

    Notes:

A license plate may have multiple journeys in one file,
if they drive at unsafe speeds in both journeys, both
should be counted (plate appears once per speeding journey)
Speeding is only marked once per journey (e.g. if there
are 4 segments 120km/h or greater, journey is only counted once)
We do not mark speeding for driving between EXIT and ENTRY

    Speed formula per segment:
      speed = (10km [i] 3600 sec/hr) / time_diff_in_seconds

    NOTE: This works correctly ONLY because timestamp is parsed
    as float. If timestamp were a string, time_diff would be NaN
    and no speeders would ever be detected.
    """
    result = []
    # Maps license plate -> list of timestamps for current journey
    # Accumulates: ENTRY timestamp, then each MAINROAD timestamp
    # On EXIT we finalize and check speeds
    active_journeys = {}

    for entry in self.log_entries:
        plate = entry.license_plate

        if entry.booth_type == 'ENTRY':
            # Start a new journey, record entry timestamp
            active_journeys[plate] = [entry.timestamp]

        elif entry.booth_type == 'MAINROAD':
            # Mid-journey checkpoint, accumulate timestamp
            if plate in active_journeys:
                active_journeys[plate].append(entry.timestamp)

        elif entry.booth_type == 'EXIT':
            if plate not in active_journeys:
                continue

            # Finalize journey: append exit timestamp
            timestamps = active_journeys[plate] + [entry.timestamp]
            del active_journeys[plate]

            # Check each consecutive segment for speeding
            # Each segment = 10km between consecutive toll booths
            fast_segments = 0  # count of segments >= 120 km/h
            is_speeder = False

            for i in range(len(timestamps) - 1):
                # BUG WOULD SURFACE HERE if timestamp was a string:
                # time_diff would be NaN, speed would be NaN,
                # no speed comparison would ever be True
                time_diff = timestamps[i + 1] - timestamps
                speed = (10  3600) / time_diff  # km/h

                if speed >= 130:
                    # Single segment too fast -> immediate speeder
                    is_speeder = True
                    break
                if speed >= 120:
                    fast_segments += 1
                    if fast_segments >= 2:
                        # Two segments too fast -> speeder
                        is_speeder = True
                        break

            if is_speeder:
                result.append(plate)

    return result

def test_log_entry():
log_line = ‘44776.619 KTB918 310E MAINROAD’
entry = LogEntry(log_line)
assert entry.timestamp == 44776.619
assert entry.license_plate == ‘KTB918’
assert entry.location == 310
assert entry.direction == ‘EAST’
assert entry.booth_type == ‘MAINROAD’

log_line2 = '52160.132 ABC123 400W ENTRY'
entry2 = LogEntry(log_line2)
assert entry2.timestamp == 52160.132
assert entry2.license_plate == 'ABC123'
assert entry2.location == 400
assert entry2.direction == 'WEST'
assert entry2.booth_type == 'ENTRY'
print("test_log_entry: OK")

def test_count_journeys():
# Small inline log matching the example in the problem
sample_log = “”“90750.191 JOX304 250E ENTRY
91081.684 JOX304 260E MAINROAD
91082.101 THX138 110E ENTRY
91483.251 JOX304 270E MAINROAD
91873.920 THX138 120E MAINROAD
91874.493 JOX304 280E EXIT
91982.102 THX138 290E EXIT
92301.302 THX138 300E ENTRY
92371.302 THX138 310E EXIT”“”

lf = LogFile(sample_log)
assert lf.count_journeys() == 3, f"Expected 3, got {lf.count_journeys()}"
print("test_count_journeys: OK")

def test_catch_speeders():
# TST002: 10km in 275 sec => ~130.91 km/h (speeder, 1 journey)
# TST003: two segments both >= 120 km/h (speeder, 2 journeys)
sample_log = “”“1000.000 TST002 270W ENTRY
1275.000 TST002 260W EXIT
2000.000 TST003 270W ENTRY
2300.000 TST003 260W MAINROAD
2601.000 TST003 250W EXIT
5000.000 TST003 270W ENTRY
5301.000 TST003 260W MAINROAD
5602.000 TST003 250W EXIT”“”

lf = LogFile(sample_log)
ticket_list = lf.catch_speeders()

assert len([t]) == 1
assert len([t]) == 2
assert len(set(ticket_list)) == 2
print("test_catch_speeders: OK")

if name == ‘main’:
test_log_entry()
test_count_journeys()
test_catch_speeders()
print(“\nAll tests passed!”)