Log Import for CreateLog

This commit is contained in:
2014-02-05 15:02:10 -05:00
parent 7976fe04c3
commit 042d3dd476
13 changed files with 349 additions and 27 deletions
+189 -1
View File
@@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.IO;
using System.Linq;
using System.Security;
using System.Web.Mvc;
using System.Web.Routing;
using MileageTraker.Web.Attributes;
using MileageTraker.Web.DAL;
using MileageTraker.Web.Models;
using MileageTraker.Web.ViewModels;
using MileageTraker.Web.ViewModels.CreateLog;
using MileageTraker.Web.ViewModels.Log;
namespace MileageTraker.Web.Controllers
{
@@ -160,5 +164,189 @@ namespace MileageTraker.Web.Controllers
}
};
}
#region Import
public ActionResult ImportUpload()
{
return View();
}
[HttpPost]
public ActionResult Import(ImportUploadViewModel viewModel)
{
if (ModelState.IsValid && viewModel.File != null && viewModel.File.ContentLength > 0)
{
var fileName = Path.GetFileName(viewModel.File.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
viewModel.File.SaveAs(path);
try
{
var logImports = LogImporter.Import(path);
// todo: delete file?
return View(logImports.ToList());
}
catch (Exception ex)
{
TempData["StatusMessage"] = "Problem reading excel document: " + ex.Message;
TempData["StatusMessage-Type"] = "alert-error";
}
}
return RedirectToAction("ImportUpload");
}
enum ImportStatus
{
Success,
Failure,
Duplicate
}
[HttpPost]
[ActionLog]
public ActionResult ImportCreate(LogImportViewModel viewModel)
{
var buttonAttributes = new Dictionary<string, object> { { "class", "btn btn-mini" }, { "target", "_blank" } };
try
{
// verify purpose
var purposeType = DataService.FindPurposeType(viewModel.Purpose);
if (purposeType == null)
{
return Json(new
{
Status = ImportStatus.Failure.ToString(),
Message = "Invalid Purpose: " + viewModel.Purpose,
Action = GetFixLink(viewModel)
}, JsonRequestBehavior.AllowGet);
}
if (!ModelState.IsValid)
{
var errorList = GetModelStateErrorList(ModelState);
// remove the object name from the errors
errorList = errorList.Select(s => s.Replace("viewModel.", ""));
return Json(new
{
Status = ImportStatus.Failure.ToString(),
Message = string.Join(", ", errorList),
Action = GetFixLink(viewModel)
}, JsonRequestBehavior.AllowGet);
}
var log = viewModel.GetLog();
log.User = DataService.FindUserByUsername(User.Identity.Name);
log.Source = HttpContext.Request.Url.AbsolutePath;
log.UserHostAddress = HttpContext.Request.UserHostAddress;
log.UserAgent = HttpContext.Request.UserAgent;
log.Purpose = purposeType;
// check duplicate
var duplicateLogs = DataService.GetDuplicateLogs(log).ToList();
if (duplicateLogs.Any())
{
var link = HtmlHelper.GenerateLink(
ControllerContext.RequestContext, RouteTable.Routes,
"Edit", "Default", "EditPast", "CreateLog",
new RouteValueDictionary(new { id = duplicateLogs.First().LogId }),
buttonAttributes);
return Json(new
{
Status = ImportStatus.Duplicate.ToString(),
Message = "This log has been previously entered",
Action = link
}, JsonRequestBehavior.AllowGet);
}
// verify vehicle exists
if (DataService.GetVehicle(log.VehicleId) == null)
{
return Json(new
{
Status = ImportStatus.Failure.ToString(),
Message = "Vehile with supplied ID does not exist",
Action = GetFixLink(viewModel)
}, JsonRequestBehavior.AllowGet);
}
// SAVE IT!
DataService.AddLog(log);
var successLink = HtmlHelper.GenerateLink(
ControllerContext.RequestContext, RouteTable.Routes,
"Edit", "Default", "EditPast", "CreateLog",
new RouteValueDictionary(new { id = log.LogId }),
buttonAttributes);
return Json(new
{
Status = ImportStatus.Success.ToString(),
Action = successLink
}, JsonRequestBehavior.AllowGet);
}
catch (DbEntityValidationException ex)
{
// Retrieve the error messages as a list of strings.
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
// Join the list to a single string.
var fullErrorMessage = string.Join(", ", errorMessages);
return Json(new
{
Status = ImportStatus.Failure.ToString(),
Message = fullErrorMessage,
Action = GetFixLink(viewModel)
},
JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
var message = ex.Message;
return Json(new
{
Status = ImportStatus.Failure.ToString(),
Message = message,
Action = GetFixLink(viewModel)
}, JsonRequestBehavior.AllowGet);
}
}
private string GetFixLink(LogImportViewModel viewModel)
{
try
{
viewModel.GetLogViewModel();
}
catch
{
return string.Empty;
}
return RenderRazorViewToString("ImportFix", viewModel);
}
[HttpPost]
public ActionResult ImportFix(LogImportViewModel viewModel)
{
var createGetLogViewModel = viewModel.GetCreateLogViewModel();
createGetLogViewModel.Purpose = new SelectListViewModel
{
Available = GetPurposeTypesSelectList()
};
return View("Index", createGetLogViewModel);
}
public ActionResult ImportTemplate()
{
var template = LogImportTemplateWriter.Write();
return File(template, "application/ms-excel", "MileageTraker_ImportTemplate.xls");
}
#endregion
}
}
+6 -2
View File
@@ -241,6 +241,8 @@ namespace MileageTraker.Web.Controllers
return PartialView(new LogPartialDetails(log));
}
#region Import
public ActionResult ImportUpload()
{
return View();
@@ -414,7 +416,7 @@ namespace MileageTraker.Web.Controllers
return string.Empty;
}
return RenderRazorViewToString("LogImportPartial", viewModel);
return RenderRazorViewToString("ImportFix", viewModel);
}
[HttpPost]
@@ -430,5 +432,7 @@ namespace MileageTraker.Web.Controllers
var template = LogImportTemplateWriter.Write();
return File(template, "application/ms-excel", "MileageTraker_ImportTemplate.xls");
}
}
#endregion
}
}
+5 -2
View File
@@ -15,11 +15,14 @@
if ($logs.length > 0) {
var $row = $($logs[0]);
var data = $('form', $row).serialize();
data += "&userFullName=" + userFullName;
var url = $('form', $row).attr('action');
if (userFullName != null) {
data += "&userFullName=" + userFullName;
}
$('.import-status', $row).html('<span class="label label-info"><i class="fa fa-spinner fa-spin"></i> Submitting</span');
$.ajax({
url: "/Log/ImportCreate",
url: url,
type: 'post',
data: data,
success: function (result) {
@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
using System.Web;
using System.Web.Mvc;
using MileageTraker.Web.Attributes;
namespace MileageTraker.Web.ViewModels.CreateLog
{
public class ImportUploadViewModel
{
[Required]
[Display(Name = "XLS Excel File")]
public HttpPostedFileBase File { get; set; }
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ using MileageTraker.Web.Attributes;
namespace MileageTraker.Web.ViewModels.Log
{
public class ImportUploadViewModel
public class ImportUploadViewModel
{
[Required]
[Display(Name = "XLS Excel File")]
+11
View File
@@ -4,6 +4,7 @@ using System.Web.Mvc;
using AutoMapper;
using MileageTraker.Web.Attributes;
using MileageTraker.Web.Utility;
using MileageTraker.Web.ViewModels.CreateLog;
using MileageTraker.Web.ViewModels.Log;
namespace MileageTraker.Web.ViewModels
@@ -61,6 +62,9 @@ namespace MileageTraker.Web.ViewModels
Mapper.CreateMap<LogImportViewModel, LogViewModel>()
.ForMember(u => u.LogType, opt => opt.Ignore())
.ForMember(u => u.Purpose, opt => opt.Ignore());
Mapper.CreateMap<LogImportViewModel, CreateLogViewModel>()
.ForMember(u => u.LogType, opt => opt.Ignore())
.ForMember(u => u.Purpose, opt => opt.Ignore());
Mapper.CreateMap<LogImportViewModel, Models.Log>()
.ForMember(u => u.Purpose, opt => opt.Ignore())
.ForMember(dest => dest.GasPurchased,
@@ -82,6 +86,13 @@ namespace MileageTraker.Web.ViewModels
return log;
}
public CreateLogViewModel GetCreateLogViewModel()
{
var log = new CreateLogViewModel();
Mapper.Map(this, log);
return log;
}
public Models.Log GetLog()
{
var log = new Models.Log();
+62
View File
@@ -0,0 +1,62 @@
@model IList<MileageTraker.Web.ViewModels.LogImportViewModel>
@{
ViewBag.Title = "Import Logs";
}
@section Styles
{
<link href="@Url.Content("~/Content/font-awesome.min.css")" rel="stylesheet" type="text/css" />
}
@Html.Partial("_StatusMessage")
<h2>@ViewBag.Title</h2>
<p id="page-import-status"></p>
<table id="logs" class="table table-striped table-bordered table-hover table-condensed">
<thead>
<tr>
<th>Import Status</th>
<th style="width:20%"></th>
<th>Vehicle ID</th>
<th>End Odometer</th>
<th>Type</th>
<th>Destination City</th>
<th>Purpose</th>
<th>Notes</th>
<th>Gas Purchased</th>
<th>Date</th>
</tr>
</thead>
@for (var i = 0; i < Model.Count; i++)
{
var viewModel = Model[i];
<tr id="log-@i">
@using (Html.BeginForm("ImportCreate", "CreateLog", FormMethod.Post))
{
@Html.EditorFor(x => viewModel)
}
<td class="import-status"></td>
<td class="import-message"></td>
<td>@Html.ValueFor(x => viewModel.VehicleId)</td>
<td>@Html.ValueFor(x => viewModel.EndOdometer)</td>
<td>@Html.ValueFor(x => viewModel.LogType)</td>
<td>@Html.ValueFor(x => viewModel.CityName)</td>
<td>@Html.ValueFor(x => viewModel.Purpose)</td>
<td>@Html.ValueFor(x => viewModel.Notes)</td>
<td>@Html.ValueFor(x => viewModel.GasPurchased)</td>
<td>@Html.ValueFor(x => viewModel.Date)</td>
</tr>
}
</table>
@section Scripts
{
<script src="@Url.Content("~/Scripts/Shared/ImportCreate.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
importLogs(null);
});
</script>
}
+9
View File
@@ -0,0 +1,9 @@
@model MileageTraker.Web.ViewModels.LogImportViewModel
@{
Layout = null;
}
@using (Html.BeginForm("ImportFix", "CreateLog", FormMethod.Post, new{target="_blank"}))
{
@Html.EditorForModel()
<input type="submit" value="Fix" class="btn btn-mini" />
}
+20
View File
@@ -0,0 +1,20 @@
@model MileageTraker.Web.ViewModels.CreateLog.ImportUploadViewModel
@{
ViewBag.Title = "Import Logs";
}
@Html.Partial("_StatusMessage")
<h2 class="center-content">@ViewBag.Title</h2>
<p class="center-content">Download the import template: @Html.ActionLink("Excel Template", "ImportTemplate")
</p>
@using (Html.BeginForm("Import", "CreateLog", FormMethod.Post, new { enctype="multipart/form-data", @class = "form-horizontal well center-content", style="max-width:440px"}))
{
@Html.Partial("_ValidationSummary")
@Html.EditorForModel()
<div class="form-actions">
<input type="submit" value="Import" class="btn btn-primary" />
</div>
}
+4
View File
@@ -23,3 +23,7 @@
<div id="RecentLogs" class="center-content well">
</div>
<div class="center-content" style="text-align: center">
@Html.ActionLink("Import", "ImportUpload", new {}, new { @class = "btn"})
</div>
+4 -1
View File
@@ -36,7 +36,10 @@
{
var viewModel = Model[i];
<tr id="log-@i">
<form>@Html.EditorFor(x => viewModel)</form>
@using (Html.BeginForm("ImportCreate", "Log", FormMethod.Post))
{
@Html.EditorFor(x => viewModel)
}
<td class="import-status"></td>
<td class="import-message"></td>
<td>@Html.ValueFor(x => viewModel.VehicleId)</td>
+24 -20
View File
@@ -25,7 +25,7 @@
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<AfterAddIisSettingAndFileContentsToSourceManifest>AddSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
<AfterAddIisSettingAndFileContentsToSourceManifest>AddSkipRules</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -187,6 +187,7 @@
<Compile Include="ViewModels\Account\RegisterModel.cs" />
<Compile Include="ViewModels\CheckBoxViewModel.cs" />
<Compile Include="ViewModels\CreateLog\EditPastViewModel.cs" />
<Compile Include="ViewModels\CreateLog\ImportUploadViewModel.cs" />
<Compile Include="ViewModels\DriverMileageItem.cs" />
<Compile Include="ViewModels\DriverMileageViewModel.cs" />
<Compile Include="ViewModels\LogImportViewModel.cs" />
@@ -233,7 +234,7 @@
<Content Include="fonts\fontawesome-webfont.ttf" />
<Content Include="fonts\fontawesome-webfont.eot" />
<Content Include="fonts\FontAwesome.otf" />
<Content Include="Views\Log\LogImportPartial.cshtml" />
<Content Include="Views\Log\ImportFix.cshtml" />
<Content Include="Views\Shared\EditorTemplates\HttpPostedFileBase.cshtml" />
</ItemGroup>
<ItemGroup>
@@ -351,6 +352,9 @@
<Content Include="Views\CreateLog\EditPast.cshtml" />
<Content Include="Views\Log\Import.cshtml" />
<Content Include="Views\Log\ImportUpload.cshtml" />
<Content Include="Views\CreateLog\Import.cshtml" />
<Content Include="Views\CreateLog\ImportUpload.cshtml" />
<Content Include="Views\CreateLog\ImportFix.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="packages.config">
@@ -487,24 +491,24 @@
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<Target Name="AddSkipRules">
<ItemGroup>
<MsDeploySkipRules Include="SkipDeleteAppData">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
<MsDeploySkipRules Include="SkipDeleteAppData">
<SkipAction>Delete</SkipAction>
<ObjectName>dirPath</ObjectName>
<AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
<Target Name="AddSkipRules">
<ItemGroup>
<MsDeploySkipRules Include="SkipDeleteAppData">
<SkipAction>Delete</SkipAction>
<ObjectName>filePath</ObjectName>
<AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
<MsDeploySkipRules Include="SkipDeleteAppData">
<SkipAction>Delete</SkipAction>
<ObjectName>dirPath</ObjectName>
<AbsolutePath>$(_Escaped_PackageTempDir)\\App_Data\\.*</AbsolutePath>
<XPath>
</XPath>
</MsDeploySkipRules>
</ItemGroup>
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">