Skip to content

Commit 38faf18

Browse files
authored
Merge pull request #33 from linkml/fix-funowl-version
fix funowl version
2 parents 55784b8 + 816062d commit 38faf18

File tree

12 files changed

+1853
-405
lines changed

12 files changed

+1853
-405
lines changed

.github/workflows/pypi-publish.yaml

Lines changed: 24 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,28 @@ jobs:
99
name: Build and publish Python 🐍 distributions 📦 to PyPI
1010
runs-on: ubuntu-latest
1111

12-
#----------------------------------------------
13-
# check-out repo and set-up python
14-
#----------------------------------------------
1512
steps:
16-
- uses: actions/checkout@v2
17-
18-
- name: Set up Python
19-
uses: actions/setup-python@v2.2.2
20-
with:
21-
python-version: 3.9
22-
23-
#----------------------------------------------
24-
# install & configure poetry
25-
#----------------------------------------------
26-
- name: Install Poetry
27-
uses: snok/install-poetry@v1.1.6
28-
with:
29-
virtualenvs-create: true
30-
virtualenvs-in-project: true
31-
32-
#----------------------------------------------
33-
# install dependencies
34-
#----------------------------------------------
35-
- name: Install dependencies
36-
run: poetry install --no-interaction
37-
38-
#----------------------------------------------
39-
# build dist
40-
#----------------------------------------------
41-
- name: Build source and wheel archives
42-
run: |
43-
poetry version $(git describe --tags --abbrev=0)
44-
poetry build
45-
46-
#----------------------------------------------
47-
# publish package to PyPI
48-
#----------------------------------------------
49-
- name: Publish distribution 📦 to PyPI
50-
uses: pypa/gh-action-pypi-publish@v1.2.2
51-
with:
52-
user: __token__
53-
password: ${{ secrets.PYPI_PASSWORD }}
13+
- uses: actions/checkout@v3.1.0
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v4
17+
with:
18+
python-version: 3.9
19+
20+
- name: Install Poetry
21+
run: |
22+
pip install poetry
23+
poetry self add "poetry-dynamic-versioning[plugin]"
24+
25+
# - name: Install dependencies
26+
# run: poetry install --no-interaction
27+
28+
- name: Build source and wheel archives
29+
run: |
30+
poetry build
31+
32+
- name: Publish distribution 📦 to PyPI
33+
uses: pypa/gh-action-pypi-publish@v1.5.0
34+
with:
35+
user: __token__
36+
password: ${{ secrets.pypi_password }}

docs/usage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Minimally you need to specify two inputs:
2222
To convert:
2323

2424
```bash
25-
linkml-data2owl -s my_schema.yaml my_data.{yaml,json,tsv,rdf} -o my_ontology.owl.ttl
25+
linkml-data2owl -s my_schema.yaml my_data.{yaml,json,tsv,rdf} -o my_ontology.ofn
2626
```
2727

2828
For all options, see:

linkml_owl/dumpers/owl_dumper.py

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from collections import defaultdict
23
from typing import Optional, List, Set, Any, Union, Dict, Tuple
34
from dataclasses import dataclass, field
@@ -15,7 +16,7 @@
1516
from linkml_runtime.index.object_index import ObjectIndex
1617
from linkml_runtime.utils.inference_utils import infer_all_slot_values, infer_slot_value, Config
1718

18-
from rdflib import URIRef
19+
from rdflib import URIRef, Graph
1920

