replication-radar 0.4.2__tar.gz → 0.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {replication_radar-0.4.2 → replication_radar-0.4.3}/PKG-INFO +2 -2
- {replication_radar-0.4.2 → replication_radar-0.4.3}/README.md +1 -1
- {replication_radar-0.4.2 → replication_radar-0.4.3}/pyproject.toml +1 -1
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/methodology.json +9 -4
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/__init__.py +1 -1
- replication_radar-0.4.3/src/replication_radar/github.py +98 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/radar.py +40 -5
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/server.py +6 -3
- {replication_radar-0.4.2 → replication_radar-0.4.3}/.github/ISSUE_TEMPLATE/join-the-team.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/.github/workflows/publish-pypi.yml +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/.gitignore +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/CITATION.cff +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/CLAUDE.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/CODE_OF_CONDUCT.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/CONTRIBUTING.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/DEMO.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/LICENSE +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/STORY.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/demo_sdm.py +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/docs/app-ui.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/docs/architecture.mmd +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/docs/link-types.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/docs/next-layers-plan.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/docs/openaire-mcp.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/docs/readiness-scoring-plan.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/netlify/functions/gh.mjs +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/netlify.toml +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/scripts/build-team-index.mjs +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/scripts/build_verdicts.py +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/README.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/app.js +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/bios/Saranjeet-Kaur-Bhogal.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/bios/anne-fouilloux.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/bios/index.json +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/bios/jean-iaquinta.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/curated.json +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/index.html +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/methodology.html +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/replicate.html +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/replicate.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/story.html +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/story.md +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/style.css +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/team.html +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/team.json +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/site/verdicts.json +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/data/verdicts.json +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/network.py +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/openaire.py +0 -0
- {replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/verdicts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: replication-radar
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: MCP server that turns the OpenAIRE Graph into a ranked replication queue — impact-ranked targets and the Science Live verification overlay (retraction/supersession-aware).
|
|
5
5
|
Project-URL: Homepage, https://github.com/ScienceLiveHub/replication-radar
|
|
6
6
|
Project-URL: Repository, https://github.com/ScienceLiveHub/replication-radar
|
|
@@ -54,7 +54,7 @@ The Radar joins three sources to add a **replication layer** on top:
|
|
|
54
54
|
| Tool | What it answers |
|
|
55
55
|
|---|---|
|
|
56
56
|
| `radar(topic)` | Impact-ranked replication targets in a field — each **OPEN** (opportunity) or **VERIFIED** (done, with verdict) + independent tooling + funder context |
|
|
57
|
-
| `find_independent_software(doi, topic)` | Reusable engines **not authored by the original team** (author-disjoint = *replication*, not *reproduction*), ranked by reuse signal not citations |
|
|
57
|
+
| `find_independent_software(doi, topic)` | Reusable engines **not authored by the original team** (author-disjoint = *replication*, not *reproduction*), ranked by reuse signal — repo · Software Heritage · downloads · **GitHub stars** — not citations (returns `stars` + `rank_score`) |
|
|
58
58
|
| `replication_status(doi)` | Has this DOI been replicated, did it hold? Verdict(s) — **live from the nanopub network, any signer** — with status, CiTO relation, repo, and signed Outcome/CiTO nanopub links; `open` if not |
|
|
59
59
|
| `verified_claims()` | The whole **verified-knowledge corpus** — every claim the network holds a verdict for (author-agnostic) |
|
|
60
60
|
|
|
@@ -31,7 +31,7 @@ The Radar joins three sources to add a **replication layer** on top:
|
|
|
31
31
|
| Tool | What it answers |
|
|
32
32
|
|---|---|
|
|
33
33
|
| `radar(topic)` | Impact-ranked replication targets in a field — each **OPEN** (opportunity) or **VERIFIED** (done, with verdict) + independent tooling + funder context |
|
|
34
|
-
| `find_independent_software(doi, topic)` | Reusable engines **not authored by the original team** (author-disjoint = *replication*, not *reproduction*), ranked by reuse signal not citations |
|
|
34
|
+
| `find_independent_software(doi, topic)` | Reusable engines **not authored by the original team** (author-disjoint = *replication*, not *reproduction*), ranked by reuse signal — repo · Software Heritage · downloads · **GitHub stars** — not citations (returns `stars` + `rank_score`) |
|
|
35
35
|
| `replication_status(doi)` | Has this DOI been replicated, did it hold? Verdict(s) — **live from the nanopub network, any signer** — with status, CiTO relation, repo, and signed Outcome/CiTO nanopub links; `open` if not |
|
|
36
36
|
| `verified_claims()` | The whole **verified-knowledge corpus** — every claim the network holds a verdict for (author-agnostic) |
|
|
37
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "replication-radar"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.3"
|
|
4
4
|
description = "MCP server that turns the OpenAIRE Graph into a ranked replication queue — impact-ranked targets and the Science Live verification overlay (retraction/supersession-aware)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title": "Replication Radar — methodology & data provenance",
|
|
3
3
|
"summary": "Replication Radar joins three public, grounded sources into one view of a research field: the OpenAIRE Graph (what exists and how cited it is), the Science Live nanopublication network (whether a claim has been independently replicated and with what verdict), and FAIR-software signals (whether the replication's code is reusable). This document states, for every label and score in the interface, exactly where it comes from and how it is computed. It is served as JSON (this file) and rendered as a page at /methodology.html.",
|
|
4
|
-
"version": "0.3
|
|
5
|
-
"updated": "2026-
|
|
4
|
+
"version": "0.4.3",
|
|
5
|
+
"updated": "2026-08-13",
|
|
6
6
|
"license": "CC-BY-4.0",
|
|
7
7
|
"sources": [
|
|
8
8
|
{ "id": "openaire", "name": "OpenAIRE Graph", "url": "https://graph.openaire.eu/", "api": "https://api.openaire.eu/graph/v1", "role": "Research products (papers, software, datasets), citation-impact indicators, open-access status, subject classification (FOS, SDG), and code/data links. Queried live from the browser; no key required." },
|
|
9
9
|
{ "id": "bip", "name": "BIP! citation indicators (via OpenAIRE)", "url": "https://bip.imsi.athenarc.gr/", "role": "The C1–C5 impact and impulse classes are BIP! indicators carried in the OpenAIRE record." },
|
|
10
10
|
{ "id": "sciencelive", "name": "Science Live nanopublication network", "url": "https://sciencelive4all.org", "api": "https://query.knowledgepixels.com/repo/full", "role": "FORRT replication chains as signed nanopublications: the Outcome (verdict), the Study (method), the Claim (what was tested) and CiTO citations to the original paper. Queried live via SPARQL; author-agnostic (any signer)." },
|
|
11
11
|
{ "id": "fairsoftware", "name": "fair-software.eu recommendations", "url": "https://fair-software.eu/", "role": "The five FAIR-for-research-software recommendations we score a replication's code against." },
|
|
12
|
-
{ "id": "github", "name": "GitHub REST API", "url": "https://docs.github.com/rest", "role": "Repository existence, licence, citation file, release/quality signals,
|
|
12
|
+
{ "id": "github", "name": "GitHub REST API", "url": "https://docs.github.com/rest", "role": "Repository existence, licence, citation file, release/quality signals, and stars/forks — used both for the FAIR-software check and as the GitHub-star reuse signal in the reusable-software ranking (unauthenticated, 60 requests/hour; set GITHUB_TOKEN to raise the limit)." },
|
|
13
13
|
{ "id": "swh", "name": "Software Heritage API", "url": "https://archive.softwareheritage.org/api/", "role": "Whether the replication's code is archived in Software Heritage (long-term preservation), for the FAIR-software check." },
|
|
14
14
|
{ "id": "zenodo", "name": "Zenodo API", "url": "https://developers.zenodo.org/", "role": "Resolving a Zenodo deposit to its source GitHub repository and DOI when OpenAIRE lacks a direct code link." },
|
|
15
15
|
{ "id": "turingway", "name": "The Turing Way", "url": "https://the-turing-way.netlify.app/", "role": "A community handbook for reproducible, ethical and collaborative research — the language-agnostic reference behind the 'RSE practices' explanations (documentation, testing, continuous integration)." },
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"means": "Whether independent replications agree. Many-agree differs from disagree.", "computed": "From the verdict counts across all of a paper's outcomes — see the 'agreement' score below.", "limits": "Depends on how many replications exist; one confirmation is weaker evidence than several." },
|
|
42
42
|
{ "id": "fair", "name": "FAIR-software score (N/5)", "shownAs": "FAIR software N/5 + per-recommendation checks", "source": "fairsoftware",
|
|
43
43
|
"means": "How reusable the replication's code is, on the five fair-software.eu recommendations: public repository, open licence, in a registry, citable (CITATION.cff / DOI), and quality artefacts.", "computed": "Computed live from the GitHub API (repository, licence, citation file, quality), Software Heritage (archival/registry) and Zenodo (DOI). One point per met recommendation. Only ever run on a replication we have a real repository URL for.", "limits": "GitHub unauthenticated rate limit is 60/hour; a check can be skipped if the limit is hit." },
|
|
44
|
+
{ "id": "reusablesoftware", "name": "Reusable software for a field", "shownAs": "the reusable-software list (and the MCP find_independent_software tool)", "source": "openaire",
|
|
45
|
+
"means": "Software an RSE could pick up to replicate work in this field, restricted to engines NOT authored by the paper's own team (author-disjoint = replication, not reproduction).", "computed": "OpenAIRE type=software matching the field, filtered to author-disjoint records, ranked by the reusable-software score below (resolvable repository · Software Heritage · downloads · citations · GitHub stars), with the repository resolved via Zenodo when OpenAIRE omits it.", "limits": "Keyword recall is OpenAIRE's — a tool whose record doesn't contain the search terms won't appear (e.g. a narrow topic can miss an established tool); the field's software may be thin or dominated by one-off study deposits." },
|
|
44
46
|
{ "id": "gap", "name": "The replication-gap map", "shownAs": "the composition bar at the top", "source": "sciencelive",
|
|
45
47
|
"means": "How much of the matched high-impact work has actually been independently checked — the rest is the replication gap.", "computed": "A composition of the ranked list by status (reproducible / validated / contested / needs check / dormant).", "limits": "Reflects the current search and the current network coverage." }
|
|
46
48
|
],
|
|
@@ -53,7 +55,10 @@
|
|
|
53
55
|
"explanation": "Each outcome is one signed nanopublication; records without a signed outcome are not counted." },
|
|
54
56
|
{ "id": "fair", "name": "FAIR-software score",
|
|
55
57
|
"formula": "1 point for each met recommendation, out of 5: public repository · open licence · in a registry · citable (CITATION.cff or DOI) · quality artefacts.",
|
|
56
|
-
"explanation": "fair-software.eu's five recommendations, evaluated from live GitHub / Software Heritage / Zenodo signals." }
|
|
58
|
+
"explanation": "fair-software.eu's five recommendations, evaluated from live GitHub / Software Heritage / Zenodo signals." },
|
|
59
|
+
{ "id": "softwarereuse", "name": "Reusable-software ranking",
|
|
60
|
+
"formula": "In the web app: a quality composite a one-off deposit can't fake — the FAIR-software score (0–5) + the count of RSE good-practice signals (0–5) + a GitHub-star bonus = min(3, log10(stars+1)·1.6) − 1.5 if the software needs a proprietary/paid runtime, plus 0.4×the OpenAIRE reuse signal and +0.5 if Software Heritage archived. In the MCP find_independent_software tool (no per-repo FAIR assessment): a resolvable code repository (+2) + Software Heritage (+2) + has downloads (+1) + has citations (+1) + the same GitHub-star bonus. Both rank author-disjoint software only.",
|
|
61
|
+
"explanation": "Citation class is useless for research software (nearly everything is C5 / 0-citation), so software is ranked by grounded REUSE signals instead. GitHub stars are the signal that separates an established, widely-used tool from a one-off study deposit — read from the repo, falling back to the Zenodo→GitHub link when OpenAIRE omits the repo URL, and capped on a log scale so a handful of very-starred repos can't dominate. A star lookup that is rate-limited or unreachable falls back to the other signals, never a guess. GitHub's unauthenticated rate limit is 60/hour; set GITHUB_TOKEN to raise it." }
|
|
57
62
|
],
|
|
58
63
|
"practices": [
|
|
59
64
|
{ "id": "documented", "name": "Documented",
|
|
@@ -7,4 +7,4 @@ OpenAIRE impact + Software Heritage reuse signals + Science Live nanopub verdict
|
|
|
7
7
|
from .radar import radar, find_independent_software, replication_status, verified_claims
|
|
8
8
|
|
|
9
9
|
__all__ = ["radar", "find_independent_software", "replication_status", "verified_claims"]
|
|
10
|
-
__version__ = "0.4.
|
|
10
|
+
__version__ = "0.4.3"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""GitHub + Zenodo helpers so reusable-software ranking reflects real reuse (stars),
|
|
2
|
+
not just OpenAIRE metadata.
|
|
3
|
+
|
|
4
|
+
Why: OpenAIRE's software records are uniformly low-citation and often omit the code
|
|
5
|
+
repository URL, so a one-off study repo can look as "reusable" as an established tool.
|
|
6
|
+
GitHub stars are the missing signal — the web app already ranks its software lens with
|
|
7
|
+
them; this brings the MCP in line.
|
|
8
|
+
|
|
9
|
+
Resolution mirrors the web app: prefer OpenAIRE's codeRepositoryUrl, else the Zenodo
|
|
10
|
+
record's related_identifiers (many Zenodo-published tools omit the repo URL in OpenAIRE —
|
|
11
|
+
e.g. XMHW). Stars come from the GitHub REST API (unauth 60/hr; set GITHUB_TOKEN to lift to
|
|
12
|
+
5000/hr). Every lookup is cached and BEST-EFFORT — a failed/rate-limited call returns None
|
|
13
|
+
and the caller falls back to the OpenAIRE-only signal, so results stay grounded and never
|
|
14
|
+
crash. Stdlib only.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import math
|
|
20
|
+
import os
|
|
21
|
+
import re
|
|
22
|
+
import urllib.request
|
|
23
|
+
|
|
24
|
+
_TIMEOUT = float(os.environ.get("RADAR_HTTP_TIMEOUT", "30"))
|
|
25
|
+
_TOKEN = os.environ.get("GITHUB_TOKEN") or os.environ.get("RADAR_GITHUB_TOKEN")
|
|
26
|
+
_stars_cache: dict[str, int | None] = {}
|
|
27
|
+
_zenodo_repo_cache: dict[str, str | None] = {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _parse(url: str | None) -> tuple[str, str] | None:
|
|
31
|
+
m = re.search(r"github\.com/([^/]+)/([^/#?]+)", url or "", re.I)
|
|
32
|
+
return (m.group(1), m.group(2).replace(".git", "")) if m else None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_json(url: str, headers: dict) -> dict | None:
|
|
36
|
+
try:
|
|
37
|
+
req = urllib.request.Request(url, headers=headers)
|
|
38
|
+
with urllib.request.urlopen(req, timeout=_TIMEOUT) as r:
|
|
39
|
+
return json.load(r)
|
|
40
|
+
except Exception:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _github_from_zenodo(doi: str | None) -> str | None:
|
|
45
|
+
m = re.search(r"zenodo\.(\d+)", doi or "", re.I)
|
|
46
|
+
if not m:
|
|
47
|
+
return None
|
|
48
|
+
key = m.group(1)
|
|
49
|
+
if key in _zenodo_repo_cache:
|
|
50
|
+
return _zenodo_repo_cache[key]
|
|
51
|
+
found = None
|
|
52
|
+
d = _get_json(
|
|
53
|
+
f"https://zenodo.org/api/records/{key}",
|
|
54
|
+
{"Accept": "application/json", "User-Agent": "replication-radar"},
|
|
55
|
+
)
|
|
56
|
+
for rel in ((d or {}).get("metadata") or {}).get("related_identifiers") or []:
|
|
57
|
+
ident = rel.get("identifier") or ""
|
|
58
|
+
if "github.com" in ident:
|
|
59
|
+
found = ident
|
|
60
|
+
break
|
|
61
|
+
_zenodo_repo_cache[key] = found
|
|
62
|
+
return found
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def resolve_repo(code_repo: str | None, doi: str | None = None) -> str | None:
|
|
66
|
+
"""Normalised ``https://github.com/owner/repo`` for this software, or None.
|
|
67
|
+
|
|
68
|
+
Prefers OpenAIRE's ``codeRepositoryUrl``; falls back to the Zenodo record's
|
|
69
|
+
``related_identifiers`` (many Zenodo-published tools omit the repo URL in OpenAIRE)."""
|
|
70
|
+
g = _parse(code_repo) or _parse(_github_from_zenodo(doi))
|
|
71
|
+
return f"https://github.com/{g[0]}/{g[1]}" if g else None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def stars(repo_url: str | None) -> int | None:
|
|
75
|
+
"""GitHub stargazers for a repo URL (cached); None if unknown/unreachable/rate-limited.
|
|
76
|
+
|
|
77
|
+
Set ``GITHUB_TOKEN`` (or ``RADAR_GITHUB_TOKEN``) to lift the 60/hr unauth limit to 5000/hr."""
|
|
78
|
+
g = _parse(repo_url)
|
|
79
|
+
if not g:
|
|
80
|
+
return None
|
|
81
|
+
key = f"{g[0]}/{g[1]}".lower()
|
|
82
|
+
if key in _stars_cache:
|
|
83
|
+
return _stars_cache[key]
|
|
84
|
+
headers = {"Accept": "application/vnd.github+json", "User-Agent": "replication-radar"}
|
|
85
|
+
if _TOKEN:
|
|
86
|
+
headers["Authorization"] = f"Bearer {_TOKEN}"
|
|
87
|
+
d = _get_json(f"https://api.github.com/repos/{g[0]}/{g[1]}", headers)
|
|
88
|
+
val = int(d["stargazers_count"]) if d and "stargazers_count" in d else None
|
|
89
|
+
_stars_cache[key] = val
|
|
90
|
+
return val
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def star_bonus(n: int | None) -> float:
|
|
94
|
+
"""Bounded log-scale star contribution to a reuse score, mirroring the web app
|
|
95
|
+
(``min(3, log10(stars+1) * 1.6)``): 0 stars → 0; ~10 → 1.7; ~30 → 2.4; ≥250 → 3.0."""
|
|
96
|
+
if not n or n <= 0:
|
|
97
|
+
return 0.0
|
|
98
|
+
return min(3.0, math.log10(n + 1) * 1.6)
|
|
@@ -11,7 +11,7 @@ Three capabilities (exposed as MCP tools in server.py):
|
|
|
11
11
|
"""
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
from . import openaire, verdicts
|
|
14
|
+
from . import github, openaire, verdicts
|
|
15
15
|
|
|
16
16
|
# impact class -> 0..1 (C1 best). Used in the readiness score.
|
|
17
17
|
_CLASS_SCORE = {"C1": 1.0, "C2": 0.8, "C3": 0.6, "C4": 0.4, "C5": 0.2, None: 0.2}
|
|
@@ -89,6 +89,21 @@ def verified_claims() -> dict:
|
|
|
89
89
|
return {"count": len(claims), "claims": claims}
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
_SW_POOL = 100 # software candidates to fetch (established tools are often relevance-buried)
|
|
93
|
+
_RESOLVE_CAP = 40 # max GitHub/Zenodo lookups per call (cached; set GITHUB_TOKEN for headroom)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _sw_rank_score(p, repo: str | None, stars: int | None) -> float:
|
|
97
|
+
"""Reuse score used to RANK software: the OpenAIRE signal plus a bounded GitHub-star bonus.
|
|
98
|
+
Uses the resolved `repo` (so a Zenodo-linked tool OpenAIRE omitted still gets repo credit)."""
|
|
99
|
+
s = 0.0
|
|
100
|
+
if repo: s += 2 # a real, resolvable code repository
|
|
101
|
+
if p.swh_archived: s += 2 # Software Heritage archived
|
|
102
|
+
if p.downloads > 0: s += 1
|
|
103
|
+
if p.citation_count > 0: s += 1
|
|
104
|
+
return s + github.star_bonus(stars) # stars separate established tools from one-off study repos
|
|
105
|
+
|
|
106
|
+
|
|
92
107
|
def find_independent_software(
|
|
93
108
|
doi: str | None = None,
|
|
94
109
|
topic: str | None = None,
|
|
@@ -109,7 +124,9 @@ def find_independent_software(
|
|
|
109
124
|
# derive a short topic from the title's leading words
|
|
110
125
|
topic = " ".join((paper.title if paper else "").split()[:3]) or "software"
|
|
111
126
|
|
|
112
|
-
pool
|
|
127
|
+
# Wide pool: established tools are often relevance-buried in OpenAIRE's software index
|
|
128
|
+
# (e.g. XMHW ranks ~60th for "marine heatwave"), so a small pool never sees them.
|
|
129
|
+
pool = openaire.search_products(topic, "software", size=_SW_POOL)
|
|
113
130
|
rows = []
|
|
114
131
|
for p in pool:
|
|
115
132
|
rows.append(
|
|
@@ -118,14 +135,32 @@ def find_independent_software(
|
|
|
118
135
|
"doi": p.doi,
|
|
119
136
|
"authors": p.authors,
|
|
120
137
|
"independent": _independence(original_authors, p.authors),
|
|
121
|
-
"reuse_score": p.reuse_score,
|
|
138
|
+
"reuse_score": p.reuse_score, # OpenAIRE-only signal (kept for transparency)
|
|
122
139
|
"code_repo": p.code_repo,
|
|
123
140
|
"swh_archived": p.swh_archived,
|
|
124
141
|
"downloads": p.downloads,
|
|
142
|
+
"stars": None,
|
|
143
|
+
"_p": p,
|
|
125
144
|
}
|
|
126
145
|
)
|
|
127
|
-
# independent
|
|
128
|
-
|
|
146
|
+
# Resolve GitHub stars for the independent candidates (bounded + cached, best-effort).
|
|
147
|
+
# Stars are the missing reuse signal: without them a 0-star one-off study repo ranks like
|
|
148
|
+
# an established, widely-used tool. Resolve highest OpenAIRE-signal first so the budget is
|
|
149
|
+
# spent well; the Zenodo fallback recovers repos OpenAIRE omits (that is how XMHW resolves).
|
|
150
|
+
resolvable = [
|
|
151
|
+
r for r in rows
|
|
152
|
+
if r["independent"] and (r["code_repo"] or "zenodo" in (r["doi"] or "").lower())
|
|
153
|
+
]
|
|
154
|
+
resolvable.sort(key=lambda r: (-r["reuse_score"], -r["downloads"]))
|
|
155
|
+
for r in resolvable[:_RESOLVE_CAP]:
|
|
156
|
+
repo = github.resolve_repo(r["code_repo"], r["doi"])
|
|
157
|
+
if repo:
|
|
158
|
+
r["code_repo"] = repo
|
|
159
|
+
r["stars"] = github.stars(repo)
|
|
160
|
+
for r in rows:
|
|
161
|
+
r["rank_score"] = round(_sw_rank_score(r.pop("_p"), r["code_repo"], r["stars"]), 2)
|
|
162
|
+
# independent first, then real (stars-augmented) reuse, then downloads
|
|
163
|
+
rows.sort(key=lambda r: (not r["independent"], -r["rank_score"], -r["downloads"]))
|
|
129
164
|
return {
|
|
130
165
|
"query_topic": topic,
|
|
131
166
|
"original_authors": original_authors,
|
|
@@ -40,9 +40,12 @@ def radar(topic: str, limit: int = 8) -> dict:
|
|
|
40
40
|
@mcp.tool()
|
|
41
41
|
def find_independent_software(doi: str = "", topic: str = "", limit: int = 8) -> dict:
|
|
42
42
|
"""Reusable method software for *replicating* a claim — engines NOT authored by
|
|
43
|
-
the original paper's team (author-disjoint), ranked by reuse signal
|
|
44
|
-
Software Heritage archival +
|
|
45
|
-
|
|
43
|
+
the original paper's team (author-disjoint), ranked by reuse signal — a resolvable
|
|
44
|
+
code repository + Software Heritage archival + downloads + GitHub stars (the signal
|
|
45
|
+
that separates an established tool from a one-off study repo) — not citations. Each
|
|
46
|
+
result carries its `stars` and `rank_score`. Pass the original paper's DOI (authors
|
|
47
|
+
are looked up) and a short topic; keep the topic broad (a narrow one can miss an
|
|
48
|
+
established tool whose OpenAIRE record doesn't contain the extra words)."""
|
|
46
49
|
return _find_software(doi=doi or None, topic=topic or None, limit=limit)
|
|
47
50
|
|
|
48
51
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{replication_radar-0.4.2 → replication_radar-0.4.3}/src/replication_radar/data/verdicts.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|