champion detail win rate chart

This commit is contained in:
Álvaro 2024-06-17 00:37:33 +02:00
parent 648dacdd1a
commit 3e443617e9
8 changed files with 133 additions and 22 deletions

View File

@ -51,7 +51,8 @@ defmodule LolAnalytics.Facts.ChampionPlayedGame.Repo do
end end
end end
defp get_all_win_rates do @spec champion_win_rates_by_patch(String.t(), String.t()) :: [map()]
def champion_win_rates_by_patch(champion_id, team_position) do
query = query =
from m in Schema, from m in Schema,
join: c in ChampionSchema, join: c in ChampionSchema,
@ -66,13 +67,15 @@ defmodule LolAnalytics.Facts.ChampionPlayedGame.Repo do
m.is_win m.is_win
), ),
id: m.champion_id, id: m.champion_id,
patch_number: m.patch_number,
name: c.name, name: c.name,
image: c.image, image: c.image,
team_position: m.team_position, team_position: m.team_position,
total_games: count("*") total_games: count("*")
}, },
group_by: [m.champion_id, c.image, c.name, m.team_position], group_by: [m.champion_id, c.image, c.name, m.patch_number, m.team_position],
having: count("*") > 100 where: m.team_position == ^team_position and m.champion_id == ^champion_id,
having: count("*") > 50
Repo.all(query) Repo.all(query)
end end

View File

@ -18,18 +18,22 @@
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons. // Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
import "phoenix_html" import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration. // Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix" import { Socket } from "phoenix"
import {LiveSocket} from "phoenix_live_view" import { LiveSocket } from "phoenix_live_view"
import topbar from "../vendor/topbar" import topbar from "../vendor/topbar"
import { ChampionWinRate } from "./hooks/champion_win_rate_patch"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, { let liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500, longPollFallbackMs: 2500,
params: {_csrf_token: csrfToken} params: { _csrf_token: csrfToken },
hooks: {
ChampionWinRate,
}
}) })
// Show progress bar on live navigation and form submits // Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" })
window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
window.addEventListener("phx:page-loading-stop", _info => topbar.hide()) window.addEventListener("phx:page-loading-stop", _info => topbar.hide())

View File

@ -0,0 +1,38 @@
import Chart from "chart.js/auto"
const ChampionWinRate = {
mounted() {
console.log("mounted")
this.handleEvent("points", (event) => console.log("123"))
// this.props = { id: this.el.getAttribute("data-id") };
this.handleEvent("win-rate", ({ winRates }) => {
console.log(">>>>>>>")
console.log(winRates);
patches = winRates.map((winRate) => {
return winRate.patch_number
})
winRateValues = winRates.map((winRate) => winRate.win_rate)
setInterval(() => {
const data = {
labels: patches,
datasets: [{
label: 'Win rate',
data: winRateValues,
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
};
this.chart = new Chart(document.getElementById("win-rate"), {
type: 'line',
data: data
})
this.chart.canvas.parentNode.style.height = '250px';
this.chart.canvas.parentNode.style.width = '400px';
}, 1000)
});
}
}
export { ChampionWinRate };

View File

@ -0,0 +1,28 @@
{
"name": "assets",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"chart.js": "^4.4.3"
}
},
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"node_modules/chart.js": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
"integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
}
}
}

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"chart.js": "^4.4.3"
}
}

View File

@ -4,11 +4,19 @@ defmodule LolAnalyticsWeb.ChampionComponents.ChampionAvatar do
attr :id, :integer, required: true attr :id, :integer, required: true
attr :image, :string, required: true attr :image, :string, required: true
attr :name, :string, required: true attr :name, :string, required: true
attr :width, :integer, default: 100
attr :height, :integer, default: 100
def champion_avatar(assigns) do def champion_avatar(assigns) do
~H""" ~H"""
<style>
.champion-avatar {
width: 100px;
height: 100px;
}
</style>
<div class="flex flex-col w-40"> <div class="flex flex-col w-40">
<img src={@image} alt="champion-icon" /> <img src={@image} class="champion-avatar" alt="champion-icon" />
</div> </div>
""" """
end end

