divan/RMuseum/Services/Implementation/DivanService-Partials/DivanService-PoemSections.cs
Anas Rashid 532d005cdb Rename Ganjoor -> Divan throughout the code
Projects (GanjooRazor -> DivanRazor, GanjoorService.sln -> DivanService.sln), files,
folders, classes, namespaces, settings, API routes (/api/divan) and DB tables (Divan*).
External addresses (ganjoor.net, github.com/ganjoor) unchanged. Migrations renamed
consistently (no pending model changes); requires a fresh database.
Upstream RUNNING_LOCALLY/SEMANTIC_SEARCH docs archived unmodified under docs/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:49:01 +02:00

1113 lines
58 KiB
C#

using Microsoft.EntityFrameworkCore;
using RMuseum.DbContext;
using RMuseum.Models.Divan;
using RSecurityBackend.Models.Generic;
using RSecurityBackend.Services.Implementation;
using System;
using System.Data;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using RSecurityBackend.Models.Generic.Db;
namespace RMuseum.Services.Implementation
{
/// <summary>
/// IDivanService implementation
/// </summary>
public partial class DivanService : IDivanService
{
/// <summary>
/// filling poem formats
/// </summary>
/// <returns></returns>
public RServiceResult<bool> StartFillingSectionsPoemFormats()
{
try
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions<RMuseumDbContext>())) //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("StartFillingSectionsPoemFormats", "Query data")).Result;
int progress = 0;
try
{
var sections = await context.DivanPoemSections.Where(s => s.SectionType == PoemSectionType.WholePoem).ToListAsync();
int count = sections.Count;
int updatedSectionsCount = 0;
for (int i = 0; i < count; i++)
{
var mainSection = sections[i];
mainSection.PoemFormat = null;
var poem = await context.DivanPoems.AsNoTracking().Where(p => p.Id == mainSection.PoemId).SingleOrDefaultAsync();
if (poem == null)
continue;
if(poem.FullTitle.Contains("شاهنامه") || poem.FullTitle.Contains("مثنوی"))
{
mainSection.PoemFormat = DivanPoemFormat.Masnavi;
context.Update(mainSection);
await context.SaveChangesAsync();
updatedSectionsCount++;
continue;
}
var nonCommentVerses = await context.DivanVerses.AsNoTracking().Where(v => v.PoemId == poem.Id && v.VersePosition != VersePosition.Comment && v.SectionIndex1 == mainSection.Index).OrderBy(v => v.VOrder).ToListAsync();
if (!nonCommentVerses.Any())
continue;
bool multiBand = false;
if (!nonCommentVerses.Where(v => v.VersePosition == VersePosition.Paragraph || v.VersePosition == VersePosition.Single).Any())
{
if (nonCommentVerses.Where(v =>
v.VersePosition == VersePosition.CenteredVerse1
||
v.VersePosition == VersePosition.CenteredVerse2
).Any())
{
multiBand = true;
}
}
if (multiBand)
{
mainSection.PoemFormat = DivanPoemFormat.MultiBand;
if (poem.FullTitle.Contains("ترجیع"))
mainSection.PoemFormat = DivanPoemFormat.TarjeeBand;
if (poem.FullTitle.Contains("ترکیب"))
mainSection.PoemFormat = DivanPoemFormat.TarkibBand;
if (poem.FullTitle.Contains("مسمط"))
mainSection.PoemFormat = DivanPoemFormat.Mosammat;
if (poem.FullTitle.Contains("مخمس"))
mainSection.PoemFormat = DivanPoemFormat.Mosammat5;
if (poem.FullTitle.Contains("مسدس"))
mainSection.PoemFormat = DivanPoemFormat.Mosammat6;
if (poem.FullTitle.Contains("چهارپاره"))
mainSection.PoemFormat = DivanPoemFormat.ChaharPare;
}
else
{
if (!string.IsNullOrEmpty(mainSection.RhymeLetters))
{
mainSection.PoemFormat = DivanPoemFormat.Generic;
if (poem.FullTitle.Contains("رباعی"))
mainSection.PoemFormat = DivanPoemFormat.Robaee;
else
if (poem.FullTitle.Contains("غزل"))
mainSection.PoemFormat = DivanPoemFormat.Ghazal;
else
if (poem.FullTitle.Contains("قصاید") || poem.FullTitle.Contains("قصید"))
mainSection.PoemFormat = DivanPoemFormat.Ghaside;
else
if (poem.FullTitle.Contains("قطعات") || poem.FullTitle.Contains("قطعه"))
mainSection.PoemFormat = DivanPoemFormat.Ghete;
if(nonCommentVerses.Count == 4)
{
if (mainSection.DivanMetreId == 24) //id for robaee from database
mainSection.PoemFormat = DivanPoemFormat.Robaee;
else
if (mainSection.DivanMetreId == 12) //id for dobeyti from database
mainSection.PoemFormat = DivanPoemFormat.Dobeyti;
}
}
else
if (_IsMasnavi(nonCommentVerses))
{
mainSection.PoemFormat = DivanPoemFormat.Masnavi;
}
}
if(mainSection.PoemFormat != null)
{
updatedSectionsCount++;
context.Update(mainSection);
await context.SaveChangesAsync();
}
if ((i * 100 / count) > progress)
{
progress = i * 100 / count;
await jobProgressServiceEF.UpdateJob(job.Id, progress, $"{updatedSectionsCount} sections updated");
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, progress, "", false, exp.ToString());
}
}
});
return new RServiceResult<bool>(true);
}
catch (Exception exp)
{
return new RServiceResult<bool>(false, exp.ToString());
}
}
/// <summary>
/// sectionizing poems
/// </summary>
/// <returns></returns>
public RServiceResult<bool> StartFillingPoemSectionsCoupletIndex()
{
try
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions<RMuseumDbContext>())) //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("StartFillingPoemSectionsCoupletIndex", "Query data")).Result;
int progress = 0;
try
{
var sections = await context.DivanPoemSections.OrderBy(s => s.SectionType).ToListAsync();
int count = sections.Count;
for (int i = 0; i < count; i++)
{
var section = sections[i];
var firstSectionVerse = await context.DivanVerses.AsNoTracking()
.Where
(v =>
v.PoemId == section.PoemId
&&
(
(section.VerseType == VersePoemSectionType.Second && v.SectionIndex2 == section.Index)
||
(section.VerseType == VersePoemSectionType.Third && v.SectionIndex3 == section.Index)
||
(section.VerseType == VersePoemSectionType.Forth && v.SectionIndex4 == section.Index)
)
)
.OrderBy(v => v.VOrder)
.FirstOrDefaultAsync();
if (firstSectionVerse != null && firstSectionVerse.CoupletIndex != null)
{
section.CachedFirstCoupletIndex = (int)firstSectionVerse.CoupletIndex;
context.Update(section);
}
if ((i * 100 / count) > progress)
{
progress = i * 100 / count;
await jobProgressServiceEF.UpdateJob(job.Id, progress);
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, progress, "", false, exp.ToString());
}
}
});
return new RServiceResult<bool>(true);
}
catch (Exception exp)
{
return new RServiceResult<bool>(false, exp.ToString());
}
}
private bool _IsMasnavi(List<DivanVerse> verses, bool bandCouplet = false)
{
if (verses.Count % 2 != 0)
return false;
if (bandCouplet)
{
if (verses.Any(v => v.VersePosition != VersePosition.CenteredVerse1 && v.VersePosition != VersePosition.CenteredVerse2))
return false;
}
else
{
if (verses.Any(v => v.VersePosition != VersePosition.Right && v.VersePosition != VersePosition.Left))
return false;
}
int rhymingCouplets = 0;
for (int i = 0; i < verses.Count; i += 2)
{
var rightVerse = verses[i];
if (bandCouplet)
{
if (rightVerse.VersePosition != VersePosition.CenteredVerse1)
return false;
}
else
{
if (rightVerse.VersePosition != VersePosition.Right)
return false;
}
var leftVerse = verses[i + 1];
if (bandCouplet)
{
if (leftVerse.VersePosition != VersePosition.CenteredVerse2)
return false;
}
else
{
if (leftVerse.VersePosition != VersePosition.Left)
return false;
}
List<DivanVerse> coupletVerses = new List<DivanVerse>();
coupletVerses.Add(rightVerse);
coupletVerses.Add(leftVerse);
var res = LanguageUtils.FindRhyme(coupletVerses, false, bandCouplet);
if (!string.IsNullOrEmpty(res.Rhyme))
rhymingCouplets++;
if (rhymingCouplets * 200 / verses.Count > 50)
return true;
}
return false;
}
private async Task<bool> _SectionizePoem(RMuseumDbContext context, DivanPoem poem, LongRunningJobProgressServiceEF jobProgressServiceEF, RLongRunningJobStatus job, DivanCat cat)
{
if (true == await context.DivanPoemSections.AsNoTracking().Where(s => s.PoemId == poem.Id).AnyAsync())
{
return true;
}
var nonCommentVerses = await context.DivanVerses.Where(v => v.PoemId == poem.Id && v.VersePosition != VersePosition.Comment).OrderBy(v => v.VOrder).ToListAsync();
if (!nonCommentVerses.Where(v => v.VersePosition == VersePosition.Paragraph || v.VersePosition == VersePosition.Single).Any())
{
if (nonCommentVerses.Where(v =>
v.VersePosition == VersePosition.CenteredVerse1
||
v.VersePosition == VersePosition.CenteredVerse2
).Any())
{
//multi-band poem
_SectionizeMultibandVerses(context, nonCommentVerses, poem, 0, cat);
}
else
if (!nonCommentVerses.Where(v => v.VersePosition != VersePosition.Right && v.VersePosition != VersePosition.Left).Any())
{
_SectionizeNormalVerses(context, nonCommentVerses, poem, 0, cat);
}
}
else
{
int sectionIndex = 0;
int vIndex = 0;
List<DivanVerse> singleVerses = new List<DivanVerse>();
List<DivanVerse> normalVerses = new List<DivanVerse>();
while (vIndex < nonCommentVerses.Count)
{
if (nonCommentVerses[vIndex].VersePosition == VersePosition.Single)
{
singleVerses.Add(nonCommentVerses[vIndex]);
vIndex++;
continue;
}
if (singleVerses.Count > 0)
{
DivanPoemSection mainSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.WholePoem,
VerseType = VersePoemSectionType.First,
Index = sectionIndex,
Number = sectionIndex + 1,
DivanMetreId = poem.DivanMetreId,
RhymeLetters = poem.RhymeLetters,
HtmlText = PrepareHtmlText(singleVerses),
PlainText = PreparePlainText(singleVerses),
CoupletsCount = singleVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count(),
PoemFormat = DivanPoemFormat.New,
};
context.Add(mainSection);//having a main section for مثنوی inside normal text helps keep track of related versess
foreach (var verse in singleVerses)
{
verse.SectionIndex1 = mainSection.Index;
verse.SectionIndex2 = null;//clear previous indices
verse.SectionIndex3 = null;//clear previous indices
verse.SectionIndex4 = null;//clear previous indices
}
context.UpdateRange(singleVerses);
sectionIndex++;
singleVerses = new List<DivanVerse>();
}
if (nonCommentVerses[vIndex].VersePosition == VersePosition.Left || nonCommentVerses[vIndex].VersePosition == VersePosition.Right
||
nonCommentVerses[vIndex].VersePosition == VersePosition.CenteredVerse1 || nonCommentVerses[vIndex].VersePosition == VersePosition.CenteredVerse2
)
{
normalVerses.Add(nonCommentVerses[vIndex]);
vIndex++;
continue;
}
if (normalVerses.Count > 0)
{
if (normalVerses.Where(v =>
v.VersePosition == VersePosition.CenteredVerse1
||
v.VersePosition == VersePosition.CenteredVerse2
).Any())
{
//multi-band poem
_SectionizeMultibandVerses(context, normalVerses, poem, sectionIndex, cat);
}
else
{
_SectionizeNormalVerses(context, normalVerses, poem, sectionIndex, cat);
}
sectionIndex++;
normalVerses = new List<DivanVerse>();
}
if (nonCommentVerses[vIndex].VersePosition != VersePosition.Paragraph)
{
if (jobProgressServiceEF != null)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, $"Poem: {poem.Id}, nonCommentVerses[{vIndex}].VersePosition != VersePosition.Paragraph");
}
return false;
}
vIndex++;
}
if (singleVerses.Count > 0)
{
DivanPoemSection mainSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.WholePoem,
VerseType = VersePoemSectionType.First,
Index = sectionIndex,
Number = sectionIndex + 1,
DivanMetreId = poem.DivanMetreId,
RhymeLetters = poem.RhymeLetters,
HtmlText = PrepareHtmlText(singleVerses),
PlainText = PreparePlainText(singleVerses),
CoupletsCount = singleVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count(),
PoemFormat = DivanPoemFormat.New,
};
context.Add(mainSection);//having a main section for مثنوی inside normal text helps keep track of related versess
foreach (var verse in singleVerses)
{
verse.SectionIndex1 = mainSection.Index;
verse.SectionIndex2 = null;//clear previous indices
verse.SectionIndex3 = null;//clear previous indices
verse.SectionIndex4 = null;//clear previous indices
}
context.UpdateRange(singleVerses);
}
if (normalVerses.Count > 0)
{
if (normalVerses.Where(v =>
v.VersePosition == VersePosition.CenteredVerse1
||
v.VersePosition == VersePosition.CenteredVerse2
).Any())
{
//multi-band poem
_SectionizeMultibandVerses(context, normalVerses, poem, sectionIndex, cat);
}
else
{
_SectionizeNormalVerses(context, normalVerses, poem, sectionIndex, cat);
}
}
}
return true;
}
private void _SectionizeMultibandVerses(RMuseumDbContext context, List<DivanVerse> nonCommentVerses, DivanPoem poem, int index, DivanCat cat)
{
DivanPoemSection mainSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.WholePoem,
VerseType = VersePoemSectionType.First,
Index = index,
Number = index + 1,
DivanMetreId = poem.DivanMetreId,
RhymeLetters = poem.RhymeLetters,
HtmlText = PrepareHtmlText(nonCommentVerses),
PlainText = PreparePlainText(nonCommentVerses),
CoupletsCount = nonCommentVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count(),
PoemFormat = DivanPoemFormat.MultiBand,
};
if (poem.FullTitle.Contains("ترجیع"))
mainSection.PoemFormat = DivanPoemFormat.TarjeeBand;
if (poem.FullTitle.Contains("ترکیب"))
mainSection.PoemFormat = DivanPoemFormat.TarkibBand;
if (poem.FullTitle.Contains("مسمط"))
mainSection.PoemFormat = DivanPoemFormat.Mosammat;
if (poem.FullTitle.Contains("مخمس"))
mainSection.PoemFormat = DivanPoemFormat.Mosammat5;
if (poem.FullTitle.Contains("مسدس"))
mainSection.PoemFormat = DivanPoemFormat.Mosammat6;
if (poem.FullTitle.Contains("چهارپاره"))
mainSection.PoemFormat = DivanPoemFormat.ChaharPare;
index++;
List<DivanVerse> currentBandVerses = new List<DivanVerse>();
DivanPoemSection currentBandSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.Band,
VerseType = VersePoemSectionType.Second,
Index = index,
Number = index + 1,
DivanMetreId = poem.DivanMetreId,
DivanMetreRefSectionIndex = mainSection.Index,
};
List<DivanVerse> bandVerses = new List<DivanVerse>();
foreach (var verse in nonCommentVerses)
{
verse.SectionIndex1 = mainSection.Index;
verse.SectionIndex2 = null;//clear previous indices
verse.SectionIndex3 = null;//clear previous indices
verse.SectionIndex4 = null;//clear previous indices
if (verse.VersePosition == VersePosition.Right || verse.VersePosition == VersePosition.Left)
{
currentBandVerses.Add(verse);
}
if (verse.VersePosition == VersePosition.CenteredVerse1 || verse.VersePosition == VersePosition.CenteredVerse2)
{
bandVerses.Add(verse);
if (currentBandVerses.Count > 0)
{
foreach (var currentBandVerse in currentBandVerses)
{
currentBandVerse.SectionIndex2 = currentBandSection.Index;
}
currentBandSection.RhymeLetters = LanguageUtils.FindRhyme(currentBandVerses).Rhyme;
currentBandSection.HtmlText = PrepareHtmlText(currentBandVerses);
currentBandSection.CoupletsCount = currentBandVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count();
currentBandSection.PlainText = PreparePlainText(currentBandVerses);
context.Add(currentBandSection);
index++;
currentBandVerses = new List<DivanVerse>();
currentBandSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.Band,
VerseType = VersePoemSectionType.Second,
Index = index,
Number = index + 1,
DivanMetreId = poem.DivanMetreId,
DivanMetreRefSectionIndex = mainSection.Index,
};
}
}
}
if (currentBandVerses.Count > 0)
{
foreach (var currentBandVerse in currentBandVerses)
{
currentBandVerse.SectionIndex2 = currentBandSection.Index;
}
currentBandSection.RhymeLetters = LanguageUtils.FindRhyme(currentBandVerses).Rhyme;
currentBandSection.CoupletsCount = currentBandVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count();
currentBandSection.HtmlText = PrepareHtmlText(currentBandVerses);
currentBandSection.PlainText = PreparePlainText(currentBandVerses);
context.Add(currentBandSection);
}
index++;
DivanPoemSection bandSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.BandCouplets,
VerseType = VersePoemSectionType.Second,
Index = index,
Number = index + 1,
DivanMetreId = poem.DivanMetreId,
DivanMetreRefSectionIndex = mainSection.Index,
};
foreach (var bandVerse in bandVerses)
{
bandVerse.SectionIndex2 = bandSection.Index;
}
if (_IsMasnavi(bandVerses, true))
{
if (mainSection.PoemFormat == DivanPoemFormat.MultiBand)
{
mainSection.PoemFormat = DivanPoemFormat.TarkibBand;
}
for (int v = 0; v < bandVerses.Count; v += 2)
{
if (bandVerses[v].VersePosition != VersePosition.CenteredVerse1 || bandVerses[v + 1].VersePosition != VersePosition.CenteredVerse2)
continue;
index++;
var rightVerse = bandVerses[v];
var leftVerse = bandVerses[v + 1];
List<DivanVerse> coupletVerses = new List<DivanVerse>();
coupletVerses.Add(rightVerse);
coupletVerses.Add(leftVerse);
var res = LanguageUtils.FindRhyme(coupletVerses, false, true, true);
if (string.IsNullOrEmpty(res.Rhyme))
{
res = LanguageUtils.FindRhyme(coupletVerses, false, true, false);
}
DivanPoemSection verseSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.Couplet,
VerseType = VersePoemSectionType.Third,
Index = index,
Number = index + 1,
DivanMetreId = poem.DivanMetreId,
RhymeLetters = res.Rhyme,
DivanMetreRefSectionIndex = mainSection.Index,
};
rightVerse.SectionIndex3 = verseSection.Index;
leftVerse.SectionIndex3 = verseSection.Index;
context.Update(rightVerse);
context.Update(leftVerse);
var rl = new List<DivanVerse>(); rl.Add(rightVerse); rl.Add(leftVerse);
verseSection.HtmlText = PrepareHtmlText(rl);
verseSection.PlainText = PreparePlainText(rl);
verseSection.CoupletsCount = rl.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count();
context.Add(verseSection);
}
}
else
{
bandSection.RhymeLetters = LanguageUtils.FindRhyme(bandVerses).Rhyme;
}
bandSection.HtmlText = PrepareHtmlText(bandVerses);
bandSection.PlainText = PreparePlainText(bandVerses);
bandSection.CoupletsCount = bandVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count();
context.Add(mainSection);
context.Add(bandSection);
context.UpdateRange(nonCommentVerses);
}
private void _SectionizeNormalVerses(RMuseumDbContext context, List<DivanVerse> nonCommentVerses, DivanPoem poem, int index, DivanCat cat)
{
//normal poem
DivanPoemSection mainSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.WholePoem,
VerseType = VersePoemSectionType.First,
Index = index,
Number = index + 1,
DivanMetreId = poem.DivanMetreId,
RhymeLetters = poem.RhymeLetters,
HtmlText = PrepareHtmlText(nonCommentVerses),
PlainText = PreparePlainText(nonCommentVerses),
CoupletsCount = nonCommentVerses.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count(),
PoemFormat = DivanPoemFormat.Unknown,
};
//checking for مثنوی phase 1
if (string.IsNullOrEmpty(poem.RhymeLetters))
{
mainSection.RhymeLetters = LanguageUtils.FindRhyme(nonCommentVerses).Rhyme;
mainSection.PoemFormat = DivanPoemFormat.Generic;
if (poem.FullTitle.Contains("رباعی"))
mainSection.PoemFormat = DivanPoemFormat.Robaee;
if (poem.FullTitle.Contains("غزل"))
mainSection.PoemFormat = DivanPoemFormat.Ghazal;
if (poem.FullTitle.Contains("قص"))
mainSection.PoemFormat = DivanPoemFormat.Ghaside;
if (poem.FullTitle.Contains("قطع"))
mainSection.PoemFormat = DivanPoemFormat.Ghete;
}
context.Add(mainSection);//having a main section for مثنوی inside normal text helps keep track of related versess
foreach (var verse in nonCommentVerses)
{
verse.SectionIndex1 = mainSection.Index;
verse.SectionIndex2 = null;//clear previous indices
verse.SectionIndex3 = null;//clear previous indices
verse.SectionIndex4 = null;//clear previous indices
}
index++;
//checking for مثنوی phase 2
if (nonCommentVerses.Count > 2 && string.IsNullOrEmpty(mainSection.RhymeLetters))
{
if (_IsMasnavi(nonCommentVerses))
{
mainSection.PoemFormat = DivanPoemFormat.Masnavi;
for (int v = 0; v < nonCommentVerses.Count; v += 2)
{
index++;
var rightVerse = nonCommentVerses[v];
var leftVerse = nonCommentVerses[v + 1];
List<DivanVerse> coupletVerses = new List<DivanVerse>();
coupletVerses.Add(rightVerse);
coupletVerses.Add(leftVerse);
var res = LanguageUtils.FindRhyme(coupletVerses);
DivanPoemSection verseSection = new DivanPoemSection()
{
PoemId = poem.Id,
PoetId = cat.PoetId,
SectionType = PoemSectionType.Couplet,
VerseType = VersePoemSectionType.Second,
Index = index,
Number = index,//couplet number
DivanMetreId = poem.DivanMetreId,
RhymeLetters = res.Rhyme,
DivanMetreRefSectionIndex = mainSection.Index,
};
rightVerse.SectionIndex2 = verseSection.Index;
leftVerse.SectionIndex2 = verseSection.Index;
var rl = new List<DivanVerse>(); rl.Add(rightVerse); rl.Add(leftVerse);
verseSection.HtmlText = PrepareHtmlText(rl);
verseSection.PlainText = PreparePlainText(rl);
verseSection.CoupletsCount = rl.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count();
context.Add(verseSection);
}
}
}
context.UpdateRange(nonCommentVerses);
}
/// <summary>
/// filter secion verses
/// </summary>
/// <param name="section"></param>
/// <param name="verses"></param>
/// <returns></returns>
public List<DivanVerse> FilterSectionVerses(DivanPoemSection section, List<DivanVerse> verses)
{
List<DivanVerse> sectionVerses = new List<DivanVerse>();
foreach (DivanVerse verse in verses)
{
switch (section.VerseType)
{
case VersePoemSectionType.First:
if (verse.SectionIndex1 == section.Index)
sectionVerses.Add(verse);
break;
case VersePoemSectionType.Second:
if (verse.SectionIndex2 == section.Index)
sectionVerses.Add(verse);
break;
case VersePoemSectionType.Third:
if (verse.SectionIndex3 == section.Index)
sectionVerses.Add(verse);
break;
default:
if (verse.SectionIndex4 == section.Index)
sectionVerses.Add(verse);
break;
}
}
return sectionVerses;
}
/// <summary>
/// get couplet sections
/// </summary>
/// <param name="poemId"></param>
/// <param name="coupletIndex"></param>
/// <returns></returns>
public async Task<RServiceResult<DivanPoemSection[]>> GetCoupletSectionsAsync(int poemId, int coupletIndex)
{
var firstVerse = await _context.DivanVerses.AsNoTracking().Where(v => v.PoemId == poemId && v.CoupletIndex == coupletIndex).OrderBy(v => v.VOrder).FirstOrDefaultAsync();
if (firstVerse == null)
return new RServiceResult<DivanPoemSection[]>(null);//not found
try
{
return new RServiceResult<DivanPoemSection[]>
(
await _context.DivanPoemSections.AsNoTracking().Include(s => s.DivanMetre)
.Where(s =>
s.PoemId == poemId
&&
(
s.Index == firstVerse.SectionIndex1
||
s.Index == firstVerse.SectionIndex2
||
s.Index == firstVerse.SectionIndex3
||
s.Index == firstVerse.SectionIndex4
)
)
.OrderBy(s => s.SectionType).ThenBy(s => s.VerseType)
.ToArrayAsync()
);
}
catch (Exception exp)
{
return new RServiceResult<DivanPoemSection[]>(null, exp.ToString());
}
}
/// <summary>
/// get all poem sections
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<RServiceResult<DivanPoemSection[]>> GetPoemSectionsAsync(int id)
{
var sections =
await _context.DivanPoemSections.AsNoTracking().Include(s => s.DivanMetre).Where(s => s.PoemId == id).OrderBy(s => s.SectionType).ThenBy(s => s.VerseType).ToArrayAsync();
return new RServiceResult<DivanPoemSection[]>(sections);
}
/// <summary>
/// regenerate poem sections (dangerous: wipes out existing data)
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<RServiceResult<bool>> RegeneratePoemSections(int id)
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(_context);
var job = (await jobProgressServiceEF.NewJob($"RegeneratePoemSections - {id}", "Query data")).Result;
try
{
var sections = await _context.DivanPoemSections.Where(s => s.PoemId == id).ToListAsync();
int? meterId = sections.Any(s => s.SectionType == PoemSectionType.WholePoem && s.VerseType == VersePoemSectionType.First) ? sections.First(s => s.SectionType == PoemSectionType.WholePoem && s.VerseType == VersePoemSectionType.First).DivanMetreId : null;
_context.RemoveRange(sections);
await _context.SaveChangesAsync();
var poem = await _context.DivanPoems.Include(p => p.Cat).AsNoTracking().SingleAsync(p => p.Id == id);
await _SectionizePoem(_context, poem, jobProgressServiceEF, job, poem.Cat);
if (meterId != null)
{
List<string> rhymes = new List<string>();
var newSections = await _context.DivanPoemSections.Where(s => s.PoemId == id).ToListAsync();
foreach (var section in newSections)
{
section.DivanMetreId = meterId;
if (!string.IsNullOrEmpty(section.RhymeLetters))
{
if (!rhymes.Contains(section.RhymeLetters))
{
rhymes.Add(section.RhymeLetters);
}
}
}
_context.UpdateRange(newSections);
await _context.SaveChangesAsync();
if (rhymes.Count > 0)
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions<RMuseumDbContext>())) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF2 = new LongRunningJobProgressServiceEF(context);
var job2 = (await jobProgressServiceEF2.NewJob($"RegeneratePoemSections - Updating related sections", "Query data")).Result;
foreach (var rhyme in rhymes)
{
await _UpdateRelatedSections(context, (int)meterId, rhyme, jobProgressServiceEF2, job2);
}
}
}
);
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
return new RServiceResult<bool>(true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
return new RServiceResult<bool>(false, exp.ToString());
}
}
/// <summary>
/// get a specific poem sections
/// </summary>
/// <param name="sectionId"></param>
/// <returns></returns>
public async Task<RServiceResult<DivanPoemSection>> GetPoemSectionByIdAsync(int sectionId)
{
return new RServiceResult<DivanPoemSection>
(
await _context.DivanPoemSections.AsNoTracking().Include(s => s.DivanMetre).Where(s => s.Id == sectionId).SingleOrDefaultAsync()
);
}
/// <summary>
/// delete a poem section
/// </summary>
/// <param name="poemId"></param>
/// <param name="sectionIndex"></param>
/// <param name="convertVerses"></param>
/// <returns></returns>
public async Task<RServiceResult<bool>> DeletePoemSectionByPoemIdAndIndexAsync(int poemId, int sectionIndex, bool convertVerses)
{
try
{
var section = await _context.DivanPoemSections.Where(s => s.PoemId == poemId && s.Index == sectionIndex).FirstOrDefaultAsync();
if (section == null)
{
return new RServiceResult<bool>(false);//Not found
}
var connextedVerses =
await _context.DivanVerses
.Where
(
v => v.PoemId == poemId
&&
(v.SectionIndex1 == sectionIndex || v.SectionIndex2 == sectionIndex || v.SectionIndex3 == sectionIndex || v.SectionIndex4 == sectionIndex)
).ToListAsync();
bool regenPageHtml = false;
foreach (var verse in connextedVerses)
{
if (section.SectionType == PoemSectionType.WholePoem)
{
if (verse.VersePosition != VersePosition.Paragraph && verse.VersePosition != VersePosition.Comment)
{
if (convertVerses)
{
verse.VersePosition = VersePosition.Paragraph;
regenPageHtml = true;
}
else
{
return new RServiceResult<bool>(false, "قطعه شامل مصرع‌های غیرپاراگرافی است.");
}
}
}
if (verse.SectionIndex1 == sectionIndex)
{
verse.SectionIndex1 = null;
}
if (verse.SectionIndex2 == sectionIndex)
{
verse.SectionIndex2 = null;
}
if (verse.SectionIndex3 == sectionIndex)
{
verse.SectionIndex3 = null;
}
if (verse.SectionIndex4 == sectionIndex)
{
verse.SectionIndex4 = null;
}
}
_context.UpdateRange(connextedVerses);
_context.Remove(section);
await _context.SaveChangesAsync();
if (regenPageHtml)
{
var poemVerses = await _context.DivanVerses.AsNoTracking().Where(p => p.PoemId == poemId).OrderBy(v => v.VOrder).ToListAsync();
var dbPoem = await _context.DivanPoems.Include(p => p.DivanMetre).Where(p => p.Id == poemId).SingleAsync();
var dbPage = await _context.DivanPages.Where(p => p.Id == poemId).SingleAsync();
dbPoem.HtmlText = PrepareHtmlText(poemVerses);
dbPoem.PlainText = PreparePlainText(poemVerses, dbPoem.Title);
dbPage.HtmlText = dbPoem.HtmlText;
_context.Update(dbPoem);
_context.Update(dbPage);
await _context.SaveChangesAsync();
}
return new RServiceResult<bool>(true);
}
catch (Exception e)
{
return new RServiceResult<bool>(false, e.ToString());
}
}
/// <summary>
/// start band couplets fix
/// </summary>
/// <returns></returns>
public RServiceResult<bool> StartOneTimeBandCoupletsFix()
{
try
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions<RMuseumDbContext>())) //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("StartBandCoupletsFix", "Query data")).Result;
int progress = 0;
try
{
var sections = await context.DivanPoemSections.Where(s => s.SectionType == PoemSectionType.BandCouplets).ToListAsync();
int count = sections.Count;
for (int i = 0; i < count; i++)
{
progress++;
var section = sections[i];
section.OldRhymeLetters = section.RhymeLetters;
var verses = await context.DivanVerses.Where(v => v.PoemId == section.PoemId).OrderBy(v => v.VOrder).ToListAsync();
var bandVerses = FilterSectionVerses(section, verses);
var resRetry = LanguageUtils.FindRhyme(bandVerses, false, true, true);
if (string.IsNullOrEmpty(resRetry.Rhyme))
resRetry = LanguageUtils.FindRhyme(bandVerses, false, true, false);
if (!string.IsNullOrEmpty(resRetry.Rhyme) && section.OldRhymeLetters != resRetry.Rhyme)
{
section.RhymeLetters = resRetry.Rhyme;
context.Update(section);
await jobProgressServiceEF.UpdateJob(job.Id, progress);
if (section.DivanMetreId != null && section.OldRhymeLetters != resRetry.Rhyme)
{
if (!string.IsNullOrEmpty(section.OldRhymeLetters))
{
await _UpdateRelatedSections(context, (int)section.DivanMetreId, section.OldRhymeLetters);
}
await _UpdateRelatedSections(context, (int)section.DivanMetreId, section.RhymeLetters);
}
continue;
}
if (_IsMasnavi(bandVerses, true))
{
var poemSections = await context.DivanPoemSections.Where(s => s.PoemId == section.PoemId).ToListAsync();
if (poemSections.Any(s => s.VerseType == VersePoemSectionType.Forth))
continue;
var mainSections = poemSections.Where(s => s.SectionType == PoemSectionType.WholePoem && s.VerseType == VersePoemSectionType.First).ToList();
DivanPoemSection mainSection = null;
if (mainSections.Count == 1)
{
mainSection = mainSections.First();
if (mainSection.PoemFormat == DivanPoemFormat.MultiBand)
{
mainSection.PoemFormat = DivanPoemFormat.TarkibBand;
context.Update(mainSection);
}
}
else
continue;
int index = poemSections.Max(s => s.Index);
VersePoemSectionType verseType =
poemSections.Any(s => s.VerseType == VersePoemSectionType.Third) ? VersePoemSectionType.Forth : VersePoemSectionType.Third;
string preRhymeLetters = "";
for (int v = 0; v < bandVerses.Count; v += 2)
{
if (bandVerses[v].VersePosition != VersePosition.CenteredVerse1 || bandVerses[v + 1].VersePosition != VersePosition.CenteredVerse2)
continue;
index++;
var rightVerse = bandVerses[v];
var leftVerse = bandVerses[v + 1];
List<DivanVerse> coupletVerses = new List<DivanVerse>();
coupletVerses.Add(rightVerse);
coupletVerses.Add(leftVerse);
var res = LanguageUtils.FindRhyme(coupletVerses);
DivanPoemSection verseSection = new DivanPoemSection()
{
PoemId = section.PoemId,
PoetId = section.PoetId,
SectionType = PoemSectionType.Couplet,
VerseType = verseType,
Index = index,
Number = index + 1,
DivanMetreId = mainSection.DivanMetreId,
RhymeLetters = res.Rhyme,
DivanMetreRefSectionIndex = mainSection.Index,
CachedFirstCoupletIndex = (int)rightVerse.CoupletIndex,
};
if (verseType == VersePoemSectionType.Third)
{
rightVerse.SectionIndex3 = verseSection.Index;
leftVerse.SectionIndex3 = verseSection.Index;
}
else
{
rightVerse.SectionIndex4 = verseSection.Index;
leftVerse.SectionIndex4 = verseSection.Index;
}
context.Update(rightVerse);
context.Update(leftVerse);
var rl = new List<DivanVerse>(); rl.Add(rightVerse); rl.Add(leftVerse);
verseSection.HtmlText = PrepareHtmlText(rl);
verseSection.PlainText = PreparePlainText(rl);
verseSection.CoupletsCount = rl.Where(v => v.VersePosition == VersePosition.Left || v.VersePosition == VersePosition.CenteredVerse1).Count();
context.Add(verseSection);
await jobProgressServiceEF.UpdateJob(job.Id, progress);
if (verseSection.DivanMetreId != null && !string.IsNullOrEmpty(verseSection.RhymeLetters) && preRhymeLetters != verseSection.RhymeLetters)
{
preRhymeLetters = verseSection.RhymeLetters;
await _UpdateRelatedSections(context, (int)verseSection.DivanMetreId, verseSection.RhymeLetters);
}
}
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, progress, "", false, exp.ToString());
}
}
});
return new RServiceResult<bool>(true);
}
catch (Exception exp)
{
return new RServiceResult<bool>(false, exp.ToString());
}
}
}
}