Merge pull request #150 from mateuszskoczek/features/profile_page

Features/profile page
This commit is contained in:
2024-11-02 18:47:07 +01:00
committed by GitHub
Unverified
101 changed files with 3614 additions and 457 deletions

View File

@@ -14,11 +14,23 @@ public class AccountResponse : Account
[JsonPropertyName("gender")]
public GenderResponse? Gender { get; set; }
[JsonPropertyName("last_active")]
public DateTime LastActive { get; set; }
[JsonPropertyName("creation_date")]
public DateTime CreationDate { get; set; }
[JsonPropertyName("is_admin")]
public bool IsAdmin { get; set; }
#endregion
#region CONSTRUCTORS
[JsonConstructor]
public AccountResponse() {}
[SetsRequiredMembers]
public AccountResponse(Database.Model.Account.Account account)
@@ -28,6 +40,9 @@ public class AccountResponse : Account
Email = account.Email;
Description = account.Description;
Gender = account.Gender is not null ? new GenderResponse(account.Gender) : null;
LastActive = account.LastActive;
CreationDate = account.CreationDate;
IsAdmin = account.IsAdmin;
}
#endregion

View File

@@ -18,7 +18,7 @@ public class GenreResponse : Genre, IQueryOrderable<GenreResponse>
[JsonPropertyName("id")]
public long Id { get; set; }
public short Id { get; set; }
#endregion

View File

@@ -54,6 +54,9 @@ public class MediaQueryParameters : QueryParameters<MediaResponse>
[FromQuery(Name = "rating_count_to")]
public long? RatingCountTo { get; set; }
[FromQuery(Name = "genre")]
public IEnumerable<short>? Genres { get; set; }
#endregion
@@ -78,6 +81,8 @@ public class MediaQueryParameters : QueryParameters<MediaResponse>
TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo)
&&
TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo)
&&
TestContains(Genres, item.Genres.Select(x => x.Id))
);
#endregion

View File

@@ -1,7 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genres;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
using WatchIt.Database.Model.Rating;
namespace WatchIt.Common.Model.Media;
@@ -31,6 +33,9 @@ public class MediaResponse : Media, IQueryOrderable<MediaResponse>
[JsonPropertyName("rating")]
public RatingResponse Rating { get; set; }
[JsonPropertyName("genres")]
public IEnumerable<GenreResponse> Genres { get; set; }
#endregion
@@ -51,7 +56,10 @@ public class MediaResponse : Media, IQueryOrderable<MediaResponse>
ReleaseDate = media.ReleaseDate;
Length = media.Length;
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();
}
#endregion

View File

@@ -60,6 +60,9 @@ public class MovieQueryParameters : QueryParameters<MovieResponse>
[FromQuery(Name = "rating_count_to")]
public long? RatingCountTo { get; set; }
[FromQuery(Name = "genre")]
public IEnumerable<short>? Genres { get; set; }
#endregion
@@ -84,6 +87,8 @@ public class MovieQueryParameters : QueryParameters<MovieResponse>
TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo)
&&
TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo)
&&
TestContains(item.Genres.Select(x => x.Id), Genres)
);
#endregion

View File

@@ -0,0 +1,115 @@
using Microsoft.AspNetCore.Mvc;
using WatchIt.Common.Query;
namespace WatchIt.Common.Model.Movies;
public class MovieRatedQueryParameters : QueryParameters<MovieRatedResponse>
{
#region PROPERTIES
[FromQuery(Name = "title")]
public string? Title { get; set; }
[FromQuery(Name = "original_title")]
public string? OriginalTitle { get; set; }
[FromQuery(Name = "description")]
public string? Description { get; set; }
[FromQuery(Name = "release_date")]
public DateOnly? ReleaseDate { get; set; }
[FromQuery(Name = "release_date_from")]
public DateOnly? ReleaseDateFrom { get; set; }
[FromQuery(Name = "release_date_to")]
public DateOnly? ReleaseDateTo { get; set; }
[FromQuery(Name = "length")]
public short? Length { get; set; }
[FromQuery(Name = "length_from")]
public short? LengthFrom { get; set; }
[FromQuery(Name = "length_to")]
public short? LengthTo { get; set; }
[FromQuery(Name = "budget")]
public decimal? Budget { get; set; }
[FromQuery(Name = "budget_from")]
public decimal? BudgetFrom { get; set; }
[FromQuery(Name = "budget_to")]
public decimal? BudgetTo { 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 = "genre")]
public IEnumerable<short>? Genres { get; set; }
[FromQuery(Name = "user_rating")]
public decimal? UserRating { get; set; }
[FromQuery(Name = "user_rating_from")]
public decimal? UserRatingFrom { get; set; }
[FromQuery(Name = "user_rating_to")]
public decimal? UserRatingTo { get; set; }
[FromQuery(Name = "user_rating_date")]
public DateOnly? UserRatingDate { get; set; }
[FromQuery(Name = "user_rating_date_from")]
public DateOnly? UserRatingDateFrom { get; set; }
[FromQuery(Name = "user_rating_date_to")]
public DateOnly? UserRatingDateTo { get; set; }
#endregion
#region PRIVATE METHODS
protected override bool IsMeetingConditions(MovieRatedResponse item) =>
(
TestStringWithRegex(item.Title, Title)
&&
TestStringWithRegex(item.OriginalTitle, OriginalTitle)
&&
TestStringWithRegex(item.Description, Description)
&&
TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo)
&&
TestComparable(item.Length, Length, LengthFrom, LengthTo)
&&
TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo)
&&
TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo)
&&
TestContains(Genres, item.Genres.Select(x => x.Id))
&&
TestComparable((decimal)item.UserRating, UserRating, UserRatingFrom, UserRatingTo)
&&
TestComparable(item.UserRatingDate, UserRatingDate, UserRatingDateFrom, UserRatingDateTo)
);
#endregion
}

View File

@@ -0,0 +1,65 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genres;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
using WatchIt.Database.Model.Media;
using WatchIt.Database.Model.Rating;
namespace WatchIt.Common.Model.Movies;
public class MovieRatedResponse : MovieResponse, IQueryOrderable<MovieRatedResponse>
{
#region PROPERTIES
[JsonIgnore]
public static IDictionary<string, Func<MovieRatedResponse, IComparable>> OrderableProperties { get; } = new Dictionary<string, Func<MovieRatedResponse, IComparable>>
{
{ "id", x => x.Id },
{ "title", x => x.Title },
{ "original_title", x => x.OriginalTitle },
{ "description", x => x.Description },
{ "release_date", x => x.ReleaseDate },
{ "length", x => x.Length },
{ "budget", x => x.Budget },
{ "rating.average", x => x.Rating.Average },
{ "rating.count", x => x.Rating.Count },
{ "user_rating", x => x.UserRating },
{ "user_rating_date", x => x.UserRatingDate }
};
[JsonPropertyName("user_rating")]
public short UserRating { get; set; }
[JsonPropertyName("user_rating_date")]
public DateTime UserRatingDate { get; set; }
#endregion
#region CONSTRUCTORS
[JsonConstructor]
public MovieRatedResponse() { }
[SetsRequiredMembers]
public MovieRatedResponse(MediaMovie mediaMovie, RatingMedia response)
{
Id = mediaMovie.Media.Id;
Title = mediaMovie.Media.Title;
OriginalTitle = mediaMovie.Media.OriginalTitle;
Description = mediaMovie.Media.Description;
ReleaseDate = mediaMovie.Media.ReleaseDate;
Length = mediaMovie.Media.Length;
Budget = mediaMovie.Budget;
Rating = RatingResponseBuilder.Initialize()
.Add(mediaMovie.Media.RatingMedia, x => x.Rating)
.Build();
Genres = mediaMovie.Media.Genres.Select(x => new GenreResponse(x)).ToList();
UserRating = response.Rating;
UserRatingDate = response.Date;
}
#endregion
}

View File

@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genres;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
using WatchIt.Database.Model.Media;
@@ -30,6 +31,9 @@ public class MovieResponse : Movie, IQueryOrderable<MovieResponse>
[JsonPropertyName("rating")]
public RatingResponse Rating { get; set; }
[JsonPropertyName("genres")]
public IEnumerable<GenreResponse> Genres { get; set; }
#endregion
@@ -50,7 +54,10 @@ public class MovieResponse : Movie, IQueryOrderable<MovieResponse>
ReleaseDate = mediaMovie.Media.ReleaseDate;
Length = mediaMovie.Media.Length;
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();
}
#endregion

View File

@@ -0,0 +1,117 @@
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; }
[FromQuery(Name = "user_rating_date")]
public DateOnly? UserRatingLastDate { get; set; }
[FromQuery(Name = "user_rating_date_from")]
public DateOnly? UserRatingLastDateFrom { get; set; }
[FromQuery(Name = "user_rating_date_to")]
public DateOnly? UserRatingLastDateTo { 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)
&&
TestComparable(item.UserRatingLastDate, UserRatingLastDate, UserRatingLastDateFrom, UserRatingLastDateTo)
);
#endregion
}

View File

@@ -0,0 +1,70 @@
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 },
{ "user_rating_last_date", x => x.UserRatingLastDate }
};
[JsonPropertyName("user_rating")]
public RatingResponse UserRating { get; set; }
[JsonPropertyName("user_rating_last_date")]
public DateTime UserRatingLastDate { 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();
UserRatingLastDate = actorUserRatings.Select(x => x.Date)
.Union(creatorUserRatings.Select(x => x.Date))
.Max();
}
#endregion
}

View File

@@ -3,6 +3,7 @@ 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;
@@ -53,7 +54,10 @@ public class PersonResponse : Person, IQueryOrderable<PersonResponse>
BirthDate = person.BirthDate;
DeathDate = person.DeathDate;
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

View File

@@ -25,35 +25,11 @@ public class RatingResponse
public RatingResponse() {}
[SetsRequiredMembers]
private RatingResponse(long ratingSum, long ratingCount)
internal RatingResponse(long ratingSum, long ratingCount)
{
Average = ratingCount > 0 ? (decimal)ratingSum / ratingCount : 0;
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
}

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

@@ -54,6 +54,9 @@ public class SeriesQueryParameters : QueryParameters<SeriesResponse>
[FromQuery(Name = "rating_count_to")]
public long? RatingCountTo { get; set; }
[FromQuery(Name = "genre")]
public IEnumerable<short>? Genres { get; set; }
#endregion
@@ -78,6 +81,8 @@ public class SeriesQueryParameters : QueryParameters<SeriesResponse>
TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo)
&&
TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo)
&&
TestContains(item.Genres.Select(x => x.Id), Genres)
);
#endregion

View File

@@ -0,0 +1,111 @@
using Microsoft.AspNetCore.Mvc;
using WatchIt.Common.Query;
namespace WatchIt.Common.Model.Series;
public class SeriesRatedQueryParameters : QueryParameters<SeriesRatedResponse>
{
#region PROPERTIES
[FromQuery(Name = "title")]
public string? Title { get; set; }
[FromQuery(Name = "original_title")]
public string? OriginalTitle { get; set; }
[FromQuery(Name = "description")]
public string? Description { get; set; }
[FromQuery(Name = "release_date")]
public DateOnly? ReleaseDate { get; set; }
[FromQuery(Name = "release_date_from")]
public DateOnly? ReleaseDateFrom { get; set; }
[FromQuery(Name = "release_date_to")]
public DateOnly? ReleaseDateTo { get; set; }
[FromQuery(Name = "length")]
public short? Length { get; set; }
[FromQuery(Name = "length_from")]
public short? LengthFrom { get; set; }
[FromQuery(Name = "length_to")]
public short? LengthTo { get; set; }
[FromQuery(Name = "has_ended")]
public bool? HasEnded { 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 = "genre")]
public IEnumerable<short>? Genres { get; set; }
[FromQuery(Name = "user_rating")]
public decimal? UserRating { get; set; }
[FromQuery(Name = "user_rating_from")]
public decimal? UserRatingFrom { get; set; }
[FromQuery(Name = "user_rating_to")]
public decimal? UserRatingTo { get; set; }
[FromQuery(Name = "user_rating_date")]
public DateOnly? UserRatingDate { get; set; }
[FromQuery(Name = "user_rating_date_from")]
public DateOnly? UserRatingDateFrom { get; set; }
[FromQuery(Name = "user_rating_date_to")]
public DateOnly? UserRatingDateTo { get; set; }
#endregion
#region PRIVATE METHODS
protected override bool IsMeetingConditions(SeriesRatedResponse item) =>
(
TestStringWithRegex(item.Title, Title)
&&
TestStringWithRegex(item.OriginalTitle, OriginalTitle)
&&
TestStringWithRegex(item.Description, Description)
&&
TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo)
&&
TestComparable(item.Length, Length, LengthFrom, LengthTo)
&&
Test(item.HasEnded, HasEnded)
&&
TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo)
&&
TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo)
&&
TestContains(item.Genres.Select(x => x.Id), Genres)
&&
TestComparable((decimal)item.UserRating, UserRating, UserRatingFrom, UserRatingTo)
&&
TestComparable(item.UserRatingDate, UserRatingDate, UserRatingDateFrom, UserRatingDateTo)
);
#endregion
}

View File

