divan/DiwanRazor/Pages/Simi.cshtml.cs
Anas Rashid e8f2306422 Rename Ganjoor -> Diwan throughout the code
Projects (GanjooRazor -> DiwanRazor, GanjoorService.sln -> DiwanService.sln), files,
folders, classes, namespaces, settings, API routes (/api/diwan) and DB tables (Diwan*).
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

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 DiwanRazor.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RMuseum.Models.Diwan;
using RMuseum.Models.Diwan.ViewModels;
using RMuseum.Services.Implementation;
using RMuseum.Utils;
using RSecurityBackend.Models.Generic;
namespace DiwanRazor.Pages
{
[OutputCache(PolicyName = "DiwanPublicPage")]
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<DiwanPoetViewModel> 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 DiwanLanguage[] Languages { get; set; }
public DiwanPoetCompleteViewModel Poet { get; set; }
public List<DiwanPoemCompleteViewModel> Poems { get; set; }
public string PagingToolsHtml { get; set; }
public PaginationMetadata PaginationMetadata { get; set; }
public string LastError { get; set; }
public string Language { get; set; }
public DiwanPoemFormat 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<DiwanLanguage[]>(await response.Content.ReadAsStringAsync());
}
/// <summary>
/// rhythms alphabetically
/// </summary>
public DiwanMetre[] RhythmsAlphabetically { get; set; }
/// <summary>
/// rhythms by frequency
/// </summary>
public DiwanMetre[] 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 = DiwanPoemFormat.Unknown;
if (f != null)
{
Format = (DiwanPoemFormat)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/diwan/rhythms?sortOnVerseCount=true");
if (!rhythmResponse.IsSuccessStatusCode)
{
LastError = await ReadErrorMessageAsync(rhythmResponse);
return Page();
}
RhythmsByVerseCount = JsonConvert.DeserializeObject<DiwanMetre[]>(await rhythmResponse.Content.ReadAsStringAsync());
List<DiwanMetre> rhythmsByVerseCount = new List<DiwanMetre>(RhythmsByVerseCount);
rhythmsByVerseCount.Sort((a, b) => a.Rhythm.CompareTo(b.Rhythm));
rhythmsByVerseCount.Insert(0, new DiwanMetre()
{
Rhythm = "null"
}
);
rhythmsByVerseCount.Insert(0, new DiwanMetre()
{
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 != DiwanPoemFormat.Unknown)
{
title += $" در قالب شعری «{DiwanPoemFormatConvertor.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/diwan/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<DiwanPoetCompleteViewModel>();
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/diwan/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<DiwanPoemCompleteViewModel>>();
foreach (var poem in Poems)
{
poem.HtmlText = DiwanPoemTools.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/diwan/sections/{poemId}");
if (!sectionResponse.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(sectionResponse));
}
var sections = JsonConvert.DeserializeObject<DiwanPoemSection[]>(await sectionResponse.Content.ReadAsStringAsync());
var section = sections.Where(s => s.Index == sectionIndex).Single();
var correctionResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/diwan/section/correction/last/{section.Id}");
if (!correctionResponse.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(correctionResponse));
}
if (null != JsonConvert.DeserializeObject<DiwanPoemSectionCorrectionViewModel>(await correctionResponse.Content.ReadAsStringAsync()))
{
return new BadRequestObjectResult("شما پیشتر پیشنهادی تصحیحی برای این قطع ثبت کرده‌اید.");
}
DiwanPoemSectionCorrectionViewModel correction = new DiwanPoemSectionCorrectionViewModel()
{
SectionId = section.Id,
Rhythm = rhythm,
};
HttpResponseMessage response = await secureClient.PostAsync(
$"{APIRoot.Url}/api/diwan/section/correction",
new StringContent(JsonConvert.SerializeObject(correction),
Encoding.UTF8,
"application/json"));
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new OkObjectResult(true);
});
}
}
}