#421 geo tag moderation

This commit is contained in:
Hamid Reza Mohammadi 2026-09-25 15:02:57 +03:30
parent fc241748e7
commit 13d493978a
4 changed files with 206 additions and 5 deletions

View File

@ -38,7 +38,7 @@
} }
} }
function moderatePoemCorrections(verseCount, correctionId) { function moderatePoemCorrections(verseCount, correctionId, geoTagCount) {
$('#moderatecorrections').hide(); $('#moderatecorrections').hide();
$('#saving').text('در حال بررسی ...'); $('#saving').text('در حال بررسی ...');
@ -133,6 +133,17 @@
} }
var geoTagReviewResult = [];
var geoTagReviewNotes = [];
for (var g = 0; g < (geoTagCount || 0); g++) {
var geoElementId = '#geotag' + (g + 1).toString() + '_review_result_Approved';
if ($(geoElementId).length > 0) {
var geoElementName = 'geotag' + (g + 1).toString() + '_review_result';
geoTagReviewResult.push($('input[name=' + geoElementName + ']:checked').val())
geoTagReviewNotes.push($('#geotag' + (g + 1).toString() + '_review_note').val())
}
}
var url = '?handler=SendCorrectionsModeration'; var url = '?handler=SendCorrectionsModeration';
var pms = {}; var pms = {};
@ -151,6 +162,8 @@
pms.verseReviewNotes= verseReviewNotes; pms.verseReviewNotes= verseReviewNotes;
pms.poemformatReviewResult= poemformatReviewResult; pms.poemformatReviewResult= poemformatReviewResult;
pms.poemformatReviewNote= $('#poemformat_review_note') == null ? null : $('#poemformat_review_note').val(); pms.poemformatReviewNote= $('#poemformat_review_note') == null ? null : $('#poemformat_review_note').val();
pms.geoTagReviewResult= geoTagReviewResult;
pms.geoTagReviewNotes= geoTagReviewNotes;
$.ajax({ $.ajax({
type: "POST", type: "POST",
@ -195,7 +208,7 @@ else
</tr> </tr>
<tr> <tr>
<td> <td>
<a role="button" id="moderatecorrections" class="up-btn up-btn--success up-btn--block" onclick="moderatePoemCorrections(@Model.PageInformation.Poem.Verses.Length, @Model.Correction.Id)">بررسی</a> <a role="button" id="moderatecorrections" class="up-btn up-btn--success up-btn--block" onclick="moderatePoemCorrections(@Model.PageInformation.Poem.Verses.Length, @Model.Correction.Id, @(Model.Correction.GeoDateTags == null ? 0 : Model.Correction.GeoDateTags.Length))">بررسی</a>
</td> </td>
<td> <td>
<form method="post"> <form method="post">
@ -755,6 +768,114 @@ else
</tr> </tr>
} }
@if (Model.Correction.GeoDateTags != null)
@for (var gi = 0; gi < Model.Correction.GeoDateTags.Length; gi++)
{
var geoTag = Model.Correction.GeoDateTags[gi];
var geoIndex = gi + 1;
<tr>
<td colspan="2">
برچسب جغرافیایی/تاریخی
@{
var geoCouplet = geoTag.CoupletIndex == null ? null : (Model.Couplets ?? Array.Empty<Tuple<int, string>>()).Where(c => c.Item1 == geoTag.CoupletIndex).Select(c => c.Item2).FirstOrDefault();
}
@if (geoTag.CoupletIndex != null)
{
<text>
- بیت <a href="@($"{Model.PageInformation.FullUrl}#bn{geoTag.CoupletIndex+1}")" target="_blank">@((geoTag.CoupletIndex + 1).ToPersianNumbers())</a>
</text>
}
</td>
</tr>
@if (geoCouplet != null)
{
<tr>
<td colspan="2">
<p>@geoCouplet</p>
</td>
</tr>
}
@if (geoTag.MarkForDelete)
{
var existingTag = geoTag.ExistingTagId == null || Model.PageInformation.Poem.GeoDateTags == null
? null
: Model.PageInformation.Poem.GeoDateTags.Where(t => t.Id == geoTag.ExistingTagId).FirstOrDefault();
<tr>
<td colspan="2">
<p class="red-color">
درخواست حذف برچسب:
@if (existingTag != null)
{
@(existingTag.Location != null ? existingTag.Location.Name : "")
@(existingTag.LunarYear != null ? $" - سال {existingTag.LunarYear}" : "")
}
</p>
</td>
</tr>
}
else
{
<tr>
<td>متن اولیه</td>
<td>پیشنهاد</td>
</tr>
<tr>
<td>
<p class="red-color">-</p>
</td>
<td>
<p class="green-color">
@if (geoTag.LocationId != null)
{
@(geoTag.Location != null ? geoTag.Location.Name : $"مکان #{geoTag.LocationId}")
}
else if (!string.IsNullOrEmpty(geoTag.SuggestedLocationName))
{
@($"{geoTag.SuggestedLocationName} ({geoTag.SuggestedLatitude}, {geoTag.SuggestedLongitude}) [مکان جدید]")
}
@if (geoTag.LunarYear != null)
{
<text> - سال @geoTag.LunarYear</text>
if (geoTag.LunarMonth != null)
{
<text>/@geoTag.LunarMonth</text>
if (geoTag.LunarDay != null)
{
<text>/@geoTag.LunarDay</text>
}
}
}
</p>
@if (!string.IsNullOrEmpty(geoTag.SuggestionNote))
{
<p><small>یادداشت پیشنهاد دهنده: @geoTag.SuggestionNote</small></p>
}
</td>
</tr>
}
<tr>
<td colspan="2">
<div class="up-radio-option">
<input type="radio" id="geotag@(geoIndex)_review_result_Approved" name="geotag@(geoIndex)_review_result" class="radio-approved" value="@RMuseum.Models.Ganjoor.CorrectionReviewResult.Approved">
<label for="geotag@(geoIndex)_review_result_Approved">@RMuseum.Services.Implementation.CorrectionReviewResultConvertor.GetString(RMuseum.Models.Ganjoor.CorrectionReviewResult.Approved)</label>
</div>
<div class="up-radio-option">
<input type="radio" id="geotag@(geoIndex)_review_result_RejectedBecauseWrong" name="geotag@(geoIndex)_review_result" class="radio-notcorrect" value="@RMuseum.Models.Ganjoor.CorrectionReviewResult.RejectedBecauseWrong">
<label for="geotag@(geoIndex)_review_result_RejectedBecauseWrong">@RMuseum.Services.Implementation.CorrectionReviewResultConvertor.GetString(RMuseum.Models.Ganjoor.CorrectionReviewResult.RejectedBecauseWrong)</label>
</div>
<div class="up-radio-option">
<input type="radio" id="geotag@(geoIndex)_review_result_RejectedBecauseUnnecessaryChange" name="geotag@(geoIndex)_review_result" class="radio-unnecessary" value="@RMuseum.Models.Ganjoor.CorrectionReviewResult.RejectedBecauseUnnecessaryChange">
<label for="geotag@(geoIndex)_review_result_RejectedBecauseUnnecessaryChange">تغییر سلیقه‌ای یا بی دلیل است</label>
</div>
<div class="up-radio-option">
<input type="radio" id="geotag@(geoIndex)_review_result_Rejected" name="geotag@(geoIndex)_review_result" class="radio-rejected" value="@RMuseum.Models.Ganjoor.CorrectionReviewResult.Rejected" checked="checked">
<label for="geotag@(geoIndex)_review_result_Rejected">@RMuseum.Services.Implementation.CorrectionReviewResultConvertor.GetString(RMuseum.Models.Ganjoor.CorrectionReviewResult.Rejected)</label>
</div>
<input type="text" id="geotag@(geoIndex)_review_note" name="geotag@(geoIndex)_review_note" value="" size="50" />
</td>
</tr>
}
@if (Model.Correction.VerseOrderText != null) @if (Model.Correction.VerseOrderText != null)
@foreach (var verse in Model.Correction.VerseOrderText) @foreach (var verse in Model.Correction.VerseOrderText)
{ {

View File

@ -56,6 +56,62 @@ namespace GanjooRazor.Areas.Admin.Pages
public bool ApproveVersePositionChanges { get; set; } public bool ApproveVersePositionChanges { get; set; }
/// <summary>
/// couplets of the poem, used to show which couplet a suggested geo/date tag belongs to
/// </summary>
public Tuple<int, string>[] Couplets { get; set; }
/// <summary>
/// groups verses into couplets - same logic as SuggestQuoted.cshtml.cs's/Editor.cshtml.cs's GetCouplets,
/// duplicated here rather than shared, so this page doesn't take on a cross-file dependency on those
/// </summary>
/// <param name="verses"></param>
/// <returns></returns>
private Tuple<int, string>[] GetCouplets(GanjoorVerseViewModel[] verses)
{
int coupetIndex = -1;
string coupletText = "";
var couplets = new System.Collections.Generic.List<Tuple<int, string>>();
int verseIndex = 0;
while (verseIndex < verses.Length)
{
switch (verses[verseIndex].VersePosition)
{
case VersePosition.Comment:
break;
case VersePosition.Paragraph:
case VersePosition.Single:
if (!string.IsNullOrEmpty(coupletText))
{
couplets.Add(new Tuple<int, string>(coupetIndex, coupletText));
coupletText = "";
}
coupetIndex++;
couplets.Add(new Tuple<int, string>(coupetIndex, verses[verseIndex].Text));
break;
case VersePosition.Right:
case VersePosition.CenteredVerse1:
if (!string.IsNullOrEmpty(coupletText))
{
couplets.Add(new Tuple<int, string>(coupetIndex, coupletText));
}
coupetIndex++;
coupletText = verses[verseIndex].Text;
break;
case VersePosition.Left:
case VersePosition.CenteredVerse2:
coupletText += $" - {verses[verseIndex].Text}";
break;
}
verseIndex++;
}
if (!string.IsNullOrEmpty(coupletText))
{
couplets.Add(new Tuple<int, string>(coupetIndex, coupletText));
}
return couplets.ToArray();
}
private async Task ReadLanguagesAsync(HttpClient secureClient) private async Task ReadLanguagesAsync(HttpClient secureClient)
{ {
HttpResponseMessage response = await secureClient.GetAsync($"{APIRoot.Url}/api/translations/languages"); HttpResponseMessage response = await secureClient.GetAsync($"{APIRoot.Url}/api/translations/languages");
@ -117,6 +173,8 @@ namespace GanjooRazor.Areas.Admin.Pages
} }
PageInformation = JObject.Parse(await pageQuery.Content.ReadAsStringAsync()).ToObject<GanjoorPageCompleteViewModel>(); PageInformation = JObject.Parse(await pageQuery.Content.ReadAsStringAsync()).ToObject<GanjoorPageCompleteViewModel>();
Couplets = GetCouplets(PageInformation.Poem.Verses);
if (PageInformation.Poem.Sections.Where(s => s.SectionType == PoemSectionType.WholePoem && !string.IsNullOrEmpty(s.RhymeLetters)).Any()) if (PageInformation.Poem.Sections.Where(s => s.SectionType == PoemSectionType.WholePoem && !string.IsNullOrEmpty(s.RhymeLetters)).Any())
{ {
RhymeLetters = PageInformation.Poem.Sections.Where(s => s.SectionType == PoemSectionType.WholePoem && !string.IsNullOrEmpty(s.RhymeLetters)).OrderBy(s => s.VerseType).First().RhymeLetters; RhymeLetters = PageInformation.Poem.Sections.Where(s => s.SectionType == PoemSectionType.WholePoem && !string.IsNullOrEmpty(s.RhymeLetters)).OrderBy(s => s.VerseType).First().RhymeLetters;
@ -318,6 +376,26 @@ namespace GanjooRazor.Areas.Admin.Pages
} }
} }
if (Correction.GeoDateTags != null && Correction.GeoDateTags.Length > 0)
{
if (pms.geoTagReviewResult == null || pms.geoTagReviewResult.Length != Correction.GeoDateTags.Length)
{
return new BadRequestObjectResult("لطفاً تکلیف بررسی تمام برچسب‌های جغرافیایی/تاریخی پیشنهادی را مشخص کنید.");
}
else
{
for (int i = 0; i < Correction.GeoDateTags.Length; i++)
{
if (pms.geoTagReviewResult[i] == null)
{
return new BadRequestObjectResult("لطفاً تکلیف بررسی تمام برچسب‌های جغرافیایی/تاریخی پیشنهادی را مشخص کنید.");
}
Correction.GeoDateTags[i].Result = (CorrectionReviewResult)Enum.Parse(typeof(CorrectionReviewResult), pms.geoTagReviewResult[i]);
Correction.GeoDateTags[i].ReviewNote = (pms.geoTagReviewNotes != null && i < pms.geoTagReviewNotes.Length) ? pms.geoTagReviewNotes[i] : null;
}
}
}
if (pms.verseReviewResult.Length != Correction.VerseOrderText.Length) if (pms.verseReviewResult.Length != Correction.VerseOrderText.Length)
{ {
return new BadRequestObjectResult("لطفاً تکلیف بررسی تمام مصرعهای پیشنهادی را مشخص کنید."); return new BadRequestObjectResult("لطفاً تکلیف بررسی تمام مصرعهای پیشنهادی را مشخص کنید.");

View File

@ -16,5 +16,7 @@
public string[] verseReviewNotes { get; set; } public string[] verseReviewNotes { get; set; }
public string poemformatReviewResult { get; set; } public string poemformatReviewResult { get; set; }
public string poemformatReviewNote { get; set; } public string poemformatReviewNote { get; set; }
public string[] geoTagReviewResult { get; set; }
public string[] geoTagReviewNotes { get; set; }
} }
} }

View File

@ -2358,7 +2358,7 @@ namespace RMuseum.Services.Implementation
public async Task<RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCorrectionViewModel[] Items)>> GetPoemEffectiveCorrections(int poemId, PagingParameterModel paging) public async Task<RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCorrectionViewModel[] Items)>> GetPoemEffectiveCorrections(int poemId, PagingParameterModel paging)
{ {
var source = from dbCorrection in var source = from dbCorrection in
_context.GanjoorPoemCorrections.AsNoTracking().Include(c => c.VerseOrderText).Include(c => c.GeoDateTags) _context.GanjoorPoemCorrections.AsNoTracking().Include(c => c.VerseOrderText).Include(c => c.GeoDateTags).ThenInclude(g => g.Location)
where where
dbCorrection.PoemId == poemId dbCorrection.PoemId == poemId
&& &&
@ -2445,7 +2445,7 @@ namespace RMuseum.Services.Implementation
/// <returns></returns> /// <returns></returns>
public async Task<RServiceResult<GanjoorPoemCorrectionViewModel>> GetCorrectionById(int id) public async Task<RServiceResult<GanjoorPoemCorrectionViewModel>> GetCorrectionById(int id)
{ {
var dbCorrection = await _context.GanjoorPoemCorrections.AsNoTracking().Include(c => c.VerseOrderText).Include(c => c.GeoDateTags).Include(c => c.User) var dbCorrection = await _context.GanjoorPoemCorrections.AsNoTracking().Include(c => c.VerseOrderText).Include(c => c.GeoDateTags).ThenInclude(g => g.Location).Include(c => c.User)
.Where(c => c.Id == id) .Where(c => c.Id == id)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
@ -2502,7 +2502,7 @@ namespace RMuseum.Services.Implementation
var systemUser = await _appUserService.FindUserByEmail(systemEmail); var systemUser = await _appUserService.FindUserByEmail(systemEmail);
var systemUserId = systemUser.Result == null ? Guid.Empty : (Guid)systemUser.Result.Id; var systemUserId = systemUser.Result == null ? Guid.Empty : (Guid)systemUser.Result.Id;
var dbCorrection = await _context.GanjoorPoemCorrections.AsNoTracking().Include(c => c.VerseOrderText).Include(c => c.GeoDateTags).Include(c => c.User) var dbCorrection = await _context.GanjoorPoemCorrections.AsNoTracking().Include(c => c.VerseOrderText).Include(c => c.GeoDateTags).ThenInclude(g => g.Location).Include(c => c.User)
.Where(c => c.Reviewed == false && (onlyUserCorrections == false || c.UserId != systemUserId)) .Where(c => c.Reviewed == false && (onlyUserCorrections == false || c.UserId != systemUserId))
.OrderBy(c => c.Id) .OrderBy(c => c.Id)
.Skip(skip) .Skip(skip)