Run black on utils

This commit is contained in:
Dion Moult
2024-07-26 12:13:29 +10:00
parent a6286293d8
commit e7b791f4a2
78 changed files with 2235 additions and 2098 deletions
+1
View File
@@ -18,6 +18,7 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with BCF. If not, see <http://www.gnu.org/licenses/>. along with BCF. If not, see <http://www.gnu.org/licenses/>.
""" """
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from typing import Optional, Union from typing import Optional, Union
+2 -2
View File
@@ -5,6 +5,7 @@ Copyright (c) 2017-2020 Anthon van der Neut, Ruamel bvba
original idea from https://stackoverflow.com/a/19722365/1307905 original idea from https://stackoverflow.com/a/19722365/1307905
""" """
import zipfile import zipfile
from io import BytesIO from io import BytesIO
from os import PathLike from os import PathLike
@@ -13,8 +14,7 @@ from typing import Any, Optional, Protocol
class ZipFileInterface(Protocol): class ZipFileInterface(Protocol):
def writestr(self, filename_in_zip: str | zipfile.ZipInfo, file_contents: bytes | str) -> None: def writestr(self, filename_in_zip: str | zipfile.ZipInfo, file_contents: bytes | str) -> None: ...
...
class InMemoryZipFile: class InMemoryZipFile:
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML V2 handler.""" """BCF XML V2 handler."""
import uuid import uuid
import warnings import warnings
import zipfile import zipfile
+36 -36
View File
@@ -34,7 +34,7 @@ class BimSnippet:
metadata={ metadata={
"name": "isExternal", "name": "isExternal",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -64,7 +64,7 @@ class HeaderFile:
"name": "Filename", "name": "Filename",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
date: Optional[XmlDateTime] = field( date: Optional[XmlDateTime] = field(
default=None, default=None,
@@ -72,7 +72,7 @@ class HeaderFile:
"name": "Date", "name": "Date",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
reference: Optional[str] = field( reference: Optional[str] = field(
default=None, default=None,
@@ -80,7 +80,7 @@ class HeaderFile:
"name": "Reference", "name": "Reference",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
ifc_project: Optional[str] = field( ifc_project: Optional[str] = field(
default=None, default=None,
@@ -89,7 +89,7 @@ class HeaderFile:
"type": "Attribute", "type": "Attribute",
"length": 22, "length": 22,
"pattern": r"[0-9,A-Z,a-z,_$]*", "pattern": r"[0-9,A-Z,a-z,_$]*",
} },
) )
ifc_spatial_structure_element: Optional[str] = field( ifc_spatial_structure_element: Optional[str] = field(
default=None, default=None,
@@ -98,14 +98,14 @@ class HeaderFile:
"type": "Attribute", "type": "Attribute",
"length": 22, "length": 22,
"pattern": r"[0-9,A-Z,a-z,_$]*", "pattern": r"[0-9,A-Z,a-z,_$]*",
} },
) )
is_external: bool = field( is_external: bool = field(
default=True, default=True,
metadata={ metadata={
"name": "isExternal", "name": "isExternal",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -120,7 +120,7 @@ class TopicDocumentReference:
"name": "ReferencedDocument", "name": "ReferencedDocument",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
description: Optional[str] = field( description: Optional[str] = field(
default=None, default=None,
@@ -128,7 +128,7 @@ class TopicDocumentReference:
"name": "Description", "name": "Description",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
guid: Optional[str] = field( guid: Optional[str] = field(
default=None, default=None,
@@ -136,14 +136,14 @@ class TopicDocumentReference:
"name": "Guid", "name": "Guid",
"type": "Attribute", "type": "Attribute",
"pattern": r"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", "pattern": r"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}",
} },
) )
is_external: bool = field( is_external: bool = field(
default=False, default=False,
metadata={ metadata={
"name": "isExternal", "name": "isExternal",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -170,7 +170,7 @@ class ViewPoint:
"name": "Viewpoint", "name": "Viewpoint",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
snapshot: Optional[str] = field( snapshot: Optional[str] = field(
default=None, default=None,
@@ -178,7 +178,7 @@ class ViewPoint:
"name": "Snapshot", "name": "Snapshot",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
index: Optional[int] = field( index: Optional[int] = field(
default=None, default=None,
@@ -186,7 +186,7 @@ class ViewPoint:
"name": "Index", "name": "Index",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -230,7 +230,7 @@ class Comment:
"name": "Viewpoint", "name": "Viewpoint",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
modified_date: Optional[XmlDateTime] = field( modified_date: Optional[XmlDateTime] = field(
default=None, default=None,
@@ -238,7 +238,7 @@ class Comment:
"name": "ModifiedDate", "name": "ModifiedDate",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
modified_author: Optional[str] = field( modified_author: Optional[str] = field(
default=None, default=None,
@@ -246,7 +246,7 @@ class Comment:
"name": "ModifiedAuthor", "name": "ModifiedAuthor",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -267,7 +267,7 @@ class Header:
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
"min_occurs": 1, "min_occurs": 1,
} },
) )
@@ -279,7 +279,7 @@ class Topic:
"name": "ReferenceLink", "name": "ReferenceLink",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
title: str = field( title: str = field(
metadata={ metadata={
@@ -295,7 +295,7 @@ class Topic:
"name": "Priority", "name": "Priority",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
index: Optional[int] = field( index: Optional[int] = field(
default=None, default=None,
@@ -303,7 +303,7 @@ class Topic:
"name": "Index", "name": "Index",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
labels: List[str] = field( labels: List[str] = field(
default_factory=list, default_factory=list,
@@ -311,7 +311,7 @@ class Topic:
"name": "Labels", "name": "Labels",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
creation_date: XmlDateTime = field( creation_date: XmlDateTime = field(
metadata={ metadata={
@@ -335,7 +335,7 @@ class Topic:
"name": "ModifiedDate", "name": "ModifiedDate",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
modified_author: Optional[str] = field( modified_author: Optional[str] = field(
default=None, default=None,
@@ -343,7 +343,7 @@ class Topic:
"name": "ModifiedAuthor", "name": "ModifiedAuthor",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
due_date: Optional[XmlDateTime] = field( due_date: Optional[XmlDateTime] = field(
default=None, default=None,
@@ -351,7 +351,7 @@ class Topic:
"name": "DueDate", "name": "DueDate",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
assigned_to: Optional[str] = field( assigned_to: Optional[str] = field(
default=None, default=None,
@@ -359,7 +359,7 @@ class Topic:
"name": "AssignedTo", "name": "AssignedTo",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
stage: Optional[str] = field( stage: Optional[str] = field(
default=None, default=None,
@@ -367,7 +367,7 @@ class Topic:
"name": "Stage", "name": "Stage",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
description: Optional[str] = field( description: Optional[str] = field(
default=None, default=None,
@@ -375,7 +375,7 @@ class Topic:
"name": "Description", "name": "Description",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
bim_snippet: Optional[BimSnippet] = field( bim_snippet: Optional[BimSnippet] = field(
default=None, default=None,
@@ -383,7 +383,7 @@ class Topic:
"name": "BimSnippet", "name": "BimSnippet",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
document_reference: List[TopicDocumentReference] = field( document_reference: List[TopicDocumentReference] = field(
default_factory=list, default_factory=list,
@@ -391,7 +391,7 @@ class Topic:
"name": "DocumentReference", "name": "DocumentReference",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
related_topic: List[TopicRelatedTopic] = field( related_topic: List[TopicRelatedTopic] = field(
default_factory=list, default_factory=list,
@@ -399,7 +399,7 @@ class Topic:
"name": "RelatedTopic", "name": "RelatedTopic",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -414,14 +414,14 @@ class Topic:
metadata={ metadata={
"name": "TopicType", "name": "TopicType",
"type": "Attribute", "type": "Attribute",
} },
) )
topic_status: Optional[str] = field( topic_status: Optional[str] = field(
default=None, default=None,
metadata={ metadata={
"name": "TopicStatus", "name": "TopicStatus",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -433,7 +433,7 @@ class Markup:
"name": "Header", "name": "Header",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
topic: Topic = field( topic: Topic = field(
metadata={ metadata={
@@ -449,7 +449,7 @@ class Markup:
"name": "Comment", "name": "Comment",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
viewpoints: List[ViewPoint] = field( viewpoints: List[ViewPoint] = field(
default_factory=list, default_factory=list,
@@ -457,5 +457,5 @@ class Markup:
"name": "Viewpoints", "name": "Viewpoints",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
+2 -2
View File
@@ -10,7 +10,7 @@ class Project:
"name": "Name", "name": "Name",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
project_id: str = field( project_id: str = field(
metadata={ metadata={
@@ -29,7 +29,7 @@ class ProjectExtension:
"name": "Project", "name": "Project",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
extension_schema: str = field( extension_schema: str = field(
metadata={ metadata={
+2 -2
View File
@@ -10,12 +10,12 @@ class Version:
"name": "DetailedVersion", "name": "DetailedVersion",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
version_id: Optional[str] = field( version_id: Optional[str] = field(
default=None, default=None,
metadata={ metadata={
"name": "VersionId", "name": "VersionId",
"type": "Attribute", "type": "Attribute",
} },
) )
+27 -24
View File
@@ -15,14 +15,14 @@ class Component:
metadata={ metadata={
"name": "OriginatingSystem", "name": "OriginatingSystem",
"type": "Element", "type": "Element",
} },
) )
authoring_tool_id: Optional[str] = field( authoring_tool_id: Optional[str] = field(
default=None, default=None,
metadata={ metadata={
"name": "AuthoringToolId", "name": "AuthoringToolId",
"type": "Element", "type": "Element",
} },
) )
ifc_guid: Optional[str] = field( ifc_guid: Optional[str] = field(
default=None, default=None,
@@ -31,7 +31,7 @@ class Component:
"type": "Attribute", "type": "Attribute",
"length": 22, "length": 22,
"pattern": r"[0-9,A-Z,a-z,_$]*", "pattern": r"[0-9,A-Z,a-z,_$]*",
} },
) )
@@ -92,21 +92,21 @@ class ViewSetupHints:
metadata={ metadata={
"name": "SpacesVisible", "name": "SpacesVisible",
"type": "Attribute", "type": "Attribute",
} },
) )
space_boundaries_visible: Optional[bool] = field( space_boundaries_visible: Optional[bool] = field(
default=None, default=None,
metadata={ metadata={
"name": "SpaceBoundariesVisible", "name": "SpaceBoundariesVisible",
"type": "Attribute", "type": "Attribute",
} },
) )
openings_visible: Optional[bool] = field( openings_visible: Optional[bool] = field(
default=None, default=None,
metadata={ metadata={
"name": "OpeningsVisible", "name": "OpeningsVisible",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -139,7 +139,7 @@ class ComponentColoringColor:
"name": "Component", "name": "Component",
"type": "Element", "type": "Element",
"min_occurs": 1, "min_occurs": 1,
} },
) )
color: Optional[str] = field( color: Optional[str] = field(
default=None, default=None,
@@ -147,7 +147,7 @@ class ComponentColoringColor:
"name": "Color", "name": "Color",
"type": "Attribute", "type": "Attribute",
"pattern": r"[0-9,a-f,A-F]{6}([0-9,a-f,A-F]{2})?", "pattern": r"[0-9,a-f,A-F]{6}([0-9,a-f,A-F]{2})?",
} },
) )
@@ -159,7 +159,7 @@ class ComponentSelection:
"name": "Component", "name": "Component",
"type": "Element", "type": "Element",
"min_occurs": 1, "min_occurs": 1,
} },
) )
@@ -174,7 +174,7 @@ class ComponentVisibilityExceptions:
"name": "Component", "name": "Component",
"type": "Element", "type": "Element",
"min_occurs": 1, "min_occurs": 1,
} },
) )
@@ -205,6 +205,7 @@ class OrthogonalCamera:
camera_up_vector: camera_up_vector:
view_to_world_scale: view's visible size in meters view_to_world_scale: view's visible size in meters
""" """
camera_view_point: Point = field( camera_view_point: Point = field(
metadata={ metadata={
"name": "CameraViewPoint", "name": "CameraViewPoint",
@@ -247,6 +248,7 @@ class PerspectiveCamera:
release and viewers should be expect values outside this release and viewers should be expect values outside this
range in current implementations. range in current implementations.
""" """
camera_view_point: Point = field( camera_view_point: Point = field(
metadata={ metadata={
"name": "CameraViewPoint", "name": "CameraViewPoint",
@@ -336,7 +338,7 @@ class ComponentColoring:
"name": "Color", "name": "Color",
"type": "Element", "type": "Element",
"min_occurs": 1, "min_occurs": 1,
} },
) )
@@ -347,14 +349,14 @@ class ComponentVisibility:
metadata={ metadata={
"name": "Exceptions", "name": "Exceptions",
"type": "Element", "type": "Element",
} },
) )
default_visibility: Optional[bool] = field( default_visibility: Optional[bool] = field(
default=None, default=None,
metadata={ metadata={
"name": "DefaultVisibility", "name": "DefaultVisibility",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -368,7 +370,7 @@ class VisualizationInfoClippingPlanes:
metadata={ metadata={
"name": "ClippingPlane", "name": "ClippingPlane",
"type": "Element", "type": "Element",
} },
) )
@@ -383,7 +385,7 @@ class VisualizationInfoLines:
"name": "Line", "name": "Line",
"type": "Element", "type": "Element",
"min_occurs": 1, "min_occurs": 1,
} },
) )
@@ -394,14 +396,14 @@ class Components:
metadata={ metadata={
"name": "ViewSetupHints", "name": "ViewSetupHints",
"type": "Element", "type": "Element",
} },
) )
selection: Optional[ComponentSelection] = field( selection: Optional[ComponentSelection] = field(
default=None, default=None,
metadata={ metadata={
"name": "Selection", "name": "Selection",
"type": "Element", "type": "Element",
} },
) )
visibility: ComponentVisibility = field( visibility: ComponentVisibility = field(
metadata={ metadata={
@@ -415,7 +417,7 @@ class Components:
metadata={ metadata={
"name": "Coloring", "name": "Coloring",
"type": "Element", "type": "Element",
} },
) )
@@ -424,47 +426,48 @@ class VisualizationInfo:
""" """
VisualizationInfo documentation. VisualizationInfo documentation.
""" """
components: Optional[Components] = field( components: Optional[Components] = field(
default=None, default=None,
metadata={ metadata={
"name": "Components", "name": "Components",
"type": "Element", "type": "Element",
} },
) )
orthogonal_camera: Optional[OrthogonalCamera] = field( orthogonal_camera: Optional[OrthogonalCamera] = field(
default=None, default=None,
metadata={ metadata={
"name": "OrthogonalCamera", "name": "OrthogonalCamera",
"type": "Element", "type": "Element",
} },
) )
perspective_camera: Optional[PerspectiveCamera] = field( perspective_camera: Optional[PerspectiveCamera] = field(
default=None, default=None,
metadata={ metadata={
"name": "PerspectiveCamera", "name": "PerspectiveCamera",
"type": "Element", "type": "Element",
} },
) )
lines: Optional[VisualizationInfoLines] = field( lines: Optional[VisualizationInfoLines] = field(
default=None, default=None,
metadata={ metadata={
"name": "Lines", "name": "Lines",
"type": "Element", "type": "Element",
} },
) )
clipping_planes: Optional[VisualizationInfoClippingPlanes] = field( clipping_planes: Optional[VisualizationInfoClippingPlanes] = field(
default=None, default=None,
metadata={ metadata={
"name": "ClippingPlanes", "name": "ClippingPlanes",
"type": "Element", "type": "Element",
} },
) )
bitmap: List[VisualizationInfoBitmap] = field( bitmap: List[VisualizationInfoBitmap] = field(
default_factory=list, default_factory=list,
metadata={ metadata={
"name": "Bitmap", "name": "Bitmap",
"type": "Element", "type": "Element",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML V2 Topic handler.""" """BCF XML V2 Topic handler."""
import datetime import datetime
import tempfile import tempfile
import uuid import uuid
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML V3 handlers.""" """BCF XML V3 handlers."""
import uuid import uuid
import warnings import warnings
import zipfile import zipfile
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML V3 Documents handler.""" """BCF XML V3 Documents handler."""
import zipfile import zipfile
from typing import Any, Optional from typing import Any, Optional
+3 -3
View File
@@ -22,7 +22,7 @@ class Document:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -45,7 +45,7 @@ class DocumentInfoDocuments:
"name": "Document", "name": "Document",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -57,5 +57,5 @@ class DocumentInfo:
"name": "Documents", "name": "Documents",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
+14 -14
View File
@@ -15,7 +15,7 @@ class ExtensionsPriorities:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -32,7 +32,7 @@ class ExtensionsSnippetTypes:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -49,7 +49,7 @@ class ExtensionsStages:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -66,7 +66,7 @@ class ExtensionsTopicLabels:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -83,7 +83,7 @@ class ExtensionsTopicStatuses:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -100,7 +100,7 @@ class ExtensionsTopicTypes:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -117,7 +117,7 @@ class ExtensionsUsers:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -129,7 +129,7 @@ class Extensions:
"name": "TopicTypes", "name": "TopicTypes",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
topic_statuses: Optional[ExtensionsTopicStatuses] = field( topic_statuses: Optional[ExtensionsTopicStatuses] = field(
default=None, default=None,
@@ -137,7 +137,7 @@ class Extensions:
"name": "TopicStatuses", "name": "TopicStatuses",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
priorities: Optional[ExtensionsPriorities] = field( priorities: Optional[ExtensionsPriorities] = field(
default=None, default=None,
@@ -145,7 +145,7 @@ class Extensions:
"name": "Priorities", "name": "Priorities",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
topic_labels: Optional[ExtensionsTopicLabels] = field( topic_labels: Optional[ExtensionsTopicLabels] = field(
default=None, default=None,
@@ -153,7 +153,7 @@ class Extensions:
"name": "TopicLabels", "name": "TopicLabels",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
users: Optional[ExtensionsUsers] = field( users: Optional[ExtensionsUsers] = field(
default=None, default=None,
@@ -161,7 +161,7 @@ class Extensions:
"name": "Users", "name": "Users",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
snippet_types: Optional[ExtensionsSnippetTypes] = field( snippet_types: Optional[ExtensionsSnippetTypes] = field(
default=None, default=None,
@@ -169,7 +169,7 @@ class Extensions:
"name": "SnippetTypes", "name": "SnippetTypes",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
stages: Optional[ExtensionsStages] = field( stages: Optional[ExtensionsStages] = field(
default=None, default=None,
@@ -177,5 +177,5 @@ class Extensions:
"name": "Stages", "name": "Stages",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
+42 -42
View File
@@ -40,7 +40,7 @@ class BimSnippet:
metadata={ metadata={
"name": "IsExternal", "name": "IsExternal",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -68,7 +68,7 @@ class DocumentReference:
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
"pattern": r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}", "pattern": r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}",
} },
) )
url: Optional[str] = field( url: Optional[str] = field(
default=None, default=None,
@@ -78,7 +78,7 @@ class DocumentReference:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
description: Optional[str] = field( description: Optional[str] = field(
default=None, default=None,
@@ -88,7 +88,7 @@ class DocumentReference:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -110,7 +110,7 @@ class File:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
date: Optional[XmlDateTime] = field( date: Optional[XmlDateTime] = field(
default=None, default=None,
@@ -118,7 +118,7 @@ class File:
"name": "Date", "name": "Date",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
reference: Optional[str] = field( reference: Optional[str] = field(
default=None, default=None,
@@ -128,7 +128,7 @@ class File:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
ifc_project: Optional[str] = field( ifc_project: Optional[str] = field(
default=None, default=None,
@@ -137,7 +137,7 @@ class File:
"type": "Attribute", "type": "Attribute",
"length": 22, "length": 22,
"pattern": r"[0-9A-Za-z_$]*", "pattern": r"[0-9A-Za-z_$]*",
} },
) )
ifc_spatial_structure_element: Optional[str] = field( ifc_spatial_structure_element: Optional[str] = field(
default=None, default=None,
@@ -146,14 +146,14 @@ class File:
"type": "Attribute", "type": "Attribute",
"length": 22, "length": 22,
"pattern": r"[0-9A-Za-z_$]*", "pattern": r"[0-9A-Za-z_$]*",
} },
) )
is_external: bool = field( is_external: bool = field(
default=True, default=True,
metadata={ metadata={
"name": "IsExternal", "name": "IsExternal",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -170,7 +170,7 @@ class TopicLabels:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -187,7 +187,7 @@ class TopicReferenceLinks:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
@@ -216,7 +216,7 @@ class ViewPoint:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
snapshot: Optional[str] = field( snapshot: Optional[str] = field(
default=None, default=None,
@@ -226,7 +226,7 @@ class ViewPoint:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
index: Optional[int] = field( index: Optional[int] = field(
default=None, default=None,
@@ -234,7 +234,7 @@ class ViewPoint:
"name": "Index", "name": "Index",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -274,7 +274,7 @@ class Comment:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
viewpoint: Optional[CommentViewpoint] = field( viewpoint: Optional[CommentViewpoint] = field(
default=None, default=None,
@@ -282,7 +282,7 @@ class Comment:
"name": "Viewpoint", "name": "Viewpoint",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
modified_date: Optional[XmlDateTime] = field( modified_date: Optional[XmlDateTime] = field(
default=None, default=None,
@@ -290,7 +290,7 @@ class Comment:
"name": "ModifiedDate", "name": "ModifiedDate",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
modified_author: Optional[str] = field( modified_author: Optional[str] = field(
default=None, default=None,
@@ -300,7 +300,7 @@ class Comment:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -323,7 +323,7 @@ class HeaderFiles:
"name": "File", "name": "File",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -338,7 +338,7 @@ class TopicDocumentReferences:
"name": "DocumentReference", "name": "DocumentReference",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -353,7 +353,7 @@ class TopicRelatedTopics:
"name": "RelatedTopic", "name": "RelatedTopic",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -368,7 +368,7 @@ class TopicViewpoints:
"name": "ViewPoint", "name": "ViewPoint",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -380,7 +380,7 @@ class Header:
"name": "Files", "name": "Files",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -395,7 +395,7 @@ class TopicComments:
"name": "Comment", "name": "Comment",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
@@ -407,7 +407,7 @@ class Topic:
"name": "ReferenceLinks", "name": "ReferenceLinks",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
title: str = field( title: str = field(
metadata={ metadata={
@@ -427,7 +427,7 @@ class Topic:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
index: Optional[int] = field( index: Optional[int] = field(
default=None, default=None,
@@ -435,7 +435,7 @@ class Topic:
"name": "Index", "name": "Index",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
labels: Optional[TopicLabels] = field( labels: Optional[TopicLabels] = field(
default=None, default=None,
@@ -443,7 +443,7 @@ class Topic:
"name": "Labels", "name": "Labels",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
creation_date: XmlDateTime = field( creation_date: XmlDateTime = field(
metadata={ metadata={
@@ -469,7 +469,7 @@ class Topic:
"name": "ModifiedDate", "name": "ModifiedDate",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
modified_author: Optional[str] = field( modified_author: Optional[str] = field(
default=None, default=None,
@@ -479,7 +479,7 @@ class Topic:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
due_date: Optional[XmlDateTime] = field( due_date: Optional[XmlDateTime] = field(
default=None, default=None,
@@ -487,7 +487,7 @@ class Topic:
"name": "DueDate", "name": "DueDate",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
assigned_to: Optional[str] = field( assigned_to: Optional[str] = field(
default=None, default=None,
@@ -497,7 +497,7 @@ class Topic:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
stage: Optional[str] = field( stage: Optional[str] = field(
default=None, default=None,
@@ -507,7 +507,7 @@ class Topic:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
description: Optional[str] = field( description: Optional[str] = field(
default=None, default=None,
@@ -517,7 +517,7 @@ class Topic:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
bim_snippet: Optional[BimSnippet] = field( bim_snippet: Optional[BimSnippet] = field(
default=None, default=None,
@@ -525,7 +525,7 @@ class Topic:
"name": "BimSnippet", "name": "BimSnippet",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
document_references: Optional[TopicDocumentReferences] = field( document_references: Optional[TopicDocumentReferences] = field(
default=None, default=None,
@@ -533,7 +533,7 @@ class Topic:
"name": "DocumentReferences", "name": "DocumentReferences",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
related_topics: Optional[TopicRelatedTopics] = field( related_topics: Optional[TopicRelatedTopics] = field(
default=None, default=None,
@@ -541,7 +541,7 @@ class Topic:
"name": "RelatedTopics", "name": "RelatedTopics",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
comments: Optional[TopicComments] = field( comments: Optional[TopicComments] = field(
default=None, default=None,
@@ -549,7 +549,7 @@ class Topic:
"name": "Comments", "name": "Comments",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
viewpoints: Optional[TopicViewpoints] = field( viewpoints: Optional[TopicViewpoints] = field(
default=None, default=None,
@@ -557,7 +557,7 @@ class Topic:
"name": "Viewpoints", "name": "Viewpoints",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
@@ -574,7 +574,7 @@ class Topic:
"type": "Attribute", "type": "Attribute",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
topic_type: str = field( topic_type: str = field(
metadata={ metadata={
@@ -604,7 +604,7 @@ class Markup:
"name": "Header", "name": "Header",
"type": "Element", "type": "Element",
"namespace": "", "namespace": "",
} },
) )
topic: Topic = field( topic: Topic = field(
metadata={ metadata={
+1 -1
View File
@@ -12,7 +12,7 @@ class Project:
"namespace": "", "namespace": "",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
project_id: str = field( project_id: str = field(
metadata={ metadata={
+28 -25
View File
@@ -17,7 +17,7 @@ class Component:
"type": "Element", "type": "Element",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
authoring_tool_id: Optional[str] = field( authoring_tool_id: Optional[str] = field(
default=None, default=None,
@@ -26,7 +26,7 @@ class Component:
"type": "Element", "type": "Element",
"min_length": 1, "min_length": 1,
"white_space": "collapse", "white_space": "collapse",
} },
) )
ifc_guid: Optional[str] = field( ifc_guid: Optional[str] = field(
default=None, default=None,
@@ -35,7 +35,7 @@ class Component:
"type": "Attribute", "type": "Attribute",
"length": 22, "length": 22,
"pattern": r"[0-9A-Za-z_$]*", "pattern": r"[0-9A-Za-z_$]*",
} },
) )
@@ -96,21 +96,21 @@ class ViewSetupHints:
metadata={ metadata={
"name": "SpacesVisible", "name": "SpacesVisible",
"type": "Attribute", "type": "Attribute",
} },
) )
space_boundaries_visible: bool = field( space_boundaries_visible: bool = field(
default=False, default=False,
metadata={ metadata={
"name": "SpaceBoundariesVisible", "name": "SpaceBoundariesVisible",
"type": "Attribute", "type": "Attribute",
} },
) )
openings_visible: bool = field( openings_visible: bool = field(
default=False, default=False,
metadata={ metadata={
"name": "OpeningsVisible", "name": "OpeningsVisible",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -191,7 +191,7 @@ class ComponentColoringColorComponents:
"name": "Component", "name": "Component",
"type": "Element", "type": "Element",
"min_occurs": 1, "min_occurs": 1,
} },
) )
@@ -202,7 +202,7 @@ class ComponentSelection:
metadata={ metadata={
"name": "Component", "name": "Component",
"type": "Element", "type": "Element",
} },
) )
@@ -216,7 +216,7 @@ class ComponentVisibilityExceptions:
metadata={ metadata={
"name": "Component", "name": "Component",
"type": "Element", "type": "Element",
} },
) )
@@ -249,6 +249,7 @@ class OrthogonalCamera:
aspect_ratio: Proportional relationship between the width and aspect_ratio: Proportional relationship between the width and
the height of the view (w/h). the height of the view (w/h).
""" """
camera_view_point: Point = field( camera_view_point: Point = field(
metadata={ metadata={
"name": "CameraViewPoint", "name": "CameraViewPoint",
@@ -302,6 +303,7 @@ class PerspectiveCamera:
aspect_ratio: Proportional relationship between the width and aspect_ratio: Proportional relationship between the width and
the height of the view (w/h). the height of the view (w/h).
""" """
camera_view_point: Point = field( camera_view_point: Point = field(
metadata={ metadata={
"name": "CameraViewPoint", "name": "CameraViewPoint",
@@ -371,21 +373,21 @@ class ComponentVisibility:
metadata={ metadata={
"name": "ViewSetupHints", "name": "ViewSetupHints",
"type": "Element", "type": "Element",
} },
) )
exceptions: Optional[ComponentVisibilityExceptions] = field( exceptions: Optional[ComponentVisibilityExceptions] = field(
default=None, default=None,
metadata={ metadata={
"name": "Exceptions", "name": "Exceptions",
"type": "Element", "type": "Element",
} },
) )
default_visibility: bool = field( default_visibility: bool = field(
default=False, default=False,
metadata={ metadata={
"name": "DefaultVisibility", "name": "DefaultVisibility",
"type": "Attribute", "type": "Attribute",
} },
) )
@@ -399,7 +401,7 @@ class VisualizationInfoBitmaps:
metadata={ metadata={
"name": "Bitmap", "name": "Bitmap",
"type": "Element", "type": "Element",
} },
) )
@@ -413,7 +415,7 @@ class VisualizationInfoClippingPlanes:
metadata={ metadata={
"name": "ClippingPlane", "name": "ClippingPlane",
"type": "Element", "type": "Element",
} },
) )
@@ -427,7 +429,7 @@ class VisualizationInfoLines:
metadata={ metadata={
"name": "Line", "name": "Line",
"type": "Element", "type": "Element",
} },
) )
@@ -438,7 +440,7 @@ class ComponentColoring:
metadata={ metadata={
"name": "Color", "name": "Color",
"type": "Element", "type": "Element",
} },
) )
@@ -449,21 +451,21 @@ class Components:
metadata={ metadata={
"name": "Selection", "name": "Selection",
"type": "Element", "type": "Element",
} },
) )
visibility: Optional[ComponentVisibility] = field( visibility: Optional[ComponentVisibility] = field(
default=None, default=None,
metadata={ metadata={
"name": "Visibility", "name": "Visibility",
"type": "Element", "type": "Element",
} },
) )
coloring: Optional[ComponentColoring] = field( coloring: Optional[ComponentColoring] = field(
default=None, default=None,
metadata={ metadata={
"name": "Coloring", "name": "Coloring",
"type": "Element", "type": "Element",
} },
) )
@@ -472,47 +474,48 @@ class VisualizationInfo:
""" """
VisualizationInfo documentation. VisualizationInfo documentation.
""" """
components: Optional[Components] = field( components: Optional[Components] = field(
default=None, default=None,
metadata={ metadata={
"name": "Components", "name": "Components",
"type": "Element", "type": "Element",
} },
) )
orthogonal_camera: Optional[OrthogonalCamera] = field( orthogonal_camera: Optional[OrthogonalCamera] = field(
default=None, default=None,
metadata={ metadata={
"name": "OrthogonalCamera", "name": "OrthogonalCamera",
"type": "Element", "type": "Element",
} },
) )
perspective_camera: Optional[PerspectiveCamera] = field( perspective_camera: Optional[PerspectiveCamera] = field(
default=None, default=None,
metadata={ metadata={
"name": "PerspectiveCamera", "name": "PerspectiveCamera",
"type": "Element", "type": "Element",
} },
) )
lines: Optional[VisualizationInfoLines] = field( lines: Optional[VisualizationInfoLines] = field(
default=None, default=None,
metadata={ metadata={
"name": "Lines", "name": "Lines",
"type": "Element", "type": "Element",
} },
) )
clipping_planes: Optional[VisualizationInfoClippingPlanes] = field( clipping_planes: Optional[VisualizationInfoClippingPlanes] = field(
default=None, default=None,
metadata={ metadata={
"name": "ClippingPlanes", "name": "ClippingPlanes",
"type": "Element", "type": "Element",
} },
) )
bitmaps: Optional[VisualizationInfoBitmaps] = field( bitmaps: Optional[VisualizationInfoBitmaps] = field(
default=None, default=None,
metadata={ metadata={
"name": "Bitmaps", "name": "Bitmaps",
"type": "Element", "type": "Element",
} },
) )
guid: str = field( guid: str = field(
metadata={ metadata={
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML V3 Topic handler.""" """BCF XML V3 Topic handler."""
import datetime import datetime
import uuid import uuid
import zipfile import zipfile
+1
View File
@@ -1,4 +1,5 @@
"""XML Parser and Serializer factories.""" """XML Parser and Serializer factories."""
from typing import Optional, Protocol, Type, TypeVar from typing import Optional, Protocol, Type, TypeVar
from xsdata.formats.dataclass.context import XmlContext from xsdata.formats.dataclass.context import XmlContext
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML tests.""" """BCF XML tests."""
import uuid import uuid
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
+1
View File
@@ -1,4 +1,5 @@
"""BCF XML tests.""" """BCF XML tests."""
import uuid import uuid
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
+21 -10
View File
@@ -3,39 +3,50 @@ from bsdd import Client
client = Client() client = Client()
ifc4x3_uri = [l["uri"] for l in client.get_dictionary()["dictionaries"] if "4.3" in l["uri"]][0] ifc4x3_uri = [l["uri"] for l in client.get_dictionary()["dictionaries"] if "4.3" in l["uri"]][0]
nbs_uri = [l["uri"] for l in client.get_dictionary()["dictionaries"] if 'Uniclass 2015' == l["name"]][0] nbs_uri = [l["uri"] for l in client.get_dictionary()["dictionaries"] if "Uniclass 2015" == l["name"]][0]
def get_ifc_classes(): def get_ifc_classes():
return client.get_classes(ifc4x3_uri, use_nested_classes= False, class_type="Class") return client.get_classes(ifc4x3_uri, use_nested_classes=False, class_type="Class")
def get_nbs_classes(): def get_nbs_classes():
return client.get_classes(nbs_uri, use_nested_classes= False, class_type="Class", offset=0, limit=5) return client.get_classes(nbs_uri, use_nested_classes=False, class_type="Class", offset=0, limit=5)
def test_get_dictionary(): def test_get_dictionary():
li_names = [l["name"] for l in client.get_dictionary()["dictionaries"]] li_names = [l["name"] for l in client.get_dictionary()["dictionaries"]]
assert 'Uniclass 2015' and 'IFC' in li_names assert "Uniclass 2015" and "IFC" in li_names
def test_get_ifc_classes(): def test_get_ifc_classes():
ifc4x3_classes = get_ifc_classes() ifc4x3_classes = get_ifc_classes()
assert "IfcBoiler" and "IfcLightFixture" in [l["code"] for l in ifc4x3_classes["classes"]] assert "IfcBoiler" and "IfcLightFixture" in [l["code"] for l in ifc4x3_classes["classes"]]
def test_get_nbs_classes(): def test_get_nbs_classes():
nbs_classes = get_nbs_classes() nbs_classes = get_nbs_classes()
assert "Ac" in [l["code"] for l in nbs_classes["classes"]] assert "Ac" in [l["code"] for l in nbs_classes["classes"]]
def test_get_class(): def test_get_class():
uri_light_fixture = [l for l in get_ifc_classes()["classes"] if "IfcLightFixture" == l["code"]][0]["uri"] uri_light_fixture = [l for l in get_ifc_classes()["classes"] if "IfcLightFixture" == l["code"]][0]["uri"]
ifc4x3_light_fixture = client.get_class(uri_light_fixture) ifc4x3_light_fixture = client.get_class(uri_light_fixture)
assert 'Maintenance Factor' and 'Light Fixture Mounting Type' in [l["name"] for l in ifc4x3_light_fixture["classProperties"]] assert "Maintenance Factor" and "Light Fixture Mounting Type" in [
l["name"] for l in ifc4x3_light_fixture["classProperties"]
]
def test_search_class(): def test_search_class():
ss_heat_pump_sys = client.search_class("Ss_60_40_36", [nbs_uri] ) ss_heat_pump_sys = client.search_class("Ss_60_40_36", [nbs_uri])
li = [l + "source heat pump systems" for l in ["Air ", "Ground ", "Water "]] li = [l + "source heat pump systems" for l in ["Air ", "Ground ", "Water "]]
assert len(li) < 8 # I think it should be 4 but just validating it isn't overfetching with some space for future change assert (
for l in li: len(li) < 8
) # I think it should be 4 but just validating it isn't overfetching with some space for future change
for l in li:
assert l in [_["name"] for _ in ss_heat_pump_sys["classes"]] assert l in [_["name"] for _ in ss_heat_pump_sys["classes"]]
def test_get_properties(): def test_get_properties():
pr = client.get_properties(ifc4x3_uri, offset=0, limit=5) pr = client.get_properties(ifc4x3_uri, offset=0, limit=5)
assert len(pr["properties"]) == 5 assert len(pr["properties"]) == 5
-1
View File
@@ -1,4 +1,3 @@
# Ifc2CA - IFC Code_Aster utility # Ifc2CA - IFC Code_Aster utility
# Copyright (C) 2020, 2021 Ioannis P. Christovasilis <ipc@aethereng.com> # Copyright (C) 2020, 2021 Ioannis P. Christovasilis <ipc@aethereng.com>
# #
+17 -47
View File
@@ -55,9 +55,7 @@ class COMMANDFILE:
conn["relatedElements"] = [] conn["relatedElements"] = []
for el in elements: for el in elements:
for rel in el["connections"]: for rel in el["connections"]:
conn = [ conn = [c for c in connections if c["referenceName"] == rel["relatedConnection"]][0]
c for c in connections if c["referenceName"] == rel["relatedConnection"]
][0]
conn["relatedElements"].append(rel) conn["relatedElements"].append(rel)
# End <-- # End <--
@@ -65,27 +63,17 @@ class COMMANDFILE:
profiles = data["db"]["profiles"] profiles = data["db"]["profiles"]
edgeGroupNames = tuple( edgeGroupNames = tuple(
[ [self.getGroupName(el["referenceName"]) for el in elements if el["geometryType"] == "line"]
self.getGroupName(el["referenceName"])
for el in elements
if el["geometryType"] == "line"
]
) )
faceGroupNames = tuple( faceGroupNames = tuple(
[ [self.getGroupName(el["referenceName"]) for el in elements if el["geometryType"] == "surface"]
self.getGroupName(el["referenceName"])
for el in elements
if el["geometryType"] == "surface"
]
) )
rigidLinkGroupNames = [] rigidLinkGroupNames = []
for conn in connections: for conn in connections:
rigidLinkGroupNames.extend( rigidLinkGroupNames.extend(
[ [
self.getGroupName(rel["relatingElement"]) self.getGroupName(rel["relatingElement"]) + "_1DR_" + self.getGroupName(conn["referenceName"])
+ "_1DR_"
+ self.getGroupName(conn["referenceName"])
for rel in conn["relatedElements"] for rel in conn["relatedElements"]
if rel["eccentricity"] if rel["eccentricity"]
] ]
@@ -192,20 +180,16 @@ model = AFFE_MODELE(
else: else:
if "shearModulus" in material["mechProps"]: if "shearModulus" in material["mechProps"]:
poissonRatio = ( poissonRatio = (
material["mechProps"]["youngModulus"] material["mechProps"]["youngModulus"] / 2.0 / material["mechProps"]["shearModulus"]
/ 2.0
/ material["mechProps"]["shearModulus"]
) - 1 ) - 1
else: else:
poissonRatio = 0.0 poissonRatio = 0.0
context = { context = {
"matNameID": "mat" + "_%s" % i, "matNameID": "mat" + "_%s" % i,
"youngModulus": float(material["mechProps"]["youngModulus"]) "youngModulus": float(material["mechProps"]["youngModulus"]) * ScaleFactor**2,
* ScaleFactor ** 2,
"poissonRatio": float(poissonRatio), "poissonRatio": float(poissonRatio),
"massDensity": float(material["commonProps"]["massDensity"]) "massDensity": float(material["commonProps"]["massDensity"]) * ScaleFactor**3,
* ScaleFactor ** 3,
} }
f.write(template.format(**context)) f.write(template.format(**context))
@@ -225,9 +209,7 @@ material = AFFE_MATERIAU(
),""" ),"""
context = { context = {
"groupNames": tuple( "groupNames": tuple([self.getGroupName(rel) for rel in material["relatedElements"]]),
[self.getGroupName(rel) for rel in material["relatedElements"]]
),
"matNameID": "mat" + "_%s" % i, "matNameID": "mat" + "_%s" % i,
} }
@@ -260,10 +242,7 @@ element = AFFE_CARA_ELEM(
) )
for profile in profiles: for profile in profiles:
if ( if profile["profileShape"] == "rectangular" and profile["profileType"] == "AREA":
profile["profileShape"] == "rectangular"
and profile["profileType"] == "AREA"
):
template = """ template = """
_F( _F(
GROUP_MA = {groupNames}, GROUP_MA = {groupNames},
@@ -273,9 +252,7 @@ element = AFFE_CARA_ELEM(
),""" ),"""
context = { context = {
"groupNames": tuple( "groupNames": tuple([self.getGroupName(rel) for rel in profile["relatedElements"]]),
[self.getGroupName(rel) for rel in profile["relatedElements"]]
),
"profileDimensions": ( "profileDimensions": (
profile["xDim"] / ScaleFactor, profile["xDim"] / ScaleFactor,
profile["yDim"] / ScaleFactor, profile["yDim"] / ScaleFactor,
@@ -284,10 +261,7 @@ element = AFFE_CARA_ELEM(
f.write(template.format(**context)) f.write(template.format(**context))
elif ( elif profile["profileShape"] == "iSymmetrical" and profile["profileType"] == "AREA":
profile["profileShape"] == "iSymmetrical"
and profile["profileType"] == "AREA"
):
template = """ template = """
_F( _F(
GROUP_MA = {groupNames}, GROUP_MA = {groupNames},
@@ -297,14 +271,12 @@ element = AFFE_CARA_ELEM(
),""" ),"""
context = { context = {
"groupNames": tuple( "groupNames": tuple([self.getGroupName(rel) for rel in profile["relatedElements"]]),
[self.getGroupName(rel) for rel in profile["relatedElements"]]
),
"profileProperties": ( "profileProperties": (
profile["mechProps"]["crossSectionArea"] / ScaleFactor ** 2, profile["mechProps"]["crossSectionArea"] / ScaleFactor**2,
profile["mechProps"]["momentOfInertiaY"] / ScaleFactor ** 4, profile["mechProps"]["momentOfInertiaY"] / ScaleFactor**4,
profile["mechProps"]["momentOfInertiaZ"] / ScaleFactor ** 4, profile["mechProps"]["momentOfInertiaZ"] / ScaleFactor**4,
profile["mechProps"]["torsionalConstantX"] / ScaleFactor ** 4, profile["mechProps"]["torsionalConstantX"] / ScaleFactor**4,
), ),
} }
@@ -572,9 +544,7 @@ if __name__ == "__main__":
files = fileNames files = fileNames
for fileName in files: for fileName in files:
BASE_PATH = Path( BASE_PATH = Path("/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/")
"/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/"
)
DATAFILENAME = BASE_PATH / fileName / f"{fileName}.json" DATAFILENAME = BASE_PATH / fileName / f"{fileName}.json"
ASTERFILENAME = BASE_PATH / fileName / f"{fileName}.comm" ASTERFILENAME = BASE_PATH / fileName / f"{fileName}.comm"
COMMANDFILE(DATAFILENAME, ASTERFILENAME) COMMANDFILE(DATAFILENAME, ASTERFILENAME)
+17 -48
View File
@@ -30,6 +30,7 @@ from pathlib import Path
flatten = itertools.chain.from_iterable flatten = itertools.chain.from_iterable
class MODEL: class MODEL:
def __init__(self, dataFilename, medFilename, meshSize, zGround): def __init__(self, dataFilename, medFilename, meshSize, zGround):
self.dataFilename = dataFilename self.dataFilename = dataFilename
@@ -97,9 +98,7 @@ class MODEL:
shapeType = "EDGE" shapeType = "EDGE"
if geometryType == "surface": if geometryType == "surface":
shapeType = "FACE" shapeType = "FACE"
return self.geompy.MakePartition( return self.geompy.MakePartition(objects, [], [], [], self.geompy.ShapeType[shapeType], 0, [], 1)
objects, [], [], [], self.geompy.ShapeType[shapeType], 0, [], 1
)
def getLinkGeometry(self, ecc, orientation, finalPoint): def getLinkGeometry(self, ecc, orientation, finalPoint):
vector = np.array(orientation).transpose().dot(ecc["vector"]) vector = np.array(orientation).transpose().dot(ecc["vector"])
@@ -195,29 +194,21 @@ class MODEL:
el["linkObjs"] = [None for _ in el["connections"]] el["linkObjs"] = [None for _ in el["connections"]]
for j, rel in enumerate(el["connections"]): for j, rel in enumerate(el["connections"]):
conn = [ conn = [c for c in connections if c["referenceName"] == rel["relatedConnection"]][0]
c for c in connections if c["referenceName"] == rel["relatedConnection"]
][0]
if rel["eccentricity"]: if rel["eccentricity"]:
rel["index"] = len(conn["relatedElements"]) + 1 rel["index"] = len(conn["relatedElements"]) + 1
geometry = self.getLinkGeometry( geometry = self.getLinkGeometry(rel["eccentricity"], el["orientation"], conn["geometry"])
rel["eccentricity"], el["orientation"], conn["geometry"]
)
el["linkObjs"][j] = self.makeObject(geometry, "line") el["linkObjs"][j] = self.makeObject(geometry, "line")
conn["relatedElements"].append(rel) conn["relatedElements"].append(rel)
# Make assemble of Building Object # Make assemble of Building Object
bldObjs = [] bldObjs = []
bldObjs.extend([el["elemObj"] for el in elements]) bldObjs.extend([el["elemObj"] for el in elements])
bldObjs.extend( bldObjs.extend(flatten([[link for link in el["linkObjs"] if link] for el in elements]))
flatten([[link for link in el["linkObjs"] if link] for el in elements])
)
# bldComp = geompy.MakeCompound(bldObjs) # bldComp = geompy.MakeCompound(bldObjs)
bldComp = geompy.MakePartition( bldComp = geompy.MakePartition(bldObjs, [], [], [], self.geompy.ShapeType[buildingShapeType], 0, [], 1)
bldObjs, [], [], [], self.geompy.ShapeType[buildingShapeType], 0, [], 1
)
geompy.addToStudy(bldComp, "bldComp") geompy.addToStudy(bldComp, "bldComp")
elapsed_time = time.time() - init_time elapsed_time = time.time() - init_time
@@ -227,25 +218,19 @@ class MODEL:
# Define and add groups for all curve, surface and rigid members # Define and add groups for all curve, surface and rigid members
if len([e for e in elements if e["geometryType"] == "line"]) > 0: if len([e for e in elements if e["geometryType"] == "line"]) > 0:
# Make compound of requested group # Make compound of requested group
compoundTemp = geompy.MakeCompound( compoundTemp = geompy.MakeCompound([e["elemObj"] for e in elements if e["geometryType"] == "line"])
[e["elemObj"] for e in elements if e["geometryType"] == "line"]
)
# Define group object and add to study # Define group object and add to study
curveCompound = geompy.GetInPlace(bldComp, compoundTemp, True) curveCompound = geompy.GetInPlace(bldComp, compoundTemp, True)
geompy.addToStudyInFather(bldComp, curveCompound, "CurveMembers") geompy.addToStudyInFather(bldComp, curveCompound, "CurveMembers")
if len([e for e in elements if e["geometryType"] == "surface"]) > 0: if len([e for e in elements if e["geometryType"] == "surface"]) > 0:
# Make compound of requested group # Make compound of requested group
compoundTemp = geompy.MakeCompound( compoundTemp = geompy.MakeCompound([e["elemObj"] for e in elements if e["geometryType"] == "surface"])
[e["elemObj"] for e in elements if e["geometryType"] == "surface"]
)
# Define group object and add to study # Define group object and add to study
surfaceCompound = geompy.GetInPlace(bldComp, compoundTemp, True) surfaceCompound = geompy.GetInPlace(bldComp, compoundTemp, True)
geompy.addToStudyInFather(bldComp, surfaceCompound, "SurfaceMembers") geompy.addToStudyInFather(bldComp, surfaceCompound, "SurfaceMembers")
linkObjs = list( linkObjs = list(flatten([[obj for obj in el["linkObjs"] if obj] for el in elements]))
flatten([[obj for obj in el["linkObjs"] if obj] for el in elements])
)
if len(linkObjs) > 0: if len(linkObjs) > 0:
# Make compound of requested group # Make compound of requested group
compoundTemp = geompy.MakeCompound(linkObjs) compoundTemp = geompy.MakeCompound(linkObjs)
@@ -256,21 +241,15 @@ class MODEL:
for el in elements: for el in elements:
# el['partObj'] = geompy.RestoreGivenSubShapes(bldComp, [el['partObj']], GEOM.FSM_GetInPlace, False, False)[0] # el['partObj'] = geompy.RestoreGivenSubShapes(bldComp, [el['partObj']], GEOM.FSM_GetInPlace, False, False)[0]
el["elemObj"] = geompy.GetInPlace(bldComp, el["elemObj"], True) el["elemObj"] = geompy.GetInPlace(bldComp, el["elemObj"], True)
geompy.addToStudyInFather( geompy.addToStudyInFather(bldComp, el["elemObj"], self.getGroupName(el["referenceName"]))
bldComp, el["elemObj"], self.getGroupName(el["referenceName"])
)
for j, rel in enumerate(el["connections"]): for j, rel in enumerate(el["connections"]):
if rel["eccentricity"]: # point geometry if rel["eccentricity"]: # point geometry
el["linkObjs"][j] = geompy.GetInPlace( el["linkObjs"][j] = geompy.GetInPlace(bldComp, el["linkObjs"][j], True)
bldComp, el["linkObjs"][j], True
)
geompy.addToStudyInFather( geompy.addToStudyInFather(
bldComp, bldComp,
el["linkObjs"][j], el["linkObjs"][j],
self.getGroupName(el["referenceName"]) self.getGroupName(el["referenceName"]) + "_1DR_" + self.getGroupName(rel["relatedConnection"]),
+ "_1DR_"
+ self.getGroupName(rel["relatedConnection"]),
) )
elapsed_time = time.time() - init_time elapsed_time = time.time() - init_time
@@ -307,9 +286,7 @@ class MODEL:
NETGEN2D_Pars.SetFuseEdges(254) NETGEN2D_Pars.SetFuseEdges(254)
isDone = bldMesh.Compute() isDone = bldMesh.Compute()
coincident_nodes_on_part = bldMesh.FindCoincidentNodesOnPart( coincident_nodes_on_part = bldMesh.FindCoincidentNodesOnPart([bldMesh], tolLoc, [], 0)
[bldMesh], tolLoc, [], 0
)
if coincident_nodes_on_part: if coincident_nodes_on_part:
# bldMesh.MergeNodes(coincident_nodes_on_part, [], 0) # bldMesh.MergeNodes(coincident_nodes_on_part, [], 0)
# print(f'{len(coincident_nodes_on_part)} Sets of Coincident Nodes Found and Merged') # print(f'{len(coincident_nodes_on_part)} Sets of Coincident Nodes Found and Merged')
@@ -349,25 +326,19 @@ class MODEL:
shapeType = SMESH.EDGE shapeType = SMESH.EDGE
if el["geometryType"] == "surface": if el["geometryType"] == "surface":
shapeType = SMESH.FACE shapeType = SMESH.FACE
tempgroup = bldMesh.GroupOnGeom( tempgroup = bldMesh.GroupOnGeom(el["elemObj"], self.getGroupName(el["referenceName"]), shapeType)
el["elemObj"], self.getGroupName(el["referenceName"]), shapeType
)
smesh.SetName(tempgroup, self.getGroupName(el["referenceName"])) smesh.SetName(tempgroup, self.getGroupName(el["referenceName"]))
for j, rel in enumerate(el["connections"]): for j, rel in enumerate(el["connections"]):
if rel["eccentricity"]: if rel["eccentricity"]:
tempgroup = bldMesh.GroupOnGeom( tempgroup = bldMesh.GroupOnGeom(
el["linkObjs"][j], el["linkObjs"][j],
self.getGroupName(el["referenceName"]) self.getGroupName(el["referenceName"]) + "_1DR_" + self.getGroupName(rel["relatedConnection"]),
+ "_1DR_"
+ self.getGroupName(rel["relatedConnection"]),
SMESH.EDGE, SMESH.EDGE,
) )
smesh.SetName( smesh.SetName(
tempgroup, tempgroup,
self.getGroupName(el["referenceName"]) self.getGroupName(el["referenceName"]) + "_1DR_" + self.getGroupName(rel["relatedConnection"]),
+ "_1DR_"
+ self.getGroupName(rel["relatedConnection"]),
) )
self.mesh = bldMesh self.mesh = bldMesh
@@ -420,9 +391,7 @@ if __name__ == "__main__":
zGround = 0 zGround = 0
for fileName in files: for fileName in files:
BASE_PATH = Path( BASE_PATH = Path("/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/")
"/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/"
)
DATAFILENAME = BASE_PATH / fileName / f"{fileName}.json" DATAFILENAME = BASE_PATH / fileName / f"{fileName}.json"
MEDFILENAME = BASE_PATH / fileName / f"{fileName}.med" MEDFILENAME = BASE_PATH / fileName / f"{fileName}.med"
model = MODEL(DATAFILENAME, str(MEDFILENAME), meshSize, zGround) model = MODEL(DATAFILENAME, str(MEDFILENAME), meshSize, zGround)
-1
View File
@@ -1,4 +1,3 @@
# Ifc2CA - IFC Code_Aster utility # Ifc2CA - IFC Code_Aster utility
# Copyright (C) 2020, 2021, 2023, 2024 Ioannis P. Christovasilis <ipc@aethereng.com> # Copyright (C) 2020, 2021, 2023, 2024 Ioannis P. Christovasilis <ipc@aethereng.com>
# #
+2
View File
@@ -24,10 +24,12 @@ from pathlib import Path
from typing import Dict, List from typing import Dict, List
import ifcopenshell as ios import ifcopenshell as ios
# import ifcopenshell.geom # import ifcopenshell.geom
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.placement import ifcopenshell.util.placement
import ifcopenshell.util.representation import ifcopenshell.util.representation
# import ifcopenshell.util.shape # import ifcopenshell.util.shape
import numpy as np import numpy as np
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
+18 -14
View File
@@ -9,23 +9,26 @@ from ifc4d.pp2ifc import PP2Ifc
import ifcopenshell import ifcopenshell
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser .add_argument('-f','--file', action='store', type=str, parser.add_argument("-f", "--file", action="store", type=str, required=True, help="schedule file name to be parsed")
required=True, help="schedule file name to be parsed") parser.add_argument(
parser .add_argument('-s','--schedule', action='store', required=True, "-s", "--schedule", action="store", required=True, type=str, help="file format as xer, p6xml, mspxml, pp"
type=str, help='file format as xer, p6xml, mspxml, pp') )
parser .add_argument('-i', '--ifcfile', action='store', required=False, parser.add_argument(
type=str, help='ifc file name as string e.g. \"file.ifc\"') "-i", "--ifcfile", action="store", required=False, type=str, help='ifc file name as string e.g. "file.ifc"'
parser .add_argument('-o', '--output', action='store', required=True, )
type=str, help='ifc file name as string e.g. \"file.ifc\"') parser.add_argument(
args = parser .parse_args() "-o", "--output", action="store", required=True, type=str, help='ifc file name as string e.g. "file.ifc"'
)
args = parser.parse_args()
def get_file(): def get_file():
ifcfile = None ifcfile = None
if args.ifcfile: if args.ifcfile:
ifcfile = ifcopenshell.open(args.ifcfile) ifcfile = ifcopenshell.open(args.ifcfile)
elif args.output: elif args.output:
ifc = ifcopenshell.file(schema='IFC4') ifc = ifcopenshell.file(schema="IFC4")
ifc.create_entity("IfcWorkPlan") ifc.create_entity("IfcWorkPlan")
ifc.create_entity("IfcProject") ifc.create_entity("IfcProject")
ifc.write(args.output) ifc.write(args.output)
@@ -34,6 +37,7 @@ def get_file():
ifcfile = None ifcfile = None
return ifcfile return ifcfile
if not args.ifcfile: if not args.ifcfile:
print("You need to provide an ifc file to add schedule") print("You need to provide an ifc file to add schedule")
print("Examples python ifc4d -i model.ifc -s xer -f schedule.xer -o newifc.ifc") print("Examples python ifc4d -i model.ifc -s xer -f schedule.xer -o newifc.ifc")
@@ -41,7 +45,7 @@ elif not args.output:
print("an output file is required to save changes") print("an output file is required to save changes")
print("python ifc4d -o newfile.ifc -s xer -f schedule.xer") print("python ifc4d -o newfile.ifc -s xer -f schedule.xer")
elif args.schedule== "xer": elif args.schedule == "xer":
p6xer = P6XER2Ifc() p6xer = P6XER2Ifc()
p6xer.xer = args.file p6xer.xer = args.file
p6xer.output = args.output p6xer.output = args.output
@@ -49,7 +53,8 @@ elif args.schedule== "xer":
if ifcfile: if ifcfile:
p6xer.file = ifcfile p6xer.file = ifcfile
p6xer.execute() p6xer.execute()
else: raise Exception("No files provided for output") else:
raise Exception("No files provided for output")
elif args.schedule == "mspxml": elif args.schedule == "mspxml":
msp = MSP2Ifc() msp = MSP2Ifc()
msp.xml = args.file() msp.xml = args.file()
@@ -76,4 +81,3 @@ elif args.schedule == "pp":
pp.execute() pp.execute()
else: else:
print("schedule type you selected is not implemented at the moment") print("schedule type you selected is not implemented at the moment")
+5 -6
View File
@@ -246,12 +246,11 @@ class ScheduleIfcGenerator:
"ScheduleStart": activity["StartDate"], "ScheduleStart": activity["StartDate"],
"ScheduleFinish": activity["FinishDate"], "ScheduleFinish": activity["FinishDate"],
"DurationType": "WORKTIME" if activity["PlannedDuration"] else None, "DurationType": "WORKTIME" if activity["PlannedDuration"] else None,
"ScheduleDuration": timedelta( "ScheduleDuration": (
days=float(activity["PlannedDuration"]) / float(calendar["HoursPerDay"] or 8) timedelta(days=float(activity["PlannedDuration"]) / float(calendar["HoursPerDay"] or 8)) or None
) if activity["PlannedDuration"]
or None else None
if activity["PlannedDuration"] ),
else None,
}, },
) )
+31 -7
View File
@@ -86,12 +86,36 @@ class Csv2Ifc:
task_relationships = self.parse_task_rel(row[self.headers["Relationships"]]) task_relationships = self.parse_task_rel(row[self.headers["Relationships"]])
scheduled_start_date = ifcopenshell.util.date.string_to_date(row[self.headers["ScheduleStart"]]) if row[self.headers["ScheduleStart"]] else None scheduled_start_date = (
scheduled_finish_date = ifcopenshell.util.date.string_to_date(row[self.headers["ScheduleFinish"]]) if row[self.headers["ScheduleFinish"]] else None ifcopenshell.util.date.string_to_date(row[self.headers["ScheduleStart"]])
scheduled_duration = ifcopenshell.util.date.string_to_duration(row[self.headers["ScheduleDuration"]]) if row[self.headers["ScheduleDuration"]] else None if row[self.headers["ScheduleStart"]]
actual_start_date = ifcopenshell.util.date.string_to_date(row[self.headers["ActualStart"]]) if row[self.headers["ActualStart"]] else None else None
actual_finish_date = ifcopenshell.util.date.string_to_date(row[self.headers["ActualFinish"]]) if row[self.headers["ActualFinish"]] else None )
actual_duration = ifcopenshell.util.date.string_to_duration(row[self.headers["ActualDuration"]]) if row[self.headers["ActualDuration"]] else None scheduled_finish_date = (
ifcopenshell.util.date.string_to_date(row[self.headers["ScheduleFinish"]])
if row[self.headers["ScheduleFinish"]]
else None
)
scheduled_duration = (
ifcopenshell.util.date.string_to_duration(row[self.headers["ScheduleDuration"]])
if row[self.headers["ScheduleDuration"]]
else None
)
actual_start_date = (
ifcopenshell.util.date.string_to_date(row[self.headers["ActualStart"]])
if row[self.headers["ActualStart"]]
else None
)
actual_finish_date = (
ifcopenshell.util.date.string_to_date(row[self.headers["ActualFinish"]])
if row[self.headers["ActualFinish"]]
else None
)
actual_duration = (
ifcopenshell.util.date.string_to_duration(row[self.headers["ActualDuration"]])
if row[self.headers["ActualDuration"]]
else None
)
return { return {
"Hierarchy": hierarchy, "Hierarchy": hierarchy,
@@ -191,7 +215,7 @@ class Csv2Ifc:
self.file, self.file,
related_process=task_2, related_process=task_2,
relating_process=task_1, relating_process=task_1,
sequence_type = rel_type, sequence_type=rel_type,
) )
if rel_type: if rel_type:
ifcopenshell.api.run( ifcopenshell.api.run(
+3 -3
View File
@@ -46,9 +46,9 @@ class Ifc2P6:
self.root = ET.Element("APIBusinessObjects") self.root = ET.Element("APIBusinessObjects")
self.root.attrib["xmlns"] = "http://xmlns.oracle.com/Primavera/P6Professional/V18.8/API/BusinessObjects" self.root.attrib["xmlns"] = "http://xmlns.oracle.com/Primavera/P6Professional/V18.8/API/BusinessObjects"
self.root.attrib["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance" self.root.attrib["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance"
self.root.attrib[ self.root.attrib["xsi:schemaLocation"] = (
"xsi:schemaLocation" "http://xmlns.oracle.com/Primavera/P6Professional/V18.8/API/BusinessObjects http://xmlns.oracle.com/Primavera/P6Professional/V18.8/API/p6apibo.xsd"
] = "http://xmlns.oracle.com/Primavera/P6Professional/V18.8/API/BusinessObjects http://xmlns.oracle.com/Primavera/P6Professional/V18.8/API/p6apibo.xsd" )
self.schedule = self.file.by_type("IfcWorkSchedule")[0] self.schedule = self.file.by_type("IfcWorkSchedule")[0]
+37 -29
View File
@@ -112,13 +112,12 @@ class MSP2Ifc:
# If first column = "all" then retrieve all columns # If first column = "all" then retrieve all columns
if len(self.optionalColumns) and self.optionalColumns[0] == "all": if len(self.optionalColumns) and self.optionalColumns[0] == "all":
self.optionalColumns = [child.tag.split("}")[1] for child in task] self.optionalColumns = [child.tag.split("}")[1] for child in task]
for column in self.optionalColumns: for column in self.optionalColumns:
if not self.tasks[task_id].get(column): if not self.tasks[task_id].get(column):
self.tasks[task_id][column] = task.find(f"pr:{column}", self.ns).text if task.find(f"pr:{column}", self.ns) else None self.tasks[task_id][column] = (
task.find(f"pr:{column}", self.ns).text if task.find(f"pr:{column}", self.ns) else None
)
def parse_calendar_xml(self, project): def parse_calendar_xml(self, project):
def parse_working_times(day): def parse_working_times(day):
@@ -139,27 +138,37 @@ class MSP2Ifc:
work_times = parse_working_times(exception) work_times = parse_working_times(exception)
time_period = exception.find("pr:TimePeriod", self.ns) time_period = exception.find("pr:TimePeriod", self.ns)
data = { data = {
"Name": exception.find("pr:Name", self.ns).text "Name": (
if exception.find("pr:Name", self.ns) is not None exception.find("pr:Name", self.ns).text if exception.find("pr:Name", self.ns) is not None else None
else None, ),
"FromDate": datetime.datetime.fromisoformat(time_period.find("pr:FromDate", self.ns).text) "FromDate": (
if time_period is not None datetime.datetime.fromisoformat(time_period.find("pr:FromDate", self.ns).text)
else None, if time_period is not None
"ToDate": datetime.datetime.fromisoformat(time_period.find("pr:ToDate", self.ns).text) else None
if time_period is not None ),
else None, "ToDate": (
"Occurrences": int(exception.find("pr:Occurrences", self.ns).text) datetime.datetime.fromisoformat(time_period.find("pr:ToDate", self.ns).text)
if exception.find("pr:Occurrences", self.ns) is not None if time_period is not None
else None, else None
"Month": exception.find("pr:Month", self.ns).text ),
if exception.find("pr:Month", self.ns) is not None "Occurrences": (
else None, int(exception.find("pr:Occurrences", self.ns).text)
"MonthDay": exception.find("pr:MonthDay", self.ns).text if exception.find("pr:Occurrences", self.ns) is not None
if exception.find("pr:MonthDay", self.ns) is not None else None
else None, ),
"Type": exception.find("pr:Type", self.ns).text "Month": (
if exception.find("pr:Type", self.ns) is not None exception.find("pr:Month", self.ns).text
else None, if exception.find("pr:Month", self.ns) is not None
else None
),
"MonthDay": (
exception.find("pr:MonthDay", self.ns).text
if exception.find("pr:MonthDay", self.ns) is not None
else None
),
"Type": (
exception.find("pr:Type", self.ns).text if exception.find("pr:Type", self.ns) is not None else None
),
"WorkingTimes": work_times, "WorkingTimes": work_times,
"ifc": None, "ifc": None,
} }
@@ -283,16 +292,15 @@ class MSP2Ifc:
for subtask_id in task["subtasks"]: for subtask_id in task["subtasks"]:
self.create_task(self.tasks[subtask_id], parent_task=task) self.create_task(self.tasks[subtask_id], parent_task=task)
# create pset for optional columns # create pset for optional columns
if len(self.optionalColumns): if len(self.optionalColumns):
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=task["ifc"] , name="Pset_MSP_Task") pset = ifcopenshell.api.run("pset.add_pset", self.file, product=task["ifc"], name="Pset_MSP_Task")
ifcopenshell.api.run( ifcopenshell.api.run(
"pset.edit_pset", "pset.edit_pset",
self.file, self.file,
pset=pset, pset=pset,
properties= {name: str(task[name]) for name in self.optionalColumns if task[name]} properties={name: str(task[name]) for name in self.optionalColumns if task[name]},
) )
def process_working_week(self, week, calendar): def process_working_week(self, week, calendar):
+1
View File
@@ -62,6 +62,7 @@ class AstaCalendarWorkPattern:
if wp["DayOfWeek"] == days[lang][index]: if wp["DayOfWeek"] == days[lang][index]:
wp["DayOfWeek"] = days["en"][index] wp["DayOfWeek"] = days["en"][index]
return return
translate_days(self.Days, self.dict_wp[-1]) translate_days(self.Days, self.dict_wp[-1])
for day in self.Days["en"]: for day in self.Days["en"]:
@@ -30,7 +30,7 @@ from bimtester.ifc import IfcStore
from bimtester.lang import _ from bimtester.lang import _
@step(u'There must be at least one "{ifc_class}" element') @step('There must be at least one "{ifc_class}" element')
def step_impl(context, ifc_class): def step_impl(context, ifc_class):
assert len(IfcStore.file.by_type(ifc_class)) >= 1, _("An element of {} could not be found").format(ifc_class) assert len(IfcStore.file.by_type(ifc_class)) >= 1, _("An element of {} could not be found").format(ifc_class)
@@ -68,7 +68,7 @@ def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_asser
return actual_value return actual_value
@step(u"The project must have coordinate reference system data") @step("The project must have coordinate reference system data")
def step_impl(context): def step_impl(context):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -77,7 +77,7 @@ def step_impl(context):
check_ifc4_geolocation("IfcProjectedCRS") check_ifc4_geolocation("IfcProjectedCRS")
@step(u'The name of the CRS must be "{coordinate_reference_name}"') @step('The name of the CRS must be "{coordinate_reference_name}"')
def step_impl(context, coordinate_reference_name): def step_impl(context, coordinate_reference_name):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -86,7 +86,7 @@ def step_impl(context, coordinate_reference_name):
check_ifc4_geolocation("IfcProjectedCRS", "Name", coordinate_reference_name) check_ifc4_geolocation("IfcProjectedCRS", "Name", coordinate_reference_name)
@step(u'The description of the CRS must be "{value}"') @step('The description of the CRS must be "{value}"')
def step_impl(context, value): def step_impl(context, value):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -95,7 +95,7 @@ def step_impl(context, value):
check_ifc4_geolocation("IfcProjectedCRS", "Description", value) check_ifc4_geolocation("IfcProjectedCRS", "Description", value)
@step(u'The geodetic datum must be "{coordinate_reference_name}"') @step('The geodetic datum must be "{coordinate_reference_name}"')
def step_impl(context, coordinate_reference_name): def step_impl(context, coordinate_reference_name):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -104,7 +104,7 @@ def step_impl(context, coordinate_reference_name):
check_ifc4_geolocation("IfcProjectedCRS", "GeodeticDatum", coordinate_reference_name) check_ifc4_geolocation("IfcProjectedCRS", "GeodeticDatum", coordinate_reference_name)
@step(u'The vertical datum must be "{coordinate_reference_name}"') @step('The vertical datum must be "{coordinate_reference_name}"')
def step_impl(context, coordinate_reference_name): def step_impl(context, coordinate_reference_name):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -113,7 +113,7 @@ def step_impl(context, coordinate_reference_name):
check_ifc4_geolocation("IfcProjectedCRS", "VerticalDatum", coordinate_reference_name) check_ifc4_geolocation("IfcProjectedCRS", "VerticalDatum", coordinate_reference_name)
@step(u'The map projection must be "{coordinate_reference_name}"') @step('The map projection must be "{coordinate_reference_name}"')
def step_impl(context, coordinate_reference_name): def step_impl(context, coordinate_reference_name):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -122,7 +122,7 @@ def step_impl(context, coordinate_reference_name):
check_ifc4_geolocation("IfcProjectedCRS", "MapProjection", coordinate_reference_name) check_ifc4_geolocation("IfcProjectedCRS", "MapProjection", coordinate_reference_name)
@step(u'The map zone must be "{coordinate_reference_name}"') @step('The map zone must be "{coordinate_reference_name}"')
def step_impl(context, coordinate_reference_name): def step_impl(context, coordinate_reference_name):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -131,7 +131,7 @@ def step_impl(context, coordinate_reference_name):
check_ifc4_geolocation("IfcProjectedCRS", "MapZone", coordinate_reference_name) check_ifc4_geolocation("IfcProjectedCRS", "MapZone", coordinate_reference_name)
@step(u'The map unit must be "{unit}"') @step('The map unit must be "{unit}"')
def step_impl(context, unit): def step_impl(context, unit):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -148,7 +148,7 @@ def step_impl(context, unit):
assert actual_value == unit, _('We expected a value of "{}" but instead got "{}"').format(unit, actual_value) assert actual_value == unit, _('We expected a value of "{}" but instead got "{}"').format(unit, actual_value)
@step(u"The project must have coordinate transformations to convert from local to global coordinates") @step("The project must have coordinate transformations to convert from local to global coordinates")
def step_impl(context): def step_impl(context):
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
for site in IfcStore.file.by_type("IfcSite"): for site in IfcStore.file.by_type("IfcSite"):
@@ -156,7 +156,7 @@ def step_impl(context):
check_ifc4_geolocation("IfcMapConversion") check_ifc4_geolocation("IfcMapConversion")
@step(u'The eastings of the model must be offset by "{number}" to derive its global coordinates') @step('The eastings of the model must be offset by "{number}" to derive its global coordinates')
def step_impl(context, number): def step_impl(context, number):
number = util.assert_number(number) number = util.assert_number(number)
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
@@ -166,7 +166,7 @@ def step_impl(context, number):
check_ifc4_geolocation("IfcMapConversion", "Eastings", number) check_ifc4_geolocation("IfcMapConversion", "Eastings", number)
@step(u'The northings of the model must be offset by "{number}" to derive its global coordinates') @step('The northings of the model must be offset by "{number}" to derive its global coordinates')
def step_impl(context, number): def step_impl(context, number):
number = util.assert_number(number) number = util.assert_number(number)
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
@@ -176,7 +176,7 @@ def step_impl(context, number):
check_ifc4_geolocation("IfcMapConversion", "Northings", number) check_ifc4_geolocation("IfcMapConversion", "Northings", number)
@step(u'The height of the model must be offset by "{number}" to derive its global coordinates') @step('The height of the model must be offset by "{number}" to derive its global coordinates')
def step_impl(context, number): def step_impl(context, number):
number = util.assert_number(number) number = util.assert_number(number)
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
@@ -186,7 +186,7 @@ def step_impl(context, number):
check_ifc4_geolocation("IfcMapConversion", "OrthogonalHeight", number) check_ifc4_geolocation("IfcMapConversion", "OrthogonalHeight", number)
@step(u'The model must be rotated clockwise by "{number}" to derive its global coordinates') @step('The model must be rotated clockwise by "{number}" to derive its global coordinates')
def step_impl(context, number): def step_impl(context, number):
number = util.assert_number(number) number = util.assert_number(number)
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
@@ -198,7 +198,7 @@ def step_impl(context, number):
assert actual_value == value, _('We expected a value of "{}" but instead got "{}"').format(value, actual_value) assert actual_value == value, _('We expected a value of "{}" but instead got "{}"').format(value, actual_value)
@step(u'The model must be scaled along the horizontal axis by "{number}" to derive its global coordinates') @step('The model must be scaled along the horizontal axis by "{number}" to derive its global coordinates')
def step_impl(context, number): def step_impl(context, number):
number = util.assert_number(number) number = util.assert_number(number)
if IfcStore.file.schema == "IFC2X3": if IfcStore.file.schema == "IFC2X3":
@@ -208,7 +208,7 @@ def step_impl(context, number):
check_ifc4_geolocation("IfcMapConversion", "Scale", number) check_ifc4_geolocation("IfcMapConversion", "Scale", number)
@step(u'The model must be rotated clockwise by "{number}" for true north to point up') @step('The model must be rotated clockwise by "{number}" for true north to point up')
def step_impl(context, number): def step_impl(context, number):
number = util.assert_number(number) number = util.assert_number(number)
project = IfcStore.file.by_type("IfcProject")[0] project = IfcStore.file.by_type("IfcProject")[0]
@@ -228,7 +228,7 @@ def step_impl(context, number):
assert False, _("True north is not defined in the file") assert False, _("True north is not defined in the file")
@step(u'The site "{guid}" has a longitude of "{number}"') @step('The site "{guid}" has a longitude of "{number}"')
def step_impl(context, guid, number): def step_impl(context, guid, number):
number = util.assert_number(number) number = util.assert_number(number)
site = util.assert_guid(IfcStore.file, guid) site = util.assert_guid(IfcStore.file, guid)
@@ -238,7 +238,7 @@ def step_impl(context, guid, number):
util.assert_attribute(site, "RefLongitude", number) util.assert_attribute(site, "RefLongitude", number)
@step(u'The site "{guid}" has a latitude of "{number}"') @step('The site "{guid}" has a latitude of "{number}"')
def step_impl(context, guid, number): def step_impl(context, guid, number):
number = util.assert_number(number) number = util.assert_number(number)
site = util.assert_guid(IfcStore.file, guid) site = util.assert_guid(IfcStore.file, guid)
@@ -248,7 +248,7 @@ def step_impl(context, guid, number):
util.assert_attribute(site, "RefLatitude", number) util.assert_attribute(site, "RefLatitude", number)
@step(u'The site "{guid}" has an elevation of "{number}"') @step('The site "{guid}" has an elevation of "{number}"')
def step_impl(context, guid, number): def step_impl(context, guid, number):
number = util.assert_number(number) number = util.assert_number(number)
site = util.assert_guid(IfcStore.file, guid) site = util.assert_guid(IfcStore.file, guid)
@@ -256,7 +256,7 @@ def step_impl(context, guid, number):
util.assert_attribute(site, "RefElevation", number) util.assert_attribute(site, "RefElevation", number)
@step(u'The site "{guid}" must be coincident with the project origin') @step('The site "{guid}" must be coincident with the project origin')
def step_impl(context, guid): def step_impl(context, guid):
site = util.assert_guid(IfcStore.file, guid) site = util.assert_guid(IfcStore.file, guid)
util.assert_type(site, "IfcSite") util.assert_type(site, "IfcSite")
-1
View File
@@ -38,7 +38,6 @@ from behave.__main__ import main as behave_main
from logging import StreamHandler from logging import StreamHandler
class TestRunner: class TestRunner:
def __init__(self, ifc_path, schema_path=None, ifc=None): def __init__(self, ifc_path, schema_path=None, ifc=None):
IfcStore.path = ifc_path IfcStore.path = ifc_path
@@ -163,6 +163,7 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
me.from_pydata(verts, [], faces) me.from_pydata(verts, [], faces)
me.validate() me.validate()
# MATERIAL CREATION # MATERIAL CREATION
def add_material(mname, props): def add_material(mname, props):
if mname in bpy.data.materials: if mname in bpy.data.materials:
+7 -5
View File
@@ -20,6 +20,7 @@ import argparse
from cjio import cityjson from cjio import cityjson
from .cityjson2ifc import Cityjson2ifc from .cityjson2ifc import Cityjson2ifc
def cmdline(): def cmdline():
# Example: # Example:
# python ifccityjson.py -i example/3DBAG_example.json -o example/output.ifc -n identificatie # python ifccityjson.py -i example/3DBAG_example.json -o example/output.ifc -n identificatie
@@ -28,10 +29,10 @@ def cmdline():
parser.add_argument("-i", "--input", type=str, help="input CityJSON file", required=True) parser.add_argument("-i", "--input", type=str, help="input CityJSON file", required=True)
parser.add_argument("-o", "--output", type=str, help="output IFC file. Standard is output.ifc") parser.add_argument("-o", "--output", type=str, help="output IFC file. Standard is output.ifc")
parser.add_argument("-n", "--name", type=str, help="Attribute containing the name") parser.add_argument("-n", "--name", type=str, help="Attribute containing the name")
parser.add_argument('--split-lod', dest='split', action='store_true', parser.add_argument("--split-lod", dest="split", action="store_true", help="Split the file in multiple LoDs")
help="Split the file in multiple LoDs") parser.add_argument(
parser.add_argument('--no-split-lod', dest='split', action='store_false', "--no-split-lod", dest="split", action="store_false", help="Do not split the file in multiple LoDs"
help="Do not split the file in multiple LoDs") )
parser.add_argument("--lod", type=str, help="extract LOD value (example: 1.2)") parser.add_argument("--lod", type=str, help="extract LOD value (example: 1.2)")
parser.set_defaults(split=True) parser.set_defaults(split=True)
args = parser.parse_args() args = parser.parse_args()
@@ -50,5 +51,6 @@ def cmdline():
converter.configuration(**data) converter.configuration(**data)
converter.convert(city_model) converter.convert(city_model)
if __name__ == '__main__':
if __name__ == "__main__":
cmdline() cmdline()
@@ -16,4 +16,4 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with ifccityjson. If not, see <http://www.gnu.org/licenses/>. # along with ifccityjson. If not, see <http://www.gnu.org/licenses/>.
__version__ = "0.1.0" __version__ = "0.1.0"
from .cityjson2ifc import * from .cityjson2ifc import *
-2
View File
@@ -1,4 +1,3 @@
# IfcClash - IFC-based clash detection. # IfcClash - IFC-based clash detection.
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com> # Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
# #
@@ -18,4 +17,3 @@
# along with IfcClash. If not, see <http://www.gnu.org/licenses/>. # along with IfcClash. If not, see <http://www.gnu.org/licenses/>.
import ifcclash.__main__ import ifcclash.__main__
-1
View File
@@ -24,4 +24,3 @@ import subprocess
cmd = "pyinstaller ./bootstrap.py --name ifcclash --onefile --clean" cmd = "pyinstaller ./bootstrap.py --name ifcclash --onefile --clean"
subprocess.check_output(cmd, shell=True) subprocess.check_output(cmd, shell=True)
+7 -1
View File
@@ -11,7 +11,13 @@ parser.add_argument(
help="The FM standard to extract. Built-in preset standards include cobie24, cobie3, aohbsem, and basic.", help="The FM standard to extract. Built-in preset standards include cobie24, cobie3, aohbsem, and basic.",
) )
parser.add_argument("-i", "--ifc", type=str, required=True, help="The IFC file") parser.add_argument("-i", "--ifc", type=str, required=True, help="The IFC file")
parser.add_argument("-s", "--spreadsheet", type=str, default="output.ods", help="The spreadsheet file, or directory if the format is csv. Defaults to output.ods") parser.add_argument(
"-s",
"--spreadsheet",
type=str,
default="output.ods",
help="The spreadsheet file, or directory if the format is csv. Defaults to output.ods",
)
parser.add_argument( parser.add_argument(
"-f", "--format", type=str, default="ods", help="The format, chosen from csv, ods, or xlsx. Defaults to ods." "-f", "--format", type=str, default="ods", help="The format, chosen from csv, ods, or xlsx. Defaults to ods."
) )
@@ -100,7 +100,7 @@ class Patcher:
if face.normal.z < 0.5: if face.normal.z < 0.5:
faces_to_delete.append(face) faces_to_delete.append(face)
bmesh.ops.delete(bm, geom=faces_to_delete, context='FACES_ONLY') bmesh.ops.delete(bm, geom=faces_to_delete, context="FACES_ONLY")
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.01) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.01)
bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method="BEAUTY", ngon_method="BEAUTY") bmesh.ops.triangulate(bm, faces=bm.faces[:], quad_method="BEAUTY", ngon_method="BEAUTY")
+9 -9
View File
@@ -119,8 +119,8 @@ class TestMergeProject(test.bootstrap.IFC4):
ifcopenshell.api.geometry.assign_representation(self.file, product=wall1, representation=rep) ifcopenshell.api.geometry.assign_representation(self.file, product=wall1, representation=rep)
shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall1) shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall1)
verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry) verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry)
assert np.any(np.all(np.isclose(np.array((1., 2., 3.)), verts), axis=1)) assert np.any(np.all(np.isclose(np.array((1.0, 2.0, 3.0)), verts), axis=1))
assert np.any(np.all(np.isclose(np.array((3., 4., 5.)), verts), axis=1)) assert np.any(np.all(np.isclose(np.array((3.0, 4.0, 5.0)), verts), axis=1))
# Second file is in millimeters with a different false origin # Second file is in millimeters with a different false origin
wall1 = second_file.by_type("IfcWall")[0] wall1 = second_file.by_type("IfcWall")[0]
@@ -134,8 +134,8 @@ class TestMergeProject(test.bootstrap.IFC4):
ifcopenshell.api.geometry.assign_representation(second_file, product=wall1, representation=rep) ifcopenshell.api.geometry.assign_representation(second_file, product=wall1, representation=rep)
shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall1) shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall1)
verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry) verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry)
assert np.any(np.all(np.isclose(np.array((1., 2., 3.)), verts), axis=1)) assert np.any(np.all(np.isclose(np.array((1.0, 2.0, 3.0)), verts), axis=1))
assert np.any(np.all(np.isclose(np.array((3., 4., 5.)), verts), axis=1)) assert np.any(np.all(np.isclose(np.array((3.0, 4.0, 5.0)), verts), axis=1))
output = ifcpatch.execute({"file": self.file, "recipe": "MergeProject", "arguments": [second_file]}) output = ifcpatch.execute({"file": self.file, "recipe": "MergeProject", "arguments": [second_file]})
@@ -155,17 +155,17 @@ class TestMergeProject(test.bootstrap.IFC4):
m2 = ifcopenshell.util.placement.get_local_placement(wall2.ObjectPlacement) m2 = ifcopenshell.util.placement.get_local_placement(wall2.ObjectPlacement)
assert np.allclose(m1[:, 3], (1, 2, 3, 1)) assert np.allclose(m1[:, 3], (1, 2, 3, 1))
# assert np.allclose(m2[:, 3], (8.321, 29.321, 3, 1), atol=1e-3) # assert np.allclose(m2[:, 3], (8.321, 29.321, 3, 1), atol=1e-3)
assert np.allclose(m2[:, 3], (17.847, 24.707, 3., 1.), atol=1e-3) assert np.allclose(m2[:, 3], (17.847, 24.707, 3.0, 1.0), atol=1e-3)
shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall1) shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall1)
verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry) verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry)
assert np.any(np.all(np.isclose(np.array((1., 2., 3.)), verts), axis=1)) assert np.any(np.all(np.isclose(np.array((1.0, 2.0, 3.0)), verts), axis=1))
assert np.any(np.all(np.isclose(np.array((3., 4., 5.)), verts), axis=1)) assert np.any(np.all(np.isclose(np.array((3.0, 4.0, 5.0)), verts), axis=1))
shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall2) shape = ifcopenshell.geom.create_shape(ifcopenshell.geom.settings(), wall2)
verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry) verts = ifcopenshell.util.shape.get_shape_vertices(shape, shape.geometry)
assert np.any(np.all(np.isclose(np.array((17.847, 24.707, 3.)), verts, atol=1e-3), axis=1)) assert np.any(np.all(np.isclose(np.array((17.847, 24.707, 3.0)), verts, atol=1e-3), axis=1))
assert np.any(np.all(np.isclose(np.array((20.410, 25.902, 5.)), verts, atol=1e-3), axis=1)) assert np.any(np.all(np.isclose(np.array((20.410, 25.902, 5.0)), verts, atol=1e-3), axis=1))
class TestMergeProjectIFC2X3(test.bootstrap.IFC2X3, TestMergeProject): class TestMergeProjectIFC2X3(test.bootstrap.IFC2X3, TestMergeProject):
+4 -6
View File
@@ -55,9 +55,9 @@ class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIf
self.outputs.new("SvStringsSocket", "Entity") self.outputs.new("SvStringsSocket", "Entity")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Add a property set and corresponding properties to IfcElements."
).tooltip = "Add a property set and corresponding properties to IfcElements." )
def process(self): def process(self):
if not any(socket.is_linked for socket in self.outputs): if not any(socket.is_linked for socket in self.outputs):
@@ -83,9 +83,7 @@ class SvIfcAddPset(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIf
def create(self, name, properties, elements): def create(self, name, properties, elements):
results = [] results = []
for element in elements: for element in elements:
result = ifcopenshell.api.run( result = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name=name)
"pset.add_pset", self.file, product=element, name=name
)
ifcopenshell.api.run( ifcopenshell.api.run(
"pset.edit_pset", "pset.edit_pset",
self.file, self.file,
+2 -1
View File
@@ -24,7 +24,8 @@ from bpy.props import StringProperty, EnumProperty
from sverchok.node_tree import SverchCustomTreeNode from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode from sverchok.data_structure import updateNode
import logging import logging
logger = logging.getLogger('sverchok.ifc')
logger = logging.getLogger("sverchok.ifc")
def update_usecase(self, context): def update_usecase(self, context):
+18 -42
View File
@@ -42,9 +42,7 @@ from sverchok.core.socket_data import sv_get_socket
from itertools import chain, cycle from itertools import chain, cycle
class SvIfcBMeshToIfcRepr( class SvIfcBMeshToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
""" """
Triggers: BMesh to Ifc Repr Triggers: BMesh to Ifc Repr
Tooltip: Blender mesh to Ifc Shape Representation Tooltip: Blender mesh to Ifc Shape Representation
@@ -61,9 +59,7 @@ class SvIfcBMeshToIfcRepr(
self.process() self.process()
self.refresh_local = False self.refresh_local = False
refresh_local: BoolProperty( refresh_local: BoolProperty(name="Update Node", description="Update Node", update=refresh_node)
name="Update Node", description="Update Node", update=refresh_node
)
context_types = [ context_types = [
("Model", "Model", "Context type: Model", 0), ("Model", "Model", "Context type: Model", 0),
@@ -113,20 +109,16 @@ class SvIfcBMeshToIfcRepr(
def sv_init(self, context): def sv_init(self, context):
self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type" self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type"
self.inputs.new( self.inputs.new("SvStringsSocket", "context_identifier").prop_name = "context_identifier"
"SvStringsSocket", "context_identifier"
).prop_name = "context_identifier"
self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view" self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view"
self.inputs.new( self.inputs.new("SvObjectSocket", "blender_objects").prop_name = "blender_objects" # no prop for now
"SvObjectSocket", "blender_objects"
).prop_name = "blender_objects" # no prop for now
self.outputs.new("SvVerticesSocket", "Representations") self.outputs.new("SvVerticesSocket", "Representations")
self.outputs.new("SvMatrixSocket", "Locations") self.outputs.new("SvMatrixSocket", "Locations")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Blender mesh to Ifc Shape Representation. \nTakes one or multiple geometries.\nDeconstructs joined geometries and creates a separate representation for each."
).tooltip = "Blender mesh to Ifc Shape Representation. \nTakes one or multiple geometries.\nDeconstructs joined geometries and creates a separate representation for each." )
row = layout.row(align=True) row = layout.row(align=True)
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True) row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
@@ -136,9 +128,7 @@ class SvIfcBMeshToIfcRepr(
self.sv_input_names = [i.name for i in self.inputs] self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict: if hash(self) not in self.node_dict:
self.node_dict[ self.node_dict[hash(self)] = {} # happens if node is already on canvas when blender loads
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]: if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0)) self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
@@ -211,21 +201,17 @@ class SvIfcBMeshToIfcRepr(
context=context, context=context,
) )
if not representation: if not representation:
raise Exception( raise Exception("Couldn't create representation. Possibly wrong context.")
"Couldn't create representation. Possibly wrong context."
)
representations_ids_obj.append([representation.id()]) representations_ids_obj.append([representation.id()])
locations_obj.append([obj.matrix_world]) locations_obj.append([obj.matrix_world])
representations_ids.append(representations_ids_obj) representations_ids.append(representations_ids_obj)
locations.append(locations_obj) locations.append(locations_obj)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault( SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Representations", []).append(
"Representations", [] representations_ids_obj
).append(representations_ids_obj) )
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault( SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Locations", []).append(locations_obj)
"Locations", []
).append(locations_obj)
bpy.ops.object.select_all(action="DESELECT") bpy.ops.object.select_all(action="DESELECT")
return representations_ids, locations return representations_ids, locations
@@ -248,13 +234,9 @@ class SvIfcBMeshToIfcRepr(
self.file, self.context_type, self.context_identifier, self.target_view self.file, self.context_type, self.context_identifier, self.target_view
) )
if not context: if not context:
parent = ifcopenshell.util.representation.get_context( parent = ifcopenshell.util.representation.get_context(self.file, self.context_type)
self.file, self.context_type
)
if not parent: if not parent:
parent = ifcopenshell.api.run( parent = ifcopenshell.api.run("context.add_context", self.file, context_type=self.context_type)
"context.add_context", self.file, context_type=self.context_type
)
context = ifcopenshell.api.run( context = ifcopenshell.api.run(
"context.add_context", "context.add_context",
self.file, self.file,
@@ -263,9 +245,7 @@ class SvIfcBMeshToIfcRepr(
target_view=self.target_view, target_view=self.target_view,
parent=parent, parent=parent,
) )
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault( SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Contexts", []).append(context.id())
"Contexts", []
).append(context.id())
return context return context
def sv_free(self): def sv_free(self):
@@ -286,14 +266,10 @@ class SvIfcBMeshToIfcRepr(
if not self.file.get_inverse(context): if not self.file.get_inverse(context):
if self.file.by_id(context_id).ParentContext: if self.file.by_id(context_id).ParentContext:
parent = self.file.by_id(context_id).ParentContext parent = self.file.by_id(context_id).ParentContext
ifcopenshell.api.run( ifcopenshell.api.run("context.remove_context", self.file, context=context)
"context.remove_context", self.file, context=context
)
if parent: if parent:
if not self.file.get_inverse(parent): if not self.file.get_inverse(parent):
ifcopenshell.api.run( ifcopenshell.api.run("context.remove_context", self.file, context=parent)
"context.remove_context", self.file, context=parent
)
SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id) SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id)
del SvIfcStore.id_map[self.node_id] del SvIfcStore.id_map[self.node_id]
del self.node_dict[hash(self)] del self.node_dict[hash(self)]
+3 -3
View File
@@ -39,9 +39,9 @@ class SvIfcByGuid(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfc
self.outputs.new("SvStringsSocket", "Entities") self.outputs.new("SvStringsSocket", "Entities")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Get IFC element by guid. Takes one or multiple guids."
).tooltip = "Get IFC element by guid. Takes one or multiple guids." )
def process(self): def process(self):
self.guids = flatten_data(self.inputs["guid"].sv_get(), target_level=1) self.guids = flatten_data(self.inputs["guid"].sv_get(), target_level=1)
+3 -3
View File
@@ -38,9 +38,9 @@ class SvIfcById(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCo
self.outputs.new("SvStringsSocket", "Entities") self.outputs.new("SvStringsSocket", "Entities")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Get IFC element by step id. Takes one or multiple step ids."
).tooltip = "Get IFC element by step id. Takes one or multiple step ids." )
def process(self): def process(self):
self.ids = flatten_data(self.inputs["id"].sv_get(), target_level=1) self.ids = flatten_data(self.inputs["id"].sv_get(), target_level=1)
+4 -6
View File
@@ -108,17 +108,15 @@ class SvIfcByType(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfc
def sv_init(self, context): def sv_init(self, context):
self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product" self.inputs.new("SvStringsSocket", "ifc_product").prop_name = "ifc_product"
self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class" self.inputs.new("SvStringsSocket", "ifc_class").prop_name = "ifc_class"
self.inputs.new( self.inputs.new("SvStringsSocket", "custom_ifc_class").prop_name = "custom_ifc_class"
"SvStringsSocket", "custom_ifc_class"
).prop_name = "custom_ifc_class"
self.outputs.new("SvStringsSocket", "Entities") self.outputs.new("SvStringsSocket", "Entities")
self.outputs.new("SvStringsSocket", "Entity Ids") self.outputs.new("SvStringsSocket", "Entity Ids")
self.width = 200 self.width = 200
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Get IFC element(s) in file by type. \nPick an IfcProduct and an IfcClass or give a custom IfcClass."
).tooltip = "Get IFC element(s) in file by type. \nPick an IfcProduct and an IfcClass or give a custom IfcClass." )
def process(self): def process(self):
self.file = SvIfcStore.get_file() self.file = SvIfcStore.get_file()
+20 -51
View File
@@ -32,9 +32,7 @@ from sverchok.data_structure import (
) )
class SvIfcCreateEntity( class SvIfcCreateEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcCreateEntity" bl_idname = "SvIfcCreateEntity"
bl_label = "IFC Create Entity" bl_label = "IFC Create Entity"
node_dict = {} node_dict = {}
@@ -46,9 +44,7 @@ class SvIfcCreateEntity(
self.process() self.process()
self.refresh_local = False self.refresh_local = False
refresh_local: BoolProperty( refresh_local: BoolProperty(name="Update Node", description="Update Node", update=refresh_node)
name="Update Node", description="Update Node", update=refresh_node
)
Names: StringProperty( Names: StringProperty(
name="Names", name="Names",
@@ -76,18 +72,16 @@ class SvIfcCreateEntity(
self.inputs.new("SvStringsSocket", "Names").prop_name = "Names" self.inputs.new("SvStringsSocket", "Names").prop_name = "Names"
self.inputs.new("SvStringsSocket", "Descriptions").prop_name = "Descriptions" self.inputs.new("SvStringsSocket", "Descriptions").prop_name = "Descriptions"
self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass" self.inputs.new("SvStringsSocket", "IfcClass").prop_name = "IfcClass"
self.inputs.new( self.inputs.new("SvStringsSocket", "Representations").prop_name = "Representations"
"SvStringsSocket", "Representations"
).prop_name = "Representations"
self.inputs.new("SvMatrixSocket", "Locations").is_mandatory = False self.inputs.new("SvMatrixSocket", "Locations").is_mandatory = False
# self.inputs.new("SvStringsSocket", "Properties").prop_name = "Properties" # self.inputs.new("SvStringsSocket", "Properties").prop_name = "Properties"
self.outputs.new("SvStringsSocket", "Entities") self.outputs.new("SvStringsSocket", "Entities")
self.node_dict[hash(self)] = {} self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Create IFC Entity. Takes one or multiple inputs. \nIf 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used."
).tooltip = "Create IFC Entity. Takes one or multiple inputs. \nIf 'Representation(s)' is given, that determines number of output entities. Otherwise, 'Names' is used." )
row = layout.row(align=True) row = layout.row(align=True)
row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True) row.prop(self, "is_interactive", icon="SCENE_DATA", icon_only=True)
@@ -95,26 +89,16 @@ class SvIfcCreateEntity(
def process(self): def process(self):
self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1) self.names = flatten_data(self.inputs["Names"].sv_get(), target_level=1)
self.descriptions = flatten_data( self.descriptions = flatten_data(self.inputs["Descriptions"].sv_get(), target_level=1)
self.inputs["Descriptions"].sv_get(), target_level=1 self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[0]
) self.representations = ensure_min_nesting(self.inputs["Representations"].sv_get(), 3)
self.ifc_class = flatten_data(self.inputs["IfcClass"].sv_get(), target_level=1)[
0
]
self.representations = ensure_min_nesting(
self.inputs["Representations"].sv_get(), 3
)
self.representations = flatten_data(self.representations, target_level=3) self.representations = flatten_data(self.representations, target_level=3)
self.locations = ensure_min_nesting( self.locations = ensure_min_nesting(self.inputs["Locations"].sv_get(default=[]), 3)
self.inputs["Locations"].sv_get(default=[]), 3
)
self.locations = flatten_data(self.locations, target_level=3) self.locations = flatten_data(self.locations, target_level=3)
self.sv_input_names = [i.name for i in self.inputs] self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict: if hash(self) not in self.node_dict:
self.node_dict[ self.node_dict[hash(self)] = {} # happens if node is already on canvas when blender loads
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]: if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0)) self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
if not self.inputs["IfcClass"].sv_get()[0][0]: if not self.inputs["IfcClass"].sv_get()[0][0]:
@@ -122,9 +106,7 @@ class SvIfcCreateEntity(
edit = False edit = False
for i in range(len(self.inputs)): for i in range(len(self.inputs)):
input = self.inputs[self.sv_input_names[i]].sv_get( input = self.inputs[self.sv_input_names[i]].sv_get(deepcopy=True, default=[])
deepcopy=True, default=[]
)
if ( if (
isinstance(self.node_dict[hash(self)][self.inputs[i].name], list) isinstance(self.node_dict[hash(self)][self.inputs[i].name], list)
and input != self.node_dict[hash(self)][self.inputs[i].name] and input != self.node_dict[hash(self)][self.inputs[i].name]
@@ -142,27 +124,18 @@ class SvIfcCreateEntity(
for group in self.representations: for group in self.representations:
try: try:
group_representations = [ group_representations = [
[self.file.by_id(step_id) for step_id in representation] [self.file.by_id(step_id) for step_id in representation] for representation in group
for representation in group
] ]
representations.append(group_representations) representations.append(group_representations)
except Exception as e: except Exception as e:
raise raise
names.append( names.append(self.repeat_input_unique(self.names, len(group_representations)))
self.repeat_input_unique(self.names, len(group_representations)) descriptions.append(self.repeat_input_unique(self.descriptions, len(group_representations)))
)
descriptions.append(
self.repeat_input_unique(
self.descriptions, len(group_representations)
)
)
self.representations = representations self.representations = representations
self.names = names self.names = names
self.descriptions = descriptions self.descriptions = descriptions
elif not self.representations[0][0][0]: elif not self.representations[0][0][0]:
self.descriptions = self.repeat_input_unique( self.descriptions = self.repeat_input_unique(self.descriptions, len(self.names))
self.descriptions, len(self.names)
)
self.names = ensure_min_nesting(self.names, 2) self.names = ensure_min_nesting(self.names, 2)
self.descriptions = ensure_min_nesting(self.descriptions, 2) self.descriptions = ensure_min_nesting(self.descriptions, 2)
if self.node_id not in SvIfcStore.id_map: if self.node_id not in SvIfcStore.id_map:
@@ -193,7 +166,7 @@ class SvIfcCreateEntity(
self.file, self.file,
ifc_class=self.ifc_class, ifc_class=self.ifc_class,
name=self.names[i][j], name=self.names[i][j],
#description=self.descriptions[i][j], # description=self.descriptions[i][j],
) )
try: try:
for repr in self.representations[i][j]: for repr in self.representations[i][j]:
@@ -266,9 +239,7 @@ class SvIfcCreateEntity(
pass pass
if entity.is_a() != self.ifc_class: if entity.is_a() != self.ifc_class:
SvIfcStore.id_map[self.node_id][i].remove(step_id) SvIfcStore.id_map[self.node_id][i].remove(step_id)
entity = ifcopenshell.util.schema.reassign_class( entity = ifcopenshell.util.schema.reassign_class(self.file, entity, self.ifc_class)
self.file, entity, self.ifc_class
)
group_entities_ids.append(entity.id()) group_entities_ids.append(entity.id())
entities_ids.append(group_entities_ids) entities_ids.append(group_entities_ids)
@@ -280,12 +251,10 @@ class SvIfcCreateEntity(
if input[0]: if input[0]:
if flag: if flag:
return [ return [
[a] if not (s := sum(j == a for j in input[:i])) else [f"{a}-{s+1}"] [a] if not (s := sum(j == a for j in input[:i])) else [f"{a}-{s+1}"] for i, a in enumerate(input)
for i, a in enumerate(input)
] ]
input = [ input = [
a if not (s := sum(j == a for j in input[:i])) else f"{a}-{s+1}" a if not (s := sum(j == a for j in input[:i])) else f"{a}-{s+1}" for i, a in enumerate(input)
for i, a in enumerate(input)
] # add number to duplicates ] # add number to duplicates
return input return input
+3 -3
View File
@@ -35,9 +35,9 @@ class SvIfcCreateProject(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helpe
self.outputs.new("SvVerticesSocket", "file") self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
op = layout.operator( op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Adds project, unit and context to IFC file"
).tooltip = "Adds project, unit and context to IFC file" )
# op.tooltip = self.tooltip # op.tooltip = self.tooltip
def process(self): def process(self):
+3 -3
View File
@@ -54,9 +54,9 @@ class SvIfcCreateShape(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
row = layout.row(align=True) row = layout.row(align=True)
row.operator( row.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Create Blender shape from IfcEntity Id. Takes one or multiple IfcEntity IDs."
).tooltip = "Create Blender shape from IfcEntity Id. Takes one or multiple IfcEntity IDs." )
row.prop(self, "refresh_local", icon="FILE_REFRESH") row.prop(self, "refresh_local", icon="FILE_REFRESH")
def process(self): def process(self):
+5 -15
View File
@@ -26,9 +26,7 @@ from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, flatten_data from sverchok.data_structure import updateNode, flatten_data
class SvIfcGetAttribute( class SvIfcGetAttribute(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcGetAttribute" bl_idname = "SvIfcGetAttribute"
bl_label = "IFC Get Attribute" bl_label = "IFC Get Attribute"
entity: StringProperty(name="Entity Ids", update=updateNode) entity: StringProperty(name="Entity Ids", update=updateNode)
@@ -40,30 +38,22 @@ class SvIfcGetAttribute(
def sv_init(self, context): def sv_init(self, context):
self.inputs.new("SvStringsSocket", "entity").prop_name = "entity" self.inputs.new("SvStringsSocket", "entity").prop_name = "entity"
self.inputs.new( self.inputs.new("SvStringsSocket", "attribute_name").prop_name = "attribute_name"
"SvStringsSocket", "attribute_name"
).prop_name = "attribute_name"
self.outputs.new("SvStringsSocket", "value") self.outputs.new("SvStringsSocket", "value")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = (
"Get the value of an attribute of an IfcEntity. Can take multiple entities." "Get the value of an attribute of an IfcEntity. Can take multiple entities."
) )
def process(self): def process(self):
self.value_out = [] self.value_out = []
entity_nested_input_ids = flatten_data( entity_nested_input_ids = flatten_data(self.inputs["entity"].sv_get(), target_level=1)
self.inputs["entity"].sv_get(), target_level=1
)
if not entity_nested_input_ids[0]: if not entity_nested_input_ids[0]:
return return
self.file = SvIfcStore.get_file() self.file = SvIfcStore.get_file()
try: try:
entity_nested_inputs = [ entity_nested_inputs = [self.file.by_id(int(step_id)) for step_id in entity_nested_input_ids]
self.file.by_id(int(step_id)) for step_id in entity_nested_input_ids
]
except Exception as e: except Exception as e:
raise Exception("Instance ID not found", e) raise Exception("Instance ID not found", e)
attribute_name = self.inputs["attribute_name"].sv_get()[0][0] attribute_name = self.inputs["attribute_name"].sv_get()[0][0]
+3 -9
View File
@@ -26,9 +26,7 @@ from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, flatten_data from sverchok.data_structure import updateNode, flatten_data
class SvIfcGetProperty( class SvIfcGetProperty(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcGetProperty" bl_idname = "SvIfcGetProperty"
bl_label = "IFC Get Property" bl_label = "IFC Get Property"
entity: StringProperty(name="Entity Ids", update=updateNode) entity: StringProperty(name="Entity Ids", update=updateNode)
@@ -50,9 +48,7 @@ class SvIfcGetProperty(
self.outputs.new("SvStringsSocket", "value") self.outputs.new("SvStringsSocket", "value")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = (
"Get the value of a property of an IfcEntity. Can take multiple entity ids." "Get the value of a property of an IfcEntity. Can take multiple entity ids."
) )
@@ -72,9 +68,7 @@ class SvIfcGetProperty(
self.value_out = [] self.value_out = []
for entity in self.entities: for entity in self.entities:
try: try:
self.value_out.append( self.value_out.append(ifcopenshell.util.element.get_psets(entity)[pset_name][prop_name])
ifcopenshell.util.element.get_psets(entity)[pset_name][prop_name]
)
except: except:
pass pass
self.outputs["value"].sv_set(self.value_out) self.outputs["value"].sv_set(self.value_out)
@@ -60,9 +60,9 @@ class SvIfcQuickProjectSetup(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h
self.outputs.new("SvVerticesSocket", "file") self.outputs.new("SvVerticesSocket", "file")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
op = layout.operator( op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Quick Project Setup: creates Ifc file and sets up a basic project"
).tooltip = "Quick Project Setup: creates Ifc file and sets up a basic project" )
# op.tooltip = self.tooltip # op.tooltip = self.tooltip
def process(self): def process(self):
+2 -6
View File
@@ -25,9 +25,7 @@ from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, ensure_min_nesting, flatten_data from sverchok.data_structure import updateNode, ensure_min_nesting, flatten_data
class SvIfcReadEntity( class SvIfcReadEntity(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
bl_idname = "SvIfcReadEntity" bl_idname = "SvIfcReadEntity"
bl_label = "IFC Read Entity" bl_label = "IFC Read Entity"
entity: StringProperty(name="Entity Id", update=updateNode) entity: StringProperty(name="Entity Id", update=updateNode)
@@ -39,9 +37,7 @@ class SvIfcReadEntity(
self.outputs.new("SvStringsSocket", "is_a") self.outputs.new("SvStringsSocket", "is_a")
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.operator( layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False
).tooltip = (
"Decompose an IfcEntity into its attributes. Takes one entity id as input" "Decompose an IfcEntity into its attributes. Takes one entity id as input"
) )
+16 -36
View File
@@ -30,9 +30,7 @@ from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, ensure_min_nesting from sverchok.data_structure import updateNode, ensure_min_nesting
class SvIfcSverchokToIfcRepr( class SvIfcSverchokToIfcRepr(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore):
bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.SvIfcCore
):
""" """
Triggers: Sv to Ifc Repr Triggers: Sv to Ifc Repr
Tooltip: Sverchok geometry to Ifc Shape Representation Tooltip: Sverchok geometry to Ifc Shape Representation
@@ -85,9 +83,7 @@ class SvIfcSverchokToIfcRepr(
def sv_init(self, context): def sv_init(self, context):
self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type" self.inputs.new("SvStringsSocket", "context_type").prop_name = "context_type"
self.inputs.new( self.inputs.new("SvStringsSocket", "context_identifier").prop_name = "context_identifier"
"SvStringsSocket", "context_identifier"
).prop_name = "context_identifier"
self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view" self.inputs.new("SvStringsSocket", "target_view").prop_name = "target_view"
self.inputs.new("SvVerticesSocket", "Vertices") self.inputs.new("SvVerticesSocket", "Vertices")
self.inputs.new("SvStringsSocket", "Edges") self.inputs.new("SvStringsSocket", "Edges")
@@ -97,9 +93,9 @@ class SvIfcSverchokToIfcRepr(
self.node_dict[hash(self)] = {} self.node_dict[hash(self)] = {}
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
op = layout.operator( op = layout.operator("node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False).tooltip = (
"node.sv_ifc_tooltip", text="", icon="QUESTION", emboss=False "Sverchok geometry to Ifc Shape Representation. \nTakes one or multiple geometries."
).tooltip = "Sverchok geometry to Ifc Shape Representation. \nTakes one or multiple geometries." )
def process(self): def process(self):
if not any(socket.is_linked for socket in self.inputs): if not any(socket.is_linked for socket in self.inputs):
@@ -108,9 +104,7 @@ class SvIfcSverchokToIfcRepr(
self.sv_input_names = [i.name for i in self.inputs] self.sv_input_names = [i.name for i in self.inputs]
if hash(self) not in self.node_dict: if hash(self) not in self.node_dict:
self.node_dict[ self.node_dict[hash(self)] = {} # happens if node is already on canvas when blender loads
hash(self)
] = {} # happens if node is already on canvas when blender loads
if not self.node_dict[hash(self)]: if not self.node_dict[hash(self)]:
self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0)) self.node_dict[hash(self)].update(dict.fromkeys(self.sv_input_names, 0))
@@ -124,9 +118,7 @@ class SvIfcSverchokToIfcRepr(
edit = True edit = True
self.node_dict[hash(self)][self.inputs[i].name] = input self.node_dict[hash(self)][self.inputs[i].name] = input
self.vertices = ensure_min_nesting( self.vertices = ensure_min_nesting(self.inputs["Vertices"].sv_get(deepcopy=False), 4)
self.inputs["Vertices"].sv_get(deepcopy=False), 4
)
self.edges = ensure_min_nesting(self.inputs["Edges"].sv_get(deepcopy=False), 4) self.edges = ensure_min_nesting(self.inputs["Edges"].sv_get(deepcopy=False), 4)
self.faces = ensure_min_nesting(self.inputs["Faces"].sv_get(deepcopy=False), 4) self.faces = ensure_min_nesting(self.inputs["Faces"].sv_get(deepcopy=False), 4)
data = list(zip(self.vertices, self.edges, self.faces)) data = list(zip(self.vertices, self.edges, self.faces))
@@ -160,14 +152,12 @@ class SvIfcSverchokToIfcRepr(
faces=[list(map(tuple, item[2]))], faces=[list(map(tuple, item[2]))],
) )
if not representation: if not representation:
raise Exception( raise Exception("Couldn't create representation. Possibly wrong context.")
"Couldn't create representation. Possibly wrong context."
)
representations_ids_obj.append([representation.id()]) representations_ids_obj.append([representation.id()])
representations_ids.append(representations_ids_obj) representations_ids.append(representations_ids_obj)
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault( SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Representations", []).append(
"Representations", [] representations_ids_obj
).append(representations_ids_obj) )
return representations_ids return representations_ids
def edit(self): def edit(self):
@@ -188,13 +178,9 @@ class SvIfcSverchokToIfcRepr(
self.file, self.context_type, self.context_identifier, self.target_view self.file, self.context_type, self.context_identifier, self.target_view
) )
if not context: if not context:
parent = ifcopenshell.util.representation.get_context( parent = ifcopenshell.util.representation.get_context(self.file, self.context_type)
self.file, self.context_type
)
if not parent: if not parent:
parent = ifcopenshell.api.run( parent = ifcopenshell.api.run("context.add_context", self.file, context_type=self.context_type)
"context.add_context", self.file, context_type=self.context_type
)
context = ifcopenshell.api.run( context = ifcopenshell.api.run(
"context.add_context", "context.add_context",
self.file, self.file,
@@ -203,9 +189,7 @@ class SvIfcSverchokToIfcRepr(
target_view=self.target_view, target_view=self.target_view,
parent=parent, parent=parent,
) )
SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault( SvIfcStore.id_map.setdefault(self.node_id, {}).setdefault("Contexts", []).append(context.id())
"Contexts", []
).append(context.id())
return context return context
def sv_free(self): def sv_free(self):
@@ -225,14 +209,10 @@ class SvIfcSverchokToIfcRepr(
if not self.file.get_inverse(context): if not self.file.get_inverse(context):
if self.file.by_id(context_id).ParentContext: if self.file.by_id(context_id).ParentContext:
parent = self.file.by_id(context_id).ParentContext parent = self.file.by_id(context_id).ParentContext
ifcopenshell.api.run( ifcopenshell.api.run("context.remove_context", self.file, context=context)
"context.remove_context", self.file, context=context
)
if parent: if parent:
if not self.file.get_inverse(parent): if not self.file.get_inverse(parent):
ifcopenshell.api.run( ifcopenshell.api.run("context.remove_context", self.file, context=parent)
"context.remove_context", self.file, context=parent
)
# print("Removed context with step ID: ", context_id) # print("Removed context with step ID: ", context_id)
SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id) SvIfcStore.id_map[self.node_id]["Contexts"].remove(context_id)
del SvIfcStore.id_map[self.node_id] del SvIfcStore.id_map[self.node_id]
+1
View File
@@ -17,4 +17,5 @@
# along with IfcTester. If not, see <http://www.gnu.org/licenses/>. # along with IfcTester. If not, see <http://www.gnu.org/licenses/>.
from .ids import open from .ids import open
__version__ = version = "0.0.0" __version__ = version = "0.0.0"
+4 -12
View File
@@ -27,18 +27,10 @@ from . import reporter
parser = argparse.ArgumentParser(description="Uses an IDS to audit an IFC") parser = argparse.ArgumentParser(description="Uses an IDS to audit an IFC")
parser.add_argument("ids", type=str, help="Path to an IDS") parser.add_argument("ids", type=str, help="Path to an IDS")
parser.add_argument("ifc", type=str, help="Path to an IFC", nargs="?") parser.add_argument("ifc", type=str, help="Path to an IFC", nargs="?")
parser.add_argument( parser.add_argument("-r", "--reporter", type=str, help="The reporting method to view audit results", default="Console")
"-r", "--reporter", type=str, help="The reporting method to view audit results", default="Console" parser.add_argument("--no-color", help="Disable colour output (supported by Console reporting)", action="store_true")
) parser.add_argument("--excel-safe", help="Make sure exported ODS is safely exported for Excel", action="store_true")
parser.add_argument( parser.add_argument("-o", "--output", help="Output file (supported for all types of reporting except Console)")
"--no-color", help="Disable colour output (supported by Console reporting)", action="store_true"
)
parser.add_argument(
"--excel-safe", help="Make sure exported ODS is safely exported for Excel", action="store_true"
)
parser.add_argument(
"-o", "--output", help="Output file (supported for all types of reporting except Console)"
)
args = parser.parse_args() args = parser.parse_args()
specs = ids.open(args.ids) specs = ids.open(args.ids)
-1
View File
@@ -444,7 +444,6 @@ class Html(Json):
requirement["total_omitted_passes"] = total_passed_entities - entity_limit requirement["total_omitted_passes"] = total_passed_entities - entity_limit
requirement["has_omitted_passes"] = total_passed_entities > entity_limit requirement["has_omitted_passes"] = total_passed_entities > entity_limit
def to_string(self) -> str: def to_string(self) -> str:
import pystache import pystache
+392 -252
View File
@@ -1,5 +1,5 @@
from uuid import UUID from uuid import UUID
from fastapi import APIRouter, Depends, UploadFile, HTTPException from fastapi import APIRouter, Depends, UploadFile, HTTPException
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from security.secure import get_current_active_user from security.secure import get_current_active_user
@@ -46,38 +46,40 @@ router = APIRouter(route_class=LoggingRoute)
@router.get("/bcf/3.0/projects", tags=["projects_get"]) @router.get("/bcf/3.0/projects", tags=["projects_get"])
def projects_get(current_user: User = Depends(get_current_active_user)) -> List[ProjectGET]: def projects_get(current_user: User = Depends(get_current_active_user)) -> List[ProjectGET]:
projects_response = bcf_db.get_projects(current_user) projects_response = bcf_db.get_projects(current_user)
bcf_db.debug(endpoint='projects_get', bcf_db.debug(
request={}, endpoint="projects_get",
response={count: value.dict() for count, value in enumerate(projects_response)}) request={},
response={count: value.dict() for count, value in enumerate(projects_response)},
)
return projects_response return projects_response
@router.get("/bcf/3.0/projects/{project_id}", tags=["project_get"]) @router.get("/bcf/3.0/projects/{project_id}", tags=["project_get"])
def project_get(project_id: UUID, current_user: User = Depends(get_current_active_user)) -> ProjectGET: def project_get(project_id: UUID, current_user: User = Depends(get_current_active_user)) -> ProjectGET:
project_response = bcf_db.get_project(project_id, current_user) project_response = bcf_db.get_project(project_id, current_user)
bcf_db.debug(endpoint='project_get', bcf_db.debug(endpoint="project_get", request={"project_id": project_id}, response=project_response.dict())
request={'project_id': project_id},
response=project_response.dict())
return project_response return project_response
@router.put("/bcf/3.0/projects/{project_id}", tags=["project_put"], status_code=200) @router.put("/bcf/3.0/projects/{project_id}", tags=["project_put"], status_code=200)
def project_put(project_id: UUID, project_request: ProjectPUT, def project_put(
current_user: User = Depends(get_current_active_user)) -> ProjectGET: project_id: UUID, project_request: ProjectPUT, current_user: User = Depends(get_current_active_user)
) -> ProjectGET:
project_response = bcf_db.put_project(project_id, project_request, current_user) project_response = bcf_db.put_project(project_id, project_request, current_user)
bcf_db.debug(endpoint='project_put', bcf_db.debug(
request={'project_id': project_id, 'project_request': project_request}, endpoint="project_put",
response=project_response.dict()) request={"project_id": project_id, "project_request": project_request},
response=project_response.dict(),
)
return project_response return project_response
@router.get("/bcf/3.0/projects/{project_id}/extensions", tags=["project_extensions_get"]) @router.get("/bcf/3.0/projects/{project_id}/extensions", tags=["project_extensions_get"])
def project_extensions_get(project_id: UUID, def project_extensions_get(project_id: UUID, current_user: User = Depends(get_current_active_user)) -> ExtensionsGET:
current_user: User = Depends(get_current_active_user)) -> ExtensionsGET:
extensions_response = bcf_db.get_project_extensions(project_id, current_user) extensions_response = bcf_db.get_project_extensions(project_id, current_user)
bcf_db.debug(endpoint='project_extensions_get', bcf_db.debug(
request={'project_id': project_id}, endpoint="project_extensions_get", request={"project_id": project_id}, response=extensions_response.dict()
response=extensions_response.dict()) )
return extensions_response return extensions_response
@@ -86,62 +88,68 @@ def project_extensions_get(project_id: UUID,
# #
# Topics # Topics
@router.get("/bcf/3.0/projects/{project_id}/topics", tags=["topics_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics", tags=["topics_get"])
def topics_get(project_id: str, def topics_get(project_id: str, current_user: User = Depends(get_current_active_user)) -> List[TopicGET]:
current_user: User = Depends(get_current_active_user)) -> List[TopicGET]:
topics_response = bcf_db.get_topics(project_id, current_user) topics_response = bcf_db.get_topics(project_id, current_user)
bcf_db.debug(endpoint='topics_get', bcf_db.debug(
request={'project_id': project_id}, endpoint="topics_get",
response={count: value.dict() for count, value in enumerate(topics_response)}) request={"project_id": project_id},
response={count: value.dict() for count, value in enumerate(topics_response)},
)
return topics_response return topics_response
@router.post("/bcf/3.0/projects/{project_id}/topics", tags=["topic_post"], status_code=201) @router.post("/bcf/3.0/projects/{project_id}/topics", tags=["topic_post"], status_code=201)
def topic_post(project_id: UUID, topic_request: TopicPOST, def topic_post(
current_user: User = Depends(get_current_active_user)) -> TopicGET: project_id: UUID, topic_request: TopicPOST, current_user: User = Depends(get_current_active_user)
) -> TopicGET:
topic_response = bcf_db.post_topic(project_id, topic_request, current_user) topic_response = bcf_db.post_topic(project_id, topic_request, current_user)
if topic_response is None: if topic_response is None:
raise HTTPException(status_code=400, detail="Could not create topic.") raise HTTPException(status_code=400, detail="Could not create topic.")
bcf_db.debug(endpoint='topic_post', bcf_db.debug(
request={'project_id': project_id, 'topic_request': topic_request.dict()}, endpoint="topic_post",
response=topic_response.dict()) request={"project_id": project_id, "topic_request": topic_request.dict()},
response=topic_response.dict(),
)
return topic_response return topic_response
# Implemented # Implemented
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}", tags=["topic_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}", tags=["topic_get"])
def topic_get(project_id: UUID, topic_id: UUID, def topic_get(project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)) -> TopicGET:
current_user: User = Depends(get_current_active_user)) -> TopicGET:
topic_response = bcf_db.get_topic(project_id, topic_id, current_user) topic_response = bcf_db.get_topic(project_id, topic_id, current_user)
if topic_response is None: if topic_response is None:
raise HTTPException(status_code=404, detail="Item not found.") raise HTTPException(status_code=404, detail="Item not found.")
bcf_db.debug(endpoint='topic_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="topic_get", request={"project_id": project_id, "topic_id": topic_id}, response=topic_response.dict()
response=topic_response.dict()) )
return topic_response return topic_response
# Implemented # Implemented
@router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}", tags=["topic_put"], status_code=200) @router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}", tags=["topic_put"], status_code=200)
def topic_put(project_id: UUID, topic_id: UUID, topic_request: TopicPUT, def topic_put(
current_user: User = Depends(get_current_active_user)) -> TopicGET: project_id: UUID, topic_id: UUID, topic_request: TopicPUT, current_user: User = Depends(get_current_active_user)
) -> TopicGET:
topic_response = bcf_db.put_topic(project_id, topic_id, topic_request, current_user) topic_response = bcf_db.put_topic(project_id, topic_id, topic_request, current_user)
bcf_db.debug(endpoint='topic_put', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'topic_request': topic_request.dict()}, endpoint="topic_put",
response=topic_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "topic_request": topic_request.dict()},
response=topic_response.dict(),
)
return topic_response return topic_response
# Implemented # Implemented
@router.delete("/bcf/3.0/projects/{project_id}/topics/{topic_id}", tags=["topic_delete"], status_code=200) @router.delete("/bcf/3.0/projects/{project_id}/topics/{topic_id}", tags=["topic_delete"], status_code=200)
def topic_delete(project_id: UUID, topic_id: UUID, def topic_delete(project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)) -> int:
current_user: User = Depends(get_current_active_user)) -> int:
topic_response = bcf_db.delete_topic(project_id, topic_id, current_user) topic_response = bcf_db.delete_topic(project_id, topic_id, current_user)
if topic_response is None: if topic_response is None:
raise HTTPException(status_code=404, detail="Item not found.") raise HTTPException(status_code=404, detail="Item not found.")
bcf_db.debug(endpoint='topic_delete', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="topic_delete", request={"project_id": project_id, "topic_id": topic_id}, response={topic_response}
response={topic_response}) )
return topic_response return topic_response
@@ -153,25 +161,31 @@ def topic_delete(project_id: UUID, topic_id: UUID,
# Implemented # Implemented
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/snippet", tags=["bim_snippet_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/snippet", tags=["bim_snippet_get"])
def bim_snippet_get(project_id: UUID, topic_id: UUID, def bim_snippet_get(
current_user: User = Depends(get_current_active_user)) -> BimSnippet: project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)
) -> BimSnippet:
bim_snippet_response = bcf_db.get_bim_snippet(project_id, topic_id, current_user) bim_snippet_response = bcf_db.get_bim_snippet(project_id, topic_id, current_user)
if bim_snippet_response is None: if bim_snippet_response is None:
raise HTTPException(status_code=404, detail="Item not found.") raise HTTPException(status_code=404, detail="Item not found.")
bcf_db.debug(endpoint='bim_snippet_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="bim_snippet_get",
response=bim_snippet_response.dict()) request={"project_id": project_id, "topic_id": topic_id},
response=bim_snippet_response.dict(),
)
return bim_snippet_response return bim_snippet_response
# Implemented # Implemented
@router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/snippet", tags=["bim_snippet_put"], status_code=200) @router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/snippet", tags=["bim_snippet_put"], status_code=200)
def bim_snippet_put(project_id: UUID, topic_id: UUID, snippet: BimSnippet, def bim_snippet_put(
current_user: User = Depends(get_current_active_user)) -> BimSnippet: project_id: UUID, topic_id: UUID, snippet: BimSnippet, current_user: User = Depends(get_current_active_user)
) -> BimSnippet:
bim_snippet_response = bcf_db.put_bim_snippet(project_id, topic_id, snippet, current_user) bim_snippet_response = bcf_db.put_bim_snippet(project_id, topic_id, snippet, current_user)
bcf_db.debug(endpoint='bim_snippet_put', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'snippet': snippet.dict()}, endpoint="bim_snippet_put",
response=bim_snippet_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "snippet": snippet.dict()},
response=bim_snippet_response.dict(),
)
return bim_snippet_response return bim_snippet_response
@@ -182,36 +196,45 @@ def bim_snippet_put(project_id: UUID, topic_id: UUID, snippet: BimSnippet,
@router.get("/bcf/3.0/projects/{project_id}/files_information", tags=["files_information_get"]) @router.get("/bcf/3.0/projects/{project_id}/files_information", tags=["files_information_get"])
def files_information_get(project_id: UUID, def files_information_get(
current_user: User = Depends(get_current_active_user)) -> List[ProjectFileInformation]: project_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[ProjectFileInformation]:
files_information_response = bcf_db.get_files_information(project_id, current_user) files_information_response = bcf_db.get_files_information(project_id, current_user)
bcf_db.debug(endpoint='files_information_get', bcf_db.debug(
request={'project_id': project_id}, endpoint="files_information_get",
response={count: value.dict() for count, value in enumerate(files_information_response)}) request={"project_id": project_id},
response={count: value.dict() for count, value in enumerate(files_information_response)},
)
return files_information_response return files_information_response
# Implemented # Implemented
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/files", tags=["files_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/files", tags=["files_get"])
def files_get(project_id: UUID, topic_id: UUID, def files_get(project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)) -> List[FileGET]:
current_user: User = Depends(get_current_active_user)) -> List[FileGET]:
files_response = bcf_db.get_files(project_id, topic_id, current_user) files_response = bcf_db.get_files(project_id, topic_id, current_user)
bcf_db.debug(endpoint='files_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="files_get",
response={count: value.dict() for count, value in enumerate(files_response)}) request={"project_id": project_id, "topic_id": topic_id},
response={count: value.dict() for count, value in enumerate(files_response)},
)
return files_response return files_response
# request body file = FilePUT # request body file = FilePUT
@router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/files", tags=["files_put"], status_code=200) @router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/files", tags=["files_put"], status_code=200)
def files_put(project_id: UUID, topic_id: UUID, files: List[FilePUT], def files_put(
current_user: User = Depends(get_current_active_user)) -> List[FileGET]: project_id: UUID, topic_id: UUID, files: List[FilePUT], current_user: User = Depends(get_current_active_user)
) -> List[FileGET]:
files_response = bcf_db.put_files(project_id, topic_id, files, current_user) files_response = bcf_db.put_files(project_id, topic_id, files, current_user)
bcf_db.debug(endpoint='files_put', bcf_db.debug(
request={'project_id': project_id, endpoint="files_put",
'topic_id': topic_id, request={
'files': {count: value.dict() for count, value in enumerate(files)}}, "project_id": project_id,
response={count: value.dict() for count, value in enumerate(files_response)}) "topic_id": topic_id,
"files": {count: value.dict() for count, value in enumerate(files)},
},
response={count: value.dict() for count, value in enumerate(files_response)},
)
return files_response return files_response
@@ -222,58 +245,80 @@ def files_put(project_id: UUID, topic_id: UUID, files: List[FilePUT],
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments", tags=["comments_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments", tags=["comments_get"])
def comments_get(project_id: UUID, topic_id: UUID, def comments_get(
current_user: User = Depends(get_current_active_user)) -> List[CommentGET]: project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[CommentGET]:
comments_response = bcf_db.get_comments(project_id, topic_id, current_user) comments_response = bcf_db.get_comments(project_id, topic_id, current_user)
bcf_db.debug(endpoint='comments_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="comments_get",
response={count: value.dict() for count, value in enumerate(comments_response)}) request={"project_id": project_id, "topic_id": topic_id},
response={count: value.dict() for count, value in enumerate(comments_response)},
)
return comments_response return comments_response
# request body comment = CommentPOST # request body comment = CommentPOST
@router.post("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments", tags=["comment_post"], status_code=201) @router.post("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments", tags=["comment_post"], status_code=201)
def comment_post(project_id: UUID, topic_id: UUID, comment: CommentPOST, def comment_post(
current_user: User = Depends(get_current_active_user)) -> CommentGET: project_id: UUID, topic_id: UUID, comment: CommentPOST, current_user: User = Depends(get_current_active_user)
) -> CommentGET:
comment_response = bcf_db.post_comment(project_id, topic_id, comment, current_user) comment_response = bcf_db.post_comment(project_id, topic_id, comment, current_user)
bcf_db.debug(endpoint='comment_post', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'comment': comment}, endpoint="comment_post",
response=comment_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "comment": comment},
response=comment_response.dict(),
)
return comment_response return comment_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", tags=["comment_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", tags=["comment_get"])
def comment_get(project_id: UUID, topic_id: UUID, comment_id: UUID, def comment_get(
current_user: User = Depends(get_current_active_user)) -> CommentGET: project_id: UUID, topic_id: UUID, comment_id: UUID, current_user: User = Depends(get_current_active_user)
) -> CommentGET:
comment_response = bcf_db.get_comment(project_id, topic_id, comment_id, current_user) comment_response = bcf_db.get_comment(project_id, topic_id, comment_id, current_user)
bcf_db.debug(endpoint='comment_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'comment_id': comment_id}, endpoint="comment_get",
response=comment_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "comment_id": comment_id},
response=comment_response.dict(),
)
return comment_response return comment_response
# request body comment = CommentPUT # request body comment = CommentPUT
@router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", tags=["comment_put"], status_code=200) @router.put(
def comment_put(project_id: UUID, topic_id: UUID, comment_id: UUID, comment: CommentPUT, "/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", tags=["comment_put"], status_code=200
current_user: User = Depends(get_current_active_user)) -> CommentGET: )
def comment_put(
project_id: UUID,
topic_id: UUID,
comment_id: UUID,
comment: CommentPUT,
current_user: User = Depends(get_current_active_user),
) -> CommentGET:
comment_response = bcf_db.put_comment(project_id, topic_id, comment_id, comment, current_user) comment_response = bcf_db.put_comment(project_id, topic_id, comment_id, comment, current_user)
bcf_db.debug(endpoint='comment_put', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'comment': comment}, endpoint="comment_put",
response=comment_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "comment": comment},
response=comment_response.dict(),
)
return comment_response return comment_response
# Implemented # Implemented
@router.delete("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", @router.delete(
tags=["comment_delete"], status_code=200) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}", tags=["comment_delete"], status_code=200
def comment_delete(project_id: UUID, topic_id: UUID, comment_id: UUID, )
current_user: User = Depends(get_current_active_user)) -> int: def comment_delete(
project_id: UUID, topic_id: UUID, comment_id: UUID, current_user: User = Depends(get_current_active_user)
) -> int:
comment_response = bcf_db.delete_comment(project_id, topic_id, comment_id, current_user) comment_response = bcf_db.delete_comment(project_id, topic_id, comment_id, current_user)
if comment_response is None: if comment_response is None:
raise HTTPException(status_code=404, detail="Item not found.") raise HTTPException(status_code=404, detail="Item not found.")
bcf_db.debug(endpoint='comment_delete', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'comment_id': comment_id}, endpoint="comment_delete",
response={comment_response}) request={"project_id": project_id, "topic_id": topic_id, "comment_id": comment_id},
response={comment_response},
)
return comment_response return comment_response
@@ -284,113 +329,160 @@ def comment_delete(project_id: UUID, topic_id: UUID, comment_id: UUID,
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints", tags=["viewpoints_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints", tags=["viewpoints_get"])
def viewpoints_get(project_id: UUID, topic_id: UUID, def viewpoints_get(
current_user: User = Depends(get_current_active_user)) -> List[ViewpointGET]: project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[ViewpointGET]:
viewpoints_response = bcf_db.get_viewpoints(project_id, topic_id, current_user) viewpoints_response = bcf_db.get_viewpoints(project_id, topic_id, current_user)
bcf_db.debug(endpoint='viewpoints_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="viewpoints_get",
response={count: value.dict() for count, value in enumerate(viewpoints_response)}) request={"project_id": project_id, "topic_id": topic_id},
response={count: value.dict() for count, value in enumerate(viewpoints_response)},
)
return viewpoints_response return viewpoints_response
# request body viewpoint = viewpointPOST # request body viewpoint = viewpointPOST
@router.post("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints", tags=["viewpoint_post"], status_code=201) @router.post("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints", tags=["viewpoint_post"], status_code=201)
def viewpoint_post(project_id: UUID, topic_id: UUID, viewpoint: ViewpointPOST, def viewpoint_post(
current_user: User = Depends(get_current_active_user)) -> ViewpointGET: project_id: UUID, topic_id: UUID, viewpoint: ViewpointPOST, current_user: User = Depends(get_current_active_user)
) -> ViewpointGET:
viewpoint_response = bcf_db.post_viewpoint(project_id, topic_id, viewpoint, current_user) viewpoint_response = bcf_db.post_viewpoint(project_id, topic_id, viewpoint, current_user)
bcf_db.debug(endpoint='viewpoint_post', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint': viewpoint.dict()}, endpoint="viewpoint_post",
response=viewpoint_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "viewpoint": viewpoint.dict()},
response=viewpoint_response.dict(),
)
return viewpoint_response return viewpoint_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}", tags=["viewpoint_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}", tags=["viewpoint_get"])
def viewpoint_get(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, def viewpoint_get(
current_user: User = Depends(get_current_active_user)) -> ViewpointGET: project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User = Depends(get_current_active_user)
) -> ViewpointGET:
viewpoint_response = bcf_db.get_viewpoint(project_id, topic_id, viewpoint_id, current_user) viewpoint_response = bcf_db.get_viewpoint(project_id, topic_id, viewpoint_id, current_user)
bcf_db.debug(endpoint='viewpoint_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint_id': viewpoint_id}, endpoint="viewpoint_get",
response=viewpoint_response.dict()) request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id},
response=viewpoint_response.dict(),
)
return viewpoint_response return viewpoint_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/snapshot", @router.get(
tags=["viewpoint_snapshot_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/snapshot",
async def viewpoint_snapshot_get(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, tags=["viewpoint_snapshot_get"],
current_user: User = Depends(get_current_active_user)) -> FileResponse: )
async def viewpoint_snapshot_get(
project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User = Depends(get_current_active_user)
) -> FileResponse:
viewpoint_snapshot_response = bcf_db.get_viewpoint_snapshot(project_id, topic_id, viewpoint_id, current_user) viewpoint_snapshot_response = bcf_db.get_viewpoint_snapshot(project_id, topic_id, viewpoint_id, current_user)
bcf_db.debug(endpoint='viewpoint_snapshot_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint_id': viewpoint_id}, endpoint="viewpoint_snapshot_get",
response=viewpoint_snapshot_response) request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id},
snapshot_name = 'snapshot_' + str(viewpoint_id) response=viewpoint_snapshot_response,
file_ending = '.' + viewpoint_snapshot_response.split('/', 2)[1] )
snapshot_path = 'data/snapshots/' + snapshot_name + file_ending snapshot_name = "snapshot_" + str(viewpoint_id)
file_ending = "." + viewpoint_snapshot_response.split("/", 2)[1]
snapshot_path = "data/snapshots/" + snapshot_name + file_ending
snapshot_type = viewpoint_snapshot_response snapshot_type = viewpoint_snapshot_response
return FileResponse(path=snapshot_path, return FileResponse(path=snapshot_path, media_type=snapshot_type)
media_type=snapshot_type)
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/bitmaps/{bitmap_id}", @router.get(
tags=["viewpoint_bitmap_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/bitmaps/{bitmap_id}",
async def viewpoint_bitmap_get(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, bitmap_id: UUID, tags=["viewpoint_bitmap_get"],
current_user: User = Depends(get_current_active_user)) -> FileResponse: )
async def viewpoint_bitmap_get(
project_id: UUID,
topic_id: UUID,
viewpoint_id: UUID,
bitmap_id: UUID,
current_user: User = Depends(get_current_active_user),
) -> FileResponse:
viewpoint_bitmap_response = bcf_db.get_viewpoint_bitmap(project_id, topic_id, viewpoint_id, bitmap_id, current_user) viewpoint_bitmap_response = bcf_db.get_viewpoint_bitmap(project_id, topic_id, viewpoint_id, bitmap_id, current_user)
bcf_db.debug(endpoint='viewpoint_bitmap_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, endpoint="viewpoint_bitmap_get",
'viewpoint_id': viewpoint_id, 'bitmap_id': bitmap_id}, request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id, "bitmap_id": bitmap_id},
response=viewpoint_bitmap_response.dict()) response=viewpoint_bitmap_response.dict(),
bitmap_name = 'bitmap_' + str(viewpoint_id) )
file_ending = '.' + viewpoint_bitmap_response['bitmap_type'].split('/', 2)[1] bitmap_name = "bitmap_" + str(viewpoint_id)
bitmap_path = 'data/bitmaps/' + bitmap_name + file_ending file_ending = "." + viewpoint_bitmap_response["bitmap_type"].split("/", 2)[1]
bitmap_type = viewpoint_bitmap_response['bitmap_type'] bitmap_path = "data/bitmaps/" + bitmap_name + file_ending
return FileResponse(path=bitmap_path, bitmap_type = viewpoint_bitmap_response["bitmap_type"]
media_type=bitmap_type) return FileResponse(path=bitmap_path, media_type=bitmap_type)
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/coloring", @router.get(
tags=["viewpoint_colored_components_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/coloring",
def viewpoint_colored_components_get(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, tags=["viewpoint_colored_components_get"],
current_user: User = Depends(get_current_active_user)) -> ColoringGET: )
viewpoint_colored_components_response = bcf_db.get_viewpoint_colored_components(project_id, topic_id, viewpoint_id, current_user) def viewpoint_colored_components_get(
bcf_db.debug(endpoint='viewpoint_colored_components_get', project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User = Depends(get_current_active_user)
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint_id': viewpoint_id}, ) -> ColoringGET:
response=viewpoint_colored_components_response.dict()) viewpoint_colored_components_response = bcf_db.get_viewpoint_colored_components(
project_id, topic_id, viewpoint_id, current_user
)
bcf_db.debug(
endpoint="viewpoint_colored_components_get",
request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id},
response=viewpoint_colored_components_response.dict(),
)
return viewpoint_colored_components_response return viewpoint_colored_components_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/selection", @router.get(
tags=["viewpoint_selected_components_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/selection",
def viewpoint_selected_components_get(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, tags=["viewpoint_selected_components_get"],
current_user: User = Depends(get_current_active_user)) -> SelectionGET: )
viewpoint_selected_components_response = bcf_db.get_viewpoint_selected_components(project_id, topic_id, viewpoint_id, current_user) def viewpoint_selected_components_get(
bcf_db.debug(endpoint='viewpoint_selected_components_get', project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User = Depends(get_current_active_user)
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint_id': viewpoint_id}, ) -> SelectionGET:
response=viewpoint_selected_components_response.dict()) viewpoint_selected_components_response = bcf_db.get_viewpoint_selected_components(
project_id, topic_id, viewpoint_id, current_user
)
bcf_db.debug(
endpoint="viewpoint_selected_components_get",
request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id},
response=viewpoint_selected_components_response.dict(),
)
return viewpoint_selected_components_response return viewpoint_selected_components_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/visibility", @router.get(
tags=["viewpoint_components_visibility_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/visibility",
def viewpoint_components_visibility_get(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, tags=["viewpoint_components_visibility_get"],
current_user: User = Depends(get_current_active_user)) -> VisibilityGET: )
viewpoint_components_visibility_response = bcf_db.get_viewpoint_components_visibility(project_id, topic_id, viewpoint_id, current_user) def viewpoint_components_visibility_get(
bcf_db.debug(endpoint='viewpoint_components_visibility_get', project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User = Depends(get_current_active_user)
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint_id': viewpoint_id}, ) -> VisibilityGET:
response=viewpoint_components_visibility_response.dict()) viewpoint_components_visibility_response = bcf_db.get_viewpoint_components_visibility(
project_id, topic_id, viewpoint_id, current_user
)
bcf_db.debug(
endpoint="viewpoint_components_visibility_get",
request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id},
response=viewpoint_components_visibility_response.dict(),
)
return viewpoint_components_visibility_response return viewpoint_components_visibility_response
# Implemented # Implemented
@router.delete("/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}", @router.delete(
tags=["viewpoint_delete"], status_code=200) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}",
def viewpoint_delete(project_id: UUID, topic_id: UUID, viewpoint_id: UUID, tags=["viewpoint_delete"],
current_user: User = Depends(get_current_active_user)) -> int: status_code=200,
)
def viewpoint_delete(
project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User = Depends(get_current_active_user)
) -> int:
viewpoint_response = bcf_db.delete_viewpoint(project_id, topic_id, viewpoint_id, current_user) viewpoint_response = bcf_db.delete_viewpoint(project_id, topic_id, viewpoint_id, current_user)
if viewpoint_response is None: if viewpoint_response is None:
raise HTTPException(status_code=404, detail="Item not found.") raise HTTPException(status_code=404, detail="Item not found.")
bcf_db.debug(endpoint='viewpoint_delete', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'viewpoint_id': viewpoint_id}, endpoint="viewpoint_delete",
response={viewpoint_response}) request={"project_id": project_id, "topic_id": topic_id, "viewpoint_id": viewpoint_id},
response={viewpoint_response},
)
return viewpoint_response return viewpoint_response
@@ -401,24 +493,38 @@ def viewpoint_delete(project_id: UUID, topic_id: UUID, viewpoint_id: UUID,
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/related_topics", tags=["related_topics_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/related_topics", tags=["related_topics_get"])
def related_topics_get(project_id: UUID, topic_id: UUID, def related_topics_get(
current_user: User = Depends(get_current_active_user)) -> List[RelatedTopicGET]: project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[RelatedTopicGET]:
related_topics_response = bcf_db.get_related_topics(project_id, topic_id, current_user) related_topics_response = bcf_db.get_related_topics(project_id, topic_id, current_user)
bcf_db.debug(endpoint='related_topics_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="related_topics_get",
response={count: value.dict() for count, value in enumerate(related_topics_response)}) request={"project_id": project_id, "topic_id": topic_id},
response={count: value.dict() for count, value in enumerate(related_topics_response)},
)
return related_topics_response return related_topics_response
# request body related_topic = RelatedTopicPUT # request body related_topic = RelatedTopicPUT
@router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/related_topics", tags=["related_topics_put"], status_code=200) @router.put(
def related_topics_put(project_id: UUID, topic_id: UUID, related_topics: List[RelatedTopicPUT], "/bcf/3.0/projects/{project_id}/topics/{topic_id}/related_topics", tags=["related_topics_put"], status_code=200
current_user: User = Depends(get_current_active_user)) -> List[RelatedTopicGET]: )
def related_topics_put(
project_id: UUID,
topic_id: UUID,
related_topics: List[RelatedTopicPUT],
current_user: User = Depends(get_current_active_user),
) -> List[RelatedTopicGET]:
related_topics_response = bcf_db.put_related_topics(project_id, topic_id, related_topics, current_user) related_topics_response = bcf_db.put_related_topics(project_id, topic_id, related_topics, current_user)
bcf_db.debug(endpoint='related_topics_put', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, endpoint="related_topics_put",
'related_topics': {count: value.dict() for count, value in enumerate(related_topics)}}, request={
response={count: value.dict() for count, value in enumerate(related_topics_response)}) "project_id": project_id,
"topic_id": topic_id,
"related_topics": {count: value.dict() for count, value in enumerate(related_topics)},
},
response={count: value.dict() for count, value in enumerate(related_topics_response)},
)
return related_topics_response return related_topics_response
@@ -428,51 +534,70 @@ def related_topics_put(project_id: UUID, topic_id: UUID, related_topics: List[Re
# Document references <- from topic # Document references <- from topic
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/document_references", @router.get(
tags=["topic_document_references_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/document_references", tags=["topic_document_references_get"]
def topic_document_references_get(project_id: UUID, topic_id: UUID, )
current_user: User = Depends(get_current_active_user)) -> List[DocumentReferenceGET]: def topic_document_references_get(
project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[DocumentReferenceGET]:
topic_document_references_response = bcf_db.get_topic_document_references(project_id, topic_id, current_user) topic_document_references_response = bcf_db.get_topic_document_references(project_id, topic_id, current_user)
bcf_db.debug(endpoint='topic_document_references_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="topic_document_references_get",
response={count: value.dict() for count, value in enumerate(topic_document_references_response)}) request={"project_id": project_id, "topic_id": topic_id},
response={count: value.dict() for count, value in enumerate(topic_document_references_response)},
)
return topic_document_references_response return topic_document_references_response
# request body document_reference = DocumentReferencePOST # request body document_reference = DocumentReferencePOST
@router.post("/bcf/3.0/projects/{project_id}/topics/{topic_id}/document_references", @router.post(
tags=["topic_document_references_post"], "/bcf/3.0/projects/{project_id}/topics/{topic_id}/document_references",
status_code=201) tags=["topic_document_references_post"],
def topic_document_reference_post(project_id: UUID, topic_id: UUID, document_reference: DocumentReferencePOST, status_code=201,
current_user: User = Depends(get_current_active_user)) -> DocumentReferenceGET: )
topic_document_references_response = bcf_db.post_topic_document_references(project_id, def topic_document_reference_post(
topic_id, project_id: UUID,
document_reference, topic_id: UUID,
current_user) document_reference: DocumentReferencePOST,
bcf_db.debug(endpoint='topic_document_references_post', current_user: User = Depends(get_current_active_user),
request={'project_id': project_id, 'topic_id': topic_id, 'document_reference': document_reference}, ) -> DocumentReferenceGET:
response={count: value.dict() for count, value in enumerate(topic_document_references_response)}) topic_document_references_response = bcf_db.post_topic_document_references(
project_id, topic_id, document_reference, current_user
)
bcf_db.debug(
endpoint="topic_document_references_post",
request={"project_id": project_id, "topic_id": topic_id, "document_reference": document_reference},
response={count: value.dict() for count, value in enumerate(topic_document_references_response)},
)
return topic_document_references_response return topic_document_references_response
# request body document_reference = DocumentReferencePUT # request body document_reference = DocumentReferencePUT
@router.put("/bcf/3.0/projects/{project_id}/topics/{topic_id}/document_references/{reference_id}", @router.put(
tags=["topic_document_references_put"], "/bcf/3.0/projects/{project_id}/topics/{topic_id}/document_references/{reference_id}",
status_code=200) tags=["topic_document_references_put"],
def topic_document_references_put(project_id: UUID, topic_id: UUID, reference_id: UUID, status_code=200,
document_reference: DocumentReferencePUT, )
current_user: User = Depends(get_current_active_user)) -> DocumentReferenceGET: def topic_document_references_put(
topic_document_references_response = bcf_db.put_topic_document_references(project_id, project_id: UUID,
topic_id, topic_id: UUID,
reference_id, reference_id: UUID,
document_reference, document_reference: DocumentReferencePUT,
current_user) current_user: User = Depends(get_current_active_user),
bcf_db.debug(endpoint='topic_document_references_put', ) -> DocumentReferenceGET:
request={'project_id': project_id, topic_document_references_response = bcf_db.put_topic_document_references(
'topic_id': topic_id, project_id, topic_id, reference_id, document_reference, current_user
'reference_id': reference_id, )
'document_reference': document_reference.dict()}, bcf_db.debug(
response={count: value.dict() for count, value in enumerate(topic_document_references_response)}) endpoint="topic_document_references_put",
request={
"project_id": project_id,
"topic_id": topic_id,
"reference_id": reference_id,
"document_reference": document_reference.dict(),
},
response={count: value.dict() for count, value in enumerate(topic_document_references_response)},
)
return topic_document_references_response return topic_document_references_response
@@ -485,30 +610,34 @@ def topic_document_references_put(project_id: UUID, topic_id: UUID, reference_id
@router.get("/bcf/3.0/projects/{project_id}/documents", tags=["documents_get"]) @router.get("/bcf/3.0/projects/{project_id}/documents", tags=["documents_get"])
def documents_get(project_id: UUID, current_user: User = Depends(get_current_active_user)) -> List[DocumentGET]: def documents_get(project_id: UUID, current_user: User = Depends(get_current_active_user)) -> List[DocumentGET]:
documents_response = bcf_db.get_documents(project_id, current_user) documents_response = bcf_db.get_documents(project_id, current_user)
bcf_db.debug(endpoint='documents_get', bcf_db.debug(
request={'project_id': project_id}, endpoint="documents_get",
response={count: value.dict() for count, value in enumerate(documents_response)}) request={"project_id": project_id},
response={count: value.dict() for count, value in enumerate(documents_response)},
)
return documents_response return documents_response
# request body file = UploadFile # request body file = UploadFile
@router.post("/bcf/3.0/projects/{project_id}/documents", tags=["document_post"], status_code=201) @router.post("/bcf/3.0/projects/{project_id}/documents", tags=["document_post"], status_code=201)
async def document_post(project_id: UUID, file: UploadFile, async def document_post(
current_user: User = Depends(get_current_active_user)) -> DocumentGET: project_id: UUID, file: UploadFile, current_user: User = Depends(get_current_active_user)
) -> DocumentGET:
document_response = bcf_db.post_document(project_id, file, current_user) document_response = bcf_db.post_document(project_id, file, current_user)
bcf_db.debug(endpoint='document_post', bcf_db.debug(endpoint="document_post", request={"project_id": project_id}, response=document_response.dict())
request={'project_id': project_id},
response=document_response.dict())
return document_response return document_response
@router.get("/bcf/3.0/projects/{project_id}/documents/{document_id}", tags=["document_get"]) @router.get("/bcf/3.0/projects/{project_id}/documents/{document_id}", tags=["document_get"])
def document_get(project_id: UUID, document_id: UUID, def document_get(
current_user: User = Depends(get_current_active_user)) -> DocumentGET: project_id: UUID, document_id: UUID, current_user: User = Depends(get_current_active_user)
) -> DocumentGET:
document_response = bcf_db.get_document(project_id, document_id, current_user) document_response = bcf_db.get_document(project_id, document_id, current_user)
bcf_db.debug(endpoint='document_get', bcf_db.debug(
request={'project_id': project_id, 'document_id': document_id}, endpoint="document_get",
response=document_response.dict()) request={"project_id": project_id, "document_id": document_id},
response=document_response.dict(),
)
return document_response return document_response
@@ -520,22 +649,26 @@ def document_get(project_id: UUID, document_id: UUID,
# ... # ...
@router.get("/bcf/3.0/projects/{project_id}/topics/events", tags=["topics_events_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/events", tags=["topics_events_get"])
def topics_events_get(project_id: UUID, def topics_events_get(project_id: UUID, current_user: User = Depends(get_current_active_user)) -> List[TopicEventGET]:
current_user: User = Depends(get_current_active_user)) -> List[TopicEventGET]:
topic_events_response = bcf_db.get_topics_events(project_id, current_user) topic_events_response = bcf_db.get_topics_events(project_id, current_user)
bcf_db.debug(endpoint='topics_events_get', bcf_db.debug(
request={'project_id': project_id}, endpoint="topics_events_get",
response={count: value.dict() for count, value in enumerate(topic_events_response)}) request={"project_id": project_id},
response={count: value.dict() for count, value in enumerate(topic_events_response)},
)
return topic_events_response return topic_events_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/events", tags=["topic_events_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/events", tags=["topic_events_get"])
def topic_events_get(project_id: UUID, topic_id: UUID, def topic_events_get(
current_user: User = Depends(get_current_active_user)) -> List[TopicEventGET]: project_id: UUID, topic_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[TopicEventGET]:
topic_events_response = bcf_db.get_topic_events(project_id, topic_id, current_user) topic_events_response = bcf_db.get_topic_events(project_id, topic_id, current_user)
bcf_db.debug(endpoint='topic_events_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id}, endpoint="topic_events_get",
response={count: value.dict() for count, value in enumerate(topic_events_response)}) request={"project_id": project_id, "topic_id": topic_id},
response={count: value.dict() for count, value in enumerate(topic_events_response)},
)
return topic_events_response return topic_events_response
@@ -546,21 +679,28 @@ def topic_events_get(project_id: UUID, topic_id: UUID,
@router.get("/bcf/3.0/projects/{project_id}/topics/comments/events", tags=["comments_events_get"]) @router.get("/bcf/3.0/projects/{project_id}/topics/comments/events", tags=["comments_events_get"])
def comments_events_get(project_id: UUID, def comments_events_get(
current_user: User = Depends(get_current_active_user)) -> List[CommentEventGET]: project_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[CommentEventGET]:
comments_events_response = bcf_db.get_comments_events(project_id, current_user) comments_events_response = bcf_db.get_comments_events(project_id, current_user)
bcf_db.debug(endpoint='comments_events_get', bcf_db.debug(
request={'project_id': project_id}, endpoint="comments_events_get",
response={count: value.dict() for count, value in enumerate(comments_events_response)}) request={"project_id": project_id},
response={count: value.dict() for count, value in enumerate(comments_events_response)},
)
return comments_events_response return comments_events_response
@router.get("/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}/events", @router.get(
tags=["comment_events_get"]) "/bcf/3.0/projects/{project_id}/topics/{topic_id}/comments/{comment_id}/events", tags=["comment_events_get"]
def comment_events_get(project_id: UUID, topic_id: UUID, comment_id: UUID, )
current_user: User = Depends(get_current_active_user)) -> List[CommentEventGET]: def comment_events_get(
project_id: UUID, topic_id: UUID, comment_id: UUID, current_user: User = Depends(get_current_active_user)
) -> List[CommentEventGET]:
comment_events_response = bcf_db.get_comment_events(project_id, topic_id, comment_id, current_user) comment_events_response = bcf_db.get_comment_events(project_id, topic_id, comment_id, current_user)
bcf_db.debug(endpoint='comment_events_get', bcf_db.debug(
request={'project_id': project_id, 'topic_id': topic_id, 'comment_id': comment_id}, endpoint="comment_events_get",
response={count: value.dict() for count, value in enumerate(comment_events_response)}) request={"project_id": project_id, "topic_id": topic_id, "comment_id": comment_id},
response={count: value.dict() for count, value in enumerate(comment_events_response)},
)
return comment_events_response return comment_events_response
+192 -161
View File
@@ -1,4 +1,3 @@
import collections import collections
import os import os
import shutil import shutil
@@ -6,7 +5,7 @@ import traceback
import sys import sys
from fastapi import HTTPException, status, APIRouter, Request, Depends from fastapi import HTTPException, status, APIRouter, Request, Depends
from fastapi import UploadFile, Form from fastapi import UploadFile, Form
from fastapi.responses import FileResponse, HTMLResponse from fastapi.responses import FileResponse, HTMLResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
@@ -87,12 +86,15 @@ templates = Jinja2Templates(directory="templates")
@router.post("/documents/1.0/upload-documents", tags=[""]) @router.post("/documents/1.0/upload-documents", tags=[""])
def upload_documents_post(upload_documents: UploadDocuments, def upload_documents_post(
current_user: User = Depends(get_current_active_user)) -> DocumentUploadSessionInitialization: upload_documents: UploadDocuments, current_user: User = Depends(get_current_active_user)
) -> DocumentUploadSessionInitialization:
post_upload_documents_response = doc_db.post_upload_documents(upload_documents, current_user) post_upload_documents_response = doc_db.post_upload_documents(upload_documents, current_user)
doc_db.debug(endpoint='upload_documents_post', doc_db.debug(
request={'upload_documents': upload_documents}, endpoint="upload_documents_post",
response=post_upload_documents_response.dict()) request={"upload_documents": upload_documents},
response=post_upload_documents_response.dict(),
)
return post_upload_documents_response return post_upload_documents_response
@@ -101,20 +103,23 @@ def upload_documents_post(upload_documents: UploadDocuments,
def upload_documents_get(request: Request, upload_session: UUID): def upload_documents_get(request: Request, upload_session: UUID):
data_for_upload_documents = doc_db.get_data_for_upload_documents(upload_session) data_for_upload_documents = doc_db.get_data_for_upload_documents(upload_session)
print('Data for site: ', data_for_upload_documents) print("Data for site: ", data_for_upload_documents)
return templates.TemplateResponse( return templates.TemplateResponse(
'upload_files.html', "upload_files.html",
{'request': request, {
'upload_session': upload_session, "request": request,
'username': data_for_upload_documents.current_user.username, "upload_session": upload_session,
'email': data_for_upload_documents.current_user.email, "username": data_for_upload_documents.current_user.username,
'full_name': data_for_upload_documents.current_user.full_name, "email": data_for_upload_documents.current_user.email,
'server_context': data_for_upload_documents.server_context, "full_name": data_for_upload_documents.current_user.full_name,
'callback_url': data_for_upload_documents.callback.url, "server_context": data_for_upload_documents.server_context,
'callback_expires_in': data_for_upload_documents.callback.expires_in, "callback_url": data_for_upload_documents.callback.url,
'documents': data_for_upload_documents.documents, "callback_expires_in": data_for_upload_documents.callback.expires_in,
'projects': data_for_upload_documents.projects}) "documents": data_for_upload_documents.documents,
"projects": data_for_upload_documents.projects,
},
)
@router.post("/documents/1.0/save-metadata-for-documents", tags=[""]) @router.post("/documents/1.0/save-metadata-for-documents", tags=[""])
@@ -127,29 +132,29 @@ async def save_metadata_for_documents_post(request: Request) -> list:
print(form_data_json) print(form_data_json)
documents = collections.defaultdict(dict) documents = collections.defaultdict(dict)
names = ('session_file_id', 'document', 'title', 'version_number', 'filename') names = ("session_file_id", "document", "title", "version_number", "filename")
for whole_form_key, value in form_data_json.items(): for whole_form_key, value in form_data_json.items():
if whole_form_key.startswith(names): if whole_form_key.startswith(names):
start_form_key, document_id = whole_form_key.split("@", 1) start_form_key, document_id = whole_form_key.split("@", 1)
print('New field: ', start_form_key, ' for document id: ', document_id) print("New field: ", start_form_key, " for document id: ", document_id)
documents[document_id][start_form_key] = value documents[document_id][start_form_key] = value
print("Documents: ") print("Documents: ")
print(documents) print(documents)
username = form_data_json['username'] username = form_data_json["username"]
upload_session = form_data_json['upload_session'] upload_session = form_data_json["upload_session"]
server_context = form_data_json['server_context'] server_context = form_data_json["server_context"]
callback_url = form_data_json['callback_url'] callback_url = form_data_json["callback_url"]
callback_expires_in = form_data_json['callback_expires_in'] callback_expires_in = form_data_json["callback_expires_in"]
project = form_data_json['project'] project = form_data_json["project"]
documents_saved = list() documents_saved = list()
for key in documents: for key in documents:
try: try:
documents[key]['project'] = project documents[key]["project"] = project
document = DocumentMetadata(**documents[key]) document = DocumentMetadata(**documents[key])
save_metadata_response = doc_db.save_metadata_for_documents(document, upload_session, username) save_metadata_response = doc_db.save_metadata_for_documents(document, upload_session, username)
documents_saved.append(save_metadata_response) documents_saved.append(save_metadata_response)
@@ -157,9 +162,11 @@ async def save_metadata_for_documents_post(request: Request) -> list:
print(e) print(e)
continue continue
doc_db.debug(endpoint='save_metadata_for_documents_post', doc_db.debug(
request={'documents': documents}, endpoint="save_metadata_for_documents_post",
response={'response': documents_saved}) request={"documents": documents},
response={"response": documents_saved},
)
return documents_saved return documents_saved
@@ -167,22 +174,30 @@ async def save_metadata_for_documents_post(request: Request) -> list:
# http://localhost:8080/cde-callback-example?upload_documents_url= # http://localhost:8080/cde-callback-example?upload_documents_url=
# https%3A%2F%2Fcde.example.com%2Fupload-instructions%3Fupload_session%3Dee56b8f3-8f93-4819-976e-46a45a5a996f # https%3A%2F%2Fcde.example.com%2Fupload-instructions%3Fupload_session%3Dee56b8f3-8f93-4819-976e-46a45a5a996f
@router.post("/documents/1.0/upload-instructions", tags=[""]) @router.post("/documents/1.0/upload-instructions", tags=[""])
def upload_instructions(session_id: str, server_context: str, upload_files: UploadFileDetails, def upload_instructions(
current_user: User = Depends(get_current_active_user)) -> DocumentsToUpload: session_id: str,
server_context: str,
upload_files: UploadFileDetails,
current_user: User = Depends(get_current_active_user),
) -> DocumentsToUpload:
documents_to_upload_model = DocumentsToUpload() documents_to_upload_model = DocumentsToUpload()
documents_to_upload_model.server_context = server_context documents_to_upload_model.server_context = server_context
documents_to_upload_model.documents_to_upload = list() documents_to_upload_model.documents_to_upload = list()
for upload_file in upload_files.files: for upload_file in upload_files.files:
get_upload_instructions_response = doc_db.get_upload_instructions(session_id, server_context, upload_file, current_user) get_upload_instructions_response = doc_db.get_upload_instructions(
doc_db.debug(endpoint='upload_instructions', session_id, server_context, upload_file, current_user
request={'session_id': session_id, )
'server_context': server_context, doc_db.debug(
'document': upload_file}, endpoint="upload_instructions",
response=get_upload_instructions_response.dict()) request={"session_id": session_id, "server_context": server_context, "document": upload_file},
response=get_upload_instructions_response.dict(),
)
documents_to_upload_model.documents_to_upload.append(get_upload_instructions_response) documents_to_upload_model.documents_to_upload.append(get_upload_instructions_response)
return documents_to_upload_model return documents_to_upload_model
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# summary: 'Upload a single file part' # summary: 'Upload a single file part'
# #
@@ -206,8 +221,7 @@ def upload_instructions(session_id: str, server_context: str, upload_files: Uplo
@router.post("/documents/1.0/upload-part/{part_id}", tags=[""]) @router.post("/documents/1.0/upload-part/{part_id}", tags=[""])
async def upload_part(part_id: str, request: Request, async def upload_part(part_id: str, request: Request, current_user: User = Depends(get_current_active_user)):
current_user: User = Depends(get_current_active_user)):
# file_name = doc_db.safe_path(part_id) # file_name = doc_db.safe_path(part_id)
file_name = part_id file_name = part_id
@@ -222,33 +236,31 @@ async def upload_part(part_id: str, request: Request,
# try to receive the uploaded part # try to receive the uploaded part
try: try:
print('File contents: ', request_body) print("File contents: ", request_body)
# use document_id instead as dir_name # use document_id instead as dir_name
# dir_name = doc_db.safe_path(document.document_id) # dir_name = doc_db.safe_path(document.document_id)
dir_name = document.document_id dir_name = document.document_id
path = './data/document_parts/' + dir_name + '/' path = "./data/document_parts/" + dir_name + "/"
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
with open(path + file_name, 'wb') as f: with open(path + file_name, "wb") as f:
f.write(request_body) f.write(request_body)
except Exception: except Exception:
print('Error uploading file') print("Error uploading file")
print(traceback.format_exc()) print(traceback.format_exc())
print('Error uploading file') print("Error uploading file")
print(sys.exc_info()[2]) print(sys.exc_info()[2])
finally: finally:
# We will write to the database, information about part successfully uploaded. # We will write to the database, information about part successfully uploaded.
doc_db.mark_part_as_uploaded(part_id, current_user) doc_db.mark_part_as_uploaded(part_id, current_user)
doc_db.debug(endpoint='upload-part', doc_db.debug(endpoint="upload-part", request={"part_id": part_id}, response={"uploaded": True})
request={'part_id': part_id},
response={'uploaded': True})
return {"message": f"Successfully uploaded part {file_name}"} return {"message": f"Successfully uploaded part {file_name}"}
@@ -269,9 +281,11 @@ async def upload_part(part_id: str, request: Request,
# /server-provided-path-document-upload-cancellation # /server-provided-path-document-upload-cancellation
# description: This operation should be called to cancel the upload # description: This operation should be called to cancel the upload
@router.post("/documents/1.0/upload-completion", tags=[""]) @router.post("/documents/1.0/upload-completion", tags=[""])
def upload_completion(upload_session: str, def upload_completion(
current_user: User = Depends(get_current_active_user)) -> Union[DocumentVersion, bool]: upload_session: str, current_user: User = Depends(get_current_active_user)
) -> Union[DocumentVersion, bool]:
# check if all parts really are marked as uploaded in database # check if all parts really are marked as uploaded in database
# retrieve document_id, file_type, file_ending, and parts_id (in order) # retrieve document_id, file_type, file_ending, and parts_id (in order)
@@ -280,23 +294,23 @@ def upload_completion(upload_session: str,
raise HTTPException(status_code=400, detail="All parts not uploaded.") raise HTTPException(status_code=400, detail="All parts not uploaded.")
parts = doc_db.retrieve_uploaded_parts(upload_session, current_user) parts = doc_db.retrieve_uploaded_parts(upload_session, current_user)
print('Number of parts: ' + str(len(parts))) print("Number of parts: " + str(len(parts)))
document = doc_db.get_document_from_session(upload_session, current_user) document = doc_db.get_document_from_session(upload_session, current_user)
# check if all parts really are uploaded to document_id-dir # check if all parts really are uploaded to document_id-dir
document_name = doc_db.safe_path(document.document_id) document_name = doc_db.safe_path(document.document_id)
path = './data/document_parts/' + document_name + '/' path = "./data/document_parts/" + document_name + "/"
for part in parts: for part in parts:
part = doc_db.safe_path(part) part = doc_db.safe_path(part)
print('Checking for part ' + part + ' in dir ' + path) print("Checking for part " + part + " in dir " + path)
if not os.path.isfile(path + part): if not os.path.isfile(path + part):
print(part + ' is not in dir ' + path) print(part + " is not in dir " + path)
raise HTTPException(status_code=400, detail="All parts not in dir.") raise HTTPException(status_code=400, detail="All parts not in dir.")
else: else:
print(part + ' is in dir ' + path) print(part + " is in dir " + path)
# merge parts to a new temporary document # merge parts to a new temporary document
temp_doc_path = path temp_doc_path = path
@@ -304,14 +318,14 @@ def upload_completion(upload_session: str,
if not os.path.exists(temp_doc_path): if not os.path.exists(temp_doc_path):
os.makedirs(temp_doc_path) os.makedirs(temp_doc_path)
new_doc_path = './data/documents/' new_doc_path = "./data/documents/"
new_doc_path_name = new_doc_path + document.file_description.name new_doc_path_name = new_doc_path + document.file_description.name
# Read parts and write to temp doc. # Read parts and write to temp doc.
with open(temp_doc_file_name, 'ab') as temp_doc: with open(temp_doc_file_name, "ab") as temp_doc:
for part in parts: for part in parts:
part = doc_db.safe_path(part) part = doc_db.safe_path(part)
with open(temp_doc_path + part, 'rb') as part_doc: with open(temp_doc_path + part, "rb") as part_doc:
temp_doc.write(part_doc.read()) temp_doc.write(part_doc.read())
# move document to new location in documents dir # move document to new location in documents dir
@@ -329,13 +343,12 @@ def upload_completion(upload_session: str,
# get DocumentVersion # get DocumentVersion
document = doc_db.get_document_version(document.document_id, document.version_index, current_user) document = doc_db.get_document_version(document.document_id, document.version_index, current_user)
doc_db.debug(endpoint='upload_completion', doc_db.debug(endpoint="upload_completion", request={"upload_session": upload_session}, response=document.dict())
request={'upload_session': upload_session},
response=document.dict())
return document return document
else: else:
return False return False
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# summary: 'Cancel the upload of a single file' # summary: 'Cancel the upload of a single file'
# #
@@ -355,14 +368,14 @@ def upload_cancellation(upload_session: str, current_user: User = Depends(get_cu
# clean temp dir # clean temp dir
document_name = doc_db.safe_path(document.document_id) document_name = doc_db.safe_path(document.document_id)
path = './data/document_parts/' + document_name + '/' path = "./data/document_parts/" + document_name + "/"
shutil.rmtree(path) shutil.rmtree(path)
except Exception as e: except Exception as e:
print(e) print(e)
finally: finally:
print('Upload cancellation complete.') print("Upload cancellation complete.")
return return
@@ -399,17 +412,17 @@ def upload_cancellation(upload_session: str, current_user: User = Depends(get_cu
# that has been flagged to have a new version in the response. # that has been flagged to have a new version in the response.
# #
@router.post("/documents/1.0/document-versions", tags=[""]) @router.post("/documents/1.0/document-versions", tags=[""])
def document_versions_post(document_ids: List[UUID], def document_versions_post(
current_user: User = Depends(get_current_active_user)) -> List[DocumentVersion]: document_ids: List[UUID], current_user: User = Depends(get_current_active_user)
) -> List[DocumentVersion]:
document_versions = list() document_versions = list()
for document_id in document_ids: for document_id in document_ids:
document_versions.append(doc_db.get_document_version(document_id, 1, current_user)) document_versions.append(doc_db.get_document_version(document_id, 1, current_user))
doc_db.debug(endpoint='document_versions_post', doc_db.debug(endpoint="document_versions_post", request={document_ids}, response={document_versions})
request={document_ids},
response={document_versions})
return document_versions return document_versions
@@ -460,12 +473,15 @@ def document_versions_post(document_ids: List[UUID],
@router.post("/documents/1.0/select-documents", tags=[""]) @router.post("/documents/1.0/select-documents", tags=[""])
def select_documents_post(select_documents: SelectDocuments, def select_documents_post(
current_user: User = Depends(get_current_active_user)) -> DocumentDiscoverySessionInitialization: select_documents: SelectDocuments, current_user: User = Depends(get_current_active_user)
) -> DocumentDiscoverySessionInitialization:
post_select_documents_response = doc_db.post_select_documents(select_documents, current_user) post_select_documents_response = doc_db.post_select_documents(select_documents, current_user)
doc_db.debug(endpoint='select_documents_post', doc_db.debug(
request={'select_documents': select_documents}, endpoint="select_documents_post",
response=post_select_documents_response.dict()) request={"select_documents": select_documents},
response=post_select_documents_response.dict(),
)
print("Returns ", post_select_documents_response) print("Returns ", post_select_documents_response)
return post_select_documents_response return post_select_documents_response
@@ -490,19 +506,22 @@ def select_documents_post(select_documents: SelectDocuments,
# documents/1.0/document-selection?selection_session=7cf3dd70-c880-4fb1-9897-f60472959533 # documents/1.0/document-selection?selection_session=7cf3dd70-c880-4fb1-9897-f60472959533
@router.get("/documents/1.0/document-selection", tags=[""], response_class=HTMLResponse) @router.get("/documents/1.0/document-selection", tags=[""], response_class=HTMLResponse)
def selected_documents_get(request: Request, def selected_documents_get(request: Request, selection_session: UUID):
selection_session: UUID):
data_for_document_selection = doc_db.get_data_for_document_selection(selection_session) data_for_document_selection = doc_db.get_data_for_document_selection(selection_session)
return templates.TemplateResponse( return templates.TemplateResponse(
'select_files.html', "select_files.html",
{'request': request, {
'selection_session': selection_session, "request": request,
'current_user': data_for_document_selection.current_user, "selection_session": selection_session,
'server_context': data_for_document_selection.server_context, "current_user": data_for_document_selection.current_user,
'callback_url': data_for_document_selection.callback.url, "server_context": data_for_document_selection.server_context,
'callback_expires_in': data_for_document_selection.callback.expires_in, "callback_url": data_for_document_selection.callback.url,
'projects': data_for_document_selection.projects}) "callback_expires_in": data_for_document_selection.callback.expires_in,
"projects": data_for_document_selection.projects,
},
)
@router.post("/documents/1.0/mark-documents-as-selected", tags=[""]) @router.post("/documents/1.0/mark-documents-as-selected", tags=[""])
@@ -516,45 +535,52 @@ async def mark_documents_as_selected_post(request: Request) -> DocumentsMarkedAs
documents = list() documents = list()
for key, value in form_data_json.items(): for key, value in form_data_json.items():
if 'document_' in key: if "document_" in key:
document_id = key.split("ocument_", 1)[1] document_id = key.split("ocument_", 1)[1]
documents.append(document_id) documents.append(document_id)
print("Sends ", documents) print("Sends ", documents)
get_selected_response = doc_db.post_mark_documents_as_selected(documents, form_data_json['selection_session']) get_selected_response = doc_db.post_mark_documents_as_selected(documents, form_data_json["selection_session"])
doc_db.debug(endpoint='mark_some_documents_as_selected_post', doc_db.debug(
request={'documents': documents, endpoint="mark_some_documents_as_selected_post",
'form_data_json[selection_session]': form_data_json['selection_session']}, request={"documents": documents, "form_data_json[selection_session]": form_data_json["selection_session"]},
response=get_selected_response.dict()) response=get_selected_response.dict(),
)
return get_selected_response return get_selected_response
@router.get("/documents/1.0/download-instructions", tags=[""]) @router.get("/documents/1.0/download-instructions", tags=[""])
def download_instructions(session_id: UUID, server_context: str, def download_instructions(
current_user: User = Depends(get_current_active_user)) -> SelectedDocuments: session_id: UUID, server_context: str, current_user: User = Depends(get_current_active_user)
) -> SelectedDocuments:
get_download_instructions_response = doc_db.get_download_instructions(session_id, server_context, current_user) get_download_instructions_response = doc_db.get_download_instructions(session_id, server_context, current_user)
doc_db.debug(endpoint='download_instructions', doc_db.debug(
request={'session_id': session_id, endpoint="download_instructions",
'server_context': server_context}, request={"session_id": session_id, "server_context": server_context},
response=get_download_instructions_response.dict()) response=get_download_instructions_response.dict(),
)
return get_download_instructions_response return get_download_instructions_response
# download links # download links
@router.get("/documents/1.0/document/{document_id}/version/{version_index}", tags=[""]) @router.get("/documents/1.0/document/{document_id}/version/{version_index}", tags=[""])
def document_version(document_id: str, version_index: int, def document_version(
current_user: User = Depends(get_current_active_user)) -> DocumentVersion: document_id: str, version_index: int, current_user: User = Depends(get_current_active_user)
) -> DocumentVersion:
# This endpoint returns the document version model itself. # This endpoint returns the document version model itself.
get_document_version = doc_db.get_document_version(document_id, version_index, current_user) get_document_version = doc_db.get_document_version(document_id, version_index, current_user)
doc_db.debug(endpoint='document_version', doc_db.debug(
request={'document_id': document_id, endpoint="document_version",
'version_index': version_index}, request={"document_id": document_id, "version_index": version_index},
response=get_document_version.dict()) response=get_document_version.dict(),
)
return get_document_version return get_document_version
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# summary: 'Get document metadata for a single document' # summary: 'Get document metadata for a single document'
# #
@@ -573,16 +599,21 @@ def document_version(document_id: str, version_index: int,
@router.get("/documents/1.0/document/{document_id}/version/{version_index}/metadata", tags=[""]) @router.get("/documents/1.0/document/{document_id}/version/{version_index}/metadata", tags=[""])
def document_version_metadata(document_id: str, version_index: int, def document_version_metadata(
current_user: User = Depends(get_current_active_user)) -> DocumentMetadataEntries: document_id: str, version_index: int, current_user: User = Depends(get_current_active_user)
) -> DocumentMetadataEntries:
# The metadata for document versions is a list of key-value pairs # The metadata for document versions is a list of key-value pairs
get_document_version_metadata_result = doc_db.get_document_version_metadata(document_id, version_index, current_user) get_document_version_metadata_result = doc_db.get_document_version_metadata(
doc_db.debug(endpoint='document_version', document_id, version_index, current_user
request={'document_id': document_id, )
'version_index': version_index}, doc_db.debug(
response=get_document_version_metadata_result.dict()) endpoint="document_version",
request={"document_id": document_id, "version_index": version_index},
response=get_document_version_metadata_result.dict(),
)
return get_document_version_metadata_result return get_document_version_metadata_result
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# summary: 'Download the document' # summary: 'Download the document'
# #
@@ -601,47 +632,48 @@ def document_version_metadata(document_id: str, version_index: int,
@router.get("/documents/1.0/document/{document_id}/version/{version_index}/download", tags=[""]) @router.get("/documents/1.0/document/{document_id}/version/{version_index}/download", tags=[""])
def document_version_download(document_id: str, version_index: int, def document_version_download(
current_user: User = Depends(get_current_active_user)) -> FileResponse: document_id: str, version_index: int, current_user: User = Depends(get_current_active_user)
) -> FileResponse:
# The url to download the binary content of this document version. # The url to download the binary content of this document version.
# May either directly return the result or redirect to a storage provider # May either directly return the result or redirect to a storage provider
keep_characters = (' ', '.', '_', '-') keep_characters = (" ", ".", "_", "-")
document_id = "".join(c for c in document_id if c.isalnum() or c in keep_characters).rstrip() document_id = "".join(c for c in document_id if c.isalnum() or c in keep_characters).rstrip()
file_location = './data/documents/' + document_id + '.ifc' file_location = "./data/documents/" + document_id + ".ifc"
return FileResponse(file_location, return FileResponse(
media_type='application/x-step', file_location, media_type="application/x-step", filename="6dbd4d52-14db-11ee-be56-0242ac120002.ifc"
filename='6dbd4d52-14db-11ee-be56-0242ac120002.ifc') )
@router.get("/documents/1.0/document/{document_id}/version/{version_index}/versions", tags=[""]) @router.get("/documents/1.0/document/{document_id}/version/{version_index}/versions", tags=[""])
def document_versions(document_id: str, version_index: int, def document_versions(
current_user: User = Depends(get_current_active_user)) -> DocumentVersions: document_id: str, version_index: int, current_user: User = Depends(get_current_active_user)
) -> DocumentVersions:
# This url returns a list of all document versions for the parent document. # This url returns a list of all document versions for the parent document.
# The client can use this URL to monitor for new document versions # The client can use this URL to monitor for new document versions
get_document_versions_result = doc_db.get_document_versions(document_id, current_user) get_document_versions_result = doc_db.get_document_versions(document_id, current_user)
doc_db.debug(endpoint='document_version', doc_db.debug(
request={'document_id': document_id}, endpoint="document_version", request={"document_id": document_id}, response=get_document_versions_result.dict()
response=get_document_versions_result.dict()) )
return get_document_versions_result return get_document_versions_result
@router.get("/documents/1.0/document/{document_id}/version/{version_index}/details", tags=[""], @router.get(
response_class=HTMLResponse) "/documents/1.0/document/{document_id}/version/{version_index}/details", tags=[""], response_class=HTMLResponse
def document_version_details(request: Request, )
document_id: str, def document_version_details(
version_index: int, request: Request, document_id: str, version_index: int, current_user: User = Depends(get_current_active_user)
current_user: User = Depends(get_current_active_user)): ):
# This url returns a list of all document versions for the parent document. # This url returns a list of all document versions for the parent document.
# The client can use this URL to monitor for new document versions # The client can use this URL to monitor for new document versions
details = doc_db.get_document_version(document_id, version_index, current_user) details = doc_db.get_document_version(document_id, version_index, current_user)
doc_db.debug(endpoint='document_version', doc_db.debug(
request={'document_id': document_id, endpoint="document_version",
'version_index': version_index}, request={"document_id": document_id, "version_index": version_index},
response=details.dict()) response=details.dict(),
return templates.TemplateResponse( )
'document_details.html', return templates.TemplateResponse("document_details.html", {"request": request, "details": details})
{'request': request,
'details': details})
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# summary: 'Get the versions of a single document' # summary: 'Get the versions of a single document'
@@ -699,7 +731,9 @@ def document_version_details(request: Request,
@router.post("/documents/1.0/upload_file_to_project", tags=[""]) @router.post("/documents/1.0/upload_file_to_project", tags=[""])
async def upload_documents_post(file: UploadFile, project: str = Form(...), selection_session: str = Form(...)) -> Document: async def upload_documents_post(
file: UploadFile, project: str = Form(...), selection_session: str = Form(...)
) -> Document:
# Get the file size (in bytes) # Get the file size (in bytes)
file.file.seek(0, 2) file.file.seek(0, 2)
@@ -713,49 +747,46 @@ async def upload_documents_post(file: UploadFile, project: str = Form(...), sele
# Find file name ending and create new storage file name # Find file name ending and create new storage file name
if file.filename.lower().endswith(tuple(file_types)): if file.filename.lower().endswith(tuple(file_types)):
file_ending = file.filename.split('.')[-1].lower() file_ending = file.filename.split(".")[-1].lower()
name = document_id + '.' + file_ending name = document_id + "." + file_ending
else: else:
file_ending = '' file_ending = ""
name = document_id name = document_id
# Get mime type and file type # Get mime type and file type
mime_type = '' mime_type = ""
file_type = '' file_type = ""
if hasattr(file_types, file_ending): if hasattr(file_types, file_ending):
mime_type = file_types[file_ending]['mime_type'] mime_type = file_types[file_ending]["mime_type"]
file_type = file_types[file_ending]['file_type'] file_type = file_types[file_ending]["file_type"]
# Create document data # Create document data
document_version_dict = { document_version_dict = {
'document_id': document_id, "document_id": document_id,
'session_file_id': '', "session_file_id": "",
'version_index': 1, "version_index": 1,
'version_number': '1', "version_number": "1",
'creation_date': doc_db.timestamp(), "creation_date": doc_db.timestamp(),
'title': file.filename, "title": file.filename,
'original_file_name': file.filename, "original_file_name": file.filename,
'file_ending': file_ending, "file_ending": file_ending,
'mime_type': mime_type, "mime_type": mime_type,
'file_type': file_type, "file_type": file_type,
'project': project, "project": project,
'file_description': { "file_description": {"name": name, "size_in_bytes": file_size},
'name': name,
'size_in_bytes': file_size
}
} }
document_version_model = Document(**document_version_dict) document_version_model = Document(**document_version_dict)
# Save file to disc # Save file to disc
upload_directory = './data/documents/' upload_directory = "./data/documents/"
destination_path = os.path.join(upload_directory, name) destination_path = os.path.join(upload_directory, name)
with open(destination_path, 'wb') as buffer: with open(destination_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer) shutil.copyfileobj(file.file, buffer)
# create database record # create database record
inserted_document = doc_db.create_node_for_uploaded_file(selection_session, project, document_version_model) inserted_document = doc_db.create_node_for_uploaded_file(selection_session, project, document_version_model)
print('Created node for document id: ' + str(inserted_document.document_id)) print("Created node for document id: " + str(inserted_document.document_id))
doc_db.create_ifc_graph_for_document(inserted_document.document_id) doc_db.create_ifc_graph_for_document(inserted_document.document_id)
# return document version of database record # return document version of database record
+81 -77
View File
@@ -28,11 +28,10 @@ authorization_code = None
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
clients = { clients = {
os.environ['KONTROLL_CLIENT_ID']: os.environ["KONTROLL_CLIENT_ID"]: {
{ "name": os.environ["KONTROLL_CLIENT_NAME"],
'name': os.environ['KONTROLL_CLIENT_NAME'], "secret": secrets["kontroll_client_secret"],
'secret': secrets['kontroll_client_secret'] }
}
} }
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- # ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
@@ -60,21 +59,25 @@ clients = {
@router.get("/foundation/versions", tags=["api_versions_get"]) @router.get("/foundation/versions", tags=["api_versions_get"])
def api_versions_get(): def api_versions_get():
return { return {
"versions": [{ "versions": [
"api_id": "foundation", {
"version_id": "1.0", "api_id": "foundation",
"detailed_version": "https://github.com/BuildingSMART/foundation-API/tree/release_1_0" "version_id": "1.0",
}, { "detailed_version": "https://github.com/BuildingSMART/foundation-API/tree/release_1_0",
"api_id": "bcf", },
"version_id": "3.0", {
"detailed_version": "https://github.com/buildingSMART/BCF-API/tree/release_3_0", "api_id": "bcf",
"api_base_url": os.environ['KONTROLL_BASE_URL'] + "bcf/3.0" "version_id": "3.0",
}, { "detailed_version": "https://github.com/buildingSMART/BCF-API/tree/release_3_0",
"api_id": "documents", "api_base_url": os.environ["KONTROLL_BASE_URL"] + "bcf/3.0",
"version_id": "1.0", },
"detailed_version": "https://github.com/buildingSMART/documents-API/tree/release_1_0", {
"api_base_url": os.environ['KONTROLL_BASE_URL'] + "documents/1.0" "api_id": "documents",
}] "version_id": "1.0",
"detailed_version": "https://github.com/buildingSMART/documents-API/tree/release_1_0",
"api_base_url": os.environ["KONTROLL_BASE_URL"] + "documents/1.0",
},
]
} }
@@ -95,18 +98,15 @@ def api_versions_get():
# is not supported by the server. # is not supported by the server.
@router.get("/foundation/1.0/auth", @router.get("/foundation/1.0/auth", tags=["foundation_auth_get"])
tags=["foundation_auth_get"])
def authentication_get(): def authentication_get():
return_variable = { return_variable = {
"oauth2_auth_url": os.environ['KONTROLL_BASE_URL'] + "foundation/oauth2/auth", "oauth2_auth_url": os.environ["KONTROLL_BASE_URL"] + "foundation/oauth2/auth",
"oauth2_token_url": os.environ['KONTROLL_BASE_URL'] + "foundation/oauth2/token", "oauth2_token_url": os.environ["KONTROLL_BASE_URL"] + "foundation/oauth2/token",
# "oauth2_dynamic_client_reg_url": os.environ['KONTROLL_BASE_URL'] + "foundation/oauth2/reg", # "oauth2_dynamic_client_reg_url": os.environ['KONTROLL_BASE_URL'] + "foundation/oauth2/reg",
"http_basic_supported": True, "http_basic_supported": True,
"supported_oauth2_flows": [ "supported_oauth2_flows": ["authorization_code_grant"],
"authorization_code_grant"
]
} }
print(return_variable) print(return_variable)
return return_variable return return_variable
@@ -135,23 +135,25 @@ def authentication_get():
@router.get("/foundation/oauth2/auth", response_class=HTMLResponse) @router.get("/foundation/oauth2/auth", response_class=HTMLResponse)
def authorization(request: Request, def authorization(
response_type: str, request: Request,
client_id: str, response_type: str,
state: str, client_id: str,
scope: str, state: str,
redirect_uri: str, scope: str,
): redirect_uri: str,
):
client_name = clients[client_id]['name'] client_name = clients[client_id]["name"]
print(f"Response type: {response_type}, " print(
f"Client_id: {client_id}, " f"Response type: {response_type}, "
f"Client_name: {client_name}, " f"Client_id: {client_id}, "
f"State: {state}, " f"Client_name: {client_name}, "
f"Scope: {scope}," f"State: {state}, "
f"Redirect_URI: {redirect_uri}." f"Scope: {scope},"
) f"Redirect_URI: {redirect_uri}."
)
# 3. Solibri sends the user to oauth2_auth_url with the following parameters: # 3. Solibri sends the user to oauth2_auth_url with the following parameters:
# response_type=code, client_id=solibri_test_001, state=..., redirect_uri=uri, scope=... # response_type=code, client_id=solibri_test_001, state=..., redirect_uri=uri, scope=...
@@ -159,38 +161,41 @@ def authorization(request: Request,
return templates.TemplateResponse( return templates.TemplateResponse(
"login.html", "login.html",
{"request": request, {
"response_type": response_type, "request": request,
"client_id": client_id, "response_type": response_type,
"client_name": client_name, "client_id": client_id,
"state": state, "client_name": client_name,
"scope": scope, "state": state,
"redirect_uri": redirect_uri, "scope": scope,
}) "redirect_uri": redirect_uri,
},
)
@router.get("/foundation/oauth2/code") @router.get("/foundation/oauth2/code")
def code(username: str, def code(
password: str, username: str,
response_type: str, password: str,
client_id, response_type: str,
client_name, client_id,
state: str, client_name,
redirect_uri: str, state: str,
scope: str = '' redirect_uri: str,
): scope: str = "",
):
global oauth2_state global oauth2_state
oauth2_state = state oauth2_state = state
print('Username: ' + username + '. Password: ' + password) print("Username: " + username + ". Password: " + password)
user = authenticate_user(username, password) user = authenticate_user(username, password)
if not user: if not user:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password", detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"} headers={"WWW-Authenticate": "Bearer"},
) )
# The user is signed in, now the main purpose of this function is to generate the authorization code. # The user is signed in, now the main purpose of this function is to generate the authorization code.
@@ -242,36 +247,35 @@ def code(username: str,
# POST https://example.com/foundation/oauth2/token?grant_type=authorization_code&code=<your_authorization_code> # POST https://example.com/foundation/oauth2/token?grant_type=authorization_code&code=<your_authorization_code>
@router.post("/foundation/oauth2/token", @router.post("/foundation/oauth2/token", tags=["login_for_access_token_post"], status_code=201)
tags=["login_for_access_token_post"],
status_code=201)
def login_for_access_token( def login_for_access_token(
grant_type: Optional[str] = Form(None), grant_type: Optional[str] = Form(None),
refresh_token: Optional[str] = Form(None), refresh_token: Optional[str] = Form(None),
code: Optional[str] = Form(None), code: Optional[str] = Form(None),
credentials: HTTPBasicCredentials = Depends(http_basic)): credentials: HTTPBasicCredentials = Depends(http_basic),
):
print('grant_type: ', grant_type) print("grant_type: ", grant_type)
print('refresh_token: ', refresh_token) print("refresh_token: ", refresh_token)
print('code: ', code) print("code: ", code)
print('credentials: ', credentials) print("credentials: ", credentials)
# The API should check that the credentials (client_id and client_secret) are correct # The API should check that the credentials (client_id and client_secret) are correct
# credentials.username contains the client_id # credentials.username contains the client_id
# credentials.password contains the client_secret # credentials.password contains the client_secret
if credentials.username not in clients or credentials.password != clients[credentials.username]['secret']: if credentials.username not in clients or credentials.password != clients[credentials.username]["secret"]:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect client_id or client_secret", detail="Incorrect client_id or client_secret",
) )
if grant_type == 'authorization_code': if grant_type == "authorization_code":
# use authorization code, # use authorization code,
# create access token and refresh token, # create access token and refresh token,
# delete authorization code # delete authorization code
user_info = foundation_db.use_authorization_code(code) user_info = foundation_db.use_authorization_code(code)
elif grant_type == 'refresh_token': elif grant_type == "refresh_token":
# use refresh token to get access token # use refresh token to get access token
# delete old access token and old refresh token # delete old access token and old refresh token
# create new access token and a new refresh token # create new access token and a new refresh token
@@ -279,8 +283,8 @@ def login_for_access_token(
user_info = foundation_db.use_refresh_token(refresh_token) user_info = foundation_db.use_refresh_token(refresh_token)
user_info.token_type = "Bearer" user_info.token_type = "Bearer"
user_info.expires_in = int(os.environ['SECURITY_ACCESS_TOKEN_EXPIRE_SECONDS']) user_info.expires_in = int(os.environ["SECURITY_ACCESS_TOKEN_EXPIRE_SECONDS"])
print('user_info: ', user_info) print("user_info: ", user_info)
return user_info return user_info
+13 -8
View File
@@ -9,8 +9,8 @@ import httpx
def log_info(req_body, res_body, route_url): def log_info(req_body, res_body, route_url):
logging.info('request:' + route_url + ':' + str(req_body)) logging.info("request:" + route_url + ":" + str(req_body))
logging.info('response:' + route_url + ':' + str(res_body)) logging.info("response:" + route_url + ":" + str(res_body))
class LoggingRoute(APIRoute): class LoggingRoute(APIRoute):
@@ -22,21 +22,26 @@ class LoggingRoute(APIRoute):
response = await original_route_handler(request) response = await original_route_handler(request)
route_url = str(request.url) route_url = str(request.url)
if isinstance(response, StreamingResponse): if isinstance(response, StreamingResponse):
res_body = b'' res_body = b""
async for item in response.body_iterator: async for item in response.body_iterator:
res_body += item res_body += item
task = BackgroundTask(log_info, req_body, res_body, route_url) task = BackgroundTask(log_info, req_body, res_body, route_url)
return Response(content=res_body, status_code=response.status_code, return Response(
headers=dict(response.headers), media_type=response.media_type, background=task) content=res_body,
status_code=response.status_code,
headers=dict(response.headers),
media_type=response.media_type,
background=task,
)
else: else:
if hasattr(response, 'body'): if hasattr(response, "body"):
res_body = response.body res_body = response.body
else: else:
res_body = {'no response': True} res_body = {"no response": True}
response.background = BackgroundTask(log_info, req_body, res_body, route_url) response.background = BackgroundTask(log_info, req_body, res_body, route_url)
return response return response
return custom_route_handler return custom_route_handler
logging.basicConfig(filename='logs/info.log', level=logging.DEBUG) logging.basicConfig(filename="logs/info.log", level=logging.DEBUG)
+54 -48
View File
@@ -1,10 +1,9 @@
import collections import collections
import os import os
import traceback import traceback
import sys import sys
from fastapi import APIRouter, Request, Depends from fastapi import APIRouter, Request, Depends
from fastapi.responses import FileResponse, HTMLResponse from fastapi.responses import FileResponse, HTMLResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
@@ -30,13 +29,17 @@ templates = Jinja2Templates(directory="templates")
# UPLOAD FLOW # UPLOAD FLOW
################################################################ ################################################################
@router.post("/user/1.0/upload-documents", tags=[""]) @router.post("/user/1.0/upload-documents", tags=[""])
def upload_documents_post(upload_documents: UploadDocuments, def upload_documents_post(
current_user: User = Depends(get_current_active_user)) -> DocumentUploadSessionInitialization: upload_documents: UploadDocuments, current_user: User = Depends(get_current_active_user)
) -> DocumentUploadSessionInitialization:
post_upload_documents_response = doc_db.post_upload_documents(upload_documents, current_user) post_upload_documents_response = doc_db.post_upload_documents(upload_documents, current_user)
doc_db.debug(endpoint='upload_documents_post', doc_db.debug(
request={'upload_documents': upload_documents}, endpoint="upload_documents_post",
response=post_upload_documents_response.dict()) request={"upload_documents": upload_documents},
response=post_upload_documents_response.dict(),
)
return post_upload_documents_response return post_upload_documents_response
@@ -45,20 +48,23 @@ def upload_documents_post(upload_documents: UploadDocuments,
def upload_documents_get(request: Request, upload_session: UUID): def upload_documents_get(request: Request, upload_session: UUID):
data_for_upload_documents = doc_db.get_data_for_upload_documents(upload_session) data_for_upload_documents = doc_db.get_data_for_upload_documents(upload_session)
print('Data for site: ', data_for_upload_documents) print("Data for site: ", data_for_upload_documents)
return templates.TemplateResponse( return templates.TemplateResponse(
'upload_files.html', "upload_files.html",
{'request': request, {
'upload_session': upload_session, "request": request,
'username': data_for_upload_documents.current_user.username, "upload_session": upload_session,
'email': data_for_upload_documents.current_user.email, "username": data_for_upload_documents.current_user.username,
'full_name': data_for_upload_documents.current_user.full_name, "email": data_for_upload_documents.current_user.email,
'server_context': data_for_upload_documents.server_context, "full_name": data_for_upload_documents.current_user.full_name,
'callback_url': data_for_upload_documents.callback.url, "server_context": data_for_upload_documents.server_context,
'callback_expires_in': data_for_upload_documents.callback.expires_in, "callback_url": data_for_upload_documents.callback.url,
'documents': data_for_upload_documents.documents, "callback_expires_in": data_for_upload_documents.callback.expires_in,
'projects': data_for_upload_documents.projects}) "documents": data_for_upload_documents.documents,
"projects": data_for_upload_documents.projects,
},
)
@router.post("/user/1.0/save-metadata-for-documents", tags=[""]) @router.post("/user/1.0/save-metadata-for-documents", tags=[""])
@@ -71,29 +77,29 @@ async def save_metadata_for_documents_post(request: Request) -> list:
print(form_data_json) print(form_data_json)
documents = collections.defaultdict(dict) documents = collections.defaultdict(dict)
names = ('session_file_id', 'document', 'title', 'version_number', 'filename') names = ("session_file_id", "document", "title", "version_number", "filename")
for whole_form_key, value in form_data_json.items(): for whole_form_key, value in form_data_json.items():
if whole_form_key.startswith(names): if whole_form_key.startswith(names):
start_form_key, document_id = whole_form_key.split("@", 1) start_form_key, document_id = whole_form_key.split("@", 1)
print('New field: ', start_form_key, ' for document id: ', document_id) print("New field: ", start_form_key, " for document id: ", document_id)
documents[document_id][start_form_key] = value documents[document_id][start_form_key] = value
print("Documents: ") print("Documents: ")
print(documents) print(documents)
username = form_data_json['username'] username = form_data_json["username"]
upload_session = form_data_json['upload_session'] upload_session = form_data_json["upload_session"]
server_context = form_data_json['server_context'] server_context = form_data_json["server_context"]
callback_url = form_data_json['callback_url'] callback_url = form_data_json["callback_url"]
callback_expires_in = form_data_json['callback_expires_in'] callback_expires_in = form_data_json["callback_expires_in"]
project = form_data_json['project'] project = form_data_json["project"]
documents_saved = list() documents_saved = list()
for key in documents: for key in documents:
try: try:
documents[key]['project'] = project documents[key]["project"] = project
document = DocumentMetadata(**documents[key]) document = DocumentMetadata(**documents[key])
save_metadata_response = doc_db.save_metadata_for_documents(document, upload_session, username) save_metadata_response = doc_db.save_metadata_for_documents(document, upload_session, username)
documents_saved.append(save_metadata_response) documents_saved.append(save_metadata_response)
@@ -101,9 +107,11 @@ async def save_metadata_for_documents_post(request: Request) -> list:
print(e) print(e)
continue continue
doc_db.debug(endpoint='save_metadata_for_documents_post', doc_db.debug(
request={'documents': documents}, endpoint="save_metadata_for_documents_post",
response={'response': documents_saved}) request={"documents": documents},
response={"response": documents_saved},
)
return documents_saved return documents_saved
@@ -113,8 +121,7 @@ async def save_metadata_for_documents_post(request: Request) -> list:
@router.post("/user/1.0/upload-part/{part_id}", tags=[""]) @router.post("/user/1.0/upload-part/{part_id}", tags=[""])
async def upload_part(part_id: str, request: Request, async def upload_part(part_id: str, request: Request, current_user: User = Depends(get_current_active_user)):
current_user: User = Depends(get_current_active_user)):
# file_name = doc_db.safe_path(part_id) # file_name = doc_db.safe_path(part_id)
file_name = part_id file_name = part_id
@@ -129,33 +136,31 @@ async def upload_part(part_id: str, request: Request,
# try to receive the uploaded part # try to receive the uploaded part
try: try:
print('File contents: ', request_body) print("File contents: ", request_body)
# use document_id instead as dir_name # use document_id instead as dir_name
# dir_name = doc_db.safe_path(document.document_id) # dir_name = doc_db.safe_path(document.document_id)
dir_name = document.document_id dir_name = document.document_id
path = './data/document_parts/' + dir_name + '/' path = "./data/document_parts/" + dir_name + "/"
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
with open(path + file_name, 'wb') as f: with open(path + file_name, "wb") as f:
f.write(request_body) f.write(request_body)
except Exception: except Exception:
print('Error uploading file') print("Error uploading file")
print(traceback.format_exc()) print(traceback.format_exc())
print('Error uploading file') print("Error uploading file")
print(sys.exc_info()[2]) print(sys.exc_info()[2])
finally: finally:
# We will write to the database, information about part successfully uploaded. # We will write to the database, information about part successfully uploaded.
doc_db.mark_part_as_uploaded(part_id, current_user) doc_db.mark_part_as_uploaded(part_id, current_user)
doc_db.debug(endpoint='upload-part', doc_db.debug(endpoint="upload-part", request={"part_id": part_id}, response={"uploaded": True})
request={'part_id': part_id},
response={'uploaded': True})
return {"message": f"Successfully uploaded part {file_name}"} return {"message": f"Successfully uploaded part {file_name}"}
@@ -166,13 +171,14 @@ async def upload_part(part_id: str, request: Request,
@router.get("/user/1.0/document/{document_id}/version/{version_index}/download", tags=[""]) @router.get("/user/1.0/document/{document_id}/version/{version_index}/download", tags=[""])
def document_version_download(document_id: str, version_index: int, def document_version_download(
current_user: User = Depends(get_current_active_user)) -> FileResponse: document_id: str, version_index: int, current_user: User = Depends(get_current_active_user)
) -> FileResponse:
# The url to download the binary content of this document version. # The url to download the binary content of this document version.
# May either directly return the result or redirect to a storage provider # May either directly return the result or redirect to a storage provider
keep_characters = (' ', '.', '_', '-') keep_characters = (" ", ".", "_", "-")
document_id = "".join(c for c in document_id if c.isalnum() or c in keep_characters).rstrip() document_id = "".join(c for c in document_id if c.isalnum() or c in keep_characters).rstrip()
file_location = './data/documents/' + document_id + '.ifc' file_location = "./data/documents/" + document_id + ".ifc"
return FileResponse(file_location, return FileResponse(
media_type='application/x-step', file_location, media_type="application/x-step", filename="6dbd4d52-14db-11ee-be56-0242ac120002.ifc"
filename='6dbd4d52-14db-11ee-be56-0242ac120002.ifc') )
+20 -12
View File
@@ -18,9 +18,9 @@ get_secrets()
# otherwise the environment variable will have a value of neo4j://kontroll_neo4j:27687 # otherwise the environment variable will have a value of neo4j://kontroll_neo4j:27687
# kontroll_neo4j is the docker-compose network. # kontroll_neo4j is the docker-compose network.
driver = GraphDatabase.driver(os.environ['NEO4J_URI'], driver = GraphDatabase.driver(
auth=(os.environ['NEO4J_USER'], os.environ["NEO4J_URI"], auth=(os.environ["NEO4J_USER"], os.environ["NEO4J_INITIAL_PASSWORD"])
os.environ['NEO4J_INITIAL_PASSWORD'])) )
# initial password should be changed to secret password # initial password should be changed to secret password
@@ -33,33 +33,38 @@ class MyDB:
def __init__(self, object_driver): def __init__(self, object_driver):
self.driver = object_driver self.driver = object_driver
self.database = 'neo4j' self.database = "neo4j"
@staticmethod @staticmethod
def timestamp(): def timestamp():
return datetime.now(timezone.utc).isoformat(sep='T', timespec='milliseconds') return datetime.now(timezone.utc).isoformat(sep="T", timespec="milliseconds")
@staticmethod @staticmethod
def bcf_time(any_datetime): def bcf_time(any_datetime):
if isinstance(any_datetime, type('str')): if isinstance(any_datetime, type("str")):
datetime_any = parser.parse(any_datetime).astimezone(pytz.utc) datetime_any = parser.parse(any_datetime).astimezone(pytz.utc)
elif isinstance(any_datetime, type(datetime.now())): elif isinstance(any_datetime, type(datetime.now())):
datetime_any = any_datetime.astimezone(pytz.utc) datetime_any = any_datetime.astimezone(pytz.utc)
else: else:
return False return False
string_date = datetime_any.isoformat(sep='T', timespec='milliseconds') string_date = datetime_any.isoformat(sep="T", timespec="milliseconds")
return str(string_date) return str(string_date)
@staticmethod @staticmethod
def safe_path(path_name): def safe_path(path_name):
safe_path_name = ''.join(x for x in path_name if x.isalnum() or '-') safe_path_name = "".join(x for x in path_name if x.isalnum() or "-")
return safe_path_name return safe_path_name
@staticmethod @staticmethod
def debug(endpoint: str, request, response): def debug(endpoint: str, request, response):
print("\n\n\nEndpoint: ", jsonpickle.dumps(endpoint), print(
"\nRequest: ", jsonpickle.dumps(request), "\n\n\nEndpoint: ",
"\nResponse: ", jsonpickle.dumps(response)) jsonpickle.dumps(endpoint),
"\nRequest: ",
jsonpickle.dumps(request),
"\nResponse: ",
jsonpickle.dumps(response),
)
@staticmethod @staticmethod
def node_to_json(node): def node_to_json(node):
@@ -89,11 +94,12 @@ class MyDB:
cypher_file = open(cypher_file_path, "r") cypher_file = open(cypher_file_path, "r")
cypher_data = cypher_file.read() cypher_data = cypher_file.read()
cypher_file.close() cypher_file.close()
cypher_statements = cypher_data.split(';') cypher_statements = cypher_data.split(";")
cypher_statements.pop() cypher_statements.pop()
for cypher_statement in cypher_statements: for cypher_statement in cypher_statements:
tx.run(cypher_statement) tx.run(cypher_statement)
return return
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_write(initialize_db_work) return session.execute_write(initialize_db_work)
@@ -113,8 +119,10 @@ class MyDB:
user_dict = self.node_to_json(user_node) user_dict = self.node_to_json(user_node)
user = UserInDB(**user_dict) user = UserInDB(**user_dict)
return user return user
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(get_user_work, username_work=username) return session.execute_read(get_user_work, username_work=username)
db = MyDB(driver) db = MyDB(driver)
db.initialize_db() db.initialize_db()
+11 -11
View File
@@ -28,24 +28,24 @@ from py2neo import Graph
def create_pure_node_from_ifc_entity(ifc_entity, ifc_file, hierarchy=True): def create_pure_node_from_ifc_entity(ifc_entity, ifc_file, hierarchy=True):
node = Node() node = Node()
if ifc_entity.id() != 0: if ifc_entity.id() != 0:
node['id'] = ifc_entity.id() node["id"] = ifc_entity.id()
else: else:
node['id'] = str(uuid4()) node["id"] = str(uuid4())
node['name'] = ifc_entity.is_a() node["name"] = ifc_entity.is_a()
if hierarchy: if hierarchy:
for label in ifc_file.wrapped_data.types_with_super(): for label in ifc_file.wrapped_data.types_with_super():
if ifc_entity.is_a(label): if ifc_entity.is_a(label):
node.add_label(label) node.add_label(label)
else: else:
node.add_label(ifc_entity.is_a()) node.add_label(ifc_entity.is_a())
attributes_type = ['ENTITY INSTANCE', 'AGGREGATE OF ENTITY INSTANCE', 'DERIVED'] attributes_type = ["ENTITY INSTANCE", "AGGREGATE OF ENTITY INSTANCE", "DERIVED"]
for i in range(ifc_entity.__len__()): for i in range(ifc_entity.__len__()):
if not ifc_entity.wrapped_data.get_argument_type(i) in attributes_type: if not ifc_entity.wrapped_data.get_argument_type(i) in attributes_type:
name = ifc_entity.wrapped_data.get_argument_name(i) name = ifc_entity.wrapped_data.get_argument_name(i)
name_value = ifc_entity.wrapped_data.get_argument(i) name_value = ifc_entity.wrapped_data.get_argument(i)
node[name]= name_value node[name] = name_value
node.__primarylabel__ = 'Root' node.__primarylabel__ = "Root"
node.__primarykey__ = 'id' node.__primarykey__ = "id"
return node return node
@@ -55,14 +55,14 @@ def create_graph_from_ifc_entity_all(graph, ifc_entity, ifc_file):
graph.merge(node) graph.merge(node)
for i in range(ifc_entity.__len__()): for i in range(ifc_entity.__len__()):
if ifc_entity[i]: if ifc_entity[i]:
if ifc_entity.wrapped_data.get_argument_type(i) == 'ENTITY INSTANCE': if ifc_entity.wrapped_data.get_argument_type(i) == "ENTITY INSTANCE":
if ifc_entity[i].is_a() in ['IfcOwnerHistory'] and ifc_entity.is_a() != 'IfcProject': if ifc_entity[i].is_a() in ["IfcOwnerHistory"] and ifc_entity.is_a() != "IfcProject":
continue continue
else: else:
sub_node = create_pure_node_from_ifc_entity(ifc_entity[i], ifc_file) sub_node = create_pure_node_from_ifc_entity(ifc_entity[i], ifc_file)
REL = Relationship(node, ifc_entity.wrapped_data.get_argument_name(i), sub_node) REL = Relationship(node, ifc_entity.wrapped_data.get_argument_name(i), sub_node)
graph.merge(REL) graph.merge(REL)
elif ifc_entity.wrapped_data.get_argument_type(i) == 'AGGREGATE OF ENTITY INSTANCE': elif ifc_entity.wrapped_data.get_argument_type(i) == "AGGREGATE OF ENTITY INSTANCE":
for sub_entity in ifc_entity[i]: for sub_entity in ifc_entity[i]:
sub_node = create_pure_node_from_ifc_entity(sub_entity, ifc_file) sub_node = create_pure_node_from_ifc_entity(sub_entity, ifc_file)
REL = Relationship(node, ifc_entity.wrapped_data.get_argument_name(i), sub_node) REL = Relationship(node, ifc_entity.wrapped_data.get_argument_name(i), sub_node)
@@ -83,7 +83,7 @@ def create_full_graph(graph, ifc_file):
length = len(ifc_file.wrapped_data.entity_names()) length = len(ifc_file.wrapped_data.entity_names())
for entity_id in ifc_file.wrapped_data.entity_names(): for entity_id in ifc_file.wrapped_data.entity_names():
entity = ifc_file.by_id(entity_id) entity = ifc_file.by_id(entity_id)
print(idx, '/', length, entity) print(idx, "/", length, entity)
create_graph_from_ifc_entity_all(graph, entity, ifc_file) create_graph_from_ifc_entity_all(graph, entity, ifc_file)
idx += 1 idx += 1
return return
+89 -60
View File
@@ -13,79 +13,110 @@ endpoint_metadata = [
{"name": "authentication_get", "description": "/authentication"}, {"name": "authentication_get", "description": "/authentication"},
{"name": "login_for_access_token_post", "description": "/foundation/oauth2/token"}, {"name": "login_for_access_token_post", "description": "/foundation/oauth2/token"},
{"name": "current_user_get", "description": "/foundation/1.0/current-user"}, {"name": "current_user_get", "description": "/foundation/1.0/current-user"},
{"name": "projects_get", {
"description": "Retrieve a collection of projects that the currently logged on user has access to."}, "name": "projects_get",
{"name": "project_get", "description": "Retrieve a collection of projects that the currently logged on user has access to.",
"description": "Retrieve a specific project. The top level data container is known as the BCF project, " },
"with a UUID and a project name attribute."}, {
{"name": "project_put", "name": "project_get",
"description": "Modify a specific project. This operation is only possible when the server returns the update " "description": "Retrieve a specific project. The top level data container is known as the BCF project, "
"flag in the Project authorization."}, "with a UUID and a project name attribute.",
{"name": "project_extensions_get", },
"description": "Retrieve a specific projects extensions. Project extensions are used to define possible values " {
"that can be used in topics and comments, for example topic labels and priorities. They may " "name": "project_put",
"change during the course of a project. The most recent extensions state which values are valid " "description": "Modify a specific project. This operation is only possible when the server returns the update "
"at a given moment for newly created topics and comments."}, "flag in the Project authorization.",
{"name": "topics_get", },
"description": "Retrieve a collection of topics related to a project (default sort order is creation_date)."}, {
{"name": "topic_post", "name": "project_extensions_get",
"description": "Add a new topic. The BCF project contains zero or more topics. Each topic represents a model " "description": "Retrieve a specific projects extensions. Project extensions are used to define possible values "
"issue. A topic will have a UUID, a title, description, priority, stage, labels (similar to " "that can be used in topics and comments, for example topic labels and priorities. They may "
"tags), creation date / author, due date, and assigned to. If modified, it may contain the " "change during the course of a project. The most recent extensions state which values are valid "
"modification date and author."}, "at a given moment for newly created topics and comments.",
},
{
"name": "topics_get",
"description": "Retrieve a collection of topics related to a project (default sort order is creation_date).",
},
{
"name": "topic_post",
"description": "Add a new topic. The BCF project contains zero or more topics. Each topic represents a model "
"issue. A topic will have a UUID, a title, description, priority, stage, labels (similar to "
"tags), creation date / author, due date, and assigned to. If modified, it may contain the "
"modification date and author.",
},
{"name": "topic_get", "description": "Retrieve a specific topic."}, {"name": "topic_get", "description": "Retrieve a specific topic."},
{"name": "topic_put", "description": "Modify a specific topic, description similar to POST."}, {"name": "topic_put", "description": "Modify a specific topic, description similar to POST."},
{"name": "bim_snippet_get", {
"description": "Retrieves a topics BIM-Snippet as binary file. BIM snippet has been in BCF specification since " "name": "bim_snippet_get",
"the very beginning, but is has never been used. Snippets have originally been added to provide " "description": "Retrieves a topics BIM-Snippet as binary file. BIM snippet has been in BCF specification since "
"for 'changes in IFC'. To get this really working in a CDE environment changes to the IFC format " "the very beginning, but is has never been used. Snippets have originally been added to provide "
"is necessary."}, "for 'changes in IFC'. To get this really working in a CDE environment changes to the IFC format "
{"name": "bim_snippet_put", "is necessary.",
"description": "Puts a new BIM Snippet binary file to a topic. If this is used, the parent topics BIM Snippet " },
"property is_external must be set to false and the reference must be the file name with " {
"extension."}, "name": "bim_snippet_put",
"description": "Puts a new BIM Snippet binary file to a topic. If this is used, the parent topics BIM Snippet "
"property is_external must be set to false and the reference must be the file name with "
"extension.",
},
{"name": "files_get", "description": "Retrieve a collection of file references as topic header."}, {"name": "files_get", "description": "Retrieve a collection of file references as topic header."},
{"name": "files_put", "description": "Update a collection of file references on the topic header."}, {"name": "files_put", "description": "Update a collection of file references on the topic header."},
{"name": "comments_get", {
"description": "Retrieve a collection of all comments related to a topic (default ordering is date)."}, "name": "comments_get",
"description": "Retrieve a collection of all comments related to a topic (default ordering is date).",
},
{"name": "comment_post", "description": "Add a new comment to a topic."}, {"name": "comment_post", "description": "Add a new comment to a topic."},
{"name": "comment_put", "description": "Update a single comment, description similar to POST."}, {"name": "comment_put", "description": "Update a single comment, description similar to POST."},
{"name": "comment_get", "description": "Get a single comment."}, {"name": "comment_get", "description": "Get a single comment."},
{"name": "viewpoints_get", "description": "Retrieve a collection of all viewpoints related to a topic."}, {"name": "viewpoints_get", "description": "Retrieve a collection of all viewpoints related to a topic."},
{"name": "viewpoint_post", {
"description": "Add a new viewpoint. Viewpoints are immutable, meaning that they should never change. " "name": "viewpoint_post",
"Requirements for different visualizations should be handled by creating new viewpoint elements."}, "description": "Add a new viewpoint. Viewpoints are immutable, meaning that they should never change. "
"Requirements for different visualizations should be handled by creating new viewpoint elements.",
},
{"name": "viewpoint_get", "description": "Retrieve a specific viewpoint."}, {"name": "viewpoint_get", "description": "Retrieve a specific viewpoint."},
{"name": "viewpoint_selected_components_get", {
"description": "Retrieve a collection of all selected components in a viewpoint."}, "name": "viewpoint_selected_components_get",
{"name": "viewpoint_colored_components_get", "description": "Retrieve a collection of all selected components in a viewpoint.",
"description": "Retrieve a collection of all colored components in a viewpoint."}, },
{
"name": "viewpoint_colored_components_get",
"description": "Retrieve a collection of all colored components in a viewpoint.",
},
{"name": "viewpoint_components_visibility_get", "description": "Retrieve visibility of components in a viewpoint."}, {"name": "viewpoint_components_visibility_get", "description": "Retrieve visibility of components in a viewpoint."},
{"name": "viewpoint_snapshot_get", "description": "Retrieve a specific viewpoints bitmap image file (png or jpg)."}, {"name": "viewpoint_snapshot_get", "description": "Retrieve a specific viewpoints bitmap image file (png or jpg)."},
{"name": "related_topics_get", "description": "Retrieve a collection of all related topics to a topic."}, {"name": "related_topics_get", "description": "Retrieve a collection of all related topics to a topic."},
{"name": "related_topics_put", "description": "Add or update a collection of all related topics to a topic."}, {"name": "related_topics_put", "description": "Add or update a collection of all related topics to a topic."},
{"name": "topic_document_references_get", {
"description": "Retrieve a collection of all document references to a topic."}, "name": "topic_document_references_get",
"description": "Retrieve a collection of all document references to a topic.",
},
{"name": "topic_document_references_post", "description": "Add or update document references to a topic."}, {"name": "topic_document_references_post", "description": "Add or update document references to a topic."},
{"name": "topic_document_references_put", "description": "Add or update document references to a topic."}, {"name": "topic_document_references_put", "description": "Add or update document references to a topic."},
{"name": "documents_get", "description": "Retrieve a collection of all documents uploaded to a project."}, {"name": "documents_get", "description": "Retrieve a collection of all documents uploaded to a project."},
{"name": "document_post", "description": "Upload a document (binary file) to a project."}, {"name": "document_post", "description": "Upload a document (binary file) to a project."},
{"name": "document_get", "description": "Retrieves a document as binary file."}, {"name": "document_get", "description": "Retrieves a document as binary file."},
{"name": "topics_events_get", {
"description": "Retrieve a collection of topic events related to a project (default sort order is date)."}, "name": "topics_events_get",
{"name": "topic_events_get", "description": "Retrieve a collection of topic events related to a project (default sort order is date).",
"description": "Retrieve a collection of topic events related to a project (default sort order is date)."}, },
{"name": "comments_events_get", {
"description": "Retrieve a collection of comment events related to a project (default sort order is date)."}, "name": "topic_events_get",
{"name": "comment_events_get", "description": "Retrieve a collection of topic events related to a project (default sort order is date).",
"description": "Retrieve a collection of comment events related to a comment (default sort order is date)."} },
{
"name": "comments_events_get",
"description": "Retrieve a collection of comment events related to a project (default sort order is date).",
},
{
"name": "comment_events_get",
"description": "Retrieve a collection of comment events related to a comment (default sort order is date).",
},
] ]
app = FastAPI( app = FastAPI(
title="Kontroll API", title="Kontroll API", description="Implementering av BCF API 3.0", version="0.0.1", openapi_tags=endpoint_metadata
description="Implementering av BCF API 3.0",
version="0.0.1",
openapi_tags=endpoint_metadata
) )
# Configure app to accept requests from anywhere # Configure app to accept requests from anywhere
@@ -98,23 +129,21 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
app.include_router(foundation.router, prefix='') app.include_router(foundation.router, prefix="")
app.include_router(bcf.router, prefix='') app.include_router(bcf.router, prefix="")
app.include_router(documents.router, prefix='') app.include_router(documents.router, prefix="")
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse) @app.get("/", response_class=HTMLResponse)
def index(request: Request): def index(request: Request):
return templates.TemplateResponse( return templates.TemplateResponse("index.html", {"request": request})
"index.html",
{"request": request})
favicon_path = 'favicon.ico' favicon_path = "favicon.ico"
@app.get('/favicon.ico', include_in_schema=False) @app.get("/favicon.ico", include_in_schema=False)
async def favicon(): async def favicon():
return FileResponse(favicon_path) return FileResponse(favicon_path)
@@ -55,13 +55,13 @@ class ClippingPlane(BaseModel):
class SnapshotType(Enum): class SnapshotType(Enum):
jpg = 'jpg' jpg = "jpg"
png = 'png' png = "png"
class BitmapType(Enum): class BitmapType(Enum):
jpg = 'jpg' jpg = "jpg"
png = 'png' png = "png"
class Component(BaseModel): class Component(BaseModel):
@@ -2,9 +2,9 @@ from models.bcf_common import *
class ProjectAction(Enum): class ProjectAction(Enum):
update = 'update' update = "update"
createTopic = 'createTopic' createTopic = "createTopic"
createDocument = 'createDocument' createDocument = "createDocument"
class ProjectGETAuthorization(BaseModel): class ProjectGETAuthorization(BaseModel):
@@ -18,19 +18,19 @@ class ProjectGET(BaseModel):
class TopicAction(Enum): class TopicAction(Enum):
update = 'update' update = "update"
updateBimSnippet = 'updateBimSnippet' updateBimSnippet = "updateBimSnippet"
updateRelatedTopics = 'updateRelatedTopics' updateRelatedTopics = "updateRelatedTopics"
updateDocumentReferences = 'updateDocumentReferences' updateDocumentReferences = "updateDocumentReferences"
updateFiles = 'updateFiles' updateFiles = "updateFiles"
createComment = 'createComment' createComment = "createComment"
createViewpoint = 'createViewpoint' createViewpoint = "createViewpoint"
delete = 'delete' delete = "delete"
class CommentAction(Enum): class CommentAction(Enum):
update = 'update' update = "update"
delete = 'delete' delete = "delete"
class ExtensionsGET(BaseModel): class ExtensionsGET(BaseModel):
@@ -123,7 +123,7 @@ class SnapshotGET(BaseModel):
class ViewpointAction(Enum): class ViewpointAction(Enum):
delete = 'delete' delete = "delete"
class ViewpointGETAuthorization(BaseModel): class ViewpointGETAuthorization(BaseModel):
@@ -174,6 +174,8 @@ class TopicEventGET(BaseModel):
topic_guid: str topic_guid: str
date: str date: str
author: str author: str
# actions: Optional[List[EventAction]] = Field(None, min_items=1) # actions: Optional[List[EventAction]] = Field(None, min_items=1)
@@ -182,6 +184,8 @@ class CommentEventGET(BaseModel):
topic_guid: str topic_guid: str
date: str date: str
author: str author: str
# actions: Optional[List[EventAction]] = Field(None, min_items=1) # actions: Optional[List[EventAction]] = Field(None, min_items=1)
@@ -10,12 +10,10 @@ from typing import List, Optional
class CallbackLink(BaseModel): class CallbackLink(BaseModel):
url: constr(min_length=1) = Field( url: constr(min_length=1) = Field(
description='The server will web-browser-redirect to this URL once the user has completed selecting ' description="The server will web-browser-redirect to this URL once the user has completed selecting "
'documents or entering document metadata on the CDE' "documents or entering document metadata on the CDE"
)
expires_in: int = Field(
description='The expiry period for the URL, in seconds'
) )
expires_in: int = Field(description="The expiry period for the URL, in seconds")
# ---- RESPONSE MODELS ---- # # ---- RESPONSE MODELS ---- #
@@ -35,62 +33,52 @@ class DocumentVersionLinks(BaseModel):
class FileDescription(BaseModel): class FileDescription(BaseModel):
name: constr(min_length=1) = Field( name: constr(min_length=1) = Field(
description='The name of the document version file on the server. The files are named by ' description="The name of the document version file on the server. The files are named by "
'document_id.file_ending', "document_id.file_ending",
example='908e1cd4-2e09-11ee-be56-0242ac120002.ifc' example="908e1cd4-2e09-11ee-be56-0242ac120002.ifc",
)
size_in_bytes: int = Field(
description='The size of the file in bytes',
example='124563'
) )
size_in_bytes: int = Field(description="The size of the file in bytes", example="124563")
class Document(BaseModel): class Document(BaseModel):
document_id: constr(min_length=1) = Field( document_id: constr(min_length=1) = Field(
description='A machine readable identifier that can be used to uniquely identify this version in future calls ' description="A machine readable identifier that can be used to uniquely identify this version in future calls "
'UUID is used - see `Query` section', "UUID is used - see `Query` section",
example='908e1cd4-2e09-11ee-be56-0242ac120002' example="908e1cd4-2e09-11ee-be56-0242ac120002",
) )
session_file_id: Optional[str] = Field( session_file_id: Optional[str] = Field(
description='A machine readable identifier that can be used to uniquely the file ' description="A machine readable identifier that can be used to uniquely the file "
'during the upload session, UUID is used', "during the upload session, UUID is used",
example='908e1cd4-2e09-11ee-be56-0242ac120002' example="908e1cd4-2e09-11ee-be56-0242ac120002",
) )
version_index: int = Field( version_index: int = Field(
description='A machine readable sequence number of the version of the document. The sequence must be ordered, ' description="A machine readable sequence number of the version of the document. The sequence must be ordered, "
'so that newer versions have higher values than previous ones. Each version index must be unique ' "so that newer versions have higher values than previous ones. Each version index must be unique "
'for that document, but there may be gaps in the sequence', "for that document, but there may be gaps in the sequence",
example='12' example="12",
) )
version_number: Optional[constr(min_length=1)] = Field( version_number: Optional[constr(min_length=1)] = Field(
description='A human readable version number. This is not expected to be in any specific format across CDEs ' description="A human readable version number. This is not expected to be in any specific format across CDEs "
'and may hold any value', "and may hold any value",
example='V2.0-larger' example="V2.0-larger",
) )
creation_date: str = Field( creation_date: str = Field(
description='The creation date of the document revision', description="The creation date of the document revision", example="2016-04-28T16:31:12.270+02:00"
example='2016-04-28T16:31:12.270+02:00'
) )
title: Optional[constr(min_length=1)] = Field( title: Optional[constr(min_length=1)] = Field(
description='A human readable code or identifier. Metadata entered by user in CDE.', description="A human readable code or identifier. Metadata entered by user in CDE.", example="Large garage"
example='Large garage'
) )
original_file_name: Optional[str] = Field( original_file_name: Optional[str] = Field(
description='The full name of the file as sent to the API', description="The full name of the file as sent to the API", example="First_floor_vent.ifc"
example='First_floor_vent.ifc') )
file_ending: Optional[str] = Field( file_ending: Optional[str] = Field(description="The ending of the file name, including the dot", example=".ifc")
description='The ending of the file name, including the dot', mime_type: Optional[str] = Field(description="The mime type identifier", example="application/x-step")
example='.ifc') file_type: Optional[str] = Field(description="The full name of the file type", example="STEP Physical File (SPF)")
mime_type: Optional[str] = Field(
description='The mime type identifier',
example='application/x-step')
file_type: Optional[str] = Field(
description='The full name of the file type',
example='STEP Physical File (SPF)')
project: Optional[str] = Field( project: Optional[str] = Field(
description='The project to which the document will belong once it has been uploaded,' description="The project to which the document will belong once it has been uploaded,"
'this information is added as metadata by the user in the CDE', "this information is added as metadata by the user in the CDE",
example='908e1cd4-2e09-11ee-be56-0242ac120003') example="908e1cd4-2e09-11ee-be56-0242ac120003",
)
file_description: FileDescription file_description: FileDescription
parts: Optional[List[str]] parts: Optional[List[str]]
@@ -21,8 +21,8 @@ class ProjectOnly(BaseModel):
class DataForUploadDocuments(BaseModel): class DataForUploadDocuments(BaseModel):
server_context: Optional[str] = Field( server_context: Optional[str] = Field(
description="A CDE controlled identifier recording the user's context on the CDE. For example which project " description="A CDE controlled identifier recording the user's context on the CDE. For example which project "
'and folder the user was on. If the client provides the `server_context` in subsequent calls then ' "and folder the user was on. If the client provides the `server_context` in subsequent calls then "
'the CDE will attemp to load the UI at the same place.' "the CDE will attemp to load the UI at the same place."
) )
documents: List[FileToUpload] documents: List[FileToUpload]
callback: Optional[CallbackLink] callback: Optional[CallbackLink]
@@ -32,25 +32,24 @@ class DataForUploadDocuments(BaseModel):
# ---- DOWNLOAD MODELS ---- # ---- DOWNLOAD MODELS ----
class DocumentMetadataEntries(BaseModel): class DocumentMetadataEntries(BaseModel):
metadata: List[DocumentMetadataEntry] = Field( metadata: List[DocumentMetadataEntry] = Field(description="An array of metadata entries")
description='An array of metadata entries'
)
class Project(BaseModel): class Project(BaseModel):
project_id: str project_id: str
name: str name: str
documents: Optional[List[Document]] = Field( documents: Optional[List[Document]] = Field(
description='An array containing all the documents selected by the user' description="An array containing all the documents selected by the user"
) )
class DataForDocumentSelection(BaseModel): class DataForDocumentSelection(BaseModel):
server_context: Optional[str] = Field( server_context: Optional[str] = Field(
description="A CDE controlled identifier recording the user's context on the CDE. For example which project " description="A CDE controlled identifier recording the user's context on the CDE. For example which project "
'and folder the user was on. If the client provides the `server_context` in subsequent calls then ' "and folder the user was on. If the client provides the `server_context` in subsequent calls then "
'the CDE will attemp to load the UI at the same place.' "the CDE will attemp to load the UI at the same place."
) )
projects: List[Project] projects: List[Project]
callback: Optional[CallbackLink] callback: Optional[CallbackLink]
@@ -61,24 +60,8 @@ class DataForDocumentSelection(BaseModel):
file_types = { file_types = {
'smc': { "smc": {"file_type": "Solibri Model Checker", "file_ending": ".smc", "mime_type": "application/octet-stream"},
'file_type': 'Solibri Model Checker', "ifc": {"file_type": "STEP Physical File", "file_ending": ".ifc", "mime_type": "application/x-step"},
'file_ending': '.smc', "ifczip": {"file_type": "ZIP of a STEP Physical File", "file_ending": ".ifcZIP", "mime_type": "application/zip"},
'mime_type': 'application/octet-stream' "pdf": {"file_type": "Adobe Portable Document Format", "file_ending": ".pdf", "mime_type": "application/pdf"},
},
'ifc': {
'file_type': 'STEP Physical File',
'file_ending': '.ifc',
'mime_type': 'application/x-step'
},
'ifczip': {
'file_type': 'ZIP of a STEP Physical File',
'file_ending': '.ifcZIP',
'mime_type': 'application/zip'
},
'pdf': {
'file_type': 'Adobe Portable Document Format',
'file_ending': '.pdf',
'mime_type': 'application/pdf'
}
} }
@@ -13,15 +13,15 @@ from models.documents_common import CallbackLink
class FileToUpload(BaseModel): class FileToUpload(BaseModel):
file_name: constr(min_length=1) = Field( file_name: constr(min_length=1) = Field(
description='The CDE UI will display this value to the User when entering document metadata. This is the ' description="The CDE UI will display this value to the User when entering document metadata. This is the "
'original name of the file. This attribute is the same as the name attribute in a document model.' "original name of the file. This attribute is the same as the name attribute in a document model."
) )
session_file_id: constr(min_length=1) = Field( session_file_id: constr(min_length=1) = Field(
description='This is a client provided id to differentiate between multiple files that are being uploaded in ' description="This is a client provided id to differentiate between multiple files that are being uploaded in "
'the same session' "the same session"
) )
document_id: Optional[constr(min_length=1)] = Field( document_id: Optional[constr(min_length=1)] = Field(
description='When present, indicates that this upload is a new version of an existing document' description="When present, indicates that this upload is a new version of an existing document"
) )
@@ -29,19 +29,17 @@ class UploadDocuments(BaseModel):
callback: CallbackLink callback: CallbackLink
server_context: Optional[str] = Field( server_context: Optional[str] = Field(
description="A CDE controlled identifier recording the user's context on the CDE. For example which project " description="A CDE controlled identifier recording the user's context on the CDE. For example which project "
'and folder the user was on. If the client provides the `server_context` in subsequent calls then ' "and folder the user was on. If the client provides the `server_context` in subsequent calls then "
'the CDE will attemp to load the UI at the same place.' "the CDE will attemp to load the UI at the same place."
) )
files: List[FileToUpload] files: List[FileToUpload]
class UploadFileDetail(BaseModel): class UploadFileDetail(BaseModel):
size_in_bytes: int = Field( size_in_bytes: int = Field(description="The uploaded file size")
description='The uploaded file size'
)
session_file_id: constr(min_length=1) = Field( session_file_id: constr(min_length=1) = Field(
description='This is a client provided id to differentiate between multiple files that are being uploaded in ' description="This is a client provided id to differentiate between multiple files that are being uploaded in "
'the same session' "the same session"
) )
@@ -56,37 +54,31 @@ class SelectDocuments(BaseModel):
callback: CallbackLink callback: CallbackLink
server_context: Optional[str] = Field( server_context: Optional[str] = Field(
description="A CDE controlled identifier recording the user's context on the CDE. For example which project " description="A CDE controlled identifier recording the user's context on the CDE. For example which project "
'and folder the user was on. If the client provides the `server_context` in subsequent calls then ' "and folder the user was on. If the client provides the `server_context` in subsequent calls then "
'the CDE will attemp to load the UI at the same place.' "the CDE will attemp to load the UI at the same place."
) )
supported_file_extensions: Optional[List[str]] = Field( supported_file_extensions: Optional[List[str]] = Field(
description='The client may optionally provide an array of accepted file extensions that should be opened ' description="The client may optionally provide an array of accepted file extensions that should be opened "
'during this flow. The CDE server UI should make an attempt to only show files matching these ' "during this flow. The CDE server UI should make an attempt to only show files matching these "
'extensions to the user for the download selection or help the user in selecting the desired ' "extensions to the user for the download selection or help the user in selecting the desired "
'files. However, the server does not have to guarantee that only files matching the extensions ' "files. However, the server does not have to guarantee that only files matching the extensions "
'will be selected. The extensions here must contain the dot separator.', "will be selected. The extensions here must contain the dot separator.",
example=['.ifc', '.ifczip'] example=[".ifc", ".ifczip"],
) )
class DataType(Enum): class DataType(Enum):
string = 'string' string = "string"
boolean = 'boolean' boolean = "boolean"
date_time = 'date-time' date_time = "date-time"
date = 'date' date = "date"
integer32 = 'integer32' integer32 = "integer32"
integer64 = 'integer64' integer64 = "integer64"
number = 'number' number = "number"
url = 'url' url = "url"
class DocumentMetadataEntry(BaseModel): class DocumentMetadataEntry(BaseModel):
name: constr(min_length=1) = Field( name: constr(min_length=1) = Field(description="The name of the metadata property")
description='The name of the metadata property' value: List[constr(min_length=1)] = Field(description="The value of the metadata property, can be a list")
) data_type: DataType = Field(description="The data type of the items in the value array")
value: List[constr(min_length=1)] = Field(
description='The value of the metadata property, can be a list'
)
data_type: DataType = Field(
description='The data type of the items in the value array'
)
@@ -13,20 +13,18 @@ from models.documents_common import DocumentVersion, LinkData
class DocumentUploadSessionInitialization(BaseModel): class DocumentUploadSessionInitialization(BaseModel):
upload_ui_url: constr(min_length=1) = Field( upload_ui_url: constr(min_length=1) = Field(
description='A CDE UI URL for the client to open in a local browser. The user would enter document metadata ' description="A CDE UI URL for the client to open in a local browser. The user would enter document metadata "
'directly in the CDE' "directly in the CDE"
)
expires_in: int = Field(
description='`upload_ui_url` expiry in seconds'
) )
expires_in: int = Field(description="`upload_ui_url` expiry in seconds")
max_size_in_bytes: int = Field( max_size_in_bytes: int = Field(
description='The maximum file size supported by the CDE. Attempts to upload a larger file will fail' description="The maximum file size supported by the CDE. Attempts to upload a larger file will fail"
) )
class HttpMethod(Enum): class HttpMethod(Enum):
POST = 'POST' POST = "POST"
PUT = 'PUT' PUT = "PUT"
class HeaderValue(BaseModel): class HeaderValue(BaseModel):
@@ -40,12 +38,12 @@ class Headers(BaseModel):
class MultipartFormData(BaseModel): class MultipartFormData(BaseModel):
prefix: str = Field( prefix: str = Field(
description='This is a server provided value. Its value must be prefixed to the binary content body when ' description="This is a server provided value. Its value must be prefixed to the binary content body when "
'uploading this part' "uploading this part"
) )
suffix: str = Field( suffix: str = Field(
description='This is a server provided value. Its value must be suffixed to the binary content body when ' description="This is a server provided value. Its value must be suffixed to the binary content body when "
'uploading this part. Typically, this is the end boundary for a multipart/form-data request' "uploading this part. Typically, this is the end boundary for a multipart/form-data request"
) )
@@ -54,26 +52,22 @@ class UploadFilePartInstruction(BaseModel):
http_method: HttpMethod http_method: HttpMethod
additional_headers: Optional[Headers] = None additional_headers: Optional[Headers] = None
include_authorization: Optional[bool] = Field( include_authorization: Optional[bool] = Field(
description='Whether or not to include the authorization request header in the file upload request. ' description="Whether or not to include the authorization request header in the file upload request. "
'Including the authorization header with some cloud storage providers might fail the request' "Including the authorization header with some cloud storage providers might fail the request"
) )
multipart_form_data: Optional[MultipartFormData] = None multipart_form_data: Optional[MultipartFormData] = None
content_range_start: int = Field( content_range_start: int = Field(description="The inclusive, zero index based start for this part")
description='The inclusive, zero index based start for this part' content_range_end: int = Field(description="The inclusive, zero index based end for this part")
)
content_range_end: int = Field(
description='The inclusive, zero index based end for this part'
)
class DocumentToUpload(BaseModel): class DocumentToUpload(BaseModel):
session_file_id: constr(min_length=1) = Field( session_file_id: constr(min_length=1) = Field(
description='A client-provided identifier that allows matching the specification with the correct file on the ' description="A client-provided identifier that allows matching the specification with the correct file on the "
"user's machine" "user's machine"
) )
upload_file_parts: List[UploadFilePartInstruction] = Field( upload_file_parts: List[UploadFilePartInstruction] = Field(
description='An array of request specifications detailing how to split the file to parts and upload each part ' description="An array of request specifications detailing how to split the file to parts and upload each part "
'to the CDE' "to the CDE"
# min_length=1, # min_length=1,
) )
upload_completion: LinkData upload_completion: LinkData
@@ -83,8 +77,8 @@ class DocumentToUpload(BaseModel):
class DocumentsToUpload(BaseModel): class DocumentsToUpload(BaseModel):
server_context: Optional[str] = Field( server_context: Optional[str] = Field(
description="A CDE controlled identifier recording the user's context on the CDE. For example which project " description="A CDE controlled identifier recording the user's context on the CDE. For example which project "
'and folder the user was on. If the client provides the `server_context` in subsequent calls then ' "and folder the user was on. If the client provides the `server_context` in subsequent calls then "
'the CDE will attemp to load the UI at the same place.' "the CDE will attemp to load the UI at the same place."
) )
documents_to_upload: Optional[List[DocumentToUpload]] documents_to_upload: Optional[List[DocumentToUpload]]
@@ -94,12 +88,10 @@ class DocumentsToUpload(BaseModel):
class DocumentDiscoverySessionInitialization(BaseModel): class DocumentDiscoverySessionInitialization(BaseModel):
select_documents_url: constr(min_length=1) = Field( select_documents_url: constr(min_length=1) = Field(
description='A CDE UI URL for the client to open in a local browser. The user would search and select ' description="A CDE UI URL for the client to open in a local browser. The user would search and select "
'documents directly in the CDE' "documents directly in the CDE"
)
expires_in: int = Field(
description='`select_documents_url` expiry in seconds'
) )
expires_in: int = Field(description="`select_documents_url` expiry in seconds")
class DocumentsMarkedAsSelected(BaseModel): class DocumentsMarkedAsSelected(BaseModel):
@@ -109,29 +101,25 @@ class DocumentsMarkedAsSelected(BaseModel):
class SelectedDocuments(BaseModel): class SelectedDocuments(BaseModel):
server_context: Optional[str] = Field( server_context: Optional[str] = Field(
description="A CDE controlled identifier recording the user's context on the CDE. For example which project " description="A CDE controlled identifier recording the user's context on the CDE. For example which project "
'and folder the user was on. If the client provides the `server_context` in subsequent calls then ' "and folder the user was on. If the client provides the `server_context` in subsequent calls then "
'the CDE will attemp to load the UI at the same place.' "the CDE will attemp to load the UI at the same place."
)
documents: List[DocumentVersion] = Field(
description='An array containing all the documents selected by the user'
) )
documents: List[DocumentVersion] = Field(description="An array containing all the documents selected by the user")
class DocumentMetadata(BaseModel): class DocumentMetadata(BaseModel):
session_file_id: constr(min_length=1) = Field( session_file_id: constr(min_length=1) = Field(
description='This is a client provided id to differentiate between multiple files that are being uploaded in ' description="This is a client provided id to differentiate between multiple files that are being uploaded in "
'the same session' "the same session"
) )
document_id: Optional[constr(min_length=1)] = Field( document_id: Optional[constr(min_length=1)] = Field(
description='When present, indicates that this upload is a new version of an existing document' description="When present, indicates that this upload is a new version of an existing document"
) )
version_number: constr(min_length=1) = Field( version_number: constr(min_length=1) = Field(
description='A human readable version number. This is not expected to be in any specific format across CDEs ' description="A human readable version number. This is not expected to be in any specific format across CDEs "
'and may hold any value' "and may hold any value"
)
title: constr(min_length=1) = Field(
description='A human readable code or identifier'
) )
title: constr(min_length=1) = Field(description="A human readable code or identifier")
project: Optional[str] project: Optional[str]
@@ -1,4 +1 @@
from pydantic import BaseModel from pydantic import BaseModel
File diff suppressed because it is too large Load Diff
+281 -278
View File
@@ -25,41 +25,42 @@ class DOCDB(MyDB):
def document_node_to_model(self, document_node): def document_node_to_model(self, document_node):
document_json = self.node_to_json(document_node) document_json = self.node_to_json(document_node)
document_json['creation_date'] = self.bcf_time(document_json['creation_date']) document_json["creation_date"] = self.bcf_time(document_json["creation_date"])
document_json['file_description'] = { document_json["file_description"] = {
'name': document_json.pop('name', '<no name>'), "name": document_json.pop("name", "<no name>"),
'size_in_bytes': document_json.pop('size_in_bytes', 0) "size_in_bytes": document_json.pop("size_in_bytes", 0),
} }
document_json['links'] = self.document_version_links(document_json) document_json["links"] = self.document_version_links(document_json)
return DocumentVersion(**document_json) return DocumentVersion(**document_json)
def get_document(self, document_id, version_index=False): def get_document(self, document_id, version_index=False):
def get_document_work(tx) -> Union[Document, bool]: def get_document_work(tx) -> Union[Document, bool]:
if version_index is None or version_index is False or not isinstance(version_index, int): if version_index is None or version_index is False or not isinstance(version_index, int):
version_index_criteria = '' version_index_criteria = ""
else: else:
version_index_criteria = 'AND d.version_index = $version_index' version_index_criteria = "AND d.version_index = $version_index"
cypher = """ cypher = (
"""
MATCH (d:Document) MATCH (d:Document)
WHERE d.document_id = $document_id WHERE d.document_id = $document_id
%s %s
RETURN d AS document RETURN d AS document
ORDER by d.version_index DESC ORDER by d.version_index DESC
LIMIT 1 LIMIT 1
""" % version_index_criteria """
% version_index_criteria
)
result = tx.run(cypher, result = tx.run(cypher, document_id=document_id, version_index=version_index)
document_id=document_id,
version_index=version_index)
first = result.single() first = result.single()
if first is None: if first is None:
print('There were no such document version.') print("There were no such document version.")
return False return False
document_node = first.get('document') document_node = first.get("document")
return self.document_node_to_model(document_node) return self.document_node_to_model(document_node)
with self.driver.session() as session: with self.driver.session() as session:
@@ -88,21 +89,23 @@ class DOCDB(MyDB):
d.name = $name, d.name = $name,
d.size_in_bytes = $size_in_bytes d.size_in_bytes = $size_in_bytes
""" """
result = tx.run(cypher, result = tx.run(
selection_session=selection_session, cypher,
project=project, selection_session=selection_session,
document_id=document_version.document_id, project=project,
session_file_id=document_version.session_file_id, document_id=document_version.document_id,
version_index=document_version.version_index, session_file_id=document_version.session_file_id,
version_number=document_version.version_number, version_index=document_version.version_index,
creation_date=document_version.creation_date, version_number=document_version.version_number,
title=document_version.title, creation_date=document_version.creation_date,
original_file_name=document_version.original_file_name, title=document_version.title,
file_ending=document_version.file_ending, original_file_name=document_version.original_file_name,
mime_type=document_version.mime_type, file_ending=document_version.file_ending,
file_type=document_version.file_type, mime_type=document_version.mime_type,
name=document_version.file_description.name, file_type=document_version.file_type,
size_in_bytes=document_version.file_description.size_in_bytes) name=document_version.file_description.name,
size_in_bytes=document_version.file_description.size_in_bytes,
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 1: if summary.counters.nodes_created < 1:
@@ -122,10 +125,10 @@ class DOCDB(MyDB):
first = result.single() first = result.single()
if first is None: if first is None:
print('There were no such document version.') print("There were no such document version.")
return False return False
document_node = first.get('document') document_node = first.get("document")
return self.document_node_to_model(document_node) return self.document_node_to_model(document_node)
with self.driver.session() as session: with self.driver.session() as session:
@@ -134,20 +137,21 @@ class DOCDB(MyDB):
def create_ifc_graph_for_document(self, document_id): def create_ifc_graph_for_document(self, document_id):
document = self.get_document(document_id) document = self.get_document(document_id)
my_ifc_file = ifcopenshell.open('./data/documents/' + document.file_description.name) my_ifc_file = ifcopenshell.open("./data/documents/" + document.file_description.name)
my_graph = Graph(os.environ['NEO4J_URI'], auth=(os.environ['NEO4J_USER'], os.environ['NEO4J_INITIAL_PASSWORD'])) my_graph = Graph(os.environ["NEO4J_URI"], auth=(os.environ["NEO4J_USER"], os.environ["NEO4J_INITIAL_PASSWORD"]))
create_full_graph(my_graph, my_ifc_file) create_full_graph(my_graph, my_ifc_file)
# ---- UPLOAD FUNCTIONS ---- # ---- UPLOAD FUNCTIONS ----
def post_upload_documents(self, upload_documents: UploadDocuments, def post_upload_documents(
current_user: User) -> DocumentUploadSessionInitialization: self, upload_documents: UploadDocuments, current_user: User
) -> DocumentUploadSessionInitialization:
def post_upload_documents_work(tx) -> DocumentUploadSessionInitialization: def post_upload_documents_work(tx) -> DocumentUploadSessionInitialization:
session_uuid = str(uuid4()) session_uuid = str(uuid4())
session_callback_timedelta = int(upload_documents.callback.expires_in) session_callback_timedelta = int(upload_documents.callback.expires_in)
session_url_validity_timedelta = 10 + int(os.environ['SESSION_URL_VALIDITY_SECONDS']) session_url_validity_timedelta = 10 + int(os.environ["SESSION_URL_VALIDITY_SECONDS"])
if not hasattr(upload_documents, 'server_context') or not upload_documents.server_context: if not hasattr(upload_documents, "server_context") or not upload_documents.server_context:
upload_documents.server_context = False upload_documents.server_context = False
cypher = """ cypher = """
@@ -165,13 +169,15 @@ class DOCDB(MyDB):
s.session_callback_timedelta = $session_callback_timedelta s.session_callback_timedelta = $session_callback_timedelta
""" """
result = tx.run(cypher, result = tx.run(
username=current_user.username, cypher,
session_callback_timedelta=session_callback_timedelta, username=current_user.username,
session_url_timedelta=session_url_validity_timedelta, session_callback_timedelta=session_callback_timedelta,
server_context=upload_documents.server_context, session_url_timedelta=session_url_validity_timedelta,
upload_session=session_uuid, server_context=upload_documents.server_context,
callback=upload_documents.callback.url) upload_session=session_uuid,
callback=upload_documents.callback.url,
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 2: if summary.counters.nodes_created < 2:
@@ -197,54 +203,60 @@ class DOCDB(MyDB):
d.file_type = $file_type d.file_type = $file_type
""" """
if not hasattr(file, 'document_id') or not file.document_id: if not hasattr(file, "document_id") or not file.document_id:
# When document_id is present, this indicates that # When document_id is present, this indicates that
# this upload is a new version of an existing document. # this upload is a new version of an existing document.
# When not present, we create a new uuid as new document_id. # When not present, we create a new uuid as new document_id.
file.document_id = doc_db.new_uuid() file.document_id = doc_db.new_uuid()
if file.file_name.lower().endswith(tuple(file_types)): if file.file_name.lower().endswith(tuple(file_types)):
file_ending = file.file_name.split('.')[-1].lower() file_ending = file.file_name.split(".")[-1].lower()
name = file.document_id + '.' + file_ending name = file.document_id + "." + file_ending
else: else:
file_ending = '' file_ending = ""
name = file.document_id name = file.document_id
print('File ending: ', file_ending) print("File ending: ", file_ending)
mime_type = '' mime_type = ""
file_type = '' file_type = ""
if hasattr(file_types, file_ending): if hasattr(file_types, file_ending):
print('We have this file ending in dict: ', file_ending) print("We have this file ending in dict: ", file_ending)
mime_type = file_types[file_ending]['mime_type'] mime_type = file_types[file_ending]["mime_type"]
file_type = file_types[file_ending]['file_type'] file_type = file_types[file_ending]["file_type"]
result = tx.run(cypher, result = tx.run(
username=current_user.username, cypher,
upload_session=session_uuid, username=current_user.username,
session_callback_timedelta=session_callback_timedelta, upload_session=session_uuid,
original_file_name=file.file_name, session_callback_timedelta=session_callback_timedelta,
name=name, original_file_name=file.file_name,
session_file_id=file.session_file_id, name=name,
document_id=file.document_id, session_file_id=file.session_file_id,
creation_date=doc_db.timestamp(), document_id=file.document_id,
file_ending=file_ending, creation_date=doc_db.timestamp(),
mime_type=mime_type, file_ending=file_ending,
file_type=file_type) mime_type=mime_type,
file_type=file_type,
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 1: if summary.counters.nodes_created < 1:
raise HTTPException(status_code=400, detail="Document node was not created.") raise HTTPException(status_code=400, detail="Document node was not created.")
session_init = DocumentUploadSessionInitialization(**{ session_init = DocumentUploadSessionInitialization(
'upload_ui_url': os.environ['KONTROLL_BASE_URL'] + 'documents/1.0/' \ **{
+ "document-upload?upload_session=" + session_uuid, "upload_ui_url": os.environ["KONTROLL_BASE_URL"]
'expires_in': os.environ['SESSION_URL_VALIDITY_SECONDS'], + "documents/1.0/"
'max_size_in_bytes': os.environ['SESSION_MAX_FILE_SIZE_BYTES'], + "document-upload?upload_session="
}) + session_uuid,
"expires_in": os.environ["SESSION_URL_VALIDITY_SECONDS"],
"max_size_in_bytes": os.environ["SESSION_MAX_FILE_SIZE_BYTES"],
}
)
return session_init return session_init
@@ -261,15 +273,14 @@ class DOCDB(MyDB):
WHERE us.upload_session = $upload_session WHERE us.upload_session = $upload_session
RETURN d AS document RETURN d AS document
""" """
results = tx.run(cypher, results = tx.run(cypher, upload_session=str(upload_session))
upload_session=str(upload_session))
document_list = list() document_list = list()
for result in results: for result in results:
document_json = self.node_to_json(result.get('document')) document_json = self.node_to_json(result.get("document"))
file_to_upload = { file_to_upload = {
'file_name': document_json['original_file_name'], "file_name": document_json["original_file_name"],
'session_file_id': document_json['session_file_id'], "session_file_id": document_json["session_file_id"],
'document_id': document_json['document_id'] "document_id": document_json["document_id"],
} }
document_model = FileToUpload(**file_to_upload) document_model = FileToUpload(**file_to_upload)
document_list.append(document_model) document_list.append(document_model)
@@ -280,16 +291,15 @@ class DOCDB(MyDB):
WHERE us.upload_session = $upload_session WHERE us.upload_session = $upload_session
RETURN p AS project RETURN p AS project
""" """
results = tx.run(cypher, results = tx.run(cypher, upload_session=str(upload_session))
upload_session=str(upload_session))
project_list = list() project_list = list()
for result in results: for result in results:
project_json = self.node_to_json(result.get('project')) project_json = self.node_to_json(result.get("project"))
project = { project = {
'project_id': project_json['project_id'], "project_id": project_json["project_id"],
'name': project_json['name'], "name": project_json["name"],
} }
print('Project: ', project) print("Project: ", project)
project_list.append(project) project_list.append(project)
# to get the user and some session data # to get the user and some session data
@@ -302,31 +312,30 @@ class DOCDB(MyDB):
us.session_callback_timedelta AS session_callback_timedelta, us.session_callback_timedelta AS session_callback_timedelta,
u AS user u AS user
""" """
result = tx.run(cypher, result = tx.run(cypher, upload_session=str(upload_session))
upload_session=str(upload_session))
first = result.single() first = result.single()
if first is None: if first is None:
raise HTTPException(status_code=401, detail="No session or link.") raise HTTPException(status_code=401, detail="No session or link.")
user_node = first.get('user') user_node = first.get("user")
user_dict = self.node_to_json(user_node) user_dict = self.node_to_json(user_node)
for_upload_documents_dict = dict() for_upload_documents_dict = dict()
for_upload_documents_dict['server_context'] = first.get('server_context') for_upload_documents_dict["server_context"] = first.get("server_context")
callback_link = dict() callback_link = dict()
callback_link['url'] = first.get('callback') callback_link["url"] = first.get("callback")
callback_link['expires_in'] = first.get('session_callback_timedelta') callback_link["expires_in"] = first.get("session_callback_timedelta")
for_upload_documents_dict['callback'] = callback_link for_upload_documents_dict["callback"] = callback_link
for_upload_documents_dict['documents'] = document_list for_upload_documents_dict["documents"] = document_list
print('Project list: ', project_list) print("Project list: ", project_list)
for_upload_documents_dict['projects'] = project_list for_upload_documents_dict["projects"] = project_list
for_upload_documents_dict['current_user'] = user_dict for_upload_documents_dict["current_user"] = user_dict
print('Documents list:', document_list) print("Documents list:", document_list)
for_upload_documents_model = DataForUploadDocuments(**for_upload_documents_dict) for_upload_documents_model = DataForUploadDocuments(**for_upload_documents_dict)
@@ -348,14 +357,16 @@ class DOCDB(MyDB):
d.title = $title, d.title = $title,
d.project = $project d.project = $project
""" """
result = tx.run(cypher, result = tx.run(
username=username, cypher,
upload_session=upload_session, username=username,
session_file_id=document.session_file_id, upload_session=upload_session,
version_number=document.version_number, session_file_id=document.session_file_id,
version_index=False, version_number=document.version_number,
title=document.title, version_index=False,
project=document.project) title=document.title,
project=document.project,
)
summary = result.consume() summary = result.consume()
if summary.counters.properties_set < 4: if summary.counters.properties_set < 4:
@@ -376,11 +387,13 @@ class DOCDB(MyDB):
SET SET
d.size_in_bytes = $size_in_bytes d.size_in_bytes = $size_in_bytes
""" """
result = tx.run(cypher, result = tx.run(
username=user.username, cypher,
upload_session=upload_session, username=user.username,
session_file_id=document.session_file_id, upload_session=upload_session,
size_in_bytes=document.size_in_bytes) session_file_id=document.session_file_id,
size_in_bytes=document.size_in_bytes,
)
summary = result.consume() summary = result.consume()
if summary.counters.properties_set < 1: if summary.counters.properties_set < 1:
raise HTTPException(status_code=400, detail="Property was not set.") raise HTTPException(status_code=400, detail="Property was not set.")
@@ -390,18 +403,20 @@ class DOCDB(MyDB):
session.execute_write(update_file_size_work) session.execute_write(update_file_size_work)
# link creation # link creation
base_url = os.environ['KONTROLL_BASE_URL'] + 'documents/1.0/' base_url = os.environ["KONTROLL_BASE_URL"] + "documents/1.0/"
upload_session_url = '?upload_session=' + upload_session upload_session_url = "?upload_session=" + upload_session
upload_complete_url = base_url + 'upload-completion' + upload_session_url upload_complete_url = base_url + "upload-completion" + upload_session_url
upload_cancellation_url = base_url + 'upload-cancellation' + upload_session_url upload_cancellation_url = base_url + "upload-cancellation" + upload_session_url
upload_completion = LinkData(**{'url': upload_complete_url}) upload_completion = LinkData(**{"url": upload_complete_url})
upload_cancellation = LinkData(**{'url': upload_cancellation_url}) upload_cancellation = LinkData(**{"url": upload_cancellation_url})
document_to_upload_model = DocumentToUpload(**{ document_to_upload_model = DocumentToUpload(
'session_file_id': document.session_file_id, **{
'upload_file_parts': list(), "session_file_id": document.session_file_id,
'upload_completion': upload_completion, "upload_file_parts": list(),
'upload_cancellation': upload_cancellation "upload_completion": upload_completion,
}) "upload_cancellation": upload_cancellation,
}
)
def add_part_work(tx) -> UUID: def add_part_work(tx) -> UUID:
cypher = """ cypher = """
@@ -416,15 +431,17 @@ class DOCDB(MyDB):
p.content_range_end = $content_range_end, p.content_range_end = $content_range_end,
p.content_length = $content_length p.content_length = $content_length
""" """
result = tx.run(cypher, result = tx.run(
username=user.username, cypher,
upload_session=upload_session, username=user.username,
session_file_id=document.session_file_id, upload_session=upload_session,
part_number=part_number, session_file_id=document.session_file_id,
part_uuid=str(upload_part_uuid), part_number=part_number,
content_range_start=content_range_start, part_uuid=str(upload_part_uuid),
content_range_end=content_range_end, content_range_start=content_range_start,
content_length=content_length) content_range_end=content_range_end,
content_length=content_length,
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created != 1: if summary.counters.nodes_created != 1:
@@ -432,7 +449,7 @@ class DOCDB(MyDB):
return upload_part_uuid return upload_part_uuid
# calculate the number of file parts to send # calculate the number of file parts to send
number_of_parts = math.ceil(int(document.size_in_bytes) / int(os.environ['SESSION_MAX_FILE_SIZE_BYTES'])) number_of_parts = math.ceil(int(document.size_in_bytes) / int(os.environ["SESSION_MAX_FILE_SIZE_BYTES"]))
part_length = math.ceil(int(document.size_in_bytes) / number_of_parts) part_length = math.ceil(int(document.size_in_bytes) / number_of_parts)
for part_number in range(number_of_parts): for part_number in range(number_of_parts):
content_range_start = part_number * part_length content_range_start = part_number * part_length
@@ -441,23 +458,18 @@ class DOCDB(MyDB):
content_range_end = document.size_in_bytes - 1 content_range_end = document.size_in_bytes - 1
content_length = content_range_end - content_range_start + 1 content_length = content_range_end - content_range_start + 1
upload_part_uuid = uuid4() upload_part_uuid = uuid4()
upload_part_url = 'upload-part/' + str(upload_part_uuid) upload_part_url = "upload-part/" + str(upload_part_uuid)
additional_headers = { additional_headers = {"values": [{"name": "Content-Length", "value": content_length}]}
'values': [ part_instruction = UploadFilePartInstruction(
{ **{
'name': 'Content-Length', "url": base_url + upload_part_url,
'value': content_length "http_method": "POST",
} "additional_headers": additional_headers,
] "include_authorization": True,
} "content_range_start": content_range_start,
part_instruction = UploadFilePartInstruction(**{ "content_range_end": content_range_end,
'url': base_url + upload_part_url, }
'http_method': 'POST', )
'additional_headers': additional_headers,
'include_authorization': True,
'content_range_start': content_range_start,
'content_range_end': content_range_end
})
with self.driver.session() as session: with self.driver.session() as session:
added_part = session.execute_write(add_part_work) added_part = session.execute_write(add_part_work)
@@ -476,19 +488,17 @@ class DOCDB(MyDB):
RETURN d AS document RETURN d AS document
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, part_id=part_id)
username=current_user.username,
part_id=part_id)
first = result.single() first = result.single()
if first is None: if first is None:
print('There were no parts in graph!') print("There were no parts in graph!")
return False return False
document_node = first.get('document') document_node = first.get("document")
document = self.document_node_to_model(document_node) document = self.document_node_to_model(document_node)
print('User had this part ' + part_id + ' in document id: ' + document.document_id) print("User had this part " + part_id + " in document id: " + document.document_id)
return document return document
with self.driver.session() as session: with self.driver.session() as session:
@@ -503,9 +513,7 @@ class DOCDB(MyDB):
SET p.uploaded = True SET p.uploaded = True
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, part_id=part_id)
username=current_user.username,
part_id=part_id)
summary = result.consume() summary = result.consume()
if summary.counters.properties_set < 1: if summary.counters.properties_set < 1:
@@ -526,26 +534,24 @@ class DOCDB(MyDB):
RETURN count(p) as parts RETURN count(p) as parts
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
first = result.single() first = result.single()
if first is None: if first is None:
return False return False
number_of_parts = first.get('parts') number_of_parts = first.get("parts")
print('There are number of parts not uploaded: ', number_of_parts) print("There are number of parts not uploaded: ", number_of_parts)
if number_of_parts > 0: if number_of_parts > 0:
raise HTTPException(status_code=400, detail="All parts not uploaded.") raise HTTPException(status_code=400, detail="All parts not uploaded.")
else: else:
return True return True
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(check_uploaded_parts_work) return session.execute_read(check_uploaded_parts_work)
def retrieve_uploaded_parts(self, upload_session: str, current_user: User) -> list: def retrieve_uploaded_parts(self, upload_session: str, current_user: User) -> list:
def retrieve_uploaded_parts_work(tx) -> list: def retrieve_uploaded_parts_work(tx) -> list:
cypher = """ cypher = """
@@ -556,14 +562,12 @@ class DOCDB(MyDB):
RETURN p as part RETURN p as part
ORDER BY part.number ORDER BY part.number
""" """
results = tx.run(cypher, results = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
parts_list = list() parts_list = list()
for result in results: for result in results:
part_json = self.node_to_json(result.get("part")) part_json = self.node_to_json(result.get("part"))
parts_list.append(part_json['uuid']) parts_list.append(part_json["uuid"])
return parts_list return parts_list
with self.driver.session() as session: with self.driver.session() as session:
@@ -579,12 +583,10 @@ class DOCDB(MyDB):
RETURN d AS document RETURN d AS document
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
first = result.single() first = result.single()
document_node = first.get('document') document_node = first.get("document")
return self.document_node_to_model(document_node) return self.document_node_to_model(document_node)
with self.driver.session() as session: with self.driver.session() as session:
@@ -604,10 +606,7 @@ class DOCDB(MyDB):
DELETE r2 DELETE r2
CREATE (proj)-[r4:CONTAINS]->(d) CREATE (proj)-[r4:CONTAINS]->(d)
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, upload_session=upload_session, project=project)
username=current_user.username,
upload_session=upload_session,
project=project)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_deleted < 1: if summary.counters.nodes_deleted < 1:
@@ -627,15 +626,13 @@ class DOCDB(MyDB):
AND us.upload_session = $upload_session AND us.upload_session = $upload_session
RETURN d as document RETURN d as document
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
first = result.single() first = result.single()
if first is None: if first is None:
return False return False
document_node = first.get('document') document_node = first.get("document")
return self.document_node_to_model(document_node) return self.document_node_to_model(document_node)
def upload_cancellation_work(tx) -> bool: def upload_cancellation_work(tx) -> bool:
@@ -645,9 +642,7 @@ class DOCDB(MyDB):
AND us.upload_session = $upload_session AND us.upload_session = $upload_session
DETACH DELETE us, d, p DETACH DELETE us, d, p
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_deleted < 1: if summary.counters.nodes_deleted < 1:
@@ -662,14 +657,15 @@ class DOCDB(MyDB):
# ---- DOWNLOAD FUNCTIONS ---- # ---- DOWNLOAD FUNCTIONS ----
def post_select_documents(self, select_documents: SelectDocuments, def post_select_documents(
current_user: User) -> DocumentDiscoverySessionInitialization: self, select_documents: SelectDocuments, current_user: User
) -> DocumentDiscoverySessionInitialization:
def post_select_documents_work(tx) -> DocumentDiscoverySessionInitialization: def post_select_documents_work(tx) -> DocumentDiscoverySessionInitialization:
session_uuid = str(uuid4()) session_uuid = str(uuid4())
session_callback_timedelta = int(select_documents.callback.expires_in) session_callback_timedelta = int(select_documents.callback.expires_in)
session_url_validity_timedelta = 10 + int(os.environ['SESSION_URL_VALIDITY_SECONDS']) session_url_validity_timedelta = 10 + int(os.environ["SESSION_URL_VALIDITY_SECONDS"])
if not hasattr(select_documents, 'server_context') or not select_documents.server_context: if not hasattr(select_documents, "server_context") or not select_documents.server_context:
select_documents.server_context = str(uuid4()) select_documents.server_context = str(uuid4())
cypher = """ cypher = """
@@ -687,22 +683,28 @@ class DOCDB(MyDB):
s.session_callback_timedelta = $session_callback_timedelta s.session_callback_timedelta = $session_callback_timedelta
""" """
result = tx.run(cypher, result = tx.run(
username=current_user.username, cypher,
session_callback_timedelta=session_callback_timedelta, username=current_user.username,
session_url_timedelta=session_url_validity_timedelta, session_callback_timedelta=session_callback_timedelta,
server_context=select_documents.server_context, session_url_timedelta=session_url_validity_timedelta,
selection_session=str(session_uuid), server_context=select_documents.server_context,
callback=select_documents.callback.url) selection_session=str(session_uuid),
callback=select_documents.callback.url,
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 2: if summary.counters.nodes_created < 2:
raise HTTPException(status_code=400, detail="Session or link node was not created.") raise HTTPException(status_code=400, detail="Session or link node was not created.")
session_init_dict = dict() session_init_dict = dict()
session_init_dict['select_documents_url'] = os.environ['KONTROLL_BASE_URL'] + 'documents/1.0/' \ session_init_dict["select_documents_url"] = (
+ "document-selection?selection_session=" + session_uuid os.environ["KONTROLL_BASE_URL"]
session_init_dict['expires_in'] = os.environ['SESSION_URL_VALIDITY_SECONDS'] + "documents/1.0/"
+ "document-selection?selection_session="
+ session_uuid
)
session_init_dict["expires_in"] = os.environ["SESSION_URL_VALIDITY_SECONDS"]
session_init_model = DocumentDiscoverySessionInitialization(**session_init_dict) session_init_model = DocumentDiscoverySessionInitialization(**session_init_dict)
return session_init_model return session_init_model
@@ -719,13 +721,12 @@ class DOCDB(MyDB):
RETURN p AS project RETURN p AS project
ORDER BY project.name ORDER BY project.name
""" """
project_results = tx.run(cypher, project_results = tx.run(cypher, selection_session=str(selection_session))
selection_session=str(selection_session))
project_list = list() project_list = list()
for project_result in project_results: for project_result in project_results:
project_json = self.node_to_json(project_result.get('project')) project_json = self.node_to_json(project_result.get("project"))
project_json['documents'] = list() project_json["documents"] = list()
project_model = Project(**project_json) project_model = Project(**project_json)
# to get the documents # to get the documents
@@ -736,12 +737,12 @@ class DOCDB(MyDB):
RETURN d AS document RETURN d AS document
ORDER BY d.title, d.version_index ORDER BY d.title, d.version_index
""" """
document_results = tx.run(cypher, document_results = tx.run(
selection_session=str(selection_session), cypher, selection_session=str(selection_session), project_id=str(project_model.project_id)
project_id=str(project_model.project_id)) )
for document_result in document_results: for document_result in document_results:
document_node = document_result.get('document') document_node = document_result.get("document")
document_model = self.document_node_to_model(document_node) document_model = self.document_node_to_model(document_node)
project_model.documents.append(document_model) project_model.documents.append(document_model)
@@ -758,8 +759,7 @@ class DOCDB(MyDB):
u AS user u AS user
""" """
result = tx.run(cypher, result = tx.run(cypher, selection_session=str(selection_session))
selection_session=str(selection_session))
first = result.single() first = result.single()
if first is None: if first is None:
@@ -769,26 +769,28 @@ class DOCDB(MyDB):
user_dict = self.node_to_json(user_node) user_dict = self.node_to_json(user_node)
for_document_selection_dict = dict() for_document_selection_dict = dict()
for_document_selection_dict['server_context'] = first.get('server_context') for_document_selection_dict["server_context"] = first.get("server_context")
callback_link = dict() callback_link = dict()
callback_link['url'] = first.get('callback') callback_link["url"] = first.get("callback")
callback_link['expires_in'] = first.get('session_callback_timedelta') callback_link["expires_in"] = first.get("session_callback_timedelta")
for_document_selection_dict['callback'] = callback_link for_document_selection_dict["callback"] = callback_link
for_document_selection_dict['projects'] = project_list for_document_selection_dict["projects"] = project_list
for_document_selection_dict['current_user'] = user_dict for_document_selection_dict["current_user"] = user_dict
for_document_selection_model = DataForDocumentSelection(**for_document_selection_dict) for_document_selection_model = DataForDocumentSelection(**for_document_selection_dict)
print('Data for document selection: ', for_document_selection_dict) print("Data for document selection: ", for_document_selection_dict)
return for_document_selection_model return for_document_selection_model
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(get_data_for_document_selection_work) return session.execute_read(get_data_for_document_selection_work)
def post_mark_documents_as_selected(self, all_documents: list, selection_session: UUID) -> DocumentsMarkedAsSelected: def post_mark_documents_as_selected(
self, all_documents: list, selection_session: UUID
) -> DocumentsMarkedAsSelected:
def mark_documents_as_selected_work(tx) -> DocumentsMarkedAsSelected: def mark_documents_as_selected_work(tx) -> DocumentsMarkedAsSelected:
cypher = """ cypher = """
@@ -798,12 +800,10 @@ class DOCDB(MyDB):
MERGE (ss)-[r5:SELECTED]->(d) MERGE (ss)-[r5:SELECTED]->(d)
""" """
selected_documents_model = DocumentsMarkedAsSelected(**{'documents': list()}) selected_documents_model = DocumentsMarkedAsSelected(**{"documents": list()})
for document in all_documents: for document in all_documents:
result = tx.run(cypher, result = tx.run(cypher, selection_session=str(selection_session), document_id=str(document))
selection_session=str(selection_session),
document_id=str(document))
summary = result.consume() summary = result.consume()
@@ -817,16 +817,20 @@ class DOCDB(MyDB):
@staticmethod @staticmethod
def document_version_links(document_json): def document_version_links(document_json):
document_id = document_json['document_id'] document_id = document_json["document_id"]
version_index = document_json['version_index'] version_index = document_json["version_index"]
base = os.environ['KONTROLL_BASE_URL'] + "documents/1.0/document/" + document_id + "/version/" + str(version_index) base = (
return DocumentVersionLinks(**{ os.environ["KONTROLL_BASE_URL"] + "documents/1.0/document/" + document_id + "/version/" + str(version_index)
'document_version': LinkData(**{'url': base}), )
'document_version_metadata': LinkData(**{'url': base + "/metadata"}), return DocumentVersionLinks(
'document_version_download': LinkData(**{'url': base + "/download"}), **{
'document_versions': LinkData(**{'url': base + "/versions"}), "document_version": LinkData(**{"url": base}),
'document_details': LinkData(**{'url': base + "/details"}) "document_version_metadata": LinkData(**{"url": base + "/metadata"}),
}) "document_version_download": LinkData(**{"url": base + "/download"}),
"document_versions": LinkData(**{"url": base + "/versions"}),
"document_details": LinkData(**{"url": base + "/details"}),
}
)
def get_download_instructions(self, session_id: UUID, server_context: str, current_user: User) -> SelectedDocuments: def get_download_instructions(self, session_id: UUID, server_context: str, current_user: User) -> SelectedDocuments:
def get_download_instructions_work(tx) -> SelectedDocuments: def get_download_instructions_work(tx) -> SelectedDocuments:
@@ -839,18 +843,16 @@ class DOCDB(MyDB):
document_list = list() document_list = list()
for result in results: for result in results:
document_node = result.get('document') document_node = result.get("document")
document_model = self.document_node_to_model(document_node) document_model = self.document_node_to_model(document_node)
document_list.append(document_model) document_list.append(document_model)
selected_documents = SelectedDocuments(**{'server_context': server_context, selected_documents = SelectedDocuments(**{"server_context": server_context, "documents": document_list})
'documents': document_list})
return selected_documents return selected_documents
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(get_download_instructions_work) return session.execute_read(get_download_instructions_work)
def get_upload_documents(self, upload_session: UUID, current_user: User) -> UploadDocuments: def get_upload_documents(self, upload_session: UUID, current_user: User) -> UploadDocuments:
def get_upload_documents_work(tx) -> UploadDocuments: def get_upload_documents_work(tx) -> UploadDocuments:
cypher = """ cypher = """
@@ -860,12 +862,10 @@ class DOCDB(MyDB):
AND us.upload_session = $upload_session AND us.upload_session = $upload_session
RETURN d AS document RETURN d AS document
""" """
results = tx.run(cypher, results = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
file_list = list() file_list = list()
for result in results: for result in results:
file_json = self.node_to_json(result.get('document')) file_json = self.node_to_json(result.get("document"))
file_model = FileToUpload(**file_json) file_model = FileToUpload(**file_json)
file_list.append(file_model) file_list.append(file_model)
@@ -878,16 +878,14 @@ class DOCDB(MyDB):
us.callback AS callback, us.callback AS callback,
us.session_callback_timedelta us.session_callback_timedelta
""" """
result = tx.run(cypher, result = tx.run(cypher, username=current_user.username, upload_session=upload_session)
username=current_user.username,
upload_session=upload_session)
callback = result.get('callback') callback = result.get("callback")
session_callback_timedelta = result.get('session_callback_timedelta') session_callback_timedelta = result.get("session_callback_timedelta")
upload_documents = UploadDocuments() upload_documents = UploadDocuments()
upload_documents.server_context = result.get('server_context') upload_documents.server_context = result.get("server_context")
upload_documents.callback.url = result.get('callback') upload_documents.callback.url = result.get("callback")
upload_documents.callback.expires_in = 3500 # difference between now and timedelta upload_documents.callback.expires_in = 3500 # difference between now and timedelta
upload_documents.files = file_list upload_documents.files = file_list
@@ -896,16 +894,18 @@ class DOCDB(MyDB):
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(get_upload_documents_work) return session.execute_read(get_upload_documents_work)
def get_document_version(self, document_id: UUID, version_index: int, def get_document_version(
current_user: User) -> Union[DocumentVersion, bool]: self, document_id: UUID, version_index: int, current_user: User
) -> Union[DocumentVersion, bool]:
def get_document_version_work(tx) -> Union[DocumentVersion, bool]: def get_document_version_work(tx) -> Union[DocumentVersion, bool]:
if version_index is None or version_index is False or not isinstance(version_index, int): if version_index is None or version_index is False or not isinstance(version_index, int):
version_index_criteria = 'AND d.version_index = $version_index' version_index_criteria = "AND d.version_index = $version_index"
else: else:
version_index_criteria = '' version_index_criteria = ""
cypher = """ cypher = (
"""
MATCH (u:User)-[r3:HAS_ACTIONS_ON]->(p:Project)-[r4:CONTAINS]->(d:Document) MATCH (u:User)-[r3:HAS_ACTIONS_ON]->(p:Project)-[r4:CONTAINS]->(d:Document)
WHERE u.username = $username WHERE u.username = $username
AND d.document_id = $document_id AND d.document_id = $document_id
@@ -913,19 +913,20 @@ class DOCDB(MyDB):
RETURN d AS document RETURN d AS document
ORDER by d.version_index DESC ORDER by d.version_index DESC
LIMIT 1 LIMIT 1
""" % version_index_criteria """
% version_index_criteria
)
result = tx.run(cypher, result = tx.run(
username=current_user.username, cypher, username=current_user.username, document_id=document_id, version_index=version_index
document_id=document_id, )
version_index=version_index)
first = result.single() first = result.single()
if first is None: if first is None:
print('There were no such document version.') print("There were no such document version.")
return False return False
document_node = first.get('document') document_node = first.get("document")
return self.document_node_to_model(document_node) return self.document_node_to_model(document_node)
with self.driver.session() as session: with self.driver.session() as session:
@@ -935,11 +936,12 @@ class DOCDB(MyDB):
def get_document_version_metadata_work(tx) -> DocumentMetadataEntries: def get_document_version_metadata_work(tx) -> DocumentMetadataEntries:
if version_index is None or version_index is False or not isinstance(version_index, int): if version_index is None or version_index is False or not isinstance(version_index, int):
version_index_criteria = 'AND d.version_index = $version_index' version_index_criteria = "AND d.version_index = $version_index"
else: else:
version_index_criteria = '' version_index_criteria = ""
cypher = """ cypher = (
"""
MATCH (u:User)-[r3:HAS_ACTIONS_ON]->(p:Project)-[r4:CONTAINS]->(d:Document) MATCH (u:User)-[r3:HAS_ACTIONS_ON]->(p:Project)-[r4:CONTAINS]->(d:Document)
WHERE u.username = $username WHERE u.username = $username
AND d.document_id = $document_id AND d.document_id = $document_id
@@ -947,32 +949,34 @@ class DOCDB(MyDB):
RETURN d AS document RETURN d AS document
ORDER by d.version_index DESC ORDER by d.version_index DESC
LIMIT 1 LIMIT 1
""" % version_index_criteria """
% version_index_criteria
)
result = tx.run(cypher, result = tx.run(
username=current_user.username, cypher, username=current_user.username, document_id=document_id, version_index=version_index
document_id=document_id, )
version_index=version_index)
first = result.single() first = result.single()
if first is None: if first is None:
print('There were no such document version.') print("There were no such document version.")
return False return False
document_json = self.node_to_json(first.get('document')) document_json = self.node_to_json(first.get("document"))
document_json['creation_date'] = self.bcf_time(document_json['creation_date']) document_json["creation_date"] = self.bcf_time(document_json["creation_date"])
metadata = ['title', 'version_number', 'creation_date'] metadata = ["title", "version_number", "creation_date"]
entries = list() entries = list()
for each_metadata in metadata: for each_metadata in metadata:
each_metadata_text = each_metadata.replace('_', ' ') each_metadata_text = each_metadata.replace("_", " ")
each_metadata_text = each_metadata_text.capitalize() each_metadata_text = each_metadata_text.capitalize()
entry = { entry = {
'name': each_metadata_text, "name": each_metadata_text,
'value': [document_json[each_metadata]], "value": [document_json[each_metadata]],
'data_type': DataType.string "data_type": DataType.string,
} }
entries.append(entry) entries.append(entry)
return DocumentMetadataEntries(**{'metadata': entries}) return DocumentMetadataEntries(**{"metadata": entries})
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(get_document_version_metadata_work) return session.execute_read(get_document_version_metadata_work)
@@ -984,15 +988,14 @@ class DOCDB(MyDB):
AND d.document_id = $document_id AND d.document_id = $document_id
RETURN d AS document RETURN d AS document
""" """
results = tx.run(cypher, results = tx.run(cypher, username=current_user.username, document_id=document_id)
username=current_user.username, document_versions = DocumentVersions({"documents": list()})
document_id=document_id)
document_versions = DocumentVersions({'documents': list()})
for result in results: for result in results:
document_json = self.node_to_json(result.get('document')) document_json = self.node_to_json(result.get("document"))
document_version = self.get_document_version(document_id, document_json['version_index'], current_user) document_version = self.get_document_version(document_id, document_json["version_index"], current_user)
document_versions.documents.append(document_version) document_versions.documents.append(document_version)
return document_versions return document_versions
with self.driver.session() as session: with self.driver.session() as session:
return session.execute_read(get_document_versions_work) return session.execute_read(get_document_versions_work)
@@ -36,11 +36,13 @@ class FoundationDB(MyDB):
CALL apoc.ttl.expireIn(ac, $time_delta, 's') CALL apoc.ttl.expireIn(ac, $time_delta, 's')
RETURN ac AS authorization_code RETURN ac AS authorization_code
""" """
result = tx.run(cypher, result = tx.run(
username=username, cypher,
authorization_code=authorization_code, username=username,
scope=scope, authorization_code=authorization_code,
time_delta=int(os.environ['SECURITY_AUTHORIZATION_CODE_EXPIRE_SECONDS'])) scope=scope,
time_delta=int(os.environ["SECURITY_AUTHORIZATION_CODE_EXPIRE_SECONDS"]),
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 1: if summary.counters.nodes_created < 1:
raise HTTPException(status_code=400, detail="Authorization code was not created.") raise HTTPException(status_code=400, detail="Authorization code was not created.")
@@ -58,14 +60,13 @@ class FoundationDB(MyDB):
RETURN RETURN
username, scope username, scope
""" """
result = tx.run(cypher, result = tx.run(cypher, authorization_code=authorization_code)
authorization_code=authorization_code)
first = result.single() first = result.single()
if first is None: if first is None:
raise HTTPException(status_code=404, detail="Authorization code not found.") raise HTTPException(status_code=404, detail="Authorization code not found.")
authorized_user = TokenData() authorized_user = TokenData()
authorized_user.username = first.get("username") authorized_user.username = first.get("username")
authorized_user.scopes = first.get("scope").split(' ') authorized_user.scopes = first.get("scope").split(" ")
return authorized_user return authorized_user
with self.driver.session() as session: with self.driver.session() as session:
@@ -73,11 +74,11 @@ class FoundationDB(MyDB):
token_info = TokenInfo() token_info = TokenInfo()
token_info.access_token = create_access_token( token_info.access_token = create_access_token(
user_info.dict(), user_info.dict(), timedelta(seconds=int(os.environ["SECURITY_ACCESS_TOKEN_EXPIRE_SECONDS"]))
timedelta(seconds=int(os.environ['SECURITY_ACCESS_TOKEN_EXPIRE_SECONDS']))) )
token_info.refresh_token = create_access_token( token_info.refresh_token = create_access_token(
user_info.dict(), user_info.dict(), timedelta(seconds=int(os.environ["SECURITY_REFRESH_TOKEN_EXPIRE_SECONDS"]))
timedelta(seconds=int(os.environ['SECURITY_REFRESH_TOKEN_EXPIRE_SECONDS']))) )
def add_tokens_and_delete_code_work(tx) -> bool: def add_tokens_and_delete_code_work(tx) -> bool:
cypher = """ cypher = """
@@ -92,13 +93,15 @@ class FoundationDB(MyDB):
SET t3.value = $refresh_token SET t3.value = $refresh_token
SET t3.hash = $refresh_token_hash SET t3.hash = $refresh_token_hash
""" """
result = tx.run(cypher, result = tx.run(
authorization_code=authorization_code, cypher,
username=user_info.username, authorization_code=authorization_code,
access_token=token_info.access_token, username=user_info.username,
access_token_hash=hashlib.md5(token_info.access_token.encode('utf-8')).hexdigest(), access_token=token_info.access_token,
refresh_token=token_info.refresh_token, access_token_hash=hashlib.md5(token_info.access_token.encode("utf-8")).hexdigest(),
refresh_token_hash=hashlib.md5(token_info.refresh_token.encode('utf-8')).hexdigest()) refresh_token=token_info.refresh_token,
refresh_token_hash=hashlib.md5(token_info.refresh_token.encode("utf-8")).hexdigest(),
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 1 or summary.counters.nodes_deleted < 1: if summary.counters.nodes_created < 1 or summary.counters.nodes_deleted < 1:
@@ -119,24 +122,25 @@ class FoundationDB(MyDB):
RETURN RETURN
at.value AS access_token at.value AS access_token
""" """
refresh_token_payload = jwt.decode(refresh_token, refresh_token_payload = jwt.decode(
secrets['security_secret_key'], refresh_token, secrets["security_secret_key"], algorithms=[os.environ["SECURITY_ALGORITHM"]]
algorithms=[os.environ['SECURITY_ALGORITHM']]) )
username_from_refresh_token: str = refresh_token_payload.get("username") username_from_refresh_token: str = refresh_token_payload.get("username")
print('refresh_token_username: ', username_from_refresh_token) print("refresh_token_username: ", username_from_refresh_token)
result = tx.run(cypher, result = tx.run(
username=username_from_refresh_token, cypher,
refresh_token_hash=hashlib.md5(refresh_token.encode('utf-8')).hexdigest() username=username_from_refresh_token,
) refresh_token_hash=hashlib.md5(refresh_token.encode("utf-8")).hexdigest(),
)
first = result.single() first = result.single()
if first is None: if first is None:
raise HTTPException(status_code=404, detail="Access token not found.") raise HTTPException(status_code=404, detail="Access token not found.")
token_info = TokenInfo() token_info = TokenInfo()
token_info.access_token = first.get("access_token") token_info.access_token = first.get("access_token")
token_info.refresh_token = refresh_token token_info.refresh_token = refresh_token
access_token_payload = jwt.decode(token_info.access_token, access_token_payload = jwt.decode(
secrets['security_secret_key'], token_info.access_token, secrets["security_secret_key"], algorithms=[os.environ["SECURITY_ALGORITHM"]]
algorithms=[os.environ['SECURITY_ALGORITHM']]) )
username_from_access_token: str = access_token_payload.get("username") username_from_access_token: str = access_token_payload.get("username")
if username_from_access_token is None: if username_from_access_token is None:
raise credentials_exception raise credentials_exception
@@ -148,11 +152,11 @@ class FoundationDB(MyDB):
got_token_data, got_token_info = session.execute_read(use_refresh_to_get_access_work) got_token_data, got_token_info = session.execute_read(use_refresh_to_get_access_work)
new_token_info = TokenInfo() new_token_info = TokenInfo()
new_token_info.access_token = create_access_token( new_token_info.access_token = create_access_token(
got_token_data.dict(), got_token_data.dict(), timedelta(seconds=int(os.environ["SECURITY_ACCESS_TOKEN_EXPIRE_SECONDS"]))
timedelta(seconds=int(os.environ['SECURITY_ACCESS_TOKEN_EXPIRE_SECONDS']))) )
new_token_info.refresh_token = create_access_token( new_token_info.refresh_token = create_access_token(
got_token_data.dict(), got_token_data.dict(), timedelta(seconds=int(os.environ["SECURITY_REFRESH_TOKEN_EXPIRE_SECONDS"]))
timedelta(seconds=int(os.environ['SECURITY_REFRESH_TOKEN_EXPIRE_SECONDS']))) )
def update_tokens_work(tx) -> bool: def update_tokens_work(tx) -> bool:
cypher = """ cypher = """
@@ -168,12 +172,14 @@ class FoundationDB(MyDB):
rt.hash = $refresh_token_hash, rt.hash = $refresh_token_hash,
at.hash = $access_token_hash at.hash = $access_token_hash
""" """
result = tx.run(cypher, result = tx.run(
username=got_token_data.username, cypher,
access_token=new_token_info.access_token, username=got_token_data.username,
refresh_token=new_token_info.refresh_token, access_token=new_token_info.access_token,
access_token_hash=hashlib.md5(new_token_info.access_token.encode('utf-8')).hexdigest(), refresh_token=new_token_info.refresh_token,
refresh_token_hash=hashlib.md5(new_token_info.refresh_token.encode('utf-8')).hexdigest()) access_token_hash=hashlib.md5(new_token_info.access_token.encode("utf-8")).hexdigest(),
refresh_token_hash=hashlib.md5(new_token_info.refresh_token.encode("utf-8")).hexdigest(),
)
summary = result.consume() summary = result.consume()
if summary.counters.nodes_created < 2 or summary.counters.nodes_deleted < 2: if summary.counters.nodes_created < 2 or summary.counters.nodes_deleted < 2:
raise HTTPException(status_code=400, detail="Tokens were not deleted and created.") raise HTTPException(status_code=400, detail="Tokens were not deleted and created.")
@@ -183,4 +189,5 @@ class FoundationDB(MyDB):
session.execute_write(update_tokens_work) session.execute_write(update_tokens_work)
return new_token_info return new_token_info
foundation_db = FoundationDB(driver) foundation_db = FoundationDB(driver)
@@ -3,8 +3,8 @@ from glob import glob
def get_secrets(): def get_secrets():
secrets = dict() secrets = dict()
for var in glob('/run/secrets/*'): for var in glob("/run/secrets/*"):
k = var.split('/')[-1] k = var.split("/")[-1]
v = open(var).read().rstrip('\n') v = open(var).read().rstrip("\n")
secrets[k] = v secrets[k] = v
return secrets return secrets
+15 -22
View File
@@ -16,25 +16,20 @@ from security.secrets import get_secrets
secrets = get_secrets() secrets = get_secrets()
# password context # password context
crypt_context = CryptContext( crypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
schemes=["bcrypt"],
deprecated="auto")
oauth2_scheme = OAuth2AuthorizationCodeBearer( oauth2_scheme = OAuth2AuthorizationCodeBearer(
authorizationUrl='foundation/oauth2/auth', authorizationUrl="foundation/oauth2/auth",
tokenUrl='foundation/oauth2/token', tokenUrl="foundation/oauth2/token",
scopes={ scopes={"test": "Full access, but only test data.", "user": "Normal user access.", "admin": "Full access to all."},
'test': 'Full access, but only test data.', )
'user': 'Normal user access.',
'admin': 'Full access to all.'
})
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials", detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"}, headers={"WWW-Authenticate": "Bearer"},
) )
def create_access_token(data: dict, expires_delta: timedelta | None = None): def create_access_token(data: dict, expires_delta: timedelta | None = None):
@@ -44,7 +39,7 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None):
else: else:
expire = datetime.utcnow() + timedelta(minutes=15) expire = datetime.utcnow() + timedelta(minutes=15)
payload.update({"expires": str(expire)}) payload.update({"expires": str(expire)})
encoded_jwt = jwt.encode(payload, secrets['security_secret_key'], algorithm=os.environ['SECURITY_ALGORITHM']) encoded_jwt = jwt.encode(payload, secrets["security_secret_key"], algorithm=os.environ["SECURITY_ALGORITHM"])
return encoded_jwt return encoded_jwt
@@ -79,20 +74,18 @@ async def get_current_user(security_scopes: SecurityScopes, token: str = Depends
print(authenticate_value) print(authenticate_value)
try: try:
print('Token: ', token) print("Token: ", token)
payload = jwt.decode(token, payload = jwt.decode(token, secrets["security_secret_key"], algorithms=[os.environ["SECURITY_ALGORITHM"]])
secrets['security_secret_key'],
algorithms=[os.environ['SECURITY_ALGORITHM']])
username_from_token: str = payload.get("username") username_from_token: str = payload.get("username")
print('Token username: ', username_from_token) print("Token username: ", username_from_token)
if username_from_token is None: if username_from_token is None:
raise credentials_exception raise credentials_exception
token_scopes = payload.get("scopes", []) token_scopes = payload.get("scopes", [])
print('Token scopes: ', token_scopes) print("Token scopes: ", token_scopes)
token_data = TokenData(scopes=token_scopes, username=username_from_token) token_data = TokenData(scopes=token_scopes, username=username_from_token)
except JWTError: except JWTError:
print('JWTError') print("JWTError")
raise credentials_exception raise credentials_exception
user = db.get_user(username=token_data.username) user = db.get_user(username=token_data.username)