Complete the create new project code path

This commit is contained in:
Veronica Berglyd Olsen
2023-12-20 21:52:57 +01:00
parent a71d0d8cfe
commit 41b7495a06
6 changed files with 203 additions and 138 deletions
+101 -107
View File
@@ -30,6 +30,7 @@ import shutil
import logging
from typing import Iterable
from pathlib import Path
from functools import partial
from PyQt5.QtCore import QCoreApplication
@@ -39,6 +40,7 @@ from novelwriter.common import minmax, simplified
from novelwriter.constants import nwItemClass
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
from novelwriter.core.storage import NWStorageCreate
logger = logging.getLogger(__name__)
@@ -310,9 +312,15 @@ class ProjectBuilder:
"""
def __init__(self) -> None:
self._path = None
self.tr = partial(QCoreApplication.translate, "NWProject")
return
@property
def projPath(self) -> Path | None:
"""The path of the newly created project."""
return self._path
##
# Methods
##
@@ -325,40 +333,56 @@ class ProjectBuilder:
logger.error("Invalid call to newProject function")
return False
popMinimal = data.get("popMinimal", True)
popCustom = data.get("popCustom", False)
popSample = data.get("popSample", False)
# Check if we're extracting the sample project. This is handled
# differently as it isn't actually a new project, so we forward
# this to another function and return here.
if popSample:
return self._extractSampleProject(data)
projPath = data.get("projPath", None)
if projPath is None:
logger.error("No project path set for the new project")
path = data.get("path", None)
if path is None:
SHARED.error("A project path is required.")
return False
if data.get("sample", False):
self._path = path
return self._extractSampleProject(path)
elif data.get("template"):
pass
else:
self._buildAndPopulate(path, data)
return True
##
# Internal Functions
##
def _buildAndPopulate(self, path: Path, data: dict) -> bool:
"""Build a project from a data dictionary of specifications
provided by the wizard.
"""
project = NWProject()
if not project.storage.createNewProject(projPath):
status = project.storage.createNewProject(path)
if status == NWStorageCreate.NOT_EMPTY:
SHARED.error(self.tr(
"A project already exists in that location. "
"Please choose another folder."
))
return False
elif status == NWStorageCreate.OS_ERROR:
SHARED.error(self.tr(
"An error occured while trying to create the project."
), exc=project.storage.exc)
return False
self._path = project.storage.storagePath
lblNewProject = self.tr("New Project")
lblNewChapter = self.tr("New Chapter")
lblNewScene = self.tr("New Scene")
lblTitlePage = self.tr("Title Page")
lblByAuthors = self.tr("By")
# Settings
projName = data.get("projName", lblNewProject)
projTitle = data.get("projTitle", lblNewProject)
projAuthor = data.get("projAuthor", "")
projLang = data.get("projLang", "en_GB")
projName = data.get("name", lblNewProject)
projAuthor = data.get("author", "")
projLang = data.get("language", "en_GB")
project.data.setUuid(None)
project.data.setName(projName)
project.data.setTitle(projTitle)
project.data.setAuthor(projAuthor)
project.data.setLanguage(projLang)
project.setDefaultStatusImport()
@@ -376,110 +400,80 @@ class ProjectBuilder:
aDoc = project.storage.getDocument(hTitlePage)
aDoc.writeDocument(titlePage)
if popMinimal:
# Creating a minimal project with a few root folders and a
# single chapter with a single scene.
hChapter = project.newFile(lblNewChapter, hNovelRoot)
aDoc = project.storage.getDocument(hChapter)
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
# Create a project structure based on selected root folders
# and a number of chapters and scenes selected in the
# wizard's custom page.
if hChapter:
hScene = project.newFile(lblNewScene, hChapter)
aDoc = project.storage.getDocument(hScene)
aDoc.writeDocument(f"### {lblNewScene}\n\n")
# Create chapters and scenes
numChapters = data.get("chapters", 0)
numScenes = data.get("scenes", 0)
project.newRoot(nwItemClass.PLOT)
project.newRoot(nwItemClass.CHARACTER)
project.newRoot(nwItemClass.WORLD)
project.newRoot(nwItemClass.ARCHIVE)
chSynop = self.tr("Summary of the chapter.")
scSynop = self.tr("Summary of the scene.")
bfNote = self.tr("A short description.")
project.saveProject()
project.closeProject()
# Create chapters
if numChapters > 0:
for ch in range(numChapters):
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
cHandle = project.newFile(chTitle, hNovelRoot)
aDoc = project.storage.getDocument(cHandle)
aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {chSynop}\n\n")
elif popCustom:
# Create a project structure based on selected root folders
# and a number of chapters and scenes selected in the
# wizard's custom page.
# Create chapter scenes
if numScenes > 0 and cHandle:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = project.newFile(scTitle, cHandle)
aDoc = project.storage.getDocument(sHandle)
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
# Create chapters and scenes
numChapters = data.get("numChapters", 0)
numScenes = data.get("numScenes", 0)
# Create scenes (no chapters)
elif numScenes > 0:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = project.newFile(scTitle, hNovelRoot)
aDoc = project.storage.getDocument(sHandle)
aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
chSynop = self.tr("Summary of the chapter.")
scSynop = self.tr("Summary of the scene.")
bfNote = self.tr("A short description.")
# Create notes folders
noteTitles = {
nwItemClass.PLOT: self.tr("Main Plot"),
nwItemClass.CHARACTER: self.tr("Protagonist"),
nwItemClass.WORLD: self.tr("Main Location"),
}
# Create chapters
if numChapters > 0:
for ch in range(numChapters):
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
cHandle = project.newFile(chTitle, hNovelRoot)
aDoc = project.storage.getDocument(cHandle)
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
addNotes = data.get("notes", False)
for newRoot in data.get("roots", []):
if newRoot in nwItemClass:
rHandle = project.newRoot(newRoot)
if addNotes:
aHandle = project.newFile(noteTitles[newRoot], rHandle)
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
aDoc = project.storage.getDocument(aHandle)
aDoc.writeDocument(
f"# {noteTitles[newRoot]}\n\n"
f"@tag: {ntTag}\n\n"
f"%Short: {bfNote}\n\n"
)
# Create chapter scenes
if numScenes > 0 and cHandle:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = project.newFile(scTitle, cHandle)
aDoc = project.storage.getDocument(sHandle)
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
# Also add the archive and trash folders
project.newRoot(nwItemClass.ARCHIVE)
project.trashFolder()
# Create scenes (no chapters)
elif numScenes > 0:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = project.newFile(scTitle, hNovelRoot)
aDoc = project.storage.getDocument(sHandle)
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
# Create notes folders
noteTitles = {
nwItemClass.PLOT: self.tr("Main Plot"),
nwItemClass.CHARACTER: self.tr("Protagonist"),
nwItemClass.WORLD: self.tr("Main Location"),
}
addNotes = data.get("addNotes", False)
for newRoot in data.get("addRoots", []):
if newRoot in nwItemClass:
rHandle = project.newRoot(newRoot)
if addNotes:
aHandle = project.newFile(noteTitles[newRoot], rHandle)
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
aDoc = project.storage.getDocument(aHandle)
aDoc.writeDocument(
f"# {noteTitles[newRoot]}\n\n"
f"@tag: {ntTag}\n\n"
f"% Short: {bfNote}\n\n"
)
# Also add the archive and trash folders
project.newRoot(nwItemClass.ARCHIVE)
project.trashFolder()
project.saveProject()
project.closeProject()
project.saveProject()
project.closeProject()
return True
##
# Internal Functions
##
def _extractSampleProject(self, data: dict) -> bool:
def _extractSampleProject(self, path: Path) -> bool:
"""Make a copy of the sample project by extracting the
sample.zip file to the new path.
"""
projPath = data.get("projPath", None)
if projPath is None:
logger.error("No project path set for the example project")
return False
pkgSample = CONFIG.assetPath("sample.zip")
if pkgSample.is_file():
try:
shutil.unpack_archive(pkgSample, projPath)
shutil.unpack_archive(pkgSample, path)
except Exception as exc:
SHARED.error(self.tr(
"Failed to create a new example project."
+13 -4
View File
@@ -57,6 +57,15 @@ class NWStorageOpen(Enum):
# END Enum NWStorageOpen
class NWStorageCreate(Enum):
NOT_EMPTY = 0
OS_ERROR = 1
READY = 2
# END Enum NWStorageCreate
class NWStorage:
"""Core: Project Storage Class
@@ -133,12 +142,12 @@ class NWStorage:
"""Check if the storage location is open."""
return self._ready and self._runtimePath is not None
def createNewProject(self, path: str | Path) -> bool:
def createNewProject(self, path: str | Path) -> NWStorageCreate:
"""Create a new project at the given location."""
inPath = Path(path).resolve()
if inPath.is_dir() and len(list(inPath.iterdir())) > 0:
logger.error("Folder is not empty: %s", inPath)
return False
return NWStorageCreate.NOT_EMPTY
self._storagePath = inPath
self._runtimePath = inPath
@@ -156,11 +165,11 @@ class NWStorage:
self._exception = exc
logger.error("Failed to create project folders", exc_info=exc)
self.clear()
return False
return NWStorageCreate.OS_ERROR
self._ready = True
return True
return NWStorageCreate.READY
def initProjectStorage(self, path: str | Path, clearLock: bool = False) -> NWStorageOpen:
"""Initialise a novelWriter project location."""