add code for exporting historic races to a file.

this includes creating an export manager, file exporter interface (WIP)
the History pages now use the Model to have a dynamic list of exporters based on plugins available through export manager.
extended race session object to have FastestLap FastestPilotId and FastestPilotName.
This commit is contained in:
chris.watts90@outlook.com 2017-07-05 22:59:56 +01:00
parent 80894ffe0d
commit 96e067153b
16 changed files with 226 additions and 28 deletions

View File

@ -0,0 +1,7 @@
namespace Interfaces
{
public class ExportProviderDetails
{
public string Type { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.IO;
namespace Interfaces
{
public interface IExportManager
{
List<IFileExporter> GetExporters();
Stream Export(string fileContent, string baseUrl);
List<ExportProviderDetails> GetExporterDetails();
}
}

View File

@ -0,0 +1,11 @@
using System.IO;
namespace Interfaces
{
public interface IFileExporter
{
Stream Export(string fileContent, string baseUrl);
ExportProviderDetails GetDetails();
}
}

View File

@ -40,10 +40,13 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ExportProviderDetails.cs" />
<Compile Include="IConfigFilePathProvider.cs" /> <Compile Include="IConfigFilePathProvider.cs" />
<Compile Include="IContainerHelper.cs" /> <Compile Include="IContainerHelper.cs" />
<Compile Include="IDbProvider.cs" /> <Compile Include="IDbProvider.cs" />
<Compile Include="IExportManager.cs" />
<Compile Include="ILoggerService.cs" /> <Compile Include="ILoggerService.cs" />
<Compile Include="IFileExporter.cs" />
<Compile Include="IPluginInformation.cs" /> <Compile Include="IPluginInformation.cs" />
<Compile Include="IPluginLocator.cs" /> <Compile Include="IPluginLocator.cs" />
<Compile Include="IPluginPathProvider.cs" /> <Compile Include="IPluginPathProvider.cs" />

View File

@ -16,5 +16,8 @@ namespace Interfaces
public int TimePenaltyPerSatellite { get; set; } public int TimePenaltyPerSatellite { get; set; }
public bool HotSeatEnabled { get; set; } public bool HotSeatEnabled { get; set; }
public int IdleTimeSeconds { get; set; } public int IdleTimeSeconds { get; set; }
public double FastestLap { get; set; }
public int FastestPilotId { get; set; }
public string FastestPilotName { get; set; }
} }
} }

View File

@ -2,7 +2,9 @@
{ {
public class SatelliteLog public class SatelliteLog
{ {
public int RaceSessionId { get; set; }
public int TransponderToken { get; set; } public int TransponderToken { get; set; }
public int LapTimeMs { get; set; } public int LapTimeMs { get; set; }
public int UserId { get; set; }
} }
} }

View File

@ -7,10 +7,12 @@ namespace RaceLapTimer.ApiControllers
public class SatelliteApiModule:NancyModule public class SatelliteApiModule:NancyModule
{ {
private readonly IDbProvider _provider; private readonly IDbProvider _provider;
public SatelliteApiModule(IDbProvider provider) : base("/api/satellite") private readonly ILoggerService _logger;
public SatelliteApiModule(IDbProvider provider, ILoggerService logger) : base("/api/v1/satellite")
{ {
_provider = provider; _provider = provider;
_logger = logger;
Get[""] = args => LogNewLapSatelliteTime(); Get[""] = args => LogNewLapSatelliteTime();
} }
@ -23,6 +25,7 @@ namespace RaceLapTimer.ApiControllers
TransponderToken = transponderToken, TransponderToken = transponderToken,
LapTimeMs = lapTimeMs LapTimeMs = lapTimeMs
}; };
_logger.Trace("New Lap Time for Transponder Token: {0}. Lap Time: {1}", transponderToken, lapTimeMs);
_provider.StoreTransponderLog(logObj); _provider.StoreTransponderLog(logObj);
return HttpStatusCode.OK; return HttpStatusCode.OK;
} }

