From f116d8d2fda8b03928a636648ac18e783462a67a Mon Sep 17 00:00:00 2001 From: "Dr. Steffen Brinckmann" Date: Thu, 16 Feb 2023 14:31:01 +0100 Subject: [PATCH 1/3] Added Tests back into distribution --- {Tests => pasta_eln/Tests}/3Projects.py | 0 .../ExampleMeasurements/1500nmXX 5 7074 -4594.txt | 0 .../Tests}/ExampleMeasurements/RobinSteel0000LC.txt | 0 .../Tests}/ExampleMeasurements/Zeiss.tif | Bin .../Tests}/ExampleMeasurements/test.odt | Bin 5 files changed, 0 insertions(+), 0 deletions(-) rename {Tests => pasta_eln/Tests}/3Projects.py (100%) rename {Tests => pasta_eln/Tests}/ExampleMeasurements/1500nmXX 5 7074 -4594.txt (100%) rename {Tests => pasta_eln/Tests}/ExampleMeasurements/RobinSteel0000LC.txt (100%) rename {Tests => pasta_eln/Tests}/ExampleMeasurements/Zeiss.tif (100%) rename {Tests => pasta_eln/Tests}/ExampleMeasurements/test.odt (100%) diff --git a/Tests/3Projects.py b/pasta_eln/Tests/3Projects.py similarity index 100% rename from Tests/3Projects.py rename to pasta_eln/Tests/3Projects.py diff --git a/Tests/ExampleMeasurements/1500nmXX 5 7074 -4594.txt b/pasta_eln/Tests/ExampleMeasurements/1500nmXX 5 7074 -4594.txt similarity index 100% rename from Tests/ExampleMeasurements/1500nmXX 5 7074 -4594.txt rename to pasta_eln/Tests/ExampleMeasurements/1500nmXX 5 7074 -4594.txt diff --git a/Tests/ExampleMeasurements/RobinSteel0000LC.txt b/pasta_eln/Tests/ExampleMeasurements/RobinSteel0000LC.txt similarity index 100% rename from Tests/ExampleMeasurements/RobinSteel0000LC.txt rename to pasta_eln/Tests/ExampleMeasurements/RobinSteel0000LC.txt diff --git a/Tests/ExampleMeasurements/Zeiss.tif b/pasta_eln/Tests/ExampleMeasurements/Zeiss.tif similarity index 100% rename from Tests/ExampleMeasurements/Zeiss.tif rename to pasta_eln/Tests/ExampleMeasurements/Zeiss.tif diff --git a/Tests/ExampleMeasurements/test.odt b/pasta_eln/Tests/ExampleMeasurements/test.odt similarity index 100% rename from Tests/ExampleMeasurements/test.odt rename to pasta_eln/Tests/ExampleMeasurements/test.odt From b2f7816c8d8f40c2e3e543a3e73700511ab95c43 Mon Sep 17 00:00:00 2001 From: "Dr. Steffen Brinckmann" Date: Fri, 17 Feb 2023 17:00:11 +0100 Subject: [PATCH 2/3] - Copy changes from V1 - add configuration to menu - commit does better testing - start with details-context menu --- commit.py | 4 ++-- pasta_eln/backend.py | 26 ++++++++++++++++++++++++-- pasta_eln/dialogConfig.py | 2 +- pasta_eln/dialogConfigGUI.py | 2 -- pasta_eln/gui.py | 18 +++++++++++++++--- pasta_eln/widgetDetails.py | 23 ++++++++++++++++++++--- 6 files changed, 62 insertions(+), 13 deletions(-) diff --git a/commit.py b/commit.py index 6f07e53d..3b2757a9 100755 --- a/commit.py +++ b/commit.py @@ -90,7 +90,7 @@ def runTests(): for fileI in os.listdir('pasta_eln/Tests'): if not fileI.endswith('.py'): continue - result = subprocess.run(['python3','-m','pasta_eln.Tests.'+fileI], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) + result = subprocess.run(['python3','-m','pasta_eln.Tests.'+fileI[:-3]], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) success = result.stdout.decode('utf-8').count('*** DONE WITH VERIFY ***') if success==1: success += result.stdout.decode('utf-8').count('**ERROR') @@ -99,7 +99,7 @@ def runTests(): else: successAll = False print(" FAILED: Python unit test "+fileI) - print(" run: 'python3 Tests/"+fileI+"' and check logFile") + print(" run: 'python3 -m pasta_eln.Tests."+fileI[:-3]+"' and check logFile") return diff --git a/pasta_eln/backend.py b/pasta_eln/backend.py index 2035ec6f..c6235887 100644 --- a/pasta_eln/backend.py +++ b/pasta_eln/backend.py @@ -8,6 +8,10 @@ from .miscTools import upIn, upOut, createDirName, generic_hash, camelCase from .handleDictionaries import ontology2Labels, fillDocBeforeCreate +#TODO_P5 App freezes on loadKey +#TODO_P5 unprocessed files should be have separate docType +#TODO_P5 rerun extractors as batch + class Backend(CLI_Mixin): """ PYTHON BACKEND @@ -287,6 +291,21 @@ def scanTree(self, **kwargs): startPath = Path(self.cwd) #prepare lists and start iterating inDB_all = self.db.getView('viewHierarchy/viewPaths') + #update content between DB and harddisk + for line in inDB_all: + if line['value'][1][0][0]=='x': + continue + doc = self.db.getDoc(line['id']) + if 'content' in doc: + path = self.basePath/line['key'] + doc= {'-type':['procedure']} + self.useExtractors(path, '', doc) + self.db.updateDoc(doc, line['id']) + #TODO_P5: Basic functionality for V1 + #V1: GUI content write-protected; assume one link and no conflicts + #V2: What happens if you change a file (procedure) on disk / database -> conflicts + # Copy of procedure exists on harddisk: one entry in db and links; then change one, but not other, -branch should separate; can they reunite? + pathsInDB_x = [i['key'] for i in inDB_all if i['value'][1][0][0]=='x'] #all structure elements: task, subtasts pathsInDB_data = [i['key'] for i in inDB_all if i['value'][1][0][0]!='x'] for root, dirs, files in os.walk(self.cwd, topdown=True): @@ -307,7 +326,7 @@ def scanTree(self, **kwargs): if path in pathsInDB_x: #path already in database pathsInDB_x.remove(path) continue - _ = self.addData('x'+str(len(hierStack)), {'-name':dirName}, hierStack) + self.addData('x'+str(len(hierStack)), {'-name':dirName}, hierStack) newDir = Path(self.basePath)/self.db.getDoc(self.currentID)['-branch'][0]['path'] (newDir/'.id_pastaELN.json').rename(Path(self.basePath)/root/dirName/'.id_pastaELN.json') #move index file into old folder newDir.rmdir() #remove created path @@ -339,7 +358,10 @@ def scanTree(self, **kwargs): change = {'-branch': {'op':'d', 'oldpath':branch['path'], 'path':branch['path'], \ 'stack':branch['stack'] }} break - self.db.updateDoc(change, docID) + if change is None: + print('**ERROR Tried to remove orphan in database but could not', orphan) + else: + self.db.updateDoc(change, docID) if rerunScanTree: self.scanTree() return diff --git a/pasta_eln/dialogConfig.py b/pasta_eln/dialogConfig.py index 58ae45cc..f3b4830e 100644 --- a/pasta_eln/dialogConfig.py +++ b/pasta_eln/dialogConfig.py @@ -11,7 +11,7 @@ class Configuration(QDialog): """ Main class of entire config dialog """ - def __init__(self, backend, startTap): + def __init__(self, backend, startTap=''): """ Initialization diff --git a/pasta_eln/dialogConfigGUI.py b/pasta_eln/dialogConfigGUI.py index 133c1111..1c0c155a 100644 --- a/pasta_eln/dialogConfigGUI.py +++ b/pasta_eln/dialogConfigGUI.py @@ -30,7 +30,6 @@ def __init__(self, backend, callbackFinished): self.wD = self.addRowList('imageWidthDetails','Image width in details view', ['500','600','700','800']) self.wS = self.addRowList('sidebarWidth','Sidebar width', ['200','300','400']) self.log = self.addRowList('loggingLevel','Logging level (more->less)', ['DEBUG','INFO','WARNING','ERROR']) - self.nTC = self.addRowList('tableColumnsMax','Maximum number of table columns', ['8','16','32']) self.tabAppearanceL.addRow(TextButton('Save changes', self.saveData, None), TextButton('Restart PASTA-ELN', restart, None)) @@ -82,7 +81,6 @@ def saveData(self): self.backend.configuration['GUI']['imageWidthDetails'] = int(self.wD.currentText()) self.backend.configuration['GUI']['sidebarWidth'] = int(self.wS.currentText()) self.backend.configuration['GUI']['loggingLevel'] = self.log.currentText() - self.backend.configuration['GUI']['tableColumnsMax'] = int(self.nTC.currentText()) with open(Path.home()/'.pastaELN.json', 'w', encoding='utf-8') as fConf: fConf.write(json.dumps(self.backend.configuration,indent=2)) restart() diff --git a/pasta_eln/gui.py b/pasta_eln/gui.py index 0a752875..7dd843f5 100644 --- a/pasta_eln/gui.py +++ b/pasta_eln/gui.py @@ -11,6 +11,7 @@ from .widgetSidebar import Sidebar from .widgetBody import Body from .dialogForm import Form +from .dialogConfig import Configuration os.environ['QT_API'] = 'pyside6' # Subclass QMainWindow to customize your application's main window @@ -28,10 +29,13 @@ def __init__(self): #Menubar menu = self.menuBar() - _ = menu.addMenu("&File") + menu.addMenu("&File") viewMenu = menu.addMenu("&View list") - _ = menu.addMenu("&System") - _ = menu.addMenu("&Help") + systemMenu = menu.addMenu("&System") + action = QAction('&Configuration',self) + action.triggered.connect(self.openConfigDialog) + systemMenu.addAction(action) + menu.addMenu("&Help") shortCuts = {'measurement':'m', 'sample':'s'} for docType, docLabel in self.comm.backend.db.dataLabels.items(): @@ -78,6 +82,14 @@ def viewMenu(self): self.comm.changeTable.emit(docType, '') return + def openConfigDialog(self): + """ + open configuration dialog + """ + configWindow = Configuration(self.comm.backend) + configWindow.exec() + return + ############## ## Main function diff --git a/pasta_eln/widgetDetails.py b/pasta_eln/widgetDetails.py index aa1b0bc7..afa66967 100644 --- a/pasta_eln/widgetDetails.py +++ b/pasta_eln/widgetDetails.py @@ -1,9 +1,9 @@ """ widget that shows the details of the items """ import json -from PySide6.QtWidgets import QScrollArea, QWidget, QVBoxLayout, QHBoxLayout, QLabel # pylint: disable=no-name-in-module +from PySide6.QtWidgets import QScrollArea, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QMenu # pylint: disable=no-name-in-module from PySide6.QtCore import Qt, Slot, QByteArray # pylint: disable=no-name-in-module from PySide6.QtSvgWidgets import QSvgWidget # pylint: disable=no-name-in-module -from PySide6.QtGui import QPixmap, QImage # pylint: disable=no-name-in-module +from PySide6.QtGui import QPixmap, QImage, QAction# pylint: disable=no-name-in-module from .style import TextButton, Image, Label class Details(QScrollArea): @@ -26,8 +26,9 @@ def __init__(self, comm): self.headerL = QHBoxLayout(headerW) self.mainL.addWidget(headerW) self.imageW = QWidget() + self.imageW.setContextMenuPolicy(Qt.CustomContextMenu) + self.imageW.customContextMenuRequested.connect(self.contextMenu) self.imageL = QVBoxLayout(self.imageW) - #TODO_P2 include extractor change self.mainL.addWidget(self.imageW) self.btnDetails = TextButton('Details', self.showArea, self.mainL, 'Details', 'Show / hide details', \ checkable=True, hide=True) @@ -51,6 +52,22 @@ def __init__(self, comm): self.mainL.addWidget(self.metaDatabaseW) self.mainL.addStretch(1) + def contextMenu(self, pos): + context = QMenu(self) + mask = '/'.join(self.doc['-type'][:3]) + choices= {key:value for key,value in self.comm.backend.configuration['extractors'].items() if key.startswith(mask)} + for key,value in choices.items(): + thisAction = QAction(value, self) + thisAction.setData(key) + thisAction.triggered.connect(self.changeExtractor) + context.addAction(thisAction) + context.exec(self.mapToGlobal(pos)) + return + + def changeExtractor(self): + extractor = self.sender().data() + print(extractor) + return @Slot(str) def changeDetails(self, docID): From 5ac8b96a31044dcc3e5f4a96e68db14cbf3a3a47 Mon Sep 17 00:00:00 2001 From: "Dr. Steffen Brinckmann" Date: Fri, 17 Feb 2023 17:14:44 +0100 Subject: [PATCH 3/3] Linting --- pasta_eln/widgetDetails.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pasta_eln/widgetDetails.py b/pasta_eln/widgetDetails.py index afa66967..78162270 100644 --- a/pasta_eln/widgetDetails.py +++ b/pasta_eln/widgetDetails.py @@ -53,6 +53,12 @@ def __init__(self, comm): self.mainL.addStretch(1) def contextMenu(self, pos): + """ + Create a context menu + + Args: + pos (position): Position to create context menu at + """ context = QMenu(self) mask = '/'.join(self.doc['-type'][:3]) choices= {key:value for key,value in self.comm.backend.configuration['extractors'].items() if key.startswith(mask)} @@ -65,6 +71,9 @@ def contextMenu(self, pos): return def changeExtractor(self): + """ + What happens when user changes extractor + """ extractor = self.sender().data() print(extractor) return