import base64 import os import jsonpickle from fastapi import UploadFile, HTTPException from uuid import UUID, uuid4 from database.neo4j import MyDB, driver from models.bcf_request import * from models.bcf_response import * from models.request import * class BCFDB(MyDB): # implemented def get_projects(self, current_user: User) -> List[ProjectGET]: def get_projects_work(tx) -> List[ProjectGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project) WHERE u.username = $username AND r1.read = True RETURN p AS project """ results = tx.run(cypher, username=current_user.username) project_model_list = list() for result in results: project_json = self.node_to_json(result.get("project")) project_model = ProjectGET(**project_json) project_model_list.append(project_model) return project_model_list with self.driver.session() as session: return session.execute_read(get_projects_work) # implemented def get_project(self, project_id: UUID, current_user: User) -> ProjectGET: def get_project_work(tx) -> ProjectGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id RETURN p AS project """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") project_node = first.get("project") project_dict = self.node_to_json(project_node) return ProjectGET(**project_dict) with self.driver.session() as session: return session.execute_read(get_project_work) # implemented def put_project(self, project_id: UUID, project: ProjectPUT, current_user: User) -> ProjectGET: def put_project_work(tx) -> ProjectGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project) WHERE u.username = $username AND r1.update = True AND p.project_id = $project_id SET p.name = $project_name RETURN p AS project """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), project_name=project.name) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") project_node = first.get("project") project_dict = self.node_to_json(project_node) return ProjectGET(**project_dict) with self.driver.session() as session: return session.execute_write(put_project_work) # implemented def get_project_extensions(self, project_id: UUID, current_user: User) -> ExtensionsGET: def get_project_extensions_work(tx) -> ExtensionsGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(e:Extensions) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id RETURN e AS extensions """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") extensions_node = first.get("extensions") extensions_dict = self.node_to_json(extensions_node) return ExtensionsGET(**extensions_dict) with self.driver.session() as session: return session.execute_read(get_project_extensions_work) # implemented # returns a collection def get_topics(self, project_id: str, current_user: User) -> List[TopicGET]: def get_topics_work(tx) -> List[TopicGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id RETURN t AS topic, ID(t) AS server_assigned_id """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id)) topic_model_list = list() for result in results: topic_json = self.node_to_json(result.get("topic")) topic_json["server_assigned_id"] = result.get("server_assigned_id") topic_model = TopicGET(**topic_json) topic_model_list.append(topic_model) return topic_model_list with self.driver.session() as session: return session.execute_read(get_topics_work) # implemented def get_topic(self, project_id: UUID, topic_id: UUID, current_user: User) -> TopicGET: def get_topic_work(tx) -> TopicGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id RETURN t AS topic, ID(t) AS server_assigned_id """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") topic_node = first.get("topic") topic_dict = self.node_to_json(topic_node) topic_dict["server_assigned_id"] = first.get("server_assigned_id") return TopicGET(**topic_dict) with self.driver.session() as session: return session.execute_read(get_topic_work) # implemented def post_topic(self, project_id: UUID, topic: TopicPOST, current_user: User) -> TopicGET: def post_topic_work(tx) -> UUID: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project) WHERE u.username = $username AND r1.createTopic = True AND p.project_id = $project_id MERGE (p)-[r2:HAS]->(t:Topic {guid: $guid}) SET t.creation_date = $creation_date, t.modified_date = $creation_date, t.creation_author = $username, t.modified_author = $username, t.topic_type = $topic_type, t.topic_status = $topic_status, t.title = $title, t.priority = $priority, t.index = $index, t.assigned_to = $assigned_to, t.stage = $stage, t.description = $description, t.due_date = $due_date RETURN t AS topic, ID(t) AS server_assigned_id """ if not hasattr(topic, 'guid') or topic.guid is None: topic.guid = uuid4() creation_date = self.timestamp() print(cypher) result = tx.run(cypher, username=current_user.username, project_id=str(project_id), guid=str(topic.guid), creation_date=creation_date, topic_type=topic.topic_type, topic_status=topic.topic_status, title=topic.title, priority=topic.priority, index=topic.index, assigned_to=topic.assigned_to, stage=topic.stage, description=topic.description, due_date=topic.due_date) summary = result.consume() if summary.counters.nodes_created < 1: raise HTTPException(status_code=400, detail="Item not added.") return topic.guid with self.driver.session() as session: topic_guid = session.execute_write(post_topic_work) return self.get_topic(project_id, topic_guid, current_user) # implemented def put_topic(self, project_id: UUID, topic_id: UUID, topic: TopicPUT, current_user: User) -> TopicGET: def put_topic_work(tx) -> bool: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.update = True AND p.project_id = $project_id AND t.guid = $topic_id SET t.topic_type = $topic_type, t.topic_status = $topic_status, t.title = $title, t.priority = $priority, t.index = $index, t.modified_date = $modified_date, t.modified_author = $username, t.assigned_to = $assigned_to, t.stage = $stage, t.description = $description, t.due_date = $due_date RETURN t AS topic, ID(t) AS server_assigned_id """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), topic_type=topic.topic_type, topic_status=topic.topic_status, title=topic.title, priority=topic.priority, index=topic.index, modified_date=self.timestamp(), assigned_to=topic.assigned_to, stage=topic.stage, description=topic.description, due_date=topic.due_date) summary = result.consume() if summary.counters.properties_set < 1: raise HTTPException(status_code=404, detail="Item not found.") return True with self.driver.session() as session: session.execute_write(put_topic_work) return self.get_topic(project_id, topic_id, current_user) # implemented def set_topic_modified_date(self, project_id: UUID, topic_id: UUID, current_user: User) -> bool: def set_topic_modified_date_work(tx) -> bool: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.update = True AND p.project_id = $project_id AND t.guid = $topic_id SET t.modified_date = $modified_date """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), modified_date=self.timestamp()) summary = result.consume() if summary.counters.properties_set < 1: raise HTTPException(status_code=404, detail="Item not found.") return True with self.driver.session() as session: return session.execute_write(set_topic_modified_date_work) # implemented def delete_topic(self, project_id: UUID, topic_id: UUID, current_user: User) -> int: def delete_topic_work(tx) -> int: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.update = True AND p.project_id = $project_id AND t.guid = $topic_id DETACH DELETE t """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) result_summary = result.consume() if result_summary.counters.nodes_deleted < 1: raise HTTPException(status_code=404, detail="Item not found.") return result_summary.counters.nodes_deleted with self.driver.session() as session: return session.execute_write(delete_topic_work) # implemented # returns a collection def get_files_information(self, project_id: UUID, current_user: User) -> List[ProjectFileInformation]: def get_files_information_work(tx) -> List[ProjectFileInformation]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:CONTAINS]->(d:Document) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id RETURN d AS document """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id)) project_file_informations = list() for result in results: document_json = self.node_to_json(result.get("document")) display_information_list = list() display_information_json = { 'field_display_name': 'File', 'field_value': document_json['title'] } display_information_list.append(display_information_json) base = os.environ['KONTROLL_BASE_URL'] + "documents/1.0/document/" base.replace('http://', 'https://') base.replace('https://', 'open-cde-documents://') document_id = document_json['document_id'] version_index = document_json['version_index'] extra_path = document_id + "/version/" + str(version_index) reference = base + extra_path file_get_json = { 'ifc_project': document_json['ifc_project'], 'filename': document_json['name'], 'reference': reference } project_file_information = ProjectFileInformation(**{ 'display_information': display_information_list, 'file': file_get_json }) project_file_informations.append(project_file_information) return project_file_informations # # BCF API compatible servers offer endpoints to list project model files, which are stored on a CDE. # To indicate that models provided by BCF API file-references can be downloaded from the CDE using # the Documents API, the reference part of a file_GET response from the BCF server will include # the URL scheme (protocol) open-cde-documents and a link to the document_version URL on the CDE. # # The actual URL scheme (protocol) that a Client Application should # use to call the document_version URL should be https. # # BCF API project_file_information example: # # { # "display_information": [ # { # "field_display_name": "Model Name", # "field_value": "ARCH-Z100-051" # } # ], # "file": { # "ifc_project": "0J$yPqHBD12v72y4qF6XcD", # "file_name": "OfficeBuilding_Architecture_0001.ifc", # "reference": "open-cde-documents://" # } # } # # In the example above, the CDE returns a value of # open-cde-documents:// for the file reference part, # indicating that a Documents API capable client can call https:// # to get the document version information. From there on, compatible # clients can retrieve metadata and the binary content of the file. # # class ProjectFileInformation(BaseModel): # display_information: Optional[List[ProjectFileDisplayInformation]] = None # file: Optional[FileGET] = None # # class ProjectFileDisplayInformation(BaseModel): # field_display_name: str # field_value: str # # class FileGET(BaseModel): # ifc_project: Optional[str] = None # ifc_spatial_structure_element: Optional[str] = None # filename: Optional[str] = None # date: Optional[str] = None # reference: Optional[str] = None # with self.driver.session() as session: return session.execute_read(get_files_information_work) # implemented # returns a collection def get_files(self, project_id: UUID, topic_id: UUID, current_user: User) -> List[FileGET]: def get_files_work(tx) -> List[FileGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:REFERS_TO]->(f:Document:Model) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id RETURN f AS file """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) file_model_list = list() for result in results: file_json = self.node_to_json(result.get("file")) file_model = FileGET(**file_json) file_model_list.append(file_model) return file_model_list with self.driver.session() as session: return session.execute_read(get_files_work) # implemented def put_files(self, project_id: UUID, topic_id: UUID, files: List[FilePUT], current_user: User) -> List[FileGET]: def put_files_work(tx) -> bool: cypher_delete_references = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:REFERS_TO]->(f:Document:Model) WHERE u.username = $username AND r1.updateFiles = True AND p.project_id = $project_id AND t.guid = $topic_id DELETE r3 """ tx.run(cypher_delete_references, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) cypher_update_and_attach_references = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(f:Document:Model) MATCH (u)-[r1]->(p)-[r3:HAS]->(t:Topic) WHERE u.username = $username AND r1.updateFiles = True AND p.project_id = $project_id AND f.reference = $reference AND t.guid = $topic_id SET f.filename = $filename, f.date = $date, f.ifc_project = $ifc_project, f.ifc_spatial_structure_element = $ifc_spatial_structure_element MERGE (t)-[r4:REFERS_TO]->(f) RETURN f """ for file in files: result = tx.run(cypher_update_and_attach_references, username=current_user.username, project_id=str(project_id), reference=file.reference, topic_id=str(topic_id), filename=file.filename, date=file.date, ifc_project=file.ifc_project, ifc_spatial_structure_element=file.ifc_spatial_structure_element) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") return True with self.driver.session() as session: session.execute_write(put_files_work) return self.get_files(project_id, topic_id, current_user) # implemented def get_comments(self, project_id: UUID, topic_id: UUID, current_user: User) -> List[CommentGET]: def get_comments_work(tx) -> List[CommentGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(c:Comment) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id RETURN c AS comment """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) comment_model_list = list() for result in results: comment_json = self.node_to_json(result.get("comment")) comment_json['topic_guid'] = str(topic_id) comment_model = CommentGET(**comment_json) comment_model_list.append(comment_model) return comment_model_list with self.driver.session() as session: return session.execute_read(get_comments_work) # implemented def get_comment(self, project_id: UUID, topic_id: UUID, comment_id: UUID, current_user: User) -> CommentGET: def get_comment_work(tx) -> CommentGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(c:Comment) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND c.guid = $comment_id RETURN c AS comment """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), comment_id=str(comment_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") comment_node = first.get("comment") comment_dict = self.node_to_json(comment_node) comment_dict['topic_guid'] = str(topic_id) comment = CommentGET(**comment_dict) return comment with self.driver.session() as session: return session.execute_read(get_comment_work) # date # author # topic_guid def post_comment(self, project_id: UUID, topic_id: UUID, comment: CommentPOST, current_user: User) -> CommentGET: def post_comment_work(tx) -> UUID: if not hasattr(comment, 'guid') or comment.guid is None: this_comment_guid = uuid4() else: this_comment_guid = UUID(comment.guid) if comment.viewpoint_guid: # make sure that given viewpoint_guid exists in database viewpoints = self.get_viewpoints(project_id, topic_id, current_user) viewpoint_guids = [viewpoint.guid for viewpoint in viewpoints] if comment.viewpoint_guid not in viewpoint_guids: raise HTTPException(status_code=400, detail="Item not added.") injection_viewpoint = '-[r3:HAS]->(v:Viewpoint)' injection_viewpoint_selection = 'AND v.guid = $viewpoint_id' injection_viewpoint_relation = 'MERGE (c2)-[r7:RELATED_TO]->(v)' else: injection_viewpoint = '' injection_viewpoint_selection = '' injection_viewpoint_relation = '' if hasattr(comment, 'reply_to_comment_guid'): # make sure that given reply_to_comment_guid exists in database get_comments = self.get_comments(project_id, topic_id, current_user) comment_guids = [get_comment.guid for get_comment in get_comments] if comment.reply_to_comment_guid not in comment_guids: raise HTTPException(status_code=400, detail="Item not added.") injection_existing_comment = 'MATCH (u)-[r1]->(p)-[r2]->(t)-[r4:HAS]->(c1:Comment)' injection_existing_comment_selection = 'AND c1.guid = $reply_to_comment_id' injection_existing_comment_relation = 'MERGE (c1)-[r5:THEN]->(c2)' else: injection_existing_comment = '' injection_existing_comment_selection = '' injection_existing_comment_relation = '' cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)%s %s WHERE u.username = $username AND r1.createComment = True AND p.project_id = $project_id AND t.guid = $topic_id %s %s MERGE (t)-[r5:HAS]->(c2:Comment {guid: $comment_id}) %s %s SET c2.date = $creation_date, c2.modified_date = $creation_date, c2.author = $username, c2.modified_author = $username, c2.comment = $comment """ % (injection_viewpoint, injection_existing_comment, injection_viewpoint_selection, injection_existing_comment_selection, injection_viewpoint_relation, injection_existing_comment_relation) result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(comment.viewpoint_guid), reply_to_comment_id=str('comment.reply_to_comment_guid'), comment_id=str(this_comment_guid), creation_date=self.timestamp(), comment=comment.comment) summary = result.consume() if summary.counters.nodes_created < 1: raise HTTPException(status_code=400, detail="Item not added.") return this_comment_guid with self.driver.session() as session: comment_guid = session.execute_write(post_comment_work) return self.get_comment(project_id, topic_id, comment_guid, current_user) def put_comment(self, project_id: UUID, topic_id: UUID, comment_id: UUID, comment: CommentPUT, current_user: User) -> CommentGET: def put_comment_work(tx) -> bool: if comment.viewpoint_guid: viewpoints = self.get_viewpoints(project_id, topic_id, current_user) viewpoint_guids = [viewpoint.guid for viewpoint in viewpoints] if comment.viewpoint_guid not in viewpoint_guids: raise HTTPException(status_code=400, detail="Item not added.") injection_new_viewpoint = 'MATCH (t)-[r5:HAS]->(v_new:Viewpoint)' injection_new_viewpoint_selection = 'WHERE v_new.guid = $new_rtv_id' injection_new_viewpoint_relation = 'MERGE (c)-[r5:RELATED_TO]->(v)' else: injection_new_viewpoint = '' injection_new_viewpoint_selection = '' injection_new_viewpoint_relation = '' cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(c:Comment) WHERE u.username = $username AND r1.updateComment = True AND p.project_id = $project_id AND t.guid = $topic_id AND c.guid = $comment_id OPTIONAL MATCH (c)-[r4:RELATED_TO]->(v_existing:Viewpoint) %s %s SET c.comment = $comment SET c.modified_date = $modified_date SET c.modified_author = $username DELETE r4 %s """ % (injection_new_viewpoint, injection_new_viewpoint_selection, injection_new_viewpoint_relation) result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), comment_id=str(comment_id), new_rtv_id=str(comment.viewpoint_guid), comment=comment.comment, modified_date=self.timestamp()) summary = result.consume() if summary.counters.properties_set < 1: raise HTTPException(status_code=400, detail="Item not added.") return True with self.driver.session() as session: session.execute_write(put_comment_work) return self.get_comment(project_id, topic_id, comment_id, current_user) # implemented def delete_comment(self, project_id: UUID, topic_id: UUID, comment_id: UUID, current_user: User) -> int: def delete_comment_work(tx) -> int: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(c:Comment) WHERE u.username = $username AND r1.update = True AND p.project_id = $project_id AND t.guid = $topic_id AND c.guid = $comment_id DETACH DELETE c """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), comment_id=str(comment_id)) result_summary = result.consume() if result_summary.counters.nodes_deleted < 1: raise HTTPException(status_code=404, detail="Item not found.") return result_summary.counters.nodes_deleted with self.driver.session() as session: self.set_topic_modified_date(project_id, topic_id, current_user) return session.execute_write(delete_comment_work) @staticmethod def enum_to_mime(enum_type) -> str: if str(enum_type) == 'png': mime_type = 'image/png' elif str(enum_type) == 'jpg' or str(enum_type) == 'jpeg': mime_type = 'image/jpeg' else: mime_type = 'image/' + str(enum_type) return mime_type # implementing def post_viewpoint(self, project_id: UUID, topic_id: UUID, viewpoint: ViewpointPOST, current_user: User) -> ViewpointGET: def post_viewpoint_work(tx) -> str: if hasattr(viewpoint, 'snapshot'): snapshot_type = self.enum_to_mime(viewpoint.snapshot.snapshot_type.value) snapshot = True set_snapshot = 'v.snapshot = $snapshot, v.snapshot_type = $snapshot_type,' else: snapshot_type = '' snapshot = False set_snapshot = '' cypher_viewpoint = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.createViewpoint = True AND p.project_id = $project_id AND t.guid = $topic_id MERGE (t)-[r3:HAS]->(v:Viewpoint {guid: $guid, index: $index}) SET v.camera_definition = $camera_definition, v.camera_view_point = point({x: $cvp_x, y: $cvp_y, z: $cvp_z}), v.camera_direction = point({x: $cad_x, y: $cad_y, z: $cad_z}), v.camera_up_vector = point({x: $cuv_x, y: $cuv_y, z: $cuv_z}), v.view_tws_or_fo_view = $view_tws_or_fo_view, v.aspect_ratio = $aspect_ratio, %s v.default_visibility = $default_visibility, v.spaces_visible = $spaces_visible, v.space_boundaries_visible = $space_boundaries_visible, v.openings_visible = $openings_visible """ % set_snapshot if viewpoint.guid is None: viewpoint.guid = uuid4() if viewpoint.orthogonal_camera is None: camera_definition = 'perspective_camera' view_tws_or_fo_view = viewpoint.perspective_camera.field_of_view aspect_ratio = viewpoint.perspective_camera.aspect_ratio cvp = viewpoint.perspective_camera.camera_view_point cad = viewpoint.perspective_camera.camera_direction cuv = viewpoint.perspective_camera.camera_up_vector else: camera_definition = 'orthogonal_camera' view_tws_or_fo_view = viewpoint.orthogonal_camera.view_to_world_scale aspect_ratio = viewpoint.orthogonal_camera.aspect_ratio cvp = viewpoint.orthogonal_camera.camera_view_point cad = viewpoint.orthogonal_camera.camera_direction cuv = viewpoint.orthogonal_camera.camera_up_vector result = tx.run(cypher_viewpoint, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), guid=str(viewpoint.guid), index=viewpoint.index, camera_definition=camera_definition, cvp_x=cvp.x, cvp_y=cvp.y, cvp_z=cvp.z, cad_x=cad.x, cad_y=cad.y, cad_z=cad.z, cuv_x=cuv.x, cuv_y=cuv.y, cuv_z=cuv.z, view_tws_or_fo_view=view_tws_or_fo_view, aspect_ratio=aspect_ratio, snapshot=snapshot, snapshot_type=snapshot_type, default_visibility=viewpoint.components.visibility.default_visibility, spaces_visible=viewpoint.components.visibility.view_setup_hints.spaces_visible, space_boundaries_visible=viewpoint.components.visibility.view_setup_hints.space_boundaries_visible, openings_visible=viewpoint.components.visibility.view_setup_hints.openings_visible) summary = result.consume() if summary.counters.nodes_created < 1: print(cypher_viewpoint) raise HTTPException(status_code=400, detail="Item not added.") # LINES for line in viewpoint.lines: cypher_line = """ MATCH (v:Viewpoint) WHERE v.guid = $guid MERGE (v)-[r1:HAS]->(l:Line) SET l.start_point = point({x: $lsp_x, y: $lsp_y, z: $lsp_z}), l.end_point = point({x: $lep_x, y: $lep_y, z: $lep_z}) """ result = tx.run(cypher_line, guid=str(viewpoint.guid), lsp_x=line.start_point.x, lsp_y=line.start_point.y, lsp_z=line.start_point.z, lep_x=line.end_point.x, lep_y=line.end_point.y, lep_z=line.end_point.z) summary = result.consume() if summary.counters.nodes_created < 1: print(cypher_line) raise HTTPException(status_code=400, detail="Item not added.") # CLIPPING PLANES for clipping_plane in viewpoint.clipping_planes: cypher_clipping_plane = """ MATCH (v:Viewpoint) WHERE v.guid = $guid MERGE (v)-[r1:HAS]->(cp:ClippingPlane) SET cp.location = point({x: $cpl_x, y: $cpl_y, z: $cpl_z}), cp.direction = point({x: $cpd_x, y: $cpd_y, z: $cpd_z}) """ result = tx.run(cypher_clipping_plane, guid=str(viewpoint.guid), lsp_x=clipping_plane.location.x, lsp_y=clipping_plane.location.y, lsp_z=clipping_plane.location.z, lep_x=clipping_plane.direction.x, lep_y=clipping_plane.direction.y, lep_z=clipping_plane.direction.z) summary = result.consume() if summary.counters.nodes_created < 1: print(cypher_clipping_plane) raise HTTPException(status_code=400, detail="Item not added.") # BITMAPS for bitmap in viewpoint.bitmaps: cypher_bitmap = """ MATCH (v:Viewpoint) WHERE v.guid = $guid MERGE (v)-[r1:HAS]->(b:Bitmap) SET b.type = $type, b.location = point({x: $bml_x, y: $bml_y, z: $bml_z}), b.normal = point({x: $bmn_x, y: $bmn_y, z: $bmn_z}), b.up = point({x: $bmu_x, y: $bmu_y, z: $bmu_z}), b.height = $height """ bitmap_type = self.enum_to_mime(bitmap.bitmap_type) result = tx.run(cypher_bitmap, guid=str(viewpoint.guid), type=bitmap_type, bml_x=bitmap.location.x, bml_y=bitmap.location.y, bml_z=bitmap.location.z, bmn_x=bitmap.direction.x, bmn_y=bitmap.direction.y, bmn_z=bitmap.direction.z, bmu_x=bitmap.direction.x, bmu_y=bitmap.direction.y, bmu_z=bitmap.direction.z, height=bitmap.height) summary = result.consume() if summary.counters.nodes_created < 1: print(bitmap_type) raise HTTPException(status_code=400, detail="Item not added.") # COMPONENTS components = {} for selected_component in viewpoint.components.selection: if selected_component.ifc_guid not in components: components[selected_component.ifc_guid] = {} components[selected_component.ifc_guid]['selected'] = True components[selected_component.ifc_guid]['originating_system'] = selected_component.originating_system components[selected_component.ifc_guid]['authoring_tool_id'] = selected_component.authoring_tool_id for color in viewpoint.components.coloring: for colored_component in color.components: if colored_component.ifc_guid not in components: components[colored_component.ifc_guid] = {} components[colored_component.ifc_guid]['color'] = color.color for exception_component in viewpoint.components.visibility.exceptions: if exception_component.ifc_guid not in components: components[exception_component.ifc_guid] = {} components[exception_component.ifc_guid]['visibility_exception'] = True set_list = list() for ifc_guid in components.keys(): set_list.append('c.ifc_guid = $ifc_guid') if 'selected' in components[ifc_guid]: selected = True originating_system = components[ifc_guid]['originating_system'] authoring_tool_id = components[ifc_guid]['authoring_tool_id'] set_list.append('c.selected = $selected') set_list.append('c.originating_system = $originating_system') set_list.append('c.authoring_tool_id = $authoring_tool_id') else: selected = False originating_system = False authoring_tool_id = False if 'color' in components[ifc_guid]: color = components[ifc_guid]['color'] set_list.append('c.color = $color') else: color = False if 'visibility_exception' in components[ifc_guid]: visibility_exception = True set_list.append('c.visibility_exception = $visibility_exception') else: visibility_exception = False set_string = ', '.join(set_list) cypher_component = """ MATCH (v:Viewpoint) WHERE v.guid = $viewpoint_id MERGE (v)-[r1:HAS]->(c:Component {ifc_guid: $ifc_guid}) SET """ cypher_component += set_string tx.run(cypher_component, viewpoint_id=str(viewpoint.guid), ifc_guid=str(ifc_guid), selected=selected, originating_system=originating_system, authoring_tool_id=authoring_tool_id, color=color, visibility_exception=visibility_exception) summary = result.consume() # Look for component UUIDs in IFC-graph and relate them for ifc_guid in components.keys(): cypher_link = """ MATCH (prod:IfcProduct) WHERE prod.GlobalId = $uuid MATCH (c:Component) WHERE c.ifc_guid = $uuid MERGE (c)-[r:SAME_AS]->(prod) """ tx.run(cypher_link, uuid=str(ifc_guid)) result.consume() cypher_link = """ MATCH (prod:IfcProduct) -[r1:IsDefinedBy]->(rs:IfcRelationship) -[r2:RelatingPropertyDefinition]->(ps:IfcPropertySet) -[r3:HasProperties]->(prop:IfcProperty) -[r4:NominalValue]->(root:Root) WHERE prod.GlobalId = $uuid AND ps.Name = 'Reuse' AND prop.Name = 'GTIN' MATCH (c:Component) WHERE c.ifc_guid = $uuid SET c.GTIN = root.wrappedValue """ tx.run(cypher_link, uuid=str(ifc_guid)) result.consume() if summary.counters.nodes_created < 1: print(cypher_component) raise HTTPException(status_code=400, detail="Item not added.") else: snapshot_path = '' bitmap_path = '' try: snapshot_name = 'snapshot_' + str(viewpoint.guid) file_ending = '.' + str(viewpoint.snapshot.snapshot_type.value) snapshot_path = 'data/snapshots/' + snapshot_name + file_ending with open(snapshot_path, "wb") as snapshot_file: snapshot_image = base64.b64decode(viewpoint.snapshot.snapshot_data, validate=True) snapshot_file.write(snapshot_image) snapshot_file.close() for bitmap in viewpoint.bitmaps: bitmap_name = 'bitmap_' + str(bitmap.guid) file_ending = '.' + str(bitmap.bitmap_type.value) bitmap_path = 'data/bitmaps/' + bitmap_name + file_ending with open(bitmap_path, "wb") as bitmap_file: bitmap_image = base64.b64decode(bitmap.bitmap_data, validate=True) bitmap_file.write(bitmap_image) bitmap_file.close() except Exception as e: print('could not create file ' + snapshot_path + ' ' + bitmap_path + ' ') print(e) raise HTTPException(status_code=400, detail="Item not added.") return viewpoint.guid with self.driver.session() as session: viewpoint.guid = session.execute_write(post_viewpoint_work) viewpoint.guid = UUID(viewpoint.guid) self.set_topic_modified_date(project_id, topic_id, current_user) return self.get_viewpoint(project_id, topic_id, viewpoint.guid, current_user) # implemented # returns a collection def get_viewpoints(self, project_id: UUID, topic_id: UUID, current_user: User) -> List[ViewpointGET]: def get_viewpoints_work(tx) -> List[ViewpointGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id RETURN v.guid AS viewpoint_id """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) viewpoint_model_list = list() for result in results: viewpoint_id = result.get("viewpoint_id") viewpoint_model = self.get_viewpoint(project_id, topic_id, viewpoint_id, current_user) viewpoint_model_list.append(viewpoint_model) return viewpoint_model_list with self.driver.session() as session: return session.execute_read(get_viewpoints_work) # pending def get_viewpoint(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> ViewpointGET: def get_viewpoint_work(tx) -> ViewpointGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id RETURN v AS viewpoint """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") v_node = first.get("viewpoint") print(v_node) v = self.node_to_json(v_node) print(v) lines = self.get_viewpoint_lines(project_id, topic_id, viewpoint_id, current_user) clipping_planes = self.get_viewpoint_clipping_planes(project_id, topic_id, viewpoint_id, current_user) bitmaps = self.get_viewpoint_bitmaps(project_id, topic_id, viewpoint_id, current_user) snapshot_get = { 'snapshot_type': v['snapshot_type'].split('/', 2)[1] } viewpoint_get = { 'guid': v['guid'], 'index': v['index'], 'lines': lines, 'clipping_planes': clipping_planes, 'bitmaps': bitmaps, 'snapshot': snapshot_get } cvp = v['camera_view_point'] cad = v['camera_direction'] cuv = v['camera_up_vector'] camera = { 'camera_view_point': {'x': cvp.x, 'y': cvp.y, 'z': cvp.z}, 'camera_direction': {'x': cad.x, 'y': cad.y, 'z': cad.z}, 'camera_up_vector': {'x': cuv.x, 'y': cuv.y, 'z': cuv.z}, 'aspect_ratio': v['aspect_ratio'] } if v['camera_definition'] == 'orthogonal_camera': camera['view_to_world_scale'] = v['view_tws_or_fo_view'] viewpoint_get['orthogonal_camera'] = camera elif v['camera_definition'] == 'perspective_camera': camera['field_of_view'] = v['view_tws_or_fo_view'] viewpoint_get['perspective_camera'] = camera jsonpickle.dumps(v) jsonpickle.dumps(viewpoint_get) return ViewpointGET(**viewpoint_get) with self.driver.session() as session: return session.execute_read(get_viewpoint_work) def get_viewpoint_lines(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> List[Line]: def get_viewpoint_lines_work(tx) -> List[Line]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(l:Line) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id RETURN l AS line """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) line_model_list = list() for result in results: line_node = result.get("line") line_json = { 'start_point': line_node.start_point, 'end_point': line_node.end_point } line_model = Line(**line_json) line_model_list.append(line_model) return line_model_list with self.driver.session() as session: return session.execute_read(get_viewpoint_lines_work) def get_viewpoint_clipping_planes(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> List[ClippingPlane]: def get_viewpoint_clipping_planes_work(tx) -> List[ClippingPlane]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(cp:ClippingPlane) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id RETURN cp AS clipping_plane """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) clipping_plane_model_list = list() for result in results: clipping_plane_json = self.node_to_json(result.get("clipping_plane")) clipping_plane_model = ClippingPlane(**clipping_plane_json) clipping_plane_model_list.append(clipping_plane_model) return clipping_plane_model_list with self.driver.session() as session: return session.execute_read(get_viewpoint_clipping_planes_work) def get_viewpoint_bitmaps(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> List[BitmapGET]: def get_viewpoint_bitmaps_work(tx) -> List[BitmapGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(b:Bitmap) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id RETURN b AS bitmap """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) bitmap_model_list = list() for result in results: bitmap_json = self.node_to_json(result.get("bitmap")) bitmap_model = BitmapGET(**bitmap_json) bitmap_model_list.append(bitmap_model) return bitmap_model_list with self.driver.session() as session: return session.execute_read(get_viewpoint_bitmaps_work) def get_viewpoint_bitmap(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, bitmap_id: UUID, current_user: User) -> BitmapGET: def get_viewpoint_bitmap_work(tx) -> BitmapGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(b:Bitmap) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id AND b.guid = $bitmap_id RETURN b AS bitmap """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id), bitmap_id=str(bitmap_id)) bitmap_json = self.node_to_json(result.get("bitmap")) return BitmapGET(**bitmap_json) with self.driver.session() as session: return session.execute_read(get_viewpoint_bitmap_work) def get_viewpoint_snapshot(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> str: def get_viewpoint_snapshot_work(tx) -> str: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id AND v.snapshot IS NOT NULL RETURN v.snapshot_type AS snapshot_type """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") snapshot_type = first.get("snapshot_type") return snapshot_type with self.driver.session() as session: return session.execute_read(get_viewpoint_snapshot_work) def get_viewpoint_colored_components(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> ColoringGET: def get_viewpoint_colored_components_work(tx) -> ColoringGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(c:Component) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id AND c.color IS NOT NULL RETURN c AS component ORDER BY c.color """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) coloring_list = list() color = '' coloring = {} occasion = 0 for result in results: component_json = self.node_to_json(result.get("component")) if component_json['color'] != color: if occasion > 0: coloring_list.append(coloring) occasion += 1 color = component_json['color'] coloring = { 'color': color, 'components': [] } coloring['components'].append(component_json) coloring_list.append(coloring) coloring_get = {'coloring': coloring_list} return ColoringGET(**coloring_get) with self.driver.session() as session: return session.execute_read(get_viewpoint_colored_components_work) def get_viewpoint_selected_components(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> SelectionGET: def get_viewpoint_selected_components_work(tx) -> SelectionGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(c:Component) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id AND c.selected IS NOT NULL RETURN c AS component """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) component_list = list() for result in results: component_json = self.node_to_json(result.get("component")) component_list.append(component_json) selection_get = {'selection': component_list} return SelectionGET(**selection_get) with self.driver.session() as session: return session.execute_read(get_viewpoint_selected_components_work) def get_viewpoint_components_visibility(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> VisibilityGET: def get_viewpoint_components_visibility_work(tx) -> VisibilityGET: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id RETURN v AS viewpoint """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) first = result.single() if first is None: raise HTTPException(status_code=404, detail="Item not found.") viewpoint_json = self.node_to_json(first.get("viewpoint")) view_setup_hints = { 'spaces_visible': viewpoint_json['spaces_visible'], 'space_boundaries_visible': viewpoint_json['space_boundaries_visible'], 'openings_visible': viewpoint_json['openings_visible'] } visibility = { 'default_visibility': viewpoint_json['default_visibility'], 'view_setup_hints': view_setup_hints } cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS]->(c:Component) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id AND c.visibility_exception IS NOT NULL RETURN c AS component """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) component_list = list() for result in results: component_json = self.node_to_json(result.get("component")) component_list.append(component_json) visibility['exceptions'] = component_list visibility_get = {'visibility': visibility} return VisibilityGET(**visibility_get) with self.driver.session() as session: return session.execute_read(get_viewpoint_components_visibility_work) # implemented def delete_viewpoint(self, project_id: UUID, topic_id: UUID, viewpoint_id: UUID, current_user: User) -> int: def delete_viewpoint_work(tx) -> int: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:HAS]->(v:Viewpoint)-[r4:HAS*]->(n) WHERE u.username = $username AND r1.update = True AND p.project_id = $project_id AND t.guid = $topic_id AND v.guid = $viewpoint_id DETACH DELETE v, n """ result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), viewpoint_id=str(viewpoint_id)) result_summary = result.consume() if result_summary.counters.nodes_deleted < 1: raise HTTPException(status_code=404, detail="Item not found.") return result_summary.counters.nodes_deleted with self.driver.session() as session: self.set_topic_modified_date(project_id, topic_id, current_user) return session.execute_write(delete_viewpoint_work) # implemented # returns a collection def get_related_topics(self, project_id: UUID, topic_id: UUID, current_user: User) -> List[TopicGET]: def get_related_topics_work(tx) -> List[TopicGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t1:Topic)-[r3:RELATED_TO]->(t2:Topic) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t1.guid = $topic_id RETURN t2 AS topic, ID(t2) AS server_assigned_id """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) topic_model_list = list() for result in results: topic_json = self.node_to_json(result.get("topic")) topic_json["server_assigned_id"] = result.get("server_assigned_id") topic_model = TopicGET(**topic_json) topic_model_list.append(topic_model) return topic_model_list with self.driver.session() as session: return session.execute_read(get_related_topics_work) def put_related_topics(self, project_id: UUID, topic_id: UUID, related_topics: List[RelatedTopicPUT], current_user: User) -> List[TopicGET]: def put_related_topics_work(tx) -> bool: for related_topic in related_topics: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t1:Topic) MATCH (p)-[r2:HAS]->(t2:Topic) WHERE u.username = $username AND r1.updateRelatedTopics = True AND p.project_id = $project_id AND t1.guid = $topic_id AND t2.guid = $related_topic_id MERGE (t1)-[r3:RELATED_TO]->(t2:Topic) """ tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=topic_id, related_topic_id=related_topic.related_topic_guid) return True with self.driver.session() as session: session.execute_write(put_related_topics_work) self.set_topic_modified_date(project_id, topic_id, current_user) return self.get_related_topics(project_id, topic_id, current_user) # returns a collection def get_topic_document_references(self, project_id: UUID, topic_id: UUID, current_user: User) -> List[DocumentReferenceGET]: def get_topic_document_references_work(tx) -> List[DocumentReferenceGET]: cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic)-[r3:REFERS_TO]->(d:Document) WHERE u.username = $username AND r1.read = True AND p.project_id = $project_id AND t.guid = $topic_id RETURN r3.guid AS reference_guid, d.guid AS document_guid, d.url AS document_url, d.description AS document_description """ results = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id)) document_reference_model_list = list() for result in results: document_reference_model = DocumentReferenceGET(**result) document_reference_model_list.append(document_reference_model) return document_reference_model_list with self.driver.session() as session: return session.execute_read(get_topic_document_references_work) def post_topic_document_reference(self, project_id: UUID, topic_id: UUID, document_reference: DocumentReferencePOST, current_user: User) -> List[DocumentReferenceGET]: def post_topic_document_reference_work(tx) -> bool: if document_reference.guid is None: document_reference.guid = uuid4() if document_reference.document_guid is None or len(document_reference.url) > 4 > len( document_reference.document_guid): document_reference.document_guid = uuid4() document_url = 'd.url = $url,' else: document_url = '' document_reference.url = '' cypher = """ MATCH (u:User)-[r1:HAS_ACTIONS_ON]->(p:Project)-[r2:HAS]->(t:Topic) WHERE u.username = $username AND r1.updateDocumentReferences = True AND p.project_id = $project_id AND t.guid = $topic_id MERGE (t)-[r3:REFERS_TO]->(d:Document {guid: $document_id}) SET r3.guid: $document_reference_id, %s d.description = $description """ % document_url result = tx.run(cypher, username=current_user.username, project_id=str(project_id), topic_id=str(topic_id), document_id=str(document_reference.document_guid), document_reference_id=str(document_reference.guid), url=document_reference.url, description=document_reference.description) summary = result.consume() if summary.counters.nodes_created < 1: raise HTTPException(status_code=400, detail="Item not added.") return True with self.driver.session() as session: session.execute_write(post_topic_document_reference_work) self.set_topic_modified_date(project_id, topic_id, current_user) return self.get_topic_document_references(project_id, topic_id, current_user) def put_topic_document_references(self, project_id: UUID, topic_id: UUID, reference_id: UUID, document_reference: DocumentReferencePUT, current_user: User) -> List[DocumentReferenceGET]: document_reference.guid = reference_id document_reference_post = DocumentReferencePOST(document_reference) topic_document_references_response = self.post_topic_document_reference(project_id, topic_id, document_reference_post, current_user) self.set_topic_modified_date(project_id, topic_id, current_user) return topic_document_references_response # returns a collection def get_documents(self, project_id: UUID, current_user: User): return True def post_document(self, project_id: UUID, file: UploadFile, current_user: User): return True def get_document(self, project_id: UUID, document_id: UUID, current_user: User): return True # returns a collection def get_topics_events(self, project_id: UUID, topic_id: UUID, current_user: User): return True # returns a collection def get_topic_events(self, project_id: UUID, topic_id: UUID, current_user: User): return True # returns a collection def get_comments_events(self, project_id: UUID, UUID, current_user: User): return True # returns a collection def get_comment_events(self, project_id: UUID, topic_id: UUID, comment_id: UUID, current_user: User): return True bcf_db = BCFDB(driver)