diff --git a/src/blenderbim/blenderbim/tool/sequence.py b/src/blenderbim/blenderbim/tool/sequence.py index 1bc7bfa736..7014bd7d8e 100644 --- a/src/blenderbim/blenderbim/tool/sequence.py +++ b/src/blenderbim/blenderbim/tool/sequence.py @@ -15,18 +15,21 @@ # # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . -from dateutil import parser + import bpy import re import isodate -from datetime import datetime import ifcopenshell +import ifcopenshell.util.sequence import json import blenderbim.core.tool import blenderbim.tool as tool import blenderbim.bim.helper import blenderbim.bim.module.sequence.helper as helper +from dateutil import parser +from datetime import datetime + class Sequence(blenderbim.core.tool.Sequence): @classmethod @@ -469,6 +472,7 @@ class Sequence(blenderbim.core.tool.Sequence): new = props.nested_task_outputs.add() new.ifc_definition_id = output.id() new.name = output.Name or "Unnamed" + @classmethod def get_highlighted_task(cls): props = bpy.context.scene.BIMWorkScheduleProperties @@ -482,12 +486,17 @@ class Sequence(blenderbim.core.tool.Sequence): @classmethod def get_task_outputs(cls, task): return [rel.RelatingProduct for rel in task.HasAssignments if rel.is_a("IfcRelAssignsToProduct")] + @classmethod def get_nested_tasks_outputs(cls, tasks): outputs = [] for subtask in tasks: - [outputs.append(rel.RelatingProduct) for rel in subtask.HasAssignments if rel.is_a("IfcRelAssignsToProduct")] - return outputs + [ + outputs.append(rel.RelatingProduct) + for rel in subtask.HasAssignments + if rel.is_a("IfcRelAssignsToProduct") + ] + return outputs @classmethod def get_task_resources(cls, task): diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index 0d30495a44..03f78e3928 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -112,23 +112,20 @@ class Usecase: # own "Company Standard" property set templates. Notice how we # prefix our property set with "Foo_", if our company name was "Foo" # this would make sense. - pset_template = ifcopenshell.api.run("pset_template.add_pset_template", model) - ifcopenshell.api.run("pset_template.edit_pset_template", model, attributes={"Name": "Foo_Bar"}) + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar") # Let's imagine we want all model authors to specify two properties, # one being a length measurement and another being a boolean. - prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=pset_template) - ifcopenshell.api.run("pset_template.edit_prop_template", model, - prop_template=prop1, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"}) - prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=pset_template) - ifcopenshell.api.run("pset_template.edit_prop_template", model, - prop_template=prop2, attributes={"Name": "DemoB", "PrimaryMeasureType": "IfcBoolean"}) + prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, + pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure") + prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, + pset_template=template, name="DemoB", primary_measure_type="IfcBoolean") # Now we can use our property set template to add our properties, # and the data types will always match our template. pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar") ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=pset_template) + pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template) # Here's a third scenario where we want to add arbitrary properties # that are not standardised by anything, not even our own custom diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py index f6423df46b..8d415ac173 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py @@ -78,25 +78,19 @@ class Usecase: # prefix our property set with "Foo_", if our company name was "Foo" # this would make sense. In this example, we say that our template # only applies to walls and is for quantities. - pset_template = ifcopenshell.api.run("pset_template.add_pset_template", model) - ifcopenshell.api.run("pset_template.edit_pset_template", model, - attributes={ - "Name": "Foo_Wall", "TemplateType": "QTO_OCCURRENCEDRIVEN", "ApplicableEntity": "IfcWall" - }) + template = ifcopenshell.api.run("pset_template.add_pset_template", model, + name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall") # Let's imagine we want all model authors to specify a length # measurement for the portion of a wall that is overhanging. - prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=pset_template) - ifcopenshell.api.run("pset_template.edit_prop_template", model, - prop_template=prop, attributes={ - "Name": "OverhangLength", "TemplateType": "Q_LENGTH", "PrimaryMeasureType": "IfcLengthMeasure" - }) + prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure") # Now we can use our property set template to add our properties, # and the data types will always match our template. qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall") ifcopenshell.api.run("pset.edit_qto", model, - qto=qto, properties={"OverhangLength": 42.3}, pset_template=pset_template) + qto=qto, properties={"OverhangLength": 42.3}, pset_template=template) # Here's a third scenario where we want to add arbitrary quantities # that are not standardised by anything, not even our own custom @@ -112,7 +106,6 @@ class Usecase: # if possible. So this will still be a length measure. ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3}) """ - self.file = file self.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template} diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py index 10061e2ec3..8fa7939740 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py @@ -20,22 +20,89 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__( + self, + file, + pset_template=None, + name="NewProperty", + description=None, + template_type="P_SINGLEVALUE", + primary_measure_type="IfcLabel", + ): + """Adds new property templates to a property set template + + Assuming you first have a property set template, this allows you to add + templates for properties within that property set. A property template + lets you specify the name, description, and data type of a property. + When the template is provided to a model author, this gives them clear + instructions about the intention of the property and exactly which data + type to use. + + Types of properties and quantities include: + + * P_SINGLEVALUE - a single value, the most common type of property. + * P_ENUMERATEDVALUE - the property value may one or more values chosen + from a preset list of values. + * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value. + * P_LISTVALUE - the property has a list of values. + * P_TABLEVALUE - the property has a table of values. + * P_REFERENCEVALUE - the property is a parametric reference to another + value. This is only for advanced users. + * Q_LENGTH - the quantity is a length. + * Q_AREA - the quantity is an area. + * Q_VOLUME - the quantity is a volume. + * Q_COUNT - the quantity is counting a item. + * Q_WEIGHT - the quantity is a weight. + * Q_TIME - the quantity is a time duration. + + :param pset_template: The property set template to add the property + template to. + :type pset_template: ifcopenshell.entity_instance.entity_instance + :param name: The name of the property + :type name: str,optional + :param description: A few words describing what the property stores. + :type description: str,optional + :param primary_measure_type: The data type of the property. Consult the + IFC documentation for the full list of data types. + :param primary_measure_type: str,optional + :return: The newly created IfcSimplePropertyTemplate. + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # Create a simple template that may be applied to all types + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + + # Here's one example property + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="HighVoltage", description="Whether there is a risk of high voltage.", + primary_measure_type="IfcBoolean") + + # Here's another + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="ChemicalType", description="The class of chemical spillage.", + primary_measure_type="IfcLabel") + """ self.file = file - self.settings = {"pset_template": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = { + "pset_template": pset_template, + "name": name, + "description": description, + "template_type": template_type, + "primary_measure_type": primary_measure_type, + } def execute(self): prop_template = self.file.create_entity( "IfcSimplePropertyTemplate", **{ "GlobalId": ifcopenshell.guid.new(), - "Name": "NewProperty", - "PrimaryMeasureType": "IfcLabel", - "TemplateType": "P_SINGLEVALUE", + "Name": self.settings["name"], + "Description": self.settings["description"], + "PrimaryMeasureType": self.settings["primary_measure_type"], + "TemplateType": self.settings["template_type"], "AccessState": "READWRITE", - "Enumerators": None + "Enumerators": None, } ) has_property_templates = list(self.settings["pset_template"].HasPropertyTemplates or []) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py index e54a9b8771..c22fefc6a1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py @@ -20,19 +20,88 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__( + self, file, name="New_Pset", template_type="PSET_TYPEDRIVENOVERRIDE", applicable_entity="IfcTypeObject" + ): + """Adds a new property set template + + This creates a new template for property sets. A template defines what + the name of the property set should be, what properties it can have, + what entities (e.g. wall) the property set can be assigned to, whether + it should be assigned at a type or occurrence level, the data types of + the properties, and descriptions of the properties. This template can + then be used as a project, company, or local government standard. + + buildingSMART itself ships a catalogue of property sets using these + templates, ensuring that internationally common properties (e.g. fire + rating of a wall) are all implemented exactly the same way across all + vendors and projects. Naturally, not everything can be standardised + internationally, so this allows you to create your own templates. + + You may either create a property template to store properties, or a + quantity template to store quantities. For convenience, we will always + call them "property templates" as they are conceptually very similar. + + This function only creates a template for the property set, not the + properties themselves within the property set. At this level, you are + allowed to define the name of the property set, whether it is type or + occurrence based, and which entities it applies to. + + See the documentation for IfcPropertySetTemplate for instructions on + the types of template type and list of applicable entities. + + The types of property set templates are: + + * PSET_TYPEDRIVENONLY - assigned only to types + * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both, + the occurrence overrides the type. + * PSET_OCCURRENCEDRIVEN - assigned to occurrences only. + * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is + only recommended for advanced users. + * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities. + * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for + quantities. If both, the occurrence overrides the type. + * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for + quantities. + + By default, this creates a template that can be applied to types, but + overridden by occurrences, and is applicable to everything. + + :param name: The name of the property set + :type name: str,optional + :param template_type: Choose from one of PSET_TYPEDRIVENONLY, + PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN, + PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE, + QTO_OCCURRENCEDRIVEN, NOTDEFINED + :type template_type: str,optional + :param applicable_entity: The entity that this template is allowed to be + applied to. For example, IfcWall means that the property set may be + assigned to walls only. IfcTypeObject, the default, means that the + property set may be assigned to any type. + :type applicable_entity: str,optional + :return: The newly created IfcPropertySetTemplate + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # Create a simple template that may be applied to all types + ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + + # Note that we aren't finished yet. Our property set template + # doesn't have any properties in it. Let's add a minimum of one + # property. + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="HighVoltage", description="Whether there is a risk of high voltage.", + primary_measure_type="IfcBoolean") + """ self.file = file - self.settings = {} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity} def execute(self): return self.file.create_entity( "IfcPropertySetTemplate", - **{ - "GlobalId": ifcopenshell.guid.new(), - "Name": "New_Pset", - "TemplateType": "PSET_TYPEDRIVENONLY", - "ApplicableEntity": "IfcTypeObject", - } + GlobalId=ifcopenshell.guid.new(), + Name=self.settings["name"], + TemplateType=self.settings["template_type"], + ApplicableEntity=self.settings["applicable_entity"], ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py index b1d26b124b..f924e9ca8d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py @@ -18,11 +18,32 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, prop_template=None, attributes=None): + """Edits the attributes of an IfcSimplePropertyTemplate + + For more information about the attributes and data types of an + IfcSimplePropertyTemplate, consult the IFC documentation. + + :param prop_template: The IfcSimplePropertyTemplate entity you want to edit + :type prop_template: ifcopenshell.entity_instance.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example:: + + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + + # Here's a property with just default values. + prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + + # Let's edit it to give the actual values we need. + ifcopenshell.api.run("pset_template.edit_prop_template", model, + prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"}) + """ self.file = file - self.settings = {"prop_template": None, "attributes": {}} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"prop_template": prop_template, "attributes": attributes or {}} def execute(self): for name, value in self.settings["attributes"].items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py index 2d2dd873e4..7bff8a7ae1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py @@ -18,11 +18,30 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, pset_template=None, attributes=None): + """Edits the attributes of an IfcPropertySetTemplate + + For more information about the attributes and data types of an + IfcPropertySetTemplate, consult the IFC documentation. + + :param pset_template: The IfcPropertySetTemplate entity you want to edit + :type pset_template: ifcopenshell.entity_instance.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example:: + + # Whoops! We named it with a buildingSMART reserved "Pset_" prefix! + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors") + + # Let's fix it to prefix with our company code instead. + ifcopenshell.api.run("pset_template.edit_pset_template", model, + pset_template=template, attributes={"Name": "ABC_RiskFactors"}) + """ self.file = file - self.settings = {"pset_template": None, "attributes": {}} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"pset_template": pset_template, "attributes": attributes or {}} def execute(self): for name, value in self.settings["attributes"].items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py index 6784d8dc39..d23dba7ef2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py @@ -20,11 +20,31 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, prop_template=None): + """Removes a property template + + Note that a property set template should always have at least one + property template to be valid, so take care when removing property + templates. + + :param prop_template: The IfcSimplePropertyTemplate to remove. + :type prop_template: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + + # Here's two propertes with just default values. + prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + + # Let's remove the second one. + ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2) + """ self.file = file - self.settings = {"prop_template": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"prop_template": prop_template} def execute(self): for inverse in self.file.get_inverse(self.settings["prop_template"]): diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py index 14c9168524..6309d6e841 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py @@ -20,9 +20,27 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, pset_template=None): + """Removes a property set template + + All property templates within the property set template are also removed + along with it. + + :param pset_template: The IfcPropertySetTemplate to remove. + :type pset_template: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # Create a template. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + + # Let's remove the template. + ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template) + """ self.file = file - self.settings = {"pset_template": None} + self.settings = {"pset_template": pset_template} for key, value in settings.items(): self.settings[key] = value