Code editing task: traffic_analysis
Topic: DSA; change kind: adaptive

Original code:
from typing import Optional, Literal
from abc import ABC, abstractmethod


class Visitor(ABC):
    """
    A visitor.
    """

    @abstractmethod
    def visit(self, city_intersection: 'CityIntersection'):
        """
        Visit a city intersection.
        """


class City:
    """
    A city with a name, population, and typical traffic. The traffic is a
    float between 0 and 1 representing the percentage of the population that
    drives at any given time.
    """

    def __init__(self, name: str, population: int, traffic: float):
        self.name = name
        self.population = population
        self.traffic = traffic


IntersectionType = Literal[
    'FourWayIntersection',
    'TIntersection',
]


class CityIntersection:
    """
    An intersection between cities. It contains a city, and two intersections.
    """

    def __init__(
        self,
        intersection1: Optional['CityIntersection'],
        intersection2: Optional['CityIntersection'],
        city: City,
        type: IntersectionType,
    ):
        self.intersection1 = intersection1
        self.intersection2 = intersection2
        self.city = city
        self.type = type

    def accept(self, visitor: Visitor):
        """
        Accepts a visitor.
        """
        visitor.visit(self)


class TrafficAnalysisVisitor(Visitor):
    """
    A visitor that performs complex traffic analysis on city intersections.
    """

    def __init__(self):
        self.traffic_data = {}

    def visit(self, city_intersection: 'CityIntersection'):
        """
        Perform traffic analysis on a city intersection and its children.
        """
        if city_intersection.type == 'FourWayIntersection':
            self.analyze_four_way_intersection(city_intersection)
        elif city_intersection.type == 'TIntersection':
            self.analyze_t_intersection(city_intersection)

    def analyze_four_way_intersection(self, intersection: 'CityIntersection'):
        """
        Analyze traffic at a four-way intersection.
        """
        traffic_volume = intersection.city.population * intersection.city.traffic
        adjusted_traffic = traffic_volume * 1.2
        self.traffic_data[intersection.city.name] = {
            "type": intersection.type,
            "traffic_volume": adjusted_traffic
        }

    def analyze_t_intersection(self, intersection: 'CityIntersection'):
        """
        Analyze traffic at a T-intersection.
        """
        traffic_volume = intersection.city.population * intersection.city.traffic
        adjusted_traffic = traffic_volume * 1.1
        self.traffic_data[intersection.city.name] = {
            "type": intersection.type,
            "traffic_volume": adjusted_traffic
        }

Instruction (descriptive):
Add a new type of intersection called 'Roundabout', and implement the functionality to handle it in the `TrafficAnalysisVisitor` class.
The 'Roundabout' intersection should reduce traffic by 30%, therefore make sure that the traffic value is adjusted by 0.7.

Also, there is a clear problem in the `visit` method of the `TrafficAnalysisVisitor` class: the visitor doesn't recur on the children of the intersection. Fix this problem.

Instruction (lazy):
Add a new type of intersection, 'Roundabout', which should reduce traffic by 30%. 
Also, make the visitor actually recur through children intersections too.

Edited code:
from typing import Optional, Literal
from abc import ABC, abstractmethod


class Visitor(ABC):
    """
    A visitor.
    """

    @abstractmethod
    def visit(self, city_intersection: 'CityIntersection'):
        """
        Visit a city intersection.
        """


class City:
    """
    A city with a name, population, and typical traffic. The traffic is a
    float between 0 and 1 representing the percentage of the population that
    drives at any given time.
    """

    def __init__(self, name: str, population: int, traffic: float):
        self.name = name
        self.population = population
        self.traffic = traffic


IntersectionType = Literal[
    'FourWayIntersection',
    'Roundabout',
    'TIntersection',
]


class CityIntersection:
    """
    An intersection between cities. It contains a city, and two intersections.
    """

    def __init__(
        self,
        intersection1: Optional['CityIntersection'],
        intersection2: Optional['CityIntersection'],
        city: City,
        type: IntersectionType,
    ):
        self.intersection1 = intersection1
        self.intersection2 = intersection2
        self.city = city
        self.type = type

    def accept(self, visitor: Visitor):
        """
        Accepts a visitor.
        """
        visitor.visit(self)


class TrafficAnalysisVisitor(Visitor):
    """
    A visitor that performs complex traffic analysis on city intersections.
    """

    def __init__(self):
        self.traffic_data = {}

    def visit(self, city_intersection: 'CityIntersection'):
        """
        Perform traffic analysis on a city intersection and its children.
        """
        if city_intersection.type == 'FourWayIntersection':
            self.analyze_four_way_intersection(city_intersection)
        elif city_intersection.type == 'Roundabout':
            self.analyze_roundabout(city_intersection)
        elif city_intersection.type == 'TIntersection':
            self.analyze_t_intersection(city_intersection)

        if city_intersection.intersection1 is not None:
            city_intersection.intersection1.accept(self)
        if city_intersection.intersection2 is not None:
            city_intersection.intersection2.accept(self)

    def analyze_four_way_intersection(self, intersection: 'CityIntersection'):
        """
        Analyze traffic at a four-way intersection.
        """
        traffic_volume = intersection.city.population * intersection.city.traffic
        adjusted_traffic = traffic_volume * 1.2
        self.traffic_data[intersection.city.name] = {
            "type": intersection.type,
            "traffic_volume": adjusted_traffic
        }

    def analyze_roundabout(self, intersection: 'CityIntersection'):
        """
        Analyze traffic at a roundabout.
        """
        traffic_volume = intersection.city.population * intersection.city.traffic
        adjusted_traffic = traffic_volume * 0.7
        self.traffic_data[intersection.city.name] = {
            "type": intersection.type,
            "traffic_volume": adjusted_traffic
        }

    def analyze_t_intersection(self, intersection: 'CityIntersection'):
        """
        Analyze traffic at a T-intersection.
        """
        traffic_volume = intersection.city.population * intersection.city.traffic
        adjusted_traffic = traffic_volume * 1.1
        self.traffic_data[intersection.city.name] = {
            "type": intersection.type,
            "traffic_volume": adjusted_traffic
        }