View File

@ -1,4 +1,5 @@
using System.IO; using System.Collections.Generic;
using System.IO;
using Interfaces; using Interfaces;
using Nancy; using Nancy;
using Nancy.Authentication.Forms; using Nancy.Authentication.Forms;
@ -6,8 +7,10 @@ using Nancy.Bootstrapper;
using Nancy.Bootstrappers.Ninject; using Nancy.Bootstrappers.Ninject;
using Nancy.Conventions; using Nancy.Conventions;
using Nancy.Diagnostics; using Nancy.Diagnostics;
using Nancy.ViewEngines.Razor;
using Ninject; using Ninject;
using RaceLapTimer.Extensions; using RaceLapTimer.Extensions;
using RaceLapTimer.Extensions.FileExport;
using RaceLapTimer.Extensions.Notifications; using RaceLapTimer.Extensions.Notifications;
using RaceLapTimer.Extensions.SystemControl; using RaceLapTimer.Extensions.SystemControl;
using RaceLapTimer.Extensions.TransponderUtilities; using RaceLapTimer.Extensions.TransponderUtilities;
@ -41,8 +44,22 @@ namespace RaceLapTimer
container.Bind<ISystemControlManager>().To<SystemControlManager>().InSingletonScope(); container.Bind<ISystemControlManager>().To<SystemControlManager>().InSingletonScope();
container.Bind<ITransponderUtilityManager>().To<TransponderUtilityManager>().InSingletonScope(); container.Bind<ITransponderUtilityManager>().To<TransponderUtilityManager>().InSingletonScope();
var tu = container.Get<ITransponderUtilityManager>(); //unused code, trying to get a friggin html to pdf converter working well. :(
var id = tu.GetLastScannedId(); container.Bind<IExportManager>().To<FileExportManager>().InSingletonScope();
//var testHtml = "<html><body><img src=\"http://knockoutjs.com/img/ko-logo.png\" alt=\"Syncfusion_logo\" width=\"200\" height=\"70\"><p>Hello World</p></body></html>";
//var tu = container.Get<IExportManager>();
//var id = tu.Export(testHtml, "http://localhost:8800");
//File.Delete("testpdf.pdf");
//using (FileStream fs = new FileStream("testpdf.pdf", FileMode.CreateNew))
//{
// id.Seek(0, SeekOrigin.Begin);
// id.CopyTo(fs);
// id.Close();
// id.Dispose();
// fs.Flush(true);
// fs.Close();
//}
} }
protected override void ConfigureRequestContainer(IKernel container, NancyContext context) protected override void ConfigureRequestContainer(IKernel container, NancyContext context)
@ -98,4 +115,21 @@ namespace RaceLapTimer
); );
} }
} }
public class RazorConfig : IRazorConfiguration
{
public IEnumerable<string> GetAssemblyNames()
{
yield return "Interfaces";
}
public IEnumerable<string> GetDefaultNamespaces()
{
yield return "Interfaces";
}
public bool AutoIncludeModelNamespace
{
get { return true; }
}
}
} }

View File

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Interfaces;
namespace RaceLapTimer.Extensions.FileExport
{
class FileExportManager:IExportManager
{
private readonly List<IFileExporter> _exporters;
public FileExportManager(IPluginLocator locator)
{
_exporters = locator.Locate<IFileExporter>();
}
public List<IFileExporter> GetExporters()
{
return _exporters;
}
public Stream Export(string fileContent, string baseUrl)
{
if(_exporters.Any())
return _exporters.First().Export(fileContent, baseUrl);
return new MemoryStream();
}
public List<ExportProviderDetails> GetExporterDetails()
{
var ret = new List<ExportProviderDetails>();
foreach (var exporter in _exporters)
{
ret.Add(exporter.GetDetails());
}
return ret;
}
}
}

