Skip to content

Commit

Permalink
feat(networks): add nodes info from publications
Browse files Browse the repository at this point in the history
  • Loading branch information
ahonestla committed Oct 3, 2024
1 parent a8d6208 commit 91104fd
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 17 deletions.
63 changes: 62 additions & 1 deletion client/src/api/networks/network/communities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { openAiLabeledClusters } from "./mistralai"
import { COLORS } from "../_utils/constants"
import { GetColorName } from "hex-color-to-color-name"
import { configGetItemUrl } from "./config"
import { CONFIG } from "./config"
import { nodeGetId } from "./network"

const CURRENT_YEAR = new Date().getFullYear()
const RECENT_YEARS = [CURRENT_YEAR - 1, CURRENT_YEAR]

const communityGetAttribute = (graph: Graph, community: number, name: string): Array<string> | Array<number> =>
graph.reduceNodes(
Expand Down Expand Up @@ -51,6 +56,18 @@ const communityGetCitationsByYear = (aggs: ElasticAggregations): Record<string,
.filter(([key]) => key.startsWith("citationsIn"))
.reduce((acc, [key, value]) => ({ ...acc, [key.slice(-4)]: value?.value }), {})

const communityGetCitationsCount = (aggs: ElasticAggregations): number =>
Object.values(communityGetCitationsByYear(aggs)).reduce((acc, value) => acc + value, 0)

const communityGetCitationsRecent = (aggs: ElasticAggregations): number =>
Object.entries(communityGetCitationsByYear(aggs)).reduce(
(acc, [key, value]) => (RECENT_YEARS.includes(Number(key)) ? acc + value : acc),
0
)

const communityGetCitationsScore = (aggs: ElasticAggregations): number =>
communityGetCitationsRecent(aggs) / communityGetPublicationsCount(aggs)

const communityGetDomains = (aggs: ElasticAggregations): Record<string, number> =>
aggs?.domains?.buckets.reduce((acc, bucket) => ({ ...acc, [labelClean(bucket.key)]: bucket.doc_count }), {})

Expand All @@ -66,6 +83,27 @@ const communityGetPublications = (hits: ElasticHits): Array<Record<string, strin
title: hit.title.default,
}))

const communityGetNodesInfos = (hits: ElasticHits, model: string): any =>
hits.reduce((acc, hit) => {
const field = CONFIG[model].field.split(".")[0]
const citationsByYear = hit?.["counts_by_year"]?.reduce(
(acc, citations) => ({
...acc,
[citations.year]: citations.cited_by_count,
}),
{}
)
hit?.[field].forEach((node) => {
const id = nodeGetId(node[CONFIG[model].field.split(".")[1]])
acc[id] = {
...acc?.[id],
publicationsCount: acc?.[id]?.publicationsCount ? acc[id].publicationsCount + 1 : 1,
citationsByYear: citationsByYear,
}
})
return acc
}, {})

