Skip to content

Commit

Permalink
Merge pull request #3 from afuersch/playerlist
Browse files Browse the repository at this point in the history
Close #1 List of players.
May not be the best solution, but this works for now.
  • Loading branch information
afuersch committed Oct 15, 2015
2 parents d7e134b + 95fbad6 commit 0a49f1a
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 2 deletions.
69 changes: 69 additions & 0 deletions src/Wuzlstats/Controllers/PlayersController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Linq;
using Microsoft.AspNet.Mvc;
using Wuzlstats.Models;
using Wuzlstats.ViewModels.Players;
using System;
using System.Threading.Tasks;
using Wuzlstats.Services;

namespace Wuzlstats.Controllers
{
public class PlayersController : Controller
{
private readonly Db _db;
private readonly AppSettings _settings;
private readonly PlayersService _statisticsService;

public PlayersController(Db db, AppSettings settings)
{
_settings = settings;
_db = db;
_statisticsService = new PlayersService(_db);
}

[Route("~/League/{league}/Players")]
public async Task<IActionResult> Index(string league, string sort, bool recent)
{
var leagueEntity = _db.Leagues.FirstOrDefault(x => x.Name.ToLower() == league.ToLower());
if (leagueEntity == null)
{
return RedirectToAction("Index", "Leagues");
}
ViewBag.CurrentLeague = leagueEntity.Name;
var players = await _statisticsService.FindPlayersOfLeague(leagueEntity.Id, recent ?_settings.DaysForStatistics : default(int?));

switch (sort)
{
case "best":
players = players.OrderByDescending(x => x.Rank);
break;
case "worst":
players = players.OrderBy(x => x.Rank);
break;
case "activity":
players = players.OrderByDescending(x => x.Wins + x.Losses);
break;
default:
break;
}

return View(new IndexViewModel
{
ActiveFilter = sort,
Recent = recent,
Players = players.Select(player => new PlayerViewModel
{
PlayerId = player.Id,
Name = player.Name,
Image = player.Image == null || player.Image.Length <= 0 ? EmptyAvatar.Base64 : Convert.ToBase64String(player.Image),
Wins = player.Wins,
Losses = player.Losses,
SingleGames = player.SingleGames,
TeamGames = player.TeamGames,
LastGamePlayedOn = player.LatestGame
})
});
}

}
}
119 changes: 119 additions & 0 deletions src/Wuzlstats/Services/PlayersService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using Microsoft.Data.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Wuzlstats.Models;

namespace Wuzlstats.Services
{
public class PlayersService
{
private readonly Db _db;

public PlayersService(Db db)
{
_db = db;
}

public async Task<IEnumerable<PlayerDto>> FindPlayersOfLeague(int leagueId, int? daysForStatistics)
{
var gamesQuery = _db.Games.AsNoTracking().Where(x => x.LeagueId == leagueId);
if (daysForStatistics.HasValue)
{
var date = DateTime.UtcNow.Date.AddDays(-daysForStatistics.Value);
gamesQuery = gamesQuery.Where(x => x.Date >= date);
}

// EF7 beta4 does not support navigation properties in queries yet
// this complicates the code a lot, because we need joins :(

var players = new List<PlayerDto>();

foreach (var game in await gamesQuery.ToListAsync())
{
var positions = await (from position in _db.PlayerPositions.AsNoTracking()
join player in _db.Players.AsNoTracking() on position.PlayerId equals player.Id
where position.GameId == game.Id
select new
{
position.Position,
Player = player
}).ToListAsync();
// player stats
foreach (var position in positions)
{
var player = players.FirstOrDefault(x => x.Equals(position.Player));
if (player == null)
{
player = PlayerDto.Create(position.Player);
players.Add(player);
}
// calculate count of single or team games
if (position.Position == PlayerPositionTypes.Blue || position.Position == PlayerPositionTypes.Red)
{
player.SingleGames++;
}
else
{
player.TeamGames++;
}

// don't count ties
if (game.BlueWins && (position.Position == PlayerPositionTypes.Blue || position.Position == PlayerPositionTypes.BlueDefense || position.Position == PlayerPositionTypes.BlueOffense))
{
player.Wins++;
}
else if (game.RedWins && (position.Position == PlayerPositionTypes.Red || position.Position == PlayerPositionTypes.RedDefense || position.Position == PlayerPositionTypes.RedOffense))
{
player.Wins++;
}
else if (game.RedWins &&
(position.Position == PlayerPositionTypes.Blue || position.Position == PlayerPositionTypes.BlueDefense || position.Position == PlayerPositionTypes.BlueOffense))
{
player.Losses++;
}
else if (game.BlueWins &&
(position.Position == PlayerPositionTypes.Red || position.Position == PlayerPositionTypes.RedDefense || position.Position == PlayerPositionTypes.RedOffense))
{
player.Losses++;
}
if (game.Date > player.LatestGame)
{
player.LatestGame = game.Date;
}
}
}
return players.OrderByDescending(x => x.LatestGame);
}
}

public class PlayerDto
{
public int Id { get; set; }
public string Name { get; set; }
public byte[] Image { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public int SingleGames { get; set; }
public int TeamGames { get; set; }
public DateTime LatestGame { get; set; }

public double Rank => Losses == 0 ? Wins : (Wins == 0 ? 0.1d / Losses : (double)Wins / Losses);

public bool Equals(Player p)
{
return Id == p.Id;
}

public static PlayerDto Create(Player p)
{
return new PlayerDto
{
Id = p.Id,
Name = p.Name,
Image = p.Image
};
}
}
}
11 changes: 11 additions & 0 deletions src/Wuzlstats/ViewModels/Players/IndexViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace Wuzlstats.ViewModels.Players
{
public class IndexViewModel
{
public string ActiveFilter { get; set; }
public bool Recent { get; set; }
public IEnumerable<PlayerViewModel> Players { get; set; }
}
}
16 changes: 16 additions & 0 deletions src/Wuzlstats/ViewModels/Players/PlayerViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Wuzlstats.ViewModels.Players
{
public class PlayerViewModel
{
public int PlayerId { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public DateTime LastGamePlayedOn { get; set; }
public int SingleGames { get; set; }
public int TeamGames { get; set; }
}
}
4 changes: 2 additions & 2 deletions src/Wuzlstats/Views/Home/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@
<div class="col-md-6">
<div class="row">
<div class="col-sm-6">
<h3 class="text-center">The best players</h3>
<h3 class="text-center"><a href="@Url.Action("Index", "Players", new { league = Model.Name, sort = "best", recent = true })">The best players</a></h3>
<ul id="bestPlayerRanking">
<li class="progress">
<div class="progress-bar progress-bar-striped active" style="width: 100%"></div>
</li>
</ul>
</div>
<div class="col-sm-6">
<h3 class="text-center">The worst players</h3>
<h3 class="text-center"><a href="@Url.Action("Index", "Players", new { league = Model.Name, sort = "worst", recent = true })">The worst players</a></h3>
<ul id="worstPlayerRanking">
<li class="progress">
<div class="progress-bar progress-bar-striped active" style="width: 100%"></div>
Expand Down
55 changes: 55 additions & 0 deletions src/Wuzlstats/Views/Players/Index.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
@model Wuzlstats.ViewModels.Players.IndexViewModel

<p class="help-block">These are all the players. Ever.<br />No one will be forgotten. Shame and fame will last till the very end of days, or at least until a database crash with no backups.</p>

<ul class="list-unstyled list-inline filter">
<li class="@(string.IsNullOrEmpty(Model.ActiveFilter) ? "active" : "")"><a href="@Url.Action("Index", "Players", new { recent = Model.Recent })">Last played</a></li>
<li class="@(Model.ActiveFilter == "best" ? "active" : "")"><a href="@Url.Action("Index", "Players", new { sort = "best", recent = Model.Recent })">Best</a></li>
<li class="@(Model.ActiveFilter == "worst" ? "active" : "")"><a href="@Url.Action("Index", "Players", new { sort = "worst", recent = Model.Recent })">Worst</a></li>
<li class="@(Model.ActiveFilter == "activity" ? "active" : "")"><a href="@Url.Action("Index", "Players", new { sort = "activity", recent = Model.Recent })">Most active</a></li>
</ul>

<table class="table table-striped ranking players">
<thead>
<tr>
<th></th>
<th>Player</th>
<th>Score</th>
<th>Last game</th>
<th># 1 vs. 1</th>
<th># 2 vs. 2</th>
</tr>
</thead>
<tbody>
@{var rank = 1; }
@foreach (var player in Model.Players)
{
<tr>
<td>
@(rank++)
</td>
<td>
<a href="@Url.Action("Index", "Player", new { id = player.PlayerId })">
<img src="data:image/png;base64,@player.Image" alt="@player.Name" class="ranking-image" />
</a>
<div class="player-name">@player.Name</div>
</td>
<td>
<div class="player-score">
<span class="ranking-wins">@player.Wins<span class="glyphicon glyphicon-thumbs-up"></span></span>
<span class="ranking-losses">@player.Losses<span class="glyphicon glyphicon-thumbs-down"></span></span>
</div>
</td>
<td>
@player.LastGamePlayedOn.ToShortDateString()
</td>
<td>
@player.SingleGames
</td>
<td>
@player.TeamGames
</td>
</tr>
}
</tbody>
</table>
13 changes: 13 additions & 0 deletions src/Wuzlstats/wwwroot/css/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,16 @@ footer {
@media (max-width: 768px) {
footer {
margin-top: 50px; } }

.filter li.active {
font-weight: bold; }

.players td > a:focus, .players td > a:hover {
text-decoration: none; }

.players .player-name {
font-weight: bold;
display: inline; }

.players .player-score {
color: #999; }
24 changes: 24 additions & 0 deletions src/Wuzlstats/wwwroot/css/site.scss
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,27 @@ footer {
margin-top: 50px;
}
}

//Players page
.filter {
li.active {
font-weight: bold;
}
}

.players {
td > a {
&:focus, &:hover {
text-decoration: none;
}
}

.player-name {
font-weight: bold;
display: inline;
}

.player-score {
color: #999;
}
}

0 comments on commit 0a49f1a

Please sign in to comment.