Tests:
### START TESTS ###
if True:  # pragma: no cover
    atlanta = City('Atlanta', 500000, 0.5)
    boston = City('Boston', 200000, 0.3)
    chicago = City('Chicago', 1000000, 0.7)
    denver = City('Denver', 300000, 0.4)
    el_paso = City('El Paso', 100000, 0.1)
    fargo = City('Fargo', 50000, 0.05)

    four_way_intersection = CityIntersection(
        CityIntersection(
            CityIntersection(
                None,
                None,
                atlanta,
                'FourWayIntersection',
            ),
            CityIntersection(
                None,
                None,
                boston,
                'FourWayIntersection',
            ),
            chicago,
            'FourWayIntersection',
        ),
        CityIntersection(
            CityIntersection(
                None,
                None,
                el_paso,
                'FourWayIntersection',
            ),
            None,
            denver,
            'FourWayIntersection',
        ),
        fargo,
        'FourWayIntersection',
    )
    visitor = TrafficAnalysisVisitor()

    four_way_intersection.accept(visitor)

    assert visitor.traffic_data['Chicago']['traffic_volume'] == 1000000 * \
        0.7 * 1.2, "Four-Way Intersection traffic calculation failed for Chicago."

    assert 'Atlanta' in visitor.traffic_data, "Atlanta not visited."
    assert 'Boston' in visitor.traffic_data, "Boston not visited."
    assert 'Denver' in visitor.traffic_data, "Denver not visited."
    assert 'El Paso' in visitor.traffic_data, "El Paso not visited."
    assert 'Fargo' in visitor.traffic_data, "Fargo not visited."

    roundabout_intersection = CityIntersection(
        None,
        None,
        boston,
        'Roundabout'
    )

    t_intersection = CityIntersection(
        None,
        None,
        denver,
        'TIntersection'
    )

    mixed_intersection = CityIntersection(
        roundabout_intersection,
        t_intersection,
        el_paso,
        'FourWayIntersection'
    )

    visitor = TrafficAnalysisVisitor()

    roundabout_intersection.accept(visitor)
    assert visitor.traffic_data['Boston']['traffic_volume'] == 200000 * \
        0.3 * 0.7, "Roundabout traffic calculation failed for Boston."

    t_intersection.accept(visitor)
    assert visitor.traffic_data['Denver']['traffic_volume'] == 300000 * \
        0.4 * 1.1, "T-Intersection traffic calculation failed for Denver."

    mixed_intersection.accept(visitor)
    assert visitor.traffic_data['El Paso']['traffic_volume'] == 100000 * \
        0.1 * 1.2, "Four-Way Intersection traffic calculation failed for El Paso."
    assert 'Boston' in visitor.traffic_data, "Boston not visited in mixed intersection."
    assert 'Denver' in visitor.traffic_data, "Denver not visited in mixed intersection."

    four_way_intersection.accept(visitor)
    assert 'Chicago' in visitor.traffic_data, "Chicago not visited in complex structure."
    assert 'Atlanta' in visitor.traffic_data, "Atlanta not visited in complex structure."
    assert 'Fargo' in visitor.traffic_data, "Fargo not visited in complex structure."

    simple_four_way = CityIntersection(
        None, None, atlanta, 'FourWayIntersection')
    simple_roundabout = CityIntersection(None, None, boston, 'Roundabout')
    simple_t_intersection = CityIntersection(
        None, None, chicago, 'TIntersection')

    nested_intersection_1 = CityIntersection(
        simple_four_way,
        simple_roundabout,
        denver,
        'Roundabout'
    )

    nested_intersection_2 = CityIntersection(
        simple_t_intersection,
        nested_intersection_1,
        el_paso,
        'TIntersection'
    )

    visitor = TrafficAnalysisVisitor()

    simple_four_way.accept(visitor)
    simple_roundabout.accept(visitor)
    simple_t_intersection.accept(visitor)

    assert visitor.traffic_data['Atlanta']['traffic_volume'] == 500000 * \
        0.5 * 1.2, "Four-Way Intersection traffic calculation failed for Atlanta."
    assert visitor.traffic_data['Boston']['traffic_volume'] == 200000 * \
        0.3 * 0.7, "Roundabout traffic calculation failed for Boston."
    assert visitor.traffic_data['Chicago']['traffic_volume'] == 1000000 * \
        0.7 * 1.1, "T-Intersection traffic calculation failed for Chicago."

    nested_intersection_1.accept(visitor)
    nested_intersection_2.accept(visitor)

    assert visitor.traffic_data['Denver']['traffic_volume'] == 300000 * 0.4 * \
        0.7, "Roundabout traffic calculation failed for Denver in nested intersection."
    assert visitor.traffic_data['El Paso']['traffic_volume'] == 100000 * 0.1 * \
        1.1, "T-Intersection traffic calculation failed for El Paso in nested intersection."

    assert 'Atlanta' in visitor.traffic_data, "Atlanta not visited in nested intersection."
    assert 'Boston' in visitor.traffic_data, "Boston not visited in nested intersection."
    assert 'Chicago' in visitor.traffic_data, "Chicago not visited in nested intersection."
    assert 'Denver' in visitor.traffic_data, "Denver not visited in nested intersection."
    assert 'El Paso' in visitor.traffic_data, "El Paso not visited in nested intersection."