Rated people panel added

This commit is contained in:
2024-11-01 20:44:01 +01:00
Unverified
parent 226b2b619c
commit 1154cc547b
28 changed files with 479 additions and 59 deletions

View File

@@ -3,6 +3,7 @@ using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genres; using WatchIt.Common.Model.Genres;
using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query; using WatchIt.Common.Query;
using WatchIt.Database.Model.Rating;
namespace WatchIt.Common.Model.Media; namespace WatchIt.Common.Model.Media;
@@ -55,7 +56,9 @@ public class MediaResponse : Media, IQueryOrderable<MediaResponse>
ReleaseDate = media.ReleaseDate; ReleaseDate = media.ReleaseDate;
Length = media.Length; Length = media.Length;
Type = mediaType; Type = mediaType;
Rating = RatingResponse.Create(media.RatingMedia); Rating = RatingResponseBuilder.Initialize()
.Add(media.RatingMedia, x => x.Rating)
.Build();
Genres = media.Genres.Select(x => new GenreResponse(x)).ToList(); Genres = media.Genres.Select(x => new GenreResponse(x)).ToList();
} }

View File

@@ -49,7 +49,9 @@ public class MovieRatedResponse : MovieResponse, IQueryOrderable<MovieRatedRespo
ReleaseDate = mediaMovie.Media.ReleaseDate; ReleaseDate = mediaMovie.Media.ReleaseDate;
Length = mediaMovie.Media.Length; Length = mediaMovie.Media.Length;
Budget = mediaMovie.Budget; Budget = mediaMovie.Budget;
Rating = RatingResponse.Create(mediaMovie.Media.RatingMedia); Rating = RatingResponseBuilder.Initialize()
.Add(mediaMovie.Media.RatingMedia, x => x.Rating)
.Build();
Genres = mediaMovie.Media.Genres.Select(x => new GenreResponse(x)).ToList(); Genres = mediaMovie.Media.Genres.Select(x => new GenreResponse(x)).ToList();
UserRating = response.Rating; UserRating = response.Rating;
} }

View File

@@ -54,7 +54,9 @@ public class MovieResponse : Movie, IQueryOrderable<MovieResponse>
ReleaseDate = mediaMovie.Media.ReleaseDate; ReleaseDate = mediaMovie.Media.ReleaseDate;
Length = mediaMovie.Media.Length; Length = mediaMovie.Media.Length;
Budget = mediaMovie.Budget; Budget = mediaMovie.Budget;
Rating = RatingResponse.Create(mediaMovie.Media.RatingMedia); Rating = RatingResponseBuilder.Initialize()
.Add(mediaMovie.Media.RatingMedia, x => x.Rating)
.Build();
Genres = mediaMovie.Media.Genres.Select(x => new GenreResponse(x)).ToList(); Genres = mediaMovie.Media.Genres.Select(x => new GenreResponse(x)).ToList();
} }

View File

@@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Mvc;
using WatchIt.Common.Query;
namespace WatchIt.Common.Model.Persons;
public class PersonRatedQueryParameters : QueryParameters<PersonRatedResponse>
{
#region PROPERTIES
[FromQuery(Name = "name")]
public string? Name { get; set; }
[FromQuery(Name = "full_name")]
public string? FullName { get; set; }
[FromQuery(Name = "description")]
public string? Description { get; set; }
[FromQuery(Name = "birth_date")]
public DateOnly? BirthDate { get; set; }
[FromQuery(Name = "birth_date_from")]
public DateOnly? BirthDateFrom { get; set; }
[FromQuery(Name = "birth_date_to")]
public DateOnly? BirthDateTo { get; set; }
[FromQuery(Name = "death_date")]
public DateOnly? DeathDate { get; set; }
[FromQuery(Name = "death_date_from")]
public DateOnly? DeathDateFrom { get; set; }
[FromQuery(Name = "death_date_to")]
public DateOnly? DeathDateTo { get; set; }
[FromQuery(Name = "gender_id")]
public short? GenderId { get; set; }
[FromQuery(Name = "rating_average")]
public decimal? RatingAverage { get; set; }
[FromQuery(Name = "rating_average_from")]
public decimal? RatingAverageFrom { get; set; }
[FromQuery(Name = "rating_average_to")]
public decimal? RatingAverageTo { get; set; }
[FromQuery(Name = "rating_count")]
public long? RatingCount { get; set; }
[FromQuery(Name = "rating_count_from")]
public long? RatingCountFrom { get; set; }
[FromQuery(Name = "rating_count_to")]
public long? RatingCountTo { get; set; }
[FromQuery(Name = "user_rating_average")]
public decimal? UserRatingAverage { get; set; }
[FromQuery(Name = "user_rating_average_from")]
public decimal? UserRatingAverageFrom { get; set; }
[FromQuery(Name = "user_rating_average_to")]
public decimal? UserRatingAverageTo { get; set; }
[FromQuery(Name = "user_rating_count")]
public long? UserRatingCount { get; set; }
[FromQuery(Name = "user_rating_count_from")]
public long? UserRatingCountFrom { get; set; }
[FromQuery(Name = "user_rating_count_to")]
public long? UserRatingCountTo { get; set; }
#endregion
#region PUBLIC METHODS
protected override bool IsMeetingConditions(PersonRatedResponse item) =>
(
TestStringWithRegex(item.Name, Name)
&&
TestStringWithRegex(item.FullName, FullName)
&&
TestStringWithRegex(item.Description, Description)
&&
TestComparable(item.BirthDate, BirthDate, BirthDateFrom, BirthDateTo)
&&
TestComparable(item.DeathDate, DeathDate, DeathDateFrom, DeathDateTo)
&&
Test(item.Gender?.Id, GenderId)
&&
TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo)
&&
TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo)
&&
TestComparable(item.UserRating.Average, UserRatingAverage, UserRatingAverageFrom, UserRatingAverageTo)
&&
TestComparable(item.UserRating.Count, UserRatingCount, UserRatingCountFrom, UserRatingCountTo)
);
#endregion
}

