Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sql/clear-down-ports.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DELETE FROM VOYAGE_EVENT;
DELETE FROM PORT;

DELETE FROM SQLITE_SEQUENCE WHERE NAME='VOYAGE_EVENT';
DELETE FROM SQLITE_SEQUENCE WHERE NAME='PORT';
6 changes: 6 additions & 0 deletions sql/prune-ports.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DELETE FROM PORT
WHERE Code NOT IN (
SELECT DISTINCT( p.Code )
FROM PORT p
INNER JOIN VOYAGE_EVENT ve ON ve.Port_Id = p.Id
);
14 changes: 13 additions & 1 deletion src/ShippingRecorder.Api/Controllers/ExportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class ExportController : Controller
private readonly IBackgroundQueue<VesselTypeExportWorkItem> _vesselTypeQueue;
private readonly IBackgroundQueue<VoyageExportWorkItem> _voyageQueue;
private readonly IBackgroundQueue<SightingExportWorkItem> _sightingQueue;
private readonly IBackgroundQueue<PortExportWorkItem> _portQueue;

public ExportController(
IBackgroundQueue<CountryExportWorkItem> countryQueue,
Expand All @@ -26,7 +27,8 @@ public ExportController(
IBackgroundQueue<VesselExportWorkItem> vesselQueue,
IBackgroundQueue<VesselTypeExportWorkItem> vesselTypeQueue,
IBackgroundQueue<VoyageExportWorkItem> voyageQueue,
IBackgroundQueue<SightingExportWorkItem> sightingQueue)
IBackgroundQueue<SightingExportWorkItem> sightingQueue,
IBackgroundQueue<PortExportWorkItem> portQueue)
{
_countryQueue = countryQueue;
_locationQueue = locationQueue;
Expand All @@ -35,6 +37,7 @@ public ExportController(
_vesselTypeQueue = vesselTypeQueue;
_voyageQueue = voyageQueue;
_sightingQueue = sightingQueue;
_portQueue = portQueue;
}

[HttpPost]
Expand Down Expand Up @@ -99,5 +102,14 @@ public IActionResult ExportVoyages([FromBody] VoyageExportWorkItem item)
_voyageQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("ports")]
public IActionResult ExportPorts([FromBody] PortExportWorkItem item)
{
item.JobName = "Port Export";
_portQueue.Enqueue(item);
return Accepted();
}
}
}
8 changes: 8 additions & 0 deletions src/ShippingRecorder.Api/Entities/PortExportWorkItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using ShippingRecorder.Entities.Jobs;

namespace ShippingRecorder.Api.Entities
{
public class PortExportWorkItem : ExportWorkItem
{
}
}
4 changes: 4 additions & 0 deletions src/ShippingRecorder.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ public static void Main(string[] args)
builder.Services.AddSingleton<IBackgroundQueue<PortImportWorkItem>, BackgroundQueue<PortImportWorkItem>>();
builder.Services.AddHostedService<PortImportService>();

// Add the port exporter hosted service
builder.Services.AddSingleton<IBackgroundQueue<PortExportWorkItem>, BackgroundQueue<PortExportWorkItem>>();
builder.Services.AddHostedService<PortExportService>();

// Add the voyage importer hosted service
builder.Services.AddSingleton<IBackgroundQueue<VoyageImportWorkItem>, BackgroundQueue<VoyageImportWorkItem>>();
builder.Services.AddHostedService<VoyageImportService>();
Expand Down
46 changes: 46 additions & 0 deletions src/ShippingRecorder.Api/Services/PortExportService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using ShippingRecorder.Api.Entities;
using ShippingRecorder.Api.Interfaces;
using ShippingRecorder.DataExchange.Export;
using Microsoft.Extensions.Options;
using ShippingRecorder.Entities.Interfaces;
using ShippingRecorder.Entities.Config;

