158 lines
4.5 KiB
Python
158 lines
4.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""novelWriter Project Index
|
||
|
||
novelWriter – Project Index
|
||
=============================
|
||
Class holding the index of tags
|
||
|
||
File History:
|
||
Created: 2019-05-27 [0.1.4]
|
||
|
||
Structure:
|
||
|
||
We need to scan all files to get the novel layout. This is done by heading depth. Each heading
|
||
is recorded in a sorted list. Each such heading again can have a set of meta tags.
|
||
In the other class root folders, each file can have a tag which the meta tags in the novel files
|
||
point to. These tags should be stored in a dictionary where the tag is the key pointing to a single
|
||
file handle. That way we can do lookups on both keys and values.
|
||
The timeline view then consists of novel header elements in the horizontal header, possibly just
|
||
truncated to a Ch or Sc abbreviation, possibly with a number. This can be extracted from the item
|
||
layout. The vertical header column is then whatever notes we want to compare against, and the links
|
||
from the novel files are dots on the row.
|
||
|
||
"""
|
||
|
||
import logging
|
||
import json
|
||
import nw
|
||
|
||
from os import path
|
||
|
||
from nw.project.document import NWDoc
|
||
from nw.enum import nwItemType
|
||
from nw.constants import nwFiles
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class NWIndex():
|
||
|
||
TAG_KEY = "tag"
|
||
NOTE_KEYS = ["pov","char","plot","time","location","object","custom"]
|
||
NOVEL_KEYS = ["scene","chapter","part"]
|
||
|
||
VALID_KEYS = [TAG_KEY] + NOTE_KEYS + NOVEL_KEYS
|
||
|
||
def __init__(self, theProject, theParent):
|
||
|
||
# Internal
|
||
self.theProject = theProject
|
||
self.theParent = theParent
|
||
self.mainConf = self.theParent.mainConf
|
||
|
||
# Indices
|
||
self.itemIndex = {}
|
||
|
||
return
|
||
|
||
def clearIndex(self):
|
||
self.itemIndex = {}
|
||
return
|
||
|
||
def loadIndex(self):
|
||
|
||
indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||
if path.isfile(indexFile):
|
||
logger.debug("Loading index file")
|
||
try:
|
||
with open(indexFile,mode="r") as inFile:
|
||
theJson = inFile.read()
|
||
self.itemIndex = json.loads(theJson)
|
||
except Exception as e:
|
||
logger.error("Failed to load index file")
|
||
logger.error(str(e))
|
||
return False
|
||
|
||
return True
|
||
|
||
return False
|
||
|
||
def saveIndex(self):
|
||
|
||
indexFile = path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||
logger.debug("Saving index file")
|
||
if self.mainConf.debugInfo:
|
||
nIndent = 2
|
||
else:
|
||
nIndent = None
|
||
try:
|
||
with open(indexFile,mode="w+") as outFile:
|
||
outFile.write(json.dumps(self.itemIndex, indent=nIndent))
|
||
except Exception as e:
|
||
logger.error("Failed to save index file")
|
||
logger.error(str(e))
|
||
return False
|
||
|
||
return True
|
||
|
||
def scanText(self, tHandle, theText):
|
||
|
||
theItem = self.theProject.getItem(tHandle)
|
||
if theItem is None:
|
||
return False
|
||
if theItem.itemType != nwItemType.FILE:
|
||
return False
|
||
|
||
logger.debug("Indexing item with handle %s" % tHandle)
|
||
|
||
self.itemIndex[tHandle] = {}
|
||
|
||
nLine = 0
|
||
for aLine in theText.splitlines():
|
||
aLine = aLine.strip()
|
||
nLine += 1
|
||
nChar = len(aLine)
|
||
if nChar > 0 and aLine[0] == "@":
|
||
self.indexThis(tHandle, aLine, nLine, theItem)
|
||
|
||
return True
|
||
|
||
def indexThis(self, tHandle, aLine, nLine, theItem):
|
||
|
||
nChar = len(aLine)
|
||
nPos = aLine.find(":")
|
||
if nPos < 2 or nChar < nPos+2:
|
||
return False
|
||
|
||
aKey = aLine[1:nPos].strip().lower()
|
||
tVal = aLine[nPos+1:].strip().lower()
|
||
if aKey not in self.VALID_KEYS:
|
||
logger.verbose("Not a valid key '%s'" % aKey)
|
||
return False
|
||
|
||
logger.verbose("Found valid key '%s'" % aKey)
|
||
if aKey == self.TAG_KEY:
|
||
if tVal.find(",") >- 0:
|
||
return False
|
||
self._addItem(tHandle, aKey, nLine, tVal)
|
||
else:
|
||
kVal = tVal.split(",")
|
||
cVal = [aVal.strip() for aVal in kVal]
|
||
if len(cVal) > 0:
|
||
self._addItem(tHandle, aKey, nLine, cVal)
|
||
else:
|
||
return False
|
||
|
||
return True
|
||
|
||
##
|
||
# Internal Functions
|
||
##
|
||
|
||
def _addItem(self, tHandle, tKey, tLine, tVal):
|
||
if tKey not in self.itemIndex[tHandle].keys():
|
||
self.itemIndex[tHandle][tKey] = []
|
||
self.itemIndex[tHandle][tKey].append([tLine, tVal])
|
||
return
|
||
|
||
# END Class NWIndex
|