From 4365a6bffbdede348b4eac7ac89b12c4bde16dc3 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 6 Apr 2020 20:01:07 +0200 Subject: [PATCH] Improvements detected in first field test (#9) Fix #2, #3, #4 and #5. --- .../LichessTournamentAggregator.App.csproj | 2 +- LichessTournamentAggregator.App/Program.cs | 19 +++++--- .../TournamentAggregatorTest.cs | 45 +++++++++++++++++++ LichessTournamentAggregator.sln | 5 ++- .../ITournamentAggregator.cs | 2 +- .../LichessTournamentAggregator.csproj | 2 +- .../Properties/AssemblyInfo.cs | 3 ++ .../TournamentAggregator.cs | 42 ++++++++++------- README.md | 19 ++++---- 9 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 LichessTournamentAggregator.Test/TournamentAggregatorTest.cs create mode 100644 LichessTournamentAggregator/Properties/AssemblyInfo.cs diff --git a/LichessTournamentAggregator.App/LichessTournamentAggregator.App.csproj b/LichessTournamentAggregator.App/LichessTournamentAggregator.App.csproj index 327183f..4c62bed 100644 --- a/LichessTournamentAggregator.App/LichessTournamentAggregator.App.csproj +++ b/LichessTournamentAggregator.App/LichessTournamentAggregator.App.csproj @@ -7,7 +7,7 @@ win-x64;win-x86;linux-x64;osx-x64 true true - 0.1.2 + 0.2.0 Eduardo Cáceres Multiplatform desktop app that allows you to aggregate the results of multiple Lichess tournaments diff --git a/LichessTournamentAggregator.App/Program.cs b/LichessTournamentAggregator.App/Program.cs index 7e596d2..78a21b3 100644 --- a/LichessTournamentAggregator.App/Program.cs +++ b/LichessTournamentAggregator.App/Program.cs @@ -11,7 +11,7 @@ public static class Program { private const string RepoUrl = "https://github.com/eduherminio/LichessTournamentAggregator"; private static readonly string FailureMessage = "The program has failed unexpectedly," + - $" please raise an issue in {RepoUrl}/issues (or just contact me)," + + $" please have a look at our FAQ: {RepoUrl}#faqs (or just contact me)," + $" including the following info:{Environment.NewLine}"; public static async Task Main(string[] args) @@ -32,7 +32,7 @@ public static async Task Main(string[] args) catch (ArgumentException e) { Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("Please make this is the tournament url you want to aggregate:"); + Console.WriteLine("Please make sure this is the tournament url you want to aggregate:"); Console.WriteLine($"*\t{e.ParamName}"); } catch (HttpRequestException) @@ -41,6 +41,13 @@ public static async Task Main(string[] args) Console.WriteLine("There may be some issues with Lichess server or you've reached the API limit."); Console.WriteLine($"Please try again in a few minutes. If the problem persists, raise an issue in {RepoUrl}/issues"); } + catch (UnauthorizedAccessException) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"This app doesn't have permissions to write to {Path.GetFullPath(fileName)}"); + Console.WriteLine("Please run it as administrator (right click -> run it as administrator)\n" + + "or move the executable somewhere under C:/Users/"); + } catch (Exception e) { Console.ForegroundColor = ConsoleColor.Red; @@ -57,8 +64,8 @@ public static async Task Main(string[] args) } Console.ResetColor(); - Console.WriteLine("\nPress any key to close this window"); - Console.ReadKey(); + Console.WriteLine("\nPress intro to close this window"); + Console.ReadLine(); } } @@ -81,13 +88,13 @@ private static IEnumerable AskForTournaments() private static async Task AggregateResultsAndCreateCsvFile(IEnumerable args, string fileName) { - var aggregator = new TournamentAggregator(); using FileStream fs = new FileStream(fileName, FileMode.Create); + var aggregator = new TournamentAggregator(); await aggregator.AggregateResultsAndExportToCsv(args, fs); Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"Aggregation finished, results can be found in {fileName}"); + Console.WriteLine($"Aggregation finished, results can be found in {Path.GetFullPath(fileName)}"); } } } diff --git a/LichessTournamentAggregator.Test/TournamentAggregatorTest.cs b/LichessTournamentAggregator.Test/TournamentAggregatorTest.cs new file mode 100644 index 0000000..0a2fe78 --- /dev/null +++ b/LichessTournamentAggregator.Test/TournamentAggregatorTest.cs @@ -0,0 +1,45 @@ +using System.Linq; +using Xunit; + +namespace LichessTournamentAggregator.Test +{ + public class TournamentAggregatorTest + { + private readonly TournamentAggregator _aggregator; + + public TournamentAggregatorTest() + { + _aggregator = new TournamentAggregator(); + } + + [Fact] + public void GetUrls() + { + const string tournamentId = "1op8aqN0"; + + var validInputs = new[] + { + $"https://lichess.org/tournament/{tournamentId}#", + $"https://lichess.org/tournament/{tournamentId}/", + $"https://lichess.org/tournament/{tournamentId}#/", + $"https://lichess.org/tournament/{tournamentId}/#", + $"lichess.org/tournament/{tournamentId}#", + $"lichess.org/tournament/{tournamentId}/", + $"lichess.org/tournament/{tournamentId}#/", + $"lichess.org/tournament/{tournamentId}/#", + tournamentId, + $"{tournamentId} ", + $"{tournamentId}#", + $"{tournamentId}/", + $"{tournamentId}#/", + $"{tournamentId}/#" + }; + + var results = _aggregator.GetUrls(validInputs).ToList(); + + Assert.Equal( + $"https://lichess.org/api/tournament/{tournamentId}/results", + results.ToHashSet().Single().OriginalString); + } + } +} diff --git a/LichessTournamentAggregator.sln b/LichessTournamentAggregator.sln index a610810..dce2d1c 100644 --- a/LichessTournamentAggregator.sln +++ b/LichessTournamentAggregator.sln @@ -5,14 +5,15 @@ VisualStudioVersion = 16.0.29911.84 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LichessTournamentAggregator", "LichessTournamentAggregator\LichessTournamentAggregator.csproj", "{059FA297-6226-4D84-98C3-D3001DDE142D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LichessTournamentAggregator.App", "LichessTournamentAggregator.App\LichessTournamentAggregator.App.csproj", "{B7DE2184-C3D7-42AE-A9BC-0955D49B3E90}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LichessTournamentAggregator.App", "LichessTournamentAggregator.App\LichessTournamentAggregator.App.csproj", "{B7DE2184-C3D7-42AE-A9BC-0955D49B3E90}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CA90D9FD-22B8-46CC-9FEB-4E7C959471C5}" ProjectSection(SolutionItems) = preProject azure-pipelines.yml = azure-pipelines.yml + README.md = README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LichessTournamentAggregator.Test", "LichessTournamentAggregator.Test\LichessTournamentAggregator.Test.csproj", "{BB2E3C40-F996-4719-BEC4-D89BD415FEBF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LichessTournamentAggregator.Test", "LichessTournamentAggregator.Test\LichessTournamentAggregator.Test.csproj", "{BB2E3C40-F996-4719-BEC4-D89BD415FEBF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/LichessTournamentAggregator/ITournamentAggregator.cs b/LichessTournamentAggregator/ITournamentAggregator.cs index a715685..0ffebff 100644 --- a/LichessTournamentAggregator/ITournamentAggregator.cs +++ b/LichessTournamentAggregator/ITournamentAggregator.cs @@ -20,7 +20,7 @@ public interface ITournamentAggregator /// /// Stream where data wants to be written into /// ; by default - /// + /// .csv FileStream with aggregated results, ordered by total scores Task AggregateResultsAndExportToCsv(IEnumerable tournamentIdsOrUrls, FileStream fileStream, string separator = ";"); } } diff --git a/LichessTournamentAggregator/LichessTournamentAggregator.csproj b/LichessTournamentAggregator/LichessTournamentAggregator.csproj index 35f76af..c4e2e0e 100644 --- a/LichessTournamentAggregator/LichessTournamentAggregator.csproj +++ b/LichessTournamentAggregator/LichessTournamentAggregator.csproj @@ -7,7 +7,7 @@ snupkg LichessTournamentAggregator LichessTournamentAggregator - 0.1.1 + 0.2.0 Chess, Lichess, Tournament, Aggregator $(PackageTags) Eduardo Cáceres diff --git a/LichessTournamentAggregator/Properties/AssemblyInfo.cs b/LichessTournamentAggregator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ee062e3 --- /dev/null +++ b/LichessTournamentAggregator/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("LichessTournamentAggregator.Test")] diff --git a/LichessTournamentAggregator/TournamentAggregator.cs b/LichessTournamentAggregator/TournamentAggregator.cs index 48385a2..10a034f 100644 --- a/LichessTournamentAggregator/TournamentAggregator.cs +++ b/LichessTournamentAggregator/TournamentAggregator.cs @@ -29,35 +29,30 @@ public async IAsyncEnumerable AggregateResults(IEnumerable AggregateResultsAndExportToCsv(IEnumerable tournamentIdsOrUrls, FileStream fileStream, string separator = ";") { - using StreamWriter sw = new StreamWriter(fileStream); - - var headers = new List { "Username", "Total Score", "Max Rating", "Ranks", "Scores", "Average Performance" }; - var lines = new List { string.Join(separator, headers) }; - - var internalSeparator = separator == ";" ? "," : ";"; - string aggregate(IEnumerable items) => $"[{string.Join(internalSeparator, items)}]"; - + var aggregatedResults = new List(); await foreach (var result in AggregateResults(tournamentIdsOrUrls)) { - var columns = new string[] { result.Username, result.TotalScores.ToString(), result.MaxRating.ToString(), aggregate(result.Ranks), aggregate(result.Scores), result.AveragePerformance.ToString("F") }; - lines.Add(string.Join(separator, columns)); + aggregatedResults.Add(result); } - lines.ForEach(sw.WriteLine); + aggregatedResults = aggregatedResults + .OrderByDescending(r => r.TotalScores) + .ThenByDescending(r => r.AveragePerformance) + .ToList(); - return fileStream; + return PopulateCsvStream(fileStream, separator, aggregatedResults); } - private IEnumerable GetUrls(IEnumerable tournamentIdsOrUrls) + internal IEnumerable GetUrls(IEnumerable tournamentIdsOrUrls) { const string lichessTournamentUrl = "lichess.org/tournament/"; foreach (var item in tournamentIdsOrUrls.Select(str => str.Trim())) { - string tournamentId = item; + string tournamentId = item.Trim(new char[] { ' ', '/', '#' }); if (tournamentId.Contains(lichessTournamentUrl)) { - var reverse = string.Join("", item.Reverse()); + var reverse = string.Join("", tournamentId.Reverse()); tournamentId = string.Join("", reverse.Take(reverse.IndexOf("/")).Reverse()); } @@ -82,5 +77,22 @@ private async Task> GetTournamentResults(Uri url) return lines.Select(line => JsonSerializer.Deserialize(line)); } + + private static FileStream PopulateCsvStream(FileStream fileStream, string separator, IEnumerable aggregatedResults) + { + var headers = new List { "Username", "Total Score", "Average Performance", "Max Rating", "Ranks", "Scores" }; + using var sw = new StreamWriter(fileStream); + sw.WriteLine(string.Join(separator, headers)); + + var internalSeparator = separator == ";" ? "," : ";"; + string aggregate(IEnumerable items) => $"[{string.Join(internalSeparator, items)}]"; + foreach (var result in aggregatedResults) + { + var columns = new string[] { result.Username, result.TotalScores.ToString(), result.AveragePerformance.ToString("F"), result.MaxRating.ToString(), aggregate(result.Ranks), aggregate(result.Scores) }; + sw.WriteLine(string.Join(separator, columns)); + } + + return fileStream; + } } } diff --git a/README.md b/README.md index dc0ce26..ae4cbc6 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,14 @@ **Lichess Tournament Aggregator** is a simple command line application that uses [Lichess API](https://github.com/lichess-org/api) to aggregate the results of multiple tournaments and write them to a `.csv` file. +It's currently available for **Windows** (10, 8.1 and 7, for both 32 and 64 bits), **Linux** (Ubuntu, CentOS, Debian, Fedora and derivatives) and **macOS** (macOS 10.12 Sierra and above). +If you happen to use another operating system, another architecture (arm) or an older version of macOS, please open an [issue](https://github.com/eduherminio/LichessTournamentAggregator/issues) and I'll be happy to try to help. + It's also available as a NuGet package. ## Usage -Executables for different operating systems can be found [here](https://github.com/eduherminio/LichessTournamentAggregator/releases). - -- Download the appropiated version. +- Download the appropiated version [from here](https://github.com/eduherminio/LichessTournamentAggregator/releases) - Run the executable. - Enter the tournament urls or ids whose results you want to aggregate (one per line). - Press double `enter` to start the process. @@ -31,10 +32,6 @@ LichessTournamentAggregator-win-x64.exe 2nu1SO6E https://lichess.org/tournament/ ## FAQs -> For which operating systems is the tool available? - -It's currently available for **Windows** (10, 8.1 and 7, for both 32 and 64 bits), **Linux** (Ubuntu, CentOS, Debian, Fedora and derivatives) and **macOS** (macOS 10.12 Sierra and above). If you happen to use another operating system, another architecture (arm) or an older version of macOS, please open an [issue](https://github.com/eduherminio/LichessTournamentAggregator/issues) and I'll be happy to try to help. - > I'm a Windows user. How do I know which version to use? You can check that in `System Information` -> `System type`. Although if you use a reasonably new computer, it's probably going to be x64. @@ -43,12 +40,14 @@ You can check that in `System Information` -> `System type`. Although if you use Try pressing enter again. If it still doesn't work and you get no further any other messages in the console, please fill an [issue](https://github.com/eduherminio/LichessTournamentAggregator/issues) and I'll do my best to help you. +> I'm seeing a red message + +Please read it and follow the instructions there. + > I've imported the `*.csv` file and the format seems wrong :( Please make sure to select `;` as separator when importing the file. -> The clasification isn't ordered. - -This program won't order the stats of the different players following any specific criteria, you should do that yourself using your spreadsheet editor. +--- Feel free to contact or or fill an [issue](https://github.com/eduherminio/LichessTournamentAggregator/issues) if you're struggling to use the application or the NuGet package in any way, I'm always happy to help 😉