This commit is contained in:
Steven Hildreth 2019-11-17 14:02:19 -06:00
parent df47a9c918
commit 25f4ba2c68
19 changed files with 254 additions and 70 deletions

View file

@ -48,9 +48,9 @@ namespace Roadie.Library.Data
return $"urn:artist_by_name:{name}";
}
public string ArtistFileFolder(IRoadieSettings configuration)
public string ArtistFileFolder(IRoadieSettings configuration, bool createIfNotFound = false)
{
return FolderPathHelper.ArtistPath(configuration, Id, SortNameValue);
return FolderPathHelper.ArtistPath(configuration, Id, SortNameValue, createIfNotFound);
}
public override string ToString()

View file

@ -1,17 +1,18 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Roadie.Library.Data.Context
{
public interface IRoadieDbRandomizer
{
SortedDictionary<int, int> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
SortedDictionary<int, int> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
SortedDictionary<int, int> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
SortedDictionary<int, int> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
SortedDictionary<int, int> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
}
}

View file

@ -109,7 +109,7 @@ namespace Roadie.Library.Data.Context.Implementation
.FirstOrDefaultAsync();
}
public override SortedDictionary<int, int> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
public override async Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT a.id
FROM `artist` a
@ -118,34 +118,34 @@ namespace Roadie.Library.Data.Context.Implementation
AND {2} = 1)
order BY RIGHT(HEX((1 << 24) * (1 + RAND())), 6)
LIMIT 0, {0}";
var ids = Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToList();
var ids = await Artists.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override SortedDictionary<int, int> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
public override async Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT g.id
FROM `genre` g
ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
var ids = Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToList();
var ids = await Genres.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override SortedDictionary<int, int> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
public override async Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT l.id
FROM `label` l
ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
var ids = Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToList();
var ids = await Labels.FromSqlRaw(sql, randomLimit).Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override SortedDictionary<int, int> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
public override async Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT r.id
FROM `release` r
@ -154,12 +154,12 @@ namespace Roadie.Library.Data.Context.Implementation
AND {2} = 1)
ORDER BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
var ids = Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToList();
var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0").Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}
public override SortedDictionary<int, int> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
public override async Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false)
{
var sql = @"SELECT t.id
FROM `track` t
@ -189,7 +189,7 @@ namespace Roadie.Library.Data.Context.Implementation
WHERE ut.userId = {1} AND ut.isFavorite = 1) AND {2} = 1) OR {2} = 0)
order BY RIGHT( HEX( (1<<24) * (1+RAND()) ), 6)
LIMIT 0, {0}";
var ids = Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToList();
var ids = await Releases.FromSqlRaw(sql, randomLimit, userId, doOnlyFavorites ? "1" : "0", doOnlyRated ? "1" : "0").Select(x => x.Id).ToListAsync();
var dict = ids.Select((id, i) => new { key = i, value = id }).ToDictionary(x => x.key, x => x.value);
return new SortedDictionary<int, int>(dict);
}

View file

@ -6,15 +6,15 @@ namespace Roadie.Library.Data.Context
{
public abstract partial class RoadieDbContext : DbContext, IRoadieDbContext
{
public abstract SortedDictionary<int, int> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomArtistIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract SortedDictionary<int, int> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomGenreIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract SortedDictionary<int, int> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomLabelIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract SortedDictionary<int, int> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomReleaseIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract SortedDictionary<int, int> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<SortedDictionary<int, int>> RandomTrackIds(int userId, int randomLimit, bool doOnlyFavorites = false, bool doOnlyRated = false);
public abstract Task<Artist> MostPlayedArtist(int userId);

View file

@ -14,6 +14,8 @@ namespace Roadie.Library.Data
[Column("sortName")] [MaxLength(250)] public string SortName { get; set; }
public string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;
[Column("tags", TypeName = "text")]
[MaxLength(65535)]
public string Tags { get; set; }

View file

@ -113,6 +113,7 @@ namespace Roadie.Library.Models
public string Tooltip => Name;
public UserArtist UserRating { get; set; }
public string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;
public Artist()
{

View file

@ -52,5 +52,6 @@ namespace Roadie.Library.Models.Collections
public CollectionStatistics Statistics { get; set; }
public Image Thumbnail { get; set; }
public string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;
}
}

View file

@ -41,5 +41,6 @@ namespace Roadie.Library.Models
public ReleaseGroupingStatistics Statistics { get; set; }
public Image Thumbnail { get; set; }
public string SortNameValue => string.IsNullOrEmpty(SortName) ? Name : SortName;
}
}

