diff --git a/ClassObj/ClsConstants.cs b/ClassObj/ClsConstants.cs index 82e41c5..72bf82a 100644 --- a/ClassObj/ClsConstants.cs +++ b/ClassObj/ClsConstants.cs @@ -1,8 +1,11 @@ -using System; +using Microsoft.Data.SqlClient; +using Dapper; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Collections.Concurrent; namespace kmCustomReportsNET.ClassObj; @@ -22,4 +25,63 @@ public class ClsConstants T1439 = 101 } + static ConcurrentDictionary dctScheduler = new(StringComparer.OrdinalIgnoreCase); + + internal static string GetCustomerRegistryItem(string KeyName) + { + string rv = string.Empty; + + try + { + using (var cn = new SqlConnection(kmCommonLibsCore.Constants.GetCnString(kmCommonLibsCore.Constants.enuServer.RDB))) + { + var Sql = "Select [Value] From crpt.[Registry] Where [Key]=@Key And [Enabled]=1"; + var parms = new { Key = KeyName }; + var theValue = cn.ExecuteScalar(Sql, parms); + + if (theValue != null && !string.IsNullOrWhiteSpace(theValue.ToString())) + rv = theValue?.ToString() ?? string.Empty; + } + } + catch (Exception ex) + { + kmCommonLibsCore.ClsErrorReporting.ErrorEncountered(ex); + rv = string.Empty; + } + + return rv; + } + + internal static void SetCustomerRegistryItem(string KeyName, string Value) + { + using (var cn = new SqlConnection(kmCommonLibsCore.Constants.GetCnString(kmCommonLibsCore.Constants.enuServer.RDB))) + { + var Sql = "Select Count(*) As Cnt From crpt.[Registry] Where [Key]=@Key And [Enabled]=1"; + var parms = new { Key = KeyName }; + int cnt = cn.ExecuteScalar(Sql, parms); + + if (cnt < 1) + Sql = "Insert Into crpt.[Registry] ([Key], [Value], [Enabled]) Values (@Key, @Value, 1)"; + else + Sql = "Update crpt.[Registry] Set [Value]=@Value, [DateChanged]=SysDateTimeOffset() Where [Key]=@Key And [Enabled]=1"; + + cn.Execute(Sql, parms); + } + } + + internal static bool NeedToRun(string TaskName) + { + bool rv = false; + + if (!dctScheduler.ContainsKey(TaskName) || dctScheduler[TaskName] < DateTimeOffset.Now) + rv = true; + else + rv = false; + return rv; + } + + internal static void SetNextRun(string TaskName, DateTimeOffset theTime) + { + dctScheduler[TaskName] = theTime; + } } diff --git a/ClassObj/ClsDobbsEmail_T1439.cs b/ClassObj/ClsDobbsEmail_T1439.cs index c89a994..5ed77fc 100644 --- a/ClassObj/ClsDobbsEmail_T1439.cs +++ b/ClassObj/ClsDobbsEmail_T1439.cs @@ -1,13 +1,16 @@ -using Microsoft.Data.SqlClient; +using log4net; +using Dapper; +using Microsoft.Data.SqlClient; using OfficeOpenXml; +using OfficeOpenXml.Export.ToDataTable; using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; +using System.Net.Mail; using System.Text; using System.Threading.Tasks; -//using static kmCustomReportsNET.ClassObj.ClsConstants; namespace kmCustomReportsNET.ClassObj; @@ -17,12 +20,14 @@ internal class ClsDobbsEmail_T1439 : IDisposable public string RptSaveToFolder { get; set; } = ClsConstants.KDrive + @"\Projects\Daily Report Archive"; public string Template1Path { get; set; } = ClsConstants.KDrive + @"\Projects\Daily Reports - Dobbs\T1439 Template 01.xlsx"; public string BusinessName { get; set; } = "DobbsTire"; - public ClsConstants.enuReportIDs ReportID { get; set; } = ClsConstants.enuReportIDs.T1439; + public ClsConstants.enuReportIDs ReportID { get; internal set; } = ClsConstants.enuReportIDs.T1439; public bool DebugMode { get; set; } = false; #region "Private Variables" + ILog appLog = LogManager.GetLogger(Program.AppName); DataSet? ds = null; List FilesToAttach = new(); + string SubjectToUse = string.Empty; #endregion public void Dispose() @@ -33,28 +38,67 @@ internal class ClsDobbsEmail_T1439 : IDisposable FilesToAttach.Clear(); } - internal void Go() + public ClsDobbsEmail_T1439() { ExcelPackage.LicenseContext = LicenseContext.NonCommercial; - using (var cn = new SqlConnection(kmCommonLibsCore.Constants.cnRDB)) - { - cn.Open(); + } - using (var cm = new SqlCommand("crpt.[Dobbs_T1439_EmailContests]", cn) { CommandType = CommandType.StoredProcedure, CommandTimeout = 120 }) - using (var da = new SqlDataAdapter(cm)) + internal void Go() + { + if (ClsConstants.NeedToRun(ReportID.ToString())) + { + DateTimeOffset nxtRunTime; + + appLog.DebugFormat("{0} report is checking to see if we need to run...", ReportID.ToString()); + using (var cn = new SqlConnection(kmCommonLibsCore.Constants.cnRDB)) { - //cm.Parameters.Add("@DateThruOverride", SqlDbType.Date).Value = DateTime.Parse("Oct 7, 2025"); // override the date thru date for testing - ds = new DataSet(); - da.Fill(ds); + bool? ReportDueNow = false; + + ReportDueNow = cn.ExecuteScalar("Select Top 1 [NeedToSend] From [crpt].[Dobbs_EmailContestCalendar] Where [SendReport]=@Today", new { Today = DateTime.Today }); + if (ReportDueNow.HasValue && ReportDueNow.Value) + { + cn.Open(); + + appLog.DebugFormat("{0} report is now running!", ReportID.ToString()); + using (var cm = new SqlCommand("crpt.[Dobbs_T1439_EmailContests]", cn) { CommandType = CommandType.StoredProcedure, CommandTimeout = 120 }) + using (var da = new SqlDataAdapter(cm)) + { + //cm.Parameters.Add("@DateThruOverride", SqlDbType.Date).Value = DateTime.Parse("Oct 7, 2025"); // override the date thru date for testing + ds = new DataSet(); + da.Fill(ds); + } + + // + + if (ds != null && ds.Tables.Count > 2 && ds.Tables[0].Rows.Count == 1) + { + CreateWeeklyStandings(); + CreateDetailSnapshots(); + + if (!DebugMode) + { + appLog.DebugFormat("{0} report is now sending out the email containing the report for this week.", ReportID.ToString()); + SendEmails(); + + cn.Execute("Update [crpt].[Dobbs_EmailContestCalendar] Set [NeedToSend]=0 Where [SendReport]=@Today", new { Today = DateTime.Today }); + } + } + + // Run it at the next 7AM I can find, starting with tomorrow + nxtRunTime = new DateTimeOffset(DateTime.Today.AddDays(1)); + } + else + { + // Run it at the next 7AM I can find + nxtRunTime = new DateTimeOffset(DateTime.Today); + appLog.DebugFormat("{0} report does not need to run.", ReportID.ToString()); + } } - } - // - - if (ds != null && ds.Tables.Count > 2 && ds.Tables[0].Rows.Count == 1) - { - CreateWeeklyStandings(); - CreateDetailSnapshots(); + while (nxtRunTime < DateTimeOffset.Now || nxtRunTime.Hour != 7) + nxtRunTime = nxtRunTime.AddHours(1); + ClsConstants.SetNextRun(ReportID.ToString(), nxtRunTime); + appLog.DebugFormat("{0} report will next check at: {1:ddd d-MMM-yyyy h:mm tt zzz}", ReportID.ToString(), nxtRunTime); } } @@ -64,6 +108,7 @@ internal class ClsDobbsEmail_T1439 : IDisposable string xlsFilename; int ctr = 0; + SubjectToUse = string.Format("Email Capture Contest - Week {0} ({1:MMM d}-{2:MMM d}) Results", drDates["WeekNumber"], drDates["wkFrom"], drDates["wkTo"]); xlsFilename = Path.Combine(RptSaveToFolder, string.Format("Dobbs {0:yyyy-MM MMM} Week {1} Email Contest Results.xlsx", drDates["mthTo"], drDates["WeekNumber"])); if (File.Exists(xlsFilename)) { @@ -260,4 +305,86 @@ internal class ClsDobbsEmail_T1439 : IDisposable FilesToAttach.Add(xlsFilename); } } + + private void SendEmails() + { + using (var em = new kmCommonLibsCore.Emails() { SendMethod = kmCommonLibsCore.enuSendMethod.OnsiteServer, Subject = SubjectToUse }) + { + string imgBackground, imgDisclaimer; + StringBuilder s0 = new(), s1 = new(); + + em.AddAddress(kmCommonLibsCore.enuAddressType.From, "data@keymotive.us", "KeyMotive Data Services"); + imgBackground = em.EmbedImageFile(ClsConstants.KDrive + @"\Projects\Daily Reports - COMMON\Background-Compass.jpg"); + imgDisclaimer = em.EmbedImageFile(ClsConstants.KDrive + @"\Projects\Daily Reports - COMMON\KM_Disclaimer_2.png"); + + s0.AppendFormat("\n", imgBackground); + s0.AppendLine("Please find attached your custom business report. If you have any questions regarding this report, please reply to this message or contact your account manager.

