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:
Amelia 2025-04-28 17:11:39 +02:00
parent 052b3f9fe4
commit 184cf46533
No known key found for this signature in database
GPG key ID: D6D0ECE365407EAA
21 changed files with 389 additions and 87 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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!;

View file

@ -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");

View file

@ -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");
}
}
}

View file

@ -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");

View file

@ -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;

View file

@ -1,5 +1,4 @@
#nullable enable
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using API.Entities.Metadata;

View file

@ -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;
}

View file

@ -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)

View file

@ -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

View 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!;
}

View file

@ -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!;

View file

@ -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; }

View file

@ -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();
}

View file

@ -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,

View 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;
}
}

View file

@ -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);

View file

@ -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;
}
}