Index Updates (#840)

* Improve the index keyword checker function
* Implement a more compact indented JSON encoder
* Remove timestamp from reference, novel and note indices
* Remove empty entries in reference index
* Merge novel and note indices into a single file index
This commit is contained in:
Veronica Berglyd Olsen
2021-08-01 18:19:03 +02:00
committed by GitHub
parent e5c715695e
commit c6973043e6
17 changed files with 766 additions and 1143 deletions
+58 -3
View File
@@ -23,6 +23,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import json
import logging
from datetime import datetime
@@ -224,16 +225,19 @@ def parseTimeStamp(theStamp, default, allowNone=False):
# String Functions
# =============================================================================================== #
def splitVersionNumber(vString):
""" Splits a version string on the form aa.bb.cc into major, minor
def splitVersionNumber(value):
"""Splits a version string on the form aa.bb.cc into major, minor
and patch, and computes an integer value aabbcc.
"""
if not isinstance(value, str):
return [0, 0, 0, 0]
vMajor = 0
vMinor = 0
vPatch = 0
vInt = 0
vBits = vString.split(".")
vBits = value.split(".")
nBits = len(vBits)
if nBits > 0:
@@ -355,6 +359,57 @@ def numberToRoman(numVal, isLower=False):
return romNum.lower() if isLower else romNum
# =============================================================================================== #
# Encoder Functions
# =============================================================================================== #
def jsonEncode(data, n=0, nmax=0):
"""Encode a dictionary, list or tuple as a json object or array, and
indent from level n up to a max level nmax if nmax is larger than 0.
"""
if not isinstance(data, (dict, list, tuple)):
return "[]"
buffer = []
indent = ""
for chunk in json.JSONEncoder().iterencode(data):
if chunk == "": # pragma: no cover
# Just a precaution
continue
first = chunk[0]
if chunk in ("{}", "[]"):
buffer.append(chunk)
elif first in ("{", "["):
n += 1
indent = "\n"+" "*n
if n > nmax and nmax > 0:
buffer.append(chunk)
else:
buffer.append(chunk[0] + indent + chunk[1:])
elif first in ("}", "]"):
n -= 1
indent = "\n"+" "*n
if n >= nmax and nmax > 0:
buffer.append(chunk)
else:
buffer.append(indent + chunk)
elif first == ",":
if n > nmax and nmax > 0:
buffer.append(chunk)
else:
buffer.append(chunk[0] + indent + chunk[1:].lstrip())
else:
buffer.append(chunk)
return "".join(buffer)
# =============================================================================================== #
# Other Functions
# =============================================================================================== #
+127 -197
View File
@@ -32,18 +32,20 @@ import os
from time import time
from nw.enum import nwItemType, nwItemClass, nwItemLayout
from nw.common import isHandle, isTitleTag, isItemClass, isItemLayout
from nw.constants import nwFiles, nwKeyWords, nwUnicode
from nw.core.document import NWDoc
from nw.common import (
isHandle, isTitleTag, isItemClass, isItemLayout, jsonEncode
)
logger = logging.getLogger(__name__)
H_VALID = ("H0", "H1", "H2", "H3", "H4")
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
class NWIndex():
H_VALID = ("H0", "H1", "H2", "H3", "H4")
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
def __init__(self, theProject):
# Internal
@@ -54,8 +56,7 @@ class NWIndex():
# Indices
self._tagIndex = {}
self._refIndex = {}
self._novelIndex = {}
self._noteIndex = {}
self._fileIndex = {}
self._textCounts = {}
# TimeStamps
@@ -74,8 +75,7 @@ class NWIndex():
"""
self._tagIndex = {}
self._refIndex = {}
self._novelIndex = {}
self._noteIndex = {}
self._fileIndex = {}
self._textCounts = {}
self._timeNovel = 0
self._timeNotes = 0
@@ -96,8 +96,7 @@ class NWIndex():
self._tagIndex.pop(tTag, None)
self._refIndex.pop(tHandle, None)
self._novelIndex.pop(tHandle, None)
self._noteIndex.pop(tHandle, None)
self._fileIndex.pop(tHandle, None)
self._textCounts.pop(tHandle, None)
return
@@ -146,12 +145,14 @@ class NWIndex():
"""
theData = {}
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")
nw.logException()
@@ -160,8 +161,7 @@ class NWIndex():
self._tagIndex = theData.get("tagIndex", {})
self._refIndex = theData.get("refIndex", {})
self._novelIndex = theData.get("novelIndex", {})
self._noteIndex = theData.get("noteIndex", {})
self._fileIndex = theData.get("fileIndex", {})
self._textCounts = theData.get("textCounts", {})
nowTime = round(time())
@@ -169,6 +169,8 @@ class NWIndex():
self._timeNotes = nowTime
self._timeIndex = nowTime
logger.verbose("Index loaded in %.3f ms", (time() - tStart)*1000)
self.checkIndex()
return True
@@ -179,21 +181,24 @@ class NWIndex():
"""
logger.debug("Saving index file")
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
tStart = time()
try:
with open(indexFile, mode="w+", encoding="utf-8") as outFile:
json.dump({
"tagIndex": self._tagIndex,
"refIndex": self._refIndex,
"novelIndex": self._novelIndex,
"noteIndex": self._noteIndex,
"textCounts": self._textCounts,
}, outFile, indent=2)
outFile.write("{\n")
outFile.write(f'"tagIndex": {jsonEncode(self._tagIndex, nmax=1)},\n')
outFile.write(f'"refIndex": {jsonEncode(self._refIndex, nmax=2)},\n')
outFile.write(f'"fileIndex": {jsonEncode(self._fileIndex, nmax=2)},\n')
outFile.write(f'"textCounts": {jsonEncode(self._textCounts, nmax=1)}\n')
outFile.write("}\n")
except Exception:
logger.error("Failed to save index file")
nw.logException()
return False
logger.verbose("Index saved in %.3f ms", (time() - tStart)*1000)
return True
def checkIndex(self):
@@ -206,8 +211,7 @@ class NWIndex():
try:
self._checkTagIndex()
self._checkRefIndex()
self._checkNovelNoteIndex("novelIndex")
self._checkNovelNoteIndex("noteIndex")
self._checkFileIndex()
self._checkTextCounts()
self.indexBroken = False
@@ -216,8 +220,7 @@ class NWIndex():
nw.logException()
self.indexBroken = True
tEnd = time()
logger.debug("Index check took %.3f ms", (tEnd - tStart)*1000)
logger.verbose("Index check took %.3f ms", (time() - tStart)*1000)
logger.debug("Index check complete")
if self.indexBroken:
@@ -268,21 +271,9 @@ class NWIndex():
logger.debug("Indexing item with handle '%s'", tHandle)
# Check file type, and reset its old index
# Also add a default entry T000000 in case the file has no title
self._refIndex[tHandle] = {}
self._refIndex[tHandle]["T000000"] = {
"tags": [],
"updated": round(time()),
}
if itemLayout == nwItemLayout.NOTE:
self._novelIndex.pop(tHandle, None)
self._noteIndex[tHandle] = {}
isNovel = False
else:
self._novelIndex[tHandle] = {}
self._noteIndex.pop(tHandle, None)
isNovel = True
# Delete or reset old entries for the file
self._refIndex.pop(tHandle, None)
self._fileIndex[tHandle] = {}
# Also clear references to file in tag index
clearTags = []
@@ -292,6 +283,7 @@ class NWIndex():
for aTag in clearTags:
self._tagIndex.pop(aTag)
# Scan the text content
nLine = 0
nTitle = 0
theLines = theText.splitlines()
@@ -302,11 +294,11 @@ class NWIndex():
continue
if aLine.startswith("#"):
isTitle = self._indexTitle(tHandle, isNovel, aLine, nLine, itemLayout)
isTitle = self._indexTitle(tHandle, aLine, nLine, itemLayout)
if isTitle and nLine > 0:
if nTitle > 0:
lastText = "\n".join(theLines[nTitle-1:nLine-1])
self._indexWordCounts(tHandle, isNovel, lastText, nTitle)
self._indexWordCounts(tHandle, lastText, nTitle)
nTitle = nLine
elif aLine.startswith("@"):
@@ -320,25 +312,25 @@ class NWIndex():
cLen = len(toCheck)
cOff = tLen - cLen
if synTag == "synopsis:":
self._indexSynopsis(tHandle, isNovel, aLine[cOff+9:].strip(), nTitle)
self._indexSynopsis(tHandle, aLine[cOff+9:].strip(), nTitle)
# Count words for remaining text after last heading
if nTitle > 0:
lastText = "\n".join(theLines[nTitle-1:])
self._indexWordCounts(tHandle, isNovel, lastText, nTitle)
self._indexWordCounts(tHandle, lastText, nTitle)
# Index page with no titles and references
if nTitle == 0:
self._indexPage(tHandle, isNovel, itemLayout)
self._indexWordCounts(tHandle, isNovel, theText, nTitle)
self._indexPage(tHandle, itemLayout)
self._indexWordCounts(tHandle, theText, nTitle)
# Update timestamps for index changes
nowTime = round(time())
self._timeIndex = nowTime
if isNovel:
self._timeNovel = nowTime
else:
if itemLayout == nwItemLayout.NOTE:
self._timeNotes = nowTime
else:
self._timeNovel = nowTime
return True
@@ -346,7 +338,7 @@ class NWIndex():
# Internal Indexers
##
def _indexTitle(self, tHandle, isNovel, aLine, nLine, itemLayout):
def _indexTitle(self, tHandle, aLine, nLine, itemLayout):
"""Save information about the title and its location in the
file to the index.
"""
@@ -366,89 +358,52 @@ class NWIndex():
return False
sTitle = "T%06d" % nLine
self._refIndex[tHandle][sTitle] = {
"tags": [],
"updated": round(time()),
}
theData = {
self._fileIndex[tHandle][sTitle] = {
"level": hDepth,
"title": hText,
"layout": itemLayout.name,
"synopsis": "",
"cCount": 0,
"wCount": 0,
"pCount": 0,
"updated": round(time()),
"synopsis": "",
}
if hText != "":
if isNovel:
if tHandle in self._novelIndex:
self._novelIndex[tHandle][sTitle] = theData
else:
if tHandle in self._noteIndex:
self._noteIndex[tHandle][sTitle] = theData
return True
def _indexPage(self, tHandle, isNovel, itemLayout):
def _indexPage(self, tHandle, itemLayout):
"""Index a page with no title.
"""
theData = {
self._fileIndex[tHandle]["T000000"] = {
"level": "H0",
"title": "Untitled Page",
"title": "",
"layout": itemLayout.name,
"synopsis": "",
"cCount": 0,
"wCount": 0,
"pCount": 0,
"updated": round(time()),
"synopsis": "",
}
if isNovel:
if tHandle in self._novelIndex:
self._novelIndex[tHandle]["T000000"] = theData
else:
if tHandle in self._noteIndex:
self._noteIndex[tHandle]["T000000"] = theData
return
def _indexWordCounts(self, tHandle, isNovel, theText, nTitle):
def _indexWordCounts(self, tHandle, theText, nTitle):
"""Count text stats and save the counts to the index.
"""
cC, wC, pC = countWords(theText)
sTitle = "T%06d" % nTitle
if isNovel:
if tHandle in self._novelIndex:
if sTitle in self._novelIndex[tHandle]:
self._novelIndex[tHandle][sTitle]["cCount"] = cC
self._novelIndex[tHandle][sTitle]["wCount"] = wC
self._novelIndex[tHandle][sTitle]["pCount"] = pC
self._novelIndex[tHandle][sTitle]["updated"] = round(time())
else:
if tHandle in self._noteIndex:
if sTitle in self._noteIndex[tHandle]:
self._noteIndex[tHandle][sTitle]["cCount"] = cC
self._noteIndex[tHandle][sTitle]["wCount"] = wC
self._noteIndex[tHandle][sTitle]["pCount"] = pC
self._noteIndex[tHandle][sTitle]["updated"] = round(time())
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
return
def _indexSynopsis(self, tHandle, isNovel, theText, nTitle):
def _indexSynopsis(self, tHandle, theText, nTitle):
"""Save the synopsis to the index.
"""
sTitle = "T%06d" % nTitle
if isNovel:
if tHandle in self._novelIndex:
if sTitle in self._novelIndex[tHandle]:
self._novelIndex[tHandle][sTitle]["synopsis"] = theText
self._novelIndex[tHandle][sTitle]["updated"] = round(time())
else:
if tHandle in self._noteIndex:
if sTitle in self._noteIndex[tHandle]:
self._noteIndex[tHandle][sTitle]["synopsis"] = theText
self._noteIndex[tHandle][sTitle]["updated"] = round(time())
if tHandle in self._fileIndex:
if sTitle in self._fileIndex[tHandle]:
self._fileIndex[tHandle][sTitle]["synopsis"] = theText
return
def _indexKeyword(self, tHandle, aLine, nLine, nTitle, itemClass):
@@ -468,9 +423,13 @@ class NWIndex():
if theBits[0] == nwKeyWords.TAG_KEY:
self._tagIndex[theBits[1]] = [nLine, tHandle, itemClass.name, sTitle]
elif sTitle in self._refIndex[tHandle]:
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]["tags"].append([nLine, theBits[0], aVal])
self._refIndex[tHandle][sTitle].append([nLine, theBits[0], aVal])
return
@@ -530,23 +489,19 @@ class NWIndex():
if not isGood[0] or nBits == 1:
return isGood
# If we have a tag, only the first value is accepted, the rest
# is ignored
# For a tag, only the first value is accepted, the rest are ignored
if theBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
isGood[0] = True
if theBits[1] in self._tagIndex:
if self._tagIndex[theBits[1]][1] == tItem.itemHandle:
isGood[1] = True
else:
isGood[1] = False
isGood[1] = self._tagIndex[theBits[1]][1] == tItem.itemHandle
else:
isGood[1] = True
return isGood
# If we're still here, we better check that the references exist
# If we're still here, we check that the references exist
theKey = nwKeyWords.KEY_CLASS[theBits[0]].name
for n in range(1, nBits):
if theBits[n] in self._tagIndex:
isGood[n] = nwKeyWords.KEY_CLASS[theBits[0]].name == self._tagIndex[theBits[n]][2]
isGood[n] = theKey == self._tagIndex[theBits[n]][2]
return isGood
@@ -560,17 +515,17 @@ class NWIndex():
files, but skipping all note files.
"""
for tHandle in self._listNovelHandles(skipExcluded):
for sTitle in sorted(self._novelIndex[tHandle]):
for sTitle in sorted(self._fileIndex[tHandle]):
tKey = "%s:%s" % (tHandle, sTitle)
yield tKey, tHandle, sTitle, self._novelIndex[tHandle][sTitle]
yield tKey, tHandle, sTitle, self._fileIndex[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._novelIndex[tHandle]:
wCount += self._novelIndex[tHandle][sTitle]["wCount"]
for sTitle in self._fileIndex[tHandle]:
wCount += self._fileIndex[tHandle][sTitle]["wCount"]
return wCount
@@ -579,9 +534,9 @@ class NWIndex():
"""
hCount = [0, 0, 0, 0, 0]
for tHandle in self._listNovelHandles(skipExcluded):
for sTitle in self._novelIndex[tHandle]:
theData = self._novelIndex[tHandle][sTitle]
iLevel = self.H_LEVEL.get(theData["level"], 0)
for sTitle in self._fileIndex[tHandle]:
theData = self._fileIndex[tHandle][sTitle]
iLevel = H_LEVEL.get(theData["level"], 0)
hCount[iLevel] += 1
return hCount
@@ -590,9 +545,7 @@ class NWIndex():
"""Get all header word counts for a specific handle.
"""
theCounts = []
hRecord = self._novelIndex.get(tHandle, None)
if hRecord is None:
hRecord = self._noteIndex.get(tHandle, None)
hRecord = self._fileIndex.get(tHandle, None)
if hRecord is None:
return theCounts
@@ -605,9 +558,7 @@ class NWIndex():
"""Get all headers for a specific handle.
"""
theHeaders = []
hRecord = self._novelIndex.get(tHandle, None)
if hRecord is None:
hRecord = self._noteIndex.get(tHandle, None)
hRecord = self._fileIndex.get(tHandle, None)
if hRecord is None:
return theHeaders
@@ -623,10 +574,10 @@ class NWIndex():
tData = {}
pKey = None
for tHandle in self._listNovelHandles(skipExcluded):
for sTitle in sorted(self._novelIndex[tHandle]):
for sTitle in sorted(self._fileIndex[tHandle]):
tKey = "%s:%s" % (tHandle, sTitle)
theData = self._novelIndex[tHandle][sTitle]
iLevel = self.H_LEVEL.get(theData["level"], 0)
theData = self._fileIndex[tHandle][sTitle]
iLevel = H_LEVEL.get(theData["level"], 0)
if iLevel > maxDepth:
if pKey in tData:
theData["wCount"]
@@ -665,16 +616,11 @@ class NWIndex():
wC = self._textCounts[tHandle][1]
pC = self._textCounts[tHandle][2]
else:
if tHandle in self._novelIndex:
if sTitle in self._novelIndex[tHandle]:
cC = self._novelIndex[tHandle][sTitle]["cCount"]
wC = self._novelIndex[tHandle][sTitle]["wCount"]
pC = self._novelIndex[tHandle][sTitle]["pCount"]
elif tHandle in self._noteIndex:
if sTitle in self._noteIndex[tHandle]:
cC = self._noteIndex[tHandle][sTitle]["cCount"]
wC = self._noteIndex[tHandle][sTitle]["wCount"]
pC = self._noteIndex[tHandle][sTitle]["pCount"]
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"]
return cC, wC, pC
@@ -690,7 +636,7 @@ class NWIndex():
return theRefs
for refTitle in self._refIndex[tHandle]:
for aTag in self._refIndex[tHandle][refTitle].get("tags", []):
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])
@@ -700,9 +646,9 @@ class NWIndex():
def getNovelData(self, tHandle, sTitle):
"""Return the novel data of a given handle and title.
"""
if tHandle in self._novelIndex:
if sTitle in self._novelIndex[tHandle]:
return self._novelIndex[tHandle][sTitle]
if tHandle in self._fileIndex:
if sTitle in self._fileIndex[tHandle]:
return self._fileIndex[tHandle][sTitle]
return None
def getBackReferenceList(self, tHandle):
@@ -721,7 +667,7 @@ class NWIndex():
if theTags:
for tHandle in self._refIndex:
for sTitle in self._refIndex[tHandle]:
for _, _, tTag in self._refIndex[tHandle][sTitle]["tags"]:
for _, _, tTag in self._refIndex[tHandle][sTitle]:
if tTag in theTags and tHandle not in theRefs:
theRefs[tHandle] = sTitle
@@ -749,7 +695,9 @@ class NWIndex():
continue
if not tItem.isExported and skipExcluded:
continue
if tItem.itemHandle in self._novelIndex:
if tItem.itemLayout == nwItemLayout.NOTE:
continue
if tItem.itemHandle in self._fileIndex:
theHandles.append(tItem.itemHandle)
return theHandles
@@ -760,7 +708,7 @@ class NWIndex():
def _checkTagIndex(self):
"""Scan the tag index for errors.
Waring: This function raises exceptions.
Warning: This function raises exceptions.
"""
for tTag in self._tagIndex:
if not isinstance(tTag, str):
@@ -782,7 +730,7 @@ class NWIndex():
def _checkRefIndex(self):
"""Scan the reference index for errors.
Waring: This function raises exceptions.
Warning: This function raises exceptions.
"""
for tHandle in self._refIndex:
if not isHandle(tHandle):
@@ -794,88 +742,70 @@ class NWIndex():
raise KeyError("refIndex[a] key is not a title tag")
sEntry = hEntry[sTitle]
if "tags" not in sEntry:
raise KeyError("refIndex[a][b] has no 'tag' key")
for tEntry in sEntry["tags"]:
for tEntry in sEntry:
if len(tEntry) != 3:
raise IndexError("refIndex[a][b][tags][i] expected 3 values")
raise IndexError("refIndex[a][b][i] expected 3 values")
if not isinstance(tEntry[0], int):
raise ValueError("refIndex[a][b][tags][i][0] is not an integer")
raise ValueError("refIndex[a][b][i][0] is not an integer")
if not tEntry[1] in nwKeyWords.VALID_KEYS:
raise ValueError("refIndex[a][b][tags][i][1] is not a keyword")
raise ValueError("refIndex[a][b][i][1] is not a keyword")
if not isinstance(tEntry[2], str):
raise ValueError("refIndex[a][b][tags][i][2] is not a string")
if "updated" not in sEntry:
raise KeyError("refIndex[a][b] has no 'updated' key")
if not isinstance(sEntry["updated"], int):
raise ValueError("%refIndex[a][b][updated] is not an integer")
raise ValueError("refIndex[a][b][i][2] is not a string")
return
def _checkNovelNoteIndex(self, idxName):
"""Scan the novel or note index for errors.
Waring: This function raises exceptions.
def _checkFileIndex(self):
"""Scan the file index for errors.
Warning: This function raises exceptions.
"""
if idxName == "novelIndex":
theIndex = self._novelIndex
elif idxName == "noteIndex":
theIndex = self._noteIndex
else:
raise IndexError("Unknown index %s" % idxName)
for tHandle in theIndex:
for tHandle in self._fileIndex:
if not isHandle(tHandle):
raise KeyError("%s key is not a handle" % idxName)
raise KeyError("fileIndex key is not a handle")
hEntry = theIndex[tHandle]
for sTitle in theIndex[tHandle]:
hEntry = self._fileIndex[tHandle]
for sTitle in self._fileIndex[tHandle]:
if not isTitleTag(sTitle):
raise KeyError("%s[a] key is not a title tag" % idxName)
raise KeyError("fileIndex[a] key is not a title tag")
sEntry = hEntry[sTitle]
if len(sEntry) != 8:
raise IndexError("%s[a][b] expected 8 values" % idxName)
if len(sEntry) != 7:
raise IndexError("fileIndex[a][b] expected 7 values")
if "level" not in sEntry:
raise KeyError("%s[a][b] has no 'level' key" % idxName)
raise KeyError("fileIndex[a][b] has no 'level' key")
if "title" not in sEntry:
raise KeyError("%s[a][b] has no 'title' key" % idxName)
raise KeyError("fileIndex[a][b] has no 'title' key")
if "layout" not in sEntry:
raise KeyError("%s[a][b] has no 'layout' key" % idxName)
if "synopsis" not in sEntry:
raise KeyError("%s[a][b] has no 'synopsis' key" % idxName)
raise KeyError("fileIndex[a][b] has no 'layout' key")
if "cCount" not in sEntry:
raise KeyError("%s[a][b] has no 'cCount' key" % idxName)
raise KeyError("fileIndex[a][b] has no 'cCount' key")
if "wCount" not in sEntry:
raise KeyError("%s[a][b] has no 'wCount' key" % idxName)
raise KeyError("fileIndex[a][b] has no 'wCount' key")
if "pCount" not in sEntry:
raise KeyError("%s[a][b] has no 'pCount' key" % idxName)
if "updated" not in sEntry:
raise KeyError("%s[a][b] has no 'updated' key" % idxName)
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 self.H_VALID:
raise ValueError("%s[a][b][level] is not a header level" % idxName)
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("%s[a][b][title] is not a string" % idxName)
raise ValueError("fileIndex[a][b][title] is not a string")
if not isItemLayout(sEntry["layout"]):
raise ValueError("%s[a][b][layout] is not an nwItemLayout" % idxName)
if not isinstance(sEntry["synopsis"], str):
raise ValueError("%s[a][b][synopsis] is not a string" % idxName)
raise ValueError("fileIndex[a][b][layout] is not an nwItemLayout")
if not isinstance(sEntry["cCount"], int):
raise ValueError("%s[a][b][cCount] is not an integer" % idxName)
raise ValueError("fileIndex[a][b][cCount] is not an integer")
if not isinstance(sEntry["wCount"], int):
raise ValueError("%s[a][b][wCount] is not an integer" % idxName)
raise ValueError("fileIndex[a][b][wCount] is not an integer")
if not isinstance(sEntry["pCount"], int):
raise ValueError("%s[a][b][pCount] is not an integer" % idxName)
if not isinstance(sEntry["updated"], int):
raise ValueError("%s[a][b][updated] is not an integer" % idxName)
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 _checkTextCounts(self):
"""Scan the text counts index for errors.
Waring: This function raises exceptions.
Warning: This function raises exceptions.
"""
for tHandle in self._textCounts:
if not isHandle(tHandle):
+3
View File
@@ -443,6 +443,9 @@ class GuiProjectDetailsContents(QWidget):
progPage = f"{cPage:n}"
progText = f"{pgProg:.1f}{nwUnicode.U_THSP}%"
if tTitle.strip() == "":
tTitle = self.tr("Untitled")
newItem.setIcon(self.C_TITLE, self.theTheme.getIcon("doc_h%d" % tLevel))
newItem.setText(self.C_TITLE, tTitle)
newItem.setText(self.C_WORDS, f"{wCount:n}")