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>
649 lines
36 KiB
C#
649 lines
36 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using RMuseum.Models.Divan;
|
|
using RSecurityBackend.Models.Generic;
|
|
using System;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using RMuseum.Models.Auth.ViewModel;
|
|
using RMuseum.Models.DivanIntegration;
|
|
using RSecurityBackend.Models.Auth.ViewModels;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http;
|
|
using System.Net;
|
|
using System.Text;
|
|
using RMuseum.DbContext;
|
|
using RSecurityBackend.Services.Implementation;
|
|
using RSecurityBackend.Models.Generic.Db;
|
|
using RMuseum.Models.PDFLibrary;
|
|
using DNTPersianUtils.Core;
|
|
using System.Collections.Generic;
|
|
|
|
namespace RMuseum.Services.Implementation
|
|
{
|
|
/// <summary>
|
|
/// IDivanService implementation
|
|
/// </summary>
|
|
public partial class DivanService : IDivanService
|
|
{
|
|
/// <summary>
|
|
/// synchronize naskban links
|
|
/// </summary>
|
|
/// <param name="divanUserId"></param>
|
|
/// <param name="naskbanUserName"></param>
|
|
/// <param name="naskbanPassword"></param>
|
|
public void SynchronizeNaskbanLinks(Guid divanUserId, string naskbanUserName, string naskbanPassword)
|
|
{
|
|
_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("SynchronizeNaskbanLinks", "Query data")).Result;
|
|
var res = await _SynchronizeNaskbanLinksAsync(context, divanUserId, naskbanUserName, naskbanPassword);
|
|
if (!string.IsNullOrEmpty(res.ExceptionString))
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, res.ExceptionString);
|
|
}
|
|
else
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// synchronize naskban links
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <param name="divanUserId"></param>
|
|
/// <param name="naskbanUserName"></param>
|
|
/// <param name="naskbanPassword"></param>
|
|
/// <returns>number of synched items</returns>
|
|
private async Task<RServiceResult<int>> _SynchronizeNaskbanLinksAsync(RMuseumDbContext context, Guid divanUserId, string naskbanUserName, string naskbanPassword)
|
|
{
|
|
try
|
|
{
|
|
LoggedOnUserModelEx loggedOnUser;
|
|
using (HttpClient client = new HttpClient())
|
|
{
|
|
LoginViewModel loginViewModel = new LoginViewModel()
|
|
{
|
|
Username = naskbanUserName,
|
|
Password = naskbanPassword,
|
|
ClientAppName = "Divan API",
|
|
Language = "ur-PK"
|
|
};
|
|
var loginResponse = await client.PostAsync("https://api.naskban.ir/api/users/login", new StringContent(JsonConvert.SerializeObject(loginViewModel), Encoding.UTF8, "application/json"));
|
|
|
|
if (loginResponse.StatusCode != HttpStatusCode.OK)
|
|
{
|
|
return new RServiceResult<int>(0, "login error: " + JsonConvert.DeserializeObject<string>(await loginResponse.Content.ReadAsStringAsync()));
|
|
}
|
|
loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await loginResponse.Content.ReadAsStringAsync());
|
|
}
|
|
|
|
|
|
using (HttpClient secureClient = new HttpClient())
|
|
{
|
|
secureClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", loggedOnUser.Token);
|
|
var unsyncedResponse = await secureClient.GetAsync("https://api.naskban.ir/api/pdf/divan/unsynched");
|
|
if (!unsyncedResponse.IsSuccessStatusCode)
|
|
{
|
|
return new RServiceResult<int>(0, "unsync error: " + JsonConvert.DeserializeObject<string>(await unsyncedResponse.Content.ReadAsStringAsync()));
|
|
}
|
|
var unsynchronizeds = JsonConvert.DeserializeObject<PDFDivanLink[]>(await unsyncedResponse.Content.ReadAsStringAsync());
|
|
foreach (var unsynchronized in unsynchronizeds)
|
|
{
|
|
bool isTextOriginalSource =
|
|
unsynchronized.IsTextOriginalSource
|
|
&&
|
|
await context.DivanLinks.Where(l => l.DivanPostId == unsynchronized.DivanPostId && l.IsTextOriginalSource).AnyAsync() == false
|
|
&&
|
|
await context.PinterestLinks.Where(l => l.DivanPostId == unsynchronized.DivanPostId && l.IsTextOriginalSource).AnyAsync() == false
|
|
;
|
|
if (false == await context.PinterestLinks.Where(p => p.NaskbanLinkId == unsynchronized.Id).AnyAsync())
|
|
{
|
|
PinterestLink link = new PinterestLink()
|
|
{
|
|
DivanPostId = unsynchronized.DivanPostId,
|
|
DivanTitle = unsynchronized.DivanTitle,
|
|
DivanUrl = unsynchronized.DivanUrl,
|
|
AltText = unsynchronized.PDFPageTitle,
|
|
LinkType = LinkType.Naskban,
|
|
PinterestUrl = $"https://naskban.ir/{unsynchronized.PDFBookId}/{unsynchronized.PageNumber}",
|
|
PinterestImageUrl = unsynchronized.ExternalThumbnailImageUrl,
|
|
ReviewResult = ReviewResult.Approved,
|
|
SuggestionDate = DateTime.Now,
|
|
SuggestedById = divanUserId,
|
|
Synchronized = true,
|
|
ReviewerId = divanUserId,
|
|
IsTextOriginalSource = isTextOriginalSource,
|
|
PDFBookId = unsynchronized.PDFBookId,
|
|
PageNumber = unsynchronized.PageNumber,
|
|
NaskbanLinkId = unsynchronized.Id,
|
|
MatchPercent = unsynchronized.SuggestedByMachine ? 100 : 0,
|
|
HumanReviewed = !unsynchronized.SuggestedByMachine,
|
|
};
|
|
context.PinterestLinks.Add(link);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
await secureClient.PutAsync($"https://api.naskban.ir/api/pdf/divan/sync/{unsynchronized.Id}", null);
|
|
}
|
|
|
|
var logoutUrl = $"https://api.naskban.ir/api/users/delsession?userId={loggedOnUser.User.Id}&sessionId={loggedOnUser.SessionId}";
|
|
await secureClient.DeleteAsync(logoutUrl);
|
|
return new RServiceResult<int>(unsynchronizeds.Length);
|
|
}
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
return new RServiceResult<int>(0, exp.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// mark naskban links as human reviewed
|
|
/// </summary>
|
|
/// <param name="bookId"></param>
|
|
/// <param name="parentCatId"></param>
|
|
/// <param name="humanReviewed"></param>
|
|
public void MarkNaskbanLinksAsHumanReviewed(int bookId, int parentCatId, bool humanReviewed)
|
|
{
|
|
_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($"MarkNaskbanLinksAsHumanReviewed(bookId: {bookId} - cat:{parentCatId})", "Query data")).Result;
|
|
try
|
|
{
|
|
List<int> catIdList = new List<int>
|
|
{
|
|
parentCatId
|
|
};
|
|
await _populateCategoryChildren(context, parentCatId, catIdList);
|
|
int poemCount = 0;
|
|
int progress = 0;
|
|
foreach (int catId in catIdList)
|
|
{
|
|
var poems = await context.DivanPoems.AsNoTracking().Where(p => p.CatId == catId).ToListAsync();
|
|
poemCount += poems.Count;
|
|
foreach (var poem in poems)
|
|
{
|
|
var naskbanLinks = await context.PinterestLinks.Where(l => l.DivanPostId == poem.Id && l.PDFBookId == bookId).ToListAsync();
|
|
foreach(var link in naskbanLinks)
|
|
{
|
|
link.HumanReviewed = humanReviewed;
|
|
context.Update(link);
|
|
}
|
|
await jobProgressServiceEF.UpdateJob(job.Id, progress, $"{progress} از {poemCount}");
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// mark naskban links a text original for a category
|
|
/// </summary>
|
|
/// <param name="bookId"></param>
|
|
/// <param name="parentCatId"></param>
|
|
/// <param name="textOriginal"></param>
|
|
public void MarkNaskbanLinksAsTextOriginal(int bookId, int parentCatId, bool textOriginal)
|
|
{
|
|
_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($"MarkNaskbanLinksAsTextOriginal(bookId: {bookId} - cat:{parentCatId})", "Query data")).Result;
|
|
try
|
|
{
|
|
List<int> catIdList = new List<int>
|
|
{
|
|
parentCatId
|
|
};
|
|
await _populateCategoryChildren(context, parentCatId, catIdList);
|
|
int poemCount = 0;
|
|
int progress = 0;
|
|
foreach (int catId in catIdList)
|
|
{
|
|
var poems = await context.DivanPoems.AsNoTracking().Where(p => p.CatId == catId).ToListAsync();
|
|
poemCount += poems.Count;
|
|
progress++;
|
|
foreach (var poem in poems)
|
|
{
|
|
if (textOriginal)
|
|
{
|
|
var sources = await context.PinterestLinks.Where(l => l.DivanPostId == poem.Id).ToListAsync();
|
|
foreach (var source in sources)
|
|
{
|
|
source.IsTextOriginalSource = source.PDFBookId == bookId;
|
|
if (source.IsTextOriginalSource)
|
|
source.HumanReviewed = true;
|
|
}
|
|
context.UpdateRange(sources);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
else
|
|
{
|
|
var source = await context.PinterestLinks.Where(l => l.DivanPostId == poem.Id && l.IsTextOriginalSource == true).SingleOrDefaultAsync();
|
|
if (source != null)
|
|
{
|
|
source.IsTextOriginalSource = false;
|
|
context.Update(source);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
}
|
|
await jobProgressServiceEF.UpdateJob(job.Id, progress, $"{progress} از {poemCount}");
|
|
}
|
|
}
|
|
|
|
var book = await context.DivanPaperSources.Where(s => s.NaskbanBookId == bookId && s.DivanCatId == parentCatId).SingleOrDefaultAsync();
|
|
if (book != null)
|
|
{
|
|
book.IsTextOriginalSource = textOriginal;
|
|
context.Update(book);
|
|
}
|
|
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// delete poem related naskban images by url
|
|
/// </summary>
|
|
/// <param name="naskbanUrl"></param>
|
|
/// <returns></returns>
|
|
public async Task<RServiceResult<bool>> DeletePoemRelatedNaskbanImagesByNaskbanUrlAsync(string naskbanUrl)
|
|
{
|
|
try
|
|
{
|
|
var images = await _context.PinterestLinks.Where(l => l.PinterestUrl == naskbanUrl && l.LinkType == LinkType.Naskban).ToListAsync();
|
|
if (images.Count > 0)
|
|
{
|
|
_context.RemoveRange(images);
|
|
await _context.SaveChangesAsync();
|
|
}
|
|
return new RServiceResult<bool>(true);
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
return new RServiceResult<bool>(false, exp.ToString());
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// justify naskban links
|
|
/// </summary>
|
|
/// <param name="naskbanUserName"></param>
|
|
/// <param name="naskbanPassword"></param>
|
|
public void JustifyNaskbanPageNumbers(string naskbanUserName, string naskbanPassword)
|
|
{
|
|
_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("JustifyNaskbanPageNumbers", "Query data")).Result;
|
|
var res = await _JustifyNaskbanPageNumbersAsync(context, naskbanUserName, naskbanPassword, jobProgressServiceEF, job);
|
|
if (!string.IsNullOrEmpty(res.ExceptionString))
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, res.ExceptionString);
|
|
}
|
|
else
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private async Task<RServiceResult<bool>> _JustifyNaskbanPageNumbersAsync(RMuseumDbContext context, string naskbanUserName, string naskbanPassword, LongRunningJobProgressServiceEF jobProgressServiceEF, RLongRunningJobStatus job)
|
|
{
|
|
try
|
|
{
|
|
LoggedOnUserModelEx loggedOnUser;
|
|
using (HttpClient client = new HttpClient())
|
|
{
|
|
LoginViewModel loginViewModel = new LoginViewModel()
|
|
{
|
|
Username = naskbanUserName,
|
|
Password = naskbanPassword,
|
|
ClientAppName = "Divan API",
|
|
Language = "ur-PK"
|
|
};
|
|
var loginResponse = await client.PostAsync("https://api.naskban.ir/api/users/login", new StringContent(JsonConvert.SerializeObject(loginViewModel), Encoding.UTF8, "application/json"));
|
|
|
|
if (loginResponse.StatusCode != HttpStatusCode.OK)
|
|
{
|
|
return new RServiceResult<bool>(false, "login error: " + JsonConvert.DeserializeObject<string>(await loginResponse.Content.ReadAsStringAsync()));
|
|
}
|
|
loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await loginResponse.Content.ReadAsStringAsync());
|
|
}
|
|
|
|
|
|
using (HttpClient secureClient = new HttpClient())
|
|
{
|
|
secureClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", loggedOnUser.Token);
|
|
|
|
|
|
DateTime firstLinkSuggestionDate = DateTime.MinValue;
|
|
var optionName = "LastJustifiedNaskbanLinkSuggestionDate";
|
|
RGenericOption lastJustifiedNaskbanLinkPoemIdGenericOption = await (from o in context.Options.AsNoTracking()
|
|
where o.Name == optionName && o.RAppUserId == null
|
|
select o).SingleOrDefaultAsync();
|
|
if (lastJustifiedNaskbanLinkPoemIdGenericOption != null)
|
|
{
|
|
firstLinkSuggestionDate = DateTime.Parse(lastJustifiedNaskbanLinkPoemIdGenericOption.Value);
|
|
}
|
|
else
|
|
{
|
|
lastJustifiedNaskbanLinkPoemIdGenericOption = new RGenericOption()
|
|
{
|
|
Name = optionName,
|
|
Value = firstLinkSuggestionDate.ToString("yyyy-MM-dd HH:mm:ss"),
|
|
};
|
|
context.Add(lastJustifiedNaskbanLinkPoemIdGenericOption);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
|
|
var naskbanLinks = await context.PinterestLinks.AsNoTracking().Where(l => l.LinkType == LinkType.Naskban && l.SuggestionDate > firstLinkSuggestionDate).OrderBy(l => l.SuggestionDate).ToListAsync();
|
|
|
|
if (firstLinkSuggestionDate == DateTime.MinValue && naskbanLinks.Any())
|
|
{
|
|
firstLinkSuggestionDate = naskbanLinks.First().SuggestionDate;
|
|
}
|
|
int progress = 0;
|
|
PDFBook book = null;
|
|
foreach (var naskbanLink in naskbanLinks)
|
|
{
|
|
progress++;
|
|
if (naskbanLink.HumanReviewed) continue;
|
|
if (naskbanLink.SuggestionDate != firstLinkSuggestionDate)
|
|
{
|
|
var option = await context.Options.Where(o => o.Id == lastJustifiedNaskbanLinkPoemIdGenericOption.Id).SingleAsync();
|
|
option.Value = firstLinkSuggestionDate.ToString("yyyy-MM-dd HH:mm:ss");
|
|
context.Update(option);
|
|
await jobProgressServiceEF.UpdateJob(job.Id, progress, $"{progress} از {naskbanLinks.Count}");
|
|
firstLinkSuggestionDate = naskbanLink.SuggestionDate;
|
|
}
|
|
if (book == null || book.Id != naskbanLink.PDFBookId)
|
|
{
|
|
HttpResponseMessage responseBook = await secureClient.GetAsync($"https://api.naskban.ir/api/pdf/{naskbanLink.PDFBookId}?includePages=true&includeBookText=false&includePageText=true");
|
|
if (responseBook.StatusCode != HttpStatusCode.OK)
|
|
{
|
|
return new RServiceResult<bool>(false, $"book fetch error bookid = {naskbanLink.PDFBookId} naskbanlinkid = {naskbanLink.Id} - " + JsonConvert.DeserializeObject<string>(await responseBook.Content.ReadAsStringAsync()));
|
|
}
|
|
responseBook.EnsureSuccessStatusCode();
|
|
|
|
book = JsonConvert.DeserializeObject<PDFBook>(await responseBook.Content.ReadAsStringAsync());
|
|
}
|
|
|
|
string bookPage = book.Title;
|
|
if (!string.IsNullOrEmpty(book.AuthorsLine))
|
|
{
|
|
bookPage = $"{book.Title} - {book.AuthorsLine}";
|
|
}
|
|
|
|
var currentPage = book.Pages.Where(p => p.PageNumber == naskbanLink.PageNumber).Single();
|
|
|
|
var modifyNaskbanLink = await context.PinterestLinks.Where(l => l.Id == naskbanLink.Id).SingleAsync();
|
|
modifyNaskbanLink.AltText = $"{bookPage} - تصویر {naskbanLink.PageNumber.ToPersianNumbers()}";
|
|
|
|
var verses = await context.DivanVerses.AsNoTracking().Where(v => v.PoemId == naskbanLink.DivanPostId && v.VersePosition != VersePosition.Comment).OrderBy(v => v.VOrder).ToListAsync();
|
|
if (!verses.Any()) continue;
|
|
string verseText = verses.First().Text;
|
|
if (verses.Count > 1)
|
|
{
|
|
verseText += $" {verses[1].Text}";
|
|
}
|
|
|
|
string[] poemWords = verseText.Split(new char[] { ' ', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (poemWords.Length == 0) continue;
|
|
|
|
string pageText = currentPage.PageText;
|
|
|
|
bool firstVerseFound = false;
|
|
|
|
int found = 0;
|
|
foreach (var poemWord in poemWords)
|
|
{
|
|
if (pageText.Contains(poemWord))
|
|
{
|
|
found++;
|
|
}
|
|
}
|
|
var percentMainPage = found * 100 / poemWords.Length;
|
|
|
|
if (percentMainPage >= 70)
|
|
{
|
|
firstVerseFound = true;
|
|
modifyNaskbanLink.MatchPercent = percentMainPage;
|
|
}
|
|
else
|
|
{
|
|
int t = 0;
|
|
do
|
|
{
|
|
t++;
|
|
if ((naskbanLink.PageNumber + t) >= book.Pages.Count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var nextPage = book.Pages.Where(p => p.PageNumber == naskbanLink.PageNumber + t).Single();
|
|
pageText = nextPage.PageText;
|
|
|
|
found = 0;
|
|
foreach (var poemWord in poemWords)
|
|
{
|
|
if (pageText.Contains(poemWord))
|
|
{
|
|
found++;
|
|
}
|
|
}
|
|
var percentNextPage = found * 100 / poemWords.Length;
|
|
|
|
if (percentNextPage >= 70)
|
|
{
|
|
modifyNaskbanLink.PageNumber = nextPage.PageNumber;
|
|
modifyNaskbanLink.PinterestImageUrl = nextPage.ExtenalThumbnailImageUrl;
|
|
modifyNaskbanLink.PinterestUrl = $"https://naskban.ir/{nextPage.PDFBookId}/{nextPage.PageNumber}";
|
|
modifyNaskbanLink.AltText = $"{bookPage} - تصویر {nextPage.PageNumber.ToPersianNumbers()}";
|
|
modifyNaskbanLink.MatchPercent = percentNextPage;
|
|
firstVerseFound = true;
|
|
break;
|
|
}
|
|
}
|
|
while (t < 5);
|
|
|
|
|
|
if (!firstVerseFound && naskbanLink.PageNumber > 1)
|
|
{
|
|
var prevPage = book.Pages.Where(p => p.PageNumber == (naskbanLink.PageNumber - 1)).Single();
|
|
pageText = prevPage.PageText;
|
|
|
|
found = 0;
|
|
foreach (var poemWord in poemWords)
|
|
{
|
|
if (pageText.Contains(poemWord))
|
|
{
|
|
found++;
|
|
}
|
|
}
|
|
var percentPrevPage = found * 100 / poemWords.Length;
|
|
|
|
if (percentPrevPage >= 70)
|
|
{
|
|
modifyNaskbanLink.PageNumber = prevPage.PageNumber;
|
|
modifyNaskbanLink.PinterestImageUrl = prevPage.ExtenalThumbnailImageUrl;
|
|
modifyNaskbanLink.PinterestUrl = $"https://naskban.ir/{prevPage.PDFBookId}/{prevPage.PageNumber}";
|
|
modifyNaskbanLink.AltText = $"{bookPage} - تصویر {prevPage.PageNumber.ToPersianNumbers()}";
|
|
modifyNaskbanLink.MatchPercent= percentPrevPage;
|
|
firstVerseFound = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!firstVerseFound)
|
|
{
|
|
modifyNaskbanLink.ReviewResult = ReviewResult.Awaiting;
|
|
}
|
|
|
|
context.Update(modifyNaskbanLink);
|
|
await context.SaveChangesAsync();
|
|
|
|
}
|
|
|
|
var logoutUrl = $"https://api.naskban.ir/api/users/delsession?userId={loggedOnUser.User.Id}&sessionId={loggedOnUser.SessionId}";
|
|
await secureClient.DeleteAsync(logoutUrl);
|
|
return new RServiceResult<bool>(true);
|
|
}
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
return new RServiceResult<bool>(false, exp.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// import naskban divan matchings
|
|
/// </summary>
|
|
/// <param name="naskbanUserName"></param>
|
|
/// <param name="naskbanPassword"></param>
|
|
public void ImportNaskbanDivanPoemMatchFindings(string naskbanUserName, string naskbanPassword)
|
|
{
|
|
_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("ImportNaskbanDivanPoemMatchFindings", "Query data")).Result;
|
|
var res = await _ImportNaskbanDivanPoemMatchFindingsAsync(context, naskbanUserName, naskbanPassword);
|
|
if (!string.IsNullOrEmpty(res.ExceptionString))
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, res.ExceptionString);
|
|
}
|
|
else
|
|
{
|
|
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// import naskban divan matchings
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <param name="naskbanUserName"></param>
|
|
/// <param name="naskbanPassword"></param>
|
|
/// <returns>number of synched items</returns>
|
|
private async Task<RServiceResult<int>> _ImportNaskbanDivanPoemMatchFindingsAsync(RMuseumDbContext context, string naskbanUserName, string naskbanPassword)
|
|
{
|
|
try
|
|
{
|
|
LoggedOnUserModelEx loggedOnUser;
|
|
using (HttpClient client = new HttpClient())
|
|
{
|
|
LoginViewModel loginViewModel = new LoginViewModel()
|
|
{
|
|
Username = naskbanUserName,
|
|
Password = naskbanPassword,
|
|
ClientAppName = "Divan API",
|
|
Language = "ur-PK"
|
|
};
|
|
var loginResponse = await client.PostAsync("https://api.naskban.ir/api/users/login", new StringContent(JsonConvert.SerializeObject(loginViewModel), Encoding.UTF8, "application/json"));
|
|
|
|
if (loginResponse.StatusCode != HttpStatusCode.OK)
|
|
{
|
|
return new RServiceResult<int>(0, "login error: " + JsonConvert.DeserializeObject<string>(await loginResponse.Content.ReadAsStringAsync()));
|
|
}
|
|
loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await loginResponse.Content.ReadAsStringAsync());
|
|
}
|
|
|
|
|
|
using (HttpClient secureClient = new HttpClient())
|
|
{
|
|
secureClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", loggedOnUser.Token);
|
|
var unsyncedResponse = await secureClient.GetAsync("https://api.naskban.ir/api/pdf/divan/matching?notStarted=false¬Finished=false");
|
|
if (!unsyncedResponse.IsSuccessStatusCode)
|
|
{
|
|
return new RServiceResult<int>(0, "unsync error: " + JsonConvert.DeserializeObject<string>(await unsyncedResponse.Content.ReadAsStringAsync()));
|
|
}
|
|
var matchings = JsonConvert.DeserializeObject<DivanPoemMatchFinding[]>(await unsyncedResponse.Content.ReadAsStringAsync());
|
|
foreach (var matching in matchings)
|
|
{
|
|
if (false == await context.DivanPaperSources.Where(p => p.NaskbanBookId == matching.BookId).AnyAsync())
|
|
{
|
|
HttpResponseMessage responseBook = await secureClient.GetAsync($"https://api.naskban.ir/api/pdf/{matching.BookId}?includePages=false&includeBookText=false&includePageText=false");
|
|
if (responseBook.StatusCode != HttpStatusCode.OK)
|
|
{
|
|
return new RServiceResult<int>(0, $"book fetch error bookid = {matching.BookId} matching id = {matching.Id} - " + JsonConvert.DeserializeObject<string>(await responseBook.Content.ReadAsStringAsync()));
|
|
}
|
|
responseBook.EnsureSuccessStatusCode();
|
|
|
|
var book = JsonConvert.DeserializeObject<PDFBook>(await responseBook.Content.ReadAsStringAsync());
|
|
var cat = await context.DivanCategories.AsNoTracking().Where(c => c.Id == matching.DivanCatId).SingleAsync();
|
|
DivanPaperSource paperSource = new DivanPaperSource()
|
|
{
|
|
DivanPoetId = cat.PoetId,
|
|
DivanCatId = matching.DivanCatId,
|
|
DivanCatFullTitle = matching.DivanCatFullTitle,
|
|
DivanCatFullUrl = matching.DivanCatFullUrl,
|
|
BookType = LinkType.Naskban,
|
|
BookFullUrl = $"https://naskban.ir/{matching.BookId}",
|
|
NaskbanBookId = matching.BookId,
|
|
BookFullTitle = book.Title,
|
|
CoverThumbnailImageUrl = book.ExtenalCoverImageUrl,
|
|
Description = "",
|
|
IsTextOriginalSource = false,
|
|
MatchPercent = 0,
|
|
HumanReviewed = false,
|
|
OrderIndicator = 0,
|
|
};
|
|
context.DivanPaperSources.Add(paperSource);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
}
|
|
|
|
var logoutUrl = $"https://api.naskban.ir/api/users/delsession?userId={loggedOnUser.User.Id}&sessionId={loggedOnUser.SessionId}";
|
|
await secureClient.DeleteAsync(logoutUrl);
|
|
return new RServiceResult<int>(matchings.Length);
|
|
}
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
return new RServiceResult<int>(0, exp.ToString());
|
|
}
|
|
}
|
|
}
|
|
} |