people tags #387
This commit is contained in:
parent
5fcf862862
commit
8110e09a07
57
GanjooRazor/Pages/People.cshtml
Normal file
57
GanjooRazor/Pages/People.cshtml
Normal file
@ -0,0 +1,57 @@
|
||||
@page
|
||||
@model GanjooRazor.Pages.PeopleModel
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@section Head {
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.LastError))
|
||||
{
|
||||
@Model.LastError
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- #hdr2 -->
|
||||
|
||||
<div id="fa">
|
||||
|
||||
<div id="maincnt">
|
||||
|
||||
<partial name="_InlineLoginPartial" model="Model" />
|
||||
|
||||
<div class="spacer"> </div>
|
||||
|
||||
<div class="poem">
|
||||
<h1>شجرهنامهها</h1>
|
||||
@if (Model.FamilyTreeRoots == null || Model.FamilyTreeRoots.Count == 0)
|
||||
{
|
||||
<p><small>هنوز شجرهنامهای ثبت نشده است.</small></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul>
|
||||
@foreach (var root in Model.FamilyTreeRoots)
|
||||
{
|
||||
<li>
|
||||
<a href="/person/@root.Id">@root.FamilyTreeCaption</a>
|
||||
<small>(@root.Name)</small>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="spacer"> </div>
|
||||
|
||||
</div>
|
||||
|
||||
<partial name="_FooterPartial" model=@(new _FooterPartialModel(){StickyEnabled = true}) />
|
||||
|
||||
</div>
|
||||
|
||||
@if (ViewData["TrackingScript"] != null && !string.IsNullOrEmpty(ViewData["TrackingScript"].ToString()))
|
||||
{
|
||||
@Html.Raw(ViewData["TrackingScript"])
|
||||
}
|
||||
}
|
||||
44
GanjooRazor/Pages/People.cshtml.cs
Normal file
44
GanjooRazor/Pages/People.cshtml.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using RMuseum.Models.Ganjoor;
|
||||
|
||||
namespace GanjooRazor.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// entry point for browsing family trees - lists every person that captions a tree
|
||||
/// (GanjoorRelatedPerson.FamilyTreeCaption not empty), grouped under that caption. A person
|
||||
/// with no caption of their own still shows up via their relatives on that person's own page
|
||||
/// (see Person.cshtml) - this index is only for jumping straight to a named tree's root(s).
|
||||
/// </summary>
|
||||
public class PeopleModel : LoginPartialEnabledPageModel
|
||||
{
|
||||
public PeopleModel(HttpClient httpClient, IConfiguration configuration) : base(httpClient, configuration)
|
||||
{
|
||||
}
|
||||
|
||||
public string LastError { get; set; }
|
||||
|
||||
public List<GanjoorRelatedPerson> FamilyTreeRoots { get; set; }
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
InitializeCommonPageState();
|
||||
|
||||
ViewData["Title"] = "گنجور » شجرهنامهها";
|
||||
|
||||
var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/people/familytrees");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
LastError = await ReadErrorMessageAsync(response);
|
||||
return Page();
|
||||
}
|
||||
FamilyTreeRoots = JsonConvert.DeserializeObject<List<GanjoorRelatedPerson>>(await response.Content.ReadAsStringAsync());
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
135
GanjooRazor/Pages/Person.cshtml
Normal file
135
GanjooRazor/Pages/Person.cshtml
Normal file
@ -0,0 +1,135 @@
|
||||
@page "{id:int}"
|
||||
@model GanjooRazor.Pages.PersonModel
|
||||
@using DNTPersianUtils.Core
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@section Head {
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.LastError))
|
||||
{
|
||||
@Model.LastError
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- #hdr2 -->
|
||||
|
||||
<div id="fa">
|
||||
|
||||
<div id="maincnt">
|
||||
|
||||
<partial name="_InlineLoginPartial" model="Model" />
|
||||
|
||||
<div class="spacer"> </div>
|
||||
|
||||
<div class="poem">
|
||||
<h1>@Model.Person.Name</h1>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Person.FamilyTreeCaption))
|
||||
{
|
||||
<p><strong>شجرهنامه: @Model.Person.FamilyTreeCaption</strong></p>
|
||||
}
|
||||
|
||||
@if (Model.Person.BirthYearInLHijri != 0 || Model.Person.DeathYearInLHijri != 0)
|
||||
{
|
||||
<p>
|
||||
@if (Model.Person.BirthYearInLHijri != 0)
|
||||
{
|
||||
<text>تولد: @Model.Person.BirthYearInLHijri.ToPersianNumbers() (قمری) @(Model.Person.ValidBirthDate ? "" : "- تخمینی")</text>
|
||||
}
|
||||
@if (Model.Person.DeathYearInLHijri != 0)
|
||||
{
|
||||
<text> — وفات: @Model.Person.DeathYearInLHijri.ToPersianNumbers() (قمری) @(Model.Person.ValidDeathDate ? "" : "- تخمینی")</text>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Person.Description))
|
||||
{
|
||||
<p>@Model.Person.Description</p>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.Person.WikiUrl))
|
||||
{
|
||||
<p><a href="@Model.Person.WikiUrl" target="_blank" rel="noopener">صفحهٔ ویکیپدیا</a></p>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Model.RelationRows.Count > 0)
|
||||
{
|
||||
<div class="spacer"> </div>
|
||||
<div class="poem">
|
||||
<h2>خویشاوندان</h2>
|
||||
<ul>
|
||||
@foreach (var row in Model.RelationRows)
|
||||
{
|
||||
<li>
|
||||
@row.Label — <a href="/person/@row.OtherPersonId">@row.OtherPersonName</a>
|
||||
@if (!string.IsNullOrEmpty(row.Note))
|
||||
{
|
||||
<text> (@row.Note)</text>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.AffiliationRows.Count > 0)
|
||||
{
|
||||
<div class="spacer"> </div>
|
||||
<div class="poem">
|
||||
<h2>وابستگیهای غیرخویشاوندی</h2>
|
||||
<ul>
|
||||
@foreach (var row in Model.AffiliationRows)
|
||||
{
|
||||
<li>
|
||||
@row.SentenceBeforeOtherName<a href="/person/@row.OtherPersonId">@row.OtherPersonName</a>@row.SentenceAfterOtherName
|
||||
@if (!string.IsNullOrEmpty(row.Note))
|
||||
{
|
||||
<text> (@row.Note)</text>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.Poems != null && Model.Poems.Length > 0)
|
||||
{
|
||||
<div class="spacer"> </div>
|
||||
<div class="poem">
|
||||
<h2>اشعار برچسبخورده با این فرد</h2>
|
||||
<ul>
|
||||
@foreach (var tag in Model.Poems)
|
||||
{
|
||||
if (tag.Poem != null)
|
||||
{
|
||||
<li><a href="@tag.Poem.FullUrl">@tag.Poem.FullTitle</a></li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="spacer"> </div>
|
||||
<div class="poem">
|
||||
<p><small>هنوز شعری با این فرد برچسبگذاری نشده است.</small></p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="spacer"> </div>
|
||||
|
||||
</div>
|
||||
|
||||
<partial name="_FooterPartial" model=@(new _FooterPartialModel(){StickyEnabled = true}) />
|
||||
|
||||
</div>
|
||||
|
||||
@if (ViewData["TrackingScript"] != null && !string.IsNullOrEmpty(ViewData["TrackingScript"].ToString()))
|
||||
{
|
||||
@Html.Raw(ViewData["TrackingScript"])
|
||||
}
|
||||
}
|
||||
166
GanjooRazor/Pages/Person.cshtml.cs
Normal file
166
GanjooRazor/Pages/Person.cshtml.cs
Normal file
@ -0,0 +1,166 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using RMuseum.Models.Ganjoor;
|
||||
using RMuseum.Models.Ganjoor.ViewModels;
|
||||
|
||||
namespace GanjooRazor.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// read-only person/family-tree browsing page - shows one GanjoorRelatedPerson, their kinship
|
||||
/// and non-family ties (see GanjoorPersonRelation/GanjoorPersonAffiliation), and the poems
|
||||
/// tagged with them. Public counterpart of the Editor.cshtml suggestion form and
|
||||
/// ReviewEdits.cshtml moderation view for the same data, once it's approved and materialized.
|
||||
/// </summary>
|
||||
public class PersonModel : LoginPartialEnabledPageModel
|
||||
{
|
||||
public PersonModel(HttpClient httpClient, IConfiguration configuration) : base(httpClient, configuration)
|
||||
{
|
||||
}
|
||||
|
||||
public string LastError { get; set; }
|
||||
|
||||
public GanjoorRelatedPerson Person { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// one row per kinship edge, already resolved into display-ready text so the .cshtml
|
||||
/// doesn't need to know the directional meaning of PersonRelationType
|
||||
/// </summary>
|
||||
public List<PersonRelationDisplayRow> RelationRows { get; set; } = new List<PersonRelationDisplayRow>();
|
||||
|
||||
/// <summary>
|
||||
/// one row per non-family tie, same purpose as RelationRows
|
||||
/// </summary>
|
||||
public List<PersonAffiliationDisplayRow> AffiliationRows { get; set; } = new List<PersonAffiliationDisplayRow>();
|
||||
|
||||
public PoemGeoDateTag[] Poems { get; set; }
|
||||
|
||||
public class PersonRelationDisplayRow
|
||||
{
|
||||
/// <summary>
|
||||
/// the other person's role relative to the subject (e.g. "فرزند", "پدر/مادر", "نیا")
|
||||
/// </summary>
|
||||
public string Label { get; set; }
|
||||
public int OtherPersonId { get; set; }
|
||||
public string OtherPersonName { get; set; }
|
||||
public string Note { get; set; }
|
||||
}
|
||||
|
||||
public class PersonAffiliationDisplayRow
|
||||
{
|
||||
/// <summary>
|
||||
/// full Persian sentence with a "{0}" placeholder for where the other person's linked
|
||||
/// name goes (kept as a placeholder, rather than a pre-built string, so the .cshtml can
|
||||
/// still render the name as a link)
|
||||
/// </summary>
|
||||
public string SentenceBeforeOtherName { get; set; }
|
||||
public string SentenceAfterOtherName { get; set; }
|
||||
public int OtherPersonId { get; set; }
|
||||
public string OtherPersonName { get; set; }
|
||||
public string Note { get; set; }
|
||||
}
|
||||
|
||||
private static string RelationLabel(GanjoorPersonRelationInfo r)
|
||||
{
|
||||
switch (r.RelationType)
|
||||
{
|
||||
case PersonRelationType.Parent:
|
||||
return r.SubjectIsPerson1 ? "فرزند" : "پدر/مادر";
|
||||
case PersonRelationType.Sibling:
|
||||
return "خواهر/برادر";
|
||||
case PersonRelationType.Spouse:
|
||||
return "همسر";
|
||||
case PersonRelationType.Ancestor:
|
||||
var word = r.SubjectIsPerson1 ? "نواده" : "نیا";
|
||||
return r.DegreeHint != null ? $"{word} (فاصلهٔ {r.DegreeHint} نسل)" : word;
|
||||
default:
|
||||
return r.RelationType.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// Persian word for the subordinate/serving-role side of each directional affiliation type -
|
||||
// used to build a full sentence below, since (unlike family relations) there's no natural
|
||||
// short label for "the person this one served"
|
||||
private static readonly System.Collections.Generic.Dictionary<PersonAffiliationType, string> _affiliationRoleWords =
|
||||
new System.Collections.Generic.Dictionary<PersonAffiliationType, string>()
|
||||
{
|
||||
{ PersonAffiliationType.Minister, "وزیرِ" },
|
||||
{ PersonAffiliationType.Advisor, "مشاورِ" },
|
||||
{ PersonAffiliationType.Courtier, "درباریِ" },
|
||||
{ PersonAffiliationType.Patron, "حامیِ" },
|
||||
{ PersonAffiliationType.Ally, "متحدِ" },
|
||||
{ PersonAffiliationType.Rival, "رقیبِ" },
|
||||
{ PersonAffiliationType.Other, "دارای نسبتی (به یادداشت نگاه کنید) با" },
|
||||
};
|
||||
|
||||
private static bool IsSymmetricAffiliation(PersonAffiliationType t) =>
|
||||
t == PersonAffiliationType.Ally || t == PersonAffiliationType.Rival || t == PersonAffiliationType.Other;
|
||||
|
||||
public async Task<IActionResult> OnGetAsync(int id)
|
||||
{
|
||||
InitializeCommonPageState();
|
||||
|
||||
var personResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/people/{id}");
|
||||
if (!personResponse.IsSuccessStatusCode)
|
||||
{
|
||||
LastError = await ReadErrorMessageAsync(personResponse);
|
||||
return Page();
|
||||
}
|
||||
Person = JsonConvert.DeserializeObject<GanjoorRelatedPerson>(await personResponse.Content.ReadAsStringAsync());
|
||||
if (Person == null)
|
||||
{
|
||||
LastError = "فردی با این کد پیدا نشد.";
|
||||
return Page();
|
||||
}
|
||||
|
||||
ViewData["Title"] = $"گنجور » {Person.Name}";
|
||||
|
||||
var relationsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/people/{id}/relations");
|
||||
if (!relationsResponse.IsSuccessStatusCode)
|
||||
{
|
||||
LastError = await ReadErrorMessageAsync(relationsResponse);
|
||||
return Page();
|
||||
}
|
||||
var relationsData = JsonConvert.DeserializeObject<GanjoorPersonRelationsViewModel>(await relationsResponse.Content.ReadAsStringAsync());
|
||||
|
||||
foreach (var r in relationsData.Relations ?? new List<GanjoorPersonRelationInfo>())
|
||||
{
|
||||
RelationRows.Add(new PersonRelationDisplayRow()
|
||||
{
|
||||
Label = RelationLabel(r),
|
||||
OtherPersonId = r.OtherPersonId,
|
||||
OtherPersonName = r.OtherPersonName,
|
||||
Note = r.Note,
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var a in relationsData.Affiliations ?? new List<GanjoorPersonAffiliationInfo>())
|
||||
{
|
||||
string roleWord = _affiliationRoleWords.TryGetValue(a.AffiliationType, out var w) ? w : a.AffiliationType.ToString();
|
||||
bool subjectServes = a.SubjectIsPerson1 || IsSymmetricAffiliation(a.AffiliationType);
|
||||
AffiliationRows.Add(new PersonAffiliationDisplayRow()
|
||||
{
|
||||
SentenceBeforeOtherName = subjectServes ? $"این فرد {roleWord} " : "",
|
||||
SentenceAfterOtherName = subjectServes ? " بود" : $" {roleWord} این فرد بود",
|
||||
OtherPersonId = a.OtherPersonId,
|
||||
OtherPersonName = a.OtherPersonName,
|
||||
Note = a.Note,
|
||||
});
|
||||
}
|
||||
|
||||
var poemsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/people/{id}/poems");
|
||||
if (!poemsResponse.IsSuccessStatusCode)
|
||||
{
|
||||
LastError = await ReadErrorMessageAsync(poemsResponse);
|
||||
return Page();
|
||||
}
|
||||
Poems = JsonConvert.DeserializeObject<PoemGeoDateTag[]>(await poemsResponse.Content.ReadAsStringAsync());
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RMuseum.Models.Ganjoor;
|
||||
using RMuseum.Models.Ganjoor.ViewModels;
|
||||
using RMuseum.Services;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
@ -30,12 +31,30 @@ namespace RMuseum.Controllers
|
||||
return Ok(res.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get people who caption a family tree - the entry points for browsing family trees.
|
||||
/// Registered before the "{id}" route below (and constrained to int there) so this literal
|
||||
/// segment isn't swallowed as an id.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("familytrees")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorRelatedPerson[]))]
|
||||
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
|
||||
public async Task<IActionResult> GetFamilyTreeRootsAsync()
|
||||
{
|
||||
var res = await _personService.GetFamilyTreeRootsAsync();
|
||||
if (!string.IsNullOrEmpty(res.ExceptionString))
|
||||
return BadRequest(res.ExceptionString);
|
||||
return Ok(res.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get person by id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{id}")]
|
||||
[HttpGet("{id:int}")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorRelatedPerson))]
|
||||
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
|
||||
@ -47,6 +66,41 @@ namespace RMuseum.Controllers
|
||||
return Ok(res.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get a person along with all their kinship/affiliation edges, for the read-only
|
||||
/// person/family-tree browsing page
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{id:int}/relations")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPersonRelationsViewModel))]
|
||||
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
|
||||
public async Task<IActionResult> GetPersonRelationsAsync(int id)
|
||||
{
|
||||
var res = await _personService.GetPersonRelationsAsync(id);
|
||||
if (!string.IsNullOrEmpty(res.ExceptionString))
|
||||
return BadRequest(res.ExceptionString);
|
||||
return Ok(res.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get the poems tagged with this person
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{id:int}/poems")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemGeoDateTag[]))]
|
||||
[ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))]
|
||||
public async Task<IActionResult> GetPoemsByPersonAsync(int id)
|
||||
{
|
||||
var res = await _personService.GetPoemsByPersonAsync(id);
|
||||
if (!string.IsNullOrEmpty(res.ExceptionString))
|
||||
return BadRequest(res.ExceptionString);
|
||||
return Ok(res.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// person service
|
||||
/// </summary>
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RMuseum.Models.Ganjoor.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// a person plus their kinship/affiliation edges, resolved with the other side's name - the
|
||||
/// read-only counterpart of the pending-suggestion PersonGraphSuggestion (see
|
||||
/// GanjoorPoemGeoDateTagCorrection.SuggestedPersonGraphJson), served by GET api/people/{id}/relations
|
||||
/// </summary>
|
||||
public class GanjoorPersonRelationsViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// the subject person
|
||||
/// </summary>
|
||||
public GanjoorRelatedPerson Person { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// kinship edges touching Person (either side)
|
||||
/// </summary>
|
||||
public List<GanjoorPersonRelationInfo> Relations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// non-family ties touching Person (either side)
|
||||
/// </summary>
|
||||
public List<GanjoorPersonAffiliationInfo> Affiliations { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// one GanjoorPersonRelation row, resolved from Person's point of view
|
||||
/// </summary>
|
||||
public class GanjoorPersonRelationInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// the other person in this relation
|
||||
/// </summary>
|
||||
public int OtherPersonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the other person's name (denormalized here so the client doesn't need a second lookup)
|
||||
/// </summary>
|
||||
public string OtherPersonName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the kind of kinship edge
|
||||
/// </summary>
|
||||
public PersonRelationType RelationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// see GanjoorPersonRelation.DegreeHint
|
||||
/// </summary>
|
||||
public int? DegreeHint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// free-text note
|
||||
/// </summary>
|
||||
public string Note { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// true if the subject person is Person1 in the underlying GanjoorPersonRelation row -
|
||||
/// needed by the client to render directional types (Parent, Ancestor) correctly
|
||||
/// </summary>
|
||||
public bool SubjectIsPerson1 { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// one GanjoorPersonAffiliation row, resolved from Person's point of view - same shape/purpose
|
||||
/// as GanjoorPersonRelationInfo above, but for a non-family tie
|
||||
/// </summary>
|
||||
public class GanjoorPersonAffiliationInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// the other person in this tie
|
||||
/// </summary>
|
||||
public int OtherPersonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the other person's name
|
||||
/// </summary>
|
||||
public string OtherPersonName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the kind of non-family tie
|
||||
/// </summary>
|
||||
public PersonAffiliationType AffiliationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// free-text note
|
||||
/// </summary>
|
||||
public string Note { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// true if the subject person is Person1 in the underlying GanjoorPersonAffiliation row
|
||||
/// </summary>
|
||||
public bool SubjectIsPerson1 { get; set; }
|
||||
}
|
||||
}
|
||||
@ -2827,6 +2827,14 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Controllers.GanjoorRelatedPersonController.GetFamilyTreeRootsAsync">
|
||||
<summary>
|
||||
get people who caption a family tree - the entry points for browsing family trees.
|
||||
Registered before the "{id}" route below (and constrained to int there) so this literal
|
||||
segment isn't swallowed as an id.
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Controllers.GanjoorRelatedPersonController.GetPersonAsync(System.Int32)">
|
||||
<summary>
|
||||
get person by id
|
||||
@ -2834,6 +2842,21 @@
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Controllers.GanjoorRelatedPersonController.GetPersonRelationsAsync(System.Int32)">
|
||||
<summary>
|
||||
get a person along with all their kinship/affiliation edges, for the read-only
|
||||
person/family-tree browsing page
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Controllers.GanjoorRelatedPersonController.GetPoemsByPersonAsync(System.Int32)">
|
||||
<summary>
|
||||
get the poems tagged with this person
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="F:RMuseum.Controllers.GanjoorRelatedPersonController._personService">
|
||||
<summary>
|
||||
person service
|
||||
@ -12843,6 +12866,95 @@
|
||||
FullUrl
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationsViewModel">
|
||||
<summary>
|
||||
a person plus their kinship/affiliation edges, resolved with the other side's name - the
|
||||
read-only counterpart of the pending-suggestion PersonGraphSuggestion (see
|
||||
GanjoorPoemGeoDateTagCorrection.SuggestedPersonGraphJson), served by GET api/people/{id}/relations
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationsViewModel.Person">
|
||||
<summary>
|
||||
the subject person
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationsViewModel.Relations">
|
||||
<summary>
|
||||
kinship edges touching Person (either side)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationsViewModel.Affiliations">
|
||||
<summary>
|
||||
non-family ties touching Person (either side)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo">
|
||||
<summary>
|
||||
one GanjoorPersonRelation row, resolved from Person's point of view
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo.OtherPersonId">
|
||||
<summary>
|
||||
the other person in this relation
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo.OtherPersonName">
|
||||
<summary>
|
||||
the other person's name (denormalized here so the client doesn't need a second lookup)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo.RelationType">
|
||||
<summary>
|
||||
the kind of kinship edge
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo.DegreeHint">
|
||||
<summary>
|
||||
see GanjoorPersonRelation.DegreeHint
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo.Note">
|
||||
<summary>
|
||||
free-text note
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonRelationInfo.SubjectIsPerson1">
|
||||
<summary>
|
||||
true if the subject person is Person1 in the underlying GanjoorPersonRelation row -
|
||||
needed by the client to render directional types (Parent, Ancestor) correctly
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonAffiliationInfo">
|
||||
<summary>
|
||||
one GanjoorPersonAffiliation row, resolved from Person's point of view - same shape/purpose
|
||||
as GanjoorPersonRelationInfo above, but for a non-family tie
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonAffiliationInfo.OtherPersonId">
|
||||
<summary>
|
||||
the other person in this tie
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonAffiliationInfo.OtherPersonName">
|
||||
<summary>
|
||||
the other person's name
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonAffiliationInfo.AffiliationType">
|
||||
<summary>
|
||||
the kind of non-family tie
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonAffiliationInfo.Note">
|
||||
<summary>
|
||||
free-text note
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:RMuseum.Models.Ganjoor.ViewModels.GanjoorPersonAffiliationInfo.SubjectIsPerson1">
|
||||
<summary>
|
||||
true if the subject person is Person1 in the underlying GanjoorPersonAffiliation row
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:RMuseum.Models.Ganjoor.ViewModels.GanjoorPoemCompleteViewModel">
|
||||
<summary>
|
||||
a more complete GanjoorPoem View Model
|
||||
@ -17587,6 +17699,29 @@
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Services.IGanjoorRelatedPersonService.GetFamilyTreeRootsAsync">
|
||||
<summary>
|
||||
get people who caption a family tree (GanjoorRelatedPerson.FamilyTreeCaption not empty) -
|
||||
used as the entry points for browsing family trees
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Services.IGanjoorRelatedPersonService.GetPersonRelationsAsync(System.Int32)">
|
||||
<summary>
|
||||
get a person along with all their kinship/affiliation edges (resolved with the other
|
||||
side's name), for the read-only person/family-tree browsing page
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Services.IGanjoorRelatedPersonService.GetPoemsByPersonAsync(System.Int32)">
|
||||
<summary>
|
||||
get the (approved, materialized) poem geo/date tags that name this person, each carrying
|
||||
enough of its Poem to link to it
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:RMuseum.Services.IGanjoorService">
|
||||
<summary>
|
||||
Ganjoor Poems Content Privider Service
|
||||
@ -21036,6 +21171,26 @@
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Services.Implementation.GanjoorRelatedPersonService.GetFamilyTreeRootsAsync">
|
||||
<summary>
|
||||
get people who caption a family tree
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Services.Implementation.GanjoorRelatedPersonService.GetPersonRelationsAsync(System.Int32)">
|
||||
<summary>
|
||||
get a person along with all their kinship/affiliation edges
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:RMuseum.Services.Implementation.GanjoorRelatedPersonService.GetPoemsByPersonAsync(System.Int32)">
|
||||
<summary>
|
||||
get the (approved, materialized) poem geo/date tags that name this person
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="F:RMuseum.Services.Implementation.GanjoorRelatedPersonService._context">
|
||||
<summary>
|
||||
Database Context
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using RMuseum.Models.Ganjoor;
|
||||
using RMuseum.Models.Ganjoor.ViewModels;
|
||||
using RSecurityBackend.Models.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -23,5 +24,28 @@ namespace RMuseum.Services
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<RServiceResult<GanjoorRelatedPerson>> GetPersonAsync(int id);
|
||||
|
||||
/// <summary>
|
||||
/// get people who caption a family tree (GanjoorRelatedPerson.FamilyTreeCaption not empty) -
|
||||
/// used as the entry points for browsing family trees
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<RServiceResult<GanjoorRelatedPerson[]>> GetFamilyTreeRootsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// get a person along with all their kinship/affiliation edges (resolved with the other
|
||||
/// side's name), for the read-only person/family-tree browsing page
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<RServiceResult<GanjoorPersonRelationsViewModel>> GetPersonRelationsAsync(int id);
|
||||
|
||||
/// <summary>
|
||||
/// get the (approved, materialized) poem geo/date tags that name this person, each carrying
|
||||
/// enough of its Poem to link to it
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
Task<RServiceResult<PoemGeoDateTag[]>> GetPoemsByPersonAsync(int id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RMuseum.DbContext;
|
||||
using RMuseum.Models.Ganjoor;
|
||||
using RMuseum.Models.Ganjoor.ViewModels;
|
||||
using RSecurityBackend.Models.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -55,6 +57,121 @@ namespace RMuseum.Services.Implementation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get people who caption a family tree
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<RServiceResult<GanjoorRelatedPerson[]>> GetFamilyTreeRootsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new RServiceResult<GanjoorRelatedPerson[]>
|
||||
(
|
||||
await _context.GanjoorRelatedPersons
|
||||
.Where(p => !string.IsNullOrEmpty(p.FamilyTreeCaption))
|
||||
.OrderBy(p => p.FamilyTreeCaption)
|
||||
.ToArrayAsync()
|
||||
);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
return new RServiceResult<GanjoorRelatedPerson[]>(null, exp.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get a person along with all their kinship/affiliation edges
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<RServiceResult<GanjoorPersonRelationsViewModel>> GetPersonRelationsAsync(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var person = await _context.GanjoorRelatedPersons.Where(p => p.Id == id).SingleOrDefaultAsync();
|
||||
if (person == null)
|
||||
{
|
||||
return new RServiceResult<GanjoorPersonRelationsViewModel>(null, "فرد پیدا نشد.");
|
||||
}
|
||||
|
||||
var relationRows = await _context.GanjoorPersonRelations
|
||||
.Include(r => r.Person1)
|
||||
.Include(r => r.Person2)
|
||||
.Where(r => r.Person1Id == id || r.Person2Id == id)
|
||||
.ToListAsync();
|
||||
|
||||
var relations = relationRows.Select(r => new GanjoorPersonRelationInfo()
|
||||
{
|
||||
OtherPersonId = r.Person1Id == id ? r.Person2Id : r.Person1Id,
|
||||
OtherPersonName = r.Person1Id == id ? r.Person2.Name : r.Person1.Name,
|
||||
RelationType = r.RelationType,
|
||||
DegreeHint = r.DegreeHint,
|
||||
Note = r.Note,
|
||||
SubjectIsPerson1 = r.Person1Id == id,
|
||||
}).ToList();
|
||||
|
||||
var affiliationRows = await _context.GanjoorPersonAffiliations
|
||||
.Include(a => a.Person1)
|
||||
.Include(a => a.Person2)
|
||||
.Where(a => a.Person1Id == id || a.Person2Id == id)
|
||||
.ToListAsync();
|
||||
|
||||
var affiliations = affiliationRows.Select(a => new GanjoorPersonAffiliationInfo()
|
||||
{
|
||||
OtherPersonId = a.Person1Id == id ? a.Person2Id : a.Person1Id,
|
||||
OtherPersonName = a.Person1Id == id ? a.Person2.Name : a.Person1.Name,
|
||||
AffiliationType = a.AffiliationType,
|
||||
Note = a.Note,
|
||||
SubjectIsPerson1 = a.Person1Id == id,
|
||||
}).ToList();
|
||||
|
||||
return new RServiceResult<GanjoorPersonRelationsViewModel>(new GanjoorPersonRelationsViewModel()
|
||||
{
|
||||
Person = person,
|
||||
Relations = relations,
|
||||
Affiliations = affiliations,
|
||||
});
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
return new RServiceResult<GanjoorPersonRelationsViewModel>(null, exp.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get the (approved, materialized) poem geo/date tags that name this person
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<RServiceResult<PoemGeoDateTag[]>> GetPoemsByPersonAsync(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tags = await _context.PoemGeoDateTags
|
||||
.Include(t => t.Poem)
|
||||
.Where(t => t.PersonId == id && t.MachineGenerated == false)
|
||||
.OrderBy(t => t.Id)
|
||||
.ToArrayAsync();
|
||||
|
||||
// Poem is included only to get to FullUrl/FullTitle for a link - strip the heavy
|
||||
// text fields before this goes over the wire, same as GetCatPoemGeoDateTagsAsync does
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (tag.Poem != null)
|
||||
{
|
||||
tag.Poem.HtmlText = null;
|
||||
tag.Poem.PlainText = null;
|
||||
}
|
||||
}
|
||||
|
||||
return new RServiceResult<PoemGeoDateTag[]>(tags);
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
return new RServiceResult<PoemGeoDateTag[]>(null, exp.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Database Context
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user