@@ -0,0 +1,65 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genres;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
using WatchIt.Database.Model.Media;
using WatchIt.Database.Model.Rating;
namespace WatchIt.Common.Model.Series;
public class SeriesRatedResponse : SeriesResponse, IQueryOrderable<SeriesRatedResponse>
{
#region PROPERTIES
[JsonIgnore]
public static IDictionary<string, Func<SeriesRatedResponse, IComparable>> OrderableProperties { get; } = new Dictionary<string, Func<SeriesRatedResponse, IComparable>>
{
{ "id", x => x.Id },
{ "title", x => x.Title },
{ "original_title", x => x.OriginalTitle },
{ "description", x => x.Description },
{ "release_date", x => x.ReleaseDate },
{ "length", x => x.Length },
{ "has_ended", x => x.HasEnded },
{ "rating.average", x => x.Rating.Average },
{ "rating.count", x => x.Rating.Count },
{ "user_rating", x => x.UserRating },
{ "user_rating_date", x => x.UserRatingDate }
};
[JsonPropertyName("user_rating")]
public short UserRating { get; set; }
[JsonPropertyName("user_rating_date")]
public DateTime UserRatingDate { get; set; }
#endregion
#region CONSTRUCTORS
[JsonConstructor]
public SeriesRatedResponse() { }
[SetsRequiredMembers]
public SeriesRatedResponse(MediaSeries mediaSeries, RatingMedia response)
{
Id = mediaSeries.Media.Id;
Title = mediaSeries.Media.Title;
OriginalTitle = mediaSeries.Media.OriginalTitle;
Description = mediaSeries.Media.Description;
ReleaseDate = mediaSeries.Media.ReleaseDate;
Length = mediaSeries.Media.Length;
HasEnded = mediaSeries.HasEnded;
Rating = RatingResponseBuilder.Initialize()
.Add(mediaSeries.Media.RatingMedia, x => x.Rating)
.Build();
Genres = mediaSeries.Media.Genres.Select(x => new GenreResponse(x)).ToList();
UserRating = response.Rating;
UserRatingDate = response.Date;
}
#endregion
}

View File

@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using WatchIt.Common.Model.Genres;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
using WatchIt.Database.Model.Media;
@@ -30,6 +31,9 @@ public class SeriesResponse : Series, IQueryOrderable<SeriesResponse>
[JsonPropertyName("rating")]
public RatingResponse Rating { get; set; }
[JsonPropertyName("genres")]
public IEnumerable<GenreResponse> Genres { get; set; }
#endregion
@@ -50,7 +54,10 @@ public class SeriesResponse : Series, IQueryOrderable<SeriesResponse>
ReleaseDate = mediaSeries.Media.ReleaseDate;
Length = mediaSeries.Media.Length;
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();
}
#endregion

View File

@@ -1,4 +1,5 @@
using System.Globalization;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Text.Json.Serialization;
@@ -39,13 +40,16 @@ public abstract class QueryParameters
FromQueryAttribute? attribute = property.GetCustomAttributes<FromQueryAttribute>(true).FirstOrDefault();
if (value is not null && attribute is not null)
{
string valueString = (value switch
if (value is IEnumerable enumerable and not string)
{
decimal d => d.ToString(CultureInfo.InvariantCulture),
_ => value.ToString()
})!;
string query = $"{attribute.Name}={valueString}";
queries.Add(query);
IEnumerable<string> arrayQueryElements = enumerable.Cast<object>().Select(x => QueryElementToString(attribute.Name!, x.ToString()));
queries.AddRange(arrayQueryElements);
}
else
{
string query = QueryElementToString(attribute.Name!, value);
queries.Add(query);
}
}
}
@@ -58,6 +62,18 @@ public abstract class QueryParameters
#region PRIVATE METHODS
private string QueryElementToString(string name, object value)
{
string valueString = (value switch
{
decimal d => d.ToString(CultureInfo.InvariantCulture),
_ => value.ToString()
})!;
string query = $"{name}={valueString}";
return query;
}
protected static bool Test<T>(T? property, T? query) =>
(
query is null
@@ -113,6 +129,15 @@ public abstract class QueryParameters
)
);
protected static bool TestContains<T>(IEnumerable<T>? shouldBeInCollection, IEnumerable<T>? collection) =>
(
collection is null
||
shouldBeInCollection is null
||
shouldBeInCollection.All(collection.Contains)
);
#endregion
}

View File

@@ -30,5 +30,9 @@ public class RatingMediaConfiguration : IEntityTypeConfiguration<RatingMedia>
builder.Property(x => x.Rating)
.IsRequired();
builder.Property(x => x.Date)
.IsRequired()
.HasDefaultValueSql("now()");
}
}

View File

@@ -30,5 +30,9 @@ public class RatingMediaSeriesEpisodeConfiguration : IEntityTypeConfiguration<Ra
builder.Property(x => x.Rating)
.IsRequired();
builder.Property(x => x.Date)
.IsRequired()
.HasDefaultValueSql("now()");
}
}

View File

@@ -30,5 +30,9 @@ public class RatingMediaSeriesSeasonConfiguration : IEntityTypeConfiguration<Rat
builder.Property(x => x.Rating)
.IsRequired();
builder.Property(x => x.Date)
.IsRequired()
.HasDefaultValueSql("now()");
}
}

View File

@@ -32,5 +32,9 @@ public class RatingPersonActorRoleConfiguration : IEntityTypeConfiguration<Ratin
builder.Property(x => x.Rating)
.IsRequired();
builder.Property(x => x.Date)
.IsRequired()
.HasDefaultValueSql("now()");
}
}

View File

@@ -32,5 +32,9 @@ public class RatingPersonCreatorRoleConfiguration : IEntityTypeConfiguration<Rat
builder.Property(x => x.Rating)
.IsRequired();
builder.Property(x => x.Date)
.IsRequired()
.HasDefaultValueSql("now()");
}
}

View File

@@ -8,6 +8,7 @@ public class RatingMedia
public required long MediaId { get; set; }
public required long AccountId { get; set; }
public required short Rating { get; set; }
public DateTime Date { get; set; }
#endregion

View File

@@ -10,6 +10,7 @@ public class RatingMediaSeriesEpisode
public required Guid MediaSeriesEpisodeId { get; set; }
public required long AccountId { get; set; }
public required short Rating { get; set; }
public DateTime Date { get; set; }
#endregion

View File

@@ -10,6 +10,7 @@ public class RatingMediaSeriesSeason
public required Guid MediaSeriesSeasonId { get; set; }
public required long AccountId { get; set; }
public required short Rating { get; set; }
public DateTime Date { get; set; }
#endregion

View File

@@ -10,6 +10,7 @@ public class RatingPersonActorRole
public required Guid PersonActorRoleId { get; set; }
public required long AccountId { get; set; }
public required short Rating { get; set; }
public DateTime Date { get; set; }
#endregion

View File

@@ -10,6 +10,7 @@ public class RatingPersonCreatorRole
public required Guid PersonCreatorRoleId { get; set; }
public required long AccountId { get; set; }
public required short Rating { get; set; }
public DateTime Date { get; set; }
#endregion

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace WatchIt.Database.Migrations
{
/// <inheritdoc />
public partial class RatingDate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "Date",
table: "RatingsPersonCreatorRole",
type: "timestamp with time zone",
nullable: false,
defaultValueSql: "now()");
migrationBuilder.AddColumn<DateTime>(
name: "Date",
table: "RatingsPersonActorRole",
type: "timestamp with time zone",
nullable: false,
defaultValueSql: "now()");
migrationBuilder.AddColumn<DateTime>(
name: "Date",
table: "RatingsMediaSeriesSeason",
type: "timestamp with time zone",
nullable: false,
defaultValueSql: "now()");
migrationBuilder.AddColumn<DateTime>(
name: "Date",
table: "RatingsMediaSeriesEpisode",
type: "timestamp with time zone",
nullable: false,
defaultValueSql: "now()");
migrationBuilder.AddColumn<DateTime>(
name: "Date",
table: "RatingsMedia",
type: "timestamp with time zone",
nullable: false,
defaultValueSql: "now()");
migrationBuilder.AlterColumn<string>(
name: "RoleName",
table: "PersonActorRoles",
type: "character varying(100)",
maxLength: 100,
nullable: false,
oldClrType: typeof(string),
oldType: "text");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Date",
table: "RatingsPersonCreatorRole");
migrationBuilder.DropColumn(
name: "Date",
table: "RatingsPersonActorRole");
migrationBuilder.DropColumn(
name: "Date",
table: "RatingsMediaSeriesSeason");
migrationBuilder.DropColumn(
name: "Date",
table: "RatingsMediaSeriesEpisode");
migrationBuilder.DropColumn(
name: "Date",
table: "RatingsMedia");
migrationBuilder.AlterColumn<string>(
name: "RoleName",
table: "PersonActorRoles",
type: "text",
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(100)",
oldMaxLength: 100);
}
}
}

View File

@@ -108,9 +108,9 @@ namespace WatchIt.Database.Migrations
Email = "root@watch.it",
IsAdmin = true,
LastActive = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
LeftSalt = "@(0PF{b6Ot?HO*:yF5`L",
Password = new byte[] { 254, 122, 19, 59, 187, 100, 174, 87, 55, 108, 14, 10, 123, 186, 129, 243, 145, 136, 152, 220, 72, 170, 196, 93, 54, 88, 192, 115, 128, 76, 133, 9, 181, 99, 181, 8, 102, 123, 197, 251, 85, 167, 146, 28, 116, 249, 118, 87, 146, 8, 194, 238, 127, 19, 33, 28, 14, 222, 218, 170, 74, 40, 223, 232 },
RightSalt = "=pt,3T0#CfC1[}Zfp{/u",
LeftSalt = "YuJiv1\"R'*0odl8${\\|S",
Password = new byte[] { 215, 154, 186, 191, 12, 223, 76, 105, 137, 67, 41, 138, 26, 3, 38, 36, 0, 71, 40, 84, 153, 152, 105, 239, 55, 60, 164, 15, 99, 175, 133, 175, 227, 245, 102, 9, 171, 119, 16, 234, 97, 179, 70, 29, 120, 112, 241, 91, 209, 91, 228, 164, 52, 244, 36, 207, 147, 60, 124, 66, 77, 252, 129, 151 },
RightSalt = "oT2N=y7^5,2o'+N>d}~!",
Username = "root"
});
});
@@ -607,7 +607,8 @@ namespace WatchIt.Database.Migrations
b.Property<string>("RoleName")
.IsRequired()
.HasColumnType("text");
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.HasKey("Id");
@@ -766,6 +767,11 @@ namespace WatchIt.Database.Migrations
b.Property<long>("AccountId")
.HasColumnType("bigint");
b.Property<DateTime>("Date")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<long>("MediaId")
.HasColumnType("bigint");
@@ -793,6 +799,11 @@ namespace WatchIt.Database.Migrations
b.Property<long>("AccountId")
.HasColumnType("bigint");
b.Property<DateTime>("Date")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("MediaSeriesEpisodeId")
.HasColumnType("uuid");
@@ -820,6 +831,11 @@ namespace WatchIt.Database.Migrations
b.Property<long>("AccountId")
.HasColumnType("bigint");
b.Property<DateTime>("Date")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("MediaSeriesSeasonId")
.HasColumnType("uuid");
@@ -847,6 +863,11 @@ namespace WatchIt.Database.Migrations
b.Property<long>("AccountId")
.HasColumnType("bigint");
b.Property<DateTime>("Date")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("PersonActorRoleId")
.HasColumnType("uuid");
@@ -874,6 +895,11 @@ namespace WatchIt.Database.Migrations
b.Property<long>("AccountId")
.HasColumnType("bigint");
b.Property<DateTime>("Date")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("PersonCreatorRoleId")
.HasColumnType("uuid");

View File

