A bit of background. In the application I work on there are emails sent to notify users about articles they need to review. Each email+recipient pair is stored in database and may be assigned a different creation date ( called DateCreated
) due to emails being generated over some time.
I needed to calculate how many times the emails were sent and how many recipients were there on each turn. For this reason I grouped emails which come in quick succession into groups. I used a TimeSpan of 30 minutes after each "first" email in a group. In order to do it I needed to store the dates defining groups "somewhere". I used a separate method to wrap my grouping function. The TimeSpan I used is safe, mailing ever only takes up to 2 minutes. Also separate mailings usually happen on separate dates.
My questions here are - Is this easy enough to understand? Could I code it simpler? Any other advice is also welcome.
And yes - this should be fixed in the database, there should be a mailing
table there but there isn't and I cannot achieve it easily at the moment.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UserCommunication
{
public class ArticleEmailSummary
{
public int ArticleId { get; }
public int EmailTemplateId { get; }
public DateTime DateCreated { get; }
public int RecipientCount { get; }
private ArticleEmailSummary(IGrouping<DateTime, ArticleEmail> grouping)
{
ArticleId = grouping.First().ArticleId;
EmailTemplateId = grouping.First().EmailTemplateId;
DateCreated = grouping.Key;
RecipientCount = grouping.Count();
}
public static List<ArticleEmailSummary> List(int ArticleId, int emailTemplateId)
{
// get email list from db
List<ArticleEmail> emails = ArticleEmail.List(ArticleId, emailTemplateId);
// order it ascending by creation date
IOrderedEnumerable<ArticleEmail> emailsWithOrder = emails.OrderBy(e => e.DateCreated);
// group into sublists, each group cachet up to X minutes of emails occuring after the first one
IEnumerable<IGrouping<DateTime, ArticleEmail>> groups = emailsWithOrder.GroupBy(GetGroupingFunction());
// convert groupings to summaries and return
return groups.Select(grouping => new ArticleEmailSummary(grouping)).ToList();
}
private static Func<ArticleEmail, DateTime> GetGroupingFunction()
{
// I am curious of other dev's opinion on this method :)
TimeSpan maxMailingTime = TimeSpan.FromMinutes(30);
DateTime lastDate = DateTime.MinValue;
return delegate (ArticleEmail email)
{
if (email.DateCreated - lastDate > maxMailingTime)
{
// the mailing was not sent within maxMailingTime from the start of first email in series
// we will update the lastDate and this will group the emails into a separate group
lastDate = email.DateCreated;
}
// now we return the value which groups our emails
return lastDate;
};
}
}
}
Edits:
- changed
emailTemplateId
to be of typeint
- it was just anenum
( with values such as "articleForApproval", "expiringArticle", etc. ), my bad, this must have been confusing indeed ArticleEmail.List(...)
simply returnsList<ArticleEmail>
that match the specified type of email and specific articleas requested here, is the
ArticleEmail
class, a bit simplified for the purpose of this question:public class ArticleEmail { public int ArticleId { get; } public int EmailTemplateId { get; } public DateTime DateCreated { get; } public int RecipientId { get; } public static List<ArticleEmail> List( int articleId, int emailTemplateId ) { // return instances from db } }
ArticleEmail
andEmailTemplateId
classes as well. So, we can have a full understanding on how your code logic works. \$\endgroup\$ArticleEmail
class as requested and updatedEmailTemplateId
to useint
type consistently. Sorry for confusing you guys and thanks for your time and effort. \$\endgroup\$