mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Various 4D improvements:
- refactor work schedule visualisation operator - Fix text handler - Improve durations UI for task times (proposal to replicate for all durations) - Simplify durations ( P32H as P4D or P1D8H) - Implement years/months in various datetime calculations - Feature to customise animation colors per construction task type - Feature to add Visual task bars - Feature to customise Visual task bar material colors - Move helper functions from blenderbim.bim.module.sequence to ifcopenshell.util.sequence
This commit is contained in:
@@ -21,6 +21,32 @@ import ifcopenshell.util.date
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=False):
|
||||
if task.TaskTime:
|
||||
current_date = (
|
||||
ifcopenshell.util.date.ifc2datetime(getattr(task.TaskTime, attribute_name))
|
||||
if getattr(task.TaskTime, attribute_name)
|
||||
else ""
|
||||
)
|
||||
if current_date:
|
||||
return current_date
|
||||
for subtask in get_all_nested_tasks(task):
|
||||
current_date = derive_date(
|
||||
subtask,
|
||||
attribute_name,
|
||||
date=date,
|
||||
is_earliest=is_earliest,
|
||||
is_latest=is_latest,
|
||||
)
|
||||
if is_earliest:
|
||||
if current_date and (date is None or current_date < date):
|
||||
date = current_date
|
||||
if is_latest:
|
||||
if current_date and (date is None or current_date > date):
|
||||
date = current_date
|
||||
return date
|
||||
|
||||
|
||||
def derive_calendar(task):
|
||||
calendar = [
|
||||
rel.RelatingControl
|
||||
@@ -38,7 +64,7 @@ def count_working_days(start, finish, calendar):
|
||||
result = 0
|
||||
current_date = datetime.date(start.year, start.month, start.day)
|
||||
finish_date = datetime.date(finish.year, finish.month, finish.day)
|
||||
while current_date < finish_date:
|
||||
while current_date <= finish_date:
|
||||
if (
|
||||
calendar
|
||||
and calendar.WorkingTimes
|
||||
@@ -58,7 +84,11 @@ def get_start_or_finish_date(
|
||||
# Typically a milestone will have zero duration, so the start == finish
|
||||
return start
|
||||
# We minus 1 because the start day itself is counted as a day
|
||||
duration = datetime.timedelta(days=duration.days - 1)
|
||||
months = int(getattr(duration, "months", 0))
|
||||
years = int(getattr(duration, "years", 0))
|
||||
total_duration = duration.days + months * 30 + years * 12 * 30
|
||||
duration = datetime.timedelta(days=total_duration - 1)
|
||||
|
||||
if date_type == "START":
|
||||
duration = -duration
|
||||
result = offset_date(start, duration, duration_type, calendar)
|
||||
@@ -69,7 +99,10 @@ def get_start_or_finish_date(
|
||||
|
||||
def offset_date(start, duration, duration_type, calendar):
|
||||
current_date = start
|
||||
abs_duration = abs(duration.days)
|
||||
months = getattr(duration, "months", 0)
|
||||
years = getattr(duration, "years", 0)
|
||||
|
||||
abs_duration = abs((duration.days + months * 30 + years * 12 * 30))
|
||||
date_offset = datetime.timedelta(days=1 if duration.days > 0 else -1)
|
||||
while abs_duration > 0:
|
||||
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(
|
||||
@@ -199,3 +232,107 @@ def is_work_time_applicable_to_day(work_time, day):
|
||||
and math.floor(day.day / 7) + 1 == recurrence.Position
|
||||
)
|
||||
return False # TODO
|
||||
|
||||
|
||||
def get_task_work_schedule(task):
|
||||
parent_task = get_parent_task(task)
|
||||
if parent_task:
|
||||
get_task_work_schedule(parent_task)
|
||||
else:
|
||||
schedules = [
|
||||
rel.RelatingControl
|
||||
for rel in task.HasAssignments
|
||||
if rel.is_a("IfcRelAssignsToControl")
|
||||
and rel.RelatingControl.is_a("IfcWorkSchedule")
|
||||
]
|
||||
return schedules
|
||||
|
||||
|
||||
def get_nested_tasks(task):
|
||||
return [object for rel in task.IsNestedBy for object in rel.RelatedObjects]
|
||||
|
||||
|
||||
def get_parent_task(task):
|
||||
return (
|
||||
task.Nests[0].RelatingObject
|
||||
if task.Nests and task.Nests[0].RelatingObject.is_a("IfcTask")
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
def get_all_nested_tasks(task):
|
||||
for nested_task in get_nested_tasks(task):
|
||||
yield nested_task
|
||||
yield from get_all_nested_tasks(nested_task)
|
||||
|
||||
|
||||
def get_work_schedule_tasks(work_schedule):
|
||||
tasks = []
|
||||
for root_task in get_root_tasks(work_schedule):
|
||||
nested_tasks = get_all_nested_tasks(root_task)
|
||||
tasks.extend(nested_tasks)
|
||||
return tasks
|
||||
|
||||
|
||||
def get_root_tasks(work_schedule):
|
||||
return [
|
||||
obj
|
||||
for rel in work_schedule.Controls
|
||||
for obj in rel.RelatedObjects
|
||||
if obj.is_a("IfcTask")
|
||||
]
|
||||
|
||||
|
||||
def get_root_tasks_ids(work_schedule):
|
||||
return [
|
||||
obj.id()
|
||||
for rel in work_schedule.Controls
|
||||
for obj in rel.RelatedObjects
|
||||
if obj.is_a("IfcTask")
|
||||
]
|
||||
|
||||
|
||||
def guess_date_range(work_schedule):
|
||||
earliest = None
|
||||
latest = None
|
||||
root_tasks = get_root_tasks(work_schedule)
|
||||
tasks_with_assignements = []
|
||||
for task in root_tasks:
|
||||
if has_task_outputs(task):
|
||||
tasks_with_assignements.append(task)
|
||||
for sub_task in get_all_nested_tasks(task):
|
||||
if has_task_outputs(sub_task):
|
||||
tasks_with_assignements.append(sub_task)
|
||||
|
||||
for task in tasks_with_assignements:
|
||||
derived_start = derive_date(task, "ScheduleStart", is_earliest=True)
|
||||
derived_finish = derive_date(task, "ScheduleFinish", is_latest=True)
|
||||
if derived_start and (not earliest or derived_start < earliest):
|
||||
earliest = derived_start
|
||||
if derived_finish and (not latest or derived_finish > latest):
|
||||
latest = derived_finish
|
||||
return earliest, latest
|
||||
|
||||
|
||||
def get_direct_task_outputs(task):
|
||||
return [
|
||||
rel.RelatingProduct
|
||||
for rel in task.HasAssignments
|
||||
if rel.is_a("IfcRelAssignsToProduct")
|
||||
]
|
||||
|
||||
|
||||
def get_task_outputs(task, is_deep=False):
|
||||
if not is_deep:
|
||||
return get_direct_task_outputs(task)
|
||||
else:
|
||||
nested_tasks = get_all_nested_tasks(task)
|
||||
return [
|
||||
output
|
||||
for nested_task in nested_tasks
|
||||
for output in get_direct_task_outputs(nested_task)
|
||||
]
|
||||
|
||||
|
||||
def has_task_outputs(task):
|
||||
return len(get_task_outputs(task)) > 0
|
||||
|
||||
Reference in New Issue
Block a user