diff --git a/services/app/assets/css/app.scss b/services/app/assets/css/app.scss
index 0003dee61..35103bdf8 100644
--- a/services/app/assets/css/app.scss
+++ b/services/app/assets/css/app.scss
@@ -82,3 +82,15 @@ a {
background: rgba(0, 0, 0, 0);
padding: 0;
}
+.polyglot {
+ width: 50px;
+ height: 50px;
+ background: url('/assets/images/achievements/polyglot.png');
+ background-size: cover;
+ padding: 5px;
+}
+.profile .polyglot{
+ width: 200px;
+ height: 200px;
+ padding: 12px;
+}
diff --git a/services/app/assets/js/widgets/components/UserAchievements.jsx b/services/app/assets/js/widgets/components/UserAchievements.jsx
new file mode 100644
index 000000000..5a44bd80c
--- /dev/null
+++ b/services/app/assets/js/widgets/components/UserAchievements.jsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import _ from 'lodash';
+
+const renderPolyglotAchievement = (languages) => {
+ return (
+
+
+ {languages.map(el =>
)}
+
+
+ );
+};
+
+const UserAchievements = (achievements) => {
+ if (!_.isEmpty(achievements)) {
+ return (
+
+ {achievements.map((el) => {
+ const [name, languages] = el.split('?');
+ if (name === 'win_games_with') {
+ return renderPolyglotAchievement(languages.split('_'));
+ }
+ return
})
+ }
+
+ );
+ }
+ return '';
+};
+export default UserAchievements;
diff --git a/services/app/assets/js/widgets/components/UserStats.jsx b/services/app/assets/js/widgets/components/UserStats.jsx
index d878e884a..ff1907257 100644
--- a/services/app/assets/js/widgets/components/UserStats.jsx
+++ b/services/app/assets/js/widgets/components/UserStats.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import _ from 'lodash';
import Loading from './Loading';
+import UserAchievements from './UserAchievements';
const UserStats = ({ data }) => {
if (data) {
@@ -23,15 +24,7 @@ const UserStats = ({ data }) => {
{achivementsTitle}
- {!_.isEmpty(achievements) && (
-
- {achievements.map(el => (
- -
-
-
- ))}
-
- )}
+ {UserAchievements(achievements)}
);
}
diff --git a/services/app/assets/static/images/achievements/clojure.png b/services/app/assets/static/images/achievements/clojure.png
new file mode 100644
index 000000000..dc29a8293
Binary files /dev/null and b/services/app/assets/static/images/achievements/clojure.png differ
diff --git a/services/app/assets/static/images/achievements/elixir.png b/services/app/assets/static/images/achievements/elixir.png
new file mode 100644
index 000000000..f7d809ccd
Binary files /dev/null and b/services/app/assets/static/images/achievements/elixir.png differ
diff --git a/services/app/assets/static/images/achievements/haskell.png b/services/app/assets/static/images/achievements/haskell.png
new file mode 100644
index 000000000..b158bec7e
Binary files /dev/null and b/services/app/assets/static/images/achievements/haskell.png differ
diff --git a/services/app/assets/static/images/achievements/js.png b/services/app/assets/static/images/achievements/js.png
new file mode 100644
index 000000000..890eb593c
Binary files /dev/null and b/services/app/assets/static/images/achievements/js.png differ
diff --git a/services/app/assets/static/images/achievements/perl.png b/services/app/assets/static/images/achievements/perl.png
new file mode 100644
index 000000000..84ecbcc9f
Binary files /dev/null and b/services/app/assets/static/images/achievements/perl.png differ
diff --git a/services/app/assets/static/images/achievements/php.png b/services/app/assets/static/images/achievements/php.png
new file mode 100644
index 000000000..5e8b57df7
Binary files /dev/null and b/services/app/assets/static/images/achievements/php.png differ
diff --git a/services/app/assets/static/images/achievements/polyglot.png b/services/app/assets/static/images/achievements/polyglot.png
new file mode 100644
index 000000000..10b92dc7d
Binary files /dev/null and b/services/app/assets/static/images/achievements/polyglot.png differ
diff --git a/services/app/assets/static/images/achievements/python.png b/services/app/assets/static/images/achievements/python.png
new file mode 100644
index 000000000..3f6442c74
Binary files /dev/null and b/services/app/assets/static/images/achievements/python.png differ
diff --git a/services/app/assets/static/images/achievements/ruby.png b/services/app/assets/static/images/achievements/ruby.png
new file mode 100644
index 000000000..0da08e29e
Binary files /dev/null and b/services/app/assets/static/images/achievements/ruby.png differ
diff --git a/services/app/lib/codebattle/game_process/engine/base.ex b/services/app/lib/codebattle/game_process/engine/base.ex
index 1a26142b4..cf4913007 100644
--- a/services/app/lib/codebattle/game_process/engine/base.ex
+++ b/services/app/lib/codebattle/game_process/engine/base.ex
@@ -54,7 +54,7 @@ defmodule Codebattle.GameProcess.Engine.Base do
creator: winner.creator,
rating: new_winner_rating,
rating_diff: winner_rating_diff,
- lang: Map.get(winner, :lang, nil)
+ lang: Map.get(winner, :editor_lang)
})
create_user_game!(%{
@@ -64,7 +64,7 @@ defmodule Codebattle.GameProcess.Engine.Base do
creator: loser.creator,
rating: new_loser_rating,
rating_diff: loser_rating_diff,
- lang: Map.get(loser, :lang, nil)
+ lang: Map.get(loser, :editor_lang)
})
winner_achievements = Achievements.recalculate_achievements(winner)
diff --git a/services/app/lib/codebattle/game_process/engine/standard.ex b/services/app/lib/codebattle/game_process/engine/standard.ex
index 10523ee07..df6b7313d 100644
--- a/services/app/lib/codebattle/game_process/engine/standard.ex
+++ b/services/app/lib/codebattle/game_process/engine/standard.ex
@@ -103,7 +103,6 @@ defmodule Codebattle.GameProcess.Engine.Standard do
def handle_give_up(game_id, loser, fsm) do
winner = FsmHelpers.get_opponent(fsm, loser.id)
-
store_game_result_async!(fsm, {winner, "won"}, {loser, "gave_up"})
ActiveGames.terminate_game(game_id)
end
diff --git a/services/app/lib/codebattle/user/achievements.ex b/services/app/lib/codebattle/user/achievements.ex
index 3ce88e604..6295d577a 100644
--- a/services/app/lib/codebattle/user/achievements.ex
+++ b/services/app/lib/codebattle/user/achievements.ex
@@ -10,6 +10,7 @@ defmodule Codebattle.User.Achievements do
def recalculate_achievements(user) do
{user.achievements, user}
|> count_played_games
+ |> count_wins
|> elem(0)
end
@@ -62,4 +63,33 @@ defmodule Codebattle.User.Achievements do
{achievements, user}
end
end
+
+ def count_wins({achievements, user}) do
+ query =
+ from(ug in UserGame,
+ select: {
+ ug.lang,
+ count(ug.id)
+ },
+ where: ug.user_id == ^user.id and ug.result == "won" and not is_nil(ug.lang),
+ group_by: ug.lang
+ )
+
+ languages = Repo.all(query) |> Enum.into(%{}) |> Map.keys()
+ exist_achievement = Enum.filter(achievements, fn x -> String.contains?(x, "win_games_with") end) |> Enum.at(0)
+ new_achievement = "win_games_with?#{Enum.join(languages, "_")}"
+ cond do
+ Enum.count(languages) >= 3 ->
+ if (new_achievement !== exist_achievement) do
+ new_list = List.delete(achievements, exist_achievement)
+ {new_list ++ [new_achievement], user}
+ else
+ {achievements, user}
+ end
+ true ->
+ {achievements, user}
+ end
+
+
+ end
end
diff --git a/services/app/lib/codebattle_web/templates/user/show.html.slim b/services/app/lib/codebattle_web/templates/user/show.html.slim
index dd577ff25..99f8c9948 100644
--- a/services/app/lib/codebattle_web/templates/user/show.html.slim
+++ b/services/app/lib/codebattle_web/templates/user/show.html.slim
@@ -35,8 +35,16 @@
.col-12.text-center.mt-4
h2.mt-1.mb-0
| Achievements:
- ul.list-inline
- = for achievement <- @user.achievements do
- li.list-inline-item
- img.img-fluid.rounded[alt="#{achievement}" title="#{achievement}" src="/assets/images/achievements/#{achievement}.png" width="200" height="200"]
+ div.d-flex.justify-content-center.profile
+ = Enum.map @user.achievements, fn achievement ->
+ - condition = String.contains?(achievement, "win_games_with")
+ = if condition do
+ div.polyglot[title="#{achievement}"]
+ div.d-flex.h-75.flex-wrap.align-items-center.justify-content-around
+ - langs = String.split(achievement, "?") |> Enum.at(1) |> String.split("_")
+ = Enum.map langs, fn lang ->
+ img.[alt="#{lang}" title="#{lang}" src="/assets/images/achievements/#{lang}.png" width="38" height="38"]
+ - else
+ img.mr-1[alt="#{achievement}" title="#{achievement}" src="/assets/images/achievements/#{achievement}.png" width="200" height="200"]
+
\ No newline at end of file
diff --git a/services/app/test/codebattle_web/integration/recalculate_achivements_test.exs b/services/app/test/codebattle_web/integration/recalculate_achivements_test.exs
index c1c5cdf8f..28899c9bd 100644
--- a/services/app/test/codebattle_web/integration/recalculate_achivements_test.exs
+++ b/services/app/test/codebattle_web/integration/recalculate_achivements_test.exs
@@ -56,7 +56,7 @@ defmodule RecalculateAchivementsTest do
conn =
conn1
|> get(page_path(conn1, :index))
- |> post(game_path(conn1, :create, level: "easy"))
+ |> post(game_path(conn1, :create, level: "easy", lang: "js"))
game_id = game_id_from_conn(conn)
@@ -66,7 +66,48 @@ defmodule RecalculateAchivementsTest do
# Second player join game
post(conn2, game_path(conn2, :join, game_id))
{:ok, _response, socket2} = subscribe_and_join(socket2, GameChannel, game_topic)
+ # First player won
+ editor_text1 = "Hello world1!"
+ Phoenix.ChannelTest.push(socket1, "check_result", %{editor_text: editor_text1, lang: "js"})
+ :timer.sleep(100)
+ fsm = Server.fsm(game_id)
+
+ user = Repo.get(User, user1.id)
+ assert user.achievements == ["played_ten_games", "win_games_with?js_php_ruby"]
+ end
+ end
+
+ test "calculate polyglot achievement", %{
+ conn1: conn1,
+ conn2: conn2,
+ socket1: socket1,
+ socket2: socket2,
+ user1: user1,
+ user2: user2
+ } do
+ with_mocks [
+ {Codebattle.CodeCheck.Checker, [], [check: fn _a, _b, _c -> {:ok, "asdf", "asdf"} end]}
+ ] do
+
+ ["js", "php", "ruby"]
+ |> Enum.each(fn x ->
+ insert_list(3, :user_game, %{user: user1, lang: x, result: "won"})
+ end)
+
+ # Create game
+ conn =
+ conn1
+ |> get(page_path(conn1, :index))
+ |> post(game_path(conn1, :create, level: "easy", lang: "js"))
+ game_id = game_id_from_conn(conn)
+
+ game_topic = "game:" <> to_string(game_id)
+ {:ok, _response, socket1} = subscribe_and_join(socket1, GameChannel, game_topic)
+
+ # Second player join game
+ post(conn2, game_path(conn2, :join, game_id))
+ {:ok, _response, socket2} = subscribe_and_join(socket2, GameChannel, game_topic)
# First player won
editor_text1 = "Hello world1!"
Phoenix.ChannelTest.push(socket1, "check_result", %{editor_text: editor_text1, lang: "js"})
@@ -74,7 +115,7 @@ defmodule RecalculateAchivementsTest do
fsm = Server.fsm(game_id)
user = Repo.get(User, user1.id)
- assert user.achievements == ["played_ten_games"]
+ assert user.achievements == ["played_ten_games", "win_games_with?js_php_ruby"]
end
end
end
diff --git a/services/app/test/codebattle_web/integration/user_achivements_test.ex b/services/app/test/codebattle_web/integration/user_achivements_test.exs
similarity index 96%
rename from services/app/test/codebattle_web/integration/user_achivements_test.ex
rename to services/app/test/codebattle_web/integration/user_achivements_test.exs
index 7ddac26b3..c0f7bb361 100644
--- a/services/app/test/codebattle_web/integration/user_achivements_test.ex
+++ b/services/app/test/codebattle_web/integration/user_achivements_test.exs
@@ -51,7 +51,7 @@ defmodule Codebattle.RandomTaskSelectorTest do
conn =
conn1
|> get(page_path(conn1, :index))
- |> post(game_path(conn1, :create, level: "easy"))
+ |> post(game_path(conn1, :create, level: "easy", lang: "js"))
game_id = game_id_from_conn(conn)