View File

@@ -0,0 +1,63 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genders;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
using WatchIt.Database.Model.Rating;
namespace WatchIt.Common.Model.Persons;
public class PersonRatedResponse : PersonResponse, IQueryOrderable<PersonRatedResponse>
{
#region PROPERTIES
[JsonIgnore]
public static IDictionary<string, Func<PersonRatedResponse, IComparable>> OrderableProperties { get; } = new Dictionary<string, Func<PersonRatedResponse, IComparable>>
{
{ "id", x => x.Id },
{ "name", x => x.Name },
{ "full_name", x => x.FullName },
{ "description", x => x.Description },
{ "birth_date", x => x.BirthDate },
{ "death_date", x => x.BirthDate },
{ "gender", x => x.Gender.Name },
{ "rating.average", x => x.Rating.Average },
{ "rating.count", x => x.Rating.Count },
{ "user_rating.average", x => x.UserRating.Average },
{ "user_rating.count", x => x.UserRating.Count }
};
[JsonPropertyName("user_rating")]
public RatingResponse UserRating { get; set; }
#endregion
#region CONSTRUCTORS
[JsonConstructor]
public PersonRatedResponse() { }
[SetsRequiredMembers]
public PersonRatedResponse(Database.Model.Person.Person person, IEnumerable<RatingPersonActorRole> actorUserRatings, IEnumerable<RatingPersonCreatorRole> creatorUserRatings)
{
Id = person.Id;
Name = person.Name;
FullName = person.FullName;
Description = person.Description;
BirthDate = person.BirthDate;
DeathDate = person.DeathDate;
Gender = person.Gender is not null ? new GenderResponse(person.Gender) : null;
Rating = RatingResponseBuilder.Initialize()
.Add(person.PersonActorRoles.SelectMany(x => x.RatingPersonActorRole), x => x.Rating)
.Add(person.PersonCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole), x => x.Rating)
.Build();
UserRating = RatingResponseBuilder.Initialize()
.Add(actorUserRatings, x => x.Rating)
.Add(creatorUserRatings, x => x.Rating)
.Build();
}
#endregion
}

View File

@@ -3,6 +3,7 @@ using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genders; using WatchIt.Common.Model.Genders;
using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query; using WatchIt.Common.Query;
using WatchIt.Database.Model.Rating;
namespace WatchIt.Common.Model.Persons; namespace WatchIt.Common.Model.Persons;
@@ -53,7 +54,10 @@ public class PersonResponse : Person, IQueryOrderable<PersonResponse>
BirthDate = person.BirthDate; BirthDate = person.BirthDate;
DeathDate = person.DeathDate; DeathDate = person.DeathDate;
Gender = person.Gender is not null ? new GenderResponse(person.Gender) : null; Gender = person.Gender is not null ? new GenderResponse(person.Gender) : null;
Rating = RatingResponse.Create(person.PersonActorRoles, person.PersonCreatorRoles); Rating = RatingResponseBuilder.Initialize()
.Add(person.PersonActorRoles.SelectMany(x => x.RatingPersonActorRole), x => x.Rating)
.Add(person.PersonCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole), x => x.Rating)
.Build();
} }
#endregion #endregion