View file

@ -81,6 +81,7 @@ namespace Roadie.Library.Models.Releases
public short TrackCount { get; set; }
public UserRelease UserRating { get; set; }
public string SortTitleValue => string.IsNullOrEmpty(SortTitle) ? Title : SortTitle;
public override string ToString()
{

View file

@ -29,7 +29,7 @@ namespace Roadie.Library.Utility
/// Full path to Artist folder
/// </summary>
/// <param name="artistSortName">Sort name of Artist to use for folder name</param>
public static string ArtistPath(IRoadieSettings configuration, int artistId, string artistSortName)
public static string ArtistPath(IRoadieSettings configuration, int artistId, string artistSortName, bool createIfNotFound = false)
{
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(artistSortName), "Invalid Artist Sort Name");
SimpleContract.Requires<ArgumentException>(configuration.LibraryFolder.Length < MaximumLibraryFolderNameLength, $"Library Folder maximum length is [{ MaximumLibraryFolderNameLength }]");
@ -76,6 +76,10 @@ namespace Roadie.Library.Utility
}
artistFolder = Path.Combine(fnSubPart, $"{ artistFolder }{ fnIdPart }");
var directoryInfo = new DirectoryInfo(Path.Combine(configuration.LibraryFolder, artistFolder));
if(createIfNotFound && !directoryInfo.Exists)
{
directoryInfo.Create();
}
return directoryInfo.FullName;
}
@ -183,7 +187,7 @@ namespace Roadie.Library.Utility
/// <param name="artistFolder">Full path to Artist folder</param>
/// <param name="releaseTitle">Title of Release</param>
/// <param name="releaseDate">Date of Release</param>
public static string ReleasePath(string artistFolder, string releaseTitle, DateTime releaseDate)
public static string ReleasePath(string artistFolder, string releaseTitle, DateTime releaseDate, bool createIfNotFound = false)
{
SimpleContract.Requires<ArgumentException>(!string.IsNullOrEmpty(artistFolder), "Invalid Artist Folder");
SimpleContract.Requires<ArgumentException>(artistFolder.Length < MaximumArtistFolderNameLength, $"Artist Folder is longer than maximum allowed [{ MaximumArtistFolderNameLength }]");
@ -211,6 +215,10 @@ namespace Roadie.Library.Utility
}
var releasePath = $"[{ releaseDate.ToString("yyyy")}] {releasePathTitle}";
var directoryInfo = new DirectoryInfo(Path.Combine(artistFolder, releasePath));
if(createIfNotFound && !directoryInfo.Exists)
{
directoryInfo.Create();
}
return directoryInfo.FullName;
}

View file

@ -252,7 +252,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomArtistData = DbContext.RandomArtistIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomArtistData = await DbContext.RandomArtistIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomArtistIds = randomArtistData.Select(x => x.Value).ToArray();
rowCount = DbContext.Artists.Count();
}
@ -592,21 +592,14 @@ namespace Roadie.Api.Services
return new OperationResult<bool>(true, $"Artist Not Found [{model.Id}]");
}
// If artist is being renamed, see if artist already exists with new model supplied name
if (artist.Name.ToAlphanumericName() != model.Name.ToAlphanumericName())
var artistName = artist.SortNameValue;
var artistModelName = model.SortNameValue;
if (artistName.ToAlphanumericName() != artistModelName.ToAlphanumericName())
{
var existingArtist = DbContext.Artists.FirstOrDefault(x => x.Name == model.Name);
var existingArtist = DbContext.Artists.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName);
if (existingArtist != null)
{
return new OperationResult<bool>($"Artist already exists with name [{ model.Name }].");
}
}
// If artist sortname is being modified, see if artist already exists with new model supplied sort name
if ((artist.SortName?.ToAlphanumericName() ?? string.Empty) != (model.SortName?.ToAlphanumericName() ?? string.Empty))
{
var existingArtist = DbContext.Artists.FirstOrDefault(x => x.SortName == model.SortName);
if (existingArtist != null)
{
return new OperationResult<bool>($"Artist already exists with sort name [{ model.SortName }].");
return new OperationResult<bool>($"Artist already exists `{ existingArtist }` with name [{artistModelName }].");
}
}
try

View file

