Email Reminders
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using MileageTraker.Web.Attributes;
|
||||
using MileageTraker.Web.DAL;
|
||||
using MileageTraker.Web.Email;
|
||||
using MileageTraker.Web.Models;
|
||||
using MileageTraker.Web.Utility;
|
||||
using MileageTraker.Web.ViewModels;
|
||||
@@ -213,5 +214,15 @@ namespace MileageTraker.Web.Controllers
|
||||
var names = DataService.GetServiceCenterNamesAutocomplete(term);
|
||||
return Json(names, JsonRequestBehavior.AllowGet);
|
||||
}
|
||||
|
||||
[ActionLog]
|
||||
[Authorize(Roles = "Developer")]
|
||||
public ActionResult SendVehicleServiceReminderEmails()
|
||||
{
|
||||
var emailService = new ServiceReminderEmailService();
|
||||
emailService.SendAllNotificationEmails();
|
||||
SetStatusMessage("Vehicle Service Reminders Sent");
|
||||
return Redirect(Request.UrlReferrer.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Configuration;
|
||||
using System.Net.Mail;
|
||||
using System.Reflection;
|
||||
using System.Web.Security;
|
||||
using MileageTraker.Web.Models;
|
||||
using log4net;
|
||||
|
||||
@@ -56,10 +57,16 @@ namespace MileageTraker.Web.Email
|
||||
SendMessage(new MailMessage(_emaialFromAddress, user.Email, _initializePasswordSubject, body));
|
||||
}
|
||||
|
||||
public void SendServiceReminder(ServiceReminderEmail email)
|
||||
{
|
||||
SendMessage(new MailMessage(_emaialFromAddress, email.Recipient.Email, email.Subject, email.Body));
|
||||
}
|
||||
|
||||
private void SendMessage(MailMessage mailMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Debug("Email sending to " + mailMessage.To + ", subject: " + mailMessage.Subject);
|
||||
_smtpClient.Send(mailMessage);
|
||||
}
|
||||
catch (SmtpException ex)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using MileageTraker.Web.Models;
|
||||
|
||||
namespace MileageTraker.Web.Email
|
||||
{
|
||||
public class ServiceReminderEmail
|
||||
{
|
||||
private readonly string _emailSubject;
|
||||
private readonly string _emailBody;
|
||||
public User Recipient { get; set; }
|
||||
public IList<ServiceReminder> ServiceReminders { get; set; }
|
||||
|
||||
public string Subject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ServiceReminders.Any())
|
||||
return "No upcoming vehicle services currently scheduled";
|
||||
var vehicles = ServiceReminders.Select(sr => sr.Vehicle.VehicleId).Distinct().ToList();
|
||||
var vehicleDesc = vehicles.Count > 1 ? vehicles.Count.ToString() + " vehicles" : "Vehicle Id " + vehicles.First();
|
||||
var subject = string.Format(_emailSubject, vehicleDesc);
|
||||
return subject;
|
||||
}
|
||||
}
|
||||
|
||||
public string Body
|
||||
{
|
||||
get
|
||||
{
|
||||
var reminderTexts = GetReminderTexts();
|
||||
var body =
|
||||
string.Format(_emailBody,
|
||||
Recipient.FullName,
|
||||
Environment.NewLine + Environment.NewLine
|
||||
+ string.Join(Environment.NewLine, reminderTexts)
|
||||
);
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceReminderEmail(User recipient, IList<ServiceReminder> serviceReminders)
|
||||
{
|
||||
_emailSubject = ConfigurationManager.AppSettings["ServiceReminderSubject"];
|
||||
_emailBody = ConfigurationManager.AppSettings["ServiceReminderBody"];
|
||||
Recipient = recipient;
|
||||
ServiceReminders = serviceReminders;
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetReminderTexts()
|
||||
{
|
||||
if (!ServiceReminders.Any())
|
||||
return new[] {"No upcoming services currently scheduled"};
|
||||
|
||||
return
|
||||
from sr in ServiceReminders
|
||||
let diff =
|
||||
sr.TargetOdometer - (sr.Vehicle.CurrentOdometer.HasValue ? sr.Vehicle.CurrentOdometer.Value : 0)
|
||||
orderby diff
|
||||
let description =
|
||||
!string.IsNullOrEmpty(sr.Description)
|
||||
? String.Format(", description is '{0}'", sr.Description)
|
||||
: string.Empty
|
||||
let assignedTo =
|
||||
!string.IsNullOrEmpty(sr.Vehicle.Assigned)
|
||||
? String.Format("assigned to {0}", sr.Vehicle.Assigned)
|
||||
: "unassigned"
|
||||
select
|
||||
string.Format("Vehicle with Id {0}, which is {3}, has a reminder for service {1} miles{2}.",
|
||||
sr.Vehicle.VehicleId, diff < 0 ? " overdue " + (-diff).ToString() : " in " + diff.ToString(),
|
||||
description,
|
||||
assignedTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MileageTraker.Web.DAL;
|
||||
using MileageTraker.Web.Models;
|
||||
using log4net;
|
||||
|
||||
namespace MileageTraker.Web.Email
|
||||
{
|
||||
public class ServiceReminderEmailService : IDisposable
|
||||
{
|
||||
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
protected readonly DataService DataService = new DataService();
|
||||
protected readonly EmailNotificationService EmailService = new EmailNotificationService();
|
||||
|
||||
public void SendAllNotificationEmails()
|
||||
{
|
||||
Logger.Info("Starting vehicle service notifications");
|
||||
var serviceReminders = DataService.GetUpcomingServiceReminders().ToList();
|
||||
|
||||
Logger.Debug("Got " + serviceReminders.Count + " service reminders.");
|
||||
|
||||
SendAdminEmails(serviceReminders);
|
||||
|
||||
SendUserEmails(serviceReminders);
|
||||
}
|
||||
|
||||
private void SendAdminEmails(IList<ServiceReminder> serviceReminders)
|
||||
{
|
||||
var admins =
|
||||
(from u in DataService.GetUsers()
|
||||
where u.Roles.Any(r => r.RoleName == "Vehicle Admin")
|
||||
select u).Distinct().ToList();
|
||||
|
||||
var adminEmails =
|
||||
from admin in admins
|
||||
select new ServiceReminderEmail(admin, serviceReminders);
|
||||
|
||||
foreach (var email in adminEmails)
|
||||
{
|
||||
EmailService.SendServiceReminder(email);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendUserEmails(IList<ServiceReminder> serviceReminders)
|
||||
{
|
||||
// send email to user
|
||||
var userEmails =
|
||||
from sr in serviceReminders
|
||||
group sr by sr.Vehicle.Assigned
|
||||
into g
|
||||
let driver = DataService.FindUserByFullName(g.Key)
|
||||
where driver != null
|
||||
let userReminders = g.ToList()
|
||||
select new ServiceReminderEmail(driver, userReminders);
|
||||
|
||||
foreach (var email in userEmails)
|
||||
{
|
||||
EmailService.SendServiceReminder(email);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataService.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,32 @@ namespace MileageTraker.Web.Migrations
|
||||
.PrimaryKey(t => t.ServiceReminderId)
|
||||
.ForeignKey("dbo.Vehicle", t => t.Vehicle_VehicleId, cascadeDelete: true)
|
||||
.Index(t => t.Vehicle_VehicleId);
|
||||
|
||||
|
||||
Sql(@"INSERT INTO [Role]
|
||||
([RoleId]
|
||||
,[RoleName]
|
||||
,[Description])
|
||||
VALUES
|
||||
('dd28bb32-afb6-4d54-bce6-04457bcf79d9'
|
||||
,'Vehicle Admin'
|
||||
,'Vehicle Administrator')");
|
||||
Sql(@"INSERT INTO [RoleUser]
|
||||
([User_UserId], [Role_RoleId])
|
||||
VALUES
|
||||
('A1720B63-5970-4313-9AC3-90B844FABD65',
|
||||
'dd28bb32-afb6-4d54-bce6-04457bcf79d9')");
|
||||
}
|
||||
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
DropForeignKey("dbo.ServiceReminder", "Vehicle_VehicleId", "dbo.Vehicle");
|
||||
DropIndex("dbo.ServiceReminder", new[] { "Vehicle_VehicleId" });
|
||||
DropTable("dbo.ServiceReminder");
|
||||
|
||||
Sql(@"DELETE FROM [Role]
|
||||
WHERE [RoleId] = 'dd28bb32-afb6-4d54-bce6-04457bcf79d9'");
|
||||
Sql(@"DELETE FROM [RoleUser]
|
||||
WHERE [Role_RoleId] = 'dd28bb32-afb6-4d54-bce6-04457bcf79d9'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Hangfire;
|
||||
using Microsoft.Owin;
|
||||
using MileageTraker.Web.Email;
|
||||
using Owin;
|
||||
|
||||
[assembly: OwinStartup(typeof(MileageTraker.Web.Startup))]
|
||||
|
||||
namespace MileageTraker.Web
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void Configuration(IAppBuilder app)
|
||||
{
|
||||
GlobalConfiguration.Configuration
|
||||
.UseSqlServerStorage("MileageTrakerContext");
|
||||
|
||||
app.UseHangfireDashboard();
|
||||
app.UseHangfireServer();
|
||||
|
||||
SetupRecurringJobs();
|
||||
}
|
||||
|
||||
private void SetupRecurringJobs()
|
||||
{
|
||||
RecurringJob.AddOrUpdate<ServiceReminderEmailService>(
|
||||
"serviceReminderJob", s => s.SendAllNotificationEmails(), Cron.Weekly(DayOfWeek.Monday, 6));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
@Html.Partial("_StatusMessage")
|
||||
|
||||
<h2 class="center-content"><i class="fa fa-clock-o"></i> @ViewBag.Title</h2>
|
||||
<h4 class="center-content">Reminder for future vehicle service</h4>
|
||||
|
||||
@using (Html.BeginForm("Create", "ServiceReminder", FormMethod.Post, new { @class = "form-horizontal well center-content" }))
|
||||
{
|
||||
|
||||
@@ -42,7 +42,11 @@
|
||||
<li>@Html.ActionLink("Vehicles", "Index", "Vehicle")</li>
|
||||
<li>@Html.ActionLink("Vehicle Service", "Index", "VehicleService")</li>
|
||||
<li>@Html.ActionLink("Fuel Logs", "Index", "FuelLog")</li>
|
||||
<li>@Html.ActionLink("Cost Report", "VehicleCostIndex", "Vehicle")</li>
|
||||
<li>@Html.ActionLink("Cost Report", "VehicleCostIndex", "Vehicle")</li>
|
||||
@if (User.IsInRole("Developer"))
|
||||
{
|
||||
<li>@Html.ActionLink("Send Service Reminder Emails", "SendVehicleServiceReminderEmails", "VehicleService")</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
<li id="user-nav">@Html.ActionLink("Users", "Index", "User")</li>
|
||||
@@ -50,7 +54,7 @@
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span>Config</span> <i class="fa fa-caret-down"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>@Html.ActionLink("Cities", "Index", "City")</li>
|
||||
<li>@Html.ActionLink("Purposes", "Index", "Purpose")</li>
|
||||
<li>@Html.ActionLink("Purposes", "Index", "Purpose")</li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
}
|
||||
|
||||
<h2 class="center-content"><i class="fa fa-wrench"></i> @ViewBag.Title</h2>
|
||||
<h4 class="center-content">Track completed service for a vehicle</h4>
|
||||
|
||||
@using (Html.BeginForm("Create", "VehicleService", FormMethod.Post, new { @class = "form-horizontal well center-content" }))
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
ViewBag.Title = "Vehicle Service";
|
||||
var grid = new WebGrid(Model.ServiceItems, rowsPerPage: 45);
|
||||
var queryParams = new { Model.Year, Model.Month, Model.MonthRange, Model.VehicleId };
|
||||
var serviceOverdueBadge = Model.UpcomingServiceReminders.Any(sr => sr.IsServiceOverdue) ? "badge-warning" : "";
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
@@ -31,9 +32,15 @@
|
||||
<div class="btn-toolbar pull-left">
|
||||
@Html.ActionLink("Add Service", "Create", null, new { @class = "btn" })
|
||||
@Html.ActionLink("Export", "Export", queryParams, new { @class = "btn" })
|
||||
<a class="qtip-show-next-div btn">Upcoming Services <span class="badge">@Model.UpcomingServiceReminders.Count</span></a>
|
||||
<a class="qtip-show-next-div btn">Upcoming Services <span class="badge @serviceOverdueBadge">@Model.UpcomingServiceReminders.Count</span></a>
|
||||
<div class="hidden">
|
||||
@Html.Partial("UpcomingServiceReminders", Model.UpcomingServiceReminders)
|
||||
@if (@Model.UpcomingServiceReminders.Count > 0)
|
||||
{
|
||||
@Html.Partial("UpcomingServiceReminders", Model.UpcomingServiceReminders)
|
||||
}
|
||||
else {
|
||||
<h5>No upcoming services currently scheduled</h5>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
<add key="ResetPasswordBody" value="Hello {1}, please open this link to set a new password for your Mileage Traker account: {0}" />
|
||||
<add key="InitializePasswordSubject" value="Initialize Mileage Traker Account" />
|
||||
<add key="InitializetPasswordBody" value="Hello {1}, welcome to Mileage Traker. Your username is {2}. Please open this link to initialize your password: {0}" />
|
||||
<add key="ServiceReminderSubject" value="Vehicle Serivce Reminder for {0}" />
|
||||
<add key="ServiceReminderBody" value="Hello {0}, please note the following vehicles due for service: {1}" />
|
||||
</appSettings>
|
||||
<system.net>
|
||||
<mailSettings>
|
||||
|
||||
+30
-4
@@ -15,7 +15,7 @@
|
||||
<AssemblyName>MileageTraker</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<MvcBuildViews>false</MvcBuildViews>
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
<UseIISExpress>true</UseIISExpress>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
@@ -71,11 +71,27 @@
|
||||
<Reference Include="ExcelLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ExcelLibrary.1.2011.7.30\lib\ExcelLibrary.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Hangfire.Core, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Hangfire.Core.1.5.2\lib\net45\Hangfire.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Hangfire.SqlServer, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Hangfire.SqlServer.1.5.2\lib\net45\Hangfire.SqlServer.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Owin, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
|
||||
@@ -87,6 +103,14 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\MvcCheckBoxList.1.4.4.5\lib\net40\MvcCheckBoxList.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.5.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.configuration" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
@@ -146,6 +170,9 @@
|
||||
<Compile Include="Controllers\AccountController.cs" />
|
||||
<Compile Include="Controllers\CityController.cs" />
|
||||
<Compile Include="Controllers\ControllerBase.cs" />
|
||||
<Compile Include="Email\ServiceReminderEmail.cs" />
|
||||
<Compile Include="Email\ServiceReminderEmailService.cs" />
|
||||
<Compile Include="Startup.cs" />
|
||||
<Compile Include="ViewModels\DriverMileageFlattenedViewModel.cs" />
|
||||
<Compile Include="ViewModels\VehicleService\UpdateServiceRemindersViewModel.cs" />
|
||||
<Compile Include="Controllers\FuelLogController.cs" />
|
||||
@@ -636,12 +663,11 @@
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>2977</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<IISUrl>http://localhost:2977/</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<package id="EntityFramework" version="6.1.3" targetFramework="net45" />
|
||||
<package id="ExcelLibrary" version="1.2011.7.30" />
|
||||
<package id="FontAwesome" version="4.4.0" targetFramework="net45" />
|
||||
<package id="Hangfire" version="1.5.2" targetFramework="net45" />
|
||||
<package id="Hangfire.Core" version="1.5.2" targetFramework="net45" />
|
||||
<package id="Hangfire.SqlServer" version="1.5.2" targetFramework="net45" />
|
||||
<package id="JonSkeet.MiscUtil" version="0.1" targetFramework="net40" />
|
||||
<package id="jQuery" version="2.1.4" targetFramework="net45" />
|
||||
<package id="jQuery.Migrate" version="1.2.1" targetFramework="net40" />
|
||||
@@ -16,7 +19,11 @@
|
||||
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
|
||||
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />
|
||||
<package id="MvcCheckBoxList" version="1.4.4.5" targetFramework="net40" requireReinstallation="True" />
|
||||
<package id="Newtonsoft.Json" version="5.0.1" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
<package id="Twitter.Bootstrap" version="2.2.2" targetFramework="net40" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user