REVAMPED FILE IMPORT.
Fixed some bugs.
This commit is contained in:
@@ -126,6 +126,37 @@ class EntitiesDB:
|
||||
|
||||
return returnValue
|
||||
|
||||
def addEntities(self, entsJsonList: list, fromServer: bool = False):
|
||||
self.dbLock.acquire()
|
||||
returnValue = []
|
||||
|
||||
for entJson in entsJsonList:
|
||||
# Check if we're overwriting an existing entity
|
||||
exists = None
|
||||
if entJson.get('uid') is not None:
|
||||
exists = self.getEntityNoLock(entJson.get('uid'))
|
||||
|
||||
entity = self.resourceHandler.getEntityJson(
|
||||
entJson.get('Entity Type'),
|
||||
entJson)
|
||||
|
||||
if entity is None:
|
||||
continue
|
||||
# Use uid as key. Code is holdover from time where primary field == uid.
|
||||
self.database.add_node(entity['uid'], **entity)
|
||||
returnValue.append(entity)
|
||||
if exists:
|
||||
# Update canvases if the node already exists.
|
||||
self.mainWindow.updateEntityNodeLabelsOnCanvases(entity['uid'], entity[list(entity)[1]])
|
||||
if not fromServer:
|
||||
self.mainWindow.sendLocalDatabaseUpdateToServer(entity, True)
|
||||
self.mainWindow.populateEntitiesWidget(entity, add=True)
|
||||
|
||||
self.dbLock.release()
|
||||
self.resetTimeline()
|
||||
|
||||
return returnValue
|
||||
|
||||
def addLink(self, linkJson: dict, fromServer=False):
|
||||
"""
|
||||
Add a link between two entities in the database.
|
||||
@@ -145,10 +176,22 @@ class EntitiesDB:
|
||||
"no uid to database.")
|
||||
else:
|
||||
linkUID = link['uid']
|
||||
self.database.add_edge(linkUID[0], linkUID[1], **link)
|
||||
if exists:
|
||||
newRes = link.get('Resolution')
|
||||
newNotes = link.get('Notes')
|
||||
if newRes and newRes != exists['Resolution']:
|
||||
link['Resolution'] = exists['Resolution'] + ' | ' + newRes
|
||||
if newNotes and newNotes != exists['Notes']:
|
||||
link['Notes'] = exists['Notes'] + '\n\n' + str(newNotes)
|
||||
exists.update(link)
|
||||
link.update(exists)
|
||||
# Update canvases if the link already exists.
|
||||
# We can do this before updating the database here because the GUI will be updated only after this
|
||||
# function returns. If we ever execute this function outside the main event loop, we will need
|
||||
# to alter the execution flow.
|
||||
self.mainWindow.updateLinkLabelsOnCanvases(linkUID[0] + linkUID[1], link['Resolution'])
|
||||
self.database.add_edge(linkUID[0], linkUID[1], **link)
|
||||
|
||||
self.dbLock.release()
|
||||
if not fromServer:
|
||||
self.mainWindow.sendLocalDatabaseUpdateToServer(link, True)
|
||||
@@ -234,10 +277,9 @@ class EntitiesDB:
|
||||
self.dbLock.release()
|
||||
return returnValue
|
||||
|
||||
def removeEntity(self, uid: str, fromServer=False):
|
||||
def removeEntity(self, uid: str, fromServer=False, updateTimeLine=True):
|
||||
"""
|
||||
Removes the entity with the given uid, if it
|
||||
exists.
|
||||
Removes the entity with the given uid, if it exists.
|
||||
"""
|
||||
self.dbLock.acquire()
|
||||
ent = None
|
||||
@@ -250,7 +292,8 @@ class EntitiesDB:
|
||||
self.mainWindow.handleGroupNodeUpdateAfterEntityDeletion(uid) # Blocking - locks the db.
|
||||
if not fromServer:
|
||||
self.mainWindow.sendLocalDatabaseUpdateToServer(ent, False)
|
||||
self.updateTimeline(ent, False)
|
||||
if updateTimeLine:
|
||||
self.updateTimeline(ent, False)
|
||||
|
||||
def removeLink(self, uid, fromServer=False):
|
||||
"""
|
||||
@@ -278,6 +321,23 @@ class EntitiesDB:
|
||||
self.dbLock.release()
|
||||
return result
|
||||
|
||||
def getEntityOfType(self, primaryAttr: str, entityType: str):
|
||||
"""
|
||||
Checks if an entity with the specified primary attribute exists, and if it does, return it.
|
||||
"""
|
||||
result = None
|
||||
primaryField = self.resourceHandler.getPrimaryFieldForEntityType(entityType)
|
||||
if primaryField is None:
|
||||
return result
|
||||
self.dbLock.acquire()
|
||||
for node in self.database.nodes():
|
||||
details = self.database.nodes[node]
|
||||
if details['Entity Type'] == entityType and details[primaryField] == primaryAttr:
|
||||
result = dict(details)
|
||||
break
|
||||
self.dbLock.release()
|
||||
return result
|
||||
|
||||
def getLinkIfExists(self, uid):
|
||||
"""
|
||||
Returns the attributes of the given link uid as a dict.
|
||||
@@ -349,14 +409,14 @@ class EntitiesDB:
|
||||
self.dbLock.release()
|
||||
return returnValue
|
||||
|
||||
def isLinkNoLock(self, uid: tuple):
|
||||
def isLinkNoLock(self, uid: tuple) -> Union[bool, dict]:
|
||||
"""
|
||||
Returns True if the uid given exists as a link, and False otherwise.
|
||||
|
||||
Used only in this class, as it does not lock.
|
||||
"""
|
||||
if self.database.edges.get(uid) is not None:
|
||||
return True
|
||||
return self.database.edges[uid]
|
||||
return False
|
||||
|
||||
def getEntityType(self, uid: str):
|
||||
|
||||
@@ -491,7 +491,7 @@ class TabbedPane(QtWidgets.QTabWidget):
|
||||
self.canvasTabs[canvas].scene().addLinkProgrammatic(linkUID, lJson['Resolution'], fromServer=True)
|
||||
self.canvasTabs[canvas].scene().rearrangeGraph()
|
||||
|
||||
def nodeRemoveAllHelper(self, nodeUID) -> None:
|
||||
def nodeRemoveAllHelper(self, nodeUID: str) -> None:
|
||||
for canvas in self.canvasTabs:
|
||||
currentScene = self.canvasTabs[canvas].scene()
|
||||
if nodeUID in currentScene.nodesDict:
|
||||
@@ -679,8 +679,11 @@ class CanvasView(QtWidgets.QGraphicsView):
|
||||
if isinstance(item, Entity.BaseNode):
|
||||
if isinstance(item, Entity.GroupNode):
|
||||
for childUID in list(item.groupedNodesUid):
|
||||
self.tabbedPane.mainWindow.deleteSpecificEntity(childUID)
|
||||
self.tabbedPane.mainWindow.deleteSpecificEntity(item.uid)
|
||||
self.tabbedPane.nodeRemoveAllHelper(childUID)
|
||||
self.tabbedPane.mainWindow.LENTDB.removeEntity(childUID, updateTimeLine=False)
|
||||
self.tabbedPane.nodeRemoveAllHelper(item.uid)
|
||||
self.tabbedPane.mainWindow.LENTDB.removeEntity(item.uid, updateTimeLine=False)
|
||||
self.tabbedPane.mainWindow.LENTDB.resetTimeline()
|
||||
|
||||
def adjustSceneRect(self) -> None:
|
||||
self.scene().adjustSceneRect()
|
||||
@@ -1404,8 +1407,8 @@ class CanvasScene(QtWidgets.QGraphicsScene):
|
||||
try:
|
||||
return self.nodesDict[uid]
|
||||
except KeyError:
|
||||
self.parent().messageHandler.error('Canvas state is undefined: Tried to draw a link involving a '
|
||||
'node not present in the canvas, uid: ' + uid)
|
||||
self.parent().messageHandler.error('Canvas state is undefined: Tried to get a node not present in the '
|
||||
'canvas, uid: ' + str(uid))
|
||||
return None
|
||||
|
||||
def removeGroupNodeLinksForUID(self, groupUID, nodeUID) -> None:
|
||||
|
||||
@@ -19,6 +19,7 @@ from typing import Union
|
||||
from urllib import parse
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
from playwright.sync_api import sync_playwright, Error, TimeoutError
|
||||
|
||||
@@ -298,66 +299,158 @@ class MenuBar(QtWidgets.QMenuBar):
|
||||
importDialog = ImportFromFileDialog(self)
|
||||
importDialogAccept = importDialog.exec_()
|
||||
return_results = []
|
||||
fileDirectory = importDialog.fileDirectory
|
||||
newLinks = []
|
||||
fileDirectory = Path(importDialog.fileDirectoryLine.text())
|
||||
if importDialogAccept and fileDirectory != '':
|
||||
sceneToAddTo = None
|
||||
if importDialog.importToCanvasCheckbox.isChecked():
|
||||
sceneToAddTo = self.parent().centralWidget().tabbedPane.getSceneByName(
|
||||
importDialog.importToCanvasDropdown.currentText())
|
||||
if importDialog.CSVFileChoice.isChecked():
|
||||
if fileDirectory.exists() and fileDirectory.is_file():
|
||||
sceneToAddTo = None
|
||||
|
||||
try:
|
||||
csvContents = pd.read_csv(fileDirectory, usecols=['EntityType', 'EntityField', 'EntityValue'])
|
||||
for entity in range(len(csvContents['EntityType'])):
|
||||
if csvContents['EntityType'][entity] in self.parent().RESOURCEHANDLER.getAllEntities():
|
||||
if csvContents['EntityField'][entity] in list(
|
||||
self.parent().RESOURCEHANDLER.getEntityJson(csvContents['EntityType'][entity])):
|
||||
return_results.append(
|
||||
[{csvContents['EntityField'][entity]: str(csvContents['EntityValue'][entity]),
|
||||
'Entity Type': csvContents['EntityType'][entity]}])
|
||||
else:
|
||||
self.parent().MESSAGEHANDLER.warning(
|
||||
'Please Check that the format of the csv provided is as described by '
|
||||
f'the documentation. Malformed entity detected at line {entity + 1}. The '
|
||||
f'field of the entity is Invalid', popUp=True)
|
||||
else:
|
||||
self.parent().MESSAGEHANDLER.warning(
|
||||
"Please Check that the format of the csv provided is as described by "
|
||||
f"the documentation. Malformed entity detected at line {entity + 1}. The "
|
||||
f"entity type is Invalid", popUp=True)
|
||||
except ValueError:
|
||||
self.parent().MESSAGEHANDLER.warning("Please check that the format of the csv provided is as "
|
||||
"described by the documentation. It should have 3 columns in "
|
||||
"order of EntityType, EntityField, EntityValue. All the "
|
||||
"'Values' should be filled accordingly", popUp=True)
|
||||
return_results = []
|
||||
elif importDialog.textFileChoice.isChecked():
|
||||
importDialog = ImportFromTextFileDialog(self)
|
||||
if importDialog.exec_():
|
||||
txtFileContents = open(fileDirectory, 'r')
|
||||
selectedEntityType = importDialog.importTypeDropdown.currentText()
|
||||
jsonOfField = self.parent().RESOURCEHANDLER.getEntityJson(selectedEntityType)
|
||||
primary_field = list(jsonOfField)[1]
|
||||
for line in txtFileContents.readlines():
|
||||
if re.match(r'\w', line):
|
||||
return_results.append(
|
||||
[{primary_field: line,
|
||||
'Entity Type': selectedEntityType}])
|
||||
newNodeUIDs = []
|
||||
newLinks = []
|
||||
for newNode in return_results:
|
||||
newNodeJson = self.parent().LENTDB.addEntity(newNode[0])
|
||||
if newNodeJson is not None:
|
||||
newNodeUIDs.append(newNodeJson['uid'])
|
||||
else:
|
||||
newNodeUIDs.append(None)
|
||||
if importDialog.textFileChoice.isChecked():
|
||||
fileContents = []
|
||||
with open(fileDirectory, 'r') as importFile:
|
||||
# Read a maximum of 3 lines from the file:
|
||||
count = 0
|
||||
for line in importFile:
|
||||
fileContents.append(line.strip())
|
||||
count += 1
|
||||
if count >= 3:
|
||||
break
|
||||
|
||||
self.parent().centralWidget().tabbedPane.linkAddHelper(newLinks)
|
||||
importTextFileDialog = ImportFromTextFileDialog(self, fileContents)
|
||||
if importTextFileDialog.exec_():
|
||||
if importTextFileDialog.importToCanvasCheckbox.isChecked():
|
||||
sceneToAddTo = self.parent().centralWidget().tabbedPane.getSceneByName(
|
||||
importTextFileDialog.importToCanvasDropdown.currentText())
|
||||
|
||||
if sceneToAddTo is not None:
|
||||
for newNodeUID in newNodeUIDs:
|
||||
if newNodeUID is not None:
|
||||
sceneToAddTo.addNodeProgrammatic(newNodeUID)
|
||||
sceneToAddTo.rearrangeGraph()
|
||||
selectedEntityType = importTextFileDialog.importTypeDropdown.currentText()
|
||||
primary_field = importTextFileDialog.typePrimaryFieldValueLabel.text()
|
||||
|
||||
with open(fileDirectory, 'r') as importFile:
|
||||
for line in importFile:
|
||||
lineValue = line.strip()
|
||||
if lineValue:
|
||||
return_results.append({primary_field: lineValue,
|
||||
'Entity Type': selectedEntityType})
|
||||
elif importDialog.CSVFileChoice.isChecked():
|
||||
csvDF = pd.read_csv(fileDirectory)
|
||||
|
||||
# Remove duplicate column names
|
||||
csvDF = csvDF.loc[:, ~csvDF.columns.duplicated()]
|
||||
|
||||
# Fill NaN values with an empty string
|
||||
csvDF.fillna('')
|
||||
|
||||
# If we have less than 2 rows, we cannot import
|
||||
rowNumber = len(csvDF.index)
|
||||
if rowNumber < 2:
|
||||
raise ValueError("Invalid CSV file data - Not enough rows.")
|
||||
|
||||
importEntityCSVDialog = ImportEntityFromCSVFile(self, csvDF)
|
||||
if importEntityCSVDialog.exec_():
|
||||
attributeRows = [comboBox.currentText()
|
||||
for comboBox in importEntityCSVDialog.fieldMappingComboBoxes]
|
||||
for attribute in range(len(attributeRows)):
|
||||
if attributeRows[attribute] == '':
|
||||
attributeRows[attribute] = csvDF.columns[attribute]
|
||||
|
||||
if importEntityCSVDialog.importToCanvasCheckbox.isChecked():
|
||||
sceneToAddTo = self.parent().centralWidget().tabbedPane.getSceneByName(
|
||||
importEntityCSVDialog.importToCanvasDropdown.currentText())
|
||||
|
||||
entityTypeToImportAs = importEntityCSVDialog.entityTypeChoiceDropdown.currentText()
|
||||
for row in csvDF.itertuples(index=False):
|
||||
newEntityJSON = {str(attributeRows[key]): str(row[key])
|
||||
for key in range(len(attributeRows))}
|
||||
newEntityJSON['Entity Type'] = entityTypeToImportAs
|
||||
return_results.append(newEntityJSON)
|
||||
|
||||
elif importDialog.CSVFileChoiceLinks.isChecked():
|
||||
csvDF = pd.read_csv(fileDirectory)
|
||||
|
||||
# Remove duplicate column names
|
||||
csvDF = csvDF.loc[:, ~csvDF.columns.duplicated()]
|
||||
|
||||
# Fill NaN values with an empty string
|
||||
csvDF.fillna('')
|
||||
|
||||
# If we have less than 2 rows, we cannot import
|
||||
rowNumber = len(csvDF.index)
|
||||
if rowNumber < 2:
|
||||
raise ValueError("Invalid CSV file data - Not enough rows.")
|
||||
|
||||
importLinksCSVDialog = ImportLinksFromCSVFile(self, csvDF)
|
||||
if importLinksCSVDialog.exec_():
|
||||
entityOneType = importLinksCSVDialog.entityOneTypeChoiceDropdown.currentText()
|
||||
entityTwoType = importLinksCSVDialog.entityTwoTypeChoiceDropdown.currentText()
|
||||
|
||||
for row in csvDF.itertuples(index=False):
|
||||
count = 0
|
||||
linkJSON = {}
|
||||
entityOneJSON = {}
|
||||
entityTwoJSON = {}
|
||||
resolutionID = ""
|
||||
notes = ""
|
||||
|
||||
for column in row:
|
||||
column = str(column)
|
||||
mapping = importLinksCSVDialog.fieldMappingComboBoxes[count].currentText()
|
||||
if mapping == 'Entity One':
|
||||
entityOneJSON = self.parent().LENTDB.getEntityOfType(column, entityOneType)
|
||||
elif mapping == 'Entity Two':
|
||||
entityTwoJSON = self.parent().LENTDB.getEntityOfType(column, entityTwoType)
|
||||
elif mapping == 'Notes':
|
||||
notes = column
|
||||
elif mapping == 'Resolution ID':
|
||||
resolutionID = column
|
||||
else:
|
||||
linkJSON[csvDF.columns[count]] = column
|
||||
count += 1
|
||||
|
||||
if (entityOneJSON is not None) and (entityTwoJSON is not None):
|
||||
linkJSON['uid'] = (entityOneJSON['uid'], entityTwoJSON['uid'])
|
||||
linkJSON['Notes'] = notes
|
||||
if importLinksCSVDialog.randAsIs.isChecked():
|
||||
linkJSON['Resolution'] = resolutionID
|
||||
elif importLinksCSVDialog.randMerge.isChecked():
|
||||
linkJSON['Resolution'] = resolutionID + ' | ' + str(uuid4())
|
||||
elif importLinksCSVDialog.randReplace.isChecked():
|
||||
linkJSON['Resolution'] = str(uuid4())
|
||||
|
||||
self.parent().LENTDB.addLink(linkJSON)
|
||||
newLinks.append((entityOneJSON['uid'], entityTwoJSON['uid'],
|
||||
linkJSON['Resolution']))
|
||||
|
||||
newNodeUIDs = [newEntity['uid'] for newEntity in self.parent().LENTDB.addEntities(return_results)]
|
||||
|
||||
if sceneToAddTo is not None:
|
||||
for newNodeUID in newNodeUIDs:
|
||||
if newNodeUID is not None:
|
||||
sceneToAddTo.addNodeProgrammatic(newNodeUID)
|
||||
sceneToAddTo.rearrangeGraph()
|
||||
|
||||
if newLinks:
|
||||
self.parent().centralWidget().tabbedPane.addLinksToTabs(newLinks, 'File Links')
|
||||
self.parent().LENTDB.resetTimeline()
|
||||
|
||||
except PermissionError:
|
||||
self.parent().MESSAGEHANDLER.error('No permission to access the file at the path provided.',
|
||||
popUp=True, exc_info=False)
|
||||
except UnicodeDecodeError:
|
||||
self.parent().MESSAGEHANDLER.error('File path provided points to a binary file that cannot be '
|
||||
'interpreted as text.', popUp=True, exc_info=False)
|
||||
except FileNotFoundError:
|
||||
self.parent().MESSAGEHANDLER.error('File path provided does not point to an existing file.',
|
||||
popUp=True, exc_info=False)
|
||||
except TypeError:
|
||||
self.parent().MESSAGEHANDLER.error('Type Error occurred while processing file. Please ensure that '
|
||||
'you have selected a file of the appropriate type.',
|
||||
popUp=True, exc_info=False)
|
||||
except Exception as e:
|
||||
self.parent().MESSAGEHANDLER.error('Exception occurred while processing file: ' + str(e),
|
||||
popUp=True, exc_info=False)
|
||||
else:
|
||||
self.parent().MESSAGEHANDLER.error('Invalid file path provided!', popUp=True, exc_info=False)
|
||||
|
||||
def savePic(self) -> None:
|
||||
canvasSaveDialog = CanvasPictureDialog(self)
|
||||
@@ -1176,40 +1269,184 @@ class ViewAndStopResolutionsDialogOption(QtWidgets.QPushButton):
|
||||
self.parent().layout().removeRow(self)
|
||||
|
||||
|
||||
class ImportFromFileDialog(QtWidgets.QDialog):
|
||||
def popupFileDialog(self):
|
||||
self.fileDirectory = QtWidgets.QFileDialog().getOpenFileName(parent=self, caption='Select file to import',
|
||||
options=QtWidgets.QFileDialog.DontUseNativeDialog,
|
||||
filter="CSV or txt (*.csv *.txt)")[0]
|
||||
if self.fileDirectory != '':
|
||||
self.fileDirectoryLine.setText(self.fileDirectory)
|
||||
class ImportLinksFromCSVFile(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
super(ImportFromFileDialog, self).__init__(parent=parent)
|
||||
self.fileDirectory = ''
|
||||
self.setWindowTitle('Import From File')
|
||||
def __init__(self, parent, csvTableContents: pd.DataFrame):
|
||||
super(ImportLinksFromCSVFile, self).__init__(parent=parent)
|
||||
|
||||
self.setStyleSheet(Stylesheets.MAIN_WINDOW_STYLESHEET)
|
||||
self.setModal(True)
|
||||
importLayout = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(importLayout)
|
||||
|
||||
dialogLayout = QtWidgets.QGridLayout()
|
||||
self.setLayout(dialogLayout)
|
||||
descriptionLabel = QtWidgets.QLabel('Choose the filetype you selected')
|
||||
descriptionLabel.setWordWrap(True)
|
||||
dialogLayout.addWidget(descriptionLabel, 0, 0, 1, 2)
|
||||
columnNumber = len(csvTableContents.columns)
|
||||
|
||||
self.fileDirectoryButton = QtWidgets.QPushButton("Select file...")
|
||||
self.fileDirectoryLine = QtWidgets.QLineEdit()
|
||||
self.fileDirectoryLine.setReadOnly(True)
|
||||
self.textFileChoice = QtWidgets.QRadioButton('Text file')
|
||||
self.textFileChoice.setStyleSheet(Stylesheets.RADIO_BUTTON_STYLESHEET)
|
||||
self.textFileChoice.setChecked(True)
|
||||
self.CSVFileChoice = QtWidgets.QRadioButton('CSV (Please see documentation for formatting)')
|
||||
self.CSVFileChoice.setStyleSheet(Stylesheets.RADIO_BUTTON_STYLESHEET)
|
||||
titleLabel = QtWidgets.QLabel("Import Links from CSV")
|
||||
titleLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
dialogLayout.addWidget(self.fileDirectoryLine, 1, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.fileDirectoryButton, 2, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.textFileChoice, 3, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.CSVFileChoice, 4, 0, 1, 2)
|
||||
descLabel = QtWidgets.QLabel("Select the Entity Type of the Parent entity (Entity One) and the Entity Type of "
|
||||
"the Child entity (Entity Two). Then, map these entities to columns in the CSV "
|
||||
"file. You may also map the rest of the fields to the other columns of the CSV "
|
||||
"file. Lastly, choose how to import and / or generate the ID for each Resolution.")
|
||||
descLabel.setWordWrap(True)
|
||||
|
||||
entityOneTypeChoiceWidget = QtWidgets.QWidget()
|
||||
entityOneTypeChoiceLayout = QtWidgets.QHBoxLayout()
|
||||
entityOneTypeChoiceWidget.setLayout(entityOneTypeChoiceLayout)
|
||||
entityOneTypeChoiceLabel = QtWidgets.QLabel("Entity One type:")
|
||||
self.entityOneTypeChoiceDropdown = QtWidgets.QComboBox()
|
||||
self.entityOneTypeChoiceDropdown.setEditable(False)
|
||||
self.entityOneTypeChoiceDropdown.addItems(parent.parent().RESOURCEHANDLER.getAllEntities())
|
||||
entityOneTypeChoiceLayout.addWidget(entityOneTypeChoiceLabel)
|
||||
entityOneTypeChoiceLayout.addWidget(self.entityOneTypeChoiceDropdown)
|
||||
|
||||
entityTwoTypeChoiceWidget = QtWidgets.QWidget()
|
||||
entityTwoTypeChoiceLayout = QtWidgets.QHBoxLayout()
|
||||
entityTwoTypeChoiceWidget.setLayout(entityTwoTypeChoiceLayout)
|
||||
entityTwoTypeChoiceLabel = QtWidgets.QLabel("Entity Two type:")
|
||||
self.entityTwoTypeChoiceDropdown = QtWidgets.QComboBox()
|
||||
self.entityTwoTypeChoiceDropdown.setEditable(False)
|
||||
self.entityTwoTypeChoiceDropdown.addItems(parent.parent().RESOURCEHANDLER.getAllEntities())
|
||||
entityTwoTypeChoiceLayout.addWidget(entityTwoTypeChoiceLabel)
|
||||
entityTwoTypeChoiceLayout.addWidget(self.entityTwoTypeChoiceDropdown)
|
||||
|
||||
self.fieldMappingComboBoxes = []
|
||||
tableFieldAttributeMapping = QtWidgets.QWidget()
|
||||
tableFieldAttributeMappingLayout = QtWidgets.QHBoxLayout()
|
||||
tableFieldAttributeMapping.setLayout(tableFieldAttributeMappingLayout)
|
||||
for fieldIndex in range(columnNumber):
|
||||
fieldMappingWidget = QtWidgets.QComboBox()
|
||||
fieldMappingWidget.setEditable(False)
|
||||
fieldMappingWidget.currentIndexChanged.connect(self.changeMappingForField)
|
||||
fieldMappingWidget.addItems(['', 'Entity One', 'Entity Two', 'Resolution ID', 'Notes'])
|
||||
self.fieldMappingComboBoxes.append(fieldMappingWidget)
|
||||
tableFieldAttributeMappingLayout.addWidget(fieldMappingWidget)
|
||||
|
||||
csvTable = QtWidgets.QTableWidget(3, columnNumber, self)
|
||||
csvTable.setHorizontalHeaderLabels(csvTableContents.columns)
|
||||
for row in csvTableContents[:3].itertuples():
|
||||
rowValues = list(row)
|
||||
for column in range(columnNumber):
|
||||
columnItem = QtWidgets.QTableWidgetItem(str(rowValues[column + 1]))
|
||||
columnItem.setFlags(columnItem.flags() & ~QtCore.Qt.ItemIsEditable)
|
||||
csvTable.setItem(rowValues[0], column, columnItem)
|
||||
|
||||
randomizationLabel = QtWidgets.QLabel("If the resolution identifiers (i.e. 'Resolution ID') are not guaranteed "
|
||||
"to be unique, you can configure whether you'd like to leave them as-is, "
|
||||
"append a random token, or ignore any resolution identifiers and just "
|
||||
"have random tokens as the Resolution IDs.\nPlease select what you would "
|
||||
"like to do:")
|
||||
randomizationLabel.setWordWrap(True)
|
||||
randomizationLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.randAsIs = QtWidgets.QRadioButton("Use 'Resolution ID' as-is.")
|
||||
self.randMerge = QtWidgets.QRadioButton("Append a random token to the values mapped to the 'Resolution ID' "
|
||||
"field.")
|
||||
self.randReplace = QtWidgets.QRadioButton("Ignore the 'Resolution ID' mapping, instead generate random values "
|
||||
"for Resolution IDs.")
|
||||
self.randAsIs.setChecked(True)
|
||||
|
||||
buttonsWidget = QtWidgets.QWidget()
|
||||
buttonsWidgetLayout = QtWidgets.QHBoxLayout()
|
||||
buttonsWidget.setLayout(buttonsWidgetLayout)
|
||||
confirmButton = QtWidgets.QPushButton('Confirm')
|
||||
cancelButton = QtWidgets.QPushButton('Cancel')
|
||||
buttonsWidgetLayout.addWidget(cancelButton)
|
||||
buttonsWidgetLayout.addWidget(confirmButton)
|
||||
cancelButton.clicked.connect(self.reject)
|
||||
confirmButton.clicked.connect(self.checkIfEntitiesAreMapped)
|
||||
|
||||
importLayout.addWidget(titleLabel)
|
||||
importLayout.addWidget(descLabel)
|
||||
importLayout.addWidget(entityOneTypeChoiceWidget)
|
||||
importLayout.addWidget(entityTwoTypeChoiceWidget)
|
||||
importLayout.addWidget(tableFieldAttributeMapping)
|
||||
importLayout.addWidget(csvTable)
|
||||
importLayout.addWidget(randomizationLabel)
|
||||
importLayout.addWidget(self.randAsIs)
|
||||
importLayout.addWidget(self.randMerge)
|
||||
importLayout.addWidget(self.randReplace)
|
||||
importLayout.addWidget(buttonsWidget)
|
||||
|
||||
def changeMappingForField(self, newIndex):
|
||||
for comboBox in self.fieldMappingComboBoxes:
|
||||
if comboBox.currentIndex() == newIndex and not comboBox.hasFocus():
|
||||
comboBox.setCurrentIndex(0)
|
||||
|
||||
def checkIfEntitiesAreMapped(self):
|
||||
# Check if Entity One and Entity Two labels are assigned, do not proceed if not.
|
||||
isOneAssigned = False
|
||||
isTwoAssigned = False
|
||||
for comboBox in self.fieldMappingComboBoxes:
|
||||
if comboBox.currentIndex() == 1:
|
||||
isOneAssigned = True
|
||||
elif comboBox.currentIndex() == 2:
|
||||
isTwoAssigned = True
|
||||
if isOneAssigned and isTwoAssigned:
|
||||
self.accept()
|
||||
else:
|
||||
self.parent().parent().MESSAGEHANDLER.warning('Need to configure mappings for Entity One (parent) and '
|
||||
'Entity Two (child) before proceeding.', popUp=True)
|
||||
|
||||
|
||||
class ImportEntityFromCSVFile(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, csvTableContents: pd.DataFrame):
|
||||
super(ImportEntityFromCSVFile, self).__init__(parent=parent)
|
||||
|
||||
self.setStyleSheet(Stylesheets.MAIN_WINDOW_STYLESHEET)
|
||||
self.setModal(True)
|
||||
importLayout = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(importLayout)
|
||||
|
||||
columnNumber = len(csvTableContents.columns)
|
||||
|
||||
titleLabel = QtWidgets.QLabel("Import Entities from CSV")
|
||||
titleLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
descLabel = QtWidgets.QLabel("Select the entity type to import the entities from the CSV file as. "
|
||||
"Then, configure the mapping between the CSV fields and entity attributes. To do "
|
||||
"so, select from the drop-down boxes the entity attributes that correspond to "
|
||||
"each field in the CSV file. The drop-down boxes left blank will have the "
|
||||
"corresponding CSV column names be treated as attribute names for the new "
|
||||
"entities.")
|
||||
descLabel.setWordWrap(True)
|
||||
|
||||
entityTypeChoiceWidget = QtWidgets.QWidget()
|
||||
entityTypeChoiceLayout = QtWidgets.QHBoxLayout()
|
||||
entityTypeChoiceWidget.setLayout(entityTypeChoiceLayout)
|
||||
|
||||
entityTypeChoiceLabel = QtWidgets.QLabel("Entity Type to Import As:")
|
||||
self.entityTypeChoiceDropdown = QtWidgets.QComboBox()
|
||||
self.entityTypeChoiceDropdown.setEditable(False)
|
||||
self.entityTypeChoiceDropdown.addItems(parent.parent().RESOURCEHANDLER.getAllEntities())
|
||||
self.entityTypeChoiceDropdown.currentIndexChanged.connect(self.pickEntityToImportAs)
|
||||
entityTypeChoiceLayout.addWidget(entityTypeChoiceLabel)
|
||||
entityTypeChoiceLayout.addWidget(self.entityTypeChoiceDropdown)
|
||||
|
||||
self.fieldMappingComboBoxes = []
|
||||
tableFieldAttributeMapping = QtWidgets.QWidget()
|
||||
tableFieldAttributeMappingLayout = QtWidgets.QHBoxLayout()
|
||||
tableFieldAttributeMapping.setLayout(tableFieldAttributeMappingLayout)
|
||||
for fieldIndex in range(columnNumber):
|
||||
fieldMappingWidget = QtWidgets.QComboBox()
|
||||
fieldMappingWidget.setEditable(False)
|
||||
fieldMappingWidget.currentIndexChanged.connect(self.changeMappingForField)
|
||||
fieldMappingWidget.addItem('')
|
||||
self.fieldMappingComboBoxes.append(fieldMappingWidget)
|
||||
tableFieldAttributeMappingLayout.addWidget(fieldMappingWidget)
|
||||
|
||||
csvTable = QtWidgets.QTableWidget(3, columnNumber, self)
|
||||
csvTable.setHorizontalHeaderLabels(csvTableContents.columns)
|
||||
for row in csvTableContents[:3].itertuples():
|
||||
rowValues = list(row)
|
||||
for column in range(columnNumber):
|
||||
columnItem = QtWidgets.QTableWidgetItem(str(rowValues[column + 1]))
|
||||
columnItem.setFlags(columnItem.flags() & ~QtCore.Qt.ItemIsEditable)
|
||||
csvTable.setItem(rowValues[0], column, columnItem)
|
||||
|
||||
importToCanvasChoiceWidget = QtWidgets.QWidget()
|
||||
importToCanvasChoiceLayout = QtWidgets.QHBoxLayout()
|
||||
importToCanvasChoiceWidget.setLayout(importToCanvasChoiceLayout)
|
||||
self.importToCanvasCheckbox = QtWidgets.QCheckBox('Import To Canvas:')
|
||||
self.importToCanvasCheckbox.setStyleSheet(Stylesheets.CHECK_BOX_STYLESHEET)
|
||||
self.importToCanvasDropdown = QtWidgets.QComboBox()
|
||||
@@ -1218,8 +1455,95 @@ class ImportFromFileDialog(QtWidgets.QDialog):
|
||||
self.importToCanvasDropdown.setDisabled(True)
|
||||
self.importToCanvasCheckbox.toggled.connect(lambda: self.importToCanvasDropdown.setDisabled(
|
||||
self.importToCanvasDropdown.isEnabled()))
|
||||
dialogLayout.addWidget(self.importToCanvasCheckbox, 5, 0, 1, 1)
|
||||
dialogLayout.addWidget(self.importToCanvasDropdown, 5, 1, 1, 1)
|
||||
importToCanvasChoiceLayout.addWidget(self.importToCanvasCheckbox)
|
||||
importToCanvasChoiceLayout.addWidget(self.importToCanvasDropdown)
|
||||
|
||||
buttonsWidget = QtWidgets.QWidget()
|
||||
buttonsWidgetLayout = QtWidgets.QHBoxLayout()
|
||||
buttonsWidget.setLayout(buttonsWidgetLayout)
|
||||
confirmButton = QtWidgets.QPushButton('Confirm')
|
||||
cancelButton = QtWidgets.QPushButton('Cancel')
|
||||
buttonsWidgetLayout.addWidget(cancelButton)
|
||||
buttonsWidgetLayout.addWidget(confirmButton)
|
||||
cancelButton.clicked.connect(self.reject)
|
||||
confirmButton.clicked.connect(self.confirmThatPrimaryFieldIsMapped)
|
||||
|
||||
importLayout.addWidget(titleLabel)
|
||||
importLayout.addWidget(descLabel)
|
||||
importLayout.addWidget(entityTypeChoiceWidget)
|
||||
importLayout.addWidget(tableFieldAttributeMapping)
|
||||
importLayout.addWidget(csvTable)
|
||||
importLayout.addWidget(importToCanvasChoiceWidget)
|
||||
importLayout.addWidget(buttonsWidget)
|
||||
|
||||
self.pickEntityToImportAs()
|
||||
|
||||
def pickEntityToImportAs(self):
|
||||
currentEntityAttributes = [''] + self.parent().parent().RESOURCEHANDLER.getEntityAttributes(
|
||||
self.entityTypeChoiceDropdown.currentText())
|
||||
for comboBox in self.fieldMappingComboBoxes:
|
||||
comboBox.clear()
|
||||
comboBox.addItems(currentEntityAttributes)
|
||||
|
||||
def changeMappingForField(self, newIndex):
|
||||
for comboBox in self.fieldMappingComboBoxes:
|
||||
if comboBox.currentIndex() == newIndex and not comboBox.hasFocus():
|
||||
comboBox.setCurrentIndex(0)
|
||||
|
||||
def confirmThatPrimaryFieldIsMapped(self):
|
||||
primaryField = self.parent().parent().RESOURCEHANDLER.getPrimaryFieldForEntityType(
|
||||
self.entityTypeChoiceDropdown.currentText())
|
||||
for comboBox in self.fieldMappingComboBoxes:
|
||||
if comboBox.currentText() == primaryField:
|
||||
self.accept()
|
||||
self.parent().parent().MESSAGEHANDLER.warning('Primary field (' + primaryField +
|
||||
') needs to be mapped before proceeding.')
|
||||
|
||||
|
||||
class ImportFromFileDialog(QtWidgets.QDialog):
|
||||
def popupFileDialog(self):
|
||||
self.fileDirectory = QtWidgets.QFileDialog().getOpenFileName(parent=self, caption='Select file to import',
|
||||
dir=str(Path.home()),
|
||||
options=QtWidgets.QFileDialog.DontUseNativeDialog,
|
||||
filter="CSV or txt (*.csv *.txt)")[0]
|
||||
if self.fileDirectory != '':
|
||||
self.fileDirectoryLine.setText(self.fileDirectory)
|
||||
|
||||
def __init__(self, parent):
|
||||
super(ImportFromFileDialog, self).__init__(parent=parent)
|
||||
self.fileDirectory = ''
|
||||
self.setWindowTitle('Import Entities From Text File')
|
||||
self.setModal(True)
|
||||
|
||||
dialogLayout = QtWidgets.QGridLayout()
|
||||
self.setLayout(dialogLayout)
|
||||
descriptionLabel = QtWidgets.QLabel('Select the file to import Entities or Links from:')
|
||||
descriptionLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
descriptionLabel.setWordWrap(True)
|
||||
dialogLayout.addWidget(descriptionLabel, 0, 0, 1, 2)
|
||||
|
||||
self.fileDirectoryButton = QtWidgets.QPushButton("Select file...")
|
||||
self.fileDirectoryLine = QtWidgets.QLineEdit()
|
||||
self.fileDirectoryLine.setReadOnly(True)
|
||||
|
||||
fileChoiceLabel = QtWidgets.QLabel('Specify the type of the chosen file and what to import:')
|
||||
fileChoiceLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
fileChoiceLabel.setWordWrap(True)
|
||||
|
||||
self.textFileChoice = QtWidgets.QRadioButton('Text file - Entities Import')
|
||||
self.textFileChoice.setStyleSheet(Stylesheets.RADIO_BUTTON_STYLESHEET)
|
||||
self.textFileChoice.setChecked(True)
|
||||
self.CSVFileChoice = QtWidgets.QRadioButton('CSV - Entities Import')
|
||||
self.CSVFileChoice.setStyleSheet(Stylesheets.RADIO_BUTTON_STYLESHEET)
|
||||
self.CSVFileChoiceLinks = QtWidgets.QRadioButton('CSV - Links Import')
|
||||
self.CSVFileChoiceLinks.setStyleSheet(Stylesheets.RADIO_BUTTON_STYLESHEET)
|
||||
|
||||
dialogLayout.addWidget(self.fileDirectoryLine, 1, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.fileDirectoryButton, 2, 0, 1, 2)
|
||||
dialogLayout.addWidget(fileChoiceLabel, 3, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.textFileChoice, 4, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.CSVFileChoice, 5, 0, 1, 2)
|
||||
dialogLayout.addWidget(self.CSVFileChoiceLinks, 6, 0, 1, 2)
|
||||
|
||||
acceptButton = QtWidgets.QPushButton('Accept')
|
||||
acceptButton.setAutoDefault(True)
|
||||
@@ -1235,28 +1559,64 @@ class ImportFromFileDialog(QtWidgets.QDialog):
|
||||
self.setMaximumHeight(300)
|
||||
self.setMinimumHeight(300)
|
||||
|
||||
dialogLayout.addWidget(cancelButton, 6, 0, 1, 1)
|
||||
dialogLayout.addWidget(acceptButton, 6, 1, 1, 1)
|
||||
dialogLayout.addWidget(cancelButton, 7, 0, 1, 1)
|
||||
dialogLayout.addWidget(acceptButton, 7, 1, 1, 1)
|
||||
|
||||
|
||||
class ImportFromTextFileDialog(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent, fileContents):
|
||||
super(ImportFromTextFileDialog, self).__init__(parent=parent)
|
||||
self.setWindowTitle('Import From Text File')
|
||||
self.setModal(True)
|
||||
|
||||
dialogLayout = QtWidgets.QGridLayout()
|
||||
self.setLayout(dialogLayout)
|
||||
descriptionLabel = QtWidgets.QLabel('Choose what type the entities will be imported as')
|
||||
descriptionLabel = QtWidgets.QLabel('Importing entities from text file, one entity per line.')
|
||||
descriptionLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
descriptionLabel.setWordWrap(True)
|
||||
dialogLayout.addWidget(descriptionLabel, 0, 0, 1, 2)
|
||||
|
||||
importTypeLabel = QtWidgets.QLabel('Choose the Entity Type to import as:')
|
||||
importTypeLabel.setWordWrap(True)
|
||||
dialogLayout.addWidget(importTypeLabel, 1, 0, 1, 1)
|
||||
|
||||
self.importTypeDropdown = QtWidgets.QComboBox()
|
||||
self.importTypeDropdown.addItems(parent.parent().RESOURCEHANDLER.getAllEntities())
|
||||
self.importTypeDropdown.setEditable(False)
|
||||
dialogLayout.addWidget(self.importTypeDropdown, 1, 1, 1, 1)
|
||||
self.importTypeDropdown.currentTextChanged.connect(self.updatePrimaryFieldValueLabel)
|
||||
|
||||
dialogLayout.addWidget(self.importTypeDropdown, 3, 1, 1, 1)
|
||||
typePrimaryFieldLabel = QtWidgets.QLabel('Primary Field for chosen type:')
|
||||
typePrimaryFieldLabel.setWordWrap(False)
|
||||
dialogLayout.addWidget(typePrimaryFieldLabel, 2, 0, 1, 1)
|
||||
|
||||
self.typePrimaryFieldValueLabel = QtWidgets.QLineEdit('')
|
||||
self.typePrimaryFieldValueLabel.setReadOnly(True)
|
||||
dialogLayout.addWidget(self.typePrimaryFieldValueLabel, 2, 1, 1, 1)
|
||||
|
||||
textTable = QtWidgets.QTableWidget(3, 1, self)
|
||||
textTable.setWordWrap(True)
|
||||
textTable.setFixedWidth(450)
|
||||
for line in range(len(fileContents)):
|
||||
columnItem = QtWidgets.QTableWidgetItem(fileContents[line])
|
||||
columnItem.setFlags(columnItem.flags() & ~QtCore.Qt.ItemIsEditable)
|
||||
textTable.setItem(line, 0, columnItem)
|
||||
textTable.setColumnWidth(0, 450)
|
||||
textTable.setHorizontalHeaderLabels(['File Entities Preview'])
|
||||
|
||||
dialogLayout.addWidget(textTable, 3, 0, 1, 2)
|
||||
|
||||
self.importToCanvasCheckbox = QtWidgets.QCheckBox('Import To Canvas:')
|
||||
self.importToCanvasCheckbox.setStyleSheet(Stylesheets.CHECK_BOX_STYLESHEET)
|
||||
self.importToCanvasDropdown = QtWidgets.QComboBox()
|
||||
self.importToCanvasDropdown.addItems(list(parent.parent().centralWidget().tabbedPane.canvasTabs))
|
||||
self.importToCanvasDropdown.setEditable(False)
|
||||
self.importToCanvasDropdown.setDisabled(True)
|
||||
self.importToCanvasCheckbox.toggled.connect(lambda: self.importToCanvasDropdown.setDisabled(
|
||||
self.importToCanvasDropdown.isEnabled()))
|
||||
dialogLayout.addWidget(self.importToCanvasCheckbox, 4, 0, 1, 1)
|
||||
dialogLayout.addWidget(self.importToCanvasDropdown, 4, 1, 1, 1)
|
||||
|
||||
acceptButton = QtWidgets.QPushButton('Accept')
|
||||
acceptButton.setAutoDefault(True)
|
||||
@@ -1266,13 +1626,19 @@ class ImportFromTextFileDialog(QtWidgets.QDialog):
|
||||
cancelButton.clicked.connect(self.reject)
|
||||
acceptButton.setFocus()
|
||||
|
||||
self.setMaximumWidth(450)
|
||||
self.setMinimumWidth(300)
|
||||
self.setMaximumHeight(300)
|
||||
self.setMinimumHeight(300)
|
||||
self.setFixedWidth(472)
|
||||
self.setFixedHeight(300)
|
||||
|
||||
dialogLayout.addWidget(cancelButton, 4, 0, 1, 1)
|
||||
dialogLayout.addWidget(acceptButton, 4, 1, 1, 1)
|
||||
dialogLayout.addWidget(cancelButton, 5, 0, 1, 1)
|
||||
dialogLayout.addWidget(acceptButton, 5, 1, 1, 1)
|
||||
|
||||
# Initialize the primary field value label to whatever is the primary field of the first entity
|
||||
# in the drop down selection box.
|
||||
self.updatePrimaryFieldValueLabel()
|
||||
|
||||
def updatePrimaryFieldValueLabel(self):
|
||||
self.typePrimaryFieldValueLabel.setText(str(self.parent().parent().RESOURCEHANDLER.getPrimaryFieldForEntityType(
|
||||
self.importTypeDropdown.currentText())))
|
||||
|
||||
|
||||
class CanvasPictureDialog(QtWidgets.QDialog):
|
||||
|
||||
@@ -168,7 +168,8 @@ class ResourceHandler:
|
||||
eJson['Icon'] = self.getEntityDefaultPicture(entityType)
|
||||
|
||||
if jsonData is not None:
|
||||
for key in eJson:
|
||||
# Allow setting of attributes that are not defined in the Entity specification.
|
||||
for key in jsonData:
|
||||
value = jsonData.get(key)
|
||||
if value is not None and value != '':
|
||||
eJson[key] = value
|
||||
@@ -181,7 +182,7 @@ class ResourceHandler:
|
||||
|
||||
return eJson
|
||||
|
||||
def getPrimaryFieldForEntityType(self, entityType: str):
|
||||
def getPrimaryFieldForEntityType(self, entityType: str) -> Union[str, None]:
|
||||
try:
|
||||
for category in self.entityCategoryList:
|
||||
if entityType in self.entityCategoryList[category]:
|
||||
@@ -207,7 +208,7 @@ class ResourceHandler:
|
||||
|
||||
return eJson
|
||||
|
||||
def getLinkJson(self, jsonData) -> Union[dict, None]:
|
||||
def getLinkJson(self, jsonData: dict) -> Union[dict, None]:
|
||||
linkJson = {}
|
||||
try:
|
||||
linkJson['uid'] = jsonData['uid']
|
||||
@@ -222,6 +223,11 @@ class ResourceHandler:
|
||||
linkJson['Date Last Edited'] = utcNow
|
||||
linkJson['Notes'] = str(jsonData.get('Notes'))
|
||||
|
||||
# Transfer all values from jsonData to linkJson, but preserve the values and order of linkJson for existing
|
||||
# keys.
|
||||
jsonData.update(linkJson)
|
||||
linkJson.update(jsonData)
|
||||
|
||||
return linkJson
|
||||
|
||||
def getEntityDefaultPicture(self, entityType) -> QByteArray:
|
||||
|
||||
53
LinkScope.py
53
LinkScope.py
@@ -339,10 +339,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.linkingNodes = True
|
||||
|
||||
def deleteSpecificEntity(self, itemUID: str) -> None:
|
||||
for canvas in self.centralWidget().tabbedPane.canvasTabs:
|
||||
scene = self.centralWidget().tabbedPane.canvasTabs[canvas].scene()
|
||||
if itemUID in scene.nodesDict:
|
||||
scene.removeNode(scene.nodesDict[itemUID])
|
||||
self.centralWidget().tabbedPane.nodeRemoveAllHelper(itemUID)
|
||||
self.LENTDB.removeEntity(itemUID)
|
||||
self.MESSAGEHANDLER.info("Deleted node: " + itemUID)
|
||||
|
||||
@@ -452,7 +449,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
shortestPath = None
|
||||
|
||||
if shortestPath is None:
|
||||
self.setStatus('No path between the selected nodes was found.')
|
||||
messagePathNotFound = 'No path found connecting the selected nodes: ' + str(endPoints)
|
||||
self.setStatus(messagePathNotFound)
|
||||
self.MESSAGEHANDLER.info(messagePathNotFound, popUp=True)
|
||||
else:
|
||||
self.centralWidget().tabbedPane.getCurrentScene().clearSelection()
|
||||
for item in [node for node in self.centralWidget().tabbedPane.getCurrentScene().items()
|
||||
@@ -462,7 +461,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
linksToSelect = [(a, b) for a, b in zip(shortestPath, shortestPath[1:])]
|
||||
for linkItem in [link for link in self.centralWidget().tabbedPane.getCurrentScene().items()
|
||||
if isinstance(link, BaseConnector)]:
|
||||
if linkItem.uid in linksToSelect:
|
||||
if linkItem.uid.intersection(linksToSelect):
|
||||
linkItem.setSelected(True)
|
||||
self.setStatus('Shortest path found.')
|
||||
|
||||
@@ -470,34 +469,38 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
currentScene = self.centralWidget().tabbedPane.getCurrentScene()
|
||||
currentUIDs = [item.uid for item in currentScene.items() if isinstance(item, BaseNode)
|
||||
or isinstance(item, BaseConnector)]
|
||||
entityPrimaryFields = []
|
||||
entityPrimaryFields = {}
|
||||
for uid in currentUIDs:
|
||||
item = self.LENTDB.getEntity(uid)
|
||||
if item is None:
|
||||
item = self.LENTDB.getLink(uid)
|
||||
if item is None:
|
||||
continue
|
||||
entityPrimaryFields.append(item[list(item)[1]])
|
||||
matchedPrimaryFieldsAndUIDs = [item for item in zip(currentUIDs, entityPrimaryFields)]
|
||||
# Remove duplicates
|
||||
entityPrimaryFields = list(set(entityPrimaryFields))
|
||||
matchedPrimaryFieldsAndUIDs = list(set(matchedPrimaryFieldsAndUIDs))
|
||||
findPrompt = FindEntityOnCanvasDialog(entityPrimaryFields)
|
||||
promptReturnCode = findPrompt.exec()
|
||||
if isinstance(uid, str):
|
||||
item = self.LENTDB.getEntity(uid)
|
||||
if item is not None:
|
||||
if not entityPrimaryFields.get(item[list(item)[1]]):
|
||||
entityPrimaryFields[item[list(item)[1]]] = set()
|
||||
entityPrimaryFields[item[list(item)[1]]].add(uid)
|
||||
|
||||
if promptReturnCode:
|
||||
elif isinstance(uid, set):
|
||||
for potentialLinkItem in uid:
|
||||
item = self.LENTDB.getLink(potentialLinkItem)
|
||||
if item is not None:
|
||||
if not entityPrimaryFields.get(item['Resolution']):
|
||||
entityPrimaryFields[item['Resolution']] = set()
|
||||
entityPrimaryFields[item['Resolution']].add(str(uid))
|
||||
findPrompt = FindEntityOnCanvasDialog(list(entityPrimaryFields))
|
||||
|
||||
if findPrompt.exec():
|
||||
uidsToSelect = []
|
||||
findText = findPrompt.findInput.text()
|
||||
for item in matchedPrimaryFieldsAndUIDs:
|
||||
if item[1].startswith(findText):
|
||||
uidsToSelect.append(item[0])
|
||||
for item in entityPrimaryFields:
|
||||
if item.startswith(findText):
|
||||
# Add the elements in each index to uidsToSelect instead of the sets themselves.
|
||||
uidsToSelect.extend(entityPrimaryFields[item])
|
||||
|
||||
currentScene.clearSelection()
|
||||
for item in [linkOrEntity for linkOrEntity in currentScene.items()
|
||||
if isinstance(linkOrEntity, BaseNode) or isinstance(linkOrEntity, BaseConnector)]:
|
||||
if item.uid in uidsToSelect:
|
||||
if str(item.uid) in uidsToSelect:
|
||||
item.setSelected(True)
|
||||
if len(uidsToSelect) == 1:
|
||||
if len(uidsToSelect) == 1 and ',' not in uidsToSelect[0]:
|
||||
self.centralWidget().tabbedPane.getCurrentView().centerViewportOnNode(uidsToSelect[0])
|
||||
|
||||
def mergeEntities(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user