/// <summary> /// Loads the data from database. /// </summary> private void LoadDataFromDb() { // Event-Id aus den Einstellungen holen var eventId = Properties.Settings.Default.EventId; using (var db = new AquariusDataContext(Properties.Settings.Default.ConnectionString)) { // Test-Zugriff auf DB try { var test = db.Events.ToList(); } catch (Exception ex) { MessageBox.Show("Fehler beim Datenbank-Zugriff! Verbindung zur Datenbank und Connection String in den Einstellungen prüfen.\r\n\r\nDetails:\r\n\r\n" + ex.ToString()); return; } var dbData = GlobalData.Instance; // Veranstaltungen laden var dbEvents = db.Events; dbData.EventsData = dbEvents.Select(e => new EventData() { Id = e.Event_ID, Title = e.Event_Title }).ToList(); if (dbData.EventsData.SingleOrDefault(ed => ed.Id == eventId) == null) { MessageBox.Show("Veranstaltung nicht gefunden! Veranstaltung in den Einstellungen auswählen und Programm neu starten!"); return; } // Altersklassen einlesen var dbAgeClasses = db.AgeClasses.ToDictionary(ac => ac.AgeClass_ID, ac => ac); // Bootsklassen einlesen var dbBoatClasses = db.BoatClasses.ToDictionary(bc => bc.BoatClass_ID, bc => bc); // Ausschreibung einlesen var dbOffers = db.Offers.Where(o => o.Offer_Event_ID_FK == eventId).ToDictionary(o => o.Offer_ID, o => o); // Wettkämpfe einlesen var dbCompetions = db.Comps.Where(c => (c.Comp_Event_ID_FK == eventId) && c.Comp_DateTime != null).OrderBy(c => c.Comp_DateTime); // Boote einlesen (Entries sind Boote, in jedem Boot sitzt eine Mannschaft) // (gruppiert nach Meldung) var dbEntries = db.Entries.Where(e => e.Entry_Event_ID_FK == eventId).GroupBy(ee => ee.Entry_Race_ID_FK).ToDictionary(ee => ee.Key, ee => ee.Select(x => x).ToList()); // Mannschaften einlesen // (gruppiert nach dem Boot, in dem sie sitzt) var dbCrews = db.Crews.GroupBy(c => c.Crew_Entry_ID_FK).ToDictionary(c => c.Key, c => c.Select(x => x).ToList()); // Ruderer einlesen var dbAthlets = db.Athlets.ToDictionary(a => a.Athlet_ID, a => a); // Vereine einlesen var dbClubs = db.Clubs.ToDictionary(c => c.Club_ID, c => c); // Mappings Boot (!) <-> Verein var dbEntryLabels = db.EntryLabels.ToDictionary(el => el.EL_ID, el => el); // Labels für Boote (!) var dbLabels = db.Labels.ToDictionary(l => l.Label_ID, l => l); // Rennen tauchen mehrfach auf, falls es z. B. mehrere Leistungsgruppen gibt. // Da die Läufe aber ggf. noch nicht eingeteilt sind, lassen sich die Personen nicht // den einzelnen Rennen/Läufen zuweisen. // Derartig mehrfach vorkommende Rennen werden daher zu einem Rennen zusammengefasst: var competitionGroups = dbCompetions.GroupBy(c => c.Comp_Race_ID_FK).ToList(); #region Wettkämpfe durchgehen dbData.RacesData = new List <RaceData>(); foreach (var competionGroup in competitionGroups) { _ = dbOffers.TryGetValue((int)competionGroup.Key, out var offer); _ = dbAgeClasses.TryGetValue(offer.Offer_AgeClass_ID_FK, out var ageClass); _ = dbBoatClasses.TryGetValue(offer.Offer_BoatClass_ID_FK, out var boatClass); var isChildrenRace = (ageClass.AgeClass_MaxAge <= 14); bool isLightweightRace = (offer.Offer_IsLightweight); var isCoxedRace = (boatClass.BoatClass_Coxed == 1); // Entscheiden, ob man das Rennen übernehmen muss var raceOk = false; if (isLightweightRace) { raceOk = true; // alle Leichtgewichtsrennen müssen zur Waage } if (isCoxedRace && !isChildrenRace) { raceOk = true; // gesteuerte Rennen, die keine Kinderrennen sind, müssen zur Waage } if (raceOk) { // Rennzeit ist die Zeit des ersten Rennens dieser Gruppe var raceTime = (DateTime)competionGroup.Select(x => x).OrderBy(c => c.Comp_DateTime).First().Comp_DateTime; #region Gewichte bestimmen var maxSingleWeight = new Weight(ageClass.AgeClass_LW_UpperLimit, true); var maxAverageWeight = new Weight(ageClass.AgeClass_LW_AvgLimit, true); var minCoxWeight = new Weight(ageClass.AgeClass_LW_CoxLowerLimit, true); var maxAdditionalCoxWeight = new Weight(ageClass.AgeClass_LW_CoxTolerance, true); // wenn es kein gesteuertes Rennen ist, dann interessiert das Gewicht des Steuermanns nicht if (!isCoxedRace) { minCoxWeight.Value = null; } // bei Kinderrennen interessiert das Gewicht des Steuermanns ebenfalls nicht if (isChildrenRace) { minCoxWeight.Value = null; } // bei Einer-Rennen kein Mannschaftsdurchschnittsgewicht if (boatClass.BoatClass_NumRowers == 1) { maxAverageWeight.Value = null; } // Wenn es kein Leichgewichtsrennen ist, dann interessieren auch die Gewichte der Ruderer nicht if (!isLightweightRace) { maxSingleWeight.Value = null; maxAverageWeight.Value = null; } #endregion // Rennen definieren und hinzufügen var newRace = new RaceData() { Id = offer.Offer_ID, RaceNumber = offer.Offer_RaceNumber, ShortTitle = offer.Offer_ShortLabel, LongTitle = offer.Offer_LongLabel, DateTime = raceTime, IsChildrenRace = isChildrenRace, IsCoxedRace = isCoxedRace, NumberOfRowers = boatClass.BoatClass_NumRowers, IsLightweightRace = isLightweightRace, MaxSingleWeight = maxSingleWeight, MaxAverageWeight = maxAverageWeight, MinCoxWeight = minCoxWeight, MaxAdditionalCoxWeight = maxAdditionalCoxWeight }; dbData.RacesData.Add(newRace); // zum aktuellen Rennen die Boote hinzufügen newRace.BoatsData = new List <BoatData>(); _ = dbEntries.TryGetValue(offer.Offer_ID, out var entries); if (entries != null) { foreach (var entry in entries) { // das Label für das Boot (!) (z. B. "Renngemeinschaft ...") bestimmen var titleShort = "?"; var titleLong = "?"; var entryLabel = dbEntryLabels.Values.FirstOrDefault(el => el.EL_Entry_ID_FK == entry.Entry_ID); if (entryLabel != null) { dbLabels.TryGetValue(entryLabel.EL_Label_ID_FK, out var label); titleShort = label?.Label_Short; titleLong = label?.Label_Long; } var newBoat = new BoatData() { TitleShort = titleShort, TitleLong = titleLong, BibNumber = (byte)entry.Entry_Bib, Canceled = (entry.Entry_CancelValue > 0) }; newRace.BoatsData.Add(newBoat); // Zum aktuellen Boot die Mannschaft hinzufügen newBoat.Rowers = new List <RowerData>(); _ = dbCrews.TryGetValue(entry.Entry_ID, out var crew); #region Sonderbehandlung der Crew für den Fall, dass es Ummeldungen gibt // TODO: Es ist nicht ganz klar, woran man Ummeldungen erkennt, bzw. woran // man erkennt, welches das alte, und welches das neue Crewmitglied // ist. Auf jeden Falls stehen beiden in der DB drin. if (crew != null) { // Gruppieren nach dem Sitzplatz var crewByPos = crew.GroupBy(c => c.Crew_Pos).ToList(); // Crew-Liste löschen und neu füllen crew = new List <Crew>(); foreach (var item in crewByPos) { // die Ruderer für die aktuelle Sitzposition sortiert nach DB-ID // TODO: Ist DB-ID wirklich das richtige ??? var rowersOnCurrentPos = item.Select(x => x).OrderBy(x => x.Crew_ID); crew.Add(rowersOnCurrentPos.Last()); } } #endregion if (crew != null) { // Crew durchgehen und ins Boot platzieren foreach (var crewMember in crew) { dbAthlets.TryGetValue(crewMember.Crew_Athlete_ID_FK, out var athlet); dbClubs.TryGetValue((int)athlet.Athlet_Club_ID_FK, out var club); if ((athlet != null) && (club != null)) { var newRower = new RowerData() { Id = athlet.Athlet_ID, ClubTitleLong = club.Club_Name, ClubTitleShort = club.Club_Abbr, LastName = athlet.Athlet_LastName, FirstName = athlet.Athlet_FirstName, DateOfBirth = athlet.Athlet_DOB, Gender = (athlet.Athlet_Gender == 'M') ? Gender.Male : Gender.Female }; if (crewMember.Crew_IsCox) { // es handelt sich um den Steuermann newBoat.Cox = newRower; } else { // normaler Ruderer newBoat.Rowers.Add(newRower); } } else { // Fehler: Ruderer oder Verein nicht gefunden Tools.LogErrorNoMessageBox("Warnung: Ruderer oder kein Verein zu Crew nicht gefunden. Rennen ", offer.Offer_RaceNumber, offer.Offer_ShortLabel); } } } else { // Fehler: keine Mannschaft für das Boot gefunden Tools.LogErrorNoMessageBox("Warnung: Mannschaft zu Rennen nicht gefunden. Rennen ", offer.Offer_RaceNumber, offer.Offer_ShortLabel); } } } // Boote sortieren nach Startnummer newRace.BoatsData = newRace.BoatsData.OrderBy(b => b.BibNumber).ToList(); } } #endregion #region Ruderer anhand der ermittelten Rennen bestimmen var newRowers = new List <RowerData>(); foreach (var raceData in dbData.RacesData) { foreach (var boatData in raceData.BoatsData) { // Ruderer hinzufügen foreach (var rowerData in boatData.Rowers) { if (!newRowers.Any(r => r.Id == rowerData.Id)) { newRowers.Add(rowerData); } } // Steuermann hinzufügen if (boatData.Cox != null) { if (!newRowers.Any(r => r.Id == boatData.Cox.Id)) { newRowers.Add(boatData.Cox); } } } } dbData.RowersData = newRowers.OrderBy(r => r.LastName).ThenBy(r => r.FirstName).ToList(); #endregion // Rennen sortieren nach Startzeit dbData.RacesData = dbData.RacesData.OrderBy(r => r.DateTime).ToList(); } }
/// <summary> /// Adds the rower. /// </summary> /// <param name="rowers">The rowers.</param> /// <param name="raceData">The race data.</param> /// <param name="rowerData">The rower data.</param> /// <param name="rowerIsCox">if set to <c>true</c> [steuermann].</param> /// <param name="delay">The delay.</param> public void AddRower(ref List <UserControls.Rower> rowers, RaceData raceData, RowerData rowerData, bool rowerIsCox, TimeSpan delay) { var coronaSonderregeln = DateTime.Now.Year <= 2021; UserControls.RowerStatus rowerStatus = UserControls.RowerStatus.None; string weightRower = "-"; float? weightInfo = null; var weightings = GlobalData.Instance.Weightings.Where(w => w.Id == rowerData.Id).ToList(); var raceDt = raceData.DateTime + delay; var weightRowerRequired = raceData.MaxSingleWeight.IsSpecified(); var weightCoxRequired = raceData.MaxAdditionalCoxWeight.IsSpecified(); if (raceData.IsChildrenRace) { #region Kinderennen #region Zeitfenster für's Wiegen im Kinderrennen DateTime existingWeightingAcceptedFromDt; DateTime newWeightingAcceptedFromDt; DateTime weightingToDt; if (coronaSonderregeln) { // ACHTUNG: Aktuell gibt es nur ein Zeitfenster, egal ob für Ruderer oder Steuermann. Das kann man nur machen, weil für beide das gleiche Zeitfenster gilt! // Es gibt kein "frühestens", das Kind darf so früh kommen wie es möchte // Daher ist "frühestens" der 01.01.2000 und das ist auf jeden Fall vor jedem beliebigen Zeitpunkt, zu dem das Kind kommt. // Könnte man eigentlich zusammen mit der Abfrage rauswerfen, aber vielleicht gibt es ja doch mal ein "frühestens" // EINSTELLUNG: ab wann wird eine bereits existierende Wiegung für das aktuelle Rennen berücksichtigt? // frühestens 01.01.2000 04:00 Uhr existingWeightingAcceptedFromDt = new DateTime(2000, 1, 1, 4, 0, 0); // EINSTELLUNG (2 Werte): Zeitfenster, in dem eine weitere/neue Wiegung für das aktuelle Rennen zugelassen ist // neue Wiegung (wenn es keine andere gibt) frühestens ??? h ??? min vorher // gleicher Fall wie oben, daher auch 01.01.2000 04:00 Uhr newWeightingAcceptedFromDt = new DateTime(2000, 1, 1, 4, 0, 0); // ansonsten so was hier: raceDt.Add(new TimeSpan(-2, -5, 0)); // spätestens 1 Stunde vorher weightingToDt = raceDt.Add(new TimeSpan(-1, 0, 0)); } else { // ACHTUNG: Aktuell gibt es nur ein Zeitfenster, egal ob für Ruderer oder Steuermann. Das kann man nur machen, weil für beide das gleiche Zeitfenster gilt! // Es gibt kein "frühestens", das Kind darf so früh kommen wie es möchte // Daher ist "frühestens" der 01.01.2000 und das ist auf jeden Fall vor jedem beliebigen Zeitpunkt, zu dem das Kind kommt. // Könnte man eigentlich zusammen mit der Abfrage rauswerfen, aber vielleicht gibt es ja doch mal ein "frühestens" // EINSTELLUNG: ab wann wird eine bereits existierende Wiegung für das aktuelle Rennen berücksichtigt? // frühestens 01.01.2000 04:00 Uhr existingWeightingAcceptedFromDt = new DateTime(2000, 1, 1, 4, 0, 0); // EINSTELLUNG (2 Werte): Zeitfenster, in dem eine weitere/neue Wiegung für das aktuelle Rennen zugelassen ist // neue Wiegung (wenn es keine andere gibt) frühestens ??? h ??? min vorher // gleicher Fall wie oben, daher auch 01.01.2000 04:00 Uhr newWeightingAcceptedFromDt = new DateTime(2000, 1, 1, 4, 0, 0); // ansonsten so was hier: raceDt.Add(new TimeSpan(-2, -5, 0)); // spätestens 1 Stunde vorher weightingToDt = raceDt.Add(new TimeSpan(-1, 0, 0)); } #endregion // Ruderer muss im Zeitfenster zum Wiegen gekommen sein // => letzte Wiegung im Zeitfenster bestimmen var weighting = weightings.Where(w => (w.Zeitstempel >= existingWeightingAcceptedFromDt) && (w.Zeitstempel <= weightingToDt)).OrderByDescending(x => x.Zeitstempel).FirstOrDefault(); if (!rowerIsCox) { // normaler Ruderer im Kinderrennen (nicht der Steuermann) if (weightRowerRequired) { // Ruderer muss gewogen werden if (weighting != null) { // Es gibt eine Wiegung im Zeitfenster, die entscheidet, ob der Ruderer starten darf. // (Wenn es hingegen keine gültige Wiegung gibt, kann der Status auch nicht OK sein.) rowerStatus = (weighting.Gewicht <= raceData.MaxSingleWeight.Value) ? UserControls.RowerStatus.WeightOk : UserControls.RowerStatus.WeightNotOk; weightInfo = weighting.Gewicht; weightRower = $"{weighting.Gewicht} kg"; } else { // keine Wiegung im Zeitfenster vorhanden // entweder es ist noch zu früh zum Wiegen oder es ist schon zu spät dazu rowerStatus = GetStatusNoWeight(newWeightingAcceptedFromDt, weightingToDt); } } else { // Ruderer muss nicht gewogen werden (kein Leichtgewichtrennen) rowerStatus = UserControls.RowerStatus.WeightOk; } } else { // Es handelt sich um einen Steuerman im Kinderrennen // Steuermann im Kinderrennen muss grundsätzlich nicht gewogen werden! rowerStatus = UserControls.RowerStatus.WeightOk; } #endregion Kinderennen } else { #region Kein Kinderennen #region Zeitfenster für's Wiegen DateTime existingWeightingAcceptedFromDt; DateTime newWeightingAcceptedFromDt; DateTime weightingToDt; if (coronaSonderregeln) { // ACHTUNG: Aktuell gibt es nur ein Zeitfenster, egal ob für Ruderer oder Steuermann. Das kann man nur machen, weil für beide das gleiche Zeitfenster gilt! // frühestens 2 Stunden vor dem ersten Rennen an diesem Tag. // Das wird hier aber nicht abgefragt, weil man dann hier abfragen müsste, ob dieses Rennen hier das erste an diesem Tag ist oder nicht. // Stattdessen verlassen wir uns hier darauf, niemand außerhalb des zugelassenen Zeitfensters zum Wiegen zugelassen wurde, // in dem Fall muss die letzte/jüngste Wiegung auch eine gültige Wiegung sein, Hauptsache, sie hat am gleichen Tag stattgefunden. // EINSTELLUNG: ab wann wird eine bereits existierende Wiegung für das aktuelle Rennen berücksichtigt? // frühestens 01.01.2000 04:00 Uhr // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! existingWeightingAcceptedFromDt = new DateTime(2000, 1, 1, 4, 0, 0); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // EINSTELLUNG (2 Werte): Zeitfenster, in dem eine weitere/neue Wiegung für das aktuelle Rennen zugelassen ist // gleicher Fall wie oben, daher auch 01.01.2000 04:00 Uhr // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! newWeightingAcceptedFromDt = new DateTime(2000, 1, 1, 4, 0, 0); // ansonsten so was hier: raceDt.Add(new TimeSpan(-2, -5, 0)); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // spätestens 1 Stunde vorher weightingToDt = raceDt.Add(new TimeSpan(-1, 0, 0)); } else { // ACHTUNG: Aktuell gibt es nur ein Zeitfenster, egal ob für Ruderer oder Steuermann. Das kann man nur machen, weil für beide das gleiche Zeitfenster gilt! // frühestens 2 Stunden vor dem ersten Rennen an diesem Tag. // Das wird hier aber nicht abgefragt, weil man dann hier abfragen müsste, ob dieses Rennen hier das erste an diesem Tag ist oder nicht. // Stattdessen verlassen wir uns hier darauf, niemand außerhalb des zugelassenen Zeitfensters zum Wiegen zugelassen wurde, // in dem Fall muss die letzte/jüngste Wiegung auch eine gültige Wiegung sein, Hauptsache, sie hat am gleichen Tag stattgefunden. // EINSTELLUNG: ab wann wird eine bereits existierende Wiegung für das aktuelle Rennen berücksichtigt? // frühestens 4 Uhr morgens am Tag des Rennens (und damit sicher nicht mehr am Vortrag) existingWeightingAcceptedFromDt = new DateTime(raceDt.Year, raceDt.Month, raceDt.Day, 4, 0, 0); // EINSTELLUNG (2 Werte): Zeitfenster, in dem eine weitere/neue Wiegung für das aktuelle Rennen zugelassen ist // neue Wiegung (wenn es keine andere gibt) frühestens 2 h vorher newWeightingAcceptedFromDt = raceDt.Add(new TimeSpan(-2, 0, 0)); // spätestens 1 Stunde vorher weightingToDt = raceDt.Add(new TimeSpan(-1, 0, 0)); } #endregion // Ruderer muss im Zeitfenster zum Wiegen gekommen sein // => letzte Wiegung im Zeitfenster bestimmen var weighting = weightings.Where(w => (w.Zeitstempel >= existingWeightingAcceptedFromDt) && (w.Zeitstempel <= weightingToDt)).OrderByDescending(x => x.Zeitstempel).FirstOrDefault(); if (!rowerIsCox) { // normaler Ruderer (nicht der Steuermann) if (weightRowerRequired) { // Ruderer muss gewogen werden if (weighting != null) { // Es gibt eine Wiegung im Zeitfenster, die entscheidet, ob der Ruderer starten darf. // (Wenn es hingegen keine gültige Wiegung gibt, kann der Status auch nicht OK sein.) rowerStatus = (weighting.Gewicht <= raceData.MaxSingleWeight.Value) ? UserControls.RowerStatus.WeightOk : UserControls.RowerStatus.WeightNotOk; weightInfo = weighting.Gewicht; weightRower = $"{weighting.Gewicht} kg"; } else { // keine Wiegung im Zeitfenster vorhanden // entweder es ist noch zu früh zum Wiegen oder es ist schon zu spät dazu rowerStatus = GetStatusNoWeight(newWeightingAcceptedFromDt, weightingToDt); } } else { // Ruderer muss nicht gewogen werden (kein Leichtgewichtrennen) rowerStatus = UserControls.RowerStatus.WeightOk; } } else { // Es handelt sich um einen Steuerman if (weightCoxRequired) { // Steuermann muss gewogen werden if (weighting != null) { // Es gibt eine Wiegung im Zeitfenster, die entscheidet, ob der Ruderer starten darf. // (Wenn es hingegen keine gültige Wiegung gibt, kann der Status auch nicht OK sein.) // (Steuermann darf leichter sein als das Mindestgewicht, sofern er damit nicht das maximale Zusatzgewicht überschreitet.) rowerStatus = (weighting.Gewicht >= raceData.MinCoxWeight.Value - raceData.MaxAdditionalCoxWeight.Value) ? UserControls.RowerStatus.WeightOk : UserControls.RowerStatus.WeightNotOk; weightInfo = weighting.Gewicht; weightRower = $"{weighting.Gewicht} kg"; } else { // keine Wiegung im Zeitfenster vorhanden // entweder es ist noch zu früh zum Wiegen oder es ist schon zu spät dazu rowerStatus = GetStatusNoWeight(newWeightingAcceptedFromDt, weightingToDt); } } else { // Steuermann muss nicht gewogen werden (kein Leichtgewichtrennen) rowerStatus = UserControls.RowerStatus.WeightOk; } } #endregion Kein Kinderennen } var newRower = new UserControls.Rower() { Id = rowerData.Id, Name = $"{rowerData.LastName}, {rowerData.FirstName} ({weightRower})", Type = rowerIsCox ? UserControls.RowerType.Cox : UserControls.RowerType.Rower, Status = rowerStatus, WeightInfo = weightInfo }; rowers.Add(newRower); }