View File

@ -1,15 +1,20 @@
using System; using System;
using System.Collections.Generic;
using System.Dynamic; using System.Dynamic;
using System.IO; using System.IO;
using Interfaces;
using Nancy; using Nancy;
using Nancy.Responses;
using Nancy.ViewEngines; using Nancy.ViewEngines;
namespace RaceLapTimer.Modules namespace RaceLapTimer.Modules
{ {
public class HistoryModule:NancyModule public class HistoryModule:NancyModule
{ {
public HistoryModule():base("/history") private IContainerHelper _helper;
public HistoryModule(IContainerHelper helper):base("/history")
{ {
_helper = helper;
Get[""] = args => GetHistoryHomePage(); Get[""] = args => GetHistoryHomePage();
Get["/pdf/{id}"] = args => GetPdfFile(args); Get["/pdf/{id}"] = args => GetPdfFile(args);
} }
@ -34,14 +39,19 @@ namespace RaceLapTimer.Modules
content = reader.ReadToEnd(); content = reader.ReadToEnd();
} }
}; };
var filePath = string.Empty;
throw new NotImplementedException(); throw new NotImplementedException();
return Response.AsFile(filePath);
} }
private dynamic GetHistoryHomePage() private dynamic GetHistoryHomePage()
{ {
dynamic model = new ExpandoObject();
var eM = _helper.Get<IExportManager>();
model.ExportTypes = eM.GetExporterDetails();//new List<ExportProviderDetails> {new ExportProviderDetails {Type="PDF"} };
return View["HistoryIndex.cshtml"]; return View["HistoryIndex.cshtml", model];
} }
} }
} }

View File

@ -114,6 +114,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Extensions\FileExport\FileExportManager.cs" />
<Compile Include="Extensions\PluginLocator.cs" /> <Compile Include="Extensions\PluginLocator.cs" />
<Compile Include="Extensions\SystemControl\SystemControlManager.cs" /> <Compile Include="Extensions\SystemControl\SystemControlManager.cs" />
<Compile Include="Extensions\TransponderUtilities\TransponderUtilityManager.cs" /> <Compile Include="Extensions\TransponderUtilities\TransponderUtilityManager.cs" />

View File

@ -9,6 +9,7 @@ namespace RaceLapTimer
{ {
private readonly List<Pilot> _pilots; private readonly List<Pilot> _pilots;
private readonly List<RaceSession> _sessions; private readonly List<RaceSession> _sessions;
private readonly List<SatelliteLog> _transponderLogs;
public TestProvider() public TestProvider()
{ {
@ -55,7 +56,7 @@ namespace RaceLapTimer
#region Session 2 #region Session 2
new RaceSession new RaceSession
{ {
Active = true, Active = false,
CreatedAt = DateTime.UtcNow, CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false, HotSeatEnabled = false,
Id = 2, Id = 2,
@ -114,6 +115,7 @@ namespace RaceLapTimer
#endregion #endregion
}; };
#endregion #endregion
_transponderLogs = new List<SatelliteLog>();
} }
public List<RaceSession> GetRaceSessions(bool getActive) public List<RaceSession> GetRaceSessions(bool getActive)
@ -121,6 +123,11 @@ namespace RaceLapTimer
return getActive ? _sessions.Where(x => x.Active == getActive).ToList() : _sessions; return getActive ? _sessions.Where(x => x.Active == getActive).ToList() : _sessions;
} }
public RaceSession GetRaceSession(int id)
{
return _sessions.FirstOrDefault(x => x.Id == id);
}
public bool CreateRaceSession(RaceSession session) public bool CreateRaceSession(RaceSession session)
{ {
_sessions.Add(session); _sessions.Add(session);
@ -209,7 +216,39 @@ namespace RaceLapTimer
public void StoreTransponderLog(SatelliteLog log) public void StoreTransponderLog(SatelliteLog log)
{ {
//do nothing. var sessionId = GetCurrentRaceSessionId();
var session = GetRaceSession(sessionId);
log.RaceSessionId = sessionId;
//Store the log..
_transponderLogs.Add(log);
// check fastest pilot!
var fastestLapTime = GetFastestLapTimeMs(sessionId);
var user = GetUserByTransponder(fastestLapTime.TransponderToken);
session.FastestLap = fastestLapTime.LapTimeMs;
session.FastestPilotId = user.Id;
session.FastestPilotName = user.Name;
}
private SatelliteLog GetFastestLapTimeMs(int sessionId)
{
var sessionLogs = _transponderLogs.Where(x => x.RaceSessionId == sessionId);
sessionLogs = sessionLogs.OrderByDescending(x => x.LapTimeMs);
var fastestLog = sessionLogs.First();
return fastestLog;
}
private int GetCurrentRaceSessionId()
{
var activeSessions = _sessions.Where(x => x.Active).ToList();
if (activeSessions.Any() || activeSessions.Count > 1)
return -1; //either dont have any, or have more than 1!!!
var session = activeSessions.First();
return session.Id;
}
private Pilot GetUserByTransponder(int token)
{
return _pilots.FirstOrDefault(x => x.TransponderToken == token);
} }
} }
} }