@ -264,12 +264,14 @@ namespace Roadie.Api.Services
return new OperationResult<bool>(true, string.Format("Collection Not Found [{0}]", model.Id));
}
// If collection is being renamed, see if collection already exists with new model supplied name
if (collection.Name.ToAlphanumericName() != model.Name.ToAlphanumericName())
var collectionName = collection.SortNameValue;
var collectionModelName = model.SortNameValue;
if (collectionName.ToAlphanumericName() != collectionModelName.ToAlphanumericName())
{
var existingCollection = DbContext.Collections.FirstOrDefault(x => x.Name == model.Name);
var existingCollection = DbContext.Collections.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName);
if (existingCollection != null)
{
return new OperationResult<bool>($"Collection already exists with name [{ model.Name }].");
return new OperationResult<bool>($"Collection already exists `{ collection }` with name [{ collectionModelName }].");
}
}
}

View file

@ -84,7 +84,7 @@ namespace Roadie.Api.Services
};
}
public Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false)
public async Task<Library.Models.Pagination.PagedResult<GenreList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false)
{
var sw = new Stopwatch();
sw.Start();
@ -102,7 +102,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomGenreData = DbContext.RandomGenreIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomGenreData = await DbContext.RandomGenreIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomGenreIds = randomGenreData.Select(x => x.Value).ToArray();
rowCount = DbContext.Genres.Count();
}
@ -152,7 +152,7 @@ namespace Roadie.Api.Services
}
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<GenreList>
return (new Library.Models.Pagination.PagedResult<GenreList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
@ -178,12 +178,15 @@ namespace Roadie.Api.Services
return new OperationResult<bool>(true, string.Format("Genre Not Found [{0}]", model.Id));
}
// If genre is being renamed, see if genre already exists with new model supplied name
if (genre.Name.ToAlphanumericName() != model.Name.ToAlphanumericName())
var genreName = genre.SortNameValue;
var genreModelName = genre.SortNameValue;
var didChangeName = !genreName.ToAlphanumericName().Equals(genreModelName.ToAlphanumericName(), StringComparison.OrdinalIgnoreCase);
if (didChangeName)
{
var existingGenre = DbContext.Genres.FirstOrDefault(x => x.Name == model.Name);
var existingGenre = DbContext.Genres.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName);
if (existingGenre != null)
{
return new OperationResult<bool>($"Genre already exists with name [{ model.Name }].");
return new OperationResult<bool>($"Genre already exists `{ existingGenre }` with name [{ genreModelName }].");
}
}
try
@ -196,10 +199,11 @@ namespace Roadie.Api.Services
alt.Add(specialGenreName);
}
genre.AlternateNames = alt.ToDelimitedList();
genre.Description = model.Description;
genre.IsLocked = model.IsLocked;
var oldPathToImage = genre.PathToImage(Configuration);
var didChangeName = genre.Name != model.Name;
genre.Name = model.Name;
genre.NormalizedName = model.NormalizedName;
genre.SortName = model.SortName;
genre.Status = SafeParser.ToEnum<Statuses>(model.Status);
genre.Tags = model.TagsList.ToDelimitedList();

View file

@ -7,8 +7,17 @@ namespace Roadie.Api.Services
{
public interface IStatisticsService
{
Task<OperationResult<LibraryStats>> LibraryStatistics();
Task<OperationResult<LibraryStats>> LibraryStatistics();
Task<OperationResult<IEnumerable<DateAndCount>>> ArtistsByDate();
Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDate();
Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDecade();
Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByDate();
Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByUser();
}
}

View file