export default async function communitiesCreate(graph: Graph, computeClusters: boolean): Promise<NetworkCommunities> {
const query: string = graph.getAttribute("query")
const model: string = graph.getAttribute("model")
Expand All @@ -88,6 +126,26 @@ export default async function communitiesCreate(graph: Graph, computeClusters: b
const hits = await networkSearchHits({ model, query, filters, links: communityGetLinks(graph, index) })
const aggs = await networkSearchAggs({ model, query, filters, links: communityGetLinks(graph, index) })

if (hits) {
const nodesInfos = communityGetNodesInfos(hits, model)
graph.forEachNode((key) => {
if (!Object.keys(nodesInfos).includes(key)) return
const nodeInfos = nodesInfos[key]
const publicationsCount = nodeInfos.publicationsCount
const citationsCount = nodeInfos?.citationsByYear
? Object.values(nodeInfos?.citationsByYear).reduce((acc: number, value: number) => acc + value, 0)
: 0
const citationsRecent = nodeInfos?.citationsByYear
? (nodeInfos.citationsByYear?.[CURRENT_YEAR - 1] || 0) + (nodeInfos.citationsByYear?.[CURRENT_YEAR] || 0)
: 0
const citationsScore = citationsRecent / (publicationsCount || 1)
graph.setNodeAttribute(key, "publicationsCount", publicationsCount)
graph.setNodeAttribute(key, "citationsCount", citationsCount)
graph.setNodeAttribute(key, "citationsRecent", citationsRecent)
graph.setNodeAttribute(key, "citationsScore", citationsScore)
})
}

const community = {
cluster: index + 1,
label: COLORS?.[index] ? GetColorName(COLORS[index]) : `Unnamed ${index + 1}`,
Expand All @@ -96,9 +154,12 @@ export default async function communitiesCreate(graph: Graph, computeClusters: b
nodes: communityGetNodes(graph, index),
maxYear: communityGetMaxYear(graph, index),
...(aggs && {
publicationsCount: communityGetPublicationsCount(aggs),
publicationsByYear: communityGetPublicationsByYear(aggs),
publicationsCount: communityGetPublicationsCount(aggs),
citationsByYear: communityGetCitationsByYear(aggs),
citationsCount: communityGetCitationsCount(aggs),
citationsRecent: communityGetCitationsRecent(aggs),
citationsScore: communityGetCitationsScore(aggs),
domains: communityGetDomains(aggs),
oaPercent: communityGetOaPercent(aggs),
}),
Expand Down
17 changes: 16 additions & 1 deletion client/src/api/networks/network/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { NetworkConfig } from "../../../types/network"
import { COLORS } from "../_utils/constants"

const CONFIG = {
export const CONFIG = {
authors: {
field: "authors.id_name",
aggregation: "authors.id_name.keyword",
co_aggregation: "co_authors.keyword",
url: (key: string) => `/authors/${key}`,
terminology: {
item: "author",
Expand All @@ -16,6 +19,9 @@ const CONFIG = {
},
},
institutions: {
field: "affiliations.id_name",
aggregation: "affiliations.id_name.keyword",
co_aggregation: "co_institutions.keyword",
url: (key: string) => `/organizations/${key}`,
terminology: {
item: "institution",
Expand All @@ -29,6 +35,9 @@ const CONFIG = {
},
},
structures: {
field: "affiliations.id_name",
aggregation: "affiliations.id_name.keyword",
co_aggregation: "co_structures.keyword",
url: (key: string) => `/organizations/${key}`,
terminology: {
item: "structure",
Expand All @@ -42,6 +51,9 @@ const CONFIG = {
},
},
domains: {
field: "domains.id_name",
aggregation: "domains.id_name.keyword",
co_aggregation: "co_domains.keyword",
url: (_: string, label: string) => `/search/publications?q="${label.replace(/ /g, "+")}"`,
terminology: {
item: "domain",
Expand All @@ -55,6 +67,9 @@ const CONFIG = {
},
},
software: {
field: "software.id_name",
aggregation: "software.id_name.keyword",
co_aggregation: "co_software.keyword",
url: (_: string, label: string) => `/search/publications?q="${label.replace(/ /g, "+")}"`,
terminology: {
item: "software",
Expand Down
8 changes: 6 additions & 2 deletions client/src/api/networks/network/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const GRAPH_MAX_ORDER = 300
export const GRAPH_MAX_COMPONENTS = 5

const nodeConcatMaxYear = (nodeMaxYear: number, maxYear: number) => (nodeMaxYear ? Math.max(nodeMaxYear, maxYear) : maxYear)
const nodeGetId = (id: string) => {
export const nodeGetId = (id: string) => {
const nodeId = id.split("###")[0]
return isNaN(+nodeId) ? nodeId : String(+nodeId)
}
Expand Down Expand Up @@ -82,10 +82,14 @@ export default async function networkCreate(
x: attr.x,
y: attr.y,
label: attr.label,
cluster: attr.community + 1,
cluster: attr?.community + 1,
weights: { Weight: attr.weight, Degree: graph.degree(key) },
scores: { "Last activity": attr?.maxYear },
page: configGetItemUrl(model, key, attr.label),
...(attr?.publicationsCount !== undefined && { publicationsCount: attr?.publicationsCount }),
...(attr?.citationsCount !== undefined && { citationsCount: attr?.citationsCount }),
...(attr?.citationsRecent !== undefined && { citationsRecent: attr?.citationsRecent }),
...(attr?.citationsScore !== undefined && { citationsScore: attr?.citationsScore }),
})),
links: graph.mapEdges((_, attr, source, target) => ({
source_id: source,
Expand Down
5 changes: 3 additions & 2 deletions client/src/api/networks/search/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
NetworkSearchHitsArgs,
ElasticAggregations,
} from "../../../types/network"
import { CONFIG } from "../network/config"
import networkCreate from "../network/network"
import configCreate from "../network/config"
import infoCreate from "../network/info"
Expand Down Expand Up @@ -34,7 +35,7 @@ const networkSearchBody = (model: string, query?: string | unknown): NetworkSear
},
aggs: {
[model]: {
terms: { field: `co_${model}.keyword`, size: DEFAULT_SIZE },
terms: { field: CONFIG[model].co_aggregation, size: DEFAULT_SIZE },
aggs: { max_year: { max: { field: "year" } } },
},
},
Expand Down Expand Up @@ -84,7 +85,7 @@ export async function networkSearchHits({ model, query, filters, links }: Networ
const linksFilter = { terms: { [`co_${model}.keyword`]: links } }
const body = {
size: DEFAULT_SIZE,
_source: HIT_FIELDS,
_source: [...HIT_FIELDS, CONFIG[model].field],
query: {
bool: {
must: [
Expand Down
9 changes: 3 additions & 6 deletions client/src/pages/networks/components/clusters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,9 @@ type ClustersSectionArgs = {

function ClusterItem({ currentTab, community, setFocusItem }: ClusterItemArgs) {
const intl = useIntl()
const currentYear = new Date().getFullYear()
const [showNodesModal, setShowNodesModal] = useState(false)
const [showPublicationsModal, setShowPublicationsModal] = useState(false)
const currentYear = new Date().getFullYear()
const citationScore = (community: NetworkCommunity) =>
(community?.citationsByYear?.[currentYear] + community?.citationsByYear?.[currentYear - 1]) /
(community?.publicationsCount || 1)
const oaColor = (percent: number) => (percent >= 40.0 ? (percent >= 70.0 ? "success" : "yellow-moutarde") : "warning")

return (
Expand Down Expand Up @@ -78,9 +75,9 @@ function ClusterItem({ currentTab, community, setFocusItem }: ClusterItemArgs) {
</Badge>
<Badge size="sm" color="blue-cumulus">{`${intl.formatMessage(
{ id: "networks.section.clusters.citations" },
{ count: community.citationsByYear?.[currentYear] + community.citationsByYear?.[currentYear - 1] }
{ count: community.citationsRecent }
)} (${currentYear - 1}-${currentYear})`}</Badge>
<Badge size="sm" color="blue-ecume">{`Citation score: ${citationScore(community).toFixed(1)}`}</Badge>
<Badge size="sm" color="blue-ecume">{`Citation score: ${community.citationsScore.toFixed(1)}`}</Badge>
</BadgeGroup>
</Col>
</Row>
Expand Down
18 changes: 14 additions & 4 deletions client/src/pages/networks/hooks/useExportData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const XSLXFormatter = (network: any) => {

const publicationsList = network.clusters?.reduce((acc, cluster) => {
cluster?.publications.forEach((publication) => {
acc = [...acc, { id: publication.id, title: publication.title, cluster: cluster.cluster }]
acc = [...acc, { id: publication.id, title: publication.title, cluster: cluster.cluster, clusterLabel: cluster.label }]
})
return acc
}, [])
Expand All @@ -43,15 +43,25 @@ const exportNetwork = (network: NetworkData) => ({
id: item.id,
label: item.label || "",
cluster: item.cluster,
degree: item?.weights?.Degree || null,
weight: item?.weights?.Weight || null,
...(network.clusters.length && {
clusterLabel: network.clusters.find((cluster) => cluster.cluster === item.cluster).label,
}),
publicationsCount: item?.publicationsCount,
citationsCount: item?.citationsCount,
citationsRecent: item?.citationsRecent,
citationsScore: item?.citationsScore,
degree: item?.weights?.Degree,
weight: item?.weights?.Weight,
})),
links: network.links,
clusters: network.clusters.map((cluster) => ({
id: cluster.cluster,
label: cluster.label,
cluster: cluster.cluster,
nodesCount: cluster.nodes.length,
publicationsCount: cluster?.publicationsCount,
citationsCount: cluster?.citationsCount,
citationsRecent: cluster?.citationsRecent,
citationsScore: cluster?.citationsScore,
publications: cluster?.publications,
})),
})
Expand Down
9 changes: 8 additions & 1 deletion client/src/types/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export type NetworkItem = {
weights: Record<string, number>
scores: Record<string, number>
page?: string
publicationsCount?: number
citationsCount?: number
citationsRecent?: number
citationsScore?: number
}
export type NetworkLinks = Array<NetworkLink>
export type NetworkLink = {
Expand All @@ -40,10 +44,13 @@ export type NetworkCommunity = {
url?: string
}>
maxYear?: number
publicationsByYear?: Record<string, number>
publicationsCount?: number
publications?: Array<Record<string, string>>
publicationsByYear?: Record<string, number>
citationsByYear?: Record<string, number>
citationsCount?: number
citationsRecent?: number
citationsScore?: number
domains?: Record<string, number>
oaPercent?: number
}
Expand Down

0 comments on commit 91104fd

Please sign in to comment.