diff --git a/examples/ontology-from-excel/make_microstructure_onto.py b/examples/ontology-from-excel/make_microstructure_onto.py index 418d812b7..19664e507 100755 --- a/examples/ontology-from-excel/make_microstructure_onto.py +++ b/examples/ontology-from-excel/make_microstructure_onto.py @@ -4,7 +4,7 @@ from ontopy.excelparser import create_ontology_from_excel from ontopy.utils import write_catalog -ontology, catalog = create_ontology_from_excel("tool/onto.xlsx") +ontology, catalog = create_ontology_from_excel("tool/microstructure.xlsx") ontology.save("microstructure_ontology.ttl", format="turtle", overwrite=True) write_catalog(catalog) diff --git a/examples/ontology-from-excel/tool/microstructure.xlsx b/examples/ontology-from-excel/tool/microstructure.xlsx new file mode 100755 index 000000000..0e786cb02 Binary files /dev/null and b/examples/ontology-from-excel/tool/microstructure.xlsx differ diff --git a/ontopy/excelparser.py b/ontopy/excelparser.py index a2dfb091a..ccdcdc32b 100755 --- a/ontopy/excelparser.py +++ b/ontopy/excelparser.py @@ -9,17 +9,23 @@ Note that correct case is mandatory. """ +from typing import Tuple, Union, Sequence import warnings -from typing import Tuple, Union -import pyparsing + import pandas as pd +import pyparsing + import ontopy -from ontopy import World -from ontopy.utils import NoSuchLabelError +from ontopy import get_ontology +from ontopy.utils import EMMOntoPyException, NoSuchLabelError from ontopy.manchester import evaluate import owlready2 # pylint: disable=C0411 +class ExcelError(EMMOntoPyException): + """Raised on errors in Excel file.""" + + def english(string): """Returns `string` as an English location string.""" return owlready2.locstr(string, lang="en") @@ -29,118 +35,276 @@ def create_ontology_from_excel( # pylint: disable=too-many-arguments excelpath: str, concept_sheet_name: str = "Concepts", metadata_sheet_name: str = "Metadata", + imports_sheet_name: str = "ImportedOntologies", base_iri: str = "http://emmo.info/emmo/domain/onto#", base_iri_from_metadata: bool = True, + imports: list = None, catalog: dict = None, + force: bool = False, ) -> Tuple[ontopy.ontology.Ontology, dict]: """ - Creates an ontology from an excelfile. + Creates an ontology from an Excel-file. + + Arguments: + excelpath: Path to Excel workbook. + concept_sheet_name: Name of sheet where concepts are defined. + The second row of this sheet should contain column names that are + supported. Currently these are 'prefLabel','altLabel', + 'Elucidation', 'Comments', 'Examples', 'subClassOf', 'Relations'. + Multiple entries are separated with ';'. + metadata_sheet_name: Name of sheet where metadata are defined. + The first row contains column names 'Metadata name' and 'Value' + Supported 'Metadata names' are: 'Ontology IRI', + 'Ontology vesion IRI', 'Ontology version Info', 'Title', + 'Abstract', 'License', 'Comment', 'Author', 'Contributor'. + Multiple entries are separated with a semi-colon (`;`). + imports_sheet_name: Name of sheet where imported ontologies are + defined. + Column name is 'Imported ontologies'. + Fully resolvable URL or path to imported ontologies provided one + per row. + base_iri: Base IRI of the new ontology. + base_iri_from_metadata: Whether to use base IRI defined from metadata. + imports: List of imported ontologies. + catalog: Imported ontologies with (name, full path) key/value-pairs. + force: Forcibly make an ontology by skipping concepts with a prefLabel + that is erroneously defined. + + Returns: + A tuple of the created ontology and the associated catalog of ontology + names and resolvable path as dict. + - Catalog is dict of imported ontologies with key name and value path. """ + # Get imported ontologies from optional "Imports" sheet + if not imports: + imports = [] + try: + imports_frame = pd.read_excel( + excelpath, sheet_name=imports_sheet_name, skiprows=[1] + ) + except ValueError: + pass + else: + imports.extend(imports_frame["Imported ontologies"].to_list()) + # Read datafile TODO: Some magic to identify the header row conceptdata = pd.read_excel( excelpath, sheet_name=concept_sheet_name, skiprows=[0, 2] ) metadata = pd.read_excel(excelpath, sheet_name=metadata_sheet_name) return create_ontology_from_pandas( - conceptdata, metadata, base_iri, base_iri_from_metadata, catalog + data=conceptdata, + metadata=metadata, + imports=imports, + base_iri=base_iri, + base_iri_from_metadata=base_iri_from_metadata, + catalog=catalog, + force=force, ) -def create_ontology_from_pandas( # pylint: disable=too-many-locals,too-many-branches,too-many-statements +def create_ontology_from_pandas( # pylint:disable=too-many-locals,too-many-branches,too-many-statements,too-many-arguments data: pd.DataFrame, metadata: pd.DataFrame, + imports: list, base_iri: str = "http://emmo.info/emmo/domain/onto#", base_iri_from_metadata: bool = True, catalog: dict = None, + force: bool = False, ) -> Tuple[ontopy.ontology.Ontology, dict]: """ Create an ontology from a pandas DataFrame. """ - # Remove Concepts without prefLabel and make all to string + # Remove lines with empty prefLabel data = data[data["prefLabel"].notna()] - data = data.astype({"prefLabel": "str"}) + # Convert all data to string, remove spaces, and finally remove + # additional rows with empty prefLabel. + data = data.astype(str) + data["prefLabel"] = data["prefLabel"].str.strip() + data = data[data["prefLabel"].str.len() > 0] + data.reset_index(drop=True, inplace=True) # Make new ontology - onto, catalog = get_metadata_from_dataframe(metadata, base_iri) + onto, catalog = get_metadata_from_dataframe( + metadata, base_iri, imports=imports + ) - # base_iri from metadata if it exists and base_iri_from_metadata + # Set given or default base_iri if base_iri_from_metadata is False. if not base_iri_from_metadata: onto.base_iri = base_iri + labels = set(data["prefLabel"]) + for altlabel in data["altLabel"].str.strip(): + if not altlabel == "nan": + labels.update(altlabel.split(";")) + onto.sync_python_names() with onto: - # loop through the rows until no more are added - new_loop = True - final_loop = False - while new_loop: - number_of_added_classes = 0 - for _, row in data.iterrows(): + remaining_rows = set(range(len(data))) + while remaining_rows: + added_rows = set() + for index in remaining_rows: + row = data.loc[index] name = row["prefLabel"] try: - if isinstance( - onto.get_by_label(name), owlready2.ThingClass - ): - continue + onto.get_by_label(name) + if not force: + raise ExcelError( + f'Concept "{name}" already in ontology' + ) + warnings.warn( + f'Ignoring concept "{name}" since it is already in ' + "the ontology." + ) + # What to do if we want to add info to this concept? + # Should that be not allowed? + # If it should be allowed the index has to be added to + # added_rows + continue + except (ValueError, TypeError) as err: + warnings.warn( + f'Ignoring concept "{name}". ' + f'The following error was raised: "{err}"' + ) + continue except NoSuchLabelError: pass - parent_names = str(row["subClassOf"]).split(";") + if pd.isna(row["subClassOf"]): + if not force: + raise ExcelError(f"{row[0]} has no subClassOf") + parent_names = [] # Should be "owl:Thing" + else: + parent_names = str(row["subClassOf"]).split(";") + + parents = [] + invalid_parent = False + for parent_name in parent_names: + try: + parent = onto.get_by_label(parent_name.strip()) + except (NoSuchLabelError, ValueError) as exc: + if parent_name not in labels: + if force: + warnings.warn( + f'Invalid parents for "{name}": ' + f'"{parent_name}".' + ) + break + raise ExcelError( + f'Invalid parents for "{name}": {exc}\n' + "Have you forgotten an imported ontology?" + ) from exc + invalid_parent = True + break + else: + parents.append(parent) - try: - parents = [onto.get_by_label(pn) for pn in parent_names] - except NoSuchLabelError: - if final_loop is True: - parents = owlready2.Thing + if invalid_parent: + continue - warnings.warn( - "Missing at least one of the defined parents. " - f"Concept: {name}; Defined parents: {parent_names}" - ) - new_loop = False - else: - continue + if not parents: + parents = [owlready2.Thing] concept = onto.new_entity(name, parents) + added_rows.add(index) # Add elucidation - _add_literal( - row, concept.elucidation, "Elucidation", only_one=True - ) + try: + _add_literal( + row, + concept.elucidation, + "Elucidation", + only_one=True, + ) + except AttributeError as err: + if force: + _add_literal( + row, + concept.comment, + "Elucidation", + only_one=True, + ) + warnings.warn("Elucidation added as comment.") + else: + raise ExcelError( + f"Not able to add elucidations. {err}." + ) from err # Add examples - _add_literal(row, concept.example, "Examples") + try: + _add_literal( + row, concept.example, "Examples", expected=False + ) + except AttributeError: + if force: + warnings.warn( + "Not able to add examples. " + "Did you forget to import an ontology?." + ) # Add comments - _add_literal(row, concept.comment, "Comments") + _add_literal(row, concept.comment, "Comments", expected=False) - # Add altLAbels - _add_literal(row, concept.altLabel, "altLabel") + # Add altLabels + try: + _add_literal( + row, concept.altLabel, "altLabel", expected=False + ) + except AttributeError as err: + if force is True: + _add_literal( + row, + concept.label, + "altLabel", + expected=False, + ) + warnings.warn("altLabel added as rdfs.label.") + else: + raise ExcelError( + f"Not able to add altLabels. " f"{err}." + ) from err - number_of_added_classes += 1 + remaining_rows.difference_update(added_rows) - if number_of_added_classes == 0: - final_loop = True + # Detect infinite loop... + if not added_rows and remaining_rows: + unadded = [data.loc[i].prefLabel for i in remaining_rows] + if force is True: + warnings.warn( + f"Not able to add the following concepts: {unadded}." + " Will continue without these." + ) + remaining_rows = False + else: + raise ExcelError( + f"Not able to add the following concepts: {unadded}." + ) # Add properties in a second loop - for _, row in data.iterrows(): + for index in added_rows: + row = data.loc[index] properties = row["Relations"] if isinstance(properties, str): try: - concept = onto.get_by_label(row["prefLabel"]) + concept = onto.get_by_label(row["prefLabel"].strip()) except NoSuchLabelError: pass props = properties.split(";") for prop in props: try: concept.is_a.append(evaluate(onto, prop)) - except pyparsing.ParseException as err: + except pyparsing.ParseException as exc: warnings.warn( f"Error in Property assignment for: {concept}. " f"Property to be Evaluated: {prop}. " - f"Error is {err}." + f"Error is {exc}." ) + except NoSuchLabelError as exc: + if force is True: + pass + else: + raise ExcelError(exc) from exc # Synchronise Python attributes to ontology onto.sync_attributes( @@ -154,11 +318,10 @@ def get_metadata_from_dataframe( # pylint: disable=too-many-locals,too-many-bra metadata: pd.DataFrame, base_iri: str, base_iri_from_metadata: bool = True, + imports: Sequence = (), catalog: dict = None, ) -> Tuple[ontopy.ontology.Ontology, dict]: - """ - Create ontology with metadata from pd.DataFrame - """ + """Create ontology with metadata from pd.DataFrame""" # base_iri from metadata if it exists and base_iri_from_metadata if base_iri_from_metadata: @@ -169,54 +332,73 @@ def get_metadata_from_dataframe( # pylint: disable=too-many-locals,too-many-bra "More than one Ontology IRI given. The first was chosen." ) base_iri = base_iris[0] + "#" - except (TypeError, ValueError, AttributeError): + except (TypeError, ValueError, AttributeError, IndexError): pass - # Make new ontology - world = World() - onto = world.get_ontology(base_iri) + # Create new ontology + onto = get_ontology(base_iri) - # Get imported ontologies from metadata - try: - imported_ontology_paths = _parse_literal( - metadata, - "Imported ontologies", - metadata=True, - ) - except (TypeError, ValueError, AttributeError): - imported_ontology_paths = [] # Add imported ontologies catalog = {} if catalog is None else catalog - for path in imported_ontology_paths: - imported = onto.world.get_ontology(path).load() - onto.imported_ontologies.append(imported) - catalog[imported.base_iri.rstrip("/")] = path + locations = set() + for location in imports: + if not pd.isna(location) and location not in locations: + imported = onto.world.get_ontology(location).load() + onto.imported_ontologies.append(imported) + catalog[imported.base_iri.rstrip("#/")] = location + locations.add(location) with onto: # Add title - _add_literal( - metadata, onto.metadata.title, "Title", metadata=True, only_one=True - ) + try: + _add_literal( + metadata, + onto.metadata.title, + "Title", + metadata=True, + only_one=True, + ) + except AttributeError: + pass # Add license - _add_literal(metadata, onto.metadata.license, "License", metadata=True) + try: + _add_literal( + metadata, onto.metadata.license, "License", metadata=True + ) + except AttributeError: + pass - # Add authors onto.metadata.author does not work! - _add_literal(metadata, onto.metadata.creator, "Author", metadata=True) + # Add authors/creators + try: + _add_literal( + metadata, onto.metadata.creator, "Author", metadata=True + ) + except AttributeError: + pass # Add contributors - _add_literal( - metadata, onto.metadata.contributor, "Contributor", metadata=True - ) + try: + _add_literal( + metadata, + onto.metadata.contributor, + "Contributor", + metadata=True, + ) + except AttributeError: + pass # Add versionInfo - _add_literal( - metadata, - onto.metadata.versionInfo, - "Ontology version Info", - metadata=True, - only_one=True, - ) + try: + _add_literal( + metadata, + onto.metadata.versionInfo, + "Ontology version Info", + metadata=True, + only_one=True, + ) + except AttributeError: + pass return onto, catalog @@ -235,9 +417,9 @@ def _parse_literal( values = data.loc[data["Metadata name"] == name]["Value"].item() else: values = data[name] - if not pd.isna(values): + if not (pd.isna(values) or values == "nan"): return str(values).split(sep) - return values.split(sep) + return [] def _add_literal( # pylint: disable=too-many-arguments @@ -247,7 +429,9 @@ def _add_literal( # pylint: disable=too-many-arguments metadata: bool = False, only_one: bool = False, sep: str = ";", + expected: bool = True, ) -> None: + """Append literal data to ontological entity.""" try: name_list = _parse_literal(data, name, metadata=metadata, sep=sep) if only_one is True and len(name_list) > 1: @@ -258,4 +442,8 @@ def _add_literal( # pylint: disable=too-many-arguments else: destination.extend([english(nm) for nm in name_list]) except (TypeError, ValueError, AttributeError): - warnings.warn(f"No {name} added.") + if expected: + if metadata: + warnings.warn(f"Missing metadata {name}") + else: + warnings.warn(f"{data[0]} has no {name}") diff --git a/ontopy/ontology.py b/ontopy/ontology.py index bf1077c87..8fc6db5f8 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -321,7 +321,7 @@ def get_by_label( if entity: return entity - raise NoSuchLabelError(f"No label annotations matches {label}") + raise NoSuchLabelError(f"No label annotations matches {label!r}") def get_by_label_all(self, label, label_annotations=None, namespace=None): """Like get_by_label(), but returns a list with all matching labels. diff --git a/tests/test_excelparser.py b/tests/test_excelparser.py index 22d871e9a..9cf1fd3ee 100644 --- a/tests/test_excelparser.py +++ b/tests/test_excelparser.py @@ -1,17 +1,20 @@ +"""Test the Excel parser module.""" +from typing import TYPE_CHECKING + from ontopy import get_ontology from ontopy.excelparser import create_ontology_from_excel -from ontopy.utils import write_catalog + +if TYPE_CHECKING: + from pathlib import Path def test_excelparser(repo_dir: "Path") -> None: + """Basic test for creating an ontology from an Excel file.""" ontopath = ( repo_dir / "tests" / "testonto" / "excelparser" / "fromexcelonto.ttl" ) + onto = get_ontology(str(ontopath)).load() xlspath = repo_dir / "tests" / "testonto" / "excelparser" / "onto.xlsx" - ontology, catalog = create_ontology_from_excel(xlspath) + ontology, catalog = create_ontology_from_excel(xlspath, force=True) assert onto == ontology - - -if __name__ == "__main__": - test_excelparser() diff --git a/tests/testonto/excelparser/fromexcelonto.ttl b/tests/testonto/excelparser/fromexcelonto.ttl index f3434c173..6953c53d5 100644 --- a/tests/testonto/excelparser/fromexcelonto.ttl +++ b/tests/testonto/excelparser/fromexcelonto.ttl @@ -6,57 +6,42 @@ @prefix term: . a owl:Ontology ; - term:creator "Astrid Marthinsen"@en, - "Georg Schmidt"@en, + term:contributor "SINTEF"@en, + "SINTEF Industry"@en ; + term:creator "Francesca L. Bleken"@en, "Jesper Friis"@en, - "Sylvain Gouttebroze"@en, - "Tomas Manik"@en, - "Ulrike Cihak-Bayr"@en ; - term:title "Microstructure ontology based on EMMO - Top concepts"@en ; + "Sylvain Gouttebroze"@en ; + term:title "A test domain ontology"@en ; owl:imports ; owl:versionInfo "0.01"@en . :EMMO_0264be35-e8ad-5b35-a1a3-84b37bde22d1 a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "Temporal pattern occurring in a time interval"@en ; emmo:EMMO_b432d2d5_25f4_4165_99c5_5935a7763c1a "Light house during one night"@en ; - rdfs:subClassOf [ a owl:Restriction ; - owl:onProperty emmo:EMMO_17e27c22_37e1_468c_9dd7_95e137f73e7f ; - owl:someValuesFrom :EMMO_b41c9cb3-3b2d-509f-9c93-aa04da134307 ], - [ a owl:Restriction ; - owl:onProperty emmo:EMMO_e1097637_70d2_4895_973f_2396f04fa204 ; - owl:someValuesFrom emmo:EMMO_d4f7d378_5e3b_468a_baa1_a7e98358cda7 ], - :EMMO_138590b8-3333-515d-87ab-717aac8434e6, + rdfs:subClassOf :EMMO_138590b8-3333-515d-87ab-717aac8434e6, :EMMO_4b32833e-0833-56a7-903c-28a6a8191fe8 ; core:prefLabel "FiniteTemporalPattern"@en . -:EMMO_70269d17-fbaa-54a6-8905-ce4dee45e0dd a owl:Class ; +:EMMO_1c81f1eb-8b94-5e74-96de-1aeacbdb5b93 a owl:Class ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "The boundary of a grain"@en ; + rdfs:subClassOf :EMMO_472ed27e-ce08-53cb-8453-56ab363275c4 ; + core:prefLabel "GrainBoundary"@en . + +:EMMO_6920d08f-b1e4-5789-9778-f75f4514ef46 a owl:Class ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; rdfs:subClassOf owl:Thing ; - core:prefLabel "Particle"@en . + core:prefLabel "SpatioTemporalBoundary"@en . :EMMO_76b2eb15-3ab7-52b3-ade2-755aa390d63e a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "Spatial pattern localized in a volume of space"@en ; emmo:EMMO_b432d2d5_25f4_4165_99c5_5935a7763c1a "Textured surface after etching"@en ; - rdfs:subClassOf [ a owl:Restriction ; - owl:onProperty emmo:EMMO_e1097637_70d2_4895_973f_2396f04fa204 ; - owl:someValuesFrom emmo:EMMO_f1a51559_aa3d_43a0_9327_918039f0dfed ], - [ a owl:Restriction ; - owl:onProperty emmo:EMMO_17e27c22_37e1_468c_9dd7_95e137f73e7f ; - owl:someValuesFrom :EMMO_472ed27e-ce08-53cb-8453-56ab363275c4 ], - :EMMO_4b32833e-0833-56a7-903c-28a6a8191fe8, + rdfs:subClassOf :EMMO_4b32833e-0833-56a7-903c-28a6a8191fe8, :EMMO_5f50f77e-f321-53e3-af76-fe5b0a347479 ; core:prefLabel "FiniteSpatialPattern"@en . -:EMMO_903bf818-c0b4-56ef-9673-799ba204795d a owl:Class ; - rdfs:subClassOf owl:Thing ; - core:prefLabel "Precipitate"@en . - -:EMMO_b0f0e57e-464d-562f-80ec-b216c92d5e88 a owl:Class ; - rdfs:subClassOf owl:Thing ; - core:prefLabel "Grain"@en . - -:EMMO_d35b8f2a-64c0-5f57-a569-308bc8f8a1c5 a owl:Class ; - rdfs:subClassOf owl:Thing ; - core:prefLabel "Phase"@en . +:EMMO_b04965e6-a9bb-591f-8f8a-1adcb2c8dc39 a owl:Class ; + rdfs:subClassOf emmo:EMMO_21f56795_ee72_4858_b571_11cfaa59c1a8 ; + core:prefLabel "1"@en . :EMMO_e0b20a22-7e6f-5c81-beca-35bc5358e11b a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; @@ -64,14 +49,28 @@ :EMMO_9fa9ca88-2891-538a-a8dd-ccb8a08b9890 ; core:prefLabel "FiniteSpatioTemporalPattern"@en . +:EMMO_e633d033-2af6-5f04-a706-dab826854fb1 a owl:Class ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "The boundary of a subgrain"@en ; + rdfs:subClassOf owl:Thing ; + core:prefLabel "SubgrainBoundary"@en . + +:EMMO_f8ad57d3-6cb5-5628-99e6-eb5915bece3a a owl:Class ; + rdfs:subClassOf owl:Thing ; + core:prefLabel "SubSubgrainBoundary"@en . + :EMMO_138590b8-3333-515d-87ab-717aac8434e6 a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "Pattern with only temporal aspect"@en ; emmo:EMMO_b432d2d5_25f4_4165_99c5_5935a7763c1a "Voltage in AC plug"@en ; - rdfs:subClassOf :EMMO_9fa9ca88-2891-538a-a8dd-ccb8a08b9890 ; + rdfs:subClassOf owl:Thing ; core:prefLabel "TemporalPattern"@en . -:EMMO_472ed27e-ce08-53cb-8453-56ab363275c4 a owl:Class ; +:EMMO_1b2bfe71-5da9-5c46-b137-be45c3e3f9c3 a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; + rdfs:subClassOf emmo:EMMO_649bf97b_4397_4005_90d9_219755d92e34 ; + core:prefLabel "Boundary"@en . + +:EMMO_472ed27e-ce08-53cb-8453-56ab363275c4 a owl:Class ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 " "@en ; rdfs:subClassOf :EMMO_1b2bfe71-5da9-5c46-b137-be45c3e3f9c3 ; core:prefLabel "SpatialBoundary"@en . @@ -81,36 +80,23 @@ rdfs:subClassOf :EMMO_9fa9ca88-2891-538a-a8dd-ccb8a08b9890 ; core:prefLabel "SpatialPattern"@en . -:EMMO_b41c9cb3-3b2d-509f-9c93-aa04da134307 a owl:Class ; - emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; - rdfs:subClassOf :EMMO_1b2bfe71-5da9-5c46-b137-be45c3e3f9c3 ; - core:prefLabel "TemporalBoundary"@en . - -:EMMO_cd254842-c697-55f6-917d-9805c77b9187 a owl:Class ; +:EMMO_9fa9ca88-2891-538a-a8dd-ccb8a08b9890 a owl:Class ; emmo:EMMO_21ae69b4_235e_479d_8dd8_4f756f694c1b "A"@en, "Just"@en, "Test"@en ; + emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; + rdfs:subClassOf :EMMO_cd254842-c697-55f6-917d-9805c77b9187 ; + core:prefLabel "SpatioTemporalPattern"@en . + +:EMMO_cd254842-c697-55f6-917d-9805c77b9187 a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "everything that can be perceived or measured"@en ; rdfs:comment " this definition is much broader than definition of pattern such as \"the regular and repeated way in which something happens or is\""@en, "a pattern is defined from a contrast"@en ; rdfs:subClassOf emmo:EMMO_649bf97b_4397_4005_90d9_219755d92e34 ; core:prefLabel "Pattern"@en . -:EMMO_1b2bfe71-5da9-5c46-b137-be45c3e3f9c3 a owl:Class ; - emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; - rdfs:subClassOf emmo:EMMO_649bf97b_4397_4005_90d9_219755d92e34 ; - core:prefLabel "Boundary"@en . - :EMMO_4b32833e-0833-56a7-903c-28a6a8191fe8 a owl:Class ; emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "Pattern occuring within a boundary in the 4D space"@en ; rdfs:comment "Every physical patterns are FinitePattern"@en ; - rdfs:subClassOf [ a owl:Restriction ; - owl:onProperty emmo:EMMO_17e27c22_37e1_468c_9dd7_95e137f73e7f ; - owl:someValuesFrom :EMMO_1b2bfe71-5da9-5c46-b137-be45c3e3f9c3 ], - :EMMO_cd254842-c697-55f6-917d-9805c77b9187 ; - core:prefLabel "FinitePattern"@en . - -:EMMO_9fa9ca88-2891-538a-a8dd-ccb8a08b9890 a owl:Class ; - emmo:EMMO_967080e5_2f42_4eb2_a3a9_c58143e835f9 "NEED elucidation"@en ; rdfs:subClassOf :EMMO_cd254842-c697-55f6-917d-9805c77b9187 ; - core:prefLabel "SpatioTemporalPattern"@en . + core:prefLabel "FinitePattern"@en . diff --git a/tests/testonto/excelparser/onto.xlsx b/tests/testonto/excelparser/onto.xlsx index 01ad4a91d..9e7f2bf6f 100755 Binary files a/tests/testonto/excelparser/onto.xlsx and b/tests/testonto/excelparser/onto.xlsx differ diff --git a/tools/excel2onto b/tools/excel2onto index 6aee6a601..23e067a1e 100755 --- a/tools/excel2onto +++ b/tools/excel2onto @@ -7,7 +7,7 @@ ontology_template.xlsx import argparse import sys import os -from ontopy.excelparser import create_ontology_from_excel +from ontopy.excelparser import create_ontology_from_excel, ExcelError from ontopy.utils import write_catalog import owlready2 # pylint: disable=C0411 @@ -30,12 +30,24 @@ def main(): default="ontology.ttl", help="Name of output ontology, ´ontology.ttl´ is default", ) + parser.add_argument( + "--force", + "-f", + action="store_true", + help="Whether to force generation of ontology on non-fatal error.", + ) + try: args = parser.parse_args() except SystemExit as exc: sys.exit(exc.code) # Exit without traceback on invalid arguments - ontology, catalog = create_ontology_from_excel(args.excelpath) + try: + ontology, catalog = create_ontology_from_excel( + args.excelpath, force=args.force + ) + except ExcelError as exc: + parser.exit(1, f"ERROR: {exc}\n") # Save new ontology as turtle ontology.save(os.path.join(args.output), format="turtle", overwrite=True) diff --git a/tools/ontoversion b/tools/ontoversion index 7c39e97c0..8b29ea525 100755 --- a/tools/ontoversion +++ b/tools/ontoversion @@ -46,7 +46,7 @@ def main(argv: list = None): fmt = args.format if args.format else guess_format(args.iri, fmap=FMAP) try: graph.parse(args.iri, format=fmt) - except Exception as err: + except Exception as err: # pylint: disable=W0703 print("rdflib could not parse the ontology.") print(err) sys.exit()