@@ -3,6 +3,9 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
using WatchIt.WebAPI.Services.Controllers.Accounts;
namespace WatchIt.WebAPI.Controllers;
@@ -49,17 +52,28 @@ public class AccountsController(IAccountsControllerService accountsControllerSer
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetAccountInfo([FromRoute]long id) => await accountsControllerService.GetAccountInfo(id);
[HttpGet("info")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetAccountInfo() => await accountsControllerService.GetAccountInfo();
[HttpPut("info")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ProducesResponseType(typeof(AccountResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> PutAccountInfo([FromBody]AccountRequest data) => await accountsControllerService.PutAccountInfo(data);
[HttpGet("{id}/movies")]
[AllowAnonymous]
[ProducesResponseType(typeof(IEnumerable<MovieRatedResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetAccountRatedMovies([FromRoute]long id, MovieRatedQueryParameters query) => await accountsControllerService.GetAccountRatedMovies(id, query);
[HttpGet("{id}/series")]
[AllowAnonymous]
[ProducesResponseType(typeof(IEnumerable<SeriesRatedResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
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

@@ -5,14 +5,21 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SimpleToolkit.Extensions;
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Media;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
using WatchIt.Database;
using WatchIt.Database.Model.Account;
using WatchIt.Database.Model.Media;
using WatchIt.Database.Model.Rating;
using WatchIt.WebAPI.Services.Controllers.Common;
using WatchIt.WebAPI.Services.Utility.Tokens;
using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions;
using WatchIt.WebAPI.Services.Utility.User;
using Account = WatchIt.Database.Model.Account.Account;
using AccountProfilePicture = WatchIt.Common.Model.Accounts.AccountProfilePicture;
using Person = WatchIt.Database.Model.Person.Person;
namespace WatchIt.WebAPI.Services.Controllers.Accounts;
@@ -62,6 +69,9 @@ public class AccountsControllerService(
RefreshToken = await refreshTokenTask,
};
account.LastActive = DateTime.UtcNow;
await database.SaveChangesAsync();
logger.LogInformation($"Account with ID {account.Id} was authenticated");
return RequestResult.Ok(response);
}
@@ -91,6 +101,9 @@ public class AccountsControllerService(
string accessToken = await tokensService.CreateAccessTokenAsync(token.Account);
token.Account.LastActive = DateTime.UtcNow;
await database.SaveChangesAsync();
logger.LogInformation($"Account with ID {token.AccountId} was authenticated by token refreshing");
return RequestResult.Ok(new AuthenticateResponse
{
@@ -130,7 +143,6 @@ public class AccountsControllerService(
return RequestResult.Ok(picture);
}
public async Task<RequestResult> GetAccountInfo() => await GetAccountInfo(userService.GetUserId());
public async Task<RequestResult> GetAccountInfo(long id)
{
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id);
@@ -154,6 +166,50 @@ public class AccountsControllerService(
data.UpdateAccount(account);
return RequestResult.Ok();
}
public async Task<RequestResult> GetAccountRatedMovies(long id, MovieRatedQueryParameters query)
{
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id);
if (account is null)
{
return RequestResult.NotFound();
}
IEnumerable<MovieRatedResponse> response = account.RatingMedia.Join(database.MediaMovies, x => x.MediaId, x => x.Id, (x, y) => new MovieRatedResponse(y, x));
response = query.PrepareData(response);
return RequestResult.Ok(response);
}
public async Task<RequestResult> GetAccountRatedSeries(long id, SeriesRatedQueryParameters query)
{
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id);
if (account is null)
{
return RequestResult.NotFound();
}
IEnumerable<SeriesRatedResponse> response = account.RatingMedia.Join(database.MediaSeries, x => x.MediaId, x => x.Id, (x, y) => new SeriesRatedResponse(y, x));
response = query.PrepareData(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

View File

@@ -1,4 +1,8 @@
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Media;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
using WatchIt.WebAPI.Services.Controllers.Common;
namespace WatchIt.WebAPI.Services.Controllers.Accounts;
@@ -10,7 +14,9 @@ public interface IAccountsControllerService
Task<RequestResult> AuthenticateRefresh();
Task<RequestResult> Logout();
Task<RequestResult> GetAccountProfilePicture(long id);
Task<RequestResult> GetAccountInfo();
Task<RequestResult> GetAccountInfo(long id);
Task<RequestResult> PutAccountInfo(AccountRequest data);
Task<RequestResult> GetAccountRatedMovies(long id, MovieRatedQueryParameters 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();
}
RatingResponse ratingResponse = RatingResponse.Create(item.RatingMedia);
RatingResponse ratingResponse = RatingResponseBuilder.Initialize()
.Add(item.RatingMedia, x => x.Rating)
.Build();
return RequestResult.Ok(ratingResponse);
}

View File

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

View File

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

View File

@@ -1,4 +1,7 @@
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
using WatchIt.Common.Services.HttpClient;
using WatchIt.Website.Services.Configuration;
using WatchIt.Website.Services.Tokens;
@@ -78,9 +81,9 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
.ExecuteAction();
}
public async Task GetAccountInfoById(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null)
public async Task GetAccountInfo(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountInfoById, id);
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountInfo, id);
HttpRequest request = new HttpRequest(HttpMethodType.Get, url);
HttpResponse response = await httpClientService.SendRequestAsync(request);
@@ -89,18 +92,6 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
.ExecuteAction();
}
public async Task GetAccountInfo(Action<AccountResponse>? successAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountInfo);
HttpRequest request = new HttpRequest(HttpMethodType.Get, url);
HttpResponse response = await httpClientService.SendRequestAsync(request);
response.RegisterActionFor2XXSuccess(successAction)
.RegisterActionFor401Unauthorized(unauthorizedAction)
.RegisterActionFor404NotFound(notFoundAction)
.ExecuteAction();
}
public async Task PutAccountInfo(AccountRequest data, Action<AccountResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.PutAccountInfo);
@@ -117,6 +108,48 @@ public class AccountsClientService(IHttpClientService httpClientService, IConfig
.ExecuteAction();
}
public async Task GetAccountRatedMovies(long id, MovieRatedQueryParameters query, Action<IEnumerable<MovieRatedResponse>>? successAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedMovies, id);
HttpRequest request = new HttpRequest(HttpMethodType.Get, url)
{
Query = query
};
HttpResponse response = await httpClientService.SendRequestAsync(request);
response.RegisterActionFor2XXSuccess(successAction)
.RegisterActionFor404NotFound(notFoundAction)
.ExecuteAction();
}
public async Task GetAccountRatedSeries(long id, SeriesRatedQueryParameters query, Action<IEnumerable<SeriesRatedResponse>>? successAction = null, Action? notFoundAction = null)
{
string url = GetUrl(EndpointsConfiguration.Accounts.GetAccountRatedSeries, id);
HttpRequest request = new HttpRequest(HttpMethodType.Get, url)
{
Query = query
};
HttpResponse response = await httpClientService.SendRequestAsync(request);
response.RegisterActionFor2XXSuccess(successAction)
.RegisterActionFor404NotFound(notFoundAction)
.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

View File

@@ -1,4 +1,7 @@
using WatchIt.Common.Model.Accounts;
using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Persons;
using WatchIt.Common.Model.Series;
namespace WatchIt.Website.Services.Client.Accounts;
@@ -9,7 +12,9 @@ public interface IAccountsClientService
Task AuthenticateRefresh(Action<AuthenticateResponse>? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
Task Logout(Action? successAction = null);
Task GetAccountProfilePicture(long id, Action<AccountProfilePictureResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null);
Task GetAccountInfoById(long id, Action<AccountResponse>? successAction = null, Action? notFoundAction = null);
Task GetAccountInfo(Action<AccountResponse>? successAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null);
Task GetAccountInfo(long id, Action<AccountResponse>? successAction = 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 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

@@ -10,7 +10,7 @@
<ProjectReference Include="..\..\..\WatchIt.Common\WatchIt.Common.Model\WatchIt.Common.Model.csproj" />
<ProjectReference Include="..\..\..\WatchIt.Common\WatchIt.Common.Services\WatchIt.Common.Services.HttpClient\WatchIt.Common.Services.HttpClient.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services.Tokens\WatchIt.Website.Services.Tokens.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services.Utility\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj" />
</ItemGroup>
</Project>

View File

@@ -8,7 +8,9 @@ public class Accounts
public string AuthenticateRefresh { get; set; }
public string Logout { get; set; }
public string GetProfilePicture { get; set; }
public string GetAccountInfoById { get; set; }
public string GetAccountInfo { get; set; }
public string PutAccountInfo { get; set; }
public string GetAccountRatedMovies { get; set; }
public string GetAccountRatedSeries { get; set; }
public string GetAccountRatedPersons { get; set; }
}

View File

@@ -8,7 +8,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\WatchIt.Common\WatchIt.Common.Model\WatchIt.Common.Model.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services.Utility\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj" />
</ItemGroup>
</Project>

View File

@@ -13,7 +13,7 @@
<link rel="stylesheet" href="css/panel.css?version=0.3.0.3"/>
<link rel="stylesheet" href="css/main_button.css?version=0.3.0.0"/>
<link rel="stylesheet" href="css/gaps.css?version=0.3.0.1"/>
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.3.0.22"/>
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.4.0.13"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- BOOTSTRAP -->

View File

@@ -1,14 +1,14 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Query;
namespace WatchIt.Website.Components.Pages.DatabasePage.Subcomponents;
namespace WatchIt.Website.Components.Common.ListComponent;
public abstract class FilterFormComponent<TItem, TQuery> : ComponentBase where TItem : IQueryOrderable<TItem> where TQuery : QueryParameters<TItem>
{
#region PARAMETERS
[CascadingParameter]
protected DatabasePageComponent<TItem, TQuery> Parent { get; set; }
protected ListComponent<TItem, TQuery> Parent { get; set; }
#endregion

View File

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

View File

@@ -4,9 +4,9 @@ using WatchIt.Common.Model.Movies;
using WatchIt.Common.Model.Rating;
using WatchIt.Common.Query;
namespace WatchIt.Website.Components.Pages.DatabasePage;
namespace WatchIt.Website.Components.Common.ListComponent;
public partial class DatabasePageComponent<TItem, TQuery> : ComponentBase where TItem : IQueryOrderable<TItem> where TQuery : QueryParameters<TItem>
public partial class ListComponent<TItem, TQuery> : ComponentBase where TItem : IQueryOrderable<TItem> where TQuery : QueryParameters<TItem>
{
#region SERVICES
@@ -23,6 +23,9 @@ public partial class DatabasePageComponent<TItem, TQuery> : ComponentBase where
[Parameter] public required Func<TItem, string> NameSource { get; set; }
[Parameter] public Func<TItem, string?> AdditionalNameInfoSource { get; set; } = _ => null;
[Parameter] public required Func<TItem, RatingResponse> RatingSource { 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 required string UrlIdTemplate { get; set; }
[Parameter] public required Func<long, Action<Picture>, Task> PictureDownloadingTask { get; set; }
[Parameter] public required Func<TQuery, Action<IEnumerable<TItem>>, Task> ItemDownloadingTask { get; set; }
@@ -33,7 +36,6 @@ public partial class DatabasePageComponent<TItem, TQuery> : ComponentBase where
[Parameter] public Func<long, RatingRequest, Task>? PutRatingMethod { get; set; }
[Parameter] public Func<long, Task>? DeleteRatingMethod { get; set; }
[Parameter] public required string PosterPlaceholder { get; set; }
#endregion

View File

@@ -1,12 +1,10 @@
@using WatchIt.Website.Components.Pages.HomePage.Subcomponents
@typeparam TItem
<div class="panel">
<div class="vstack gap-3">
<span class="panel-text-title">Top @(Count) @(Name) this week by popularity</span>
<span class="panel-text-title">@(Title)</span>
@if (_loaded)
{
<div class="container-grid">
@@ -18,10 +16,10 @@
{
<a class="text-reset text-decoration-none" href="@(string.Format(ItemUrlFormatString, IdSource(_items.ElementAt(i))))">
@{int iCopy = i;}
<ViewRankItemComponent Place="@(i + 1)"
Name="@(NameSource(_items.ElementAt(iCopy)))"
PosterPlaceholder="@(PosterPlaceholder)"
GetPosterAction="@(action => GetPictureAction(IdSource(_items.ElementAt(iCopy)), action))"/>
<HorizontalListItemComponent Place="@(i + 1)"
Name="@(NameSource(_items.ElementAt(iCopy)))"
PosterPlaceholder="@(PosterPlaceholder)"
GetPosterAction="@(action => GetPictureAction(IdSource(_items.ElementAt(iCopy)), action))"/>
</a>
}
</div>

View File

@@ -3,19 +3,19 @@ using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model;
using WatchIt.Common.Query;
namespace WatchIt.Website.Components.Pages.HomePage.Panels;
namespace WatchIt.Website.Components.Common.Panels;
public partial class ViewRankPanelComponent<TItem> : ComponentBase
public partial class HorizontalListPanelComponent<TItem> : ComponentBase
{
#region PARAMETERS
[Parameter] public int Count { get; set; } = 5;
[Parameter] public required string Name {get; set; }
[Parameter] public required Func<int, Action<IEnumerable<TItem>>, Task> GetViewRankAction { get; set; }
[Parameter] public required string Title {get; set; }
[Parameter] public required Func<Action<IEnumerable<TItem>>, Task> GetItemsAction { get; set; }
[Parameter] public required string ItemUrlFormatString { get; set; }
[Parameter] public required Func<TItem, long> IdSource { get; set; }
[Parameter] public required Func<TItem, string> NameSource { get; set; }
[Parameter] public required string PosterPlaceholder {get; set; }
[Parameter] public required string PosterPlaceholder { get; set; }
[Parameter] public required Func<long, Action<Picture>, Task> GetPictureAction { get; set; }
#endregion
@@ -43,7 +43,7 @@ public partial class ViewRankPanelComponent<TItem> : ComponentBase
// STEP 0
endTasks.AddRange(
[
GetViewRankAction(Count, data => _items = data)
GetItemsAction(data => _items = data)
]);
// END

View File

@@ -1,4 +1,4 @@
<div class="container-grid mt-grid">
<div class="container-grid mt-header">
<div class="row">
<div class="col-auto">
<PictureComponent Picture="@(_poster)" Placeholder="@(PosterPlaceholder)" AlternativeText="poster" Height="350"/>

View File

@@ -1,9 +1,5 @@
/* CLASSES */
.mt-grid {
margin-top: 9rem !important;
}
.title-shadow {
text-shadow: 2px 2px 2px #000;
}

View File

@@ -0,0 +1 @@
<img class="rounded-circle object-fit-cover @(Class)" alt="avatar" height="@(Size)" src="@(_picture is null ? "assets/user_placeholder.png" : _picture.ToString())"/>

View File

@@ -0,0 +1,58 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model.Accounts;
using WatchIt.Website.Services.Client.Accounts;
namespace WatchIt.Website.Components.Common.Subcomponents;
public partial class AccountPictureComponent : ComponentBase
{
#region SERVICES
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
#endregion
#region PARAMETERS
[Parameter] public required long Id { get; set; }
[Parameter] public required int Size { get; set; }
[Parameter] public string Class { get; set; } = string.Empty;
#endregion
#region FIELDS
private AccountProfilePictureResponse? _picture;
#endregion
#region PRIVATE METHODS
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
List<Task> endTasks = new List<Task>();
// STEP 0
endTasks.AddRange(
[
AccountsClientService.GetAccountProfilePicture(Id, data => _picture = data)
]);
// END
await Task.WhenAll(endTasks);
StateHasChanged();
}
}
#endregion
}

View File

@@ -5,14 +5,18 @@
<i id="star" class="fas fa-star"></i>
}
<div class="vstack">
@if (Rating is not null && Rating.Count > 0)
@if (SingleRating is not null)
{
<span id="ratingSingleLine">@($"{SingleRating}/10")</span>
}
else if (Rating is not null && Rating.Count > 0)
{
<span id="ratingAverage">@($"{Math.Round(Rating.Average, 2)}/10")</span>
<span id="ratingCount">@(Rating.Count)</span>
}
else
{
<div id="ratingNoItems">
<div id="ratingSingleLine">
@if (Rating is null)
{
<span><div class="spinner-border spinner-border-sm"></div>/10</span>
@@ -40,7 +44,7 @@
font-size: @((1.3 * Scale).ToCultureInvariantString())rem;
}
#ratingNoItems {
#ratingSingleLine {
font-size: @((1.3 * Scale).ToCultureInvariantString())rem;
}

View File

@@ -8,6 +8,7 @@ public partial class DisplayRatingComponent : ComponentBase
#region PARAMETERS
[Parameter] public RatingResponse? Rating { get; set; }
[Parameter] public short? SingleRating { get; set; }
[Parameter] public DisplayRatingComponentEmptyMode EmptyMode { get; set; } = DisplayRatingComponentEmptyMode.NoRatings;
[Parameter] public double Scale { get; set; } = 1;

View File

@@ -1,9 +1,9 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model;
namespace WatchIt.Website.Components.Pages.HomePage.Subcomponents;
namespace WatchIt.Website.Components.Common.Subcomponents;
public partial class ViewRankItemComponent : ComponentBase
public partial class HorizontalListItemComponent : ComponentBase
{
#region PARAMETERS

View File

@@ -12,40 +12,67 @@
<strong>@(Name)</strong>@(string.IsNullOrWhiteSpace(AdditionalInfo) ? string.Empty : AdditionalInfo)
</a>
</div>
<div class="d-inline-flex gap-3">
<a class="text-reset text-decoration-none" href="@(ItemUrl)">
<div class="vstack gap-2">
<span id="ratingNameText">Global rating:</span>
<DisplayRatingComponent Rating="GlobalRating"
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/>
</div>
</a>
@if (GetUserRatingMethod is not null && PutRatingMethod is not null && DeleteRatingMethod is not null)
{
<div class="vr"></div>
<div class="vstack gap-2">
<span id="ratingNameText">Your rating:</span>
<div class="d-inline-flex align-items-center h-100">
@if (_user is null)
{
<span id="ratingLoginInfoText">You must be logged in to rate</span>
}
else if (!_userRatingLoaded)
{
<div class="d-flex align-items-center gap-2">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Loading...</span>
<table id="ratingTable" class="table table-transparent table-bordered align-middle m-0">
<thead>
<tr>
<th scope="col">
<span class="rating-name-text">Global rating:</span>
</th>
@if (SecondaryRatingSingle is not null || SecondaryRatingMultiple is not null)
{
<th scope="col">
<span class="rating-name-text">@(SecondaryRatingTitle):</span>
</th>
}
@if (GetUserRatingMethod is not null && PutRatingMethod is not null && DeleteRatingMethod is not null)
{
<th class scope="col">
<span class="rating-name-text">Your rating:</span>
</th>
}
</tr>
</thead>
<tbody>
<tr>
<td>
<DisplayRatingComponent Rating="@(GlobalRating)"
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/>
</td>
@if (SecondaryRatingSingle is not null || SecondaryRatingMultiple is not null)
{
<td>
<DisplayRatingComponent SingleRating="@(SecondaryRatingSingle)"
Rating="@(SecondaryRatingMultiple)"
EmptyMode="DisplayRatingComponent.DisplayRatingComponentEmptyMode.DoubleDash"
Scale="0.85"/>
</td>
}
@if (GetUserRatingMethod is not null && PutRatingMethod is not null && DeleteRatingMethod is not null)
{
<td>
<div class="d-inline-flex align-items-center h-100">
@if (_user is null)
{
<span id="ratingLoginInfoText">You must be logged in to rate</span>
}
else if (!_userRatingLoaded)
{
<div class="d-flex align-items-center gap-2">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Loading...</span>
</div>
}
else
{
<Rating Color="Color.Light" MaxValue="10" @bind-SelectedValue="@(_userRating)" @onclick="@(RatingChanged)"/>
}
</div>
}
else
{
<Rating Color="Color.Light" MaxValue="10" @bind-SelectedValue="@(_userRating)" @onclick="@(RatingChanged)"/>
}
</div>
</div>
}
</div>
</td>
}
</tr>
</tbody>
</table>
</div>
</div>
</div>

View File

@@ -27,6 +27,9 @@ public partial class ListItemComponent : ComponentBase
[Parameter] public required Func<Action<Picture>, Task> PosterDownloadingTask { get; set; }
[Parameter] public RatingResponse? GlobalRating { get; set; }
[Parameter] public short? SecondaryRatingSingle { get; set; }
[Parameter] public RatingResponse? SecondaryRatingMultiple { get; set; }
[Parameter] public string? SecondaryRatingTitle { 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<RatingRequest, Task>? PutRatingMethod { get; set; }

View File

@@ -1,10 +1,39 @@
/* IDS */
/* TAGS */
#ratingNameText {
font-size: 14px;
font-weight: bold;
tbody, td, tfoot, th, thead, tr {
border-color: inherit;
border-style: unset;
border-width: 0;
}
/* IDS */
#ratingLoginInfoText {
font-size: 13px;
}
#ratingTable {
width: fit-content;
border-spacing: unset;
border-collapse: separate;
margin-left: -0.5rem !important;
}
#ratingTable > thead > tr > th:not(:last-child) {
border-right: 1px solid #444;
}
#ratingTable > tbody > tr > td:not(:last-child) {
border-right: 1px solid #444;
}
/* CLASSES */
.rating-name-text {
font-size: 14px;
font-weight: bold;
}

View File

@@ -1,4 +1,4 @@
@inherits FilterFormComponent<WatchIt.Common.Model.Movies.MovieResponse, WatchIt.Common.Model.Movies.MovieQueryParameters>
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Movies.MovieResponse, WatchIt.Common.Model.Movies.MovieQueryParameters>

View File

@@ -1,5 +1,5 @@
@using WatchIt.Common.Model.Genders
@inherits FilterFormComponent<WatchIt.Common.Model.Persons.PersonResponse, WatchIt.Common.Model.Persons.PersonQueryParameters>
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Persons.PersonResponse, WatchIt.Common.Model.Persons.PersonQueryParameters>

View File

@@ -1,6 +1,7 @@
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.DatabasePage.Subcomponents;

View File

@@ -1,4 +1,4 @@
@inherits FilterFormComponent<WatchIt.Common.Model.Series.SeriesResponse, WatchIt.Common.Model.Series.SeriesQueryParameters>
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Series.SeriesResponse, WatchIt.Common.Model.Series.SeriesQueryParameters>
<EditForm Model="@(Query)">
<div class="container-grid">

View File

@@ -0,0 +1,30 @@
<div id="base" class="vstack">
<AccountPictureComponent Class="shadow position-absolute z-1 start-50 translate-middle" Id="@(AccountData.Id)" Size="240"/>
<div class="panel z-0">
<div class="vstack gap-3">
<div id="space" class="container-grid"></div>
<div class="d-flex justify-content-center">
<h3 class="fw-bold">@(AccountData.Username)</h3>
</div>
@if (!string.IsNullOrWhiteSpace(AccountData.Description))
{
<span>
@(AccountData.Description)
</span>
}
<div class="d-flex flex-wrap justify-content-center metadata-pill-container">
<div class="metadata-pill"><strong>Email:</strong> @(AccountData.Email)</div>
@if (!string.IsNullOrWhiteSpace(AccountData.Gender?.Name))
{
<div class="metadata-pill"><strong>Gender:</strong> @(AccountData.Gender?.Name)</div>
}
<div class="metadata-pill"><strong>Account created:</strong> @(AccountData.CreationDate.ToShortDateString())</div>
<div class="metadata-pill"><strong>Last active:</strong> @(AccountData.LastActive.ToShortDateString())</div>
@if (AccountData.IsAdmin)
{
<div class="metadata-pill"><strong>Admin</strong></div>
}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,57 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model.Accounts;
using WatchIt.Website.Services.Authentication;
using WatchIt.Website.Services.Client.Accounts;
namespace WatchIt.Website.Components.Pages.UserPage.Panels;
public partial class UserPageHeaderPanelComponent : ComponentBase
{
#region SERVICES
[Inject] private IAuthenticationService AuthenticationService { get; set; } = default!;
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
#endregion
#region PARAMETERS
[Parameter] public required AccountResponse AccountData { get; set; }
#endregion
#region FIELDS
private AccountProfilePictureResponse? _accountProfilePicture;
#endregion
#region PRIVATE METHODS
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
List<Task> endTasks = new List<Task>();
// STEP 0
endTasks.AddRange(
[
AccountsClientService.GetAccountProfilePicture(AccountData.Id, data => _accountProfilePicture = data),
]);
// END
await Task.WhenAll(endTasks);
StateHasChanged();
}
}
#endregion
}

View File

@@ -0,0 +1,9 @@
/* IDS */
#base {
margin-top: 120px;
}
#space {
height: 100px;
}

View File

@@ -0,0 +1,82 @@
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Movies.MovieRatedResponse, WatchIt.Common.Model.Movies.MovieRatedQueryParameters>
<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">Title</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.Title)"></InputText>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Original title</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.OriginalTitle)"/>
</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">Release date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.ReleaseDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.ReleaseDateTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Length</span>
<NumericEdit TValue="short?" Class="col form-control" Min="0" @bind-Value="@(Query.LengthFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="short?" Class="col form-control" Min="0" @bind-Value="@(Query.LengthTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Budget</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" @bind-Value="@(Query.BudgetFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" @bind-Value="@(Query.BudgetTo)"/>
</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 mt-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="1" @bind-Value="@(Query.UserRatingFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="1" @bind-Value="@(Query.UserRatingTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.UserRatingDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.UserRatingDateTo)"/>
</div>
</div>
</div>
</EditForm>

View File

@@ -0,0 +1,96 @@
@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-4 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-4 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-4 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-4 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-4 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-4 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-4 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-4 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-4 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-4 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 class="row my-1">
<div class="input-group input-group-sm">
<span class="col-4 input-group-text">User rating (date)</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.UserRatingLastDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.UserRatingLastDateTo)"/>
</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

@@ -0,0 +1,85 @@
@inherits WatchIt.Website.Components.Common.ListComponent.FilterFormComponent<WatchIt.Common.Model.Series.SeriesRatedResponse, WatchIt.Common.Model.Series.SeriesRatedQueryParameters>
<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">Title</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.Title)"></InputText>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Original title</span>
<InputText class="col form-control" placeholder="Search with regex" @bind-Value="@(Query.OriginalTitle)"/>
</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">Release date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.ReleaseDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.ReleaseDateTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Length</span>
<NumericEdit TValue="short?" Class="col form-control" Min="0" @bind-Value="@(Query.LengthFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="short?" Class="col form-control" Min="0" @bind-Value="@(Query.LengthTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">Has ended</span>
<div class="btn-group col">
<input type="radio" class="btn-check" name="has_ended" id="has_ended_yes" autocomplete="off" @onclick="() => Query.HasEnded = true">
<label class="btn btn-outline-secondary btn-sm" for="has_ended_yes">Yes</label>
<input type="radio" class="btn-check" name="has_ended" id="has_ended_no_choice" autocomplete="off" @onclick="() => Query.HasEnded = null" checked>
<label class="btn btn-outline-secondary btn-sm" for="has_ended_no_choice">No choice</label>
<input type="radio" class="btn-check" name="has_ended" id="has_ended_no" autocomplete="off" @onclick="() => Query.HasEnded = false">
<label class="btn btn-outline-secondary btn-sm" for="has_ended_no">No</label>
</div>
</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 mt-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="1" @bind-Value="@(Query.UserRatingFrom)"/>
<span class="col-auto input-group-text">-</span>
<NumericEdit TValue="decimal?" Class="col form-control" Min="0" Max="10" Step="1" @bind-Value="@(Query.UserRatingTo)"/>
</div>
</div>
<div class="row my-1">
<div class="input-group input-group-sm">
<span class="col-3 input-group-text">User rating date</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.UserRatingDateFrom)"/>
<span class="col-auto input-group-text">-</span>
<InputDate TValue="DateOnly?" class="col form-control" @bind-Value="@(Query.UserRatingDateTo)"/>
</div>
</div>
</div>
</EditForm>

View File

@@ -53,16 +53,18 @@
else
{
<Dropdown RightAligned>
<Button Color="Color.Default">
<Button Color="Color.Default" Clicked="@(() => NavigationManager.NavigateTo("/user"))">
<div class="d-flex gap-2 align-items-center">
<img class="rounded-circle" alt="avatar" height="30" src="@(_userProfilePicture is null ? "assets/user_placeholder.png" : _userProfilePicture.ToString())"/>
<AccountPictureComponent Id="@(_user.Id)" Size="30"/>
<span>@(_user.Username)</span>
</div>
</Button>
<DropdownToggle Color="Color.Default" Split />
<DropdownMenu >
<DropdownMenu>
<DropdownItem Clicked="@(() => NavigationManager.NavigateTo("/user"))">Your profile</DropdownItem>
@if (_user.IsAdmin)
{
<DropdownDivider/>
<DropdownItem Clicked="@(() => NavigationManager.NavigateTo("/admin"))">Administrator panel</DropdownItem>
}
<DropdownDivider/>
@@ -77,7 +79,7 @@
</div>
</div>
</div>
<div class="row">
<div class="row pb-3">
<div class="col">
@Body
</div>

View File

@@ -18,7 +18,6 @@ public partial class MainLayout : LayoutComponentBase
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
[Inject] public ITokensService TokensService { get; set; } = default!;
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
[Inject] public IAccountsClientService AccountsClientService { get; set; } = default!;
[Inject] public IMediaClientService MediaClientService { get; set; } = default!;
[Inject] public IPhotosClientService PhotosClientService { get; set; } = default!;
@@ -32,7 +31,6 @@ public partial class MainLayout : LayoutComponentBase
private User? _user;
private PhotoResponse? _defaultBackgroundPhoto;
private AccountProfilePictureResponse? _userProfilePicture;
private bool _searchbarVisible;
private string _searchbarText = string.Empty;
@@ -67,28 +65,14 @@ public partial class MainLayout : LayoutComponentBase
if (firstRender)
{
List<Task> endTasks = new List<Task>();
List<Task> step1Tasks = new List<Task>();
// STEP 0
step1Tasks.AddRange(
[
Task.Run(async () => _user = await AuthenticationService.GetUserAsync())
]);
endTasks.AddRange(
[
Task.Run(async () => _user = await AuthenticationService.GetUserAsync()),
PhotosClientService.GetPhotoRandomBackground(data => _defaultBackgroundPhoto = data)
]);
// STEP 1
await Task.WhenAll(step1Tasks);
if (_user is not null)
{
endTasks.AddRange(
[
AccountsClientService.GetAccountProfilePicture(_user.Id, data => _userProfilePicture = data)
]);
}
// END
await Task.WhenAll(endTasks);

View File

@@ -3,6 +3,7 @@
@using WatchIt.Common.Model.Series
@using WatchIt.Website.Components.Pages.DatabasePage
@using WatchIt.Website.Components.Pages.DatabasePage.Subcomponents
@using WatchIt.Website.Components.Common.ListComponent
@page "/database/{type?}"
@@ -30,78 +31,78 @@
@switch (Type)
{
case "movies":
<DatabasePageComponent TItem="MovieResponse"
TQuery="MovieQueryParameters"
Title="Movies database"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
ItemDownloadingTask="@(MoviesClientService.GetAllMovies)"
SortingOptions="@(new Dictionary<string, string>
{
{ "rating.count", "Number of ratings" },
{ "rating.average", "Average rating" },
{ "title", "Title" },
{ "release_date", "Release date" },
})"
PosterPlaceholder="/assets/media_poster.png"
GetGlobalRatingMethod="@((id, action) => MediaClientService.GetMediaRating(id, action))"
GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaClientService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))"
PutRatingMethod="@((id, request) => MediaClientService.PutMediaRating(id, request))"
DeleteRatingMethod="@(id => MediaClientService.DeleteMediaRating(id))">
<ListComponent TItem="MovieResponse"
TQuery="MovieQueryParameters"
Title="Movies database"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
ItemDownloadingTask="@(MoviesClientService.GetAllMovies)"
SortingOptions="@(new Dictionary<string, string>
{
{ "rating.count", "Number of ratings" },
{ "rating.average", "Average rating" },
{ "title", "Title" },
{ "release_date", "Release date" },
})"
PosterPlaceholder="/assets/media_poster.png"
GetGlobalRatingMethod="@((id, action) => MediaClientService.GetMediaRating(id, action))"
GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaClientService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))"
PutRatingMethod="@((id, request) => MediaClientService.PutMediaRating(id, request))"
DeleteRatingMethod="@(id => MediaClientService.DeleteMediaRating(id))">
<MoviesFilterFormComponent/>
</DatabasePageComponent>
</ListComponent>
break;
case "series":
<DatabasePageComponent TItem="SeriesResponse"
TQuery="SeriesQueryParameters"
Title="TV series database"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
ItemDownloadingTask="@(SeriesClientService.GetAllSeries)"
SortingOptions="@(new Dictionary<string, string>
{
{ "rating.count", "Number of ratings" },
{ "rating.average", "Average rating" },
{ "title", "Title" },
{ "release_date", "Release date" },
})"
PosterPlaceholder="/assets/media_poster.png"
GetGlobalRatingMethod="@((id, action) => MediaClientService.GetMediaRating(id, action))"
GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaClientService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))"
PutRatingMethod="@((id, request) => MediaClientService.PutMediaRating(id, request))"
DeleteRatingMethod="@(id => MediaClientService.DeleteMediaRating(id))">
<ListComponent TItem="SeriesResponse"
TQuery="SeriesQueryParameters"
Title="TV series database"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
ItemDownloadingTask="@(SeriesClientService.GetAllSeries)"
SortingOptions="@(new Dictionary<string, string>
{
{ "rating.count", "Number of ratings" },
{ "rating.average", "Average rating" },
{ "title", "Title" },
{ "release_date", "Release date" },
})"
PosterPlaceholder="/assets/media_poster.png"
GetGlobalRatingMethod="@((id, action) => MediaClientService.GetMediaRating(id, action))"
GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaClientService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))"
PutRatingMethod="@((id, request) => MediaClientService.PutMediaRating(id, request))"
DeleteRatingMethod="@(id => MediaClientService.DeleteMediaRating(id))">
<SeriesFilterFormComponent/>
</DatabasePageComponent>
</ListComponent>
break;
case "people":
<DatabasePageComponent TItem="PersonResponse"
TQuery="PersonQueryParameters"
Title="People database"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
RatingSource="@(item => item.Rating)"
UrlIdTemplate="/person/{0}"
PictureDownloadingTask="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"
ItemDownloadingTask="@(PersonsClientService.GetAllPersons)"
SortingOptions="@(new Dictionary<string, string>
{
{ "rating.count", "Number of ratings" },
{ "rating.average", "Average rating" },
{ "name", "Name" },
{ "birth_date", "Birth date" },
{ "death_date", "Death date" },
})"
PosterPlaceholder="/assets/person_poster.png"
GetGlobalRatingMethod="@((id, action) => PersonsClientService.GetPersonGlobalRating(id, action))">
<ListComponent TItem="PersonResponse"
TQuery="PersonQueryParameters"
Title="People database"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
RatingSource="@(item => item.Rating)"
UrlIdTemplate="/person/{0}"
PictureDownloadingTask="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"
ItemDownloadingTask="@(PersonsClientService.GetAllPersons)"
SortingOptions="@(new Dictionary<string, string>
{
{ "rating.count", "Number of ratings" },
{ "rating.average", "Average rating" },
{ "name", "Name" },
{ "birth_date", "Birth date" },
{ "death_date", "Death date" },
})"
PosterPlaceholder="/assets/person_poster.png"
GetGlobalRatingMethod="@((id, action) => PersonsClientService.GetPersonGlobalRating(id, action))">
<PersonsFilterFormComponent/>
</DatabasePageComponent>
</ListComponent>
break;
}

