Skip to content

Commit

Permalink
Implement the 'My Flights' report
Browse files Browse the repository at this point in the history
  • Loading branch information
davewalker5 committed Dec 1, 2024
1 parent 904c677 commit a57a5ce
Show file tree
Hide file tree
Showing 18 changed files with 418 additions and 7 deletions.
30 changes: 29 additions & 1 deletion src/FlightRecorder.Api/Controllers/ReportsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public async Task<ActionResult<List<ModelStatistics>>> GetModelStatisticsAsync(s
}

/// <summary>
/// Generate the aircraft model statistics report
/// Generate the flights by month statistics report
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
Expand Down Expand Up @@ -222,5 +222,33 @@ public async Task<ActionResult<List<JobStatus>>> GetJobsAsync(string start, stri
// Convert to a list and return the results
return results;
}

/// <summary>
/// Generate the "My Flights" report
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
[HttpGet]
[Route("myflights/{start}/{end}/{pageNumber}/{pageSize}")]
public async Task<ActionResult<List<MyFlights>>> GetMyFlightsAsync(string start, string end, int pageNumber, int pageSize)
{
// Decode the start and end date and convert them to dates
DateTime startDate = DateTime.ParseExact(HttpUtility.UrlDecode(start), DateTimeFormat, null);
DateTime endDate = DateTime.ParseExact(HttpUtility.UrlDecode(end), DateTimeFormat, null);

// Get the report content
var results = await _factory.MyFlights.GenerateReportAsync(startDate, endDate, pageNumber, pageSize);

if (!results.Any())
{
return NoContent();
}

// Convert to a list and return the results
return results.ToList();
}
}
}
3 changes: 2 additions & 1 deletion src/FlightRecorder.Api/Entities/ReportType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum ReportType
ManufacturerStatistics = 2,
ModelStatistics = 3,
FlightsByMonth = 4,
JobStatus = 5
JobStatus = 5,
MyFlights = 6
}
}
19 changes: 19 additions & 0 deletions src/FlightRecorder.Api/Services/ReportExportService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ protected override async Task ProcessWorkItemAsync(ReportExportWorkItem item, Fl
case ReportType.JobStatus:
await ExportJobStatusAsync(factory, item);
break;
case ReportType.MyFlights:
await ExportMyFlightsAsync(factory, item);
break;
default:
break;
}
Expand Down Expand Up @@ -165,5 +168,21 @@ private async Task ExportJobStatusAsync(FlightRecorderFactory factory, ReportExp
var exporter = new CsvExporter<JobStatus>();
exporter.Export(records, filePath, ',');
}