View File

@@ -25,35 +25,11 @@ public class RatingResponse
public RatingResponse() {} public RatingResponse() {}
[SetsRequiredMembers] [SetsRequiredMembers]
private RatingResponse(long ratingSum, long ratingCount) internal RatingResponse(long ratingSum, long ratingCount)
{ {
Average = ratingCount > 0 ? (decimal)ratingSum / ratingCount : 0; Average = ratingCount > 0 ? (decimal)ratingSum / ratingCount : 0;
Count = ratingCount; Count = ratingCount;
} }
public static RatingResponse Create(long ratingSum, long ratingCount) => new RatingResponse(ratingSum, ratingCount);
public static RatingResponse Create(IEnumerable<RatingMedia> ratingMedia) => Create(ratingMedia, x => x.Rating);
public static RatingResponse Create(IEnumerable<RatingPersonActorRole> ratingPersonActorRoles) => Create(ratingPersonActorRoles, x => x.Rating);
public static RatingResponse Create(IEnumerable<RatingPersonCreatorRole> ratingPersonCreatorRoles) => Create(ratingPersonCreatorRoles, x => x.Rating);
public static RatingResponse Create<T>(IEnumerable<T> ratingList, Func<T, short> ratingSelector) => new RatingResponse(ratingList.Sum(x => ratingSelector(x)), ratingList.Count());
public static RatingResponse Create(IEnumerable<PersonActorRole> personActorRoles, IEnumerable<PersonCreatorRole> personCreatorRoles)
{
IEnumerable<RatingPersonActorRole> ratingsActorRoles = personActorRoles.SelectMany(x => x.RatingPersonActorRole);
IEnumerable<RatingPersonCreatorRole> ratingsCreatorRoles = personCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole);
return Create(ratingsActorRoles, ratingsCreatorRoles);
}
public static RatingResponse Create(IEnumerable<RatingPersonActorRole> ratingsActorRoles, IEnumerable<RatingPersonCreatorRole> ratingsCreatorRoles)
{
long ratingSum = ratingsActorRoles.Sum(x => x.Rating) + ratingsCreatorRoles.Sum(x => x.Rating);
long ratingCount = ratingsActorRoles.Count() + ratingsCreatorRoles.Count();
return new RatingResponse(ratingSum, ratingCount);
}
#endregion #endregion
} }

View File

@@ -0,0 +1,35 @@
namespace WatchIt.Common.Model.Rating;
public class RatingResponseBuilder
{
#region FIELDS
private long _sum;
private long _count;
#endregion
#region CONSTRUCTORS
private RatingResponseBuilder() { }
public static RatingResponseBuilder Initialize() => new RatingResponseBuilder();
#endregion
#region PUBLIC METHODS
public RatingResponseBuilder Add<T>(IEnumerable<T> collection, Func<T, short> selector)
{
_sum += collection.Sum(x => selector(x));
_count += collection.Count();
return this;
}
public RatingResponse Build() => new RatingResponse(_sum, _count);
#endregion
}

View File

@@ -49,7 +49,9 @@ public class SeriesRatedResponse : SeriesResponse, IQueryOrderable<SeriesRatedRe
ReleaseDate = mediaSeries.Media.ReleaseDate; ReleaseDate = mediaSeries.Media.ReleaseDate;
Length = mediaSeries.Media.Length; Length = mediaSeries.Media.Length;
HasEnded = mediaSeries.HasEnded; HasEnded = mediaSeries.HasEnded;
Rating = RatingResponse.Create(mediaSeries.Media.RatingMedia); Rating = RatingResponseBuilder.Initialize()
.Add(mediaSeries.Media.RatingMedia, x => x.Rating)
.Build();
Genres = mediaSeries.Media.Genres.Select(x => new GenreResponse(x)).ToList(); Genres = mediaSeries.Media.Genres.Select(x => new GenreResponse(x)).ToList();
UserRating = response.Rating; UserRating = response.Rating;
} }

View File

