def plot_timeline()

in tools/analysis/ping-patterns/ping-patterns.py [0:0]


def plot_timeline(client_id, data, metrics_rows, baseline_rows):
    """
    Make the SVG timeline.
    """
    data = sorted(data, key=lambda x: x["start_time_hour"])

    # Find the date range to determine the size of the plot
    min_time = data[0]["start_time_hour"]
    max_time = max(ping["end_time_hour"] for ping in data)
    width = max_time - min_time
    height = (len(metrics_rows) + len(baseline_rows) + 2) * ROW_HEIGHT

    svg = ET.Element(
        "svg",
        {
            "version": "1.1",
            "width": str(width),
            "height": str(height),
            "xmlns": "http://www.w3.org/2000/svg",
        },
    )
    ET.SubElement(
        svg,
        "rect",
        {
            "x": "0",
            "y": "0",
            "width": str(width),
            "height": str(height),
            "fill": "white",
        },
    )

    # Draw vertical lines at midnight and 4am, with the date indicated
    dt = data[0]["start_time_local"].replace(hour=0, minute=0, second=0)
    while get_fractional_hour(dt) < max_time:
        x = get_fractional_hour(dt) - min_time
        draw_line(svg, x, x, 0, height, stroke="#cccccc")
        draw_text(svg, x + 2, height - 2, dt.strftime("%m-%d"))

        four = dt.replace(hour=4)
        x = get_fractional_hour(four) - min_time
        draw_line(svg, x, x, 0, height, stroke="#cccccc", stroke_dasharray="2,1")

        dt += datetime.timedelta(days=1)

    # Draw markers for the first time key "FIX" versions were seen in the ping metadata
    fixes = list(enumerate(FIXES))
    for ping in sorted(data, key=lambda x: x["end_time_local"]):
        if ping["version_date"] is not None and ping["version_date"] >= fixes[0][1][1]:
            x = ping["end_time_hour"] - min_time
            draw_line(svg, x, x, 0, height, stroke="#33aa33")
            draw_text(svg, x + 2, 12, str(fixes[0][0] + 1), title=fixes[0][1][0])
            fixes.pop(0)

            if len(fixes) == 0:
                break

    # Draw the actual pings in the timeline
    y = ROW_HEIGHT
    for (rows, color) in ((baseline_rows, "#000088"), (metrics_rows, "#880000")):
        for row in rows[::-1]:
            for ping in row:
                draw_line(
                    svg,
                    ping["start_time_hour"] - min_time,
                    ping["end_time_hour"] - min_time,
                    y,
                    y,
                    stroke=color,
                    stroke_width="0.5",
                )

                if ping["ping_type"] == "baseline" and ping["duration"]:
                    session_start = (
                        get_fractional_hour(
                            ping["end_time_local"]
                            - datetime.timedelta(seconds=int(ping["duration"]))
                        )
                        - min_time
                    )
                    draw_line(
                        svg,
                        session_start,
                        ping["end_time_hour"] - min_time,
                        y,
                        y,
                        stroke=color,
                        stroke_width="3",
                    )

                if ping["notes"]:
                    x = 0
                    for note in sorted(list(ping["notes"])):
                        draw_text(
                            svg,
                            ping["end_time_hour"] - min_time + 2 + x,
                            y + 3,
                            str(note),
                            font_size="6px",
                            title=NOTE_SUMMARIES[note],
                        )
                        x += 8

            y += ROW_HEIGHT

    draw_text(svg, 2, 12, f"Android SDK: {data[0]['sdk']}")

    tree = ET.ElementTree(svg)

    with open(f"{client_id}.svg", "wb") as fd:
        tree.write(fd)