namespace ShippingRecorder.Api.Services
{
public class PortExportService : BackgroundQueueProcessor<PortExportWorkItem>
{
private readonly ShippingRecorderApplicationSettings _settings;

public PortExportService(
ILogger<BackgroundQueueProcessor<PortExportWorkItem>> logger,
IBackgroundQueue<PortExportWorkItem> queue,
IServiceScopeFactory serviceScopeFactory,
IOptions<ShippingRecorderApplicationSettings> settings)
: base(logger, queue, serviceScopeFactory)
{
_settings = settings.Value;
}

/// <summary>
/// Export the data
/// </summary>
/// <param name="item"></param>
/// <param name="factory"></param>
/// <returns></returns>
protected override async Task ProcessWorkItemAsync(PortExportWorkItem item, IShippingRecorderFactory factory)
{
// Get the list of items to export
MessageLogger.LogInformation("Retrieving ports for export");
var ports = await factory.Ports.ListAsync(x => true, 1, int.MaxValue).ToListAsync();

// Get the full path to the export file
var filePath = Path.Combine(_settings.ExportPath, item.FileName);

// Export the items
MessageLogger.LogInformation($"Exporting {ports.Count} ports to {filePath}");
var exporter = new PortExporter(factory);
await exporter.ExportAsync(ports, filePath);
MessageLogger.LogInformation("Port export completed");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ public ManagerCommandLineParser(IHelpGenerator generator) : base(generator)
Add(CommandLineOptionType.ImportCountries, false, "--import-countries", "-ic", "Import countries from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportLocations, false, "--import-locations", "-il", "Import locations from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportOperators, false, "--import-operators", "-io", "Import operators from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportVesselTypes, false, "--import-vessel-types", "-ivt", "Import vessel types from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportPorts, false, "--import-ports", "-ip", "Import ports from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportSightings, false, "--import-sightings", "-is", "Import sightings from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportVessels, false, "--import-vessels", "-iv", "Import vessels from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportVesselTypes, false, "--import-vessel-types", "-ivt", "Import vessel types from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportVoyages, false, "--import-voyages", "-ivo", "Import voyages from a CSV file", 1, 1);
Add(CommandLineOptionType.ImportSightings, false, "--import-sightings", "-is", "Import sightings from a CSV file", 1, 1);
Add(CommandLineOptionType.ExportCountries, false, "--export-countries", "-ec", "Export countries to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportLocations, false, "--export-locations", "-el", "Export locations to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportOperators, false, "--export-operators", "-eo", "Export operators to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportVesselTypes, false, "--export-vessel-typess", "-evt", "Export vessel types to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportPorts, false, "--export-ports", "-ep", "Export ports to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportSightings, false, "--export-sightings", "-es", "Export sightings to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportVessels, false, "--export-vessels", "-ev", "Export vessels to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportVesselTypes, false, "--export-vessel-typess", "-evt", "Export vessel types to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportVoyages, false, "--export-voyages", "-evo", "Export voyages to a CSV file", 1, 1);
Add(CommandLineOptionType.ExportSightings, false, "--export-sightings", "-es", "Export sightings to a CSV file", 1, 1);
Add(CommandLineOptionType.AddUser, false, "--add-user", "-au", "Add a user to the database", 2, 2);
Add(CommandLineOptionType.SetPassword, false, "--set-password", "-sp", "Set the password for an existing user", 2, 2);
Add(CommandLineOptionType.DeleteUser, false, "--delete-user", "-du", "Delete an existing user", 1, 1);
Expand Down
16 changes: 14 additions & 2 deletions src/ShippingRecorder.Client/ApiClient/PortClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class PortClient : ShippingRecorderClientBase, IPortClient
{
private const string RouteKey = "Port";
private const string ImportRouteKey = "ImportPort";
private const string ExportRouteKey = "ExportPort";

public PortClient(
IShippingRecorderHttpClient client,
Expand All @@ -36,8 +37,6 @@ public async Task<Port> GetAsync(string code)
string baseRoute = @$"{Settings.ApiRoutes.First(r => r.Name == RouteKey).Route}";
var route = $"{baseRoute}/unlocode/{code}";
string json = await SendDirectAsync(route, null, HttpMethod.Get);

// TODO:
Port port = Deserialize<Port>(json);
return port;
}
Expand Down Expand Up @@ -142,5 +141,18 @@ public async Task ImportFromFileContentAsync(string content)
/// <returns></returns>
public async Task ImportFromFileAsync(string filePath)
=> await ImportFromFileContentAsync(File.ReadAllText(filePath));

/// <summary>
/// Request an export of ports to a named file in the export port
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task ExportAsync(string fileName)
{
dynamic data = new{ FileName = fileName };
var json = Serialize(data);
await SendIndirectAsync(ExportRouteKey, json, HttpMethod.Post);
}
}
}
2 changes: 1 addition & 1 deletion src/ShippingRecorder.Client/Interfaces/IPortClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace ShippingRecorder.Client.Interfaces
{
public interface IPortClient : IPortsRetriever, IImporter
public interface IPortClient : IPortsRetriever, IImporter, IExporter
{
Task<Port> AddAsync(long countryId, string code, string name);
Task DeleteAsync(long id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace ShippingRecorder.DataExchange.Entities
public class ExportablePort : ExportableEntityBase
{
/// <summary>
/// Country Code
/// UN/LOCODE
/// Port Name
/// </summary>
Expand Down
62 changes: 62 additions & 0 deletions src/ShippingRecorder.DataExchange/Export/PortExporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using ShippingRecorder.DataExchange.Entities;
using ShippingRecorder.DataExchange.Interfaces;
using ShippingRecorder.DataExchange.Extensions;
using ShippingRecorder.Entities.Interfaces;
using System;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using ShippingRecorder.Entities.Db;

namespace ShippingRecorder.DataExchange.Export
{
public class PortExporter : IPortExporter
{
private readonly IShippingRecorderFactory _factory;

public event EventHandler<ExportEventArgs<ExportablePort>> RecordExport;

public PortExporter(IShippingRecorderFactory factory)
=> _factory = factory;

/// <summary>
/// Export the ports to a CSV file
/// </summary>
/// <param name="file"></param>
public async Task ExportAsync(string file)
{
var ports = await _factory.Ports.ListAsync(x => true, 1, int.MaxValue).ToListAsync();
await ExportAsync(ports, file);
}

/// <summary>
/// Export a collection of ports to a CSV file
/// </summary>
/// <param name="ports"></param>
/// <param name="file"></param>
#pragma warning disable CS1998
public async Task ExportAsync(IEnumerable<Port> ports, string file)
{
// Convert the ports to exportable (flattened hierarchy) ports
var exportable = ports.ToExportable();

// Configure an exporter to export them
var exporter = new CsvExporter<ExportablePort>(ExportableEntityBase.DateFormat);
exporter.RecordExport += OnRecordExported;

// Export the records
exporter.Export(exportable, file, ',');
}
#pragma warning restore CS1998

/// <summary>
/// Handler for port export notifications
/// </summary>
/// <param name="_"></param>
/// <param name="e"></param>
private void OnRecordExported(object _, ExportEventArgs<ExportablePort> e)
{
RecordExport?.Invoke(this, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ public static ExportableCountry ToExportable(this Country country)
};

/// <summary>
/// Return a collection of exportable countrys from a collection of countrys
/// Return a collection of exportable countries from a collection of countries
/// </summary>
/// <param name="countrys"></param>
/// <param name="countries"></param>
/// <returns></returns>
public static IEnumerable<ExportableCountry> ToExportable(this IEnumerable<Country> countrys)
public static IEnumerable<ExportableCountry> ToExportable(this IEnumerable<Country> countries)
{
var exportable = new List<ExportableCountry>();

foreach (var country in countrys)
foreach (var country in countries)
{
exportable.Add(country.ToExportable());
}
Expand Down
38 changes: 38 additions & 0 deletions src/ShippingRecorder.DataExchange/Extensions/PortExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using ShippingRecorder.DataExchange.Entities;
using ShippingRecorder.Entities.Db;

namespace ShippingRecorder.DataExchange.Extensions
{
public static class PortExtensions
{
/// <summary>
/// Return an exportable port from a port
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public static ExportablePort ToExportable(this Port port)
=> new()
{
Code = port.Code,
Name = port.Name
};

/// <summary>
/// Return a collection of exportable ports from a collection of ports
/// </summary>
/// <param name="ports"></param>
/// <returns></returns>
public static IEnumerable<ExportablePort> ToExportable(this IEnumerable<Port> ports)
{
var exportable = new List<ExportablePort>();

foreach (var port in ports)
{
exportable.Add(port.ToExportable());
}

return exportable;
}
}
}
9 changes: 9 additions & 0 deletions src/ShippingRecorder.DataExchange/Interfaces/IPortExporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using ShippingRecorder.DataExchange.Entities;
using ShippingRecorder.Entities.Db;

namespace ShippingRecorder.DataExchange.Interfaces
{
public interface IPortExporter : IExporter<ExportablePort, Port>
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public enum CommandLineOptionType
ExportCountries,
ExportLocations,
ExportOperators,
ExportPorts,
ExportSightings,
ExportVessels,
ExportVesselTypes,
Expand Down
13 changes: 10 additions & 3 deletions src/ShippingRecorder.Manager/Logic/ExportHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ public async Task HandleOperatorExportAsync()
=> await HandleExport<OperatorExporter, ExportableOperator, Operator>(CommandLineOptionType.ExportOperators);

/// <summary>
/// Handle the vessel type export command
/// Handle the port export command
/// </summary>
/// <returns></returns>
public async Task HandleVesselTypeExportAsync()
=> await HandleExport<VesselTypeExporter, ExportableVesselType, VesselType>(CommandLineOptionType.ExportVesselTypes);
public async Task HandlePortExportAsync()
=> await HandleExport<PortExporter, ExportablePort, Port>(CommandLineOptionType.ExportPorts);

/// <summary>
/// Handle the sightings export command
Expand All @@ -106,6 +106,13 @@ public async Task HandleSightingExportAsync()
public async Task HandleVesselExportAsync()
=> await HandleExport<VesselExporter, ExportableVessel, Vessel>(CommandLineOptionType.ExportVessels);

/// <summary>
/// Handle the vessel type export command
/// </summary>
/// <returns></returns>
public async Task HandleVesselTypeExportAsync()
=> await HandleExport<VesselTypeExporter, ExportableVesselType, VesselType>(CommandLineOptionType.ExportVesselTypes);

/// <summary>
/// Handle the voyage export command
/// </summary>
Expand Down
Loading