Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Swimmer Growth

livebooks/growth.livemd

Swimmer Growth

Mix.install(
  [
    {:swim_heat, path: Path.join(__DIR__, ".."), env: :dev},
    {:kino_vega_lite, "~> 0.1.13"}
  ],
  lockfile: :swim_heat
)

Individual

data =
  ".."
  |> Path.expand(__DIR__)
  |> File.cd!(fn ->
    "Edmond North"
    |> SwimHeat.Database.list_swimmers()
    |> Enum.map(fn swimmer ->
      SwimHeat.Database.list_swims("Edmond North", swimmer)
    end)
  end)
Enum.each(data, fn {swimmer, swims} ->
  swims
  |> Enum.reject(& &1.event.relay?)
  |> Enum.group_by(fn swim ->
    "#{swimmer.name} - #{swim.event.distance} #{swim.event.unit} #{swim.event.stroke}"
  end)
  |> Enum.map(fn {se, swims} -> {se, Enum.reject(swims, & &1.swim.dq?)} end)
  |> Enum.reject(fn {_se, swims} -> length(swims) < 2 end)
  |> Enum.map(fn {se, swims} ->
    results =
      Enum.map(swims, fn s ->
        %{
          swim:
            "#{Date.to_iso8601(s.meet.start_date)}#{if not s.event.prelim?, do: " Final"}",
          time: s.swim.time
        }
      end)

    {min, max} = results |> Enum.map(fn s -> s.time end) |> Enum.min_max()

    VegaLite.new(title: se)
    |> VegaLite.data_from_values(results)
    |> VegaLite.mark(:line)
    |> VegaLite.encode_field(:x, "swim", type: :nominal, title: "Event")
    |> VegaLite.encode_field(:y, "time",
      type: :quantitative,
      scale: [domain: [floor(min), ceil(max)]],
      title: "Seconds"
    )
    |> Kino.render()
  end)
end)

IMs

data
|> Enum.map(fn {swimmer, swims} ->
  {
    swimmer,
    Enum.filter(swims, fn s ->
      s.event.stroke == :im and length(s.swim.splits) == 4 and not s.swim.dq?
    end)
  }
end)
|> Enum.reject(fn {_swimmer, swims} -> swims == [] end)
|> Enum.each(fn {swimmer, ims} ->
  results =
    ims
    |> Enum.flat_map(fn s ->
      splits =
        if s.swim.time == Enum.at(s.swim.splits, -1) do
          [0 | s.swim.splits]
          |> Enum.chunk_every(2, 1, :discard)
          |> Enum.map(fn [prev, curr] -> curr - prev end)
        else
          s.swim.splits
        end

      splits
      |> Enum.zip(~w[fly back breast free])
      |> Enum.with_index()
      |> Enum.map(fn {{time, stroke}, i} ->
        %{
          swim:
            "#{Date.to_iso8601(s.meet.start_date)}#{if not s.event.prelim?, do: " Final"}",
          time: time,
          stroke: stroke,
          stroke_order: i
        }
      end)
    end)

  VegaLite.new(title: swimmer.name)
  |> VegaLite.data_from_values(results)
  |> VegaLite.mark(:bar)
  |> VegaLite.encode_field(:x, "time", aggregate: :sum, type: :quantitative, title: "Seconds")
  |> VegaLite.encode_field(:y, "swim", type: :nominal, title: "Event")
  |> VegaLite.encode_field(:color, "stroke")
  |> VegaLite.encode_field(:order, "stroke_order")
  |> Kino.render()
end)

Team

data
|> Enum.flat_map(fn {swimmer, swims} ->
  Enum.map(swims, fn s -> Map.put(s, :swimmer, swimmer) end)
end)
|> Enum.reject(fn s -> s.event.relay? || s.swim.dq? end)
|> Enum.group_by(fn swim ->
  "#{swim.event.gender} #{swim.event.distance} #{swim.event.unit} #{swim.event.stroke}"
end)
|> Enum.each(fn {event, swims} ->
  results =
    Enum.map(swims, fn s ->
      %{
        swim: "#{Date.to_iso8601(s.meet.start_date)}#{if not s.event.prelim?, do: " Final"}",
        time: s.swim.time,
        swimmer: s.swimmer.name
      }
    end)

  singles =
    results
    |> Enum.frequencies_by(&amp; &amp;1.swimmer)
    |> Enum.filter(fn {_swimmer, count} -> count < 2 end)
    |> Enum.map(fn {swimmer, _count} -> swimmer end)

  results = Enum.reject(results, fn s -> s.swimmer in singles end)

  if results != [] do
    {min, max} = results |> Enum.map(fn s -> s.time end) |> Enum.min_max()

    VegaLite.new(title: event)
    |> VegaLite.data_from_values(results)
    |> VegaLite.mark(:line)
    |> VegaLite.encode_field(:x, "swim", type: :nominal, title: "Event")
    |> VegaLite.encode_field(:y, "time",
      type: :quantitative,
      scale: [domain: [floor(min), ceil(max)]],
      title: "Seconds"
    )
    |> VegaLite.encode_field(:color, "swimmer", scale: %{scheme: "category20"})
    |> Kino.render()
  end
end)