diff --git a/API.Tests/Services/WordCountAnalysisTests.cs b/API.Tests/Services/WordCountAnalysisTests.cs new file mode 100644 index 000000000..845f729de --- /dev/null +++ b/API.Tests/Services/WordCountAnalysisTests.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions.TestingHelpers; +using System.Linq; +using System.Threading.Tasks; +using API.Entities; +using API.Entities.Enums; +using API.Helpers; +using API.Services; +using API.Services.Tasks.Metadata; +using API.SignalR; +using API.Tests.Helpers; +using Microsoft.Extensions.Logging; +using NSubstitute; +using Xunit; + +namespace API.Tests.Services; + +public class WordCountAnalysisTests : AbstractDbTest +{ + private readonly IReaderService _readerService; + private readonly string _testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService"); + private const long WordCount = 37417; + private const long MinHoursToRead = 1; + private const long AvgHoursToRead = 2; + private const long MaxHoursToRead = 4; + public WordCountAnalysisTests() : base() + { + _readerService = new ReaderService(_unitOfWork, Substitute.For>(), + Substitute.For()); + } + + protected override async Task ResetDb() + { + _context.Series.RemoveRange(_context.Series.ToList()); + + await _context.SaveChangesAsync(); + } + + [Fact] + public async Task ReadingTimeShouldBeNonZero() + { + await ResetDb(); + var series = EntityFactory.CreateSeries("Test Series"); + series.Format = MangaFormat.Epub; + var chapter = EntityFactory.CreateChapter("", false, new List() + { + EntityFactory.CreateMangaFile( + Path.Join(_testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub"), + MangaFormat.Epub, 0) + }); + + _context.Library.Add(new Library() + { + Name = "Test", + Type = LibraryType.Book, + Series = new List() {series} + }); + + series.Volumes = new List() + { + EntityFactory.CreateVolume("0", new List() {chapter}) + }; + + await _context.SaveChangesAsync(); + + + var cacheService = new CacheHelper(new FileService()); + var service = new WordCountAnalyzerService(Substitute.For>(), _unitOfWork, + Substitute.For(), cacheService, _readerService); + + + await service.ScanSeries(1, 1); + + Assert.Equal(WordCount, series.WordCount); + Assert.Equal(MinHoursToRead, series.MinHoursToRead); + Assert.Equal(AvgHoursToRead, series.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, series.MaxHoursToRead); + + // Validate the Chapter gets updated correctly + var volume = series.Volumes.First(); + Assert.Equal(WordCount, volume.WordCount); + Assert.Equal(MinHoursToRead, volume.MinHoursToRead); + Assert.Equal(AvgHoursToRead, volume.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, volume.MaxHoursToRead); + + Assert.Equal(WordCount, chapter.WordCount); + Assert.Equal(MinHoursToRead, chapter.MinHoursToRead); + Assert.Equal(AvgHoursToRead, chapter.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, chapter.MaxHoursToRead); + } + + + + [Fact] + public async Task ReadingTimeShouldIncreaseWhenNewBookAdded() + { + await ResetDb(); + var series = EntityFactory.CreateSeries("Test Series"); + series.Format = MangaFormat.Epub; + var chapter = EntityFactory.CreateChapter("", false, new List() + { + EntityFactory.CreateMangaFile( + Path.Join(_testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub"), + MangaFormat.Epub, 0) + }); + + _context.Library.Add(new Library() + { + Name = "Test", + Type = LibraryType.Book, + Series = new List() {series} + }); + + series.Volumes = new List() + { + EntityFactory.CreateVolume("0", new List() {chapter}) + }; + + await _context.SaveChangesAsync(); + + + var cacheService = new CacheHelper(new FileService()); + var service = new WordCountAnalyzerService(Substitute.For>(), _unitOfWork, + Substitute.For(), cacheService, _readerService); + + + await service.ScanSeries(1, 1); + + var chapter2 = EntityFactory.CreateChapter("2", false, new List() + { + EntityFactory.CreateMangaFile( + Path.Join(_testDirectory, "The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub"), + MangaFormat.Epub, 0) + }); + + series.Volumes.Add(EntityFactory.CreateVolume("1", new List() {chapter2})); + + series.Volumes.First().Chapters.Add(chapter2); + await _unitOfWork.CommitAsync(); + + await service.ScanSeries(1, 1); + + Assert.Equal(WordCount * 2L, series.WordCount); + Assert.Equal(MinHoursToRead * 2, series.MinHoursToRead); + Assert.Equal(AvgHoursToRead * 2, series.AvgHoursToRead); + Assert.Equal((MaxHoursToRead * 2) - 1, series.MaxHoursToRead); // This is just a rounding issue + + var firstVolume = series.Volumes.ElementAt(0); + Assert.Equal(WordCount, firstVolume.WordCount); + Assert.Equal(MinHoursToRead, firstVolume.MinHoursToRead); + Assert.Equal(AvgHoursToRead, firstVolume.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, firstVolume.MaxHoursToRead); + + var secondVolume = series.Volumes.ElementAt(1); + Assert.Equal(WordCount, secondVolume.WordCount); + Assert.Equal(MinHoursToRead, secondVolume.MinHoursToRead); + Assert.Equal(AvgHoursToRead, secondVolume.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, secondVolume.MaxHoursToRead); + + // Validate original chapter doesn't change + Assert.Equal(WordCount, chapter.WordCount); + Assert.Equal(MinHoursToRead, chapter.MinHoursToRead); + Assert.Equal(AvgHoursToRead, chapter.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, chapter.MaxHoursToRead); + + // Validate new chapter gets updated + Assert.Equal(WordCount, chapter2.WordCount); + Assert.Equal(MinHoursToRead, chapter2.MinHoursToRead); + Assert.Equal(AvgHoursToRead, chapter2.AvgHoursToRead); + Assert.Equal(MaxHoursToRead, chapter2.MaxHoursToRead); + } + + +} diff --git a/API/Controllers/DownloadController.cs b/API/Controllers/DownloadController.cs index a2fae1b9c..98fa072e9 100644 --- a/API/Controllers/DownloadController.cs +++ b/API/Controllers/DownloadController.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using API.Constants; using API.Data; using API.DTOs.Downloads; using API.Entities; @@ -12,7 +11,6 @@ using API.Services; using API.SignalR; using Kavita.Common; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; diff --git a/API/Services/Tasks/Metadata/WordCountAnalyzerService.cs b/API/Services/Tasks/Metadata/WordCountAnalyzerService.cs index ada691b21..65f4d427e 100644 --- a/API/Services/Tasks/Metadata/WordCountAnalyzerService.cs +++ b/API/Services/Tasks/Metadata/WordCountAnalyzerService.cs @@ -143,7 +143,7 @@ public class WordCountAnalyzerService : IWordCountAnalyzerService } - public async Task ProcessSeries(Series series, bool forceUpdate = false, bool useFileName = true) + private async Task ProcessSeries(Series series, bool forceUpdate = false, bool useFileName = true) { var isEpub = series.Format == MangaFormat.Epub; var existingWordCount = series.WordCount; diff --git a/UI/Web/src/app/cards/card-item/card-item.component.ts b/UI/Web/src/app/cards/card-item/card-item.component.ts index d77333a24..af301d955 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.ts +++ b/UI/Web/src/app/cards/card-item/card-item.component.ts @@ -213,7 +213,6 @@ export class CardItemComponent implements OnInit, OnDestroy { }); this.download$ = this.downloadService.activeDownloads$.pipe(takeUntil(this.onDestroy), map((events) => { - console.log('Active downloads: ', events); if(this.utilityService.isSeries(this.entity)) return events.find(e => e.entityType === 'series' && e.subTitle === this.downloadService.downloadSubtitle('series', (this.entity as Series))) || null; if(this.utilityService.isVolume(this.entity)) return events.find(e => e.entityType === 'volume' && e.subTitle === this.downloadService.downloadSubtitle('volume', (this.entity as Volume))) || null; if(this.utilityService.isChapter(this.entity)) return events.find(e => e.entityType === 'chapter' && e.subTitle === this.downloadService.downloadSubtitle('chapter', (this.entity as Chapter))) || null; diff --git a/UI/Web/src/app/cards/entity-title/entity-title.component.ts b/UI/Web/src/app/cards/entity-title/entity-title.component.ts index b4e768aac..e5ac891b7 100644 --- a/UI/Web/src/app/cards/entity-title/entity-title.component.ts +++ b/UI/Web/src/app/cards/entity-title/entity-title.component.ts @@ -36,10 +36,7 @@ export class EntityTitleComponent implements OnInit { return LibraryType; } - - - constructor(private utilityService: UtilityService, private readonly cdRef: ChangeDetectorRef) { - } + constructor(private utilityService: UtilityService, private readonly cdRef: ChangeDetectorRef) {} ngOnInit(): void { this.isChapter = this.utilityService.isChapter(this.entity); diff --git a/UI/Web/src/theme/_variables.scss b/UI/Web/src/theme/_variables.scss index 10628e73e..611bfac8f 100644 --- a/UI/Web/src/theme/_variables.scss +++ b/UI/Web/src/theme/_variables.scss @@ -19,8 +19,3 @@ $grid-breakpoints-lg: 992px; $grid-breakpoints-xl: 1200px; $grid-breakpoints: (xs: $grid-breakpoints-xs, sm: $grid-breakpoints-sm, md: $grid-breakpoints-md, lg: $grid-breakpoints-lg, xl: $grid-breakpoints-xl); - -// Override any bootstrap styles we don't want -// :root { -// --hr-color: transparent; -// } diff --git a/openapi.json b/openapi.json index 15d6a4b61..0a355a7e8 100644 --- a/openapi.json +++ b/openapi.json @@ -7,7 +7,7 @@ "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" }, - "version": "0.6.1.30" + "version": "0.6.1.31" }, "servers": [ {