Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
222 commits
Select commit Hold shift + click to select a range
48f2ef0
bugfix: snapshots configuration
andreishket Feb 10, 2026
1419b03
feat: usage of dccollection
andreishket Feb 11, 2026
6fd83b8
refactor: usage of is_anchor flag instead of keep and is_latest
andreishket Feb 11, 2026
f16c5fb
refactor: filtering non_datasets and pure_datasets the same way
andreishket Feb 11, 2026
8b3d651
feat: prioritize entries using doi and data provider
andreishket Feb 11, 2026
78ee23c
refactor: usage of doi and provider prioritization only for ORCID
andreishket Feb 11, 2026
45b483c
feat: subject_orig replacement
andreishket Feb 11, 2026
84db510
feat: paper_abstract replacement
andreishket Feb 11, 2026
26541d0
feat: subject replacement
andreishket Feb 12, 2026
b9219f4
refactor: group all enrichments in one cycle
andreishket Feb 12, 2026
814be76
feat: native merging strategy for subject and subject_orig
andreishket Feb 12, 2026
c4ad1fe
bugfix: dates in the request to base
andreishket Feb 12, 2026
4cc4681
bugfix: limit in the request to base
andreishket Feb 12, 2026
f4ae820
feat: merging keywords with usage of RapidFuzz library
andreishket Feb 12, 2026
8fb46f0
feat: finding best OA status
andreishket Feb 12, 2026
d0a099f
feat: link merging
andreishket Feb 13, 2026
4556aff
refactor: code clean up
andreishket Feb 13, 2026
ef58c57
refactor: deduplication functions moved to deduplication.py
andreishket Feb 13, 2026
c380e29
refactor: subject strategy from envs
andreishket Feb 13, 2026
f073cfa
bugfix: some entries are not enriched
andreishket Feb 16, 2026
e1207ad
bugfix: mark the only entry in a group as is_anchor
andreishket Feb 16, 2026
a90ec0f
feat: DOIs preparation before the BASE request
andreishket Feb 17, 2026
9459892
feat: mapping DOIs back to initial case
andreishket Feb 17, 2026
4dd20fa
doi metadata bugfix
chreman Feb 17, 2026
692d4ec
updated query to include docs without date
chreman Feb 18, 2026
dbd95dc
increased limits and size of possible return set
chreman Feb 18, 2026
308a6c5
work-in-progress
chreman Feb 18, 2026
2ff5c37
wip for merging with multiple DOIs
chreman Feb 18, 2026
cf93efb
feat: preparing merged version of DOIs
andreishket Feb 18, 2026
0c66fc4
feat: explode merged DOIs
andreishket Feb 18, 2026
231265d
refactor: simplier explode function
andreishket Feb 18, 2026
47e24d9
feat: DOIs versions mapping
andreishket Feb 18, 2026
98d2c7f
add timing logs
chreman Feb 18, 2026
1eefbf4
bugfix for column handling and OA state merging
chreman Feb 18, 2026
4889ec1
improved doi replacement
chreman Feb 18, 2026
9a09e40
refactor: log that shows amount of not recieved DOIs from BASE
andreishket Feb 20, 2026
8d67706
added waiting time analysis script
chreman Feb 16, 2026
9857010
added new run time evaluation script
chreman Feb 18, 2026
68af5f0
README update
chreman Feb 19, 2026
5d4f9fb
datatype bugfix for oa_state enrichment; code & logging cleanup
chreman Feb 19, 2026
228ebb8
bugfix in unstable merge logic for oa_state
chreman Feb 19, 2026
705d06e
tweaking batch size
chreman Feb 19, 2026
76f4ba9
further query simplification
chreman Feb 19, 2026
b77e5b5
reconcile code divergence
chreman Feb 20, 2026
b321c2f
reconcile code divergence
chreman Feb 20, 2026
bc83ca3
reconcile code divergence
chreman Feb 20, 2026
04bd131
reconcile code divergence
chreman Feb 20, 2026
ed6641b
reconcile code divergence
chreman Feb 20, 2026
b7fbc8e
reconcile code divergence
chreman Feb 20, 2026
b057045
enable enrichment for BASE
chreman Feb 20, 2026
0830490
reconcile code divergence
chreman Feb 20, 2026
b79a7a0
reconcile code divergence
chreman Feb 20, 2026
e6b3749
bugfix for oa state priorisation
chreman Feb 20, 2026
3b62fed
activate enrichment pipeline for base
chreman Feb 20, 2026
8d38836
revert to original approach
chreman Feb 20, 2026
1446dde
Merge branch 'feat/thematic-mapping-must-have-cherrypicks' into feat/…
chreman Feb 20, 2026
11b8d86
bugfix for handling duplicates
chreman Feb 20, 2026
5c8fb3a
bugfix for batch retrieval
chreman Feb 22, 2026
f542371
refactor: remove code doubling in logging
andreishket Feb 23, 2026
fadade2
Merge pull request #860 from OpenKnowledgeMaps/feat/thematic-mapping-…
andreishket Feb 23, 2026
a252eb1
feat: a new filed that contains merged links
andreishket Feb 23, 2026
3af393c
feat: remove anchor link from the array with links from doubles
andreishket Feb 23, 2026
3cc946e
feat: parsing of pdf link candidates
andreishket Feb 24, 2026
2b22d69
feat: usage of the pdf link candidates for pdf loading
andreishket Feb 24, 2026
ace1ff2
Merge pull request #861 from OpenKnowledgeMaps/bugfix/separate-field-…
andreishket Feb 24, 2026
c721d91
refactor: set initial batch size
andreishket Feb 24, 2026
ca9fb8c
refactor: remove commented code
andreishket Feb 24, 2026
b0e2fee
Merge pull request #862 from OpenKnowledgeMaps/refactor/revert-batch-…
chreman Feb 26, 2026
9209505
Merge pull request #863 from OpenKnowledgeMaps/refactor/commented-cod…
chreman Feb 26, 2026
0b76e98
Merge remote-tracking branch 'upstream/feat/thematic-mapping-must-hav…
chreman Feb 26, 2026
a4510ce
analytic tool update
chreman Feb 27, 2026
995749a
group orcids before analysis
chreman Mar 1, 2026
7a0ff09
Merge branch 'master' into fix/thematic-mapping-ms2-preparations
chreman May 11, 2026
d8c6602
maintenance: remove replace strategy in enrichment
chreman May 11, 2026
41753bd
Merge branch 'feat/test-framework' into fix/thematic-mapping-ms2-prep…
chreman May 11, 2026
2286f7f
update of contentprovider.json cache
chreman May 11, 2026
2394d9d
clean up container build workflow
chreman May 11, 2026
c39eeae
e2e tests for ORCID enrichment tests
chreman May 12, 2026
10f58e6
first e2e tests for orcid enrichment bugfixes and regression testing
chreman May 12, 2026
95d82be
add db warm-up and cache refresh to ORCID tests
chreman May 12, 2026
337453f
deduplication test updates
chreman May 12, 2026
02833dc
debug log expansion in deduplication pipeline
chreman May 12, 2026
172fc51
bugfix for wrong anchor record merge
chreman May 12, 2026
a7e86ae
deduplication test updates
chreman May 12, 2026
bc508fd
added test cases for wrong DOIs in duplicate groups
chreman May 13, 2026
9e7031f
added unit test for the doi_title_filter function
chreman May 13, 2026
7ca69d0
sanity check by DOI title filtering of duplicates
chreman May 13, 2026
4e54e0f
test case updates after bugfix
chreman May 13, 2026
c186ee0
debug logging for DOI case merging
chreman May 14, 2026
5ba7980
cleanup DOI case-based enrichment, replace with simplified merge
chreman May 14, 2026
727ed60
reinstate some code
chreman May 14, 2026
eed7e93
revert e2e test param change
chreman May 14, 2026
41a982b
added tests for DOI case variants
chreman May 14, 2026
a3d97c1
added tests for DOI case invariant enrichment
chreman May 15, 2026
5cccb42
added BASE test cases for anchor record retention bugfix
chreman May 15, 2026
26fc56a
bugfix and logging for anchor document receiving best position in dup…
chreman May 15, 2026
258ca6d
update of contentprovider.json cache
chreman May 15, 2026
a939494
test case update
chreman May 19, 2026
2e6d31c
make test case titles unique
chreman May 19, 2026
7371d5b
fix query handling in tests
chreman May 19, 2026
9f0368c
bugfix and tests for anchor document enrichment in DOI versioned data…
chreman May 19, 2026
1615c38
test fixes
chreman May 19, 2026
22667b4
test wrapup for flaky tests
chreman May 19, 2026
2b62546
update of contentprovider.json cache
chreman May 19, 2026
395baeb
test wrapup for flaky tests
chreman May 19, 2026
db284e6
pin flask-restx version for compatibility with python 3.9 - bump agai…
chreman Jun 1, 2026
7a96d56
pin flask-restx dependency for compatibility with python 3.9 - bump a…
chreman Jun 1, 2026
e955ba8
add logging for OA status field
chreman Jun 2, 2026
9aa3450
add logging for PDF link field
chreman Jun 2, 2026
4aa0f84
fix build order issue in renv
chreman Jun 2, 2026
55724bb
e2e tests for OA status and PDF link enrichment; with some helper fun…
chreman Jun 3, 2026
77cf6ad
remove link enrichment tests
chreman Jun 3, 2026
f226599
cleanup of PDF link related debug logging
chreman Jun 3, 2026
7f62635
rename data fields for additional DOIs
chreman Jun 5, 2026
79133b8
implement new data contract (guarded by exception) for DOI enrichment…
chreman Jun 5, 2026
ac978b8
implement new data contract (guarded by exception) for DOI enrichment…
chreman Jun 5, 2026
6fa59e7
fix test drift
chreman Jun 5, 2026
ff21017
bugfix for gap in metadata enrichment in cases where DOIs are only pr…
chreman Jun 5, 2026
d538804
update of contentprovider.json cache
chreman Jun 8, 2026
366d014
add tests for bugfix for escaping SOLR instructions in DOIs
chreman Jun 9, 2026
e01500d
update of contentprovider.json cache
chreman Jun 9, 2026
96eeb02
bugfix for SOLR parsing of DOIs with special characters
chreman Jun 11, 2026
d590678
Merge remote-tracking branch 'upstream/master' into feat/keyword-labe…
chreman Jun 15, 2026
3cc0b01
Merge branch 'dev' into feat/keyword-label-improvements
chreman Jun 16, 2026
b753e71
comment out debug logging
chreman Jun 16, 2026
9dd38d2
clean up debug logging
chreman Jun 16, 2026
16c5ba9
add debug logging
chreman Jun 16, 2026
f6d2a0a
clean up debug logging
chreman Jun 16, 2026
0fb25b4
debug updates
chreman Jun 19, 2026
80cf235
bugfix for deduplication in orcid merge
chreman Jun 11, 2026
08edfc2
update of contentprovider.json cache
chreman Jun 19, 2026
5941e38
Merge branch 'master' into fix/thematic-mapping-ms2-oastatus
chreman Jun 19, 2026
05c92ad
Merge branch 'fix/thematic-mapping-ms2-oastatus' into feat/keyword-la…
chreman Jun 19, 2026
30f9da1
add additional mesh term cleanup and MeSH term de-inversion; added me…
chreman Jun 19, 2026
de70515
added classification cleanup in more structured approach than the cha…
chreman Jun 20, 2026
04eb9d4
move subject cleaning to separate R file; update test imports
chreman Jun 20, 2026
89fbfaf
add docstring
chreman Jun 22, 2026
d111266
add bugfix/expression for SDG
chreman Jun 22, 2026
56ca04b
add additional expression of FoR-classification
chreman Jun 22, 2026
fad5772
add e2e keyword cleaning invariant test
chreman Jun 23, 2026
d4eca50
add new markers to e2e test
chreman Jun 23, 2026
48c9605
update for classification cleanup docstring with known issue
chreman Jun 23, 2026
fdd7340
strip mesh qualifier terms; keep root keyword; includes tests
chreman Jun 23, 2026
6b5e931
add MeSH qualifier partial removal from keywords; include R tests and…
chreman Jun 25, 2026
ec2da0b
add LoC subclass cleanup; include unit and e2e tests
chreman Jun 25, 2026
fe0aa47
add grant scheme cleanup; include tests
chreman Jun 26, 2026
de28cb3
update of contentprovider.json cache
chreman Jun 26, 2026
5809a6d
add debug and logging functionality for summarize.R area title genera…
chreman Jun 30, 2026
bed26f6
add wedge-in for ranking modes; config variables via env; basic tests
chreman Jul 6, 2026
58a6d3a
add integration tests and test harness for ranking mode before/after …
chreman Jul 7, 2026
b15d699
debug dump functionality
chreman Jul 7, 2026
4b090ab
tests for ranking mode 1
chreman Jul 7, 2026
767b32f
first implementation of ranking mode 1
chreman Jul 7, 2026
43989c3
area label debug tooling
chreman Jul 9, 2026
03ed430
add two separate metadata fields for heuristically generated keywords…
chreman Jul 9, 2026
2f24728
extract fallback into separate function; bugfix for fallback trigger …
chreman Jul 9, 2026
5c677da
update of contentprovider.json cache
chreman Jul 9, 2026
e54e380
ranking mode 2 implementation; include tests
chreman Jul 9, 2026
178e8dd
include list of empty-keyword replacements to list of heuristically g…
chreman Jul 9, 2026
2519f83
update of contentprovider.json cache
chreman Jul 9, 2026
78e6215
revert heuristic keyword threshold filtering concerning legacy rankin…
chreman Jul 9, 2026
3e891e6
revert heuristic keyword threshold filtering concerning legacy rankin…
chreman Jul 9, 2026
1c8a4fa
add additional test fixtures for area labeling tests
chreman Jul 9, 2026
ec55c8d
added test replay fixtures for ranking mode 1
chreman Jul 9, 2026
f4148f8
implement global heuristic keyword threshold instead of per-cluster; …
chreman Jul 10, 2026
280c891
expand stopwords list
chreman Jul 10, 2026
8107fc6
update of contentprovider.json cache
chreman Jul 10, 2026
a0c4dc8
added area label exclusion list, a more targeted approach than stopwo…
chreman Jul 11, 2026
9a716d4
Merge branch 'feat/area-label-improvements-mode1' into feat/area-labe…
chreman Jul 12, 2026
372fd4f
update test fixtures for ranked area labeling
chreman Jul 12, 2026
7c534b9
add ranking mode 3; including tests and separate mesh_fields extractor
chreman Jul 12, 2026
9096250
mode 3 replay fixtures
chreman Jul 12, 2026
baf3d88
mode 3 replay fixtures
chreman Jul 13, 2026
d5d68c4
mode 3 replay fixtures
chreman Jul 13, 2026
c276fa1
metadata handling bugfix
chreman Jul 13, 2026
4fb4371
metadata handling bugfix
chreman Jul 13, 2026
7379cd0
unit tests for metadata handling esp. of new columns
chreman Jul 14, 2026
4bc33fb
update of contentprovider.json cache
chreman Jul 17, 2026
b5e1f64
Merge pull request #881 from OpenKnowledgeMaps/dev
chreman Jul 17, 2026
b77a730
update gitignore
chreman Jul 20, 2026
b738b95
Merge branch 'fix/base-request-spike'
chreman Jul 20, 2026
5d821e9
Merge branch 'master' into feat/keyword-label-improvements-m3
chreman Jul 20, 2026
d03906d
debug data dumps
chreman Jul 20, 2026
c1f9436
add debug functionality for label generation
chreman Jul 27, 2026
3f19cc6
add debug functionality for label generation
chreman Jul 27, 2026
de1bc60
First implementation of anchor resource deterministic selection and o…
chreman Aug 21, 2026
974638a
debug tooling and tests for deterministic anchor selection and ordering
chreman Aug 21, 2026
d7a2fb3
update of contentprovider.json cache
chreman Aug 21, 2026
dc5886f
update e2e tests with new expected outcomes
chreman Aug 22, 2026
bdb36d7
update of contentprovider.json cache
chreman Aug 22, 2026
7853974
fix stability bug
chreman Aug 23, 2026
205cfcb
update of contentprovider.json cache
chreman Aug 25, 2026
b2c7da2
bugfixes for different keyword cleaning issues in BASE
chreman Aug 26, 2026
9e4a948
test updates for keyword cleaning improvements
chreman Aug 26, 2026
5de2a21
expand unit tests for keyword cleaning
chreman Aug 27, 2026
18895f0
asterisk cleanup bugfix
chreman Aug 27, 2026
bf34aab
update of contentprovider.json cache
chreman Sep 2, 2026
e511a0e
punctuation processing in heuristic keyword generation functions upda…
chreman Sep 2, 2026
183f7b8
test case updates
chreman Sep 3, 2026
746725a
ngram generation rework: foundational settings and test setup
chreman Sep 4, 2026
db44d7d
implementation of ngram construction; settings; incl. test updates
chreman Sep 5, 2026
b95c632
update of contentprovider.json cache
chreman Sep 5, 2026
2e5cf99
re-instate overpruning bug in a controlled way for full legacy equiva…
chreman Sep 7, 2026
38d018e
re-instate overpruning bug in a controlled way for full legacy equiva…
chreman Sep 7, 2026
81c82c0
file split for code readability
chreman Sep 7, 2026
0cbdf76
settings bugfix
chreman Sep 10, 2026
4c56079
text update in ORCID researcher metrics modal
chreman Sep 14, 2026
868ee98
wip for deduplication bugfix
chreman Sep 15, 2026
67d5fe9
wip for deduplication bugfix
chreman Sep 15, 2026
90b849b
make pandas sorting deterministic
chreman Sep 15, 2026
0ba5adb
update of contentprovider.json cache
chreman Sep 15, 2026
c2b66ed
test cases for JEL, AMS, PACS classification cleanup
chreman Sep 15, 2026
051fc42
additional keyword cleanup for JEL, AMS, PACS classifications; arxiv …
chreman Sep 15, 2026
f32098e
update of contentprovider.json cache
chreman Sep 15, 2026
f452b44
case restoration bugfix work-in-progress
chreman Sep 16, 2026
f24b055
Merge branch 'feat/thematic-mapping-polishing-2' into feat/thematic-m…
chreman Sep 16, 2026
664687e
adjustment of case restoration to ignore ALLCAPS titles
chreman Sep 16, 2026
e0e2589
adjustment of case restoration to ignore ALLCAPS keywords of length 2+
chreman Sep 17, 2026
6584773
adjustment of case restoration by improving recognition of intra-word…
chreman Sep 17, 2026
00c58dd
re-baseline of test files for current state
chreman Sep 17, 2026
c6084f3
add docstrings to deduplication.py functions
chreman Sep 21, 2026
d9c0d35
correction in env file docstring
chreman Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,13 @@ local_dev/tools/**/*.log
/blob-report/
/playwright/.cache/