View File

@@ -1,7 +1,6 @@
@using WatchIt.Common.Model.Movies
@using WatchIt.Common.Model.Persons
@using WatchIt.Common.Model.Series
@using WatchIt.Website.Components.Pages.HomePage.Panels
@page "/"
@@ -10,28 +9,28 @@
<div class="vstack gap-default">
<ViewRankPanelComponent TItem="MovieResponse"
Name="movies"
GetViewRankAction="@((count, action) => MoviesClientService.GetMoviesViewRank(count, successAction: action))"
ItemUrlFormatString="/media/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)"
PosterPlaceholder="/assets/media_poster.png"
GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/>
<ViewRankPanelComponent TItem="SeriesResponse"
Name="TV series"
GetViewRankAction="@((count, action) => SeriesClientService.GetSeriesViewRank(count, successAction: action))"
ItemUrlFormatString="/media/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)"
PosterPlaceholder="/assets/media_poster.png"
GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/>
<ViewRankPanelComponent TItem="PersonResponse"
Name="people"
GetViewRankAction="@((count, action) => PersonsClientService.GetPersonsViewRank(count, successAction: action))"
ItemUrlFormatString="/person/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
PosterPlaceholder="/assets/person_poster.png"
GetPictureAction="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"/>
<HorizontalListPanelComponent TItem="MovieResponse"
Title="Top 5 movies this week by popularity"
GetItemsAction="@(action => MoviesClientService.GetMoviesViewRank(successAction: action))"
ItemUrlFormatString="/media/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)"
PosterPlaceholder="/assets/media_poster.png"
GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/>
<HorizontalListPanelComponent TItem="SeriesResponse"
Title="Top 5 TV series this week by popularity"
GetItemsAction="@(action => SeriesClientService.GetSeriesViewRank(successAction: action))"
ItemUrlFormatString="/media/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)"
PosterPlaceholder="/assets/media_poster.png"
GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/>
<HorizontalListPanelComponent TItem="PersonResponse"
Title="Top 5 people this week by popularity"
GetItemsAction="@(action => PersonsClientService.GetPersonsViewRank(successAction: action))"
ItemUrlFormatString="/person/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
PosterPlaceholder="/assets/person_poster.png"
GetPictureAction="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"/>
</div>

View File

@@ -169,7 +169,7 @@ else
<Tabs Pills
RenderMode="TabsRenderMode.LazyLoad"
SelectedTab="actors"
Class="panel panel-menu panel-background-menu">
Class="panel panel-menu panel-background-menu justify-content-center">
<Items>
<Tab Name="actors">Actors</Tab>
<Tab Name="creators">Creators</Tab>

View File

@@ -44,7 +44,7 @@
<Tabs Pills
RenderMode="TabsRenderMode.LazyLoad"
SelectedTab="actor"
Class="panel panel-menu panel-background-menu">
Class="panel panel-menu panel-background-menu justify-content-center">
<Items>
<Tab Name="actor">Actor</Tab>
<Tab Name="creator">Creator</Tab>

View File

@@ -1 +1,181 @@
@page "/user/{id:long}"
@using System.Text
@using WatchIt.Common.Model.Movies
@using WatchIt.Common.Model.Persons
@using WatchIt.Common.Model.Series
@using WatchIt.Website.Components.Pages.UserPage.Panels
@using WatchIt.Website.Components.Common.ListComponent
@using WatchIt.Website.Components.Pages.UserPage.Subcomponents
@using WatchIt.Website.Services.Client.Persons
@page "/user/{id:long?}"
@{
StringBuilder sb = new StringBuilder(" - WatchIt");
if (!_loaded) sb.Insert(0, "Loading...");
else if (_accountData is null) sb.Insert(0, "Error");
else
{
if (_owner) sb.Insert(0, "Your profile");
else sb.Insert(0, $"\"{_accountData.Username}\" profile");
}
<PageTitle>@(sb.ToString())</PageTitle>
}
<div class="container-grid">
@if (!_loaded)
{
<div class="row">
<div class="col">
<div class="m-5">
<LoadingComponent/>
</div>
</div>
</div>
}
else if (_accountData is null)
{
<div class="row">
<div class="col">
<ErrorPanelComponent ErrorMessage="@($"User with ID {Id!.Value} was not found")"/>
</div>
</div>
}
else
{
<div class="row mt-header">
<div class="col">
<UserPageHeaderPanelComponent AccountData="@(_accountData)"/>
</div>
</div>
<div class="row mt-over-panel-menu">
<div class="col">
<Tabs Pills
RenderMode="TabsRenderMode.LazyLoad"
SelectedTab="summary"
Class="panel panel-menu panel-background-menu justify-content-center">
<Items>
<Tab Name="summary">Summary</Tab>
<Tab Name="movies">Movies</Tab>
<Tab Name="series">TV Series</Tab>
<Tab Name="people">People</Tab>
</Items>
<Content>
<TabPanel Name="summary">
<div class="vstack gap-default mt-default">
<HorizontalListPanelComponent TItem="MovieRatedResponse"
Title="Recently rated movies"
Count="6"
GetItemsAction="@(action => AccountsClientService.GetAccountRatedMovies(Id!.Value, new MovieRatedQueryParameters { First = 6, OrderBy = "user_rating_date" }, successAction: action))"
ItemUrlFormatString="/media/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)"
PosterPlaceholder="/assets/media_poster.png"
GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/>
<HorizontalListPanelComponent TItem="SeriesRatedResponse"
Title="Recently rated TV series"
Count="6"
GetItemsAction="@(action => AccountsClientService.GetAccountRatedSeries(Id!.Value, new SeriesRatedQueryParameters { First = 6, OrderBy = "user_rating_date" }, successAction: action))"
ItemUrlFormatString="/media/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.ReleaseDate.HasValue ? $"{item.Title} ({item.ReleaseDate.Value.Year})" : item.Title)"
PosterPlaceholder="/assets/media_poster.png"
GetPictureAction="@((id, action) => MediaClientService.GetMediaPoster(id, action))"/>
<HorizontalListPanelComponent TItem="PersonRatedResponse"
Title="Recently rated people"
Count="6"
GetItemsAction="@(action => AccountsClientService.GetAccountRatedPersons(Id!.Value, new PersonRatedQueryParameters { First = 6, OrderBy = "user_rating_last_date" }, successAction: action))"
ItemUrlFormatString="/person/{0}"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Name)"
PosterPlaceholder="/assets/person_poster.png"
GetPictureAction="@((id, action) => PersonsClientService.GetPersonPhoto(id, action))"/>
</div>
</TabPanel>
<TabPanel Name="movies">
<div class="mt-default">
<ListComponent TItem="MovieRatedResponse"
TQuery="MovieRatedQueryParameters"
Title="Rated movies"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
SecondaryRatingSingleSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
ItemDownloadingTask="@((query, action) => AccountsClientService.GetAccountRatedMovies(Id!.Value, query, action))"
SortingOptions="@(new Dictionary<string, string> { { "user_rating", "User rating" }, { "user_rating_date", "User rating date" }, { "rating.count", "Number of ratings" }, { "rating.average", "Average rating" }, { "title", "Title" }, { "release_date", "Release date" } })"
PosterPlaceholder="/assets/media_poster.png"
GetGlobalRatingMethod="@((id, action) => MediaClientService.GetMediaRating(id, action))"
GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaClientService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))"
PutRatingMethod="@((id, request) => MediaClientService.PutMediaRating(id, request))"
DeleteRatingMethod="@(id => MediaClientService.DeleteMediaRating(id))">
<MoviesRatedFilterFormComponent/>
</ListComponent>
</div>
</TabPanel>
<TabPanel Name="series">
<div class="mt-default">
<ListComponent TItem="SeriesRatedResponse"
TQuery="SeriesRatedQueryParameters"
Title="Rated TV series"
IdSource="@(item => item.Id)"
NameSource="@(item => item.Title)"
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
RatingSource="@(item => item.Rating)"
SecondaryRatingSingleSource="@(item => _owner ? null : item.UserRating)"
SecondaryRatingTitle="User rating"
UrlIdTemplate="/media/{0}"
PictureDownloadingTask="@((id, action) => MediaClientService.GetMediaPoster(id, action))"
ItemDownloadingTask="@((query, action) => AccountsClientService.GetAccountRatedSeries(Id!.Value, query, action))"
SortingOptions="@(new Dictionary<string, string> { { "user_rating", "User rating" }, { "user_rating_date", "User rating date" }, { "rating.count", "Number of ratings" }, { "rating.average", "Average rating" }, { "title", "Title" }, { "release_date", "Release date" } })"
PosterPlaceholder="/assets/media_poster.png"
GetGlobalRatingMethod="@((id, action) => MediaClientService.GetMediaRating(id, action))"
GetUserRatingMethod="@((id, userId, successAction, notfoundAction) => MediaClientService.GetMediaRatingByUser(id, userId, successAction, notfoundAction))"
PutRatingMethod="@((id, request) => MediaClientService.PutMediaRating(id, request))"
DeleteRatingMethod="@(id => MediaClientService.DeleteMediaRating(id))">
<SeriesRatedFilterFormComponent/>
</ListComponent>
</div>
</TabPanel>
<TabPanel Name="people">
<div class="mt-default">
<ListComponent TItem="PersonRatedResponse"
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" },
{ "user_rating_last_date", "User rating date" },
{ "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>
</Content>
</Tabs>
</div>
</div>
}
</div>