@@ -54,7 +54,9 @@ public class SeriesResponse : Series, IQueryOrderable<SeriesResponse>
ReleaseDate = mediaSeries.Media.ReleaseDate; ReleaseDate = mediaSeries.Media.ReleaseDate;
Length = mediaSeries.Media.Length; Length = mediaSeries.Media.Length;
HasEnded = mediaSeries.HasEnded; HasEnded = mediaSeries.HasEnded;
Rating = RatingResponse.Create(mediaSeries.Media.RatingMedia); Rating = RatingResponseBuilder.Initialize()
.Add(mediaSeries.Media.RatingMedia, x => x.Rating)
.Build();
Genres = mediaSeries.Media.Genres.Select(x => new GenreResponse(x)).ToList(); Genres = mediaSeries.Media.Genres.Select(x => new GenreResponse(x)).ToList();
} }

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series; using WatchIt.Common.Model.Series;
using WatchIt.WebAPI.Services.Controllers.Accounts; using WatchIt.WebAPI.Services.Controllers.Accounts;
@@ -69,4 +70,10 @@ public class AccountsController(IAccountsControllerService accountsControllerSer
[ProducesResponseType(typeof(IEnumerable<SeriesRatedResponse>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(IEnumerable<SeriesRatedResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetAccountRatedSeries([FromRoute]long id, SeriesRatedQueryParameters query) => await accountsControllerService.GetAccountRatedSeries(id, query); public async Task<ActionResult> GetAccountRatedSeries([FromRoute]long id, SeriesRatedQueryParameters query) => await accountsControllerService.GetAccountRatedSeries(id, query);
[HttpGet("{id}/persons")]
[AllowAnonymous]
[ProducesResponseType(typeof(IEnumerable<PersonRatedResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetAccountRatedPersons([FromRoute]long id, PersonRatedQueryParameters query) => await accountsControllerService.GetAccountRatedPersons(id, query);
} }

View File

@@ -7,6 +7,7 @@ using SimpleToolkit.Extensions;
using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Media; using WatchIt.Common.Model.Media;
using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series; using WatchIt.Common.Model.Series;
using WatchIt.Database; using WatchIt.Database;
using WatchIt.Database.Model.Account; using WatchIt.Database.Model.Account;
@@ -18,6 +19,7 @@ using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions;
using WatchIt.WebAPI.Services.Utility.User; using WatchIt.WebAPI.Services.Utility.User;
using Account = WatchIt.Database.Model.Account.Account; using Account = WatchIt.Database.Model.Account.Account;
using AccountProfilePicture = WatchIt.Common.Model.Accounts.AccountProfilePicture; using AccountProfilePicture = WatchIt.Common.Model.Accounts.AccountProfilePicture;
using Person = WatchIt.Database.Model.Person.Person;
namespace WatchIt.WebAPI.Services.Controllers.Accounts; namespace WatchIt.WebAPI.Services.Controllers.Accounts;
@@ -190,6 +192,24 @@ public class AccountsControllerService(
response = query.PrepareData(response); response = query.PrepareData(response);
return RequestResult.Ok(response); return RequestResult.Ok(response);
} }
public async Task<RequestResult> GetAccountRatedPersons(long id, PersonRatedQueryParameters query)
{
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id);
if (account is null)
{
return RequestResult.NotFound();
}
IEnumerable<RatingPersonActorRole> actorRolesRatings = account.RatingPersonActorRole;
IEnumerable<RatingPersonCreatorRole> creatorRolesRatings = account.RatingPersonCreatorRole;
IEnumerable<Person> persons = actorRolesRatings.Select(x => x.PersonActorRole.Person)
.Union(creatorRolesRatings.Select(x => x.PersonCreatorRole.Person));
IEnumerable<PersonRatedResponse> response = persons.Select(x => new PersonRatedResponse(x, actorRolesRatings.Where(y => y.PersonActorRole.Person.Id == x.Id), creatorRolesRatings.Where(y => y.PersonCreatorRole.Person.Id == x.Id)));
response = query.PrepareData(response);
return RequestResult.Ok(response);
}
#endregion #endregion

View File

@@ -1,6 +1,7 @@
using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Media; using WatchIt.Common.Model.Media;
using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series; using WatchIt.Common.Model.Series;
using WatchIt.WebAPI.Services.Controllers.Common; using WatchIt.WebAPI.Services.Controllers.Common;
@@ -17,4 +18,5 @@ public interface IAccountsControllerService
Task<RequestResult> PutAccountInfo(AccountRequest data); Task<RequestResult> PutAccountInfo(AccountRequest data);
Task<RequestResult> GetAccountRatedMovies(long id, MovieRatedQueryParameters query); Task<RequestResult> GetAccountRatedMovies(long id, MovieRatedQueryParameters query);
Task<RequestResult> GetAccountRatedSeries(long id, SeriesRatedQueryParameters query); Task<RequestResult> GetAccountRatedSeries(long id, SeriesRatedQueryParameters query);
Task<RequestResult> GetAccountRatedPersons(long id, PersonRatedQueryParameters query);
} }

View File

@@ -118,7 +118,9 @@ public class MediaControllerService(DatabaseContext database, IUserService userS
return RequestResult.NotFound(); return RequestResult.NotFound();
} }
RatingResponse ratingResponse = RatingResponse.Create(item.RatingMedia); RatingResponse ratingResponse = RatingResponseBuilder.Initialize()
.Add(item.RatingMedia, x => x.Rating)
.Build();
return RequestResult.Ok(ratingResponse); return RequestResult.Ok(ratingResponse);
} }