# inspection output
e2e/reproducibility/output/*
e2e/reproducibility/logs/*
local_dev/tools/label_analysis
local_dev/tools/keyword_analysis
local_dev/tools/reproducibility
local_dev/tools/dedup_review/*

# mac os
.DS_Store
11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ services:
REDIS_PASSWORD: "${REDIS_PASSWORD}"
LOGLEVEL: "${LOGLEVEL}"
LOGFILE: "${LOGFILE}"
RANKING_MODE: "${RANKING_MODE}"
RANKING_MODE_BASE: "${RANKING_MODE_BASE}"
RANKING_MODE_PUBMED: "${RANKING_MODE_PUBMED}"
RANKING_MODE_ORCID: "${RANKING_MODE_ORCID}"
RANKING_MODE_OPENAIRE: "${RANKING_MODE_OPENAIRE}"
NGRAM_SETTING: "${NGRAM_SETTING}"
NGRAM_SETTING_BASE: "${NGRAM_SETTING_BASE}"
NGRAM_SETTING_PUBMED: "${NGRAM_SETTING_PUBMED}"
NGRAM_SETTING_ORCID: "${NGRAM_SETTING_ORCID}"
NGRAM_SETTING_OPENAIRE: "${NGRAM_SETTING_OPENAIRE}"
INCLUDE_ABSTRACTS: "${INCLUDE_ABSTRACTS}"
RENV_VERSION: 0.14.0-5
CRAN_REPOS: https://cran.wu.ac.at
LC_ALL: "en_US.UTF-8"
Expand Down
2 changes: 1 addition & 1 deletion e2e/basicTests/OpenAIRE.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test.describe("Basic tests for OpenAIRE integration (Knowledge Map visualisation
);
await page.getByTestId("context").getByText("More information").click();
await expect(page.locator("#info-body")).toContainText(
"This knowledge map presents you with a topical overview of research conducted in the following project:Project DetailsTitleOPENing UP new methods, indicators and tools for peer review, impact measurement and dissemination of research resultsAcronymOpenUPFunderECFunding programCSAH2020CallH2020-GARRI-2015-1Contract (GA) number710722Start Date2016-06-01End Date2018-11-30Open Access mandatetrueOrganizationsUGOE, UvA, PUBLIC POLICY AND MANAGEMENT INSTITUTE, FRONTIERS MEDIA SA, UoA, DZHW, AIT, Know Center, CNROpenAire LinkLinkWe use text similarity to create a knowledge map. The algorithm groups those resources together that have many words in common. Area titles are created from subject keywords of resources that have been assigned to the same area. We select those keywords and phrases that appear frequently in one area, and seldom in other areas.Knowledge maps provide an instant overview of a topic by showing the main areas at a glance, and resources related to them. This makes it possible to easily identify useful, pertinent information.Please read our FAQs to find out more about knowledge maps.Data sourceThe data is taken from OpenAIRE. OpenAIRE is a key infrastructure that enables the European transition to open science. It provides access to 3 million research projects and more than 70 million research outputs from more than 100,000 data sources.Open source softwareThe visualization is created with the award winning open source software Head Start provided by Open Knowledge Maps. Open Knowledge Maps is a non-profit organisation run by a group of dedicated team members and volunteers. In order to improve our free and open service, we need your support. Please send us your feedback to info@openknowledgemaps.org.If you want to support us financially, you can:Make a donationBecome an organisational memberBecome a funder of our roadmapSign-up for our newsletter to receive occasional updates.",
"This knowledge map presents you with a topical overview of research conducted in the following project:Project DetailsTitleOPENing UP new methods, indicators and tools for peer review, impact measurement and dissemination of research resultsAcronymOpenUPFunderECFunding programCSAH2020CallH2020-GARRI-2015-1Contract (GA) number710722Start Date2016-06-01End Date2018-11-30Open Access mandatetrueOrganizationsKnow Center, UvA, UOA (ΕΚΠΑ), DZHW, CNR, AIT, PPMI, UGOE, FRONTIERS MEDIA SAOpenAire LinkLinkWe use text similarity to create a knowledge map. The algorithm groups those resources together that have many words in common. Area titles are created from subject keywords of resources that have been assigned to the same area. We select those keywords and phrases that appear frequently in one area, and seldom in other areas.Knowledge maps provide an instant overview of a topic by showing the main areas at a glance, and resources related to them. This makes it possible to easily identify useful, pertinent information.Please read our FAQs to find out more about knowledge maps.Data sourceThe data is taken from OpenAIRE. OpenAIRE is a key infrastructure that enables the European transition to open science. It provides access to 3 million research projects and more than 70 million research outputs from more than 100,000 data sources.Open source softwareThe visualization is created with the award winning open source software Head Start provided by Open Knowledge Maps. Open Knowledge Maps is a non-profit organisation run by a group of dedicated team members and volunteers. In order to improve our free and open service, we need your support. Please send us your feedback to info@openknowledgemaps.org.If you want to support us financially, you can:Make a donationBecome an organisational memberBecome a funder of our roadmapSign-up for our newsletter to receive occasional updates.",
);
});
});
Expand Down
82 changes: 82 additions & 0 deletions e2e/keywordsCleaning/BASE.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { test, expect } from "@playwright/test";
import { prepareVisualisation } from "../../vis/js/utils/e2eVisualisationLoader";
import { assertNoClassificationLeaks, extractDocuments } from "./invariant";

// Classification-leak invariant for BASE searches (mirrors ORCID.spec.ts). The
// queries are chosen to surface the schemes/forms the cleaner targets:
// * MeSH descriptors, qualifiers and space-joined blobs (biomedical queries:
// "pharmacology", "therapeutic use", "chemically induced", ...);
// * FOS / arXiv / ddc / LCC classifications (discipline queries: "game theory",
// "machine translation", ...).
// document_types=121 (articles) + min_descsize=300 reproduces the records the
// cleaning was validated against. See ./invariant.ts for the marker set.

interface BaseSearch {
query: string;
search_params: string;
}

// most-relevant articles, min description size 300 (the records the cleaning was
// validated against).
//
// NOTE: `type=get` returns the cached revision for a given (query, params, date)
// hash. So after a cleaning change, re-running the same search returns the stale
// pre-change map. To force a fresh recomputation against the current backend,
// vary a date param, e.g. append "&from=1665-02-05" (any unused value). A clean
// CI database computes fresh on first run, so the default below is fine there.
const ARTICLES =
"&service=base&sorting=most-relevant&document_types%5B%5D=121&lang_id%5B%5D=all-lang&min_descsize=300";

const searches: BaseSearch[] = [
{ query: "pharmacology", search_params: ARTICLES },
{ query: "therapeutic use", search_params: ARTICLES },
{ query: "chemically induced", search_params: ARTICLES },
{ query: "antagonists & inhibitors", search_params: ARTICLES },
{ query: "enzyme inhibitors", search_params: ARTICLES },
{ query: '"game theory"', search_params: ARTICLES },
{ query: "machine translation", search_params: ARTICLES },
{ query: "physics", search_params: ARTICLES },
{ query: "medicine", search_params: ARTICLES },
];

// Readiness check only (the assertions under test are the leak invariants). The
// heading currently renders "&" as a literal "&" (double HTML escaping in the
// title component), so the expected display text mirrors that until the display
// bug is fixed; quotes are stripped by the UI.
function displayedQuery(query: string): string {
return query.replace(/"/g, "").replace(/&/g, "&");
}

// Pre-load the BASE searches so subsequent runs are faster.
// npx playwright test "e2e/keywordsCleaning/BASE.spec.ts" --grep "Warm-up: pre-load BASE searches"
test.describe("Warm-up: pre-load BASE searches", () => {
for (const { query, search_params } of searches) {
test(`${query}`, async ({ page }) => {
const url = `/search?type=get&vis_type=overview&q=${encodeURIComponent(query)}${search_params}`;
await prepareVisualisation(page, url);
await expect(page.locator("#search-term-unique")).toContainText(displayedQuery(query));
});
}
});

test.describe("No classification markers leak into subject or area titles", () => {
for (const { query, search_params } of searches) {
test(`subject + area titles are clean — ${query}`, async ({ page }) => {
const url = `/search?type=get&vis_type=overview&q=${encodeURIComponent(query)}${search_params}`;

// Register before navigation so we capture the data load that populates
// the visualisation. The backend pipeline can take minutes, so match the
// global test timeout.
const responsePromise = page.waitForResponse(
(r) => r.url().includes("getLatestRevision.php") && r.ok(),
{ timeout: 5 * 60 * 1000 },
);

await prepareVisualisation(page, url);
await expect(page.locator("#search-term-unique")).toContainText(displayedQuery(query));

const json = await (await responsePromise).json();
assertNoClassificationLeaks(extractDocuments(json), query);
});
}
});
67 changes: 67 additions & 0 deletions e2e/keywordsCleaning/ORCID.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { test, expect } from "@playwright/test";
import { prepareVisualisation } from "../../vis/js/utils/e2eVisualisationLoader";
import { assertNoClassificationLeaks, extractDocuments } from "./invariant";

// const uniqueOrcids = [...new Set(testCases.map((tc) => tc.orcid).filter(Boolean))];

const additionalUniqueOrcids = [
"0000-0001-6011-4382",
"0000-0003-0204-881X",
"0000-0001-5116-955X",
"0000-0003-2897-6075",
"0000-0002-8911-7832",
"0000-0002-9843-6798",
"0000-0003-4221-6275",
"0000-0002-4505-0517",
"0000-0002-2233-6926",
"0000-0002-2441-4043",
"0000-0001-9287-3770",
"0000-0001-9612-7791",
"0000-0002-4971-2944",
"0000-0001-9062-6039",
"0000-0002-3924-6636",
"0000-0001-5849-8137",
"0000-0003-0297-9614",
];

const allUniqueOrcids = [...new Set([ ...additionalUniqueOrcids])];


// This can also be used to pre-load the unique ORCID profiles, so that the subsequent tests run faster
// npx playwright test "e2e/keywordsCleaning/ORCID.spec.ts" --grep "Warm-up: pre-load unique ORCID profiles"
test.describe("Warm-up: pre-load unique ORCID profiles", () => {
for (const orcid of allUniqueOrcids) {
test(`${orcid}`, async ({ page }) => {
const url = `/search?type=get&vis_type=overview&orcid=${orcid}&service=orcid&embed=true&academic_age_offset=1`;
await prepareVisualisation(page, url);
await expect(page.locator("#search-term-unique")).toContainText(`(${orcid})`);
});
}
});

// Classification-leak invariant: no classification marker or MeSH qualifier may
// survive in the cleaned `subject` or in an area title. See ./invariant.ts for
// the rationale and the marker set.
test.describe("No classification markers leak into subject or area titles", () => {
for (const orcid of allUniqueOrcids) {
test(`subject + area titles are clean — ${orcid}`, async ({ page }) => {
const url = `/search?type=get&vis_type=overview&orcid=${orcid}&service=orcid&embed=true&academic_age_offset=1`;

// Register before navigation so we capture the data load that populates
// the visualisation. The backend pipeline can take minutes, so match the
// global test timeout.
const responsePromise = page.waitForResponse(
(r) => r.url().includes("getLatestRevision.php") && r.ok(),
{ timeout: 5 * 60 * 1000 },
);

await prepareVisualisation(page, url);
await expect(page.locator("#search-term-unique")).toContainText(`(${orcid})`);

const json = await (await responsePromise).json();
// A loaded profile should carry documents; if it is genuinely empty there
// is nothing to clean, which trivially satisfies the invariant.
assertNoClassificationLeaks(extractDocuments(json), orcid);
});
}
});
169 changes: 169 additions & 0 deletions e2e/keywordsCleaning/invariant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { expect } from "@playwright/test";

// Shared classification-leak invariant for the keyword-cleaning e2e tests
// (ORCID + BASE). The cleaning step removes classification-scheme keywords and
// MeSH subheading qualifiers from the display `subject` field, and that cleaned
// field also feeds the cluster/area titles (summarize.R derives labels from
// metadata$subject). These are *invariant* checks, not exact-output ones: the
// live BASE API returns different records run-to-run, so we cannot pin the
// cleaned string. Instead we assert that no recognised classification marker or
// MeSH qualifier survives anywhere in `subject` or in an area title, whatever
// BASE happens to return. Exact input -> output is covered deterministically by
// the R unit tests (other-scripts/test/test_subject_cleaning.R).
//
// Vocabulary-list schemes that carry no inline marker (e.g. the Toulouse
// letter-domain list) cannot be detected by pattern without false positives, so
// they are out of scope here and remain covered by the unit tests only.
//
// Deliberately NOT asserted: the "anzsrc-for: 3402 ..." form. In the current
// pipeline an earlier generic gsub strips its "for: ..." tail and leaves an
// orphan "anzsrc-" fragment, so neither the full marker nor a clean drop holds;
// the fix requires refactoring the existing cleanup, so it is left out rather
// than encoding a failing or misleading assertion.

// Classification-scheme markers. Keep in sync with kwlib.SCHEMES / the cleaner.
// Each pattern has been checked against real cleaned `subject` data: it is absent
// after cleaning while the scheme is present in the source `subject_orig`.
export const CLASSIFICATION_MARKERS: { name: string; re: RegExp }[] = [
{ name: "mesh [MeSH]", re: /\[MeSH\]/i },
{ name: "mesh (mesh)", re: /\(mesh\)/i },
{ name: "mesh-chemical", re: /\[Chemical\]/i },
{ name: "rcdc", re: /\(rcdc\)/i },
{ name: "for / for-2020", re: /\(for(-2020)?\)/i },
{ name: "science-metrix", re: /\(science-metrix\)/i },
{ name: "sdg (suffix marker)", re: /\(sdg\)/i },
{ name: "sdg (numbered prefix)", re: /^SDG ?\d+ ?[:.-]/i },
{ name: "hrcs", re: /\(hrcs-[a-z]+\)/i },
{ name: "acm-ccs arrow", re: /→/ },
{ name: "keyvalue name=", re: /^name=/i },
{
name: "hal-shs domain code",
re: /^\[(CHIM|INFO|MATH|NLIN|PHYS|SCCO|SDE|SDU|SDV|SHS|SPI|STAT|QFIN)(\.[A-Z-]+)*\]/,
},
{ name: "ddc", re: /\bddc:\s*\d/i },
{ name: "info:eu-repo", re: /info:eu-repo/i },
{ name: "pure ontology path", re: /\/dk\/atira/i },
{ name: "fos", re: /^FOS:\s/i },
{
name: "arxiv category (dotted)",
re: /\b(cs|econ|eess|math|astro-ph|nlin|q-bio|q-fin|stat)\.[A-Z]{2}\b/,
},
{
name: "arxiv category (bare)",
re: /\b(quant-ph|gr-qc|math-ph|cond-mat|hep-(ex|lat|ph|th)|nucl-(ex|th))\b/,
},
{ name: "url", re: /^https?:\/\// },
{ name: "not elsewhere classified", re: /not elsewhere classified/i },
{ name: "numeric path", re: /^\/[0-9/]+$/ },
// Funder grant / scheme reference, e.g. "SP/19/3/34678", "MR/S003991/1".
// Conservative regression marker: requires 3+ slash-separated alphanumeric
// segments with both a letter and a digit, so 1-slash MeSH/gene forms
// ("COVID-19/epidemiology", "HER-2/neu") and all-digit dates are not matched.
{
name: "grant id (slash code)",
re: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9][A-Za-z0-9-]*(\/[A-Za-z0-9][A-Za-z0-9-]*){2,}$/,
},
];

// MeSH subheadings/qualifiers (mirrors MESH_QUALIFIERS in subject_cleaning.R).
// A leak is a "Descriptor/qualifier" or "Descriptor - qualifier" pair, or a
// space-joined blob of headings, surviving into the cleaned subject.
const MESH_QUALIFIERS = [
"analysis", "blood", "cerebrospinal fluid", "isolation & purification", "urine",
"anatomy & histology", "blood supply", "cytology", "ultrastructure", "embryology",
"abnormalities", "innervation", "pathology", "chemistry", "agonists",
"analogs & derivatives", "antagonists & inhibitors", "chemical synthesis",
"diagnosis", "diagnostic imaging", "etiology", "chemically induced", "complications",
"secondary", "congenital", "genetics", "immunology", "microbiology", "virology",
"parasitology", "transmission", "organization & administration", "economics",
"legislation & jurisprudence", "standards", "supply & distribution", "trends",
"pharmacology", "adverse effects", "poisoning", "toxicity", "pharmacokinetics",
"physiology", "growth & development", "metabolism", "biosynthesis", "deficiency",
"enzymology", "physiopathology", "statistics & numerical data", "epidemiology",
"ethnology", "mortality", "therapeutic use", "therapy", "diet therapy",
"drug therapy", "nursing", "prevention & control", "radiotherapy", "rehabilitation",
"surgery", "transplantation", "classification", "drug effects", "education",
"ethics", "history", "injuries", "instrumentation", "methods", "pathogenicity",
"psychology", "radiation effects", "veterinary",
];
const QUAL = MESH_QUALIFIERS.slice()
.sort((a, b) => b.length - a.length)
.map((q) => q.replace(/[&]/g, "\\$&"))
.join("|");

export const QUALIFIER_MARKERS: { name: string; re: RegExp }[] = [
{ name: "mesh qualifier (slash)", re: new RegExp(`/\\s*\\*?\\s*(?:${QUAL})\\*?\\s*$`, "i") },
{ name: "mesh qualifier (dash)", re: new RegExp(`\\s-\\s\\*?(?:${QUAL})\\*?\\s*$`, "i") },
{ name: "mesh blob (space-joined headings)", re: /\S+\/\S+\s+\S+\/\S+/ },
];

// Library of Congress Classification — clear-cut forms only, as regression
// markers. Deliberately conservative: each pattern matches a form that is
// unambiguously a classification, so an ambiguous true-negative (a non-LCC
// keyword that the cleaner legitimately keeps) does NOT trip it. We do not assert
// the bare-code / code+caption forms here, as those are intentionally ambiguous.
export const LCC_MARKERS: { name: string; re: RegExp }[] = [
// Numeric class-number range, e.g. "QC1-999", "QH301-705.5", "HF5001-6182".
// Both sides are pure digits, so MRI sequences ("T1-T2") and cell markers
// ("CD4-CD8") — which have a letter after the dash — are not matched.
{ name: "lcc range", re: /^[A-Z]{1,3}\d{1,4}(\.\d+)?-\d{1,4}(\.\d+)?$/ },
];

const ALL_MARKERS = [...CLASSIFICATION_MARKERS, ...QUALIFIER_MARKERS, ...LCC_MARKERS];

export interface BackendDocument {
subject?: string;
area?: string;
}

// getLatestRevision.php returns { context, data }, where data (a string that
// needs parsing) carries documents[] (itself sometimes a nested JSON string).
// Each document has the cleaned `subject` and the `area` (cluster) title.
export function extractDocuments(json: any): BackendDocument[] {
let data = json?.data;
if (typeof data === "string") {
data = JSON.parse(data);
}
let documents = data?.documents ?? [];
if (typeof documents === "string") {
documents = JSON.parse(documents);
}
return Array.isArray(documents) ? documents : [];
}

export function splitKeywords(subject: string | undefined): string[] {
return String(subject ?? "")
.split(";")
.map((s) => s.trim())
.filter(Boolean);
}

// Assert that no classification marker or MeSH qualifier survives in any
// `subject` keyword or in any area title. `label` identifies the search (ORCID /
// query) in the failure message. An empty document set trivially passes.
export function assertNoClassificationLeaks(
documents: BackendDocument[],
label: string,
): void {
const keywords = documents.flatMap((d) => splitKeywords(d.subject));
for (const kw of keywords) {
for (const { name, re } of ALL_MARKERS) {
expect(
re.test(kw),
`subject keyword "${kw}" (${label}) still carries the ${name} marker`,
).toBe(false);
}
}

const areaTitles = [
...new Set(documents.map((d) => d.area).filter(Boolean) as string[]),
];
for (const title of areaTitles) {
for (const { name, re } of ALL_MARKERS) {
expect(
re.test(title),
`area title "${title}" (${label}) still carries the ${name} marker`,
).toBe(false);
}
}
}
Loading
Loading