From 4ba7da57b8dd2986df8a7d060c22203a310340c7 Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Sat, 3 Oct 2026 08:41:31 +0330 Subject: [PATCH] people tags #387 --- .../Admin/Pages/ReviewPersonEdits.cshtml | 5 ++ .../Admin/Pages/ReviewPersonEdits.cshtml.cs | 13 +++++ GanjooRazor/Areas/User/Pages/Editor.cshtml | 13 ++++- .../Areas/User/Pages/SuggestPersonEdit.cshtml | 13 +++++ .../User/Pages/SuggestPersonEdit.cshtml.cs | 1 + GanjooRazor/wwwroot/js/peoplegraph.js | 17 +++++-- .../Ganjoor/GanjoorPersonEditSuggestion.cs | 5 ++ .../Models/Ganjoor/GanjoorRelatedPerson.cs | 6 +++ RMuseum/Models/Ganjoor/PersonImportance.cs | 26 ++++++++++ .../ViewModels/GanjoorPersonGraphViewModel.cs | 6 +++ .../ViewModels/PersonGraphSuggestion.cs | 7 +++ RMuseum/RMuseum.xml | 47 +++++++++++++++++++ .../GanjoorRelatedPersonService.cs | 2 + .../GanjoorService-ModeratePoemCorrection.cs | 7 +++ 14 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 RMuseum/Models/Ganjoor/PersonImportance.cs diff --git a/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml b/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml index 1f9a3cbb..1216683e 100644 --- a/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml +++ b/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml @@ -108,6 +108,11 @@ else @p.FamilyTreeCaption @s.SuggestedFamilyTreeCaption + + درجهٔ اهمیت + @Model.ImportanceLabel(p.Importance) + @Model.ImportanceLabel(s.SuggestedImportance) + توضیحات @p.Description diff --git a/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml.cs b/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml.cs index 09a820d6..93ab2129 100644 --- a/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml.cs +++ b/GanjooRazor/Areas/Admin/Pages/ReviewPersonEdits.cshtml.cs @@ -56,6 +56,19 @@ namespace GanjooRazor.Areas.Admin.Pages return loc == null ? "" : loc.Name; } + public string ImportanceLabel(PersonImportance importance) + { + switch (importance) + { + case PersonImportance.Important: + return "مهم"; + case PersonImportance.VeryImportant: + return "بسیار مهم"; + default: + return "معمولی"; + } + } + public IActionResult OnPost() { Skip = string.IsNullOrEmpty(Request.Query["skip"]) ? 0 : int.Parse(Request.Query["skip"]); diff --git a/GanjooRazor/Areas/User/Pages/Editor.cshtml b/GanjooRazor/Areas/User/Pages/Editor.cshtml index c79ff9b7..34f28869 100644 --- a/GanjooRazor/Areas/User/Pages/Editor.cshtml +++ b/GanjooRazor/Areas/User/Pages/Editor.cshtml @@ -560,7 +560,8 @@ validDeathDate: false, birthLocationId: null, deathLocationId: null, - familyTreeCaption: $('#geotag-new-person-familytreecaption').val() || null + familyTreeCaption: $('#geotag-new-person-familytreecaption').val() || null, + importance: $('#geotag-new-person-importance').val() }; var relatedPeople = []; @@ -852,6 +853,7 @@ $('#geotag-new-person-description').val(''); $('#geotag-new-person-wikiurl').val(''); $('#geotag-new-person-familytreecaption').val(''); + $('#geotag-new-person-importance').val('Normal'); $('#geotag-person-relatives-list').empty(); if (document.getElementById('new-person-widget').style.display !== 'none') { toggleNewPersonWidget(); @@ -2668,6 +2670,15 @@ else +
+ + +

فقط اندازهٔ نمایش این نامبرده را در نمودار شخصیت‌ها تعیین می‌کند.

+

