private void Email_report_Daily_TER_avg_permonth(object obj) { var timer_data = (Timer_data)obj; using (var scope = scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService <ScaffoldContext>(); DateTime startDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).AddDays(-1); DateTime endDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).AddSeconds(-1); var reportName = new ReportName("TERperMonthperTonne;Средневзвешенное ТЭР на тонну по циклам производства"); var reportDates = new ReportDates(startDate, endDate, tesYear: DateTime.Now.Year); var reportInfo = new ReportInfo(); reportInfo.EvalReportInfo(reportName, dbContext, reportDates ); var excel_file = Excel_methods.Export_to_Excel_TERperMonthperTonne(reportInfo); string serverUrl = "http://192.168.0.147:8080/"; string msg = $@"<html><body><h2>Здравствуйте. Отчет средневзвешенное ТЭР на тонну по циклам производства во вложении.</h2> <h4>Версию с графиками можно посмотреть и выгрузить здесь: <a href= ""{serverUrl}Reports/ShowReport?lastDay=1&report_name=TERperMonthperTonne%3B%D0%A1%D1%80%D0%B5%D0%B4%D0%BD%D0%B5%D0%B2%D0%B7%D0%B2%D0%B5%D1%88%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5+%D0%A2%D0%AD%D0%A0+%D0%BD%D0%B0+%D1%82%D0%BE%D0%BD%D0%BD%D1%83+%D0%BF%D0%BE+%D1%86%D0%B8%D0%BA%D0%BB%D0%B0%D0%BC+%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%B0"">Дневной отчет средневзвешенное ТЭР на тонну по циклам производства</a><h4></body></html>"; try { StaticMethods.SendEmailReport(ref SentMsges_daily_TER_avg_permonth , StaticMethods.Get_email_receivers(dbContext, "Daily_TER_avg_permonth") //new List<string> {"*****@*****.**"} //debug , "Средневзвешенное ТЭР на тонну по циклам производства" , msg , attachment_data: excel_file.FileContents); } catch (Exception e) { CustomExceptionHandler(e); } } }
public void EvalReportInfo(ReportName reportName, ScaffoldContext context, ReportDates reportDates, int currShiftIndex=-1, int export_excel = 0, string selectedSortOfProduction=null, string[] baseStrings=null,//хранится выгрузки в base64 рисунки графиков Controller curr_controller = null ) { //В некоторых случаях, вроде экспорта в эксель, можно использовать уже вычисленные данные if ( #region IsNeedCachedDateForReport (this.Successfull &&(export_excel==1 &&!ReportListForRebuildForExcel.Contains(reportName.Eng))//Некоторые отчёты требуют не округлённые данные для экселя, так что формируем их заново || shiftsReportNames.Contains(reportName.Eng))//Или для смен && this.ReportName != null && reportName.Eng == this.ReportName.Eng && this.ReportDates.StartDate == reportDates.StartDate && this.ReportDates.EndDate == reportDates.EndDate #endregion ) { //Данные по сменам формируются из списка смен в периоде, но выводится только одна, так что обновляем эти данные if (shiftsReportNames.Contains(reportName.Eng)) { this.CurrShiftIndex = currShiftIndex; ReportDates.RecalcShiftsDate(this);//Пересчитываем края текущей смены } if (baseStrings != null) { this.BaseStrings = baseStrings; } this.SelectedSortOfProduction = selectedSortOfProduction; return; } #region init var this.Successfull = false; this.stopwatch.Restart(); this.CurrShiftIndex = currShiftIndex; this.ReportDates = reportDates; this.SelectedSortOfProduction = selectedSortOfProduction; this.BaseStrings = baseStrings; this.ReportName = reportName; this.Dbcontext = context; this.Curr_controller = curr_controller; this.IsByDay = IsByDay_report_list.Contains(reportName.Eng); isBDM1 = BDM1_reports_list.Contains(reportName.Eng); this.PlaceName = isBDM1 ? "БДМ-1" : "БДМ-2"; placeId = isBDM1 ? 1:2; this.roundNum = 3; this.Sums = new Dictionary<string, double[]>(); this.Records_List = new Dictionary<string, List<object>>(); this.Records = new List<object>(); #endregion switch (reportName.Eng)//на основе названия отчёта дополнительные операции над записями { case "ConsumptionByCycleByBDM1"://БДМ-1 по видам продукции case "ConsumptionByCycleByBDM2"://БДМ-2 по видам продукции case "ConsumptionByBDM1ByDay"://БДМ-1(Суточный) case "ConsumptionByBDM2ByDay"://БДМ-2(Суточный) //делаем итого для нужных категорий //double[] sums = StaticMethods.Generate_sums(RecordsToShow_ConsumptionByCycleByBDM); Records = Form_records<ConsumptionByCycleOfProduction>("ConsumptionByCycle").Select(r=>(object)r).ToList(); var t_sum = Sums_CycleOfProduction(Records.Select(r => (ISums_CycleOfProduction)r).ToList()); if (t_sum!=null) { Sums.Add("ConsumptionByCycleByBDM", t_sum); } break; case "TERperMonthperTonne"://Средневзвешенное ТЭР на тонну по циклам производства //В данном отчете ограничение по датам используется для ограничения SKU по которым выводить годовой отчет, год передается отдельно Records_List = new Dictionary<string, List<object>>(); Records_List.Add("TERperMonthperTonne",Form_records<TERperMonthperTonne>("TERperMonthperTonne").Select(r => (object)r).ToList()); Records_List.Add("TERperCycleperTonne", Form_records<TERperCycleperTonne>("TERperCycleperTonne").Select(r => (object)r).ToList()); Records_List.Add("TER_Plan", Form_records<TERperMonthperTonne>("TER_Plan").Select(r => (object)r).ToList()); break; case "EnergyConsumptionByManufactureByHour"://Часовой расход электроэнергии по производству #region case "EnergyConsumptionByManufactureByHour" //Задача для каждого поля для указного для подсчёта, посчитать данные в разрезе списка индификаторов в Constants.places //Довольно интересный алгоритм, позволяющий делать группировку по динамическому Id закреплённому за местами Constants.places // причем за одно место может быть закреленно несколько ID //Получаем в результате массив double, где индекс массива это индекс места в Constants.places // А сам массив содержит информацию в разрезе часа, по всем местам. var precompute_data = Form_records<EnergyConsumptionByManufactureByHour>(); if (precompute_data == null || precompute_data.Count == 0) { break; } //Собираем id по которым будем считать итоговую сумму по производству(колонка итого:) List<int> total_sums_place_ids_EnergyConsumptionByManufactureByHour = new List<int>(); Constants.places_total.Values.ToList() .ForEach(r => { total_sums_place_ids_EnergyConsumptionByManufactureByHour.AddRange(r); } ); ////строка итого, храним Dictionary<string, double> для поддержки и других видов ТЭР, газа, пара и т.д. //List<Dictionary<string, double>> sums_EnergyConsumptionByManufactureByHour = new List<Dictionary<string, double>>(); //подсчитываем данные по местам //var data_EnergyConsumptionByManufactureByHour; //= new Dictionary<string, List<double>>(); var field_name_EnergyConsumptionByManufactureByHour = "EnergyConsumption"; Records= precompute_data .Select(r => ((IHaveMeasureDate)r).Measure_Date) .Distinct() .Select(Measure_Date => { var values = SumField_byPlaceId(field_name_EnergyConsumptionByManufactureByHour, precompute_data.Where(r => ((IHaveMeasureDate)r).Measure_Date == Measure_Date).Select(r=>(EnergyConsumptionByManufactureByHour)r) .ToList(), roundNum, total_sums_place_ids_EnergyConsumptionByManufactureByHour); return new Data_EnergyConsumptionByManufactureByHour { Date = Measure_Date.ToShortDateString(), Time = Measure_Date.ToShortTimeString(), Values = values//массив из double с показаниями за текущий слайс на время и дату }; }).ToList<object>(); //Добавляем строковые итоги //Нужно пробежатся по срезам по часам и посчитать итоговые данные по каждому месту //напомню что индекс в массиве data это индекс места массиве Constants.places_total var sums_row_EnergyConsumptionByManufactureByHour = new double[places.Count + 1];//разменость +1 потому что ещё считаем последную колонку итого var param_values = typeof(Data_EnergyConsumptionByManufactureByHour).GetProperty("Values"); foreach (var data in Records) { var curr_data = (List<double>)param_values.GetValue(data); for (int i = 0; i < curr_data.Count; i++) { sums_row_EnergyConsumptionByManufactureByHour[i] += curr_data[i]; } } Sums.Add("row_EnergyConsumptionByManufactureByHour", sums_row_EnergyConsumptionByManufactureByHour); break; #endregion case "EnergyConsumptionByDevicesByDay"://Суточный расход электроэнергии по учётам case "EnergyConsumptionByDevicesByHour"://Часовой расход электроэнергии по учётам #region EnergyConsumptionByDevicesBy Stopwatch tempWatch = new Stopwatch(); tempWatch.Start(); //отличия только в группировках между этими отчетами, так что формируем признак //EnergyConsumptionByDevices //Собираем записи для формирования вывода //Т.к список устройств динамический, то под него нельзя создать класс, так что делаем группировки по ходу Records = Form_records<EnergyConsumptionByDevices>("EnergyConsumptionByDevicesByDay").Select(r => (object)r).ToList(); tempWatch.Stop(); //Для строки Итого: Sums.Add("EnergyConsumptionByDevices", Records .Select(r=>(EnergyConsumptionByDevices)r) .GroupBy(r => r.Device_Name, (g, r) => Math.Round(r.Sum(e => e.EnergyConsumption ?? 0), 3)) .Append(0) .ToArray()); //Для колонки Итого по производству: Sums.Add("EnergyConsumptionByDevices_col_production", Records .Select(r => (EnergyConsumptionByDevices)r) .Where(r => Constants.EnergyConsumptionByDevices_total_devices.ContainsKey(r.Device_Name))//итого для строк считаем только для определенных колонок .GroupBy(r => r.Measure_Date, (g, rs) => rs.Sum(r => r.EnergyConsumption) ?? 0).ToArray()); //Добавляем сумму по производству Sums["EnergyConsumptionByDevices"][Sums["EnergyConsumptionByDevices"].Length-1] = (Sums["EnergyConsumptionByDevices_col_production"].Sum(r => r)); break; #endregion case "ConsumptionByManufactureByPeriod"://общая по производству #region "ConsumptionByManufactureByPeriod" Records = Form_records<ConsumptionByManufactureByPeriod>().Select(r=>(object)r).ToList(); //Подготавливаем лист id мест по которым будем считать данные для итогов List<int> total_sums_place_ids = new List<int>(); Constants.places_total.Values.ToList() .ForEach(r => { total_sums_place_ids.AddRange(r); } ); //Задача для каждого поля для указного для подсчёта, //посчитать данные в разрезе списка индификаторов в Constants.places var fInfo_ConsumptionByManufactureByPeriod = typeof(ConsumptionByManufactureByPeriod).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); Records_List = new Dictionary<string, List<object>>(Constants.fields_to_sum_ConsumptionByManufactureByPeriod.Count); foreach (var field_name in Constants.fields_to_sum_ConsumptionByManufactureByPeriod) { //подсчитываем данные по местам Records_List.Add(field_name, SumField_byPlaceId( field_name, Records .Select(r=>(ConsumptionByManufactureByPeriod)r) .ToList(), roundNum, total_sums_place_ids) .Select(r=>(object)r) .ToList()); } break; #endregion case "ConsumptionByBDM1ByHour"://БДМ-1(Часовой) case "ConsumptionByBDM2ByHour"://БДМ-2(Часовой) #region ConsumptionByBDMByHour Records = Form_records<ConsumptionByBDMbyHour>("ConsumptionByBDMByHour").Select(r=>(object)r).ToList(); if (Records==null||Records.Count==0) { break; } //Из-за того что для вывода по часового мы берем сетку по часам, //а циклы могот заканчиватся и начинатся внутри часа, то на эти цикла происходит дублирование по параметрам, //так что пробегаемся по записям и ищем такие случаи, и объединяем их в одну запись //берем элементы по count-2 потому что сравниваем два элемента и последний не счем сравнивать var temp_recs = new List<ConsumptionByBDMbyHour>(); for (int i = 0; i < Records.Count - 1; i++) { var curr = (IHaveComposition)Records.ElementAt(i); var next = (IHaveComposition)Records.ElementAt(i + 1); var excludeProp = new Dictionary<string, bool>() {["ID"]=false,["Composition"]=false}; if (IsEqualPropValue(firstObject: curr, secondObject: next, excludePropList: excludeProp)) { curr.Composition += ", " + next.Composition; i++; } temp_recs.Add((ConsumptionByBDMbyHour)curr); } Records = temp_recs.Select(r=>(object)r).ToList(); //Для строки Итого: Sums.Add("ConsumptionByBDMByHour", value: //чтобы получить итого по записям, выделяем нужные столбцы и добаляем столбец по кторому будем вести группировку GetSumsAndAvgProp( Records.Select(r => (ConsumptionByBDMbyHour)r).ToList(), propForSumList: tesSumList.ToDictionary(r => r, r => false), propForAvgList: tesAvgList.ToDictionary(r => r, r => false)) .Select(d => (double)(d??0.0)).ToArray());//распаковываем в дабл break; #endregion case "SkuDataByShifts_BDM1": case "SkuDataByShifts_BDM2": #region SkuDataByShifts var ter_list_shifts = Constants.ter_list_shifts; //нарезаем указный период на смены var shifts_list = new List<Shift>(); int shift_index = 0; reportDates.CurrShiftStart = reportDates.CurrShiftStart.AddHours(-reportDates.ShiftLength); reportDates.CurrShiftEnd = reportDates.CurrShiftEnd.AddHours(-reportDates.ShiftLength); while (true) { if (reportDates.CurrShiftStart > DateTime.Now || reportDates.CurrShiftStart > reportDates.EndDate)//поледняя смена не должна выходить за текущюю дату или конец преиода { break; } else { shifts_list.Add(new Shift() { shift_start = reportDates.CurrShiftStart, shift_end = reportDates.CurrShiftEnd, shift_index = shift_index }); } reportDates.CurrShiftStart = reportDates.CurrShiftStart.AddHours(reportDates.ShiftLength); reportDates.CurrShiftEnd = reportDates.CurrShiftEnd.AddHours(reportDates.ShiftLength); shift_index++; } var shiftStart = DateTime.Now; var shiftEnd = DateTime.Now; //если смена не выбрана(-1) то выводим последнию смену в нарезке, иначе выбраную смену if (shifts_list.Count > 0) { shiftStart = shifts_list[GetCurShiftIndex(currShiftIndex, shifts_list.Count)].shift_start; shiftEnd = shifts_list[GetCurShiftIndex(currShiftIndex, shifts_list.Count)].shift_end; } else { shiftStart = reportDates.CurrShiftStart; shiftEnd = reportDates.CurrShiftEnd; } reportDates.CurrShiftStart= shiftStart; reportDates.CurrShiftEnd = shiftEnd; Records_List.Add("SkuDataByShifts", Form_records<SkuDataByShifts>("SkuDataByShifts") .OrderBy(r => r.CycleDateBegin) .Select(r=>(object)r) .ToList()); var sku_list = Records_List["SkuDataByShifts"] .Select(r => ((IHaveSku)r).Sku) .ToList(); Records_List.Add("Shifts_plan", context.StgPlanTer .Where(r => r.Year == shiftStart.Year && r.Month == shiftStart.Month && sku_list.Contains(r.Sku) && r.PlaceId == (isBDM1 ? 1 : 2)) .Select(r=>(object)r) .ToList()); Records_List.Add("Shifts_index_list", shifts_list.Select(r => (object)r.shift_index.ToString()).ToList()); Records_List.Add("Shifts_dates_list", shifts_list .Select(r => $"{r.shift_start.ToShortDateString()} {r.shift_start.ToShortTimeString()}-{r.shift_end.ToShortDateString()} {r.shift_end.ToShortTimeString()}") .Select(r=>(object)r) .ToList()); //if (export_excel == 1) //{ return Excel_methods.Export_to_Excel_SkuDataByShifts(new ExcelExport(), report_name, shiftStart, shiftEnd, placeName: place_name, this, baseStrings, RecordsToShow_SkuDataByShifts, ter_list_shifts, shiftId: RecordsToShow_SkuDataByShifts.Count > 0 ? RecordsToShow_SkuDataByShifts.FirstOrDefault().ShiftId : 0, machinist_name: RecordsToShow_SkuDataByShifts.Count > 0 ? RecordsToShow_SkuDataByShifts.FirstOrDefault().Machinist_name : ""); } break; #endregion default: break; } //Если нужен список продуктов if (Records?.Count>0? Records?.FirstOrDefault().GetType().GetInterface("IListSortOfProd") != null:false) { ListSortOfProd = Records.OrderBy(m => ((IListSortOfProd)m).SortofProduction).Select(m => ((IListSortOfProd)m).SortofProduction).Distinct().ToArray(); if (SelectedSortOfProduction != null && SelectedSortOfProduction != "All") Records = Records.Where(r => ((IListSortOfProd)r).SortofProduction == SelectedSortOfProduction).ToList(); } //Округляем все поля RoundProps(); this.Successfull = true; }