View File

@ -1,10 +1,11 @@
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic> 
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
@{ @{
Layout = "razor-layout.cshtml"; Layout = "razor-layout.cshtml";
ViewBag.Title = "History"; ViewBag.Title = "History";
} }
<h3>@ViewBag.Title</h3> <h3>@ViewBag.Title</h3>
<link rel="stylesheet" href="css/site.css"/> <link rel="stylesheet" href="css/site.css" />
<table class="table table-striped dataTable"> <table class="table table-striped dataTable">
<thead> <thead>
@ -23,10 +24,28 @@
<td class="col-md-2 vertical-center"><span data-bind="text: createdDate"></span></td> <td class="col-md-2 vertical-center"><span data-bind="text: createdDate"></span></td>
<td class="vertical-center" data-bind="text: title">Race Name 1</td> <td class="vertical-center" data-bind="text: title">Race Name 1</td>
<td class="col-md-1 vertical-center" data-bind="text: mode">standard</td> <td class="col-md-1 vertical-center" data-bind="text: mode">standard</td>
<td class="text-center col-md-1 vertical-center" >5</td> <td class="text-center col-md-1 vertical-center">5</td>
<td class="col-md-3 text-center"> <td class="col-md-3 text-center">
<button type="button" class="btn btn-warning" data-bind="click: $root.deleteSession">Delete</button> <button type="button" class="btn btn-warning" data-bind="click: $root.deleteSession">Delete</button>
<button type="button" class="btn btn-default">Export as PDF</button> <div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
@{ if (Model.ExportTypes == null || Model.ExportTypes.Count == 0)
{
@: disabled
}
}>
Export <span class="caret"></span>
</button>
@if (Model.ExportTypes != null)
{
<ul class="dropdown-menu">
@foreach (Interfaces.ExportProviderDetails eT in Model.ExportTypes)
{
<li><a data-bind="click: $root.CreateExport(id(), '@eT.Type')">@eT.Type</a></li>
}
</ul>
}
</div>
</td> </td>
<td class="vertical-center"> <td class="vertical-center">
<span data-bind="attr:{id:'expIcon'+id()}" class="glyphicon glyphicon-plus"></span> <span data-bind="attr:{id:'expIcon'+id()}" class="glyphicon glyphicon-plus"></span>
@ -48,8 +67,8 @@
<tbody> <tbody>
<tr> <tr>
<td data-bind="text: maxLaps">12</td> <td data-bind="text: maxLaps">12</td>
<td>30.0s</td> <td data-bind="text: fastestLap()">30.0s</td>
<td>a pilot</td> <td data-bind="text: fastestPilotName()">a pilot</td>
<td>35.0s</td> <td>35.0s</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -15,4 +15,7 @@
self.timePenaltyPerSatellite = ko.observable(data.timePenaltyPerSatellite); self.timePenaltyPerSatellite = ko.observable(data.timePenaltyPerSatellite);
self.hotSeatEnabled = ko.observable(data.hotSeatEnabled); self.hotSeatEnabled = ko.observable(data.hotSeatEnabled);
self.idleTimeSeconds = ko.observable(data.idleTimeSeconds); self.idleTimeSeconds = ko.observable(data.idleTimeSeconds);
self.fastestLap = ko.observable(data.fastestLap);
self.fastestPilotName = ko.observable(data.fastestPilotName);
self.fastestPilotId = ko.observable(data.fastestPilotId);
} }

