Revert "Unify ChapterRating with Rating"
This wasn't working out, there is still some duplicate code. But not that much, and from the API, there is no different. Hooray!
This commit is contained in:
parent
052b3f9fe4
commit
184cf46533
21 changed files with 389 additions and 87 deletions
|
|
@ -399,7 +399,7 @@ public class ChapterController : BaseApiController
|
|||
|
||||
|
||||
[HttpGet("chapter-detail-plus")]
|
||||
public async Task<ChapterDetailPlusDto> ChapterDetailPlus([FromQuery] int seriesId, [FromQuery] int chapterId)
|
||||
public async Task<ChapterDetailPlusDto> ChapterDetailPlus([FromQuery] int chapterId)
|
||||
{
|
||||
var ret = new ChapterDetailPlusDto();
|
||||
|
||||
|
|
@ -408,7 +408,7 @@ public class ChapterController : BaseApiController
|
|||
.OrderByDescending(review => review.Username.Equals(User.GetUsername()) ? 1 : 0)
|
||||
.ToList();
|
||||
|
||||
var ownRating = await _unitOfWork.UserRepository.GetUserRatingAsync(seriesId, User.GetUserId(), chapterId);
|
||||
var ownRating = await _unitOfWork.UserRepository.GetUserChapterRatingAsync(User.GetUserId(), chapterId);
|
||||
if (ownRating != null)
|
||||
{
|
||||
ret.Rating = ownRating.Rating;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Builders;
|
||||
|
|
@ -39,15 +40,37 @@ public class ReviewController : BaseApiController
|
|||
[HttpPost]
|
||||
public async Task<ActionResult<UserReviewDto>> UpdateReview(UpdateUserReviewDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Ratings);
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Ratings | AppUserIncludes.ChapterRatings);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
var ratingBuilder = new RatingBuilder(await _unitOfWork.UserRepository.GetUserRatingAsync(dto.SeriesId, user.Id, dto.ChapterId));
|
||||
UserReviewDto review;
|
||||
if (dto.ChapterId != null)
|
||||
{
|
||||
review = await UpdateChapterReview(user, dto, dto.ChapterId.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
review = await UpdateSeriesReview(user, dto);
|
||||
}
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
if (dto.ChapterId == null)
|
||||
{
|
||||
BackgroundJob.Enqueue(() =>
|
||||
_scrobblingService.ScrobbleReviewUpdate(user.Id, dto.SeriesId, string.Empty, dto.Body));
|
||||
}
|
||||
return Ok(review);
|
||||
}
|
||||
|
||||
private async Task<UserReviewDto> UpdateSeriesReview(AppUser user, UpdateUserReviewDto dto)
|
||||
{
|
||||
var ratingBuilder = new RatingBuilder(await _unitOfWork.UserRepository.GetUserRatingAsync(dto.SeriesId, user.Id));
|
||||
|
||||
var rating = ratingBuilder
|
||||
.WithBody(dto.Body)
|
||||
.WithSeriesId(dto.SeriesId)
|
||||
.WithChapterId(dto.ChapterId)
|
||||
.WithTagline(string.Empty)
|
||||
.Build();
|
||||
|
||||
|
|
@ -55,14 +78,24 @@ public class ReviewController : BaseApiController
|
|||
{
|
||||
user.Ratings.Add(rating);
|
||||
}
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
return _mapper.Map<UserReviewDto>(rating);
|
||||
}
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
private async Task<UserReviewDto> UpdateChapterReview(AppUser user, UpdateUserReviewDto dto, int chapterId)
|
||||
{
|
||||
var ratingBuilder = new ChapterRatingBuilder(await _unitOfWork.UserRepository.GetUserChapterRatingAsync(user.Id, chapterId));
|
||||
|
||||
var rating = ratingBuilder
|
||||
.WithBody(dto.Body)
|
||||
.WithSeriesId(dto.SeriesId)
|
||||
.WithChapterId(chapterId)
|
||||
.Build();
|
||||
|
||||
BackgroundJob.Enqueue(() =>
|
||||
_scrobblingService.ScrobbleReviewUpdate(user.Id, dto.SeriesId, string.Empty, dto.Body));
|
||||
return Ok(_mapper.Map<UserReviewDto>(rating));
|
||||
if (rating.Id == 0)
|
||||
{
|
||||
user.ChapterRatings.Add(rating);
|
||||
}
|
||||
return _mapper.Map<UserReviewDto>(rating);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -76,7 +109,14 @@ public class ReviewController : BaseApiController
|
|||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Ratings);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
user.Ratings = user.Ratings.Where(r => !(r.SeriesId == seriesId && r.ChapterId == chapterId)).ToList();
|
||||
if (chapterId != null)
|
||||
{
|
||||
user.ChapterRatings = user.ChapterRatings.Where(r => r.ChapterId != chapterId).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Ratings = user.Ratings.Where(r => r.SeriesId != seriesId).ToList();
|
||||
}
|
||||
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using API.SignalR;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ public class UserReviewDto
|
|||
/// The series this is for
|
||||
/// </summary>
|
||||
public int SeriesId { get; set; }
|
||||
public int? VolumeId { get; set; }
|
||||
public int? ChapterId { get; set; }
|
||||
/// <summary>
|
||||
/// The library this series belongs in
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
public DbSet<EmailHistory> EmailHistory { get; set; } = null!;
|
||||
public DbSet<MetadataSettings> MetadataSettings { get; set; } = null!;
|
||||
public DbSet<MetadataFieldMapping> MetadataFieldMapping { get; set; } = null!;
|
||||
public DbSet<AppUserChapterRating> AppUserChapterRating { get; set; } = null!;
|
||||
public DbSet<ExternalChapterReview> ExternalChapterReview { get; set; } = null!;
|
||||
public DbSet<ExternalChapterMetadata> ExternalChapterMetadata { get; set; } = null!;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||
namespace API.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250428141809_ChapterRating")]
|
||||
[Migration("20250428151027_ChapterRating")]
|
||||
partial class ChapterRating
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -198,6 +198,41 @@ namespace API.Data.Migrations
|
|||
b.ToTable("AppUserBookmark");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserChapterRating", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChapterId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasBeenRated")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("Rating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("Review")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ChapterId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("AppUserChapterRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserCollection", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -553,9 +588,6 @@ namespace API.Data.Migrations
|
|||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ChapterId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasBeenRated")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
|
@ -575,8 +607,6 @@ namespace API.Data.Migrations
|
|||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ChapterId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("AppUserRating");
|
||||
|
|
@ -2705,6 +2735,33 @@ namespace API.Data.Migrations
|
|||
b.Navigation("AppUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserChapterRating", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany("ChapterRatings")
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany()
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("Chapter");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserCollection", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
|
|
@ -2811,10 +2868,6 @@ namespace API.Data.Migrations
|
|||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("ChapterId");
|
||||
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("SeriesId")
|
||||
|
|
@ -2823,8 +2876,6 @@ namespace API.Data.Migrations
|
|||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("Chapter");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
|
|
@ -3449,6 +3500,8 @@ namespace API.Data.Migrations
|
|||
{
|
||||
b.Navigation("Bookmarks");
|
||||
|
||||
b.Navigation("ChapterRatings");
|
||||
|
||||
b.Navigation("Collections");
|
||||
|
||||
b.Navigation("DashboardStreams");
|
||||
|
|
@ -10,11 +10,41 @@ namespace API.Data.Migrations
|
|||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ChapterId",
|
||||
table: "AppUserRating",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AppUserChapterRating",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Rating = table.Column<float>(type: "REAL", nullable: false),
|
||||
HasBeenRated = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
Review = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ChapterId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
AppUserId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AppUserChapterRating", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserChapterRating_AspNetUsers_AppUserId",
|
||||
column: x => x.AppUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserChapterRating_Chapter_ChapterId",
|
||||
column: x => x.ChapterId,
|
||||
principalTable: "Chapter",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserChapterRating_Series_SeriesId",
|
||||
column: x => x.SeriesId,
|
||||
principalTable: "Series",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalChapterMetadata",
|
||||
|
|
@ -84,10 +114,20 @@ namespace API.Data.Migrations
|
|||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserRating_ChapterId",
|
||||
table: "AppUserRating",
|
||||
name: "IX_AppUserChapterRating_AppUserId",
|
||||
table: "AppUserChapterRating",
|
||||
column: "AppUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserChapterRating_ChapterId",
|
||||
table: "AppUserChapterRating",
|
||||
column: "ChapterId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserChapterRating_SeriesId",
|
||||
table: "AppUserChapterRating",
|
||||
column: "SeriesId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExternalChapterMetadata_ChapterId",
|
||||
table: "ExternalChapterMetadata",
|
||||
|
|
@ -98,21 +138,13 @@ namespace API.Data.Migrations
|
|||
name: "IX_ExternalChapterMetadataExternalChapterReview_ExternalReviewsId",
|
||||
table: "ExternalChapterMetadataExternalChapterReview",
|
||||
column: "ExternalReviewsId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_AppUserRating_Chapter_ChapterId",
|
||||
table: "AppUserRating",
|
||||
column: "ChapterId",
|
||||
principalTable: "Chapter",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_AppUserRating_Chapter_ChapterId",
|
||||
table: "AppUserRating");
|
||||
migrationBuilder.DropTable(
|
||||
name: "AppUserChapterRating");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalChapterMetadataExternalChapterReview");
|
||||
|
|
@ -122,14 +154,6 @@ namespace API.Data.Migrations
|
|||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalChapterReview");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_AppUserRating_ChapterId",
|
||||
table: "AppUserRating");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ChapterId",
|
||||
table: "AppUserRating");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,6 +195,41 @@ namespace API.Data.Migrations
|
|||
b.ToTable("AppUserBookmark");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserChapterRating", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChapterId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasBeenRated")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("Rating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("Review")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ChapterId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("AppUserChapterRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserCollection", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -550,9 +585,6 @@ namespace API.Data.Migrations
|
|||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ChapterId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HasBeenRated")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
|
@ -572,8 +604,6 @@ namespace API.Data.Migrations
|
|||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ChapterId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("AppUserRating");
|
||||
|
|
@ -2702,6 +2732,33 @@ namespace API.Data.Migrations
|
|||
b.Navigation("AppUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserChapterRating", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany("ChapterRatings")
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany()
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("Chapter");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserCollection", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
|
|
@ -2808,10 +2865,6 @@ namespace API.Data.Migrations
|
|||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("ChapterId");
|
||||
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("SeriesId")
|
||||
|
|
@ -2820,8 +2873,6 @@ namespace API.Data.Migrations
|
|||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("Chapter");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
|
|
@ -3446,6 +3497,8 @@ namespace API.Data.Migrations
|
|||
{
|
||||
b.Navigation("Bookmarks");
|
||||
|
||||
b.Navigation("ChapterRatings");
|
||||
|
||||
b.Navigation("Collections");
|
||||
|
||||
b.Navigation("DashboardStreams");
|
||||
|
|
|
|||
|
|
@ -315,14 +315,14 @@ public class ChapterRepository : IChapterRepository
|
|||
public async Task<int> GetAverageUserRating(int chapterId, int userId)
|
||||
{
|
||||
// If there is 0 or 1 rating and that rating is you, return 0 back
|
||||
var countOfRatingsThatAreUser = await _context.AppUserRating
|
||||
var countOfRatingsThatAreUser = await _context.AppUserChapterRating
|
||||
.Where(r => r.ChapterId == chapterId && r.HasBeenRated)
|
||||
.CountAsync(u => u.AppUserId == userId);
|
||||
if (countOfRatingsThatAreUser == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var avg = (await _context.AppUserRating
|
||||
var avg = (await _context.AppUserChapterRating
|
||||
.Where(r => r.ChapterId == chapterId && r.HasBeenRated)
|
||||
.AverageAsync(r => (int?) r.Rating));
|
||||
return avg.HasValue ? (int) (avg.Value * 20) : 0;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#nullable enable
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.Metadata;
|
||||
|
|
|
|||
|
|
@ -758,7 +758,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
foreach (var s in series)
|
||||
{
|
||||
s.PagesRead = userProgress.Where(p => p.SeriesId == s.Id).Sum(p => p.PagesRead);
|
||||
var rating = userRatings.SingleOrDefault(r => r.SeriesId == s.Id && r.ChapterId == null);
|
||||
var rating = userRatings.SingleOrDefault(r => r.SeriesId == s.Id);
|
||||
if (rating != null)
|
||||
{
|
||||
s.UserRating = rating.Rating;
|
||||
|
|
@ -2177,14 +2177,14 @@ public class SeriesRepository : ISeriesRepository
|
|||
{
|
||||
// If there is 0 or 1 rating and that rating is you, return 0 back
|
||||
var countOfRatingsThatAreUser = await _context.AppUserRating
|
||||
.Where(r => r.SeriesId == seriesId && r.HasBeenRated && r.ChapterId == null)
|
||||
.Where(r => r.SeriesId == seriesId && r.HasBeenRated)
|
||||
.CountAsync(u => u.AppUserId == userId);
|
||||
if (countOfRatingsThatAreUser == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var avg = (await _context.AppUserRating
|
||||
.Where(r => r.SeriesId == seriesId && r.HasBeenRated && r.ChapterId == null)
|
||||
.Where(r => r.SeriesId == seriesId && r.HasBeenRated)
|
||||
.AverageAsync(r => (int?) r.Rating));
|
||||
return avg.HasValue ? (int) (avg.Value * 20) : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public enum AppUserIncludes
|
|||
SideNavStreams = 4096,
|
||||
ExternalSources = 8192,
|
||||
Collections = 16384, // 2^14
|
||||
ChapterRatings = 1 << 15,
|
||||
}
|
||||
|
||||
public interface IUserRepository
|
||||
|
|
@ -64,7 +65,8 @@ public interface IUserRepository
|
|||
Task<IEnumerable<AppUser>> GetAdminUsersAsync();
|
||||
Task<bool> IsUserAdminAsync(AppUser? user);
|
||||
Task<IList<string>> GetRoles(int userId);
|
||||
Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId, int? chapterId = null);
|
||||
Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId);
|
||||
Task<AppUserChapterRating?> GetUserChapterRatingAsync(int userId, int chapterId);
|
||||
Task<IList<UserReviewDto>> GetUserRatingDtosForSeriesAsync(int seriesId, int userId);
|
||||
Task<IList<UserReviewDto>> GetUserRatingDtosForChapterAsync(int chapterId, int userId);
|
||||
Task<AppUserPreferences?> GetPreferencesAsync(string username);
|
||||
|
|
@ -584,10 +586,17 @@ public class UserRepository : IUserRepository
|
|||
return await _userManager.GetRolesAsync(user);
|
||||
}
|
||||
|
||||
public async Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId, int? chapterId = null)
|
||||
public async Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId)
|
||||
{
|
||||
return await _context.AppUserRating
|
||||
.Where(r => r.SeriesId == seriesId && r.AppUserId == userId && r.ChapterId == chapterId)
|
||||
.Where(r => r.SeriesId == seriesId && r.AppUserId == userId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserChapterRating?> GetUserChapterRatingAsync(int userId, int chapterId)
|
||||
{
|
||||
return await _context.AppUserChapterRating
|
||||
.Where(r => r.AppUserId == userId && r.ChapterId == chapterId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
|
@ -595,7 +604,7 @@ public class UserRepository : IUserRepository
|
|||
{
|
||||
return await _context.AppUserRating
|
||||
.Include(r => r.AppUser)
|
||||
.Where(r => r.SeriesId == seriesId && r.ChapterId == null)
|
||||
.Where(r => r.SeriesId == seriesId)
|
||||
.Where(r => r.AppUser.UserPreferences.ShareReviews || r.AppUserId == userId)
|
||||
.OrderBy(r => r.AppUserId == userId)
|
||||
.ThenBy(r => r.Rating)
|
||||
|
|
@ -606,7 +615,7 @@ public class UserRepository : IUserRepository
|
|||
|
||||
public async Task<IList<UserReviewDto>> GetUserRatingDtosForChapterAsync(int chapterId, int userId)
|
||||
{
|
||||
return await _context.AppUserRating
|
||||
return await _context.AppUserChapterRating
|
||||
.Include(r => r.AppUser)
|
||||
.Where(r => r.ChapterId == chapterId)
|
||||
.Where(r => r.AppUser.UserPreferences.ShareReviews || r.AppUserId == userId)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public class AppUser : IdentityUser<int>, IHasConcurrencyToken
|
|||
public ICollection<AppUserRole> UserRoles { get; set; } = null!;
|
||||
public ICollection<AppUserProgress> Progresses { get; set; } = null!;
|
||||
public ICollection<AppUserRating> Ratings { get; set; } = null!;
|
||||
public ICollection<AppUserChapterRating> ChapterRatings { get; set; } = null!;
|
||||
public AppUserPreferences UserPreferences { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Bookmarks associated with this User
|
||||
|
|
|
|||
30
API/Entities/AppUserChapterRating.cs
Normal file
30
API/Entities/AppUserChapterRating.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
namespace API.Entities;
|
||||
|
||||
public class AppUserChapterRating
|
||||
{
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// A number between 0-5.0 that represents how good a series is.
|
||||
/// </summary>
|
||||
public float Rating { get; set; }
|
||||
/// <summary>
|
||||
/// If the rating has been explicitly set. Otherwise, the 0.0 rating should be ignored as it's not rated
|
||||
/// </summary>
|
||||
public bool HasBeenRated { get; set; }
|
||||
/// <summary>
|
||||
/// A short summary the user can write when giving their review.
|
||||
/// </summary>
|
||||
public string? Review { get; set; }
|
||||
/// <summary>
|
||||
/// An optional tagline for the review
|
||||
/// </summary>
|
||||
public int SeriesId { get; set; }
|
||||
public Series Series { get; set; } = null!;
|
||||
|
||||
public int ChapterId { get; set; }
|
||||
public Chapter Chapter { get; set; } = null!;
|
||||
|
||||
// Relationships
|
||||
public int AppUserId { get; set; }
|
||||
public AppUser AppUser { get; set; } = null!;
|
||||
}
|
||||
|
|
@ -26,10 +26,6 @@ public class AppUserRating
|
|||
public int SeriesId { get; set; }
|
||||
public Series Series { get; set; } = null!;
|
||||
|
||||
public int? ChapterId { get; set; } = null;
|
||||
public Chapter? Chapter { get; set; } = null;
|
||||
|
||||
|
||||
// Relationships
|
||||
public int AppUserId { get; set; }
|
||||
public AppUser AppUser { get; set; } = null!;
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
|
|||
/// </summary>
|
||||
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
|
||||
public ICollection<Tag> Tags { get; set; } = new List<Tag>();
|
||||
public ICollection<AppUserRating> Ratings { get; set; } = [];
|
||||
public ICollection<AppUserChapterRating> Ratings { get; set; } = [];
|
||||
|
||||
public ICollection<AppUserProgress> UserProgress { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -254,6 +254,11 @@ public static class IncludesExtensions
|
|||
.ThenInclude(c => c.Items);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.ChapterRatings))
|
||||
{
|
||||
query = query.Include(u => u.ChapterRatings);
|
||||
}
|
||||
|
||||
return query.AsSplitQuery();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,16 @@ public class AutoMapperProfiles : Profile
|
|||
.ForMember(dest => dest.Username,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.AppUser.UserName));
|
||||
CreateMap<AppUserChapterRating, UserReviewDto>()
|
||||
.ForMember(dest => dest.LibraryId,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.Series.LibraryId))
|
||||
.ForMember(dest => dest.Body,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.Review))
|
||||
.ForMember(dest => dest.Username,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.AppUser.UserName));
|
||||
|
||||
CreateMap<AppUserProgress, ProgressDto>()
|
||||
.ForMember(dest => dest.PageNum,
|
||||
|
|
|
|||
40
API/Helpers/Builders/AppUserChapterRatingBuilder.cs
Normal file
40
API/Helpers/Builders/AppUserChapterRatingBuilder.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Helpers.Builders;
|
||||
|
||||
public class ChapterRatingBuilder : IEntityBuilder<AppUserChapterRating>
|
||||
{
|
||||
private readonly AppUserChapterRating _rating;
|
||||
public AppUserChapterRating Build() => _rating;
|
||||
|
||||
public ChapterRatingBuilder(AppUserChapterRating? rating = null)
|
||||
{
|
||||
_rating = rating ?? new AppUserChapterRating();
|
||||
}
|
||||
|
||||
public ChapterRatingBuilder WithSeriesId(int seriesId)
|
||||
{
|
||||
_rating.SeriesId = seriesId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChapterRatingBuilder WithChapterId(int chapterId)
|
||||
{
|
||||
_rating.ChapterId = chapterId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChapterRatingBuilder WithRating(int rating)
|
||||
{
|
||||
_rating.Rating = Math.Clamp(rating, 0, 5);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChapterRatingBuilder WithBody(string body)
|
||||
{
|
||||
_rating.Review = body;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,12 +20,6 @@ public class RatingBuilder : IEntityBuilder<AppUserRating>
|
|||
return this;
|
||||
}
|
||||
|
||||
public RatingBuilder WithChapterId(int? chapterId)
|
||||
{
|
||||
_rating.ChapterId = chapterId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RatingBuilder WithRating(int rating)
|
||||
{
|
||||
_rating.Rating = Math.Clamp(rating, 0, 5);
|
||||
|
|
|
|||
|
|
@ -37,9 +37,19 @@ public class RatingService: IRatingService
|
|||
}
|
||||
|
||||
public async Task<bool> UpdateRating(AppUser user, UpdateRatingDto updateRatingDto)
|
||||
{
|
||||
if (updateRatingDto.ChapterId != null)
|
||||
{
|
||||
return await UpdateChapterRating(user, updateRatingDto);
|
||||
}
|
||||
|
||||
return await UpdateSeriesRating(user, updateRatingDto);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateSeriesRating(AppUser user, UpdateRatingDto updateRatingDto)
|
||||
{
|
||||
var userRating =
|
||||
await _unitOfWork.UserRepository.GetUserRatingAsync(updateRatingDto.SeriesId, user.Id, updateRatingDto.ChapterId) ??
|
||||
await _unitOfWork.UserRepository.GetUserRatingAsync(updateRatingDto.SeriesId, user.Id) ??
|
||||
new AppUserRating();
|
||||
|
||||
try
|
||||
|
|
@ -47,7 +57,6 @@ public class RatingService: IRatingService
|
|||
userRating.Rating = Math.Clamp(updateRatingDto.UserRating, 0f, 5f);
|
||||
userRating.HasBeenRated = true;
|
||||
userRating.SeriesId = updateRatingDto.SeriesId;
|
||||
userRating.ChapterId = updateRatingDto.ChapterId;
|
||||
|
||||
if (userRating.Id == 0)
|
||||
{
|
||||
|
|
@ -75,4 +84,45 @@ public class RatingService: IRatingService
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateChapterRating(AppUser user, UpdateRatingDto updateRatingDto)
|
||||
{
|
||||
if (updateRatingDto.ChapterId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var userRating =
|
||||
await _unitOfWork.UserRepository.GetUserChapterRatingAsync(user.Id, updateRatingDto.ChapterId.Value) ??
|
||||
new AppUserChapterRating();
|
||||
|
||||
try
|
||||
{
|
||||
userRating.Rating = Math.Clamp(updateRatingDto.UserRating, 0f, 5f);
|
||||
userRating.HasBeenRated = true;
|
||||
userRating.SeriesId = updateRatingDto.SeriesId;
|
||||
userRating.ChapterId = updateRatingDto.ChapterId.Value;
|
||||
|
||||
if (userRating.Id == 0)
|
||||
{
|
||||
user.ChapterRatings ??= new List<AppUserChapterRating>();
|
||||
user.ChapterRatings.Add(userRating);
|
||||
}
|
||||
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an exception saving rating");
|
||||
}
|
||||
|
||||
await _unitOfWork.RollbackAsync();
|
||||
user.ChapterRatings?.Remove(userRating);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue