ose-plugin-hierarchical-spreadsheets 0.2.5__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.
- ose_plugin_hierarchical_spreadsheets-0.2.5/PKG-INFO +34 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/README.md +26 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/pyproject.toml +23 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/setup.cfg +4 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets/GenerateHierarchicalSpreadsheetReleaseStep.py +185 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets/__init__.py +14 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets.egg-info/PKG-INFO +34 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets.egg-info/SOURCES.txt +10 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets.egg-info/dependency_links.txt +1 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets.egg-info/entry_points.txt +2 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets.egg-info/requires.txt +1 -0
- ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ose-plugin-hierarchical-spreadsheets
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: OntoSpreadEd plugin for hierarchical spreadsheet generation
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: ose-core==0.2.5
|
|
8
|
+
|
|
9
|
+
# OSE Plugin: Hierarchical Spreadsheets
|
|
10
|
+
|
|
11
|
+
OntoSpreadEd plugin for generating hierarchical spreadsheet exports during ontology releases.
|
|
12
|
+
|
|
13
|
+
## Description
|
|
14
|
+
|
|
15
|
+
This plugin adds a release step that generates hierarchical spreadsheet representations of ontologies. It exports ontology structures in a format that preserves parent-child relationships in an easy-to-read tabular format.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install ose-plugin-hierarchical-spreadsheets
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Python 3.12+
|
|
26
|
+
- ose-core
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Add the `GenerateHierarchicalSpreadsheetReleaseStep` to your release script configuration to include hierarchical spreadsheet generation in your release pipeline.
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
LGPL-3.0-or-later
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# OSE Plugin: Hierarchical Spreadsheets
|
|
2
|
+
|
|
3
|
+
OntoSpreadEd plugin for generating hierarchical spreadsheet exports during ontology releases.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
This plugin adds a release step that generates hierarchical spreadsheet representations of ontologies. It exports ontology structures in a format that preserves parent-child relationships in an easy-to-read tabular format.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install ose-plugin-hierarchical-spreadsheets
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Python 3.12+
|
|
18
|
+
- ose-core
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Add the `GenerateHierarchicalSpreadsheetReleaseStep` to your release script configuration to include hierarchical spreadsheet generation in your release pipeline.
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
LGPL-3.0-or-later
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ose-plugin-hierarchical-spreadsheets"
|
|
7
|
+
version = "0.2.5"
|
|
8
|
+
description = "OntoSpreadEd plugin for hierarchical spreadsheet generation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"ose-core==0.2.5",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.entry-points.'ose.plugins']
|
|
16
|
+
hierarchical_spreadsheets = "ose_plugin_hierarchical_spreadsheets:HierarchicalSpreadsheetsPlugin"
|
|
17
|
+
|
|
18
|
+
[tool.uv.sources]
|
|
19
|
+
ose-core = { workspace = true }
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.packages.find]
|
|
22
|
+
where = ["src"]
|
|
23
|
+
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import re
|
|
3
|
+
from typing import List, Optional, Dict, Callable, Any, Tuple
|
|
4
|
+
|
|
5
|
+
import openpyxl
|
|
6
|
+
import pyhornedowl
|
|
7
|
+
from flask_github import GitHub
|
|
8
|
+
from flask_sqlalchemy import SQLAlchemy
|
|
9
|
+
from typing_extensions import Self
|
|
10
|
+
|
|
11
|
+
from ose.release.ReleaseStep import ReleaseStep
|
|
12
|
+
from ose.model.ReleaseScript import ReleaseScript, ReleaseScriptFile
|
|
13
|
+
from ose.model.Result import Result
|
|
14
|
+
from ose.services.ConfigurationService import ConfigurationService
|
|
15
|
+
from ose.utils import letters
|
|
16
|
+
from ose.utils.github import parse_spreadsheet
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclasses.dataclass
|
|
20
|
+
class Node:
|
|
21
|
+
item: str
|
|
22
|
+
label: str
|
|
23
|
+
definition: str
|
|
24
|
+
children: List[Self] = dataclasses.field(default_factory=list)
|
|
25
|
+
parent: Optional[Self] = None
|
|
26
|
+
annotations: Dict[str, str] = dataclasses.field(default_factory=dict)
|
|
27
|
+
|
|
28
|
+
def to_plain(self):
|
|
29
|
+
plain_children = []
|
|
30
|
+
for c in self.children:
|
|
31
|
+
plain_children.append(c.to_plain())
|
|
32
|
+
|
|
33
|
+
return dict(item=self.item, label=self.label, definition=self.definition, children=plain_children)
|
|
34
|
+
|
|
35
|
+
def height(self) -> int:
|
|
36
|
+
return max((c.height() for c in self.children), default=0) + 1
|
|
37
|
+
|
|
38
|
+
def recurse(self, fn: Callable[[Self], Any]):
|
|
39
|
+
fn(self)
|
|
40
|
+
for c in self.children:
|
|
41
|
+
c.recurse(fn)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def form_tree(edges: List[Tuple[Tuple[str, str | None, str | None], Optional[str]]]) -> List[Node]:
|
|
45
|
+
all_nodes = set(n for n, _ in edges)
|
|
46
|
+
item_to_node = dict((c, Node(item=c, label=lbl if lbl is not None else c, definition=d or "<no definition>")) for (c, lbl, d) in all_nodes)
|
|
47
|
+
|
|
48
|
+
for (child, _, _), parent in edges:
|
|
49
|
+
if parent is None:
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
if child == parent:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
child_node = item_to_node[child]
|
|
56
|
+
parent_node = item_to_node.setdefault(parent, Node(item=parent, label=parent, definition=""))
|
|
57
|
+
|
|
58
|
+
child_node.parent = parent_node
|
|
59
|
+
parent_node.children.append(child_node)
|
|
60
|
+
|
|
61
|
+
return [n for n in item_to_node.values() if n.parent is None]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class GenerateHierarchicalSpreadsheetReleaseStep(ReleaseStep):
|
|
65
|
+
|
|
66
|
+
def __init__(self, db: SQLAlchemy, gh: GitHub, release_script: ReleaseScript, release_id: int, tmp: str,
|
|
67
|
+
config: ConfigurationService, *, included_files: Dict[str, str]) -> None:
|
|
68
|
+
super().__init__(db, gh, release_script, release_id, tmp, config)
|
|
69
|
+
|
|
70
|
+
self._included_files = included_files
|
|
71
|
+
|
|
72
|
+
def run(self) -> bool:
|
|
73
|
+
result = Result(())
|
|
74
|
+
|
|
75
|
+
files = [f for k, f in self._release_script.files.items() if k in self._included_files]
|
|
76
|
+
self._total_items = len(files)
|
|
77
|
+
|
|
78
|
+
for file in files:
|
|
79
|
+
self._next_item(item=file.target.file, message="Generating hierarchical spreadsheet for")
|
|
80
|
+
|
|
81
|
+
hierarchies, ontology = self.build_hierarchy(file)
|
|
82
|
+
|
|
83
|
+
wb = openpyxl.Workbook()
|
|
84
|
+
assert wb.active is not None
|
|
85
|
+
sheet = wb.active
|
|
86
|
+
|
|
87
|
+
height = max(h.height() for h in hierarchies)
|
|
88
|
+
annotations = list({k for h in hierarchies for k in h.annotations.keys()})
|
|
89
|
+
|
|
90
|
+
sheet.append(["ID", "Label"] + [""] * (height - 1) + ["Definition"] + annotations)
|
|
91
|
+
|
|
92
|
+
def write_line(n: Node, depth: int) -> None:
|
|
93
|
+
sheet.append([ontology.get_id_for_iri(n.item)] +
|
|
94
|
+
[""] * depth +
|
|
95
|
+
[n.label] + [""] * (height - depth - 1) +
|
|
96
|
+
[n.definition] +
|
|
97
|
+
[n.annotations.get(a, None) for a in annotations])
|
|
98
|
+
|
|
99
|
+
for child in n.children:
|
|
100
|
+
write_line(child, depth + 1)
|
|
101
|
+
|
|
102
|
+
for hierarchy in hierarchies:
|
|
103
|
+
write_line(hierarchy, 0)
|
|
104
|
+
|
|
105
|
+
[path, name] = file.target.file.rsplit("/", 1)
|
|
106
|
+
sub_name = name.rsplit(".", 1)[0]
|
|
107
|
+
sub_name = re.sub(f"^{self._release_script.short_repository_name}[_]?", "", sub_name, flags=re.IGNORECASE)
|
|
108
|
+
|
|
109
|
+
file_name = f"{self._release_script.short_repository_name}-{sub_name}-hierarchy.xlsx"
|
|
110
|
+
|
|
111
|
+
wb.save(self._local_name(file_name))
|
|
112
|
+
|
|
113
|
+
self._store_artifact(self._local_name(file_name), f"{path}/{file_name}")
|
|
114
|
+
|
|
115
|
+
result.warnings = []
|
|
116
|
+
self._set_release_result(result)
|
|
117
|
+
return result.ok()
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def name(cls) -> str:
|
|
121
|
+
return "HIERARCHICAL_SPREADSHEETS"
|
|
122
|
+
|
|
123
|
+
def build_hierarchy(self, file: ReleaseScriptFile) -> Tuple[List[Node], pyhornedowl.PyIndexedOntology]:
|
|
124
|
+
# Excel files to extract annotations
|
|
125
|
+
excel_files: List[str]
|
|
126
|
+
release_file: str
|
|
127
|
+
|
|
128
|
+
excel_files = [self._local_name(s.file) for s in file.sources]
|
|
129
|
+
release_file = next((a.local_path for a in self._artifacts()
|
|
130
|
+
if a.target_path == file.target.file and a.kind == 'final'),
|
|
131
|
+
self._local_name(file.target.file))
|
|
132
|
+
|
|
133
|
+
ontology = pyhornedowl.open_ontology_from_file(release_file)
|
|
134
|
+
|
|
135
|
+
for p, d in self._repo_config.prefixes.items():
|
|
136
|
+
ontology.prefix_mapping.add_prefix(p, d)
|
|
137
|
+
|
|
138
|
+
classes = [(c, ontology.get_annotation(c, "http://www.w3.org/2000/01/rdf-schema#label"),
|
|
139
|
+
ontology.get_annotation(c, "http://purl.obolibrary.org/obo/IAO_0000115")) for c in
|
|
140
|
+
ontology.get_classes()]
|
|
141
|
+
child_parent: List[Tuple[Tuple[str, Optional[str], Optional[str]], Optional[str]]] = []
|
|
142
|
+
for c in classes:
|
|
143
|
+
for p in ontology.get_superclasses(c[0]):
|
|
144
|
+
child_parent.append((c, p))
|
|
145
|
+
else:
|
|
146
|
+
child_parent.append((c, None))
|
|
147
|
+
|
|
148
|
+
# child_parent = [(c, p) for c in classes for p in ontology.get_superclasses(c[0])]
|
|
149
|
+
hierarchies = form_tree(child_parent)
|
|
150
|
+
|
|
151
|
+
# If we do not collapse (or do not import) imported ontologies a root will always contain no label
|
|
152
|
+
# We remove these roots as they only indicate where the subontology should be mounted in the overall ontology
|
|
153
|
+
hierarchies = [c for h in hierarchies for c in h.children]
|
|
154
|
+
|
|
155
|
+
for excel_file in excel_files:
|
|
156
|
+
with open(excel_file, "rb") as f:
|
|
157
|
+
file_data = f.read()
|
|
158
|
+
|
|
159
|
+
rows, header = parse_spreadsheet(file_data)
|
|
160
|
+
|
|
161
|
+
data = dict((r["ID"], r) for r in rows if "ID" in r)
|
|
162
|
+
|
|
163
|
+
def annotate(n: Node):
|
|
164
|
+
id = ontology.get_id_for_iri(n.item)
|
|
165
|
+
if id is None:
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
fields = {
|
|
169
|
+
"comment": "Comment",
|
|
170
|
+
"subontology": "Sub-ontology",
|
|
171
|
+
"examples": "Examples",
|
|
172
|
+
"synonyms": "Synonyms",
|
|
173
|
+
"crossreference": "Cross reference",
|
|
174
|
+
"informaldefinition": "Informal definition",
|
|
175
|
+
}
|
|
176
|
+
for field_key, field in fields.items():
|
|
177
|
+
node_data = data.get(id, dict())
|
|
178
|
+
key = next((k for k in node_data.keys() if letters(k) == field_key), None)
|
|
179
|
+
if key is not None and n.annotations.get(field, None) is None:
|
|
180
|
+
n.annotations[field] = node_data[key]
|
|
181
|
+
|
|
182
|
+
for h in hierarchies:
|
|
183
|
+
h.recurse(annotate)
|
|
184
|
+
|
|
185
|
+
return hierarchies, ontology
|
ose_plugin_hierarchical_spreadsheets-0.2.5/src/ose_plugin_hierarchical_spreadsheets/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from ose.model.Plugin import Plugin
|
|
2
|
+
from .GenerateHierarchicalSpreadsheetReleaseStep import GenerateHierarchicalSpreadsheetReleaseStep
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
HierarchicalSpreadsheetsPlugin = Plugin(
|
|
7
|
+
id="org.bssofoundry.hierarchicalspreadsheets",
|
|
8
|
+
name="HierarchicalSpreadsheet Plugin",
|
|
9
|
+
version="0.1.0",
|
|
10
|
+
description="Plugin to generate hierarchical spreadsheets during release.",
|
|
11
|
+
contents=[
|
|
12
|
+
GenerateHierarchicalSpreadsheetReleaseStep,
|
|
13
|
+
],
|
|
14
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ose-plugin-hierarchical-spreadsheets
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary: OntoSpreadEd plugin for hierarchical spreadsheet generation
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: ose-core==0.2.5
|
|
8
|
+
|
|
9
|
+
# OSE Plugin: Hierarchical Spreadsheets
|
|
10
|
+
|
|
11
|
+
OntoSpreadEd plugin for generating hierarchical spreadsheet exports during ontology releases.
|
|
12
|
+
|
|
13
|
+
## Description
|
|
14
|
+
|
|
15
|
+
This plugin adds a release step that generates hierarchical spreadsheet representations of ontologies. It exports ontology structures in a format that preserves parent-child relationships in an easy-to-read tabular format.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install ose-plugin-hierarchical-spreadsheets
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Python 3.12+
|
|
26
|
+
- ose-core
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Add the `GenerateHierarchicalSpreadsheetReleaseStep` to your release script configuration to include hierarchical spreadsheet generation in your release pipeline.
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
LGPL-3.0-or-later
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/ose_plugin_hierarchical_spreadsheets/GenerateHierarchicalSpreadsheetReleaseStep.py
|
|
4
|
+
src/ose_plugin_hierarchical_spreadsheets/__init__.py
|
|
5
|
+
src/ose_plugin_hierarchical_spreadsheets.egg-info/PKG-INFO
|
|
6
|
+
src/ose_plugin_hierarchical_spreadsheets.egg-info/SOURCES.txt
|
|
7
|
+
src/ose_plugin_hierarchical_spreadsheets.egg-info/dependency_links.txt
|
|
8
|
+
src/ose_plugin_hierarchical_spreadsheets.egg-info/entry_points.txt
|
|
9
|
+
src/ose_plugin_hierarchical_spreadsheets.egg-info/requires.txt
|
|
10
|
+
src/ose_plugin_hierarchical_spreadsheets.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ose-core==0.2.5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ose_plugin_hierarchical_spreadsheets
|