Tighten up the item class (#937)
* Protect the attributes of the NWItem class * Fix test coverage * Add a type checker function to the tree class
This commit is contained in:
committed by
GitHub
parent
af3d0d2eb0
commit
6ac9e1aec4
@@ -107,11 +107,7 @@ class NWIndex():
|
||||
project.
|
||||
"""
|
||||
logger.debug("Re-indexing item '%s'", tHandle)
|
||||
|
||||
tItem = self.theProject.projTree[tHandle]
|
||||
if tItem is None:
|
||||
return False
|
||||
if tItem.itemType != nwItemType.FILE:
|
||||
if not self.theProject.projTree.checkType(tHandle, nwItemType.FILE):
|
||||
return False
|
||||
|
||||
theDoc = NWDoc(self.theProject, tHandle)
|
||||
|
||||
+135
-65
@@ -42,26 +42,96 @@ class NWItem():
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
self.itemName = ""
|
||||
self.itemHandle = None
|
||||
self.itemParent = None
|
||||
self.itemOrder = 0
|
||||
self.itemType = nwItemType.NO_TYPE
|
||||
self.itemClass = nwItemClass.NO_CLASS
|
||||
self.itemLayout = nwItemLayout.NO_LAYOUT
|
||||
self.itemStatus = None
|
||||
self.isExpanded = False
|
||||
self.isExported = True
|
||||
self._name = ""
|
||||
self._handle = None
|
||||
self._parent = None
|
||||
self._order = 0
|
||||
self._type = nwItemType.NO_TYPE
|
||||
self._class = nwItemClass.NO_CLASS
|
||||
self._layout = nwItemLayout.NO_LAYOUT
|
||||
self._status = None
|
||||
self._expanded = False
|
||||
self._exported = True
|
||||
|
||||
# Document Meta Data
|
||||
self.charCount = 0 # Current character count
|
||||
self.wordCount = 0 # Current word count
|
||||
self.paraCount = 0 # Current paragraph count
|
||||
self.initCount = 0 # Initial word count
|
||||
self.cursorPos = 0 # Last cursor position
|
||||
self._charCount = 0 # Current character count
|
||||
self._wordCount = 0 # Current word count
|
||||
self._paraCount = 0 # Current paragraph count
|
||||
self._cursorPos = 0 # Last cursor position
|
||||
self._initCount = 0 # Initial word count
|
||||
|
||||
return
|
||||
|
||||
def __repr__(self):
|
||||
return f"<NWItem handle={self._handle}, parent={self._parent}, name='{self._name}'>"
|
||||
|
||||
def __bool__(self):
|
||||
return self._handle is not None
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def itemName(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def itemHandle(self):
|
||||
return self._handle
|
||||
|
||||
@property
|
||||
def itemParent(self):
|
||||
return self._parent
|
||||
|
||||
@property
|
||||
def itemOrder(self):
|
||||
return self._order
|
||||
|
||||
@property
|
||||
def itemType(self):
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def itemClass(self):
|
||||
return self._class
|
||||
|
||||
@property
|
||||
def itemLayout(self):
|
||||
return self._layout
|
||||
|
||||
@property
|
||||
def itemStatus(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def isExpanded(self):
|
||||
return self._expanded
|
||||
|
||||
@property
|
||||
def isExported(self):
|
||||
return self._exported
|
||||
|
||||
@property
|
||||
def charCount(self):
|
||||
return self._charCount
|
||||
|
||||
@property
|
||||
def wordCount(self):
|
||||
return self._wordCount
|
||||
|
||||
@property
|
||||
def paraCount(self):
|
||||
return self._paraCount
|
||||
|
||||
@property
|
||||
def initCount(self):
|
||||
return self._initCount
|
||||
|
||||
@property
|
||||
def cursorPos(self):
|
||||
return self._cursorPos
|
||||
|
||||
##
|
||||
# XML Pack/Unpack
|
||||
##
|
||||
@@ -70,23 +140,23 @@ class NWItem():
|
||||
"""Pack all the data in the class instance into an XML object.
|
||||
"""
|
||||
xPack = etree.SubElement(xParent, "item", attrib={
|
||||
"handle": str(self.itemHandle),
|
||||
"order": str(self.itemOrder),
|
||||
"parent": str(self.itemParent),
|
||||
"handle": str(self._handle),
|
||||
"order": str(self._order),
|
||||
"parent": str(self._parent),
|
||||
})
|
||||
self._subPack(xPack, "name", text=str(self.itemName))
|
||||
self._subPack(xPack, "type", text=str(self.itemType.name))
|
||||
self._subPack(xPack, "class", text=str(self.itemClass.name))
|
||||
self._subPack(xPack, "status", text=str(self.itemStatus))
|
||||
if self.itemType == nwItemType.FILE:
|
||||
self._subPack(xPack, "exported", text=str(self.isExported))
|
||||
self._subPack(xPack, "layout", text=str(self.itemLayout.name))
|
||||
self._subPack(xPack, "charCount", text=str(self.charCount), none=False)
|
||||
self._subPack(xPack, "wordCount", text=str(self.wordCount), none=False)
|
||||
self._subPack(xPack, "paraCount", text=str(self.paraCount), none=False)
|
||||
self._subPack(xPack, "cursorPos", text=str(self.cursorPos), none=False)
|
||||
self._subPack(xPack, "name", text=str(self._name))
|
||||
self._subPack(xPack, "type", text=str(self._type.name))
|
||||
self._subPack(xPack, "class", text=str(self._class.name))
|
||||
self._subPack(xPack, "status", text=str(self._status))
|
||||
if self._type == nwItemType.FILE:
|
||||
self._subPack(xPack, "exported", text=str(self._exported))
|
||||
self._subPack(xPack, "layout", text=str(self._layout.name))
|
||||
self._subPack(xPack, "charCount", text=str(self._charCount), none=False)
|
||||
self._subPack(xPack, "wordCount", text=str(self._wordCount), none=False)
|
||||
self._subPack(xPack, "paraCount", text=str(self._paraCount), none=False)
|
||||
self._subPack(xPack, "cursorPos", text=str(self._cursorPos), none=False)
|
||||
else:
|
||||
self._subPack(xPack, "expanded", text=str(self.isExpanded))
|
||||
self._subPack(xPack, "expanded", text=str(self._expanded))
|
||||
|
||||
return
|
||||
|
||||
@@ -162,12 +232,12 @@ class NWItem():
|
||||
"""Return a string description of the item.
|
||||
"""
|
||||
descKey = "none"
|
||||
if self.itemType == nwItemType.ROOT:
|
||||
if self._type == nwItemType.ROOT:
|
||||
descKey = "root"
|
||||
elif self.itemType == nwItemType.FOLDER:
|
||||
elif self._type == nwItemType.FOLDER:
|
||||
descKey = "folder"
|
||||
elif self.itemType == nwItemType.FILE:
|
||||
if self.itemLayout == nwItemLayout.DOCUMENT:
|
||||
elif self._type == nwItemType.FILE:
|
||||
if self._layout == nwItemLayout.DOCUMENT:
|
||||
if hLevel == "H1":
|
||||
descKey = "doc_h1"
|
||||
elif hLevel == "H2":
|
||||
@@ -176,7 +246,7 @@ class NWItem():
|
||||
descKey = "doc_h3"
|
||||
else:
|
||||
descKey = "document"
|
||||
elif self.itemLayout == nwItemLayout.NOTE:
|
||||
elif self._layout == nwItemLayout.NOTE:
|
||||
descKey = "note"
|
||||
|
||||
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
|
||||
@@ -189,29 +259,29 @@ class NWItem():
|
||||
"""Set the item name.
|
||||
"""
|
||||
if isinstance(theName, str):
|
||||
self.itemName = theName.strip()
|
||||
self._name = theName.strip()
|
||||
else:
|
||||
self.itemName = ""
|
||||
self._name = ""
|
||||
return
|
||||
|
||||
def setHandle(self, theHandle):
|
||||
"""Set the item handle, and ensure it is valid.
|
||||
"""
|
||||
if isHandle(theHandle):
|
||||
self.itemHandle = theHandle
|
||||
self._handle = theHandle
|
||||
else:
|
||||
self.itemHandle = None
|
||||
self._handle = None
|
||||
return
|
||||
|
||||
def setParent(self, theParent):
|
||||
"""Set the parent handle, and ensure it is valid.
|
||||
"""
|
||||
if theParent is None:
|
||||
self.itemParent = None
|
||||
self._parent = None
|
||||
elif isHandle(theParent):
|
||||
self.itemParent = theParent
|
||||
self._parent = theParent
|
||||
else:
|
||||
self.itemParent = None
|
||||
self._parent = None
|
||||
return
|
||||
|
||||
def setOrder(self, theOrder):
|
||||
@@ -219,7 +289,7 @@ class NWItem():
|
||||
is purely a meta value, and not actually used by novelWriter at
|
||||
the moment.
|
||||
"""
|
||||
self.itemOrder = checkInt(theOrder, 0)
|
||||
self._order = checkInt(theOrder, 0)
|
||||
return
|
||||
|
||||
def setType(self, theType):
|
||||
@@ -227,12 +297,12 @@ class NWItem():
|
||||
from a string representing an nwItemType.
|
||||
"""
|
||||
if isinstance(theType, nwItemType):
|
||||
self.itemType = theType
|
||||
self._type = theType
|
||||
elif isItemType(theType):
|
||||
self.itemType = nwItemType[theType]
|
||||
self._type = nwItemType[theType]
|
||||
else:
|
||||
logger.error("Unrecognised item type '%s'", theType)
|
||||
self.itemType = nwItemType.NO_TYPE
|
||||
self._type = nwItemType.NO_TYPE
|
||||
return
|
||||
|
||||
def setClass(self, theClass):
|
||||
@@ -240,12 +310,12 @@ class NWItem():
|
||||
it from a string representing an nwItemClass.
|
||||
"""
|
||||
if isinstance(theClass, nwItemClass):
|
||||
self.itemClass = theClass
|
||||
self._class = theClass
|
||||
elif isItemClass(theClass):
|
||||
self.itemClass = nwItemClass[theClass]
|
||||
self._class = nwItemClass[theClass]
|
||||
else:
|
||||
logger.error("Unrecognised item class '%s'", theClass)
|
||||
self.itemClass = nwItemClass.NO_CLASS
|
||||
self._class = nwItemClass.NO_CLASS
|
||||
return
|
||||
|
||||
def setLayout(self, theLayout):
|
||||
@@ -253,42 +323,42 @@ class NWItem():
|
||||
it from a string representing an nwItemLayout.
|
||||
"""
|
||||
if isinstance(theLayout, nwItemLayout):
|
||||
self.itemLayout = theLayout
|
||||
self._layout = theLayout
|
||||
elif isItemLayout(theLayout):
|
||||
self.itemLayout = nwItemLayout[theLayout]
|
||||
self._layout = nwItemLayout[theLayout]
|
||||
elif theLayout in nwLists.DEP_LAYOUT:
|
||||
self.itemLayout = nwItemLayout.DOCUMENT
|
||||
self._layout = nwItemLayout.DOCUMENT
|
||||
else:
|
||||
logger.error("Unrecognised item layout '%s'", theLayout)
|
||||
self.itemLayout = nwItemLayout.NO_LAYOUT
|
||||
self._layout = nwItemLayout.NO_LAYOUT
|
||||
return
|
||||
|
||||
def setStatus(self, theStatus):
|
||||
"""Set the item status by looking it up in the valid status
|
||||
items of the current project.
|
||||
"""
|
||||
if self.itemClass in nwLists.CLS_NOVEL:
|
||||
self.itemStatus = self.theProject.statusItems.checkEntry(theStatus)
|
||||
if self._class in nwLists.CLS_NOVEL:
|
||||
self._status = self.theProject.statusItems.checkEntry(theStatus)
|
||||
else:
|
||||
self.itemStatus = self.theProject.importItems.checkEntry(theStatus)
|
||||
self._status = self.theProject.importItems.checkEntry(theStatus)
|
||||
return
|
||||
|
||||
def setExpanded(self, expState):
|
||||
"""Set the expanded status of an item in the project tree.
|
||||
"""
|
||||
if isinstance(expState, str):
|
||||
self.isExpanded = (expState == str(True))
|
||||
self._expanded = (expState == str(True))
|
||||
else:
|
||||
self.isExpanded = (expState is True)
|
||||
self._expanded = (expState is True)
|
||||
return
|
||||
|
||||
def setExported(self, expState):
|
||||
"""Set the export flag.
|
||||
"""
|
||||
if isinstance(expState, str):
|
||||
self.isExported = (expState == str(True))
|
||||
self._exported = (expState == str(True))
|
||||
else:
|
||||
self.isExported = (expState is True)
|
||||
self._exported = (expState is True)
|
||||
return
|
||||
|
||||
##
|
||||
@@ -298,31 +368,31 @@ class NWItem():
|
||||
def setCharCount(self, theCount):
|
||||
"""Set the character count, and ensure that it is an integer.
|
||||
"""
|
||||
self.charCount = max(0, checkInt(theCount, 0))
|
||||
self._charCount = max(0, checkInt(theCount, 0))
|
||||
return
|
||||
|
||||
def setWordCount(self, theCount):
|
||||
"""Set the word count, and ensure that it is an integer.
|
||||
"""
|
||||
self.wordCount = max(0, checkInt(theCount, 0))
|
||||
self._wordCount = max(0, checkInt(theCount, 0))
|
||||
return
|
||||
|
||||
def setParaCount(self, theCount):
|
||||
"""Set the paragraph count, and ensure that it is an integer.
|
||||
"""
|
||||
self.paraCount = max(0, checkInt(theCount, 0))
|
||||
self._paraCount = max(0, checkInt(theCount, 0))
|
||||
return
|
||||
|
||||
def setCursorPos(self, thePosition):
|
||||
"""Set the cursor position, and ensure that it is an integer.
|
||||
"""
|
||||
self.cursorPos = max(0, checkInt(thePosition, 0))
|
||||
self._cursorPos = max(0, checkInt(thePosition, 0))
|
||||
return
|
||||
|
||||
def saveInitialCount(self):
|
||||
"""Save the initial word count.
|
||||
"""
|
||||
self.initCount = self.wordCount
|
||||
self._initCount = self._wordCount
|
||||
return
|
||||
|
||||
# END Class NWItem
|
||||
|
||||
@@ -1166,7 +1166,7 @@ class NWProject():
|
||||
nMax = min(len(iterItems), 10000)
|
||||
while n < nMax:
|
||||
tHandle = iterItems[n]
|
||||
tItem = self.projTree[tHandle]
|
||||
tItem = self.projTree[tHandle]
|
||||
n += 1
|
||||
if tItem is None:
|
||||
# Technically a bug since treeOrder is built from the
|
||||
|
||||
@@ -270,11 +270,7 @@ class Tokenizer():
|
||||
def addRootHeading(self, theHandle):
|
||||
"""Add a heading at the start of a new root folder.
|
||||
"""
|
||||
theItem = self.theProject.projTree[theHandle]
|
||||
if theItem is None:
|
||||
return False
|
||||
|
||||
if theItem.itemType != nwItemType.ROOT:
|
||||
if not self.theProject.projTree.checkType(theHandle, nwItemType.ROOT):
|
||||
return False
|
||||
|
||||
if self._isFirst:
|
||||
@@ -283,6 +279,7 @@ class Tokenizer():
|
||||
else:
|
||||
textAlign = self.A_PBB | self.A_CENTRE
|
||||
|
||||
theItem = self.theProject.projTree[theHandle]
|
||||
locNotes = self._localLookup("Notes")
|
||||
theTitle = f"{locNotes}: {theItem.itemName}"
|
||||
self._theTokens = []
|
||||
@@ -299,7 +296,7 @@ class Tokenizer():
|
||||
not set, load it from the file.
|
||||
"""
|
||||
self._theHandle = theHandle
|
||||
self._theItem = self.theProject.projTree[theHandle]
|
||||
self._theItem = self.theProject.projTree[theHandle]
|
||||
if self._theItem is None:
|
||||
return False
|
||||
|
||||
|
||||
@@ -210,6 +210,14 @@ class NWTree():
|
||||
# Tree Structure Methods
|
||||
##
|
||||
|
||||
def checkType(self, tHandle, itemType):
|
||||
"""Return true of item exists and is of the specified item type.
|
||||
"""
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if not tItem:
|
||||
return False
|
||||
return tItem.itemType == itemType
|
||||
|
||||
def trashRoot(self):
|
||||
"""Returns the handle of the trash folder, or None if there
|
||||
isn't one.
|
||||
|
||||
Reference in New Issue
Block a user