Remove project meta attribute from project class
This commit is contained in:
@@ -305,11 +305,10 @@ class ProjectBuilder:
|
||||
return False
|
||||
|
||||
project = NWProject(self.mainGui)
|
||||
if not project.setProjectPath(projPath, newProject=True):
|
||||
if not project.storage.openProjectInPlace(projPath, newProject=True):
|
||||
return False
|
||||
|
||||
if not project.storage.openProjectInPlace(projPath):
|
||||
return False
|
||||
project.projPath = projPath
|
||||
|
||||
lblNewProject = self.tr("New Project")
|
||||
lblNewChapter = self.tr("New Chapter")
|
||||
|
||||
@@ -26,11 +26,11 @@ 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 os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.enum import nwItemType, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
@@ -141,12 +141,15 @@ class NWIndex:
|
||||
def loadIndex(self):
|
||||
"""Load index from last session from the project meta folder.
|
||||
"""
|
||||
indexFile = self.theProject.storage.getMetaFile(nwFiles.INDEX_FILE)
|
||||
if not isinstance(indexFile, Path):
|
||||
return False
|
||||
|
||||
theData = {}
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
tStart = time()
|
||||
|
||||
self._indexBroken = False
|
||||
if os.path.isfile(indexFile):
|
||||
if indexFile.exists():
|
||||
logger.debug("Loading index file")
|
||||
try:
|
||||
with open(indexFile, mode="r", encoding="utf-8") as inFile:
|
||||
@@ -184,8 +187,11 @@ class NWIndex:
|
||||
"""Save the current index as a json file in the project meta
|
||||
data folder.
|
||||
"""
|
||||
indexFile = self.theProject.storage.getMetaFile(nwFiles.INDEX_FILE)
|
||||
if not isinstance(indexFile, Path):
|
||||
return False
|
||||
|
||||
logger.debug("Saving index file")
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
tStart = time()
|
||||
|
||||
try:
|
||||
|
||||
@@ -24,11 +24,11 @@ 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 os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkBool, checkFloat, checkInt, checkString
|
||||
@@ -77,13 +77,12 @@ class OptionState:
|
||||
def loadSettings(self):
|
||||
"""Load the options dictionary from the project settings file.
|
||||
"""
|
||||
if self.theProject.projMeta is None:
|
||||
stateFile = self.theProject.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||
if not isinstance(stateFile, Path):
|
||||
return False
|
||||
|
||||
stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
|
||||
theState = {}
|
||||
|
||||
if os.path.isfile(stateFile):
|
||||
if stateFile.exists():
|
||||
logger.debug("Loading GUI options file")
|
||||
try:
|
||||
with open(stateFile, mode="r", encoding="utf-8") as inFile:
|
||||
@@ -106,12 +105,11 @@ class OptionState:
|
||||
def saveSettings(self):
|
||||
"""Save the options dictionary to the project settings file.
|
||||
"""
|
||||
if self.theProject.projMeta is None:
|
||||
stateFile = self.theProject.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||
if not isinstance(stateFile, Path):
|
||||
return False
|
||||
|
||||
stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
|
||||
logger.debug("Saving GUI options file")
|
||||
|
||||
try:
|
||||
with open(stateFile, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self._theState, outFile, indent=2)
|
||||
|
||||
@@ -27,6 +27,7 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import logging
|
||||
import novelwriter
|
||||
@@ -84,7 +85,6 @@ class NWProject(QObject):
|
||||
|
||||
# Class Settings
|
||||
self.projPath = None # The full path to where the currently open project is saved
|
||||
self.projMeta = None # The full path to the project's meta data folder
|
||||
self.projCache = None # The full path to the project's cache folder
|
||||
self.projContent = None # The full path to the project's content folder
|
||||
self.projDict = None # The spell check dictionary
|
||||
@@ -254,7 +254,6 @@ class NWProject(QObject):
|
||||
|
||||
# Project Settings
|
||||
self.projPath = None
|
||||
self.projMeta = None
|
||||
self.projCache = None
|
||||
self.projContent = None
|
||||
self.projDict = None
|
||||
@@ -276,11 +275,10 @@ class NWProject(QObject):
|
||||
self.projPath = str(self._storage.runtimePath)
|
||||
self.projContent = str(self._storage.contentPath)
|
||||
self.projCache = str(self._storage.cachePath)
|
||||
self.projMeta = str(self._storage.metaPath)
|
||||
|
||||
logger.info("Opening project: %s", self.projPath)
|
||||
|
||||
self.projDict = os.path.join(self.projMeta, nwFiles.PROJ_DICT)
|
||||
self.projDict = str(self._storage.getMetaFile(nwFiles.PROJ_DICT))
|
||||
|
||||
# Project Lock
|
||||
# ============
|
||||
@@ -421,8 +419,6 @@ class NWProject(QObject):
|
||||
return False
|
||||
|
||||
saveTime = time()
|
||||
if not self.ensureFolderStructure():
|
||||
return False
|
||||
|
||||
logger.info("Saving project: %s", self.projPath)
|
||||
|
||||
@@ -482,7 +478,6 @@ class NWProject(QObject):
|
||||
if self.projPath is None or self.projPath == "":
|
||||
return False
|
||||
|
||||
self.projMeta = os.path.join(self.projPath, "meta")
|
||||
self.projCache = os.path.join(self.projPath, "cache")
|
||||
self.projContent = os.path.join(self.projPath, "content")
|
||||
|
||||
@@ -490,8 +485,6 @@ class NWProject(QObject):
|
||||
# Don't make a mess in the user's home folder
|
||||
return False
|
||||
|
||||
if not self._checkFolder(self.projMeta):
|
||||
return False
|
||||
if not self._checkFolder(self.projCache):
|
||||
return False
|
||||
if not self._checkFolder(self.projContent):
|
||||
@@ -589,41 +582,6 @@ class NWProject(QObject):
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setProjectPath(self, projPath, newProject=False):
|
||||
"""Set the project storage path, and also expand ~ to the user
|
||||
directory using the path library.
|
||||
"""
|
||||
if projPath is None or projPath == "":
|
||||
self.projPath = None
|
||||
else:
|
||||
if projPath.startswith("~"):
|
||||
projPath = os.path.expanduser(projPath)
|
||||
self.projPath = os.path.abspath(projPath)
|
||||
|
||||
if newProject:
|
||||
if not os.path.isdir(projPath):
|
||||
try:
|
||||
os.mkdir(projPath)
|
||||
logger.debug("Created folder: %s", projPath)
|
||||
except Exception as exc:
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Could not create new project folder."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
|
||||
if os.path.isdir(projPath):
|
||||
if os.listdir(self.projPath):
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"New project folder is not empty. "
|
||||
"Each project requires a dedicated project folder."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
self.ensureFolderStructure()
|
||||
self.setProjectChanged(True)
|
||||
|
||||
return True
|
||||
|
||||
def setProjectLang(self, theLang):
|
||||
"""Set the project-specific language.
|
||||
"""
|
||||
@@ -934,12 +892,10 @@ class NWProject(QObject):
|
||||
def _appendSessionStats(self, idleTime):
|
||||
"""Append session statistics to the sessions log file.
|
||||
"""
|
||||
if not self.ensureFolderStructure():
|
||||
sessionFile = self._storage.getMetaFile(nwFiles.SESS_STATS)
|
||||
if not isinstance(sessionFile, Path):
|
||||
return False
|
||||
|
||||
sessionFile = os.path.join(self.projMeta, nwFiles.SESS_STATS)
|
||||
isFile = os.path.isfile(sessionFile)
|
||||
|
||||
nowTime = time()
|
||||
iNovel, iNotes = self._data.initCounts
|
||||
cNovel, cNotes = self._data.currCounts
|
||||
@@ -953,6 +909,7 @@ class NWProject(QObject):
|
||||
return False
|
||||
|
||||
try:
|
||||
isFile = sessionFile.exists() # We must save the state before we open
|
||||
with open(sessionFile, mode="a+", encoding="utf-8") as outFile:
|
||||
if not isFile:
|
||||
# It's a new file, so add a header
|
||||
|
||||
@@ -97,7 +97,7 @@ class NWStorage:
|
||||
"""
|
||||
return self._runtimePath is not None
|
||||
|
||||
def openProjectInPlace(self, path):
|
||||
def openProjectInPlace(self, path, newProject=False):
|
||||
"""Open a novelWriter project in-place. That is, it is opened
|
||||
directly from a project folder.
|
||||
"""
|
||||
@@ -112,7 +112,7 @@ class NWStorage:
|
||||
self._lockFilePath = inPath / nwFiles.PROJ_LOCK
|
||||
self._openMode = self.MODE_INPLACE
|
||||
|
||||
if self._prepareStorage(checkLegacy=True) is False:
|
||||
if not self._prepareStorage(checkLegacy=True, newProject=newProject):
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
@@ -142,7 +142,7 @@ class NWStorage:
|
||||
##
|
||||
|
||||
def getXmlReader(self):
|
||||
"""
|
||||
"""Return a properly configured ProjectXMLReader instance.
|
||||
"""
|
||||
if self._runtimePath is None:
|
||||
return None
|
||||
@@ -153,7 +153,7 @@ class NWStorage:
|
||||
return xmlReader
|
||||
|
||||
def getXmlWriter(self):
|
||||
"""
|
||||
"""Return a properly configured ProjectXMLWriter instance.
|
||||
"""
|
||||
if self._runtimePath is None:
|
||||
return None
|
||||
@@ -165,8 +165,12 @@ class NWStorage:
|
||||
def getDocument(self, tHandle):
|
||||
pass
|
||||
|
||||
def getMetaFile(self, kind):
|
||||
pass
|
||||
def getMetaFile(self, fileName):
|
||||
"""Return the path to a file in the project meta folder.
|
||||
"""
|
||||
if self._runtimePath is not None:
|
||||
return self._runtimePath / "meta" / fileName
|
||||
return None
|
||||
|
||||
def readLockFile(self):
|
||||
"""Read the project lock file.
|
||||
@@ -234,7 +238,7 @@ class NWStorage:
|
||||
def _writeLockFile(self):
|
||||
pass
|
||||
|
||||
def _prepareStorage(self, checkLegacy=True):
|
||||
def _prepareStorage(self, checkLegacy=True, newProject=False):
|
||||
"""Prepare the storage area for the project.
|
||||
"""
|
||||
path = self._runtimePath
|
||||
@@ -248,6 +252,15 @@ class NWStorage:
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
if newProject:
|
||||
# If it's a new project, we check that there is no existing
|
||||
# project in the selected path.
|
||||
projFile = path / nwFiles.PROJ_FILE
|
||||
if projFile.exists():
|
||||
logger.error("A project already exists in this path")
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
# The folder is not required to exist, as it could be a new
|
||||
# project, so we make sure it does. Then we add subfolders.
|
||||
try:
|
||||
|
||||
@@ -23,10 +23,11 @@ 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 os
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QListWidget,
|
||||
@@ -150,9 +151,11 @@ class GuiWordList(QDialog):
|
||||
"""
|
||||
self._saveGuiSettings()
|
||||
|
||||
dctFile = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
|
||||
tmpFile = dctFile + "~"
|
||||
dctFile = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
|
||||
if not isinstance(dctFile, Path):
|
||||
return False
|
||||
|
||||
tmpFile = dctFile.with_suffix(".tmp")
|
||||
try:
|
||||
with open(tmpFile, mode="w", encoding="utf-8") as outFile:
|
||||
for i in range(self.listBox.count()):
|
||||
@@ -160,15 +163,16 @@ class GuiWordList(QDialog):
|
||||
if item is not None:
|
||||
outFile.write(item.text() + "\n")
|
||||
|
||||
if dctFile.exists():
|
||||
dctFile.unlink()
|
||||
tmpFile.rename(dctFile)
|
||||
|
||||
except Exception:
|
||||
logger.error("Could not save new word list")
|
||||
logException()
|
||||
self.reject()
|
||||
return False
|
||||
|
||||
if os.path.isfile(dctFile):
|
||||
os.unlink(dctFile)
|
||||
os.rename(tmpFile, dctFile)
|
||||
self.accept()
|
||||
|
||||
return True
|
||||
@@ -187,10 +191,12 @@ class GuiWordList(QDialog):
|
||||
def _loadWordList(self):
|
||||
"""Load the project's word list, if it exists.
|
||||
"""
|
||||
self.listBox.clear()
|
||||
wordList = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
|
||||
if not isinstance(wordList, Path):
|
||||
return False
|
||||
|
||||
wordList = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
|
||||
if not os.path.isfile(wordList):
|
||||
self.listBox.clear()
|
||||
if not wordList.exists():
|
||||
logger.debug("No project dictionary file found")
|
||||
return False
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import json
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtGui import QPixmap, QCursor
|
||||
@@ -439,8 +440,8 @@ class GuiWritingStats(QDialog):
|
||||
ttTime = 0
|
||||
ttIdle = 0
|
||||
|
||||
logFile = os.path.join(self.theProject.projMeta, nwFiles.SESS_STATS)
|
||||
if not os.path.isfile(logFile):
|
||||
logFile = self.theProject.storage.getMetaFile(nwFiles.SESS_STATS)
|
||||
if not isinstance(logFile, Path) or not logFile.exists():
|
||||
logger.info("This project has no writing stats logfile")
|
||||
return False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user