mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
Implement identifier, desc, and instructions for specification node in IDS
This commit is contained in:
@@ -49,7 +49,7 @@ class ids:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
title="Title",
|
title="Untitled",
|
||||||
copyright=None,
|
copyright=None,
|
||||||
version=None,
|
version=None,
|
||||||
description=None,
|
description=None,
|
||||||
@@ -79,7 +79,7 @@ class ids:
|
|||||||
"""
|
"""
|
||||||
self.specifications = []
|
self.specifications = []
|
||||||
self.info = {}
|
self.info = {}
|
||||||
self.info["title"] = title or "Unnamed"
|
self.info["title"] = title or "Untitled"
|
||||||
if copyright:
|
if copyright:
|
||||||
self.info["copyright"] = copyright
|
self.info["copyright"] = copyright
|
||||||
if version:
|
if version:
|
||||||
@@ -113,7 +113,7 @@ class ids:
|
|||||||
"specifications": [],
|
"specifications": [],
|
||||||
}
|
}
|
||||||
for spec in self.specifications:
|
for spec in self.specifications:
|
||||||
ids_dict["specifications"].append({"specification": spec.asdict()}) #TEST!
|
ids_dict["specifications"].append({"specification": spec.asdict()}) # TEST!
|
||||||
return ids_dict
|
return ids_dict
|
||||||
|
|
||||||
def to_string(self, ids_schema=ids_schema):
|
def to_string(self, ids_schema=ids_schema):
|
||||||
@@ -211,7 +211,9 @@ class ids:
|
|||||||
class specification:
|
class specification:
|
||||||
"""Represents the XML <specification> node and its two children <applicability> and <requirements>"""
|
"""Represents the XML <specification> node and its two children <applicability> and <requirements>"""
|
||||||
|
|
||||||
def __init__(self, name="Specification", use="required", ifcVersion="IFC2X3"):
|
def __init__(
|
||||||
|
self, name="Unnamed", use="required", ifcVersion="IFC2X3", identifier=None, description=None, instructions=None
|
||||||
|
):
|
||||||
"""Create a specification to be added in ids.
|
"""Create a specification to be added in ids.
|
||||||
|
|
||||||
:param name:, defaults to "Specification"
|
:param name:, defaults to "Specification"
|
||||||
@@ -219,11 +221,14 @@ class specification:
|
|||||||
:param use: 'required'|'optional', defaults to "required"
|
:param use: 'required'|'optional', defaults to "required"
|
||||||
:type use: str, optional
|
:type use: str, optional
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name or "Unnamed"
|
||||||
self.applicability = None
|
self.applicability = None
|
||||||
self.requirements = None
|
self.requirements = None
|
||||||
self.ifcVersion = ifcVersion
|
|
||||||
self.use = use
|
self.use = use
|
||||||
|
self.ifcVersion = ifcVersion
|
||||||
|
self.identifier = identifier
|
||||||
|
self.description = description
|
||||||
|
self.instructions = instructions
|
||||||
|
|
||||||
def asdict(self):
|
def asdict(self):
|
||||||
"""Converts object to a dictionary, adding required attributes.
|
"""Converts object to a dictionary, adding required attributes.
|
||||||
@@ -232,21 +237,28 @@ class specification:
|
|||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
# if older python collections.OrderedDict()
|
# if older python collections.OrderedDict()
|
||||||
spec_dict = {
|
results = {
|
||||||
"@name": self.name,
|
"@name": self.name,
|
||||||
"@use": self.use,
|
"@use": self.use,
|
||||||
"@ifcVersion": self.ifcVersion,
|
"@ifcVersion": self.ifcVersion,
|
||||||
"applicability": {},
|
"applicability": {},
|
||||||
"requirements": {},
|
"requirements": {},
|
||||||
}
|
}
|
||||||
for x in ["applicability", "requirements"]:
|
for attribute in ["identifier", "description", "instructions"]:
|
||||||
for fac in (getattr(self, x)).terms:
|
value = getattr(self, attribute)
|
||||||
|
if value:
|
||||||
|
results[f"@{attribute}"] = value
|
||||||
|
for clause_type in ["applicability", "requirements"]:
|
||||||
|
clause = getattr(self, clause_type)
|
||||||
|
if not clause:
|
||||||
|
continue
|
||||||
|
for fac in clause.terms:
|
||||||
fclass = type(fac).__name__
|
fclass = type(fac).__name__
|
||||||
if fclass in spec_dict[x]:
|
if fclass in results[clause_type]:
|
||||||
spec_dict[x][fclass].append(fac.asdict())
|
results[clause_type][fclass].append(fac.asdict())
|
||||||
else:
|
else:
|
||||||
spec_dict[x][fclass] = [fac.asdict()]
|
results[clause_type][fclass] = [fac.asdict()]
|
||||||
return spec_dict
|
return results
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse(ids_dict):
|
def parse(ids_dict):
|
||||||
@@ -266,7 +278,7 @@ class specification:
|
|||||||
spec = specification()
|
spec = specification()
|
||||||
try:
|
try:
|
||||||
spec.name = ids_dict["@name"]
|
spec.name = ids_dict["@name"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
spec.name = ""
|
spec.name = ""
|
||||||
spec.use = ids_dict["@use"]
|
spec.use = ids_dict["@use"]
|
||||||
spec.ifcVersion = ids_dict["@ifcVersion"]
|
spec.ifcVersion = ids_dict["@ifcVersion"]
|
||||||
@@ -416,10 +428,10 @@ class facet(metaclass=meta_facet):
|
|||||||
self.location = "any"
|
self.location = "any"
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
|
|
||||||
if attr in getattr(self, 'node', None):
|
if attr in getattr(self, "node", None):
|
||||||
v = self.node[attr]
|
v = self.node[attr]
|
||||||
|
|
||||||
# BUG list of dictionaries should not happen
|
# BUG list of dictionaries should not happen
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
v = v[0]
|
v = v[0]
|
||||||
@@ -671,11 +683,11 @@ class partOf(facet):
|
|||||||
|
|
||||||
parameters = ["entity"]
|
parameters = ["entity"]
|
||||||
message = "relation as part of %(entity)s"
|
message = "relation as part of %(entity)s"
|
||||||
#TODO temp default
|
# TODO temp default
|
||||||
entity = "IfcElementAssembly"
|
entity = "IfcElementAssembly"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
#TODO should not assume IfcElementAssembly
|
# TODO should not assume IfcElementAssembly
|
||||||
# def create(entity=None):
|
# def create(entity=None):
|
||||||
def create(entity="IfcElementAssembly"):
|
def create(entity="IfcElementAssembly"):
|
||||||
"""Create a partOf facet that can be added to applicability or requirements of IDS specification.
|
"""Create a partOf facet that can be added to applicability or requirements of IDS specification.
|
||||||
@@ -713,33 +725,33 @@ class partOf(facet):
|
|||||||
:rtype: facet_evaluation(bool, str)
|
:rtype: facet_evaluation(bool, str)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#TODO handle partOf facet
|
# TODO handle partOf facet
|
||||||
|
|
||||||
# instance_classiciations = inst.HasAssociations
|
# instance_classiciations = inst.HasAssociations
|
||||||
# if ifcopenshell.util.element.get_type(inst):
|
# if ifcopenshell.util.element.get_type(inst):
|
||||||
# type_classifications = ifcopenshell.util.element.get_type(inst).HasAssociations
|
# type_classifications = ifcopenshell.util.element.get_type(inst).HasAssociations
|
||||||
# else:
|
# else:
|
||||||
# type_classifications = ()
|
# type_classifications = ()
|
||||||
|
|
||||||
# if self.location == "instance" and instance_classiciations:
|
# if self.location == "instance" and instance_classiciations:
|
||||||
# associations = instance_classiciations
|
# associations = instance_classiciations
|
||||||
# elif self.location == "type" and type_classifications:
|
# elif self.location == "type" and type_classifications:
|
||||||
# associations = type_classifications
|
# associations = type_classifications
|
||||||
# elif self.location == "any" and (instance_classiciations or type_classifications):
|
# elif self.location == "any" and (instance_classiciations or type_classifications):
|
||||||
# associations = instance_classiciations + type_classifications
|
# associations = instance_classiciations + type_classifications
|
||||||
# else:
|
# else:
|
||||||
# associations = ()
|
# associations = ()
|
||||||
|
|
||||||
refs = []
|
refs = []
|
||||||
# for association in associations:
|
# for association in associations:
|
||||||
# if association.is_a("IfcRelAssociatesClassification"):
|
# if association.is_a("IfcRelAssociatesClassification"):
|
||||||
# cref = association.RelatingClassification
|
# cref = association.RelatingClassification
|
||||||
# if hasattr(cref, "ItemReference"): # IFC2x3
|
# if hasattr(cref, "ItemReference"): # IFC2x3
|
||||||
# refs.append((cref.ReferencedSource.Name, cref.ItemReference))
|
# refs.append((cref.ReferencedSource.Name, cref.ItemReference))
|
||||||
# elif hasattr(cref, "Identification"): # IFC4
|
# elif hasattr(cref, "Identification"): # IFC4
|
||||||
# refs.append((cref.ReferencedSource.Name, cref.Identification))
|
# refs.append((cref.ReferencedSource.Name, cref.Identification))
|
||||||
|
|
||||||
# self.location_msg = location[self.location]
|
# self.location_msg = location[self.location]
|
||||||
|
|
||||||
if refs:
|
if refs:
|
||||||
pass
|
pass
|
||||||
@@ -753,7 +765,7 @@ class partOf(facet):
|
|||||||
# }, # what if not first item of refs?
|
# }, # what if not first item of refs?
|
||||||
# )
|
# )
|
||||||
else:
|
else:
|
||||||
return facet_evaluation(False, "is not a part of %s" % self.node['@entity'])
|
return facet_evaluation(False, "is not a part of %s" % self.node["@entity"])
|
||||||
|
|
||||||
|
|
||||||
class property(facet):
|
class property(facet):
|
||||||
@@ -818,7 +830,7 @@ class property(facet):
|
|||||||
|
|
||||||
# self.location = self.node["@location"]
|
# self.location = self.node["@location"]
|
||||||
|
|
||||||
#TODO add documentation that attributes should have "attribute" as propertySets
|
# TODO add documentation that attributes should have "attribute" as propertySets
|
||||||
if self.propertySet == "attribute":
|
if self.propertySet == "attribute":
|
||||||
val = {k.lower(): v for k, v in inst.get_info().items()}.get(self.name, None)
|
val = {k.lower(): v for k, v in inst.get_info().items()}.get(self.name, None)
|
||||||
else:
|
else:
|
||||||
@@ -1035,17 +1047,17 @@ class restriction:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def parse(ids_dict):
|
def parse(ids_dict):
|
||||||
"""Parse xml restriction to python object.
|
"""Parse xml restriction to python object.
|
||||||
|
|
||||||
:param ids_dict:
|
:param ids_dict:
|
||||||
:type ids_dict: dict
|
:type ids_dict: dict
|
||||||
"""
|
"""
|
||||||
r = restriction()
|
r = restriction()
|
||||||
if ids_dict:
|
if ids_dict:
|
||||||
# TODO 'base' missing in some IDS?!
|
# TODO 'base' missing in some IDS?!
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r.base = ids_dict["@base"][3:]
|
r.base = ids_dict["@base"][3:]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
r.base = "String"
|
r.base = "String"
|
||||||
|
|
||||||
for n in ids_dict:
|
for n in ids_dict:
|
||||||
@@ -1168,7 +1180,7 @@ class restriction:
|
|||||||
result = True
|
result = True
|
||||||
elif self.type == "pattern":
|
elif self.type == "pattern":
|
||||||
if isinstance(self.options, list):
|
if isinstance(self.options, list):
|
||||||
#TODO handle case with multiple pattern options
|
# TODO handle case with multiple pattern options
|
||||||
translated_pattern = identities.translate_pattern(self.options[0])
|
translated_pattern = identities.translate_pattern(self.options[0])
|
||||||
else:
|
else:
|
||||||
translated_pattern = identities.translate_pattern(self.options)
|
translated_pattern = identities.translate_pattern(self.options)
|
||||||
|
|||||||
@@ -17,10 +17,11 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import pytest
|
||||||
import logging
|
import logging
|
||||||
import unittest
|
import unittest
|
||||||
import tempfile
|
import tempfile
|
||||||
import requests
|
import xmlschema
|
||||||
from bcf import bcfxml
|
from bcf import bcfxml
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from ifcopenshell import ids
|
from ifcopenshell import ids
|
||||||
@@ -163,8 +164,15 @@ class TestIdsAuthoring(unittest.TestCase):
|
|||||||
# TODO test this without resorting to hooking into logger output
|
# TODO test this without resorting to hooking into logger output
|
||||||
|
|
||||||
def test_create_an_ids_with_minimal_information(self):
|
def test_create_an_ids_with_minimal_information(self):
|
||||||
specs = ids.ids(title="title")
|
specs = ids.ids()
|
||||||
assert specs.info == {"title": "title"}
|
assert specs.asdict() == {
|
||||||
|
"@xmlns": "http://standards.buildingsmart.org/IDS",
|
||||||
|
"@xmlns:xs": "http://www.w3.org/2001/XMLSchema",
|
||||||
|
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
|
"@xsi:schemaLocation": "http://standards.buildingsmart.org/IDS/ids_05.xsd",
|
||||||
|
"info": {"title": "Untitled"},
|
||||||
|
"specifications": [],
|
||||||
|
}
|
||||||
|
|
||||||
def test_create_an_ids_with_all_possible_information(self):
|
def test_create_an_ids_with_all_possible_information(self):
|
||||||
specs = ids.ids(
|
specs = ids.ids(
|
||||||
@@ -177,22 +185,83 @@ class TestIdsAuthoring(unittest.TestCase):
|
|||||||
purpose="purpose",
|
purpose="purpose",
|
||||||
milestone="milestone",
|
milestone="milestone",
|
||||||
)
|
)
|
||||||
assert specs.info["title"] == "title"
|
assert specs.asdict() == {
|
||||||
assert specs.info["copyright"] == "copyright"
|
"@xmlns": "http://standards.buildingsmart.org/IDS",
|
||||||
assert specs.info["version"] == "version"
|
"@xmlns:xs": "http://www.w3.org/2001/XMLSchema",
|
||||||
assert specs.info["description"] == "description"
|
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
assert specs.info["author"] == "author@test.com"
|
"@xsi:schemaLocation": "http://standards.buildingsmart.org/IDS/ids_05.xsd",
|
||||||
assert specs.info["date"] == "2020-01-01"
|
"info": {
|
||||||
assert specs.info["purpose"] == "purpose"
|
"title": "title",
|
||||||
assert specs.info["milestone"] == "milestone"
|
"copyright": "copyright",
|
||||||
|
"version": "version",
|
||||||
|
"description": "description",
|
||||||
|
"author": "author@test.com",
|
||||||
|
"date": "2020-01-01",
|
||||||
|
"purpose": "purpose",
|
||||||
|
"milestone": "milestone",
|
||||||
|
},
|
||||||
|
"specifications": [],
|
||||||
|
}
|
||||||
|
|
||||||
def test_check_invalid_ids_information(self):
|
def test_check_invalid_ids_information(self):
|
||||||
specs = ids.ids(title=None)
|
specs = ids.ids(title=None, author="author", date="9999-99-99")
|
||||||
assert specs.info["title"] == "Unnamed"
|
assert specs.asdict() == {
|
||||||
specs = ids.ids(author="author")
|
"@xmlns": "http://standards.buildingsmart.org/IDS",
|
||||||
assert not specs.info.get("author")
|
"@xmlns:xs": "http://www.w3.org/2001/XMLSchema",
|
||||||
specs = ids.ids(date="9999-99-99")
|
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
assert not specs.info.get("date")
|
"@xsi:schemaLocation": "http://standards.buildingsmart.org/IDS/ids_05.xsd",
|
||||||
|
"info": {"title": "Untitled"},
|
||||||
|
"specifications": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_authoring_an_ids_with_no_specifications_is_invalid(self):
|
||||||
|
specs = ids.ids()
|
||||||
|
with pytest.raises(xmlschema.validators.exceptions.XMLSchemaChildrenValidationError):
|
||||||
|
specs.to_string()
|
||||||
|
|
||||||
|
def test_create_specification_with_minimal_information(self):
|
||||||
|
spec = ids.specification()
|
||||||
|
assert spec.asdict() == {
|
||||||
|
"@name": "Unnamed",
|
||||||
|
"@use": "required",
|
||||||
|
"@ifcVersion": "IFC2X3", # :(
|
||||||
|
"applicability": {},
|
||||||
|
"requirements": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_create_specification_with_all_possible_information(self):
|
||||||
|
spec = ids.specification(
|
||||||
|
name="name",
|
||||||
|
use="use",
|
||||||
|
ifcVersion="version",
|
||||||
|
identifier="identifier",
|
||||||
|
description="description",
|
||||||
|
instructions="instructions",
|
||||||
|
)
|
||||||
|
assert spec.asdict() == {
|
||||||
|
"@name": "name",
|
||||||
|
"@use": "use",
|
||||||
|
"@ifcVersion": "version",
|
||||||
|
"@identifier": "identifier",
|
||||||
|
"@description": "description",
|
||||||
|
"@instructions": "instructions",
|
||||||
|
"applicability": {},
|
||||||
|
"requirements": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_ids_add_content(self):
|
||||||
|
i = ids.ids(title="My IDS")
|
||||||
|
i.specifications.append(ids.specification(name="Test_Specification"))
|
||||||
|
self.assertEqual(i.specifications[0].name, "Test_Specification")
|
||||||
|
m = ids.material.create(location="any", value="Test_Value")
|
||||||
|
i.specifications[0].add_applicability(m)
|
||||||
|
self.assertEqual(i.specifications[0].applicability.terms[0].value, "Test_Value")
|
||||||
|
i.specifications[0].add_applicability(m)
|
||||||
|
self.assertEqual(i.specifications[0].applicability.terms[1].value, "Test_Value")
|
||||||
|
i.specifications[0].add_requirement(m)
|
||||||
|
self.assertEqual(i.specifications[0].requirements.terms[0].value, "Test_Value")
|
||||||
|
i.specifications[0].add_requirement(m)
|
||||||
|
self.assertEqual(i.specifications[0].requirements.terms[1].value, "Test_Value")
|
||||||
|
|
||||||
def test_entity_create(self):
|
def test_entity_create(self):
|
||||||
e = ids.entity.create(name="Test_Name", predefinedType="Test_PredefinedType")
|
e = ids.entity.create(name="Test_Name", predefinedType="Test_PredefinedType")
|
||||||
@@ -229,24 +298,6 @@ class TestIdsAuthoring(unittest.TestCase):
|
|||||||
self.assertEqual(m.location, "any")
|
self.assertEqual(m.location, "any")
|
||||||
self.assertEqual(m.value, "Test_Value")
|
self.assertEqual(m.value, "Test_Value")
|
||||||
|
|
||||||
def test_specification_create(self):
|
|
||||||
s = ids.specification(name="Test_Specification")
|
|
||||||
self.assertEqual(s.name, "Test_Specification")
|
|
||||||
|
|
||||||
def test_ids_add_content(self):
|
|
||||||
i = ids.ids(title="My IDS")
|
|
||||||
i.specifications.append(ids.specification(name="Test_Specification"))
|
|
||||||
self.assertEqual(i.specifications[0].name, "Test_Specification")
|
|
||||||
m = ids.material.create(location="any", value="Test_Value")
|
|
||||||
i.specifications[0].add_applicability(m)
|
|
||||||
self.assertEqual(i.specifications[0].applicability.terms[0].value, "Test_Value")
|
|
||||||
i.specifications[0].add_applicability(m)
|
|
||||||
self.assertEqual(i.specifications[0].applicability.terms[1].value, "Test_Value")
|
|
||||||
i.specifications[0].add_requirement(m)
|
|
||||||
self.assertEqual(i.specifications[0].requirements.terms[0].value, "Test_Value")
|
|
||||||
i.specifications[0].add_requirement(m)
|
|
||||||
self.assertEqual(i.specifications[0].requirements.terms[1].value, "Test_Value")
|
|
||||||
|
|
||||||
""" Creating IDS with restrictions """
|
""" Creating IDS with restrictions """
|
||||||
|
|
||||||
def test_create_restrictions_enumeration(self):
|
def test_create_restrictions_enumeration(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user