Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
186 commits
Select commit Hold shift + click to select a range
0a3a101
get abstracts from aop-wiki
rdurnik May 20, 2026
4ccfb7b
prompt to categorize text
rdurnik May 20, 2026
09eeb38
added sparqlwrapper
rdurnik May 20, 2026
1501095
fixed dep
rdurnik May 20, 2026
7687424
Update README.md by removing old badges
rdurnik May 25, 2026
0867fb2
badges update
rdurnik May 25, 2026
b366e29
badges update
rdurnik May 25, 2026
4b000d6
badges update
rdurnik May 25, 2026
77249da
badges update
rdurnik May 25, 2026
fd5c7b3
badges update
rdurnik May 25, 2026
8d3755d
update badges
rdurnik May 25, 2026
0a1cb32
Badges update
rdurnik May 25, 2026
0b61160
Add funding section to README
rdurnik Jun 5, 2026
688c189
Reorganize Funding section in README.md
rdurnik Jun 5, 2026
2adab8b
created dockerfile
rdurnik Jun 8, 2026
2ffa0c4
created .json file
rdurnik Jun 8, 2026
57e0c98
updated readme
rdurnik Jun 8, 2026
0f79654
added dockerignore
rdurnik Jun 8, 2026
12a7f33
removed enfile
rdurnik Jun 9, 2026
ba6d02f
introduced ids as parameters for download
hechth May 6, 2026
f5f5a38
starting update on PMC
hechth May 7, 2026
a5192f3
removed pagination
rdurnik May 7, 2026
19d7af3
europepmc text fix - get_ids is independent
rdurnik May 11, 2026
95e6a9f
keeping query empty to avoid confussion
rdurnik May 11, 2026
2401b86
europepmc mock test updates
rdurnik May 11, 2026
4577d90
lint
rdurnik May 11, 2026
9b58871
pmc tests fix
rdurnik May 11, 2026
0782b19
pubmed ids independent
rdurnik May 11, 2026
df12dbc
lint
rdurnik May 11, 2026
aa2d95f
mypy fixes
rdurnik May 11, 2026
48e8367
lint
rdurnik May 11, 2026
6cf9da9
added type checke to ci
rdurnik May 11, 2026
c48841c
lint
rdurnik May 11, 2026
7411708
removed tests
rdurnik May 11, 2026
2373dfe
ignore type: ignore[assignment] for api keys
rdurnik May 11, 2026
655288a
lint
rdurnik May 11, 2026
49120e2
re-add mypy for tests to CI
rdurnik May 12, 2026
db0826e
mypy for tests fix
rdurnik May 12, 2026
90814ba
lint
rdurnik May 12, 2026
eac30ca
fix pmc could return a path even if download silently fails
rdurnik May 12, 2026
6f2033e
if response.ok replaced with raise_for_status() for europe pmc
rdurnik May 12, 2026
5900585
added option to disable image download
rdurnik May 12, 2026
66cc910
lint
rdurnik May 12, 2026
9cf7b34
added test for empty publications europe pmc
rdurnik May 12, 2026
b683790
images are converted to one format
rdurnik May 12, 2026
31b4d32
added PIL
rdurnik May 12, 2026
99a46ed
fixed pil to pillow
rdurnik May 12, 2026
2445399
fixed util on ci
rdurnik May 12, 2026
f333e1a
added sorted
rdurnik May 12, 2026
a5f30a1
allow for size difference for windows
rdurnik May 12, 2026
84480f5
added rel for utils
rdurnik May 12, 2026
72bb9cb
added option to set up the unified image format
rdurnik May 12, 2026
df9746c
moved relationship_type to proper folder
rdurnik May 12, 2026
a978ac6
lint
rdurnik May 12, 2026
f9ef018
added markxfail
rdurnik May 12, 2026
d15ff0b
removed get_ids from pmc constructor
rdurnik May 13, 2026
feb176e
removed get_ids from pubmed constructor
rdurnik May 13, 2026
4d366de
fixed pubmed mock tests
rdurnik May 13, 2026
06f8ee2
query datastructure draft
rdurnik May 13, 2026
cd0614d
created and implemented query object
rdurnik May 13, 2026
4671bbd
added query for pubmed
rdurnik May 14, 2026
c06f277
fixed europepmc tests being ran multiple times
rdurnik May 14, 2026
e69e694
removed filtering for abs/title to avoid confusion and added option t…
rdurnik May 14, 2026
2eb8746
updated options for pmc
rdurnik May 14, 2026
a204ed5
added tests for pmc
rdurnik May 14, 2026
132815e
changed date to str as the format must include 0 at the start if ther…
rdurnik May 14, 2026
a65f221
updated to str for date in tests
rdurnik May 14, 2026
c29fefb
europepmc filtering fix
rdurnik May 14, 2026
635fc4b
added test for europe pmc filtering
rdurnik May 14, 2026
a436c3c
pubmed filtering update
rdurnik May 14, 2026
af9c1d0
pubmed filter update
rdurnik May 14, 2026
7f609b2
added tests for pubmed filtering
rdurnik May 14, 2026
e4e973e
changed descs
rdurnik May 19, 2026
8cf6b7c
removed unused _get_abstract from pubmed
rdurnik May 21, 2026
5213f80
fixed get metadata to match get abstracts
rdurnik May 21, 2026
fd56b43
lint
rdurnik May 21, 2026
db08633
moved async get ids to helper class
rdurnik May 22, 2026
f5f8859
made ncbi less dependent on pmc
rdurnik May 22, 2026
99190bc
ncbi implements get id interface
rdurnik May 22, 2026
ccf29e7
pubmed is using ncbi get id
rdurnik May 22, 2026
f473a12
removed limit for pubmed
rdurnik May 22, 2026
7bcf7d1
removed maximum_results var
rdurnik May 22, 2026
49732af
shared get_abstract in ncbi
rdurnik May 22, 2026
25cbfa3
publication metadata moved to ncbi
rdurnik May 22, 2026
6e9dc23
removed leftover
rdurnik May 22, 2026
e7efa60
removed search term from ncbi constructor
rdurnik May 22, 2026
ad3f673
prep for get abstract for pmc
rdurnik May 22, 2026
5f5cb9d
fix pubmed mock tests
rdurnik May 22, 2026
933ab5d
added test for multiple abstracts pubmed
rdurnik May 25, 2026
c606c7d
tests for pmc get abstracts
rdurnik May 25, 2026
0213dea
pmc get abstract
rdurnik May 25, 2026
d231e13
lint
rdurnik May 25, 2026
dbaa69c
mypy fix
rdurnik May 25, 2026
9269cf6
lint
rdurnik May 25, 2026
e6117b2
accounted for missing abstracts in pmc
rdurnik May 25, 2026
ee27043
increased retries
rdurnik May 25, 2026
c2ee151
moved semaphore and limiter to constructor
rdurnik May 25, 2026
4670763
added max retries fix
rdurnik May 25, 2026
375c3c8
fixed entrez retries
rdurnik May 25, 2026
fac19c7
prep for metadata extension
rdurnik May 25, 2026
0535913
improved for loop
rdurnik May 25, 2026
349d501
removed comment
rdurnik May 25, 2026
7b8832f
lint
rdurnik May 25, 2026
f327302
initial version of get metadata for pmc
rdurnik May 25, 2026
10053ab
mypy fix
rdurnik May 25, 2026
4864c32
test files clean up
rdurnik May 25, 2026
5207b4d
moved pmc prefix trimming to utils
rdurnik May 25, 2026
1ee75ab
updated ratio
rdurnik May 25, 2026
3b7c39e
updated entrez retry
rdurnik May 25, 2026
0c129dc
changed base image test file
rdurnik May 25, 2026
9c1cb59
changed file name
rdurnik May 25, 2026
4edbc7b
fixed test
rdurnik May 25, 2026
af1c6b2
updated metadata for pmc
rdurnik May 26, 2026
9a48951
avoid returning empty metadata
rdurnik May 26, 2026
70eaafa
added test for id
rdurnik May 26, 2026
caecba4
updated metadata for pubmed
rdurnik May 26, 2026
56028ec
europe pmc metadata update
rdurnik May 26, 2026
ef95307
mypy fix
rdurnik May 26, 2026
114a1c6
renamed test-data to test_data
rdurnik May 27, 2026
007b190
moved test data to the same folder
rdurnik May 27, 2026
58a419d
changed desc id
rdurnik May 27, 2026
0b17a17
changed name publication_metadata to metadata
rdurnik May 27, 2026
6ba4e69
lint
rdurnik May 27, 2026
6da3cb0
lint
rdurnik May 27, 2026
a17b7f2
added eq for metadata
rdurnik May 27, 2026
07ecdef
simplified hashing
rdurnik May 27, 2026
1972836
mypy fix
rdurnik May 28, 2026
685743c
reduced image size
rdurnik Jun 2, 2026
cc7def0
added parametrization to image conversion test
rdurnik Jun 2, 2026
ec8cc7a
More image conversion test cases. Note: .tif converted to .png has la…
rdurnik Jun 2, 2026
478f999
additional image format from europe pmc and pmc
rdurnik Jun 2, 2026
06fd269
europe pmc get abstracts correctly returns empty list if none was found
rdurnik Jun 2, 2026
bdb3a3a
increased ncbi sleep between retries
rdurnik Jun 2, 2026
71f59c0
lint
rdurnik Jun 2, 2026
5b5b9d5
readded get pdfs for europe pmc
rdurnik Jun 2, 2026
cb205d9
lint
rdurnik Jun 2, 2026
9992636
fix for empty year and empty authors metadata
rdurnik Jun 2, 2026
f1815c5
ncbi refactor
rdurnik Jun 2, 2026
ac4bbd0
pubmed general id is pmid
rdurnik Jun 2, 2026
03e2265
metadata can be compared against id
rdurnik Jun 2, 2026
1314501
implemented ncbi as private variable
rdurnik Jun 2, 2026
26079d9
removed forgoten attributes pubmed
rdurnik Jun 3, 2026
8812986
fixed ncbi desc
rdurnik Jun 3, 2026
432b099
extended examples
hechth Jun 2, 2026
70dcf6b
slight refactor
hechth Jun 2, 2026
2107f8a
limited error handling
hechth Jun 3, 2026
3442913
updated parsing logic and image conversions
hechth Jun 3, 2026
b339afe
updated gitignore and merged local changes
hechth Jun 10, 2026
1e217b0
updated example scripts
hechth Jun 10, 2026
5bbfabc
allowed multiple relationship types and updated chemicals
hechth Jun 11, 2026
c271dba
removed .full_text from string in pmc
rdurnik Jun 11, 2026
22a98af
fixed europepmc tests wrong init
rdurnik Jun 11, 2026
d8099bb
removed acetaminophen capital letter at the start
rdurnik Jun 11, 2026
7fb9611
fixed relationship types in tests
rdurnik Jun 11, 2026
44ba03e
test europepmc mypy fix
rdurnik Jun 11, 2026
1537952
lint
rdurnik Jun 11, 2026
1763b7a
removed europepmc commented out code for get_pdfs
rdurnik Jun 11, 2026
91746c2
lint
rdurnik Jun 11, 2026
f64bd5a
examples lint
rdurnik Jun 11, 2026
d7ba933
fixed examples
rdurnik Jun 11, 2026
4faed05
renamed find_chemicals example to find_chemicals_relationships
rdurnik Jun 11, 2026
53069d7
lint
rdurnik Jun 11, 2026
69c1eab
fixed network errors and added try except blocks europe pmc
rdurnik Jun 11, 2026
3d4739e
try except blocks pubmed and pmc
rdurnik Jun 11, 2026
7288ae3
mypy fix
rdurnik Jun 11, 2026
8b840ba
added saving text to files
rdurnik Jun 11, 2026
8984162
updated mock test pubmed
rdurnik Jun 11, 2026
a4cf022
updated examples
rdurnik Jun 11, 2026
b7a118c
lint
rdurnik Jun 11, 2026
0a91009
lint
rdurnik Jun 11, 2026
c4e5b9e
changed example to pmc
rdurnik Jun 11, 2026
874eb32
added encoding to fix windows fail
rdurnik Jun 11, 2026
0cc7ce4
bump version
rdurnik Jun 11, 2026
192a8a8
added sparqlwrapper
rdurnik May 20, 2026
a57121a
updated prompt categorization
rdurnik Jun 16, 2026
8954632
added interface
rdurnik Jun 16, 2026
cb66b75
added categorization text generation
rdurnik Jun 16, 2026
1586ee1
added tests
rdurnik Jun 16, 2026
028e204
added prompt specification for categorization
rdurnik Jun 16, 2026
7c9692b
updated categorization prompt
rdurnik Jun 16, 2026
3946938
modified get abstracts to not include empty abstracts
rdurnik Jun 16, 2026
33ed55d
updated prompt categorization
rdurnik Jun 17, 2026
e4be8e4
added example for aop-wiki
rdurnik Jun 17, 2026
ef90703
Merge branch 'main' into rdurnik/issue117
rdurnik Jun 17, 2026
34cb7c1
lint
rdurnik Jun 17, 2026
4a8423a
updated aop-wiki tests
rdurnik Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
124 changes: 124 additions & 0 deletions examples/aop_categorization/aop_wiki_organs.ipynb

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions examples/aop_categorization/prompt_specification.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The text describes an Adverse Outcome Pathway (AOP). Classify the AOP according to its target organ / system.

