Vereinsmeisterschaften  22aa7800eae54b428d40e835886cefe1fdefdfdf
This is a software that can be used to manage the internal competition of the swimming club Illertissen called "Vereinsmeisterschaften".
Loading...
Searching...
No Matches
CompetitionService.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using MathNet.Numerics;
3using System.Collections.ObjectModel;
4using System.ComponentModel;
9
11{
15 public class CompetitionService : ObservableObject, ICompetitionService
16 {
21
25 public event EventHandler OnFileFinished;
26
27 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28
32 public string PersistentPath { get; set; }
33
34 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
35
39 private ObservableCollection<Competition> _competitionList { get; set; }
40
44 private List<Competition> _competitionListOnLoad { get; set; }
45
46 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
47
48 private IFileService _fileService;
49 private IPersonService _personService;
50 private IWorkspaceService _workspaceService;
51 private ICompetitionDistanceRuleService _competitionDistanceRuleService;
52
59 public CompetitionService(IFileService fileService, IPersonService personService, ICompetitionDistanceRuleService competitionDistanceRuleService)
60 {
61 _competitionList = new ObservableCollection<Competition>();
62 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
63 _fileService = fileService;
64 _personService = personService;
65 _personService.SetCompetitionServiceObj(this); // Dependency Injection can't be used in the constructor because of circular dependency
66 _competitionDistanceRuleService = competitionDistanceRuleService;
67 _competitionDistanceRuleService.SetCompetitionServiceObj(this); // Dependency Injection can't be used in the constructor because of circular dependency
68 }
69
75 public void SetWorkspaceServiceObj(IWorkspaceService workspaceService)
76 {
77 _workspaceService = workspaceService;
78
79 if(_workspaceService != null)
80 {
81 _workspaceService.PropertyChanged += (sender, e) =>
82 {
83 if (e.PropertyName == nameof(IWorkspaceService.Settings))
84 {
86 }
87 };
88 }
89 }
90
91 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
92
101 public async Task<bool> Load(string path, CancellationToken cancellationToken)
102 {
103 bool importingResult = false;
104 Exception exception = null;
105 await Task.Run(() =>
106 {
107 try
108 {
109 if (!File.Exists(path))
110 {
111 OnFileProgress?.Invoke(this, 0);
112 ClearAll();
113 OnFileProgress?.Invoke(this, 100);
114 }
115 else
116 {
117 List<Competition> list = _fileService.LoadFromCsv<Competition>(path, cancellationToken, Competition.SetPropertyFromString, OnFileProgress, (header) =>
118 {
119 return PropertyNameLocalizedStringHelper.FindProperty(typeof(Competition), header);
120 });
121 _competitionList = new ObservableCollection<Competition>();
122 foreach (Competition competition in list)
123 {
124 AddCompetition(competition);
125 }
126 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
127 }
128
129 _competitionListOnLoad = _competitionList.ToList().ConvertAll(c => new Competition(c));
130
131 PersistentPath = path;
132 importingResult = true;
133 }
134 catch (OperationCanceledException)
135 {
136 importingResult = false;
137 }
138 catch (Exception ex)
139 {
140 exception = ex;
141 }
142 });
143 OnFileFinished?.Invoke(this, null);
144 if (exception != null) { throw exception; }
145 return importingResult;
146 }
147
148 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
149
156 public async Task<bool> Save(CancellationToken cancellationToken, string path = "")
157 {
158 if (string.IsNullOrEmpty(path)) { path = PersistentPath; }
159
160 bool saveResult = false;
161 Exception exception = null;
162 await Task.Run(() =>
163 {
164 try
165 {
166 _fileService.SaveToCsv(path, _competitionList.ToList(), cancellationToken, OnFileProgress, (data, parentObject, currentProperty) =>
167 {
168 if (data is Enum dataEnum)
169 {
170 return EnumCoreLocalizedStringHelper.Convert(dataEnum);
171 }
172 else if (data is bool dataBool)
173 {
174 return dataBool ? "X" : "";
175 }
176 else
177 {
178 return data.ToString();
179 }
180 },
181 (header, type) =>
182 {
183 return PropertyNameLocalizedStringHelper.Convert(typeof(Competition), header);
184 });
185
186 _competitionListOnLoad = _competitionList.ToList().ConvertAll(c => new Competition(c));
187 saveResult = true;
188 }
189 catch (OperationCanceledException)
190 {
191 saveResult = false;
192 }
193 catch (Exception ex)
194 {
195 exception = ex;
196 }
197 });
198 OnFileFinished?.Invoke(this, null);
199 if (exception != null) { throw exception; }
200 return saveResult;
201 }
202
203 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
204
209 public ObservableCollection<Competition> GetCompetitions() => _competitionList;
210
214 public void ClearAll()
215 {
216 if (_competitionList == null)
217 {
218 _competitionList = new ObservableCollection<Competition>();
219 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
220 }
221 foreach (Competition competition in _competitionList)
222 {
223 competition.PropertyChanged -= Competition_PropertyChanged;
224 }
225 if (_competitionList.Count > 0) { _competitionList.Clear(); }
226
227 OnPropertyChanged(nameof(CompetitionCount));
229 OnPropertyChanged(nameof(HasUnsavedChanges));
230 }
231
236 public void ResetToLoadedState()
237 {
238 if (_competitionListOnLoad == null) { return; }
239 _competitionList.CollectionChanged -= _competitionList_CollectionChanged;
240 ClearAll();
241 foreach (Competition competition in _competitionListOnLoad)
242 {
243 AddCompetition(new Competition(competition));
244 }
245 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
247 }
248
253 public void AddCompetition(Competition competition)
254 {
255 if (_competitionList == null)
256 {
257 _competitionList = new ObservableCollection<Competition>();
258 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
259 }
260 _competitionList.Add(competition);
261
262 competition.PropertyChanged += Competition_PropertyChanged;
263
265
266 OnPropertyChanged(nameof(CompetitionCount));
268 OnPropertyChanged(nameof(HasUnsavedChanges));
269 }
270
275 public void RemoveCompetition(Competition competition)
276 {
277 competition.PropertyChanged -= Competition_PropertyChanged;
278 _competitionList?.Remove(competition);
279
281
282 OnPropertyChanged(nameof(CompetitionCount));
284 OnPropertyChanged(nameof(HasUnsavedChanges));
285 }
286
287 private void Competition_PropertyChanged(object sender, PropertyChangedEventArgs e)
288 {
289 switch (e.PropertyName)
290 {
291 case nameof(Competition.Gender):
292 case nameof(Competition.SwimmingStyle):
293 case nameof(Competition.Age):
294 UpdateHasDuplicatesForCompetitions();
295 UpdateAllCompetitionsForPerson();
296 UpdateCompetitionDistanceFromDistanceRules(sender as Competition, false);
297 break;
298 default: break;
299 }
300
301 OnPropertyChanged(nameof(HasUnsavedChanges));
302 }
303
304 private void _competitionList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
305 {
306 UpdateHasDuplicatesForCompetitions();
307 UpdateAllCompetitionsForPerson();
308 OnPropertyChanged(nameof(HasUnsavedChanges));
309 }
310
315 public int CompetitionCount => _competitionList?.Count ?? 0;
316
321 {
323 IEnumerable<IGrouping<Competition, Competition>> duplicateGroups = _competitionList.GroupBy(c => c, basicCompetitionComparer).Where(g => g.Count() > 1);
324
325 // Reset all HasDuplicates flags
326 foreach (Competition c in _competitionList) { c.HasDuplicates = false; }
327
328 // Set HasDuplicates flag for all competitions that are in a duplicate group
329 foreach (IGrouping<Competition, Competition> duplicateGroup in duplicateGroups)
330 {
331 foreach (Competition c in duplicateGroup) { c.HasDuplicates = true; }
332 }
333 }
334
335 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
336
341 public bool HasUnsavedChanges => (_competitionList != null && _competitionListOnLoad != null) ? !_competitionList.SequenceEqual(_competitionListOnLoad) : false;
342
343 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
344
346 public Competition GetCompetitionForPerson(Person person, SwimmingStyles swimmingStyle, out bool isUsingMaxAgeCompetition, out bool isUsingExactAgeCompetition)
347 {
348 isUsingMaxAgeCompetition = false;
349 isUsingExactAgeCompetition = true;
350
351 // Get all competitions that match the gender and swimming style
352 List<Competition> competitions = _competitionList.Where(c => c.Gender == person.Gender && c.SwimmingStyle == swimmingStyle)
353 .OrderBy(c => c.Age).ToList();
354 if (competitions.Count == 0) { return null; }
355
356 CompetitionSearchModes searchMode = _workspaceService?.Settings?.GetSettingValue<CompetitionSearchModes>(WorkspaceSettings.GROUP_GENERAL, WorkspaceSettings.SETTING_GENERAL_COMPETITIONSEARCHMODE) ?? CompetitionSearchModes.ExactOrNextLowerOnlyMaxAge;
357 ushort competitionYear = _workspaceService?.Settings?.GetSettingValue<ushort>(WorkspaceSettings.GROUP_GENERAL, WorkspaceSettings.SETTING_GENERAL_COMPETITIONYEAR) ?? 0;
358 int personAge = competitionYear - person.BirthYear;
359 byte maxAge = competitions.Max(c => c.Age);
360 byte minAge = competitions.Min(c => c.Age);
361
362 // Special case WaterFlea
363 if (swimmingStyle == SwimmingStyles.WaterFlea)
364 {
365 return competitions.Where(c => personAge <= c.Age).FirstOrDefault();
366 }
367
368 Competition foundCompetition = null;
369 switch (searchMode)
370 {
371 case CompetitionSearchModes.OnlyExactAge:
372 foundCompetition = competitions.FirstOrDefault(c => c.Age == personAge);
373 break;
374 case CompetitionSearchModes.ExactOrNextLowerAge:
375 foundCompetition = competitions.LastOrDefault(c => c.Age <= personAge);
376 break;
377 case CompetitionSearchModes.ExactOrNextHigherAge:
378 foundCompetition = competitions.FirstOrDefault(c => c.Age >= personAge);
379 break;
380 case CompetitionSearchModes.ExactOrNextLowerOnlyMaxAge:
381 foundCompetition = competitions.FirstOrDefault(c => c.Age == personAge);
382 if (foundCompetition == null && personAge > maxAge) // if no exact match was found and the person age is greater than the max age, use the competition with the max age
383 {
384 foundCompetition = competitions.FirstOrDefault(c => c.Age == maxAge);
385 }
386 break;
387 case CompetitionSearchModes.ExactOrNearestPreferLower:
388 foundCompetition = competitions.FirstOrDefault(c => c.Age == personAge);
389 if (foundCompetition == null)
390 {
391 // Calculate age distance and order by minimal distance. Prefer lower age on equal distance.
392 foundCompetition = competitions.OrderBy(c => Math.Abs(c.Age - personAge)).ThenBy(c => c.Age).FirstOrDefault();
393 }
394 break;
395 case CompetitionSearchModes.ExactOrNearestPreferHigher:
396 foundCompetition = competitions.FirstOrDefault(c => c.Age == personAge);
397 if (foundCompetition == null)
398 {
399 // Calculate age distance and order by minimal distance. Prefer higher age on equal distance.
400 foundCompetition = competitions.OrderBy(c => Math.Abs(c.Age - personAge)).ThenByDescending(c => c.Age).FirstOrDefault();
401 }
402 break;
403 }
404
405 if (foundCompetition != null && foundCompetition.Age == maxAge) { isUsingMaxAgeCompetition = true; }
406 if (foundCompetition != null && foundCompetition.Age != personAge) { isUsingExactAgeCompetition = false; }
407
408 return foundCompetition;
409 }
410
411 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
412
415 {
416 // Update the available competitions for the person
417 List<SwimmingStyles> _availableSwimmingStyles = Enum.GetValues(typeof(SwimmingStyles)).Cast<SwimmingStyles>().Where(s => s != SwimmingStyles.Unknown).ToList();
418 Dictionary<SwimmingStyles, Competition> availableCompetitions = new Dictionary<SwimmingStyles, Competition>();
419 Dictionary<SwimmingStyles, bool> isUsingMaxAgeCompetitionDict = new Dictionary<SwimmingStyles, bool>();
420 Dictionary<SwimmingStyles, bool> isUsingExactAgeCompetitionDict = new Dictionary<SwimmingStyles, bool>();
421 foreach (SwimmingStyles swimmingStyle in _availableSwimmingStyles)
422 {
423 bool isUsingMaxAgeCompetition, isUsingExactAgeCompetition;
424 Competition competition = GetCompetitionForPerson(person, swimmingStyle, out isUsingMaxAgeCompetition, out isUsingExactAgeCompetition);
425
426 availableCompetitions[swimmingStyle] = competition;
427 isUsingMaxAgeCompetitionDict[swimmingStyle] = isUsingMaxAgeCompetition;
428 isUsingExactAgeCompetitionDict[swimmingStyle] = isUsingExactAgeCompetition;
429 }
430 person.AvailableCompetitions = availableCompetitions;
431 person.IsUsingMaxAgeCompetitionDict = isUsingMaxAgeCompetitionDict;
432 person.IsUsingExactAgeCompetitionDict = isUsingExactAgeCompetitionDict;
433
434 // Update the competitions for the person starts
435 foreach (PersonStart personStart in _personService.GetAllPersonStarts(PersonStartFilters.Person, person))
436 {
437 personStart.CompetitionObj = person.AvailableCompetitions[personStart.Style];
438 personStart.IsUsingMaxAgeCompetition = person.IsUsingMaxAgeCompetitionDict[personStart.Style];
439 personStart.IsUsingExactAgeCompetition = person.IsUsingExactAgeCompetitionDict[personStart.Style];
440 }
441 }
442
443 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
444
447 {
448 foreach (Person person in _personService.GetPersons())
449 {
451 }
452 }
453
454 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
455
457 public void UpdateCompetitionDistanceFromDistanceRules(Competition competition, bool keepRudolphTableFlags)
458 {
459 if (competition == null) { return; }
460 bool isTimeFromRudolphTable = competition.IsTimeFromRudolphTable;
461 bool isTimeInterpolatedFromRudolphTable = competition.IsTimeInterpolatedFromRudolphTable;
462 bool isOpenAgeTimeFromRudolphTable = competition.IsOpenAgeTimeFromRudolphTable;
463 competition.Distance = _competitionDistanceRuleService.GetCompetitionDistanceFromRules(competition.Age, competition.SwimmingStyle);
464 if(keepRudolphTableFlags)
465 {
466 competition.IsTimeFromRudolphTable = isTimeFromRudolphTable;
467 competition.IsTimeInterpolatedFromRudolphTable = isTimeInterpolatedFromRudolphTable;
468 competition.IsOpenAgeTimeFromRudolphTable = isOpenAgeTimeFromRudolphTable;
469 }
470 }
471
472 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
473
475 public void UpdateAllCompetitionDistancesFromDistanceRules(bool keepRudolphTableFlags)
476 {
477 foreach (Competition competition in _competitionList)
478 {
479 UpdateCompetitionDistanceFromDistanceRules(competition, keepRudolphTableFlags);
480 }
481 }
482
483 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
484
486 public void CreateCompetitionsFromRudolphTable(string rudolphTableCsvFile, byte rudolphScore)
487 {
488 RudolphTable rudolphTable = new RudolphTable(rudolphTableCsvFile);
489
490 // Find the maximum ages for each swimming style in the rudolph table
491 Dictionary<SwimmingStyles, byte> maxAgesBySwimmingStyles = rudolphTable.Entries
492 .GroupBy(e => e.SwimmingStyle)
493 .ToDictionary(g => g.Key, g => g.Max(e => e.Age));
494
495 List<Competition> competitions = rudolphTable.Entries
496 .Where(e => e.RudolphScore == rudolphScore &&
497 e.Distance == _competitionDistanceRuleService.GetCompetitionDistanceFromRules(e.IsOpenAge ? maxAgesBySwimmingStyles[e.SwimmingStyle] : e.Age, e.SwimmingStyle))
498 .Select(e => new Competition
499 {
500 Gender = e.Gender,
501 Age = e.IsOpenAge ? (byte)(maxAgesBySwimmingStyles[e.SwimmingStyle] + 1) : e.Age, // for the open age, use the maximum age + 1
502 SwimmingStyle = e.SwimmingStyle,
503 Distance = e.Distance,
504 BestTime = e.Time,
505 IsTimeFromRudolphTable = true,
506 IsTimeInterpolatedFromRudolphTable = false,
507 IsOpenAgeTimeFromRudolphTable = e.IsOpenAge
508 }
509 ).ToList();
510
511 // All competitions should be deleted here that are later added from the competitions list.
512 // All competitions that are not in this list are kept.
514 List<Competition> competitionsToDelete = _competitionList.Intersect(competitions, basicEqualityComparer).ToList();
515 foreach (Competition competition in competitionsToDelete)
516 {
517 competition.PropertyChanged -= Competition_PropertyChanged;
518 _competitionList.Remove(competition);
519 }
520 OnPropertyChanged(nameof(CompetitionCount));
522 // Reset the IsTimeInterpolatedFromRudolphTable and IsOpenAgeTimeFromRudolphTable flag for all remaining competitions
523 foreach (Competition competition in _competitionList)
524 {
525 competition.IsTimeInterpolatedFromRudolphTable = false;
526 competition.IsOpenAgeTimeFromRudolphTable = false;
527 }
528 OnPropertyChanged(nameof(HasUnsavedChanges));
529
530 // Add all new competitions
531 int id = 1;
532 foreach (Competition competition in competitions)
533 {
534 List<int> currentIds = _competitionList.Select(c => c.Id).ToList();
535 while (currentIds.Contains(id)) { id++; }
536
537 competition.Id = id;
538 AddCompetition(competition);
539 }
540 }
541
542 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
543
545 public void UpdateAllCompetitionTimesFromRudolphTable(string rudolphTableCsvFile, byte rudolphScore)
546 {
547 RudolphTable rudolphTable = new RudolphTable(rudolphTableCsvFile);
548
549 foreach (Competition competition in _competitionList)
550 {
551 RudolphTableEntry foundRudolphTableEntry = rudolphTable.GetEntryByParameters(competition.Gender, competition.Age, competition.SwimmingStyle, competition.Distance, rudolphScore);
552 if (foundRudolphTableEntry != null)
553 {
554 competition.BestTime = foundRudolphTableEntry.Time;
555 competition.IsTimeFromRudolphTable = true;
556 competition.IsTimeInterpolatedFromRudolphTable = false;
557 competition.IsOpenAgeTimeFromRudolphTable = foundRudolphTableEntry.IsOpenAge;
558 }
559 else
560 {
561 competition.IsTimeFromRudolphTable = false;
562 competition.IsTimeInterpolatedFromRudolphTable = false;
563 competition.IsOpenAgeTimeFromRudolphTable = false;
564 }
565 }
566 interpolateMissingCompetitionTimesFromRudolphTable(rudolphTable, rudolphScore);
567 }
568
569 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
570
577 private void interpolateMissingCompetitionTimesFromRudolphTable(RudolphTable rudolphTable, byte rudolphScore)
578 {
579 // Create groups for specific combinations of SwimmingStyle, Gender and Distance
580 var groups = _competitionList.GroupBy(c => (c.SwimmingStyle, c.Gender, c.Distance));
581 List<string> groupErrorStrings = new List<string>();
582 foreach(var group in groups)
583 {
584 SwimmingStyles groupSwimmingStyle = group.Key.SwimmingStyle;
585 Genders groupGender = group.Key.Gender;
586 ushort groupDistance = group.Key.Distance;
587
588 List<Competition> competitionsRudolphTable = group.Where(c => c.IsTimeFromRudolphTable).ToList();
589 List<Competition> competitionsMissingTimes = group.Where(c => !c.IsTimeFromRudolphTable).ToList();
590
591 // Continue if the swimming style is WaterFlea. This is not supported by the Rudolph Table and will always produce errors.
592 if(groupSwimmingStyle == SwimmingStyles.WaterFlea)
593 {
594 continue;
595 }
596
597 // Continue if all times are from the rudolph table (nothing to do)
598 if (competitionsMissingTimes.Count == 0)
599 {
600 continue;
601 }
602
603 // Create error entry and continue when there are too less times from the rudolph table (not able to interpolate)
604 if (competitionsRudolphTable.Count < 3)
605 {
606 groupErrorStrings.Add(string.Format(Properties.Resources.InterpolateMissingCompetitionTimesFromRudolphTableErrorFormatString, groupDistance, EnumCoreLocalizedStringHelper.Convert(groupSwimmingStyle), EnumCoreLocalizedStringHelper.Convert(groupGender)));
607 continue;
608 }
609
610 double[] dataX = competitionsRudolphTable.Select(c => (double)c.Age).ToArray();
611 double[] dataY = competitionsRudolphTable.Select(c => c.BestTime.TotalMilliseconds).ToArray();
612
613 // Rudolph data seems to use a polynom of 3th grade: https://schwimmlexikon.de/rudolph-tabelle/
614 Polynomial poly = Polynomial.Fit(dataX, dataY, 3);
615 foreach (Competition missingTimeCompetition in competitionsMissingTimes)
616 {
617 double interpolatedTimeMilliseconds = poly.Evaluate(missingTimeCompetition.Age);
618 missingTimeCompetition.BestTime = new TimeSpan(0, 0, 0, 0, (int)interpolatedTimeMilliseconds);
619 missingTimeCompetition.IsTimeInterpolatedFromRudolphTable = true;
620 }
621 }
622 if(groupErrorStrings.Count > 0)
623 {
624 string errorString = string.Join(Environment.NewLine, groupErrorStrings);
625 throw new Exception(errorString);
626 }
627 }
628 }
629}
Comparer that only uses the most basic properties of a Competition to determine equality:
Class describing a competition.
Genders Gender
Gender for this competition.
bool IsOpenAgeTimeFromRudolphTable
True, when the BestTime was the open age value taken from the RudolphTable.
ushort Distance
Distance in meters for this competition (e.g.
SwimmingStyles SwimmingStyle
Swimming style for this competition.
TimeSpan BestTime
Time for this competition to reach the maximum points.
byte Age
Age for the person that is assigned for this competition.
bool IsTimeInterpolatedFromRudolphTable
True, when the BestTime was an interpolated value based on the competitions with IsTimeFromRudolphTab...
static void SetPropertyFromString(Competition dataObj, string propertyName, string value)
Set the requested property in the Competition object by parsing the given string value.
bool IsTimeFromRudolphTable
True, when the BestTime was the value taken from the RudolphTable.
Class describing a person.
Definition Person.cs:12
UInt16 BirthYear
Birth year of the person.
Definition Person.cs:99
Genders Gender
Gender of the person.
Definition Person.cs:88
Class describing a start of a person.
Definition PersonStart.cs:9
Class describing a single entry in the rudolph table (one single cell containing one time).
Class that offers methods that can be used to parse a rudolph table to objects.
Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
static string InterpolateMissingCompetitionTimesFromRudolphTableErrorFormatString
Sucht eine lokalisierte Zeichenfolge, die Not able to interpolate for {0}m {1} {2}...
EventHandler OnFileFinished
Event that is raised when the file operation is finished.
void ResetToLoadedState()
Reset the list of Competitions to the state when the Load(string, CancellationToken) method was calle...
void interpolateMissingCompetitionTimesFromRudolphTable(RudolphTable rudolphTable, byte rudolphScore)
For each swimming style, gender and distance take all competitions with times from the rudolph table ...
ObservableCollection< Competition > GetCompetitions()
Return all available Competitions.
void CreateCompetitionsFromRudolphTable(string rudolphTableCsvFile, byte rudolphScore)
Create the Competition objects from the given rudolph table.The lines from the given rudolph score ar...
void SetWorkspaceServiceObj(IWorkspaceService workspaceService)
Save the reference to the IWorkspaceService object.
List< Competition > _competitionListOnLoad
List with all competitions at the time the Load(string, CancellationToken) method was called.
ObservableCollection< Competition > _competitionList
List with all competitions.
bool HasUnsavedChanges
Check if the list of Competition has not saved changed.
CompetitionService(IFileService fileService, IPersonService personService, ICompetitionDistanceRuleService competitionDistanceRuleService)
Constructor.
async Task< bool > Save(CancellationToken cancellationToken, string path="")
Save the list of Competitions to a file.
void UpdateCompetitionDistanceFromDistanceRules(Competition competition, bool keepRudolphTableFlags)
Update the Competition.Distance from the matching CompetitionDistanceRule
void UpdateAllCompetitionsForPerson(Person person)
Update all PersonStart objects and the Person.AvailableCompetitions for the given Person with the cor...
void AddCompetition(Competition competition)
Add a new Competition to the list of Competitions.
void UpdateAllCompetitionsForPerson()
Update all PersonStart and the Person.AvailableCompetitions objects with the corresponding Competitio...
void UpdateAllCompetitionTimesFromRudolphTable(string rudolphTableCsvFile, byte rudolphScore)
Update all Competition.BestTime properties from the given rudolph table.
Competition GetCompetitionForPerson(Person person, SwimmingStyles swimmingStyle, out bool isUsingMaxAgeCompetition, out bool isUsingExactAgeCompetition)
Return the competition that matches the person and swimming style.Found Competition or null
void UpdateHasDuplicatesForCompetitions()
Find all duplicate Competition objects and update the Competition.HasDuplicates flags.
void UpdateAllCompetitionDistancesFromDistanceRules(bool keepRudolphTableFlags)
Update the Competition.Distance from the matching CompetitionDistanceRule for all Competition objects...
async Task< bool > Load(string path, CancellationToken cancellationToken)
Load a list of Competitions to the _competitionList.
void RemoveCompetition(Competition competition)
Remove the given Competition from the list of Competitions.
ProgressDelegate OnFileProgress
Event that is raised when the file operation progress changes.
Interface for a service used to get and store a list of CompetitionDistanceRule objects.
void SetCompetitionServiceObj(ICompetitionService competitionService)
Save the reference to the ICompetitionService object.
Interface for a service used to get and store a list of objects.
Interface for a service that handles file operations.
Interface for a service used to get and store a list of Person objects.
void SetCompetitionServiceObj(ICompetitionService competitionService)
Save the reference to the ICompetitionService object.
Interface for a service used to manage a workspace.
WorkspaceSettings Settings
Settings for the current workspace.
delegate void ProgressDelegate(object sender, float progress, string currentStep="")
Delegate void for progress changes.
CompetitionSearchModes
Different types of modes to search for the matching competition.
SwimmingStyles
Available swimming styles.
Genders
Available genders for a person.
Definition Genders.cs:7
PersonStartFilters
Available filters for PersonStart objects.