/
AstroJohn
/
MailRobot
Обзор
Документация
Войти
/
AstroJohn
/
MailRobot
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
LogScoring/LogScoringBase.cs
1 824 строки
55 KB
AstroJohn
Delete log menu
05 авг 2026, 21:25
05 авг 2026, 21:25
5efcbdd
Код
Авторство
О чём код?
using Common; using DbContext; using DbContext.Interfaces; using DbContext.Models; using LogProcessingModels.PrefixResolving; using LogProcessingModels.Scoring; using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.SqlClient; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; namespace LogScoring { public abstract partial class LogScoringBase: ILogScoring { /// <summary> /// Ни в какие удаленные таблицы ничего не пишется /// </summary> protected virtual bool LocalOnly => false; public virtual bool IgnoreQsoMonth => true; public void Initialize(DateTime startTime, DateTime endTime, string ubnLinkTemplate, IPrefixResolver prefixResolver, string connectionString, Action<string> writeLog) { StartTime = startTime; EndTime = endTime; UbnLinkTemplate = ubnLinkTemplate; PrefixResolver = prefixResolver; ConnectionString = connectionString; WriteLog = writeLog; using (var context = new CrossCheckDb(connectionString)) { Bands = context.Bands.ToArray(); Modes = context.Modes.ToArray(); Categories = context.Category.ToArray(); SuperCategories = context.SuperCategory.ToArray(); InitializeInternal(context); WriteLog($"Initialization completed: {GetType().Name}"); } } protected Action<string> WriteLog; protected virtual void InitializeInternal(CrossCheckDb context) { } public event KeepInTouchEventHandler KeepInTouch; protected void KeepInTouchInternal(string txt) { KeepInTouch?.Invoke(txt); } protected bool IsUnitedSuperCategoriesExists => SuperCategories.Any(x => x.UniteCategories == true); public string UbnLinkTemplate { get; private set; } public virtual string LinkedServerName => "MySQL_UA1DZ"; public abstract string MySqlTableNamePrefix { get; } public abstract bool CheckOperatorsTagForRussianCallsigns { get; } public virtual bool ParseOperatorsLine => CheckOperatorsTagForRussianCallsigns; /// <summary> /// Период повторов в минутах (null - повторы не разрешены) /// </summary> protected virtual int? AllowedDupePeriod => null; public abstract bool IsVhf { get; } public IPrefixResolver PrefixResolver { get; private set; } protected string ConnectionString { get; private set; } /// <summary> /// Продолжительность тура в минутах /// </summary> public virtual int? TourDuration => null; /// <summary> /// Допустимы повторы на разных диапазонах /// </summary> public virtual bool IsDupesByBandAllowed => true; /// <summary> /// Допустимы повторы разными видами работы /// </summary> public virtual bool IsDupesByModeAllowed => false; /// <summary> /// Множитель по диапазонам /// </summary> public virtual bool IsMultByBand => true; /// <summary> /// Множитель по видам работы /// </summary> public virtual bool IsMultByMode => false; /// <summary> /// Начало теста /// </summary> public DateTime StartTime { get; private set; } /// <summary> /// Конец теста /// </summary> public DateTime EndTime { get; private set; } protected abstract string GetMult(Qsos qso, int multType); protected abstract int[] MultiplierTypes { get; } protected abstract decimal CalcQsoPoints(Qsos qso, LogFiles log); protected virtual decimal CalcSwlPoints(Swls swl, LogFiles log) { return 0; } protected abstract int GetScore(int points, IEnumerable<LogFilesBandResultsMults> mult, int? qsos = null); protected virtual int GetSwlScore(int points, IEnumerable<LogFilesBandResultsMults> mult, int? qsos = null) { return GetScore(points, mult, qsos); } public abstract string GetTextScore(Log log, IEnumerable<LogFilesBandResults> scores); protected virtual void ProcessPrefixes(Log log) { } public string GetOblast(string callsign) { return PrefixResolver.GetOblast(callsign); } public string GetTeam(string callsign) { return PrefixResolver.GetTeam(callsign); } private void ResolvePrefixes(Log log) { var logDxccPrefix = PrefixResolver.ResolveDxccPrefix(log.Station.Call, out _, out _, out _); if (log.Station.CountryId != logDxccPrefix?.DxccEntity.PrimaryPrefix) { log.Station.CountryId = logDxccPrefix?.DxccEntity.PrimaryPrefix; log.Station.IsModified = true; } if (logDxccPrefix != null && logDxccPrefix.DxccEntity.PrimaryPrefix.IsRussianPrefix()) { var region = PrefixResolver.ResolveOblastPrefix(log.Station.Call); var regionPrefix = region?.OblastEntity.Oblast; if (log.Station.RegionId.IsNullOrEmpty()) { log.Station.RegionId = regionPrefix; log.Station.IsModified = true; } } else { log.Station.RegionId = null; log.Station.IsModified = true; } ProcessPrefixes(log); var calls = log.Qsos.Select(q => q.Call2).Distinct().ToArray(); foreach (var call in calls) { var dxccPrefix = PrefixResolver.ResolveDxccPrefix(call, out _, out _, out _); string regionPrefix = null; if (dxccPrefix != null && dxccPrefix.DxccEntity.PrimaryPrefix.IsRussianPrefix()) { var region = PrefixResolver.ResolveOblastPrefix(call); if (region != null) regionPrefix = region.OblastEntity.Oblast; } foreach (var qso in log.Qsos.Where(x => x.Call2 == call)) { if ( qso.CountryId != dxccPrefix?.DxccEntity.PrimaryPrefix || qso.RegionId != regionPrefix ) { qso.CountryId = dxccPrefix?.DxccEntity.PrimaryPrefix; qso.RegionId = regionPrefix; qso.IsModified = true; } } } } private void ResolveSwlPrefixes(Log log) { var dxccPrefix = PrefixResolver.ResolveDxccPrefix(log.Station.Call, out _, out _, out _); if (dxccPrefix != null) { if (log.Station.CountryId != dxccPrefix.DxccEntity.PrimaryPrefix) { log.Station.CountryId = dxccPrefix.DxccEntity.PrimaryPrefix; log.Station.IsModified = true; } if (dxccPrefix.DxccEntity.PrimaryPrefix.IsRussianPrefix()) { var region = PrefixResolver.ResolveOblastPrefix(log.Station.Call); var regionPrefix = region?.OblastEntity.Oblast; if (log.Station.RegionId.IsNullOrEmpty()) { log.Station.RegionId = regionPrefix; log.Station.IsModified = true; } } } var calls1 = log.Swls.Select(q => q.Call1).Distinct().ToArray(); var calls2 = log.Swls.Select(q => q.Call2).Distinct().ToArray(); var calls = calls1.Concat(calls2).Distinct().ToArray(); foreach (var call in calls) { dxccPrefix = PrefixResolver.ResolveDxccPrefix(call, out _, out _, out _); if (dxccPrefix != null) { string regionPrefix = null; if (dxccPrefix.DxccEntity.PrimaryPrefix.IsRussianPrefix()) { var region = PrefixResolver.ResolveOblastPrefix(call); if (region != null) regionPrefix = region.OblastEntity.Oblast; } foreach (var swl in log.Swls.Where(x => x.Call1 == call)) { if ( swl.CountryId1 != dxccPrefix.DxccEntity.PrimaryPrefix || swl.RegionId1 != regionPrefix ) { swl.CountryId1 = dxccPrefix.DxccEntity.PrimaryPrefix; swl.RegionId1 = regionPrefix; swl.IsModified = true; } } foreach (var swl in log.Swls.Where(x => x.Call2 == call)) { if ( swl.CountryId2 != dxccPrefix.DxccEntity.PrimaryPrefix || swl.RegionId2 != regionPrefix ) { swl.CountryId2 = dxccPrefix.DxccEntity.PrimaryPrefix; swl.RegionId2 = regionPrefix; swl.IsModified = true; } } } } } protected class Multiplier { public string Band { get; set; } public string Mode { get; set; } public int MultType { get; set; } public string Mult { get; set; } public int QsoId { get; set; } } protected int CurrentScoreId { get; set; } protected virtual bool CheckQsoIsDuringContestTime<T>(T qso) where T: IHasDate { return qso.Date <= EndTime && qso.Date >= StartTime; } protected virtual bool IsDupe(IEnumerable<Qsos> qsos, Qsos currentQso, int resType) { var q = qsos .Where ( x => x.Call2 == currentQso.Call2 && x.Date >= GetTourStartTime(currentQso.Date.GetValueOrDefault()) && currentQso.Date >= x.Date && ( AllowedDupePeriod == null || (AllowedDupePeriod != null && currentQso.Date < x.Date.Value.AddMinutes(AllowedDupePeriod.Value)) ) && !x.IsDupe && x.Id != currentQso.Id && (resType == ResultsType.Claimed || !x.IsConfirmedError) ); return q.Any(); } protected virtual void CheckDupes(Log log, int resType) { var bands = IsDupesByBandAllowed ? log.Qsos.Select(x => x.Band).Distinct().ToArray() : new string[] { null }; var modes = IsDupesByModeAllowed ? log.Qsos.Select(x => x.Mode).Distinct().ToArray() : new string[] { null }; foreach (var band in bands) foreach (var mode in modes) { var qsosQuery = log.Qsos.Select(x => x); if (mode != null) qsosQuery = qsosQuery.Where(x => x.Mode == mode); if (band != null) qsosQuery = qsosQuery.Where(x => x.Band == band); qsosQuery = qsosQuery.OrderBy(x => x.Time); var qsos = qsosQuery.ToArray(); foreach (var qso in qsos) { if ( !CheckQsoIsDuringContestTime(qso) && qso.ErrType != "TT" ) { qso.ErrType = "TT"; qso.IsModified = true; } else { var isDupe = IsDupe(qsos, qso, resType); if ( isDupe && !qso.IsConfirmedError ) { qso.ErrType = "D"; qso.IsModified = true; } else if ( !isDupe && qso.ErrType == "D" ) { qso.ErrType = "+"; qso.IsModified = true; } } } } } protected virtual DateTime GetTourStartTime(DateTime time) { if (!TourDuration.HasValue) return StartTime; if (Math.Abs((time - StartTime).TotalDays) > 2) { time = StartTime.AddHours(time.Hour).AddMinutes(time.Minute); } var tourNumber = Convert.ToInt16(Math.Floor((time - StartTime).TotalMinutes)) / TourDuration.Value; return StartTime.AddMinutes(tourNumber * TourDuration.Value); } private static QsosPoints AddPoints(Qsos qso, decimal points, int resType) { var qsosPoints = new QsosPoints { QsoId = qso.Id, Qso = qso, Points = Convert.ToInt32(Math.Round(points, 0, MidpointRounding.AwayFromZero)), ResType = resType, }; return qsosPoints; } protected virtual QsosMults AddMult(Qsos qso, string mult, int multType, int resType, string category) { var qsosMults = new QsosMults { QsoId = qso.Id, Qso = qso, Mult = mult, MultType = multType, ResType = resType, Category = category }; return qsosMults; } public virtual IEnumerable<LogFilesBandResults> ScoreLogInternal(Log log, bool resolvePrefix = false, int resType = 1 /* ResultsType.Claimed */) { var scores = new List<LogFilesBandResults>(); var enabledBands = Bands.Where(x => x.Enabled).Select(x => x.Band).ToArray(); var enabledModes = Modes.Where(x => x.Enabled).Select(x => x.Mode).ToArray(); var bands = IsMultByBand ? log.Qsos.Where(x => enabledBands.Contains(x.Band)).Select(x => x.Band).Distinct().ToList() : new List<string> { null }; var modes = IsMultByMode ? log.Qsos.Where(x => enabledModes.Contains(x.Mode)).Select(x => x.Mode).Distinct().ToList() : new List<string> { null }; var multList = new List<Multiplier>(); foreach (var band in bands) foreach (var mode in modes) { var qsosQuery = log.Qsos.Select(x => x); qsosQuery = mode != null ? qsosQuery.Where(x => x.Mode == mode) : qsosQuery.Where(x => enabledModes.Contains(x.Mode)); qsosQuery = band != null ? qsosQuery.Where(x => x.Band == band) : qsosQuery.Where(x => enabledBands.Contains(x.Band)); qsosQuery = qsosQuery.OrderBy(x => x.Time); var qsos = qsosQuery.ToList(); foreach (var qso in qsos) { if ( ( qso.IsConfirmedError && resType == ResultsType.Confirmed ) || ( qso.IsClaimedError && resType == ResultsType.Claimed ) ) { qso.QsoPoints.Add(AddPoints(qso, 0, resType)); continue; } qso.QsoPoints.Add(AddPoints(qso, CalcQsoPoints(qso, log.Station), resType)); foreach (var multType in MultiplierTypes) { var mult = GetMult(qso, multType); if (mult != null) { var isMultExists = multList.Any ( x => x.Mult == mult && (x.Mode == mode || mode == null) && (x.Band == band || band == null) && x.MultType == multType ); if (!isMultExists) { var multiplier = new Multiplier { Band = band, Mode = mode, QsoId = qso.Id, MultType = multType, Mult = mult }; multList.Add(multiplier); qso.QsoMults.Add(AddMult(qso, mult, multType, resType, log.Category.Category)); } } } } } bands = log.Qsos.Where(x => enabledBands.Contains(x.Band)).Select(x => x.Band).Distinct().ToList(); modes = log.Qsos.Where(x => enabledModes.Contains(x.Mode)).Select(x => x.Mode).Distinct().ToList(); bands.Add(null); modes.Add(null); foreach (var band in bands) foreach (var mode in modes) { var qsos = log.Qsos .Where ( x => (x.Mode == mode || mode == null) && (x.Band == band || band == null) ) .ToList(); var bandId = Bands.FirstOrDefault(x => x.Band == band)?.Id ?? 0; var modeId = Modes.FirstOrDefault(x => x.Mode == mode)?.Id ?? 0; var qsosCount = resType == ResultsType.Claimed ? qsos.Count : qsos.Count(x => !x.IsConfirmedError); CurrentScoreId++; var bandModeScore = new LogFilesBandResults { Id = CurrentScoreId, Call = log.Station.Call, Category = log.Category.Category, BandId = bandId, ModeId = modeId, Qsos = qsosCount, Points = qsos.SelectMany(x => x.QsoPoints).Where(x => x.ResType == resType).Sum(x => x.Points), Type = resType }; foreach (var multType in MultiplierTypes) { var bandModeMults = new LogFilesBandResultsMults { LogFilesBandResultsId = CurrentScoreId, LogFilesBandResults = bandModeScore, Mult = qsos.Count(x => x.QsoMults.Any(y => y.MultType == multType && y.ResType == resType && y.Mult != null)), Type = multType, ModeId = modeId }; bandModeScore.Mults.Add(bandModeMults); } // if // ( // bandModeScore.BandId == 0 // && bandModeScore.ModeId == 0 // ) bandModeScore.Score = GetScore(bandModeScore.Points ?? 0, bandModeScore.Mults, bandModeScore.Qsos); scores.Add(bandModeScore); } #region Дублируем результаты для дополнительных сводных подгрупп (category = null) if (!IsUnitedSuperCategoriesExists) return scores; foreach (var score in scores.Where(x => x.BandId == 0).ToList()) { CurrentScoreId++; var newScore = new LogFilesBandResults { Id = CurrentScoreId, Call = score.Call, Category = null, BandId = score.BandId, ModeId = score.ModeId, Qsos = score.Qsos, Points = score.Points, Type = score.Type }; foreach (var mult in score.Mults) { var newMult = new LogFilesBandResultsMults { LogFilesBandResultsId = CurrentScoreId, LogFilesBandResults = newScore, Mult = mult.Mult, Type = mult.Type, ModeId = mult.ModeId }; newScore.Mults.Add(newMult); } newScore.Score = GetScore(newScore.Points ?? 0, newScore.Mults); scores.Add(newScore); } #endregion return scores; } protected virtual void PreProcessLog(Log log) { } public IEnumerable<LogFilesBandResults> ScoreLog(Log log, bool resolvePrefix, int resultsType) { if (resolvePrefix) if (log.IsSwl) ResolveSwlPrefixes(log); else ResolvePrefixes(log); PreProcessLog(log); CheckDupes(log, resultsType); if (log.IsSwl) { CrossCheckSwlLog(log); var swlLog = ScoreSwlLogInternal(log, resolvePrefix, resultsType); AdditionalCheckSwlLog(log, resultsType); return swlLog; } return ScoreLogInternal(log, resolvePrefix, resultsType); } protected virtual void AdditionalCheckSwlLog(Log log, int resType) { } private byte CheckTime(string time1, string time2) { // 0 - OK // 1 - ошибка // 2 - NIL byte checkTime; var hr1 = int.Parse(time1.Left(2)); var hr2 = int.Parse(time2.Left(2)); var min1 = int.Parse(time1.Right(2)); var min2 = int.Parse(time2.Right(2)); var dm = Math.Abs((hr2 - hr1) * 60.0 + min2 - min1); if (dm <= 3) checkTime = 0; else if ( ( hr1 != hr2 && min2 == min1 ) || dm <= 20 ) checkTime = 1; else checkTime = 2; return checkTime; } protected Bands[] Bands { get; private set; } protected Modes[] Modes { get; private set; } protected Category[] Categories { get; private set; } protected SuperCategory[] SuperCategories { get; private set; } public void PrepareResults() { using (var context = new CrossCheckDb(ConnectionString)) { PrepareResults(context); } } protected virtual IEnumerable<Entry> SplitLogFilesCategories(CrossCheckDb context) { return context.LogFilesCategory .Select ( x => new Entry { Call = x.Call, Category = x.Category, Location = x.Location, Month = x.Date == null ? null : x.Date.Value.Month } ) .Distinct() .OrderBy(x => x.Call) .ThenBy(x => x.Month) .ThenBy(x => x.Category); } public void ClearDatabase(bool makeEmpty = false) { using var context = new CrossCheckDb(ConnectionString); context.Database.CommandTimeout = 1200; var sqlCommandText = "Exec MakeBackupNew"; KeepInTouchInternal("Making database backup..."); context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); KeepInTouchInternal("Clearing database..."); sqlCommandText = @" Delete CheckAll_Table Delete _CheckAll_Table Delete __CheckAll_Table Delete NILs Delete NILs2 Delete CrossCheck Delete ErrorsStatistics Delete LogFiles_BandResults_Mults Delete LogFiles_BandResults Delete QSOs_Points Delete QSOs_Mults --If @Delete_All = 'DELETE_ALL' -- Begin Delete QSOs Delete LogFilesCategory Delete LogFiles Delete SWLs -- Delete LogFilesCategory_Replace -- Delete LogFilesAudio -- Delete SuperCategory_Callsigns -- Delete QSOs_SystemErrors -- Delete QSOs_DSQ -- Delete QSOs_BadErrors -- Delete LogFiles_ChangeCategory -- End "; context.Database.ExecuteSqlCommand(sqlCommandText); if (!makeEmpty) return; sqlCommandText = @" Delete LogFilesCategory_Replace Delete LogFilesAudio Delete SuperCategory_Callsigns Delete QSOs_SystemErrors Delete QSOs_DSQ Delete QSOs_BadErrors Delete LogFiles_ChangeCategory "; context.Database.ExecuteSqlCommand(sqlCommandText); } public void RescoreResults(bool localOnly) { using (var context = new CrossCheckDb(ConnectionString)) { context.Database.CommandTimeout = 1200; string sqlCommandText; #if !DEBUG sqlCommandText = "Exec MakeBackupNew"; KeepInTouchInternal("Making database backup..."); context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); #endif BeforeCrossCheckProcessing(context); KeepInTouchInternal("Deleting old results..."); sqlCommandText = @" Delete LogFiles_BandResults_Mults Delete LogFiles_BandResults Delete QSOs_Mults Delete QSOs_Points DBCC CHECKIDENT ('LogFiles_BandResults_Mults', RESEED, 0); DBCC CHECKIDENT ('LogFiles_BandResults', RESEED, 0); DBCC CHECKIDENT ('QSOs_Mults', RESEED, 0); DBCC CHECKIDENT ('QSOs_Points', RESEED, 0); Delete CheckAll_Table Delete _CheckAll_Table Delete __CheckAll_Table Delete NILs Delete NILs2 Delete CrossCheck Delete ErrorsStatistics"; context.Database.ExecuteSqlCommand(sqlCommandText); KeepInTouchInternal("Crosschecking..."); sqlCommandText = "Exec MakeResultNew"; context.Database.ExecuteSqlCommand(sqlCommandText); if (!localOnly) CreateLinkedServer(context); AfterCrossCheckProcessing(context); KeepInTouchInternal("Filling statistics..."); FillErrorStaistics(context); var entries = SplitLogFilesCategories(context).ToList(); var portions = entries.ChunkList(10).ToList(); var i = 0; CurrentScoreId = 0; foreach (var portion in portions) { i++; KeepInTouchInternal($"Rescoring portion {i} from {portions.Count} portions..."); try { RecalcEntryPortion(portion); } catch (Exception e) { WriteLog?.Invoke(e.GetLoggingTrace()); WriteLog?.Invoke("Callsigns: " + string.Join(", ", portion.Select(x => x.Call))); } } KeepInTouchInternal("Preparing results..."); if (ParseOperatorsLine) ParseOperators(context); PrepareResults(context); if (!localOnly && !LocalOnly) { KeepInTouchInternal("Uploading results..."); ClearRemoteDateStamp(context); ClearRemoteResults(context); UpdateRemoteDateStamp(context); UploadResults(context); } } } protected virtual void CreateLinkedServer(CrossCheckDb context) { var sqlCommandText = @"Exec CreateLinkedServer"; context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); } protected abstract void PrepareResults(CrossCheckDb context); protected virtual void ClearRemoteResults(CrossCheckDb context) { var sqlCommandText = $@" Declare @SQL Nvarchar(max) Set @SQL = 'Delete From {MySqlTableNamePrefix}Results' Execute (@SQL) at {LinkedServerName}"; context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); sqlCommandText = $@" Declare @SQL Nvarchar(max) Set @SQL = 'Alter Table {MySqlTableNamePrefix}Results AUTO_INCREMENT = 1' Execute (@SQL) at {LinkedServerName}"; context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); } protected virtual void ClearRemoteDateStamp(CrossCheckDb context) { var sqlCommandText = $@" Declare @SQL Nvarchar(max) Set @SQL = 'Delete From {MySqlTableNamePrefix}Results_DateStamp' Execute (@SQL) at {LinkedServerName}"; context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); } protected virtual void UpdateRemoteDateStamp(CrossCheckDb context) { var sqlCommandText = $@" Insert {LinkedServerName}...{MySqlTableNamePrefix}Results_DateStamp ( [Date], Logs ) Select convert(varchar(19), getdate(), 120), count(*) From LogFilesCategory_Select"; context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlCommandText); } protected abstract void UploadResults(CrossCheckDb context); protected virtual void AfterCrossCheckProcessing(CrossCheckDb context) { /* var qsos = context.QSOs.Where(x => x.IsLogRcvd == true).ToList(); qsos.ForEach ( x => { x.ErrType = "NIL"; x.ErrPar = string.Empty; } ); qsos = context.QSOs.Where(x => x.IsLogRcvd == false && (x.ErrType == null || x.ErrType == string.Empty)).ToList(); qsos.ForEach ( x => { x.ErrType = "nl"; x.ErrPar = "log is not received"; } ); var catsQuery = from cat in context.LogFilesCategory join newCat in context.LogFilesChangeCategory on new {cat.Call, cat.Category} equals new {newCat.Call, newCat.Category} select new { Item = cat, newCat.NewCategory }; var cats = catsQuery.ToList(); cats.ForEach(x => x.Item.Category = x.NewCategory); context.SaveChanges(); */ var sqlCommandText = @" Update QSOs Set ErrType = 'NIL', ErrPar = '' Where IsLogRcvd = 1 And isnull(ErrType, '') = '' Update QSOs Set ErrType = 'nl', ErrPar = 'log is not received' Where IsLogRcvd = 0 And isnull(ErrType, '') = '' Update LogFilesCategory Set LogFilesCategory.Category = LogFiles_ChangeCategory.Category_new From LogFilesCategory Join LogFiles_ChangeCategory On LogFiles_ChangeCategory.Call = LogFilesCategory.Call And LogFiles_ChangeCategory.Category = LogFilesCategory.Category "; context.Database.ExecuteSqlCommand(sqlCommandText); } protected virtual void BeforeCrossCheckProcessing(CrossCheckDb context) { } private void FillErrorStaistics(CrossCheckDb context) { var sqlCommandText = @" Insert Into ErrorsStatistics ( Call, ErrType, Errors ) Select QSOs.Call1, QSOs.ErrType, round(cast(count(*) As Float) / cast(QSOCount.QSOCount As Float) * 100, 2) From QSOs Join ( Select Call1, count(*) QSOCount From QSOs Group By Call1 ) QSOCount On QSOCount.Call1 = QSOs.Call1 Where QSOs.ErrType <> '+' Group By QSOs.Call1, QSOCount.QSOCount, QSOs.ErrType"; context.Database.ExecuteSqlCommand(sqlCommandText); } protected virtual LogFilesCategory GetLogFilesCategoryByEntry(IEnumerable<LogFilesCategory> logFilesCategories, Entry entry) { return logFilesCategories.FirstOrDefault ( x => x.Call == entry.Call && x.Category == entry.Category && x.Location == entry.Location && x.Date?.Month == entry.Month ); } protected virtual Category GetCategoryDescription(LogFilesCategory logFilesCategory) { return Categories.FirstOrDefault(x => x.CategoryName == logFilesCategory?.Category); } private void RecalcEntryPortion(IEnumerable<Entry> portion) { var portionList = portion.ToList(); var calls = portionList.Select(x => x.Call).Distinct().ToList(); using (var context = new CrossCheckDb(ConnectionString)) { context.Configuration.AutoDetectChangesEnabled = false; context.Configuration.ValidateOnSaveEnabled = false; var logFiles = context.LogFiles.Where(x => calls.Contains(x.Call)).ToList(); var logFilesCategories = context.LogFilesCategory.Where(x => calls.Contains(x.Call)).ToList(); var qsos = context.QSOs.Where(x => calls.Contains(x.Call1)).ToList(); var swls = context.SWLs.Where(x => calls.Contains(x.SwlCall)).ToList(); var resTypes = new[] { ResultsType.Claimed, ResultsType.Confirmed }; var qsoPoints = new List<QsosPoints>(); var qsoMults = new List<QsosMults>(); // var logFilesBandResults = new List<LogFilesBandResults>(); // var logFilesBandResultsMults = new List<LogFilesBandResultsMults>(); foreach (var entry in portionList) { var station = logFiles.First(x => x.Call == entry.Call); var category = GetLogFilesCategoryByEntry(logFilesCategories, entry); var categoryDescription = GetCategoryDescription(category); if (categoryDescription == null) continue; var tour = categoryDescription.Month; var band = Bands.FirstOrDefault(x => x.Id == categoryDescription.Band)?.Band; var logQsosQuery = qsos.Where(x => x.Call1 == entry.Call); var logSwlsQuery = swls.Where(x => x.SwlCall == entry.Call); if (tour != null) logQsosQuery = GetTourQsoQuery(logQsosQuery.AsQueryable(), tour.Value).AsEnumerable(); if (band != null) logQsosQuery = logQsosQuery.Where(x => x.Band == band); var logQsos = logQsosQuery.ToList(); var logSwls = logSwlsQuery.ToList(); var log = new Log { Station = station, Category = category, Qsos = logQsos, Swls = logSwls }; foreach (var resType in resTypes) { var scores = ScoreLog(log, resType == ResultsType.Claimed, resType); foreach (var score in scores) { context.LogFilesBandResults.Add(score); context.LogFilesBandResultsMults.AddRange(score.Mults); // logFilesBandResults.Add(score); // logFilesBandResultsMults.AddRange(score.Mults); } } if (log.Station.IsModified) context.Entry(log.Station).State = EntityState.Modified; foreach (var qso in log.Qsos.Where(x => x.IsModified)) context.Entry(qso).State = EntityState.Modified; foreach (var swl in log.Swls.Where(x => x.IsModified)) context.Entry(swl).State = EntityState.Modified; // ReSharper disable EntityFramework.NPlusOne.Usage qsoPoints.AddRange(log.Qsos.SelectMany(x => x.QsoPoints)); qsoMults.AddRange(log.Qsos.SelectMany(x => x.QsoMults)); // ReSharper restore EntityFramework.NPlusOne.Usage // foreach (var item in log.Qsos.SelectMany(x => x.QsoPoints)) // context.QSOsPoints.Add(item); // foreach (var item in log.Qsos.SelectMany(x => x.QsoMults)) // context.QSOsMults.Add(item); } context.SaveChanges(); // return; #region Bulk copy результатов в БД var qsoPointsTable = qsoPoints.ToDataTable(context.GetTableName(typeof(QsosPoints))); var qsoMultsTable = qsoMults.ToDataTable(context.GetTableName(typeof(QsosMults))); // var logFilesBandResultsTable = logFilesBandResults.ToDataTable(); // var logFilesBandResultsMultsTable = logFilesBandResultsMults.ToDataTable(); using (var destinationConnection = new SqlConnection(ConnectionString)) { destinationConnection.Open(); using (var transaction = destinationConnection.BeginTransaction()) { try { // using (var bulkCopy2 = new SqlBulkCopy(destinationConnection, SqlBulkCopyOptions.KeepIdentity, transaction)) // { // bulkCopy2.BulkCopyTimeout = 600; // // bulkCopy2.DestinationTableName = logFilesBandResultsTable.TableName; // bulkCopy2.WriteToServer(logFilesBandResultsTable); // } using (var bulkCopy1 = new SqlBulkCopy(destinationConnection, SqlBulkCopyOptions.CheckConstraints, transaction)) { bulkCopy1.BulkCopyTimeout = 600; bulkCopy1.DestinationTableName = qsoPointsTable.TableName; bulkCopy1.WriteToServer(qsoPointsTable); bulkCopy1.DestinationTableName = qsoMultsTable.TableName; bulkCopy1.WriteToServer(qsoMultsTable); // bulkCopy1.DestinationTableName = logFilesBandResultsMultsTable.TableName; // bulkCopy1.WriteToServer(logFilesBandResultsMultsTable); } transaction.Commit(); } catch (Exception) { transaction.Commit(); throw; } } } #endregion } } protected class Entry { public string Call { get; set; } public string Category { get; set; } public string Location { get; set; } public int? Month { get; set; } } public virtual bool SaveLog(Log log, string category, string band, DateTime? tourDate, out string errorMessage) { errorMessage = null; using (var context = new CrossCheckDb(ConnectionString)) { if (context.LogFiles.Any(x => x.Call == log.Station.Call)) { if (!DeleteLog(log.Station.Call, category, band, tourDate, out errorMessage)) return false; } context.LogFiles.Add(log.Station); context.LogFilesCategory.Add(log.Category); context.QSOs.AddRange(log.Qsos); context.SWLs.AddRange(log.Swls); context.QSOsPoints.AddRange(log.Qsos.SelectMany(x => x.QsoPoints)); context.QSOsMults.AddRange(log.Qsos.SelectMany(x => x.QsoMults)); using (var tran = context.Database.BeginTransaction()) { try { context.SaveChanges(); tran.Commit(); return true; } catch (Exception ex) { tran.Rollback(); errorMessage = ex.GetLoggingTrace(); return false; } } } } protected virtual IQueryable<Qsos> GetTourQsoQuery(IQueryable<Qsos> qsoQuery, int tour) { return qsoQuery.Where(x => x.Date != null && x.Date.Value.Month == tour); } protected virtual IQueryable<LogFilesCategory> GetTourCategoryQuery(IQueryable<LogFilesCategory> categoriesQuery, int tour) { return categoriesQuery.Where(x => x.Date != null && x.Date.Value.Month == tour); } protected virtual int? GetTourByDate(DateTime? tourDate) { return tourDate?.Month; } public bool DeleteLog(string callsign, string category, string band, DateTime? tourDate, out string errorMessage) { errorMessage = null; using var context = new CrossCheckDb(ConnectionString); #region Обработка переданных параметров band ??= string.Empty; category ??= string.Empty; var tour = GetTourByDate(tourDate) ?? 0; // подгонка категории под SP LO if (category.Length > 6 && category.Substring(0, 6) != "SP LO ") { var spLoCategory = context.LogFilesCategory.FirstOrDefault(x => x.Call == callsign && x.Category.StartsWith("SP LO "))?.Category; if (spLoCategory != null) category = spLoCategory; } var onlyThisCategory = tour != 0; #endregion #region QSOs var qsosQuery = context.QSOs.Where(x => x.Call1 == callsign); if (band != string.Empty) qsosQuery = qsosQuery.Where(x => x.Band == band); if (!IgnoreQsoMonth && tour != 0) qsosQuery = GetTourQsoQuery(qsosQuery, tour); if (onlyThisCategory) qsosQuery = from qso in qsosQuery join bnd in context.Bands on qso.Band equals bnd.Band join cb in context.CategoryBands on bnd.Id equals cb.Band join cat in context.Category on cb.CategoryId equals cat.Id where cat.CategoryName == category select qso; var qsos = qsosQuery.Include(x => x.QsoPoints).Include(x => x.QsoMults).ToArray(); var swls = context.SWLs.Where(x => x.SwlCall == callsign).ToArray(); var mults = qsos.SelectMany(x => x.QsoMults).ToArray(); var points = qsos.SelectMany(x => x.QsoPoints).ToArray(); #endregion #region LogFilesСategory var categoriesQuery = context.LogFilesCategory.Where(x => x.Call == callsign); if (band != string.Empty) categoriesQuery = categoriesQuery.Where(x => x.Band == band || x.Band == null); if (!IgnoreQsoMonth && tour != 0) categoriesQuery = GetTourCategoryQuery(categoriesQuery, tour); if (onlyThisCategory) categoriesQuery = categoriesQuery.Where(x => x.Category == category); var categories = categoriesQuery.ToArray(); #endregion #region Results var resultsQuery = context.LogFilesBandResults.Where(x => x.Call == callsign); if (onlyThisCategory) resultsQuery = resultsQuery.Where(x => x.Category == category); var results = resultsQuery.Include(x => x.Mults).ToArray(); var resMults = results.SelectMany(x => x.Mults).ToArray(); #endregion #region LogFiles var log = context.LogFiles.FirstOrDefault(x => x.Call == callsign); #endregion #region Удаление в транзакции using (var tran = context.Database.BeginTransaction()) { try { context.QSOsMults.RemoveRange(mults); context.QSOsPoints.RemoveRange(points); context.QSOs.RemoveRange(qsos); context.SWLs.RemoveRange(swls); context.SaveChanges(); context.LogFilesBandResultsMults.RemoveRange(resMults); context.LogFilesBandResults.RemoveRange(results); context.LogFilesCategory.RemoveRange(categories); context.SaveChanges(); if (!context.LogFilesCategory.Any(x => x.Call == callsign) && log != null) { context.LogFiles.Remove(log); context.SaveChanges(); } tran.Commit(); return true; } catch (Exception ex) { tran.Rollback(); errorMessage = ex.GetLoggingTrace(); return false; } } #endregion } public DateTime? GetLogFileInfo(string fileName) { using (var context = new CrossCheckDb(ConnectionString)) { return context.LogFilesCategory.Where(x => x.FileName == fileName).Select(x => x.DateCreate).FirstOrDefault(); } } protected sealed class OperatorsParsingItem { public string Call { get; set; } public string Operators { get; set; } public string Category { get; set; } public string CountryId { get; set; } public string RegionId { get; set; } public string Name { get; set; } } protected virtual void ParseOperators(CrossCheckDb context) { var resultsHelper = new ResultsQueryHelper(context); var items = ( from app in resultsHelper.GetAllApplications() join log in context.LogFiles on app.Call equals log.Call select new OperatorsParsingItem { Call = log.Call, Operators = log.Operators, Category = app.Category, CountryId = log.CountryId, RegionId = log.TeamRegionId ?? log.RegionId, Name = log.Name } ) .ToList() .Where(x => x.Call.IsRussianCallsign() || x.CountryId.IsRussianPrefix() || !x.RegionId.IsNullOrEmpty()) .ToList(); var operators = items.SelectMany(ParseOperatorsItem).ToList(); operators = operators .GroupBy ( x => new { x.Радиостанция, x.Позывной, x.Фамилия, x.Имя, x.Отчество, } ) .Select(x => x.First()) .ToList(); operators .Where(x => !x.Подгруппа.Contains("MULTI-OP")) .ToList() .ForEach ( x => { if (x.Позывной == x.Радиостанция) x.Позывной = string.Empty; } ); var operators2 = operators.GroupBy(x => x.Радиостанция).Select(x => CreateOperators2Item(x.ToArray())); using (var saveContext = new CrossCheckDb(context.Database.Connection.ConnectionString)) using (var transaction = saveContext.Database.BeginTransaction()) { try { saveContext.Operators.ClearTable(context); saveContext.Operators.ReseedTable(context); saveContext.Operators2.ClearTable(context); saveContext.Operators2.ReseedTable(context); saveContext.Operators.AddRange(operators); saveContext.Operators2.AddRange(operators2); saveContext.SaveChanges(); transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); KeepInTouchInternal(ex.GetLoggingTrace()); } } } protected abstract IEnumerable<Operators> ParseOperatorsItem(OperatorsParsingItem item); protected IEnumerable<Operators> ParseOperatorsItemEdi(OperatorsParsingItem item) { var ops = (item.Operators ?? string.Empty).Replace("\r", "").Trim().Split((char)10).Distinct().ToArray(); if (ops.Length == 0) yield break; var region = PrefixResolver.OblastEntities.FirstOrDefault(x => x.Oblast == item.RegionId)?.Name; foreach (var op in ops) { var parseOp = op.Replace("разряд", "").Replace(";", ",").Replace(" ,", ", ").Replace(" ", ", ").Replace(",, ", ",").Replace(" ", " "); var opDataArray = parseOp.Split(','); var len = opDataArray.Length; var годРожденияString = len > 5 ? opDataArray[5].Trim().Left(10) : string.Empty; var датаРожденияString = len > 5 ? opDataArray[5].Trim().Left(10) : string.Empty; if (int.TryParse(годРожденияString, out int годРождения) && годРождения < 100) { годРожденияString = (StartTime.Year - годРождения).ToString("D"); } DateTime? датаРождения = null; if (DateTime.TryParseExact(датаРожденияString, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var датаРожденияParsed)) { датаРождения = датаРожденияParsed; } var opItem = new Operators { Радиостанция = item.Call, Подгруппа = item.Category, Регион = region, Фамилия = len > 1 ? opDataArray[1].GetRussainNameExceptCallsign().Left(50) : string.Empty, Имя = len > 2 ? opDataArray[2].GetRussainNameExceptCallsign().Left(50) : string.Empty, Отчество = len > 3 ? opDataArray[3].GetRussainNameExceptCallsign().Left(50) : string.Empty, ГодРождения = годРожденияString, ДатаРождения = датаРождения, Разряд = len > 4 ? opDataArray[4].NormalizeRank().Left(5) : string.Empty, Позывной = opDataArray[0].Trim().ToUpper().Left(15), Категория = null, // Тренер = len > 6 && !opDataArray[6].IsNullOrEmpty() ? "Тренер" : string.Empty, Тренер = len > 6 && opDataArray[6].Trim().ToLower() == "тренер" ? "Тренер" : len > 6 && opDataArray[6].Trim().ToLower() == "техник" ? "Техник" : string.Empty }; if ( opItem.Позывной == "?" || opItem.Позывной == "-" || opItem.Позывной.ToLower() == "нет" || opItem.Позывной.Length < 3 || !Regex.IsMatch(opItem.Позывной, @"^[A-Z0-9]+$") ) opItem.Позывной = string.Empty; yield return opItem; } } protected IEnumerable<Operators> ParseOperatorsItemErmak(OperatorsParsingItem item) { var region = PrefixResolver.OblastEntities.FirstOrDefault(x => x.Oblast == item.RegionId)?.Name; if ( !item.Operators.IsNullOrEmpty() && !item.Operators.Contains((char)10) && item.Operators.All(x => x.IsLatinLetter() || x == ' ' || char.IsDigit(x)) ) { var callsigns = item.Operators.Split(' ').Select(x => x.ToUpper()).ToArray(); foreach (var callsign in callsigns) { yield return new Operators { Позывной = callsign, Радиостанция = item.Call, Подгруппа = item.Category, Регион = region, }; } } var ops = item.Operators?.Split((char)10); if (ops == null || ops.Length == 0) yield break; foreach (var op in ops) { var parseOp = op.Replace(", ", ", ").Replace(" ,", ",").Replace(" ", ", ").Replace(",, ", ",").Replace(" ", ""); var opDataArray = parseOp.Split(','); var len = opDataArray.Length; if (len < 1) yield return new Operators { Радиостанция = item.Call, Подгруппа = item.Category, Регион = region, }; var lastName = GetLastName(opDataArray); if (lastName.IsNullOrEmpty()) yield return new Operators { Радиостанция = item.Call, Подгруппа = item.Category, Регион = region, }; var годРожденияString = len > 3 ? opDataArray[3].Trim().Left(10) : string.Empty; var датаРожденияString = len > 3 ? opDataArray[3].Trim().Left(10) : string.Empty; if (int.TryParse(годРожденияString, out int годРождения) && годРождения < 100) { годРожденияString = (StartTime.Year - годРождения).ToString("D"); } DateTime? датаРождения = null; if (DateTime.TryParseExact(датаРожденияString, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out var датаРожденияParsed)) { датаРождения = датаРожденияParsed; } if (датаРождения < new DateTime(1753, 1, 1)) датаРождения = null; var opItem = new Operators { Радиостанция = item.Call, Подгруппа = item.Category, Регион = region, Фамилия = opDataArray[0].GetRussainNameExceptCallsign().Left(50), Имя = len > 1 ? opDataArray[1].GetRussainNameExceptCallsign().Left(50) : string.Empty, Отчество = len > 2 ? opDataArray[2].GetRussainNameExceptCallsign().Left(50) : string.Empty, ГодРождения = годРожденияString, ДатаРождения = датаРождения, Разряд = len > 4 ? opDataArray[4].NormalizeRank().Left(5) : string.Empty, Позывной = len > 5 ? opDataArray[5].Trim().ToUpper().Left(15) : string.Empty, Категория = len > 6 ? opDataArray[6].Trim().Left(5) : string.Empty, Тренер = len > 7 && opDataArray[7].Trim().ToLower() == "тренер" ? "Тренер" : string.Empty }; if ( opItem.Позывной == "?" || opItem.Позывной == "-" || opItem.Позывной.ToLower() == "нет" || opItem.Позывной.Length < 3 || !Regex.IsMatch(opItem.Позывной, @"^[A-Z0-9]+$") ) opItem.Позывной = string.Empty; yield return opItem; } } private string GetLastName(string[] opDataArray) { var len = opDataArray.Length; if (len == 1 && Regex.IsMatch(opDataArray[0], @"^[A-Z0-9]+$")) return null; if (len == 2 && opDataArray[0].ToLower() == "ex" && Regex.IsMatch(opDataArray[1], @"^[A-Z0-9]+$")) return null; return opDataArray[0].GetRussainNameExceptCallsign().Left(50); } protected Operators2 CreateOperators2Item(IEnumerable<Operators> items) { if (items == null) return null; var itemsArr = items.ToArray(); var itemsArray = itemsArr .Select ( x => new { x.Радиостанция, x.Регион, x.Фамилия, x.Имя, x.Отчество, x.Позывной, x.ГодРождения, ДатаРождения = x.ДатаРождения.HasValue ? $"{x.ДатаРождения:dd.MM.yyyy}" : "", x.Разряд, x.Тренер } ) .Where ( x => !x.Фамилия.IsNullOrEmpty() && !x.Имя.IsNullOrEmpty() ) .ToArray(); if (itemsArray.Length == 0) itemsArray = itemsArr .Select ( x => new { x.Радиостанция, x.Регион, x.Фамилия, x.Имя, x.Отчество, x.Позывной, x.ГодРождения, ДатаРождения = x.ДатаРождения.HasValue ? $"{x.ДатаРождения:dd.MM.yyyy}" : x.ГодРождения, x.Разряд, x.Тренер } ) .ToArray(); if (itemsArray.Length == 0) return null; var регион = itemsArray.First()?.Регион; var радиостанция = itemsArray.First()?.Радиостанция; var фио = itemsArray .Select ( x => { var тренер = x.Тренер == "Тренер" ? "Тренер: " : x.Тренер == "Техник" ? "Техник: " : string.Empty; return ( x.Позывной.IsNullOrEmpty() || x.Позывной == радиостанция && itemsArray.Length == 1 ? $"{тренер}{x.Фамилия} {x.Имя} {x.Отчество}" : $"{тренер}{x.Фамилия} {x.Имя} {x.Отчество} ({x.Позывной})" ) .Trim(); } ); var ифп = itemsArray .Select ( x => { var тренер = x.Тренер == "Тренер" ? "Тренер: " : string.Empty; return ( x.Позывной.IsNullOrEmpty() || x.Позывной == радиостанция && itemsArray.Length == 1 ? $"{тренер}{x.Имя} {x.Фамилия}" : $"{тренер}{x.Имя} {x.Фамилия} ({x.Позывной})" ) .Trim(); } ); var ops2 = new Operators2 { Фио = string.Join("<br />", фио), Разряд = string.Join("<br />", itemsArray.Select(x => $"{x.Разряд}")), ГодРождения = string.Join("<br />", itemsArray.Select(x => $"{x.ГодРождения}")), ДатаРождения = string.Join("<br />", itemsArray.Select(x => $"{x.ДатаРождения}")), Ифп = string.Join(", ", ифп), Регион = регион, Радиостанция = радиостанция }; return ops2; } } }