diff --git a/apps/lol_analytics/lib/lol_analytics/analyzer/champion_analyzer.ex b/apps/lol_analytics/lib/lol_analytics/analyzer/champion_analyzer.ex index dbcb534..e6cf9ce 100644 --- a/apps/lol_analytics/lib/lol_analytics/analyzer/champion_analyzer.ex +++ b/apps/lol_analytics/lib/lol_analytics/analyzer/champion_analyzer.ex @@ -1,13 +1,19 @@ defmodule LolAnalytics.Analyzer.ChampionAnalyzer do - alias Hex.HTTP + alias LolAnalytics.Facts.ChampionPlayedGame.ChampionPlayedGameSchema @behaviour LolAnalytics.Analyzer def analyze_all_matches do - Storage.MatchStorage.S3MatchStorage.list_files("ranked") - |> Enum.map(& &1.key) - |> Enum.each(fn path -> - LolAnalytics.Analyzer.ChampionAnalyzer.analyze(:url, "http://localhost:9000/ranked/#{path}") + Storage.MatchStorage.S3MatchStorage.stream_files("ranked") + |> Enum.each(fn %{key: path} -> + IO.inspect(path) + LolAnalytics.Analyzer.ChampionAnalyzer.analyze(:url, "http://192.168.1.55:9000/ranked/#{path}") end) + + # Storage.MatchStorage.S3MatchStorage.list_files("ranked") + # |> Enum.map(& &1.key) + # |> Enum.each(fn path -> + # LolAnalytics.Analyzer.ChampionAnalyzer.analyze(:url, "http://localhost:9000/ranked/#{path}") + # end) end @doc """ @@ -30,12 +36,16 @@ defmodule LolAnalytics.Analyzer.ChampionAnalyzer do participants |> Enum.each(fn participant = %LoLAPI.Model.Participant{} -> if participant.teamPosition != "" do - LolAnalytics.ChampionWinRate.ChampionWinRateRepo.add_champion_win_rate( - participant.championId, - version, - participant.teamPosition, - participant.win - ) + attrs = %{ + champion_id: participant.championId, + match_id: decoded_match.metadata.matchId, + is_win: participant.win, + game_length_seconds: decoded_match.info.gameDuration, + queue_id: decoded_match.info.queueId, + puuid: participant.puuid, + team_position: participant.teamPosition + } + LolAnalytics.Facts.ChampionPlayedGame.ChampionPlayedGameRepo.insert(attrs) end end) end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/champion/champion_repo.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/champion/champion_repo.ex new file mode 100644 index 0000000..8bc8e44 --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/champion/champion_repo.ex @@ -0,0 +1,24 @@ +defmodule LolAnalytics.Dimensions.Champion.ChampionRepo do + import Ecto.Query + + alias LoLAnalytics.Repo + alias LolAnalytics.Dimensions.Champion.ChampionSchema + + @spec get_or_create(String.t()) :: struct() + def get_or_create(champion_id) do + champion = Repo.get_by(ChampionSchema, champion_id: champion_id) + + case champion do + nil -> + changeset = ChampionSchema.changeset(%ChampionSchema{}, %{champion_id: champion_id}) + Repo.insert(changeset) + + champion -> + champion + end + end + + def list_champions() do + Repo.all(ChampionSchema) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/champion/champion_schema.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/champion/champion_schema.ex new file mode 100644 index 0000000..7cc68cb --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/champion/champion_schema.ex @@ -0,0 +1,15 @@ +defmodule LolAnalytics.Dimensions.Champion.ChampionSchema do + use Ecto.Schema + import Ecto.Changeset + + schema "dim_champion" do + field :champion_id, :integer + timestamps() + end + + def changeset(champion = %__MODULE__{}, attrs \\ %{}) do + champion + |> cast(attrs, [:champion_id]) + |> validate_required([:champion_id]) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/item/item_repo.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/item/item_repo.ex new file mode 100644 index 0000000..214cc7c --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/item/item_repo.ex @@ -0,0 +1,21 @@ +defmodule LolAnalytics.Dimensions.Item.ItemRepo do + alias LolAnalytics.Dimensions.Item.ItemSchema + alias LoLAnalytics.Repo + + def get_or_create(item_id) do + item = Repo.get(ItemSchema, item_id: item_id) + + case item do + nil -> + item_changeset = ItemSchema.changeset(%ItemSchema{}, %{item_id: item_id}) + Repo.insert(item_changeset) + + item -> + item + end + end + + def list_items() do + Repo.all(ItemSchema) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/item/item_schema.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/item/item_schema.ex new file mode 100644 index 0000000..e61a07b --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/item/item_schema.ex @@ -0,0 +1,15 @@ +defmodule LolAnalytics.Dimensions.Item.ItemSchema do + use Ecto.Schema + import Ecto.Changeset + + schema "dim_item" do + field :item_id, :integer + timestamps() + end + + def changeset(item = %__MODULE__{}, attrs \\ %{}) do + item + |> cast(attrs, [:item_id]) + |> validate_required([:item_id]) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/match/match_repo.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/match/match_repo.ex new file mode 100644 index 0000000..9a46374 --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/match/match_repo.ex @@ -0,0 +1,30 @@ +defmodule LolAnalytics.Dimensions.Match.MatchRepo do + alias LolAnalytics.Dimensions.Match.MatchSchema + alias LoLAnalytics.Repo + + import Ecto.Query + + @spec get_or_create(String.t()) :: %MatchSchema{} + def get_or_create(match_id) do + query = from m in MatchSchema, where: m.match_id == ^match_id + match = Repo.one(query) + + case match do + nil -> + match_changeset = + MatchSchema.changeset( + %MatchSchema{}, + %{match_id: match_id} + ) + + Repo.insert(match_changeset) + + match -> + match + end + end + + def list_matches() do + Repo.all(MatchSchema) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/match/match_schema.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/match/match_schema.ex new file mode 100644 index 0000000..b5f50ce --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/match/match_schema.ex @@ -0,0 +1,15 @@ +defmodule LolAnalytics.Dimensions.Match.MatchSchema do + use Ecto.Schema + import Ecto.Changeset + + schema "dim_match" do + field :match_id, :string + timestamps() + end + + def changeset(match = %__MODULE__{}, attrs \\ %{}) do + match + |> cast(attrs, [:match_id]) + |> validate_required([:match_id]) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/patch/patch_repo.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/patch/patch_repo.ex new file mode 100644 index 0000000..2a18e18 --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/patch/patch_repo.ex @@ -0,0 +1,26 @@ +defmodule LolAnalytics.Dimensions.Patch.PatchRepo do + alias LolAnalytics.Dimensions.Patch.PatchSchema + alias LoLAnalytics.Repo + + def get_or_create(patch_number) do + patch = Repo.get(PatchSchema, patch_number: patch_number) + + case patch do + nil -> + patch_changeset = + PatchSchema.changeset( + %PatchSchema{}, + %{patch_number: patch_number} + ) + + Repo.insert(patch_changeset) + + patch -> + patch + end + end + + def list_patches() do + Repo.all(PatchSchema) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/patch/patch_schema.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/patch/patch_schema.ex new file mode 100644 index 0000000..f37e600 --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/patch/patch_schema.ex @@ -0,0 +1,15 @@ +defmodule LolAnalytics.Dimensions.Patch.PatchSchema do + use Ecto.Schema + import Ecto.Changeset + + schema "dim_patch" do + field :patch_number, :string + timestamps() + end + + def changeset(patch = %__MODULE__{}, attrs \\ %{}) do + patch + |> cast(attrs, [:patch_number]) + |> validate_required([:patch_number]) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/player/player_repo.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/player/player_repo.ex new file mode 100644 index 0000000..c28bb0d --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/player/player_repo.ex @@ -0,0 +1,28 @@ +defmodule LolAnalytics.Dimensions.Player.PlayerRepo do + import Ecto.Query + + alias LolAnalytics.Dimensions.Player.PlayerSchema + alias LoLAnalytics.Repo + + def get_or_create(puuid) do + query = from p in PlayerSchema, where: p.puuid == ^puuid + player = Repo.one(query) + + case player do + nil -> + player_changeset = + PlayerSchema.changeset( + %PlayerSchema{}, + %{puuid: puuid} + ) + Repo.insert(player_changeset) + + player -> + player + end + end + + def list_players() do + Repo.all(PlayerSchema) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/player/player_schema.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/player/player_schema.ex new file mode 100644 index 0000000..27ff15c --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/player/player_schema.ex @@ -0,0 +1,15 @@ +defmodule LolAnalytics.Dimensions.Player.PlayerSchema do + use Ecto.Schema + import Ecto.Changeset + + schema "dim_player" do + field :puuid, :string + timestamps() + end + + def changeset(player = %__MODULE__{}, attrs \\ %{}) do + player + |> cast(attrs, [:puuid]) + |> validate_required([:puuid]) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/summoner_spell/summoner_spell_repo.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/summoner_spell/summoner_spell_repo.ex new file mode 100644 index 0000000..c1ff76c --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/summoner_spell/summoner_spell_repo.ex @@ -0,0 +1,26 @@ +defmodule LolAnalytics.Dimensions.SummonerSpell.SummonerSpellRepo do + alias LolAnalytics.Dimensions.SummonerSpell.SummonerSpellSchema + alias LoLAnalytics.Repo + + def get_or_create(spell_id) do + spell = Repo.get(SummonerSpellSchema, spell_id: spell_id) + + case spell do + nil -> + spell_changeset = + SummonerSpellSchema.changeset( + %SummonerSpellSchema{}, + %{spell_id: spell_id} + ) + + Repo.insert(spell_changeset) + + spell -> + spell + end + end + + def list_spells() do + Repo.all(SummonerSpellSchema) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/dimensions/summoner_spell/summoner_spell_schema.ex b/apps/lol_analytics/lib/lol_analytics/dimensions/summoner_spell/summoner_spell_schema.ex new file mode 100644 index 0000000..9a678da --- /dev/null +++ b/apps/lol_analytics/lib/lol_analytics/dimensions/summoner_spell/summoner_spell_schema.ex @@ -0,0 +1,15 @@ +defmodule LolAnalytics.Dimensions.SummonerSpell.SummonerSpellSchema do + use Ecto.Schema + import Ecto.Changeset + + schema "dim_summoner_spell" do + field :spell_id, :integer + timestamps() + end + + def changeset(summoner_spell = %__MODULE__{}, attrs) do + summoner_spell + |> cast(attrs, [:spell_id]) + |> validate_required([:spell_id]) + end +end diff --git a/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_repo.ex b/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_repo.ex index c431962..6d70cdf 100644 --- a/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_repo.ex +++ b/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_repo.ex @@ -1,3 +1,27 @@ defmodule LolAnalytics.Facts.ChampionPlayedGame.ChampionPlayedGameRepo do + import Ecto.Query + alias LolAnalytics.Dimensions.Player.PlayerRepo + alias LolAnalytics.Dimensions.Champion.ChampionRepo + alias LolAnalytics.Dimensions.Match.MatchRepo + alias LolAnalytics.Facts.ChampionPlayedGame.ChampionPlayedGameSchema + alias LolAnalytics.Facts.ChampionPlayedGame + alias LoLAnalytics.Repo + + def insert(attrs) do + match = MatchRepo.get_or_create(attrs.match_id) + champion = ChampionRepo.get_or_create(attrs.champion_id) + player = PlayerRepo.get_or_create(attrs.puuid) + IO.puts(">>>>") + IO.inspect(attrs) + changeset = ChampionPlayedGameSchema.changeset(%ChampionPlayedGameSchema{}, attrs) + + IO.inspect(changeset) + Repo.insert(changeset) + # Repo.insert(match) + end + + def list_played_matches() do + Repo.all(ChampionPlayedGameSchema) + end end diff --git a/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_schema.ex b/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_schema.ex index 3595cdc..0467b0d 100644 --- a/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_schema.ex +++ b/apps/lol_analytics/lib/lol_analytics/facts/champion_played_game/champion_played_game_schema.ex @@ -1,11 +1,33 @@ defmodule LolAnalytics.Facts.ChampionPlayedGame.ChampionPlayedGameSchema do use Ecto.Schema + import Ecto.Changeset + + @casting_attrs [ + :champion_id, + :match_id, + :is_win, + :game_length_seconds, + :team_position, + :puuid, + :queue_id + ] + schema "fact_champion_played_game" do field :champion_id, :integer - field :match_id, :integer + field :match_id, :string field :is_win, :boolean field :game_length_seconds, :integer + field :team_position, :string + field :puuid, :string field :queue_id, :integer + timestamps() + end + + def changeset(fact = %__MODULE__{}, attrs \\ %{}) do + fact + |> cast(attrs, @casting_attrs) + |> validate_required(@casting_attrs) + |> unique_constraint([:id, :champion_id, :queue_id]) end end diff --git a/apps/lol_analytics/mix.exs b/apps/lol_analytics/mix.exs index 8d4a25d..f19733e 100644 --- a/apps/lol_analytics/mix.exs +++ b/apps/lol_analytics/mix.exs @@ -42,6 +42,7 @@ defmodule LoLAnalytics.MixProject do {:postgrex, ">= 0.0.0"}, {:jason, "~> 1.2"}, {:lol_api, in_umbrella: true}, + {:storage, in_umbrella: true}, {:httpoison, "~> 2.2"}, {:poison, "~> 5.0"} ] diff --git a/apps/lol_analytics/priv/repo/migrations/20240526160430_analytics_tables.exs b/apps/lol_analytics/priv/repo/migrations/20240526160430_analytics_tables.exs index 894e6ab..5d42b10 100644 --- a/apps/lol_analytics/priv/repo/migrations/20240526160430_analytics_tables.exs +++ b/apps/lol_analytics/priv/repo/migrations/20240526160430_analytics_tables.exs @@ -2,50 +2,61 @@ defmodule LoLAnalytics.Repo.Migrations.AnalyticsTables do use Ecto.Migration def change do - create table("dom_champion") do - add :champion_id, :integer, primary_key: true + create table("dim_champion") do + add :champion_id, :integer, primary_key: true, null: false timestamps() end - create table("dom_match") do - add :match_id, :integer, primary_key: true + create index("dim_champion", [:champion_id], unique: true) + + create table("dim_item") do + add :item_id, :integer, primary_key: true, null: false timestamps() end - create index("dom_match", [:match_id], unique: true) + create index("dim_item", [:item_id], unique: true) - create table("dom_patch") do - add :patch_number, :string, primary_key: true + create table("dim_match") do + add :match_id, :string, primary_key: true, null: false timestamps() end - create index("dom_patch", [:patch_number], unique: true) + create index("dim_match", [:match_id], unique: true) - create table("dom_item") do - add :item_id, :integer, primary_key: true + create table("dim_patch") do + add :patch_number, :string, primary_key: true, null: false timestamps() end - create index("dom_item", [:item_id], unique: true) + create index("dim_patch", [:patch_number], unique: true) - create table("dom_summoner_spell") do - add :spell_id, :integer, primary_key: true + create table("dim_player") do + add :puuid, :string, primary_key: true, null: false timestamps() end - create index("dom_summoner_spell", [:spell_id], unique: true) + create index("dim_player", [:puuid], unique: true) + + create table("dim_summoner_spell") do + add :spell_id, :integer, primary_key: true, null: false + timestamps() + end + + create index("dim_summoner_spell", [:spell_id], unique: true) create table("fact_champion_played_game") do - add :champion_id, references("dom_champion", with: [champion_id: :champion_id]), - primary_key: true - - add :match_id, references("dom_match", with: [match_id: :match_id]) + add :champion_id, references("dim_champion", column: :champion_id, type: :integer) + add :match_id, references("dim_match", column: :match_id, type: :string) add :is_win, :boolean add :game_length_seconds, :integer add :queue_id, :integer + add :patch_number, references("dim_patch", column: :patch_number, type: :string) + add :team_position, :string + add :puuid, references("dim_player", column: :puuid, type: :string) timestamps() end create index("fact_champion_played_game", [:id, :champion_id, :queue_id]) + create index("fact_champion_played_game", [:puuid, :match_id], unique: true) end end diff --git a/queries.md b/queries.md new file mode 100644 index 0000000..4652a32 --- /dev/null +++ b/queries.md @@ -0,0 +1,10 @@ +``` +SELECT + (cast(count(CASE WHEN is_win THEN 1 END) as float) / cast(count(*) as float)) * 100.0 as win_rate, + count(CASE WHEN is_win THEN 1 END) as games_won, + count(*) as total_games, + champion_id + FROM fact_champion_played_game + GROUP BY champion_id + ORDER BY win_rate desc; +``` \ No newline at end of file