Merge remote-tracking branch 'origin/v0.8.0' into datamodel-v1.0

This commit is contained in:
Thomas Krijnen
2026-04-18 20:15:28 +02:00
1655 changed files with 105534 additions and 46934 deletions
-9
View File
@@ -15,12 +15,3 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import toposort
import ifcpatch
import ifcpatch.recipes
import ifcopenshell.util.selector
import ifcopenshell.util.element
import ifcopenshell.util.schema
import ifcpatch.__main__
+12 -13
View File
@@ -18,22 +18,21 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import os
import shutil
import sys
import ifcopenshell
import logging
import typing
import inspect
import collections
import importlib
import importlib.util
import inspect
import logging
import os
import re
from pathlib import Path
from typing import Union, Optional, Any, TypedDict
from typing_extensions import NotRequired
import shutil
import sys
import typing
from collections.abc import Sequence
from pathlib import Path
from typing import Any, Optional, TypedDict, Union
import ifcopenshell
from typing_extensions import NotRequired
__version__ = version = "0.0.0"
@@ -216,11 +215,11 @@ def _extract_docs(cls: type, method_name: str, boilerplate_args: Union[Sequence[
input_data = inputs[input_name]
# E.g. list[str].
if isinstance(type_hint, typing.GenericAlias):
if isinstance(type_hint, typing.GenericAlias): # pyright: ignore[reportAttributeAccessIssue]
input_data["generic_type"] = type_hint.__name__
type_hint = typing.get_args(type_hint)[0]
if isinstance(type_hint, typing._UnionGenericAlias):
if isinstance(type_hint, typing._UnionGenericAlias): # pyright: ignore[reportAttributeAccessIssue]
inputs[input_name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
elif type_hint.__name__ == "Literal":
inputs[input_name]["type"] = "Literal"
+3 -1
View File
@@ -19,9 +19,11 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import argparse
import ifcpatch
import ifcopenshell
import ifcpatch
parser = argparse.ArgumentParser(description="Patches IFC files to fix badly formatted data")
parser.add_argument("-i", "--input", type=str, required=True, help="The IFC file to patch")
parser.add_argument("-o", "--output", type=str, help="The output file to save the patched IFC")
+10 -11
View File
@@ -17,22 +17,21 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import os
import csv
import numpy as np
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.api.style
import os
import ifcopenshell.api.aggregate
import ifcopenshell.api.context
import ifcopenshell.api.document
import ifcopenshell.api.project
import ifcopenshell.api.spatial
import ifcopenshell.api.geometry
import ifcopenshell.api.aggregate
import ifcopenshell.api.project
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.api.spatial
import ifcopenshell.api.style
import ifcopenshell.api.unit
import ifcopenshell.util.shape_builder
from itertools import cycle
import numpy as np
from ifcopenshell.util.shape_builder import V
@@ -16,13 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.util.alignment
import ifcpatch
from logging import Logger
import typing
from typing import Union
import ifcpatch
class Patcher(ifcpatch.BasePatcher):
@@ -16,13 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.util.alignment
import ifcpatch
from logging import Logger
import typing
from typing import Union
import ifcpatch
class Patcher(ifcpatch.BasePatcher):
@@ -16,13 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.util.alignment
import ifcpatch
from logging import Logger
import typing
from typing import Union
import ifcpatch
class Patcher(ifcpatch.BasePatcher):
@@ -16,10 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.unit
from logging import Logger
class Patcher:
@@ -16,18 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.owner.settings
import ifcopenshell.util.pset
import ifcopenshell.util.element
import ifcopenshell.util.unit
import ifcpatch
from logging import Logger
import typing
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.util.unit
import ifcpatch
LengthUnit = typing.Literal[
"ATTOMETER",
"FEMTOMETER",
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.guid
@@ -16,14 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.pset
import ifcopenshell.util.pset
import ifcopenshell.util.element
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.util.pset
class Patcher:
def __init__(self, file: ifcopenshell.file, logger: Logger, property_name: str, quantity_name: str):
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
@@ -16,14 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.project
import ifcopenshell.guid
import ifcopenshell.util.selector
import ifcpatch
from typing import Union
from logging import Logger
class Patcher(ifcpatch.BasePatcher):
@@ -17,25 +17,47 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import json
import time
import logging
import tempfile
import ifcopenshell
from typing import NamedTuple
import ifcopenshell.util.element
import ifcpatch
try:
import sqlite3
import sqlite3 # noqa: F401
except:
print("No SQLite support")
class Patcher:
class ElementRow(NamedTuple):
element_id: int
guid: str
class_: str
predefined_type: str | None
name: str | None
description: str | None
class PropertyRow(NamedTuple):
element_id: int
pset_name: str
name: str
value: str
class RelationshipRow(NamedTuple):
element_id: int
rel_ifc_class: str
to_id: int
class Patcher(ifcpatch.BasePatcher):
def __init__(
self,
file,
logger,
file: ifcopenshell.file,
logger: logging.Logger | None = None,
):
"""Extracts properties and relationships from a IFC-SPF model to SQLite.
@@ -49,18 +71,18 @@ class Patcher:
result = ifcpatch.execute({"input": fn, "file": model, "recipe": "ExtractPropertiesToSQLite"})
ifcpatch.write(result, "output.sqlite")
"""
self.file = file
self.logger = logger
super().__init__(file, logger)
def patch(self):
import sqlite3
tmp = tempfile.NamedTemporaryFile(delete=False)
db_file = tmp.name
self.db = sqlite3.connect(db_file)
self.c = self.db.cursor()
self.file_patched = db_file
self.c.execute(
"""
self.c.execute("""
CREATE TABLE IF NOT EXISTS elements (
id integer PRIMARY KEY NOT NULL UNIQUE,
global_id text,
@@ -69,51 +91,47 @@ class Patcher:
name text,
description text
);
"""
)
""")
self.c.execute("CREATE INDEX IF NOT EXISTS idx_global_id ON elements (global_id);")
self.c.execute("CREATE INDEX IF NOT EXISTS idx_ifc_class ON elements (ifc_class);")
self.c.execute("CREATE INDEX IF NOT EXISTS idx_predefined_type ON elements (predefined_type);")
self.c.execute(
"""
self.c.execute("""
CREATE TABLE IF NOT EXISTS relationships (
from_id integer NOT NULL,
type text,
to_id integer NOT NULL
);
"""
)
""")
self.c.execute("CREATE INDEX IF NOT EXISTS idx_from_id ON relationships (from_id);")
self.c.execute(
"""
self.c.execute("""
CREATE TABLE IF NOT EXISTS properties (
element_id integer NOT NULL,
set_name text,
name text,
value text
);
"""
)
""")
self.c.execute("CREATE INDEX IF NOT EXISTS idx_element_id ON properties (element_id);")
elements = self.file.by_type("IfcObjectDefinition")
rows = []
properties = []
relationships = []
rows: list[ElementRow] = []
properties: list[PropertyRow] = []
relationships: list[RelationshipRow] = []
id_map = {e.id(): i for i, e in enumerate(elements)}
for i, element in enumerate(elements):
rows.append(
[
ElementRow(
i,
element[0], # IfcRoot.GlobalId
element.is_a(),
ifcopenshell.util.element.get_predefined_type(element),
element[2], # IfcRoot.Name
element[3], # IfcRoot.Description
]
)
)
psets = ifcopenshell.util.element.get_psets(element, should_inherit=False)
for pset_name, pset_data in psets.items():
@@ -124,49 +142,55 @@ class Patcher:
value = "True" if value else "False"
elif not isinstance(value, str):
value = str(value)
properties.append([i, pset_name, prop_name, value])
properties.append(PropertyRow(i, pset_name, prop_name, value))
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
if material:
name = getattr(material, "Name", getattr(material, "LayerSetName", None)) or "Unnamed"
properties.append([i, "IFC Material", "Name", name])
properties.append([i, "IFC Material", "Class", material.is_a()])
properties.append(PropertyRow(i, "IFC Material", "Name", name))
properties.append(PropertyRow(i, "IFC Material", "Class", material.is_a()))
if material.is_a("IfcMaterial"):
materials = []
elif material.is_a("IfcMaterialLayerSet"):
for idx, item in enumerate(material.MaterialLayers or []):
material = item.Material
properties.append([i, "IFC Material", f"Layer {idx + 1} Name", getattr(item, "Name", None)])
properties.append([i, "IFC Material", f"Layer {idx + 1} Material", material.Name])
properties.append(
PropertyRow(i, "IFC Material", f"Layer {idx + 1} Name", getattr(item, "Name", None))
)
properties.append(PropertyRow(i, "IFC Material", f"Layer {idx + 1} Material", material.Name))
if category := getattr(material, "Category", None):
properties.append([i, "IFC Material", f"Layer {idx + 1} Category", category])
properties.append(PropertyRow(i, "IFC Material", f"Layer {idx + 1} Category", category))
elif material.is_a("IfcMaterialProfileSet"):
for idx, item in enumerate(material.MaterialProfiles or []):
material = item.Material
properties.append([i, "IFC Material", f"Profile {idx + 1} Name", item.Name])
properties.append([i, "IFC Material", f"Profile {idx + 1} Material", material.Name])
properties.append(PropertyRow(i, "IFC Material", f"Profile {idx + 1} Name", item.Name))
properties.append(PropertyRow(i, "IFC Material", f"Profile {idx + 1} Material", material.Name))
if category := getattr(material, "Category", None):
properties.append([i, "IFC Material", f"Profile {idx + 1} Category", category])
properties.append(PropertyRow(i, "IFC Material", f"Profile {idx + 1} Category", category))
elif material.is_a("IfcMaterialConstituentSet"):
for idx, item in enumerate(material.MaterialConstituents or []):
material = item.Material
properties.append([i, "IFC Material", f"Constituent {idx + 1} Name", item.Name])
properties.append([i, "IFC Material", f"Constituent {idx + 1} Material", material.Name])
properties.append(PropertyRow(i, "IFC Material", f"Constituent {idx + 1} Name", item.Name))
properties.append(
PropertyRow(i, "IFC Material", f"Constituent {idx + 1} Material", material.Name)
)
if category := getattr(material, "Category", None):
properties.append([i, "IFC Material", f"Constituent {idx + 1} Category", category])
properties.append(
PropertyRow(i, "IFC Material", f"Constituent {idx + 1} Category", category)
)
elif material.is_a("IfcMaterialList"):
for idx, material in enumerate(material.Materials):
properties.append([i, "IFC Material", f"Material {idx + 1} Name", material.Name])
properties.append(PropertyRow(i, "IFC Material", f"Material {idx + 1} Name", material.Name))
if category := getattr(material, "Category", None):
properties.append([i, "IFC Material", f"Material {idx + 1} Category", category])
properties.append(PropertyRow(i, "IFC Material", f"Material {idx + 1} Category", category))
layers = ifcopenshell.util.element.get_layers(self.file, element)
for idx, layer in enumerate(layers):
properties.append([i, "IFC Presentation Layer Assignment", f"Layer {idx + 1}", layer.Name])
properties.append(PropertyRow(i, "IFC Presentation Layer Assignment", f"Layer {idx + 1}", layer.Name))
relating_type = ifcopenshell.util.element.get_type(element)
if relating_type and relating_type != element:
relationships.append([i, "IfcRelDefinesByType", id_map[relating_type.id()]])
relationships.append(RelationshipRow(i, "IfcRelDefinesByType", id_map[relating_type.id()]))
self.c.executemany("INSERT INTO elements VALUES (?, ?, ?, ?, ?, ?);", rows)
self.c.executemany("INSERT INTO properties VALUES (?, ?, ?, ?);", properties)
@@ -18,17 +18,16 @@
import logging
import math
import os
from typing import Union
import ifcpatch
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.element
import ifcopenshell.util.schema
import ifcopenshell.util.unit
import ifcpatch
class Patcher(ifcpatch.BasePatcher):
def __init__(self, file: ifcopenshell.file, logger: Union[logging.Logger, None] = None):
@@ -17,7 +17,6 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import logging
import ifcopenshell
class Patcher:
@@ -67,11 +66,10 @@ class Patcher:
self.filepath = filepath
def patch(self) -> None:
import bpy
import bonsai.tool as tool
import ifcopenshell
import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import ifcopenshell.util.element
from mathutils import Vector, Matrix
from mathutils import Matrix, Vector # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
if len(bpy.data.objects) > 0:
bpy.data.batch_remove(bpy.data.objects)
@@ -16,13 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import logging
from typing import Optional, TYPE_CHECKING
import ifcopenshell
import ifcopenshell.util.schema
import ifcopenshell.util.shape_builder
import ifcopenshell.util.unit
import logging
from typing import Optional
if TYPE_CHECKING:
import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
class Patcher:
@@ -110,12 +114,14 @@ class Patcher:
self.should_create_edges = should_create_edges
def patch(self) -> None:
import bpy
import bmesh
import bmesh # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import bonsai.tool as tool
import ifcopenshell.util.shape_builder
import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import ifcopenshell.util.schema
import ifcopenshell.util.unit
bpy.context.scene.BIMProjectProperties.should_use_native_meshes = True
props = tool.Project.get_project_props()
props.should_use_native_meshes = True
bpy.ops.bim.load_project(filepath=self.filepath)
old_history_size = tool.Ifc.get().history_size
@@ -126,7 +132,7 @@ class Patcher:
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id or not obj.data:
if not tool.Blender.get_ifc_definition_id(obj) or not obj.data:
continue
if not obj.data.polygons:
continue
@@ -160,16 +166,14 @@ class Patcher:
self.file = tool.Ifc.get()
def create_edges(self, obj):
import bpy
import bmesh
def create_edges(self, obj: bpy.types.Object) -> None:
import bmesh # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import bonsai.tool as tool
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.api.root
import ifcopenshell.api.type
import ifcopenshell.api.spatial
import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.representation
import ifcopenshell.util.schema
element = tool.Ifc.get_entity(obj)
data = obj.data
@@ -227,19 +231,17 @@ class Patcher:
ifcopenshell.util.schema.reassign_class(tool.Ifc.get(), element2, "IfcVirtualElement")
bm.free()
def create_face_sampleable_object(self, obj):
def create_face_sampleable_object(self, obj: bpy.types.Object) -> None:
# No sharp faces
import bpy
import bmesh
import bonsai.tool as tool
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.api.root
import ifcopenshell.api.type
import ifcopenshell.api.spatial
import ifcopenshell.api.geometry
from math import degrees
import bmesh # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import bonsai.tool as tool
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
print("working on ", obj.name)
element = tool.Ifc.get_entity(obj)
data = obj.data
@@ -276,19 +278,17 @@ class Patcher:
bm.free()
def create_edge_sampleable_object(self, obj):
def create_edge_sampleable_object(self, obj: bpy.types.Object) -> None:
# This is crazy but we need a sharp face per island
import bpy
import bmesh
import bonsai.tool as tool
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.api.root
import ifcopenshell.api.type
import ifcopenshell.api.spatial
import ifcopenshell.api.geometry
from math import degrees, radians, sin
from mathutils import Matrix
import bmesh # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import bonsai.tool as tool
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
from mathutils import Matrix # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
# Get the active object (assumed to have a mesh)
mesh = obj.data
@@ -437,7 +437,6 @@ class Patcher:
bm.free()
print("Added a triangle to", islands_count, "mesh island(s).")
return
def create_curves_from_curve_ifc2x3(
self, is_2d: bool = False, curve_object_data=None
@@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
@@ -17,7 +17,6 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import logging
@@ -79,11 +78,12 @@ class Patcher:
self.is_solid = is_solid
def patch(self) -> None:
import bpy
import bmesh
import bonsai.tool as tool
from math import degrees
import bmesh # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
import bonsai.tool as tool
import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
props = tool.Project.get_project_props()
props.should_use_native_meshes = True
bpy.ops.bim.load_project(filepath=self.filepath)
+13 -15
View File
@@ -17,16 +17,16 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import json
import time
import tempfile
import typing
import itertools
import json
import logging
import numpy as np
import multiprocessing
import re
import time
import typing
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal, Union
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as W
@@ -37,15 +37,16 @@ import ifcopenshell.util.representation
import ifcopenshell.util.schema
import ifcopenshell.util.shape
import ifcopenshell.util.unit
import ifcpatch
from pathlib import Path
from typing import Any, TYPE_CHECKING, Literal, Union
import numpy as np
from typing_extensions import assert_never
import ifcpatch
SQLTypes = typing.Literal["SQLite", "MySQL"]
if TYPE_CHECKING:
import sqlite3
import mysql.connector
import mysql.connector.abstracts
else:
@@ -124,7 +125,6 @@ class Patcher(ifcpatch.BasePatcher):
)
"""
super().__init__(file, logger)
self.logger = logger
self.sql_type: Literal["sqlite", "mysql"] = sql_type.lower()
self.host = host
self.username = username
@@ -348,13 +348,11 @@ class Patcher(ifcpatch.BasePatcher):
assert cursor is not None
row = cursor.fetchone()
elif self.sql_type == "mysql":
cursor = self.c.execute(
f"""
cursor = self.c.execute(f"""
SELECT 1 FROM information_schema.tables
WHERE table_schema = '{self.database}' AND table_name = 'id_map'
LIMIT 1;
"""
)
""")
row = self.c.fetchone()
else:
assert_never(self.sql_type)
@@ -16,13 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.type
import ifcopenshell.util.element
from logging import Logger
from typing import Any
import ifcopenshell
import ifcopenshell.api.type
import ifcopenshell.util.element
class Patcher:
def __init__(self, file: ifcopenshell.file, logger: Logger, attribute: str = "Tag", should_merge_null: bool = True):
@@ -97,5 +97,5 @@ class Patcher:
relating_type=relating_type,
related_objects=related_objects,
should_map_representations=False,
should_run_listeners=False,
should_run_listeners=False, # ty:ignore[unknown-argument]
)
+19 -8
View File
@@ -16,18 +16,28 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from collections.abc import Sequence
from logging import Logger
from typing import Union
import ifcpatch
import ifcopenshell
import ifcopenshell.util.unit
import ifcopenshell.util.element
import ifcopenshell.util.geolocation
import ifcopenshell.util.unit
import numpy as np
import ifcpatch
from ifcpatch.recipes.SetFalseOrigin import Patcher as SetFalseOrigin
from typing import Union
from logging import Logger
class Patcher:
def __init__(self, file: ifcopenshell.file, logger: Logger, filepaths: list[Union[str, ifcopenshell.file]]):
class Patcher(ifcpatch.BasePatcher):
def __init__(
self,
file: ifcopenshell.file,
logger: Logger | None = None,
filepaths: Sequence[Union[str, ifcopenshell.file]] = (),
):
"""Merge two or more IFC models into one
Note that other than combining the two (or more) IfcProject elements into
@@ -48,8 +58,7 @@ class Patcher:
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "MergeProjects", "arguments": ["/path/to/model2.ifc"]})
"""
self.file = file
self.logger = logger
super().__init__(file, logger)
self.filepaths = filepaths
def patch(self):
@@ -59,6 +68,8 @@ class Patcher:
"replace it with a list of file/filepaths."
)
self.filepaths = [self.filepaths]
if len(self.filepaths) == 0:
raise ValueError("At least one file/filepath must be provided to merge with the main model.")
for filepath in self.filepaths:
if isinstance(filepath, ifcopenshell.file):
other = filepath
+5 -3
View File
@@ -16,12 +16,14 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
import ifcpatch
import logging
from typing import Union
import ifcopenshell
import ifcopenshell.util.element
import ifcpatch
class Patcher(ifcpatch.BasePatcher):
def __init__(self, file: ifcopenshell.file, logger: Union[logging.Logger, None] = None):
+4 -3
View File
@@ -16,12 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.util.schema
import ifcpatch
import typing
from typing import Union
from logging import Logger
class Patcher(ifcpatch.BasePatcher):
@@ -17,10 +17,10 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import math
import numpy as np
import ifcopenshell
import ifcopenshell.util.placement
import typing
import ifcopenshell.util.placement
import numpy as np
from ifcopenshell.util.shape_builder import ShapeBuilder
@@ -17,7 +17,6 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
class Patcher:
@@ -1,11 +1,11 @@
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.api.alignment
import ifcopenshell.util.element
import ifcpatch
from logging import Logger
import typing
from typing import Union
import ifcpatch
class Patcher(ifcpatch.BasePatcher):
+3 -8
View File
@@ -17,9 +17,10 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import datetime
import ifcopenshell
from logging import Logger
import ifcopenshell
class Patcher:
def __init__(self, file: ifcopenshell.file, logger: Logger):
@@ -56,13 +57,7 @@ class Patcher:
def patch(self):
self.file.header.file_name.name = "Rabbit"
self.file.header.file_name.time_stamp = (
datetime.datetime.utcnow()
.replace(tzinfo=datetime.timezone.utc)
.astimezone()
.replace(microsecond=0)
.isoformat()
)
self.file.header.file_name.time_stamp = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
self.file.header.file_name.preprocessor_version = "Rabbit"
self.file.header.file_name.originating_system = "Rabbit"
@@ -16,8 +16,9 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from collections import deque
import logging
from collections import deque
import ifcopenshell.util.element
@@ -16,9 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
import ifcopenshell
import ifcopenshell.guid
from logging import Logger
class Patcher:
@@ -17,10 +17,11 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import Literal, Union
import ifcopenshell
import numpy as np
import numpy.typing as npt
import ifcopenshell
from typing import Literal, Optional, Union
class Patcher:
@@ -16,11 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import logging
import ifcopenshell.api
import ifcopenshell
import ifcopenshell.api.geometry
import ifcopenshell.util
import ifcopenshell.util.element
import ifcopenshell.util.placement
import numpy as np
@@ -16,11 +16,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import typing
import ifcopenshell.api.georeference
import ifcopenshell.util.geolocation
from ifcpatch.recipes import OffsetObjectPlacements, SetWorldCoordinateSystem, ResetSpatialElementLocations
import typing
from ifcpatch.recipes import (
OffsetObjectPlacements,
ResetSpatialElementLocations,
SetWorldCoordinateSystem,
)
class Patcher:
@@ -17,9 +17,10 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import math
import numpy as np
import typing
import numpy as np
class Patcher:
def __init__(
@@ -16,12 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
import ifcopenshell
import os
from pathlib import Path
from typing import Union
import ifcopenshell
class Patcher:
def __init__(self, file: ifcopenshell.file, logger: logging.Logger, output_dir: Union[str, None] = None):
@@ -45,10 +46,11 @@ class Patcher:
self.output_dir = output_dir
def patch(self) -> None:
import ifcopenshell
import tempfile
from shutil import copyfile
import ifcopenshell
if self.output_dir is None:
output_dir = Path()
else:
@@ -16,17 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.geometry
import ifcopenshell.geom
import ifcopenshell.util.selector
import ifcopenshell.util.shape
import ifcopenshell.util.representation
import multiprocessing
from logging import Logger
from typing import Union
import ifcopenshell
import ifcopenshell.api.geometry
import ifcopenshell.geom
import ifcopenshell.util.representation
import ifcopenshell.util.selector
import ifcopenshell.util.shape
class Patcher:
def __init__(
@@ -16,12 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
from logging import Logger
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcopenshell.util.selector
from logging import Logger
class Patcher:
+8
View File
@@ -31,3 +31,11 @@ Issues = "https://github.com/IfcOpenShell/IfcOpenShell/issues"
[tool.setuptools.packages.find]
include = ["ifcpatch*"]
exclude = ["test*"]
[tool.ruff]
extend = "../../pyproject.toml"
[tool.ruff.lint]
select = [
"F401", # unused imports
]
+2 -2
View File
@@ -16,11 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.project
import ifcopenshell.api.owner.settings
import ifcopenshell.api.project
import pytest
class IFC4X3:
+2 -2
View File
@@ -16,12 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.util.unit
import ifcpatch
import test.bootstrap
class TestConvertLengthUnit(test.bootstrap.IFC4):
@@ -16,11 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcpatch
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.util.element
import ifcpatch
import test.bootstrap
+4 -3
View File
@@ -17,14 +17,15 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import os
import pytest
import ifcpatch
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.aggregate
import ifcopenshell.api.root
import ifcopenshell.api.spatial
import ifcopenshell.util.element
import pytest
import ifcpatch
import test.bootstrap
+4 -11
View File
@@ -17,19 +17,12 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import tempfile
import ifcpatch
import ifcopenshell
import ifcopenshell.api.context
import ifcopenshell.api.geometry
import ifcopenshell.api.georeference
import ifcopenshell.geom
import ifcopenshell.util.geolocation
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import ifcopenshell.util.shape
import ifcopenshell.util.shape_builder
from pathlib import Path
import ifcopenshell
import ifcpatch
class TestIfc2Sql:
def test_run(self):
@@ -16,17 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import os
import pytest
import ifcpatch
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.geometry
import ifcopenshell.api.material
import ifcopenshell.api.root
import ifcopenshell.api.type
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcpatch
import test.bootstrap
+7 -5
View File
@@ -16,7 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcpatch
import tempfile
from pathlib import Path
from typing import Optional
import ifcopenshell
import ifcopenshell.api.context
import ifcopenshell.api.geometry
@@ -29,11 +32,10 @@ import ifcopenshell.util.placement
import ifcopenshell.util.representation
import ifcopenshell.util.shape
import ifcopenshell.util.shape_builder
import test.bootstrap
import tempfile
import numpy as np
from pathlib import Path
from typing import Optional
import ifcpatch
import test.bootstrap
class TestMergeProjects(test.bootstrap.IFC4):
+1 -1
View File
@@ -16,8 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcpatch
import ifcopenshell
import test.bootstrap
@@ -16,13 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import os
import pytest
import ifcpatch
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.root
import ifcopenshell.util.element
import ifcpatch
import test.bootstrap
@@ -16,16 +16,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.api.aggregate
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.api.unit
import ifcopenshell.util.placement
import ifcpatch
import numpy as np
import ifcpatch
import test.bootstrap
class TestResetSpatialElementLocations(test.bootstrap.IFC4):
def test_run(self):
+3 -3
View File
@@ -16,8 +16,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.api.aggregate
import ifcopenshell.api.context
import ifcopenshell.api.geometry
@@ -27,9 +25,11 @@ import ifcopenshell.api.unit
import ifcopenshell.util.element
import ifcopenshell.util.geolocation
import ifcopenshell.util.placement
import ifcpatch
import numpy as np
import ifcpatch
import test.bootstrap
class TestSetFalseOrigin(test.bootstrap.IFC4):
def test_run(self):
+4 -7
View File
@@ -16,19 +16,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.representation
import ifcpatch
import ifcopenshell
import ifcopenshell.api.context
import ifcopenshell.api.geometry
import ifcopenshell.api.profile
import ifcopenshell.api.root
import ifcopenshell.api.unit
import test.bootstrap
import ifcopenshell.geom
import ifcopenshell.util.element
import ifcopenshell.util.representation
from ifcopenshell.util.shape_builder import ShapeBuilder
import ifcpatch
import test.bootstrap
class TestTesselateElements(test.bootstrap.IFC4):
def test_run(self):
+2 -2
View File
@@ -16,11 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcpatch
import ifcopenshell
import ifcopenshell.api.pset
import ifcopenshell.geom
import ifcopenshell.util.element
import ifcpatch
import test.bootstrap
+7 -5
View File
@@ -17,10 +17,12 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import tempfile
import ifcopenshell.api.root
import ifcpatch
from pathlib import Path
import ifcopenshell.api.root
import ifcpatch
class Test:
def test_parsing_docs(self):
@@ -50,10 +52,10 @@ class Test:
assert output.by_type("IfcProject")[0].GlobalId == project.GlobalId
assert output.by_type("IfcWall")[0].GlobalId == wall.GlobalId
output_path = Path(tempfile.mktemp())
output_path = Path(tempfile.mkstemp()[1])
try:
assert not output_path.exists()
assert output_path.stat().st_size == 0
ifcpatch.write(patcher.get_output(), output_path)
assert output_path.exists()
assert output_path.stat().st_size != 0
finally:
output_path.unlink()