MyBookmark/ExcelTableImporter/LibraryLoader.cs

177 lines
5.8 KiB
C#

using System.Globalization;
using System.Text.RegularExpressions;
using ExcelTableImporter.Models.ExcelModels;
using ExcelTableImporter.Models.ExcelModels.ExcelModels;
using NPOI.SS.UserModel;
namespace ExcelTableImporter;
public class LibrayLoader
{
public async Task<AnimeLibrary> LoadLibrary(string filePath)
{
var animeRows = await ReadExcelFile(filePath);
return MakeLibrary(animeRows);
}
private async Task<List<AnimeRow>> ReadExcelFile(string filePath)
{
await using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
IWorkbook workbook = WorkbookFactory.Create(stream);
var sheet = workbook.GetSheetAt(0);
var animeRows = new List<AnimeRow>();
for (var i = 1; i <= sheet.LastRowNum; i++)
{
var row = sheet.GetRow(i);
var isEmpty = row.GetCell(0) is null
&& row.GetCell(1) is null
&& row.GetCell(2) is null
&& row.GetCell(3) is null
&& row.GetCell(4) is null
&& row.GetCell(8) is null
&& row.GetCell(9) is null;
if (isEmpty) continue;
var c1 = row.GetCell(0)?.ToString(); // russian name
var c2 = row.GetCell(1)?.ToString(); // original name
var c3 = TryShort(row.GetCell(2)); // release year
var c4 = ReadSerialsCount(row.GetCell(3)); // formula cell
var c5 = ReadSerialsCount(row.GetCell(4)); // series viewed
var c6 = row.GetCell(6)?.ToString(); // is completed
var c7 = TryShort(row.GetCell(8)); // rating
var c8 = row.GetCell(9)?.ToString(); // comment
// var str = $"{c1} | {c2} [{c3}] - {c4} - : — {c5} — [{c6}] [{c7}]; {c8}";
animeRows.Add(new AnimeRow
{
NameRussian = c1,
NameOriginal = c2,
ReleaseYear = c3,
AnimeSeriesCountItems = ParseFormula(c4),
AnimeSeriesViewsCountItems = ParseFormula(c5),
IsCompleted = c6 == "Да",
RatingValue = c7,
Comment = c8,
});
// Console.WriteLine(str);
}
return animeRows;
}
private AnimeLibrary MakeLibrary(List<AnimeRow> animeRows)
{
var animeTitles = new Dictionary<string, AnimeTitle>(StringComparer.OrdinalIgnoreCase);
foreach (var row in animeRows)
{
if (!animeTitles.TryGetValue(row.NameOriginal, out var title))
{
title = new AnimeTitle
{
NameRussian = row.NameRussian,
NameOriginal = row.NameOriginal,
ReleaseYear = row.ReleaseYear,
RatingValue = row.RatingValue,
Comment = row.Comment
};
animeTitles[row.NameOriginal] = title;
}
var part = new AnimePart
{
ReleaseYear = row.ReleaseYear,
Type = DetectPartType(row.NameRussian, row.NameOriginal),
SeasonNumber = DetectSeasonNumber(row.NameRussian)
};
foreach (var item in row.AnimeSeriesCountItems)
{
part.EpisodeGroups.Add(new AnimeEpisodeGroup
{
Order = item.Order,
Count = item.Count,
Viewed = row.AnimeSeriesViewsCountItems
.FirstOrDefault(v => v.Order == item.Order)?.Count ?? 0
});
}
title.Parts.Add(part);
}
return new AnimeLibrary
{
Titles = animeTitles.Values.ToList()
};
}
static short? TryShort(ICell? cell)
{
if (cell == null) return null;
if (cell.CellType == CellType.Numeric) return Convert.ToInt16(cell.NumericCellValue);
if (short.TryParse(cell.ToString(), out var v)) return v;
return null;
}
static string? ReadSerialsCount(ICell? cell)
{
if (cell == null) return null;
return cell.CellType switch
{
CellType.Numeric => cell.NumericCellValue.ToString(CultureInfo.InvariantCulture),
CellType.Formula => cell.CellFormula,
_ => null
};
}
static List<AnimeSeriesCountItem> ParseFormula(string? formula)
{
var result = new List<AnimeSeriesCountItem>();
if (string.IsNullOrWhiteSpace(formula)) return result;
formula = formula.TrimStart('=');
var parts = formula.Split('+', StringSplitOptions.RemoveEmptyEntries);
var order = 1;
foreach (var p in parts)
{
if (int.TryParse(p, out var count))
{
result.Add(new AnimeSeriesCountItem
{
Order = order++,
Count = count
});
}
}
return result;
}
static AnimePartType DetectPartType(string nameRu, string nameOrig)
{
var name = (nameRu + " " + nameOrig).ToLowerInvariant();
if (name.Contains("фильм") || name.Contains("movie") || name.Contains("gekijouban"))
return AnimePartType.Movie;
if (name.Contains("ova"))
return AnimePartType.Ova;
if (name.Contains("special") || name.Contains("спешл"))
return AnimePartType.Special;
return AnimePartType.Season;
}
static int? DetectSeasonNumber(string nameRu)
{
var match = Regex.Match(nameRu, @"сезон\s*(\d+)", RegexOptions.IgnoreCase);
if (match.Success && int.TryParse(match.Groups[1].Value, out var num))
return num;
return null;
}
}