|
18 | 18 | <div class="tool-subtitle">Find and visualize move deviations across PGN games</div> |
19 | 19 |
|
20 | 20 | <div class="tool-controls"> |
21 | | - <button class="tool-upload-btn" @onclick="BrowsePgnFile">Load PGN file</button> |
| 21 | + <button class="tool-upload-btn" @onclick="BrowsePgnFile" disabled="@isProcessing">Load PGN file</button> |
22 | 22 | <div class="tool-input-group" style="margin-bottom:0; min-width:220px;"> |
23 | 23 | <label>Reference player name</label> |
24 | 24 | <input type="text" class="tool-input" style="width:100%" @bind="refPlayer" /> |
|
39 | 39 |
|
40 | 40 | <div class="file-name">@fileName</div> |
41 | 41 |
|
| 42 | + @if (isProcessing) |
| 43 | + { |
| 44 | + <div class="file-name" style="display:flex; align-items:center; gap:10px;"> |
| 45 | + <MudProgressCircular Indeterminate="true" Size="Size.Small" /> |
| 46 | + <span>@processingStatus</span> |
| 47 | + </div> |
| 48 | + } |
| 49 | + |
42 | 50 | @if (!string.IsNullOrEmpty(resultDescription1)) |
43 | 51 | { |
44 | 52 | <h2>@(isFindMoveAllDifferencesEnabled ? "Critical Move Differences Between Engines" : "Move Deviations By Same Engine")</h2> |
|
257 | 265 | return deviatorColor == "w" ? res : -res; |
258 | 266 | } |
259 | 267 |
|
| 268 | + private bool isProcessing; |
| 269 | + private string processingStatus = ""; |
| 270 | + |
260 | 271 | private async Task BrowsePgnFile() |
261 | 272 | { |
| 273 | + if (isProcessing) return; |
262 | 274 | var initialDir = SettingsService.Settings.PgnOutputFolder; |
263 | 275 | var parameters = new DialogParameters<WebGUI.Components.Layout.ExperimentalLayout.FileBrowserDialog> |
264 | 276 | { |
|
280 | 292 | resultDescription1 = ""; |
281 | 293 | resultDescription2 = ""; |
282 | 294 |
|
283 | | - List<ChessLibrary.PGNTypes.PgnGame> games; |
| 295 | + isProcessing = true; |
| 296 | + processingStatus = "Parsing PGN…"; |
| 297 | + StateHasChanged(); |
284 | 298 | try |
285 | 299 | { |
286 | | - games = FullPGNParser.parsePgnFile(filePath).ToList(); |
287 | | - } |
288 | | - catch (Exception ex) |
289 | | - { |
| 300 | + List<ChessLibrary.PGNTypes.PgnGame> games; |
| 301 | + try |
| 302 | + { |
| 303 | + games = await Task.Run(() => FullPGNParser.parsePgnFile(filePath).ToList()); |
| 304 | + } |
| 305 | + catch (Exception ex) |
| 306 | + { |
290 | 307 | logger.LogError(ex, "Failed to parse PGN file {File}", pgnName); |
291 | 308 | return; |
| 309 | + } |
| 310 | + if (games.Count == 0) |
| 311 | + { |
| 312 | + logger.LogWarning("No games found in {File} — not a PGN file?", pgnName); |
| 313 | + return; |
| 314 | + } |
| 315 | + |
| 316 | + processingStatus = $"Analyzing move deviations in {games.Count:N0} games…"; |
| 317 | + StateHasChanged(); |
| 318 | + var comparList = ConvertCsvStringToList(comparePlayers); |
| 319 | + // Deviation analysis compares games pairwise — quadratic-ish work that must not |
| 320 | + // run on the dispatcher. Materialize the lazy sequences once inside the task |
| 321 | + // instead of re-running the whole analysis on every LINQ enumeration below. |
| 322 | + var (deviations, critical, desc1, desc2) = await Task.Run(() => |
| 323 | + { |
| 324 | + var devs = |
| 325 | + (isFindMoveAllDifferencesEnabled ? |
| 326 | + DeviationAnalysis.findMoveDifferencesInPGN(games, refPlayer, comparList) : |
| 327 | + DeviationAnalysis.findAllDeviationsForAllPlayers(games)) |
| 328 | + .ToList(); |
| 329 | + var crit = devs.Where(e => e.Result != e.DevRes).ToList(); |
| 330 | + |
| 331 | + var sb = new StringBuilder(); |
| 332 | + foreach (var deviator in crit.Select(e => e.PlayerToDeviate).Distinct()) |
| 333 | + { |
| 334 | + var playerDeviations = crit.Where(e => e.PlayerToDeviate == deviator).ToList(); |
| 335 | + var playerScore = playerDeviations.Sum(e => GetScore(e)); |
| 336 | + sb.AppendLine($"Player: {deviator} did deviate {playerDeviations.Count} times and resulted in a calculated net score of: {playerScore}"); |
| 337 | + } |
| 338 | + var sb1 = new StringBuilder(); |
| 339 | + foreach (var deviator in devs.Select(e => e.PlayerToDeviate).Distinct()) |
| 340 | + { |
| 341 | + var playerDeviations = devs.Where(e => e.PlayerToDeviate == deviator).ToList(); |
| 342 | + var playerScore = playerDeviations.Sum(e => GetScore(e)); |
| 343 | + sb1.AppendLine($"Player: {deviator} did deviate {playerDeviations.Count} times and resulted in a calculated net score of: {playerScore}"); |
| 344 | + } |
| 345 | + return (devs, crit, sb.ToString(), sb1.ToString()); |
| 346 | + }); |
| 347 | + |
| 348 | + deviationListAll.AddRange(deviations); |
| 349 | + deviationListCritical.AddRange(critical); |
| 350 | + resultDescription1 = desc1; |
| 351 | + resultDescription2 = desc2; |
| 352 | + logger.LogInformation(resultDescription1); |
| 353 | + logger.LogInformation(resultDescription2); |
| 354 | + logger.LogInformation("Done with calculation"); |
| 355 | + currentPage = 0; |
292 | 356 | } |
293 | | - var comparList = ConvertCsvStringToList(comparePlayers); |
294 | | - var deviations = |
295 | | - isFindMoveAllDifferencesEnabled ? |
296 | | - DeviationAnalysis.findMoveDifferencesInPGN(games, refPlayer, comparList) : |
297 | | - DeviationAnalysis.findAllDeviationsForAllPlayers(games); |
298 | | - |
299 | | - deviationListAll.AddRange(deviations); |
300 | | - var critical = deviations.Where(e => e.Result != e.DevRes); |
301 | | - deviationListCritical.AddRange(critical); |
302 | | - |
303 | | - var distinctCritical = critical.Select(e => e.PlayerToDeviate).Distinct(); |
304 | | - var distinctAll = deviations.Select(e => e.PlayerToDeviate).Distinct(); |
305 | | - var sb = new StringBuilder(); |
306 | | - foreach (var deviator in distinctCritical) |
| 357 | + catch (Exception ex) |
307 | 358 | { |
308 | | - var playerDeviations = critical.Where(e => e.PlayerToDeviate == deviator); |
309 | | - var playerScore = playerDeviations.Sum(e => GetScore(e)); |
310 | | - sb.AppendLine($"Player: {deviator} did deviate {playerDeviations.Count()} times and resulted in a calculated net score of: {playerScore}"); |
| 359 | + logger.LogError(ex, "Deviation analysis failed for {File}", pgnName); |
311 | 360 | } |
312 | | - resultDescription1 = sb.ToString(); |
313 | | - var sb1 = new StringBuilder(); |
314 | | - foreach (var deviator in distinctAll) |
| 361 | + finally |
315 | 362 | { |
316 | | - var playerDeviations = deviations.Where(e => e.PlayerToDeviate == deviator); |
317 | | - var playerScore = playerDeviations.Sum(e => GetScore(e)); |
318 | | - sb1.AppendLine($"Player: {deviator} did deviate {playerDeviations.Count()} times and resulted in a calculated net score of: {playerScore}"); |
| 363 | + isProcessing = false; |
| 364 | + processingStatus = ""; |
| 365 | + StateHasChanged(); |
319 | 366 | } |
320 | | - resultDescription2 = sb1.ToString(); |
321 | | - logger.LogInformation(resultDescription1); |
322 | | - logger.LogInformation(resultDescription2); |
323 | | - logger.LogInformation("Done with calculation"); |
324 | | - currentPage = 0; |
325 | 367 | } |
326 | 368 |
|
327 | 369 |
|
|
0 commit comments