1using CommunityToolkit.Mvvm.ComponentModel;
3using System.Collections.ObjectModel;
4using System.ComponentModel;
62 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
63 _fileService = fileService;
64 _personService = personService;
66 _competitionDistanceRuleService = competitionDistanceRuleService;
77 _workspaceService = workspaceService;
79 if(_workspaceService !=
null)
81 _workspaceService.PropertyChanged += (sender, e) =>
101 public async Task<bool>
Load(
string path, CancellationToken cancellationToken)
103 bool importingResult =
false;
104 Exception exception =
null;
109 if (!File.Exists(path))
119 return PropertyNameLocalizedStringHelper.FindProperty(typeof(
Competition), header);
126 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
132 importingResult =
true;
134 catch (OperationCanceledException)
136 importingResult =
false;
144 if (exception !=
null) {
throw exception; }
145 return importingResult;
156 public async Task<bool>
Save(CancellationToken cancellationToken,
string path =
"")
160 bool saveResult =
false;
161 Exception exception =
null;
168 if (data is Enum dataEnum)
170 return EnumCoreLocalizedStringHelper.Convert(dataEnum);
172 else if (data is
bool dataBool)
174 return dataBool ?
"X" :
"";
178 return data.ToString();
183 return PropertyNameLocalizedStringHelper.Convert(typeof(
Competition), header);
189 catch (OperationCanceledException)
199 if (exception !=
null) {
throw exception; }
219 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
223 competition.PropertyChanged -= Competition_PropertyChanged;
239 _competitionList.CollectionChanged -= _competitionList_CollectionChanged;
245 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
258 _competitionList.CollectionChanged += _competitionList_CollectionChanged;
262 competition.PropertyChanged += Competition_PropertyChanged;
277 competition.PropertyChanged -= Competition_PropertyChanged;
287 private void Competition_PropertyChanged(
object sender, PropertyChangedEventArgs e)
289 switch (e.PropertyName)
294 UpdateHasDuplicatesForCompetitions();
295 UpdateAllCompetitionsForPerson();
296 UpdateCompetitionDistanceFromDistanceRules(sender as
Competition,
false);
301 OnPropertyChanged(nameof(HasUnsavedChanges));
304 private void _competitionList_CollectionChanged(
object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
306 UpdateHasDuplicatesForCompetitions();
307 UpdateAllCompetitionsForPerson();
308 OnPropertyChanged(nameof(HasUnsavedChanges));
323 IEnumerable<IGrouping<Competition, Competition>> duplicateGroups =
_competitionList.GroupBy(c => c, basicCompetitionComparer).Where(g => g.Count() > 1);
329 foreach (IGrouping<Competition, Competition> duplicateGroup
in duplicateGroups)
331 foreach (
Competition c
in duplicateGroup) { c.HasDuplicates =
true; }
348 isUsingMaxAgeCompetition =
false;
349 isUsingExactAgeCompetition =
true;
353 .OrderBy(c => c.
Age).ToList();
354 if (competitions.Count == 0) {
return null; }
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);
365 return competitions.Where(c => personAge <= c.
Age).FirstOrDefault();
372 foundCompetition = competitions.FirstOrDefault(c => c.
Age == personAge);
375 foundCompetition = competitions.LastOrDefault(c => c.
Age <= personAge);
378 foundCompetition = competitions.FirstOrDefault(c => c.
Age >= personAge);
381 foundCompetition = competitions.FirstOrDefault(c => c.
Age == personAge);
382 if (foundCompetition ==
null && personAge > maxAge)
384 foundCompetition = competitions.FirstOrDefault(c => c.
Age == maxAge);
388 foundCompetition = competitions.FirstOrDefault(c => c.
Age == personAge);
389 if (foundCompetition ==
null)
392 foundCompetition = competitions.OrderBy(c => Math.Abs(c.
Age - personAge)).ThenBy(c => c.
Age).FirstOrDefault();
396 foundCompetition = competitions.FirstOrDefault(c => c.
Age == personAge);
397 if (foundCompetition ==
null)
400 foundCompetition = competitions.OrderBy(c => Math.Abs(c.
Age - personAge)).ThenByDescending(c => c.
Age).FirstOrDefault();
405 if (foundCompetition !=
null && foundCompetition.
Age == maxAge) { isUsingMaxAgeCompetition =
true; }
406 if (foundCompetition !=
null && foundCompetition.
Age != personAge) { isUsingExactAgeCompetition =
false; }
408 return foundCompetition;
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)
423 bool isUsingMaxAgeCompetition, isUsingExactAgeCompetition;
426 availableCompetitions[swimmingStyle] = competition;
427 isUsingMaxAgeCompetitionDict[swimmingStyle] = isUsingMaxAgeCompetition;
428 isUsingExactAgeCompetitionDict[swimmingStyle] = isUsingExactAgeCompetition;
430 person.AvailableCompetitions = availableCompetitions;
431 person.IsUsingMaxAgeCompetitionDict = isUsingMaxAgeCompetitionDict;
432 person.IsUsingExactAgeCompetitionDict = isUsingExactAgeCompetitionDict;
437 personStart.CompetitionObj = person.AvailableCompetitions[personStart.Style];
438 personStart.IsUsingMaxAgeCompetition = person.IsUsingMaxAgeCompetitionDict[personStart.Style];
439 personStart.IsUsingExactAgeCompetition = person.IsUsingExactAgeCompetitionDict[personStart.Style];
448 foreach (
Person person
in _personService.GetPersons())
459 if (competition ==
null) {
return; }
463 competition.Distance = _competitionDistanceRuleService.GetCompetitionDistanceFromRules(competition.
Age, competition.
SwimmingStyle);
464 if(keepRudolphTableFlags)
466 competition.IsTimeFromRudolphTable = isTimeFromRudolphTable;
467 competition.IsTimeInterpolatedFromRudolphTable = isTimeInterpolatedFromRudolphTable;
468 competition.IsOpenAgeTimeFromRudolphTable = isOpenAgeTimeFromRudolphTable;
491 Dictionary<SwimmingStyles, byte> maxAgesBySwimmingStyles = rudolphTable.Entries
492 .GroupBy(e => e.SwimmingStyle)
493 .ToDictionary(g => g.Key, g => g.Max(e => e.Age));
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))
501 Age = e.IsOpenAge ? (byte)(maxAgesBySwimmingStyles[e.SwimmingStyle] + 1) : e.Age,
503 Distance = e.Distance,
505 IsTimeFromRudolphTable =
true,
506 IsTimeInterpolatedFromRudolphTable =
false,
507 IsOpenAgeTimeFromRudolphTable = e.IsOpenAge
514 List<Competition> competitionsToDelete =
_competitionList.Intersect(competitions, basicEqualityComparer).ToList();
515 foreach (
Competition competition
in competitionsToDelete)
517 competition.PropertyChanged -= Competition_PropertyChanged;
525 competition.IsTimeInterpolatedFromRudolphTable =
false;
526 competition.IsOpenAgeTimeFromRudolphTable =
false;
535 while (currentIds.Contains(
id)) {
id++; }
552 if (foundRudolphTableEntry !=
null)
554 competition.BestTime = foundRudolphTableEntry.Time;
555 competition.IsTimeFromRudolphTable =
true;
556 competition.IsTimeInterpolatedFromRudolphTable =
false;
557 competition.IsOpenAgeTimeFromRudolphTable = foundRudolphTableEntry.IsOpenAge;
561 competition.IsTimeFromRudolphTable =
false;
562 competition.IsTimeInterpolatedFromRudolphTable =
false;
563 competition.IsOpenAgeTimeFromRudolphTable =
false;
581 List<string> groupErrorStrings =
new List<string>();
582 foreach(var group
in groups)
585 Genders groupGender = group.Key.Gender;
586 ushort groupDistance = group.Key.Distance;
598 if (competitionsMissingTimes.Count == 0)
604 if (competitionsRudolphTable.Count < 3)
610 double[] dataX = competitionsRudolphTable.Select(c => (
double)c.
Age).ToArray();
611 double[] dataY = competitionsRudolphTable.Select(c => c.
BestTime.TotalMilliseconds).ToArray();
614 Polynomial poly = Polynomial.Fit(dataX, dataY, 3);
615 foreach (
Competition missingTimeCompetition
in competitionsMissingTimes)
617 double interpolatedTimeMilliseconds = poly.Evaluate(missingTimeCompetition.
Age);
618 missingTimeCompetition.BestTime =
new TimeSpan(0, 0, 0, 0, (
int)interpolatedTimeMilliseconds);
619 missingTimeCompetition.IsTimeInterpolatedFromRudolphTable =
true;
622 if(groupErrorStrings.Count > 0)
624 string errorString =
string.Join(Environment.NewLine, groupErrorStrings);
625 throw new Exception(errorString);
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.
UInt16 BirthYear
Birth year of the person.
Genders Gender
Gender of the person.
Class describing a start of a person.
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.
string PersistentPath
Path to the competition file.
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.
void ClearAll()
Clear all Competitions.
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.
int CompetitionCount
Return the number of Competition
ProgressDelegate OnFileProgress
Event that is raised when the file operation progress changes.
Class holding all workspace settings.
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.
PersonStartFilters
Available filters for PersonStart objects.
@ SwimmingStyle
Filter by the SwimmingStyles