Import FuelLog
This commit is contained in:
@@ -447,6 +447,7 @@ namespace MileageTraker.Web.DAL
|
||||
let existingOdometer = log.EndOdometer
|
||||
where
|
||||
log.VehicleId == vehicleId &&
|
||||
// this is also in Utility.Algorithms, but doesn't get emitted to DB as SQL if used in method call
|
||||
!(existingDate == date
|
||||
|| existingOdometer == odometer
|
||||
|| existingDate < date && existingOdometer < odometer
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using MileageTraker.Web.Models;
|
||||
using MileageTraker.Web.Utility;
|
||||
|
||||
namespace MileageTraker.Web.DAL
|
||||
{
|
||||
public class FuelmanCsvImporter
|
||||
{
|
||||
private sealed class FuelmanCsvClassMap : CsvClassMap<FuelLog>
|
||||
{
|
||||
public FuelmanCsvClassMap()
|
||||
{
|
||||
Map(m => m.Date).ConvertUsing(row =>
|
||||
{
|
||||
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);
|
||||
});
|
||||
Map(m => m.DriverFullName).ConvertUsing(row =>
|
||||
{
|
||||
var name = row.GetField("CEEMNAME");
|
||||
return NameNormalizer.Normalize(name);
|
||||
});
|
||||
|
||||
Map(m => m.TagNumber).Name("CEVEHNO");
|
||||
Map(m => m.Odometer).Name("CEODOMETER");
|
||||
Map(m => m.CityName).ConvertUsing(row =>
|
||||
{
|
||||
var city = row.GetField("CESITCTY");
|
||||
return city.ToLower().ToTitleCase();
|
||||
});
|
||||
Map(m => m.MPG).Name("CEMPG");
|
||||
Map(m => m.GasPurchased).Name("CEQTY");
|
||||
Map(m => m.TotalPrice).Name("CECUSAMT");
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<FuelLog> Import(string filename)
|
||||
{
|
||||
var fileInfo = new FileInfo(filename);
|
||||
if (!fileInfo.Exists)
|
||||
throw new ImportException("Cannot find file '" + filename + "'");
|
||||
|
||||
using (var tr = File.OpenText(fileInfo.FullName))
|
||||
{
|
||||
var csv = new CsvReader(tr);
|
||||
csv.Configuration.RegisterClassMap<FuelmanCsvClassMap>();
|
||||
|
||||
var fuelLogs = new List<FuelLog>();
|
||||
while (csv.Read())
|
||||
{
|
||||
try
|
||||
{
|
||||
// Skip non-FUEL entries
|
||||
if (csv.GetField<string>("CEPRDTYPE") != "FUEL")
|
||||
continue;
|
||||
|
||||
fuelLogs.Add(csv.GetRecord<FuelLog>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ImportException("Problem with document encountered on row " + csv.Row + ". " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return fuelLogs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace MileageTraker.Web.DAL
|
||||
{
|
||||
public class ImportException : Exception
|
||||
{
|
||||
public ImportException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MileageTraker.Web.DAL
|
||||
{
|
||||
public class LogImportException : Exception
|
||||
{
|
||||
public LogImportException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace MileageTraker.Web.DAL
|
||||
var missingHeaderColumns = GetMissingHeaderColumns(columnIndex.Keys);
|
||||
if (missingHeaderColumns.Any())
|
||||
{
|
||||
throw new LogImportException(string.Format("Excel document missing columns: {0}", string.Join(", ", missingHeaderColumns)));
|
||||
throw new ImportException(string.Format("Excel document missing columns: {0}", string.Join(", ", missingHeaderColumns)));
|
||||
}
|
||||
|
||||
for (var r = 1; r <= sheet.Cells.LastRowIndex; r++)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MileageTraker.Web.Models
|
||||
{
|
||||
public class FuelLog
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.Date)]
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(128)]
|
||||
public string DriverFullName { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Tag#")]
|
||||
public string TagNumber { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(1, 500000, ErrorMessage = "Between 1 and 500k")]
|
||||
public int Odometer { get; set; }
|
||||
|
||||
[StringLength(64)]
|
||||
public string CityName { get; set; }
|
||||
|
||||
public double MPG { get; set; }
|
||||
|
||||
[Required]
|
||||
public double GasPurchased { get; set; }
|
||||
|
||||
[Required]
|
||||
public decimal TotalPrice { get; set; }
|
||||
|
||||
// Matched log
|
||||
public virtual Log Log { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Data.SqlTypes;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
@@ -21,6 +22,12 @@ namespace MileageTraker.Web.Utility
|
||||
current + (Char.IsUpper(c) ? " " + c : c.ToString()));
|
||||
}
|
||||
|
||||
public static string ToTitleCase(this string str)
|
||||
{
|
||||
// http://stackoverflow.com/q/1943273/99492
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str);
|
||||
}
|
||||
|
||||
private static T GetAttribute<T>(this Enum enumeration) where T : Attribute
|
||||
{
|
||||
var type = enumeration.GetType();
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MileageTraker.Web.Utility
|
||||
{
|
||||
public static class NameNormalizer
|
||||
{
|
||||
private const string NamePattern = @"(?<Last>\w+),\s*(?<First>\w+)(?:\s*)?(?<Suffix>(jr|sr|iii))?";
|
||||
private static readonly Regex NameRegex = new Regex(NamePattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
|
||||
|
||||
public static string Normalize(string name)
|
||||
{
|
||||
var match = NameRegex.Match(name);
|
||||
if (!match.Success)
|
||||
return name;
|
||||
return string.Format("{0} {1} {2}",
|
||||
match.Groups["First"], match.Groups["Last"], match.Groups["Suffix"]).TrimEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace MileageTraker.Web.Utility
|
||||
{
|
||||
protected override string FormatValueCore(string value)
|
||||
{
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value);
|
||||
return value.ToTitleCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -151,7 +151,7 @@
|
||||
<Compile Include="DAL\CodeFirstRoleProvider.cs" />
|
||||
<Compile Include="DAL\FuelmanCsvImporter.cs" />
|
||||
<Compile Include="DAL\LogImporter.cs" />
|
||||
<Compile Include="DAL\LogImportException.cs" />
|
||||
<Compile Include="DAL\ImportException.cs" />
|
||||
<Compile Include="DAL\LogImportTemplateWriter.cs" />
|
||||
<Compile Include="DAL\UninitializedAccountException.cs" />
|
||||
<Compile Include="DAL\UserAccountDisabledException.cs" />
|
||||
@@ -210,6 +210,7 @@
|
||||
<Compile Include="Models\DomainRules.cs" />
|
||||
<Compile Include="Models\FuelLog.cs" />
|
||||
<Compile Include="Models\PurposeType.cs" />
|
||||
<Compile Include="Utility\NameNormalizer.cs" />
|
||||
<Compile Include="ViewModels\Account\ResetPasswordViewModel.cs" />
|
||||
<Compile Include="Models\Role.cs" />
|
||||
<Compile Include="Models\User.cs" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
|
||||
<package id="CsvHelper" version="2.13.2.0" targetFramework="net45" />
|
||||
<package id="EntityFramework" version="6.1.3" targetFramework="net45" />
|
||||
<package id="ExcelLibrary" version="1.2011.7.30" />
|
||||
<package id="FontAwesome" version="4.0.3.1" targetFramework="net40" />
|
||||
|
||||
Reference in New Issue
Block a user