Generate functions for all API usecases for better static code features. See #2693.

This commit is contained in:
Dion Moult
2024-05-06 14:35:39 +10:00
parent 10f894e2ea
commit d11ec67129
330 changed files with 13283 additions and 13751 deletions
@@ -15,3 +15,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/>.
from .add_library import add_library
from .add_reference import add_reference
from .assign_reference import assign_reference
from .edit_library import edit_library
from .edit_reference import edit_reference
from .remove_library import remove_library
from .remove_reference import remove_reference
from .unassign_reference import unassign_reference
@@ -21,48 +21,45 @@ import ifcopenshell.util.schema
import ifcopenshell.util.date
class Usecase:
def __init__(self, file, name=None):
"""Adds a new library to the project
def add_library(file, name=None) -> None:
"""Adds a new library to the project
A library is an external data source that is related to the project. It
may be a database, a spreadsheet, an API, or even a stack of papers in a
filing cabinet. This allows IFC data to store relationships to these
external data sources.
A library is an external data source that is related to the project. It
may be a database, a spreadsheet, an API, or even a stack of papers in a
filing cabinet. This allows IFC data to store relationships to these
external data sources.
For example, you may have a list of laser scans of a site stored in an
online platform, which can be queried using an API. Or, you might have a
database of live building sensor data. So long as there is a clear
identifier you can use to link the two datasets together, you can create
a relationship.
For example, you may have a list of laser scans of a site stored in an
online platform, which can be queried using an API. Or, you might have a
database of live building sensor data. So long as there is a clear
identifier you can use to link the two datasets together, you can create
a relationship.
Note that IFC does not store any instructions on how to access the
library. It does not specify whether a HTTP request or database
connection needs to be made or what protocol the library operates with.
Until this is fleshed out further, it is the users responsibility to
name the libraries consistently and use appropriate identifiers. For
example, if you are linking IFC data and Brickschema data, use a full
URI for the identifier with no abbreviation (e.g.
'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01').
Note that IFC does not store any instructions on how to access the
library. It does not specify whether a HTTP request or database
connection needs to be made or what protocol the library operates with.
Until this is fleshed out further, it is the users responsibility to
name the libraries consistently and use appropriate identifiers. For
example, if you are linking IFC data and Brickschema data, use a full
URI for the identifier with no abbreviation (e.g.
'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01').
A library will then contain a list of references within that library.
These references will then be related to IFC elements. For example, a
library will represent an external database, and a reference will point
to a particular table and row within that database.
A library will then contain a list of references within that library.
These references will then be related to IFC elements. For example, a
library will represent an external database, and a reference will point
to a particular table and row within that database.
:param name: The name of the library
:type name: str
:return: The newly created IfcLibraryInformation
:rtype: ifcopenshell.entity_instance
:param name: The name of the library
:type name: str
:return: The newly created IfcLibraryInformation
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
ifcopenshell.api.run("library.add_library", model, name="Brickschema")
"""
self.file = file
self.settings = {"name": name}
ifcopenshell.api.run("library.add_library", model, name="Brickschema")
"""
settings = {"name": name}
def execute(self):
return self.file.create_entity("IfcLibraryInformation", Name=self.settings["name"])
return file.create_entity("IfcLibraryInformation", Name=settings["name"])
@@ -19,48 +19,45 @@
import ifcopenshell
class Usecase:
def __init__(self, file: ifcopenshell.file, library: ifcopenshell.entity_instance):
"""Adds a new reference to a library
def add_reference(file: ifcopenshell.file, library: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Adds a new reference to a library
A library represents an external data source, such as a database,
spreadsheet, API, or something else that contains information related to
the IFC project. Within a library, there will be one or more references,
such as reference to a particular table or row in a database, or a sheet
and row or column in a spreadsheet, a URI in a linked data Brickschema
file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP
address in a network, and so on.
A library represents an external data source, such as a database,
spreadsheet, API, or something else that contains information related to
the IFC project. Within a library, there will be one or more references,
such as reference to a particular table or row in a database, or a sheet
and row or column in a spreadsheet, a URI in a linked data Brickschema
file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP
address in a network, and so on.
These references can then be related to IFC elements. You cannot relate
an IFC element directly to a library, it must be related to one of the
library's references.
These references can then be related to IFC elements. You cannot relate
an IFC element directly to a library, it must be related to one of the
library's references.
:param library: The IfcLibraryInformation element to add a reference to
:type library: ifcopenshell.entity_instance
:return: The newly created IfcLibraryReference element
:rtype: ifcopenshell.entity_instance
:param library: The IfcLibraryInformation element to add a reference to
:type library: ifcopenshell.entity_instance
:return: The newly created IfcLibraryReference element
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
self.file = file
self.settings = {
"library": library,
}
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
settings = {
"library": library,
}
def execute(self) -> ifcopenshell.entity_instance:
if self.file.schema == "IFC2X3":
reference = self.file.createIfcLibraryReference()
references = list(self.settings["library"].LibraryReference or [])
references.append(reference)
self.settings["library"].LibraryReference = references
return reference
return self.file.createIfcLibraryReference(ReferencedLibrary=self.settings["library"])
if file.schema == "IFC2X3":
reference = file.createIfcLibraryReference()
references = list(settings["library"].LibraryReference or [])
references.append(reference)
settings["library"].LibraryReference = references
return reference
return file.createIfcLibraryReference(ReferencedLibrary=settings["library"])
@@ -22,82 +22,75 @@ import ifcopenshell.util.element
from typing import Union
class Usecase:
def __init__(
self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
):
"""Associates a list products with a library reference
def assign_reference(
file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
) -> Union[ifcopenshell.entity_instance, None]:
"""Associates a list products with a library reference
A product may be associated with zero, one, or many references across
multiple libraries. See ifcopenshell.api.library.add_reference for more
detail about how references work.
A product may be associated with zero, one, or many references across
multiple libraries. See ifcopenshell.api.library.add_reference for more
detail about how references work.
:param products: The list of IfcProducts you want to associate with the reference
:type products: list[ifcopenshell.entity_instance]
:param reference: The IfcLibraryReference you want the product to be
associated with.
:type reference: ifcopenshell.entity_instance
:return: The IfcRelAssociatesLibrary relationship entity
or `None` if `products` was an empty list or all products were
already assigned to the `reference`.
:rtype: Union[ifcopenshell.entity_instance, None]
:param products: The list of IfcProducts you want to associate with the reference
:type products: list[ifcopenshell.entity_instance]
:param reference: The IfcLibraryReference you want the product to be
associated with.
:type reference: ifcopenshell.entity_instance
:return: The IfcRelAssociatesLibrary relationship entity
or `None` if `products` was an empty list or all products were
already assigned to the `reference`.
:rtype: Union[ifcopenshell.entity_instance, None]
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
# Let's assume we have an AHU in our model.
ahu = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# Let's assume we have an AHU in our model.
ahu = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
"""
self.file = file
self.settings = {
"products": products,
"reference": reference,
}
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
"""
settings = {
"products": products,
"reference": reference,
}
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
products = products - referenced_elements
referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
products: set[ifcopenshell.entity_instance] = set(settings["products"])
products = products - referenced_elements
if not products:
return
if not products:
return
if self.file.schema == "IFC2X3":
rel = next(
(
r
for r in self.file.by_type("IfcRelAssociatesLibrary")
if r.RelatingLibrary == self.settings["reference"]
),
None,
)
else:
rel = next(iter(self.settings["reference"].LibraryRefForObjects), None)
if file.schema == "IFC2X3":
rel = next(
(r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == settings["reference"]),
None,
)
else:
rel = next(iter(settings["reference"].LibraryRefForObjects), None)
if not rel:
return self.file.create_entity(
"IfcRelAssociatesLibrary",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
RelatedObjects=list(products),
RelatingLibrary=self.settings["reference"],
)
if not rel:
return file.create_entity(
"IfcRelAssociatesLibrary",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
RelatedObjects=list(products),
RelatingLibrary=settings["reference"],
)
related_objects = set(rel.RelatedObjects) | products
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
return rel
related_objects = set(rel.RelatedObjects) | products
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
return rel
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, library=None, attributes=None):
"""Edits the attributes of an IfcLibraryInformation
def edit_library(file, library=None, attributes=None) -> None:
"""Edits the attributes of an IfcLibraryInformation
For more information about the attributes and data types of an
IfcLibraryInformation, consult the IFC documentation.
For more information about the attributes and data types of an
IfcLibraryInformation, consult the IFC documentation.
:param library: The IfcLibraryInformation entity you want to edit
:type library: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param library: The IfcLibraryInformation entity you want to edit
:type library: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
ifcopenshell.api.run("library.edit_library", model, library=library,
attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."})
"""
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
ifcopenshell.api.run("library.edit_library", model, library=library,
attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."})
"""
self.file = file
self.settings = {"library": library, "attributes": attributes or {}}
settings = {"library": library, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["library"], name, value)
for name, value in settings["attributes"].items():
setattr(settings["library"], name, value)
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, reference=None, attributes=None):
"""Edits the attributes of an IfcLibraryReference
def edit_reference(file, reference=None, attributes=None) -> None:
"""Edits the attributes of an IfcLibraryReference
For more information about the attributes and data types of an
IfcLibraryReference, consult the IFC documentation.
For more information about the attributes and data types of an
IfcLibraryReference, consult the IFC documentation.
:param reference: The IfcLibraryReference entity you want to edit
:type reference: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param reference: The IfcLibraryReference entity you want to edit
:type reference: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
self.file = file
self.settings = {"reference": reference, "attributes": attributes or {}}
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
settings = {"reference": reference, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["reference"], name, value)
for name, value in settings["attributes"].items():
setattr(settings["reference"], name, value)
@@ -20,35 +20,32 @@ import ifcopenshell
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, library=None):
"""Removes a library
def remove_library(file, library=None) -> None:
"""Removes a library
All references along with their relationships will also be removed. Any
products which have relationships to this library will not be removed.
All references along with their relationships will also be removed. Any
products which have relationships to this library will not be removed.
:param library: The IfcLibraryInformation entity you want to remove
:type library: ifcopenshell.entity_instance
:return: None
:rtype: None
:param library: The IfcLibraryInformation entity you want to remove
:type library: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
ifcopenshell.api.run("library.remove_library", model, library=library)
"""
self.file = file
self.settings = {"library": library}
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
ifcopenshell.api.run("library.remove_library", model, library=library)
"""
settings = {"library": library}
def execute(self):
for reference in set(self.settings["library"].HasLibraryReferences or []):
self.file.remove(reference)
self.file.remove(self.settings["library"])
for rel in self.file.by_type("IfcRelAssociatesLibrary"):
if not rel.RelatingLibrary:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
for reference in set(settings["library"].HasLibraryReferences or []):
file.remove(reference)
file.remove(settings["library"])
for rel in file.by_type("IfcRelAssociatesLibrary"):
if not rel.RelatingLibrary:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -20,34 +20,31 @@ import ifcopenshell
import ifcopenshell.util.element
class Usecase:
def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance):
"""Removes a library reference
def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None:
"""Removes a library reference
Any products which have relationships to this reference will not be
removed.
Any products which have relationships to this reference will not be
removed.
:param reference: The IfcLibraryReference entity you want to remove
:type reference: ifcopenshell.entity_instance
:return: None
:rtype: None
:param reference: The IfcLibraryReference entity you want to remove
:type reference: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
# Let's change our mind and remove it.
ifcopenshell.api.run("library.remove_reference", model, reference=reference)
"""
self.file = file
self.settings = {"reference": reference}
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
# Let's change our mind and remove it.
ifcopenshell.api.run("library.remove_reference", model, reference=reference)
"""
settings = {"reference": reference}
def execute(self) -> None:
for rel in self.settings["reference"].LibraryRefForObjects:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
self.file.remove(self.settings["reference"])
for rel in settings["reference"].LibraryRefForObjects:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
file.remove(settings["reference"])
@@ -21,70 +21,66 @@ import ifcopenshell.util.element
import ifcopenshell.api
class Usecase:
def __init__(
self,
file: ifcopenshell.file,
reference: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
):
"""Unassigns a product of products from a reference
def unassign_reference(
file: ifcopenshell.file,
reference: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
) -> None:
"""Unassigns a product of products from a reference
If the product isn't assigned to the reference, nothing will happen.
If the product isn't assigned to the reference, nothing will happen.
:param reference: The IfcLibraryReference to unassign from
:type reference: ifcopenshell.entity_instance
:param products: A list of IfcProduct elements to unassign from the reference
:type products: list[ifcopenshell.entity_instance]
:return: None
:rtype: None
:param reference: The IfcLibraryReference to unassign from
:type reference: ifcopenshell.entity_instance
:param products: A list of IfcProduct elements to unassign from the reference
:type products: list[ifcopenshell.entity_instance]
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.run("library.add_reference", model, library=library)
ifcopenshell.api.run("library.edit_reference", model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
# Let's assume we have an AHU in our model.
ahu = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# Let's assume we have an AHU in our model.
ahu = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
# Let's change our mind and unassign it.
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
"""
# Let's change our mind and unassign it.
ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
"""
self.file = file
self.settings = {"reference": reference, "products": products}
settings = {"reference": reference, "products": products}
def execute(self):
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
reference_rels: set[ifcopenshell.entity_instance] = set()
products = set(self.settings["products"])
for product in products:
reference_rels.update(product.HasAssociations)
reference_rels: set[ifcopenshell.entity_instance] = set()
products = set(settings["products"])
for product in products:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"]
}
reference_rels = {
rel
for rel in reference_rels
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == settings["reference"]
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
else:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)