divan/GanjooRazor/Pages/Simi.cshtml.cs
2026-09-27 14:03:38 +03:30

466 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using DNTPersianUtils.Core;
using GanjooRazor.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RMuseum.Models.Ganjoor;
using RMuseum.Models.Ganjoor.ViewModels;
using RMuseum.Services.Implementation;
using RMuseum.Utils;
using RSecurityBackend.Models.Generic;
namespace GanjooRazor.Pages
{
[OutputCache(PolicyName = "GanjoorPublicPage")]
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<GanjoorPoetViewModel> 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 GanjoorLanguage[] Languages { get; set; }
public GanjoorPoetCompleteViewModel Poet { get; set; }
public List<GanjoorPoemCompleteViewModel> Poems { get; set; }
public string PagingToolsHtml { get; set; }
public PaginationMetadata PaginationMetadata { get; set; }
public string LastError { get; set; }
public string Language { get; set; }
public GanjoorPoemFormat 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<GanjoorLanguage[]>(await response.Content.ReadAsStringAsync());
}
/// <summary>
/// rhythms alphabetically
/// </summary>
public GanjoorMetre[] RhythmsAlphabetically { get; set; }
/// <summary>
/// rhythms by frequency
/// </summary>
public GanjoorMetre[] 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 ??= "fa-IR";
string f = Request.Query["f"];
anyParamsGiven |= f != null;
Format = GanjoorPoemFormat.Unknown;
if (f != null)
{
Format = (GanjoorPoemFormat)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/ganjoor/rhythms?sortOnVerseCount=true");
if (!rhythmResponse.IsSuccessStatusCode)
{
LastError = await ReadErrorMessageAsync(rhythmResponse);
return Page();
}
RhythmsByVerseCount = JsonConvert.DeserializeObject<GanjoorMetre[]>(await rhythmResponse.Content.ReadAsStringAsync());
List<GanjoorMetre> rhythmsByVerseCount = new List<GanjoorMetre>(RhythmsByVerseCount);
rhythmsByVerseCount.Sort((a, b) => a.Rhythm.CompareTo(b.Rhythm));
rhythmsByVerseCount.Insert(0, new GanjoorMetre()
{
Rhythm = "null"
}
);
rhythmsByVerseCount.Insert(0, new GanjoorMetre()
{
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 != "fa-IR")
{
var langModel = Languages.Where(l => l.Code == Language).FirstOrDefault();
if (langModel != null)
{
title += $" با زبان غالب «{langModel.Name}»";
}
}
if (Format != GanjoorPoemFormat.Unknown)
{
title += $" در قالب شعری «{GanjoorPoemFormatConvertor.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/ganjoor/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<GanjoorPoetCompleteViewModel>();
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/ganjoor/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<GanjoorPoemCompleteViewModel>>();
foreach (var poem in Poems)
{
poem.HtmlText = GanjoorPoemTools.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 ? $"&amp;a={PoetId}" : "";
if (authorParam != "" && CatId != 0)
{
authorParam += $"&amp;c={CatId}";
}
title += $" - صفحهٔ {pageNumber.ToPersianNumbers()}";
if (PaginationMetadata.currentPage > 3)
{
htmlText += $"<a href=\"/simi/?v={Uri.EscapeDataString(Metre)}&amp;g={Uri.EscapeDataString(Rhyme)}&amp;page=1{authorParam}&amp;l={Language}&amp;f={(int)Format}&amp;c1={CoupletCountsFrom}&amp;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)}&amp;g={Uri.EscapeDataString(Rhyme)}&amp;page={i}{authorParam}&amp;l={Language}&amp;f={(int)Format}&amp;c1={CoupletCountsFrom}&amp;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)}&amp;g={Uri.EscapeDataString(Rhyme)}&amp;page={PaginationMetadata.totalPages}{authorParam}&amp;l={Language}&amp;f={(int)Format}&amp;c1={CoupletCountsFrom}&amp;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/ganjoor/sections/{poemId}");
if (!sectionResponse.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(sectionResponse));
}
var sections = JsonConvert.DeserializeObject<GanjoorPoemSection[]>(await sectionResponse.Content.ReadAsStringAsync());
var section = sections.Where(s => s.Index == sectionIndex).Single();
var correctionResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/section/correction/last/{section.Id}");
if (!correctionResponse.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(correctionResponse));
}
if (null != JsonConvert.DeserializeObject<GanjoorPoemSectionCorrectionViewModel>(await correctionResponse.Content.ReadAsStringAsync()))
{
return new BadRequestObjectResult("شما پیشتر پیشنهادی تصحیحی برای این قطع ثبت کرده‌اید.");
}
GanjoorPoemSectionCorrectionViewModel correction = new GanjoorPoemSectionCorrectionViewModel()
{
SectionId = section.Id,
Rhythm = rhythm,
};
HttpResponseMessage response = await secureClient.PostAsync(
$"{APIRoot.Url}/api/ganjoor/section/correction",
new StringContent(JsonConvert.SerializeObject(correction),
Encoding.UTF8,
"application/json"));
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new OkObjectResult(true);
});
}
}
}