Skip to content

Commit

Permalink
Merge pull request #4 from davewalker5/MC-36-CSV-Import-Export
Browse files Browse the repository at this point in the history
MC-36 CSV and XLSX Import and Export
  • Loading branch information
davewalker5 authored Oct 8, 2023
2 parents 2b20d49 + cf41095 commit 6a72e5d
Show file tree
Hide file tree
Showing 41 changed files with 1,465 additions and 112 deletions.
6 changes: 3 additions & 3 deletions src/MusicCatalogue.Api/MusicCatalogue.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ReleaseVersion>1.1.0.0</ReleaseVersion>
<FileVersion>1.1.0.0</FileVersion>
<ProductVersion>1.1.0</ProductVersion>
<ReleaseVersion>1.2.0.0</ReleaseVersion>
<FileVersion>1.2.0.0</FileVersion>
<ProductVersion>1.2.0</ProductVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/MusicCatalogue.Data/MusicCatalogue.Data.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>MusicCatalogue.Data</PackageId>
<PackageVersion>1.1.0.0</PackageVersion>
<PackageVersion>1.2.0.0</PackageVersion>
<Authors>Dave Walker</Authors>
<Copyright>Copyright (c) Dave Walker 2023</Copyright>
<Owners>Dave Walker</Owners>
Expand All @@ -17,7 +17,7 @@
<PackageProjectUrl>https://github.com/davewalker5/MusicCatalogue</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<ReleaseVersion>1.1.0.0</ReleaseVersion>
<ReleaseVersion>1.2.0.0</ReleaseVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
16 changes: 16 additions & 0 deletions src/MusicCatalogue.Entities/CommandLine/CommandLineOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;

namespace MusicCatalogue.Entities.CommandLine
{
[ExcludeFromCodeCoverage]
public class CommandLineOption
{
public CommandLineOptionType OptionType { get; set; }
public bool IsOperation { get; set; }
public string Name { get; set; } = "";
public string ShortName { get; set; } = "";
public string Description { get; set; } = "";
public int MinimumNumberOfValues { get; set; } = 0;
public int MaximumNumberOfValues { get; set; } = 0;
}
}
10 changes: 10 additions & 0 deletions src/MusicCatalogue.Entities/CommandLine/CommandLineOptionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace MusicCatalogue.Entities.CommandLine
{
public enum CommandLineOptionType
{
Unknown,
Lookup,
Import,
Export,
}
}
11 changes: 11 additions & 0 deletions src/MusicCatalogue.Entities/CommandLine/CommandLineOptionValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Diagnostics.CodeAnalysis;

namespace MusicCatalogue.Entities.CommandLine
{
[ExcludeFromCodeCoverage]
public class CommandLineOptionValue
{
public CommandLineOption? Option { get; set; }
public List<string> Values { get; private set; } = new List<string>();
}
}
96 changes: 96 additions & 0 deletions src/MusicCatalogue.Entities/DataExchange/FlattenedTrack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using MusicCatalogue.Entities.Database;
using System.Diagnostics.CodeAnalysis;
using System.Text;

