mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +00:00
1475 lines
71 KiB
Python
1475 lines
71 KiB
Python
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://<document_version_url>"
|
|
# }
|
|
# }
|
|
#
|
|
# In the example above, the CDE returns a value of
|
|
# open-cde-documents://<document_version_url> for the file reference part,
|
|
# indicating that a Documents API capable client can call https://<document_version_url>
|
|
# 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)
|