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" />
</ItemGroup>
<ItemGroup>
<Compile Include="ExportProviderDetails.cs" />
<Compile Include="IConfigFilePathProvider.cs" />
<Compile Include="IContainerHelper.cs" />
<Compile Include="IDbProvider.cs" />
<Compile Include="IExportManager.cs" />
<Compile Include="ILoggerService.cs" />
<Compile Include="IFileExporter.cs" />
<Compile Include="IPluginInformation.cs" />
<Compile Include="IPluginLocator.cs" />
<Compile Include="IPluginPathProvider.cs" />

View File

@ -16,5 +16,8 @@ namespace Interfaces
public int TimePenaltyPerSatellite { get; set; }
public bool HotSeatEnabled { 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 int RaceSessionId { get; set; }
public int TransponderToken { 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
{
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;
_logger = logger;
Get[""] = args => LogNewLapSatelliteTime();
}
@ -23,6 +25,7 @@ namespace RaceLapTimer.ApiControllers
TransponderToken = transponderToken,
LapTimeMs = lapTimeMs
};
_logger.Trace("New Lap Time for Transponder Token: {0}. Lap Time: {1}", transponderToken, lapTimeMs);
_provider.StoreTransponderLog(logObj);
return HttpStatusCode.OK;
}

View File

@ -1,4 +1,5 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using Interfaces;
using Nancy;
using Nancy.Authentication.Forms;
@ -6,8 +7,10 @@ using Nancy.Bootstrapper;
using Nancy.Bootstrappers.Ninject;
using Nancy.Conventions;
using Nancy.Diagnostics;
using Nancy.ViewEngines.Razor;
using Ninject;
using RaceLapTimer.Extensions;
using RaceLapTimer.Extensions.FileExport;
using RaceLapTimer.Extensions.Notifications;
using RaceLapTimer.Extensions.SystemControl;
using RaceLapTimer.Extensions.TransponderUtilities;
@ -41,8 +44,22 @@ namespace RaceLapTimer
container.Bind<ISystemControlManager>().To<SystemControlManager>().InSingletonScope();
container.Bind<ITransponderUtilityManager>().To<TransponderUtilityManager>().InSingletonScope();
var tu = container.Get<ITransponderUtilityManager>();
var id = tu.GetLastScannedId();
//unused code, trying to get a friggin html to pdf converter working well. :(
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)
@ -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.Collections.Generic;
using System.Dynamic;
using System.IO;
using Interfaces;
using Nancy;
using Nancy.Responses;
using Nancy.ViewEngines;
namespace RaceLapTimer.Modules
{
public class HistoryModule:NancyModule
{
public HistoryModule():base("/history")
private IContainerHelper _helper;
public HistoryModule(IContainerHelper helper):base("/history")
{
_helper = helper;
Get[""] = args => GetHistoryHomePage();
Get["/pdf/{id}"] = args => GetPdfFile(args);
}
@ -34,14 +39,19 @@ namespace RaceLapTimer.Modules
content = reader.ReadToEnd();
}
};
var filePath = string.Empty;
throw new NotImplementedException();
return Response.AsFile(filePath);
}
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" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\FileExport\FileExportManager.cs" />
<Compile Include="Extensions\PluginLocator.cs" />
<Compile Include="Extensions\SystemControl\SystemControlManager.cs" />
<Compile Include="Extensions\TransponderUtilities\TransponderUtilityManager.cs" />

View File

@ -9,6 +9,7 @@ namespace RaceLapTimer
{
private readonly List<Pilot> _pilots;
private readonly List<RaceSession> _sessions;
private readonly List<SatelliteLog> _transponderLogs;
public TestProvider()
{
@ -55,7 +56,7 @@ namespace RaceLapTimer
#region Session 2
new RaceSession
{
Active = true,
Active = false,
CreatedAt = DateTime.UtcNow,
HotSeatEnabled = false,
Id = 2,
@ -114,6 +115,7 @@ namespace RaceLapTimer
#endregion
};
#endregion
_transponderLogs = new List<SatelliteLog>();
}
public List<RaceSession> GetRaceSessions(bool getActive)
@ -121,6 +123,11 @@ namespace RaceLapTimer
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)
{
_sessions.Add(session);
@ -209,7 +216,39 @@ namespace RaceLapTimer
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,4 +1,5 @@
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>

@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
@{
Layout = "razor-layout.cshtml";
ViewBag.Title = "History";
@ -26,7 +27,25 @@
<td class="text-center col-md-1 vertical-center">5</td>
<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-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 class="vertical-center">
<span data-bind="attr:{id:'expIcon'+id()}" class="glyphicon glyphicon-plus"></span>
@ -48,8 +67,8 @@
<tbody>
<tr>
<td data-bind="text: maxLaps">12</td>
<td>30.0s</td>
<td>a pilot</td>
<td data-bind="text: fastestLap()">30.0s</td>
<td data-bind="text: fastestPilotName()">a pilot</td>
<td>35.0s</td>
</tr>
</tbody>

View File

@ -15,4 +15,7 @@
self.timePenaltyPerSatellite = ko.observable(data.timePenaltyPerSatellite);
self.hotSeatEnabled = ko.observable(data.hotSeatEnabled);
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) {
$(e).toggleClass("glyphicon-plus glyphicon-minus");
}
};
self.CreateExport = function(sessionId, exportType) {
console.log(sessionId);
console.log(exportType);
};
};
ko.applyBindings(new ViewModel());

View File

@ -93,7 +93,13 @@
<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 "$(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>
<!-- 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.