View File

@ -19,23 +19,37 @@ defmodule LoLAnalyticsWeb.ChampionLive.Show do
socket socket
|> assign(:page_title, page_title(socket.assigns.live_action)) |> assign(:page_title, page_title(socket.assigns.live_action))
|> assign(:champion, load_champion_info(id)) |> assign(:champion, load_champion_info(id))
|> load_win_rates(id, team_position)
|> load_summoner_spells(id, team_position) |> load_summoner_spells(id, team_position)
|> load_items(id, team_position, patch)} |> load_items(id, team_position, patch)}
end end
def load_win_rates(socket, champion_id, team_position) do
socket
|> start_async(:get_win_rates, fn ->
LolAnalytics.Facts.ChampionPlayedGame.Repo.champion_win_rates_by_patch(
champion_id,
team_position
)
|> Enum.sort(fn p1, p2 ->
[_, minor_1] = String.split(p1.patch_number, ".") |> Enum.map(&String.to_integer/1)
[_, minor_2] = String.split(p2.patch_number, ".") |> Enum.map(&String.to_integer/1)
p1 < p2 && minor_1 < minor_2
end)
end)
end
defp load_summoner_spells(socket, champion_id, team_position) do defp load_summoner_spells(socket, champion_id, team_position) do
socket socket
|> assign(:summoner_spells, %{status: :loading}) |> assign(:summoner_spells, %{status: :loading})
|> start_async( |> start_async(:get_summoners, fn ->
:get_summoners,
fn ->
LolAnalytics.Facts.ChampionPickedSummonerSpell.Repo.get_champion_picked_summoners( LolAnalytics.Facts.ChampionPickedSummonerSpell.Repo.get_champion_picked_summoners(
champion_id, champion_id,
team_position team_position
) )
|> ShowMapper.map_spells() |> ShowMapper.map_spells()
end end)
)
end end
defp load_items(socket, champion_id, team_position, patch) do defp load_items(socket, champion_id, team_position, patch) do
@ -61,9 +75,12 @@ defmodule LoLAnalyticsWeb.ChampionLive.Show do
) )
end end
def handle_async(:get_items, {:ok, %{popular: popular, boots: boots}} = result, socket) do def handle_async(:get_win_rates, {:ok, result}, socket) do
IO.inspect(result) IO.inspect(result)
{:noreply, push_event(socket, "win-rate", %{winRates: result})}
end
def handle_async(:get_items, {:ok, %{popular: popular, boots: boots}} = result, socket) do
socket = socket =
socket socket
|> assign(:items, %{ |> assign(:items, %{

View File

@ -1,3 +1,9 @@
<style>
.win-rate {
height: 250px;
}
</style>
<.header> <.header>
<h1 class="text-4xl"> <h1 class="text-4xl">
<%= @champion.name %> <%= @champion.name %>
@ -6,9 +12,11 @@
<div class="my-6" /> <div class="my-6" />
<.champion_avatar id={@champion.id} name={@champion.name} <div class="flex flex-row gap-4">
<.champion_avatar class="w-20" id={@champion.id} name={@champion.name}
image={"https://ddragon.leagueoflegends.com/cdn/14.11.1/img/champion/#{@champion.image}"} /> image={"https://ddragon.leagueoflegends.com/cdn/14.11.1/img/champion/#{@champion.image}"} />
<canvas class="w-full win-rate" height="250" class="win-rate" id="win-rate" phx-hook="ChampionWinRate" />
</div>
<div class="my-4" /> <div class="my-4" />
<.render_summoners summoner_pells={@summoner_spells} /> <.render_summoners summoner_pells={@summoner_spells} />