"); + s0.AppendLine("Thank you for the opportunity to service the analytical needs of your organization!
"); + s0.AppendLine("KeyMotive Data Services



"); + s0.AppendFormat("\"\"\n", imgDisclaimer); + + s1.AppendFormat("Please find attached your custom business report. If you have any questions regarding this report, please reply to this message or contact your account manager.\n\n"); + s1.AppendLine("Thank you for the opportunity to service the analytical needs of your organization!"); + s1.AppendLine("KeyMotive Data Services"); + + for (byte grp = 0; grp < 3; grp++) + { + string ToAudience = new[] { "", "CC", "BCC" }[grp]; + string KeyName = string.Format("{0} MailTo{1} {2}", BusinessName, ToAudience, ReportID.ToString()); + string k0 = ClsConstants.GetCustomerRegistryItem(KeyName); + + if (string.IsNullOrEmpty(k0)) + { + KeyName = string.Format("{0} MailTo{1}", BusinessName, ToAudience); + k0 = ClsConstants.GetCustomerRegistryItem(KeyName); + } + + if (!string.IsNullOrEmpty(k0)) + { + var lst = ConvertToEmailAddressList(k0); + + foreach (var itm in lst) + { + switch (grp) + { + case 1: + em.AddAddress(kmCommonLibsCore.enuAddressType.CC, itm.EmailAddress, itm.DisplayName); + break; + case 2: + em.AddAddress(kmCommonLibsCore.enuAddressType.BCC, itm.EmailAddress, itm.DisplayName); + break; + default: + em.AddAddress(kmCommonLibsCore.enuAddressType.To, itm.EmailAddress, itm.DisplayName); + break; + } + } + } + } + + foreach (var itm in FilesToAttach) + em.AttachFile(itm); + + em.HtmlBody = s0.ToString(); + em.TextBody = s1.ToString(); + em.Send(); + } + } + + private List ConvertToEmailAddressList(string RawAddressString) + { + List rv = new(); + if (!string.IsNullOrWhiteSpace(RawAddressString)) + { + string[] k1 = RawAddressString.Split(';'); + + foreach (var emx in k1) + { + var addr = new MailAddress(emx.Trim()); + + rv.Add(new MdlEmailAddress() { EmailAddress = addr.Address, DisplayName = addr.DisplayName }); + } + } + + return rv; + } } diff --git a/Models/MdlEmailAddress.cs b/Models/MdlEmailAddress.cs new file mode 100644 index 0000000..1ae28ac --- /dev/null +++ b/Models/MdlEmailAddress.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace kmCustomReportsNET; + +internal class MdlEmailAddress +{ + public string EmailAddress { get; set; } = string.Empty; + public string DisplayName { get; set; } = string.Empty; +} + diff --git a/Program.cs b/Program.cs index 3bce25f..1cb05d1 100644 --- a/Program.cs +++ b/Program.cs @@ -23,9 +23,10 @@ internal class Program if (Debugger.IsAttached) { - using (var obj = new ClsDobbsEmail_T1439()) + using (var obj = new ClsDobbsEmail_T1439() { DebugMode = false }) { obj.Go(); + obj.Go(); } } else @@ -34,7 +35,7 @@ internal class Program { x.Service(); x.EnableServiceRecovery(r => r.RestartService(TimeSpan.FromSeconds(15))); - x.SetServiceName("kmAtomicTime2"); + x.SetServiceName("kmCustomReportsNET"); x.StartAutomaticallyDelayed(); x.SetDescription("Will run some custom reports occasionally for KeyMotive's customers."); x.UseLog4Net(); diff --git a/kmCustomReportsNET.csproj b/kmCustomReportsNET.csproj index a4f739b..d96a2fe 100644 --- a/kmCustomReportsNET.csproj +++ b/kmCustomReportsNET.csproj @@ -2,14 +2,14 @@ Exe - net9.0 + net9.0-windows enable enable - +