/// <summary>
/// Export the "My Flights" report
/// </summary>
/// <param name="factory"></param>
/// <param name="item"></param>
/// <returns></returns>
private async Task ExportMyFlightsAsync(FlightRecorderFactory factory, ReportExportWorkItem item)
{
// The third argument to the report generation method is an arbitrarily large value intended
// to return all records
var records = await factory.MyFlights.GenerateReportAsync(item.Start, item.End, 1, int.MaxValue);
var filePath = Path.Combine(_settings.ReportsExportPath, item.FileName);
var exporter = new CsvExporter<MyFlights>();
exporter.Export(records, filePath, ',');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class FlightRecorderFactory
private readonly Lazy<IDateBasedReport<ManufacturerStatistics>> _manufacturerStatistics = null;
private readonly Lazy<IDateBasedReport<ModelStatistics>> _modelStatistics = null;
private readonly Lazy<IDateBasedReport<FlightsByMonth>> _flightsByMonth = null;
private readonly Lazy<IDateBasedReport<MyFlights>> _myFlights = null;

public FlightRecorderDbContext Context { get; private set; }

Expand Down Expand Up @@ -55,6 +56,9 @@ public class FlightRecorderFactory
[ExcludeFromCodeCoverage]
public IDateBasedReport<FlightsByMonth> FlightsByMonth { get { return _flightsByMonth.Value; } }

[ExcludeFromCodeCoverage]
public IDateBasedReport<MyFlights> MyFlights { get { return _myFlights.Value; } }

public FlightRecorderFactory(FlightRecorderDbContext context)
{
// Store the database context
Expand All @@ -81,6 +85,7 @@ public FlightRecorderFactory(FlightRecorderDbContext context)
_manufacturerStatistics = new Lazy<IDateBasedReport<ManufacturerStatistics>>(() => new DateBasedReport<ManufacturerStatistics>(context));
_modelStatistics = new Lazy<IDateBasedReport<ModelStatistics>>(() => new DateBasedReport<ModelStatistics>(context));
_flightsByMonth = new Lazy<IDateBasedReport<FlightsByMonth>>(() => new DateBasedReport<FlightsByMonth>(context));
_myFlights = new Lazy<IDateBasedReport<MyFlights>>(() => new DateBasedReport<MyFlights>(context));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
<EmbeddedResource Include="Sql\ModelStatistics.sql">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Sql\MyFlights.sql">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Factory\" />
Expand Down
17 changes: 17 additions & 0 deletions src/FlightRecorder.BusinessLogic/Sql/MyFlights.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SELECT s.Date,
a.Name AS "Airline",
f.Number,
f.Embarkation,
f.Destination,
ac.Registration,
m.Name AS "Model",
ma.Name AS "Manufacturer"
FROM SIGHTING s
INNER JOIN FLIGHT f on f.Id = s.Flight_Id
INNER JOIN AIRLINE a on a.Id = f.Airline_Id
INNER JOIN AIRCRAFT ac ON ac.Id = s.Aircraft_Id
INNER JOIN MODEL m ON m.Id = ac.Model_Id
INNER JOIN MANUFACTURER ma ON ma.Id = m.Manufacturer_Id
WHERE s.Is_My_Flight = 1
AND s.Date BETWEEN '$from' AND '$to'
ORDER BY s.Date;
2 changes: 2 additions & 0 deletions src/FlightRecorder.Data/FlightRecorderDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public partial class FlightRecorderDbContext : DbContext
public virtual DbSet<ManufacturerStatistics> ManufacturerStatistics { get; set; }
public virtual DbSet<ModelStatistics> ModelStatistics { get; set; }
public virtual DbSet<FlightsByMonth> FlightsByMonth { get; set; }
public virtual DbSet<MyFlights> MyFlights { get; set; }

public FlightRecorderDbContext(DbContextOptions<FlightRecorderDbContext> options) : base(options)
{
Expand All @@ -43,6 +44,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<ManufacturerStatistics>().HasNoKey();
modelBuilder.Entity<ModelStatistics>().HasNoKey();
modelBuilder.Entity<FlightsByMonth>().HasNoKey();
modelBuilder.Entity<MyFlights>().HasNoKey();

modelBuilder.Entity<Aircraft>(entity =>
{
Expand Down
36 changes: 36 additions & 0 deletions src/FlightRecorder.Entities/Reporting/MyFlights.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using FlightRecorder.Entities.Attributes;
using Microsoft.EntityFrameworkCore;
using System;
using System.Diagnostics.CodeAnalysis;

namespace FlightRecorder.Entities.Reporting
{
[Keyless]
[ExcludeFromCodeCoverage]
public class MyFlights
{
[Export("Date", 1)]
public DateTime Date { get; set; }

[Export("Airline", 2)]
public string Airline { get; set; }

[Export("Number", 3)]
public string Number { get; set; }

[Export("Embarkation", 4)]
public string Embarkation { get; set; }

[Export("Destination", 5)]
public string Destination { get; set; }

[Export("Registration", 6)]
public string Registration { get; set; }

[Export("Model", 7)]
public string Model { get; set; }

[Export("Manufacturer", 7)]
public string Manufacturer { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/FlightRecorder.Mvc/Api/ReportsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ public async Task<List<FlightsByMonth>> FlightsByMonthAsync(DateTime? from, Date
public async Task<List<JobStatus>> JobStatusAsync(DateTime? from, DateTime? to, int pageNumber, int pageSize)
=> await DateBasedReportAsync<JobStatus>("JobStatus", from, to, pageNumber, pageSize);

/// <summary>
/// Return the "My Flights" report
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
public async Task<List<MyFlights>> MyFlightsAsync(DateTime? from, DateTime? to, int pageNumber, int pageSize)
=> await DateBasedReportAsync<MyFlights>("MyFlights", from, to, pageNumber, pageSize);

/// <summary>
/// Return a date-based statistics report
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
using FlightRecorder.Mvc.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;

namespace FlightRecorder.Mvc.Controllers
{
Expand Down
94 changes: 94 additions & 0 deletions src/FlightRecorder.Mvc/Controllers/MyFlightsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using FlightRecorder.Mvc.Api;
using FlightRecorder.Mvc.Configuration;
using FlightRecorder.Mvc.Entities;
using FlightRecorder.Mvc.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace FlightRecorder.Mvc.Controllers
{
public class MyFlightsController : Controller
{
private readonly ReportsClient _reportsClient;
private readonly ExportClient _exportClient;
private readonly IOptions<AppSettings> _settings;

public MyFlightsController(
ReportsClient reportsClient,
ExportClient exportsClient,
IOptions<AppSettings> settings)
{
_reportsClient = reportsClient;
_exportClient = exportsClient;
_settings = settings;
}

/// <summary>
/// Serve the empty report page
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Index()
{
MyFlightsViewModel model = new MyFlightsViewModel
{
PageNumber = 1
};
return View(model);
}

/// <summary>
/// Respond to a POST event triggering the report generation
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(MyFlightsViewModel model)
{
if (ModelState.IsValid)
{
int page = model.PageNumber;
switch (model.Action)
{
case ControllerActions.ActionPreviousPage:
page -= 1;
break;
case ControllerActions.ActionNextPage:
page += 1;
break;
case ControllerActions.ActionSearch:
page = 1;
break;
default:
break;
}

// Need to clear model state here or the page number that was posted
// is returned and page navigation doesn't work correctly. So, capture
// and amend the page number, above, then apply it, below
ModelState.Clear();

DateTime start = !string.IsNullOrEmpty(model.From) ? DateTime.Parse(model.From) : DateTime.MinValue;
DateTime end = !string.IsNullOrEmpty(model.To) ? DateTime.Parse(model.To) : DateTime.MaxValue;

List<MyFlights> records = await _reportsClient.MyFlightsAsync(start, end, page, _settings.Value.SearchPageSize);
model.SetRecords(records, page, _settings.Value.SearchPageSize);
}

return View(model);
}

/// <summary>
/// Request export of the report
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> Export([FromBody] MyFlightsViewModel model)
{
await _exportClient.ExportReport<MyFlights>(model);
return Ok();
}
}
}
14 changes: 14 additions & 0 deletions src/FlightRecorder.Mvc/Entities/MyFlights.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace FlightRecorder.Mvc.Entities
{
public class MyFlights
{
public DateTime Date { get; set; }
public string Airline { get; set; }
public string Number { get; set; }
public string Embarkation { get; set; }
public string Destination { get; set; }
public string Registration { get; set; }
public string Model { get; set; }
public string Manufacturer { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/FlightRecorder.Mvc/Entities/ReportDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public static class ReportDefinitions
new ReportDefinition(ReportType.ManufacturerStatistics, typeof(ManufacturerStatistics), "Manufacturer Statistics"),
new ReportDefinition(ReportType.ModelStatistics, typeof(ModelStatistics), "Model Statistics"),
new ReportDefinition(ReportType.FlightsByMonth, typeof(FlightsByMonth), "Flights By Month"),
new ReportDefinition(ReportType.JobStatus, typeof(JobStatus), "Job Status")
new ReportDefinition(ReportType.JobStatus, typeof(JobStatus), "Job Status"),
new ReportDefinition(ReportType.MyFlights, typeof(MyFlights), "My Flights")
};
}
}
3 changes: 2 additions & 1 deletion src/FlightRecorder.Mvc/Entities/ReportType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum ReportType
ManufacturerStatistics = 2,
ModelStatistics = 3,
FlightsByMonth = 4,
JobStatus = 5
JobStatus = 5,
MyFlights = 6
}
}
8 changes: 8 additions & 0 deletions src/FlightRecorder.Mvc/Models/MyFlightsViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FlightRecorder.Mvc.Entities;

namespace FlightRecorder.Mvc.Models
{
public class MyFlightsViewModel : DateBasedReportViewModelBase<MyFlights>
{
}
}
Loading

0 comments on commit a57a5ce

Please sign in to comment.