diff --git a/Makefile b/Makefile index 075c1778b..fced0e661 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .PHONY: run-explorer run-tests run-linters build-ui build-python build-docker run-docker compose-up -version="0.88.2" +version="0.88.3" run-explorer: @echo "Running explorer API server..." # open "http://localhost:8000/static/index.html" || true diff --git a/cognite/neat/_version.py b/cognite/neat/_version.py index 57a4f493a..6828fd00c 100644 --- a/cognite/neat/_version.py +++ b/cognite/neat/_version.py @@ -1 +1 @@ -__version__ = "0.88.2" +__version__ = "0.88.3" diff --git a/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py b/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py index 28be40c4b..7304ed17c 100644 --- a/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +++ b/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py @@ -53,7 +53,7 @@ def parse_imf_to_classes(graph: Graph, language: str = "en") -> list[dict]: BIND(IF(!bound(?parent) && ?type = imf:AttributeType, imf:Attribute, ?parent) AS ?parentClass) # Rebind the IRI of the IMF-type to the ?reference variable to align with dataframe column headers - # This is solely for readability, the ?imfClass could have been returnered directly instead of ?reference + # This is solely for readability, the ?imfClass could have been returned directly instead of ?reference BIND(?imfClass AS ?reference) FILTER (!isBlank(?class)) diff --git a/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py b/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py index 1308b213f..941f91294 100644 --- a/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +++ b/cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py @@ -36,7 +36,7 @@ class IMFImporter(BaseImporter): """ def __init__(self, filepath: Path): - self.owl_filepath = filepath + self.filepath = filepath @overload def to_rules(self, errors: Literal["raise"], role: RoleTypes | None = None) -> VerifiedRules: ... @@ -47,11 +47,13 @@ def to_rules( ) -> tuple[VerifiedRules | None, IssueList]: ... def to_rules( - self, errors: Literal["raise", "continue"] = "continue", role: RoleTypes | None = None + self, + errors: Literal["raise", "continue"] = "continue", + role: RoleTypes | None = None, ) -> tuple[VerifiedRules | None, IssueList] | VerifiedRules: graph = Graph() try: - graph.parse(self.owl_filepath) + graph.parse(self.filepath) except Exception as e: raise Exception(f"Could not parse owl file: {e}") from e diff --git a/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py b/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py index a01439739..746e6a5c8 100644 --- a/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +++ b/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py @@ -34,6 +34,7 @@ def parse_owl_classes(graph: Graph, language: str = "en") -> list[dict]: FILTER (!bound(?parentClass) || !isBlank(?parentClass)) FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en")) FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en")) + BIND(?class AS ?reference) } """ diff --git a/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py b/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py index df2f407ad..fa1eb9c5a 100644 --- a/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +++ b/cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py @@ -40,6 +40,7 @@ def parse_owl_properties(graph: Graph, language: str = "en") -> list[dict]: FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en")) BIND(IF(bound(?minCount), ?minCount, 0) AS ?minCount) BIND(IF(bound(?maxCount), ?maxCount, 1) AS ?maxCount) + BIND(?property AS ?reference) } """ diff --git a/cognite/neat/rules/importers/_rdf/_shared.py b/cognite/neat/rules/importers/_rdf/_shared.py index 5e9dc7ab5..481fdae38 100644 --- a/cognite/neat/rules/importers/_rdf/_shared.py +++ b/cognite/neat/rules/importers/_rdf/_shared.py @@ -23,13 +23,13 @@ def parse_raw_classes_dataframe(query_results: list[tuple]) -> pd.DataFrame: "Comment", ], ) + if df.empty: return df # # remove NaNs df.replace(np.nan, "", regex=True, inplace=True) - df.Reference = df.Class df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x)) df["Match Type"] = len(df) * [MatchType.exact] df["Comment"] = len(df) * [None] @@ -202,7 +202,7 @@ def parse_raw_properties_dataframe(query_results: list[tuple]) -> pd.DataFrame: return df df.replace(np.nan, "", regex=True, inplace=True) - df.Reference = df.Reference if df.Reference.unique() else df.Property.copy(deep=True) + df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x)) df.Property = df.Property.apply(lambda x: remove_namespace_from_uri(x)) df["Value Type"] = df["Value Type"].apply(lambda x: remove_namespace_from_uri(x)) @@ -231,7 +231,7 @@ def clean_up_properties(df: pd.DataFrame) -> pd.DataFrame: "Min Count": property_grouped_df["Min Count"].unique()[0], "Max Count": property_grouped_df["Max Count"].unique()[0], "Default": property_grouped_df["Default"].unique()[0], - "Reference": property_grouped_df["Reference"].unique()[0] or property_, + "Reference": property_grouped_df["Reference"].unique()[0], "Match Type": property_grouped_df["Match Type"].unique()[0], "Comment": property_grouped_df["Comment"].unique()[0], "_property_type": property_grouped_df["_property_type"].unique()[0], diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3f54af074..ee8ced5a8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,6 +16,15 @@ Changes are grouped as follows: - `Security` in case of vulnerabilities. +## [0.88.3] - 20-08-24 +### Fixed +- IMF rules importer failing due to references +### Improved +- Handling of references for OWL importer +### Added +- Test for IMF importer + + ## [0.88.2] - 24-07-24 ### Added - IMF rules importer diff --git a/pyproject.toml b/pyproject.toml index 45bcbf8a6..d155f2501 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cognite-neat" -version = "0.88.2" +version = "0.88.3" readme = "README.md" description = "Knowledge graph transformation" authors = [ diff --git a/tests/config.py b/tests/config.py index 254590803..152399db7 100644 --- a/tests/config.py +++ b/tests/config.py @@ -28,5 +28,6 @@ GRAPH_CAPTURING_SHEET = DATA_FOLDER / "sheet2cdf-graph-capturing.xlsx" WIND_ONTOLOGY = DATA_FOLDER / "wind-energy.owl" DEXPI_EXAMPLE = DATA_FOLDER / "depxi_example.xml" +IMF_EXAMPLE = DATA_FOLDER / "TempTransmitterComplete.ttl" CLASSIC_CDF_EXTRACTOR_DATA = DATA_FOLDER / "class_cdf_extractor_data" diff --git a/tests/data/TempTransmitterComplete.ttl b/tests/data/TempTransmitterComplete.ttl new file mode 100644 index 000000000..de0687021 --- /dev/null +++ b/tests/data/TempTransmitterComplete.ttl @@ -0,0 +1,948 @@ +PREFIX imf: +PREFIX rdf: +PREFIX rdfs: +PREFIX sh: +PREFIX shsh: +PREFIX xsd: + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A 'fill fluid class' that can be used for a capillary tube"; + + "FILL_FLUID" . + + + rdf:type imf:BlockType , sh:NodeShape , rdfs:Class; + rdfs:subClassOf imf:Block; + + "IMF Type Block Diaphragm Seal Capilliary in Product aspect."; + + "Diaphragm Seal Capilliary"; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ] . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MATERIAL SUBCODE is an IDENTIFYING CODE that is derived from either the API-610 or a different specification in order to specify the applicable materials"; + + "DIAPHRAGM_MATERIAL"; + + . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MATERIAL SUBCODE is an IDENTIFYING CODE that is derived from either the API-610 or a different specification in order to specify the applicable materials"; + + "ARMOUR_MATERIAL"; + + . + + + rdf:type imf:BlockType , sh:NodeShape , rdfs:Class; + rdfs:subClassOf imf:Block; + + "IMF Type Block Diaphragm Seal in Product aspect."; + + "Diaphragm Seal"; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasTerminal; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasPart; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ] . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "alphanumeric designation of a pressure allowable within a piping system or piece of equipment, which is used for reference purposes"; + + "CONN_RATING" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "classification of the sealing surface of a flange according to its design code or characteristics"; + + "FLANGE_FACING" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "classification of a process connection according to its fabricated form"; + + "CONN_TYPE" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A NOMINAL SIZE is an IndirectProperty that is a SIZE used for purposes of general identification; the actual size of a part will be approximately the same as the nominal size but need not be exactly the same;"; + + "NOMINAL_SIZE" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "standard to which the code of a material refers"; + + "FLANGE_CODE"; + + . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A TYPE DESCRIPTION is a CONSTRUCTION FEATURE DESCRIPTION and a MANUFACTURER PRODUCT DESCRIPTION that referes to a particular product type being manufactured"; + + "THREAD_TYPE"; + + . + + + rdf:type imf:TerminalType , sh:NodeShape , rdfs:Class; + rdfs:subClassOf imf:Terminal; + + "Terminal ProcessConnection in Product aspect."; + + "ProcessConnection"; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ] . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A DIAMETER is a ShapeDimension that refers to a intercept made by the circumference on a straight line through the centre of a circle"; + + "DIAMETER" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A UPPER LIMIT DESIGN TEMPERATURE is an IndirectProperty that refers to a TEMPERATUREthat an object is designed to withstand"; + + "DESIGN_TEMP_MAX" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "overall length of that part of a device or component to be found within a containment"; + + "INSERTION_LENGTH" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "properties characterizing the material of which a sheath/additional sleeve is made"; + + "SHEATH_MATERIAL" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "largest diameter of an insert that guarantees accommodation in a receptacle"; + + "OUTSIDE_DIAMETER_OF_INSERT_ELEMENT" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "material of which the wetted parts of a sensor are made"; + + "MATERIAL_SOCKET_INLET_PORT" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "length for which a detector or probe is sensitive"; + + "SENSITIVE_LENGTH" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A LOWER LIMIT DESIGN TEMPERATURE is an IndirectProperty that refers to the lowest TEMPERATURE at which a functional or physical object is designed"; + + "DESIGN_TEMP_MIN" . + + + rdf:type , imf:AttributeType; + imf:hasAttributeQualifier imf:continuousQualifier; + imf:predicate ; + + "A DESIGN STANDARD is an InformationObject and a STANDARD that governs the design of a product"; + + "DESIGN_STANDARD" . + + + rdf:type rdfs:Class , sh:NodeShape , imf:BlockType; + rdfs:subClassOf imf:Block; + + "IMF Type Block Sensing Element in Product aspect."; + + "Sensing Element"; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasPart; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasTerminal; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasTerminal; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ] . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "minimum value that a current signal can assume and still exhibit linear behaviour"; + + "SIGNAL_LOWER_LEVEL" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "classification of a cable termination according to its constructional design"; + + "CABLE_TERMINATION" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "highest value that a current signal can assume and still exhibit linear behaviour"; + + "SIGNAL_UPPER_LEVEL" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "nominal value of the rated voltage an EM component is designed for"; + + "RATED_VOLTAGE" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A definition of what the tag or equipment class is"; + + "FUNCTIONAL_CLASS" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "The number of cores of the functional or physical object."; + + "WIRE_CONFIGURATION" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "properties characterizing the communication protocol of a digital communication interface"; + + "COMMUNICATION_PROTOCOL" . + + + rdf:type imf:TerminalType , sh:NodeShape , rdfs:Class; + rdfs:subClassOf imf:Terminal; + + "Terminal CabledConnection in Product aspect."; + + "CabledConnection"; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ] . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "extent of protection provided by an enclosure against access to hazardous parts, against ingress of solid foreign objects and/or ingress of water and verified by standardized test methods, expressed as an IP rating"; + + "IP_GRADE" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "hazardous area zone for which the device is designed to be used"; + + "EX_CLASSIFICATION" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MATERIAL SUBCODE is an IDENTIFYING CODE that is derived from either the API-610 or a different specification in order to specify the applicable materials"; + + "MATERIAL" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MATERIAL SUBCODE is an IDENTIFYING CODE that is derived from either the API-610 or a different specification in order to specify the applicable materials"; + + "MATERIAL_BOLT"; + + . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A CABLE ENTRY is a FunctionalObject that has a purpose to allow a CABLE ENTERING a space"; + + "CABLE_ENTRY" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A STRAIGHT LINEAR DISTANCE describing one of three orthogonal extreme dimensions of a thing relative to a reference system: height"; + + "WIDTH" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A REQUIREMENT is a ClassOfRelationship that is required by a STAKEHOLDER"; + + "MOUNTING_BRACKET_REQ"; + + . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A LENGTH is a Property that is the straight-line longest distance between two points along an object"; + + "LENGTH" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MATERIAL SUBCODE is an IDENTIFYING CODE that is derived from either the API-610 or a different specification in order to specify the applicable materials"; + + "MATERIAL_NUTS"; + + . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A HEIGHT is a ShapeDimension that is the distance from the bottom to the top of something standing upright"; + + "HEIGHT" . + + + rdf:type , imf:AttributeType; + imf:hasAttributeQualifier imf:absoluteQualifier , imf:nominalQualifier; + imf:predicate ; + + "classification of a protective coating according to the type and finish of the coating material"; + + "PROTECTIVE_COATING" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "Is a possessed aspect being a role of the substance of an artefact when used to construct the artefact from. Therefore the substance should be in a solid state at the conditions at which the artefact exists."; + + "MOUNTING_BRACKET_MATERIAL"; + + . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "design of a device or a device component mounting classified by the type of surface on/to which it will be mounted"; + + "MOUNTING" . + + + rdf:type rdfs:Class , sh:NodeShape , imf:BlockType; + rdfs:subClassOf imf:Block; + + "IMF Type Block Instrument Housing in Product aspect."; + + "Instrument Housing"; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasTerminal; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ] . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "An ALARM LIMIT HIGH HIGH is an ALARM LIMIT for an acceptable high-high value"; + + "SETPOINT_HH" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MANUFACTURER NAME is a NAME of a person or organisation who is the manufacturer of an object or a type of objects"; + + "MANUFACTURER" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "An ALARM LIMIT HIGH is an ALARM LIMIT for an acceptable high value"; + + "SETPOINT_H" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "lowest value of other variable that a device can be adjusted to measure within its specified accuracy limits"; + + "MEASUREMENT_RANGE_MINIMUM" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A TOTAL WEIGHT is a WEIGHT that is a sum of the weights of identified parts of an assembly"; + + "TOTAL_WEIGHT" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "An ALARM LIMIT LOW is an ALARM LIMIT for an acceptable low value"; + + "SETPOINT_L" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "value of variation of the relevant output signal (analog output value or number of detected output pulses) under specified conditions"; + + "REPEATABILITY" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A MANUFACTURER PRODUCT CODE is a PRODUCT CODE that is assigned by the manufacturer to a product to identify an item or a type of items"; + + "MANUFACTURER_MODEL_NO" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A TYPE DESCRIPTION is a CONSTRUCTION FEATURE DESCRIPTION and a MANUFACTURER PRODUCT DESCRIPTION that referes to a particular product type being manufactured"; + + "TYPE" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "An ALARM LIMIT LOW LOW is an ALARM LIMIT for an acceptable low-low value"; + + "SETPOINT_LL" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "engineering units associated with a variable, display, scale, graduation or any other means of indicating a value"; + + "INTENDED_DATUM_UOM" . + + + rdf:type , imf:AttributeType; + imf:hasAttributeQualifier imf:maximumQualifier; + imf:predicate ; + + "values for designation of calibration point"; + + "CALIBRATION_RANGE_MAXIMUM" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "highest value of other variable that a device can be adjusted to measure within its specified accuracy limits"; + + "MEASUREMENT_RANGE_MAXIMUM" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "An ACCURACY is an IndirectProperty that represents the closeness of agreement between the result of a measurement and the (conventional) true value of the quantity being measured"; + + "ACCURACY" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A T02 2.01 CHARACTERISTIC is assign if the reading is linear"; + + "CHARACTERISTIC" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "A REQUIREMENT is a ClassOfRelationship that is required by a STAKEHOLDER"; + + "INDICATOR_REQ"; + + . + + + rdf:type , imf:AttributeType; + imf:hasAttributeQualifier imf:minimumQualifier; + imf:predicate ; + + "values for designation of calibration point"; + + "CALIBRATION_RANGE_MINIMUM" . + + + rdf:type , imf:AttributeType; + imf:predicate ; + + "guaranteed power consumption of an electric/electronic or electromechanical component."; + + "CONSUMPTION" . + + + rdf:type rdfs:Class , sh:NodeShape , imf:BlockType; + rdfs:subClassOf imf:Block; + + "IMF Type Block Temperature Transmitter in Product aspect."; + + "Temperature Transmitter"; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasPart; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:hasValue imf:productAspect; + sh:nodeKind sh:IRI; + sh:path imf:hasAspect + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ]; + sh:property [ sh:nodeKind sh:IRI; + sh:path imf:hasPart; + sh:qualifiedMaxCount 1; + sh:qualifiedMinCount 0; + sh:qualifiedValueShape [ sh:class ] + ]; + sh:property [ sh:maxCount 1; + sh:minCount 0; + sh:nodeKind sh:IRIOrLiteral; + sh:path + ] . diff --git a/tests/tests_unit/rules/test_importers/test_imf_importer.py b/tests/tests_unit/rules/test_importers/test_imf_importer.py new file mode 100644 index 000000000..6b647e4a0 --- /dev/null +++ b/tests/tests_unit/rules/test_importers/test_imf_importer.py @@ -0,0 +1,19 @@ +from cognite.neat.rules import importers +from cognite.neat.rules.analysis import InformationAnalysis +from cognite.neat.rules.models.entities import ClassEntity, EntityTypes +from tests.config import IMF_EXAMPLE + + +def test_imf_importer(): + rules, _ = importers.IMFImporter(filepath=IMF_EXAMPLE).to_rules() + + assert len(rules.classes) == 69 + assert len(rules.properties) == 156 + + # this is rdf:PlainLiteral edge case + assert ( + InformationAnalysis(rules) + .class_property_pairs()[ClassEntity.load("pca-imf:IMF_1ccc23fc-42ca-4b8a-acd5-ef2beddf7f12")]["hasTerminal"] + .type_ + == EntityTypes.object_property + )