From abfb56350d2de439bd6b9011684c6919a5568aad Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 9 Sep 2025 11:14:57 +0500 Subject: [PATCH] control.assign_control to support batching --- src/bonsai/bonsai/core/cost.py | 10 ++--- src/bonsai/bonsai/core/sequence.py | 2 +- src/ifc4d/ifc4d/common.py | 3 +- src/ifc4d/ifc4d/msp2ifc.py | 2 +- src/ifc5d/ifc5d/csv2ifc.py | 5 ++- .../ifcopenshell/api/__init__.py | 22 +++++++++- .../api/control/assign_control.py | 41 ++++++++++++------- .../api/control/unassign_control.py | 2 +- .../ifcopenshell/api/cost/add_cost_item.py | 2 +- .../api/cost/add_cost_item_quantity.py | 2 +- .../api/cost/assign_cost_item_quantity.py | 2 +- .../calculate_cost_item_resource_value.py | 4 +- .../api/cost/copy_cost_schedule.py | 2 +- .../ifcopenshell/api/sequence/add_task.py | 2 +- .../api/sequence/add_work_calendar.py | 2 +- .../api/sequence/copy_work_schedule.py | 2 +- .../api/sequence/create_baseline.py | 2 +- .../test/api/control/test_assign_control.py | 16 ++++++-- .../test/api/control/test_unassign_control.py | 8 ++-- .../api/cost/test_add_cost_item_quantity.py | 2 +- .../test/api/sequence/test_edit_task_time.py | 4 +- .../api/sequence/test_remove_work_calendar.py | 2 +- src/ifcopenshell-python/test/api/test_api.py | 15 ++++++- 23 files changed, 104 insertions(+), 50 deletions(-) diff --git a/src/bonsai/bonsai/core/cost.py b/src/bonsai/bonsai/core/cost.py index 1243123e82..adca7e3399 100644 --- a/src/bonsai/bonsai/core/cost.py +++ b/src/bonsai/bonsai/core/cost.py @@ -135,10 +135,8 @@ def assign_cost_item_type( List of found product types. """ product_types = list(spatial.get_selected_product_types()) - rels = [ - ifc.run("control.assign_control", relating_control=cost_item, related_object=product_type) - for product_type in product_types - ] + ifc.run("control.assign_control", relating_control=cost_item, related_objects=product_types) + cost.load_cost_item_types(cost_item) return product_types @@ -213,10 +211,10 @@ def assign_cost_value( ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate) existing_cost_rate = cost.get_assigned_rate_cost_item(cost_item) if existing_cost_rate is None: - ifc.run("control.assign_control", relating_control=cost_rate, related_object=cost_item) + ifc.run("control.assign_control", relating_control=cost_rate, related_objects=[cost_item]) else: ifc.run("control.unassign_control", relating_control=existing_cost_rate, related_object=cost_item) - ifc.run("control.assign_control", relating_control=cost_rate, related_object=cost_item) + ifc.run("control.assign_control", relating_control=cost_rate, related_objects=[cost_item]) def load_schedule_of_rates(cost: type[tool.Cost], schedule_of_rates: ifcopenshell.entity_instance) -> None: diff --git a/src/bonsai/bonsai/core/sequence.py b/src/bonsai/bonsai/core/sequence.py index 63cf984078..771bc259c4 100644 --- a/src/bonsai/bonsai/core/sequence.py +++ b/src/bonsai/bonsai/core/sequence.py @@ -422,7 +422,7 @@ def edit_task_calendar( task: ifcopenshell.entity_instance, work_calendar: ifcopenshell.entity_instance, ) -> None: - ifc.run("control.assign_control", relating_control=work_calendar, related_object=task) + ifc.run("control.assign_control", relating_control=work_calendar, related_objects=[task]) ifc.run("sequence.cascade_schedule", task=task) sequence.load_task_properties() diff --git a/src/ifc4d/ifc4d/common.py b/src/ifc4d/ifc4d/common.py index 2bb117df9c..d13840267a 100644 --- a/src/ifc4d/ifc4d/common.py +++ b/src/ifc4d/ifc4d/common.py @@ -261,6 +261,7 @@ class ScheduleIfcGenerator: wbs: Union[WBSEntry, None], work_schedule: Union[ifcopenshell.entity_instance, None], ) -> None: + assert self.file activity["ifc"] = ifcopenshell.api.sequence.add_task( self.file, work_schedule=None if wbs else work_schedule, @@ -283,7 +284,7 @@ class ScheduleIfcGenerator: ifcopenshell.api.control.assign_control( self.file, relating_control=calendar["ifc"], - related_object=activity["ifc"], + related_objects=[activity["ifc"]], ) ifcopenshell.api.sequence.edit_task_time( self.file, diff --git a/src/ifc4d/ifc4d/msp2ifc.py b/src/ifc4d/ifc4d/msp2ifc.py index 7ebcb6558f..2285a1a1ea 100644 --- a/src/ifc4d/ifc4d/msp2ifc.py +++ b/src/ifc4d/ifc4d/msp2ifc.py @@ -263,7 +263,7 @@ class MSP2Ifc: ifcopenshell.api.control.assign_control( self.file, relating_control=calendar, - related_object=task["ifc"], + related_objects=[task["ifc"]], ) ifcopenshell.api.sequence.edit_task( diff --git a/src/ifc5d/ifc5d/csv2ifc.py b/src/ifc5d/ifc5d/csv2ifc.py index 3708763aa8..35a31df751 100644 --- a/src/ifc5d/ifc5d/csv2ifc.py +++ b/src/ifc5d/ifc5d/csv2ifc.py @@ -20,6 +20,7 @@ from __future__ import annotations import csv import ifcopenshell import ifcopenshell.api +import ifcopenshell.api.control import ifcopenshell.api.cost import ifcopenshell.api.root import ifcopenshell.util.unit @@ -353,14 +354,14 @@ class Csv2Ifc: existing_cost_rate = assignment.RelatingControl if existing_cost_rate is None: ifcopenshell.api.control.assign_control( - self.file, relating_control=rate_cost_item, related_object=cost_item["ifc"] + self.file, relating_control=rate_cost_item, related_objects=[cost_item["ifc"]] ) else: ifcopenshell.api.control.unassign_control( self.file, relating_control=existing_cost_rate, related_object=cost_item["ifc"] ) ifcopenshell.api.control.assign_control( - self.file, relating_control=rate_cost_item, related_object=cost_item["ifc"] + self.file, relating_control=rate_cost_item, related_objects=[cost_item["ifc"]] ) else: print(f"No cost item found with RateID={cost_rate['RateID']}") diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index d6e0066af9..0eb95b2c65 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -53,6 +53,22 @@ pre_listeners: dict[str, dict] = {} post_listeners: dict[str, dict] = {} +def batching_argument_deprecation( + usecase_path: str, settings: dict, prev_argument: str, new_argument: str, replace_usecase: Optional[str] = None +) -> tuple[str, dict]: + if replace_usecase is not None: + print(f"WARNING. `{usecase_path}` api method is deprecated and should be replaced with `{replace_usecase}`.") + + if prev_argument in settings: + print( + f"WARNING. `{prev_argument}` argument is deprecated for API method " + f'"{usecase_path}" and should be replaced with `{new_argument}`.' + ) + settings = settings | {new_argument: [settings[prev_argument]]} + settings.pop(prev_argument) + return (replace_usecase or usecase_path, settings) + + def renamed_arguments_deprecation( usecase_path: str, settings: dict, arguments_remapped: dict[str, str] ) -> tuple[str, dict]: @@ -71,7 +87,11 @@ def renamed_arguments_deprecation( # "group.add_group": partial( # renamed_arguments_deprecation, arguments_remapped={"Name": "name", "Description": "description"} # ), -ARGUMENTS_DEPRECATION: dict[str, Callable[[str, dict[str, Any]], tuple[str, dict[str, Any]]]] = {} +ARGUMENTS_DEPRECATION: dict[str, Callable[[str, dict[str, Any]], tuple[str, dict[str, Any]]]] = { + "control.assign_control": partial( + batching_argument_deprecation, prev_argument="related_object", new_argument="related_objects" + ), +} CACHED_USECASE_CLASSES: dict[str, Callable] = {} diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py index 3d70fec7d2..cd7c398a54 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py @@ -25,9 +25,9 @@ from typing import Union def assign_control( file: ifcopenshell.file, relating_control: ifcopenshell.entity_instance, - related_object: ifcopenshell.entity_instance, + related_objects: list[ifcopenshell.entity_instance], ) -> Union[ifcopenshell.entity_instance, None]: - """Assigns a planning control or constraint to an object + """Assigns a planning control or constraint to a list of objects. IFC can describe concepts that control other objects. For example, a planning calendar controls the availability of working days for @@ -42,7 +42,7 @@ def assign_control( :param relating_control: The IfcControl entity that is creating the control or constraint - :param related_object: The IfcObjectDefinition that is being controlled + :param related_objects: The list of IfcObjectDefinition that is being controlled :return: The newly created IfcRelAssignsToControl. If relationship already existed before and wasn't changed then returns None. @@ -59,7 +59,7 @@ def assign_control( # All subtasks will inherit this calendar, so assigning a single # calendar to the root task effectively defines a "default" calendar ifcopenshell.api.control.assign_control(model, - relating_control=calendar, related_object=task) + relating_control=calendar, related_objects=[task]) # Another common example might be relating a cost item and a product wall = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWall") @@ -67,22 +67,33 @@ def assign_control( cost_item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule) ifcopenshell.api.control.assign_control(model, - relating_control=cost_item, related_object=wall) + relating_control=cost_item, related_objects=[wall]) """ - if related_object.HasAssignments: - for assignment in related_object.HasAssignments: - if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == relating_control: - return + # Filter out already assigned objects. + related_objects_set = set(related_objects) + objects_to_assign: set[ifcopenshell.entity_instance] = set() + + control_assignments = set(relating_control.Controls) + if control_assignments: + for obj in related_objects_set: + existing_assignment = next((a for a in obj.HasAssignments if a in control_assignments), None) + # Skip objects already assigned to this control. + if existing_assignment: + continue + objects_to_assign.add(obj) + else: + objects_to_assign = related_objects_set + + if not objects_to_assign: + return None controls: Union[ifcopenshell.entity_instance, None] controls = next(iter(relating_control.Controls), None) if controls: - if related_object in controls.RelatedObjects: - return - related_objects = set(controls.RelatedObjects) - related_objects.add(related_object) - controls.RelatedObjects = list(related_objects) + related_objects_new: list[ifcopenshell.entity_instance] = list(controls.RelatedObjects) + related_objects_new.extend(objects_to_assign) + controls.RelatedObjects = list(related_objects_new) ifcopenshell.api.owner.update_owner_history(file, element=controls) else: controls = file.create_entity( @@ -90,7 +101,7 @@ def assign_control( **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.owner.create_owner_history(file), - "RelatedObjects": [related_object], + "RelatedObjects": list(objects_to_assign), "RelatingControl": relating_control, }, ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py index a7ba9fb045..0672b49f59 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py @@ -45,7 +45,7 @@ def unassign_control( cost_item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule) ifcopenshell.api.control.assign_control(model, - relating_control=cost_item, related_object=wall) + relating_control=cost_item, related_objects=[wall]) # And now let's change our mind ifcopenshell.api.control.unassign_control(model, diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py index 86881265af..70c0cfc06c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py @@ -59,7 +59,7 @@ def add_cost_item( cost_item_ = ifcopenshell.api.root.create_entity(file, ifc_class="IfcCostItem") if cost_schedule: - ifcopenshell.api.control.assign_control(file, cost_schedule, cost_item_) + ifcopenshell.api.control.assign_control(file, cost_schedule, [cost_item_]) elif cost_item: ifcopenshell.api.nest.assign_object(file, related_objects=[cost_item_], relating_object=cost_item) return cost_item_ diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py index 58f3ed343a..5e8fcb0b34 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py @@ -66,7 +66,7 @@ def add_cost_item_quantity( schedule = ifcopenshell.api.cost.add_cost_schedule(model) item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule) ifcopenshell.api.control.assign_control(model, - relating_control=item, related_object=chair) + relating_control=item, related_objects=[chair]) # Let's assume we want to count the amount of chairs to calculate our cost item # Because this is an IfcQuantityCount the count will be automatically set to "1" chair diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index 5970c08d6d..2db09c45f6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -122,7 +122,7 @@ class Usecase: ) -> ifcopenshell.entity_instance: return ifcopenshell.api.control.assign_control( self.file, - related_object=related_object, + related_objects=[related_object], relating_control=cost_item, ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py index a1a86b36e5..fc53e2ff3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py @@ -57,7 +57,7 @@ def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcop concrete = ifcopenshell.api.resource.add_resource(model, ifc_class="IfcConstructionMaterialResource", parent_resource=crew) ifcopenshell.api.control.assign_control(model, - relating_control=item, related_object=concrete) + relating_control=item, related_objects=[concrete]) # ... which has a unit price of 42.0 per m3 value = ifcopenshell.api.cost.add_cost_value(model, parent=concrete) ifcopenshell.api.cost.edit_cost_value(model, cost_value=value, @@ -72,7 +72,7 @@ def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcop equipment = ifcopenshell.api.resource.add_resource(model, ifc_class="IfcConstructionEquipmentResource", parent_resource=crew) ifcopenshell.api.control.assign_control(model, - relating_control=item, related_object=equipment) + relating_control=item, related_objects=[equipment]) # ... with a fixed price of 50,000 value = ifcopenshell.api.cost.add_cost_value(model, parent=concrete) ifcopenshell.api.cost.edit_cost_value(model, cost_value=value, diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_schedule.py index b23c1d5694..3101e820c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_schedule.py @@ -45,5 +45,5 @@ def copy_cost_schedule( if isinstance(duplicated_cost_item, list): # All other nested items are not connected to the cost schedule explicitly. duplicated_cost_item = duplicated_cost_item[0] - ifcopenshell.api.control.assign_control(file, new_schedule, duplicated_cost_item) + ifcopenshell.api.control.assign_control(file, new_schedule, [duplicated_cost_item]) return new_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py index 340709594c..36d7d452b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py @@ -138,7 +138,7 @@ def add_task( task.Identification = identification task.IsMilestone = False if work_schedule: - ifcopenshell.api.control.assign_control(file, work_schedule, task) + ifcopenshell.api.control.assign_control(file, work_schedule, [task]) elif parent_task: rel = ifcopenshell.api.nest.assign_object( file, diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py index 254188dcd8..c530a766de 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py @@ -75,7 +75,7 @@ def add_work_calendar( # We associate the calendar with the construction root task. All # subtasks underneath the construction work task will also inherit # this calendar by default (though you can override them). - ifcopenshell.api.control.assign_control(model, relating_control=calendar, related_object=task) + ifcopenshell.api.control.assign_control(model, relating_control=calendar, related_objects=[task]) """ work_calendar = ifcopenshell.api.root.create_entity( file, diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/copy_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/copy_work_schedule.py index 30b1123ac7..44e19c0803 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/copy_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/copy_work_schedule.py @@ -47,5 +47,5 @@ def copy_work_schedule( duplicated_tasks = ifcopenshell.api.sequence.duplicate_task(file, task)[1] # All other nested items are not connected to the work schedule explicitly. duplicated_task = duplicated_tasks[0] - ifcopenshell.api.control.assign_control(file, new_schedule, duplicated_task) + ifcopenshell.api.control.assign_control(file, new_schedule, [duplicated_task]) return new_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py index 8b9571aa4d..00764a4f7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py @@ -79,7 +79,7 @@ class Usecase: assert isinstance(res, list) current, duplicate = res ifcopenshell.api.control.assign_control( - self.file, relating_control=baseline_work_schedule, related_object=duplicate[0] + self.file, relating_control=baseline_work_schedule, related_objects=[duplicate[0]] ) for i, task in enumerate(current): self.create_baseline_reference(task, duplicate[i]) diff --git a/src/ifcopenshell-python/test/api/control/test_assign_control.py b/src/ifcopenshell-python/test/api/control/test_assign_control.py index a0afc2c74a..75c6c0e8d3 100644 --- a/src/ifcopenshell-python/test/api/control/test_assign_control.py +++ b/src/ifcopenshell-python/test/api/control/test_assign_control.py @@ -27,23 +27,33 @@ class TestAssignControl(test.bootstrap.IFC4): control = ifcopenshell.api.cost.add_cost_schedule(self.file) # simple assignment - relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_object=wall) + relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=[wall]) + assert relation assert len(self.file.by_type("IfcRelAssignsToControl")) == 1 assert relation.RelatingControl == control assert relation.RelatedObjects == (wall,) # trying to establish existing relationship - relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_object=wall) + relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=[wall]) assert relation is None # assigning same control to another object wall1 = self.file.createIfcWall() - relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_object=wall1) + relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=[wall1]) assert relation is not None assert len(self.file.by_type("IfcRelAssignsToControl")) == 1 assert relation.RelatingControl == control assert set(relation.RelatedObjects) == set((wall, wall1)) + def test_batch_assignment(self): + walls = [self.file.createIfcWall() for _ in range(5)] + control = ifcopenshell.api.cost.add_cost_schedule(self.file) + relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=walls) + assert relation + assert len(self.file.by_type("IfcRelAssignsToControl")) == 1 + assert relation.RelatingControl == control + assert set(relation.RelatedObjects) == set(walls) + class TestAssignControlIFC2X3(test.bootstrap.IFC2X3, TestAssignControl): pass diff --git a/src/ifcopenshell-python/test/api/control/test_unassign_control.py b/src/ifcopenshell-python/test/api/control/test_unassign_control.py index 76302c06b5..d730d6d261 100644 --- a/src/ifcopenshell-python/test/api/control/test_unassign_control.py +++ b/src/ifcopenshell-python/test/api/control/test_unassign_control.py @@ -27,14 +27,16 @@ class TestUnassignControl(test.bootstrap.IFC4): control = ifcopenshell.api.cost.add_cost_schedule(self.file) # assign and unassign - relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_object=wall) + relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=[wall]) + assert relation ifcopenshell.api.control.unassign_control(self.file, relating_control=control, related_object=wall) assert len(self.file.by_type("IfcRelAssignsToControl")) == 0 # 1 control 2 related objects wall1 = self.file.createIfcWall() - relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_object=wall) - ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_object=wall1) + relation = ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=[wall]) + assert relation + ifcopenshell.api.control.assign_control(self.file, relating_control=control, related_objects=[wall1]) ifcopenshell.api.control.unassign_control(self.file, relating_control=control, related_object=wall1) assert len(self.file.by_type("IfcRelAssignsToControl")) == 1 assert relation.RelatedObjects == (wall,) diff --git a/src/ifcopenshell-python/test/api/cost/test_add_cost_item_quantity.py b/src/ifcopenshell-python/test/api/cost/test_add_cost_item_quantity.py index c954ec169d..e5ffe38ce0 100644 --- a/src/ifcopenshell-python/test/api/cost/test_add_cost_item_quantity.py +++ b/src/ifcopenshell-python/test/api/cost/test_add_cost_item_quantity.py @@ -30,7 +30,7 @@ class TestAddCostItemQuantity(test.bootstrap.IFC4): schedule = ifcopenshell.api.cost.add_cost_schedule(self.file) item = ifcopenshell.api.cost.add_cost_item(self.file, cost_schedule=schedule) wall = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall") - ifcopenshell.api.control.assign_control(self.file, relating_control=item, related_object=wall) + ifcopenshell.api.control.assign_control(self.file, relating_control=item, related_objects=[wall]) quantities = [] for quantity_type in quantity_types: diff --git a/src/ifcopenshell-python/test/api/sequence/test_edit_task_time.py b/src/ifcopenshell-python/test/api/sequence/test_edit_task_time.py index b4e138f16b..dcaf082bf8 100644 --- a/src/ifcopenshell-python/test/api/sequence/test_edit_task_time.py +++ b/src/ifcopenshell-python/test/api/sequence/test_edit_task_time.py @@ -95,7 +95,7 @@ class TestEditTaskTime(test.bootstrap.IFC4): ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") calendar = ifcopenshell.api.sequence.add_work_calendar(self.file) task = self.file.createIfcTask() - ifcopenshell.api.control.assign_control(self.file, relating_control=calendar, related_object=task) + ifcopenshell.api.control.assign_control(self.file, relating_control=calendar, related_objects=[task]) task_time = ifcopenshell.api.sequence.add_task_time(self.file, task=task) ifcopenshell.api.sequence.edit_task_time( self.file, @@ -195,7 +195,7 @@ class TestEditTaskTime(test.bootstrap.IFC4): ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") calendar = ifcopenshell.api.sequence.add_work_calendar(self.file) task = self.file.createIfcTask() - ifcopenshell.api.control.assign_control(self.file, relating_control=calendar, related_object=task) + ifcopenshell.api.control.assign_control(self.file, relating_control=calendar, related_objects=[task]) task_time = ifcopenshell.api.sequence.add_task_time(self.file, task=task) ifcopenshell.api.sequence.edit_task_time( self.file, diff --git a/src/ifcopenshell-python/test/api/sequence/test_remove_work_calendar.py b/src/ifcopenshell-python/test/api/sequence/test_remove_work_calendar.py index 4abb5d111a..99b0a035dc 100644 --- a/src/ifcopenshell-python/test/api/sequence/test_remove_work_calendar.py +++ b/src/ifcopenshell-python/test/api/sequence/test_remove_work_calendar.py @@ -38,7 +38,7 @@ class TestRemoveWorkCalendar(test.bootstrap.IFC4): # Assign tasks. task = ifcopenshell.api.sequence.add_task(self.file) - ifcopenshell.api.control.assign_control(self.file, work_calendar, task) + ifcopenshell.api.control.assign_control(self.file, work_calendar, [task]) ifcopenshell.api.sequence.remove_work_calendar(self.file, work_calendar) diff --git a/src/ifcopenshell-python/test/api/test_api.py b/src/ifcopenshell-python/test/api/test_api.py index 382db08d53..3c43d8e54f 100644 --- a/src/ifcopenshell-python/test/api/test_api.py +++ b/src/ifcopenshell-python/test/api/test_api.py @@ -16,19 +16,30 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . +import ifcopenshell.api.cost +import ifcopenshell.api.root import test.bootstrap import ifcopenshell.api +import ifcopenshell.api.control +import ifcopenshell.api.sequence +import ifcopenshell.util.element from datetime import datetime from typing import Union def deprecation_check(test): def new_test(self): - assert datetime.now().date() < datetime(2024, 8, 1).date(), "API arguments are completely deprecated" + assert datetime.now().date() < datetime(2026, 1, 9).date(), "API arguments are completely deprecated" test(self) return new_test class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4): - pass + @deprecation_check + def test_assigning_control(self): + model = self.file + element = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWall") + control = ifcopenshell.api.cost.add_cost_schedule(model) + ifcopenshell.api.control.assign_control(model, relating_control=control, related_objects=[element]) + assert list(ifcopenshell.util.element.get_controls(element)) == [control]