View File

@@ -1,7 +1,91 @@
using Microsoft.AspNetCore.Components;
using WatchIt.Common.Model.Accounts;
using WatchIt.Website.Layout;
using WatchIt.Website.Services.Authentication;
using WatchIt.Website.Services.Client.Accounts;
using WatchIt.Website.Services.Client.Media;
using WatchIt.Website.Services.Client.Persons;
namespace WatchIt.Website.Pages;
public partial class UserPage : ComponentBase
{
#region SERVICES
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
[Inject] private IAuthenticationService AuthenticationService { get; set; } = default!;
[Inject] private IAccountsClientService AccountsClientService { get; set; } = default!;
[Inject] private IMediaClientService MediaClientService { get; set; } = default!;
[Inject] private IPersonsClientService PersonsClientService { get; set; } = default!;
#endregion
#region PARAMETERS
[Parameter] public long? Id { get; set; }
[CascadingParameter] public MainLayout Layout { get; set; } = default!;
#endregion
#region FIELDS
private bool _loaded;
private bool _redirection;
private bool _owner;
private AccountResponse? _accountData;
#endregion
#region PRIVATE METHODS
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
List<Task> endTasks = new List<Task>();
// INIT
Layout.BackgroundPhoto = null;
// STEP 0
endTasks.AddRange(
[
GetUserData()
]);
// END
await Task.WhenAll(endTasks);
_loaded = !_redirection;
StateHasChanged();
}
}
private async Task GetUserData()
{
User? user = await AuthenticationService.GetUserAsync();
if (!Id.HasValue)
{
if (user is null)
{
NavigationManager.NavigateTo("/auth");
_redirection = true;
return;
}
Id = user.Id;
}
await AccountsClientService.GetAccountInfo(Id.Value, data => _accountData = data);
_owner = Id.Value == user?.Id;
}
#endregion
}

View File