2021
from linkml_runtime.linkml_model.meta import ClassDefinition, SchemaDefinition, SlotDefinition, Definition, \
2122
ClassDefinitionName
@@ -168,20 +169,26 @@ def to_ontology_document(self, element: Union[YAMLRoot, List[YAMLRoot]], schema:
168169
doc.prefixDeclarations.append(Prefix(pfx.prefix_prefix, pfx.prefix_reference))
169170
return doc
170171

171-
def dumps(self, element: YAMLRoot, schema: SchemaDefinition = None, schemaview: SchemaView = None, iri=None) -> str:
172+
def dumps(self, element: YAMLRoot, schema: SchemaDefinition = None, schemaview: SchemaView = None, iri=None, output_type=None) -> str:
172173
"""
173174
Dump a linkml instance tree to a function syntax OWL ontology string
174175
175176
:param element:
176177
:param schema:
177178
:param schemaview:
178179
:param iri:
180+
:param output_type:
179181
:return:
180182
"""
181183
if schemaview:
182184
schema = schemaview.schema
183185
doc = self.to_ontology_document(element, schema, iri=iri)
184-
return str(doc)
186+
if output_type == "ttl":
187+
g = Graph()
188+
doc.to_rdf(g)
189+
return g.serialize(format="ttl")
190+
else:
191+
return str(doc)
185192

186193
def transform(self, element: YAMLRoot, schema: SchemaDefinition, is_element_an_object=True) -> Any:
187194
"""
@@ -296,7 +303,7 @@ def transform(self, element: YAMLRoot, schema: SchemaDefinition, is_element_an_o
296303
axiom_annotations.append(Annotation(ann_slot_iri, ann_val))
297304
# templates
298305
for tmpl in owl_templates:
299-
self.add_axioms_from_template(tmpl, element)
306+
self.add_axioms_from_template(tmpl, element, schema=schema)
300307
if schema_level_slot.slot_uri is not None:
301308
slot_uri = self._get_IRI_str(schema_level_slot.slot_uri)
302309
else:
@@ -524,8 +531,9 @@ def _get_inferred_slot_annotations(self, slot: SlotDefinition, ann_key: str,
524531
anc_slots = [slot]
525532
sv = self.schemaview
526533
for anc_c in sv.class_ancestors(class_name, reflexive=True):
527-
induced_slot = sv.induced_slot(slot.name, anc_c)
528-
anc_slots.append(induced_slot)
534+
if slot.name in sv.class_slots(anc_c):
535+
induced_slot = sv.induced_slot(slot.name, anc_c)
536+
anc_slots.append(induced_slot)
529537
for a in sv.slot_ancestors(slot.name, reflexive=True):
530538
anc_slots.append(sv.get_slot(a))
531539
for s in anc_slots:
@@ -634,20 +642,20 @@ def parse_axioms_string(self, owl_str: str, schemaview: SchemaView = None) -> On
634642
for prefix, url in schemaview.namespaces().items():
635643
prefix_lines.append(f'Prefix( {prefix}: = <{url}> )')
636644
header = "\n".join(prefix_lines)
637-
owl_str = f'{header}\nOntology(\n{owl_str}\n)'
645+
owl_str = f'{header}\nOntology(<http://example.org>\n{owl_str}\n)'
638646
logging.debug(owl_str)
639647
try:
640648
doc = to_python(owl_str)
641649
except Exception as e:
642650
logging.error(f'Error parsing generated OWL: {owl_str}')
643651
raise e
644-
from funowl.writers.FunctionalWriter import FunctionalWriter
645-
from rdflib import Graph
646-
g = Graph()
647-
for p in doc.prefixDeclarations:
648-
g.namespace_manager.bind(p.prefixName, p.fullIRI)
649-
fw = FunctionalWriter(g=g)
650-
owl_str_roundtrip = doc.to_functional(fw)
652+
#from funowl.writers.FunctionalWriter import FunctionalWriter
653+
#from rdflib import Graph
654+
#g = Graph()
655+
#for p in doc.prefixDeclarations:
656+
# g.namespace_manager.bind(p.prefixName, p.fullIRI)
657+
#fw = FunctionalWriter(g=g)
658+
#owl_str_roundtrip = doc.to_functional(fw)
651659
#logging.debug(f'ROUNDTRIP = {owl_str_roundtrip}')
652660
return doc
653661

@@ -686,6 +694,12 @@ def tr(e: YAMLRoot):
686694
else:
687695
d["tr"] = tr
688696
jt = Template(tstr)
697+
698+
def _tr(x):
699+
fw = FunctionalWriter()
700+
expr = self.transform(x, schema)
701+
return expr.to_functional(fw)
702+
d["tr"] = _tr
689703
owl_str = jt.render(**d)
690704
axioms = self.parse_axioms_string(owl_str).ontology.axioms
691705
self.ontology.axioms += axioms
@@ -724,34 +738,42 @@ def populate_missing_values(self, element: YAMLRoot):
724738
help="Path to python datamodel module")
725739
@click.option("--format", "-f",
726740
help="Input format (will be inferred from file suffix if not specified)")
727-
@click.option('-o', '--output', required=True,
741+
@click.option('-o', '--output',
742+
type=click.File(mode="w"),
743+
default=sys.stdout,
728744
help="Path to OWL functional syntax output")
745+
@click.option('-O', '--output-type',
746+
type=click.Choice(["ofn", "ttl"]),
747+
help="Output format")
729748
@click.option("--autofill/--no-autofill",
730749
default=False,
731750
show_default=True,
732751
help="If True, fill missing data slots using string_serialization")
733752
@click.argument('inputfile')
734-
def cli(inputfile: str, schema: str, target_class, module, output, format, autofill: bool, verbose: int, quiet: bool, **args):
753+
def cli(inputfile: str, schema: str, target_class, module, output, output_type, format, autofill: bool, verbose: int, quiet: bool, **args):
735754
"""
736755
Dump LinkML instance data as OWL
737756
738757
Examples:
739758
740759
Convert a CSV to OWL
741760
742-
linkml-data2owl -s tests/inputs/owl_dumper_test.yaml tests/inputs/parts.csv -o parts.ofn
761+
linkml-data2owl -s owl_dumper_test.yaml parts.csv -o parts.ofn
743762
744-
Note in this example, there must be a class type designator column `@type` in the CSV
763+
Note in this example, there must be a class type designator column `@type` in the CSV
745764
746765
Convert a CSV to OWL, homogeneous classes:
747766
748-
linkml-data2owl -C EquivGenusAndPartOf -s tests/inputs/owl_dumper_test.yaml \
749-
tests/inputs/parts_implicit_type.csv -o parts.ofn
767+
linkml-data2owl -C EquivGenusAndPartOf -s owl_dumper_test.yaml \
768+
parts_implicit_type.csv -o parts.ofn
750769
751770
Convert YAML or JSON to OWL:
752771
753-
linkml-data2owl -s tests/inputs/owl_dumper_test.yaml tests/inputs/owl_dumper_test_data.yaml -o ont.ofn
772+
linkml-data2owl -s owl_dumper_test.yaml owl_dumper_test_data.yaml -o ont.ofn
754773
774+
More documentation:
775+
776+
https://linkml.io/linkml-owl/
755777
"""
756778
logger = logging.getLogger()
757779
if verbose >= 2:
@@ -775,12 +797,8 @@ def cli(inputfile: str, schema: str, target_class, module, output, format, autof
775797
dumper = OWLDumper()
776798
if autofill:
777799
dumper.autofill = True
778-
doc = dumper.dumps(element, schemaview=sv)
779-
if output is None:
780-
print(str(doc))
781-
else:
782-
with open(output, 'w') as stream:
783-
stream.write(str(doc))
800+
doc = dumper.dumps(element, schemaview=sv, output_type=output_type)
801+
output.write(str(doc))
784802

785803

786804
if __name__ == '__main__':

0 commit comments

Comments
 (0)