diff --git a/src/ifcbimtester/bimtester/features/steps/all.py b/src/ifcbimtester/bimtester/features/steps/all.py new file mode 100644 index 0000000000..50eaec41aa --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/all.py @@ -0,0 +1,15 @@ +use_step_matcher("parse") +from bimtester.features.steps.classification import en +use_step_matcher("parse") +from bimtester.features.steps.element_classes import en +use_step_matcher("parse") +from bimtester.features.steps.geocoding import en +use_step_matcher("parse") +from bimtester.features.steps.geolocation import en +use_step_matcher("parse") +from bimtester.features.steps.geometric_detail import en +use_step_matcher("parse") +from bimtester.features.steps.model_federation import en +use_step_matcher("parse") +from bimtester.features.steps.project_setup import de, en, fr, it, nl +use_step_matcher("parse") diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_de.py b/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_de.py deleted file mode 100644 index 51517ab5b0..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_de.py +++ /dev/null @@ -1,52 +0,0 @@ -from behave import step - -import attributes_eleclasses_methods as aem -from utils import switch_locale - - -the_lang = "de" - - -@step("Es sind keine {ifc_class} Bauteile vorhanden") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.no_eleclass( - context, - ifc_class - ) - - -@step("Aus folgendem Grund gibt es keine {ifc_class} Bauteile: {reason}") -def step_impl(context, ifc_class, reason): - switch_locale(context.localedir, the_lang) - aem.no_eleclass( - context, - ifc_class - ) - - -@step("Alle {ifc_class} Bauteilklassenattribute haben einen Wert") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_have_class_attributes_with_a_value( - context, - ifc_class - ) - - -@step("Bei allen {ifc_class} Bauteile ist der Name angegeben") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_has_name_with_a_value( - context, - ifc_class - ) - - -@step("Bei allen {ifc_class} Bauteile ist die Beschreibung angegeben") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_has_description_with_a_value( - context, - ifc_class - ) diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_fr.py b/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_fr.py deleted file mode 100644 index adbf1d9796..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_fr.py +++ /dev/null @@ -1,59 +0,0 @@ -from behave import step - -import attributes_eleclasses_methods as aem -from utils import switch_locale - - -the_lang = "fr" - - -""" -# TODO the next line needs translation -@step("There are no {ifc_class} elements") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.no_eleclass( - context, - ifc_class - ) - - -# TODO the next line needs translation -@step("There are no {ifc_class} elements because {reason}") -def step_impl(context, ifc_class, reason): - switch_locale(context.localedir, the_lang) - aem.no_eleclass( - context, - ifc_class - ) - - -# TODO the next line needs translation -@step("All {ifc_class} elements class attributes have a value") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_have_class_attributes_with_a_value( - context, - ifc_class - ) - - -# TODO the next line needs translation -@step("All {ifc_class} elements have a name given") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_has_name_with_a_value( - context, - ifc_class - ) - - -# TODO the next line needs translation -@step("All {ifc_class} elements have a description given") -def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_has_description_with_a_value( - context, - ifc_class - ) -""" diff --git a/src/ifcbimtester/bimtester/features/steps/classification.py b/src/ifcbimtester/bimtester/features/steps/classification.py deleted file mode 100644 index 3ed1e8d35a..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/classification.py +++ /dev/null @@ -1,73 +0,0 @@ -import json -from behave import step - -from utils import assert_attribute -from utils import assert_type -from utils import IfcFile - - -def get_classification(name): - classifications = [c for c in IfcFile.get().by_type("IfcClassification") if c.Name == name] - if len(classifications) != 1: - assert False, f'The classification "{name}" was not found' - return classifications[0] - - -@step("The classification {name} must be used") -def step_impl(context, name): - get_classification(name) - - -@step("The classification {name} is published by {source}") -def step_impl(context, name, source): - assert_attribute(get_classification(name), "Source", source) - - -@step("The classification {name} is the edition {edition} on {edition_date}") -def step_impl(context, name, edition, edition_date): - element = get_classification(name) - assert_attribute(element, "Edition", edition) - assert_attribute(element, "EditionDate", edition_date) - - -@step('The classification {name} has the description "{description}"') -def step_impl(context, name, description): - assert_attribute(get_classification(name), "Description", description) - - -@step("The classification {name} is referenced by the website {location}") -def step_impl(context, name, location): - assert_attribute(get_classification(name), "Location", location) - - -@step("The classification {name} has a hierarchy denoted by the tokens {tokens}") -def step_impl(context, name, tokens): - try: - tokens = json.loads(tokens) - except: - assert False, f"Tokens {tokens} are not specified as a JSON list" - assert_attribute(get_classification(name), "ReferenceTokens", tokens) - - -@step('The element {guid} is classified as a "{identification}" with name "{reference_name}"') -def step_impl(context, guid, identification, reference_name): - element = IfcFile.by_guid(guid) - if not hasattr(element, "HasAssociations") or not element.HasAssociations: - assert False, f"The element {element} has no associations." - references = [a.RelatingClassification for a in element.HasAssociations if a.is_a("IfcRelAssociatesClassification")] - if not references: - assert False, f"The element {element} has no associated classification references." - is_success = False - for reference in references: - try: - assert_attribute(reference, "Identification", identification) - assert_attribute(reference, "Name", reference_name) - is_success = True - except: - pass - if not is_success: - assert ( - False - ), "No classification references met the requirement for an identification {} and name {} for the element {}. The references we found were: {}".format( - identification, reference_name, element, references - ) diff --git a/src/ifcbimtester/bimtester/features/steps/classification/en.py b/src/ifcbimtester/bimtester/features/steps/classification/en.py new file mode 100644 index 0000000000..81e54e6ad7 --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/classification/en.py @@ -0,0 +1,70 @@ +import json +from behave import step +from bimtester import util +from bimtester.ifc import IfcStore +from bimtester.lang import _ + + +def get_classification(name): + classifications = [c for c in IfcStore.file.by_type("IfcClassification") if c.Name == name] + if len(classifications) != 1: + assert False, f'The classification "{name}" was not found' + return classifications[0] + + +@step('The classification "{name}" must be used') +def step_impl(context, name): + get_classification(name) + + +@step('The classification "{name}" is published by "{source}"') +def step_impl(context, name, source): + util.assert_attribute(get_classification(name), "Source", source) + + +@step('The classification "{name}" is the edition "{edition}" on "{edition_date}"') +def step_impl(context, name, edition, edition_date): + element = get_classification(name) + util.assert_attribute(element, "Edition", edition) + util.assert_attribute(element, "EditionDate", edition_date) + + +@step('The classification "{name}" has the description "{description}"') +def step_impl(context, name, description): + util.assert_attribute(get_classification(name), "Description", description) + + +@step('The classification "{name}" is referenced by the website "{location}"') +def step_impl(context, name, location): + util.assert_attribute(get_classification(name), "Location", location) + + +@step('The classification "{name}" has a hierarchy denoted by the tokens "{tokens}"') +def step_impl(context, name, tokens): + try: + tokens = json.loads(tokens) + except: + assert False, _("Tokens {} are not specified as a JSON list").format(tokens) + util.assert_attribute(get_classification(name), "ReferenceTokens", tokens) + + +@step('The element "{guid}" is classified as a "{identification}" with name "{reference_name}"') +def step_impl(context, guid, identification, reference_name): + element = util.assert_guid(IfcStore.file, guid) + if not hasattr(element, "HasAssociations") or not element.HasAssociations: + assert False, _("The element {} has no associations.").format(element) + references = [a.RelatingClassification for a in element.HasAssociations if a.is_a("IfcRelAssociatesClassification")] + if not references: + assert False, _("The element {element} has no associated classification references.").format(element) + is_success = False + for reference in references: + try: + util.assert_attribute(reference, "Identification", identification) + util.assert_attribute(reference, "Name", reference_name) + is_success = True + except: + pass + if not is_success: + assert False, _( + "No classification references met the requirement for an identification {} and name {} for the element {}. The references we found were: {}" + ).format(identification, reference_name, element, references) diff --git a/src/ifcbimtester/bimtester/features/steps/element_classes/en.py b/src/ifcbimtester/bimtester/features/steps/element_classes/en.py new file mode 100644 index 0000000000..ab4031f5bc --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/element_classes/en.py @@ -0,0 +1,40 @@ +from behave import step +from bimtester import util +from bimtester.ifc import IfcStore +from bimtester.lang import _ + + +@step('The element "{guid}" is an "{ifc_class}" only') +def step_impl(context, guid, ifc_class): + element = util.assert_guid(IfcStore.file, guid) + util.assert_type(element, ifc_class, is_exact=True) + + +@step('The element "{guid}" is an "{ifc_class}"') +def step_impl(context, guid, ifc_class): + element = util.assert_guid(IfcStore.file, guid) + util.assert_type(element, ifc_class) + + +@step('The element "{guid}" is further defined as a "{predefined_type}"') +def step_impl(context, guid, predefined_type): + element = util.assert_guid(IfcStore.file, guid) + if ( + hasattr(element, "PredefinedType") + and element.PredefinedType == "USERDEFINED" + and hasattr(element, "ObjectType") + ): + util.assert_attribute(element, "ObjectType", predefined_type) + elif hasattr(element, "PredefinedType"): + util.assert_attribute(element, "PredefinedType", predefined_type) + else: + assert False, _("The element {} does not have a PredefinedType or ObjectType attribute").format(element) + + +@step('The element "{guid}" should not exist because "{reason}"') +def step_impl(context, guid, reason): + try: + element = IfcStore.file.by_id(guid) + except: + return + assert False, _("This element {} should be reevaluated.").format(element) diff --git a/src/ifcbimtester/bimtester/features/steps/element_classes_guid.py b/src/ifcbimtester/bimtester/features/steps/element_classes_guid.py deleted file mode 100644 index 07ea37ffce..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/element_classes_guid.py +++ /dev/null @@ -1,41 +0,0 @@ -from behave import step - -from utils import assert_attribute -from utils import assert_type -from utils import IfcFile - - -@step("The element {guid} is an {ifc_class} only") -def step_impl(context, guid, ifc_class): - element = IfcFile.by_guid(guid) - assert_type(element, ifc_class, is_exact=True) - - -@step("The element {guid} is an {ifc_class}") -def step_impl(context, guid, ifc_class): - element = IfcFile.by_guid(guid) - assert_type(element, ifc_class) - - -@step("The element {guid} is further defined as a {predefined_type}") -def step_impl(context, guid, predefined_type): - element = IfcFile.by_guid(guid) - if ( - hasattr(element, "PredefinedType") - and element.PredefinedType == "USERDEFINED" - and hasattr(element, "ObjectType") - ): - assert_attribute(element, "ObjectType", predefined_type) - elif hasattr(element, "PredefinedType"): - assert_attribute(element, "PredefinedType", predefined_type) - else: - assert False, "The element {} does not have a PredefinedType or ObjectType attribute".format(element) - - -@step("The element {guid} should not exist because {reason}") -def step_impl(context, guid, reason): - try: - element = IfcFile.get().by_id(guid) - except: - return - assert False, "This element {} should be reevaluated.".format(element) diff --git a/src/ifcbimtester/bimtester/features/steps/geocoding.py b/src/ifcbimtester/bimtester/features/steps/geocoding/en.py similarity index 57% rename from src/ifcbimtester/bimtester/features/steps/geocoding.py rename to src/ifcbimtester/bimtester/features/steps/geocoding/en.py index 3cc4a8ad4a..ed3c8ae5f8 100644 --- a/src/ifcbimtester/bimtester/features/steps/geocoding.py +++ b/src/ifcbimtester/bimtester/features/steps/geocoding/en.py @@ -1,8 +1,7 @@ -from behave import step - -from utils import assert_attribute -from utils import assert_type -from utils import IfcFile +from behave import step, use_step_matcher +from bimtester import util +from bimtester.ifc import IfcStore +from bimtester.lang import _ def get_ifc_class_from_spatial_type(spatial_type): @@ -14,71 +13,71 @@ def get_ifc_class_from_spatial_type(spatial_type): def check_geocode_attribute(guid, spatial_type, name, value): - element = IfcFile.by_guid(guid) - assert_type(element, get_ifc_class_from_spatial_type(spatial_type)) - assert_attribute(element, name, value) + element = util.assert_guid(IfcStore.file, guid) + util.assert_type(element, get_ifc_class_from_spatial_type(spatial_type)) + util.assert_attribute(element, name, value) def check_geocode_address(guid, spatial_type, name, value): - element = IfcFile.by_guid(guid) + element = util.assert_guid(IfcStore.file, guid) ifc_class = get_ifc_class_from_spatial_type(spatial_type) - assert_type(element, ifc_class) + util.assert_type(element, ifc_class) if ifc_class == "IfcSite": address_name = "SiteAddress" elif ifc_class == "IfcBuilding": address_name = "BuildingAddress" - assert_attribute(element, address_name) - assert_attribute(getattr(element, address_name), name, value) + util.assert_attribute(element, address_name) + util.assert_attribute(getattr(element, address_name), name, value) use_step_matcher("re") -@step("The (site|building|facility) (?P.*) has a name of (?P.*)") +@step('The (site|building|facility) "(?P.*)" has a name of "(?P.*)"') def step_impl(context, spatial_type, guid, name): check_geocode_attribute(guid, spatial_type, "Name", name) -@step('The (site|building|facility) (?P.*) has a description of "(?P.*)"') +@step('The (site|building|facility) "(?P.*)" has a description of "(?P.*)"') def step_impl(context, spatial_type, guid, description): check_geocode_attribute(guid, spatial_type, "Description", description) -@step("The site (?P.*) has a land title number of (?P.*)") +@step('The site "(?P.*)" has a land title number of "(?P.*)"') def step_impl(context, guid, land_title_number): check_geocode_attribute(guid, "site", "LandTitleNumber", land_title_number) -@step('The (site|building) (?P.*) has the address "(?P.*)"') +@step('The (site|building) "(?P.*)" has the address "(?P.*)"') def step_impl(context, spatial_type, guid, address_lines): check_geocode_address(guid, spatial_type, "AddressLines", address_lines.split("\\n")) -@step("The (site|building) (?P.*) has a postal box of (?P.*)") +@step('The (site|building) "(?P.*)" has a postal box of "(?P.*)"') def step_impl(context, spatial_type, guid, postal_box): check_geocode_address(guid, spatial_type, "PostalBox", postal_box) -@step("The (site|building) (?P.*) is in the town (?P.*)") +@step('The (site|building) "(?P.*)" is in the town "(?P.*)"') def step_impl(context, spatial_type, guid, town): check_geocode_address(guid, spatial_type, "Town", town) -@step("The (site|building) (?P.*) is in the region (?P.*)") +@step('The (site|building) "(?P.*)" is in the region "(?P.*)"') def step_impl(context, spatial_type, guid, region): check_geocode_address(guid, spatial_type, "Region", region) -@step("The (site|building) (?P.*) has a post code of (?P.*)") +@step('The (site|building) "(?P.*)" has a post code of "(?P.*)"') def step_impl(context, spatial_type, guid, post_code): check_geocode_address(guid, spatial_type, "PostalCode", post_code) -@step("The (site|building) (?P.*) is in the country (?P.*)") +@step('The (site|building) "(?P.*)" is in the country "(?P.*)"') def step_impl(context, spatial_type, guid, country): check_geocode_address(guid, spatial_type, "Country", country) -@step('The (site|building) (?P.*) has an address description of "(?P.*)"') +@step('The (site|building) "(?P.*)" has an address description of "(?P.*)"') def step_impl(context, spatial_type, guid, description): check_geocode_address(guid, spatial_type, "Description", description) diff --git a/src/ifcbimtester/bimtester/features/steps/geolocation.py b/src/ifcbimtester/bimtester/features/steps/geolocation.py deleted file mode 100644 index 8981b0f4de..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/geolocation.py +++ /dev/null @@ -1,219 +0,0 @@ -import math -import ifcopenshell.util -import ifcopenshell.util.element -import ifcopenshell.util.geolocation -from behave import step - -from utils import assert_attribute -from utils import assert_number -from utils import assert_pset -from utils import IfcFile - - -@step(u"There must be at least one {ifc_class} element") -def step_impl(context, ifc_class): - assert len(IfcFile.get().by_type(ifc_class)) >= 1, "An element of {} could not be found".format(ifc_class) - - -def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_assert=True): - if entity_name not in IfcFile.bookmarks: - has_entity = False - project = IfcFile.get().by_type("IfcProject")[0] - for context in project.RepresentationContexts: - if entity_name == "IfcMapConversion": - if ( - context.is_a("IfcGeometricRepresentationContext") - and context.ContextType == "Model" - and context.HasCoordinateOperation - ): - IfcFile.bookmarks[entity_name] = context.HasCoordinateOperation[0] - has_entity = True - elif entity_name == "IfcProjectedCRS": - if ( - context.is_a("IfcGeometricRepresentationContext") - and context.ContextType == "Model" - and context.HasCoordinateOperation - and context.HasCoordinateOperation[0].TargetCRS - ): - IfcFile.bookmarks[entity_name] = context.HasCoordinateOperation[0].TargetCRS - has_entity = True - if not has_entity: - assert False, "No model geometric representation contexts refer to an {}".format(entity_name) - if not prop_name: - return - actual_value = getattr(IfcFile.bookmarks[entity_name], prop_name) - if should_assert: - assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value) - else: - return actual_value - - -@step(u"The project must have coordinate reference system data") -def step_impl(context): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS") - return - check_ifc4_geolocation("IfcProjectedCRS") - - -@step(u"The name of the CRS must be {coordinate_reference_name}") -def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "Name", coordinate_reference_name) - return - check_ifc4_geolocation("IfcProjectedCRS", "Name", coordinate_reference_name) - - -@step(u"The description of the CRS must be {value}") -def step_impl(context, value): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "Description", value) - return - check_ifc4_geolocation("IfcProjectedCRS", "Description", value) - - -@step(u"The geodetic datum must be {coordinate_reference_name}") -def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "GeodeticDatum", coordinate_reference_name) - return - check_ifc4_geolocation("IfcProjectedCRS", "GeodeticDatum", coordinate_reference_name) - - -@step(u"The vertical datum must be {coordinate_reference_name}") -def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "VerticalDatum", coordinate_reference_name) - return - check_ifc4_geolocation("IfcProjectedCRS", "VerticalDatum", coordinate_reference_name) - - -@step(u"The map projection must be {coordinate_reference_name}") -def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "MapProjection", coordinate_reference_name) - return - check_ifc4_geolocation("IfcProjectedCRS", "MapProjection", coordinate_reference_name) - - -@step(u"The map zone must be {coordinate_reference_name}") -def step_impl(context, coordinate_reference_name): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "MapZone", coordinate_reference_name) - return - check_ifc4_geolocation("IfcProjectedCRS", "MapZone", coordinate_reference_name) - - -@step(u"The map unit must be {unit}") -def step_impl(context, unit): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_ProjectedCRS", "MapUnit", unit) - return - actual_value = check_ifc4_geolocation("IfcProjectedCRS", "MapUnit", should_assert=False) - if not actual_value: - assert False, "A unit was not provided in the projected CRS" - if actual_value.is_a("IfcSIUnit"): - prefix = actual_value.Prefix if actual_value.Prefix else "" - actual_value = prefix + actual_value.Name - elif actual_value.is_a("IfcConversionBasedUnit"): - actual_value = actual_value.Name - assert actual_value == unit, 'We expected a value of "{}" but instead got "{}"'.format(unit, actual_value) - - -@step(u"The project must have coordinate transformations to convert from local to global coordinates") -def step_impl(context): - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_MapConversion") - check_ifc4_geolocation("IfcMapConversion") - - -@step(u"The eastings of the model must be offset by {number} to derive its global coordinates") -def step_impl(context, number): - number = assert_number(number) - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_MapConversion", "Eastings", number) - return - check_ifc4_geolocation("IfcMapConversion", "Eastings", number) - - -@step(u"The northings of the model must be offset by {number} to derive its global coordinates") -def step_impl(context, number): - number = assert_number(number) - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_MapConversion", "Northings", number) - return - check_ifc4_geolocation("IfcMapConversion", "Northings", number) - - -@step(u"The height of the model must be offset by {number} to derive its global coordinates") -def step_impl(context, number): - number = assert_number(number) - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_MapConversion", "OrthogonalHeight", number) - return - check_ifc4_geolocation("IfcMapConversion", "OrthogonalHeight", number) - - -@step(u"The model must be rotated clockwise by {number} to derive its global coordinates") -def step_impl(context, number): - number = assert_number(number) - if IfcFile.get().schema == "IFC2X3": - return check_ifc2x3_geolocation("EPset_MapConversion", "Height", number) - abscissa = check_ifc4_geolocation("IfcMapConversion", "XAxisAbscissa", should_assert=False) - ordinate = check_ifc4_geolocation("IfcMapConversion", "XAxisOrdinate", should_assert=False) - actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate), 3) - value = round(number, 3) - assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value) - - -@step(u"The model must be scaled along the horizontal axis by {number} to derive its global coordinates") -def step_impl(context, number): - number = assert_number(number) - if IfcFile.get().schema == "IFC2X3": - for site in IfcFile.get().by_type("IfcSite"): - assert_pset(site, "EPset_MapConversion", "Scale", number) - return - check_ifc4_geolocation("IfcMapConversion", "Scale", number) - - -@step(u"The site {guid} has a longitude of {number}") -def step_impl(context, guid, number): - number = assert_number(number) - site = IfcFile.by_guid(guid) - if not site.is_a("IfcSite"): - assert False, "The element {} is not an IfcSite".format(site) - ref = assert_attribute(site, "RefLongitude") - number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) - assert_attribute(site, "RefLongitude", number) - - -@step(u"The site {guid} has a latitude of {number}") -def step_impl(context, guid, number): - number = assert_number(number) - site = IfcFile.by_guid(guid) - if not site.is_a("IfcSite"): - assert False, "The element {} is not an IfcSite".format(site) - ref = assert_attribute(site, "RefLatitude") - number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) - assert_attribute(site, "RefLatitude", number) - - -@step(u"The site {guid} has an elevation of {number}") -def step_impl(context, guid, number): - number = assert_number(number) - site = IfcFile.by_guid(guid) - if not site.is_a("IfcSite"): - assert False, "The element {} is not an IfcSite".format(site) - assert_attribute(site, "RefElevation", number) diff --git a/src/ifcbimtester/bimtester/features/steps/geolocation/en.py b/src/ifcbimtester/bimtester/features/steps/geolocation/en.py new file mode 100644 index 0000000000..c83813456c --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/geolocation/en.py @@ -0,0 +1,214 @@ +import math +import ifcopenshell +import ifcopenshell.util.element +import ifcopenshell.util.geolocation +from behave import step +from bimtester import util +from bimtester.ifc import IfcStore +from bimtester.lang import _ + + +@step(u'There must be at least one "{ifc_class}" element') +def step_impl(context, ifc_class): + assert len(IfcStore.file.by_type(ifc_class)) >= 1, _("An element of {} could not be found").format(ifc_class) + + +def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_assert=True): + if entity_name not in IfcStore.bookmarks: + has_entity = False + project = IfcStore.file.by_type("IfcProject")[0] + for context in project.RepresentationContexts: + if entity_name == "IfcMapConversion": + if ( + context.is_a("IfcGeometricRepresentationContext") + and context.ContextType == "Model" + and context.HasCoordinateOperation + ): + IfcStore.bookmarks[entity_name] = context.HasCoordinateOperation[0] + has_entity = True + elif entity_name == "IfcProjectedCRS": + if ( + context.is_a("IfcGeometricRepresentationContext") + and context.ContextType == "Model" + and context.HasCoordinateOperation + and context.HasCoordinateOperation[0].TargetCRS + ): + IfcStore.bookmarks[entity_name] = context.HasCoordinateOperation[0].TargetCRS + has_entity = True + if not has_entity: + assert False, _("No model geometric representation contexts refer to an {}").format(entity_name) + if not prop_name: + return + actual_value = getattr(IfcStore.bookmarks[entity_name], prop_name) + if should_assert: + assert actual_value == value, _('We expected a value of "{}" but instead got "{}"').format(value, actual_value) + else: + return actual_value + + +@step(u"The project must have coordinate reference system data") +def step_impl(context): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS") + return + check_ifc4_geolocation("IfcProjectedCRS") + + +@step(u'The name of the CRS must be "{coordinate_reference_name}"') +def step_impl(context, coordinate_reference_name): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "Name", coordinate_reference_name) + return + check_ifc4_geolocation("IfcProjectedCRS", "Name", coordinate_reference_name) + + +@step(u'The description of the CRS must be "{value}"') +def step_impl(context, value): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "Description", value) + return + check_ifc4_geolocation("IfcProjectedCRS", "Description", value) + + +@step(u'The geodetic datum must be "{coordinate_reference_name}"') +def step_impl(context, coordinate_reference_name): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "GeodeticDatum", coordinate_reference_name) + return + check_ifc4_geolocation("IfcProjectedCRS", "GeodeticDatum", coordinate_reference_name) + + +@step(u'The vertical datum must be "{coordinate_reference_name}"') +def step_impl(context, coordinate_reference_name): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "VerticalDatum", coordinate_reference_name) + return + check_ifc4_geolocation("IfcProjectedCRS", "VerticalDatum", coordinate_reference_name) + + +@step(u'The map projection must be "{coordinate_reference_name}"') +def step_impl(context, coordinate_reference_name): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "MapProjection", coordinate_reference_name) + return + check_ifc4_geolocation("IfcProjectedCRS", "MapProjection", coordinate_reference_name) + + +@step(u'The map zone must be "{coordinate_reference_name}"') +def step_impl(context, coordinate_reference_name): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "MapZone", coordinate_reference_name) + return + check_ifc4_geolocation("IfcProjectedCRS", "MapZone", coordinate_reference_name) + + +@step(u'The map unit must be "{unit}"') +def step_impl(context, unit): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_ProjectedCRS", "MapUnit", unit) + return + actual_value = check_ifc4_geolocation("IfcProjectedCRS", "MapUnit", should_assert=False) + if not actual_value: + assert False, _("A unit was not provided in the projected CRS") + if actual_value.is_a("IfcSIUnit"): + prefix = actual_value.Prefix if actual_value.Prefix else "" + actual_value = prefix + actual_value.Name + elif actual_value.is_a("IfcConversionBasedUnit"): + actual_value = actual_value.Name + assert actual_value == unit, _('We expected a value of "{}" but instead got "{}"').format(unit, actual_value) + + +@step(u"The project must have coordinate transformations to convert from local to global coordinates") +def step_impl(context): + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_MapConversion") + check_ifc4_geolocation("IfcMapConversion") + + +@step(u'The eastings of the model must be offset by "{number}" to derive its global coordinates') +def step_impl(context, number): + number = util.assert_number(number) + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_MapConversion", "Eastings", number) + return + check_ifc4_geolocation("IfcMapConversion", "Eastings", number) + + +@step(u'The northings of the model must be offset by "{number}" to derive its global coordinates') +def step_impl(context, number): + number = util.assert_number(number) + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_MapConversion", "Northings", number) + return + check_ifc4_geolocation("IfcMapConversion", "Northings", number) + + +@step(u'The height of the model must be offset by "{number}" to derive its global coordinates') +def step_impl(context, number): + number = util.assert_number(number) + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_MapConversion", "OrthogonalHeight", number) + return + check_ifc4_geolocation("IfcMapConversion", "OrthogonalHeight", number) + + +@step(u'The model must be rotated clockwise by "{number}" to derive its global coordinates') +def step_impl(context, number): + number = util.assert_number(number) + if IfcStore.file.schema == "IFC2X3": + return check_ifc2x3_geolocation("EPset_MapConversion", "Height", number) + abscissa = check_ifc4_geolocation("IfcMapConversion", "XAxisAbscissa", should_assert=False) + ordinate = check_ifc4_geolocation("IfcMapConversion", "XAxisOrdinate", should_assert=False) + actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate), 3) + value = round(number, 3) + assert actual_value == value, _('We expected a value of "{}" but instead got "{}"').format(value, actual_value) + + +@step(u'The model must be scaled along the horizontal axis by "{number}" to derive its global coordinates') +def step_impl(context, number): + number = util.assert_number(number) + if IfcStore.file.schema == "IFC2X3": + for site in IfcStore.file.by_type("IfcSite"): + util.assert_pset(site, "EPset_MapConversion", "Scale", number) + return + check_ifc4_geolocation("IfcMapConversion", "Scale", number) + + +@step(u'The site "{guid}" has a longitude of "{number}"') +def step_impl(context, guid, number): + number = util.assert_number(number) + site = util.assert_guid(IfcStore.file, guid) + util.assert_type(site, "IfcSite") + ref = util.assert_attribute(site, "RefLongitude") + number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) + util.assert_attribute(site, "RefLongitude", number) + + +@step(u'The site "{guid}" has a latitude of "{number}"') +def step_impl(context, guid, number): + number = util.assert_number(number) + site = util.assert_guid(IfcStore.file, guid) + util.assert_type(site, "IfcSite") + ref = util.assert_attribute(site, "RefLatitude") + number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4)) + util.assert_attribute(site, "RefLatitude", number) + + +@step(u'The site "{guid}" has an elevation of "{number}"') +def step_impl(context, guid, number): + number = util.assert_number(number) + site = util.assert_guid(IfcStore.file, guid) + util.assert_type(site, "IfcSite") + util.assert_attribute(site, "RefElevation", number) diff --git a/src/ifcbimtester/bimtester/features/steps/geometric_detail/en.py b/src/ifcbimtester/bimtester/features/steps/geometric_detail/en.py new file mode 100644 index 0000000000..c2f7a7d694 --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/geometric_detail/en.py @@ -0,0 +1,29 @@ +from behave import step +from bimtester import util +from bimtester.ifc import IfcStore +from bimtester.lang import _ + + +@step('All elements must be under "{number}" polygons') +def step_impl(context, number): + number = int(number) + errors = [] + for element in IfcStore.file.by_type("IfcElement"): + if not element.Representation: + continue + total_polygons = 0 + tree = IfcStore.file.traverse(element.Representation) + for e in tree: + if e.is_a("IfcFace"): + total_polygons += 1 + elif e.is_a("IfcPolygonalFaceSet"): + total_polygons += len(e.Faces) + elif e.is_a("IfcTriangulatedFaceSet"): + total_polygons += len(e.CoordIndex) + if total_polygons > number: + errors.append((total_polygons, element)) + if errors: + message = "The following {} elements are over 500 polygons:\n".format(len(errors)) + for error in errors: + message += "Polygons: {} - {}\n".format(error[0], error[1]) + assert False, message diff --git a/src/ifcbimtester/bimtester/features/steps/geometric_detail_fr.py b/src/ifcbimtester/bimtester/features/steps/geometric_detail_fr.py deleted file mode 100644 index b1e285e1a8..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/geometric_detail_fr.py +++ /dev/null @@ -1,20 +0,0 @@ -from behave import step - -import geometric_detail_methods as gdm -from utils import switch_locale - - -the_lang = "fr" - - -""" -# TODO the next line needs translation -@step("all {ifc_class} elements have an {representation_class} representation") -def step_impl(context, ifc_class, representation_class): - switch_locale(context.localedir, the_lang) - gdm.eleclass_has_geometric_representation_of_specific_class( - context, - ifc_class, - representation_class - ) -""" diff --git a/src/ifcbimtester/bimtester/features/steps/ifcdata_de.py b/src/ifcbimtester/bimtester/features/steps/ifcdata_de.py deleted file mode 100644 index c36af66e98..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/ifcdata_de.py +++ /dev/null @@ -1,6 +0,0 @@ -from behave import step - - -@step("Die IFC-Daten müssen das {schema} Schema benutzen") -def step_impl(context, schema): - context.execute_steps(f"* IFC data must use the {schema} schema") diff --git a/src/ifcbimtester/bimtester/features/steps/ifcdata_fr.py b/src/ifcbimtester/bimtester/features/steps/ifcdata_fr.py deleted file mode 100644 index 74d4cfd5ac..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/ifcdata_fr.py +++ /dev/null @@ -1,6 +0,0 @@ -from behave import step - - -@step("Les données IFC doivent utiliser le schéma {schema}") -def step_impl(context, schema): - context.execute_steps(f"* IFC data must use the {schema} schema") diff --git a/src/ifcbimtester/bimtester/features/steps/ifcdata_it.py b/src/ifcbimtester/bimtester/features/steps/ifcdata_it.py deleted file mode 100644 index 435322227f..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/ifcdata_it.py +++ /dev/null @@ -1,6 +0,0 @@ -from behave import step - - -@step("I dati IFC devono seguire lo schema {schema}") -def step_impl(context, schema): - context.execute_steps(f"* IFC data must use the {schema} schema") diff --git a/src/ifcbimtester/bimtester/features/steps/ifcdata_nl.py b/src/ifcbimtester/bimtester/features/steps/ifcdata_nl.py deleted file mode 100644 index de9919e319..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/ifcdata_nl.py +++ /dev/null @@ -1,6 +0,0 @@ -from behave import step - - -@step("IFC-gegevens moeten het {schema} -schema gebruiken") -def step_impl(context, schema): - context.execute_steps(f"* IFC data must use the {schema} schema") diff --git a/src/ifcbimtester/bimtester/features/steps/model_federation.py b/src/ifcbimtester/bimtester/features/steps/model_federation/en.py similarity index 64% rename from src/ifcbimtester/bimtester/features/steps/model_federation.py rename to src/ifcbimtester/bimtester/features/steps/model_federation/en.py index 7190918533..c120825d77 100644 --- a/src/ifcbimtester/bimtester/features/steps/model_federation.py +++ b/src/ifcbimtester/bimtester/features/steps/model_federation/en.py @@ -1,10 +1,9 @@ import ifcopenshell.util.geolocation import ifcopenshell.util.placement from behave import step - -from utils import assert_number -from utils import assert_type -from utils import IfcFile +from bimtester import util +from bimtester.ifc import IfcStore +from bimtester.lang import _ def get_decimal_points(value): @@ -28,17 +27,17 @@ def get_containing_spatial_elements(element): return results -@step("There is a datum element {guid} as an {ifc_class}") +@step('There is a datum element "{guid}" as an "{ifc_class}"') def step_impl(context, guid, ifc_class): - element = IfcFile.by_guid(guid) - assert_type(element, ifc_class) + element = utils.assert_guid(IfcStore.file, guid) + util.assert_type(element, ifc_class) @step( - "The element {guid} has a global easting, northing, and elevation of {easting}, {northing}, and {elevation} respectively" + 'The element "{guid}" has a global easting, northing, and elevation of "{easting}", "{northing}", and "{elevation}" respectively' ) def step_impl(context, guid, easting, northing, elevation): - if IfcFile.get().schema == "IFC2X3": + if IfcStore.file.schema == "IFC2X3": if element.is_a("IfcSite"): site = element else: @@ -46,18 +45,18 @@ def step_impl(context, guid, easting, northing, elevation): if potential_sites: site = potential_sites[0] else: - assert False, "The datum element does not belong to a geolocated site" + assert False, _("The datum element does not belong to a geolocated site") map_conversion = assert_pset(site, "EPset_MapConversion") else: - map_conversion = IfcFile.get().by_type("IfcMapConversion") + map_conversion = IfcStore.file.by_type("IfcMapConversion") if map_conversion: map_conversion = map_conversion[0].get_info() else: - assert False, "No map conversion was found in the file" + assert False, _("No map conversion was found in the file") - element = IfcFile.by_guid(guid) + element = utils.assert_guid(IfcStore.file, guid) if not element.ObjectPlacement: - assert False, "The element does not have an object placement: {}".format(element) + assert False, _("The element does not have an object placement: {}").format(element) m = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) e, n, h = ifcopenshell.util.geolocation.xyz2enh( m[0][3], @@ -73,24 +72,24 @@ def step_impl(context, guid, easting, northing, elevation): element_x = round(e, get_decimal_points(easting)) element_y = round(n, get_decimal_points(northing)) element_z = round(h, get_decimal_points(elevation)) - expected_placement = (assert_number(easting), assert_number(northing), assert_number(elevation)) + expected_placement = (util.assert_number(easting), util.assert_number(northing), util.assert_number(elevation)) if (element_x, element_y, element_z) != expected_placement: - assert False, "The element {} is meant to have a location of {} but instead we found {}".format( + assert False, _("The element {} is meant to have a location of {} but instead we found {}").format( element, expected_placement, (element_x, element_y, element_z) ) -@step("The element {guid} has a local X, Y, and Z coordinate of {x}, {y}, and {z} respectively") +@step('The element "{guid}" has a local X, Y, and Z coordinate of "{x}", "{y}", and "{z}" respectively') def step_impl(context, guid, x, y, z): - element = IfcFile.by_guid(guid) + element = utils.assert_guid(IfcStore.file, guid) if not element.ObjectPlacement: - assert False, "The element does not have an object placement: {}".format(element) + assert False, _("The element does not have an object placement: {}").format(element) m = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement) element_x = round(m[0][3], get_decimal_points(x)) element_y = round(m[1][3], get_decimal_points(y)) element_z = round(m[2][3], get_decimal_points(z)) - expected_placement = (assert_number(x), assert_number(y), assert_number(z)) + expected_placement = (util.assert_number(x), util.assert_number(y), util.assert_number(z)) if (element_x, element_y, element_z) != expected_placement: - assert False, "The element {} is meant to have a location of {} but instead we found {}".format( + assert False, _("The element {} is meant to have a location of {} but instead we found {}").format( element, expected_placement, (element_x, element_y, element_z) ) diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup/de.py b/src/ifcbimtester/bimtester/features/steps/project_setup/de.py new file mode 100644 index 0000000000..8163292264 --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/project_setup/de.py @@ -0,0 +1,16 @@ +from behave import step + + +@step('Die IFC-Daten müssen das "{schema}" Schema benutzen') +def step_impl(context, schema): + context.execute_steps(f'* IFC data must use the "{schema}" schema') + + +@step('Die Globale Identifikationskennung (Globally Unique Identifier = GUID) des Projektes ist "{guid}"') +def step_impl(context, guid): + context.execute_steps(f'* The project must have an identifier of "{guid}"') + + +@step('Der Name, die Abkürzung oder die Kurzkennung des Projektes ist "{value}"') +def step_impl(context, value): + context.execute_steps(f'* The project name, code, or short identifier must be "{value}"') diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup.py b/src/ifcbimtester/bimtester/features/steps/project_setup/en.py similarity index 63% rename from src/ifcbimtester/bimtester/features/steps/project_setup.py rename to src/ifcbimtester/bimtester/features/steps/project_setup/en.py index d51a55ab40..eb5e9a0bb4 100644 --- a/src/ifcbimtester/bimtester/features/steps/project_setup.py +++ b/src/ifcbimtester/bimtester/features/steps/project_setup/en.py @@ -1,38 +1,53 @@ from behave import step - -from utils import assert_attribute -from utils import IfcFile +from bimtester import util from bimtester.ifc import IfcStore +from bimtester.lang import _ -@step("The project must have an identifier of {guid}") +@step('IFC data must use the "{schema}" schema') +def step_impl(context, schema): + real_schema = IfcStore.file.schema + assert real_schema == schema, _("We expected a schema of {} but instead got {}").format(schema, real_schema) + + +@step('The IFC file "{file}" is exempt from being provided') +def step_impl(context, file): + pass + + +@step('No further requirements are specified because "{reason}"') +def step_impl(context, reason): + pass + + +@step('The project must have an identifier of "{guid}"') def step_impl(context, guid): - assert_attribute(IfcStore.file.by_type("IfcProject")[0], "GlobalId", guid) + util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "GlobalId", guid) @step('The project name, code, or short identifier must be "{value}"') def step_impl(context, value): - assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Name", value) + util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Name", value) @step('The project must have a longer form name of "{value}"') def step_impl(context, value): - assert_attribute(IfcStore.file.by_type("IfcProject")[0], "LongName", value) + util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "LongName", value) @step('The project must be described as "{value}"') def step_impl(context, value): - assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Description", value) + util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Description", value) @step('The project must be categorised under "{value}"') def step_impl(context, value): - assert_attribute(IfcStore.file.by_type("IfcProject")[0], "ObjectType", value) + util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "ObjectType", value) @step('The project must contain information about the "{value}" phase') def step_impl(context, value): - assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Phase", value) + util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Phase", value) @step("The project must contain 3D geometry representing the shape of objects") @@ -56,7 +71,7 @@ def step_impl(context): def get_subcontext(identifier, type, target_view): - project = IfcFile.get().by_type("IfcProject")[0] + project = IfcStore.file.by_type("IfcProject")[0] for rep_context in project.RepresentationContexts: for subcontext in rep_context.HasSubContexts: if ( @@ -72,5 +87,5 @@ def get_subcontext(identifier, type, target_view): @step('the project has a {attribute_name} attribute with a value of "{attribute_value}"') def step_impl(context, attribute_name, attribute_value): - project = IfcFile.get().by_type("IfcProject")[0] + project = IfcStore.file.by_type("IfcProject")[0] assert getattr(project, attribute_name) == attribute_value diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup/fr.py b/src/ifcbimtester/bimtester/features/steps/project_setup/fr.py new file mode 100644 index 0000000000..0a49367ccf --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/project_setup/fr.py @@ -0,0 +1,6 @@ +from behave import step + + +@step('Les données IFC doivent utiliser le schéma "{schema}"') +def step_impl(context, schema): + context.execute_steps(f'* IFC data must use the "{schema}" schema') diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup/it.py b/src/ifcbimtester/bimtester/features/steps/project_setup/it.py new file mode 100644 index 0000000000..a9b103e7ca --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/project_setup/it.py @@ -0,0 +1,11 @@ +from behave import step + + +@step('I dati IFC devono seguire lo schema "{schema}"') +def step_impl(context, schema): + context.execute_steps(f'* IFC data must use the "{schema}" schema') + + +@step('Il nome del progetto, codice o identificatore breve deve essere "{value}"') +def step_impl(context, value): + context.execute_steps(f'* "The project name, code, or short identifier must be "{value}"') diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup/nl.py b/src/ifcbimtester/bimtester/features/steps/project_setup/nl.py new file mode 100644 index 0000000000..84618014ef --- /dev/null +++ b/src/ifcbimtester/bimtester/features/steps/project_setup/nl.py @@ -0,0 +1,11 @@ +from behave import step + + +@step('IFC-gegevens moeten het "{schema}" -schema gebruiken') +def step_impl(context, schema): + context.execute_steps(f'* IFC data must use the "{schema}" schema') + + +@step('De projectnaam, code of korte ID moet "{value}"') +def step_impl(context, value): + context.execute_steps(f'* "The project name, code, or short identifier must be "{value}"') diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup_de.py b/src/ifcbimtester/bimtester/features/steps/project_setup_de.py deleted file mode 100644 index 56ff7d3359..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/project_setup_de.py +++ /dev/null @@ -1,20 +0,0 @@ -from behave import step - -from utils import assert_attribute -from utils import IfcFile -from utils import switch_locale - - -the_lang = "de" - - -@step("Die Globale Identifikationskennung (Globally Unique Identifier = GUID) des Projektes ist {guid}") -def step_impl(context, guid): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid) - - -@step('Der Name, die Abkürzung oder die Kurzkennung des Projektes ist "{value}"') -def step_impl(context, value): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value) diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup_fr.py b/src/ifcbimtester/bimtester/features/steps/project_setup_fr.py deleted file mode 100644 index c8f6621e6f..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/project_setup_fr.py +++ /dev/null @@ -1,23 +0,0 @@ -from behave import step - -from utils import assert_attribute -from utils import IfcFile -from utils import switch_locale - - -the_lang = "fr" - - -""" -# TODO the next line needs translation -@step("The project must have an identifier of {guid}") -def step_impl(context, guid): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid) - - -@step('The project name, code, or short identifier must be "{value}"') -def step_impl(context, value): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value) -""" diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup_it.py b/src/ifcbimtester/bimtester/features/steps/project_setup_it.py deleted file mode 100644 index 9a2e78c304..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/project_setup_it.py +++ /dev/null @@ -1,23 +0,0 @@ -from behave import step - -from utils import assert_attribute -from utils import IfcFile -from utils import switch_locale - - -the_lang = "it" - - -""" -# TODO the next line needs translation -@step("The project must have an identifier of {guid}") -def step_impl(context, guid): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid) -""" - - -@step('Il nome del progetto, codice o identificatore breve deve essere "{value}"') -def step_impl(context, value): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value) diff --git a/src/ifcbimtester/bimtester/features/steps/project_setup_nl.py b/src/ifcbimtester/bimtester/features/steps/project_setup_nl.py deleted file mode 100644 index 8a29cf9e1f..0000000000 --- a/src/ifcbimtester/bimtester/features/steps/project_setup_nl.py +++ /dev/null @@ -1,23 +0,0 @@ -from behave import step - -from utils import assert_attribute -from utils import IfcFile -from utils import switch_locale - - -the_lang = "nl" - - -""" -# TODO the next line needs translation -@step("The project must have an identifier of {guid}") -def step_impl(context, guid): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "GlobalId", guid) -""" - - -@step('De projectnaam, code of korte ID moet "{value}"') -def step_impl(context, value): - switch_locale(context.localedir, the_lang) - assert_attribute(IfcFile.get().by_type("IfcProject")[0], "Name", value) diff --git a/src/ifcbimtester/bimtester/ifc.py b/src/ifcbimtester/bimtester/ifc.py index 922d975888..5f4321d923 100644 --- a/src/ifcbimtester/bimtester/ifc.py +++ b/src/ifcbimtester/bimtester/ifc.py @@ -1,3 +1,4 @@ class IfcStore: path = "" file = None + bookmarks = {} diff --git a/src/ifcbimtester/bimtester/features/steps/utils.py b/src/ifcbimtester/bimtester/util.py similarity index 55% rename from src/ifcbimtester/bimtester/features/steps/utils.py rename to src/ifcbimtester/bimtester/util.py index 3e4032d463..bb5be50da3 100644 --- a/src/ifcbimtester/bimtester/features/steps/utils.py +++ b/src/ifcbimtester/bimtester/util.py @@ -1,80 +1,46 @@ -import gettext import ifcopenshell -import ifcopenshell.express -import ifcopenshell.util import ifcopenshell.util.element +from bimtester.lang import _ -class IfcFile(object): - file = None - bookmarks = {} - @classmethod - def load(cls, path=None): - cls.file = ifcopenshell.open(path) - if not cls.file: - assert False - - @classmethod - def load_schema(cls, path=None): - schema = ifcopenshell.express.parse(path) - ifcopenshell.register_schema(schema) - - @classmethod - def get(cls): - if not cls.file: - assert False, "No file was loaded, so this requirement cannot be checked" - return cls.file - - @classmethod - def by_guid(cls, guid): - try: - return cls.get().by_guid(guid) - except: - assert False, "An element with the ID {} could not be found.".format(guid) - - @classmethod - def by_type(cls, ifc_type): - return cls.get().by_type(ifc_type.strip()) - - @classmethod - def by_types(cls, ifc_types): - elements = [] - for ifc_type in ifc_types.split(","): - elements += cls.by_type(ifc_type.strip()) - return elements +def assert_guid(ifc, guid): + try: + return ifc.by_guid(guid) + except: + assert False, _("An element with the ID {} could not be found.").format(guid) def assert_number(number): try: return float(number) except ValueError: - assert False, "A number should be specified, not {}".format(number) + assert False, _("A number should be specified, not {}").format(number) def assert_type(element, ifc_class, is_exact=False): if is_exact: - assert element.is_a() == ifc_class, "The element {} is an {} instead of {}.".format( + assert element.is_a() == ifc_class, _("The element {} is an {} instead of {}.").format( element, element.is_a(), ifc_class ) else: - assert element.is_a(ifc_class), "The element {} is an {} instead of {}.".format( + assert element.is_a(ifc_class), _("The element {} is an {} instead of {}.").format( element, element.is_a(), ifc_class ) def assert_attribute(element, name, value=None): if not hasattr(element, name): - assert False, "The element {} does not have the attribute {}".format(element, name) + assert False, _("The element {} does not have the attribute {}").format(element, name) if not value: if getattr(element, name) is None: - assert False, "The element {} does not have a value for the attribute {}".format(element, name) + assert False, _("The element {} does not have a value for the attribute {}").format(element, name) return getattr(element, name) if value == "NULL": value = None actual_value = getattr(element, name) if isinstance(value, list) and actual_value: actual_value = list(actual_value) - assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format( + assert actual_value == value, _('We expected a value of "{}" but instead got "{}" for the element {}').format( value, actual_value, element ) @@ -84,21 +50,22 @@ def assert_pset(element, pset_name, prop_name=None, value=None): value = None psets = ifcopenshell.util.element.get_psets(site) if pset_name not in psets: - assert False, "The element {} does not have a property set named {}".format(element, pset_name) + assert False, _("The element {} does not have a property set named {}").format(element, pset_name) if prop_name is None: return psets[pset_name] if prop_name not in psets[pset_name]: - assert False, 'The element {} does not have a property named "{}" in the pset "{}"'.format( + assert False, _('The element {} does not have a property named "{}" in the pset "{}"').format( element, prop_name, pset_name ) if value is None: return psets[pset_name][prop_name] actual_value = psets[pset_name][prop_name] - assert actual_value == value, 'We expected a value of "{}" but instead got "{}" for the element {}'.format( + assert actual_value == value, _('We expected a value of "{}" but instead got "{}" for the element {}').format( value, actual_value, element ) +# TODO: what is this? def assert_elements( ifc_class, elemcount, @@ -136,7 +103,3 @@ def assert_elements( ) else: assert False, _("Error in falsecount, something went wrong.") - - -def switch_locale(locale_dir, locale_id="en"): - pass diff --git a/src/ifcbimtester/bimtester/features/steps/aggregation.py b/src/ifcbimtester/examples/steps/aggregation.py similarity index 65% rename from src/ifcbimtester/bimtester/features/steps/aggregation.py rename to src/ifcbimtester/examples/steps/aggregation.py index edd3d06050..cfe4fe0f23 100644 --- a/src/ifcbimtester/bimtester/features/steps/aggregation.py +++ b/src/ifcbimtester/examples/steps/aggregation.py @@ -1,15 +1,16 @@ from behave import step, given, when, then, use_step_matcher -from utils import IfcFile - use_step_matcher("parse") -@step(u"There must be exactly {number} {ifc_class} element") -@step(u"There must be exactly {number} {ifc_class} elements") + + +@step("There must be exactly {number} {ifc_class} element") +@step("There must be exactly {number} {ifc_class} elements") def step_impl(context, number, ifc_class): - num = len(IfcFile.get().by_type(ifc_class)) + num = len(IfcStore.file.by_type(ifc_class)) assert num == int(number), "Could not find {} elements of {}. Found {} element(s).".format(number, ifc_class, num) -@given(u'a set of specific related elements') + +@given("a set of specific related elements") def step_impl(context): model = getattr(context, "model", None) if not model: @@ -17,52 +18,67 @@ def step_impl(context): for row in context.table: context.model.add_row(row["RelatedObjects"], row["RelatingGroup"]) -@given(u'a set of specific related elements taken from the file "{path_file}"') + +@given('a set of specific related elements taken from the file "{path_file}"') def step_impl(context, path_file): import csv import os + model = getattr(context, "model", None) if not model: context.model = TableModel() - if context.config.userdata.get('path'): - path_file = os.path.join(context.config.userdata.get('path'), path_file) + if context.config.userdata.get("path"): + path_file = os.path.join(context.config.userdata.get("path"), path_file) if not os.path.exists(path_file): assert False, "File {} not found".format(path_file) - with open(path_file, 'r', encoding="utf-8-sig") as csvfile: + with open(path_file, "r", encoding="utf-8-sig") as csvfile: reader = csv.DictReader(csvfile) for row in reader: context.model.add_row(row["RelatedObjects"], row["RelatingGroup"]) -@then(u'there must be exactly a number of {ifc_class} equals to the number of distinct row value') + +@then("there must be exactly a number of {ifc_class} equals to the number of distinct row value") def step_impl(context, ifc_class): try: - context.execute_steps(u""" + context.execute_steps( + """ then There must be exactly {number} {ifc_class} elements - """.format(ifc_class=ifc_class, number=context.model.get_count_distinct_values())) + """.format( + ifc_class=ifc_class, number=context.model.get_count_distinct_values() + ) + ) except AssertionError as error: str_error = str(error) - assert False, str_error[:str_error.find("Traceback")] + assert False, str_error[: str_error.find("Traceback")] assert True -@then(u'there is a relationship {ifc_class} with {left_attribute} and {right_attribute} between the two elements of each row') + +@then( + "there is a relationship {ifc_class} with {left_attribute} and {right_attribute} between the two elements of each row" +) def step_impl(context, ifc_class, left_attribute, right_attribute): rows = context.model.rows - elements = IfcFile.by_type(ifc_class) + elements = IfcStore.file.by_type(ifc_class) errors = [] for key, value in rows.items(): found = False for element in elements: - if any(x.Name == key for x in getattr(element, left_attribute))\ - and getattr(element, right_attribute).Name == value: + if ( + any(x.Name == key for x in getattr(element, left_attribute)) + and getattr(element, right_attribute).Name == value + ): found = True if not found: - errors.append(f'The row ({key}, {value}) does not have the relationship.') + errors.append(f"The row ({key}, {value}) does not have the relationship.") assert not errors, "Errors occured:\n{}".format("\n".join(errors)) + use_step_matcher("re") + + @step("all IfcGroup must be linked to a type in the list (?P.*)") def step_impl(context, linked_ifc_classes): - groups = IfcFile.by_type("IfcGroup") + groups = IfcStore.file.by_type("IfcGroup") errors = [] for group in groups: if not hasattr(group, "IsGroupedBy"): @@ -75,26 +91,33 @@ def step_impl(context, linked_ifc_classes): for related_object in getattr(grouped_by, "RelatedObjects"): found = False for linked_ifc_class in linked_ifc_classes.split(","): - if(related_object.is_a(linked_ifc_class)): + if related_object.is_a(linked_ifc_class): found = True if not found: - errors.append(f'The element "{related_object.Name}" does not have the right associated type.') + errors.append( + f'The element "{related_object.Name}" does not have the right associated type.' + ) assert not errors, "Errors occured:\n{}".format("\n".join(errors)) -@then(u'there is an element of type (?P.*) with a (?P.*) attribute for each row key') + +@then("there is an element of type (?P.*) with a (?P.*) attribute for each row key") def step_impl(context, ifc_types, attribute_name): check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_name, context.model.rows.keys()) -@then(u'there is an element of type (?P.*) with a (?P.*) attribute for each row value') + +@then("there is an element of type (?P.*) with a (?P.*) attribute for each row value") def step_impl(context, ifc_types, attribute_name): values = set(context.model.rows.values()) check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_name, values) + def check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_name, attribute_values): errors = [] - # retrieve all elements of that type - elements = IfcFile.by_types(ifc_types) - # loop + + elements = [] + for ifc_type in ifc_types.split(","): + elements += ifc.by_type(ifc_type.strip()) + for attribute_value in attribute_values: found = False for element in elements: @@ -104,16 +127,18 @@ def check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_na errors.append(f'An element with {attribute_name} attribute "{attribute_value}" was not found.') assert not errors, "Errors occured:\n{}".format("\n".join(errors)) + class TableModel(object): """This class represents a table of data.""" + def __init__(self): self.rows = dict() def add_row(self, related, relating): self.rows[related] = relating - + def get_count(self): return len(self.rows) - + def get_count_distinct_values(self): - return len(set(self.rows.values())) \ No newline at end of file + return len(set(self.rows.values())) diff --git a/src/ifcbimtester/bimtester/features/steps/ifcdata.py b/src/ifcbimtester/examples/steps/application.py similarity index 65% rename from src/ifcbimtester/bimtester/features/steps/ifcdata.py rename to src/ifcbimtester/examples/steps/application.py index 9693719928..84f1243e49 100644 --- a/src/ifcbimtester/bimtester/features/steps/ifcdata.py +++ b/src/ifcbimtester/examples/steps/application.py @@ -6,39 +6,6 @@ from utils import IfcFile from bimtester.ifc import IfcStore from bimtester.lang import _ -@step('The IFC schema "{schema}" must be provided') -def step_impl(context, schema): - try: - if context.config.userdata.get("path"): - schema = os.path.join(context.config.userdata.get("path"), schema) - IfcFile.load_schema(schema) - except: - assert False, f"The schema {schema} could not be loaded" - - -@step('The IFC file "{file}" must be provided') -def step_impl(context, file): - try: - IfcFile.load(file) - except: - assert False, f"The file {file} could not be loaded" - - -@step("IFC data must use the {schema} schema") -def step_impl(context, schema): - real_schema = IfcStore.file.schema - assert real_schema == schema, _("We expected a schema of {} but instead got {}").format(schema, real_schema) - - -@step('The IFC file "{file}" is exempt from being provided') -def step_impl(context, file): - pass - - -@step("No further requirements are specified because {reason}") -def step_impl(context, reason): - pass - @step("The IFC file must be exported by application full name {fullname}") def step_impl(context, fullname): diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses.py b/src/ifcbimtester/examples/steps/attributes_eleclasses.py similarity index 76% rename from src/ifcbimtester/bimtester/features/steps/attributes_eleclasses.py rename to src/ifcbimtester/examples/steps/attributes_eleclasses.py index 3921418fc7..d587773166 100644 --- a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses.py +++ b/src/ifcbimtester/examples/steps/attributes_eleclasses.py @@ -3,55 +3,31 @@ from behave import step import attributes_eleclasses_methods as aem from utils import assert_elements from utils import IfcFile -from utils import switch_locale - - -the_lang = "en" @step("There are no {ifc_class} elements") def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.no_eleclass( - context, - ifc_class - ) + aem.no_eleclass(context, ifc_class) @step("There are no {ifc_class} elements because {reason}") def step_impl(context, ifc_class, reason): - switch_locale(context.localedir, the_lang) - aem.no_eleclass( - context, - ifc_class - ) + aem.no_eleclass(context, ifc_class) @step("All {ifc_class} elements class attributes have a value") def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_have_class_attributes_with_a_value( - context, - ifc_class - ) + aem.eleclass_have_class_attributes_with_a_value(context, ifc_class) @step("All {ifc_class} elements have a name given") def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_has_name_with_a_value( - context, - ifc_class - ) + aem.eleclass_has_name_with_a_value(context, ifc_class) @step("All {ifc_class} elements have a description given") def step_impl(context, ifc_class): - switch_locale(context.localedir, the_lang) - aem.eleclass_has_description_with_a_value( - context, - ifc_class - ) + aem.eleclass_has_description_with_a_value(context, ifc_class) @step('all {ifc_class} elements have a name matching the pattern "{pattern}"') @@ -73,9 +49,6 @@ def step_impl(context, ifc_class, attribute_name, attribute_value): assert False -# ------------------------------------------------------------------------ -# STEPS with Regular Expression Matcher ("re") -# ------------------------------------------------------------------------ use_step_matcher("re") diff --git a/src/ifcbimtester/examples/steps/attributes_eleclasses_de.py b/src/ifcbimtester/examples/steps/attributes_eleclasses_de.py new file mode 100644 index 0000000000..9cf25d075a --- /dev/null +++ b/src/ifcbimtester/examples/steps/attributes_eleclasses_de.py @@ -0,0 +1,26 @@ +from behave import step + + +@step("Es sind keine {ifc_class} Bauteile vorhanden") +def step_impl(context, ifc_class): + context.execute_steps(f"* There are no {ifc_class} elements") + + +@step("Aus folgendem Grund gibt es keine {ifc_class} Bauteile: {reason}") +def step_impl(context, ifc_class, reason): + context.execute_steps(f"* There are no {ifc_class} elements because {reason}") + + +@step("Alle {ifc_class} Bauteilklassenattribute haben einen Wert") +def step_impl(context, ifc_class): + context.execute_steps(f"* All {ifc_class} elements class attributes have a value") + + +@step("Bei allen {ifc_class} Bauteile ist der Name angegeben") +def step_impl(context, ifc_class): + context.execute_steps(f"* All {ifc_class} elements have a name given") + + +@step("Bei allen {ifc_class} Bauteile ist die Beschreibung angegeben") +def step_impl(context, ifc_class): + context.execute_steps(f"* All {ifc_class} elements have a description given") diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_methods.py b/src/ifcbimtester/examples/steps/attributes_eleclasses_methods.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/attributes_eleclasses_methods.py rename to src/ifcbimtester/examples/steps/attributes_eleclasses_methods.py diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_psets.py b/src/ifcbimtester/examples/steps/attributes_psets.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/attributes_psets.py rename to src/ifcbimtester/examples/steps/attributes_psets.py diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_psets_de.py b/src/ifcbimtester/examples/steps/attributes_psets_de.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/attributes_psets_de.py rename to src/ifcbimtester/examples/steps/attributes_psets_de.py diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_psets_fr.py b/src/ifcbimtester/examples/steps/attributes_psets_fr.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/attributes_psets_fr.py rename to src/ifcbimtester/examples/steps/attributes_psets_fr.py diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_psets_methods.py b/src/ifcbimtester/examples/steps/attributes_psets_methods.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/attributes_psets_methods.py rename to src/ifcbimtester/examples/steps/attributes_psets_methods.py diff --git a/src/ifcbimtester/bimtester/features/steps/attributes_qsets.py b/src/ifcbimtester/examples/steps/attributes_qsets.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/attributes_qsets.py rename to src/ifcbimtester/examples/steps/attributes_qsets.py diff --git a/src/ifcbimtester/bimtester/features/steps/geometric_detail.py b/src/ifcbimtester/examples/steps/geometric_detail.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/geometric_detail.py rename to src/ifcbimtester/examples/steps/geometric_detail.py diff --git a/src/ifcbimtester/bimtester/features/steps/geometric_detail_de.py b/src/ifcbimtester/examples/steps/geometric_detail_de.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/geometric_detail_de.py rename to src/ifcbimtester/examples/steps/geometric_detail_de.py diff --git a/src/ifcbimtester/bimtester/features/steps/geometric_detail_methods.py b/src/ifcbimtester/examples/steps/geometric_detail_methods.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/geometric_detail_methods.py rename to src/ifcbimtester/examples/steps/geometric_detail_methods.py diff --git a/src/ifcbimtester/bimtester/features/steps/spatial_structure.py b/src/ifcbimtester/examples/steps/spatial_structure.py similarity index 100% rename from src/ifcbimtester/bimtester/features/steps/spatial_structure.py rename to src/ifcbimtester/examples/steps/spatial_structure.py