@@ -17,7 +17,7 @@
<ProjectReference Include="..\..\WatchIt.Common\WatchIt.Common.Services\WatchIt.Common.Services.HttpClient\WatchIt.Common.Services.HttpClient.csproj" />
<ProjectReference Include="..\..\WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.Tokens\WatchIt.WebAPI.Services.Utility.Tokens.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.Authentication\WatchIt.Website.Services.Authentication.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.Utility\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj" />
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -22,9 +22,11 @@
"AuthenticateRefresh": "/authenticate-refresh",
"Logout": "/logout",
"GetProfilePicture": "/{0}/profile-picture",
"GetAccountInfoById": "/{0}/info",
"GetAccountInfo": "/info",
"PutAccountInfo": "/info"
"GetAccountInfo": "/{0}/info",
"PutAccountInfo": "/info",
"GetAccountRatedMovies": "/{0}/movies",
"GetAccountRatedSeries": "/{0}/series",
"GetAccountRatedPersons": "/{0}/persons"
},
"Genders": {
"Base": "/genders",

View File

@@ -1,5 +1,9 @@
/* DEFAULT */
.mt-header {
margin-top: 9rem !important;
}
.mt-default {
margin-top: 1rem !important;
}

View File

@@ -1,241 +1,241 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website", "WatchIt.Website", "{4CB91BF6-87F1-4088-A943-62548CD1F9F4}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Common", "WatchIt.Common", "{0EDF709C-99F4-3EE5-9136-4C92A768A3F8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Database", "WatchIt.Database", "{15BAFF8D-02CA-4B6D-BB81-E0AA135FEDAF}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Model", "WatchIt.Common\WatchIt.Common.Model\WatchIt.Common.Model.csproj", "{F9E593F3-49A5-3F24-D468-932375E9971D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI", "WatchIt.WebAPI", "{301EEE5B-B54C-491F-ADDA-58E3F4448684}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Query", "WatchIt.Common\WatchIt.Common.Query\WatchIt.Common.Query.csproj", "{5E9025E1-41C3-A27B-99BE-5AAB5CF7EE27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database", "WatchIt.Database\WatchIt.Database\WatchIt.Database.csproj", "{23383776-1F27-4B5D-8C7C-57BFF75FA473}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Common.Services", "WatchIt.Common.Services", "{DC2B5349-B77C-59FD-0E4D-E6749FACB96D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.WorkerServices", "WatchIt.WebAPI\WatchIt.WebAPI.WorkerServices\WatchIt.WebAPI.WorkerServices.csproj", "{0EAD71FB-ED04-4054-9071-61A14FC89B27}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Services.HttpClient", "WatchIt.Common\WatchIt.Common.Services\WatchIt.Common.Services.HttpClient\WatchIt.Common.Services.HttpClient.csproj", "{BCD06E73-4DF3-68A2-1A5A-F4870D5AEF84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI", "WatchIt.WebAPI\WatchIt.WebAPI\WatchIt.WebAPI.csproj", "{E8907CA7-18D5-4DB0-AEAB-911B85DDA63D}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Database", "WatchIt.Database", "{227CC20A-F08E-180E-2EE3-ADA5DB2A8891}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Database.Model", "WatchIt.Database.Model", "{2BCB1F43-033A-4CED-A88C-4126B7CAE107}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database", "WatchIt.Database\WatchIt.Database\WatchIt.Database.csproj", "{B045DE15-B896-BB0E-3D0A-F2B55D2704FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.Model", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model\WatchIt.Database.Model.csproj", "{23F6E4EC-3105-4E94-905B-E6939C528224}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Database.Model", "WatchIt.Database.Model", "{90A007E0-353E-9587-79F1-149A51AEA943}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.Model.Configuration", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model.Configuration\WatchIt.Database.Model.Configuration.csproj", "{698BD418-94F5-4500-80B8-79B1955C9C9F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.Model.Configuration", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model.Configuration\WatchIt.Database.Model.Configuration.csproj", "{C046FE28-DA43-139C-BA9E-289844A7CC9A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.Model.Seeding", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model.Seeding\WatchIt.Database.Model.Seeding.csproj", "{11470B68-F26D-4F15-90CC-25B11A1A36AB}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.Model.Seeding", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model.Seeding\WatchIt.Database.Model.Seeding.csproj", "{3E2F07F5-838A-6141-5B48-D879F2B645E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Controllers", "WatchIt.WebAPI\WatchIt.WebAPI.Controllers\WatchIt.WebAPI.Controllers.csproj", "{B5EA38DF-F396-44DF-80EE-7639EDBEF8BC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Database.Model", "WatchIt.Database\WatchIt.Database.Model\WatchIt.Database.Model\WatchIt.Database.Model.csproj", "{8B33B44E-0245-511C-C2AD-D47E6153670D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Validators", "WatchIt.WebAPI\WatchIt.WebAPI.Validators\WatchIt.WebAPI.Validators.csproj", "{C05705D7-6E53-4BF1-840F-BC5FAB7C1C4E}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI", "WatchIt.WebAPI", "{580BCC26-D6F3-27C7-CD4B-4CDBB56CF3A3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services", "WatchIt.WebAPI.Services", "{9A652B6B-EF78-4029-91A4-AD48168CBF2B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Controllers", "WatchIt.WebAPI\WatchIt.WebAPI.Controllers\WatchIt.WebAPI.Controllers.csproj", "{1E179F33-2D2F-752B-4AE6-5C72B29B4123}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services.Controllers", "WatchIt.WebAPI.Services.Controllers", "{CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Validators", "WatchIt.WebAPI\WatchIt.WebAPI.Validators\WatchIt.WebAPI.Validators.csproj", "{AD58457B-9326-03B8-5831-ED515464503A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Common", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Common\WatchIt.WebAPI.Services.Controllers.Common.csproj", "{A0AA9F70-44A6-49DA-ACC4-ACF0E2F16316}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.WorkerServices", "WatchIt.WebAPI\WatchIt.WebAPI.WorkerServices\WatchIt.WebAPI.WorkerServices.csproj", "{0F30275E-5CE6-D2A6-4D2C-01B0ECF8EC3C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Common", "WatchIt.Common", "{E98C42C1-26E5-4939-8C22-72C253DE874B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI", "WatchIt.WebAPI\WatchIt.WebAPI\WatchIt.WebAPI.csproj", "{935F490B-5A14-87EA-A6F7-C808E8233619}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Model", "WatchIt.Common\WatchIt.Common.Model\WatchIt.Common.Model.csproj", "{C7FF493E-C2E4-4498-8212-D4CBD8C234B9}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services", "WatchIt.WebAPI.Services", "{57D9D146-511A-42D7-C1C5-0D18D3E1ADAF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Accounts", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Accounts\WatchIt.WebAPI.Services.Controllers.Accounts.csproj", "{BB1ACEB6-6834-44D0-8382-DE4E3B297632}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services.Controllers", "WatchIt.WebAPI.Services.Controllers", "{8D8C51C4-760C-9908-3CE3-2A00341BA9B3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services.Utility", "WatchIt.WebAPI.Services.Utility", "{BD014A3C-E574-4ADF-A2E4-9B2D754A854D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Accounts", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Accounts\WatchIt.WebAPI.Services.Controllers.Accounts.csproj", "{3C42534E-57D6-1270-FD53-1F85E7D1B136}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.Tokens", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.Tokens\WatchIt.WebAPI.Services.Utility.Tokens.csproj", "{261C0D38-4D5C-4775-9BF9-819FE30D6ECB}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Common", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Common\WatchIt.WebAPI.Services.Controllers.Common.csproj", "{5D9EB880-7F26-85D2-0606-FFBA669CA8FB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.Configuration", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.Configuration\WatchIt.WebAPI.Services.Utility.Configuration.csproj", "{3B5A06E2-6A5A-4BBF-944D-0E08141A94DA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Genders", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Genders\WatchIt.WebAPI.Services.Controllers.Genders.csproj", "{195DE0A1-20F2-DE84-330E-64EF72C116A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.User", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.User\WatchIt.WebAPI.Services.Utility.User.csproj", "{55777911-8032-4607-B533-12941A52019F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Genres", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Genres\WatchIt.WebAPI.Services.Controllers.Genres.csproj", "{B400518F-D1EE-66BF-31C8-033E502F04C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Genres", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Genres\WatchIt.WebAPI.Services.Controllers.Genres.csproj", "{2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Media", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Media\WatchIt.WebAPI.Services.Controllers.Media.csproj", "{F5813724-3299-71CB-B219-1133BFB3CE49}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website", "WatchIt.Website\WatchIt.Website\WatchIt.Website.csproj", "{46CE78A1-3EC3-4112-AAAD-26EEB8D8B194}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Movies", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Movies\WatchIt.WebAPI.Services.Controllers.Movies.csproj", "{CE807E91-B0E6-0E34-4842-4BCD74C15A71}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Movies", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Movies\WatchIt.WebAPI.Services.Controllers.Movies.csproj", "{69BB6A9E-B673-42AB-A516-6B2513E21FDC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Persons", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Persons\WatchIt.WebAPI.Services.Controllers.Persons.csproj", "{F4521C82-C834-1392-313E-A23DD3C91379}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website.Services", "WatchIt.Website.Services", "{A82972D0-9A60-4B3F-AE46-9F304D79137F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Photos", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Photos\WatchIt.WebAPI.Services.Controllers.Photos.csproj", "{3103A43A-694B-46B4-904D-84FCBAB3DDBE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Common.Services", "WatchIt.Common.Services", "{882A9795-4AC0-4556-9750-6582B2701EFA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Roles", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Roles\WatchIt.WebAPI.Services.Controllers.Roles.csproj", "{F6AAC506-626B-A519-AAA9-DED4C1511B7D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Query", "WatchIt.Common\WatchIt.Common.Query\WatchIt.Common.Query.csproj", "{6C3AE7B4-18C5-42D3-B254-460027E50143}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Series", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Series\WatchIt.WebAPI.Services.Controllers.Series.csproj", "{5FB4B0AA-09A7-11A8-D45F-73946CBDF400}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Common.Services.HttpClient", "WatchIt.Common\WatchIt.Common.Services\WatchIt.Common.Services.HttpClient\WatchIt.Common.Services.HttpClient.csproj", "{A4A75CCA-0DEE-4F1E-9816-60674CA807FA}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.WebAPI.Services.Utility", "WatchIt.WebAPI.Services.Utility", "{0D7F6705-9114-1C0D-61F6-E76D43BBFD95}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Configuration", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Utility\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj", "{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.Configuration", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.Configuration\WatchIt.WebAPI.Services.Utility.Configuration.csproj", "{654F560F-B171-9B66-30C2-FA728CB812E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Media", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Media\WatchIt.WebAPI.Services.Controllers.Media.csproj", "{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.Tokens", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.Tokens\WatchIt.WebAPI.Services.Utility.Tokens.csproj", "{115E0CB8-501B-F73A-B4E5-343DC9C0FC93}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Series", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Series\WatchIt.WebAPI.Services.Controllers.Series.csproj", "{F8FCEF7B-72EA-48BC-AC68-E11244B067DD}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Utility.User", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Utility\WatchIt.WebAPI.Services.Utility.User\WatchIt.WebAPI.Services.Utility.User.csproj", "{872394DD-5F95-2FD2-E30D-7E2B021F055B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Photos", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Photos\WatchIt.WebAPI.Services.Controllers.Photos.csproj", "{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website", "WatchIt.Website", "{A476FF40-6AD9-F237-094D-BC9CD170091F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Persons", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Persons\WatchIt.WebAPI.Services.Controllers.Persons.csproj", "{335686F5-65B8-4D66-AAA8-C5032906451D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website", "WatchIt.Website\WatchIt.Website\WatchIt.Website.csproj", "{53AB0957-9473-8422-8FB2-2611339FAACC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Genders", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Genders\WatchIt.WebAPI.Services.Controllers.Genders.csproj", "{13BE36AB-2120-4F1B-815A-6F5E3F589EE8}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WatchIt.Website.Services", "WatchIt.Website.Services", "{AE403159-2725-6961-6E01-1CB0FBD8C1DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Roles", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Roles\WatchIt.WebAPI.Services.Controllers.Roles.csproj", "{847D157A-E486-4FB6-9AA3-43931A60FB5F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Authentication", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Authentication\WatchIt.Website.Services.Authentication.csproj", "{27B65069-12AD-68A4-238F-58FA84F24813}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Tokens", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Tokens\WatchIt.Website.Services.Tokens.csproj", "{188CE560-A1DC-459D-BF41-1B62E5C0D7B5}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Client", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Client\WatchIt.Website.Services.Client.csproj", "{F176F958-3A95-DDBC-8036-2B7E49B3254E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Authentication", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Authentication\WatchIt.Website.Services.Authentication.csproj", "{B647BAB2-D261-40A0-95AF-95E8A2F00ED1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Configuration", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Configuration\WatchIt.Website.Services.Configuration.csproj", "{979B1DA2-CEA0-95C6-3E85-9329D28A41D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Client", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Client\WatchIt.Website.Services.Client.csproj", "{C9FCD231-3BC9-4453-8949-F521D2D71BCF}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.Tokens", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.Tokens\WatchIt.Website.Services.Tokens.csproj", "{95A0DC72-B175-3174-5EE2-F1E3473DE428}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{23383776-1F27-4B5D-8C7C-57BFF75FA473} = {15BAFF8D-02CA-4B6D-BB81-E0AA135FEDAF}
{0EAD71FB-ED04-4054-9071-61A14FC89B27} = {301EEE5B-B54C-491F-ADDA-58E3F4448684}
{E8907CA7-18D5-4DB0-AEAB-911B85DDA63D} = {301EEE5B-B54C-491F-ADDA-58E3F4448684}
{2BCB1F43-033A-4CED-A88C-4126B7CAE107} = {15BAFF8D-02CA-4B6D-BB81-E0AA135FEDAF}
{23F6E4EC-3105-4E94-905B-E6939C528224} = {2BCB1F43-033A-4CED-A88C-4126B7CAE107}
{698BD418-94F5-4500-80B8-79B1955C9C9F} = {2BCB1F43-033A-4CED-A88C-4126B7CAE107}
{11470B68-F26D-4F15-90CC-25B11A1A36AB} = {2BCB1F43-033A-4CED-A88C-4126B7CAE107}
{B5EA38DF-F396-44DF-80EE-7639EDBEF8BC} = {301EEE5B-B54C-491F-ADDA-58E3F4448684}
{C05705D7-6E53-4BF1-840F-BC5FAB7C1C4E} = {301EEE5B-B54C-491F-ADDA-58E3F4448684}
{9A652B6B-EF78-4029-91A4-AD48168CBF2B} = {301EEE5B-B54C-491F-ADDA-58E3F4448684}
{CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} = {9A652B6B-EF78-4029-91A4-AD48168CBF2B}
{A0AA9F70-44A6-49DA-ACC4-ACF0E2F16316} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{C7FF493E-C2E4-4498-8212-D4CBD8C234B9} = {E98C42C1-26E5-4939-8C22-72C253DE874B}
{BB1ACEB6-6834-44D0-8382-DE4E3B297632} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{BD014A3C-E574-4ADF-A2E4-9B2D754A854D} = {9A652B6B-EF78-4029-91A4-AD48168CBF2B}
{261C0D38-4D5C-4775-9BF9-819FE30D6ECB} = {BD014A3C-E574-4ADF-A2E4-9B2D754A854D}
{3B5A06E2-6A5A-4BBF-944D-0E08141A94DA} = {BD014A3C-E574-4ADF-A2E4-9B2D754A854D}
{55777911-8032-4607-B533-12941A52019F} = {BD014A3C-E574-4ADF-A2E4-9B2D754A854D}
{2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{46CE78A1-3EC3-4112-AAAD-26EEB8D8B194} = {4CB91BF6-87F1-4088-A943-62548CD1F9F4}
{69BB6A9E-B673-42AB-A516-6B2513E21FDC} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{A82972D0-9A60-4B3F-AE46-9F304D79137F} = {4CB91BF6-87F1-4088-A943-62548CD1F9F4}
{882A9795-4AC0-4556-9750-6582B2701EFA} = {E98C42C1-26E5-4939-8C22-72C253DE874B}
{6C3AE7B4-18C5-42D3-B254-460027E50143} = {E98C42C1-26E5-4939-8C22-72C253DE874B}
{A4A75CCA-0DEE-4F1E-9816-60674CA807FA} = {882A9795-4AC0-4556-9750-6582B2701EFA}
{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{F8FCEF7B-72EA-48BC-AC68-E11244B067DD} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{335686F5-65B8-4D66-AAA8-C5032906451D} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{13BE36AB-2120-4F1B-815A-6F5E3F589EE8} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{847D157A-E486-4FB6-9AA3-43931A60FB5F} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7}
{188CE560-A1DC-459D-BF41-1B62E5C0D7B5} = {A82972D0-9A60-4B3F-AE46-9F304D79137F}
{B647BAB2-D261-40A0-95AF-95E8A2F00ED1} = {A82972D0-9A60-4B3F-AE46-9F304D79137F}
{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F} = {A82972D0-9A60-4B3F-AE46-9F304D79137F}
{C9FCD231-3BC9-4453-8949-F521D2D71BCF} = {A82972D0-9A60-4B3F-AE46-9F304D79137F}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{23383776-1F27-4B5D-8C7C-57BFF75FA473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23383776-1F27-4B5D-8C7C-57BFF75FA473}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23383776-1F27-4B5D-8C7C-57BFF75FA473}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23383776-1F27-4B5D-8C7C-57BFF75FA473}.Release|Any CPU.Build.0 = Release|Any CPU
{0EAD71FB-ED04-4054-9071-61A14FC89B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0EAD71FB-ED04-4054-9071-61A14FC89B27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0EAD71FB-ED04-4054-9071-61A14FC89B27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0EAD71FB-ED04-4054-9071-61A14FC89B27}.Release|Any CPU.Build.0 = Release|Any CPU
{E8907CA7-18D5-4DB0-AEAB-911B85DDA63D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8907CA7-18D5-4DB0-AEAB-911B85DDA63D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8907CA7-18D5-4DB0-AEAB-911B85DDA63D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8907CA7-18D5-4DB0-AEAB-911B85DDA63D}.Release|Any CPU.Build.0 = Release|Any CPU
{23F6E4EC-3105-4E94-905B-E6939C528224}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23F6E4EC-3105-4E94-905B-E6939C528224}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23F6E4EC-3105-4E94-905B-E6939C528224}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23F6E4EC-3105-4E94-905B-E6939C528224}.Release|Any CPU.Build.0 = Release|Any CPU
{698BD418-94F5-4500-80B8-79B1955C9C9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{698BD418-94F5-4500-80B8-79B1955C9C9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{698BD418-94F5-4500-80B8-79B1955C9C9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{698BD418-94F5-4500-80B8-79B1955C9C9F}.Release|Any CPU.Build.0 = Release|Any CPU
{11470B68-F26D-4F15-90CC-25B11A1A36AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11470B68-F26D-4F15-90CC-25B11A1A36AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11470B68-F26D-4F15-90CC-25B11A1A36AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11470B68-F26D-4F15-90CC-25B11A1A36AB}.Release|Any CPU.Build.0 = Release|Any CPU
{B5EA38DF-F396-44DF-80EE-7639EDBEF8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5EA38DF-F396-44DF-80EE-7639EDBEF8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5EA38DF-F396-44DF-80EE-7639EDBEF8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5EA38DF-F396-44DF-80EE-7639EDBEF8BC}.Release|Any CPU.Build.0 = Release|Any CPU
{C05705D7-6E53-4BF1-840F-BC5FAB7C1C4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C05705D7-6E53-4BF1-840F-BC5FAB7C1C4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C05705D7-6E53-4BF1-840F-BC5FAB7C1C4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C05705D7-6E53-4BF1-840F-BC5FAB7C1C4E}.Release|Any CPU.Build.0 = Release|Any CPU
{A0AA9F70-44A6-49DA-ACC4-ACF0E2F16316}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0AA9F70-44A6-49DA-ACC4-ACF0E2F16316}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0AA9F70-44A6-49DA-ACC4-ACF0E2F16316}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0AA9F70-44A6-49DA-ACC4-ACF0E2F16316}.Release|Any CPU.Build.0 = Release|Any CPU
{C7FF493E-C2E4-4498-8212-D4CBD8C234B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7FF493E-C2E4-4498-8212-D4CBD8C234B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7FF493E-C2E4-4498-8212-D4CBD8C234B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7FF493E-C2E4-4498-8212-D4CBD8C234B9}.Release|Any CPU.Build.0 = Release|Any CPU
{BB1ACEB6-6834-44D0-8382-DE4E3B297632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB1ACEB6-6834-44D0-8382-DE4E3B297632}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB1ACEB6-6834-44D0-8382-DE4E3B297632}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB1ACEB6-6834-44D0-8382-DE4E3B297632}.Release|Any CPU.Build.0 = Release|Any CPU
{261C0D38-4D5C-4775-9BF9-819FE30D6ECB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{261C0D38-4D5C-4775-9BF9-819FE30D6ECB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{261C0D38-4D5C-4775-9BF9-819FE30D6ECB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{261C0D38-4D5C-4775-9BF9-819FE30D6ECB}.Release|Any CPU.Build.0 = Release|Any CPU
{3B5A06E2-6A5A-4BBF-944D-0E08141A94DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B5A06E2-6A5A-4BBF-944D-0E08141A94DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B5A06E2-6A5A-4BBF-944D-0E08141A94DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B5A06E2-6A5A-4BBF-944D-0E08141A94DA}.Release|Any CPU.Build.0 = Release|Any CPU
{55777911-8032-4607-B533-12941A52019F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55777911-8032-4607-B533-12941A52019F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55777911-8032-4607-B533-12941A52019F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55777911-8032-4607-B533-12941A52019F}.Release|Any CPU.Build.0 = Release|Any CPU
{2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2EC6FD28-C580-45FA-B6A7-92A6BF0CCC54}.Release|Any CPU.Build.0 = Release|Any CPU
{46CE78A1-3EC3-4112-AAAD-26EEB8D8B194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46CE78A1-3EC3-4112-AAAD-26EEB8D8B194}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46CE78A1-3EC3-4112-AAAD-26EEB8D8B194}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46CE78A1-3EC3-4112-AAAD-26EEB8D8B194}.Release|Any CPU.Build.0 = Release|Any CPU
{69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69BB6A9E-B673-42AB-A516-6B2513E21FDC}.Release|Any CPU.Build.0 = Release|Any CPU
{6C3AE7B4-18C5-42D3-B254-460027E50143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C3AE7B4-18C5-42D3-B254-460027E50143}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C3AE7B4-18C5-42D3-B254-460027E50143}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C3AE7B4-18C5-42D3-B254-460027E50143}.Release|Any CPU.Build.0 = Release|Any CPU
{A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4A75CCA-0DEE-4F1E-9816-60674CA807FA}.Release|Any CPU.Build.0 = Release|Any CPU
{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DBBE7EA-05FE-481F-8814-6FA0BC9E571F}.Release|Any CPU.Build.0 = Release|Any CPU
{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3156AD7B-D6EC-4EB6-AEE8-4FBAF14C18E4}.Release|Any CPU.Build.0 = Release|Any CPU
{F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8FCEF7B-72EA-48BC-AC68-E11244B067DD}.Release|Any CPU.Build.0 = Release|Any CPU
{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Release|Any CPU.Build.0 = Release|Any CPU
{335686F5-65B8-4D66-AAA8-C5032906451D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{335686F5-65B8-4D66-AAA8-C5032906451D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{335686F5-65B8-4D66-AAA8-C5032906451D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{335686F5-65B8-4D66-AAA8-C5032906451D}.Release|Any CPU.Build.0 = Release|Any CPU
{13BE36AB-2120-4F1B-815A-6F5E3F589EE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13BE36AB-2120-4F1B-815A-6F5E3F589EE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13BE36AB-2120-4F1B-815A-6F5E3F589EE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13BE36AB-2120-4F1B-815A-6F5E3F589EE8}.Release|Any CPU.Build.0 = Release|Any CPU
{847D157A-E486-4FB6-9AA3-43931A60FB5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{847D157A-E486-4FB6-9AA3-43931A60FB5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{847D157A-E486-4FB6-9AA3-43931A60FB5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{847D157A-E486-4FB6-9AA3-43931A60FB5F}.Release|Any CPU.Build.0 = Release|Any CPU
{188CE560-A1DC-459D-BF41-1B62E5C0D7B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{188CE560-A1DC-459D-BF41-1B62E5C0D7B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{188CE560-A1DC-459D-BF41-1B62E5C0D7B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{188CE560-A1DC-459D-BF41-1B62E5C0D7B5}.Release|Any CPU.Build.0 = Release|Any CPU
{B647BAB2-D261-40A0-95AF-95E8A2F00ED1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B647BAB2-D261-40A0-95AF-95E8A2F00ED1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B647BAB2-D261-40A0-95AF-95E8A2F00ED1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B647BAB2-D261-40A0-95AF-95E8A2F00ED1}.Release|Any CPU.Build.0 = Release|Any CPU
{C9FCD231-3BC9-4453-8949-F521D2D71BCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9FCD231-3BC9-4453-8949-F521D2D71BCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9FCD231-3BC9-4453-8949-F521D2D71BCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9FCD231-3BC9-4453-8949-F521D2D71BCF}.Release|Any CPU.Build.0 = Release|Any CPU
{F9E593F3-49A5-3F24-D468-932375E9971D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9E593F3-49A5-3F24-D468-932375E9971D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9E593F3-49A5-3F24-D468-932375E9971D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9E593F3-49A5-3F24-D468-932375E9971D}.Release|Any CPU.Build.0 = Release|Any CPU
{5E9025E1-41C3-A27B-99BE-5AAB5CF7EE27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E9025E1-41C3-A27B-99BE-5AAB5CF7EE27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E9025E1-41C3-A27B-99BE-5AAB5CF7EE27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E9025E1-41C3-A27B-99BE-5AAB5CF7EE27}.Release|Any CPU.Build.0 = Release|Any CPU
{BCD06E73-4DF3-68A2-1A5A-F4870D5AEF84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCD06E73-4DF3-68A2-1A5A-F4870D5AEF84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCD06E73-4DF3-68A2-1A5A-F4870D5AEF84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCD06E73-4DF3-68A2-1A5A-F4870D5AEF84}.Release|Any CPU.Build.0 = Release|Any CPU
{B045DE15-B896-BB0E-3D0A-F2B55D2704FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B045DE15-B896-BB0E-3D0A-F2B55D2704FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B045DE15-B896-BB0E-3D0A-F2B55D2704FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B045DE15-B896-BB0E-3D0A-F2B55D2704FF}.Release|Any CPU.Build.0 = Release|Any CPU
{C046FE28-DA43-139C-BA9E-289844A7CC9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C046FE28-DA43-139C-BA9E-289844A7CC9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C046FE28-DA43-139C-BA9E-289844A7CC9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C046FE28-DA43-139C-BA9E-289844A7CC9A}.Release|Any CPU.Build.0 = Release|Any CPU
{3E2F07F5-838A-6141-5B48-D879F2B645E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E2F07F5-838A-6141-5B48-D879F2B645E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E2F07F5-838A-6141-5B48-D879F2B645E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E2F07F5-838A-6141-5B48-D879F2B645E0}.Release|Any CPU.Build.0 = Release|Any CPU
{8B33B44E-0245-511C-C2AD-D47E6153670D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B33B44E-0245-511C-C2AD-D47E6153670D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B33B44E-0245-511C-C2AD-D47E6153670D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B33B44E-0245-511C-C2AD-D47E6153670D}.Release|Any CPU.Build.0 = Release|Any CPU
{1E179F33-2D2F-752B-4AE6-5C72B29B4123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E179F33-2D2F-752B-4AE6-5C72B29B4123}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E179F33-2D2F-752B-4AE6-5C72B29B4123}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E179F33-2D2F-752B-4AE6-5C72B29B4123}.Release|Any CPU.Build.0 = Release|Any CPU
{AD58457B-9326-03B8-5831-ED515464503A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD58457B-9326-03B8-5831-ED515464503A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD58457B-9326-03B8-5831-ED515464503A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD58457B-9326-03B8-5831-ED515464503A}.Release|Any CPU.Build.0 = Release|Any CPU
{0F30275E-5CE6-D2A6-4D2C-01B0ECF8EC3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F30275E-5CE6-D2A6-4D2C-01B0ECF8EC3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F30275E-5CE6-D2A6-4D2C-01B0ECF8EC3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F30275E-5CE6-D2A6-4D2C-01B0ECF8EC3C}.Release|Any CPU.Build.0 = Release|Any CPU
{935F490B-5A14-87EA-A6F7-C808E8233619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{935F490B-5A14-87EA-A6F7-C808E8233619}.Debug|Any CPU.Build.0 = Debug|Any CPU
{935F490B-5A14-87EA-A6F7-C808E8233619}.Release|Any CPU.ActiveCfg = Release|Any CPU
{935F490B-5A14-87EA-A6F7-C808E8233619}.Release|Any CPU.Build.0 = Release|Any CPU
{3C42534E-57D6-1270-FD53-1F85E7D1B136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C42534E-57D6-1270-FD53-1F85E7D1B136}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C42534E-57D6-1270-FD53-1F85E7D1B136}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C42534E-57D6-1270-FD53-1F85E7D1B136}.Release|Any CPU.Build.0 = Release|Any CPU
{5D9EB880-7F26-85D2-0606-FFBA669CA8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D9EB880-7F26-85D2-0606-FFBA669CA8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D9EB880-7F26-85D2-0606-FFBA669CA8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D9EB880-7F26-85D2-0606-FFBA669CA8FB}.Release|Any CPU.Build.0 = Release|Any CPU
{195DE0A1-20F2-DE84-330E-64EF72C116A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{195DE0A1-20F2-DE84-330E-64EF72C116A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{195DE0A1-20F2-DE84-330E-64EF72C116A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{195DE0A1-20F2-DE84-330E-64EF72C116A1}.Release|Any CPU.Build.0 = Release|Any CPU
{B400518F-D1EE-66BF-31C8-033E502F04C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B400518F-D1EE-66BF-31C8-033E502F04C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B400518F-D1EE-66BF-31C8-033E502F04C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B400518F-D1EE-66BF-31C8-033E502F04C2}.Release|Any CPU.Build.0 = Release|Any CPU
{F5813724-3299-71CB-B219-1133BFB3CE49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5813724-3299-71CB-B219-1133BFB3CE49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5813724-3299-71CB-B219-1133BFB3CE49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5813724-3299-71CB-B219-1133BFB3CE49}.Release|Any CPU.Build.0 = Release|Any CPU
{CE807E91-B0E6-0E34-4842-4BCD74C15A71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE807E91-B0E6-0E34-4842-4BCD74C15A71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE807E91-B0E6-0E34-4842-4BCD74C15A71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE807E91-B0E6-0E34-4842-4BCD74C15A71}.Release|Any CPU.Build.0 = Release|Any CPU
{F4521C82-C834-1392-313E-A23DD3C91379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4521C82-C834-1392-313E-A23DD3C91379}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4521C82-C834-1392-313E-A23DD3C91379}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4521C82-C834-1392-313E-A23DD3C91379}.Release|Any CPU.Build.0 = Release|Any CPU
{3103A43A-694B-46B4-904D-84FCBAB3DDBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3103A43A-694B-46B4-904D-84FCBAB3DDBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3103A43A-694B-46B4-904D-84FCBAB3DDBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3103A43A-694B-46B4-904D-84FCBAB3DDBE}.Release|Any CPU.Build.0 = Release|Any CPU
{F6AAC506-626B-A519-AAA9-DED4C1511B7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6AAC506-626B-A519-AAA9-DED4C1511B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6AAC506-626B-A519-AAA9-DED4C1511B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6AAC506-626B-A519-AAA9-DED4C1511B7D}.Release|Any CPU.Build.0 = Release|Any CPU
{5FB4B0AA-09A7-11A8-D45F-73946CBDF400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FB4B0AA-09A7-11A8-D45F-73946CBDF400}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FB4B0AA-09A7-11A8-D45F-73946CBDF400}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FB4B0AA-09A7-11A8-D45F-73946CBDF400}.Release|Any CPU.Build.0 = Release|Any CPU
{654F560F-B171-9B66-30C2-FA728CB812E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{654F560F-B171-9B66-30C2-FA728CB812E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{654F560F-B171-9B66-30C2-FA728CB812E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{654F560F-B171-9B66-30C2-FA728CB812E1}.Release|Any CPU.Build.0 = Release|Any CPU
{115E0CB8-501B-F73A-B4E5-343DC9C0FC93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{115E0CB8-501B-F73A-B4E5-343DC9C0FC93}.Debug|Any CPU.Build.0 = Debug|Any CPU
{115E0CB8-501B-F73A-B4E5-343DC9C0FC93}.Release|Any CPU.ActiveCfg = Release|Any CPU
{115E0CB8-501B-F73A-B4E5-343DC9C0FC93}.Release|Any CPU.Build.0 = Release|Any CPU
{872394DD-5F95-2FD2-E30D-7E2B021F055B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{872394DD-5F95-2FD2-E30D-7E2B021F055B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{872394DD-5F95-2FD2-E30D-7E2B021F055B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{872394DD-5F95-2FD2-E30D-7E2B021F055B}.Release|Any CPU.Build.0 = Release|Any CPU
{53AB0957-9473-8422-8FB2-2611339FAACC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53AB0957-9473-8422-8FB2-2611339FAACC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53AB0957-9473-8422-8FB2-2611339FAACC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53AB0957-9473-8422-8FB2-2611339FAACC}.Release|Any CPU.Build.0 = Release|Any CPU
{27B65069-12AD-68A4-238F-58FA84F24813}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27B65069-12AD-68A4-238F-58FA84F24813}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27B65069-12AD-68A4-238F-58FA84F24813}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27B65069-12AD-68A4-238F-58FA84F24813}.Release|Any CPU.Build.0 = Release|Any CPU
{F176F958-3A95-DDBC-8036-2B7E49B3254E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F176F958-3A95-DDBC-8036-2B7E49B3254E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F176F958-3A95-DDBC-8036-2B7E49B3254E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F176F958-3A95-DDBC-8036-2B7E49B3254E}.Release|Any CPU.Build.0 = Release|Any CPU
{979B1DA2-CEA0-95C6-3E85-9329D28A41D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{979B1DA2-CEA0-95C6-3E85-9329D28A41D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{979B1DA2-CEA0-95C6-3E85-9329D28A41D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{979B1DA2-CEA0-95C6-3E85-9329D28A41D1}.Release|Any CPU.Build.0 = Release|Any CPU
{95A0DC72-B175-3174-5EE2-F1E3473DE428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95A0DC72-B175-3174-5EE2-F1E3473DE428}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95A0DC72-B175-3174-5EE2-F1E3473DE428}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95A0DC72-B175-3174-5EE2-F1E3473DE428}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F9E593F3-49A5-3F24-D468-932375E9971D} = {0EDF709C-99F4-3EE5-9136-4C92A768A3F8}
{5E9025E1-41C3-A27B-99BE-5AAB5CF7EE27} = {0EDF709C-99F4-3EE5-9136-4C92A768A3F8}
{DC2B5349-B77C-59FD-0E4D-E6749FACB96D} = {0EDF709C-99F4-3EE5-9136-4C92A768A3F8}
{BCD06E73-4DF3-68A2-1A5A-F4870D5AEF84} = {DC2B5349-B77C-59FD-0E4D-E6749FACB96D}
{B045DE15-B896-BB0E-3D0A-F2B55D2704FF} = {227CC20A-F08E-180E-2EE3-ADA5DB2A8891}
{90A007E0-353E-9587-79F1-149A51AEA943} = {227CC20A-F08E-180E-2EE3-ADA5DB2A8891}
{C046FE28-DA43-139C-BA9E-289844A7CC9A} = {90A007E0-353E-9587-79F1-149A51AEA943}
{3E2F07F5-838A-6141-5B48-D879F2B645E0} = {90A007E0-353E-9587-79F1-149A51AEA943}
{8B33B44E-0245-511C-C2AD-D47E6153670D} = {90A007E0-353E-9587-79F1-149A51AEA943}
{1E179F33-2D2F-752B-4AE6-5C72B29B4123} = {580BCC26-D6F3-27C7-CD4B-4CDBB56CF3A3}
{AD58457B-9326-03B8-5831-ED515464503A} = {580BCC26-D6F3-27C7-CD4B-4CDBB56CF3A3}
{0F30275E-5CE6-D2A6-4D2C-01B0ECF8EC3C} = {580BCC26-D6F3-27C7-CD4B-4CDBB56CF3A3}
{935F490B-5A14-87EA-A6F7-C808E8233619} = {580BCC26-D6F3-27C7-CD4B-4CDBB56CF3A3}
{57D9D146-511A-42D7-C1C5-0D18D3E1ADAF} = {580BCC26-D6F3-27C7-CD4B-4CDBB56CF3A3}
{8D8C51C4-760C-9908-3CE3-2A00341BA9B3} = {57D9D146-511A-42D7-C1C5-0D18D3E1ADAF}
{0D7F6705-9114-1C0D-61F6-E76D43BBFD95} = {57D9D146-511A-42D7-C1C5-0D18D3E1ADAF}
{3C42534E-57D6-1270-FD53-1F85E7D1B136} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{5D9EB880-7F26-85D2-0606-FFBA669CA8FB} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{195DE0A1-20F2-DE84-330E-64EF72C116A1} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{B400518F-D1EE-66BF-31C8-033E502F04C2} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{F5813724-3299-71CB-B219-1133BFB3CE49} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{CE807E91-B0E6-0E34-4842-4BCD74C15A71} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{F4521C82-C834-1392-313E-A23DD3C91379} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{3103A43A-694B-46B4-904D-84FCBAB3DDBE} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{F6AAC506-626B-A519-AAA9-DED4C1511B7D} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{5FB4B0AA-09A7-11A8-D45F-73946CBDF400} = {8D8C51C4-760C-9908-3CE3-2A00341BA9B3}
{654F560F-B171-9B66-30C2-FA728CB812E1} = {0D7F6705-9114-1C0D-61F6-E76D43BBFD95}
{115E0CB8-501B-F73A-B4E5-343DC9C0FC93} = {0D7F6705-9114-1C0D-61F6-E76D43BBFD95}
{872394DD-5F95-2FD2-E30D-7E2B021F055B} = {0D7F6705-9114-1C0D-61F6-E76D43BBFD95}
{53AB0957-9473-8422-8FB2-2611339FAACC} = {A476FF40-6AD9-F237-094D-BC9CD170091F}
{AE403159-2725-6961-6E01-1CB0FBD8C1DF} = {A476FF40-6AD9-F237-094D-BC9CD170091F}
{27B65069-12AD-68A4-238F-58FA84F24813} = {AE403159-2725-6961-6E01-1CB0FBD8C1DF}
{F176F958-3A95-DDBC-8036-2B7E49B3254E} = {AE403159-2725-6961-6E01-1CB0FBD8C1DF}
{979B1DA2-CEA0-95C6-3E85-9329D28A41D1} = {AE403159-2725-6961-6E01-1CB0FBD8C1DF}
{95A0DC72-B175-3174-5EE2-F1E3473DE428} = {AE403159-2725-6961-6E01-1CB0FBD8C1DF}
EndGlobalSection
EndGlobal

Some files were not shown because too many files have changed in this diff Show More