namespace MusicCatalogue.Entities.DataExchange
{
[ExcludeFromCodeCoverage]
public class FlattenedTrack : TrackBase
{
public const int ArtistField = 0;
public const int AlbumField = 1;
private const int GenreField = 2;
private const int ReleasedField = 3;
private const int CoverField = 4;
private const int TrackNumberField = 5;
private const int TitleField = 6;
private const int DurationField = 7;

public const string CsvRecordPattern = @"^(""[a-zA-Z0-9-() \/']+"",){3}""[0-9]+"",("".*"",)""[0-9]+"",(""[a-zA-Z0-9-() \/']+"",)""[0-9]+\:[0-9]{2}""$";

public string ArtistName{ get; set; } = "";
public string AlbumTitle { get; set; } = "";
public string? Genre { get; set; } = "";
public int? Released { get; set; }
public string? CoverUrl { get; set; } = "";
public int? TrackNumber { get; set; }
public string Title { get; set; } = "";

/// <summary>
/// Create a representation of the flattened track in CSV format
/// </summary>
/// <returns></returns>
public string ToCsv()
{
StringBuilder builder = new StringBuilder();
AppendField(builder, ArtistName);
AppendField(builder, AlbumTitle);
AppendField(builder, Genre);
AppendField(builder, Released);
AppendField(builder, CoverUrl);
AppendField(builder, TrackNumber);
AppendField(builder, Title);
AppendField(builder, FormattedDuration());
return builder.ToString();
}

/// <summary>
/// Create a flattened track record from a CSV string
/// </summary>
/// <param name="record"></param>
/// <returns></returns>
public static FlattenedTrack FromCsv(string record)
{
// Split the record into words
var words = record.Split(new string[] { "\",\"" }, StringSplitOptions.None);

// Get the release date and cover URL, both of which may be NULL
int? releaseYear = !string.IsNullOrEmpty(words[ReleasedField]) ? int.Parse(words[ReleasedField]) : null;
string? coverUrl = !string.IsNullOrEmpty(words[CoverField]) ? words[CoverField] : null;

// Split the duration on the ":" separator and convert to milliseconds
var durationWords = words[DurationField][..^1].Split(new string[] { ":" }, StringSplitOptions.None);
var durationMs = 1000 * (60 * int.Parse(durationWords[0]) + int.Parse(durationWords[1]));

// Create a new "flattened" record containing artist, album and track details
return new FlattenedTrack
{
ArtistName = words[ArtistField][1..],
AlbumTitle = words[AlbumField],
Genre = words[GenreField],
Released = releaseYear,
CoverUrl = coverUrl,
TrackNumber = int.Parse(words[TrackNumberField]),
Title = words[TitleField],
Duration = durationMs
};
}

/// <summary>
/// Append a value to a string builder holding a representation of a flattened track in CSV format
/// </summary>
/// <param name="builder"></param>
/// <param name="value"></param>
private static void AppendField(StringBuilder builder, object? value)
{
if (builder.Length > 0)
{
builder.Append(',');
}

builder.Append('"');
builder.Append(value?.ToString() ?? "");
builder.Append('"');
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Diagnostics.CodeAnalysis;

namespace MusicCatalogue.Entities.DataExchange
{
[ExcludeFromCodeCoverage]
public class TrackDataExchangeEventArgs : EventArgs
{
public long RecordCount { get; set; }
public FlattenedTrack? Track { get; set; }
}
}
23 changes: 1 addition & 22 deletions src/MusicCatalogue.Entities/Database/Track.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace MusicCatalogue.Entities.Database
{
[ExcludeFromCodeCoverage]
public class Track
public class Track : TrackBase
{
[Key]
public int Id { get; set; }
Expand All @@ -18,26 +18,5 @@ public class Track

[Required]
public string Title { get; set; } = "";

public int? Duration { get; set; }

/// <summary>
/// Format the duration in MM:SS format
/// </summary>
/// <returns></returns>
public string? FormattedDuration()
{
string? formatted = null;

if (Duration != null)
{
int seconds = (Duration ?? 0) / 1000;
int minutes = seconds / 60;
seconds -= 60 * minutes;
formatted = $"{minutes:00}:{seconds:00}";
}

return formatted;
}
}
}
32 changes: 32 additions & 0 deletions src/MusicCatalogue.Entities/Database/TrackBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MusicCatalogue.Entities.Database
{
public abstract class TrackBase
{
public int? Duration { get; set; }

/// <summary>
/// Format the duration in MM:SS format
/// </summary>
/// <returns></returns>
public string? FormattedDuration()
{
string? formatted = null;

if (Duration != null)
{
int seconds = (Duration ?? 0) / 1000;
int minutes = seconds / 60;
seconds -= 60 * minutes;
formatted = $"{minutes:00}:{seconds:00}";
}

return formatted;
}
}
}
32 changes: 32 additions & 0 deletions src/MusicCatalogue.Entities/Exceptions/DuplicateOptionException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace MusicCatalogue.Entities.Exceptions
{

[Serializable]
[ExcludeFromCodeCoverage]
public class DuplicateOptionException : Exception
{
public DuplicateOptionException()
{
}

public DuplicateOptionException(string message) : base(message)
{
}

public DuplicateOptionException(string message, Exception inner) : base(message, inner)
{
}

protected DuplicateOptionException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext)
{
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace MusicCatalogue.Entities.Exceptions
{
[Serializable]
[ExcludeFromCodeCoverage]
public class InvalidRecordFormatException : Exception
{
public InvalidRecordFormatException()
{
}

public InvalidRecordFormatException(string message) : base(message)
{
}

public InvalidRecordFormatException(string message, Exception inner) : base(message, inner)
{
}

protected InvalidRecordFormatException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext)
{
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace MusicCatalogue.Entities.Exceptions
{
[Serializable]
[ExcludeFromCodeCoverage]
public class MalformedCommandLineException : Exception
{
public MalformedCommandLineException()
{
}

public MalformedCommandLineException(string message) : base(message)
{
}

public MalformedCommandLineException(string message, Exception inner) : base(message, inner)
{
}

protected MalformedCommandLineException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext)
{
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace MusicCatalogue.Entities.Exceptions
{
[Serializable]
[ExcludeFromCodeCoverage]
public class MultipleOperationsException : Exception
{
public MultipleOperationsException()
{
}

public MultipleOperationsException(string message) : base(message)
{
}

public MultipleOperationsException(string message, Exception inner) : base(message, inner)
{
}

protected MultipleOperationsException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext)
{
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
}

Loading

0 comments on commit 6a72e5d

Please sign in to comment.