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,47 @@
#
# 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_task import add_task
from .add_task_time import add_task_time
from .add_time_period import add_time_period
from .add_work_calendar import add_work_calendar
from .add_work_plan import add_work_plan
from .add_work_schedule import add_work_schedule
from .add_work_time import add_work_time
from .assign_lag_time import assign_lag_time
from .assign_process import assign_process
from .assign_product import assign_product
from .assign_recurrence_pattern import assign_recurrence_pattern
from .assign_sequence import assign_sequence
from .assign_workplan import assign_workplan
from .calculate_task_duration import calculate_task_duration
from .cascade_schedule import cascade_schedule
from .create_baseline import create_baseline
from .duplicate_task import duplicate_task
from .edit_lag_time import edit_lag_time
from .edit_recurrence_pattern import edit_recurrence_pattern
from .edit_sequence import edit_sequence
from .edit_task import edit_task
from .edit_task_time import edit_task_time
from .edit_work_calendar import edit_work_calendar
from .edit_work_plan import edit_work_plan
from .edit_work_schedule import edit_work_schedule
from .edit_work_time import edit_work_time
from .get_related_products import get_related_products
try:
from .recalculate_schedule import recalculate_schedule
except ModuleNotFoundError as e:
print(f"Note: API not available due to missing dependencies: sequence.recalculate_schedule - {e}")
from .remove_task import remove_task
from .remove_time_period import remove_time_period
from .remove_work_calendar import remove_work_calendar
from .remove_work_plan import remove_work_plan
from .remove_work_schedule import remove_work_schedule
from .remove_work_time import remove_work_time
from .unassign_lag_time import unassign_lag_time
from .unassign_process import unassign_process
from .unassign_product import unassign_product
from .unassign_recurrence_pattern import unassign_recurrence_pattern
from .unassign_sequence import unassign_sequence
@@ -20,169 +20,159 @@ import ifcopenshell.api
import ifcopenshell
class Usecase:
def __init__(
self,
def add_task(
file,
work_schedule=None,
parent_task=None,
name=None,
description=None,
identification=None,
predefined_type="NOTDEFINED",
) -> None:
"""Adds a new task
Tasks are typically used for two purposes: construction scheduling and
facility management.
In construction scheduling, a task represents a job to be done in a work
schedule. Tasks are organised in a hierarchical manner known as a work
breakdown structure (WBS) and have lots of sequential relationships
(e.g. this task must finish before the next task can start) and date
information (e.g. durations, start dates). This is often represented as
a gantt chart and used to analyse critical paths to try and reduce
project time to stay on-time and within budget.
In facility management, a task represents a maintenance task to maintain
a piece of equipment. Tasks are broken down into a punch list, or simply
a bulleted or ordered sequence of tasks to be performed (e.g. turn off
equipment, check power connection, etc) in order to maintain the
equipment. Tasks will also typically have recurring scheduled dates in
line with the maintenance schedule. These maintenance tasks and
procedures are typically published as part of an operations and
maintenance manual.
All tasks must be grouped in a work schedule, either directly as a root
or top-level task, or indirectly as a child or subtask of a parent task.
In construction scheduling, tasks may be nested many times to create the
work breakdown structure, and the "leaf" tasks (i.e. tasks with no more
subtasks) are considered to be the activities with dates, whereas all
parent tasks are part of the breakdown structure used for categorisation
purposes. In facility management, top-level tasks represent the overall
maintenance job to be performed, and child tasks represent an ordered
list of things to do for that maintenance. These form a 2-level
hierarchy. No further child tasks are recommended.
:param work_schedule: The work schedule to group the task in, if the
task is to be a top-level or root task. This is mutually exclusive
with the parent_task parameter.
:type work_schedule: ifcopenshell.entity_instance
:param parent_task: The parent task, if the task is to be a subtask or
child task. This is mutually exclusive with the work_schedule
parameter.
:type parent_task: ifcopenshell.entity_instance
:param name: The name of the task.
:type name: str,optional
:param description: The description of the task.
:type description: str,optional
:param identification: The identification code of the task.
:type identification: str,optional
:param predefined_type: The predefined type of the task. Common ones
include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the
IFC documentation for IfcTaskTypeEnum for more information.
:type predefined_type: str
:return: The newly created IfcTask
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Add a root task to represent the design milestones, and major
# project phases.
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Design", identification="B")
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's start creating our work breakdown structure.
ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Early Works", identification="C1")
ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Substructure", identification="C2")
superstructure = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Superstructure", identification="C3")
# Notice how the leaf task is the actual activity
ifcopenshell.api.run("sequence.add_task", model,
parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
# Let's imagine we are digitising an operations and maintenance
# manual for the mechanical discipline.
maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance")
# Imagine we have to clean the condenser coils for a chiller every
# month. Like the schedule above, to keep things simple we won't
# show scheduling times and calendars. This root task represents the
# overall maintenance task.
cleaning = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=maintenance, name="Condenser coil cleaning")
# These subtasks represent the punch list of maintenance tasks.
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1",
description="Prior to work, wear safety shoes, gloves, and goggles.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2",
description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
description="Switch OFF the chiller unit.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
description="Open the isolator switch.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
description="Setup the water pressure by tapping to a water supply and connecting to a ...")
"""
settings = {
"work_schedule": work_schedule,
"parent_task": parent_task,
"name": name,
"description": description,
"identification": identification,
"predefined_type": predefined_type,
}
task = ifcopenshell.api.run(
"root.create_entity",
file,
work_schedule=None,
parent_task=None,
name=None,
description=None,
identification=None,
predefined_type="NOTDEFINED",
):
"""Adds a new task
Tasks are typically used for two purposes: construction scheduling and
facility management.
In construction scheduling, a task represents a job to be done in a work
schedule. Tasks are organised in a hierarchical manner known as a work
breakdown structure (WBS) and have lots of sequential relationships
(e.g. this task must finish before the next task can start) and date
information (e.g. durations, start dates). This is often represented as
a gantt chart and used to analyse critical paths to try and reduce
project time to stay on-time and within budget.
In facility management, a task represents a maintenance task to maintain
a piece of equipment. Tasks are broken down into a punch list, or simply
a bulleted or ordered sequence of tasks to be performed (e.g. turn off
equipment, check power connection, etc) in order to maintain the
equipment. Tasks will also typically have recurring scheduled dates in
line with the maintenance schedule. These maintenance tasks and
procedures are typically published as part of an operations and
maintenance manual.
All tasks must be grouped in a work schedule, either directly as a root
or top-level task, or indirectly as a child or subtask of a parent task.
In construction scheduling, tasks may be nested many times to create the
work breakdown structure, and the "leaf" tasks (i.e. tasks with no more
subtasks) are considered to be the activities with dates, whereas all
parent tasks are part of the breakdown structure used for categorisation
purposes. In facility management, top-level tasks represent the overall
maintenance job to be performed, and child tasks represent an ordered
list of things to do for that maintenance. These form a 2-level
hierarchy. No further child tasks are recommended.
:param work_schedule: The work schedule to group the task in, if the
task is to be a top-level or root task. This is mutually exclusive
with the parent_task parameter.
:type work_schedule: ifcopenshell.entity_instance
:param parent_task: The parent task, if the task is to be a subtask or
child task. This is mutually exclusive with the work_schedule
parameter.
:type parent_task: ifcopenshell.entity_instance
:param name: The name of the task.
:type name: str,optional
:param description: The description of the task.
:type description: str,optional
:param identification: The identification code of the task.
:type identification: str,optional
:param predefined_type: The predefined type of the task. Common ones
include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the
IFC documentation for IfcTaskTypeEnum for more information.
:type predefined_type: str
:return: The newly created IfcTask
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Add a root task to represent the design milestones, and major
# project phases.
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Design", identification="B")
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's start creating our work breakdown structure.
ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Early Works", identification="C1")
ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Substructure", identification="C2")
superstructure = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Superstructure", identification="C3")
# Notice how the leaf task is the actual activity
ifcopenshell.api.run("sequence.add_task", model,
parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
# Let's imagine we are digitising an operations and maintenance
# manual for the mechanical discipline.
maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance")
# Imagine we have to clean the condenser coils for a chiller every
# month. Like the schedule above, to keep things simple we won't
# show scheduling times and calendars. This root task represents the
# overall maintenance task.
cleaning = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=maintenance, name="Condenser coil cleaning")
# These subtasks represent the punch list of maintenance tasks.
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1",
description="Prior to work, wear safety shoes, gloves, and goggles.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2",
description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
description="Switch OFF the chiller unit.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
description="Open the isolator switch.")
ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
description="Setup the water pressure by tapping to a water supply and connecting to a ...")
"""
self.file = file
self.settings = {
"work_schedule": work_schedule,
"parent_task": parent_task,
"name": name,
"description": description,
"identification": identification,
"predefined_type": predefined_type,
}
def execute(self):
task = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class="IfcTask",
name=self.settings["name"],
predefined_type=self.settings["predefined_type"],
ifc_class="IfcTask",
name=settings["name"],
predefined_type=settings["predefined_type"],
)
if settings["description"]:
task.Description = settings["description"]
if settings["identification"]:
task.Identification = settings["identification"]
task.IsMilestone = False
if settings["work_schedule"]:
file.create_entity(
"IfcRelAssignsToControl",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [task],
"RelatingControl": settings["work_schedule"],
}
)
if self.settings["description"]:
task.Description = self.settings["description"]
if self.settings["identification"]:
task.Identification = self.settings["identification"]
task.IsMilestone = False
if self.settings["work_schedule"]:
self.file.create_entity(
"IfcRelAssignsToControl",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run(
"owner.create_owner_history", self.file
),
"RelatedObjects": [task],
"RelatingControl": self.settings["work_schedule"],
}
)
elif self.settings["parent_task"]:
rel = ifcopenshell.api.run(
"nest.assign_object",
self.file,
related_objects=[task],
relating_object=self.settings["parent_task"],
)
if self.settings["parent_task"].Identification:
task.Identification = (
self.settings["parent_task"].Identification
+ "."
+ str(len(rel.RelatedObjects))
)
return task
elif settings["parent_task"]:
rel = ifcopenshell.api.run(
"nest.assign_object",
file,
related_objects=[task],
relating_object=settings["parent_task"],
)
if settings["parent_task"].Identification:
task.Identification = settings["parent_task"].Identification + "." + str(len(rel.RelatedObjects))
return task
@@ -17,55 +17,52 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, task=None, is_recurring=False):
"""Adds a task time to a task
def add_task_time(file, task=None, is_recurring=False) -> None:
"""Adds a task time to a task
Some tasks, such as activities within a work breakdown structure or
overall maintenance tasks will have time related information. This
includes start dates, durations, end dates, and possible recurring times
(especially for maintenance tasks).
Some tasks, such as activities within a work breakdown structure or
overall maintenance tasks will have time related information. This
includes start dates, durations, end dates, and possible recurring times
(especially for maintenance tasks).
:param task: The task to add time data to.
:type task: ifcopenshell.entity_instance
:param is_recurring: Whether or not the time should recur.
:type is_recurring: bool
:return: The newly created IfcTaskTime.
:rtype: ifcopenshell.entity_instance
:param task: The task to add time data to.
:type task: ifcopenshell.entity_instance
:param is_recurring: Whether or not the time should recur.
:type is_recurring: bool
:return: The newly created IfcTaskTime.
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Create a portion of a work breakdown structure.
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
superstructure = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Superstructure", identification="C3")
task = ifcopenshell.api.run("sequence.add_task", model,
parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
# Create a portion of a work breakdown structure.
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
superstructure = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Superstructure", identification="C3")
task = ifcopenshell.api.run("sequence.add_task", model,
parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
# Add time data. Note that time data is blank by default.
time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
# Add time data. Note that time data is blank by default.
time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
# Let's say our task starts on the first of January when everybody
# is still drunk from the new years celebration, and lasts for 2
# days. Note we don't need to specify the end date, as that is
# derived from the start plus the duration. In this simple example,
# no calendar has been specified, so we are working 24/7. Yikes!
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
"""
self.file = file
self.settings = {"task": task, "is_recurring": is_recurring}
# Let's say our task starts on the first of January when everybody
# is still drunk from the new years celebration, and lasts for 2
# days. Note we don't need to specify the end date, as that is
# derived from the start plus the duration. In this simple example,
# no calendar has been specified, so we are working 24/7. Yikes!
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
"""
settings = {"task": task, "is_recurring": is_recurring}
def execute(self):
if self.settings["is_recurring"]:
task_time = self.file.create_entity("IfcTaskTimeRecurring")
else:
task_time = self.file.create_entity("IfcTaskTime")
self.settings["task"].TaskTime = task_time
return task_time
if settings["is_recurring"]:
task_time = file.create_entity("IfcTaskTimeRecurring")
else:
task_time = file.create_entity("IfcTaskTime")
settings["task"].TaskTime = task_time
return task_time
@@ -23,79 +23,72 @@ from datetime import datetime
from datetime import timedelta
class Usecase:
def __init__(self, file, recurrence_pattern=None, start_time=None, end_time=None):
"""Adds a time period to a recurrence pattern
def add_time_period(file, recurrence_pattern=None, start_time=None, end_time=None) -> None:
"""Adds a time period to a recurrence pattern
A recurring time may be an all-day event, or only during certain time
periods of the day. For example, you might say that every 1st of January
recurring is a public holiday, which is an all-day event. Alternatively,
you might say that you work every (i.e. recurringly) Monday to Friday,
from 9am to 5pm. The 9am to 5pm is the time period.
A recurring time may be an all-day event, or only during certain time
periods of the day. For example, you might say that every 1st of January
recurring is a public holiday, which is an all-day event. Alternatively,
you might say that you work every (i.e. recurringly) Monday to Friday,
from 9am to 5pm. The 9am to 5pm is the time period.
There may also be multiple recurrence patterns, such as from 9am to
12pm, and then another from 1pm to 5pm (to indicate an hour break for
lunch).
There may also be multiple recurrence patterns, such as from 9am to
12pm, and then another from 1pm to 5pm (to indicate an hour break for
lunch).
:param recurrence_pattern: The IfcRecurrencePattern to add the time
period to. See ifcopenshell.api.sequence.assign_recurrence_pattern.
:type recurrence_pattern: ifcopenshell.entity_instance
:param start_time: The start time of the time period, in a format
compatible with IfcTime, such as an ISO format time string or a
datetime.time object.
:type start_time: str,datetime.time
:param end_time: The end time of the time period, in a format
compatible with IfcTime, such as an ISO format time string or a
datetime.time object.
:type end_time: str,datetime.time
:return: The newly created IfcTimePeriod
:rtype: ifcopenshell.entity_instance
:param recurrence_pattern: The IfcRecurrencePattern to add the time
period to. See ifcopenshell.api.sequence.assign_recurrence_pattern.
:type recurrence_pattern: ifcopenshell.entity_instance
:param start_time: The start time of the time period, in a format
compatible with IfcTime, such as an ISO format time string or a
datetime.time object.
:type start_time: str,datetime.time
:param end_time: The end time of the time period, in a format
compatible with IfcTime, such as an ISO format time string or a
datetime.time object.
:type end_time: str,datetime.time
:return: The newly created IfcTimePeriod
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# The morning work session, lunch, then the afternoon work session.
ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
"""
self.file = file
self.settings = {
"recurrence_pattern": recurrence_pattern,
"start_time": start_time,
"end_time": end_time,
}
# The morning work session, lunch, then the afternoon work session.
ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
"""
settings = {
"recurrence_pattern": recurrence_pattern,
"start_time": start_time,
"end_time": end_time,
}
def execute(self):
time_period = self.file.create_entity("IfcTimePeriod")
time_period.StartTime = ifcopenshell.util.date.datetime2ifc(
self.settings["start_time"], "IfcTime"
)
time_period.EndTime = ifcopenshell.util.date.datetime2ifc(
self.settings["end_time"], "IfcTime"
)
time_periods = list(self.settings["recurrence_pattern"].TimePeriods or [])
time_periods.append(time_period)
self.settings["recurrence_pattern"].TimePeriods = time_periods
time_period = file.create_entity("IfcTimePeriod")
time_period.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcTime")
time_period.EndTime = ifcopenshell.util.date.datetime2ifc(settings["end_time"], "IfcTime")
time_periods = list(settings["recurrence_pattern"].TimePeriods or [])
time_periods.append(time_period)
settings["recurrence_pattern"].TimePeriods = time_periods
ifcopenshell.util.sequence.is_working_day.cache_clear()
ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
ifcopenshell.util.sequence.is_working_day.cache_clear()
ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
return time_period
return time_period
@@ -19,80 +19,77 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, name="Unnamed", predefined_type="NOTDEFINED"):
"""Add a work calendar
def add_work_calendar(file, name="Unnamed", predefined_type="NOTDEFINED") -> None:
"""Add a work calendar
A work calendar defines when work is allowed to occur and when the
holidays are. This is a fundamental concept in construction planning.
Every task in a work schedule will have an associated calendar. Some
task and resources work 24/7, whereas others work Monday to Friday, or
5.5 day weeks, etc. This is important, as tasks durations may only occur
during working times in a work calendar.
A work calendar defines when work is allowed to occur and when the
holidays are. This is a fundamental concept in construction planning.
Every task in a work schedule will have an associated calendar. Some
task and resources work 24/7, whereas others work Monday to Friday, or
5.5 day weeks, etc. This is important, as tasks durations may only occur
during working times in a work calendar.
Work calendars can also be used to associate with events, such as
indicating that during certain days and times of the year, motion
sensors should turn on the lights, and other smart building controls.
Work calendars can also be used to associate with events, such as
indicating that during certain days and times of the year, motion
sensors should turn on the lights, and other smart building controls.
:param name: The name of the calendar. Typically something like
"5 Day Working Week" or "24/7".
:type name: str, optional
:param predefined_type: The type of calendar, typically used to more
specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or
THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage.
:return: The newly created IfcWorkCalendar
:rtype: ifcopenshell.entity_instance
:param name: The name of the calendar. Typically something like
"5 Day Working Week" or "24/7".
:type name: str, optional
:param predefined_type: The type of calendar, typically used to more
specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or
THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage.
:return: The newly created IfcWorkCalendar
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Add a root task to represent the construction tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Add a root task to represent the construction tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="09:00", end_time="17:00")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="09:00", end_time="17:00")
# 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.run("control.assign_control", model, relating_control=calendar, related_object=task)
"""
self.file = file
self.settings = {"name": name, "predefined_type": predefined_type}
# 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.run("control.assign_control", model, relating_control=calendar, related_object=task)
"""
settings = {"name": name, "predefined_type": predefined_type}
def execute(self):
work_calendar = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class="IfcWorkCalendar",
predefined_type=self.settings["predefined_type"],
name=self.settings["name"],
)
context = self.file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
self.file,
definitions=[work_calendar],
relating_context=context,
)
return work_calendar
work_calendar = ifcopenshell.api.run(
"root.create_entity",
file,
ifc_class="IfcWorkCalendar",
predefined_type=settings["predefined_type"],
name=settings["name"],
)
context = file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
file,
definitions=[work_calendar],
relating_context=context,
)
return work_calendar
@@ -21,70 +21,63 @@ import ifcopenshell.util.date
from datetime import datetime
class Usecase:
def __init__(self, file, name=None, predefined_type="NOTDEFINED", start_time=None):
"""Add a new work plan
def add_work_plan(file, name=None, predefined_type="NOTDEFINED", start_time=None) -> None:
"""Add a new work plan
A work plan is a group of work schedules. Since work schedules may have
different purposes, such as for maintenance or construction scheduling,
baseline comparison, or phasing, work plans can be used to group related
work schedules. At a minimum, it is recommended to use work plans to
indicate whether the work schedules are for facility management or for
construction scheduling.
A work plan is a group of work schedules. Since work schedules may have
different purposes, such as for maintenance or construction scheduling,
baseline comparison, or phasing, work plans can be used to group related
work schedules. At a minimum, it is recommended to use work plans to
indicate whether the work schedules are for facility management or for
construction scheduling.
:param name: The name of the work plan. Recommended to be "Maintenance"
or "Construction" for the two main purposes.
:type name: str, optional
:param predefined_type: The type of work plan, used for baselining.
Leave as "NOTDEFINED" if unsure.
:type predefined_type: str
:param start_time: The earliest start time when the schedules grouped
within the work plan are relevant.
:type start_time: str,datetime.time
:return: The newly created IfcWorkPlan
:rtype: ifcopenshell.entity_instance
:param name: The name of the work plan. Recommended to be "Maintenance"
or "Construction" for the two main purposes.
:type name: str, optional
:param predefined_type: The type of work plan, used for baselining.
Leave as "NOTDEFINED" if unsure.
:type predefined_type: str
:param start_time: The earliest start time when the schedules grouped
within the work plan are relevant.
:type start_time: str,datetime.time
:return: The newly created IfcWorkPlan
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
"""
self.file = file
self.settings = {
"name": name,
"predefined_type": predefined_type,
"start_time": start_time or datetime.now(),
}
# This is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
"""
settings = {
"name": name,
"predefined_type": predefined_type,
"start_time": start_time or datetime.now(),
}
def execute(self):
work_plan = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class="IfcWorkPlan",
predefined_type=self.settings["predefined_type"],
name=self.settings["name"],
)
work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(
datetime.now(), "IfcDateTime"
)
user = ifcopenshell.api.owner.settings.get_user(self.file)
if user:
work_plan.Creators = [user.ThePerson]
work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(
self.settings["start_time"], "IfcDateTime"
)
work_plan = ifcopenshell.api.run(
"root.create_entity",
file,
ifc_class="IfcWorkPlan",
predefined_type=settings["predefined_type"],
name=settings["name"],
)
work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
user = ifcopenshell.api.owner.settings.get_user(file)
if user:
work_plan.Creators = [user.ThePerson]
work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
context = self.file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
self.file,
definitions=[work_plan],
relating_context=context,
)
return work_plan
context = file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
file,
definitions=[work_plan],
relating_context=context,
)
return work_plan
@@ -21,104 +21,96 @@ import ifcopenshell.util.date
from datetime import datetime
class Usecase:
def __init__(
self,
def add_work_schedule(
file,
name="Unnamed",
predefined_type="NOTDEFINED",
object_type=None,
start_time=None,
work_plan=None,
) -> None:
"""Add a new work schedule
A work schedule is a group of tasks, where the tasks are typically
either for maintenance or for construction scheduling.
:param name: The name of the work schedule.
:type name: str
:param predefined_type: The type of schedule, chosen from ACTUAL,
BASELINE, and PLANNED. Typically you would start with PLANNED, then
convert to a BASELINE when changes are made with separate schedules,
then have a parallel ACTUAL schedule.
:type predefined_type: str
:param start_time: The earlier start time when the schedule is relevant.
May be represented with an ISO standard string.
:type start_time: str,datetime.time,optional
:param work_plan: The IfcWorkPlan the schedule will be part of. If not
provided, the schedule will not be grouped in a work plan and would
exist as a top level schedule in the project. This is not
recommended.
:type work_plan: ifcopenshell.entity_instance,optional
:return: The newly created IfcWorkSchedule
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# Let's imagine this is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
# Add a root task to represent the design milestones, and major
# project phases.
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Design", identification="B")
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
"""
settings = {
"name": name,
"predefined_type": predefined_type,
"object_type": object_type,
"start_time": start_time or datetime.now(),
"work_plan": work_plan,
}
work_schedule = ifcopenshell.api.run(
"root.create_entity",
file,
name="Unnamed",
predefined_type="NOTDEFINED",
object_type=None,
start_time=None,
work_plan=None,
):
"""Add a new work schedule
A work schedule is a group of tasks, where the tasks are typically
either for maintenance or for construction scheduling.
:param name: The name of the work schedule.
:type name: str
:param predefined_type: The type of schedule, chosen from ACTUAL,
BASELINE, and PLANNED. Typically you would start with PLANNED, then
convert to a BASELINE when changes are made with separate schedules,
then have a parallel ACTUAL schedule.
:type predefined_type: str
:param start_time: The earlier start time when the schedule is relevant.
May be represented with an ISO standard string.
:type start_time: str,datetime.time,optional
:param work_plan: The IfcWorkPlan the schedule will be part of. If not
provided, the schedule will not be grouped in a work plan and would
exist as a top level schedule in the project. This is not
recommended.
:type work_plan: ifcopenshell.entity_instance,optional
:return: The newly created IfcWorkSchedule
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# Let's imagine this is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
# Add a root task to represent the design milestones, and major
# project phases.
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Design", identification="B")
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
"""
self.file = file
self.settings = {
"name": name,
"predefined_type": predefined_type,
"object_type": object_type,
"start_time": start_time or datetime.now(),
"work_plan": work_plan,
}
def execute(self):
work_schedule = ifcopenshell.api.run(
"root.create_entity",
self.file,
ifc_class="IfcWorkSchedule",
predefined_type=self.settings["predefined_type"],
name=self.settings["name"],
ifc_class="IfcWorkSchedule",
predefined_type=settings["predefined_type"],
name=settings["name"],
)
work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
user = ifcopenshell.api.owner.settings.get_user(file)
if user:
work_schedule.Creators = [user.ThePerson]
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
if settings["object_type"]:
work_schedule.ObjectType = settings["object_type"]
if settings["work_plan"]:
ifcopenshell.api.run(
"aggregate.assign_object",
file,
**{
"products": [work_schedule],
"relating_object": settings["work_plan"],
}
)
work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(
datetime.now(), "IfcDateTime"
else:
# TODO: this is an ambiguity by buildingSMART
# See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
context = file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
file,
definitions=[work_schedule],
relating_context=context,
)
user = ifcopenshell.api.owner.settings.get_user(self.file)
if user:
work_schedule.Creators = [user.ThePerson]
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(
self.settings["start_time"], "IfcDateTime"
)
if self.settings["object_type"]:
work_schedule.ObjectType = self.settings["object_type"]
if self.settings["work_plan"]:
ifcopenshell.api.run(
"aggregate.assign_object",
self.file,
**{
"products": [work_schedule],
"relating_object": self.settings["work_plan"],
}
)
else:
# TODO: this is an ambiguity by buildingSMART
# See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
context = self.file.by_type("IfcContext")[0]
ifcopenshell.api.run(
"project.assign_declaration",
self.file,
definitions=[work_schedule],
relating_context=context,
)
return work_schedule
return work_schedule
@@ -17,69 +17,66 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, work_calendar=None, time_type="WorkingTimes"):
"""Add either working times or holiday times to a calendar
def add_work_time(file, work_calendar=None, time_type="WorkingTimes") -> None:
"""Add either working times or holiday times to a calendar
A calendar defines when work occurs by defining working times and
holiday times. First, the working times are defined, then the holidays
may override the working times. For this reason, holidays are also known
as exception times. For example, you might define the working times as
every Monday to Friday, then define a few holidays in the year, such as
the 1st of January. If the 1st of January is on a weekday, it will
override the work time.
A calendar defines when work occurs by defining working times and
holiday times. First, the working times are defined, then the holidays
may override the working times. For this reason, holidays are also known
as exception times. For example, you might define the working times as
every Monday to Friday, then define a few holidays in the year, such as
the 1st of January. If the 1st of January is on a weekday, it will
override the work time.
:param work_calendar: The IfcWorkCalendar to add the work or holiday
time definition to.
:type work_calendar: ifcopenshell.entity_instance
:param time_type: Either WorkingTimes or ExceptionTimes, depending on
what you want to define.
:type time_type: str
:return: The newly created IfcWorkTime
:rtype: ifcopenshell.entity_instance
:param work_calendar: The IfcWorkCalendar to add the work or holiday
time definition to.
:type work_calendar: ifcopenshell.entity_instance
:param time_type: Either WorkingTimes or ExceptionTimes, depending on
what you want to define.
:type time_type: str
:return: The newly created IfcWorkTime
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# Let's set some holidays
holidays = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="ExceptionTimes")
# Let's set some holidays
holidays = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="ExceptionTimes")
# We create a yearly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH")
# We create a yearly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH")
# The holiday is every 1st of January
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]})
"""
self.file = file
self.settings = {"work_calendar": work_calendar, "time_type": time_type}
# The holiday is every 1st of January
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]})
"""
settings = {"work_calendar": work_calendar, "time_type": time_type}
def execute(self):
work_time = self.file.create_entity("IfcWorkTime")
if self.settings["time_type"] == "WorkingTimes":
working_times = list(self.settings["work_calendar"].WorkingTimes or [])
working_times.append(work_time)
self.settings["work_calendar"].WorkingTimes = working_times
elif self.settings["time_type"] == "ExceptionTimes":
exception_times = list(self.settings["work_calendar"].ExceptionTimes or [])
exception_times.append(work_time)
self.settings["work_calendar"].ExceptionTimes = exception_times
return work_time
work_time = file.create_entity("IfcWorkTime")
if settings["time_type"] == "WorkingTimes":
working_times = list(settings["work_calendar"].WorkingTimes or [])
working_times.append(work_time)
settings["work_calendar"].WorkingTimes = working_times
elif settings["time_type"] == "ExceptionTimes":
exception_times = list(settings["work_calendar"].ExceptionTimes or [])
exception_times.append(work_time)
settings["work_calendar"].ExceptionTimes = exception_times
return work_time
@@ -19,88 +19,78 @@
import ifcopenshell.util.date
class Usecase:
def __init__(self, file, rel_sequence=None, lag_value=None, duration_type="WORKTIME"):
"""Assign a lag time to a sequence relationship between tasks
def assign_lag_time(file, rel_sequence=None, lag_value=None, duration_type="WORKTIME") -> None:
"""Assign a lag time to a sequence relationship between tasks
A task sequence (e.g. finish to start) may optionally have a lag time
defined. This is a fundamental concept in construction scheduling. The
lag is defined as a duration, and the duration is typically either
calendar based (i.e. follows the working times and holidays of the
calendar) or elapsed time based (i.e. 24/7).
A task sequence (e.g. finish to start) may optionally have a lag time
defined. This is a fundamental concept in construction scheduling. The
lag is defined as a duration, and the duration is typically either
calendar based (i.e. follows the working times and holidays of the
calendar) or elapsed time based (i.e. 24/7).
A sequence may only have a single lag time defined. Negative lag times
are allowed.
A sequence may only have a single lag time defined. Negative lag times
are allowed.
:param rel_sequence: The IfcRelSequence to assign the lag time to.
:type rel_sequence: ifcopenshell.entity_instance
:param lag_value: An ISO standardised duration string.
:type lag_value: str
:param duration_type: Choose from WORKTIME for the associated
calendar-based lag times (this is the most common scenario and is
recommended as a default), or ELAPSEDTIME to not follow the
calendar. You may also choose NOTDEFINED but the behaviour of this
is unclear.
:type duration_type: str
:return: The newly created IfcLagTime
:rtype: ifcopenshell.entity_instance
:param rel_sequence: The IfcRelSequence to assign the lag time to.
:type rel_sequence: ifcopenshell.entity_instance
:param lag_value: An ISO standardised duration string.
:type lag_value: str
:param duration_type: Choose from WORKTIME for the associated
calendar-based lag times (this is the most common scenario and is
recommended as a default), or ELAPSEDTIME to not follow the
calendar. You may also choose NOTDEFINED but the behaviour of this
is unclear.
:type duration_type: str
:return: The newly created IfcLagTime
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine we're doing a typically formwork, reinforcement,
# pour sequence. Let's start with the formwork. It'll take us 2
# days.
formwork = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Formwork", identification="C.1")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Let's imagine we're doing a typically formwork, reinforcement,
# pour sequence. Let's start with the formwork. It'll take us 2
# days.
formwork = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Formwork", identification="C.1")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's do the reinforcement. It'll take us another 2 days.
reinforcement = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.2")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's do the reinforcement. It'll take us another 2 days.
reinforcement = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.2")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's say the formwork must finish before the reinforcement
# can start. This is a typical finish to start relationship (FS).
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=formwork, related_process=reinforcement)
# Now let's say the formwork must finish before the reinforcement
# can start. This is a typical finish to start relationship (FS).
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=formwork, related_process=reinforcement)
# Now typically there would be no lag time between formwork and
# reinforcement, but let's pretend that we had to allow 1 day gap
# for whatever reason.
ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
"""
self.file = file
self.settings = {
"rel_sequence": rel_sequence,
"lag_value": lag_value,
"duration_type": duration_type,
}
# Now typically there would be no lag time between formwork and
# reinforcement, but let's pretend that we had to allow 1 day gap
# for whatever reason.
ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
"""
settings = {
"rel_sequence": rel_sequence,
"lag_value": lag_value,
"duration_type": duration_type,
}
def execute(self):
lag_value = self.file.createIfcDuration(
ifcopenshell.util.date.datetime2ifc(self.settings["lag_value"], "IfcDuration")
)
lag_time = self.file.create_entity(
"IfcLagTime", DurationType=self.settings["duration_type"], LagValue=lag_value
)
if self.settings["rel_sequence"].is_a("IfcRelSequence"):
if (
self.settings["rel_sequence"].TimeLag
and len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1
):
self.file.remove(self.settings["rel_sequence"].TimeLag)
self.settings["rel_sequence"].TimeLag = lag_time
lag_value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(settings["lag_value"], "IfcDuration"))
lag_time = file.create_entity("IfcLagTime", DurationType=settings["duration_type"], LagValue=lag_value)
if settings["rel_sequence"].is_a("IfcRelSequence"):
if settings["rel_sequence"].TimeLag and len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1:
file.remove(settings["rel_sequence"].TimeLag)
settings["rel_sequence"].TimeLag = lag_time
@@ -20,109 +20,99 @@ import ifcopenshell
import ifcopenshell.api
class Usecase:
def __init__(self, file, relating_process=None, related_object=None):
"""Assigns an object to be related to a process, typically a construction task
def assign_process(file, relating_process=None, related_object=None) -> None:
"""Assigns an object to be related to a process, typically a construction task
Processes work using the ICOM (Input, Controls, Outputs, Mechanisms)
paradigm in IFC. This process model is commonly used in modeling
manufacturing functions.
Processes work using the ICOM (Input, Controls, Outputs, Mechanisms)
paradigm in IFC. This process model is commonly used in modeling
manufacturing functions.
For example, processes (such as tasks) consume Inputs and transform them
into Outputs. The process may only occur within the limits of Controls
(e.g. cost items) and may require Mechanisms (ISO9000 calls them
Mechanisms, whereas IFC calls them resources, such as raw materials,
labour, or equipment).
For example, processes (such as tasks) consume Inputs and transform them
into Outputs. The process may only occur within the limits of Controls
(e.g. cost items) and may require Mechanisms (ISO9000 calls them
Mechanisms, whereas IFC calls them resources, such as raw materials,
labour, or equipment).
+----------+
| Controls |
+----------+
|
V
+--------+ +---------+ +---------+
| Inputs | --> | Process | --> | Outputs |
+--------+ +---------+ +---------+
^
|
+-----------+
| Resources |
+-----------+
+----------+
| Controls |
+----------+
|
V
+--------+ +---------+ +---------+
| Inputs | --> | Process | --> | Outputs |
+--------+ +---------+ +---------+
^
|
+-----------+
| Resources |
+-----------+
There are three main scenarios where an object may be related to a
task: defining inputs, controls, and resources of a process.
There are three main scenarios where an object may be related to a
task: defining inputs, controls, and resources of a process.
For inputs, a product (i.e. wall) may be defined as an input to a task,
such as when the task is to demolish the wall (i.e. the wall is an
input, and there is no output).
For inputs, a product (i.e. wall) may be defined as an input to a task,
such as when the task is to demolish the wall (i.e. the wall is an
input, and there is no output).
For controls, a cost item may be defined as a control to a task.
For controls, a cost item may be defined as a control to a task.
For resources, any construction resource may be assigned to a task.
For resources, any construction resource may be assigned to a task.
:param relating_process: The IfcProcess (typically IfcTask) that the
input, control, or resource is related to.
:type relating_process: ifcopenshell.entity_instance
:param related_object: The IfcProduct (for input), IfcCostItem (for
control) or IfcConstructionResource (for resource).
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToProcess relationship
:rtype: ifcopenshell.entity_instance
:param relating_process: The IfcProcess (typically IfcTask) that the
input, control, or resource is related to.
:type relating_process: ifcopenshell.entity_instance
:param related_object: The IfcProduct (for input), IfcCostItem (for
control) or IfcConstructionResource (for resource).
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToProcess relationship
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's demolish that wall!
ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
"""
self.file = file
self.settings = {
"relating_process": relating_process,
"related_object": related_object,
}
# Let's demolish that wall!
ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
"""
settings = {
"relating_process": relating_process,
"related_object": related_object,
}
def execute(self):
if self.settings["related_object"].HasAssignments:
for assignment in self.settings["related_object"].HasAssignments:
if (
assignment.is_a("IfcRelAssignsToProcess")
and assignment.RelatingProcess == self.settings["relating_process"]
):
return
if settings["related_object"].HasAssignments:
for assignment in settings["related_object"].HasAssignments:
if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == settings["relating_process"]:
return
operates_on = None
if self.settings["relating_process"].OperatesOn:
operates_on = self.settings["relating_process"].OperatesOn[0]
operates_on = None
if settings["relating_process"].OperatesOn:
operates_on = settings["relating_process"].OperatesOn[0]
if operates_on:
related_objects = list(operates_on.RelatedObjects)
related_objects.append(self.settings["related_object"])
operates_on.RelatedObjects = related_objects
ifcopenshell.api.run(
"owner.update_owner_history", self.file, **{"element": operates_on}
)
else:
operates_on = self.file.create_entity(
"IfcRelAssignsToProcess",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run(
"owner.create_owner_history", self.file
),
"RelatedObjects": [self.settings["related_object"]],
"RelatingProcess": self.settings["relating_process"],
}
)
return operates_on
if operates_on:
related_objects = list(operates_on.RelatedObjects)
related_objects.append(settings["related_object"])
operates_on.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, **{"element": operates_on})
else:
operates_on = file.create_entity(
"IfcRelAssignsToProcess",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [settings["related_object"]],
"RelatingProcess": settings["relating_process"],
}
)
return operates_on
@@ -20,85 +20,75 @@ import ifcopenshell
import ifcopenshell.api
class Usecase:
def __init__(self, file, relating_product=None, related_object=None):
"""Assigns a product to be produced as a result of a process
def assign_product(file, relating_product=None, related_object=None) -> None:
"""Assigns a product to be produced as a result of a process
A construction task may result in products (e.g. a wall) being
constructed. These task "Outputs" are defined in IFC through product
relationships.
A construction task may result in products (e.g. a wall) being
constructed. These task "Outputs" are defined in IFC through product
relationships.
Not all tasks have Outputs. For example, maintenance tasks will
typically not have any outputs.
Not all tasks have Outputs. For example, maintenance tasks will
typically not have any outputs.
See ifcopenshell.api.sequence.assign_process for Inputs and other types
of process relationships that can be described in manufacturing
process modeling.
See ifcopenshell.api.sequence.assign_process for Inputs and other types
of process relationships that can be described in manufacturing
process modeling.
:param relating_product: The IfcProduct that was constructed as a result
of the task.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcProcess (typically IfcTask) of the
construction task.
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToProduct relationship
:rtype: ifcopenshell.entity_instance
:param relating_product: The IfcProduct that was constructed as a result
of the task.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcProcess (typically IfcTask) of the
construction task.
:type related_object: ifcopenshell.entity_instance
:return: The newly created IfcRelAssignsToProduct relationship
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's construct that wall!
ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task)
"""
self.file = file
self.settings = {
"relating_product": relating_product,
"related_object": related_object,
}
# Let's construct that wall!
ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
def execute(self):
if self.settings["related_object"].HasAssignments:
for assignment in self.settings["related_object"].HasAssignments:
if (
assignment.is_a("IfcRelAssignsToProduct")
and assignment.RelatingProduct == self.settings["relating_product"]
):
return
if settings["related_object"].HasAssignments:
for assignment in settings["related_object"].HasAssignments:
if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == settings["relating_product"]:
return
referenced_by = None
if self.settings["relating_product"].ReferencedBy:
referenced_by = self.settings["relating_product"].ReferencedBy[0]
referenced_by = None
if settings["relating_product"].ReferencedBy:
referenced_by = settings["relating_product"].ReferencedBy[0]
if referenced_by:
related_objects = list(referenced_by.RelatedObjects)
related_objects.append(self.settings["related_object"])
referenced_by.RelatedObjects = related_objects
ifcopenshell.api.run(
"owner.update_owner_history", self.file, **{"element": referenced_by}
)
else:
referenced_by = self.file.create_entity(
"IfcRelAssignsToProduct",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run(
"owner.create_owner_history", self.file
),
"RelatedObjects": [self.settings["related_object"]],
"RelatingProduct": self.settings["relating_product"],
}
)
return referenced_by
if referenced_by:
related_objects = list(referenced_by.RelatedObjects)
related_objects.append(settings["related_object"])
referenced_by.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by})
else:
referenced_by = file.create_entity(
"IfcRelAssignsToProduct",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatedObjects": [settings["related_object"]],
"RelatingProduct": settings["relating_product"],
}
)
return referenced_by
@@ -17,112 +17,101 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, parent=None, recurrence_type="WEEKLY"):
"""Define a time to recur at a particular interval
def assign_recurrence_pattern(file, parent=None, recurrence_type="WEEKLY") -> None:
"""Define a time to recur at a particular interval
There are two scenarios where you might want to define a recurring time
pattern.
There are two scenarios where you might want to define a recurring time
pattern.
You might want a task to be scheduled at a recurring interval,
this is common for maintenance tasks which need to be performed monthly,
every 6 months, every year, etc.
You might want a task to be scheduled at a recurring interval,
this is common for maintenance tasks which need to be performed monthly,
every 6 months, every year, etc.
Alternatively, you might be defining a work calendar, which defines
working days or holidays. The working days might be every week from
monday to friday ("every" week means it recurs every week), or the
holidays might be the same every year.
Alternatively, you might be defining a work calendar, which defines
working days or holidays. The working days might be every week from
monday to friday ("every" week means it recurs every week), or the
holidays might be the same every year.
The types of recurrence are:
The types of recurrence are:
- DAILY: every Nth (interval) day for up to X (Occurrences) occurrences.
e.g. Every day, every 2 days, every day up to 5 times, etc
- WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X
(Occurrences) occurrences. e.g. Every Monday, every weekday, every
other saturday, etc
- MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth
(Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of
the Month.
- MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent)
of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g.
Every second Tuesday of the Month.
- YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND
(MonthComponent) month of every Yth (Interval) Year up to Z
(Occurrences) occurrences. e.g. every 25th of December.
- YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of
every JFMAMJJASOND (MonthComponent) month of every Yth (Interval)
Year up to Z (Occurrences) occurrences. e.g. every third Wednesday
of January.
- DAILY: every Nth (interval) day for up to X (Occurrences) occurrences.
e.g. Every day, every 2 days, every day up to 5 times, etc
- WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X
(Occurrences) occurrences. e.g. Every Monday, every weekday, every
other saturday, etc
- MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth
(Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of
the Month.
- MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent)
of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g.
Every second Tuesday of the Month.
- YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND
(MonthComponent) month of every Yth (Interval) Year up to Z
(Occurrences) occurrences. e.g. every 25th of December.
- YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of
every JFMAMJJASOND (MonthComponent) month of every Yth (Interval)
Year up to Z (Occurrences) occurrences. e.g. every third Wednesday
of January.
These recurrence patterns are fairly standard in all calendar and
scheduling applications.
These recurrence patterns are fairly standard in all calendar and
scheduling applications.
:param parent: Either an IfcTaskTimeRecurring if you are defining a
recurring schedule for a task, or IfcWorkTime if you are defining a
recurring pattern for a workdays or holidays in a calendar.
:type parent: ifcopenshell.entity_instance
:param recurrence_type: One of the types of recurrences.
:type recurrence_type: str
:return: The newly created IfcRecurrencePattern
:rtype: ifcopenshell.entity_instance
:param parent: Either an IfcTaskTimeRecurring if you are defining a
recurring schedule for a task, or IfcWorkTime if you are defining a
recurring pattern for a workdays or holidays in a calendar.
:type parent: ifcopenshell.entity_instance
:param recurrence_type: One of the types of recurrences.
:type recurrence_type: str
:return: The newly created IfcRecurrencePattern
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# Let's imagine we are creating a maintenance schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance")
# Let's imagine we are creating a maintenance schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance")
# Now let's imagine we have a task to maintain the chillers
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Chiller maintenance")
# Now let's imagine we have a task to maintain the chillers
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Chiller maintenance")
# Because it is a maintenance task, we must schedule a recurring time
time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True)
# Because it is a maintenance task, we must schedule a recurring time
time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True)
# We create a monthly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH")
# We create a monthly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH")
# Specifically, the maintenance task must occur every 6 months
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6})
"""
self.file = file
self.settings = {"parent": parent, "recurrence_type": recurrence_type}
# Specifically, the maintenance task must occur every 6 months
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6})
"""
settings = {"parent": parent, "recurrence_type": recurrence_type}
def execute(self):
recurrence = self.file.createIfcRecurrencePattern(
self.settings["recurrence_type"]
)
recurrence = file.createIfcRecurrencePattern(settings["recurrence_type"])
if self.settings["parent"].is_a("IfcWorkTime"):
if (
self.settings["parent"].RecurrencePattern
and len(
self.file.get_inverse(self.settings["parent"].RecurrencePattern)
)
== 1
):
self.file.remove(self.settings["parent"].RecurrencePattern)
self.settings["parent"].RecurrencePattern = recurrence
elif self.settings["parent"].is_a("IfcTaskTimeRecurring"):
if len(self.file.get_inverse(self.settings["parent"].Recurrence)) == 1:
self.file.remove(self.settings["parent"].Recurrence)
self.settings["parent"].Recurrence = recurrence
return recurrence
if settings["parent"].is_a("IfcWorkTime"):
if settings["parent"].RecurrencePattern and len(file.get_inverse(settings["parent"].RecurrencePattern)) == 1:
file.remove(settings["parent"].RecurrencePattern)
settings["parent"].RecurrencePattern = recurrence
elif settings["parent"].is_a("IfcTaskTimeRecurring"):
if len(file.get_inverse(settings["parent"].Recurrence)) == 1:
file.remove(settings["parent"].Recurrence)
settings["parent"].Recurrence = recurrence
return recurrence
@@ -20,119 +20,111 @@ import ifcopenshell
import ifcopenshell.api
class Usecase:
def __init__(
self,
file,
relating_process=None,
related_process=None,
sequence_type="FINISH_START",
):
"""Assign a sequential relationship between tasks
def assign_sequence(
file,
relating_process=None,
related_process=None,
sequence_type="FINISH_START",
) -> None:
"""Assign a sequential relationship between tasks
Tasks in construction sequencing typically have sequence relationships
between them, indicating that one task must happen after another. This
is used to automatically compute new start and end dates and cascade
changes when dates are changed. This is also used to calculate critical
paths and floats.
Tasks in construction sequencing typically have sequence relationships
between them, indicating that one task must happen after another. This
is used to automatically compute new start and end dates and cascade
changes when dates are changed. This is also used to calculate critical
paths and floats.
There are four types of sequence relationships, known as finish to
start, finish to finish, start to start, and start to finish, sometimes
abbreviated as a (FS, FF, SS, and SF). The most common is the finish to
start relationship, indicating that the previous task must finish before
the next task can start.
There are four types of sequence relationships, known as finish to
start, finish to finish, start to start, and start to finish, sometimes
abbreviated as a (FS, FF, SS, and SF). The most common is the finish to
start relationship, indicating that the previous task must finish before
the next task can start.
You must not create cyclical task sequences. This makes the computer
unhappy.
You must not create cyclical task sequences. This makes the computer
unhappy.
Note that "previous" or "next" does not necessarily mean the task
chronologically happens before or after. They simply indicate the order
of the sequence relationship. For this reason, they are often called
predecessor and successor tasks in the planning profession.
Note that "previous" or "next" does not necessarily mean the task
chronologically happens before or after. They simply indicate the order
of the sequence relationship. For this reason, they are often called
predecessor and successor tasks in the planning profession.
:param relating_process: The previous / predecessor task.
:type relating_process: ifcopenshell.entity_instance
:param related_process: The next / successor task.
:type related_process: ifcopenshell.entity_instance
:param sequence_type: Choose from FINISH_START, FINISH_FINISH,
START_START, or START_FINISH.
:return: The newly created IfcRelSequence
:rtype: ifcopenshell.entity_instance
:param relating_process: The previous / predecessor task.
:type relating_process: ifcopenshell.entity_instance
:param related_process: The next / successor task.
:type related_process: ifcopenshell.entity_instance
:param sequence_type: Choose from FINISH_START, FINISH_FINISH,
START_START, or START_FINISH.
:return: The newly created IfcRelSequence
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine we're doing a typically formwork, reinforcement,
# pour sequence. Let's start with the formwork. It'll take us 2
# days.
formwork = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Formwork", identification="C.1")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Let's imagine we're doing a typically formwork, reinforcement,
# pour sequence. Let's start with the formwork. It'll take us 2
# days.
formwork = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Formwork", identification="C.1")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's do the reinforcement. It'll take us another 2 days.
reinforcement = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.2")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's do the reinforcement. It'll take us another 2 days.
reinforcement = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.2")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now the pour itself. It'll only take 1 day.
pour = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.3")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"})
# Now the pour it It'll only take 1 day.
pour = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.3")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"})
# Now let's say the formwork must finish before the reinforcement
# can start, and the reinforcement must finish before the pour can
# start. This is a typical finish to start relationship (FS).
ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=formwork, related_process=reinforcement)
ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=reinforcement, related_process=pour)
# Now let's say the formwork must finish before the reinforcement
# can start, and the reinforcement must finish before the pour can
# start. This is a typical finish to start relationship (FS).
ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=formwork, related_process=reinforcement)
ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=reinforcement, related_process=pour)
# Notice how we set all the scheduled start dates arbitrarily at
# 2000-01-01. This is because we can ask IfcOpenShell to
# automatically cascade the dates, starting from any task. This will
# update the reinforcement date to be 2000-01-03 and the pour date
# to be 2000-01-05.
ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork)
"""
self.file = file
self.settings = {
"relating_process": relating_process,
"related_process": related_process,
"sequence_type": sequence_type,
# Notice how we set all the scheduled start dates arbitrarily at
# 2000-01-01. This is because we can ask IfcOpenShell to
# automatically cascade the dates, starting from any task. This will
# update the reinforcement date to be 2000-01-03 and the pour date
# to be 2000-01-05.
ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork)
"""
settings = {
"relating_process": relating_process,
"related_process": related_process,
"sequence_type": sequence_type,
}
for rel in settings["related_process"].IsSuccessorFrom or []:
if rel.RelatingProcess == settings["relating_process"]:
return rel
rel = file.create_entity(
"IfcRelSequence",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
"RelatingProcess": settings["relating_process"],
"RelatedProcess": settings["related_process"],
"SequenceType": settings["sequence_type"],
}
def execute(self):
for rel in self.settings["related_process"].IsSuccessorFrom or []:
if rel.RelatingProcess == self.settings["relating_process"]:
return rel
rel = self.file.create_entity(
"IfcRelSequence",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run(
"owner.create_owner_history", self.file
),
"RelatingProcess": self.settings["relating_process"],
"RelatedProcess": self.settings["related_process"],
"SequenceType": self.settings["sequence_type"],
}
)
ifcopenshell.api.run(
"sequence.cascade_schedule", self.file, task=self.settings["relating_process"]
)
return rel
)
ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["relating_process"])
return rel
@@ -20,52 +20,49 @@ import ifcopenshell
import ifcopenshell.api
class Usecase:
def __init__(self, file, work_schedule=None, work_plan=None):
"""Assigns a work schedule to a work plan
def assign_workplan(file, work_schedule=None, work_plan=None) -> None:
"""Assigns a work schedule to a work plan
Typically, work schedules would be assigned to a work plan at creation.
However you may also delay this and do it manually afterwards.
Typically, work schedules would be assigned to a work plan at creation.
However you may also delay this and do it manually afterwards.
:param work_schedule: The IfcWorkSchedule that will be assigned to the
work plan.
:type work_schedule: ifcopenshell.entity_instance
:param work_plan: The IfcWorkPlan for the schedule to be assigned to.
:type work_plan: ifcopenshell.entity_instance
:return: The IfcRelAggregates relationship
:rtype: ifcopenshell.entity_instance
:param work_schedule: The IfcWorkSchedule that will be assigned to the
work plan.
:type work_schedule: ifcopenshell.entity_instance
:param work_plan: The IfcWorkPlan for the schedule to be assigned to.
:type work_plan: ifcopenshell.entity_instance
:return: The IfcRelAggregates relationship
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# Alternatively, if you create a schedule without a work plan ...
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Alternatively, if you create a schedule without a work plan ...
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# ... you can assign the work plan afterwards.
ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan)
"""
self.file = file
self.settings = {"work_schedule": work_schedule, "work_plan": work_plan}
# ... you can assign the work plan afterwards.
ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan)
"""
settings = {"work_schedule": work_schedule, "work_plan": work_plan}
def execute(self):
# TODO: this is an ambiguity by buildingSMART
# See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
ifcopenshell.api.run(
"project.unassign_declaration",
self.file,
definitions=[self.settings["work_schedule"]],
relating_context=self.file.by_type("IfcContext")[0],
)
rel_aggregates = ifcopenshell.api.run(
"aggregate.assign_object",
self.file,
**{
"products": [self.settings["work_schedule"]],
"relating_object": self.settings["work_plan"],
}
)
return rel_aggregates
# TODO: this is an ambiguity by buildingSMART
# See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
ifcopenshell.api.run(
"project.unassign_declaration",
file,
definitions=[settings["work_schedule"]],
relating_context=file.by_type("IfcContext")[0],
)
rel_aggregates = ifcopenshell.api.run(
"aggregate.assign_object",
file,
**{
"products": [settings["work_schedule"]],
"relating_object": settings["work_plan"],
}
)
return rel_aggregates
@@ -22,68 +22,71 @@ import ifcopenshell.util.date
import ifcopenshell.util.element
def calculate_task_duration(file, task=None) -> None:
"""Calculates the task duration based on resource usage
If a task has labour or equipment resources assigned to it, its duration
may be parametrically derived from the scheduled work of the resource.
For example, a labour resource with scheduled work of 10 working days
and a resource utilisation of 200% (i.e. two labour teams) will imply
that the task duration is 5 working days.
If this data is not available, such as if the task has no resources,
then nothing happens.
:param task: The IfcTask to calculate the duration for.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# Add our own crew
crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
# Add some labour to our crew.
labour = ifcopenshell.api.run("resource.add_resource", model,
parent_resource=crew, ifc_class="IfcLaborResource")
# Labour resource is quantified in terms of time.
quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
resource=labour, ifc_class="IfcQuantityTime")
# Store the unit time used in hours
ifcopenshell.api.run("resource.edit_resource_quantity", model,
physical_quantity=quantity, attributes={"TimeValue": 8.0})
# Let's imagine we've used the resource for 10 days with a
# utilisation of 200%.
time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
ifcopenshell.api.run("resource.edit_resource_time", model,
resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2})
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Foundations", identification="A")
# Assign our resource to the task.
ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour)
# Now we can calculate the task duration based on the resource. This
# will set task.TaskTime.ScheduleDuration to be P5D.
ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task)
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"task": task}
return usecase.execute()
class Usecase:
def __init__(self, file, task=None):
"""Calculates the task duration based on resource usage
If a task has labour or equipment resources assigned to it, its duration
may be parametrically derived from the scheduled work of the resource.
For example, a labour resource with scheduled work of 10 working days
and a resource utilisation of 200% (i.e. two labour teams) will imply
that the task duration is 5 working days.
If this data is not available, such as if the task has no resources,
then nothing happens.
:param task: The IfcTask to calculate the duration for.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# Add our own crew
crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
# Add some labour to our crew.
labour = ifcopenshell.api.run("resource.add_resource", model,
parent_resource=crew, ifc_class="IfcLaborResource")
# Labour resource is quantified in terms of time.
quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
resource=labour, ifc_class="IfcQuantityTime")
# Store the unit time used in hours
ifcopenshell.api.run("resource.edit_resource_quantity", model,
physical_quantity=quantity, attributes={"TimeValue": 8.0})
# Let's imagine we've used the resource for 10 days with a
# utilisation of 200%.
time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
ifcopenshell.api.run("resource.edit_resource_time", model,
resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2})
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Foundations", identification="A")
# Assign our resource to the task.
ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour)
# Now we can calculate the task duration based on the resource. This
# will set task.TaskTime.ScheduleDuration to be P5D.
ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task)
"""
self.file = file
self.settings = {"task": task}
def execute(self):
self.seconds_per_workday = self.calculate_seconds_per_workday()
duration = self.calculate_max_resource_usage_duration()
@@ -93,9 +96,7 @@ class Usecase:
def calculate_seconds_per_workday(self):
def get_work_schedule(task):
for rel in task.HasAssignments or []:
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a(
"IfcWorkSchedule"
):
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"):
return rel.RelatingControl
for rel in task.Nests or []:
return get_work_schedule(rel.RelatingObject)
@@ -111,9 +112,7 @@ class Usecase:
or "WorkDayDuration" not in psets["Pset_WorkControlCommon"]
):
return default_seconds_per_workday
work_day_duration = ifcopenshell.util.date.ifc2datetime(
psets["Pset_WorkControlCommon"]["WorkDayDuration"]
)
work_day_duration = ifcopenshell.util.date.ifc2datetime(psets["Pset_WorkControlCommon"]["WorkDayDuration"])
return work_day_duration.seconds
def calculate_max_resource_usage_duration(self):
@@ -133,23 +132,15 @@ class Usecase:
if not resource.Usage or not resource.Usage.ScheduleWork:
return
schedule_usage = resource.Usage.ScheduleUsage or 1
schedule_duration = ifcopenshell.util.date.ifc2datetime(
resource.Usage.ScheduleWork
)
schedule_duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork)
if is_hourly_work(resource.Usage.ScheduleWork):
schedule_seconds = (
schedule_duration.days * 24 * 60 * 60
) + schedule_duration.seconds
schedule_seconds = (schedule_duration.days * 24 * 60 * 60) + schedule_duration.seconds
else:
partial_days = schedule_duration.seconds / (24 * 60 * 60)
schedule_seconds = (
schedule_duration.days + partial_days
) * self.seconds_per_workday
schedule_seconds = (schedule_duration.days + partial_days) * self.seconds_per_workday
return math.ceil((schedule_seconds / self.seconds_per_workday) / schedule_usage)
def set_task_duration(self, duration):
if not self.settings["task"].TaskTime:
ifcopenshell.api.run(
"sequence.add_task_time", self.file, task=self.settings["task"]
)
ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.settings["task"])
self.settings["task"].TaskTime.ScheduleDuration = f"P{duration}D"
@@ -21,90 +21,93 @@ import ifcopenshell.util.date
import ifcopenshell.util.sequence
def cascade_schedule(file, task=None) -> None:
"""Cascades start and end dates of tasks based on durations
Given a start task with a start date and duration, the end date, and the
start and end of all successor tasks with durations may be automatically
computed.
Using this automatic computation is recommended is an alternative to
manually specifying dates. It is useful for doing edits and cascading
changes.
Dates can only cascade from predecessor to successors, not backwards.
Cyclical relationships are invalid and will result in a recursion error
being raised.
Note that there may be differences between how different planning
software calculate start and end dates. Some may consider Monday 5pm to
be equivalent to be Tuesday 8am, for instance.
:param task: The start task to begin cascading from.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# Define a convenience function to add a task chained to a predecessor
def add_task(model, name, predecessor, work_schedule):
# Add a construction task
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION")
# Give it a time
task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
# Arbitrarily set the task's scheduled time duration to be 1 week
ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time,
attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"})
# If a predecessor exists, create a finish to start relationship
if predecessor:
ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=predecessor, related_process=task)
return task
# Open an existing IFC4 model you have of a building
model = ifcopenshell.open("/path/to/existing/model.ifc")
# Create a new construction schedule
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction")
# Let's imagine a starting task for site establishment.
task = add_task(model, "Site establishment", None, schedule)
start_task = task
# Get all our storeys sorted by elevation ascending.
storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s))
# For each storey ...
for storey in storeys:
# Add a construction task to construct that storey, using our convenience function
task = add_task(model, f"Construct {storey.Name}", task, schedule)
# Assign all the products in that storey to the task as construction outputs.
for product in get_decomposition(storey):
ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task)
# Ask the computer to calculate all the dates for us from the start task.
# For example, if the first task started on the 1st of January and took a
# week, the next task will start on the 8th of January. This saves us
# manually doing date calculations.
ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task)
# Calculate the critical path and floats.
ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule)
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"task": task}
return usecase.execute()
class Usecase:
def __init__(self, file, task=None):
"""Cascades start and end dates of tasks based on durations
Given a start task with a start date and duration, the end date, and the
start and end of all successor tasks with durations may be automatically
computed.
Using this automatic computation is recommended is an alternative to
manually specifying dates. It is useful for doing edits and cascading
changes.
Dates can only cascade from predecessor to successors, not backwards.
Cyclical relationships are invalid and will result in a recursion error
being raised.
Note that there may be differences between how different planning
software calculate start and end dates. Some may consider Monday 5pm to
be equivalent to be Tuesday 8am, for instance.
:param task: The start task to begin cascading from.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# Define a convenience function to add a task chained to a predecessor
def add_task(model, name, predecessor, work_schedule):
# Add a construction task
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION")
# Give it a time
task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
# Arbitrarily set the task's scheduled time duration to be 1 week
ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time,
attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"})
# If a predecessor exists, create a finish to start relationship
if predecessor:
ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=predecessor, related_process=task)
return task
# Open an existing IFC4 model you have of a building
model = ifcopenshell.open("/path/to/existing/model.ifc")
# Create a new construction schedule
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction")
# Let's imagine a starting task for site establishment.
task = add_task(model, "Site establishment", None, schedule)
start_task = task
# Get all our storeys sorted by elevation ascending.
storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s))
# For each storey ...
for storey in storeys:
# Add a construction task to construct that storey, using our convenience function
task = add_task(model, f"Construct {storey.Name}", task, schedule)
# Assign all the products in that storey to the task as construction outputs.
for product in get_decomposition(storey):
ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task)
# Ask the computer to calculate all the dates for us from the start task.
# For example, if the first task started on the 1st of January and took a
# week, the next task will start on the 8th of January. This saves us
# manually doing date calculations.
ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task)
# Calculate the critical path and floats.
ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule)
"""
self.file = file
self.settings = {"task": task}
def execute(self):
self.calendar_cache = {}
self.cascade_task(self.settings["task"], is_first_task=True)
@@ -135,14 +138,10 @@ class Usecase:
finishes = []
starts = []
for rel in ifcopenshell.util.sequence.get_sequence_assignment(
task, "predecessor"
):
for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor"):
predecessor = rel.RelatingProcess
predecessor_duration = (
ifcopenshell.util.date.ifc2datetime(
predecessor.TaskTime.ScheduleDuration
)
ifcopenshell.util.date.ifc2datetime(predecessor.TaskTime.ScheduleDuration)
if predecessor.TaskTime and predecessor.TaskTime.ScheduleDuration
else datetime.timedelta()
)
@@ -154,14 +153,16 @@ class Usecase:
duration_type = "WORKTIME"
if rel.TimeLag:
# updated to handle IfcRatioMeasure as a TimeLag value
days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
days += (
self.get_lag_time_days(rel.TimeLag)
if rel.TimeLag.LagValue.is_a("IfcDuration")
else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
)
duration_type = rel.TimeLag.DurationType
if days:
starts.append(
datetime.datetime.combine(
self.offset_date(
finish, days, duration_type, self.get_calendar(task)
),
self.offset_date(finish, days, duration_type, self.get_calendar(task)),
datetime.time(9),
)
)
@@ -183,18 +184,14 @@ class Usecase:
if not start:
continue
if rel.TimeLag:
days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
days = (
self.get_lag_time_days(rel.TimeLag)
if rel.TimeLag.LagValue.is_a("IfcDuration")
else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
)
duration_type = rel.TimeLag.DurationType
starts.append(
self.offset_date(
start, days, duration_type, self.get_calendar(task)
)
)
starts.append(
self.offset_date(
start, days, duration_type, self.get_calendar(predecessor)
)
)
starts.append(self.offset_date(start, days, duration_type, self.get_calendar(task)))
starts.append(self.offset_date(start, days, duration_type, self.get_calendar(predecessor)))
else:
starts.append(start)
elif rel.SequenceType == "FINISH_FINISH":
@@ -202,18 +199,14 @@ class Usecase:
if not finish:
continue
if rel.TimeLag:
days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
days = (
self.get_lag_time_days(rel.TimeLag)
if rel.TimeLag.LagValue.is_a("IfcDuration")
else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
)
duration_type = rel.TimeLag.DurationType
finishes.append(
self.offset_date(
finish, days, duration_type, self.get_calendar(task)
)
)
finishes.append(
self.offset_date(
finish, days, duration_type, self.get_calendar(predecessor)
)
)
finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(task)))
finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(predecessor)))
else:
finishes.append(finish)
elif rel.SequenceType == "START_FINISH":
@@ -223,14 +216,16 @@ class Usecase:
days = -1
duration_type = "WORKTIME"
if rel.TimeLag:
days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
days += (
self.get_lag_time_days(rel.TimeLag)
if rel.TimeLag.LagValue.is_a("IfcDuration")
else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
)
duration_type = rel.TimeLag.DurationType
if days or rel.TimeLag:
finishes.append(
datetime.datetime.combine(
self.offset_date(
start, days, duration_type, self.get_calendar(task)
),
self.offset_date(start, days, duration_type, self.get_calendar(task)),
datetime.time(17),
)
)
@@ -263,9 +258,7 @@ class Usecase:
if task.TaskTime.ScheduleStart == start_ifc and not is_first_task:
return
task.TaskTime.ScheduleStart = start_ifc
task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(
potential_finish, "IfcDateTime"
)
task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(potential_finish, "IfcDateTime")
else:
finish_ifc = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime")
if task.TaskTime.ScheduleFinish == finish_ifc and not is_first_task:
@@ -328,15 +321,11 @@ class Usecase:
def get_calendar(self, task):
if task.id() not in self.calendar_cache:
self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(
task
)
self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(task)
return self.calendar_cache[task.id()]
def offset_date(self, date, days, duration_type, calendar):
return ifcopenshell.util.sequence.offset_date(
date, datetime.timedelta(days=days), duration_type, calendar
)
return ifcopenshell.util.sequence.offset_date(date, datetime.timedelta(days=days), duration_type, calendar)
def get_task_time_attribute(self, task, attribute):
if task.TaskTime:
@@ -21,38 +21,41 @@ import ifcopenshell.util.system
import ifcopenshell.util.element
def create_baseline(file, work_schedule=None, name=None) -> None:
"""Creates a baseline for your Work Schedule
Using a IfcWorkSchdule having PredefinedType=PLANNED,
We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE
and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline.
The following relationships are also baselined:
* Same Tasks & attributes
* Same Task Relationships
* Same Construction Resources
* Same Resource Relationships
:param work_schedule: The planned work_schedule to baseline
:type work_schedule: ifcopenshell.entity_instance
:return: The baseline work_schedule
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# We have a Work Schedule
planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01")
# And now we have a baseline for our Work Schedule
baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1")
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"work_schedule": work_schedule, "name": name}
return usecase.execute()
class Usecase:
def __init__(self, file, work_schedule=None, name=None):
"""Creates a baseline for your Work Schedule
Using a IfcWorkSchdule having PredefinedType=PLANNED,
We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE
and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline.
The following relationships are also baselined:
* Same Tasks & attributes
* Same Task Relationships
* Same Construction Resources
* Same Resource Relationships
:param work_schedule: The planned work_schedule to baseline
:type work_schedule: ifcopenshell.entity_instance
:return: The baseline work_schedule
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# We have a Work Schedule
planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01")
# And now we have a baseline for our Work Schedule
baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1")
"""
self.file = file
self.settings = {"work_schedule": work_schedule, "name": name}
def execute(self):
result = self.create_baseline_work_schedule(self.settings["work_schedule"])
return result
@@ -92,17 +95,13 @@ class Usecase:
related_objects = list(referenced_by.RelatedObjects)
related_objects.append(related_object)
referenced_by.RelatedObjects = related_objects
ifcopenshell.api.run(
"owner.update_owner_history", self.file, **{"element": referenced_by}
)
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by})
else:
referenced_by = self.file.create_entity(
"IfcRelDefinesByObject",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run(
"owner.create_owner_history", self.file
),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
"RelatedObjects": [related_object],
"RelatingObject": relating_object,
}
@@ -22,33 +22,36 @@ import ifcopenshell.util.element
import ifcopenshell.util.sequence
def duplicate_task(file, task=None) -> None:
"""Duplicates a task in the project
The following relationships are also duplicated:
* The copy will have the same attributes and property sets as the original task
* The copy will be assigned to the parent task or work schedule
* The copy will have duplicated nested tasks
:param task: The task to be duplicated
:type task: ifcopenshell.entity_instance
:return: The duplicated task or the list of duplicated tasks if the latter has children
:rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
Example:
.. code:: python
# We have a task
original_task = Task(name="Design new feature", deadline="2023-03-01")
# And now we have two
duplicated_task = project.duplicate_task(original_task)
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"task": task}
return usecase.execute()
class Usecase:
def __init__(self, file, task=None):
"""Duplicates a task in the project
The following relationships are also duplicated:
* The copy will have the same attributes and property sets as the original task
* The copy will be assigned to the parent task or work schedule
* The copy will have duplicated nested tasks
:param task: The task to be duplicated
:type task: ifcopenshell.entity_instance
:return: The duplicated task or the list of duplicated tasks if the latter has children
:rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
Example:
.. code:: python
# We have a task
original_task = Task(name="Design new feature", deadline="2023-03-01")
# And now we have two
duplicated_task = project.duplicate_task(original_task)
"""
self.file = file
self.settings = {"task": task}
def execute(self):
self.tracker = {"current": [], "duplicate": []}
self.duplicate_task(self.settings["task"])
@@ -20,79 +20,68 @@ import ifcopenshell.api
import ifcopenshell.util.date
class Usecase:
def __init__(self, file, lag_time=None, attributes=None):
"""Edits the attributes of an IfcLagTime
def edit_lag_time(file, lag_time=None, attributes=None) -> None:
"""Edits the attributes of an IfcLagTime
For more information about the attributes and data types of an
IfcLagTime, consult the IFC documentation.
For more information about the attributes and data types of an
IfcLagTime, consult the IFC documentation.
:param lag_time: The IfcLagTime entity you want to edit
:type lag_time: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param lag_time: The IfcLagTime entity you want to edit
:type lag_time: 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
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine we're doing a typically formwork, reinforcement,
# pour sequence. Let's start with the formwork. It'll take us 2
# days.
formwork = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Formwork", identification="C.1")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Let's imagine we're doing a typically formwork, reinforcement,
# pour sequence. Let's start with the formwork. It'll take us 2
# days.
formwork = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Formwork", identification="C.1")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's do the reinforcement. It'll take us another 2 days.
reinforcement = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.2")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's do the reinforcement. It'll take us another 2 days.
reinforcement = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Reinforcement", identification="C.2")
time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
# Now let's say the formwork must finish before the reinforcement
# can start. This is a typical finish to start relationship (FS).
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=formwork, related_process=reinforcement)
# Now let's say the formwork must finish before the reinforcement
# can start. This is a typical finish to start relationship (FS).
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=formwork, related_process=reinforcement)
# Now typically there would be no lag time between formwork and
# reinforcement, but let's pretend that we had to allow 1 day gap
# for whatever reason.
lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
# Now typically there would be no lag time between formwork and
# reinforcement, but let's pretend that we had to allow 1 day gap
# for whatever reason.
lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
# Or, let's make it 2 days instead.
ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"})
"""
self.file = file
self.settings = {"lag_time": lag_time, "attributes": attributes or {}}
# Or, let's make it 2 days instead.
ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"})
"""
settings = {"lag_time": lag_time, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
if name == "LagValue" and value is not None:
if isinstance(value, float):
value = self.file.createIfcRatioMeasure(value)
else:
value = self.file.createIfcDuration(
ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
)
setattr(self.settings["lag_time"], name, value)
for rel in [
r
for r in self.file.get_inverse(self.settings["lag_time"])
if r.is_a("IfcRelSequence")
]:
ifcopenshell.api.run(
"sequence.cascade_schedule", self.file, task=rel.RelatedProcess
)
for name, value in settings["attributes"].items():
if name == "LagValue" and value is not None:
if isinstance(value, float):
value = file.createIfcRatioMeasure(value)
else:
value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(value, "IfcDuration"))
setattr(settings["lag_time"], name, value)
for rel in [r for r in file.get_inverse(settings["lag_time"]) if r.is_a("IfcRelSequence")]:
ifcopenshell.api.run("sequence.cascade_schedule", file, task=rel.RelatedProcess)
@@ -20,48 +20,45 @@ import ifcopenshell
import ifcopenshell.util.sequence
class Usecase:
def __init__(self, file, recurrence_pattern=None, attributes=None):
"""Edits the attributes of an IfcRecurrencePattern
def edit_recurrence_pattern(file, recurrence_pattern=None, attributes=None) -> None:
"""Edits the attributes of an IfcRecurrencePattern
For more information about the attributes and data types of an
IfcRecurrencePattern, consult the IFC documentation.
For more information about the attributes and data types of an
IfcRecurrencePattern, consult the IFC documentation.
:param recurrence_pattern: The IfcRecurrencePattern entity you want to edit
:type recurrence_pattern: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param recurrence_pattern: The IfcRecurrencePattern entity you want to edit
:type recurrence_pattern: 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
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
"""
self.file = file
self.settings = {
"recurrence_pattern": recurrence_pattern,
"attributes": attributes or {},
}
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
"""
settings = {
"recurrence_pattern": recurrence_pattern,
"attributes": attributes or {},
}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["recurrence_pattern"], name, value)
for name, value in settings["attributes"].items():
setattr(settings["recurrence_pattern"], name, value)
ifcopenshell.util.sequence.is_working_day.cache_clear()
ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
ifcopenshell.util.sequence.is_working_day.cache_clear()
ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
@@ -20,55 +20,52 @@ import ifcopenshell
import ifcopenshell.api
class Usecase:
def __init__(self, file, rel_sequence=None, attributes=None):
"""Edits the attributes of an IfcRelSequence
def edit_sequence(file, rel_sequence=None, attributes=None) -> None:
"""Edits the attributes of an IfcRelSequence
For more information about the attributes and data types of an
IfcRelSequence, consult the IFC documentation.
For more information about the attributes and data types of an
IfcRelSequence, consult the IFC documentation.
:param rel_sequence: The IfcRelSequence entity you want to edit
:type rel_sequence: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param rel_sequence: The IfcRelSequence entity you want to edit
:type rel_sequence: 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
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine we're building 2 zones, one after another.
zone1 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 1", identification="C.1")
zone2 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 2", identification="C.2")
# Let's imagine we're building 2 zones, one after another.
zone1 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 1", identification="C.1")
zone2 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 2", identification="C.2")
# Zone 1 finishes, then zone 2 starts.
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=zone1, related_process=zone2)
# Zone 1 finishes, then zone 2 starts.
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=zone1, related_process=zone2)
# What if they both started at the same time?
ifcopenshell.api.run("sequence.edit_sequence", model,
rel_sequence=sequence, attributes={"SequenceType": "START_START"})
"""
self.file = file
self.settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}}
# What if they both started at the same time?
ifcopenshell.api.run("sequence.edit_sequence", model,
rel_sequence=sequence, attributes={"SequenceType": "START_START"})
"""
settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["rel_sequence"], name, value)
if "SequenceType" in self.settings["attributes"].keys():
ifcopenshell.api.run(
"sequence.cascade_schedule",
self.file,
task=self.settings["rel_sequence"].RelatedProcess,
)
for name, value in settings["attributes"].items():
setattr(settings["rel_sequence"], name, value)
if "SequenceType" in settings["attributes"].keys():
ifcopenshell.api.run(
"sequence.cascade_schedule",
file,
task=settings["rel_sequence"].RelatedProcess,
)
@@ -17,39 +17,36 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, task=None, attributes=None):
"""Edits the attributes of an IfcTask
def edit_task(file, task=None, attributes=None) -> None:
"""Edits the attributes of an IfcTask
For more information about the attributes and data types of an
IfcTask, consult the IFC documentation.
For more information about the attributes and data types of an
IfcTask, consult the IFC documentation.
:param task: The IfcTask entity you want to edit
:type task: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param task: The IfcTask entity you want to edit
:type task: 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
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Add a root task to represent the design milestones, and major
# project phases.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
# Add a root task to represent the design milestones, and major
# project phases.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
# Change the identification
ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"})
"""
self.file = file
self.settings = {"task": task, "attributes": attributes or {}}
# Change the identification
ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"})
"""
settings = {"task": task, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["task"], name, value)
for name, value in settings["attributes"].items():
setattr(settings["task"], name, value)
@@ -23,46 +23,48 @@ import ifcopenshell.util.sequence
from typing import Any, Optional
def edit_task_time(
file: ifcopenshell.file,
task_time: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
) -> None:
"""Edits the attributes of an IfcTaskTime
For more information about the attributes and data types of an
IfcTaskTime, consult the IFC documentation.
:param task_time: The IfcTaskTime entity you want to edit
:type task_time: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Create a task to do formwork
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Formwork", identification="A")
# Let's say it takes 2 days and starts on the 1st of January, 2000
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"task_time": task_time, "attributes": attributes or {}}
return usecase.execute()
class Usecase:
def __init__(
self,
file: ifcopenshell.file,
task_time: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
):
"""Edits the attributes of an IfcTaskTime
For more information about the attributes and data types of an
IfcTaskTime, consult the IFC documentation.
:param task_time: The IfcTaskTime entity you want to edit
:type task_time: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Create a task to do formwork
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Formwork", identification="A")
# Let's say it takes 2 days and starts on the 1st of January, 2000
time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
ifcopenshell.api.run("sequence.edit_task_time", model,
task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
"""
self.file = file
self.settings = {"task_time": task_time, "attributes": attributes or {}}
def execute(self) -> None:
def execute(self):
self.task = self.get_task()
self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task)
@@ -73,17 +75,13 @@ class Usecase:
):
del self.settings["attributes"]["ScheduleFinish"]
duration_type = self.settings["attributes"].get(
"DurationType", self.settings["task_time"].DurationType
)
duration_type = self.settings["attributes"].get("DurationType", self.settings["task_time"].DurationType)
finish = self.settings["attributes"].get("ScheduleFinish", None)
if finish:
if isinstance(finish, str):
finish = datetime.datetime.fromisoformat(finish)
self.settings["attributes"]["ScheduleFinish"] = datetime.datetime.combine(
ifcopenshell.util.sequence.get_soonest_working_day(
finish, duration_type, self.calendar
),
ifcopenshell.util.sequence.get_soonest_working_day(finish, duration_type, self.calendar),
datetime.time(17),
)
start = self.settings["attributes"].get("ScheduleStart", None)
@@ -91,9 +89,7 @@ class Usecase:
if isinstance(start, str):
start = datetime.datetime.fromisoformat(start)
self.settings["attributes"]["ScheduleStart"] = datetime.datetime.combine(
ifcopenshell.util.sequence.get_soonest_working_day(
start, duration_type, self.calendar
),
ifcopenshell.util.sequence.get_soonest_working_day(start, duration_type, self.calendar),
datetime.time(9),
)
@@ -101,11 +97,7 @@ class Usecase:
if value is not None:
if "Start" in name or "Finish" in name or name == "StatusTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif (
name == "ScheduleDuration"
or name == "ActualDuration"
or name == "RemainingTime"
):
elif name == "ScheduleDuration" or name == "ActualDuration" or name == "RemainingTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["task_time"], name, value)
@@ -115,15 +107,9 @@ class Usecase:
and self.settings["task_time"].ScheduleStart
):
self.calculate_finish()
elif (
self.settings["attributes"].get("ScheduleStart", None)
and self.settings["task_time"].ScheduleDuration
):
elif self.settings["attributes"].get("ScheduleStart", None) and self.settings["task_time"].ScheduleDuration:
self.calculate_finish()
elif (
self.settings["attributes"].get("ScheduleFinish", None)
and self.settings["task_time"].ScheduleStart
):
elif self.settings["attributes"].get("ScheduleFinish", None) and self.settings["task_time"].ScheduleStart:
self.calculate_duration()
if self.settings["task_time"].ScheduleDuration and (
@@ -137,57 +123,36 @@ class Usecase:
def calculate_finish(self):
finish = ifcopenshell.util.sequence.get_start_or_finish_date(
ifcopenshell.util.date.ifc2datetime(
self.settings["task_time"].ScheduleStart
),
ifcopenshell.util.date.ifc2datetime(
self.settings["task_time"].ScheduleDuration
),
ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart),
ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleDuration),
self.settings["task_time"].DurationType,
self.calendar,
date_type="FINISH",
)
self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(
finish, "IfcDateTime"
)
self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime")
def calculate_duration(self):
start = ifcopenshell.util.date.ifc2datetime(
self.settings["task_time"].ScheduleStart
)
finish = ifcopenshell.util.date.ifc2datetime(
self.settings["task_time"].ScheduleFinish
)
start = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart)
finish = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleFinish)
current_date = datetime.date(start.year, start.month, start.day)
finish_date = datetime.date(finish.year, finish.month, finish.day)
duration = datetime.timedelta(days=1)
while current_date < finish_date:
if (
self.settings["task_time"].DurationType == "ELAPSEDTIME"
or not self.calendar
):
if self.settings["task_time"].DurationType == "ELAPSEDTIME" or not self.calendar:
duration += datetime.timedelta(days=1)
elif ifcopenshell.util.sequence.is_working_day(current_date, self.calendar):
duration += datetime.timedelta(days=1)
current_date += datetime.timedelta(days=1)
self.settings[
"task_time"
].ScheduleDuration = ifcopenshell.util.date.datetime2ifc(
duration, "IfcDuration"
)
self.settings["task_time"].ScheduleDuration = ifcopenshell.util.date.datetime2ifc(duration, "IfcDuration")
def get_task(self) -> ifcopenshell.entity_instance:
return next(
e
for e in self.file.get_inverse(self.settings["task_time"])
if e.is_a("IfcTask")
)
return next(e for e in self.file.get_inverse(self.settings["task_time"]) if e.is_a("IfcTask"))
def handle_resource_calculation(self):
resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False)
for resource in resources:
if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleWork"):
ifcopenshell.api.run("resource.calculate_resource_usage", self.file, resource=resource)
#TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated.
# TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated.
# elif ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleUsage"):
# ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=resource)
@@ -17,34 +17,31 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, work_calendar=None, attributes=None):
"""Edits the attributes of an IfcWorkCalendar
def edit_work_calendar(file, work_calendar=None, attributes=None) -> None:
"""Edits the attributes of an IfcWorkCalendar
For more information about the attributes and data types of an
IfcWorkCalendar, consult the IFC documentation.
For more information about the attributes and data types of an
IfcWorkCalendar, consult the IFC documentation.
:param work_calendar: The IfcWorkCalendar entity you want to edit
:type work_calendar: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param work_calendar: The IfcWorkCalendar entity you want to edit
:type work_calendar: 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
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
# Let's give it a description
ifcopenshell.api.run("sequence.edit_work_calendar", model,
work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"})
"""
self.file = file
self.settings = {"work_calendar": work_calendar, "attributes": attributes or {}}
# Let's give it a description
ifcopenshell.api.run("sequence.edit_work_calendar", model,
work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"})
"""
settings = {"work_calendar": work_calendar, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["work_calendar"], name, value)
for name, value in settings["attributes"].items():
setattr(settings["work_calendar"], name, value)
@@ -19,39 +19,36 @@
import ifcopenshell.util.date
class Usecase:
def __init__(self, file, work_plan=None, attributes=None):
"""Edits the attributes of an IfcWorkPlan
def edit_work_plan(file, work_plan=None, attributes=None) -> None:
"""Edits the attributes of an IfcWorkPlan
For more information about the attributes and data types of an
IfcWorkPlan, consult the IFC documentation.
For more information about the attributes and data types of an
IfcWorkPlan, consult the IFC documentation.
:param work_plan: The IfcWorkPlan entity you want to edit
:type work_plan: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param work_plan: The IfcWorkPlan entity you want to edit
:type work_plan: 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
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# Let's give it a description
ifcopenshell.api.run("sequence.edit_work_plan", model,
work_plan=work_plan, attributes={"Description": "Construction of phase 1"})
"""
self.file = file
self.settings = {"work_plan": work_plan, "attributes": attributes or {}}
# Let's give it a description
ifcopenshell.api.run("sequence.edit_work_plan", model,
work_plan=work_plan, attributes={"Description": "Construction of phase 1"})
"""
settings = {"work_plan": work_plan, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
if value:
if "Date" in name or "Time" in name:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif name == "Duration" or name == "TotalFloat":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["work_plan"], name, value)
for name, value in settings["attributes"].items():
if value:
if "Date" in name or "Time" in name:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif name == "Duration" or name == "TotalFloat":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(settings["work_plan"], name, value)
@@ -19,43 +19,40 @@
import ifcopenshell.util.date
class Usecase:
def __init__(self, file, work_schedule=None, attributes=None):
"""Edits the attributes of an IfcWorkSchedule
def edit_work_schedule(file, work_schedule=None, attributes=None) -> None:
"""Edits the attributes of an IfcWorkSchedule
For more information about the attributes and data types of an
IfcWorkSchedule, consult the IFC documentation.
For more information about the attributes and data types of an
IfcWorkSchedule, consult the IFC documentation.
:param work_schedule: The IfcWorkSchedule entity you want to edit
:type work_schedule: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param work_schedule: The IfcWorkSchedule entity you want to edit
:type work_schedule: 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
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# Let's imagine this is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
# Let's imagine this is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
# Let's give it a description
ifcopenshell.api.run("sequence.edit_work_schedule", model,
work_schedule=work_schedule, attributes={"Description": "3 crane design option"})
"""
self.file = file
self.settings = {"work_schedule": work_schedule, "attributes": attributes or {}}
# Let's give it a description
ifcopenshell.api.run("sequence.edit_work_schedule", model,
work_schedule=work_schedule, attributes={"Description": "3 crane design option"})
"""
settings = {"work_schedule": work_schedule, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
if value:
if "Date" in name or "Time" in name:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif name == "Duration" or name == "TotalFloat":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["work_schedule"], name, value)
for name, value in settings["attributes"].items():
if value:
if "Date" in name or "Time" in name:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif name == "Duration" or name == "TotalFloat":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(settings["work_schedule"], name, value)
@@ -20,54 +20,50 @@ import ifcopenshell.util.date
from typing import Any, Optional
class Usecase:
def __init__(
self,
file: ifcopenshell.file,
work_time: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
):
"""Edits the attributes of an IfcWorkTime
def edit_work_time(
file: ifcopenshell.file,
work_time: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
) -> None:
"""Edits the attributes of an IfcWorkTime
For more information about the attributes and data types of an
IfcWorkTime, consult the IFC documentation.
For more information about the attributes and data types of an
IfcWorkTime, consult the IFC documentation.
:param work_time: The IfcWorkTime entity you want to edit
:type work_time: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param work_time: The IfcWorkTime entity you want to edit
:type work_time: 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
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# If we don't specify any recurring time periods in our work time,
# we need to specify a start and end date of the work time. It
# starts at 0:00 on the start date and 24:00 at the end date.
ifcopenshell.api.run("sequence.edit_work_time", model,
work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"})
"""
self.file = file
self.settings = {"work_time": work_time, "attributes": attributes or {}}
# If we don't specify any recurring time periods in our work time,
# we need to specify a start and end date of the work time. It
# starts at 0:00 on the start date and 24:00 at the end date.
ifcopenshell.api.run("sequence.edit_work_time", model,
work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"})
"""
settings = {"work_time": work_time, "attributes": attributes or {}}
def execute(self) -> None:
for name, value in self.settings["attributes"].items():
if name in ("Start", "StartDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
# 4 IfcWorktime Start
self.settings["work_time"][4] = value
elif name in ("Finish", "FinishDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
# 5 IfcWorktime Finish
self.settings["work_time"][5] = value
else:
setattr(self.settings["work_time"], name, value)
for name, value in settings["attributes"].items():
if name in ("Start", "StartDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
# 4 IfcWorktime Start
settings["work_time"][4] = value
elif name in ("Finish", "FinishDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
# 5 IfcWorktime Finish
settings["work_time"][5] = value
else:
setattr(settings["work_time"], name, value)
@@ -19,61 +19,58 @@
import ifcopenshell
class Usecase:
def __init__(self, file, relating_product=None, related_object=None):
"""Gets the related products being output by a task
def get_related_products(file, relating_product=None, related_object=None) -> None:
"""Gets the related products being output by a task
This API function will be removed in the future and migrated to a
utility module.
This API function will be removed in the future and migrated to a
utility module.
:param relating_product: One of the products already output by the task.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcTask that you want to get all the related
products for.
:type related_object: ifcopenshell.entity_instance
:return: A set of IfcProducts output by the IfcTask.
:rtype: set[ifcopenshell.entity_instance]
:param relating_product: One of the products already output by the task.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcTask that you want to get all the related
products for.
:type related_object: ifcopenshell.entity_instance
:return: A set of IfcProducts output by the IfcTask.
:rtype: set[ifcopenshell.entity_instance]
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's construct that wall!
ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
# Let's construct that wall!
ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
# This will give us a set with that wall in it.
products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task)
"""
self.file = file
self.settings = {
"relating_product": relating_product,
"related_object": related_object,
}
# This will give us a set with that wall in it.
products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
def execute(self):
products = set()
related_object = None
if self.settings["related_object"]:
related_object = self.settings["related_object"]
elif self.settings["relating_product"]:
for reference in self.settings["relating_product"].ReferencedBy:
if reference.is_a("IfcRelAssignsToProduct"):
related_object = reference.RelatedObjects[0]
if related_object:
assignments = self.settings["related_object"].HasAssignments
for assignment in assignments:
if assignment.is_a("IfcRelAssignsToProduct"):
products.add(assignment.RelatingProduct.id())
return products
products = set()
related_object = None
if settings["related_object"]:
related_object = settings["related_object"]
elif settings["relating_product"]:
for reference in settings["relating_product"].ReferencedBy:
if reference.is_a("IfcRelAssignsToProduct"):
related_object = reference.RelatedObjects[0]
if related_object:
assignments = settings["related_object"].HasAssignments
for assignment in assignments:
if assignment.is_a("IfcRelAssignsToProduct"):
products.add(assignment.RelatingProduct.id())
return products
@@ -23,35 +23,38 @@ import ifcopenshell.util.date
import ifcopenshell.util.sequence
def recalculate_schedule(file, work_schedule=None) -> None:
"""Calculate the critical path and floats for a work schedule
This implements critical path analysis, using the forward pass and
backward pass method. When run, any tasks that have no float will be
marked as critical, and both the total and free floats will be
populated for all task times.
Cyclical relationships are detected and will result in a recursion
error.
:param work_schedule: The IfcWorkSchedule to perform the calculation on.
:type work_schedule: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# See the example for ifcopenshell.api.sequence.cascade_schedule for
# details of how to set up a basic set of tasks and calculate the
# critical path. Typically cascade_schedule is run prior to ensure
# that dates are correct.
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"work_schedule": work_schedule}
return usecase.execute()
class Usecase:
def __init__(self, file, work_schedule=None):
"""Calculate the critical path and floats for a work schedule
This implements critical path analysis, using the forward pass and
backward pass method. When run, any tasks that have no float will be
marked as critical, and both the total and free floats will be
populated for all task times.
Cyclical relationships are detected and will result in a recursion
error.
:param work_schedule: The IfcWorkSchedule to perform the calculation on.
:type work_schedule: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# See the example for ifcopenshell.api.sequence.cascade_schedule for
# details of how to set up a basic set of tasks and calculate the
# critical path. Typically cascade_schedule is run prior to ensure
# that dates are correct.
"""
self.file = file
self.settings = {"work_schedule": work_schedule}
def execute(self):
# The method implemented is the same as shown here:
# https://www.youtube.com/watch?v=qTErIV6OqLg
@@ -84,9 +87,7 @@ class Usecase:
break # We have an infinite loop due to a cyclic graph
if is_cyclic:
raise RecursionError(
"Task graph is cyclic and so critical path method cannot be performed."
)
raise RecursionError("Task graph is cyclic and so critical path method cannot be performed.")
return
self.pending_nodes = set(self.g.nodes)
@@ -112,9 +113,7 @@ class Usecase:
self.g = nx.DiGraph()
self.edges = []
self.g.add_node("start", duration=0, duration_type="ELAPSEDTIME", calendar=None)
self.g.add_node(
"finish", duration=0, duration_type="ELAPSEDTIME", calendar=None
)
self.g.add_node("finish", duration=0, duration_type="ELAPSEDTIME", calendar=None)
for rel in self.settings["work_schedule"].Controls:
for related_object in rel.RelatedObjects:
if not related_object.is_a("IfcTask"):
@@ -129,9 +128,7 @@ class Usecase:
return
if task.TaskTime and task.TaskTime.ScheduleDuration:
duration = ifcopenshell.util.date.ifc2datetime(
task.TaskTime.ScheduleDuration
).days
duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration).days
duration_type = task.TaskTime.DurationType
else:
duration = 0
@@ -150,11 +147,11 @@ class Usecase:
rel.RelatingProcess.id(),
task.id(),
{
"lag_time": 0
if not rel.TimeLag
else ifcopenshell.util.date.ifc2datetime(
rel.TimeLag.LagValue.wrappedValue
).days,
"lag_time": (
0
if not rel.TimeLag
else ifcopenshell.util.date.ifc2datetime(rel.TimeLag.LagValue.wrappedValue).days
),
"type": self.sequence_type_map[rel.SequenceType],
},
)
@@ -162,16 +159,20 @@ class Usecase:
]
)
predecessor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor")]
successor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor")]
predecessor_types = [
rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor")
]
successor_types = [
rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor")
]
if not predecessor_types:
self.edges.append(("start", task.id(), {"lag_time": 0, "type": "FS"}))
if task.TaskTime and task.TaskTime.ScheduleStart:
self.start_dates.append(
ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart)
)
self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart) # we assume this task is constrained to start on this date
self.start_dates.append(ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart))
self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime(
task.TaskTime.ScheduleStart
) # we assume this task is constrained to start on this date
if not successor_types:
self.edges.append((task.id(), "finish", {"lag_time": 0, "type": "FF"}))
@@ -188,25 +189,13 @@ class Usecase:
self.file,
task_time=task.TaskTime,
attributes={
"FreeFloat": ifcopenshell.util.date.datetime2ifc(
data["free_float"], "IfcDuration"
),
"TotalFloat": ifcopenshell.util.date.datetime2ifc(
data["total_float"], "IfcDuration"
),
"FreeFloat": ifcopenshell.util.date.datetime2ifc(data["free_float"], "IfcDuration"),
"TotalFloat": ifcopenshell.util.date.datetime2ifc(data["total_float"], "IfcDuration"),
"IsCritical": data["total_float"].days == 0,
"EarlyStart": ifcopenshell.util.date.datetime2ifc(
data["early_start"], "IfcDateTime"
),
"EarlyFinish": ifcopenshell.util.date.datetime2ifc(
data["early_finish"], "IfcDateTime"
),
"LateStart": ifcopenshell.util.date.datetime2ifc(
data["late_start"], "IfcDateTime"
),
"LateFinish": ifcopenshell.util.date.datetime2ifc(
data["late_finish"], "IfcDateTime"
),
"EarlyStart": ifcopenshell.util.date.datetime2ifc(data["early_start"], "IfcDateTime"),
"EarlyFinish": ifcopenshell.util.date.datetime2ifc(data["early_finish"], "IfcDateTime"),
"LateStart": ifcopenshell.util.date.datetime2ifc(data["late_start"], "IfcDateTime"),
"LateFinish": ifcopenshell.util.date.datetime2ifc(data["late_finish"], "IfcDateTime"),
},
)
@@ -246,11 +235,7 @@ class Usecase:
if edge["lag_time"]:
days += edge["lag_time"]
if days:
starts.append(
datetime.datetime.combine(
self.offset_date(finish, days, data), datetime.time(9)
)
)
starts.append(datetime.datetime.combine(self.offset_date(finish, days, data), datetime.time(9)))
starts.append(
datetime.datetime.combine(
self.offset_date(finish, days, predecessor_data),
@@ -265,9 +250,7 @@ class Usecase:
return
if edge["lag_time"]:
starts.append(self.offset_date(start, edge["lag_time"], data))
starts.append(
self.offset_date(start, edge["lag_time"], predecessor_data)
)
starts.append(self.offset_date(start, edge["lag_time"], predecessor_data))
else:
starts.append(start)
elif edge["type"] == "FF":
@@ -275,12 +258,8 @@ class Usecase:
if finish is None:
return
if edge["lag_time"]:
finishes.append(
self.offset_date(finish, edge["lag_time"], data)
)
finishes.append(
self.offset_date(finish, edge["lag_time"], predecessor_data)
)
finishes.append(self.offset_date(finish, edge["lag_time"], data))
finishes.append(self.offset_date(finish, edge["lag_time"], predecessor_data))
else:
finishes.append(finish)
elif edge["type"] == "SF":
@@ -292,9 +271,7 @@ class Usecase:
days += edge["lag_time"]
if days or edge["lag_time"]:
finishes.append(
datetime.datetime.combine(
self.offset_date(start, days, data), datetime.time(17)
)
datetime.datetime.combine(self.offset_date(start, days, data), datetime.time(17))
)
finishes.append(
datetime.datetime.combine(
@@ -317,9 +294,7 @@ class Usecase:
if potential_finish > data["early_finish"]:
data["early_finish"] = potential_finish
else:
data[
"early_start"
] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["early_start"] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["early_finish"],
datetime.timedelta(days=data["duration"]),
data["duration_type"],
@@ -375,9 +350,7 @@ class Usecase:
days += edge["lag_time"]
if days or edge["lag_time"]:
finishes.append(
datetime.datetime.combine(
self.offset_date(start, -days, data), datetime.time(17)
)
datetime.datetime.combine(self.offset_date(start, -days, data), datetime.time(17))
)
finishes.append(
datetime.datetime.combine(
@@ -402,9 +375,7 @@ class Usecase:
return
if edge["lag_time"]:
starts.append(self.offset_date(start, -edge["lag_time"], data))
starts.append(
self.offset_date(start, -edge["lag_time"], successor_data)
)
starts.append(self.offset_date(start, -edge["lag_time"], successor_data))
else:
starts.append(start)
free_floats.append(
@@ -421,12 +392,8 @@ class Usecase:
if finish is None:
return
if edge["lag_time"]:
finishes.append(
self.offset_date(finish, -edge["lag_time"], data)
)
finishes.append(
self.offset_date(finish, -edge["lag_time"], successor_data)
)
finishes.append(self.offset_date(finish, -edge["lag_time"], data))
finishes.append(self.offset_date(finish, -edge["lag_time"], successor_data))
else:
finishes.append(finish)
free_floats.append(
@@ -447,9 +414,7 @@ class Usecase:
days += edge["lag_time"]
if days:
starts.append(
datetime.datetime.combine(
self.offset_date(finish, -days, data), datetime.time(9)
)
datetime.datetime.combine(self.offset_date(finish, -days, data), datetime.time(9))
)
starts.append(
datetime.datetime.combine(
@@ -471,13 +436,8 @@ class Usecase:
if starts and finishes:
data["late_start"] = min(starts)
data["late_finish"] = min(finishes)
if (
self.offset_date(data["late_start"], data["duration"], data)
< data["late_finish"]
):
data[
"late_finish"
] = ifcopenshell.util.sequence.get_start_or_finish_date(
if self.offset_date(data["late_start"], data["duration"], data) < data["late_finish"]:
data["late_finish"] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["late_start"],
datetime.timedelta(days=data["duration"]),
data["duration_type"],
@@ -485,9 +445,7 @@ class Usecase:
date_type="FINISH",
)
else:
data[
"late_start"
] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["late_start"] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["late_finish"],
datetime.timedelta(days=data["duration"]),
data["duration_type"],
@@ -528,9 +486,7 @@ class Usecase:
data["total_float"] = data["late_finish"] - data["early_finish"]
# If the float is within the span of a single day, it may show as a 8 hours
if data["total_float"].seconds == 60 * 60 * 8:
data["total_float"] = datetime.timedelta(
days=data["total_float"].days + 1
)
data["total_float"] = datetime.timedelta(days=data["total_float"].days + 1)
data["free_float"] = min(free_floats) if free_floats else None
# If the float is within the span of a single day, it may show as a 8 hours
@@ -21,124 +21,121 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, task=None):
"""Removes a task
def remove_task(file, task=None) -> None:
"""Removes a task
All subtasks are also removed recursively. Any relationships such as
sequences or controls are also removed.
All subtasks are also removed recursively. Any relationships such as
sequences or controls are also removed.
:param task: The IfcTask to remove.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
:param task: The IfcTask to remove.
:type task: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Add a root task to represent the design milestones, and major
# project phases.
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
design = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Design", identification="B")
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Add a root task to represent the design milestones, and major
# project phases.
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Milestones", identification="A")
design = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Design", identification="B")
ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Ah, let's delete the design section, who needs it anyway we'll
# just fix it on site.
ifcopenshell.api.run("sequence.remove_task", model, task=design)
"""
self.file = file
self.settings = {"task": task}
# Ah, let's delete the design section, who needs it anyway we'll
# just fix it on site.
ifcopenshell.api.run("sequence.remove_task", model, task=design)
"""
settings = {"task": task}
def execute(self):
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
self.file,
definitions=[self.settings["task"]],
relating_context=self.file.by_type("IfcContext")[0],
)
if self.settings["task"].TaskTime:
self.file.remove(self.settings["task"].TaskTime)
for inverse in self.file.get_inverse(self.settings["task"]):
if inverse.is_a("IfcRelSequence"):
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
file,
definitions=[settings["task"]],
relating_context=file.by_type("IfcContext")[0],
)
if settings["task"].TaskTime:
file.remove(settings["task"].TaskTime)
for inverse in file.get_inverse(settings["task"]):
if inverse.is_a("IfcRelSequence"):
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelNests"):
if inverse.RelatingObject == settings["task"]:
for related_object in inverse.RelatedObjects:
ifcopenshell.api.run("sequence.remove_task", file, task=related_object)
elif not inverse.RelatedObjects:
history = inverse.OwnerHistory
self.file.remove(inverse)
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
elif inverse.is_a("IfcRelNests"):
if inverse.RelatingObject == self.settings["task"]:
for related_object in inverse.RelatedObjects:
ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object)
elif not inverse.RelatedObjects:
history = inverse.OwnerHistory
self.file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
elif self.settings["task"] in inverse.RelatedObjects:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(self.settings["task"])
if not related_objects:
self.file.remove(inverse)
else:
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToControl"):
if inverse.RelatingControl == self.settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
self.file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
ifcopenshell.util.element.remove_deep2(file, history)
elif settings["task"] in inverse.RelatedObjects:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
if not related_objects:
file.remove(inverse)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(self.settings["task"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelDefinesByProperties"):
ifcopenshell.api.run(
"pset.remove_pset",
self.file,
product=self.settings["task"],
pset=inverse.RelatingPropertyDefinition,
)
elif inverse.is_a("IfcRelAssignsToProcess"):
if inverse.RelatingProcess == self.settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
self.file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
elif inverse.is_a("IfcRelAssignsToProduct"):
if inverse.RelatingProduct == self.settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
self.file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(self.settings["task"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToObject"):
if inverse.RelatingObject == self.settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
self.file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(self.settings["task"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToProcess"):
elif inverse.is_a("IfcRelAssignsToControl"):
if inverse.RelatingControl == settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
self.file.remove(inverse)
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelDefinesByProperties"):
ifcopenshell.api.run(
"pset.remove_pset",
file,
product=settings["task"],
pset=inverse.RelatingPropertyDefinition,
)
elif inverse.is_a("IfcRelAssignsToProcess"):
if inverse.RelatingProcess == settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
elif inverse.is_a("IfcRelAssignsToProduct"):
if inverse.RelatingProduct == settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToObject"):
if inverse.RelatingObject == settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["task"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToProcess"):
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
history = self.settings["task"].OwnerHistory
self.file.remove(self.settings["task"])
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
history = settings["task"].OwnerHistory
file.remove(settings["task"])
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -19,45 +19,42 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, time_period=None):
"""Removes a time period
def remove_time_period(file, time_period=None) -> None:
"""Removes a time period
:param time_period: The IfcTimePeriod to remove.
:type time_period: ifcopenshell.entity_instance
:return: None
:rtype: None
:param time_period: The IfcTimePeriod to remove.
:type time_period: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
# The morning work session, lunch, then the afternoon work session.
morning = ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
afternoon = ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
# The morning work session, lunch, then the afternoon work session.
morning = ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
afternoon = ifcopenshell.api.run("sequence.add_time_period", model,
recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
# Let's take the afternoon off!
ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon)
"""
self.file = file
self.settings = {"time_period": time_period}
# Let's take the afternoon off!
ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon)
"""
settings = {"time_period": time_period}
def execute(self):
self.file.remove(self.settings["time_period"])
file.remove(settings["time_period"])
@@ -20,49 +20,46 @@ import ifcopenshell
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, work_calendar=None):
"""Removes a work calendar
def remove_work_calendar(file, work_calendar=None) -> None:
"""Removes a work calendar
All relationships are also removed, such as if a task is set to use that
calendar.
All relationships are also removed, such as if a task is set to use that
calendar.
:param work_calendar: The IfcWorkCalendar to remove
:type work_calendar: ifcopenshell.entity_instance
:return: None
:rtype: None
:param work_calendar: The IfcWorkCalendar to remove
:type work_calendar: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar)
"""
self.file = file
self.settings = {"work_calendar": work_calendar}
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar)
"""
settings = {"work_calendar": work_calendar}
def execute(self):
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
self.file,
definitions=[self.settings["work_calendar"]],
relating_context=self.file.by_type("IfcContext")[0],
)
if self.settings["work_calendar"].Controls:
for rel in self.settings["work_calendar"].Controls:
for related_object in rel.RelatedObjects:
ifcopenshell.api.run(
"control.unassign_control",
self.file,
relating_control=self.settings["work_calendar"],
related_object=related_object,
)
history = self.settings["work_calendar"].OwnerHistory
self.file.remove(self.settings["work_calendar"])
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
file,
definitions=[settings["work_calendar"]],
relating_context=file.by_type("IfcContext")[0],
)
if settings["work_calendar"].Controls:
for rel in settings["work_calendar"].Controls:
for related_object in rel.RelatedObjects:
ifcopenshell.api.run(
"control.unassign_control",
file,
relating_control=settings["work_calendar"],
related_object=related_object,
)
history = settings["work_calendar"].OwnerHistory
file.remove(settings["work_calendar"])
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -20,40 +20,37 @@ import ifcopenshell
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, work_plan=None):
"""Removes a work plan
def remove_work_plan(file, work_plan=None) -> None:
"""Removes a work plan
Note that schedules that are grouped under the work plan are not
removed.
Note that schedules that are grouped under the work plan are not
removed.
:param work_plan: The IfcWorkPlan to remove.
:type work_plan: ifcopenshell.entity_instance
:return: None
:rtype: None
:param work_plan: The IfcWorkPlan to remove.
:type work_plan: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan)
"""
self.file = file
self.settings = {"work_plan": work_plan}
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan)
"""
settings = {"work_plan": work_plan}
def execute(self):
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
self.file,
definitions=[self.settings["work_plan"]],
relating_context=self.file.by_type("IfcContext")[0],
)
history = self.settings["work_plan"].OwnerHistory
self.file.remove(self.settings["work_plan"])
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
file,
definitions=[settings["work_plan"]],
relating_context=file.by_type("IfcContext")[0],
)
history = settings["work_plan"].OwnerHistory
file.remove(settings["work_plan"])
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -21,69 +21,66 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, work_schedule=None):
"""Removes a work schedule
def remove_work_schedule(file, work_schedule=None) -> None:
"""Removes a work schedule
All tasks in the work schedule are also removed recursively.
All tasks in the work schedule are also removed recursively.
:param work_schedule: The IfcWorkSchedule to remove.
:type work_schedule: ifcopenshell.entity_instance
:return: None
:rtype: None
:param work_schedule: The IfcWorkSchedule to remove.
:type work_schedule: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# This will hold all our construction schedules
work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
# Let's imagine this is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
# Let's imagine this is one of our schedules in our work plan.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
name="Construction Schedule A", work_plan=work_plan)
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule)
"""
self.file = file
self.settings = {"work_schedule": work_schedule}
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule)
"""
settings = {"work_schedule": work_schedule}
def execute(self):
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
self.file,
definitions=[self.settings["work_schedule"]],
relating_context=self.file.by_type("IfcContext")[0],
)
if self.settings["work_schedule"].Declares:
for rel in self.settings["work_schedule"].Declares:
for work_schedule in rel.RelatedObjects:
ifcopenshell.api.run(
"sequence.remove_work_schedule",
self.file,
work_schedule=work_schedule,
)
for inverse in self.file.get_inverse(self.settings["work_schedule"]):
if inverse.is_a("IfcRelDefinesByObject"):
if inverse.RelatingObject == self.settings["work_schedule"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
self.file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(self.settings["work_schedule"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToControl"):
[
ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object)
for related_object in inverse.RelatedObjects
if related_object.is_a("IfcTask")
]
# TODO: do a deep purge
ifcopenshell.api.run(
"project.unassign_declaration",
file,
definitions=[settings["work_schedule"]],
relating_context=file.by_type("IfcContext")[0],
)
if settings["work_schedule"].Declares:
for rel in settings["work_schedule"].Declares:
for work_schedule in rel.RelatedObjects:
ifcopenshell.api.run(
"sequence.remove_work_schedule",
file,
work_schedule=work_schedule,
)
for inverse in file.get_inverse(settings["work_schedule"]):
if inverse.is_a("IfcRelDefinesByObject"):
if inverse.RelatingObject == settings["work_schedule"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
file.remove(inverse)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
else:
related_objects = list(inverse.RelatedObjects)
related_objects.remove(settings["work_schedule"])
inverse.RelatedObjects = related_objects
elif inverse.is_a("IfcRelAssignsToControl"):
[
ifcopenshell.api.run("sequence.remove_task", file, task=related_object)
for related_object in inverse.RelatedObjects
if related_object.is_a("IfcTask")
]
history = self.settings["work_schedule"].OwnerHistory
self.file.remove(self.settings["work_schedule"])
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
history = settings["work_schedule"].OwnerHistory
file.remove(settings["work_schedule"])
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, work_time=None):
"""Removes a work time
def remove_work_time(file, work_time=None) -> None:
"""Removes a work time
:param work_time: The IfcWorkTime to remove.
:type work_time: ifcopenshell.entity_instance
:return: None
:rtype: None
:param work_time: The IfcWorkTime to remove.
:type work_time: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time)
"""
self.file = file
self.settings = {"work_time": work_time}
# And remove it immediately
ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time)
"""
settings = {"work_time": work_time}
def execute(self):
self.file.remove(self.settings["work_time"])
file.remove(settings["work_time"])
@@ -19,57 +19,54 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, rel_sequence=None):
"""Removes any lag time in a sequence
def unassign_lag_time(file, rel_sequence=None) -> None:
"""Removes any lag time in a sequence
The schedule is cascaded afterwards.
The schedule is cascaded afterwards.
:param rel_sequence: The sequence to remove the lag time from.
:type rel_sequence: ifcopenshell.entity_instance
:return: None
:rtype: None
:param rel_sequence: The sequence to remove the lag time from.
:type rel_sequence: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine we're building 2 zones, one after another.
zone1 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 1", identification="C.1")
zone2 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 2", identification="C.2")
# Let's imagine we're building 2 zones, one after another.
zone1 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 1", identification="C.1")
zone2 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 2", identification="C.2")
# Zone 1 finishes, then zone 2 starts.
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=zone1, related_process=zone2)
# Zone 1 finishes, then zone 2 starts.
sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
relating_process=zone1, related_process=zone2)
# What if you had to wait 1 week before you could start zone 2?
ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W")
# What if you had to wait 1 week before you could start zone 2?
ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W")
# What if you didn't?
ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence)
"""
self.file = file
self.settings = {
"rel_sequence": rel_sequence,
}
# What if you didn't?
ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence)
"""
settings = {
"rel_sequence": rel_sequence,
}
def execute(self):
if len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1:
self.file.remove(self.settings["rel_sequence"].TimeLag)
else:
self.settings["rel_sequence"].TimeLag = None
ifcopenshell.api.run(
"sequence.cascade_schedule",
self.file,
task=self.settings["rel_sequence"].RelatedProcess,
)
if len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1:
file.remove(settings["rel_sequence"].TimeLag)
else:
settings["rel_sequence"].TimeLag = None
ifcopenshell.api.run(
"sequence.cascade_schedule",
file,
task=settings["rel_sequence"].RelatedProcess,
)
@@ -21,59 +21,56 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, relating_process=None, related_object=None):
"""Unassigns a process and object relationship
def unassign_process(file, relating_process=None, related_object=None) -> None:
"""Unassigns a process and object relationship
See ifcopenshell.api.sequence.assign_process for details.
See ifcopenshell.api.sequence.assign_process for details.
:param relating_process: The IfcTask in the relationship.
:type relating_process: ifcopenshell.entity_instance
:param related_object: The related object.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
:param relating_process: The IfcTask in the relationship.
:type relating_process: ifcopenshell.entity_instance
:param related_object: The related object.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's demolish that wall!
ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
# Let's demolish that wall!
ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
# Change our mind.
ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall)
"""
self.file = file
self.settings = {
"relating_process": relating_process,
"related_object": related_object,
}
# Change our mind.
ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall)
"""
settings = {
"relating_process": relating_process,
"related_object": related_object,
}
def execute(self):
for rel in self.settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != self.settings["relating_process"]:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(self.settings["related_object"])
rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
return rel
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != settings["relating_process"]:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
return rel
@@ -21,59 +21,56 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, relating_product=None, related_object=None):
"""Unassigns a product and object relationship
def unassign_product(file, relating_product=None, related_object=None) -> None:
"""Unassigns a product and object relationship
See ifcopenshell.api.sequence.assign_product for details.
See ifcopenshell.api.sequence.assign_product for details.
:param relating_product: The IfcProduct in the relationship.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcTask in the relationship.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
:param relating_product: The IfcProduct in the relationship.
:type relating_product: ifcopenshell.entity_instance
:param related_object: The IfcTask in the relationship.
:type related_object: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
# Let's create a construction task. Note that the predefined type is
# important to distinguish types of tasks.
task = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's say we have a wall somewhere.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's construct that wall!
ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
# Let's construct that wall!
ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
# Change our mind.
ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task)
"""
self.file = file
self.settings = {
"relating_product": relating_product,
"related_object": related_object,
}
# Change our mind.
ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task)
"""
settings = {
"relating_product": relating_product,
"related_object": related_object,
}
def execute(self):
for rel in self.settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(self.settings["related_object"])
rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
return rel
for rel in settings["related_object"].HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
continue
if len(rel.RelatedObjects) == 1:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
return
related_objects = list(rel.RelatedObjects)
related_objects.remove(settings["related_object"])
rel.RelatedObjects = related_objects
ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
return rel
@@ -17,40 +17,37 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, recurrence_pattern=None):
"""Unassigns a recurrence pattern
def unassign_recurrence_pattern(file, recurrence_pattern=None) -> None:
"""Unassigns a recurrence pattern
Note that a recurring task time must have a recurrence pattern, so if
you remove it, be sure to clean up after yourself.
Note that a recurring task time must have a recurrence pattern, so if
you remove it, be sure to clean up after your
:param recurrence_pattern: The IfcRecurrencePattern to remove.
:type recurrence_pattern: ifcopenshell.entity_instance
:return: None
:rtype: None
:param recurrence_pattern: The IfcRecurrencePattern to remove.
:type recurrence_pattern: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's create a new calendar.
calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# Let's start defining the times that we work during the week.
work_time = ifcopenshell.api.run("sequence.add_work_time", model,
work_calendar=calendar, time_type="WorkingTimes")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# We create a weekly recurrence
pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
parent=work_time, recurrence_type="WEEKLY")
# Change our mind, let's just maintain it whenever we feel like it.
ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern)
"""
self.file = file
self.settings = {"recurrence_pattern": recurrence_pattern}
# Change our mind, let's just maintain it whenever we feel like it.
ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern)
"""
settings = {"recurrence_pattern": recurrence_pattern}
def execute(self):
for time_period in self.settings["recurrence_pattern"].TimePeriods or []:
self.file.remove(time_period)
self.file.remove(self.settings["recurrence_pattern"])
for time_period in settings["recurrence_pattern"].TimePeriods or []:
file.remove(time_period)
file.remove(settings["recurrence_pattern"])
@@ -21,53 +21,50 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, relating_process=None, related_process=None):
"""Removes a sequence relationship between tasks
def unassign_sequence(file, relating_process=None, related_process=None) -> None:
"""Removes a sequence relationship between tasks
:param relating_process: The previous / predecessor task.
:type relating_process: ifcopenshell.entity_instance
:param related_process: The next / successor task.
:type related_process: ifcopenshell.entity_instance
:return: None
:rtype: None
:param relating_process: The previous / predecessor task.
:type relating_process: ifcopenshell.entity_instance
:param related_process: The next / successor task.
:type related_process: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine we are creating a construction schedule. All tasks
# need to be part of a work schedule.
schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine a root construction task
construction = ifcopenshell.api.run("sequence.add_task", model,
work_schedule=schedule, name="Construction", identification="C")
# Let's imagine we're building 2 zones, one after another.
zone1 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 1", identification="C.1")
zone2 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 2", identification="C.2")
# Let's imagine we're building 2 zones, one after another.
zone1 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 1", identification="C.1")
zone2 = ifcopenshell.api.run("sequence.add_task", model,
parent_task=construction, name="Zone 2", identification="C.2")
# Zone 1 finishes, then zone 2 starts.
ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2)
# Zone 1 finishes, then zone 2 starts.
ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2)
# Let's make them unrelated
ifcopenshell.api.run("sequence.unassign_sequence", model,
relating_process=zone1, related_process=zone2)
"""
self.file = file
self.settings = {
"relating_process": relating_process,
"related_process": related_process,
}
# Let's make them unrelated
ifcopenshell.api.run("sequence.unassign_sequence", model,
relating_process=zone1, related_process=zone2)
"""
settings = {
"relating_process": relating_process,
"related_process": related_process,
}
def execute(self):
for rel in self.settings["related_process"].IsSuccessorFrom or []:
if rel.RelatingProcess == self.settings["relating_process"]:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
ifcopenshell.api.run("sequence.cascade_schedule", self.file, task=self.settings["related_process"])
for rel in settings["related_process"].IsSuccessorFrom or []:
if rel.RelatingProcess == settings["relating_process"]:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["related_process"])