@ -111,7 +111,7 @@ namespace Roadie.Api.Services
};
}
public Task<Library.Models.Pagination.PagedResult<LabelList>> List(User roadieUser, PagedRequest request,
public async Task<Library.Models.Pagination.PagedResult<LabelList>> List(User roadieUser, PagedRequest request,
bool? doRandomize = false)
{
var sw = new Stopwatch();
@ -134,7 +134,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomLabelData = DbContext.RandomLabelIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomLabelData = await DbContext.RandomLabelIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomLabelIds = randomLabelData.Select(x => x.Value).ToArray();
rowCount = DbContext.Labels.Count();
}
@ -188,14 +188,14 @@ namespace Roadie.Api.Services
}
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<LabelList>
return new Library.Models.Pagination.PagedResult<LabelList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
};
}
public async Task<OperationResult<bool>> MergeLabelsIntoLabel(ApplicationUser user, Guid intoLabelId, IEnumerable<Guid> labelIdsToMerge)
@ -274,12 +274,16 @@ namespace Roadie.Api.Services
return new OperationResult<bool>(true, string.Format("Label Not Found [{0}]", model.Id));
}
// If label is being renamed, see if label already exists with new model supplied name
if (label.Name.ToAlphanumericName() != model.Name.ToAlphanumericName())
var labelName = label.SortNameValue;
var labelModelName = model.SortNameValue;
var oldPathToImage = label.PathToImage(Configuration);
var didChangeName = !labelName.ToAlphanumericName().Equals(labelModelName.ToAlphanumericName(), StringComparison.OrdinalIgnoreCase);
if (didChangeName)
{
var existingLabel = DbContext.Labels.FirstOrDefault(x => x.Name == model.Name);
var existingLabel = DbContext.Labels.FirstOrDefault(x => x.Name == model.Name || x.SortName == model.SortName );
if (existingLabel != null)
{
return new OperationResult<bool>($"Label already exists with name [{ model.Name }].");
return new OperationResult<bool>($"Label already exists `{ existingLabel }` with name [{ labelModelName }].");
}
}
try
@ -297,8 +301,6 @@ namespace Roadie.Api.Services
label.EndDate = model.EndDate;
label.IsLocked = model.IsLocked;
label.MusicBrainzId = model.MusicBrainzId;
var oldPathToImage = label.PathToImage(Configuration);
var didChangeName = label.Name != model.Name;
label.Name = model.Name;
label.Profile = model.Profile;
label.SortName = model.SortName;

View file

@ -397,7 +397,7 @@ namespace Roadie.Api.Services
};
}
public Task<Library.Models.Pagination.PagedResult<ReleaseList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null)
public async Task<Library.Models.Pagination.PagedResult<ReleaseList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, IEnumerable<string> includes = null)
{
var sw = new Stopwatch();
sw.Start();
@ -486,7 +486,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false)
{
var randomLimit = request.Limit ?? roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomReleaseData = DbContext.RandomReleaseIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomReleaseData = await DbContext.RandomReleaseIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomReleaseIds = randomReleaseData.Select(x => x.Value).ToArray();
rowCount = DbContext.Releases.Count();
}
@ -754,14 +754,14 @@ namespace Roadie.Api.Services
if (request.FilterFavoriteOnly) rows = rows.OrderBy(x => x.UserRating.Rating).ToArray();
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<ReleaseList>
return new Library.Models.Pagination.PagedResult<ReleaseList>
{
TotalCount = rowCount.Value,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
};
}
public async Task<OperationResult<bool>> MergeReleases(ApplicationUser user, Guid releaseToMergeId, Guid releaseToMergeIntoId, bool addAsMedia)
@ -1534,12 +1534,16 @@ namespace Roadie.Api.Services
return new OperationResult<bool>(true, string.Format("Release Not Found [{0}]", model.Id));
}
// If release is being renamed, see if release already exists for artist with new model supplied name
if (release.Title.ToAlphanumericName() != model.Title.ToAlphanumericName())
var releaseTitle = release.SortTitleValue;
var releaseModelTitle = model.SortTitleValue;
if (releaseTitle.ToAlphanumericName() != releaseModelTitle.ToAlphanumericName())
{
var existingRelease = DbContext.Releases.FirstOrDefault(x => x.Title == model.Title && x.ArtistId == release.ArtistId);
var existingRelease = DbContext.Releases.FirstOrDefault(x => x.Id != release.Id &&
(x.Title == releaseModelTitle || x.SortTitle == releaseModelTitle) &&
x.ArtistId == release.ArtistId);
if (existingRelease != null)
{
return new OperationResult<bool>($"Release already exists for Artist with title [{ model.Title }].");
return new OperationResult<bool>($"Release already exists `{ existingRelease }` for Artist with title [{ releaseModelTitle }].");
}
}
try

View file

