Use the new index instead of the old

This commit is contained in:
Veronica Berglyd Olsen
2022-05-28 21:59:52 +02:00
parent 01be8be44f
commit 94ba7a2f94
7 changed files with 343 additions and 1015 deletions
+126 -274
View File
@@ -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
##
+4 -4
View File
@@ -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}")
+8 -8
View File
@@ -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}")
+7 -7
View File
@@ -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))