Skip to content

Commit 179f257

Browse files
committed
Add more fields parsed from worldjigsawpuzzle.org
Add first version of CVS parsing
1 parent eded931 commit 179f257

File tree

10 files changed

+112
-20
lines changed

10 files changed

+112
-20
lines changed

Arctic.Puzzlers.Objects/CompetitionObjects/Competition.cs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public class Competition
55
public Competition()
66
{
77
CompetitionGroups = new List<CompetitionGroup>();
8+
CompetitionId = Guid.NewGuid();
89
}
910

1011
public Guid CompetitionId { get; set; }

Arctic.Puzzlers.Objects/CompetitionObjects/CompetitionRound.cs

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace Arctic.Puzzlers.Objects.CompetitionObjects
66
{
77
public class CompetitionRound
88
{
9+
public Guid RoundId { get; set; } = Guid.NewGuid();
10+
911
public string RoundName { get; set; } = string.Empty;
1012
[JsonConverter(typeof(JsonStringEnumConverter))]
1113
public RoundType RoundType { get; set; }
@@ -14,6 +16,9 @@ public class CompetitionRound
1416
public List<ParticipantResult> Participants { get; set; } = new List<ParticipantResult>();
1517
public List<Puzzle> Puzzles { get; set; } = new List<Puzzle>();
1618
public DateTime Time { get; set; } = new DateTime();
19+
20+
public TimeSpan MaxTime { get; set; } = new TimeSpan();
21+
public int NumberOfPieces { get; set; } = 0;
1722
public string Location { get; set; } = string.Empty;
1823
public string Url { get; set; } = string.Empty;
1924
}

Arctic.Puzzlers.Objects/Misc/CompetitionCSV.cs

+6-7
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
////date, player(s) name event name, piece count, rank, time, and remaining pieces
44
public class CompetitionCSV
55
{
6-
DateTime Date { get; set; }
7-
string PlayersName { get; set; } = string.Empty;
8-
string EventName { get; set; } = string.Empty;
9-
long PieceCount { get; set; }
10-
11-
long Rank { get; set; }
12-
TimeSpan Time { get; set; }
6+
public DateTime Date { get; set; }
7+
public string PlayersName { get; set; } = string.Empty;
8+
public string EventName { get; set; } = string.Empty;
9+
public long PieceCount { get; set; }
1310

11+
public long Rank { get; set; }
12+
public TimeSpan Time { get; set; }
1413
}
1514
}

Arctic.Puzzlers.Objects/PuzzleObjects/Puzzle.cs

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public class Puzzle
99
[JsonConverter(typeof(BrandNameJsonConvert))]
1010
public BrandName BrandName { get; set; }
1111
public string? Name { get; set; }
12+
public int Size { get; set; }
1213
}
1314
}

Arctic.Puzzlers.Parsers/CompetitionParsers/CompetitionRoundExtensions.cs

-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
using Arctic.Puzzlers.Objects.CompetitionObjects;
2-
using System;
3-
using System.Collections.Generic;
4-
using System.Linq;
5-
using System.Text;
6-
using System.Threading.Tasks;
72