View File

@@ -335,7 +335,10 @@ public class PersonsControllerService : IPersonsControllerService
return RequestResult.NotFound(); return RequestResult.NotFound();
} }
RatingResponse ratingResponse = RatingResponse.Create(item.PersonActorRoles, item.PersonCreatorRoles); RatingResponse ratingResponse = RatingResponseBuilder.Initialize()
.Add(item.PersonActorRoles.SelectMany(x => x.RatingPersonActorRole), x => x.Rating)
.Add(item.PersonCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole), x => x.Rating)
.Build();
return RequestResult.Ok(ratingResponse); return RequestResult.Ok(ratingResponse);
} }
@@ -347,10 +350,11 @@ public class PersonsControllerService : IPersonsControllerService
{ {
return RequestResult.NotFound(); return RequestResult.NotFound();
} }
IEnumerable<RatingPersonActorRole> actorRoleRatings = item.PersonActorRoles.SelectMany(x => x.RatingPersonActorRole).Where(x => x.AccountId == userId); RatingResponse ratingResponse = RatingResponseBuilder.Initialize()
IEnumerable<RatingPersonCreatorRole> creatorRoleRatings = item.PersonCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole).Where(x => x.AccountId == userId); .Add(item.PersonActorRoles.SelectMany(x => x.RatingPersonActorRole).Where(x => x.AccountId == userId), x => x.Rating)
RatingResponse ratingResponse = RatingResponse.Create(actorRoleRatings, creatorRoleRatings); .Add(item.PersonCreatorRoles.SelectMany(x => x.RatingPersonCreatorRole).Where(x => x.AccountId == userId), x => x.Rating)
.Build();
return RequestResult.Ok(ratingResponse); return RequestResult.Ok(ratingResponse);
} }

View File

@@ -97,7 +97,9 @@ public class RolesControllerService : IRolesControllerService
return RequestResult.NotFound(); return RequestResult.NotFound();
} }
RatingResponse ratingResponse = RatingResponse.Create(item.RatingPersonActorRole); RatingResponse ratingResponse = RatingResponseBuilder.Initialize()
.Add(item.RatingPersonActorRole, x => x.Rating)
.Build();
return RequestResult.Ok(ratingResponse); return RequestResult.Ok(ratingResponse);
} }
@@ -281,7 +283,9 @@ public class RolesControllerService : IRolesControllerService
return RequestResult.NotFound(); return RequestResult.NotFound();
} }
RatingResponse ratingResponse = RatingResponse.Create(item.RatingPersonCreatorRole); RatingResponse ratingResponse = RatingResponseBuilder.Initialize()
.Add(item.RatingPersonCreatorRole, x => x.Rating)
.Build();
return RequestResult.Ok(ratingResponse); return RequestResult.Ok(ratingResponse);
} }

View File

@@ -1,5 +1,6 @@
using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series; using WatchIt.Common.Model.Series;
using WatchIt.Common.Services.HttpClient; using WatchIt.Common.Services.HttpClient;
using WatchIt.Website.Services.Configuration; using WatchIt.Website.Services.Configuration;
@@ -135,6 +136,20 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
.ExecuteAction(); .ExecuteAction();
} }
public async Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedPersons, id);
HttpRequest request = new HttpRequest(HttpMethodType.Get, url)
{
Query = query
};
HttpResponse response = await httpClientService.SendRequestAsync(request);
response.RegisterActionFor2XXSuccess(successAction)
.RegisterActionFor404NotFound(notFoundAction)
.ExecuteAction();
}
#endregion #endregion

View File

@@ -1,5 +1,6 @@
using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series; using WatchIt.Common.Model.Series;
namespace WatchIt.Website.Services.Client.Accounts; namespace WatchIt.Website.Services.Client.Accounts;
@@ -15,4 +16,5 @@ public interface IAccountsClientService
Task PutAccountInfo(AccountRequest data, Action<AccountResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null); Task PutAccountInfo(AccountRequest data, Action<AccountResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null);
Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null);
Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null); Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null);
Task GetAccountRatedPersons(long id, PersonRatedQueryParameters query, Action<IEnumerable<PersonRatedResponse>>? successAction = null, Action? notFoundAction = null);
} }

