using Microsoft.EntityFrameworkCore;
using RMuseum.DbContext;
using RMuseum.Models.Ganjoor;
using RMuseum.Models.Ganjoor.ViewModels;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Services;
using RSecurityBackend.Services.Implementation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RMuseum.Services.Implementation
{
///
/// numbering service implementation
///
public class GanjoorNumberingService : IGanjoorNumberingService
{
///
/// add numbering
///
///
///
public async Task> AddNumberingAsync(GanjoorNumbering numbering)
{
numbering.Name = numbering.Name == null ? numbering.Name : numbering.Name.Trim();
if (string.IsNullOrEmpty(numbering.Name))
return new RServiceResult(null, "ورود نام طرح شمارهگذاری اجباری است.");
var existing = await _context.GanjoorNumberings.Where(l => l.Name == numbering.Name && l.StartCatId == numbering.StartCatId).FirstOrDefaultAsync();
if (existing != null)
return new RServiceResult(null, "نام در این بخش تکراری است.");
try
{
_context.GanjoorNumberings.Add(numbering);
await _context.SaveChangesAsync();
Recount(numbering.Id); //start counting
return new RServiceResult(numbering);
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// update an existing numbering (only name)
///
///
///
public async Task> UpdateNumberingAsync(GanjoorNumbering updated)
{
try
{
var numbering = await _context.GanjoorNumberings.Where(l => l.Id == updated.Id).SingleOrDefaultAsync();
if (numbering == null)
return new RServiceResult(false, "اطلاعات طرح شماره گذاری یافت نشد.");
numbering.Name = updated.Name;
_context.Update(numbering);
await _context.SaveChangesAsync();
return new RServiceResult(true);
}
catch (Exception exp)
{
return new RServiceResult(false, exp.ToString());
}
}
///
/// delete numbering
///
///
///
public async Task> DeleteNumberingAsync(int id)
{
try
{
var numbering = await _context.GanjoorNumberings.Where(l => l.Id == id).SingleOrDefaultAsync();
if (numbering == null)
return new RServiceResult(false, "اطلاعات طرح شماره گذاری یافت نشد.");
_context.Remove(numbering);
await _context.SaveChangesAsync();
return new RServiceResult(true);
}
catch (Exception exp)
{
return new RServiceResult(false, exp.ToString());
}
}
///
/// get numbering by id
///
///
///
public async Task> GetNumberingAsync(int id)
{
try
{
return new RServiceResult(await _context.GanjoorNumberings.Where(l => l.Id == id).SingleOrDefaultAsync());
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// get all numberings
///
///
public async Task> GetNumberingsAsync()
{
try
{
return new RServiceResult(await _context.GanjoorNumberings.OrderBy(l => l.Id).ToArrayAsync());
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// get numberings for a cat
///
///
public async Task> GetNumberingsForCatAsync(int catId)
{
try
{
return new RServiceResult(await _context.GanjoorNumberings.Where(n => n.StartCatId == catId).OrderBy(l => l.Id).ToArrayAsync());
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// get numberings for direct subcats of parent cat
///
///
public async Task> GetNumberingsForDirectSubCatsAsync(int parentCatId)
{
try
{
var subcatIds = await _context.GanjoorCategories.AsNoTracking().Where(c => c.ParentId == parentCatId).Select(c => c.Id).ToListAsync();
return new RServiceResult(await _context.GanjoorNumberings.Where(n => subcatIds.Contains(n.StartCatId)).OrderBy(l => l.Id).ToArrayAsync());
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// get all numbering patterns for a couplet
///
///
///
///
public async Task> GetNumberingsForCouplet(int poemId, int coupletIndex)
{
return new RServiceResult
(
await _context.GanjoorVerseNumbers.Include(n => n.Numbering)
.Where(n => n.PoemId == poemId && n.CoupletIndex == coupletIndex)
.OrderBy(n => n.NumberingId)
.Select
(
n =>
new GanjoorCoupletNumberViewModel()
{
NumberingName = n.Numbering.Name,
IsPoemVerse = n.IsPoemVerse,
Number = n.Number,
SameTypeNumber = n.SameTypeNumber,
TotalLines = n.Numbering.TotalLines,
TotalCouplets = n.Numbering.TotalCouplets,
TotalParagraphs = n.Numbering.TotalParagraphs
}
).ToArrayAsync()
);
}
private async Task> _FindAllSubCategories(RMuseumDbContext context, int catId)
{
List cats = new List();
foreach (var cat in await context.GanjoorCategories.AsNoTracking().Where(c => c.ParentId == catId).OrderBy(c => c.Id).ToListAsync())
{
cats.Add(cat);
var subCats = await _FindAllSubCategories(context, cat.Id);
if (subCats.Count > 0)
{
cats.AddRange(subCats);
}
}
return cats;
}
private async Task> _FindCategoryRanges(RMuseumDbContext context, GanjoorCat startCat, int endCatId)
{
var parents = await context.GanjoorCategories.AsNoTracking().Where(c => c.ParentId == startCat.ParentId && c.Id >= startCat.Id && c.Id <= endCatId).OrderBy(c => c.Id).ToListAsync();
List cats = new List();
foreach (GanjoorCat parentCat in parents)
{
cats.Add(parentCat);
var subCats = await _FindAllSubCategories(context, parentCat.Id);
if (subCats.Count > 0)
{
cats.AddRange(subCats);
}
}
return cats;
}
private async Task> _FindTargetCategories(RMuseumDbContext context, GanjoorCat startCat, int? endCatId)
{
if (endCatId == null || startCat.Id == endCatId)
{
List cats = new List();
cats.Add(startCat);
cats.AddRange(await _FindAllSubCategories(context, startCat.Id));
return cats;
}
else
{
return await _FindCategoryRanges(context, startCat, (int)endCatId);
}
}
///
/// start counting
///
///
public RServiceResult Recount(int numberingId)
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(context);
var job = (await jobProgressServiceEF.NewJob("NumberingService::Count", "Query Cats")).Result;
try
{
var numbering = await context.GanjoorNumberings.Include(n => n.StartCat).Where(n => n.Id == numberingId).SingleOrDefaultAsync();
var cats = await _FindTargetCategories(context, numbering.StartCat, numbering.EndCatId);
await jobProgressServiceEF.UpdateJob(job.Id, 0, "Deleting Old Data");
var oldNumbers = await context.GanjoorVerseNumbers.Where(n => n.NumberingId == numberingId).ToListAsync();
context.GanjoorVerseNumbers.RemoveRange(oldNumbers);
await context.SaveChangesAsync();
await jobProgressServiceEF.UpdateJob(job.Id, 0, "Counting");
int number = 0;
int coupletnumber = 0;
int paragraphnumber = 0;
int totalVerseCount = 0;
foreach (var cat in cats)
{
var poems = await context.GanjoorPoems.AsNoTracking().Where(p => p.CatId == cat.Id).OrderBy(p => p.Id).ToListAsync();
await jobProgressServiceEF.UpdateJob(job.Id, 0, $"Counting:: {cat.Title}");
foreach (var poem in poems)
{
totalVerseCount += await context.GanjoorVerses.Where(v => v.PoemId == poem.Id).CountAsync();
var verses = await context.GanjoorVerses.AsNoTracking()
.Where(v => v.PoemId == poem.Id && v.VersePosition != VersePosition.Left && v.VersePosition != VersePosition.CenteredVerse2 && v.VersePosition != VersePosition.Comment)
.OrderBy(v => v.VOrder)
.ToListAsync();
for (int coupletIndex = 0; coupletIndex < verses.Count; coupletIndex++)
{
number++;
bool isPoemVerse = verses[coupletIndex].VersePosition == VersePosition.Right || verses[coupletIndex].VersePosition == VersePosition.CenteredVerse1;
if (isPoemVerse)
coupletnumber++;
else
paragraphnumber++;
GanjoorVerseNumber verseNumber = new GanjoorVerseNumber()
{
NumberingId = numberingId,
PoemId = poem.Id,
CoupletIndex = coupletIndex,
Number = number,
IsPoemVerse = isPoemVerse,
SameTypeNumber = isPoemVerse ? coupletnumber : paragraphnumber
};
context.GanjoorVerseNumbers.Add(verseNumber);
}
}
}
numbering.TotalLines = number;
numbering.TotalVerses = totalVerseCount;
numbering.TotalCouplets = coupletnumber;
numbering.TotalParagraphs = paragraphnumber;
numbering.LastCountingDate = DateTime.Now;
context.GanjoorNumberings.Update(numbering);
await context.SaveChangesAsync();
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
}
}
}
);
return new RServiceResult(true);
}
///
/// generate missing default numberings and start counting
///
///
public RServiceResult GenerateMissingDefaultNumberings()
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(context);
var job = (await jobProgressServiceEF.NewJob("GenerateMissingDefaultNumberings", "Query")).Result;
try
{
var cats = await context.GanjoorCategories.Where(c => c.ParentId != null).ToListAsync();
foreach (var cat in cats)
{
var numbering = await context.GanjoorNumberings.Where(n => n.StartCatId == cat.Id && n.Name == cat.Title).FirstOrDefaultAsync();
if(numbering == null)
{
await jobProgressServiceEF.UpdateJob(job.Id, 0, cat.FullUrl);
numbering = new GanjoorNumbering()
{
Name = cat.Title,
StartCatId = cat.Id,
EndCatId = cat.Id
};
context.GanjoorNumberings.Add(numbering);
await context.SaveChangesAsync();
Recount(numbering.Id); //start counting
}
}
await context.SaveChangesAsync();
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
}
}
}
);
return new RServiceResult(true);
}
///
/// Database Context
///
protected readonly RMuseumDbContext _context;
///
/// Background Task Queue Instance
///
protected readonly IBackgroundTaskQueue _backgroundTaskQueue;
///
/// constructor
///
///
///
public GanjoorNumberingService(RMuseumDbContext context, IBackgroundTaskQueue backgroundTaskQueue)
{
_context = context;
_backgroundTaskQueue = backgroundTaskQueue;
}
}
}