Move new project tool to core tools (resolves #1152)
This commit is contained in:
@@ -24,15 +24,30 @@ 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 shutil
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
from novelwriter.common import minmax
|
||||
from time import time
|
||||
from functools import partial
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from novelwriter.enum import nwAlert
|
||||
from novelwriter.common import minmax, simplified
|
||||
from novelwriter.constants import nwItemClass
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.document import NWDoc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocMerger:
|
||||
"""Document tool for merging a set of documents into a single new
|
||||
document. The parameters are defined by the user using the
|
||||
GuiDocMerge dialog.
|
||||
"""
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
@@ -122,6 +137,10 @@ class DocMerger:
|
||||
|
||||
|
||||
class DocSplitter:
|
||||
"""Document tool for splitting a document into a set of new
|
||||
documents. The parameters are defined by the user using the
|
||||
GuiDocSplit dialog.
|
||||
"""
|
||||
|
||||
def __init__(self, theProject, sHandle):
|
||||
|
||||
@@ -242,3 +261,197 @@ class DocSplitter:
|
||||
return
|
||||
|
||||
# END Class DocSplitter
|
||||
|
||||
|
||||
class ProjectBuilder:
|
||||
"""A class to build a new project from a set of user-defined
|
||||
parameter provided by the New Projecty Wizard.
|
||||
"""
|
||||
|
||||
def __init__(self, mainGui):
|
||||
|
||||
self.mainGui = mainGui
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
|
||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def buildProject(self, data):
|
||||
"""Build a project from a data dictionary of specifications
|
||||
provided by the wizard.
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
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")
|
||||
return False
|
||||
|
||||
project = NWProject(self.mainGui)
|
||||
if not project.setProjectPath(projPath, newProject=True):
|
||||
return False
|
||||
|
||||
if not project.storage.openProjectInPlace(projPath):
|
||||
return False
|
||||
|
||||
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)
|
||||
projAuthors = data.get("projAuthors", "")
|
||||
|
||||
project.data.setName(projName)
|
||||
project.data.setTitle(projTitle)
|
||||
project.data.setAuthors(projAuthors)
|
||||
project.setDefaultStatusImport()
|
||||
project._projOpened = int(time())
|
||||
|
||||
# Add Root Folders
|
||||
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
||||
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
|
||||
novelTitle = project.data.title if project.data.title else project.data.name
|
||||
|
||||
titlePage = f"#! {novelTitle}\n\n"
|
||||
if project.data.authors:
|
||||
titlePage += f">> {lblByAuthors} {project.getFormattedAuthors()} <<\n\n"
|
||||
|
||||
aDoc = NWDoc(project, 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 = NWDoc(project, hChapter)
|
||||
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
|
||||
|
||||
hScene = project.newFile(lblNewScene, hChapter)
|
||||
aDoc = NWDoc(project, hScene)
|
||||
aDoc.writeDocument(f"### {lblNewScene}\n\n")
|
||||
|
||||
project.newRoot(nwItemClass.PLOT)
|
||||
project.newRoot(nwItemClass.CHARACTER)
|
||||
project.newRoot(nwItemClass.WORLD)
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
|
||||
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 chapters and scenes
|
||||
numChapters = data.get("numChapters", 0)
|
||||
numScenes = data.get("numScenes", 0)
|
||||
|
||||
chSynop = self.tr("Summary of the chapter.")
|
||||
scSynop = self.tr("Summary of the scene.")
|
||||
|
||||
# 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 = NWDoc(project, cHandle)
|
||||
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
||||
|
||||
# Create chapter scenes
|
||||
if numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, cHandle)
|
||||
aDoc = NWDoc(project, sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||
|
||||
# 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 = NWDoc(project, 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 = NWDoc(project, aHandle)
|
||||
aDoc.writeDocument(f"# {noteTitles[newRoot]}\n\n@tag: {ntTag}\n\n")
|
||||
|
||||
# Also add the archive and trash folders
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
project.trashFolder()
|
||||
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _extractSampleProject(self, data):
|
||||
"""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 = os.path.join(self.mainConf.assetPath, "sample.zip")
|
||||
if os.path.isfile(pkgSample):
|
||||
try:
|
||||
shutil.unpack_archive(pkgSample, projPath)
|
||||
except Exception as exc:
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
|
||||
else:
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project. "
|
||||
"Could not find the necessary files. "
|
||||
"They seem to be missing from this installation."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# END Class ProjectBuilder
|
||||
|
||||
Reference in New Issue
Block a user