View File

@@ -12,4 +12,5 @@ public class Accounts
public string PutAccountInfo { get; set; } public string PutAccountInfo { get; set; }
public string GetAccountRatedMovies { get; set; } public string GetAccountRatedMovies { get; set; }
public string GetAccountRatedSeries { get; set; } public string GetAccountRatedSeries { get; set; }
public string GetAccountRatedPersons { get; set; }
} }

View File

@@ -60,7 +60,8 @@
PosterPlaceholder="@(PosterPlaceholder)" PosterPlaceholder="@(PosterPlaceholder)"
PosterDownloadingTask="@(action => PictureDownloadingTask(id, action))" PosterDownloadingTask="@(action => PictureDownloadingTask(id, action))"
GlobalRating="@(RatingSource(item))" GlobalRating="@(RatingSource(item))"
SecondaryRating="@(SecondaryRatingSource?.Invoke(item))" SecondaryRatingSingle="@(SecondaryRatingSingleSource?.Invoke(item))"
SecondaryRatingMultiple="@(SecondaryRatingMultipleSource?.Invoke(item))"
SecondaryRatingTitle="@(SecondaryRatingTitle)" SecondaryRatingTitle="@(SecondaryRatingTitle)"
GetGlobalRatingMethod="@(action => GetGlobalRatingMethod(id, action))" GetGlobalRatingMethod="@(action => GetGlobalRatingMethod(id, action))"
GetUserRatingMethod="@(GetUserRatingMethod is not null ? (user, actionSuccess, actionNotFound) => GetUserRatingMethod(id, user, actionSuccess, actionNotFound) : null)" GetUserRatingMethod="@(GetUserRatingMethod is not null ? (user, actionSuccess, actionNotFound) => GetUserRatingMethod(id, user, actionSuccess, actionNotFound) : null)"

View File

@@ -23,7 +23,8 @@ public partial class ListComponent<TItem, TQuery> : ComponentBase where TItem :
[Parameter] public required Func<TItem, string> NameSource { get; set; } [Parameter] public required Func<TItem, string> NameSource { get; set; }
[Parameter] public Func<TItem, string?> AdditionalNameInfoSource { get; set; } = _ => null; [Parameter] public Func<TItem, string?> AdditionalNameInfoSource { get; set; } = _ => null;
[Parameter] public required Func<TItem, RatingResponse> RatingSource { get; set; } [Parameter] public required Func<TItem, RatingResponse> RatingSource { get; set; }
[Parameter] public Func<TItem, short?>? SecondaryRatingSource { get; set; } [Parameter] public Func<TItem, short?>? SecondaryRatingSingleSource { get; set; }
[Parameter] public Func<TItem, RatingResponse?>? SecondaryRatingMultipleSource { get; set; }
[Parameter] public string? SecondaryRatingTitle { get; set; } [Parameter] public string? SecondaryRatingTitle { get; set; }
[Parameter] public required string UrlIdTemplate { get; set; } [Parameter] public required string UrlIdTemplate { get; set; }
[Parameter] public required Func<long, Action<Picture>, Task> PictureDownloadingTask { get; set; } [Parameter] public required Func<long, Action<Picture>, Task> PictureDownloadingTask { get; set; }

View File