Category other includes AOPs not associated with a specific organ system, including changes in body weight, decreased survival, and other nonspecific toxicity.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies = [
"tenacity",
"boto3>=1.43.2",
"pillow",
"sparqlwrapper",
]
description = "Tools to support data mining for the development of (q)AOPs"
keywords = ["AOP"," toxicology"," data mining"," ai"," machine learning"," data analysis"]
Expand Down
20 changes: 20 additions & 0 deletions src/aoptk/literature/categorize_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import annotations
from abc import ABC
from abc import abstractmethod


class CategorizeText(ABC):
"""Abstract base class for categorizing text."""

@abstractmethod
def categorize_text(self, text: str, categories: list[str]) -> str | None:
"""Categorize the given text into one of the specified categories.

Args:
text (str): The text to categorize.
categories (list[str]): The list of available categories.

Returns:
str | None: The categorized label or None if no match is found.
"""
...
54 changes: 54 additions & 0 deletions src/aoptk/literature/databases/aop_wiki.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from SPARQLWrapper import JSON
from SPARQLWrapper import SPARQLWrapper
from aoptk.literature.abstract import Abstract
from aoptk.literature.get_abstract import GetAbstract
from aoptk.literature.id import ID


class AOPWiki(GetAbstract):
"""Class to extract data from AOP-Wiki."""

