Commit 6b56287f authored by Magnus Westergaard's avatar Magnus Westergaard
Browse files

DEICH-5484: Fix relation display names in the 'about' section of the publication page.

parent 48d26d53
......@@ -4,57 +4,89 @@ import Link from "next/link";
import guid from "../../../utilities/guid";
import { translations as TRANSLATIONS } from "../../../constants/translations";
import { fullTitle } from "../../../utilities/title";
import { fullName } from "../../../utilities/name";
import { pageLinkFromUri } from "../../../utilities/pageLink";
const relationDisplayName = (relation, relationType) => {
if (relationType.label !== "Relatert til") {
return fullTitle(relation);
const relationMaybeLinked = (relation, label) => {
const { linkData, contributors, literaryForms, type } = relation;
// fallback in case decoration of the relation has failed
if (!linkData) {
return <span>{fullTitle(relation)}</span>;
}
const {
targetPublicationId,
relatedViaPubPart,
publicationTitleInfo,
publicationPartTitleInfo,
workTitleInfo
} = linkData;
// no publication to link to
if (!targetPublicationId) {
const displayName = fullTitle(workTitleInfo);
if (type === "WorkSeries") {
const { href, as } = pageLinkFromUri(relation.uri, displayName);
return (
<Link href={href} as={as}>
{displayName}
</Link>
);
}
return <span>{displayName}</span>; // it's a work, nothing to link to
}
let displayName = fullTitle(relation);
const mainEntryContributor = relation.contributors.find(c =>
c.type.includes("MainEntry")
// linkable publication exists
let displayName = relatedViaPubPart
? fullTitle(publicationPartTitleInfo)
: fullTitle(publicationTitleInfo);
if (label !== "Relatert til") {
return (
<Link
href="/utgivelse/[publicationId]"
as={`/utgivelse/${targetPublicationId}`}
>
{displayName}
</Link>
);
if (mainEntryContributor) {
displayName = `${displayName} / ${mainEntryContributor.agent.name}`;
}
if (relation.literaryForms && relation.literaryForms.length > 0) {
const formattedLiteraryForms = relation.literaryForms
.map(lf => TRANSLATIONS[lf])
.sort((a, b) => a.localeCompare(b))
.join(", ");
displayName = `${displayName} (${formattedLiteraryForms})`;
}
return displayName;
};
}
const mainEntry = contributors.find(c => c.type.includes("MainEntry"));
if (mainEntry) {
displayName = `${displayName} / ${fullName(mainEntry.agent)}`;
}
if (literaryForms?.length > 0) {
const formattedLiteraryForms = literaryForms
.map(lf => TRANSLATIONS[lf])
.sort((a, b) => a.localeCompare(b))
.join(", ");
displayName = `${displayName} (${formattedLiteraryForms})`;
}
if (relatedViaPubPart) {
displayName = `${displayName}. I: ${fullTitle(publicationTitleInfo)}`;
}
return (
<Link
href="/utgivelse/[publicationId]"
as={`/utgivelse/${targetPublicationId}`}
>
{displayName}
</Link>
);
};
function RelationList({ dataType, data }) {
const { label } = dataType;
return (
<ul>
{data.map(item => {
let linkHref;
let linkAs;
if (item.publicationId) {
linkHref = "/utgivelse/[publicationId]";
linkAs = `/utgivelse/${item.publicationId}`
}
if (!item.publicationId && item.type === "WorkSeries") {
linkHref = "/sok/[searchQuery]";
linkAs = `/sok/series: "${item.mainTitle}"`
}
{data.map(relation => {
return (
<li className="meta-value__item" key={guid()}>
{linkHref &&
<Link
href={linkHref}
as={linkAs}
>
<a>{relationDisplayName(item, dataType)}</a>
</Link>
}
{!linkHref &&
<span>{relationDisplayName(item, dataType)}</span>
}
{relationMaybeLinked(relation, label)}
</li>
);
})}
......
......@@ -5,6 +5,7 @@ const {
frameJsonLD,
addItemsToPublications
} = require("../utils/resourceHelpers");
const { getId } = require("../utils/uriParser");
const { translations } = require("../../constants/translations");
const { timeout } = require("../utils/apiUtils");
const { publicationsSort } = require("../utils/sortWorkSeriesItems");
......@@ -21,29 +22,34 @@ const publicationLinkScore = binding => {
let score = 0;
if (
binding.mediaType &&
binding.targetMediaType &&
binding.mediaType === binding.targetMediaType
binding.linkTargetMediaType &&
binding.mediaType === binding.linkTargetMediaType
) {
score++;
}
if (
binding.format &&
binding.targetFormat &&
binding.format === binding.targetFormat
binding.linkTargetFormat &&
binding.format === binding.linkTargetFormat
) {
score++;
}
if (
binding.lang &&
binding.targetLang &&
binding.lang === binding.targetLang
binding.language &&
binding.linkTargetLanguage &&
binding.language === binding.linkTargetLanguage
) {
score += 3;
}
return score;
};
async function decorateRelation(key, publicationId, relation, deichmanCallId) {
async function decorateRelation(
relationType,
publicationId,
relation,
deichmanCallId
) {
const url = `${INTERNAL_URL_EULER}/api/authorities/decorateRelation?id=${publicationId}&relation=${encodeURIComponent(
relation.uri
)}`;
......@@ -59,69 +65,92 @@ async function decorateRelation(key, publicationId, relation, deichmanCallId) {
// TODO else Promise.resolve(undefined) or Promise.reject
})
.then((relations = []) => {
let selected = false;
let candidates = relations;
if (relations.length === 0) {
return { relationType, relation };
}
// we prefer linking to publications that are not related via publication parts,
// so if we have any direct relations, filter out the rest before selecting one
if (relations.some(rel => !rel.relatedViaPublicationPart)) {
candidates = relations.filter(rel => !rel.relatedViaPublicationPart);
let candidates = relations;
if (relations.some(rel => !rel.relatedViaPubPart)) {
candidates = relations.filter(rel => !rel.relatedViaPubPart);
}
candidates.forEach(binding => {
const score = publicationLinkScore(binding);
if (!selected || score > selected.score) {
if (binding.targetPub) {
selected = {
score,
publicationId: binding.targetPub.replace(
"http://data.deichman.no/publication/",
""
),
mainTitle: binding.targetMainTitle
};
if (binding.targetSubtitle) {
selected.subtitle = binding.targetSubtitle;
}
if (binding.targetPartNumber) {
selected.partNumber = binding.targetPartNumber;
}
if (binding.targetPartTitle) {
selected.partTitle = binding.targetPartTitle;
}
}
// select a publication to link to (if any exist)
let selected = null;
candidates.forEach(c => {
const score = publicationLinkScore(c);
if ((!selected || score > selected.score) && c.linkTarget) {
selected = {
score,
candidate: c
};
}
});
relation.publicationId = selected.publicationId;
relation.mainTitle = selected.mainTitle;
relation.subtitle = selected.subtitle;
relation.partNumber = selected.partNumber;
relation.partTitle = selected.partTitle;
if (!selected && candidates.length > 0 && candidates[0].workMainTitle) {
// Related work has no publications yet, so we use the title from the work.
relation.mainTitle = candidates[0].workMainTitle;
// no linkable publications found, fall back to using work data
if (!selected) {
const aCandidate = candidates[0]; // all candidates are equal wrt work data
const decoratedRelation = {
...relation,
linkData: {
workTitleInfo: {
mainTitle: aCandidate.workMainTitle,
subtitle: aCandidate.workSubtitle,
partNumber: aCandidate.workPartNumber,
partTitle: aCandidate.workPartTitle
}
}
};
return { relationType, decoratedRelation };
}
return { key, relation };
// a linkable publication exists, so extract the title data
const { candidate: c } = selected;
const linkData = {
targetPublicationId: getId(c.linkTarget),
relatedViaPubPart: c.relatedViaPubPart,
publicationTitleInfo: {
mainTitle: c.linkTargetMainTitle,
subtitle: c.linkTargetSubtitle,
partNumber: c.linkTargetPartNumber,
partTitle: c.linkTargetPartTitle
},
publicationPartTitleInfo: {
mainTitle: c.pubPartMainTitle,
subtitle: c.pubPartSubTitle
}
};
const decoratedRelation = {
...relation,
linkData
};
return { relationType, decoratedRelation };
});
}
async function addWorkLinks(work, publicationId, deichmanCallId) {
const promises = [];
Object.keys(work.workRelations).forEach(key => {
work.workRelations[key].forEach(relation => {
Object.keys(work.workRelations).forEach(relationType => {
work.workRelations[relationType].forEach(relation => {
promises.push(
decorateRelation(key, publicationId, relation, deichmanCallId)
decorateRelation(relationType, publicationId, relation, deichmanCallId)
);
});
});
// TODO filter out relations without mainTitle = bad data and nothing to display
const relations = await Promise.all(promises);
const decoratedRelations = await Promise.all(promises);
work.workRelations = {};
relations.forEach(res => {
if (!work.workRelations[res.key]) {
work.workRelations[res.key] = [];
decoratedRelations.forEach(rel => {
if (!work.workRelations[rel.relationType]) {
work.workRelations[rel.relationType] = [];
}
work.workRelations[res.key].push(res.relation);
work.workRelations[rel.relationType].push(rel.decoratedRelation);
});
return work;
}
......
PREFIX : <http://data.deichman.no/ontology#>
SELECT ?mediaType ?lang ?format ?targetPub ?targetMediaType ?targetLang ?targetFormat ?targetMainTitle ?targetSubtitle ?targetPartNumber ?targetPartTitle ?workMainTitle ?relatedViaPublicationPart
SELECT *
FROM <https://katalog.deichman.no>
WHERE {
OPTIONAL { <http://data.deichman.no/publication/__PUBLICATION_ID__> :hasMediaType ?mediaType }
OPTIONAL { <http://data.deichman.no/publication/__PUBLICATION_ID__> :language ?lang }
<http://data.deichman.no/publication/__PUBLICATION_ID__> :hasMediaType ?mediaType .
OPTIONAL { <http://data.deichman.no/publication/__PUBLICATION_ID__> :language ?language }
OPTIONAL { <http://data.deichman.no/publication/__PUBLICATION_ID__> :format ?format }
# case 0: the related work does not have a publication (or it's a workseries)
<__RELATION_URI__> :mainTitle ?workMainTitle .
OPTIONAL { <__RELATION_URI__> :subtitle ?workSubtitle }
OPTIONAL { <__RELATION_URI__> :partNumber ?workPartNumber }
OPTIONAL { <__RELATION_URI__> :partTitle ?workPartTitle }
OPTIONAL {
?targetPub a :Publication ;
:publicationOf|:hasPublicationPart/:publicationOf <__RELATION_URI__> ;
:mainTitle ?targetMainTitle .
OPTIONAL { ?targetPub :subtitle ?targetSubtitle }
OPTIONAL { ?targetPub :partNumber ?targetPartNumber }
OPTIONAL { ?targetPub :partTitle ?targetPartTitle }
OPTIONAL { ?targetPub :hasMediaType ?targetMediaType }
OPTIONAL { ?targetPub :language ?targetLang }
OPTIONAL { ?targetPub :format ?targetFormat }
FILTER NOT EXISTS {
?targetPub :deleted ?deleted .
{
# case 1: the related work is directly linked to a publication
?linkTarget a :Publication ;
:publicationOf <__RELATION_URI__> ;
:mainTitle ?linkTargetMainTitle ;
:hasMediaType ?linkTargetMediaType .
OPTIONAL { ?linkTarget :subtitle ?linkTargetSubtitle }
OPTIONAL { ?linkTarget :partNumber ?linkTargetPartNumber }
OPTIONAL { ?linkTarget :partTitle ?linkTargetPartTitle }
OPTIONAL { ?linkTarget :language ?linkTargetLanguage }
OPTIONAL { ?linkTarget :format ?linkTargetFormat }
FILTER NOT EXISTS {
?linkTarget :deleted ?deleted .
}
BIND("" as ?relatedViaPubPart)
} UNION {
# case 2: the related work is linked to a publication part, which in turn is linked to a publication
?linkTarget a :Publication ;
:hasPublicationPart ?pubPart ;
:mainTitle ?linkTargetMainTitle ;
:hasMediaType ?linkTargetMediaType .
?pubPart :publicationOf <__RELATION_URI__> ;
:mainTitle ?pubPartMainTitle .
OPTIONAL { ?linkTarget :subtitle ?linkTargetSubtitle }
OPTIONAL { ?linkTarget :partNumber ?linkTargetPartNumber }
OPTIONAL { ?linkTarget :partTitle ?linkTargetPartTitle }
OPTIONAL { ?linkTarget :language ?linkTargetLanguage }
OPTIONAL { ?linkTarget :format ?linkTargetFormat }
OPTIONAL { ?pubPart :subtitle ?pubPartSubtitle }
FILTER NOT EXISTS {
?linkTarget :deleted ?deleted .
}
BIND("1" as ?relatedViaPubPart)
}
# we need to distinguish if the connection between publication and work is direct or via a publication part
BIND(IF(EXISTS { ?targetPub :publicationOf <__RELATION_URI__> }, "", "1") AS ?relatedViaPublicationPart )
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment