Use the new index instead of the old
This commit is contained in:
+126
-274
@@ -34,9 +34,7 @@ from novelwriter.enum import nwItemType, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.constants import nwFiles, nwKeyWords, nwUnicode
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.common import (
|
||||
checkInt, isHandle, isTitleTag, isItemLayout, jsonEncode
|
||||
)
|
||||
from novelwriter.common import checkInt, jsonEncode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -55,10 +53,6 @@ class NWIndex():
|
||||
self._indexBroken = False
|
||||
|
||||
# Indices
|
||||
self._refIndex = {}
|
||||
self._fileIndex = {}
|
||||
self._fileMeta = {}
|
||||
|
||||
self._tags = {}
|
||||
self._items = {}
|
||||
|
||||
@@ -80,9 +74,6 @@ class NWIndex():
|
||||
def clearIndex(self):
|
||||
"""Clear the index dictionaries and time stamps.
|
||||
"""
|
||||
self._refIndex = {}
|
||||
self._fileIndex = {}
|
||||
self._fileMeta = {}
|
||||
self._timeNovel = 0
|
||||
self._timeNotes = 0
|
||||
self._timeIndex = 0
|
||||
@@ -95,15 +86,15 @@ class NWIndex():
|
||||
def deleteHandle(self, tHandle):
|
||||
"""Delete all entries of a given document handle.
|
||||
"""
|
||||
if tHandle not in self._items:
|
||||
return
|
||||
|
||||
logger.debug("Removing item '%s' from the index", tHandle)
|
||||
|
||||
delTags = list(filter(lambda x: self._tags[x].get("handle") == tHandle, self._tags))
|
||||
for tTag in delTags:
|
||||
for tTag in self._items[tHandle].allTags():
|
||||
self._tags.pop(tTag, None)
|
||||
|
||||
self._refIndex.pop(tHandle, None)
|
||||
self._fileIndex.pop(tHandle, None)
|
||||
self._fileMeta.pop(tHandle, None)
|
||||
self._items.pop(tHandle, None)
|
||||
|
||||
return
|
||||
|
||||
@@ -148,32 +139,6 @@ class NWIndex():
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
tStart = time()
|
||||
|
||||
if os.path.isfile(indexFile):
|
||||
logger.debug("Loading index file")
|
||||
try:
|
||||
with open(indexFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = json.load(inFile)
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load index file")
|
||||
logException()
|
||||
self._indexBroken = True
|
||||
return False
|
||||
|
||||
self._refIndex = theData.get("refIndex", {})
|
||||
self._fileIndex = theData.get("fileIndex", {})
|
||||
self._fileMeta = theData.get("fileMeta", {})
|
||||
|
||||
nowTime = round(time())
|
||||
self._timeNovel = nowTime
|
||||
self._timeNotes = nowTime
|
||||
self._timeIndex = nowTime
|
||||
|
||||
logger.verbose("Index loaded in %.3f ms", (time() - tStart)*1000)
|
||||
|
||||
indexFile = os.path.join(self.theProject.projMeta, "tagsIndex2.json")
|
||||
tStart = time()
|
||||
|
||||
if os.path.isfile(indexFile):
|
||||
logger.debug("Loading index file")
|
||||
try:
|
||||
@@ -194,6 +159,11 @@ class NWIndex():
|
||||
tItem.unpackData(tData)
|
||||
self._items[tHandle] = tItem
|
||||
|
||||
nowTime = round(time())
|
||||
self._timeNovel = nowTime
|
||||
self._timeNotes = nowTime
|
||||
self._timeIndex = nowTime
|
||||
|
||||
logger.verbose("Index loaded in %.3f ms", (time() - tStart)*1000)
|
||||
|
||||
self._checkIndex()
|
||||
@@ -209,11 +179,11 @@ class NWIndex():
|
||||
tStart = time()
|
||||
|
||||
try:
|
||||
itemsIndex = {handle: item.packData() for handle, item in self._items.items()}
|
||||
with open(indexFile, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write("{\n")
|
||||
outFile.write(f' "refIndex": {jsonEncode(self._refIndex, n=1, nmax=3)},\n')
|
||||
outFile.write(f' "fileIndex": {jsonEncode(self._fileIndex, n=1, nmax=3)},\n')
|
||||
outFile.write(f' "fileMeta": {jsonEncode(self._fileMeta, n=1, nmax=2)}\n')
|
||||
outFile.write(f' "tagsIndex": {jsonEncode(self._tags, n=1, nmax=2)},\n')
|
||||
outFile.write(f' "itemIndex": {jsonEncode(itemsIndex, n=1, nmax=4)}\n')
|
||||
outFile.write("}\n")
|
||||
|
||||
except Exception:
|
||||
@@ -223,18 +193,6 @@ class NWIndex():
|
||||
|
||||
logger.verbose("Index saved in %.3f ms", (time() - tStart)*1000)
|
||||
|
||||
indexFile = os.path.join(self.theProject.projMeta, "tagsIndex2.json")
|
||||
tStart = time()
|
||||
|
||||
itemsIndex = {handle: item.packData() for handle, item in self._items.items()}
|
||||
with open(indexFile, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write("{\n")
|
||||
outFile.write(f' "tagsIndex": {jsonEncode(self._tags, n=1, nmax=2)},\n')
|
||||
outFile.write(f' "itemIndex": {jsonEncode(itemsIndex, n=1, nmax=4)}\n')
|
||||
outFile.write("}\n")
|
||||
|
||||
logger.verbose("Index saved in %.3f ms", (time() - tStart)*1000)
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
@@ -256,11 +214,12 @@ class NWIndex():
|
||||
logger.info("Not indexing non-file item '%s'", tHandle)
|
||||
return False
|
||||
|
||||
# Run word counter for the whole text
|
||||
cC, wC, pC = countWords(theText)
|
||||
self._fileMeta[tHandle] = ["H0", cC, wC, pC]
|
||||
self.deleteHandle(tHandle)
|
||||
|
||||
# Run word counter for the whole text
|
||||
self._items[tHandle] = IndexItem(tHandle, theItem)
|
||||
|
||||
cC, wC, pC = countWords(theText)
|
||||
theItem.setCharCount(cC)
|
||||
theItem.setWordCount(wC)
|
||||
theItem.setParaCount(pC)
|
||||
@@ -282,15 +241,6 @@ class NWIndex():
|
||||
|
||||
logger.debug("Indexing item with handle '%s'", tHandle)
|
||||
|
||||
# Delete or reset old entries for the file
|
||||
self._refIndex.pop(tHandle, None)
|
||||
self._fileIndex[tHandle] = {}
|
||||
|
||||
# Also clear references to the file in the tags index
|
||||
clearTags = list(filter(lambda x: self._tags[x].get("handle") == tHandle, self._tags))
|
||||
for aTag in clearTags:
|
||||
self._tags.pop(aTag)
|
||||
|
||||
# Scan the text content
|
||||
nTitle = 0
|
||||
theLines = theText.splitlines()
|
||||
@@ -326,7 +276,6 @@ class NWIndex():
|
||||
|
||||
# Index page with no titles and references
|
||||
if nTitle == 0:
|
||||
self._indexPage(tHandle, itemLayout)
|
||||
self._indexWordCounts(tHandle, theText, nTitle)
|
||||
|
||||
# Update timestamps for index changes
|
||||
@@ -369,51 +318,17 @@ class NWIndex():
|
||||
return False
|
||||
|
||||
sTitle = f"T{nLine:06d}"
|
||||
self._fileIndex[tHandle][sTitle] = {
|
||||
"level": hDepth,
|
||||
"title": hText,
|
||||
"layout": itemLayout.name,
|
||||
"cCount": 0,
|
||||
"wCount": 0,
|
||||
"pCount": 0,
|
||||
"synopsis": "",
|
||||
}
|
||||
|
||||
if self._fileMeta[tHandle][0] == "H0":
|
||||
# Since this initialises to H0, this ensures that only the
|
||||
# first header level is recorded in the file meta index
|
||||
self._fileMeta[tHandle][0] = hDepth
|
||||
|
||||
tItem = self._items[tHandle]
|
||||
tItem.updateLevel(hDepth)
|
||||
tItem.addHeading(IndexHeading(sTitle, hDepth, hText))
|
||||
|
||||
return True
|
||||
|
||||
def _indexPage(self, tHandle, itemLayout):
|
||||
"""Index a page with no title.
|
||||
"""
|
||||
self._fileIndex[tHandle][H_NONE] = {
|
||||
"level": "H0",
|
||||
"title": "",
|
||||
"layout": itemLayout.name,
|
||||
"cCount": 0,
|
||||
"wCount": 0,
|
||||
"pCount": 0,
|
||||
"synopsis": "",
|
||||
}
|
||||
return
|
||||
|
||||
def _indexWordCounts(self, tHandle, theText, nTitle):
|
||||
"""Count text stats and save the counts to the index.
|
||||
"""
|
||||
cC, wC, pC = countWords(theText)
|
||||
sTitle = f"T{nTitle:06d}"
|
||||
if tHandle in self._fileIndex:
|
||||
if sTitle in self._fileIndex[tHandle]:
|
||||
self._fileIndex[tHandle][sTitle]["cCount"] = cC
|
||||
self._fileIndex[tHandle][sTitle]["wCount"] = wC
|
||||
self._fileIndex[tHandle][sTitle]["pCount"] = pC
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].setHeadingCounts(sTitle, cC, wC, pC)
|
||||
return
|
||||
@@ -422,9 +337,6 @@ class NWIndex():
|
||||
"""Save the synopsis to the index.
|
||||
"""
|
||||
sTitle = f"T{nTitle:06d}"
|
||||
if tHandle in self._fileIndex:
|
||||
if sTitle in self._fileIndex[tHandle]:
|
||||
self._fileIndex[tHandle][sTitle]["synopsis"] = theText
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].setHeadingSynopsis(sTitle, theText)
|
||||
return
|
||||
@@ -451,14 +363,7 @@ class NWIndex():
|
||||
}
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].setHeadingTag(sTitle, theBits[1])
|
||||
|
||||
else:
|
||||
if tHandle not in self._refIndex:
|
||||
self._refIndex[tHandle] = {}
|
||||
if sTitle not in self._refIndex[tHandle]:
|
||||
self._refIndex[tHandle][sTitle] = []
|
||||
for aVal in theBits[1:]:
|
||||
self._refIndex[tHandle][sTitle].append([nLine, theBits[0], aVal])
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].addHeadingReferences(sTitle, theBits[1:], theBits[0])
|
||||
|
||||
@@ -546,17 +451,17 @@ class NWIndex():
|
||||
files, but skipping all note files.
|
||||
"""
|
||||
for tHandle in self._listNovelHandles(skipExcluded):
|
||||
for sTitle in sorted(self._fileIndex[tHandle]):
|
||||
for sTitle in self._items[tHandle].headings:
|
||||
tKey = f"{tHandle}:{sTitle}"
|
||||
yield tKey, tHandle, sTitle, self._fileIndex[tHandle][sTitle]
|
||||
yield tKey, tHandle, sTitle, self._items[tHandle][sTitle]
|
||||
|
||||
def getNovelWordCount(self, skipExcluded=True):
|
||||
"""Count the number of words in the novel project.
|
||||
"""
|
||||
wCount = 0
|
||||
for tHandle in self._listNovelHandles(skipExcluded):
|
||||
for sTitle in self._fileIndex[tHandle]:
|
||||
wCount += self._fileIndex[tHandle][sTitle]["wCount"]
|
||||
for hItem in self._items[tHandle].entries:
|
||||
wCount += hItem.wordCount
|
||||
|
||||
return wCount
|
||||
|
||||
@@ -565,8 +470,8 @@ class NWIndex():
|
||||
"""
|
||||
hCount = [0, 0, 0, 0, 0]
|
||||
for tHandle in self._listNovelHandles(skipExcluded):
|
||||
for sTitle in self._fileIndex[tHandle]:
|
||||
iLevel = H_LEVEL.get(self._fileIndex[tHandle][sTitle]["level"], 0)
|
||||
for hItem in self._items[tHandle].entries:
|
||||
iLevel = H_LEVEL.get(hItem.level, 0)
|
||||
hCount[iLevel] += 1
|
||||
|
||||
return hCount
|
||||
@@ -574,19 +479,26 @@ class NWIndex():
|
||||
def getHandleWordCounts(self, tHandle):
|
||||
"""Get all header word counts for a specific handle.
|
||||
"""
|
||||
hRecord = self._fileIndex.get(tHandle, {})
|
||||
return [(f"{tHandle}:{sTitle}", sData["wCount"]) for sTitle, sData in hRecord.items()]
|
||||
return [
|
||||
(f"{tHandle}:{sTitle}", hItem.wordCount)
|
||||
for sTitle, hItem in self._items.get(tHandle, {}).items()
|
||||
]
|
||||
|
||||
def getHandleHeaders(self, tHandle):
|
||||
"""Get all headers for a specific handle.
|
||||
"""
|
||||
hRecord = self._fileIndex.get(tHandle, {})
|
||||
return [(sTitle, sData["level"], sData["title"]) for sTitle, sData in hRecord.items()]
|
||||
return [
|
||||
(sTitle, hItem.level, hItem.title)
|
||||
for sTitle, hItem in self._items.get(tHandle, {}).items()
|
||||
]
|
||||
|
||||
def getHandleHeaderLevel(self, tHandle):
|
||||
"""Get the header level of the first header of a handle.
|
||||
"""
|
||||
return self._fileMeta.get(tHandle, ["H0"])[0]
|
||||
if tHandle in self._items:
|
||||
return self._items[tHandle].level
|
||||
else:
|
||||
return "H0"
|
||||
|
||||
def getTableOfContents(self, maxDepth, skipExcluded=True):
|
||||
"""Generate a table of contents up to a maximum depth.
|
||||
@@ -595,21 +507,20 @@ class NWIndex():
|
||||
tData = {}
|
||||
pKey = None
|
||||
for tHandle in self._listNovelHandles(skipExcluded):
|
||||
for sTitle in sorted(self._fileIndex[tHandle]):
|
||||
for sTitle in self._items[tHandle].headings:
|
||||
tKey = f"{tHandle}:{sTitle}"
|
||||
theData = self._fileIndex[tHandle][sTitle]
|
||||
iLevel = H_LEVEL.get(theData["level"], 0)
|
||||
hItem = self._items[tHandle][sTitle]
|
||||
iLevel = H_LEVEL.get(hItem.level, 0)
|
||||
if iLevel > maxDepth:
|
||||
if pKey in tData:
|
||||
theData["wCount"]
|
||||
tData[pKey]["words"] += theData["wCount"]
|
||||
tData[pKey]["words"] += hItem.wordCount
|
||||
else:
|
||||
pKey = tKey
|
||||
tOrder.append(tKey)
|
||||
tData[tKey] = {
|
||||
"level": iLevel,
|
||||
"title": theData["title"],
|
||||
"words": theData["wCount"],
|
||||
"title": hItem.title,
|
||||
"words": hItem.wordCount,
|
||||
}
|
||||
|
||||
theToC = [(
|
||||
@@ -630,16 +541,18 @@ class NWIndex():
|
||||
pC = 0
|
||||
|
||||
if sTitle is None:
|
||||
if tHandle in self._fileMeta:
|
||||
cC = self._fileMeta[tHandle][1]
|
||||
wC = self._fileMeta[tHandle][2]
|
||||
pC = self._fileMeta[tHandle][3]
|
||||
if tHandle in self._items:
|
||||
tItem = self._items[tHandle].item
|
||||
cC = tItem.charCount
|
||||
wC = tItem.wordCount
|
||||
pC = tItem.paraCount
|
||||
else:
|
||||
if tHandle in self._fileIndex:
|
||||
if sTitle in self._fileIndex[tHandle]:
|
||||
cC = self._fileIndex[tHandle][sTitle]["cCount"]
|
||||
wC = self._fileIndex[tHandle][sTitle]["wCount"]
|
||||
pC = self._fileIndex[tHandle][sTitle]["pCount"]
|
||||
if tHandle in self._items:
|
||||
if sTitle in self._items[tHandle]:
|
||||
hItem = self._items[tHandle][sTitle]
|
||||
cC = hItem.charCount
|
||||
wC = hItem.wordCount
|
||||
pC = hItem.paraCount
|
||||
|
||||
return cC, wC, pC
|
||||
|
||||
@@ -648,40 +561,43 @@ class NWIndex():
|
||||
section.
|
||||
"""
|
||||
theRefs = {x: [] for x in nwKeyWords.KEY_CLASS}
|
||||
if tHandle not in self._refIndex:
|
||||
if tHandle not in self._items:
|
||||
return theRefs
|
||||
|
||||
for refTitle in self._refIndex[tHandle]:
|
||||
for aTag in self._refIndex[tHandle][refTitle]:
|
||||
if len(aTag) == 3 and (sTitle is None or sTitle == refTitle):
|
||||
if aTag[1] in theRefs:
|
||||
theRefs[aTag[1]].append(aTag[2])
|
||||
for rTitle, hItem in self._items[tHandle].items():
|
||||
if sTitle is None or sTitle == rTitle:
|
||||
for aTag, refTypes in hItem.references.items():
|
||||
for refType in refTypes:
|
||||
if refType in theRefs:
|
||||
theRefs[refType].append(aTag)
|
||||
|
||||
return theRefs
|
||||
|
||||
def getNovelData(self, tHandle, sTitle):
|
||||
"""Return the novel data of a given handle and title.
|
||||
"""
|
||||
if tHandle in self._fileIndex:
|
||||
if sTitle in self._fileIndex[tHandle]:
|
||||
return self._fileIndex[tHandle][sTitle]
|
||||
if tHandle in self._items:
|
||||
if sTitle in self._items[tHandle]:
|
||||
return self._items[tHandle][sTitle]
|
||||
return None
|
||||
|
||||
def getBackReferenceList(self, tHandle):
|
||||
"""Build a list of files referring back to our file, specified
|
||||
by tHandle.
|
||||
"""
|
||||
if tHandle is None:
|
||||
if tHandle is None or tHandle not in self._items:
|
||||
return {}
|
||||
|
||||
theRefs = {}
|
||||
theTags = set(filter(lambda x: self._tags[x].get("handle") == tHandle, self._tags))
|
||||
if theTags:
|
||||
for tHandle in self._refIndex:
|
||||
for sTitle in self._refIndex[tHandle]:
|
||||
for _, _, tTag in self._refIndex[tHandle][sTitle]:
|
||||
if tTag in theTags and tHandle not in theRefs:
|
||||
theRefs[tHandle] = sTitle
|
||||
theTags = self._items[tHandle].allTags()
|
||||
if not theTags:
|
||||
return theRefs
|
||||
|
||||
for aHandle, tItem in self._items.items():
|
||||
for sTitle, hItem in tItem.items():
|
||||
for aTag in hItem.references:
|
||||
if aTag in theTags and aHandle not in theRefs:
|
||||
theRefs[aHandle] = sTitle
|
||||
|
||||
return theRefs
|
||||
|
||||
@@ -706,7 +622,7 @@ class NWIndex():
|
||||
continue
|
||||
if tItem.itemLayout == nwItemLayout.NOTE:
|
||||
continue
|
||||
if tItem.itemHandle in self._fileIndex:
|
||||
if tItem.itemHandle in self._items:
|
||||
theHandles.append(tItem.itemHandle)
|
||||
|
||||
return theHandles
|
||||
@@ -724,25 +640,9 @@ class NWIndex():
|
||||
logger.debug("Checking index")
|
||||
tStart = time()
|
||||
|
||||
try:
|
||||
self._checkRefIndex()
|
||||
self._checkFileIndex()
|
||||
self._checkFileMeta()
|
||||
self._indexBroken = False
|
||||
|
||||
except Exception:
|
||||
logger.error("Error while checking index")
|
||||
logException()
|
||||
self._indexBroken = True
|
||||
|
||||
if self._indexBroken:
|
||||
self.clearIndex()
|
||||
logger.verbose("Index check completed in %.3f ms", (time() - tStart)*1000)
|
||||
return
|
||||
|
||||
# If the index was ok, we check that project files are indexed
|
||||
for fHandle in self.theProject.projFiles:
|
||||
if fHandle not in self._fileMeta:
|
||||
if fHandle not in self._items:
|
||||
logger.warning("Item '%s' is not in the index", fHandle)
|
||||
self.reIndexHandle(fHandle)
|
||||
|
||||
@@ -750,103 +650,6 @@ class NWIndex():
|
||||
|
||||
return
|
||||
|
||||
def _checkRefIndex(self):
|
||||
"""Scan the reference index for errors.
|
||||
Warning: This function raises exceptions.
|
||||
"""
|
||||
for tHandle in self._refIndex:
|
||||
if not isHandle(tHandle):
|
||||
raise KeyError("refIndex key is not a handle")
|
||||
|
||||
hEntry = self._refIndex[tHandle]
|
||||
for sTitle in hEntry:
|
||||
if not isTitleTag(sTitle):
|
||||
raise KeyError("refIndex[a] key is not a title tag")
|
||||
|
||||
sEntry = hEntry[sTitle]
|
||||
for tEntry in sEntry:
|
||||
if len(tEntry) != 3:
|
||||
raise IndexError("refIndex[a][b][i] expected 3 values")
|
||||
if not isinstance(tEntry[0], int):
|
||||
raise ValueError("refIndex[a][b][i][0] is not an integer")
|
||||
if not tEntry[1] in nwKeyWords.VALID_KEYS:
|
||||
raise ValueError("refIndex[a][b][i][1] is not a keyword")
|
||||
if not isinstance(tEntry[2], str):
|
||||
raise ValueError("refIndex[a][b][i][2] is not a string")
|
||||
|
||||
return
|
||||
|
||||
def _checkFileIndex(self):
|
||||
"""Scan the file index for errors.
|
||||
Warning: This function raises exceptions.
|
||||
"""
|
||||
for tHandle in self._fileIndex:
|
||||
if not isHandle(tHandle):
|
||||
raise KeyError("fileIndex key is not a handle")
|
||||
|
||||
hEntry = self._fileIndex[tHandle]
|
||||
for sTitle in self._fileIndex[tHandle]:
|
||||
if not isTitleTag(sTitle):
|
||||
raise KeyError("fileIndex[a] key is not a title tag")
|
||||
|
||||
sEntry = hEntry[sTitle]
|
||||
if len(sEntry) != 7:
|
||||
raise IndexError("fileIndex[a][b] expected 7 values")
|
||||
|
||||
if "level" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'level' key")
|
||||
if "title" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'title' key")
|
||||
if "layout" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'layout' key")
|
||||
if "cCount" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'cCount' key")
|
||||
if "wCount" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'wCount' key")
|
||||
if "pCount" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'pCount' key")
|
||||
if "synopsis" not in sEntry:
|
||||
raise KeyError("fileIndex[a][b] has no 'synopsis' key")
|
||||
|
||||
if not sEntry["level"] in H_VALID:
|
||||
raise ValueError("fileIndex[a][b][level] is not a header level")
|
||||
if not isinstance(sEntry["title"], str):
|
||||
raise ValueError("fileIndex[a][b][title] is not a string")
|
||||
if not isItemLayout(sEntry["layout"]):
|
||||
raise ValueError("fileIndex[a][b][layout] is not an nwItemLayout")
|
||||
if not isinstance(sEntry["cCount"], int):
|
||||
raise ValueError("fileIndex[a][b][cCount] is not an integer")
|
||||
if not isinstance(sEntry["wCount"], int):
|
||||
raise ValueError("fileIndex[a][b][wCount] is not an integer")
|
||||
if not isinstance(sEntry["pCount"], int):
|
||||
raise ValueError("fileIndex[a][b][pCount] is not an integer")
|
||||
if not isinstance(sEntry["synopsis"], str):
|
||||
raise ValueError("fileIndex[a][b][synopsis] is not a string")
|
||||
|
||||
return
|
||||
|
||||
def _checkFileMeta(self):
|
||||
"""Scan the text counts index for errors.
|
||||
Warning: This function raises exceptions.
|
||||
"""
|
||||
for tHandle in self._fileMeta:
|
||||
if not isHandle(tHandle):
|
||||
raise KeyError("fileMeta key is not a handle")
|
||||
|
||||
tEntry = self._fileMeta[tHandle]
|
||||
if len(tEntry) != 4:
|
||||
raise IndexError("fileMeta[a] expected 4 values")
|
||||
if not tEntry[0] in H_VALID:
|
||||
raise ValueError("fileMeta[a][0] is not a header level")
|
||||
if not isinstance(tEntry[1], int):
|
||||
raise ValueError("fileMeta[a][1] is not an integer")
|
||||
if not isinstance(tEntry[2], int):
|
||||
raise ValueError("fileMeta[a][2] is not an integer")
|
||||
if not isinstance(tEntry[3], int):
|
||||
raise ValueError("fileMeta[a][3] is not an integer")
|
||||
|
||||
return
|
||||
|
||||
# END Class NWIndex
|
||||
|
||||
|
||||
@@ -950,13 +753,21 @@ class IndexItem:
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
return self._item
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
@property
|
||||
def itemClass(self):
|
||||
return self._item.itemClass
|
||||
def headings(self):
|
||||
return sorted(self._headings.keys())
|
||||
|
||||
@property
|
||||
def entries(self):
|
||||
return self._headings.values()
|
||||
|
||||
##
|
||||
# Setters
|
||||
@@ -1012,9 +823,22 @@ class IndexItem:
|
||||
def __getitem__(self, sTitle):
|
||||
return self._headings.get(sTitle, None)
|
||||
|
||||
def __contains__(self, sTitle):
|
||||
return sTitle in self._headings
|
||||
|
||||
def items(self):
|
||||
return self._headings.items()
|
||||
|
||||
def allTags(self):
|
||||
"""Return a list of all tags in the current item.
|
||||
"""
|
||||
tags = []
|
||||
for hItem in self._headings.values():
|
||||
tag = hItem.tag
|
||||
if tag:
|
||||
tags.append(tag)
|
||||
return tags
|
||||
|
||||
##
|
||||
# Pack/Unpack
|
||||
##
|
||||
@@ -1077,10 +901,38 @@ class IndexHeading:
|
||||
def key(self):
|
||||
return self._key
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self._title
|
||||
|
||||
@property
|
||||
def charCount(self):
|
||||
return self._charCount
|
||||
|
||||
@property
|
||||
def wordCount(self):
|
||||
return self._wordCount
|
||||
|
||||
@property
|
||||
def paraCount(self):
|
||||
return self._paraCount
|
||||
|
||||
@property
|
||||
def synopsis(self):
|
||||
return self._synopsis
|
||||
|
||||
@property
|
||||
def tag(self):
|
||||
return self._tag
|
||||
|
||||
@property
|
||||
def references(self):
|
||||
return self._refs
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
@@ -258,7 +258,7 @@ class GuiNovelTree(QTreeWidget):
|
||||
tItem = self._createTreeItem(tHandle, sTitle, tKey, novIdx)
|
||||
self._treeMap[tKey] = tItem
|
||||
|
||||
tLevel = novIdx["level"]
|
||||
tLevel = novIdx.level
|
||||
if tLevel == "H1":
|
||||
self.addTopLevelItem(tItem)
|
||||
currTitle = tItem
|
||||
@@ -305,12 +305,12 @@ class GuiNovelTree(QTreeWidget):
|
||||
"""Populate a tree item with all the column values.
|
||||
"""
|
||||
newItem = QTreeWidgetItem()
|
||||
hIcon = "doc_%s" % novIdx["level"].lower()
|
||||
hIcon = "doc_%s" % novIdx.level.lower()
|
||||
theData = (tHandle, sTitle[1:].lstrip("0"), titleKey)
|
||||
|
||||
wC = int(novIdx["wCount"])
|
||||
wC = int(novIdx.wordCount)
|
||||
|
||||
newItem.setText(self.C_TITLE, novIdx["title"])
|
||||
newItem.setText(self.C_TITLE, novIdx.title)
|
||||
newItem.setData(self.C_TITLE, Qt.UserRole, theData)
|
||||
newItem.setIcon(self.C_TITLE, self.theTheme.getIcon(hIcon))
|
||||
newItem.setText(self.C_WORDS, f"{wC:n}")
|
||||
|
||||
@@ -393,7 +393,7 @@ class GuiOutline(QTreeWidget):
|
||||
|
||||
tItem = self._createTreeItem(tHandle, sTitle, novIdx)
|
||||
|
||||
tLevel = novIdx["level"]
|
||||
tLevel = novIdx.level
|
||||
if tLevel == "H1":
|
||||
self.addTopLevelItem(tItem)
|
||||
currTitle = tItem
|
||||
@@ -441,24 +441,24 @@ class GuiOutline(QTreeWidget):
|
||||
"""
|
||||
nwItem = self.theProject.tree[tHandle]
|
||||
newItem = QTreeWidgetItem()
|
||||
hIcon = "doc_%s" % novIdx["level"].lower()
|
||||
hIcon = "doc_%s" % novIdx.level.lower()
|
||||
|
||||
hLevel = self.theProject.index.getHandleHeaderLevel(tHandle)
|
||||
dIcon = self.theTheme.getItemIcon(nwItemType.FILE, None, nwItemLayout.DOCUMENT, hLevel)
|
||||
|
||||
cC = int(novIdx["cCount"])
|
||||
wC = int(novIdx["wCount"])
|
||||
pC = int(novIdx["pCount"])
|
||||
cC = int(novIdx.charCount)
|
||||
wC = int(novIdx.wordCount)
|
||||
pC = int(novIdx.paraCount)
|
||||
|
||||
newItem.setText(self._colIdx[nwOutline.TITLE], novIdx["title"])
|
||||
newItem.setText(self._colIdx[nwOutline.TITLE], novIdx.title)
|
||||
newItem.setData(self._colIdx[nwOutline.TITLE], Qt.UserRole, tHandle)
|
||||
newItem.setIcon(self._colIdx[nwOutline.TITLE], self.theTheme.getIcon(hIcon))
|
||||
newItem.setText(self._colIdx[nwOutline.LEVEL], novIdx["level"])
|
||||
newItem.setText(self._colIdx[nwOutline.LEVEL], novIdx.level)
|
||||
newItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
|
||||
newItem.setIcon(self._colIdx[nwOutline.LABEL], dIcon)
|
||||
newItem.setText(self._colIdx[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
||||
newItem.setData(self._colIdx[nwOutline.LINE], Qt.UserRole, sTitle)
|
||||
newItem.setText(self._colIdx[nwOutline.SYNOP], novIdx["synopsis"])
|
||||
newItem.setText(self._colIdx[nwOutline.SYNOP], novIdx.synopsis)
|
||||
newItem.setText(self._colIdx[nwOutline.CCOUNT], f"{cC:n}")
|
||||
newItem.setText(self._colIdx[nwOutline.WCOUNT], f"{wC:n}")
|
||||
newItem.setText(self._colIdx[nwOutline.PCOUNT], f"{pC:n}")
|
||||
|
||||
@@ -288,26 +288,26 @@ class GuiOutlineDetails(QScrollArea):
|
||||
if nwItem is None or novIdx is None:
|
||||
return False
|
||||
|
||||
if novIdx["level"] in self.LVL_MAP:
|
||||
self.titleLabel.setText("<b>%s</b>" % self.tr(self.LVL_MAP[novIdx["level"]]))
|
||||
if novIdx.level in self.LVL_MAP:
|
||||
self.titleLabel.setText("<b>%s</b>" % self.tr(self.LVL_MAP[novIdx.level]))
|
||||
else:
|
||||
self.titleLabel.setText("<b>%s</b>" % self.tr("Title"))
|
||||
self.titleValue.setText(novIdx["title"])
|
||||
self.titleValue.setText(novIdx.title)
|
||||
|
||||
itemStatus, _ = nwItem.getImportStatus()
|
||||
|
||||
self.fileValue.setText(nwItem.itemName)
|
||||
self.itemValue.setText(itemStatus)
|
||||
|
||||
cC = checkInt(novIdx["cCount"], 0)
|
||||
wC = checkInt(novIdx["wCount"], 0)
|
||||
pC = checkInt(novIdx["pCount"], 0)
|
||||
cC = checkInt(novIdx.charCount, 0)
|
||||
wC = checkInt(novIdx.wordCount, 0)
|
||||
pC = checkInt(novIdx.paraCount, 0)
|
||||
|
||||
self.cCValue.setText(f"{cC:n}")
|
||||
self.wCValue.setText(f"{wC:n}")
|
||||
self.pCValue.setText(f"{pC:n}")
|
||||
|
||||
self.synopValue.setText(novIdx["synopsis"])
|
||||
self.synopValue.setText(novIdx.synopsis)
|
||||
|
||||
self.povKeyValue.setText(self._formatTags(theRefs, nwKeyWords.POV_KEY))
|
||||
self.focKeyValue.setText(self._formatTags(theRefs, nwKeyWords.FOCUS_KEY))
|
||||
|
||||
@@ -1,99 +1,125 @@
|
||||
{
|
||||
"tagIndex": {
|
||||
"Bod": [3, "4c4f28287af27", "CHARACTER", "T000001"],
|
||||
"Main": [3, "2426c6f0ca922", "PLOT", "T000001"],
|
||||
"Europe": [3, "04468803b92e1", "WORLD", "T000001"]
|
||||
},
|
||||
"refIndex": {
|
||||
"fb609cd8319dc": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
"tagsIndex": {
|
||||
"Bod": {"handle": "4c4f28287af27", "heading": "T000001", "class": "CHARACTER"},
|
||||
"Main": {"handle": "2426c6f0ca922", "heading": "T000001", "class": "PLOT"},
|
||||
"Europe": {"handle": "04468803b92e1", "heading": "T000001", "class": "WORLD"}
|
||||
},
|
||||
"88243afbe5ed8": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
},
|
||||
"f96ec11c6a3da": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
},
|
||||
"441420a886d82": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
},
|
||||
"eb103bc70c90c": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
},
|
||||
"f8c0562e50f1b": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
},
|
||||
"47666c91c7ccf": {
|
||||
"T000001": [[3, "@pov", "Bod"], [4, "@plot", "Main"], [5, "@location", "Europe"]]
|
||||
},
|
||||
"4c4f28287af27": {
|
||||
"T000001": [[4, "@plot", "Main"]]
|
||||
"itemIndex": {
|
||||
"7a992350f3eb6": {
|
||||
"level": "H1",
|
||||
"headings": {
|
||||
"T000001": {"level": "H1", "title": "Lorem Ipsum", "tag": "", "cCount": 230, "wCount": 40, "pCount": 3, "synopsis": ""}
|
||||
}
|
||||
},
|
||||
"8c58a65414c23": {
|
||||
"level": "H0",
|
||||
"headings": {
|
||||
"T000000": {"level": "H0", "title": "", "tag": "", "cCount": 1058, "wCount": 176, "pCount": 2, "synopsis": ""}
|
||||
}
|
||||
},
|
||||
"88d59a277361b": {
|
||||
"level": "H2",
|
||||
"headings": {
|
||||
"T000001": {"level": "H2", "title": "Prologue", "tag": "", "cCount": 584, "wCount": 92, "pCount": 1, "synopsis": "Explanation from the lipsum.com website."}
|
||||
}
|
||||
},
|
||||
"db7e733775d4d": {
|
||||
"level": "H1",
|
||||
"headings": {
|
||||
"T000001": {"level": "H1", "title": "Act One", "tag": "", "cCount": 35, "wCount": 6, "pCount": 1, "synopsis": ""}
|
||||
}
|
||||
},
|
||||
"fb609cd8319dc": {
|
||||
"level": "H2",
|
||||
"headings": {
|
||||
"T000001": {"level": "H2", "title": "Chapter One", "tag": "", "cCount": 419, "wCount": 67, "pCount": 1, "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"88243afbe5ed8": {
|
||||
"level": "H0",
|
||||
"headings": {
|
||||
"T000001": {"level": "H3", "title": "Scene One", "tag": "", "cCount": 1197, "wCount": 174, "pCount": 2, "synopsis": "Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur."},
|
||||
"T000013": {"level": "H4", "title": "Scene One, Section Two", "tag": "", "cCount": 1561, "wCount": 230, "pCount": 2, "synopsis": ""}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"f96ec11c6a3da": {
|
||||
"level": "H0",
|
||||
"headings": {
|
||||
"T000001": {"level": "H3", "title": "Scene Two", "tag": "", "cCount": 2034, "wCount": 299, "pCount": 3, "synopsis": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci."},
|
||||
"T000015": {"level": "H4", "title": "Scene Two, Section Two", "tag": "", "cCount": 2009, "wCount": 301, "pCount": 3, "synopsis": ""}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"846352075de7d": {
|
||||
"level": "H2",
|
||||
"headings": {
|
||||
"T000001": {"level": "H2", "title": "Why do we use it?", "tag": "", "cCount": 631, "wCount": 109, "pCount": 3, "synopsis": ""}
|
||||
}
|
||||
},
|
||||
"441420a886d82": {
|
||||
"level": "H2",
|
||||
"headings": {
|
||||
"T000001": {"level": "H2", "title": "Chapter Two", "tag": "", "cCount": 477, "wCount": 70, "pCount": 1, "synopsis": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"eb103bc70c90c": {
|
||||
"level": "H3",
|
||||
"headings": {
|
||||
"T000001": {"level": "H3", "title": "Scene Three", "tag": "", "cCount": 3006, "wCount": 439, "pCount": 4, "synopsis": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"f8c0562e50f1b": {
|
||||
"level": "H3",
|
||||
"headings": {
|
||||
"T000001": {"level": "H3", "title": "Scene Four", "tag": "", "cCount": 3839, "wCount": 563, "pCount": 6, "synopsis": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"47666c91c7ccf": {
|
||||
"level": "H3",
|
||||
"headings": {
|
||||
"T000001": {"level": "H3", "title": "Scene Five", "tag": "", "cCount": 3644, "wCount": 543, "pCount": 5, "synopsis": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Bod": ["@pov"], "Main": ["@plot"], "Europe": ["@location"]}
|
||||
}
|
||||
},
|
||||
"4c4f28287af27": {
|
||||
"level": "H1",
|
||||
"headings": {
|
||||
"T000001": {"level": "H1", "title": "Nobody Owens", "tag": "Bod", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""}
|
||||
},
|
||||
"references": {
|
||||
"T000001": {"Main": ["@plot"]}
|
||||
}
|
||||
},
|
||||
"2426c6f0ca922": {
|
||||
"level": "H1",
|
||||
"headings": {
|
||||
"T000001": {"level": "H1", "title": "Main Plot", "tag": "Main", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""}
|
||||
}
|
||||
},
|
||||
"04468803b92e1": {
|
||||
"level": "H1",
|
||||
"headings": {
|
||||
"T000001": {"level": "H1", "title": "Ancient Europe", "tag": "Europe", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fileIndex": {
|
||||
"7a992350f3eb6": {
|
||||
"T000001": {"level": "H1", "title": "Lorem Ipsum", "layout": "DOCUMENT", "cCount": 230, "wCount": 40, "pCount": 3, "synopsis": ""}
|
||||
},
|
||||
"8c58a65414c23": {
|
||||
"T000000": {"level": "H0", "title": "", "layout": "DOCUMENT", "cCount": 1058, "wCount": 176, "pCount": 2, "synopsis": ""}
|
||||
},
|
||||
"88d59a277361b": {
|
||||
"T000001": {"level": "H2", "title": "Prologue", "layout": "DOCUMENT", "cCount": 584, "wCount": 92, "pCount": 1, "synopsis": "Explanation from the lipsum.com website."}
|
||||
},
|
||||
"db7e733775d4d": {
|
||||
"T000001": {"level": "H1", "title": "Act One", "layout": "DOCUMENT", "cCount": 35, "wCount": 6, "pCount": 1, "synopsis": ""}
|
||||
},
|
||||
"fb609cd8319dc": {
|
||||
"T000001": {"level": "H2", "title": "Chapter One", "layout": "DOCUMENT", "cCount": 419, "wCount": 67, "pCount": 1, "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."}
|
||||
},
|
||||
"88243afbe5ed8": {
|
||||
"T000001": {"level": "H3", "title": "Scene One", "layout": "DOCUMENT", "cCount": 1197, "wCount": 174, "pCount": 2, "synopsis": "Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur."},
|
||||
"T000013": {"level": "H4", "title": "Scene One, Section Two", "layout": "DOCUMENT", "cCount": 1561, "wCount": 230, "pCount": 2, "synopsis": ""}
|
||||
},
|
||||
"f96ec11c6a3da": {
|
||||
"T000001": {"level": "H3", "title": "Scene Two", "layout": "DOCUMENT", "cCount": 2034, "wCount": 299, "pCount": 3, "synopsis": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci."},
|
||||
"T000015": {"level": "H4", "title": "Scene Two, Section Two", "layout": "DOCUMENT", "cCount": 2009, "wCount": 301, "pCount": 3, "synopsis": ""}
|
||||
},
|
||||
"846352075de7d": {
|
||||
"T000001": {"level": "H2", "title": "Why do we use it?", "layout": "DOCUMENT", "cCount": 631, "wCount": 109, "pCount": 3, "synopsis": ""}
|
||||
},
|
||||
"441420a886d82": {
|
||||
"T000001": {"level": "H2", "title": "Chapter Two", "layout": "DOCUMENT", "cCount": 477, "wCount": 70, "pCount": 1, "synopsis": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."}
|
||||
},
|
||||
"eb103bc70c90c": {
|
||||
"T000001": {"level": "H3", "title": "Scene Three", "layout": "DOCUMENT", "cCount": 3006, "wCount": 439, "pCount": 4, "synopsis": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."}
|
||||
},
|
||||
"f8c0562e50f1b": {
|
||||
"T000001": {"level": "H3", "title": "Scene Four", "layout": "DOCUMENT", "cCount": 3839, "wCount": 563, "pCount": 6, "synopsis": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."}
|
||||
},
|
||||
"47666c91c7ccf": {
|
||||
"T000001": {"level": "H3", "title": "Scene Five", "layout": "DOCUMENT", "cCount": 3644, "wCount": 543, "pCount": 5, "synopsis": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."}
|
||||
},
|
||||
"4c4f28287af27": {
|
||||
"T000001": {"level": "H1", "title": "Nobody Owens", "layout": "NOTE", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""}
|
||||
},
|
||||
"2426c6f0ca922": {
|
||||
"T000001": {"level": "H1", "title": "Main Plot", "layout": "NOTE", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""}
|
||||
},
|
||||
"04468803b92e1": {
|
||||
"T000001": {"level": "H1", "title": "Ancient Europe", "layout": "NOTE", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""}
|
||||
}
|
||||
},
|
||||
"fileMeta": {
|
||||
"7a992350f3eb6": ["H1", 230, 40, 3],
|
||||
"8c58a65414c23": ["H0", 1058, 176, 2],
|
||||
"88d59a277361b": ["H2", 584, 92, 1],
|
||||
"db7e733775d4d": ["H1", 35, 6, 1],
|
||||
"fb609cd8319dc": ["H2", 419, 67, 1],
|
||||
"88243afbe5ed8": ["H3", 2758, 404, 4],
|
||||
"f96ec11c6a3da": ["H3", 4043, 600, 6],
|
||||
"846352075de7d": ["H2", 631, 109, 3],
|
||||
"441420a886d82": ["H2", 477, 70, 1],
|
||||
"eb103bc70c90c": ["H3", 3006, 439, 4],
|
||||
"f8c0562e50f1b": ["H3", 3839, 563, 6],
|
||||
"47666c91c7ccf": ["H3", 3644, 543, 5],
|
||||
"4c4f28287af27": ["H1", 1864, 284, 3],
|
||||
"2426c6f0ca922": ["H1", 1369, 195, 2],
|
||||
"04468803b92e1": ["H1", 1770, 259, 3]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,27 +69,19 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
|
||||
# Take a copy of the index
|
||||
tagIndex = str(theIndex._tags)
|
||||
refIndex = str(theIndex._refIndex)
|
||||
fileIndex = str(theIndex._fileIndex)
|
||||
textCounts = str(theIndex._fileMeta)
|
||||
itemsIndex = str({handle: item.packData() for handle, item in theIndex._items.items()})
|
||||
|
||||
# Delete a handle
|
||||
assert theIndex._tags.get("Bod", None) is not None
|
||||
assert theIndex._refIndex.get("4c4f28287af27", None) is not None
|
||||
assert theIndex._fileIndex.get("4c4f28287af27", None) is not None
|
||||
assert theIndex._fileMeta.get("4c4f28287af27", None) is not None
|
||||
assert theIndex._items.get("4c4f28287af27", None) is not None
|
||||
theIndex.deleteHandle("4c4f28287af27")
|
||||
assert theIndex._tags.get("Bod", None) is None
|
||||
assert theIndex._refIndex.get("4c4f28287af27", None) is None
|
||||
assert theIndex._fileIndex.get("4c4f28287af27", None) is None
|
||||
assert theIndex._fileMeta.get("4c4f28287af27", None) is None
|
||||
assert theIndex._items.get("4c4f28287af27", None) is None
|
||||
|
||||
# Clear the index
|
||||
theIndex.clearIndex()
|
||||
assert theIndex._tags == {}
|
||||
assert theIndex._refIndex == {}
|
||||
assert theIndex._fileIndex == {}
|
||||
assert theIndex._fileMeta == {}
|
||||
assert theIndex._items == {}
|
||||
|
||||
# Make the load fail
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -100,9 +92,9 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
assert theIndex.loadIndex() is True
|
||||
|
||||
assert str(theIndex._tags) == tagIndex
|
||||
assert str(theIndex._refIndex) == refIndex
|
||||
assert str(theIndex._fileIndex) == fileIndex
|
||||
assert str(theIndex._fileMeta) == textCounts
|
||||
assert str(
|
||||
{handle: item.packData() for handle, item in theIndex._items.items()}
|
||||
) == itemsIndex
|
||||
|
||||
# Break the index and check that we notice
|
||||
# assert theIndex.indexBroken is False
|
||||
@@ -201,7 +193,7 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI):
|
||||
assert theIndex._tags == {
|
||||
"Jane": {"handle": cHandle, "heading": "T000001", "class": "CHARACTER"}
|
||||
}
|
||||
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
|
||||
assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!"
|
||||
assert theIndex.getReferences(nHandle, "T000001") == {
|
||||
"@char": [],
|
||||
"@custom": [],
|
||||
@@ -314,7 +306,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
assert theIndex._tags == {
|
||||
"Jane": {"handle": cHandle, "heading": "T000001", "class": "CHARACTER"}
|
||||
}
|
||||
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
|
||||
assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!"
|
||||
|
||||
# Title Indexing
|
||||
# ==============
|
||||
@@ -336,42 +328,40 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
"##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word
|
||||
"Paragraph Five.\n\n"
|
||||
))
|
||||
assert nHandle not in theIndex._refIndex
|
||||
assert theIndex._items[nHandle]["T000001"].references == {}
|
||||
assert theIndex._items[nHandle]["T000007"].references == {}
|
||||
assert theIndex._items[nHandle]["T000013"].references == {}
|
||||
assert theIndex._items[nHandle]["T000019"].references == {}
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["level"] == "H1"
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["level"] == "H2"
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["level"] == "H3"
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["level"] == "H4"
|
||||
assert theIndex._items[nHandle]["T000001"].level == "H1"
|
||||
assert theIndex._items[nHandle]["T000007"].level == "H2"
|
||||
assert theIndex._items[nHandle]["T000013"].level == "H3"
|
||||
assert theIndex._items[nHandle]["T000019"].level == "H4"
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["title"] == "Title One"
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["title"] == "Title Two"
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["title"] == "Title Three"
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["title"] == "Title Four"
|
||||
assert theIndex._items[nHandle]["T000001"].title == "Title One"
|
||||
assert theIndex._items[nHandle]["T000007"].title == "Title Two"
|
||||
assert theIndex._items[nHandle]["T000013"].title == "Title Three"
|
||||
assert theIndex._items[nHandle]["T000019"].title == "Title Four"
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._items[nHandle]["T000001"].charCount == 23
|
||||
assert theIndex._items[nHandle]["T000007"].charCount == 23
|
||||
assert theIndex._items[nHandle]["T000013"].charCount == 27
|
||||
assert theIndex._items[nHandle]["T000019"].charCount == 56
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["cCount"] == 23
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["cCount"] == 23
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["cCount"] == 27
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["cCount"] == 56
|
||||
assert theIndex._items[nHandle]["T000001"].wordCount == 4
|
||||
assert theIndex._items[nHandle]["T000007"].wordCount == 4
|
||||
assert theIndex._items[nHandle]["T000013"].wordCount == 4
|
||||
assert theIndex._items[nHandle]["T000019"].wordCount == 9
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["wCount"] == 4
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["wCount"] == 4
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["wCount"] == 4
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["wCount"] == 9
|
||||
assert theIndex._items[nHandle]["T000001"].paraCount == 1
|
||||
assert theIndex._items[nHandle]["T000007"].paraCount == 1
|
||||
assert theIndex._items[nHandle]["T000013"].paraCount == 1
|
||||
assert theIndex._items[nHandle]["T000019"].paraCount == 3
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["pCount"] == 3
|
||||
|
||||
assert theIndex._fileIndex[nHandle]["T000001"]["synopsis"] == "Synopsis One."
|
||||
assert theIndex._fileIndex[nHandle]["T000007"]["synopsis"] == "Synopsis Two."
|
||||
assert theIndex._fileIndex[nHandle]["T000013"]["synopsis"] == "Synopsis Three."
|
||||
assert theIndex._fileIndex[nHandle]["T000019"]["synopsis"] == "Synopsis Four."
|
||||
assert theIndex._items[nHandle]["T000001"].synopsis == "Synopsis One."
|
||||
assert theIndex._items[nHandle]["T000007"].synopsis == "Synopsis Two."
|
||||
assert theIndex._items[nHandle]["T000013"].synopsis == "Synopsis Three."
|
||||
assert theIndex._items[nHandle]["T000019"].synopsis == "Synopsis Four."
|
||||
|
||||
# Note File
|
||||
assert theIndex.scanText(cHandle, (
|
||||
@@ -380,15 +370,13 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
"% synopsis: Synopsis One.\n\n"
|
||||
"Paragraph One.\n\n"
|
||||
))
|
||||
assert cHandle not in theIndex._refIndex
|
||||
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["level"] == "H1"
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["title"] == "Title One"
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["layout"] == "NOTE"
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["cCount"] == 23
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["wCount"] == 4
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[cHandle]["T000001"]["synopsis"] == "Synopsis One."
|
||||
assert theIndex._items[cHandle]["T000001"].references == {}
|
||||
assert theIndex._items[cHandle]["T000001"].level == "H1"
|
||||
assert theIndex._items[cHandle]["T000001"].title == "Title One"
|
||||
assert theIndex._items[cHandle]["T000001"].charCount == 23
|
||||
assert theIndex._items[cHandle]["T000001"].wordCount == 4
|
||||
assert theIndex._items[cHandle]["T000001"].paraCount == 1
|
||||
assert theIndex._items[cHandle]["T000001"].synopsis == "Synopsis One."
|
||||
|
||||
# Valid and Invalid References
|
||||
assert theIndex.scanText(sHandle, (
|
||||
@@ -399,9 +387,9 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
"% synopsis: Synopsis One.\n\n"
|
||||
"Paragraph One.\n\n"
|
||||
))
|
||||
assert theIndex._refIndex[sHandle]["T000001"] == (
|
||||
[[3, "@pov", "One"], [5, "@char", "Two"]]
|
||||
)
|
||||
assert theIndex._items[sHandle]["T000001"].references == {
|
||||
"One": {"@pov"}, "Two": {"@char"}
|
||||
}
|
||||
|
||||
# Special Titles
|
||||
# ==============
|
||||
@@ -410,29 +398,25 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
"#! My Project\n\n"
|
||||
">> By Jane Doe <<\n\n"
|
||||
))
|
||||
assert tHandle not in theIndex._refIndex
|
||||
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H1"
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "My Project"
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 21
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 5
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""
|
||||
assert theIndex._items[cHandle]["T000001"].references == {}
|
||||
assert theIndex._items[tHandle]["T000001"].level == "H1"
|
||||
assert theIndex._items[tHandle]["T000001"].title == "My Project"
|
||||
assert theIndex._items[tHandle]["T000001"].charCount == 21
|
||||
assert theIndex._items[tHandle]["T000001"].wordCount == 5
|
||||
assert theIndex._items[tHandle]["T000001"].paraCount == 1
|
||||
assert theIndex._items[tHandle]["T000001"].synopsis == ""
|
||||
|
||||
assert theIndex.scanText(tHandle, (
|
||||
"##! Prologue\n\n"
|
||||
"In the beginning there was time ...\n\n"
|
||||
))
|
||||
assert tHandle not in theIndex._refIndex
|
||||
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H2"
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "Prologue"
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 43
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 8
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""
|
||||
assert theIndex._items[cHandle]["T000001"].references == {}
|
||||
assert theIndex._items[tHandle]["T000001"].level == "H2"
|
||||
assert theIndex._items[tHandle]["T000001"].title == "Prologue"
|
||||
assert theIndex._items[tHandle]["T000001"].charCount == 43
|
||||
assert theIndex._items[tHandle]["T000001"].wordCount == 8
|
||||
assert theIndex._items[tHandle]["T000001"].paraCount == 1
|
||||
assert theIndex._items[tHandle]["T000001"].synopsis == ""
|
||||
|
||||
# Page wo/Title
|
||||
# =============
|
||||
@@ -441,27 +425,25 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
assert theIndex.scanText(pHandle, (
|
||||
"This is a page with some text on it.\n\n"
|
||||
))
|
||||
assert pHandle in theIndex._fileIndex
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0"
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["title"] == ""
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "DOCUMENT"
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["synopsis"] == ""
|
||||
assert theIndex._items[pHandle]["T000000"].references == {}
|
||||
assert theIndex._items[pHandle]["T000000"].level == "H0"
|
||||
assert theIndex._items[pHandle]["T000000"].title == ""
|
||||
assert theIndex._items[pHandle]["T000000"].charCount == 36
|
||||
assert theIndex._items[pHandle]["T000000"].wordCount == 9
|
||||
assert theIndex._items[pHandle]["T000000"].paraCount == 1
|
||||
assert theIndex._items[pHandle]["T000000"].synopsis == ""
|
||||
|
||||
theProject.tree[pHandle]._layout = nwItemLayout.NOTE
|
||||
assert theIndex.scanText(pHandle, (
|
||||
"This is a page with some text on it.\n\n"
|
||||
))
|
||||
assert pHandle in theIndex._fileIndex
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0"
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["title"] == ""
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "NOTE"
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1
|
||||
assert theIndex._fileIndex[pHandle]["T000000"]["synopsis"] == ""
|
||||
assert theIndex._items[pHandle]["T000000"].references == {}
|
||||
assert theIndex._items[pHandle]["T000000"].level == "H0"
|
||||
assert theIndex._items[pHandle]["T000000"].title == ""
|
||||
assert theIndex._items[pHandle]["T000000"].charCount == 36
|
||||
assert theIndex._items[pHandle]["T000000"].wordCount == 9
|
||||
assert theIndex._items[pHandle]["T000000"].paraCount == 1
|
||||
assert theIndex._items[pHandle]["T000000"].synopsis == ""
|
||||
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
@@ -700,538 +682,6 @@ def testCoreIndex_ExtractData(nwMinimal, mockGUI):
|
||||
# END Test testCoreIndex_ExtractData
|
||||
|
||||
|
||||
# @pytest.mark.core
|
||||
# def testCoreIndex_CheckTagIndex(mockGUI):
|
||||
# """Test the tag index checker.
|
||||
# """
|
||||
# theProject = NWProject(mockGUI)
|
||||
# theIndex = NWIndex(theProject)
|
||||
|
||||
# # Valid Index
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# "Jane": [3, "bb2c23b3c42cc", "CHARACTER", "T000001"],
|
||||
# }
|
||||
# assert theIndex._checkTagIndex() is None
|
||||
|
||||
# # Wrong Key Type
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# 123456: [3, "bb2c23b3c42cc", "CHARACTER", "T000001"],
|
||||
# }
|
||||
# with pytest.raises(KeyError):
|
||||
# theIndex._checkTagIndex()
|
||||
|
||||
# # Wrong Length
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# "Jane": [3, "bb2c23b3c42cc", "CHARACTER", "T000001", "Stuff"],
|
||||
# }
|
||||
# with pytest.raises(IndexError):
|
||||
# theIndex._checkTagIndex()
|
||||
|
||||
# # Wrong Type of Entry 0
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# "Jane": ["3", "bb2c23b3c42cc", "CHARACTER", "T000001"],
|
||||
# }
|
||||
# with pytest.raises(ValueError):
|
||||
# theIndex._checkTagIndex()
|
||||
|
||||
# # Wrong Type of Entry 1
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# "Jane": [3, 0xbb2c23b3c42cc, "CHARACTER", "T000001"],
|
||||
# }
|
||||
# with pytest.raises(ValueError):
|
||||
# theIndex._checkTagIndex()
|
||||
|
||||
# # Wrong Type of Entry 2
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# "Jane": [3, "bb2c23b3c42cc", "INVALID_CLASS", "T000001"],
|
||||
# }
|
||||
# with pytest.raises(ValueError):
|
||||
# theIndex._checkTagIndex()
|
||||
|
||||
# # Wrong Type of Entry 3
|
||||
# theIndex._tagIndex = {
|
||||
# "John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||
# "Jane": [3, "bb2c23b3c42cc", "CHARACTER", "INVALID"],
|
||||
# }
|
||||
# with pytest.raises(ValueError):
|
||||
# theIndex._checkTagIndex()
|
||||
|
||||
# # END Test testCoreIndex_CheckTagIndex
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckRefIndex(mockGUI):
|
||||
"""Test the reference index checker.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
theIndex._refIndex = {
|
||||
"6a2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"T000001": [[3, "@pov", "Jane"], [4, "@location", "Earth"]],
|
||||
}
|
||||
}
|
||||
assert theIndex._checkRefIndex() is None
|
||||
|
||||
# Invalid Handle
|
||||
theIndex._refIndex = {
|
||||
"Ha2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"T000001": [[3, "@pov", "Jane"], [4, "@location", "Earth"]],
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkRefIndex()
|
||||
|
||||
# Invalid Title
|
||||
theIndex._refIndex = {
|
||||
"6a2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"INVALID": [[3, "@pov", "Jane"], [4, "@location", "Earth"]],
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkRefIndex()
|
||||
|
||||
# Wrong Length
|
||||
theIndex._refIndex = {
|
||||
"6a2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"T000001": [[3, "@pov", "Jane"], [4, "@location", "Earth", "Stuff"]],
|
||||
}
|
||||
}
|
||||
with pytest.raises(IndexError):
|
||||
theIndex._checkRefIndex()
|
||||
|
||||
# Wrong Type of Entry 0
|
||||
theIndex._refIndex = {
|
||||
"6a2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"T000001": [[3, "@pov", "Jane"], ["4", "@location", "Earth"]],
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkRefIndex()
|
||||
|
||||
# Wrong Type of Entry 1
|
||||
theIndex._refIndex = {
|
||||
"6a2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"T000001": [[3, "@pov", "Jane"], [4, "@stuff", "Earth"]],
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkRefIndex()
|
||||
|
||||
# Wrong Type of Entry 2
|
||||
theIndex._refIndex = {
|
||||
"6a2d6d5f4f401": {
|
||||
"T000000": [],
|
||||
"T000001": [[3, "@pov", "Jane"], [4, "@location", 123456]],
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkRefIndex()
|
||||
|
||||
# END Test testCoreIndex_CheckRefIndex
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckFileIndex(mockGUI):
|
||||
"""Test the file index checker.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
theIndex._fileIndex = theIndex._fileIndex.copy()
|
||||
assert theIndex._checkFileIndex() is None
|
||||
|
||||
# Invalid Handle
|
||||
theIndex._fileIndex = {
|
||||
"H3b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Invalid Title
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"INVALID": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Length
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
"stuff": None
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(IndexError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing Keys
|
||||
# ============
|
||||
|
||||
# Missing 'level'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"stuff": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing 'title'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"stuff": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing 'layout'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"stuff": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing 'cCount'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"stuff": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing 'wCount'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"stuff": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing 'pCount'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"stuff": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Missing 'synopsis'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"stuff": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Types
|
||||
# ===========
|
||||
|
||||
# Wrong Type for 'level'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "XX",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Type for 'title'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": 12345678,
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Type for 'layout'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "INVALID",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Type for 'cCount'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": "72",
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Type for 'wCount'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": "15",
|
||||
"pCount": 2,
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Type for 'pCount'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": "2",
|
||||
"synopsis": "text",
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# Wrong Type for 'synopsis'
|
||||
theIndex._fileIndex = {
|
||||
"53b69b83cdafc": {
|
||||
"T000001": {
|
||||
"level": "H1",
|
||||
"title": "My Novel",
|
||||
"layout": "DOCUMENT",
|
||||
"cCount": 72,
|
||||
"wCount": 15,
|
||||
"pCount": 2,
|
||||
"synopsis": 123456,
|
||||
}
|
||||
}
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileIndex()
|
||||
|
||||
# END Test testCoreIndex_CheckFileIndex
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckFileMeta(mockGUI):
|
||||
"""Test the file meta checker.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"974e400180a99": ["H0", 210, 40, 2],
|
||||
}
|
||||
assert theIndex._checkFileMeta() is None
|
||||
|
||||
# Invalid Handle
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"h74e400180a99": ["H0", 210, 40, 2],
|
||||
}
|
||||
with pytest.raises(KeyError):
|
||||
theIndex._checkFileMeta()
|
||||
|
||||
# Wrong Length
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"974e400180a99": ["H0", 210, 40, 2, 8],
|
||||
}
|
||||
with pytest.raises(IndexError):
|
||||
theIndex._checkFileMeta()
|
||||
|
||||
# Content of Entry 0
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"974e400180a99": ["XXX", 210, 40, 2],
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileMeta()
|
||||
|
||||
# Type of Entry 1
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"974e400180a99": ["H0", "210", 40, 2],
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileMeta()
|
||||
|
||||
# Type of Entry 2
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"974e400180a99": ["H0", 210, "40", 2],
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileMeta()
|
||||
|
||||
# Type of Entry 3
|
||||
theIndex._fileMeta = {
|
||||
"53b69b83cdafc": ["H0", 72, 15, 2],
|
||||
"974e400180a99": ["H0", 210, 40, "2"],
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
theIndex._checkFileMeta()
|
||||
|
||||
# END Test testCoreIndex_CheckFileMeta
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CountWords():
|
||||
"""Test the word counter and the exclusion filers.
|
||||
|
||||
@@ -47,8 +47,8 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
|
||||
# Rebuild the index
|
||||
nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger)
|
||||
assert nwGUI.theProject.index._tagIndex != {}
|
||||
assert nwGUI.theProject.index._refIndex != {}
|
||||
assert nwGUI.theProject.index._tags != {}
|
||||
assert nwGUI.theProject.index._items != {}
|
||||
|
||||
# Select a document in the project tree
|
||||
nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||
|
||||
Reference in New Issue
Block a user