View File

@ -15,6 +15,10 @@
}); });
self.toggleIcon = function(e) { self.toggleIcon = function(e) {
$(e).toggleClass("glyphicon-plus glyphicon-minus"); $(e).toggleClass("glyphicon-plus glyphicon-minus");
} };
self.CreateExport = function(sessionId, exportType) {
console.log(sessionId);
console.log(exportType);
};
}; };
ko.applyBindings(new ViewModel()); ko.applyBindings(new ViewModel());

View File

@ -93,7 +93,13 @@
<PostBuildEvent>copy /B /Y "$(SolutionDir)UdpNotifier\$(OutDir)UdpNotifier.dll" "$(TargetDir)Plugins\UdpNotifier.dll" <PostBuildEvent>copy /B /Y "$(SolutionDir)UdpNotifier\$(OutDir)UdpNotifier.dll" "$(TargetDir)Plugins\UdpNotifier.dll"
copy /B /Y "$(SolutionDir)IrDaemonNotifier\$(OutDir)IrDaemonNotifier.dll" "$(TargetDir)Plugins\IrDaemonNotifier.dll" copy /B /Y "$(SolutionDir)IrDaemonNotifier\$(OutDir)IrDaemonNotifier.dll" "$(TargetDir)Plugins\IrDaemonNotifier.dll"
copy /B /Y "$(TargetDir)NLogConfig.xml" "$(TargetDir)Configs\NLogConfig.xml" copy /B /Y "$(TargetDir)NLogConfig.xml" "$(TargetDir)Configs\NLogConfig.xml"
copy /B /Y "$(SolutionDir)RaceLapTimer\$(OutDir)Ninject.Extensions.Xml.dll" "$(TargetDir)Ninject.Extensions.Xml.dll"</PostBuildEvent> copy /B /Y "$(SolutionDir)RaceLapTimer\$(OutDir)Ninject.Extensions.Xml.dll" "$(TargetDir)Ninject.Extensions.Xml.dll"
copy /B /Y "$(SolutionDir)PDFExporter\$(OutDir)PDFExporter.dll" "$(TargetDir)Plugins\PDFExporter.dll"
copy /B /Y "$(SolutionDir)PDFExporter\$(OutDir).dll" "$(TargetDir)Plugins\PDFExporter.dll"
copy /B /Y "$(SolutionDir)PDFExporter\$(OutDir)Syncfusion.HtmlConverter.Base.dll" "$(TargetDir)Syncfusion.HtmlConverter.Base.dll"
copy /B /Y "$(SolutionDir)PDFExporter\$(OutDir)Syncfusion.Pdf.Base.dll" "$(TargetDir)Syncfusion.Pdf.Base.dll"
copy /B /Y "$(SolutionDir)PDFExporter\$(OutDir)Syncfusion.Compression.Base.dll" "$(TargetDir)Syncfusion.Compression.Base.dll"
copy /B /Y "$(SolutionDir)PDFExporter\$(OutDir)Microsoft.mshtml.dll" "$(TargetDir)Microsoft.mshtml.dll"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.