From 6cdf203079958cb5e3300febad1350111bebb753 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 9 May 2020 12:17:56 +0200 Subject: [PATCH 1/4] Cleanup of comments and a bit of syntax in the project file --- nw/core/project.py | 126 +++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/nw/core/project.py b/nw/core/project.py index 57fc7051..965b4746 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -3,7 +3,7 @@ novelWriter – Project Wrapper =============================== - Class holding a project + Class wrapping the data if a novelWriter project File History: Created: 2018-09-29 [0.0.1] NWProject @@ -62,12 +62,12 @@ class NWProject(): self.projTree = NWTree(self) # The project tree # Project Status - self.projOpened = None # The time stamp of when the project file was opened - self.projChanged = None # The project has unsaved changes - self.projAltered = None # The project has been altered this session - self.lockedBy = None # Data on which computer has the project open - self.saveCount = None # Meta data: number of saves - self.autoCount = None # Meta data: number of automatic saves + self.projOpened = 0 # The time stamp of when the project file was opened + self.projChanged = False # The project has unsaved changes + self.projAltered = False # The project has been altered this session + self.lockedBy = None # Data on which computer has the project open + self.saveCount = None # Meta data: number of saves + self.autoCount = None # Meta data: number of automatic saves # Class Settings self.projPath = None # The full path to where the currently open project is saved @@ -76,23 +76,23 @@ class NWProject(): self.projFile = None # The file name of the project main XML file # Project Meta - self.projName = None - self.bookTitle = None - self.bookAuthors = None + self.projName = "" # Project name (working title) + self.bookTitle = "" # The final title; should only be used for exports + self.bookAuthors = [] # A list of book authors # Various - self.autoReplace = None + self.autoReplace = {} # Text to auto-replace on exports # Project Settings - self.spellCheck = False - self.autoOutline = True - self.statusItems = None - self.importItems = None - self.lastEdited = None - self.lastViewed = None - self.lastWCount = 0 - self.currWCount = 0 - self.doBackup = True + self.spellCheck = False # Controls the spellcheck-as-you-type feature + self.autoOutline = True # If true, the Project Outline is updated automatically + self.statusItems = None # Novel file progress status values + self.importItems = None # Note file importance values + self.lastEdited = None # The handle of the last file to be edited + self.lastViewed = None # The handle of the last file to be viewed + self.lastWCount = 0 # The project word count from last session + self.currWCount = 0 # The project word count in current session + self.doBackup = True # Run project backup on exit # Set Defaults self.clearProject() @@ -182,8 +182,8 @@ class NWProject(): """ # Project Status - self.projOpened = None - self.projChanged = None + self.projOpened = 0 + self.projChanged = False self.projAltered = False self.saveCount = 0 self.autoCount = 0 @@ -240,7 +240,7 @@ class NWProject(): self.projDict = path.join(self.projMeta, nwFiles.PROJ_DICT) if not self._checkFolder(self.projMeta): - return + return False if overrideLock: self._clearLockFile() @@ -384,8 +384,10 @@ class NWProject(): self.projMeta = path.join(self.projPath,"meta") saveTime = time() - if not self._checkFolder(self.projPath): return - if not self._checkFolder(self.projMeta): return + if not self._checkFolder(self.projPath): + return False + if not self._checkFolder(self.projMeta): + return False logger.debug("Saving project: %s" % self.projPath) @@ -559,6 +561,9 @@ class NWProject(): ## def setProjectPath(self, projPath): + """Set the project storage path, and also expand ~ to the user + directory using the path library. + """ if projPath is None or projPath == "": self.projPath = None else: @@ -569,16 +574,23 @@ class NWProject(): return True def setProjectName(self, projName): + """Set the project name (working title), This is the the title + used for backup files etc. + """ self.projName = projName.strip() self.setProjectChanged(True) return True def setBookTitle(self, bookTitle): + """Set the boom title, that is, the title to include in exports. + """ self.bookTitle = bookTitle.strip() self.setProjectChanged(True) return True def setBookAuthors(self, bookAuthors): + """A line separated list of book authors, parsed into an array. + """ self.bookAuthors = [] for bookAuthor in bookAuthors.split("\n"): bookAuthor = bookAuthor.strip() @@ -589,36 +601,44 @@ class NWProject(): return True def setProjBackup(self, doBackup): - self.doBackup = False + """Set whether projects should be backed up or not. The user + will notified in case dependant settings are missing. + """ + self.doBackup = doBackup if doBackup: if not path.isdir(self.mainConf.backupPath): self.theParent.makeAlert(( "You must set a valid backup path in preferences to use " "the automatic project backup feature." - ), nwAlert.ERROR) - return False + ), nwAlert.WARN) if self.projName == "": self.theParent.makeAlert(( "You must set a valid project name in project settings to " "use the automatic project backup feature." - ), nwAlert.ERROR) - return False - self.doBackup = True + ), nwAlert.WARN) return True def setSpellCheck(self, theMode): + """Enable/disable spell checking. + """ if self.spellCheck != theMode: self.spellCheck = theMode self.setProjectChanged(True) return True def setAutoOutline(self, theMode): + """Enable/disable automatic update of project outline. + """ if self.autoOutline != theMode: self.autoOutline = theMode self.setProjectChanged(True) return True def setTreeOrder(self, newOrder): + """A list representing the liner/flattened order of project + items in the GUI project tree. The user can rearrange the order + by drag-and-drop. Forwarded to the NWTree class. + """ if len(self.projTree) != len(newOrder): logger.warning("Size of new and old tree order does not match") self.projTree.setOrder(newOrder) @@ -626,48 +646,64 @@ class NWProject(): return True def setLastEdited(self, tHandle): + """Set last edited project item. + """ if self.lastEdited != tHandle: self.lastEdited = tHandle self.setProjectChanged(True) return True def setLastViewed(self, tHandle): + """Set last viewed project item. + """ if self.lastViewed != tHandle: self.lastViewed = tHandle self.setProjectChanged(True) return True def setProjectWordCount(self, theCount): + """Set the current project word count. + """ if self.currWCount != theCount: self.currWCount = theCount self.setProjectChanged(True) return True def setStatusColours(self, newCols): + """Update the list of novel file status flags. Also iterate + through the project and replace keys that have been renamed. + """ replaceMap = self.statusItems.setNewEntries(newCols) - if self.projTree: - for nwItem in self.projTree: - if nwItem.itemClass == nwItemClass.NOVEL: - if nwItem.itemStatus in replaceMap.keys(): - nwItem.setStatus(replaceMap[nwItem.itemStatus]) + for nwItem in self.projTree: + if nwItem.itemClass == nwItemClass.NOVEL: + if nwItem.itemStatus in replaceMap.keys(): + nwItem.setStatus(replaceMap[nwItem.itemStatus]) self.setProjectChanged(True) return def setImportColours(self, newCols): + """Update the list of note file importance flags. Also iterate + through the project and replace keys that have been renamed. + """ replaceMap = self.importItems.setNewEntries(newCols) - if self.projTree: - for nwItem in self.projTree: - if nwItem.itemClass != nwItemClass.NOVEL: - if nwItem.itemStatus in replaceMap.keys(): - nwItem.setStatus(replaceMap[nwItem.itemStatus]) + for nwItem in self.projTree: + if nwItem.itemClass != nwItemClass.NOVEL: + if nwItem.itemStatus in replaceMap.keys(): + nwItem.setStatus(replaceMap[nwItem.itemStatus]) self.setProjectChanged(True) return def setAutoReplace(self, autoReplace): + """Update the auto-replace dictionary. This replaces the entire + dictionary, so alterations have to be made in a copy. + """ self.autoReplace = autoReplace return def setProjectChanged(self, bValue): + """Toggle the project changed flag, and propagate the + information to the GUI statusbar. + """ self.projChanged = bValue self.theParent.setProjectStatus(self.projChanged) if bValue: @@ -733,7 +769,8 @@ class NWProject(): def countStatus(self): """Count how many times the various status flags are used in the - project tree. + project tree. The counts themselves are kept in the NWStatus + objects. This is essentially a refresh. """ self.statusItems.resetCounts() self.importItems.resetCounts() @@ -838,7 +875,7 @@ class NWProject(): def _scanProjectFolder(self): """Scan the project folder and check that the files in it are - also in the project CML file. If they aren't, import them as + also in the project XML file. If they aren't, import them as orphaned files so the user can either delete them, or put them back into the project tree. """ @@ -1019,7 +1056,8 @@ class NWTree(): def checkRootUnique(self, theClass): """Checks if there already is a root entry of class 'theClass' - in the root of the project tree. + in the root of the project tree. CUSTOM class is skipped as it + is not required to be unique. """ if theClass == nwItemClass.CUSTOM: return True From d44dca6a21e4715be5de9ce9a956931494a2b84e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 9 May 2020 13:08:11 +0200 Subject: [PATCH 2/4] Moved the packing of XML tree into NWTree, and added a function for formatting time stamps --- nw/common.py | 10 ++++ nw/config.py | 6 +-- nw/constants/constants.py | 3 +- nw/core/project.py | 89 ++++++++++++++++++++++-------------- nw/gui/dialogs/sessionlog.py | 4 +- 5 files changed, 72 insertions(+), 40 deletions(-) diff --git a/nw/common.py b/nw/common.py index a9ebb7d0..727f74c0 100644 --- a/nw/common.py +++ b/nw/common.py @@ -28,6 +28,10 @@ import logging import nw +from datetime import datetime + +from nw.constants import nwConst + logger = logging.getLogger(__name__) def checkString(checkValue, defaultValue, allowNone=False): @@ -122,6 +126,12 @@ def formatInt(theInt): return "%d" % theInt +def formatTimeStamp(theTime, fileSafe=False): + if fileSafe: + return datetime.fromtimestamp(theTime).strftime(nwConst.fStampFmt) + else: + return datetime.fromtimestamp(theTime).strftime(nwConst.tStampFmt) + def splitVersionNumber(vString): """ Splits a version string on the form aa.bb.cc into major, minor and patch, and computes an integer value aabbcc. diff --git a/nw/config.py b/nw/config.py index 7789ad15..f0f21f32 100644 --- a/nw/config.py +++ b/nw/config.py @@ -32,13 +32,13 @@ import sys import nw from os import path, mkdir, unlink, rename -from datetime import datetime +from time import time from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo from nw.constants import nwFiles, nwUnicode -from nw.common import splitVersionNumber +from nw.common import splitVersionNumber, formatTimeStamp logger = logging.getLogger(__name__) @@ -445,7 +445,7 @@ class Config: ## Main cnfSec = "Main" cnfParse.add_section(cnfSec) - cnfParse.set(cnfSec,"timestamp", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + cnfParse.set(cnfSec,"timestamp", formatTimeStamp(time())) cnfParse.set(cnfSec,"theme", str(self.guiTheme)) cnfParse.set(cnfSec,"syntax", str(self.guiSyntax)) cnfParse.set(cnfSec,"guidark", str(self.guiDark)) diff --git a/nw/constants/constants.py b/nw/constants/constants.py index ef331715..33a6fc15 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -29,7 +29,8 @@ from nw.constants.enum import nwItemClass, nwItemLayout, nwOutline class nwConst(): - tStampFmt = "%Y-%m-%d %H:%M:%S" + tStampFmt = "%Y-%m-%d %H:%M:%S" # Default format + fStampFmt = "%Y-%m-%d %H.%M.%S" # FileName safe format # END Class nwConst diff --git a/nw/core/project.py b/nw/core/project.py index 965b4746..c2c24a9d 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -7,8 +7,8 @@ File History: Created: 2018-09-29 [0.0.1] NWProject - Added: 2018-10-27 [0.0.1] NWItem - Added: 2019-05-19 [0.1.3] NWStatus + Created: 2018-10-27 [0.0.1] NWItem + Created: 2019-05-19 [0.1.3] NWStatus Merged: 2020-05-07 [0.4.5] Moved NWItem class to this file Merged: 2020-05-07 [0.4.5] Moved NWStatus class to this file Added: 2020-05-07 [0.4.5] NWTree @@ -36,13 +36,12 @@ import nw from os import path, mkdir, listdir, unlink, rename from lxml import etree from hashlib import sha256 -from datetime import datetime from time import time from shutil import make_archive from nw.gui.tools import OptionState from nw.core.tools import projectMaintenance -from nw.common import checkString, checkBool, checkInt +from nw.common import checkString, checkBool, checkInt, formatTimeStamp from nw.constants import ( nwFiles, nwConst, nwItemType, nwItemClass, nwItemLayout, nwAlert ) @@ -290,7 +289,7 @@ class NWProject(): self.autoCount = 0 if "appVersion" in xRoot.attrib: - appVersion = xRoot.attrib["appVersion"] + appVersion = xRoot.attrib["appVersion"] if "fileVersion" in xRoot.attrib: fileVersion = xRoot.attrib["fileVersion"] if "saveCount" in xRoot.attrib: @@ -324,35 +323,32 @@ class NWProject(): logger.verbose("Author: '%s'" % xItem.text) self.bookAuthors.append(xItem.text) elif xItem.tag == "backup": - self.doBackup = checkBool(xItem.text,False) + self.doBackup = checkBool(xItem.text, False) elif xChild.tag == "settings": logger.debug("Found project settings") for xItem in xChild: if xItem.text is None: continue if xItem.tag == "spellCheck": - self.spellCheck = checkBool(xItem.text,False) + self.spellCheck = checkBool(xItem.text, False) elif xItem.tag == "autoOutline": - self.autoOutline = checkBool(xItem.text,True) + self.autoOutline = checkBool(xItem.text, True) elif xItem.tag == "lastEdited": - self.lastEdited = checkString(xItem.text,None,True) + self.lastEdited = checkString(xItem.text, None, True) elif xItem.tag == "lastViewed": - self.lastViewed = checkString(xItem.text,None,True) + self.lastViewed = checkString(xItem.text, None, True) elif xItem.tag == "lastWordCount": - self.lastWCount = checkInt(xItem.text,0,False) + self.lastWCount = checkInt(xItem.text, 0, False) elif xItem.tag == "status": self.statusItems.unpackEntries(xItem) elif xItem.tag == "importance": self.importItems.unpackEntries(xItem) elif xItem.tag == "autoReplace": for xEntry in xItem: - self.autoReplace[xEntry.tag] = checkString(xEntry.text,None,False) + self.autoReplace[xEntry.tag] = checkString(xEntry.text, None, False) elif xChild.tag == "content": logger.debug("Found project content") - for xItem in xChild: - nwItem = NWItem(self) - if nwItem.unpackXML(xItem): - self.projTree.append(nwItem.itemHandle, nwItem.parHandle, nwItem) + self.projTree.unpackXML(xChild) self.optState.loadSettings() @@ -403,27 +399,27 @@ class NWProject(): "fileVersion" : "1.0", "saveCount" : str(self.saveCount), "autoCount" : str(self.autoCount), - "timeStamp" : datetime.fromtimestamp(saveTime).strftime("%Y-%m-%d %H:%M:%S"), + "timeStamp" : formatTimeStamp(saveTime), }) # Save Project Meta xProject = etree.SubElement(nwXML, "project") - self._saveProjectValue(xProject, "name", self.projName, True) - self._saveProjectValue(xProject, "title", self.bookTitle, True) - self._saveProjectValue(xProject, "author", self.bookAuthors) - self._saveProjectValue(xProject, "backup", self.doBackup) + self._packProjectValue(xProject, "name", self.projName, True) + self._packProjectValue(xProject, "title", self.bookTitle, True) + self._packProjectValue(xProject, "author", self.bookAuthors) + self._packProjectValue(xProject, "backup", self.doBackup) # Save Project Settings xSettings = etree.SubElement(nwXML, "settings") - self._saveProjectValue(xSettings, "spellCheck", self.spellCheck) - self._saveProjectValue(xSettings, "autoOutline", self.autoOutline) - self._saveProjectValue(xSettings, "lastEdited", self.lastEdited) - self._saveProjectValue(xSettings, "lastViewed", self.lastViewed) - self._saveProjectValue(xSettings, "lastWordCount", self.currWCount) + self._packProjectValue(xSettings, "spellCheck", self.spellCheck) + self._packProjectValue(xSettings, "autoOutline", self.autoOutline) + self._packProjectValue(xSettings, "lastEdited", self.lastEdited) + self._packProjectValue(xSettings, "lastViewed", self.lastViewed) + self._packProjectValue(xSettings, "lastWordCount", self.currWCount) xAutoRep = etree.SubElement(xSettings, "autoReplace") for aKey, aValue in self.autoReplace.items(): if len(aKey) > 0: - self._saveProjectValue(xAutoRep,aKey,aValue) + self._packProjectValue(xAutoRep,aKey,aValue) xStatus = etree.SubElement(xSettings,"status") self.statusItems.packEntries(xStatus) @@ -432,9 +428,7 @@ class NWProject(): # Save Tree Content logger.debug("Writing project content") - xContent = etree.SubElement(nwXML, "content", attrib={"count":str(len(self.projTree))}) - for tItem in self.projTree: - tItem.packXML(xContent) + self.projTree.packXML(nwXML) # Write the xml tree to file tempFile = path.join(self.projPath, self.projFile+"~") @@ -531,7 +525,7 @@ class NWProject(): ) return False - archName = "Backup on %s" % datetime.now().strftime("%Y-%m-%d at %H.%M.%S") + archName = "Backup from %s" % formatTimeStamp(time(),True) baseName = path.join(baseDir, archName) try: @@ -862,7 +856,7 @@ class NWProject(): return False return True - def _saveProjectValue(self, xParent, theName, theValue, allowNone=True): + def _packProjectValue(self, xParent, theName, theValue, allowNone=True): if not isinstance(theValue, list): theValue = [theValue] for aValue in theValue: @@ -946,8 +940,8 @@ class NWProject(): "End: {closed:s} " "Words: {words:8d}" ).format( - opened = datetime.fromtimestamp(self.projOpened).strftime(nwConst.tStampFmt), - closed = datetime.now().strftime(nwConst.tStampFmt), + opened = formatTimeStamp(self.projOpened), + closed = formatTimeStamp(time()), words = self.getSessionWordCount(), ), file=outFile) @@ -1030,6 +1024,33 @@ class NWTree(): return + def packXML(self, xParent): + """Pack the content of the tree into an XML object. + """ + xContent = etree.SubElement(xParent, "content", attrib={ + "count":str(self._theLength)} + ) + for tHandle in self._treeOrder: + tItem = self.__getitem__(tHandle) + tItem.packXML(xContent) + return + + def unpackXML(self, xContent): + """Iterate through all items of a content XML object and add + them to the project tree. + """ + if xContent.tag != "content": + logger.error("XML entry is not a NWTree") + return False + + self.clear() + for xItem in xContent: + nwItem = NWItem(self.theProject) + if nwItem.unpackXML(xItem): + self.append(nwItem.itemHandle, nwItem.parHandle, nwItem) + + return True + ## # Tree Structure Methods ## diff --git a/nw/gui/dialogs/sessionlog.py b/nw/gui/dialogs/sessionlog.py index a2e99f9d..59750dce 100644 --- a/nw/gui/dialogs/sessionlog.py +++ b/nw/gui/dialogs/sessionlog.py @@ -180,8 +180,8 @@ class GuiSessionLogView(QDialog): inData = inLine.split() if len(inData) != 8: continue - dStart = datetime.strptime("%s %s" % (inData[1],inData[2]),nwConst.tStampFmt) - dEnd = datetime.strptime("%s %s" % (inData[4],inData[5]),nwConst.tStampFmt) + dStart = datetime.strptime("%s %s" % (inData[1],inData[2]), nwConst.tStampFmt) + dEnd = datetime.strptime("%s %s" % (inData[4],inData[5]), nwConst.tStampFmt) nWords = int(inData[7]) tDiff = dEnd - dStart sDiff = tDiff.total_seconds() From 75adcdf6791f7548d7273a2893d9f0f4c26371ee Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 9 May 2020 13:59:57 +0200 Subject: [PATCH 3/4] Can now restore document label for orphaned files --- nw/common.py | 17 ++++++++++++ nw/core/document.py | 63 +++++++++++++++++++++++++++++++++++++-------- nw/core/project.py | 14 ++++++++-- nw/guimain.py | 2 +- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/nw/common.py b/nw/common.py index 727f74c0..9f4b8011 100644 --- a/nw/common.py +++ b/nw/common.py @@ -77,6 +77,20 @@ def checkBool(checkValue, defaultValue, allowNone=False): return defaultValue return defaultValue +def isHandle(theString): + """Check if a string is a valid novelWriter handle. + Note: This is case sensitive. Must be lower case! + """ + if not isinstance(theString, str): + return False + if len(theString) != 13: + return False + invalidChar = False + for c in theString: + if c not in "0123456789abcdef": + invalidChar = True + return not invalidChar + def colRange(rgbStart, rgbEnd, nStep): if len(rgbStart) != 3 and len(rgbEnd) != 3 and nStep < 1: @@ -127,6 +141,9 @@ def formatInt(theInt): return "%d" % theInt def formatTimeStamp(theTime, fileSafe=False): + """Take a number (on the format returned by time.time()) and convert + it to a timestamp string. + """ if fileSafe: return datetime.fromtimestamp(theTime).strftime(nwConst.fStampFmt) else: diff --git a/nw/core/document.py b/nw/core/document.py index 17b3203f..aaef4e9e 100644 --- a/nw/core/document.py +++ b/nw/core/document.py @@ -31,6 +31,7 @@ import nw from os import path, mkdir, rename, unlink from nw.constants import nwAlert +from nw.common import isHandle logger = logging.getLogger(__name__) @@ -68,25 +69,29 @@ class NWDoc(): self.docMeta = "" return - def openDocument(self, tHandle, showStatus=True): + def openDocument(self, tHandle, showStatus=True, isOrphan=False): """Open a document from handle, capturing potential file system errors and parse meta data. """ self.docHandle = tHandle - self.theItem = self.theProject.projTree[tHandle] + if not isOrphan: + self.theItem = self.theProject.projTree[tHandle] + else: + self.theItem = None - if self.theItem is None: + if self.theItem is None and not isOrphan: self.clearDocument() return None # By default, the document is editable. # Except for files in the trash folder. self.docEditable = True - if self.theItem.parHandle == self.theProject.projTree.trashRoot(): - self.docEditable = False + if self.theItem is not None: + if self.theItem.parHandle == self.theProject.projTree.trashRoot(): + self.docEditable = False - docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN) + docDir, docFile = self._assemblePath(self.docHandle, self.FILE_MN) self.fileLoc = path.join(docDir,docFile) logger.debug("Opening document %s" % self.fileLoc) dataDir = path.join(self.theProject.projPath, docDir) @@ -100,7 +105,7 @@ class NWDoc(): fstLine = inFile.readline() if fstLine.startswith("%%~ "): # This is the meta line - self.docMeta = fstLine.strip() + self.docMeta = fstLine[4:].strip() else: theText = fstLine theText += inFile.read() @@ -120,7 +125,7 @@ class NWDoc(): logger.verbose("DocMeta: '%s'" % self.docMeta) - if showStatus: + if showStatus and not isOrphan: self.theParent.statusBar.setStatus("Opened Document: %s" % self.theItem.itemName) return theText @@ -133,7 +138,7 @@ class NWDoc(): if self.docHandle is None or not self.docEditable: return False - docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN) + docDir, docFile = self._assemblePath(self.docHandle, self.FILE_MN) logger.debug("Saving document %s" % path.join(docDir,docFile)) dataPath = path.join(self.theProject.projPath, docDir) docPath = path.join(dataPath, docFile) @@ -171,7 +176,7 @@ class NWDoc(): """Permanently delete a document source file and its backups from the project data folder. """ - docDir, docFile = self.assemblePath(tHandle, self.FILE_MN) + docDir, docFile = self._assemblePath(tHandle, self.FILE_MN) dataPath = path.join(self.theProject.projPath, docDir) chkList = [] chkList.append(path.join(dataPath, docFile)) @@ -187,8 +192,44 @@ class NWDoc(): return False return True + ## + # Getters + ## + + def getMeta(self): + """Parses the document meta tag and returns the path and name as + a list and a string. + """ + + if len(self.docMeta) < 14: + # Not enough information + return "", [] + + theMeta = self.docMeta + + # Scan for handles + thePath = [] + for n in range(200): + if len(theMeta) < 14: + break + if theMeta[13] == ":": + theHandle = theMeta[:13] + if isHandle(theHandle): + thePath.append(theHandle) + theMeta = theMeta[14:] + else: + break + else: + break + + return theMeta, thePath + + ## + # Internal Functions + ## + @staticmethod - def assemblePath(tHandle, docExt): + def _assemblePath(tHandle, docExt): if tHandle is None: return None, None docDir = "data_"+tHandle[0] diff --git a/nw/core/project.py b/nw/core/project.py index c2c24a9d..b3cad92f 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -41,6 +41,7 @@ from shutil import make_archive from nw.gui.tools import OptionState from nw.core.tools import projectMaintenance +from nw.core.document import NWDoc from nw.common import checkString, checkBool, checkInt, formatTimeStamp from nw.constants import ( nwFiles, nwConst, nwItemType, nwItemClass, nwItemLayout, nwAlert @@ -913,11 +914,20 @@ class NWProject(): return # Handle orphans + aDoc = NWDoc(self, self.theParent) nOrph = 0 for oHandle in orphanFiles: - nOrph += 1 + + # Look for meta data + if aDoc.openDocument(oHandle, showStatus=False, isOrphan=True): + oName, oPath = aDoc.getMeta() + aDoc.clearDocument() + else: + nOrph += 1 + oName = "Orphaned File %d" % nOrph + orphItem = NWItem(self) - orphItem.setName("Orphaned File %d" % nOrph) + orphItem.setName(oName) orphItem.setType(nwItemType.FILE) orphItem.setClass(nwItemClass.NO_CLASS) orphItem.setLayout(nwItemLayout.NO_LAYOUT) diff --git a/nw/guimain.py b/nw/guimain.py index b3356301..2f1380bd 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -404,7 +404,7 @@ class GuiMain(QMainWindow): else: return False - # project is loaded + # Project is loaded self.hasProject = True # Load the tag index From 59a7be6b89870ee8d57f630ff80b5ca39a7d51ae Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 9 May 2020 14:02:20 +0200 Subject: [PATCH 4/4] Imrpoved readability of call --- nw/core/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nw/core/project.py b/nw/core/project.py index b3cad92f..ca1d7a55 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -526,7 +526,7 @@ class NWProject(): ) return False - archName = "Backup from %s" % formatTimeStamp(time(),True) + archName = "Backup from %s" % formatTimeStamp(time(), fileSafe=True) baseName = path.join(baseDir, archName) try: