diff --git a/.github/workflows/build-and-deploy-docs.yml b/.github/workflows/build-and-deploy-docs.yml new file mode 100644 index 00000000..172fbd66 --- /dev/null +++ b/.github/workflows/build-and-deploy-docs.yml @@ -0,0 +1,31 @@ +name: "Build docs with Sphinx and deploy to Github Pages" + +on: + push: + branches: docs/gh-pages + +jobs: + build_and_deploy: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + pages: write + env: + INPUT_DOCS-FOLDER: docs + steps: + - uses: actions/checkout@v4 + - name: Build HTML + uses: civilx64/sphinx-action@master + with: + docs-folder: docs/ + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload html docs + path: './docs/_build/html' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 00000000..f1f25772 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,28 @@ +name: "Build docs with Sphinx" + +on: + pull_request: + branches: docs/gh-pages + +jobs: + build_and_deploy: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + pages: write + env: + INPUT_DOCS-FOLDER: docs + steps: + - uses: actions/checkout@v4 + - name: Build HTML + uses: civilx64/sphinx-action@master + with: + docs-folder: docs/ + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload html docs + path: './docs/_build/html' diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 7a3fad07..a8ba9a79 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -79,7 +79,16 @@ jobs: source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt - pip install ifcopenshell # TEMP workaround + + - name: Install ifcopenshell package (temp) + run: | + cd backend + source venv/bin/activate + # use version of ifcopenshell with desired schema parsing + # TODO: revert to pyPI when schema parsing is published in the future + wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.8.1-92b63a0-linux64.zip" + mkdir -p venv/lib/python3.11/site-packages + unzip -d venv/lib/python3.11/site-packages /tmp/ifcopenshell_python.zip - name: Check Django config run: | diff --git a/Makefile b/Makefile index dc65941d..96403781 100644 --- a/Makefile +++ b/Makefile @@ -71,4 +71,6 @@ clean-all: fetch-modules: git submodule update --init --recursive - git submodule update --remote \ No newline at end of file + git submodule foreach git clean -f . + git submodule foreach git reset --hard + git submodule update --remote --recursive diff --git a/backend/Makefile b/backend/Makefile index 9d7202a1..baf435af 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -13,7 +13,7 @@ install: venv . $(VIRTUAL_ENV)/bin/activate && \ pip install --upgrade pip && \ pip install -r requirements.txt && \ - wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.9-c18e4ea-linux64.zip" && \ + wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.8.1-92b63a0-linux64.zip" && \ mkdir -p $(VIRTUAL_ENV)/lib/python3.11/site-packages && \ unzip -f -d $(VIRTUAL_ENV)/lib/python3.11/site-packages /tmp/ifcopenshell_python.zip && \ rm /tmp/ifcopenshell_python.zip @@ -22,7 +22,7 @@ install-macos: venv . $(VIRTUAL_ENV)/bin/activate && \ pip install --upgrade pip && \ pip install -r requirements.txt && \ - wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.9-c18e4ea-macos64.zip" && \ + wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.8.1-92b63a0-macos64.zip" && \ mkdir -p $(VIRTUAL_ENV)/lib/python3.11/site-packages && \ unzip /tmp/ifcopenshell_python.zip -d .dev/venv/lib/python3.11/site-packages && \ rm /tmp/ifcopenshell_python.zip diff --git a/backend/apps/ifc_validation/checks/ifc_gherkin_rules b/backend/apps/ifc_validation/checks/ifc_gherkin_rules index e9f4441c..24294494 160000 --- a/backend/apps/ifc_validation/checks/ifc_gherkin_rules +++ b/backend/apps/ifc_validation/checks/ifc_gherkin_rules @@ -1 +1 @@ -Subproject commit e9f4441c9a20b8d446918cae711892905ccebabe +Subproject commit 242944946b45e7aacd6a7ba0bb15c704d97caee0 diff --git a/backend/apps/ifc_validation/tasks.py b/backend/apps/ifc_validation/tasks.py index 81f64e00..d5904ffd 100644 --- a/backend/apps/ifc_validation/tasks.py +++ b/backend/apps/ifc_validation/tasks.py @@ -330,6 +330,17 @@ def parse_info_subtask(self, prev_result, id, file_name, *args, **kwargs): task.mark_as_initiated() + # try to open IFC file (catch segfaults) + try: + code = "import ifcopenshell; ifcopenshell.open('" + file_path + "')" + check_program = [sys.executable, '-c', code, file_path] + logger.debug(f'Command for {self.__qualname__}: {" ".join(check_program)}') + subprocess.run(check_program, check=True) + + except subprocess.CalledProcessError as err: + task.mark_as_failed(err) + raise + # retrieve IFC info try: task.set_process_details(None, f"(module) ifcopenshell.open() for file '{file_path}')") diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index f8433795..08dbbaf4 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -37,7 +37,7 @@ RUN --mount=type=cache,target=/root/.cache \ pip install --no-cache-dir -r /app/backend/requirements.txt && \ # use version of ifcopenshell with desired schema parsing # TODO: revert to pyPI when schema parsing is published in the future - wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.7.9-c18e4ea-linux64.zip" && \ + wget -O /tmp/ifcopenshell_python.zip "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-311-v0.8.1-92b63a0-linux64.zip" && \ mkdir -p /opt/venv/lib/python3.11/site-packages && \ unzip -d /opt/venv/lib/python3.11/site-packages /tmp/ifcopenshell_python.zip && \ # some cleanup diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..c6a151b3 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +_build/ \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/dev_system_architecture_sketch.png b/docs/_static/dev_system_architecture_sketch.png new file mode 100644 index 00000000..2719d6f4 Binary files /dev/null and b/docs/_static/dev_system_architecture_sketch.png differ diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 00000000..635011ff Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/_static/user_auth.png b/docs/_static/user_auth.png new file mode 100644 index 00000000..a07b5b81 Binary files /dev/null and b/docs/_static/user_auth.png differ diff --git a/docs/_static/user_include_passed_na_etc.png b/docs/_static/user_include_passed_na_etc.png new file mode 100644 index 00000000..e70418aa Binary files /dev/null and b/docs/_static/user_include_passed_na_etc.png differ diff --git a/docs/_static/user_results_icons.png b/docs/_static/user_results_icons.png new file mode 100644 index 00000000..54f569ae Binary files /dev/null and b/docs/_static/user_results_icons.png differ diff --git a/docs/_static/user_results_icons_no_bSDD.png b/docs/_static/user_results_icons_no_bSDD.png new file mode 100644 index 00000000..c77b413f Binary files /dev/null and b/docs/_static/user_results_icons_no_bSDD.png differ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..90cc0f7b --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,33 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'buildingSMART Validation Service' +copyright = '2024, buildingSMART International' +author = 'buildingSMART International' +release = '0.6' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx_rtd_theme', + 'myst_parser', +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +html_favicon = '_static/favicon.ico' + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] + +myst_links_external_new_tab = True diff --git a/docs/dev/functional_parts.md b/docs/dev/functional_parts.md new file mode 100644 index 00000000..9ce3a134 --- /dev/null +++ b/docs/dev/functional_parts.md @@ -0,0 +1,76 @@ +# Functional parts + +Functional parts refer to logical grouping of rules that involve similar types of validation. + +These functional part prefixes are used in the naming of the normative rules +and are reported in the results listed by the validation service. + +## Catalog + +Below is the list of the IFC functional parts. + +| TAG | FUNCTIONAL PART | DESCRIPTION | +|-----|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| PJS | Project definition | The ability to define the overall context and directory of objects within the model. Among others, the context definition includes default units and geometric representation context for shape representations. | +| GRF | Georeferencing | The ability to accurately define the geographic location and orientation of the model relative to a reference coordinate system, such as a national or global coordinate system. | +| BLT | Built elements (kickable) | The ability to model various building and infrastructure elements, including walls, floors, roofs, stairs, doors, windows, columns, road pavements, bridge decks, railway track elements, etc. | +| ASM | Assemblies (kickable) | The ability to model elements composed of / constructed by other elements. For example, a roof might be assembled from a series of prefabricated truss components. | +| SPA | Spaces (non-kickable) | The ability to model spaces, such as rooms, hallways, clearance zones, and circulation areas. | +| VRT | Virtual elements (non-kickable) | The ability to model spatial element used to provide imaginary, placeholder, or provisional areas (e.g. clearance), volumes, and boundaries. Virtual elements are not displayed, do not have quantities, materials, or other measures. | +| GEM | *Geometry representation | The ability to represent built elements and spaces using various geometry types, including parametric, mesh, and voxel-based representations. | +| OJP | *Object placement | The ability to define the location, orientation, and scale of built elements and systems within the building model. | +| POS | *Positioning elements | The ability to define (virtual) objects that are used to position other elements relatively. Includes grids, alignments, and referents. | +| OJT | Objects typing | The ability to define elements (and unfortunately not systems) based on their type or function within the model, allowing reusability of information by the occurrence of such types. | +| GRP | Groups (non-kickable) | The ability to group objects related by functional or logical criteria. Used, for example to model building systems, such as HVAC (heating, ventilation, and air conditioning) systems; to group assets that have the same maintenance schedule; to group all terminals of a fire-protection system. | +| SPS | Spatial breakdown | The ability to define spatial organisation within a building or infrastructure project. This includes the hierarchical relationships between spatial containers, such as buildings within a site, storeys within a building and rooms within a storey. | +| MAT | Materials | The ability to define materials assigned to elements. | +| PSE | Properties for object | The ability to define properties of elements and systems, such as their performance characteristics. | +| QTY | Quantities for objects | The ability to define quantities of elements, such as their dimensions, volume, area, weight. | +| CLS | Classification reference | The ability to classify elements, materials, and systems according to various classification systems, such as the UNIFORMAT or Omniclass classification systems. | +| ANN | Annotations | The ability to add annotations to elements and spaces, such as labels, notes, and dimensions. | +| LAY | Presentation layer | The ability to assign layers (also known as, CAD layer) to collection of elements. This is used mainly for grouping and visibility control, and in general to organise geometry into groups that may be shown or hidden. | +| CTX | Presentation Colours and Textures | The ability to assign colour, texture and other presentation appearance information to objects. | +| POR | Port connectivity & nesting | The ability to define ports (as means for an element to connect to other elements) and the relationship that is made between two ports. | +| STR | Structural items and actions | The ability to define structural members and structural connections, as analysis idealizations of built elements. And the ability to define actions (such as forces, displacements, etc.) and reactions (support reactions, internal forces, deflections, etc.) associated to structural items and specified by using the basic load definitions. | +| CST | Costing | The ability to assign costing information to objects. | +| SDL | Scheduling of activities | The ability to assign scheduling information to objects. | +| LIB | Library reference | The ability to associate library entities, such as from a product library or external database to objects and object types. | +| DOC | Documentation reference | The ability to associate reference to documentation to objects. | +| CTR | Constraints | The ability to model constraints on building elements, such as minimum and maximum dimensions or clearances, and to enforce these constraints during design and construction. | +| VER | Versioning / revision control | The ability to track changes to building data over time and to maintain a history of changes. | + +*These 3 functional parts are further decomposed as indicated below. + +### Geometry representation sub-parts + +| TAG | FUNCTIONAL PART | DESCRIPTION | +|-----|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| AXG | Axis geometry | This involves representing the geometry using a set of axes, where each axis consists of a position in 3D space and a direction vector. This can be useful for representing geometry that follows a certain pattern or direction, such as walls, beams, columns, or other elements. In IFC, axis geometry can be represented using the IfcCartesianPoint and IfcDirection classes, which define the position and direction of the axes, respectively. | +| ALS | Alignment geometry | This involves representing the geometry using a collection of segments that define a linear path in 3D space. This can be useful for representing linear objects such as roads, railways, or pipelines. | +| PBG | Point-based geometry | This involves representing geometry using a collection of individual points in 3D space. Point-based geometry can be useful for representing large-scale environments or for creating simplified representations of more complex geometry. Point-based geometry can be represented in IFC using a range of different point types, such as 3D point clouds or scattered point data. | +| TAS | Tessellated (i.e. meshes) | This involves representing geometry using a collection of interconnected vertices, edges, and faces, which are used to approximate a smooth surface. Tessellated geometry can be useful for representing organic or irregular shapes, or for creating low-resolution representations of more complex geometry. In IFC, tessellated geometry can be represented using a range of different mesh types, such as triangular, quadrilateral, or polyhedral meshes. | +| BRP | Boundary Representation (BREP) | This involves representing geometry using a collection of connected surfaces and edges. BREP geometry can be used to represent complex shapes with curved surfaces or unusual topology. BREP geometry can be represented in IFC using IfcFacetedBrep for polygonal meshes or IfcManifoldSolidBrep for more complex solid geometry. These entities can be further described using a collection of geometric representations such as IfcCartesianPoint, IfcPolyLoop, and IfcSurface. | +| CSG | Constructive Solid Geometry (CSG) | This involves creating complex geometry by combining simpler shapes using Boolean operations such as union, intersection, and difference. CSG can be useful for creating complex shapes with predictable and repeatable geometry. CSG can be represented in IFC using the IfcBooleanResult entity, which defines the Boolean operation to be applied to a set of one or more shape representations. | +| SWE | Sweeps (i.e., extrusions, lofts, blends) | This involves taking a 2D shape (often called the "profile") and moving it along a 3D path, generating a 3D shape. The resulting shape is typically smooth and continuous, with its cross-section changing gradually along the length of the path. Sweep geometry can be useful for representing a variety of objects, such as pipes, cables, and architectural details like moldings. In IFC, sweep geometry can be represented using the IfcExtrudedAreaSolid, IfcRevolvedAreaSolid, and IfcSweptAreaSolid entities. | +| TFM | Transformations | Transformation geometry involves representing geometry using spatial transformations, such as rotations, translations, and scaling. It allows for the modification of existing geometry without the need to create new geometry from scratch. Transformation geometry can be used to represent various types of transformations, including cartesian transformations, placement transformations, and composite transformations, among others. These transformations can be applied to a range of geometric entities, including points, curves, surfaces, and solids, to create new or modified geometry. In IFC, transformation geometry is represented using the IfcCartesianTransformationOperator and IfcObjectPlacement entities. | +| BBX | Bounding box | This involves defining an orthogonal box, oriented parallel to the axes of the object coordinate system in which it is defined and containing a geometry object, which defines the spatial extent of the latter. | +| MPD | Mapped geometry | This involves identifying a representation and a representation item in that representation for the purpose of mapping. The representation item defines the origin of the mapping. The representation map is used as the source of a mapping by a mapped item. | +| RCO | Relational constructs | Such as connection geometry, space boundaries, interference geometries. This is not a geometry category per se, but it's a specific way geometry is used in IFC | +| CPD | Clipped representations | This involves visualizations of a 3D model where part of the model is "clipped" or cut away to allow for better examination of the internal structures or components. This is often done using clipping planes or sections. | + +### Object placement sub-parts + +| TAG | FUNCTIONAL PART | DESCRIPTION | +|-----|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| LOP | Local placement | The ability to define the placement of a product in relation to the placement of another product; or its absolute placement within the geometric representation context of the project. | +| LIP | Linear placement | The ability to define the placement of a product in relation to a curve. | +| GDP | Grid placement | The ability to define the placement of a product in relation to a design grid. | + +### Positioning elements sub-parts + +| TAG | FUNCTIONAL PART | DESCRIPTION | +|----------|-----------------|--------------------------------------------------------------------------------------------------------------------| +| GRD | Grid | The ability to define a design grid to be used as reference for object placement. | +| ALA, ALB | Alignment | The ability to define an alignment curve, and its components. These can be used as reference for object placement. | +| RFT | Referent | The ability to define a referent to be used as reference for object placement. | + diff --git a/docs/dev/gherkin_deep_dive.md b/docs/dev/gherkin_deep_dive.md new file mode 100644 index 00000000..67a40820 --- /dev/null +++ b/docs/dev/gherkin_deep_dive.md @@ -0,0 +1,36 @@ +# A deep dive into gherkin rule implementations + +## Decorators + +### `@gherkin_ifc` + +This is used in place of `behave`'s default `@step_implementation` decorator +to provide additional capabilities related to context stacking and other concerns +related to tracking and evaluating instances in the IFC model. + +### `@register_enum_type` + +This is a small new decorator for registering enumeration types in a simpler way. + +## Step handling + +### `execute_step()` + +Checks whether the current step being processed is a `Given` or `Then`. + +### `handle_given()` + +Handles a `Given` step. + +### `handle_then()` + +Handles a `Then` step. + +## Context stacking + +As steps are processed, they are captured in a persistent object of type `behave.runner.Context`. +This context object includes a hidden attribute `_stack` that is used to 'stack' information +and results for each step that is processed. + +It can be helpful to monitor the content of the `instances` attribute of each item in the +`context._stack` list. diff --git a/docs/dev/ifc_gherkin_rules_readme.md b/docs/dev/ifc_gherkin_rules_readme.md new file mode 100644 index 00000000..88aa539f --- /dev/null +++ b/docs/dev/ifc_gherkin_rules_readme.md @@ -0,0 +1,62 @@ +# IFC Gherkin rules + +## Usage as part of buildingSMART validation service + +This repository is one of three submodules in the overall validation service. +See [application_structure](#application-structure) for more information. + +## Making changes + +The rules developed in this repository follow the general ideas of Gherkin and its python implementation behave. + +This means there are human-readable definitions of rules and Python implementations. + +A third component of this repository are minimal sample files with expected outcomes, which means that extensions and modifications can be suggested with confidence of not breaking existing functionality. + +## Command line usage + +Informal propositions and implementer agreements written in Gherkin for automatic validation of IFC building models using steps implemented in IfcOpenShell. + +```shell +$ python -m ifc-gherkin-rules ifc-gherkin-rules\test\files\gem001\fail-gem001-cube-advanced-brep.ifc +Feature: Shell geometry propositions/IfcClosedShell.v1 + URL: /blob/8dbd61e/features/geometry.shells.feature + + Step: Every oriented edge shall be referenced exactly 1 times by the loops of the face + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (-0.183012701892219, + 0.683012701892219, 1.0) -> (-0.683012701892219, -0.183012701892219, 1.0) was + referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (-0.5, -0.5, 0.0) -> + (-0.5, 0.5, 0.0) was referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (-0.5, 0.5, 0.0) -> + (0.5, 0.5, 0.0) was referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (-0.683012701892219, + -0.183012701892219, 1.0) -> (0.183012701892219, -0.683012701892219, 1.0) was + referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (0.183012701892219, + -0.683012701892219, 1.0) -> (0.683012701892219, 0.183012701892219, 1.0) was + referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (0.5, -0.5, 0.0) -> + (-0.5, -0.5, 0.0) was referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (0.5, 0.5, 0.0) -> + (0.5, -0.5, 0.0) was referenced 2 times + + * #29=IfcClosedShell((#104,#115,#131,#147,#163,#179)) + On instance #29=IfcClosedShell((#104,...,#179)) the edge (0.683012701892219, + 0.183012701892219, 1.0) -> (-0.183012701892219, 0.683012701892219, 1.0) was + referenced 2 times +``` diff --git a/docs/dev/index.md b/docs/dev/index.md new file mode 100644 index 00000000..3e3e42b5 --- /dev/null +++ b/docs/dev/index.md @@ -0,0 +1,21 @@ +# Developer Guide + +```{include} validate_readme.md +:heading-offset: 1 +``` + +```{include} ifc_gherkin_rules_readme.md +:heading-offset: 1 +``` + +```{include} rule_details.md +:heading-offset: 1 +``` + +```{include} functional_parts.md +:heading-offset: 1 +``` + +```{include} gherkin_deep_dive.md +:heading-offset: 1 +``` diff --git a/docs/dev/outcome_severities.md b/docs/dev/outcome_severities.md new file mode 100644 index 00000000..cf6ccc28 --- /dev/null +++ b/docs/dev/outcome_severities.md @@ -0,0 +1,151 @@ +# Outcome Severities + +Note: This documentation was developed in Nov 2024 based on the codebase at version 0.6.8. + +## Overview - Database model enumerations + +Severities are an enumeration of possible values for the severity of a +[Validation Outcome](https://github.com/buildingSMART/ifc-validation-data-model/blob/main/models.py#L978). + +| String Value | Integer Enumeration Value | +|----------------|---------------------------| +| EXECUTED | 1 | +| PASSED | 2 | +| WARNING | 3 | +| ERROR | 4 | +| NOT_APPLICABLE | 0 | + +## Severity Usage + +### Schema Check + +Schema checks are always applicable and therefore the only possible enumeration values are `PASSED` and `ERROR`. +[Passing outcomes for schema checks are stored to the database](https://github.com/buildingSMART/validate/blob/8f04bcc6d1f400240485a33b2c81e2f7d0edbeab/backend/apps/ifc_validation/tasks.py#L607). + +### Syntax Check + +Syntax checks are also always applicable and therefore the only possible enumeration values are `PASSED` and `ERROR`. +[Passing outcomes for syntax checks are stored to the database](https://github.com/buildingSMART/validate/blob/8f04bcc6d1f400240485a33b2c81e2f7d0edbeab/backend/apps/ifc_validation/tasks.py#L607). + +### Gherkin Rules (Normative Rules and Industry Best Practices) + +These rules are generally processed in similar fashion. +Unlike syntax and schema checks, a gherkin rule may or may not be applicable to a given model depending on the schema +and content of the model. +For example, alignment rules are only applicable to models with schema `IFC4X3_ADD2` as those entities were not part of +the +`IFC2X3` or `IFC4` schema versions. + +Therefore, both sets of validation checks can potentially also return outcomes with severity of `NOT_APPLICABLE`. + +#### Normative Rules + +Normative rules enforce requirements from implementer agreements and informal propositions. +Therefore, the potential outcomes are: + +- `NOT_APPLICABLE` +- `ERROR` +- `PASSED` + +#### Industry Best Practices + +Industry Best Practice checks enforce items that are not required, +but rather represent the preferred or most idiomatic way of implementing the IFC standard. + +Therefore, the potential outcomes are: + +- `NOT_APPLICABLE` +- `WARNING` +- `PASSED` + +#### Gherkin Rule processing + +Currently there are no individual instance outcomes from gherkin rules stored with severity=`PASSED`. +The initial idea was to pass them to the DB but it was quickly flooded with outcomes of this severity +and is currently [commented out](https://github.com/buildingSMART/ifc-gherkin-rules/blob/b363041433f252fc1b9e043ee3aac0bd6fcfad3d/features/steps/validation_handling.py#L254-L268). + +_(potentially remove the following paragraph to avoid confusion...)_ + +When processing gherkin rules (*.feature files) with `behave`, Severity=`PASSED` is used only for `Given` statements. +A `PASSED` outcome is added to the temporary outcomes when the conditions of a `Given` statement are met. + +The entire processing loop is as follows: + +1. Severity=`NOT_APPLICABLE` is applied to all entity instances +2. The `Given` statements are executed +3. If there is *at least one* entity instance that meets the requirement of *ALL* `Given` statements, + the rule is considered to be "activated" and severity=`EXECUTED` is applied to these instance(s). + This is an 'all or nothing' situation where all `Given` statements must be "activated" in order for the rule + to be considered "activated". +4. Those instance(s) are then tested against the `Then` statements. +5. Severity=`ERROR` is applied to each instance that fails a requirement of a `Then` statement. + +If there is at least one instance with an `ERROR` outcome then an aggregated status for the rule is returned as follows: + +- Severity=`ERROR` for normative rules (implementer agreements and informal propositions) +- Severity=`WARNING` for industry best practices. + +This status is used to colour the "block" for each rule in the validation report. + +## Outcome display and reporting + +### Individual rules (Normative and Industry Best Practices) + +Outcomes are always reported in the web UI based on aggregated status of all instances +activated by a given rule. + +| Aggregated Value of `Severity` for a rule | Display colour | Label reported in `Severity` column | +|-------------------------------------------|----------------|-------------------------------------| +| `NOT_APPLICABLE` | grey | 'N/A' | +| `EXECUTED` | green | 'Applicable' | +| `PASSED` | (not used) | | +| `WARNING` | yellow | 'Warning' | +| `ERROR` | red | 'Error' | + +Display colour is determined by the +[statusToColor](https://github.com/buildingSMART/validate/blob/development/frontend/src/mappings.js#L1) +mapping function. + +Display label is determined by the +[statusToLabel](https://github.com/buildingSMART/validate/blob/development/frontend/src/mappings.js#L10) +mapping function. + +### Overall status + +A single overall status for each type of check (syntax, schema, normative rules, best practices) +is displayed on the Validation Service dashboard for each model. + +The overall status of each +[ValidationTask](https://github.com/buildingSMART/ifc-validation-data-model/blob/main/models.py#L778) +is captured in a different data structure of +[Model.Status](https://github.com/buildingSMART/ifc-validation-data-model/blob/main/models.py#L324) +with the following possible enumeration values: + +| Value of `Model.Status` | Display colour | Symbol | +|-------------------------|----------------|------------------------------------------------------------------------------| +| `VALID` | green | CheckCircleIcon | +| `INVALID` | red | WarningIcon | +| `NOT_VALIDATED` | grey | HourglassBottomIcon | +| `WARNING` | yellow | ErrorIcon | +| `NOT_APPLICABLE` | grey | BrowserNotSupportedIcon (technically possible but doesn't occur in practice) | + +The options for overall status of each category of ValidationTask are as follows: + +- Syntax and Schema + - `VALID` + - `INVALID` + +- Normative Rules + - `VALID` + - `INVALID` + - `NOT_APPLICABLE` + +- Best Practices + - `VALID` + - `WARNING` + - `NOT_APPLICABLE` + +The overall status of a normative rule ValidationTask is determined by +[taking the highest severity](https://github.com/buildingSMART/ifc-validation-data-model/blob/f32164ab762fc695690d380e12e87c815b641912/models.py#L948) +of outcomes for all the rules contained in that task. + diff --git a/docs/dev/rule_details.md b/docs/dev/rule_details.md new file mode 100644 index 00000000..6dde7a1b --- /dev/null +++ b/docs/dev/rule_details.md @@ -0,0 +1,492 @@ +# Detailed Information for Normative Rules + +Follow these steps to add a new rule to the Validation Service + +| n. | Step | Responsible | +|----|------------------------------------------------------------------------------------------|-----------------------------| +| 1 | Create a new branch in the bSI ifc-gherkin-rules repository | bSI Validation Service team | +| 2 | In this branch, start developing the rule needed **following instructions below** | rule developer | +| 3 | Create a pull request to further test the rule(s) behavior using the sandbox environment | rule developer | +| 4 | Assign a reviewer to the pull request when you think the rule is ready to be merged | rule developer | +| 5 | Review the pull request | bSI Validation Service team | +| 6 | (optional) Fix the rule according to feedback from reviewer | rule developer | +| 7 | Approve and merge the pull request | bSI Validation Service team | + +## 1. Branch creation + +In the buildingSMART [GitHub repository containing all rules](https://github.com/buildingSMART/ifc-gherkin-rules), create the branch that will be used to develop the new rule. + +- Name the branch with the name of the new rule. Example: `GEM900` for a new rule in the geometry functional part +- Add 1 rule per branch, to facilitate review (1 rule = 1 `.feature` file) + +## 2. Rule development + +A rule is considered complete when it has: + +- a Gherkin [**feature file**](21-write-feature-files-gherkin-rules-for-ifc) +- corresponding python implementation (aka, [**python steps**](22-write-python-steps)) +- a set of [**unit test files**](23-write-unit-test-files) + +Below are instructions for all these 3 components. + +(21-write-feature-files-gherkin-rules-for-ifc)= +### 2.1) Write feature files (gherkin rules) for IFC + +A feature file is a file, written using Gherkin syntax, describing the rule behavior. +In the branch just created, add a Gherkin feature file following these instructions. + +**File format**: `.feature` + +**Location**: https://github.com/buildingSMART/ifc-gherkin-rules/tree/main/features + +#### Naming convention for feature files + +- The file name is rule code_rule title +- The rule code is made of 3 digits capital letters (taken from the list of [Functional parts](./functional_parts.md)) + 3 digits number +- The rule code, and rule title, must be unique +- The rule title shall have no space and shall use `-` as separator + +
wrong + +``` +SPS001 - Basic-spatial-structure-for-buildings.feature +SPS001_Basic spatial structure for buildings.feature +SPS001 - Basic spatial structure for buildings.feature +``` +
+
right + +``` +SPS001_Basic-spatial-structure-for-buildings.feature +``` +
+ +#### Mandatory content + +`.feature` files: +- must include one and only one of these tags to classify the validation category: + - `@critical` + - `@implementer-agreement` + - `@informal-proposition` + - `@industry-practice` (warning; not a pass / fail) +- must include a 3-character alpha tag to the functional part. See [Functional parts](./functional_parts.md) +- must include a single tag indicating the version of the feature file as a 1-based integer + - Example: `@version1` for initial version of a feature file + - Example: `@version3` for the third version of a feature file + - Minor changes such as fixing typos or re-wording the description do not increment the version + - Any change to a **"Given"** or **"Then"** statement, or to a step implementation, requires the version number to be incremented by 1. +- must include one or more tags indicating the [error code](error-codes) to be raised + - If all scenarios raise the same error, then this tag should be placed immediately above the **"Feature:"** line + +
example + + ``` + @implementer-agreement + @GRF + @version1 + @E00050 + Feature: GRF001 - Identical.... + ``` + +
+ + - If some scenarios raise different error codes, then this tag should be placed immediately above each **"Scenario" + ** line + +
example + + ``` + @implementer-agreement + @ALS + @version1 + Feature: ALS005 - Alignment shape representation + + Background: ... + + @E00020 + Scenario: Agreement on ... representation - Value + + @E00010 + Scenario: Agreement on ... representation - Type + ``` + +
+ +- must include exactly 1 Feature +- the naming convention for the Feature is the following: rule code - rule title (the same used for the file name). For the rule title blank spaces must be used instead of `-` + +
wrong + +``` +Feature: ALB001_Alignment Layout + +Given ... +Then ... +``` +``` +@ALB +Feature: ALB001_Alignment-Layout + +Given ... +Then ... +``` +``` +@ALB +Feature: ALB001 - Alignment-Layout + +Given ... +Then ... +``` + +
+
right + +``` +@ALB +Feature: ALB001 - Alignment Layout + +Given ... +Then ... +``` +
+ + - must include **a description of the rule** that start with "The rule verifies that..." + +
example + +``` +@implementer-agreement +@ALB +Feature: ALB003 - Allowed entities nested in Alignment +The rule verifies that an Alignment has a nesting relationship with its components (i.e., Horizontal, Vertical, Cant layouts) or with Referents (e.g., mileage markers). And not with any other entity. + + Scenario: Agreement on nested elements of IfcAlignment + Given ... + Then ... +``` +
+ +#### Mandatory Given(s) +If the rule in the feature file applies only to specific IFC version(s) and/or View Definition(s), then the feature file (or each of its Scenarios, if it has more than one) must start with Given steps specifying the applicability of the following steps + +
examples + +``` +Given A model with Schema "IFC2X3" +Given A file with Model View Definition "CoordinationView" +``` +``` +Given A model with Schema "IFC2X3" or "IFC4" +Given A file with Model View Definition "CoordinationView" or "ReferenceView" +``` +
+ +#### Optional content +`.feature` files: +- can include 1 or more Scenarios +- Scenario titles have no constraints +- can include the `@disabled` tag to temporarily remove them from processing + +#### No spaces between steps + +
wrong + +``` +Given A model with Schema "IFC4.3" + +Then Each IfcAlignmentHorizontal must be nested only by 1 IfcAlignment +Then Each IfcAlignmentVertical must be nested only by 1 IfcAlignment +Then Each IfcAlignmentCant must be nested only by 1 IfcAlignment +``` +
+
right + +``` +Given A model with Schema "IFC4.3" +Then Each IfcAlignmentHorizontal must be nested only by 1 IfcAlignment +Then Each IfcAlignmentVertical must be nested only by 1 IfcAlignment +Then Each IfcAlignmentCant must be nested only by 1 IfcAlignment +``` +
+ +#### Watch out for extra blank spaces + +
wrong + +``` +Given A model with Schema "IFC4.3" +Then Each IfcAlignmentHorizontal must be nested only by 1 IfcAlignment +Then Each IfcAlignmentVertical must be nested only by 1 IfcAlignment +Then Each IfcAlignmentCant must be nested only by 1 IfcAlignment +``` +
+
right + +``` +Given A model with Schema "IFC4.3" +Then Each IfcAlignmentHorizontal must be nested only by 1 IfcAlignment +Then Each IfcAlignmentVertical must be nested only by 1 IfcAlignment +Then Each IfcAlignmentCant must be nested only by 1 IfcAlignment +``` +
+ +#### Do not use punctuation at the end of the steps + +
wrong + +``` +Given A model with Schema "IFC4.3", +Then Each IfcAlignmentHorizontal must be nested only by 1 IfcAlignment; +Then Each IfcAlignmentVertical must be nested only by 1 IfcAlignment; +Then Each IfcAlignmentCant must be nested only by 1 IfcAlignment. +``` +
+
right + +``` +Given A model with Schema "IFC4.3" +Then Each IfcAlignmentHorizontal must be nested only by 1 IfcAlignment +Then Each IfcAlignmentVertical must be nested only by 1 IfcAlignment +Then Each IfcAlignmentCant must be nested only by 1 IfcAlignment +``` +
+ +#### Be careful when typing parameters. They are case-sensitive! + +
wrong + +``` +Given A model with schema "IFC4.3", +``` +
+
right + +``` +Given A model with Schema "IFC4.3" +``` +
+ +#### Must vs Shall +Use **must**, not **shall** to impose requirements. +[ALB001_Alignment-in-spatial-structure.feature](https://github.com/buildingSMART/ifc-gherkin-rules/blob/main/features/ALB002_Alignment-layout.feature) +"Shall" is ambiguous, also in the legal field the community is moving to a strong preference for “must” as the clearest way to express a requirement or obligation. + +
wrong + +``` +Given A model with Schema "IFC2X3" +Given A file with Model View Definition "CoordinationView" +Then There shall be exactly 1 IfcSite element(s) +``` +
+
right + +``` +Given A model with Schema "IFC2X3" +Given A file with Model View Definition "CoordinationView" +Then There must be exactly 1 IfcSite element(s) +``` +
+ +#### Verbs for IFC relationships + +When a rule requires a specific IFC relationship to exist, refer to the table below for the right verb to be used. + +| IFC relationship | Verb for rules | Examples | +|------------------------|-----------------------|--------------------------------------------------------------------| +| IfcRelAggregates | aggregate, aggregates | Then IfcSite must aggregate IfcBuilding | +| IfcRelNests | nest, nests | Then Each IfcAlignmentVertical nests a list of IfcAlignmentSegment | +| ... | | + + +#### Reference for schema versioning + +Rules that are applicable only to specific schema versions must specify +the schema version with the initial `Given` statement. + +For example, alignment entities were introduced in IFC4.3 and are not valid +in earlier schema versions. + +``` +Given A model with Schema "IFC4.3" +Given An IfcAlignment +Then ... +``` + +Multiple schema versions may be specified if applicable. + +``` +Given A model with Schema "IFC2X3" or "IFC4" +Given An IfcElement +Then ... +``` + +##### Valid (active, not withdrawn or retired) Schema Versions + +| Version | Formal Name | Schema id | Common Name | +|---------|---------------|-------------|-------------| +| 4.3.2.0 | IFC4.3 ADD2 | IFC4X3_ADD2 | IFC4.3 | +| 4.0.2.1 | IFC4 ADD2 TC1 | IFC4 | IFC4 | +| 2.3.0.1 | IFC2x3 TC1 | IFC2X3 | IFC2x3 | + +(22-write-python-steps)= +### 2.2) Write python steps + +The python steps are the implementation (using python language) of the Gherkin grammar used in the feature files. +In the same branch used for the Gherkin rules, change or add python steps following these instructions. + +**File format**: `.py` + +**Location**: https://github.com/buildingSMART/ifc-gherkin-rules/tree/main/features/steps + +#### Naming convention for python files + +For the moment, all python steps are contained in [steps.py](https://github.com/buildingSMART/ifc-gherkin-rules/blob/main/features/steps/steps.py). Therefore, **you should not create a new python file, just expand the existing one.** + +:construction: :construction: :construction: +*In the future, when this file grows, python steps may be splitted in more files - using a certain criteria (e.g., functional parts). When this will be the case, the instruction will be: locate the best .py file to host your steps and start adding your steps* + +#### Steps parametrisation + +When creating a new step, think about parametrisation and optimisation of the step for future uses. + +#### Step re-use + +Before creating a new step, check if something similar already exist. +Try to reuse existing steps. + +#### Do not use "when" or "And" keywords + +The "when" keyword must not be used. +The "And" keyword must not be used. +Instead, repeat the "Given" or "Then" as appropriate. + +Allowed keywords are: `Given`, and `Then`. + +#### Use of existing IfcOpenShell APIs + +Try not to use existing functionality included in the `ifcopenshell.api` namespace. + + + + + + + + +(23-write-unit-test-files)= +### 2.3) Write unit test files + +Unit test files are atomic IFC files, created to develop a rule and test its behavior. +In the same branch used for the Gherkin rules, and python steps, create unit test files following these instructions. **IMPORTANT**: every rule developed must have a set of unit test files. + +**File format**: `.ifc` + +**Location**:[ifc-gherkin-rules/tree/main/test/files](https://github.com/buildingSMART/ifc-gherkin-rules/tree/main/test/files) + +- in the test/files folder, create a subfolder using the rule code (E.g., ALB001) +- add the set of unit test files for that rule in this subfolder + +#### Naming convention for unit test files + +Unit test files must follow this naming convention: + +`Expected result`-`rule code`-`rule scenario`-`short_informative_description`.ifc + +Or in case where a rule has no scenarios: +`Expected result`-`rule code`-`short_informative_description`.ifc + +
Examples + +```shell +pass-alb001-short_informative_description.ifc +fail-alb001-scenario01-short_informative_description.ifc +fail-alb001-short_informative_description.ifc +``` + +
+ + +#### Content of the unit tests subfolder + +The unit test subfolder must contain: + +- all unit test files (.ifc) +- a README file (.md), listing the files and their expected behavior. Using the [template table](#table-template-for-unit-test-files) below +- where used, the script (.py) created to generate the unit test files + +#### Number of unit tests required + +- Each rule developed must have a set of unit test files +- There must be at least 1 fully compliant unit test file +- Fail files must cover all scenarios of the rule + +(table-template-for-unit-test-files)= +#### Table template for unit test files + +Example table describing unit test expected results + +| File name | Expected result | Error log | Description | +|-------------------------------------------------------|-----------------|----------------------------------------------------------------------------------|----------------------------------------------------------------------------------| +| pass-alb002-alignment-layout | success | n.a. | | +| fail-alb002-scenario01-nested_attributes_IfcAlignment | fail | The instance IfcAlignment is nesting two instances of IfcAlignmentHorizontal ... | Error is descriptive or exactly the error in pytest? If exactly, multiple row... | +| fail-alb002-scenario02-two_alignments | fail | The following 2 instances were encountered: IfcAlignment #23, IfcAlignment #906 | For IfcAlignmentHorizontal, IfcAlignmentVertical and IfcAlignmentCant | +| fail-alb002-scenario03-layout | fail | The instance #906=IfcAlignment is nesting #907=IfcWall | Includes errors for scenario 2 | +| fail-alb002-scenario04-alignment_segments | fail | The instance (s) #28=IfcAlignmentHorizontal is assigned to #906=IfcWall | @todo IfcAlignmentVertical, IfcAlignmentCant. As well as empty list/typo's? | + + + +## 4. Assign a reviewer to the pull request +... +## 5. Review the pull request +... +## 6. (optional) Fix the rule according to feedback from reviewer +... +## 7. Approve and merge the pull request +... + +## Appendix + +(error-codes)= +### Error Codes + +Error codes are used to classify and categorize outcomes from the validation service and are +implemented in [ifc-validation-data-model/main/models.py#L937](https://github.com/buildingSMART/ifc-validation-data-model/blob/main/models.py#L937). + +| Error Code | Description | +|------------|----------------------------------------| +| P00010 | Passed | +| N00010 | Not Applicable | +| | | +| E00001 | Syntax Error | +| E00002 | Schema Error | +| E00010 | Type Error | +| E00020 | Value Error | +| E00030 | Geometry Error | +| E00040 | Cardinality Error | +| E00050 | Duplicate Error | +| E00060 | Placement Error | +| E00070 | Units Error | +| E00080 | Quantity Error | +| E00090 | Enumerated Value Error | +| E00100 | Relationship Error | +| E00110 | Naming Error | +| E00120 | Reference Error | +| E00130 | Resource Error | +| E00140 | Deprecation Error | +| E00150 | Shape Representation Error | +| E00160 | Instance Structure Error | +| | | +| W00010 | Alignment Contains Business Logic Only | +| W00020 | Alignment Contains Geometry Only | +| W00030 | Warning | +| | | +| X00040 | Executed | + +#### Notes + +`Not Applicable` refers to a rule that does not apply because of the schema version. +`Executed` refers to a rule that does apply because of schema version, +but the model does not contain any entities validated as part of a particular rule. + +Both outcomes are reported as "N/A" in the validation service user interface. \ No newline at end of file diff --git a/docs/dev/validate_readme.md b/docs/dev/validate_readme.md new file mode 100644 index 00000000..c4f2988a --- /dev/null +++ b/docs/dev/validate_readme.md @@ -0,0 +1,170 @@ + +# Application Structure + +## System Architecture + +```{image} ../_static/dev_system_architecture_sketch.png +:alt: System Architure +:align: center +``` + +The Validation Service is built on [django](https://www.djangoproject.com), +using [Postgres](https://www.postgresql.org) as the database, +[Redis](https://www.redis.io) for task management, +and [Celery](https://docs.celeryq.dev/en/stable/index.html) for distributing the work of running the validation tasks. +The service consists of multiple containers managed with Docker compose. + +## Submodules + +The application consists of three main submodules, each hosted in separate GitHub repositories. Docker Compose is configured to automatically bind the correct submodule versions for local deployment. + +Documentation of the separate functionalities can be found within each submodule. + +1. **File Parser**: A [module within IfcOpenShell](https://github.com/IfcOpenShell/step-file-parser), dedicated to parsing files. +2. **Gherkin Rules**: Contains the rules for validation. It can be run independently by cloning the [repository](https://github.com/buildingSMART/ifc-gherkin-rules) and executing: + + ```shell + pytest -sv + ``` + + Debugging individual rules is supported with commands like: + + ``````shell + python test/test_main.py alb001 # For a single rule + python test/test_main.py alb001 alb002 # For multiple rules + python test/test_main.py path_to_separate_file.py # For a separate file + `````` + +3. **Shared DataModel**: This [module](https://github.com/buildingSMART/ifc-validation-data-model) includes Django data models shared between the main repository and the Gherkin repository, +serving as a submodule for both. + +## Running Validation Checks + +The application supports multiple validation checks on one or multiple IFC files that can be run separately: + +- Syntax Check +- Schema Check +- Normative Rules (gherkin) Check +- bSDD Check + +# How to start? + +Depending on your workflow, you can run all or some services via Docker Compose. + +Below are a few common options to run and debug these services locally. +More scenario's exist - have a look at the various *make* files. + +## Option 1 - Run minimal set of services via Docker Compose (easiest to run) + +1. Make sure Docker is running. + +2. Start all services. + +```shell +make start + +or + +docker compose up +``` + +3. This pulls Docker-hub images, builds and spins up **six** different services: + +``` +db - PostgreSQL database +redis - Redis instance +backend - Django Admin + API's +worker - Celery worker +flower - Celery flower dashboard +frontend - React UI +``` + +4. One-time only: create Django superuser accounts for Django Admin and Celery background worker(s), for example: + +```shell +docker exec -it backend sh + +cd backend + +DJANGO_SUPERUSER_USERNAME=root DJANGO_SUPERUSER_PASSWORD=root DJANGO_SUPERUSER_EMAIL=root@localhost python3 manage.py createsuperuser --noinput + +DJANGO_SUPERUSER_USERNAME=SYSTEM DJANGO_SUPERUSER_PASSWORD=system DJANGO_SUPERUSER_EMAIL=system@localhost python3 manage.py createsuperuser --noinput + +exit +``` + +5. Navigate to different services: + +- Validation Service - React UI: http://localhost +- Django Admin UI: http://localhost/admin (or http://localhost:8000/admin) - default user/password: root/root +- Django API - Swagger: http://localhost/api/swagger-ui +- Django API - Redoc: http://localhost/api/redoc +- Celery Flower UI: http://localhost:5555 + +6. Optionally, use a tool like curl or Postman to invoke API requests directly + +## Option 2 - Local debugging + infrastructure via Docker Compose (easiest to debug) + +1. Make sure Docker is running. + +2. Start infrastructure services only (Redis, Postgres, Celery Flower) + +```shell +make start-infra + +or + +docker compose -f docker-compose.infra_only.yml up +``` + + +3. This pulls **three** different Docker-hub images and spins up services: + +``` +db - PostgreSQL database +redis - Redis instance +flower - Celery flower dashboard +``` + +4. Start Django backend (Admin + API) + +```shell +cd backend +make install +make start-django +``` + +5. Start Celery worker(s) + +```shell +cd backend +make start-worker +``` + +6. Start Node Development server to serve the React UI + +```shell +cd frontend +npm install +npm run start +``` + +7. One-time only: create Django superuser accounts for Django Admin and Celery background worker(s), for example: + +```shell +cd backend + +DJANGO_SUPERUSER_USERNAME=root DJANGO_SUPERUSER_PASSWORD=root DJANGO_SUPERUSER_EMAIL=root@localhost python3 manage.py createsuperuser --noinput + +DJANGO_SUPERUSER_USERNAME=SYSTEM DJANGO_SUPERUSER_PASSWORD=system DJANGO_SUPERUSER_EMAIL=system@localhost python3 manage.py createsuperuser --noinput +``` + +8. Navigate to different services: + +- Validation Service - React UI: http://localhost:3000 +- Django Admin UI: http://localhost:8000/admin - default user/password: root/root +- Django API - Swagger: http://localhost:8000/api/swagger-ui +- Django API - Redoc: http://localhost:8000/api/redoc +- Celery Flower UI: http://localhost:5555 + +9. Optionally, use a tool like curl or Postman to invoke API requests directly \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..c2ce4924 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,7 @@ +# Welcome to the buildingSMART Validation Service Docs! + +```{toctree} +user/index.md +dev/index.md +ref/index.md +``` diff --git a/docs/ref/index.md b/docs/ref/index.md new file mode 100644 index 00000000..d1991f5e --- /dev/null +++ b/docs/ref/index.md @@ -0,0 +1,13 @@ +# Reference Information + +## Additional Information for Normative Rules + +```{include} ./normative-rules/ALB021.md +:heading-offset: 1 +:relative-images: +``` + +```{include} ./normative-rules/ALS016.md +:heading-offset: 1 +:relative-images: +``` diff --git a/docs/ref/normative-rules/ALB021.md b/docs/ref/normative-rules/ALB021.md new file mode 100644 index 00000000..c05080f5 --- /dev/null +++ b/docs/ref/normative-rules/ALB021.md @@ -0,0 +1,39 @@ +# ALB021 + +This normative rule validates the three implementer agreements captured in three concept templates: + +- [4.1.7.1.1.1 Alignment Geometry - Horizontal](https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Alignment_Geometry/Alignment_Geometry_-_Horizontal/content.html) +- [4.1.7.1.1.2 Alignment Geometry - Horizontal and Vertical](https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Alignment_Geometry/Alignment_Geometry_-_Horizontal_and_Vertical/content.html) +- [4.1.7.1.1.3 Alignment Geometry - Horizontal, Vertical and Cant](https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Alignment_Geometry/Alignment_Geometry_-_Horizontal,_Vertical_and_Cant/content.html) + +## Case 1 - Horizontal Alignment Only + + - Only 1 IfcShapeRepresentation + - IfcShapeRepresentation + - Item: IfcCompositeCurve + - RepresentationIdentifier = `Axis` + - RepresentationType = `Curve2D` + +## Case 2 - Horizontal and Vertical Alignment + + - 2 IfcShapeRepresentations + - First IfcShapeRepresentation + - Item: IfcCompositeCurve + - RepresentationIdentifier = `FootPrint` + - RepresentationType = `Curve2D` + - Second IfcShapeRepresentation + - Item: IfcGradientCurve (using IfcCompositeCurve as BaseCurve) + - RepresentationIdentifier = `Axis` + - RepresentationType = `Curve3D` + +## Case 3 - Horizontal, Vertical, and Cant Alignment + +- 2 IfcShapeRepresentations + - First IfcShapeRepresentation + - Item: IfcCompositeCurve + - RepresentationIdentifier = `FootPrint` + - RepresentationType = `Curve2D` + - Second IfcShapeRepresentation + - Item: IfcSegmentedReferenceCurve (using IfcGradientCurve as BaseCurve) + - RepresentationIdentifier = `Axis` + - RepresentationType = `Curve3D` \ No newline at end of file diff --git a/docs/ref/normative-rules/ALS016.md b/docs/ref/normative-rules/ALS016.md new file mode 100644 index 00000000..fc51f33f --- /dev/null +++ b/docs/ref/normative-rules/ALS016.md @@ -0,0 +1,17 @@ +# ALS016 + +This industry best practice rule performs geometric calculations to assess geometric continuity between +successive `IfcCurveSegment` entities that make up an alignment representation. + +This rule checks the shape representation (geometry) only and does not +utilize any information in the semantic (business logic) definition of the alignment. + +The `Transition` attribute on +[`IfcCurveSegment`](https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcCurveSegment.htm) +is utilized to determine the model author's intended +continuity between segments. + +A value of `.CONTINUOUS.` would mean that a warning for ALS016 could be raised positional discontinuity only +(not tangency). +Similarly, a value of `.DISCONTINUOUS.` would mean that a warning for ALS016 could not be raised, +full stop. \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..3eacdc73 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +myst-parser==2.0.0 +Sphinx==7.2.6 +sphinx-rtd-theme==2.0.0 \ No newline at end of file diff --git a/docs/user/index.md b/docs/user/index.md new file mode 100644 index 00000000..16c7613d --- /dev/null +++ b/docs/user/index.md @@ -0,0 +1,17 @@ +# User Guide + +## Introduction + +Data validation is a key component of openBIM® workflows. +The IFC Validation Services allows users to check the validity of their IFC models +against the IFC standard. + +```{include} using.md +:heading-offset: 1 +:relative-images: +``` + +```{include} validation_overview.md +:heading-offset: 1 +:relative-images: +``` diff --git a/docs/user/using.md b/docs/user/using.md new file mode 100644 index 00000000..70dc8257 --- /dev/null +++ b/docs/user/using.md @@ -0,0 +1,45 @@ +# Using the Validation Service + +## Logging In + +Upon first visiting the site, you will need to authenticate with your user name and password. + +```{image} ../_static/user_auth.png +:alt: Login screen +:scale: 80 % +:align: center +``` + +You can create a new account if you don't have one already by following +the **Sign up now** link. + +```{note} +Your usage of the Validation Service is subject to the +[Terms of Service](https://www.buildingsmart.org/wp-content/uploads/2021/09/20210923_TermsOfService.pdf). +``` + +## File upload + +```{note} +At this time the service will only accept non-zipped STEP physical files having extension *.ifc* and no greater than 256 MB in size. +``` + +## File processing + +## Results + +### Color codes and icons + +```{image} ../_static/user_results_icons_no_bSDD.png +:alt: Color codes and icons displayed by the service +:scale: 80 % +:align: center +``` + +Pass results are hidden by default but can be shown by clicking the checkbox in the upper-right corner: + +```{image} ../_static/user_include_passed_na_etc.png +:alt: Button to hide passing, disabled, or not applicable results +:scale: 110 % +:align: center +``` diff --git a/docs/user/validation_overview.md b/docs/user/validation_overview.md new file mode 100644 index 00000000..7d25594a --- /dev/null +++ b/docs/user/validation_overview.md @@ -0,0 +1,69 @@ +# Understanding the validation process + +Given an IFC file, the Validation Service provides a judgement of conformity +against the IFC standard - including schema and specification + +## STEP Syntax + +The first step in the validation process looks at the uploaded file to confirm that +it is a valid STEP Physical File (SPF) in accordance with [ISO 10303-21](https://www.iso.org/standard/63141.html). + +## IFC Schema + +Schema validation consists of two parts: + +1. Schema Version +2. Schema Compliance + +### Schema Version + +This check confirms that the schema identifier is one of the following: + +- `IFC2X3` +- `IFC4` +- `IFC4X3_ADD2` + +### Schema Compliance + +The schema compliance checks the following aspects that are defined in the EXPRESS schema: + + - Entity attributes are correctly populated, correct number of attributes and correct type and cardinalities in case of aggregates + - Inverse attributes are correctly populated and with the correct cardinalities + - Entity-scoped `WHERE` rules + - Global rules + +This check also flags any entity types that are not included in a given schema version, or the instantiation of abstract entities. + +For example: `IfcAlignment` entity is only valid for schema version `IFC4X3_ADD2`, +so it is not valid as part of a file with schema version `IFC2X3`. + +## Normative Checks + +There are two categories of normative checks: + +1. Implementer Agreements +2. Informal Propositions + +### Implementer Agreements + +These are normative checks that have been ratified as official agreements amongst software implementers. + +### Informal Propositions + +These are normative checks that have not been ratified as implementer agreements, +but are still considered mandatory for a file to be considered valid. + +## Additional, Non-normative Checks + +### Industry Practices + +This step involves checking the IFC file against common practices and sensible defaults. +None of these checks render the IFC file invalid. +Therefore, any issues identified result in warnings rather than errors. + +### buildingSMART Data Dictionary (bSDD) Compliance + +```{note} +bSDD Checks are temporarily disabled as of v0.6.6. +``` + diff --git a/frontend/src/GherkinResult.js b/frontend/src/GherkinResult.js index 7fcf6e52..2dcd41fa 100644 --- a/frontend/src/GherkinResult.js +++ b/frontend/src/GherkinResult.js @@ -66,7 +66,8 @@ function unsafe_format(obj) { directionLabel = 'Gradient'; } else { - directionLabel= ''; + // warning is raised for position, so don't report any details of direction or gradient + directionLabel= 'suppress'; } if ('continuity_details' in obj) { @@ -76,11 +77,13 @@ function unsafe_format(obj) {
{ctx} {display_value}
at end of {dts.previous_segment}
- + { directionLabel !== 'suppress' && ( + ) }
and start of {dts.segment_to_analyze}
- + { directionLabel !== 'suppress' && ( + )} ); } else { @@ -174,8 +177,9 @@ export default function GherkinResult({ summary, content, status, instances }) { setGrouped(grouped) }, [page, content, checked]); - function getSuffix(rows) { - return (rows && rows.length > 0) ? '(failed ' + rows.length.toLocaleString() + ' times)' : '' + function getSuffix(rows, status) { + let times = (rows && rows.length > 1) ? ' times' : ' time'; + return (rows && rows.length > 0 && rows[0].severity >= 4) ? '(failed ' + rows.length.toLocaleString() + times + ')' : ''; } return ( @@ -214,6 +218,7 @@ export default function GherkinResult({ summary, content, status, instances }) { ".MuiTreeItem-content.Mui-expanded": { borderBottom: 'solid 1px black' }, ".MuiTreeItem-group .MuiTreeItem-content.Mui-expanded": { borderBottom: 0 }, ".caption" : { paddingTop: "1em", paddingBottom: "1em", textTransform: 'capitalize' }, + ".caption-suffix" : { paddingTop: "1em", paddingBottom: "1em", fontSize: '0.9em', textTransform: 'none', fontStyle: 'italic' }, ".subcaption" : { visibility: "hidden", fontSize: '80%' }, ".MuiTreeItem-content.Mui-expanded .subcaption" : { visibility: "visible" }, "table": { borderCollapse: 'collapse', fontSize: '80%' }, @@ -237,7 +242,7 @@ export default function GherkinResult({ summary, content, status, instances }) { > {feature} {getSuffix(rows)}} + label={
{feature} {getSuffix(rows, status)}
} sx={{ "backgroundColor": severityToColor[severity] }} >
diff --git a/frontend/src/SchemaResult.js b/frontend/src/SchemaResult.js index ae595fa8..4a7e4a98 100644 --- a/frontend/src/SchemaResult.js +++ b/frontend/src/SchemaResult.js @@ -56,8 +56,9 @@ export default function SchemaResult({ summary, content, status, instances }) { setGrouped(grouped) }, [page, content, checked]); - function getSuffix(rows) { - return (rows && rows.length > 0) ? '(failed ' + rows.length.toLocaleString() + ' times)' : '' + function getSuffix(rows, status) { + let times = (rows && rows.length > 1) ? ' times' : ' time'; + return (rows && rows.length > 0 && rows[0].severity >= 4) ? '(failed ' + rows.length.toLocaleString() + times + ')' : ''; } return ( @@ -98,6 +99,7 @@ export default function SchemaResult({ summary, content, status, instances }) { ".MuiTreeItem-content.Mui-expanded": { borderBottom: 'solid 1px black' }, ".MuiTreeItem-group .MuiTreeItem-content.Mui-expanded": { borderBottom: 0 }, ".caption" : { paddingTop: "1em", paddingBottom: "1em", textTransform: 'capitalize' }, + ".caption-suffix" : { paddingTop: "1em", paddingBottom: "1em", fontSize: '0.9em', textTransform: 'none', fontStyle: 'italic' }, ".subcaption" : { visibility: "hidden", fontSize: '80%' }, ".MuiTreeItem-content.Mui-expanded .subcaption" : { visibility: "visible" }, "table": { borderCollapse: 'collapse', fontSize: '80%' }, @@ -115,7 +117,7 @@ export default function SchemaResult({ summary, content, status, instances }) { defaultCollapseIcon={} defaultExpandIcon={} > -
{(rows[0].constraint_type || '').replace('_', ' ')}{rows[0].constraint_type && ' - '}{hd} {getSuffix(rows)}
{rows[0].constraint_type !== 'schema' ? (coerceToStr(rows[0].msg)).split('\n')[0] : ''}
} +
{(rows[0].constraint_type || '').replace('_', ' ')}{rows[0].constraint_type && ' - '}{hd} {getSuffix(rows, status)}
{rows[0].constraint_type !== 'schema' ? (coerceToStr(rows[0].msg)).split('\n')[0] : ''}
} sx={{ "backgroundColor": severityToColor[rows[0].severity] }} >