Msgpack is used to save all objects.
Small bug fix with importing database.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from shutil import move
|
||||
from pickle import load, dump
|
||||
from msgpack import load, dump
|
||||
from threading import Lock
|
||||
from pathlib import Path
|
||||
|
||||
@@ -26,16 +26,16 @@ class EntitiesDB:
|
||||
|
||||
def loadDatabase(self):
|
||||
"""
|
||||
Load DiGraph from pickle file.
|
||||
Load DiGraph from LinkScope Database file - msgpack dumped object.
|
||||
"""
|
||||
self.dbLock.acquire()
|
||||
if self.database is not None:
|
||||
self.save()
|
||||
databaseFile = Path(self.mainWindow.SETTINGS.value("Project/FilesDir")).joinpath("LocalEntitiesDB.pkl")
|
||||
databaseFile = Path(self.mainWindow.SETTINGS.value("Project/FilesDir")).joinpath("LocalEntitiesDB.lsdb")
|
||||
self.messageHandler.debug('Opening Database at: ' + str(databaseFile))
|
||||
try:
|
||||
dbFile = open(databaseFile, "rb")
|
||||
self.database = load(dbFile)
|
||||
self.database = self.mainWindow.RESOURCEHANDLER.reconstructGraphFullFromFile(load(dbFile))
|
||||
dbFile.close()
|
||||
self.messageHandler.info('Loaded Local Entities Database.')
|
||||
except FileNotFoundError:
|
||||
@@ -63,17 +63,17 @@ class EntitiesDB:
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Saves the graph (pickles it too) to the specified file.
|
||||
Saves the graph to the specified file.
|
||||
"""
|
||||
|
||||
# Get the database file path again, in case it changed.
|
||||
databaseFile = Path(self.mainWindow.SETTINGS.value("Project/FilesDir")).joinpath("LocalEntitiesDB.pkl")
|
||||
databaseFile = Path(self.mainWindow.SETTINGS.value("Project/FilesDir")).joinpath("LocalEntitiesDB.lsdb")
|
||||
if databaseFile is None:
|
||||
raise ValueError('Database File is None, cannot save database.')
|
||||
self.dbLock.acquire()
|
||||
tmpSavePath = databaseFile.with_suffix(databaseFile.suffix + '.tmp')
|
||||
dbFile = open(tmpSavePath, "wb")
|
||||
dump(self.database, dbFile)
|
||||
dump(self.mainWindow.RESOURCEHANDLER.deconstructGraphForFileDump(self.database), dbFile)
|
||||
dbFile.close()
|
||||
move(tmpSavePath, databaseFile)
|
||||
self.messageHandler.info('Database Saved.')
|
||||
|
||||
@@ -413,7 +413,7 @@ class CommunicationsHandler(QtCore.QObject):
|
||||
self.transmitMessage(message)
|
||||
|
||||
def receiveSyncDatabase(self, database: str):
|
||||
database_nodes, database_edges = self.mainWindow.RESOURCEHANDLER.reconstructGraph(database)
|
||||
database_nodes, database_edges = self.mainWindow.RESOURCEHANDLER.reconstructGraphFromString(database)
|
||||
self.receive_sync_database.emit(database_nodes, database_edges)
|
||||
|
||||
def askServerForFileList(self, project_name: str):
|
||||
@@ -431,7 +431,7 @@ class CommunicationsHandler(QtCore.QObject):
|
||||
self.transmitMessage(message)
|
||||
|
||||
def receiveSyncCanvas(self, canvas_name: str, canvas_graph: str):
|
||||
graph_nodes, graph_edges = self.mainWindow.RESOURCEHANDLER.reconstructGraph(canvas_graph)
|
||||
graph_nodes, graph_edges = self.mainWindow.RESOURCEHANDLER.reconstructGraphFromString(canvas_graph)
|
||||
self.receive_sync_canvas_signal.emit(canvas_name, graph_nodes, graph_edges)
|
||||
|
||||
def closeCanvas(self, project_name: str, canvas_name: str):
|
||||
|
||||
@@ -9,7 +9,7 @@ from typing import Union
|
||||
import folium
|
||||
import networkx as nx
|
||||
from shutil import move
|
||||
from pickle import dump, load
|
||||
from msgpack import dump, load
|
||||
from PIL import Image
|
||||
from PIL.ImageQt import ImageQt
|
||||
from pathlib import Path
|
||||
@@ -505,28 +505,28 @@ class TabbedPane(QtWidgets.QTabWidget):
|
||||
def save(self) -> None:
|
||||
if len(self.canvasTabs) == 0:
|
||||
return
|
||||
canvasDBPath = Path(self.mainWindow.SETTINGS.value("Project/BaseDir")) / "Project Files" / "canvasTabs.pkl"
|
||||
canvasDBPath = Path(self.mainWindow.SETTINGS.value("Project/BaseDir")) / "Project Files" / "canvasTabs.lscanvas"
|
||||
canvasDBPathTmp = canvasDBPath.with_suffix(canvasDBPath.suffix + '.tmp')
|
||||
|
||||
canvasDBFile = open(canvasDBPathTmp, "wb")
|
||||
saveJson = {}
|
||||
for canvasName in self.canvasTabs:
|
||||
saveJson[canvasName] = [
|
||||
self.canvasTabs[canvasName].scene().sceneGraph,
|
||||
self.resourceHandler.deconstructGraphForFileDump(self.canvasTabs[canvasName].scene().sceneGraph),
|
||||
self.canvasTabs[canvasName].scene().scenePos]
|
||||
dump(saveJson, canvasDBFile)
|
||||
canvasDBFile.close()
|
||||
move(canvasDBPathTmp, canvasDBPath)
|
||||
|
||||
def open(self) -> None:
|
||||
canvasDBPath = Path(self.mainWindow.SETTINGS.value("Project/BaseDir")) / "Project Files" / "canvasTabs.pkl"
|
||||
canvasDBPath = Path(self.mainWindow.SETTINGS.value("Project/BaseDir")) / "Project Files" / "canvasTabs.lscanvas"
|
||||
|
||||
if Path(canvasDBPath).exists():
|
||||
canvasDBFile = open(canvasDBPath, "rb")
|
||||
savedJson = load(canvasDBFile)
|
||||
for canvasName in savedJson:
|
||||
self.addCanvas(canvasName,
|
||||
savedJson[canvasName][0],
|
||||
self.resourceHandler.reconstructGraphFullFromFile(savedJson[canvasName][0]),
|
||||
savedJson[canvasName][1])
|
||||
canvasDBFile.close()
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
from typing import Union
|
||||
|
||||
import networkx as nx
|
||||
from defusedxml.ElementTree import parse
|
||||
from datetime import datetime
|
||||
@@ -19,7 +22,7 @@ class ResourceHandler:
|
||||
return self.icons[iconName]
|
||||
|
||||
# Load all resources needed.
|
||||
def __init__(self, mainWindow, messageHandler):
|
||||
def __init__(self, mainWindow, messageHandler) -> None:
|
||||
self.mainWindow = mainWindow
|
||||
self.messageHandler = messageHandler
|
||||
self.entityCategoryList = {}
|
||||
@@ -58,13 +61,13 @@ class ResourceHandler:
|
||||
|
||||
self.loadCoreEntities()
|
||||
|
||||
def getEntityCategories(self):
|
||||
def getEntityCategories(self) -> list:
|
||||
eList = []
|
||||
for category in self.entityCategoryList:
|
||||
eList.append(category)
|
||||
return eList
|
||||
|
||||
def getAllEntityDetailsWithIconsInCategory(self, category):
|
||||
def getAllEntityDetailsWithIconsInCategory(self, category) -> list:
|
||||
eList = []
|
||||
for entity in self.entityCategoryList[category]:
|
||||
entityValue = self.entityCategoryList[category][entity]
|
||||
@@ -73,7 +76,7 @@ class ResourceHandler:
|
||||
))
|
||||
return eList
|
||||
|
||||
def getEntityAttributes(self, entityType):
|
||||
def getEntityAttributes(self, entityType) -> Union[None, list]:
|
||||
aList = []
|
||||
try:
|
||||
for category in self.entityCategoryList:
|
||||
@@ -87,7 +90,7 @@ class ResourceHandler:
|
||||
return None
|
||||
return aList
|
||||
|
||||
def getAllEntitiesInCategory(self, category):
|
||||
def getAllEntitiesInCategory(self, category) -> list:
|
||||
"""
|
||||
Get all Entity Types in the specified category.
|
||||
"""
|
||||
@@ -96,7 +99,7 @@ class ResourceHandler:
|
||||
eList.append(entity)
|
||||
return eList
|
||||
|
||||
def getAllEntities(self):
|
||||
def getAllEntities(self) -> list:
|
||||
"""
|
||||
Get all recognised Entity Types.
|
||||
"""
|
||||
@@ -132,13 +135,13 @@ class ResourceHandler:
|
||||
'Icon': str(Path(self.mainWindow.SETTINGS.value("Program/BaseDir")) / "Resources" / "Icons" / icon)}
|
||||
return True
|
||||
|
||||
def loadCoreEntities(self):
|
||||
def loadCoreEntities(self) -> None:
|
||||
entDir = Path(self.mainWindow.SETTINGS.value("Program/BaseDir")) / "Core" / "Entities"
|
||||
for entFile in listdir(entDir):
|
||||
if entFile.endswith('.xml'):
|
||||
self.addRecognisedEntityTypes(entDir / entFile)
|
||||
|
||||
def loadModuleEntities(self):
|
||||
def loadModuleEntities(self) -> None:
|
||||
entDir = Path(self.mainWindow.SETTINGS.value("Program/BaseDir")) / "Modules"
|
||||
for module in listdir(entDir):
|
||||
for entFile in listdir(entDir / module):
|
||||
@@ -146,7 +149,7 @@ class ResourceHandler:
|
||||
self.addRecognisedEntityTypes(
|
||||
entDir / module / entFile)
|
||||
|
||||
def getEntityJson(self, entityType: str, jsonData=None):
|
||||
def getEntityJson(self, entityType: str, jsonData=None) -> Union[dict, None]:
|
||||
eJson = {'uid': str(uuid4())}
|
||||
try:
|
||||
for category in self.entityCategoryList:
|
||||
@@ -188,7 +191,7 @@ class ResourceHandler:
|
||||
"malformed entity type: " + str(entityType), True)
|
||||
return None
|
||||
|
||||
def getBareBonesEntityJson(self, entityType):
|
||||
def getBareBonesEntityJson(self, entityType) -> Union[dict, None]:
|
||||
eJson = {}
|
||||
try:
|
||||
for category in self.entityCategoryList:
|
||||
@@ -204,7 +207,7 @@ class ResourceHandler:
|
||||
|
||||
return eJson
|
||||
|
||||
def getLinkJson(self, jsonData):
|
||||
def getLinkJson(self, jsonData) -> Union[dict, None]:
|
||||
linkJson = {}
|
||||
try:
|
||||
linkJson['uid'] = jsonData['uid']
|
||||
@@ -221,7 +224,7 @@ class ResourceHandler:
|
||||
|
||||
return linkJson
|
||||
|
||||
def getEntityDefaultPicture(self, entityType):
|
||||
def getEntityDefaultPicture(self, entityType) -> QByteArray:
|
||||
picture = Path(self.mainWindow.SETTINGS.value("Program/BaseDir")) / "Resources" / "Icons" / "Default.svg"
|
||||
try:
|
||||
for category in self.entityCategoryList:
|
||||
@@ -260,7 +263,20 @@ class ResourceHandler:
|
||||
edges = {edgeKey: graph.edges.get(edgeKey) for edgeKey in graph.edges}
|
||||
return nodes, edges
|
||||
|
||||
def reconstructGraph(self, graphString: str) -> tuple:
|
||||
def deconstructGraphForFileDump(self, graph: nx.DiGraph) -> tuple:
|
||||
nodes = {}
|
||||
for nodeKey in graph.nodes:
|
||||
# Dereference the original dict so we don't actually convert its icon to data.
|
||||
nodes[nodeKey] = dict(graph.nodes.get(nodeKey))
|
||||
try:
|
||||
nodes[nodeKey]['Icon'] = nodes[nodeKey]['Icon'].toBase64().data()
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
edges = {str(edgeKey): graph.edges.get(edgeKey) for edgeKey in graph.edges}
|
||||
return nodes, edges
|
||||
|
||||
def reconstructGraphFromString(self, graphString: str) -> tuple:
|
||||
nodes, edges = literal_eval(graphString)
|
||||
for node in nodes:
|
||||
try:
|
||||
@@ -269,3 +285,20 @@ class ResourceHandler:
|
||||
pass
|
||||
|
||||
return nodes, edges
|
||||
|
||||
def reconstructGraphFullFromFile(self, graphNodesAndEdges: Union[tuple, list]) -> nx.DiGraph:
|
||||
returnGraph = nx.DiGraph()
|
||||
graphNodes = graphNodesAndEdges[0]
|
||||
graphEdges = graphNodesAndEdges[1]
|
||||
for node in graphNodes:
|
||||
try:
|
||||
graphNodes[node]['Icon'] = QByteArray(b64decode(graphNodes[node]['Icon']))
|
||||
except KeyError:
|
||||
pass
|
||||
returnGraph.add_node(node, **graphNodes[node])
|
||||
|
||||
for edge in graphEdges:
|
||||
edgeUID = literal_eval(edge)
|
||||
returnGraph.add_edge(*edgeUID, **graphEdges[edge])
|
||||
|
||||
return returnGraph
|
||||
|
||||
18
LinkScope.py
18
LinkScope.py
@@ -160,7 +160,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
nx.write_graphml(currentCanvasGraph, filePath)
|
||||
self.setStatus('Canvas exported successfully.')
|
||||
except Exception as exc:
|
||||
self.MESSAGEHANDLER.error("Could not export canvas to file.", popUp=True)
|
||||
self.MESSAGEHANDLER.error("Could not export canvas to file: " + str(exc), popUp=True)
|
||||
self.setStatus('Canvas export failed.')
|
||||
|
||||
def importCanvasFromGraphML(self):
|
||||
@@ -227,17 +227,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
try:
|
||||
filePath = openDialog.selectedFiles()[0]
|
||||
read_graphml = nx.read_graphml(filePath)
|
||||
for node in read_graphml.nodes:
|
||||
read_graphml.nodes[node]['Icon'] = self.RESOURCEHANDLER.getEntityDefaultPicture(
|
||||
read_graphml.nodes[node]['Entity Type'])
|
||||
for edge in read_graphml.edges:
|
||||
read_graphml.edges[edge]['uid'] = literal_eval(read_graphml.edges[edge]['uid'])
|
||||
self.LENTDB.mergeDatabases(read_graphml, fromServer=False)
|
||||
read_graphml_nodes = {key: read_graphml.nodes[key] for key in read_graphml.nodes}
|
||||
read_graphml_edges = {key: read_graphml.edges[key] for key in read_graphml.edges}
|
||||
for node in read_graphml_nodes:
|
||||
read_graphml_nodes[node]['Icon'] = self.RESOURCEHANDLER.getEntityDefaultPicture(
|
||||
read_graphml_nodes[node]['Entity Type'])
|
||||
for edge in read_graphml_edges:
|
||||
read_graphml_edges[edge]['uid'] = literal_eval(read_graphml_edges[edge]['uid'])
|
||||
self.LENTDB.mergeDatabases(read_graphml_nodes, read_graphml_edges, fromServer=False)
|
||||
self.dockbarOne.existingEntitiesPalette.loadEntities()
|
||||
self.LENTDB.resetTimeline()
|
||||
self.setStatus('Database imported successfully.')
|
||||
except Exception as exc:
|
||||
self.MESSAGEHANDLER.error("Could not import database from file.", popUp=True)
|
||||
self.MESSAGEHANDLER.error("Could not import database from file: " + str(exc), popUp=True)
|
||||
self.setStatus('Database import failed.')
|
||||
|
||||
def generateReport(self):
|
||||
|
||||
Reference in New Issue
Block a user