443 lines
12 KiB
C#
443 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Text.RegularExpressions;
|
|
using MileageTraker.Web.Context;
|
|
using MileageTraker.Web.Models;
|
|
using MileageTraker.Web.Utility;
|
|
using MileageTraker.Web.ViewModels;
|
|
using MileageTraker.Web.ViewModels.Log;
|
|
using MileageTraker.Web.ViewModels.Vehicle;
|
|
|
|
namespace MileageTraker.Web.DAL
|
|
{
|
|
public class DataService : IDisposable
|
|
{
|
|
private readonly MileageTrakerContext _db = new MileageTrakerContext();
|
|
|
|
#region Log
|
|
|
|
public void AddLog(Log log)
|
|
{
|
|
log.Created = DateTime.Now;
|
|
_db.Logs.Add(log);
|
|
|
|
// update previous log links
|
|
var previousLog = SearchPreviousLog(log);
|
|
log.VehiclePreviousLog = previousLog;
|
|
var nextLog = SearchNextLog(log);
|
|
if (nextLog != null)
|
|
nextLog.VehiclePreviousLog = log;
|
|
|
|
_db.SaveChanges();
|
|
|
|
UpdateCurrentOdometer(log.VehicleId);
|
|
}
|
|
|
|
public void UpdateLog(Log log)
|
|
{
|
|
_db.Logs.Attach(log);
|
|
var original = _db.Logs.Find(log.LogId);
|
|
|
|
// ignore these properties while updating
|
|
log.Created = original.Created;
|
|
log.Source = original.Source;
|
|
log.UserAgent = original.UserAgent;
|
|
log.UserHostAddress = original.UserHostAddress;
|
|
|
|
// remove from the list
|
|
var nextLog =
|
|
(from l in _db.Logs
|
|
where l.VehiclePreviousLogId == log.LogId
|
|
select l).FirstOrDefault();
|
|
if (nextLog != null)
|
|
nextLog.VehiclePreviousLogId = log.VehiclePreviousLogId;
|
|
|
|
log.VehiclePreviousLog = null;
|
|
log.VehiclePreviousLogId = null;
|
|
|
|
// add it back in new position
|
|
var previousLog = SearchPreviousLog(log);
|
|
if (previousLog != null)
|
|
log.VehiclePreviousLogId = previousLog.LogId;
|
|
var newNextLog = SearchNextLog(log);
|
|
if (newNextLog != null)
|
|
newNextLog.VehiclePreviousLogId = log.LogId;
|
|
|
|
_db.Entry(log).State = EntityState.Modified;
|
|
_db.SaveChanges();
|
|
|
|
UpdateCurrentOdometer(log.VehicleId);
|
|
}
|
|
|
|
public void DeleteLog(int id)
|
|
{
|
|
var log = _db.Logs.Find(id);
|
|
|
|
var nextLog = GetNextLog(log.LogId);
|
|
if (nextLog != null)
|
|
nextLog.VehiclePreviousLog = log.VehiclePreviousLog;
|
|
|
|
_db.Logs.Remove(log);
|
|
_db.SaveChanges();
|
|
|
|
UpdateCurrentOdometer(log.VehicleId);
|
|
}
|
|
|
|
public IQueryable<Log> GetLogs()
|
|
{
|
|
return _db.Logs;
|
|
}
|
|
|
|
public Log GetLog(int id)
|
|
{
|
|
return _db.Logs.Find(id);
|
|
}
|
|
|
|
public Log GetNextLog(int id)
|
|
{
|
|
return
|
|
(from l in _db.Logs
|
|
where l.VehiclePreviousLogId == id
|
|
select l).FirstOrDefault();
|
|
}
|
|
|
|
public IEnumerable<int> GetValidLogYears()
|
|
{
|
|
return
|
|
from l in GetLogs()
|
|
group l by new {year = l.Date.Year}
|
|
into y
|
|
orderby y.Key descending
|
|
select y.Key.year;
|
|
}
|
|
|
|
public IEnumerable<int> GetValidLogMonths(int year)
|
|
{
|
|
var start = new DateTime(year, 1, 1);
|
|
var end = new DateTime(year + 1, 1, 1);
|
|
return
|
|
from l in GetLogs()
|
|
where l.Date >= start && l.Date < end
|
|
group l by new { month = l.Date.Month }
|
|
into m
|
|
orderby m.Key descending
|
|
select m.Key.month;
|
|
}
|
|
|
|
public IEnumerable<LogIndexViewModel> GetLogIndexViewModels(IQueryable<Log> logs)
|
|
{
|
|
var t =
|
|
from log in logs
|
|
select new { log, log.VehiclePreviousLog };
|
|
return from l in t.ToList()
|
|
select new LogIndexViewModel(l.log, l.VehiclePreviousLog);
|
|
}
|
|
|
|
public IEnumerable<VehicleMileageItem> GetMonthlyVehicleMileageItems(LogQueryViewModel query)
|
|
{
|
|
var items =
|
|
from l in
|
|
from log in FilterLogs(_db.Logs, query)
|
|
let previousLog = log.VehiclePreviousLog
|
|
select new {log, previousLog, miles = (previousLog != null ? log.EndOdometer - previousLog.EndOdometer : 0)}
|
|
group l by l.log.VehicleId
|
|
into g
|
|
join v in _db.Vehicles on g.Key equals v.VehicleId
|
|
let miles = g.Sum(l => l.miles)
|
|
let gasPurchased = g.Sum(l => l.log.GasPurchased)
|
|
where miles > 0 || gasPurchased > 0
|
|
let logPairs =
|
|
from i in g
|
|
orderby i.log.Date descending , i.log.EndOdometer descending
|
|
select new { PreviousLog = i.previousLog, Log = i.log }
|
|
select
|
|
new
|
|
{
|
|
VehicleId = g.Key,
|
|
GasPurchased = gasPurchased,
|
|
Miles = miles,
|
|
v.Prog,
|
|
LogPairs = logPairs
|
|
};
|
|
|
|
return items.ToList().Select(i => new VehicleMileageItem
|
|
{
|
|
VehicleId = i.VehicleId,
|
|
GasPurchased = i.GasPurchased,
|
|
Miles = i.Miles,
|
|
Prog = i.Prog,
|
|
LogPairs = i.LogPairs.ToList()
|
|
.Select(lp => new Tuple<Log,Log>(lp.PreviousLog, lp.Log))
|
|
});
|
|
}
|
|
|
|
public IEnumerable<EmployeeMileageItem> GetMonthlyEmployeeMileageItems(LogQueryViewModel query)
|
|
{
|
|
var items =
|
|
from l in
|
|
from log in FilterLogs(_db.Logs, query)
|
|
let previousLog = log.VehiclePreviousLog
|
|
select new { log, previousLog, miles = (previousLog != null ? log.EndOdometer - previousLog.EndOdometer : 0) }
|
|
group l by l.log.EmployeeName
|
|
into g
|
|
let miles = g.Sum(l => l.miles)
|
|
let gasPurchased = g.Sum(l => l.log.GasPurchased)
|
|
where miles > 0 || gasPurchased > 0
|
|
let logPairs =
|
|
from i in g
|
|
orderby i.log.Date descending, i.log.EndOdometer descending
|
|
select new { PreviousLog = i.previousLog, Log = i.log }
|
|
select
|
|
new
|
|
{
|
|
EmployeeName = g.Key,
|
|
GasPurchased = gasPurchased,
|
|
Miles = miles,
|
|
LogPairs = logPairs
|
|
};
|
|
|
|
return items.ToList().Select(i => new EmployeeMileageItem
|
|
{
|
|
EmployeeName = i.EmployeeName,
|
|
GasPurchased = i.GasPurchased,
|
|
Miles = i.Miles,
|
|
LogPairs = i.LogPairs.ToList()
|
|
.Select(lp => new Tuple<Log, Log>(lp.PreviousLog, lp.Log))
|
|
});
|
|
}
|
|
|
|
public static IQueryable<Log> FilterLogs(IQueryable<Log> logs, LogQueryViewModel query)
|
|
{
|
|
if (query.Year.HasValue & query.Month.HasValue)
|
|
{
|
|
var start = new DateTime(query.Year.Value, query.Month.Value, 1);
|
|
var end = start.AddMonths(1);
|
|
logs = logs.Where(l => l.Date >= start && l.Date < end);
|
|
}
|
|
|
|
if (query.LogType.HasValue)
|
|
{
|
|
// WTF The specified value is not an instance of type 'Edm.Int32'
|
|
Expression<Func<Log, bool>> predicate;
|
|
switch (query.LogType.Value)
|
|
{
|
|
case MileageLogType.NonCommuting:
|
|
predicate = l => l.LogType.Value == 1;
|
|
break;
|
|
case MileageLogType.Commuting:
|
|
predicate = l => l.LogType.Value == 2;
|
|
break;
|
|
case MileageLogType.GasPurchase:
|
|
predicate = l => l.LogType.Value == 3;
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
logs = logs.Where(predicate);
|
|
}
|
|
|
|
return logs;
|
|
}
|
|
|
|
public IEnumerable<Log> GetRecentLogsByEmployee(string employeeName)
|
|
{
|
|
return
|
|
(from l in _db.Logs
|
|
where l.EmployeeName == employeeName
|
|
orderby l.Created descending
|
|
select l)
|
|
.Take(3)
|
|
.ToList();
|
|
}
|
|
|
|
public Log SearchPreviousLog(Log current)
|
|
{
|
|
var vehicleId = current.VehicleId;
|
|
var date = current.Date;
|
|
var endOdometer = current.EndOdometer;
|
|
return SearchPreviousLog(endOdometer, vehicleId, date, current.Created, current.LogId);
|
|
}
|
|
|
|
public Log SearchPreviousLog(int endOdometer, string vehicleId, DateTime date, DateTime created, int? excludeLogId = null)
|
|
{
|
|
return (from pl in _db.Logs
|
|
where pl.VehicleId == vehicleId &&
|
|
((pl.EndOdometer < endOdometer && pl.Date <= date)
|
|
|| (pl.EndOdometer == endOdometer && pl.Date < date)
|
|
|| (pl.EndOdometer == endOdometer && pl.Date == date && pl.Created < created))
|
|
orderby pl.EndOdometer descending, pl.Date descending, pl.Created descending
|
|
select pl)
|
|
.FirstOrDefault(l => !excludeLogId.HasValue || l.LogId != excludeLogId.Value);
|
|
}
|
|
|
|
public Log SearchNextLog(Log current)
|
|
{
|
|
var vehicleId = current.VehicleId;
|
|
var date = current.Date;
|
|
var endOdometer = current.EndOdometer;
|
|
return SearchNextLog(endOdometer, vehicleId, date, current.Created, current.LogId);
|
|
}
|
|
|
|
public Log SearchNextLog(int endOdometer, string vehicleId, DateTime date, DateTime created, int? excludeLogId = null)
|
|
{
|
|
return (from nl in GetLogs()
|
|
where nl.VehicleId == vehicleId &&
|
|
((nl.EndOdometer > endOdometer && nl.Date >= date)
|
|
|| (nl.EndOdometer == endOdometer && nl.Date > date)
|
|
|| (nl.EndOdometer == endOdometer && nl.Date >= date && nl.Created > created))
|
|
orderby nl.EndOdometer ascending, nl.Date ascending, nl.Created ascending
|
|
select nl)
|
|
.FirstOrDefault(l => !excludeLogId.HasValue || l.LogId != excludeLogId.Value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Vehicle
|
|
|
|
public void AddVehicle(Vehicle vehicle)
|
|
{
|
|
if (vehicle.Assigned == "Unassigned")
|
|
vehicle.Assigned = null;
|
|
_db.Vehicles.Add(vehicle);
|
|
_db.SaveChanges();
|
|
}
|
|
|
|
public void UpdateVehicle(Vehicle vehicle)
|
|
{
|
|
if (vehicle.Assigned == "Unassigned")
|
|
vehicle.Assigned = null;
|
|
_db.Entry(vehicle).State = EntityState.Modified;
|
|
_db.SaveChanges();
|
|
}
|
|
|
|
public IEnumerable<Vehicle> GetVehicles()
|
|
{
|
|
var vehicles = _db.Vehicles;
|
|
return vehicles;
|
|
}
|
|
|
|
public Vehicle GetVehicle(string id)
|
|
{
|
|
return _db.Vehicles.Find(id);
|
|
}
|
|
|
|
public void UpdateCurrentOdometer(string vehicleId)
|
|
{
|
|
var mostRecentOdometerQuery =
|
|
from log in _db.Logs
|
|
where
|
|
log.VehicleId == vehicleId
|
|
orderby log.Date descending, log.EndOdometer descending
|
|
select log;
|
|
var mostRecentOdometer = mostRecentOdometerQuery.FirstOrDefault();
|
|
var vehicle = _db.Vehicles.Find(vehicleId);
|
|
if (mostRecentOdometer != null && mostRecentOdometer.EndOdometer != vehicle.CurrentOdometer)
|
|
{
|
|
vehicle.CurrentOdometer = mostRecentOdometer.EndOdometer;
|
|
UpdateVehicle(vehicle);
|
|
}
|
|
}
|
|
|
|
public void ValidateOdometerChronology(string vehicleId, int odometer, DateTime date)
|
|
{
|
|
var conflictingLog = GetFirstConflictingOdometerChronologyLog(vehicleId, date, odometer);
|
|
if (conflictingLog != null)
|
|
{
|
|
throw new ChronologicalOrderException(
|
|
String.Format(
|
|
"Odometer of {0} miles on {1:d} conflicts with other logs for vehicle {2}" +
|
|
", including an odometer log of {3} miles on {4:d} reported by {5}.",
|
|
odometer, date, vehicleId, conflictingLog.EndOdometer, conflictingLog.Date, conflictingLog.EmployeeName));
|
|
}
|
|
}
|
|
|
|
public Log GetFirstConflictingOdometerChronologyLog(string vehicleId, DateTime date, int odometer)
|
|
{
|
|
var conflictingLogsQuery =
|
|
from log in _db.Logs
|
|
let existingDate = log.Date
|
|
let existingOdometer = log.EndOdometer
|
|
where
|
|
log.VehicleId == vehicleId &&
|
|
!(existingDate == date
|
|
|| existingOdometer == odometer
|
|
|| existingDate < date && existingOdometer < odometer
|
|
|| existingDate > date && existingOdometer > odometer)
|
|
orderby existingOdometer descending
|
|
select log;
|
|
|
|
return conflictingLogsQuery.FirstOrDefault();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Employee Name
|
|
|
|
public string GetEmployeeNameCorrected(string term)
|
|
{
|
|
var names = GetEmployeeNames().ToList();
|
|
return (from potentialName in names
|
|
let potentialNameClean = Regex.Replace(potentialName, @"\s*\(.*\)", "").ToLower()
|
|
let name = term.ToLower()
|
|
let similarity = Algorithms.Similarity(potentialNameClean, name)
|
|
where similarity > .7 || potentialNameClean.StartsWith(name)
|
|
orderby similarity descending
|
|
select potentialName).FirstOrDefault();
|
|
}
|
|
|
|
public IEnumerable<string> GetEmployeeNamesAutocomplete(string term)
|
|
{
|
|
var termLower = term.ToLower();
|
|
return from name in GetEmployeeNames()
|
|
let nameLower = name.ToLower()
|
|
where nameLower.StartsWith(termLower) || Algorithms.Similarity(nameLower, termLower) > .8
|
|
select name;
|
|
}
|
|
|
|
public IEnumerable<string> GetEmployeeNames()
|
|
{
|
|
var names = _db.Vehicles.Select(v => v.Assigned).Where(n => !String.IsNullOrEmpty(n) && n != "to be sold");
|
|
var logNames =
|
|
(from l in _db.Logs
|
|
select l.EmployeeName);
|
|
return names.Union(logNames).Distinct();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region City
|
|
|
|
public string GetCitiesCorrected(string term)
|
|
{
|
|
var termLower = term.ToLower();
|
|
return (from city in GetCities().ToList()
|
|
let cityToLower = city.Name.ToLower()
|
|
let similarity = Algorithms.Similarity(cityToLower, termLower)
|
|
where city.Name.StartsWith(term) || Algorithms.Similarity(cityToLower, termLower) > .8
|
|
select city.Name).FirstOrDefault();
|
|
}
|
|
|
|
public IEnumerable<string> GetCitiesAutocomplete(string term)
|
|
{
|
|
return
|
|
from city in GetCities()
|
|
where city.Name.StartsWith(term)
|
|
select city.Name;
|
|
}
|
|
|
|
private IQueryable<City> GetCities()
|
|
{
|
|
return _db.Cities;
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void Dispose()
|
|
{
|
|
_db.Dispose();
|
|
}
|
|
}
|
|
} |