endpoint_url = "https://aopwiki.rdf.bigcat-bioinformatics.org/sparql"
prefix = """PREFIX aop: <http://aopkb.org/aop_ontology#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX dcterms: <http://purl.org/dc/terms/>"""
abstract_query = (
prefix
+ """

SELECT ?AOP ?AOPTitle ?abstract

WHERE {
?AOP a aop:AdverseOutcomePathway ;
dc:title ?AOPTitle .
OPTIONAL { ?AOP dcterms:abstract ?abstract. }
}
"""
)

def __init__(
self,
):
self.sparql = SPARQLWrapper(self.endpoint_url)
self.sparql.setReturnFormat(JSON)

def get_abstracts(self) -> list[Abstract]:

Check failure on line 35 in src/aoptk/literature/databases/aop_wiki.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add missing parameters ids.

See more on https://sonarcloud.io/project/issues?id=rdurnik_aoptk&issues=AZ7UslOBTDSzUabIwvlA&open=AZ7UslOBTDSzUabIwvlA&pullRequest=136
"""Get abstracts of AOPs from AOP-Wiki.

Returns:
list[Abstract]: List of abstracts.
"""
self.sparql.setQuery(self.abstract_query)
results = self.sparql.query().convert()

abstracts: list[Abstract] = []
for result in results["results"]["bindings"]:
aop_id = result.get("AOP", {}).get("value")
title = result.get("AOPTitle", {}).get("value", "")
abstract = result.get("abstract", {}).get("value", "")
if abstract == "":
continue
text = f"{title}\n\n{abstract}".strip()
abstracts.append(Abstract(id=ID(aop_id), text=text))

