diff --git a/Web/Controllers/CreateLogController.cs b/Web/Controllers/CreateLogController.cs index 64ae75a..7da31b8 100644 --- a/Web/Controllers/CreateLogController.cs +++ b/Web/Controllers/CreateLogController.cs @@ -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 { { "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 } } \ No newline at end of file diff --git a/Web/Controllers/LogController.cs b/Web/Controllers/LogController.cs index 6a5255f..2ff8e18 100644 --- a/Web/Controllers/LogController.cs +++ b/Web/Controllers/LogController.cs @@ -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 + } } \ No newline at end of file diff --git a/Web/Scripts/Shared/ImportCreate.js b/Web/Scripts/Shared/ImportCreate.js index 536231f..2eab185 100644 --- a/Web/Scripts/Shared/ImportCreate.js +++ b/Web/Scripts/Shared/ImportCreate.js @@ -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(' Submitting() .ForMember(u => u.LogType, opt => opt.Ignore()) .ForMember(u => u.Purpose, opt => opt.Ignore()); + Mapper.CreateMap() + .ForMember(u => u.LogType, opt => opt.Ignore()) + .ForMember(u => u.Purpose, opt => opt.Ignore()); Mapper.CreateMap() .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(); diff --git a/Web/Views/CreateLog/Import.cshtml b/Web/Views/CreateLog/Import.cshtml new file mode 100644 index 0000000..27981c6 --- /dev/null +++ b/Web/Views/CreateLog/Import.cshtml @@ -0,0 +1,62 @@ +@model IList + +@{ + ViewBag.Title = "Import Logs"; +} +@section Styles +{ + +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+ +

+ + + + + + + + + + + + + + + + + @for (var i = 0; i < Model.Count; i++) + { + var viewModel = Model[i]; + + @using (Html.BeginForm("ImportCreate", "CreateLog", FormMethod.Post)) + { + @Html.EditorFor(x => viewModel) + } + + + + + + + + + + + + } +
Import StatusVehicle IDEnd OdometerTypeDestination CityPurposeNotesGas PurchasedDate
@Html.ValueFor(x => viewModel.VehicleId)@Html.ValueFor(x => viewModel.EndOdometer)@Html.ValueFor(x => viewModel.LogType)@Html.ValueFor(x => viewModel.CityName)@Html.ValueFor(x => viewModel.Purpose)@Html.ValueFor(x => viewModel.Notes)@Html.ValueFor(x => viewModel.GasPurchased)@Html.ValueFor(x => viewModel.Date)
+ +@section Scripts +{ + + +} \ No newline at end of file diff --git a/Web/Views/CreateLog/ImportFix.cshtml b/Web/Views/CreateLog/ImportFix.cshtml new file mode 100644 index 0000000..8f15159 --- /dev/null +++ b/Web/Views/CreateLog/ImportFix.cshtml @@ -0,0 +1,9 @@ +@model MileageTraker.Web.ViewModels.LogImportViewModel +@{ + Layout = null; +} +@using (Html.BeginForm("ImportFix", "CreateLog", FormMethod.Post, new{target="_blank"})) +{ + @Html.EditorForModel() + +} \ No newline at end of file diff --git a/Web/Views/CreateLog/ImportUpload.cshtml b/Web/Views/CreateLog/ImportUpload.cshtml new file mode 100644 index 0000000..45708d1 --- /dev/null +++ b/Web/Views/CreateLog/ImportUpload.cshtml @@ -0,0 +1,20 @@ +@model MileageTraker.Web.ViewModels.CreateLog.ImportUploadViewModel +@{ + ViewBag.Title = "Import Logs"; +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+ +

Download the import template: @Html.ActionLink("Excel Template", "ImportTemplate") +

+ +@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() +
+ +
+} diff --git a/Web/Views/CreateLog/Index.cshtml b/Web/Views/CreateLog/Index.cshtml index f3a8d23..665190d 100644 --- a/Web/Views/CreateLog/Index.cshtml +++ b/Web/Views/CreateLog/Index.cshtml @@ -23,3 +23,7 @@
+ +
+ @Html.ActionLink("Import", "ImportUpload", new {}, new { @class = "btn"}) +
\ No newline at end of file diff --git a/Web/Views/Log/Import.cshtml b/Web/Views/Log/Import.cshtml index 875b92f..f9fbb27 100644 --- a/Web/Views/Log/Import.cshtml +++ b/Web/Views/Log/Import.cshtml @@ -36,7 +36,10 @@ { var viewModel = Model[i]; -
@Html.EditorFor(x => viewModel)
+ @using (Html.BeginForm("ImportCreate", "Log", FormMethod.Post)) + { + @Html.EditorFor(x => viewModel) + } @Html.ValueFor(x => viewModel.VehicleId) diff --git a/Web/Views/Log/LogImportPartial.cshtml b/Web/Views/Log/ImportFix.cshtml similarity index 100% rename from Web/Views/Log/LogImportPartial.cshtml rename to Web/Views/Log/ImportFix.cshtml diff --git a/Web/Web.csproj b/Web/Web.csproj index 8cbc1e6..3f4f940 100644 --- a/Web/Web.csproj +++ b/Web/Web.csproj @@ -25,7 +25,7 @@ - AddSkipRules + AddSkipRules true @@ -187,6 +187,7 @@ + @@ -233,7 +234,7 @@ - + @@ -351,6 +352,9 @@ + + + @@ -487,24 +491,24 @@ - - - - Delete - filePath - $(_Escaped_PackageTempDir)\\App_Data\\.* - - - - - Delete - dirPath - $(_Escaped_PackageTempDir)\\App_Data\\.* - - - - - + + + + Delete + filePath + $(_Escaped_PackageTempDir)\\App_Data\\.* + + + + + Delete + dirPath + $(_Escaped_PackageTempDir)\\App_Data\\.* + + + + +