@ -66,6 +66,38 @@ namespace Roadie.Api.Services
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> ArtistsByDate()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.Artists
orderby r.CreatedDate
select r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy-MM-dd"))
.Select(x => new
{
date = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.date,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDate()
{
var sw = new Stopwatch();
@ -97,5 +129,112 @@ namespace Roadie.Api.Services
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByUser()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.UserTracks
join u in DbContext.Users on r.UserId equals u.Id
select new { u.UserName, r.PlayedCount })
.ToArray()
.GroupBy(x => x.UserName)
.Select(x => new
{
username = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.username,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> SongsPlayedByDate()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var dateInfos = (from r in DbContext.UserTracks
orderby r.LastPlayed
select r.LastPlayed ?? r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy-MM-dd"))
.Select(x => new
{
date = x.Key,
count = x.Count()
});
foreach (var dateInfo in dateInfos)
{
result.Add(new DateAndCount
{
Date = dateInfo.date,
Count = dateInfo.count
});
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
public Task<OperationResult<IEnumerable<DateAndCount>>> ReleasesByDecade()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<DateAndCount>();
var decadeInfos = (from r in DbContext.Releases
orderby r.ReleaseDate
select r.ReleaseDate ?? r.CreatedDate)
.ToArray()
.GroupBy(x => x.ToString("yyyy"))
.Select(x => new
{
year = SafeParser.ToNumber<int>(x.Key),
count = x.Count()
});
var decadeInterval = 10;
var startingDecade = (decadeInfos.Min(x => x.year) / 10) * 10;
var endingDecade = (decadeInfos.Max(x => x.year) / 10) * 10;
for (int decade = startingDecade; decade <= endingDecade; decade += decadeInterval)
{
var endOfDecade = decade + 9;
var count = decadeInfos.Where(x => x.year >= decade && x.year <= endOfDecade).Sum(x => x.count);
if (count > 0)
{
result.Add(new DateAndCount
{
Date = decade.ToString(),
Count = count
});
}
}
sw.Stop();
return Task.FromResult(new OperationResult<IEnumerable<DateAndCount>>
{
OperationTime = sw.ElapsedMilliseconds,
IsSuccess = result != null,
Data = result
});
}
}
}

View file

@ -199,7 +199,7 @@ namespace Roadie.Api.Services
};
}
public Task<Library.Models.Pagination.PagedResult<TrackList>> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null)
public async Task<Library.Models.Pagination.PagedResult<TrackList>> List(PagedRequest request, User roadieUser, bool? doRandomize = false, Guid? releaseId = null)
{
try
{
@ -289,7 +289,7 @@ namespace Roadie.Api.Services
if (doRandomize ?? false)
{
var randomLimit = roadieUser?.RandomReleaseLimit ?? request.LimitValue;
randomTrackData = DbContext.RandomTrackIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomTrackData = await DbContext.RandomTrackIds(roadieUser?.Id ?? -1, randomLimit, request.FilterFavoriteOnly, request.FilterRatedOnly);
randomTrackIds = randomTrackData.Select(x => x.Value).ToArray();
rowCount = DbContext.Releases.Count();
}
@ -623,22 +623,22 @@ namespace Roadie.Api.Services
}
sw.Stop();
return Task.FromResult(new Library.Models.Pagination.PagedResult<TrackList>
return new Library.Models.Pagination.PagedResult<TrackList>
{
TotalCount = rowCount ?? 0,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
OperationTime = sw.ElapsedMilliseconds,
Rows = rows
});
};
}
catch (Exception ex)
{
Logger.LogError(ex, "Error In List, Request [{0}], User [{1}]", JsonConvert.SerializeObject(request), roadieUser);
return Task.FromResult(new Library.Models.Pagination.PagedResult<TrackList>
return new Library.Models.Pagination.PagedResult<TrackList>
{
Message = "An Error has occured"
});
};
}
}

View file

@ -59,8 +59,24 @@ namespace Roadie.Api.Controllers
[AllowAnonymous]
public IActionResult Ping() => Ok("pong");
[HttpGet("artistsByDate")]
[ProducesResponseType(200)]
public async Task<IActionResult> ArtistsByDate() => Ok(await StatisticsService.ArtistsByDate());
[HttpGet("releasesByDate")]
[ProducesResponseType(200)]
public async Task<IActionResult> ReleasesByDate() => Ok(await StatisticsService.ReleasesByDate());
[HttpGet("releasesByDecade")]
[ProducesResponseType(200)]
public async Task<IActionResult> ReleasesByDecade() => Ok(await StatisticsService.ReleasesByDecade());
[HttpGet("songsPlayedByDate")]
[ProducesResponseType(200)]
public async Task<IActionResult> SongsPlayedByDate() => Ok(await StatisticsService.SongsPlayedByDate());
[HttpGet("songsPlayedByUser")]
[ProducesResponseType(200)]
public async Task<IActionResult> SongsPlayedByUser() => Ok(await StatisticsService.SongsPlayedByUser());
}
}