اگر خویشاوندی یا وابستگی این نامبرده با نامبردۀ دیگری را می‌دانید، می‌توانید همین‌جا اضافه کنید (نامبردۀ دیگر باید از قبل در فهرست موجود باشد؛ diff --git a/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml b/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml index 581ee72b..e1f1ace0 100644 --- a/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml +++ b/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml @@ -65,6 +65,19 @@ else + + + درجهٔ اهمیت: +

صِرفاً تعیین می‌کند این نامبرده در نمودار شخصیت‌ها (شخصیت‌ها/شبکهٔ کامل) با چه اندازه نشان داده شود؛ در هیچ فهرست یا جستجویی تأثیری ندارد.

+ + + + + توضیحات: diff --git a/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml.cs b/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml.cs index 4ae9c63c..6ef494c4 100644 --- a/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml.cs +++ b/GanjooRazor/Areas/User/Pages/SuggestPersonEdit.cshtml.cs @@ -89,6 +89,7 @@ namespace GanjooRazor.Areas.User.Pages SuggestedBirthLocationId = Person.BirthLocationId, SuggestedDeathLocationId = Person.DeathLocationId, SuggestedFamilyTreeCaption = Person.FamilyTreeCaption, + SuggestedImportance = Person.Importance, }; } diff --git a/GanjooRazor/wwwroot/js/peoplegraph.js b/GanjooRazor/wwwroot/js/peoplegraph.js index 43845200..51f06f67 100644 --- a/GanjooRazor/wwwroot/js/peoplegraph.js +++ b/GanjooRazor/wwwroot/js/peoplegraph.js @@ -4,14 +4,17 @@ // self-hosted, dependency-light front-end code over pulling in something like d3-force. // // Data shape (RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonGraphViewModel, camelCased) - -// { nodes: [{id, name, hasFamilyTree, directlyTagged}], edges: [{person1Id, person1Name, +// { nodes: [{id, name, hasFamilyTree, directlyTagged, importance}], edges: [{person1Id, person1Name, // person2Id, person2Name, category, typeValue, degreeHint, note}] } where category is // "Relation" (typeValue is PersonRelationType: 0=Parent,1=Sibling,2=Spouse,3=Ancestor) or // "Affiliation" (typeValue is PersonAffiliationType: 0=Minister,1=Advisor,2=Courtier,3=Patron, // 4=Ally,5=Rival,6=Servant,7=Companion,8=Successor,9=Panegyrized,10=Satirized,99=Other). directlyTagged is false only on // the category-scoped graph (GET api/ganjoor/cat/{id}/persongraph, the "characters in this work" // tab): such a node was pulled in as a one-hop relative/affiliate of someone actually named in the -// work's verses, and is never false on the whole-site graph fed by PeopleExplorer.cshtml. +// work's verses, and is never false on the whole-site graph fed by PeopleExplorer.cshtml. importance +// is PersonImportance (0=Normal,1=Important,2=VeryImportant), an editorial prominence set on the +// person themselves (see Editor.cshtml's new-person widget and SuggestPersonEdit.cshtml) - used +// below, alongside hasFamilyTree and degree, to size a node's circle (see nodeRadius()). // // Called with an options object (see PeopleExplorer.cshtml and _PersonGraphPartial.cshtml for two // call sites with different element-id prefixes and data payloads), so the same renderer serves @@ -87,7 +90,7 @@ var nodesById = {}; var nodes = data.nodes.map(function (n) { var node = { - id: n.id, name: n.name, hasFamilyTree: n.hasFamilyTree, + id: n.id, name: n.name, hasFamilyTree: n.hasFamilyTree, importance: n.importance || 0, // absent on the whole-site graph payload (always directly tagged there) - default true directlyTagged: n.directlyTagged !== false, x: W / 2 + (Math.random() - 0.5) * W * 0.6, @@ -204,8 +207,14 @@ return { el: line, edge: e }; }); + // importance (PersonImportance: 0=Normal,1=Important,2=VeryImportant) is the dominant term - + // a VeryImportant person should read as clearly bigger even with few edges - hasFamilyTree + // adds a smaller, independent bump on top (a tree root is worth noting regardless of its + // own importance), and degree only fine-tunes within that. + var IMPORTANCE_RADIUS_BUMP = { 0: 0, 1: 4, 2: 9 }; function nodeRadius(n) { - return (n.hasFamilyTree ? 10 : 7) + Math.min(6, n.degree * 0.6); + var base = (n.hasFamilyTree ? 10 : 7) + (IMPORTANCE_RADIUS_BUMP[n.importance] || 0); + return base + Math.min(6, n.degree * 0.6); } var nodeEls = nodes.map(function (n) { diff --git a/RMuseum/Models/Ganjoor/GanjoorPersonEditSuggestion.cs b/RMuseum/Models/Ganjoor/GanjoorPersonEditSuggestion.cs index 8f7e0502..8be9cbad 100644 --- a/RMuseum/Models/Ganjoor/GanjoorPersonEditSuggestion.cs +++ b/RMuseum/Models/Ganjoor/GanjoorPersonEditSuggestion.cs @@ -108,6 +108,11 @@ namespace RMuseum.Models.Ganjoor /// public string SuggestedFamilyTreeCaption { get; set; } + /// + /// suggested importance (see GanjoorRelatedPerson.Importance) + /// + public PersonImportance SuggestedImportance { get; set; } + /// /// true if this suggestion is actually a request to delete the person outright, not to /// change their fields - when true, every Suggested* field above is ignored on approval diff --git a/RMuseum/Models/Ganjoor/GanjoorRelatedPerson.cs b/RMuseum/Models/Ganjoor/GanjoorRelatedPerson.cs index cc1aea2c..bad5f5b4 100644 --- a/RMuseum/Models/Ganjoor/GanjoorRelatedPerson.cs +++ b/RMuseum/Models/Ganjoor/GanjoorRelatedPerson.cs @@ -78,5 +78,11 @@ /// needs one set). /// public string FamilyTreeCaption { get; set; } + + /// + /// editorial prominence, used to size this person's node in the people graph larger or + /// smaller (see PersonImportance and peoplegraph.js's nodeRadius()) - defaults to Normal + /// + public PersonImportance Importance { get; set; } } } diff --git a/RMuseum/Models/Ganjoor/PersonImportance.cs b/RMuseum/Models/Ganjoor/PersonImportance.cs new file mode 100644 index 00000000..d94fd0a1 --- /dev/null +++ b/RMuseum/Models/Ganjoor/PersonImportance.cs @@ -0,0 +1,26 @@ +namespace RMuseum.Models.Ganjoor +{ + /// + /// how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing + /// hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and + /// peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query + /// results. New values can be appended safely later (stored as int). + /// + public enum PersonImportance + { + /// + /// default - no special prominence, drawn at the normal node size + /// + Normal = 0, + + /// + /// somewhat prominent (e.g. a well-known minister or local ruler) - drawn larger than Normal + /// + Important = 1, + + /// + /// most prominent (e.g. a major poet or king) - drawn largest + /// + VeryImportant = 2, + } +} diff --git a/RMuseum/Models/Ganjoor/ViewModels/GanjoorPersonGraphViewModel.cs b/RMuseum/Models/Ganjoor/ViewModels/GanjoorPersonGraphViewModel.cs index ed32dc78..57f59ccb 100644 --- a/RMuseum/Models/Ganjoor/ViewModels/GanjoorPersonGraphViewModel.cs +++ b/RMuseum/Models/Ganjoor/ViewModels/GanjoorPersonGraphViewModel.cs @@ -54,6 +54,12 @@ namespace RMuseum.Models.Ganjoor.ViewModels /// as secondary (e.g. dashed/dimmer) since it's context the reader wasn't shown directly. /// public bool DirectlyTagged { get; set; } = true; + + /// + /// numeric value of PersonImportance (0=Normal, 1=Important, 2=VeryImportant) - used + /// client-side to size this node larger or smaller, see peoplegraph.js's nodeRadius() + /// + public int Importance { get; set; } } /// diff --git a/RMuseum/Models/Ganjoor/ViewModels/PersonGraphSuggestion.cs b/RMuseum/Models/Ganjoor/ViewModels/PersonGraphSuggestion.cs index 2a997ee0..952e95a9 100644 --- a/RMuseum/Models/Ganjoor/ViewModels/PersonGraphSuggestion.cs +++ b/RMuseum/Models/Ganjoor/ViewModels/PersonGraphSuggestion.cs @@ -60,6 +60,13 @@ namespace RMuseum.Models.Ganjoor.ViewModels /// to be a tree's named root /// public string FamilyTreeCaption { get; set; } + + /// + /// name of a PersonImportance value ("Normal", "Important", "VeryImportant") - same + /// string-enum convention as PersonGraphRelationEntry.RelationType/AffiliationType below. + /// Null/empty is treated as Normal. + /// + public string Importance { get; set; } } /// diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index f484ad83..6f98a476 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -9517,6 +9517,11 @@ suggested family tree caption + + + suggested importance (see GanjoorRelatedPerson.Importance) + + true if this suggestion is actually a request to delete the person outright, not to @@ -11317,6 +11322,12 @@ needs one set). + + + editorial prominence, used to size this person's node in the people graph larger or + smaller (see PersonImportance and peoplegraph.js's nodeRadius()) - defaults to Normal + + ganjoor.net site banner @@ -11884,6 +11895,29 @@ doesn't fit any of the above - rely on Note for what the tie actually is + + + how editorially prominent a GanjoorRelatedPerson is treated as - purely a display/sizing + hint (see GanjoorRelatedPerson.Importance, GanjoorPersonGraphNode.Importance and + peoplegraph.js's nodeRadius()), never used to filter, rank or otherwise affect query + results. New values can be appended safely later (stored as int). + + + + + default - no special prominence, drawn at the normal node size + + + + + somewhat prominent (e.g. a well-known minister or local ruler) - drawn larger than Normal + + + + + most prominent (e.g. a major poet or king) - drawn largest + + what a GanjoorPersonRelationEditSuggestion proposes doing to the kinship graph @@ -13528,6 +13562,12 @@ as secondary (e.g. dashed/dimmer) since it's context the reader wasn't shown directly. + + + numeric value of PersonImportance (0=Normal, 1=Important, 2=VeryImportant) - used + client-side to size this node larger or smaller, see peoplegraph.js's nodeRadius() + + one edge of the people graph - either a kinship edge or a non-family tie, flattened to a @@ -15063,6 +15103,13 @@ to be a tree's named root + + + name of a PersonImportance value ("Normal", "Important", "VeryImportant") - same + string-enum convention as PersonGraphRelationEntry.RelationType/AffiliationType below. + Null/empty is treated as Normal. + + one edge in a PersonGraphSuggestion, between two nodes referenced by their LocalKey (Person1/ diff --git a/RMuseum/Services/Implementation/GanjoorRelatedPersonService.cs b/RMuseum/Services/Implementation/GanjoorRelatedPersonService.cs index 86934b30..5941cb08 100644 --- a/RMuseum/Services/Implementation/GanjoorRelatedPersonService.cs +++ b/RMuseum/Services/Implementation/GanjoorRelatedPersonService.cs @@ -421,6 +421,7 @@ namespace RMuseum.Services.Implementation person.BirthLocationId = suggestion.SuggestedBirthLocationId; person.DeathLocationId = suggestion.SuggestedDeathLocationId; person.FamilyTreeCaption = suggestion.SuggestedFamilyTreeCaption; + person.Importance = suggestion.SuggestedImportance; // Id and MachineGenerated on the person are intentionally left untouched } } @@ -1115,6 +1116,7 @@ namespace RMuseum.Services.Implementation Name = p.Name, HasFamilyTree = !string.IsNullOrEmpty(p.FamilyTreeCaption), DirectlyTagged = directlyTaggedIds == null || directlyTaggedIds.Contains(p.Id), + Importance = (int)p.Importance, }).ToList(); } diff --git a/RMuseum/Services/Implementation/GanjoorService-Partials/GanjoorService-ModeratePoemCorrection.cs b/RMuseum/Services/Implementation/GanjoorService-Partials/GanjoorService-ModeratePoemCorrection.cs index d5ad0e6b..74e04172 100644 --- a/RMuseum/Services/Implementation/GanjoorService-Partials/GanjoorService-ModeratePoemCorrection.cs +++ b/RMuseum/Services/Implementation/GanjoorService-Partials/GanjoorService-ModeratePoemCorrection.cs @@ -904,6 +904,12 @@ namespace RMuseum.Services.Implementation return new Tuple(0, "نام یکی از افراد پیشنهادی وارد نشده است."); } + var importance = PersonImportance.Normal; + if (!string.IsNullOrWhiteSpace(node.Importance) && !Enum.TryParse(node.Importance, out importance)) + { + return new Tuple(0, $"درجهٔ اهمیت «{node.Importance}» نامعتبر است."); + } + var newPerson = new GanjoorRelatedPerson() { Name = node.Name.Trim(), @@ -916,6 +922,7 @@ namespace RMuseum.Services.Implementation BirthLocationId = node.BirthLocationId, DeathLocationId = node.DeathLocationId, FamilyTreeCaption = string.IsNullOrWhiteSpace(node.FamilyTreeCaption) ? null : node.FamilyTreeCaption.Trim(), + Importance = importance, MachineGenerated = false, }; _context.GanjoorRelatedPersons.Add(newPerson);