Add TSV support.
Import Upload
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using MileageTraker.Web.DAL;
|
||||
using MileageTraker.Web.Models;
|
||||
using MileageTraker.Web.Utility;
|
||||
using MileageTraker.Web.ViewModels.FuelLog;
|
||||
|
||||
namespace MileageTraker.Web.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Administrator, Developer")]
|
||||
public class FuelLogController : ControllerBase
|
||||
{
|
||||
public ViewResult Index(FuelLogQueryViewModel query)
|
||||
{
|
||||
var validLogYearMonths = DataService.GetValidFuelLogMonths();
|
||||
|
||||
if (!validLogYearMonths.Any()) // this means no logs in DB
|
||||
return View("Empty");
|
||||
|
||||
// default parameter processing
|
||||
if (!query.HasParameters())
|
||||
{
|
||||
query.Year = validLogYearMonths.First().Year;
|
||||
query.Month = validLogYearMonths.First().Month;
|
||||
}
|
||||
if (query.Year.HasValue && !query.Month.HasValue)
|
||||
{
|
||||
var validLogMonths = validLogYearMonths.Where(dt => dt.Year == query.Year).ToList();
|
||||
query.Month = validLogMonths.Min(dt => dt.Month);
|
||||
}
|
||||
|
||||
var fuelLogs = DataService.GetFuelLogs();
|
||||
//fuelLogs.OrderBy(f => f.FuelLogId);
|
||||
|
||||
//var filteredLogs =
|
||||
// (from log in DataService.GetFuelLogIndexViewModels(DataService.FilterLogs(fuelLogs, query))
|
||||
// orderby log. descending
|
||||
// select log).ToList();
|
||||
|
||||
var viewModel = new FuelLogResultsViewModel(fuelLogs, query, CustomExtensions.YearMonthList(validLogYearMonths));
|
||||
|
||||
//Session.Add("FuelLogPage", Request.Url.PathAndQuery);
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
#region Import
|
||||
|
||||
public ActionResult ImportUpload()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Import(FuelLogImportUploadViewModel 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);
|
||||
var fileInfo = new FileInfo(path);
|
||||
viewModel.File.SaveAs(path);
|
||||
|
||||
try
|
||||
{
|
||||
var logImports = FuelmanCsvImporter.Import(fileInfo);
|
||||
// todo: add to the database
|
||||
DataService.AddFuelLogs(logImports);
|
||||
// todo: delete file?
|
||||
return View(logImports.ToList());
|
||||
}
|
||||
//catch (OutOfMemoryException)
|
||||
//{
|
||||
// TempData["StatusMessage"] = "Problem reading excel document - please verify that the document is in Excel 97-2003 Workbook (.xls) format";
|
||||
// TempData["StatusMessage-Type"] = "alert-error";
|
||||
//}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["StatusMessage"] = "Problem reading document: " + ex.Message;
|
||||
TempData["StatusMessage-Type"] = "alert-error";
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fileInfo.Exists)
|
||||
fileInfo.Delete();
|
||||
}
|
||||
}
|
||||
return RedirectToAction("ImportUpload");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -600,5 +600,52 @@ namespace MileageTraker.Web.DAL
|
||||
|
||||
#endregion
|
||||
|
||||
#region FuelLog
|
||||
|
||||
public void AddFuelLog(FuelLog fuelLog)
|
||||
{
|
||||
_db.FuelLogs.Add(fuelLog);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public void AddFuelLogs(IEnumerable<FuelLog> fuelLogs)
|
||||
{
|
||||
_db.FuelLogs.AddRange(fuelLogs);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public void UpdateFuelLog(FuelLog fuelLog)
|
||||
{
|
||||
_db.Entry(fuelLog).State = EntityState.Modified;
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public IQueryable<FuelLog> GetFuelLogs()
|
||||
{
|
||||
return _db.FuelLogs;
|
||||
}
|
||||
|
||||
public FuelLog GetFuelLog(int id)
|
||||
{
|
||||
return _db.FuelLogs.Find(id);
|
||||
}
|
||||
|
||||
|
||||
public IList<DateTime> GetValidFuelLogMonths()
|
||||
{
|
||||
var months =
|
||||
from l in GetFuelLogs()
|
||||
group l by new { l.Date.Year, l.Date.Month }
|
||||
into g
|
||||
//let dt = new DateTime(g.Key.Year, g.Key.Month, 1)
|
||||
select g.Key;
|
||||
|
||||
var ym =
|
||||
from m in months.ToList()
|
||||
select new DateTime(m.Year, m.Month, 1);
|
||||
|
||||
return ym.OrderByDescending(dt => dt).ToList();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using MileageTraker.Web.Models;
|
||||
@@ -9,10 +10,11 @@ using MileageTraker.Web.Utility;
|
||||
|
||||
namespace MileageTraker.Web.DAL
|
||||
{
|
||||
public class FuelmanCsvImporter
|
||||
public static class FuelmanCsvImporter
|
||||
{
|
||||
private sealed class FuelmanCsvClassMap : CsvClassMap<FuelLog>
|
||||
{
|
||||
readonly string[] _dateFormats = { @"M/d/yy HH:mm", @"M/d/yyyy HH:mm" };
|
||||
public FuelmanCsvClassMap()
|
||||
{
|
||||
Map(m => m.Date).ConvertUsing(row =>
|
||||
@@ -20,7 +22,12 @@ namespace MileageTraker.Web.DAL
|
||||
var date = row.GetField("CEDATE");
|
||||
var time = row.GetField<int>("CETIME");
|
||||
var dttm = string.Format(@"{0} {1:00:00}", date, time);
|
||||
return DateTime.ParseExact(dttm, @"M/d/yyyy HH:mm", CultureInfo.InvariantCulture);
|
||||
|
||||
DateTime parsed;
|
||||
if (DateTime.TryParseExact(dttm, _dateFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
||||
return parsed;
|
||||
|
||||
throw new ArgumentException(string.Format("Date in incorrect format: CEDATE:{0} CETIME:{1}", date, time));
|
||||
});
|
||||
Map(m => m.DriverFullName).ConvertUsing(row =>
|
||||
{
|
||||
@@ -41,16 +48,35 @@ namespace MileageTraker.Web.DAL
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<FuelLog> Import(string filename)
|
||||
public static bool IsProbablyTabSeparated(FileInfo fileInfo)
|
||||
{
|
||||
using (var tr = File.OpenText(fileInfo.FullName))
|
||||
{
|
||||
var line = tr.ReadLine();
|
||||
var tabHeadCount =
|
||||
(from c in line
|
||||
where c == '\t'
|
||||
select c).Count();
|
||||
// if there's more than 10 tabs in the first line, probably TSV
|
||||
return tabHeadCount > 10;
|
||||
}
|
||||
}
|
||||
|
||||
public static ICollection<FuelLog> Import(FileInfo fileInfo)
|
||||
{
|
||||
var fileInfo = new FileInfo(filename);
|
||||
if (!fileInfo.Exists)
|
||||
throw new ImportException("Cannot find file '" + filename + "'");
|
||||
throw new ImportException("Cannot find file '" + fileInfo.Name + "'");
|
||||
|
||||
var tabSeparatedValues = IsProbablyTabSeparated(fileInfo);
|
||||
|
||||
using (var tr = File.OpenText(fileInfo.FullName))
|
||||
{
|
||||
var csv = new CsvReader(tr);
|
||||
csv.Configuration.RegisterClassMap<FuelmanCsvClassMap>();
|
||||
var csvConfiguration = new CsvConfiguration();
|
||||
if (tabSeparatedValues)
|
||||
csvConfiguration.Delimiter = "\t";
|
||||
csvConfiguration.RegisterClassMap<FuelmanCsvClassMap>();
|
||||
|
||||
var csv = new CsvReader(tr, csvConfiguration);
|
||||
|
||||
var fuelLogs = new List<FuelLog>();
|
||||
while (csv.Read())
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ namespace MileageTraker.Web.Migrations
|
||||
|
||||
string IMigrationMetadata.Id
|
||||
{
|
||||
get { return "201509100253084_FuelLog"; }
|
||||
get { return "201509111820437_FuelLog"; }
|
||||
}
|
||||
|
||||
string IMigrationMetadata.Source
|
||||
@@ -21,7 +21,6 @@ namespace MileageTraker.Web.Models
|
||||
public string TagNumber { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(1, 500000, ErrorMessage = "Between 1 and 500k")]
|
||||
public int Odometer { get; set; }
|
||||
|
||||
[StringLength(64)]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web;
|
||||
|
||||
namespace MileageTraker.Web.ViewModels.FuelLog
|
||||
{
|
||||
public class FuelLogImportUploadViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "FuelMan CSV File")]
|
||||
public HttpPostedFileBase File { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using MileageTraker.Web.Models;
|
||||
using MileageTraker.Web.ViewModels.Log;
|
||||
|
||||
namespace MileageTraker.Web.ViewModels.FuelLog
|
||||
{
|
||||
public class FuelLogResultsViewModel
|
||||
{
|
||||
public IEnumerable<Models.FuelLog> Logs { get; set; }
|
||||
public Dictionary<string, List<string>> AvailableYearMonths { get; set; }
|
||||
public IEnumerable<string> SelectedYearMonths{get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Year))
|
||||
return AvailableYearMonths[Year];
|
||||
return new List<string>();
|
||||
}}
|
||||
|
||||
// filter parameters
|
||||
public string Year { get; set; }
|
||||
public string Month { get; set; }
|
||||
|
||||
public FuelLogResultsViewModel(IEnumerable<Models.FuelLog> logs, FuelLogQueryViewModel query, Dictionary<string, List<string>> availableYearMonths)
|
||||
{
|
||||
Logs = logs;
|
||||
AvailableYearMonths = availableYearMonths;
|
||||
Year = query.Year.HasValue ? query.Year.Value.ToString() : string.Empty;
|
||||
Month = query.Month.HasValue ? query.Month.Value.ToString() : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MileageTraker.Web.ViewModels.FuelLog
|
||||
{
|
||||
public class FuelLogQueryViewModel
|
||||
{
|
||||
public int? Year { get; set; }
|
||||
public int? Month { get; set; }
|
||||
|
||||
public string YearMonthStart {get
|
||||
{
|
||||
return string.Format("{0}-{1:00}", Year, Month);
|
||||
}}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var v = new List<string>();
|
||||
|
||||
if (Year.HasValue && Month.HasValue)
|
||||
{
|
||||
var str = YearMonthStart;
|
||||
|
||||
v.Add(str);
|
||||
}
|
||||
else if (Year.HasValue)
|
||||
{
|
||||
v.Add(Year.ToString());
|
||||
}
|
||||
|
||||
return String.Join("_", v).Replace(' ', '-');
|
||||
}
|
||||
|
||||
public bool HasParameters()
|
||||
{
|
||||
return Year.HasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
@model IList<MileageTraker.Web.Models.FuelLog>
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Import Fuel Logs";
|
||||
}
|
||||
@section Styles
|
||||
{
|
||||
<link href="@Url.Content("~/Content/font-awesome.min.css")" rel="stylesheet" type="text/css" />
|
||||
}
|
||||
|
||||
@{ Html.RenderPartial("BackToLogs"); }
|
||||
|
||||
@Html.Partial("_StatusMessage")
|
||||
|
||||
<h2>@ViewBag.Title</h2>
|
||||
<p>Driver: <strong>@ViewData["UserFullName"]</strong></p>
|
||||
|
||||
<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", "Log", 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("@ViewData["UserFullName"]");
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
@model MileageTraker.Web.ViewModels.FuelLog.FuelLogImportUploadViewModel
|
||||
@{
|
||||
ViewBag.Title = "Import Fuel Logs";
|
||||
}
|
||||
|
||||
@Html.Partial("_StatusMessage")
|
||||
|
||||
<h2 class="center-content">@ViewBag.Title</h2>
|
||||
|
||||
@using (Html.BeginForm("Import", "FuelLog", 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>
|
||||
}
|
||||
+11
-5
@@ -144,6 +144,7 @@
|
||||
<Compile Include="Controllers\AccountController.cs" />
|
||||
<Compile Include="Controllers\CityController.cs" />
|
||||
<Compile Include="Controllers\ControllerBase.cs" />
|
||||
<Compile Include="Controllers\FuelLogController.cs" />
|
||||
<Compile Include="Controllers\PurposeController.cs" />
|
||||
<Compile Include="Controllers\UserController.cs" />
|
||||
<Compile Include="DAL\CityNotFoundException.cs" />
|
||||
@@ -206,9 +207,9 @@
|
||||
<Compile Include="Migrations\201506181329243_VehicleInactiveDate.Designer.cs">
|
||||
<DependentUpon>201506181329243_VehicleInactiveDate.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\201509100253084_FuelLog.cs" />
|
||||
<Compile Include="Migrations\201509100253084_FuelLog.Designer.cs">
|
||||
<DependentUpon>201509100253084_FuelLog.cs</DependentUpon>
|
||||
<Compile Include="Migrations\201509111820437_FuelLog.cs" />
|
||||
<Compile Include="Migrations\201509111820437_FuelLog.Designer.cs">
|
||||
<DependentUpon>201509111820437_FuelLog.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\Configuration.cs" />
|
||||
<Compile Include="Models\DomainRules.cs" />
|
||||
@@ -229,6 +230,9 @@
|
||||
<Compile Include="ViewModels\CreateLog\ImportUploadViewModel.cs" />
|
||||
<Compile Include="ViewModels\DriverMileageItem.cs" />
|
||||
<Compile Include="ViewModels\DriverMileageViewModel.cs" />
|
||||
<Compile Include="ViewModels\FuelLog\FuelLogImportUploadViewModel.cs" />
|
||||
<Compile Include="ViewModels\FuelLog\LogQueryViewModel.cs" />
|
||||
<Compile Include="ViewModels\FuelLog\FuelLogResultsViewModel.cs" />
|
||||
<Compile Include="ViewModels\Log\ImportLogViewModel.cs" />
|
||||
<Compile Include="ViewModels\Log\ImportUploadViewModel.cs" />
|
||||
<Compile Include="ViewModels\Log\LogViewModel.cs" />
|
||||
@@ -293,6 +297,8 @@
|
||||
<Content Include="Views\City\Index.cshtml" />
|
||||
<Content Include="Views\City\Create.cshtml" />
|
||||
<Content Include="Views\Shared\DisplayTemplates\LogQueryViewModel.cshtml" />
|
||||
<Content Include="Views\FuelLog\ImportUpload.cshtml" />
|
||||
<Content Include="Views\FuelLog\Import.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\Account.Login.css" />
|
||||
@@ -522,8 +528,8 @@
|
||||
<Folder Include="App_Data\Uploads\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Migrations\201509100253084_FuelLog.resx">
|
||||
<DependentUpon>201509100253084_FuelLog.cs</DependentUpon>
|
||||
<EmbeddedResource Include="Migrations\201509111820437_FuelLog.resx">
|
||||
<DependentUpon>201509111820437_FuelLog.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
|
||||
Reference in New Issue
Block a user