1using CommunityToolkit.Mvvm.ComponentModel;
2using System.Collections.ObjectModel;
32 private int _nextVariantID;
42 _fileService = fileService;
43 _personService = personService;
45 _competitionService = competitionService;
57 _workspaceService = workspaceService;
65 public ObservableCollection<RacesVariant>
AllRacesVariants {
get;
private set; } =
new ObservableCollection<RacesVariant>();
94 _competitionService.UpdateAllCompetitionsForPerson();
95 List<PersonStart> starts = _personService.GetAllPersonStarts();
98 Dictionary<(
SwimmingStyles, ushort), List<PersonStart>> groupedValuesStarts =
new Dictionary<(
SwimmingStyles, ushort), List<PersonStart>>();
99 for (
int i = 0; i < starts.Count; i++)
101 if (!starts[i].IsCompetitionObjAssigned) {
continue; }
103 (
SwimmingStyles, ushort) key = (starts[i].CompetitionObj.SwimmingStyle, starts[i].CompetitionObj.Distance);
104 if (!groupedValuesStarts.ContainsKey(key))
106 groupedValuesStarts.Add(key,
new List<PersonStart>());
108 else if (groupedValuesStarts[key] ==
null)
110 groupedValuesStarts[key] =
new List<PersonStart>();
112 groupedValuesStarts[key].Add(starts[i]);
115 ushort numSwimLanes = _workspaceService?.Settings?.GetSettingValue<ushort>(
WorkspaceSettings.GROUP_RACE_CALCULATION,
WorkspaceSettings.SETTING_RACE_CALCULATION_NUMBER_OF_SWIM_LANES) ?? 0;
116 ushort numberRequestedVariants = _workspaceService?.Settings?.GetSettingValue<ushort>(
WorkspaceSettings.GROUP_RACE_CALCULATION,
WorkspaceSettings.SETTING_RACE_CALCULATION_NUM_RACE_VARIANTS_AFTER_CALCULATION) ?? 0;
117 int maxRaceVariantCalculationLoops = _workspaceService?.Settings?.GetSettingValue<
int>(
WorkspaceSettings.GROUP_RACE_CALCULATION,
WorkspaceSettings.SETTING_RACE_CALCULATION_MAX_CALCULATION_LOOPS) ?? 0;
118 double minRacesVariantsScore = _workspaceService?.Settings?.GetSettingValue<
double>(
WorkspaceSettings.GROUP_RACE_CALCULATION,
WorkspaceSettings.SETTING_RACE_CALCULATION_MIN_RACES_VARIANTS_SCORE) ?? 0;
119 int numberOfResultsToGenerate = Math.Max(0, numberRequestedVariants -
AllRacesVariants.Count(r => r.KeepWhileRacesCalculation));
122 new Progress<double>(progress => onProgressSolution?.Invoke(
this, (
float)progress,
"")),
123 numberOfResultsToGenerate,
124 maxRaceVariantCalculationLoops,
125 minRacesVariantsScore,
127 List<RacesVariant> tmpRacesVariants = await generator.
GenerateBestRacesAsync(groupedValuesStarts.Values.ToList(), _workspaceService, cancellationToken);
129 if (!cancellationToken.IsCancellationRequested)
131 List<RacesVariant> racesToDelete =
AllRacesVariants.Where(r => !r.KeepWhileRacesCalculation).ToList();
150 public async Task<bool>
Load(
string path, CancellationToken cancellationToken)
152 bool importingResult =
false;
153 Exception exception =
null;
155 SynchronizationContext uiContext = SynchronizationContext.Current;
160 if (!File.Exists(path))
170 return PropertyNameLocalizedStringHelper.FindProperty(typeof(
Race), header);
173 bestRacesVariant.IsPersistent =
true;
174 uiContext.Send((d) =>
185 importingResult =
true;
187 catch (OperationCanceledException)
189 importingResult =
false;
197 if (exception !=
null) {
throw exception; }
198 return importingResult;
201 private int _lastParsedDistance = 0;
211 if (
string.IsNullOrEmpty(value))
return;
213 switch (propertyName)
215 case nameof(
Race.
Distance): _lastParsedDistance =
int.Parse(value);
break;
216 case nameof(
Race.
Style):
if (EnumCoreLocalizedStringHelper.TryParse(value, out
SwimmingStyles parsedStyle)) { _lastParsedStyle = parsedStyle; }
break;
219 if (dataObj.
Starts ==
null) { dataObj.Starts =
new ObservableCollection<PersonStart>(); }
220 if(_lastParsedDistance != 0 && _lastParsedStyle !=
SwimmingStyles.Unknown)
222 string[] nameParts = value.Split(
',');
223 string firstName = nameParts.Length > 0 ? nameParts[0].Trim() :
"";
224 string name = nameParts.Length > 1 ? nameParts[1].Trim() :
"";
225 PersonStart matchingStart = _personService.GetAllPersonStarts().Where(s => s.Style == _lastParsedStyle &&
226 s.CompetitionObj?.Distance == _lastParsedDistance &&
227 s.PersonObj?.FirstName == firstName &&
228 s.PersonObj?.Name == name).FirstOrDefault();
229 if (matchingStart !=
null)
231 dataObj.
Starts.Add(matchingStart);
247 public async Task<bool>
Save(CancellationToken cancellationToken,
string path =
"")
251 bool saveResult =
false;
252 Exception exception =
null;
259 if (File.Exists(path)) { File.Delete(path); }
264 int maxNumberStarts = PersistedRacesVariant.Races.Count == 0 ? 0 :
PersistedRacesVariant.Races.Select(r => r.Starts.Count).Max();
266 (data, parentObject, currentProperty) =>
268 if (data is IList<PersonStart> dataList)
270 return string.Join(
";", dataList.Select(p => p.PersonObj?.FirstName +
", " + p.PersonObj?.Name));
272 else if (data is Enum dataEnum)
274 return EnumCoreLocalizedStringHelper.Convert(dataEnum);
278 return data.ToString();
281 (header, headerType) =>
283 if (headerType == typeof(ObservableCollection<PersonStart>))
285 string formatedHeader =
"";
286 for(
int i = 1; i <= maxNumberStarts; i++)
288 formatedHeader += $
"{PropertyNameLocalizedStringHelper.Convert(typeof(Race), "Person")} {i};";
290 formatedHeader = formatedHeader.Trim(
';');
291 return formatedHeader;
295 return PropertyNameLocalizedStringHelper.Convert(typeof(
Race), header);
303 catch (OperationCanceledException)
313 if (exception !=
null) {
throw exception; }
326 racesVariant.VariantID = _nextVariantID;
328 racesVariant.PropertyChanged += RacesVariant_PropertyChanged;
338 if(racesVariant ==
null) {
return; }
339 racesVariant.PropertyChanged -= RacesVariant_PropertyChanged;
351 racesVariant.PropertyChanged -= RacesVariant_PropertyChanged;
365 if (persistedRaceIndex != -1)
367 PersistedRacesVariant.PropertyChanged -= RacesVariant_PropertyChanged;
369 resetRaceVariant.PropertyChanged += RacesVariant_PropertyChanged;
388 List<PersonStart> allStarts = _personService.GetAllPersonStarts();
391 foreach (
Race race
in racesVariant.
Races)
393 ObservableCollection<PersonStart> newRaceStarts =
new ObservableCollection<PersonStart>();
396 PersonStart newStart = allStarts.Where(s => s.PersonObj.Equals(oldStart.PersonObj) &&
397 s.Style == oldStart.Style).FirstOrDefault();
398 newRaceStarts.Add(newStart ?? oldStart);
400 race.Starts = newRaceStarts;
422 int newVariantID = -1;
426 if (racesVariant.
VariantID == oldVariantID) { newVariantID = _nextVariantID; }
427 racesVariant.VariantID = _nextVariantID;
436 private void RacesVariant_PropertyChanged(
object sender,
System.ComponentModel.PropertyChangedEventArgs e)
442 OnPropertyChanged(nameof(HasUnsavedChanges));
443 OnPropertyChanged(nameof(AllRacesVariants));
444 OnPropertyChanged(nameof(PersistedRacesVariant));
456 List<PersonStart> validPersonStarts = _personService.GetAllPersonStarts();
464 if (removeInactiveStarts)
467 startsToDelete.AddRange(race.
Starts.Where(s => !s.IsActive));
472 startsToDelete.ForEach(s => race.
Starts.Remove(s));
476 List<Race> racesToDelete = racesVariant.
Races.Where(r => r.Starts.Count == 0).ToList();
477 racesToDelete.ForEach(r => racesVariant.
Races.Remove(r));
Class describing a person.
Class describing a start of a person.
Comparer that compares a PersonStart without regarding the PersonStart.IsActive
Class that represents a single race.
int Distance
Distance for this Race.
ObservableCollection< PersonStart > Starts
List with all starts of this Race
SwimmingStyles Style
for this .
Comparer that only uses most properties of a RacesVariant to determine equality:
bool Equals(RacesVariant x, RacesVariant y)
Check if two RacesVariant objects are equal based on basic properties.
Class that represents a combination variant of all single races.
void UpdateNotAssignedStarts(List< PersonStart > allStarts)
Update the list of not assigned PersonStart objects.
ObservableCollection< Race > Races
List with races.
int VariantID
Number for this RacesVariant
void UpdateRaceStartsCollectionChangedEvent()
Update the internal event handlers for the Race.Starts collection changed events.
async Task< bool > Save(CancellationToken cancellationToken, string path="")
Save the PersistedRacesVariant to a file.
RaceService(IFileService fileService, IPersonService personService, ICompetitionService competitionService)
Constructor.
RacesVariant _persistedRacesVariantOnLoad
RacesVariant object that is marked as best result at the time the Load(string, CancellationToken) met...
async Task< bool > Load(string path, CancellationToken cancellationToken)
Load the best race as the only element to the AllRacesVariants.
bool HasUnsavedChanges
Check if the PersistedRacesVariant has not saved changed.
void AddRacesVariant(RacesVariant racesVariant)
Add a new RacesVariant to the list AllRacesVariants
void CleanupRacesVariants(bool removeInactiveStarts=true)
Remove non-existing and inactive PersonStart objects from all races in AllRacesVariants.
void SetWorkspaceServiceObj(IWorkspaceService workspaceService)
Save the reference to the IWorkspaceService object.
void ClearAllRacesVariants()
Remove all RacesVariant from the list AllRacesVariants
async Task< ObservableCollection< RacesVariant > > CalculateRacesVariants(CancellationToken cancellationToken, ProgressDelegate onProgressIteration=null, ProgressDelegate onProgressSolution=null)
Calculate some RacesVariant objects for all person starts.
void ReassignAllPersonStarts()
Reassign all PersonStart objects in all RacesVariant in AllRacesVariants.
int RecalculateVariantIDs(int oldVariantID=-1)
Reassign all RacesVariant.VariantID so that the IDs start from 1 and have no gaps.
void ResetToLoadedState()
Reset the to the state when the method was called.
void RemoveRacesVariant(RacesVariant racesVariant)
Remove the given RacesVariant object from the list AllRacesVariants
EventHandler OnFileFinished
Event that is raised when the file operation is finished.
ObservableCollection< RacesVariant > AllRacesVariants
List with all RacesVariant (including the loaded and calculated ones)
RacesVariant PersistedRacesVariant
RacesVariant object that is persisted (saved/loaded to/from a file).
ProgressDelegate OnFileProgress
Event that is raised when the file operation progress changes.
void SortVariantsByScore()
Sort the complete list AllRacesVariants descending by the RacesVariant.Score
void setRacePropertyFromString(Race dataObj, string propertyName, string value)
Set the requested property in the Race object by parsing the given string value.
string PersistentPath
Path to the race file.
Class to generate different variants of RacesVariant using the score calculation.
async Task< List< RacesVariant > > GenerateBestRacesAsync(List< List< PersonStart > > sets, IWorkspaceService workspaceService=null, CancellationToken cancellationToken=default)
Calculate the variants asynchronously.
Class holding all workspace settings.
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.
List< PersonStart > GetAllPersonStarts(PersonStartFilters filter=PersonStartFilters.None, object filterParameter=null, bool onlyValidStarts=false)
Get all PersonStart objects for all Person objects that are not null.
void SetRaceServiceObj(IRaceService raceService)
Save the reference to the IRaceService object.
Interface for a service used to manage Race and RacesVariant objects.
Interface for a service used to manage a workspace.
delegate void ProgressDelegate(object sender, float progress, string currentStep="")
Delegate void for progress changes.
SwimmingStyles
Available swimming styles.