Msgpack is used to save all objects.

Small bug fix with importing database.
This commit is contained in:
AccentuSoft
2022-01-06 18:22:15 +02:00
parent 7a082ce322
commit dd03575ab1
5 changed files with 70 additions and 35 deletions

View File

@@ -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.')

View File

@@ -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):

View File

@@ -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()

View File

@@ -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

View File

@@ -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):