83
namespace Arctic.Puzzlers.Parsers.CompetitionParsers
94
{

Arctic.Puzzlers.Parsers/CompetitionParsers/WorldJigsawPuzzleOrgParser.cs

+47-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Arctic.Puzzlers.Stores;
44
using HtmlAgilityPack;
55
using Microsoft.Extensions.Logging;
6+
using System.Diagnostics;
67
using System.Globalization;
78
using System.Text.RegularExpressions;
89
using System.Xml.Linq;
@@ -42,12 +43,20 @@ public async Task Parse(string baseurl)
4243
{
4344
continue;
4445
}
46+
47+
var competitionName = competitionPage.DocumentNode.SelectNodes("//p[contains(@class,'nombre_campeonato')]");
4548
var listOfRounds = competitionPage.DocumentNode.SelectNodes("//nav[contains(@class,'nav-underline')]/a");
49+
var competition = new Competition();
50+
if (competitionName != null && competitionName.Count() == 1)
51+
{
52+
competition.Name = competitionName[0].InnerText;
53+
}
54+
4655

4756
var individualRounds = listOfRounds.Where(t => t.GetAttributeValue("href", "").ToLower().Contains("individual"));
4857
var pairRounds = listOfRounds.Where(t => t.GetAttributeValue("href", "").ToLower().Contains("pairs"));
4958
var teamRounds = listOfRounds.Where(t => t.GetAttributeValue("href", "").ToLower().Contains("teams"));
50-
var competition = new Competition();
59+
5160
competition.Url = competitionUrl;
5261
if (individualRounds.Any())
5362
{
@@ -184,7 +193,8 @@ private async Task<CompetitionGroup> AddRound(string baseUrl, IEnumerable<HtmlNo
184193

185194
competitionRound.RoundType = competitionRound.RoundName.ToLower() == "final"? RoundType.Final: competitionRound.RoundName.ToLower().StartsWith('s') ? RoundType.Semifinal : RoundType.Qualification;
186195
var placeAndTime = doc.DocumentNode.SelectSingleNode("//p[@class='lead']").InnerText;
187-
196+
var date = doc.DocumentNode.SelectNodes("//span[contains(i/@class,'bi-calendar3')]");
197+
var clock = doc.DocumentNode.SelectNodes("//span[contains(i/@class,'bi-clock')]");
188198
if (!string.IsNullOrEmpty(placeAndTime))
189199
{
190200
var placeAndTimeList = placeAndTime.Split('.', 2);
@@ -194,9 +204,36 @@ private async Task<CompetitionGroup> AddRound(string baseUrl, IEnumerable<HtmlNo
194204
if (DateTime.TryParseExact(datetimeString, "dd/MM/yyyy-HH:mm", CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime time))
195205
{
196206
competitionRound.Time = time;
197-
}
207+
}
198208
competitionRound.Location = placeAndTimeList[1];
199209
}
210+
else if(placeAndTimeList.Length == 1)
211+
{
212+
competitionRound.Location = placeAndTimeList[0];
213+
}
214+
}
215+
216+
if (date != null && date.Count == 1 && clock != null && clock.Count == 1)
217+
{
218+
var datetimestring = date[0].InnerText + "-" + clock[0].InnerText;
219+
datetimestring = datetimestring.Replace(" ", string.Empty);
220+
if (DateTime.TryParseExact(datetimestring, "dd/MM/yyyy-HH:mm", CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime timeFromText))
221+
{
222+
competitionRound.Time = timeFromText;
223+
}
224+
}
225+
226+
227+
var maxTime = doc.DocumentNode.SelectNodes("//span[contains(i/@class,'bi-stopwatch')]");
228+
if(maxTime != null && maxTime.Count == 1 && TimeSpan.TryParse(CleanUpString(maxTime[0].InnerText), out TimeSpan maxTimeSpan))
229+
{
230+
competitionRound.MaxTime= maxTimeSpan;
231+
}
232+
233+
var pieceCount = doc.DocumentNode.SelectNodes("//span[contains(i/@class,'bi-puzzle')]");
234+
if (pieceCount != null && pieceCount.Count == 1 && int.TryParse(CleanUpString(pieceCount[0].InnerText), out int pieceCountOut))
235+
{
236+
competitionRound.NumberOfPieces = pieceCountOut;
200237
}
201238
int namefield = 3;
202239
int timefield = 7;
@@ -247,5 +284,12 @@ public bool TryResolveCompetitionUrl(string baseUrl, HtmlNode node, out string c
247284
currentUrl += node.GetAttributeValue("href", "");
248285
return true;
249286
}
287+
288+
private string CleanUpString(string value)
289+
{
290+
value = value.Replace("&nbsp;", string.Empty);
291+
value = value.Replace(" ", string.Empty);
292+
return value;
293+
}
250294
}
251295
}

Arctic.Puzzlers.Stores/Filestore/JsonCompetitionStore.cs

+6
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,11 @@ public Task<List<Competition>> GetByName(string name)
115115
var results = m_competitionList.Where(t=> t.Name != null && t.Name.StartsWith(name, StringComparison.InvariantCultureIgnoreCase)).ToList();
116116
return Task.FromResult(results);
117117
}
118+
119+
public Task<Competition?> Get(Guid guid)
120+
{
121+
var results = m_competitionList.Where(t => t.Name != null && t.CompetitionId == guid).FirstOrDefault();
122+
return Task.FromResult(results);
123+
}
118124
}
119125
}