@@ -18,7 +18,7 @@
<th scope="col"> <th scope="col">
<span class="rating-name-text">Global rating:</span> <span class="rating-name-text">Global rating:</span>
</th> </th>
@if (SecondaryRating is not null) @if (SecondaryRatingSingle is not null || SecondaryRatingMultiple is not null)
{ {
<th scope="col"> <th scope="col">
<span class="rating-name-text">@(SecondaryRatingTitle):</span> <span class="rating-name-text">@(SecondaryRatingTitle):</span>
@@ -39,10 +39,11 @@
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash" EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/> Scale="0.85"/>
</td> </td>
@if (SecondaryRating is not null) @if (SecondaryRatingSingle is not null || SecondaryRatingMultiple is not null)
{ {
<td> <td>
<DisplayRatingComponent SingleRating="@(SecondaryRating)" <DisplayRatingComponent SingleRating="@(SecondaryRatingSingle)"
Rating="@(SecondaryRatingMultiple)"
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash" EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/> Scale="0.85"/>
</td> </td>
@@ -53,18 +54,18 @@
<div class="d-inline-flex align-items-center h-100"> <div class="d-inline-flex align-items-center h-100">
@if (_user is null) @if (_user is null)
{ {
<span id="ratingLoginInfoText">You must be logged in to rate</span> <span id="ratingLoginInfoText">You must be logged in to rate</span>
} }
else if (!_userRatingLoaded) else if (!_userRatingLoaded)
{ {
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Loading...</span> <span>Loading...</span>
</div> </div>
} }
else else
{ {
<Rating Color="Color.Light" MaxValue="10" @bind-SelectedValue="@(_userRating)" @onclick="@(RatingChanged)"/> <Rating Color="Color.Light" MaxValue="10" @bind-SelectedValue="@(_userRating)" @onclick="@(RatingChanged)"/>
} }
</div> </div>
</td> </td>

View File

@@ -27,7 +27,8 @@ public partial class ListItemComponent : ComponentBase
[Parameter] public required Func<Action<Picture>, Task> PosterDownloadingTask { get; set; } [Parameter] public required Func<Action<Picture>, Task> PosterDownloadingTask { get; set; }
[Parameter] public RatingResponse? GlobalRating { get; set; } [Parameter] public RatingResponse? GlobalRating { get; set; }
[Parameter] public short? SecondaryRating { get; set; } [Parameter] public short? SecondaryRatingSingle { get; set; }
[Parameter] public RatingResponse? SecondaryRatingMultiple { get; set; }
[Parameter] public string? SecondaryRatingTitle { get; set; } [Parameter] public string? SecondaryRatingTitle { get; set; }
[Parameter] public required Func<Action<RatingResponse>, Task> GetGlobalRatingMethod { get; set; } [Parameter] public required Func<Action<RatingResponse>, Task> GetGlobalRatingMethod { get; set; }
[Parameter] public Func<long, Action<short>, Action, Task>? GetUserRatingMethod { get; set; } [Parameter] public Func<long, Action<short>, Action, Task>? GetUserRatingMethod { get; set; }

View File

@@ -0,0 +1,88 @@
@using WatchIt.Common.Model.Genders
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Persons.PersonRatedResponse, WatchIt.Common.Model.Persons.PersonRatedQueryParameters>
<EditForm Model="@(Query)">
<div class="container-grid">
<div class="row mb-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Name</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.Name)"></InputText>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Full name</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.FullName)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Description</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.Description)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Birth date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.BirthDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.BirthDateTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Death date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.DeathDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.DeathDateTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Gender</span>
<InputSelect TValue="short?" class="col form-control" @bind-Value="@(Query.GenderId)">
<option @onclick="() => Query.GenderId = null">No choice</option>
@foreach (GenderResponse gender in _genders)
{
<option value="@(gender.Id)">@(gender.Name)</option>
}
</InputSelect>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Rating (count)</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.RatingCountFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.RatingCountTo)"/>
</div>
</div>
<div class="row mt-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Rating (average)</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.RatingAverageFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.RatingAverageTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating (count)</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.UserRatingCountFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="long?" Class="col form-control" Min="0" @bind-Value="@(Query.UserRatingCountTo)"/>
</div>
</div>
<div class="row mt-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating (average)</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.UserRatingAverageFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="@(0.01M)" @bind-Value="@(Query.UserRatingAverageTo)"/>
</div>
</div>
</div>
</EditForm>

View File

@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model.Genders;
using WatchIt.Common.Model.Persons;
using WatchIt.Website.Components.Common.ListComponent;
using WatchIt.Website.Services.Client.Genders;
namespace WatchIt.Website.Components.Pages.UserPage.Subcomponents;
public partial class PersonsRatedFilterFormComponent : FilterFormComponent<PersonRatedResponse, PersonRatedQueryParameters>
{
#region SERVICES
[Inject] private IGendersClientService GendersClientService { get; set; } = default!;
#endregion
#region FIELDS
private IEnumerable<GenderResponse> _genders = [];
#endregion
#region PRIVATE METHODS
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
List<Task> endTasks = new List<Task>();
// STEP 0
endTasks.AddRange(
[
GendersClientService.GetAllGenders(successAction: data => _genders = data)
]);
// END
await Task.WhenAll(endTasks);
StateHasChanged();
}
}
#endregion
}

View File

