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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
@@ -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,2 @@
1
+ [ose.plugins]
2
+ hierarchical_spreadsheets = ose_plugin_hierarchical_spreadsheets:HierarchicalSpreadsheetsPlugin