Update to ifcopenshell.util.date for handling of IfcDuration with python and import of Planned Durations from P6 .xml exports in blenderBIM

This commit is contained in:
bosonprojets
2021-05-04 00:27:53 +00:00
parent 6b46ff71a6
commit 8a8bdeeaa3
6 changed files with 51 additions and 7 deletions
@@ -7,6 +7,7 @@ import webbrowser
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.date import ifcopenshell.util.date
from datetime import datetime from datetime import datetime
from datetime import timedelta
from dateutil import parser from dateutil import parser
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from bpy_extras.io_utils import ImportHelper from bpy_extras.io_utils import ImportHelper
@@ -301,8 +302,7 @@ class LoadTaskProperties(bpy.types.Operator):
task_time = Data.task_times[task["TaskTime"]] task_time = Data.task_times[task["TaskTime"]]
item.start = self.canonicalise_time(task_time["ScheduleStart"]) item.start = self.canonicalise_time(task_time["ScheduleStart"])
item.finish = self.canonicalise_time(task_time["ScheduleFinish"]) item.finish = self.canonicalise_time(task_time["ScheduleFinish"])
# TODO: duration item.duration = str(task_time["ScheduleDuration"].days) if task_time["ScheduleDuration"] else "-"
item.duration = "-"
else: else:
item.start = "-" item.start = "-"
item.finish = "-" item.finish = "-"
@@ -431,6 +431,8 @@ class EnableEditingTaskTime(bpy.types.Operator):
if data_type == "string": if data_type == "string":
if isinstance(data[attribute.name()], datetime): if isinstance(data[attribute.name()], datetime):
new.string_value = "" if new.is_null else data[attribute.name()].isoformat() new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif isinstance(data[attribute.name()], timedelta):
new.string_value = "" if new.is_null else ifcopenshell.util.date.datetime2ifc(data[attribute.name()], "IfcDuration")
else: else:
new.string_value = "" if new.is_null else data[attribute.name()] new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean": elif data_type == "boolean":
@@ -497,6 +499,8 @@ class EditTaskTime(bpy.types.Operator):
attributes[key] = parser.parse(value, dayfirst=True, fuzzy=True) attributes[key] = parser.parse(value, dayfirst=True, fuzzy=True)
except: except:
attributes[key] = None attributes[key] = None
elif key == "ScheduleDuration":
attributes[key] = ifcopenshell.util.date.ifc2datetime(value)
return attributes return attributes
@@ -1,6 +1,4 @@
import ifcopenshell.util.date import ifcopenshell.util.date
from datetime import datetime
from datetime import timedelta
class Usecase: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
@@ -154,7 +154,8 @@ class Data:
continue continue
if "Start" in key or "Finish" in key or key == "StatusTime": if "Start" in key or "Finish" in key or key == "StatusTime":
data[key] = ifcopenshell.util.date.ifc2datetime(value) data[key] = ifcopenshell.util.date.ifc2datetime(value)
# TODO parse duration elif key == "ScheduleDuration":
data[key] = ifcopenshell.util.date.ifc2datetime(value)
cls.task_times[task_time.id()] = data cls.task_times[task_time.id()] = data
@classmethod @classmethod
@@ -13,4 +13,7 @@ class Usecase:
if "Start" in name or "Finish" in name or name == "StatusTime": if "Start" in name or "Finish" in name or name == "StatusTime":
if value: if value:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
if name == "ScheduleDuration":
if value:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["task_time"], name, value) setattr(self.settings["task_time"], name, value)
@@ -1,6 +1,39 @@
import datetime import datetime
from re import findall from re import findall
# https://gist.github.com/spatialtime/c1924a3b178b4fe721fe406e0bf1a1dc
import re
from datetime import timedelta
def format_duration(td):
s = td.seconds
ms = td.microseconds
if ms != 0: # Round microseconds to milliseconds.
ms /= 1000000
ms = round(ms,3)
s += ms
return "P{}DT{}S".format(td.days,s)
def parse_duration(iso_duration):
m = re.match(r'^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:.\d+)?)S)?$',
iso_duration)
if m is None:
raise ValueError("invalid ISO 8601 duration string")
days = 0
hours = 0
minutes = 0
seconds = 0.0
if m[3]:
days = int(m[3])
if m[4]:
hours = int(m[4])
if m[5]:
minutes = int(m[5])
if m[6]:
seconds = float(m[6])
return timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
def duration2dict(duration): def duration2dict(duration):
results = {} results = {}
@@ -11,7 +44,7 @@ def duration2dict(duration):
def ifc2datetime(element): def ifc2datetime(element):
if isinstance(element, str) and element[0] == "P": # IfcDuration if isinstance(element, str) and element[0] == "P": # IfcDuration
return duration2dict(element) return parse_duration(element)
elif isinstance(element, str) and element[2] == ":": # IfcTime elif isinstance(element, str) and element[2] == ":": # IfcTime
return datetime.time.fromisoformat(element) return datetime.time.fromisoformat(element)
elif isinstance(element, str) and ":" in element: # IfcDateTime elif isinstance(element, str) and ":" in element: # IfcDateTime
@@ -39,8 +72,10 @@ def ifc2datetime(element):
def datetime2ifc(dt, ifc_type): def datetime2ifc(dt, ifc_type):
if isinstance(dt, str): if isinstance(dt, str) and ifc_type != "IfcDuration":
dt = datetime.datetime.fromisoformat(dt) dt = datetime.datetime.fromisoformat(dt)
if ifc_type == "IfcDuration":
return format_duration(dt)
if ifc_type == "IfcTimeStamp": if ifc_type == "IfcTimeStamp":
return int(dt.timestamp()) return int(dt.timestamp())
elif ifc_type == "IfcDateTime": elif ifc_type == "IfcDateTime":
+3
View File
@@ -1,4 +1,5 @@
import datetime import datetime
from datetime import timedelta
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.date import ifcopenshell.util.date
@@ -121,6 +122,7 @@ class P62Ifc:
"Identification": activity.find("pr:Id", self.ns).text, "Identification": activity.find("pr:Id", self.ns).text,
"StartDate": datetime.datetime.fromisoformat(activity.find("pr:StartDate", self.ns).text), "StartDate": datetime.datetime.fromisoformat(activity.find("pr:StartDate", self.ns).text),
"FinishDate": datetime.datetime.fromisoformat(activity.find("pr:FinishDate", self.ns).text), "FinishDate": datetime.datetime.fromisoformat(activity.find("pr:FinishDate", self.ns).text),
"PlannedDuration": datetime.timedelta(days=float(activity.find("pr:PlannedDuration", self.ns).text)),
"Status": activity.find("pr:Status", self.ns).text, "Status": activity.find("pr:Status", self.ns).text,
"ifc": None, "ifc": None,
} }
@@ -335,6 +337,7 @@ class P62Ifc:
attributes={ attributes={
"ScheduleStart": activity["StartDate"], "ScheduleStart": activity["StartDate"],
"ScheduleFinish": activity["FinishDate"], "ScheduleFinish": activity["FinishDate"],
"ScheduleDuration": activity["PlannedDuration"] if activity["PlannedDuration"] else None,
}, },
) )