return abstracts
14 changes: 14 additions & 0 deletions src/aoptk/prompts/categorize_text_prompt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Task:
Categorize the provided text into exactly one category from the following list:
{{ categories }}

{{ specification_categorization_prompt }}

Output requirements:
1. Return exactly one category label. Treat all category names as fixed labels, not words. Do not modify, stem, normalize, correct, interpret, paraphrase, or transform labels in any way. Output the label exactly as provided, character-for-character.
2. Do not include explanations, labels, punctuation, or additional text.
3. If there is no fitting category, return exactly:
"none"

Text:
{{ text }}
20 changes: 20 additions & 0 deletions src/aoptk/text_generation_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ class TextGenerationAPI(
convert_pdf_scan_prompt_template: str = "convert_pdf_scan_prompt.txt"
convert_image_prompt_template: str = "convert_image_prompt.txt"
find_relevant_publications_prompt_template: str = "find_relevant_publications_prompt.txt"
categorize_text_prompt_template: str = "categorize_text_prompt.txt"

specification_relationship_text_prompt: str = ""
specification_categorization_prompt: str = ""

def __init__(
self,
Expand Down Expand Up @@ -465,3 +467,21 @@ def find_relevant_publications(self, question: str, text: str) -> bool | None:
if response == "no":
return False
return None

def categorize_text(self, text: str, categories: list[str]) -> str | None:
"""Categorize the given text into one of the specified categories.

Args:
text (str): The text to categorize.
categories (list[str]): The list of available categories.

Returns:
str | None: The categorized label or None if no match is found.
"""
if response := self._prompt(
self._render_prompt(self.categorize_text_prompt_template, text=text, categories=", ".join(categories)),
).lower():
if response == "none":
return None
return response
return None
26 changes: 26 additions & 0 deletions tests/test_aop_wiki.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import annotations
from aoptk.literature.databases.aop_wiki import AOPWiki
from aoptk.literature.get_abstract import GetAbstract
from aoptk.literature.id import ID


def test_can_create():
"""Test that AOPWiki can be instantiated."""
actual = AOPWiki()
assert actual is not None


def test_implements_interface():
"""Test that AOPWiki implements GetAbstract interface."""
assert issubclass(AOPWiki, GetAbstract)


def test_get_abstracts():
"""Test that AOP 38 has the correct title."""
abstracts = AOPWiki().get_abstracts()
aop_38 = next(abstract for abstract in abstracts if abstract.id == ID("https://identifiers.org/aop/38"))
min_length_of_abstract = 2000
min_number_of_abstracts = 300
assert aop_38.text.startswith("Protein Alkylation leading to Liver Fibrosis")
assert len(aop_38.text) > min_length_of_abstract
assert len(abstracts) > min_number_of_abstracts
22 changes: 22 additions & 0 deletions tests/test_text_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,25 @@ def test_find_relevant_publications(question: str, text: str, expected: bool):
"""Test that find_relevant_publications method finds relevant publications."""
actual = TextGenerationAPI().find_relevant_publications(question=question, text=text)
assert actual == expected


@pytest.mark.openai
@pytest.mark.parametrize(
("text", "categories", "expected"),
[
(
"This text is about liver.",
["Liver", "Heart", "Kidney"],
"liver",
),
(
"This text is not about any organ.",
["Liver", "Heart", "Kidney"],
None,
),
],
)
def test_categorize_text(text: str, categories: list[str], expected: str | None):
"""Test that categorize_text method categorizes text correctly."""
actual = TextGenerationAPI().categorize_text(text=text, categories=categories)
assert actual == expected
Loading