Arctic.Puzzlers.Stores/ICompetitionStore.cs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public interface ICompetitionStore
99
public Task<List<Competition>> GetAll();
1010

1111
public Task<List<Competition>> GetByName(string name);
12+
public Task<Competition?> Get(Guid guid);
1213
public Task<List<PlayerCompetitionResult>> GetPlayerCompetitionResultByName(string name);
1314
}
1415
}

Arctic.Puzzlers.Stores/MemoryStore/MemoryCompetitionStore.cs

+5
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,10 @@ public Task<List<PlayerCompetitionResult>> GetPlayerCompetitionResultByName(stri
6767
}
6868
return Task.FromResult(results);
6969
}
70+
public Task<Competition?> Get(Guid guid)
71+
{
72+
var results = m_competitionList.Where(t => t.Name != null && t.CompetitionId == guid).FirstOrDefault();
73+
return Task.FromResult(results);
74+
}
7075
}
7176
}

Arctic.Puzzlers.Webapi/Controllers/CompetitionController.cs

+40-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using Microsoft.AspNetCore.Mvc;
44
using System.Globalization;
55
using CsvHelper;
6+
using Arctic.Puzzlers.Objects.Misc;
7+
using Microsoft.VisualBasic;
68

79
namespace Arctic.Puzzlers.Webapi.Controllers
810
{
@@ -43,7 +45,7 @@ public async Task<ContentResult> GetAllCompetitions()
4345
{
4446
foreach (var round in competitionGroups.Rounds) {
4547
html += "<br/> ";
46-
html += $"<a href=\"{baseurl}/api/Competition/CSV?Competitionname={result.Name}&RoundName={competitionGroups.ContestType + "-" + round.RoundName} \">{result.Name + " " + competitionGroups.ContestType + " " + round.RoundName} </a>";
48+
html += $"<a href=\"{baseurl}api/Competition/CSV?competition={result.CompetitionId}&round={round.RoundId} \">{result.Name + " " + competitionGroups.ContestType + " " + round.RoundName} </a>";
4749
}
4850
}
4951

@@ -59,16 +61,49 @@ public async Task<ContentResult> GetAllCompetitions()
5961
[HttpGet]
6062
[Route("CSV")]
6163
[Produces("text/csv")]
62-
public async Task<FileResult> GetCsv(string Competitionname, string RoundName)
64+
public async Task<ActionResult> GetCsv(Guid competition, Guid round)
6365
{
6466

65-
var data = await m_store.GetByName(Competitionname);
66-
using (var memoryStream = new MemoryStream())
67+
var data = await m_store.Get(competition);
68+
if(data == null)
69+
{
70+
return NotFound();
71+
}
72+
var groupData = data.CompetitionGroups.Where(t => t.Rounds.Any(t => t.RoundId == round)).FirstOrDefault();
73+
if (groupData == null)
74+
{
75+
return NotFound();
76+
}
77+
var rounddata = groupData.Rounds.Where(t => t.RoundId == round).FirstOrDefault();
78+
if(rounddata == null)
79+
{
80+
return NotFound();
81+
}
82+
var competitionCSVs = new List<CompetitionCSV>();
83+
84+
foreach(var result in rounddata.Participants)
6785
{
86+
var playersName = string.Empty;
87+
playersName = string.Join(";", result.Participants.Select(t=>t.FullName));
88+
if (!string.IsNullOrEmpty(result.GroupName))
89+
{
90+
playersName = result.GroupName + "(" + playersName + ")";
91+
}
92+
var competitionCSV = new CompetitionCSV()
93+
{
94+
Date = rounddata.Time,
95+
EventName = data.Name + " " + groupData.ContestType + " " + rounddata.RoundName,
96+
PlayersName = playersName
97+
};
98+
99+
competitionCSVs.Add(competitionCSV);
100+
}
101+
using (var memoryStream = new MemoryStream())
102+
{
68103
using (var streamWriter = new StreamWriter(memoryStream))
69104
using (var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture))
70105
{
71-
csvWriter.WriteRecords(data);
106+
csvWriter.WriteRecords(competitionCSVs);
72107
}
73108

74109
return File(memoryStream.ToArray(), "text/csv", $"Export-{DateTime.Now.ToString("s")}.csv");

0 commit comments

Comments
 (0)