@@ -1,9 +1,11 @@
@using System.Text @using System.Text
@using WatchIt.Common.Model.Movies @using WatchIt.Common.Model.Movies
@using WatchIt.Common.Model.Persons
@using WatchIt.Common.Model.Series @using WatchIt.Common.Model.Series
@using WatchIt.Website.Components.Pages.UserPage.Panels @using WatchIt.Website.Components.Pages.UserPage.Panels
@using WatchIt.Website.Components.Common.ListComponent @using WatchIt.Website.Components.Common.ListComponent
@using WatchIt.Website.Components.Pages.UserPage.Subcomponents @using WatchIt.Website.Components.Pages.UserPage.Subcomponents
@using WatchIt.Website.Services.Client.Persons
@page "/user/{id:long?}" @page "/user/{id:long?}"
@@ -60,7 +62,6 @@
<Tab Name="movies">Movies</Tab> <Tab Name="movies">Movies</Tab>
<Tab Name="series">TV Series</Tab> <Tab Name="series">TV Series</Tab>
<Tab Name="people">People</Tab> <Tab Name="people">People</Tab>
<Tab Name="roles">Roles</Tab>
</Items> </Items>
<Content> <Content>
<TabPanel Name="summary"> <TabPanel Name="summary">
@@ -75,7 +76,7 @@
NameSource="@(item => item.Title)" NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)" AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)" RatingSource="@(item => item.Rating)"
SecondaryRatingSource="@(item => _owner ? null : item.UserRating)" SecondaryRatingSingleSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingTitle="User rating" SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}" UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))" PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
@@ -99,7 +100,7 @@
NameSource="@(item => item.Title)" NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)" AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)" RatingSource="@(item => item.Rating)"
SecondaryRatingSource="@(item => _owner ? null : item.UserRating)" SecondaryRatingSingleSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingTitle="User rating" SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}" UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))" PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
@@ -115,10 +116,33 @@
</div> </div>
</TabPanel> </TabPanel>
<TabPanel Name="people"> <TabPanel Name="people">
<div class="mt-default">
</TabPanel> <ListComponent TItem="PersonRatedResponse"
<TabPanel Name="roles"> TQuery="PersonRatedQueryParameters"
Title="Rated people"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
RatingSource="@(item => item.Rating)"
SecondaryRatingMultipleSource="@(item => item.UserRating)"
SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"
ItemDownloadingTask="@((query, action) => AccountsClientService.GetAccountRatedPersons(Id!.Value, query, action))"
SortingOptions="@(new Dictionary<string, string>
{
{ "user_rating.average", "Average user rating" },
{ "user_rating.count", "Number of user ratings" },
{ "rating.average", "Average rating" },
{ "rating.count", "Number of ratings" },
{ "name", "Name" },
{ "birth_date", "Birth date" },
{ "death_date", "Death date" },
})"
PosterPlaceholder="/assets/person_poster.png"
GetGlobalRatingMethod="@((id, action) => PersonsClientService.GetPersonGlobalRating(id, action))">
<PersonsRatedFilterFormComponent/>
</ListComponent>
</div>
</TabPanel> </TabPanel>
</Content> </Content>
</Tabs> </Tabs>

View File

@@ -4,6 +4,7 @@ using WatchIt.Website.Layout;
using WatchIt.Website.Services.Authentication; using WatchIt.Website.Services.Authentication;
using WatchIt.Website.Services.Client.Accounts; using WatchIt.Website.Services.Client.Accounts;
using WatchIt.Website.Services.Client.Media; using WatchIt.Website.Services.Client.Media;
using WatchIt.Website.Services.Client.Persons;
namespace WatchIt.Website.Pages; namespace WatchIt.Website.Pages;
@@ -15,6 +16,7 @@ public partial class UserPage : ComponentBase
[Inject] private IAuthenticationService AuthenticationService { get; set; } = default!; [Inject] private IAuthenticationService AuthenticationService { get; set; } = default!;
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!; [Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
[Inject] private IMediaClientService MediaClientService { get; set; } = default!; [Inject] private IMediaClientService MediaClientService { get; set; } = default!;
[Inject] private IPersonsClientService PersonsClientService { get; set; } = default!;
#endregion #endregion

View File

@@ -25,7 +25,8 @@
"GetAccountInfo": "/{0}/info", "GetAccountInfo": "/{0}/info",
"PutAccountInfo": "/info", "PutAccountInfo": "/info",
"GetAccountRatedMovies": "/{0}/movies", "GetAccountRatedMovies": "/{0}/movies",
"GetAccountRatedSeries": "/{0}/series" "GetAccountRatedSeries": "/{0}/series",
"GetAccountRatedPersons": "/{0}/persons"
}, },
"Genders": { "Genders": {
"Base": "/genders", "Base": "/genders",