- translate remaining public views and page-model strings (titles, messages, relation names) - Urdu stopword list for word-frequency stats (replaces Persian list) - error pages: drop list of alternative Ganjoor services Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
466 lines
17 KiB
C#
466 lines
17 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using DNTPersianUtils.Core;
|
||
using DivanRazor.Utils;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.AspNetCore.OutputCaching;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using RMuseum.Models.Divan;
|
||
using RMuseum.Models.Divan.ViewModels;
|
||
using RMuseum.Services.Implementation;
|
||
using RMuseum.Utils;
|
||
using RSecurityBackend.Models.Generic;
|
||
|
||
namespace DivanRazor.Pages
|
||
{
|
||
[OutputCache(PolicyName = "DivanPublicPage")]
|
||
public class SimiModel : LoginPartialEnabledPageModel
|
||
{
|
||
private readonly PoetCacheService _poetCache;
|
||
|
||
/// <summary>
|
||
/// constructor
|
||
/// </summary>
|
||
public SimiModel(HttpClient httpClient, IConfiguration configuration, PoetCacheService poetCache) : base(httpClient, configuration)
|
||
{
|
||
_poetCache = poetCache;
|
||
}
|
||
|
||
public List<DivanPoetViewModel> Poets { get; set; }
|
||
public int PoetId { get; set; }
|
||
public int CatId { get; set; }
|
||
public string CatFullTitle { get; set; }
|
||
public string CatFullUrl { get; set; }
|
||
public string Metre { get; set; }
|
||
public string Rhyme { get; set; }
|
||
public int CoupletCountsFrom { get; set; }
|
||
public int CoupletCountsTo { get; set; }
|
||
public DivanLanguage[] Languages { get; set; }
|
||
public DivanPoetCompleteViewModel Poet { get; set; }
|
||
public List<DivanPoemCompleteViewModel> Poems { get; set; }
|
||
public string PagingToolsHtml { get; set; }
|
||
|
||
public PaginationMetadata PaginationMetadata { get; set; }
|
||
public string LastError { get; set; }
|
||
public string Language { get; set; }
|
||
public DivanPoemFormat Format { get; set; }
|
||
public string Query { get; set; }
|
||
public bool Quoted { get; set; }
|
||
public bool ExactSearch { get; set; }
|
||
|
||
public int[] ExceptPoetId { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// can edit
|
||
/// </summary>
|
||
public bool CanEdit { get; set; }
|
||
|
||
private async Task ReadLanguagesAsync()
|
||
{
|
||
HttpResponseMessage response = await _httpClient.GetAsync($"{APIRoot.Url}/api/translations/languages");
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
LastError = await ReadErrorMessageAsync(response);
|
||
return;
|
||
}
|
||
|
||
Languages = JsonConvert.DeserializeObject<DivanLanguage[]>(await response.Content.ReadAsStringAsync());
|
||
}
|
||
|
||
/// <summary>
|
||
/// rhythms alphabetically
|
||
/// </summary>
|
||
public DivanMetre[] RhythmsAlphabetically { get; set; }
|
||
|
||
/// <summary>
|
||
/// rhythms by frequency
|
||
/// </summary>
|
||
public DivanMetre[] RhythmsByVerseCount { get; set; }
|
||
|
||
public async Task<IActionResult> OnGetPoetInformationAsync(int id)
|
||
{
|
||
if (id == 0)
|
||
{
|
||
return new OkObjectResult(null);
|
||
}
|
||
var (success, poet, error) = await _poetCache.GetPoetAsync(id, EditorCacheBypass);
|
||
if (!success)
|
||
{
|
||
return BadRequest(error);
|
||
}
|
||
return new OkObjectResult(poet);
|
||
}
|
||
|
||
public async Task<IActionResult> OnGetAsync()
|
||
{
|
||
var maintenanceResult = TryGetMaintenanceModeResult();
|
||
if (maintenanceResult != null)
|
||
{
|
||
return maintenanceResult;
|
||
}
|
||
|
||
bool anyParamsGiven = false;
|
||
|
||
InitializeCommonPageState();
|
||
|
||
CanEdit = Request.Cookies["CanEdit"] == "True";
|
||
|
||
PoetId = string.IsNullOrEmpty(Request.Query["a"]) ? 0 : int.Parse(Request.Query["a"]);
|
||
|
||
CatId = string.IsNullOrEmpty(Request.Query["c"]) ? 0 : int.Parse(Request.Query["c"]);
|
||
|
||
anyParamsGiven |= PoetId != 0;
|
||
|
||
Language = Request.Query["l"];
|
||
|
||
anyParamsGiven |= Language != null;
|
||
|
||
Language ??= "ur-PK";
|
||
|
||
string f = Request.Query["f"];
|
||
anyParamsGiven |= f != null;
|
||
|
||
Format = DivanPoemFormat.Unknown;
|
||
if (f != null)
|
||
{
|
||
Format = (DivanPoemFormat)int.Parse(f);
|
||
}
|
||
|
||
Query = Request.Query["s"].ApplyCorrectYeKe().Trim();
|
||
ExactSearch = Request.Query["es"] == "1";
|
||
bool quotes = Query.IndexOf("\"") != -1 || ExactSearch;
|
||
Query = LanguageUtils.MakeTextSearchable(Query); //replace zwnj with space
|
||
if (quotes)
|
||
Query = $"\"{Query}\"";
|
||
|
||
Quoted = quotes && Query.Contains(" ");
|
||
|
||
//todo: use html master layout or make it partial
|
||
// 1. poets
|
||
var (poetsOk, poets, poetsError) = await _poetCache.GetPoetsAsync(EditorCacheBypass);
|
||
if (!poetsOk)
|
||
{
|
||
LastError = poetsError;
|
||
return Page();
|
||
}
|
||
Poets = poets;
|
||
await ReadLanguagesAsync();
|
||
|
||
if (PoetId != 0)
|
||
{
|
||
var (poetOk, poet, poetError) = await _poetCache.GetPoetAsync(PoetId, EditorCacheBypass);
|
||
if (!poetOk)
|
||
{
|
||
LastError = poetError;
|
||
return Page();
|
||
}
|
||
Poet = poet;
|
||
}
|
||
|
||
// 2. search verses
|
||
|
||
|
||
|
||
var rhythmResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/rhythms?sortOnVerseCount=true");
|
||
if (!rhythmResponse.IsSuccessStatusCode)
|
||
{
|
||
LastError = await ReadErrorMessageAsync(rhythmResponse);
|
||
return Page();
|
||
}
|
||
|
||
RhythmsByVerseCount = JsonConvert.DeserializeObject<DivanMetre[]>(await rhythmResponse.Content.ReadAsStringAsync());
|
||
|
||
List<DivanMetre> rhythmsByVerseCount = new List<DivanMetre>(RhythmsByVerseCount);
|
||
rhythmsByVerseCount.Sort((a, b) => a.Rhythm.CompareTo(b.Rhythm));
|
||
rhythmsByVerseCount.Insert(0, new DivanMetre()
|
||
{
|
||
Rhythm = "null"
|
||
}
|
||
);
|
||
rhythmsByVerseCount.Insert(0, new DivanMetre()
|
||
{
|
||
Rhythm = ""
|
||
}
|
||
);
|
||
|
||
RhythmsAlphabetically = rhythmsByVerseCount.ToArray();
|
||
|
||
int pageNumber = 1;
|
||
if (!string.IsNullOrEmpty(Request.Query["page"]))
|
||
{
|
||
pageNumber = int.Parse(Request.Query["page"]);
|
||
}
|
||
|
||
Metre = Request.Query["v"];
|
||
Rhyme = Request.Query["g"];
|
||
|
||
anyParamsGiven |= Metre != null;
|
||
anyParamsGiven |= Rhyme != null;
|
||
|
||
Metre ??= "";
|
||
Rhyme ??= "";
|
||
|
||
|
||
if (!string.IsNullOrEmpty(Rhyme))
|
||
{
|
||
Rhyme = Rhyme.Replace(" ", "");
|
||
}
|
||
|
||
CoupletCountsFrom = 0;
|
||
if (!string.IsNullOrEmpty(Request.Query["c1"]))
|
||
{
|
||
if (int.TryParse(Request.Query["c1"], out int i))
|
||
{
|
||
CoupletCountsFrom = i;
|
||
anyParamsGiven = anyParamsGiven || CoupletCountsFrom != 0;
|
||
}
|
||
}
|
||
|
||
CoupletCountsTo = 0;
|
||
if (!string.IsNullOrEmpty(Request.Query["c2"]))
|
||
{
|
||
if (int.TryParse(Request.Query["c2"], out int i))
|
||
{
|
||
CoupletCountsTo = i;
|
||
anyParamsGiven = anyParamsGiven || CoupletCountsTo != 0;
|
||
}
|
||
}
|
||
|
||
|
||
if (!anyParamsGiven)
|
||
{
|
||
ViewData["Title"] = $"دیوان » ملتی جلتی تخلیقات";
|
||
return Page();
|
||
}
|
||
|
||
|
||
|
||
|
||
string title = "اشعار ";
|
||
|
||
if (PoetId != 0)
|
||
{
|
||
var poetInfo = Poets.Where(p => p.Id == PoetId).SingleOrDefault();
|
||
if (poetInfo != null)
|
||
{
|
||
title += $"{poetInfo.Nickname} ";
|
||
}
|
||
}
|
||
if (!string.IsNullOrEmpty(Metre))
|
||
{
|
||
title += $"بحر: «{Metre}»";
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(Rhyme))
|
||
{
|
||
if (!string.IsNullOrEmpty(Metre))
|
||
{
|
||
title += $" و";
|
||
}
|
||
else
|
||
{
|
||
title += $"با";
|
||
}
|
||
title += $" حروفِ قافیہ: «{Rhyme}»";
|
||
}
|
||
|
||
|
||
if (Language != "ur-PK")
|
||
{
|
||
var langModel = Languages.Where(l => l.Code == Language).FirstOrDefault();
|
||
if (langModel != null)
|
||
{
|
||
title += $" غالب زبان: «{langModel.Name}»";
|
||
}
|
||
}
|
||
|
||
if (Format != DivanPoemFormat.Unknown)
|
||
{
|
||
title += $" صنف: «{DivanPoemFormatConvertor.GetString(Format)}»";
|
||
}
|
||
|
||
if (CoupletCountsFrom != 0)
|
||
{
|
||
title += $" اشعار کی کم از کم تعداد «{CoupletCountsFrom.ToPersianNumbers()}»";
|
||
}
|
||
|
||
if (CoupletCountsTo != 0)
|
||
{
|
||
title += $" اشعار کی زیادہ سے زیادہ تعداد «{CoupletCountsTo.ToPersianNumbers()}»";
|
||
}
|
||
|
||
if (CatId != 0)
|
||
{
|
||
var catResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/cat/{CatId}?poems=false&mainSections=false");
|
||
if (!catResponse.IsSuccessStatusCode)
|
||
{
|
||
LastError = await ReadErrorMessageAsync(catResponse);
|
||
return Page();
|
||
}
|
||
else
|
||
{
|
||
var cat = JObject.Parse(await catResponse.Content.ReadAsStringAsync()).ToObject<DivanPoetCompleteViewModel>();
|
||
CatFullUrl = cat.Cat.FullUrl;
|
||
CatFullTitle = "";
|
||
foreach (var parentCat in cat.Cat.Ancestors)
|
||
{
|
||
CatFullTitle += parentCat.Title;
|
||
CatFullTitle += " »";
|
||
}
|
||
CatFullTitle += " " + cat.Cat.Title;
|
||
|
||
title += $" حصے میں {CatFullTitle}";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CatFullUrl = "";
|
||
CatFullTitle = "";
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(Query))
|
||
{
|
||
title += $" کلیدی لفظ: «{Query}»";
|
||
}
|
||
|
||
List<int> exceptPoetId = new List<int>();
|
||
foreach (var e in Request.Query["e"])
|
||
{
|
||
exceptPoetId.Add(int.Parse(e));
|
||
}
|
||
ExceptPoetId = exceptPoetId.ToArray();
|
||
|
||
|
||
string url = $"{APIRoot.Url}/api/divan/poems/similar?PageNumber={pageNumber}&PageSize=20&metre={Metre}&rhyme={Rhyme}&poetId={PoetId}&catId={CatId}&language={Language}&format={(int)Format}&coupletCountsFrom={CoupletCountsFrom}&coupletCountsTo={CoupletCountsTo}";
|
||
if (!string.IsNullOrEmpty(Query))
|
||
{
|
||
url += $"&term={Query}";
|
||
}
|
||
string exceptUrl = "";
|
||
foreach (var e in exceptPoetId)
|
||
{
|
||
exceptUrl += $"&e={e}";
|
||
}
|
||
url += exceptUrl;
|
||
|
||
var response = await _httpClient.GetAsync(url);
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
LastError = await ReadErrorMessageAsync(response);
|
||
return Page();
|
||
}
|
||
|
||
Poems = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<DivanPoemCompleteViewModel>>();
|
||
|
||
foreach (var poem in Poems)
|
||
{
|
||
poem.HtmlText = DivanPoemTools.GetPoemHtmlExcerpt(poem.HtmlText);
|
||
}
|
||
|
||
string paginnationMetadata = response.Headers.GetValues("paging-headers").FirstOrDefault();
|
||
PaginationMetadata = JsonConvert.DeserializeObject<PaginationMetadata>(paginnationMetadata);
|
||
|
||
string htmlText = "";
|
||
if (PaginationMetadata.totalPages > 1)
|
||
{
|
||
htmlText = $"<div>{Environment.NewLine}";
|
||
string authorParam = PoetId != 0 ? $"&a={PoetId}" : "";
|
||
if (authorParam != "" && CatId != 0)
|
||
{
|
||
authorParam += $"&c={CatId}";
|
||
}
|
||
title += $" - صفحہ {pageNumber.ToPersianNumbers()}";
|
||
if (PaginationMetadata.currentPage > 3)
|
||
{
|
||
htmlText += $"<a href=\"/simi/?v={Uri.EscapeDataString(Metre)}&g={Uri.EscapeDataString(Rhyme)}&page=1{authorParam}&l={Language}&f={(int)Format}&c1={CoupletCountsFrom}&c2={CoupletCountsTo}{exceptUrl}\"><div class=\"circled-number\">۱</div></a> …";
|
||
}
|
||
for (int i = PaginationMetadata.currentPage - 2; i <= (PaginationMetadata.currentPage + 2); i++)
|
||
{
|
||
if (i >= 1 && i <= PaginationMetadata.totalPages)
|
||
{
|
||
if (i == PaginationMetadata.currentPage)
|
||
{
|
||
htmlText += $"<div class=\"circled-number-diff\">{i.ToPersianNumbers()}</div>{Environment.NewLine}";
|
||
}
|
||
else
|
||
{
|
||
htmlText += $"<a href=\"/simi/?v={Uri.EscapeDataString(Metre)}&g={Uri.EscapeDataString(Rhyme)}&page={i}{authorParam}&l={Language}&f={(int)Format}&c1={CoupletCountsFrom}&c2={CoupletCountsTo}{exceptUrl}\"><div class=\"circled-number\">{i.ToPersianNumbers()}</div></a>{Environment.NewLine}";
|
||
}
|
||
}
|
||
}
|
||
if (PaginationMetadata.totalPages > (PaginationMetadata.currentPage + 2))
|
||
{
|
||
htmlText += $"… <a href=\"/simi/?v={Uri.EscapeDataString(Metre)}&g={Uri.EscapeDataString(Rhyme)}&page={PaginationMetadata.totalPages}{authorParam}&l={Language}&f={(int)Format}&c1={CoupletCountsFrom}&c2={CoupletCountsTo}{exceptUrl}\"><div class=\"circled-number\">{PaginationMetadata.totalPages.ToPersianNumbers()}</div></a>{Environment.NewLine}";
|
||
}
|
||
htmlText += $"</div>{Environment.NewLine}";
|
||
}
|
||
|
||
ViewData["Title"] = $"دیوان » {title}";
|
||
PagingToolsHtml = htmlText;
|
||
|
||
return Page();
|
||
}
|
||
public string HtmlText { get; set; }
|
||
|
||
public Task<IActionResult> OnPostSendSectionMetreSuggestionAsync(int poemId, int sectionIndex, string rhythm)
|
||
{
|
||
return WithSecureClientAsync(async secureClient =>
|
||
{
|
||
if (rhythm == "null")
|
||
rhythm = "";
|
||
|
||
if (string.IsNullOrEmpty(rhythm))
|
||
{
|
||
return new BadRequestObjectResult("بحر منتخب نہیں");
|
||
}
|
||
|
||
var sectionResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/sections/{poemId}");
|
||
if (!sectionResponse.IsSuccessStatusCode)
|
||
{
|
||
return new BadRequestObjectResult(await ReadErrorMessageAsync(sectionResponse));
|
||
}
|
||
var sections = JsonConvert.DeserializeObject<DivanPoemSection[]>(await sectionResponse.Content.ReadAsStringAsync());
|
||
|
||
var section = sections.Where(s => s.Index == sectionIndex).Single();
|
||
|
||
var correctionResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/section/correction/last/{section.Id}");
|
||
if (!correctionResponse.IsSuccessStatusCode)
|
||
{
|
||
return new BadRequestObjectResult(await ReadErrorMessageAsync(correctionResponse));
|
||
}
|
||
|
||
if (null != JsonConvert.DeserializeObject<DivanPoemSectionCorrectionViewModel>(await correctionResponse.Content.ReadAsStringAsync()))
|
||
{
|
||
return new BadRequestObjectResult("آپ اس حصے کے لیے پہلے ہی تصحیح تجویز کر چکے ہیں.");
|
||
}
|
||
|
||
DivanPoemSectionCorrectionViewModel correction = new DivanPoemSectionCorrectionViewModel()
|
||
{
|
||
SectionId = section.Id,
|
||
Rhythm = rhythm,
|
||
};
|
||
|
||
HttpResponseMessage response = await secureClient.PostAsync(
|
||
$"{APIRoot.Url}/api/divan/section/correction",
|
||
new StringContent(JsonConvert.SerializeObject(correction),
|
||
Encoding.UTF8,
|
||
"application/json"));
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
|
||
}
|
||
return new OkObjectResult(true);
|
||
});
|
||
}
|
||
|
||
}
|
||
}
|