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