class ProjCommander (object) : def __init__(self, pid) : '''Intitate the whole class and create the object.''' self.pid = pid self.tools = Tools() self.user = UserConfig() self.userConfig = self.user.userConfig self.projHome = os.path.join(os.environ['RAPUMA_PROJECTS'], self.pid) self.local = ProjLocal(self.pid) self.proj_config = Config(pid) self.proj_config.getProjectConfig() self.projectConfig = self.proj_config.projectConfig self.projectMediaIDCode = self.projectConfig['ProjectInfo']['projectMediaIDCode'] # Log messages for this module self.errorCodes = { '0000' : ['MSG', 'Placeholder message'], } ############################################################################### ########################## Command Creation Functions ######################### ############################################################################### def removeScripts (self) : '''Remove any unnecessary group control scripts from the project.''' self.tools.dieNow('removeScripts() not implemented yet.') def updateScripts (self) : '''Update all the helper command scripts in a project.''' self.makeStaticScripts() self.makeGrpScripts() def makeGrpScripts (self) : '''Create scripts that process specific group components.''' if not os.path.isdir(self.local.projHelpScriptFolder) : os.mkdir(self.local.projHelpScriptFolder) # Output the scripts (If this is a new project we need to pass) if self.projectConfig.has_key('Groups') : for gid in self.projectConfig['Groups'].keys() : allScripts = self.getGrpScripInfo(gid) for key in allScripts.keys() : fullFile = os.path.join(self.local.projHelpScriptFolder, key) + gid with codecs.open(fullFile, "w", encoding='utf_8') as writeObject : writeObject.write(self.makeScriptHeader(allScripts[key][0], allScripts[key][1])) # Strip out extra spaces from command cmd = re.sub(ur'\s+', ur' ', allScripts[key][1]) writeObject.write(cmd + '\n\n') # Make the script executable self.tools.makeExecutable(fullFile) self.tools.terminal('\nCompleted creating/recreating group helper scripts.\n') else : pass def makeStaticScripts (self) : '''Create helper scripts for a project to help with repetitive tasks. If any scripts are present with the same name they will be overwritten. Note: This is only for temporary use due to the lack of an interface at this time (20130306140636). It assumes the cType is usfm which, at some point may not be the case.''' if not os.path.isdir(self.local.projHelpScriptFolder) : os.mkdir(self.local.projHelpScriptFolder) # Output the scripts allScripts = self.getStaticScripInfo() for key in allScripts.keys() : fullFile = os.path.join(self.local.projHelpScriptFolder, key) with codecs.open(fullFile, "w", encoding='utf_8') as writeObject : writeObject.write(self.makeScriptHeader(allScripts[key][0], allScripts[key][1])) writeObject.write(allScripts[key][1] + '\n\n') # Make the script executable self.tools.makeExecutable(fullFile) self.tools.terminal('\nCompleted creating/recreating static helper scripts.\n') def makeScriptHeader (self, desc, cmd) : '''Make a helper script header.''' return '#!/bin/sh\n\n# Description: ' + desc + '\n\necho \necho Rapuma helper script: ' + desc + '\n\necho \necho command: ' + self.echoClean(cmd) + '\n\n' def echoClean (self, cmdStr) : '''Clean up a string for an echo statement in a shell script.''' clean = re.sub(ur'\;', ur'\\;', cmdStr) clean = re.sub(ur'\s+', ur' ', clean) return clean def getStaticScripInfo (self) : '''Create a dictionary of all the static auxillary script information used in most projects.''' pid = self.pid mid = self.projectMediaIDCode return { 'addBible' : ['Add Scripture components for a Bible group.', 'rapuma group ' + pid + ' BIBLE group add --source_path $1 '], 'addNT' : ['Add Scripture components for an NT group.', 'rapuma group ' + pid + ' NT group add --source_path $1 '], 'addOT' : ['Add Scripture components for an OT group.', 'rapuma group ' + pid + ' OT group add --source_path $1 '], 'archive' : ['Archive this project', 'rapuma project ' + pid + ' archive save '], 'backup' : ['Backup this project', 'rapuma project ' + pid + ' backup save '], 'cloudPull' : ['Pull data for this project from the cloud', 'rapuma project ' + pid + ' cloud restore '], 'cloudPush' : ['Push data from this project to the cloud', 'rapuma project ' + pid + ' cloud save $1 '], 'restore' : ['Restore a backup.', 'rapuma project ' + pid + ' backup restore '], 'template' : ['Create a template of the project.', 'rapuma project ' + pid + ' template save --id $1 '], 'updateScripts' : ['Update the project scripts.', 'rapuma project ' + pid + ' project update --update_type helper '], 'bind' : ['Create the binding PDF file', 'if [ "$1" ]; then CMD=" $1"; fi; if [ "$2" ]; then CMD=" $1 $2"; fi; rapuma project ' + pid + ' project bind $CMD '], 'placeholdOff' : ['Turn off illustration placeholders.', 'rapuma settings ' + pid + ' ' + mid + '_layout Illustrations useFigurePlaceHolders False '], 'placeholdOn' : ['Turn on illustration placeholders.', 'rapuma settings ' + pid + ' ' + mid + '_layout Illustrations useFigurePlaceHolders True '] } def getGrpScripInfo (self, gid) : '''Create a dictionary of the auxillary group script information used in most projects.''' # import pdb; pdb.set_trace() # Set the vars for this function pid = self.pid cType = self.projectConfig['Groups'][gid]['cType'] Ctype = cType.capitalize() renderer = self.projectConfig['CompTypes'][Ctype]['renderer'] self.proj_macro = Macro(self.pid, gid) macroConfig = self.proj_macro.macroConfig font = '' if macroConfig and macroConfig['FontSettings'].has_key('primaryFont') : font = macroConfig['FontSettings']['primaryFont'] macro = self.projectConfig['CompTypes'][Ctype]['macroPackage'] mid = self.projectMediaIDCode # Return a dictionary of all the commands we generate return { 'compare' : ['Compare component working text with backup.', 'if [ "$1" ]; then CMD="--cid_list $1"; fi; rapuma group ' + pid + ' ' + gid + ' group compare --compare_type backup $CMD '], 'render' : ['Render ' + gid + ' group PDF file.', 'if [ "$1" ]; then CMD="--cid_list $1"; fi; if [ "$2" ]; then CMD="--cid_list $1 $2"; fi; if [ "$3" ]; then CMD="--cid_list $1 $2 $3"; fi; rapuma group ' + pid + ' ' + gid + ' group render $CMD '], 'update' : ['Update the ' + gid + ' group from its source.', 'if [ "$2" ]; then CMD="--cid_list $2"; fi; rapuma group ' + pid + ' ' + gid + ' group update --source_path $1 $CMD '], 'background' : ['Re/Create the project background.', 'rapuma project ' + pid + ' project update --update_type background '], 'transparency' : ['Re/Create the project diagnostic layer.', 'rapuma project ' + pid + ' project update --update_type diagnostic '], 'addFont' : ['Add a font to the ' + gid + ' group.', 'rapuma package ' + pid + ' ' + gid + ' $1 font add -f '], 'removeFont' : ['Remove a font from the ' + gid + ' group.', 'rapuma package ' + pid + ' ' + gid + ' $1 font remove -f '], 'primaryFont' : ['Make font primary for the ' + gid + ' group.', 'rapuma package ' + pid + ' ' + gid + ' $1 font primary -f '], 'updateFont' : ['Update the ' + gid + ' font.', 'rapuma package ' + pid + ' ' + gid + ' \"' + font + '\" font update '], 'updateMacro' : ['Update the ' + gid + ' macro package.', 'rapuma package ' + pid + ' ' + gid + ' \"' + macro + '\" macro update '] }
class ProjScript (object) : def __init__(self, pid, gid) : '''Do the primary initialization for this class.''' self.pid = pid self.gid = gid self.tools = Tools() self.local = ProjLocal(pid) self.user = UserConfig() self.userConfig = self.user.userConfig self.proj_config = Config(pid, gid) self.proj_config.getProjectConfig() self.projectConfig = self.proj_config.projectConfig self.cType = self.projectConfig['Groups'][gid]['cType'] self.Ctype = self.cType.capitalize() self.log = ProjLog(pid) # Log messages for this module self.errorCodes = { '0000' : ['MSG', 'Placeholder message'], '1010' : ['MSG', 'Script install process for [<<1>>] Succeeded.'], '1020' : ['ERR', 'Script install process for [<<1>>] failed.'], '1030' : ['ERR', 'Script type [<<1>>] not supported.'], '1040' : ['ERR', 'Script install cannot proceed for [<<1>>] because this script already exists in the project. You must remove it first before you can add another script.'], '2010' : ['MSG', 'Script remove process for [<<1>>] Succeeded.'], '2020' : ['ERR', 'Script remove process for [<<1>>] failed.'], '4210' : ['MSG', 'Processes completed successfully on: [<<1>>] by [<<2>>]'], '4220' : ['ERR', 'Processes for [<<1>>] failed. Script [<<2>>] returned this error: [<<3>>]'], '4260' : ['ERR', 'Installed the default component preprocessing script. Editing will be required for it to work with your project.'], '4265' : ['LOG', 'Component preprocessing script is already installed.'], '4310' : ['ERR', 'Script is an unrecognized type: [<<1>>] Cannot continue with installation.'] } ############################################################################### ############################# Script Add Functions ############################ ############################################################################### ######################## Error Code Block Series = 1000 ####################### ############################################################################### def addScriptFiles (self, path, scriptType) : '''Import/add processing script files for a group that are found in the given path. Assumes valid path. Will fail if a copy doesn't succeed. If the file is already there, give a warning and will not copy.''' # import pdb; pdb.set_trace() source = path fileName = self.tools.fName(path) target = os.path.join(self.local.projScriptFolder, fileName) # Do an initial check to see if the script is already there # Never copy over the top of an existing script if os.path.isfile(target) : self.log.writeToLog(self.errorCodes['1040'], [fileName]) # Make script folder if needed if not os.path.isdir(self.local.projScriptFolder) : os.makedirs(self.local.projScriptFolder) # Copy in the script if self.scriptInstall(source, target) : # Record the script file name if scriptType == 'preprocess' : self.projectConfig['Groups'][self.gid]['preprocessScript'] = fileName elif scriptType == 'postprocess' : self.projectConfig['Groups'][self.gid]['postprocessScript'] = fileName else : self.log.writeToLog(self.errorCodes['1030'], [scriptType]) self.tools.writeConfFile(self.projectConfig) self.log.writeToLog(self.errorCodes['1010'], [fileName]) return True else : self.log.writeToLog(self.errorCodes['1020'], [fileName]) ############################################################################### ############################ Script Remove Functions ########################## ############################################################################### ######################## Error Code Block Series = 2000 ####################### ############################################################################### def removeScriptFiles (self, scriptType) : '''Remove processing script files for a group.''' if scriptType == 'preprocess' : fileName = self.projectConfig['Groups'][self.gid]['preprocessScript'] elif scriptType == 'postprocess' : fileName = self.projectConfig['Groups'][self.gid]['postprocessScript'] else : self.log.writeToLog(self.errorCodes['1030'], [scriptType]) target = os.path.join(self.local.projScriptFolder, fileName) # Remove the script (if it's not there, we don't care) try : os.remove(target) except : pass if not os.path.isfile(target) : if scriptType == 'preprocess' : self.projectConfig['Groups'][self.gid]['preprocessScript'] = '' elif scriptType == 'postprocess' : self.projectConfig['Groups'][self.gid]['postprocessScript'] = '' else : self.log.writeToLog(self.errorCodes['1030'], [scriptType]) self.tools.writeConfFile(self.projectConfig) self.log.writeToLog(self.errorCodes['2010'], [fileName]) return True else : self.log.writeToLog(self.errorCodes['2020'], [fileName]) ############################################################################### ########################## General Script Functions ########################### ############################################################################### ######################## Error Code Block Series = 4000 ####################### ############################################################################### def runProcessScript (self, target, scriptFile) : '''Run a text processing script on a component. This assumes the component and the script are valid and the component lock is turned off. If not, you cannot expect any good to come of this.''' # subprocess will fail if permissions are not set on the # script we want to run. The correct permission should have # been set when we did the installation. err = subprocess.call([scriptFile, target]) if err == 0 : self.log.writeToLog(self.errorCodes['4210'], [self.tools.fName(target), self.tools.fName(scriptFile)]) else : self.log.writeToLog(self.errorCodes['4220'], [self.tools.fName(target), self.tools.fName(scriptFile), str(err)]) return False return True def scriptInstall (self, source, target) : '''Install a script. A script can be a collection of items in a zip file or a single .py script file.''' scriptTargetFolder, fileName = os.path.split(target) if self.tools.isExecutable(source) : if not shutil.copy(source, target) : self.tools.makeExecutable(target) return True elif self.tools.fName(source).split('.')[1].lower() == 'zip' : myZip = zipfile.ZipFile(source, 'r') for f in myZip.namelist() : data = myZip.read(f, source) # Pretty sure zip represents directory separator char as "/" regardless of OS myPath = os.path.join(scriptTargetFolder, f.split("/")[-1]) try : myFile = open(myPath, "wb") myFile.write(data) myFile.close() except : pass myZip.close() return True else : self.log.writeToLog(self.errorCodes['4310'], [self.tools.fName(source)])
class Macro (object) : def __init__(self, pid, cType, gid=None) : '''Do the primary initialization for this class.''' self.pid = pid self.gid = gid self.cType = cType self.user = UserConfig() self.userConfig = self.user.userConfig self.projHome = os.path.join(os.path.expanduser(os.environ['RAPUMA_PROJECTS']), self.pid) self.local = ProjLocal(pid, gid, cType) # import pdb; pdb.set_trace() self.proj_config = Config(pid) self.proj_config.getProjectConfig() self.projectConfig = self.proj_config.projectConfig self.layoutConfig = self.proj_config.layoutConfig self.tools = Tools() self.log = ProjLog(pid) # Create config placeholders self.layoutConfig = None self.illustrationConfig = None self.macroConfig = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile) # Log messages for this module self.errorCodes = { '3010' : ['ERR', 'No macro package is registered for the [<<1>>] component type.'], '3020' : ['ERR', 'Cannot update! No macro package is registered for the [<<1>>] component type.'], '3050' : ['ERR', 'Macro package file not found: [<<1>>]'], '3100' : ['ERR', 'Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.'], '3200' : ['ERR', 'Failed to install macro package: [<<1>>]'], '3300' : ['MSG', 'Installed macro package: [<<1>>], Reinitialized [<<2>>]'], '3310' : ['ERR', 'Failed to copy [<<1>>] to folder [<<2>>].'], '3400' : ['MSG', 'Removed macro package configuration settings for: [<<1>>] from the macro.conf file.'], '3500' : ['MSG', 'Removed macro package [<<1>>] folder and all files contained.'], '3600' : ['MSG', 'Updated component type [<<1>>] with macro package [<<2>>]'], '3650' : ['ERR', 'Failed to updated macro package [<<1>>]'] } ############################################################################### ###################### Macro Package Handling Functions ####################### ############################################################################### ######################## Error Code Block Series = 3000 ####################### ############################################################################### def createMacroFiles (self, macPackId) : '''Create all the necessary macro file names with their assigned paths.''' self.projMacPackFolder = os.path.join(self.local.projMacPackFolder, macPackId) texFileIds = {'preStyTexExtFile':'preSty-ext.tex', 'macSettingsFile':'settings.tex', 'extTexFile':'extension.tex', 'grpExtTexFile': self.gid + '-extension.tex', '':'', '':'', '':'', '':'', '':'', } styFileIds = {'glbExtStyFile':'extension.sty', 'grpExtStyFile': self.gid + '-extension.sty', '':'', '':'', '':'', '':'', '':''} def getMacPackIdFromFileName (self, fileName) : '''Return the macPack ID based on the file name''' # File name less ext is the ID parts = len(fileName.split('.')) return '.'.join(fileName.split('.')[:parts-1]) def getMacPackIdFromSource (self, source) : '''Return the macPack ID based on the complete path and file name.''' # Get the file name from the path fileName = self.tools.fName(source) # Return the ID return self.getMacPackIdFromFileName(fileName) def addMacPack (self, source) : '''Add a macro package to the project. It will not work if the same package is already present. Remove must be used to get rid of the existing one first.''' # import pdb; pdb.set_trace() macPackId = self.getMacPackIdFromSource(source) confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.xml') if not os.path.isfile(source) : self.log.writeToLog(self.errorCodes['3050'], [source]) # Do not add/install if there seems to be a macro package there already if self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] and os.path.exists(self.local.macroConfFile) : self.log.writeToLog(self.errorCodes['3100'], [macPackId]) return False # Set the projectConf to the new/same package self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = macPackId self.tools.writeConfFile(self.projectConfig) # If we got this far, install the a fresh copy of the macPack self.installMacPackOnly(source) # Move the style files and custom TeX files out of the macPack self.moveMacStyles(macPackId) self.moveMacTex(macPackId) # Create a fresh macro.conf file if it dosn't exist if not os.path.isfile(self.local.macroConfFile) : self.macroConfig = self.tools.initNewConfig(self.local.macroConfFile, self.local.macroConfXmlFile) # Inject information from this particular macro package mInfo = self.tools.getXMLSettings(confXml) self.macroConfig['Macros'][macPackId] = mInfo.dict() # Save the settings now self.tools.writeConfFile(self.macroConfig) self.log.writeToLog(self.errorCodes['3300'], [macPackId, self.local.macroConfFileName]) return True def moveMacStyles (self, macPackId) : '''Move the default macro package styles out of the freshly installed project macro package folder to the project Style folder.''' # import pdb; pdb.set_trace() # Collect the style files to copy for f in self.getMacStyExtFiles(macPackId) : source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f)) target = os.path.join(self.local.projStyleFolder, f) self.tools.makedirs(self.local.projStyleFolder) # Do not overwrite existing files unless force is used if not os.path.exists(target) : shutil.copy(source, target) # Look for default and set to read-only defaultStyFile = os.path.join(self.local.projStyleFolder, macPackId + '.sty') if target == defaultStyFile : self.tools.makeReadOnly(defaultStyFile) # Remove the source to avoid confusion if os.path.exists(target) : os.remove(source) else : self.log.writeToLog(self.errorCodes['3310'], [source,self.local.projStyleFolder]) def getMacStyExtFiles (self, macPackId) : '''Return a list of macro package style extention files.''' sFiles = [] macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId)) for f in macPackFiles : if f.split('.')[1].lower() == 'sty' : sFiles.append(f) return sFiles def moveMacTex (self, macPackId) : '''Move the custom macro package TeX out of the freshly installed project macro package folder to the project TeX folder.''' # Collect the TeX extention files to copy for f in self.getMacTexExtFiles(macPackId) : source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f)) target = os.path.join(self.local.projTexFolder, f) self.tools.makedirs(self.local.projTexFolder) # Do not overwrite existing files if not os.path.exists(target) : shutil.copy(source, target) # Remove the source to avoid confusion if os.path.exists(target) : os.remove(source) else : self.log.writeToLog(self.errorCodes['3310'], [source,self.local.projTexFolder]) def getMacTexExtFiles (self, macPackId) : '''Return a list of macro package TeX extention files.''' tFiles = [] macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId)) for f in macPackFiles : if f.find('ext.tex') > 0 : tFiles.append(f) return tFiles def removeMacPack (self) : '''Remove the macro package from a component type''' # import pdb; pdb.set_trace() if self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] != '' : macPackId = self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] else : self.log.writeToLog(self.errorCodes['3010'], [self.cType]) # Aquire target to delete target = os.path.join(self.local.projMacroFolder, macPackId) # Remove the macPack settings from the config file if it is there try : del self.macroConfig['Macros'][macPackId] # Save the settings now self.tools.writeConfFile(self.macroConfig) self.log.writeToLog(self.errorCodes['3400'], [macPackId]) except : pass # Now remove the macro folder (with all its contents) if os.path.exists(target) : shutil.rmtree(target) self.log.writeToLog(self.errorCodes['3500'], [macPackId]) # Remove any style files associated with the macro styTarget = os.path.join(self.local.projStyleFolder, macPackId + '.sty') self.tools.makeExecutable(styTarget) self.tools.removeFile(styTarget) # Remove the reference for this macro package from the component type # that uses it. Normally that would probably be just be one of them. self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = '' self.tools.writeConfFile(self.projectConfig) def updateMacPack (self, source) : '''Update a macro package with the latest version but do not touch the config file.''' # Do not update if no macro package is registered in the projectConfig if not self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] : self.log.writeToLog(self.errorCodes['3020'], [self.cType.capitalize()]) return False macPackId = self.getMacPackIdFromSource(source) confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.xml') oldMacPackId = self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] oldMacDir = os.path.join(self.local.projMacroFolder, oldMacPackId) newMacDir = os.path.join(self.local.projMacroFolder, macPackId) # Install the new macPack (but use the old settings) if self.installMacPackOnly(source) : # The current macro system must have a "master" style file. # This is a version of the ParaText style file. Because # an update might include an update to the style file, # we need to copy that from the macro folder to the Style # folder. We will do that now. The name of the file should # be the macPackId + ".sty", in theory. srcStyleFile = os.path.join(self.local.projMacroFolder, macPackId, macPackId + '.sty') oldStyleFile = os.path.join(self.local.projStyleFolder, oldMacPackId + '.sty') newStyleFile = os.path.join(self.local.projStyleFolder, macPackId + '.sty') # Unlock the old one so it can be deleted. self.tools.makeExecutable(oldStyleFile) self.tools.removeFile(oldStyleFile) # Now copy in the new one. shutil.copy(srcStyleFile, newStyleFile) # The style file should never be edited by the user, relock it self.tools.makeReadOnly(newStyleFile) # Update exsisting extention file names. We never want to loose # any settings that the user may have added to the extention # files so we will rename the existing files to have the # new ID otherwise the system will not find them at render time. oldStyExtFile = os.path.join(self.local.projStyleFolder, oldMacPackId + '_ext.sty') oldTexExtFile = os.path.join(self.local.projTexFolder, oldMacPackId + '_ext.tex') oldTexPreStyExtFile = os.path.join(self.local.projTexFolder, oldMacPackId + '_preSty-ext.tex') newStyExtFile = os.path.join(self.local.projStyleFolder, macPackId + '_ext.sty') newTexExtFile = os.path.join(self.local.projTexFolder, macPackId + '_ext.tex') newTexPreStyExtFile = os.path.join(self.local.projTexFolder, macPackId + '_preSty-ext.tex') # By default, we protect any existing versions if os.path.exists(newStyExtFile) : os.remove(oldStyExtFile) else : self.tools.renameFile(oldStyExtFile, newStyExtFile) if os.path.exists(newTexExtFile) : os.remove(oldTexExtFile) else : self.tools.renameFile(oldTexExtFile, newTexExtFile) if os.path.exists(newTexPreStyExtFile) : os.remove(oldTexPreStyExtFile) else : self.tools.renameFile(oldTexPreStyExtFile, newTexPreStyExtFile) # Remove un-needed sty and tex files from the newMacDir to # avoid confusion. The ext files never are updated because # they could contain custom project code that we don't want # to loose in an update. for f in self.getMacStyExtFiles(macPackId) : source = os.path.join(newMacDir, f) if os.path.exists(source) : os.remove(source) for f in self.getMacTexExtFiles(macPackId) : source = os.path.join(newMacDir, f) if os.path.exists(source) : os.remove(source) # Remove the old macPack folder shutil.rmtree(oldMacDir) # Merge new settings into old section (change name to new ID) # When updating, we are assuming the new macro is in the same # family as the old one. As such, settings should be almost # identical, but in case new settings are being added, we will # merge them in now. oldConfSettings = self.macroConfig['Macros'][oldMacPackId] newConfSettings = self.tools.getXMLSettings(confXml) # Now merge newConfSettings.merge(oldConfSettings) # Inject the new section # self.tools.buildConfSection(self.macroConfig, macPackId) self.macroConfig['Macros'][macPackId] = newConfSettings.dict() # Delete the old section del self.macroConfig['Macros'][oldMacPackId] # Save the changes self.tools.writeConfFile(self.macroConfig) # Assuming everything went well we will change the macPackID on the cType self.projectConfig['CompTypes'][self.cType.capitalize()]['macroPackage'] = macPackId self.tools.writeConfFile(self.projectConfig) # Report success self.log.writeToLog(self.errorCodes['3600'], [self.cType.capitalize(), macPackId]) return True # But if the install fails everything stays the same and we report else : self.log.writeToLog(self.errorCodes['3650'], [macPackId]) return False def installMacPackOnly (self, source) : '''Install the new macro package but only that.''' # import pdb; pdb.set_trace() if self.tools.pkgExtract(source, self.local.projMacroFolder, self.local.macroConfXmlFile) : return True else : self.log.writeToLog(self.errorCodes['3200'], [self.tools.fName(source)]) return False
class ProjProcess (object) : def __init__(self, pid, gid = None, projectConfig = None) : '''Intitate the whole class and create the object.''' self.pid = pid self.tools = Tools() self.user = UserConfig() self.userConfig = self.user.userConfig if projectConfig : self.projectConfig = projectConfig else : self.proj_config = Config(self.pid) self.proj_config.getProjectConfig() self.projectConfig = self.proj_config.projectConfig self.local = ProjLocal(pid, gid, self.projectConfig) self.log = ProjLog(pid) # Log messages for this module self.errorCodes = { '0000' : ['MSG', 'Placeholder message'], 'XPRT-000' : ['MSG', 'Messages for export issues (probably only in project.py)'], 'XPRT-005' : ['MSG', 'Unassigned error message ID.'], 'XPRT-010' : ['ERR', 'Export file name could not be formed with available configuration information.'], 'XPRT-020' : ['ERR', 'Unable to export: [<<1>>].'], 'XPRT-030' : ['MSG', 'Files exported to [<<1>>].'], 'XPRT-040' : ['MSG', 'Beginning export, please wait...'], 'XPRT-050' : ['MSG', 'Unassigned error message ID.'], '1210' : ['MSG', 'Processes completed successfully on: [<<1>>] by [<<2>>]'], '1220' : ['ERR', 'Processes for [<<1>>] failed. Script [<<2>>] returned this error: [<<3>>]'], '1240' : ['MSG', 'Component group preprocessing [<<1>>] for group [<<2>>].'], '1260' : ['ERR', 'Installed the default component preprocessing script. Editing will be required for it to work with your project.'], '1265' : ['LOG', 'Component preprocessing script is already installed.'], } ############################################################################### ############################### Export Functions ############################## ############################################################################### ####################### Error Code Block Series = 0200 ######################## ############################################################################### # FIXME: This needs to be rewritten #def export (self, cType, cName, path = None, script = None, bundle = False, force = False) : #'''Facilitate the exporting of project text. It is assumed that the #text is clean and ready to go and if any extraneous publishing info #has been injected into the text, it will be removed by an appropreate #post-process that can be applied by this function. No validation #will be initiated by this function.''' ## FIXME - Todo: add post processing script feature ## Probably need to create the component object now #self.createComponent(cName) ## Figure out target path #if path : #path = self.tools.resolvePath(path) #else : #parentFolder = os.path.dirname(self.local.projHome) #path = os.path.join(parentFolder, 'Export') ## Make target folder if needed #if not os.path.isdir(path) : #os.makedirs(path) ## Start a list for one or more files we will process #fList = [] ## Will need the stylesheet for copy #projSty = self.projectConfig['Managers'][cType + '_Style']['mainStyleFile'] #projSty = os.path.join(self.local.projStyleFolder, projSty) ## Process as list of components #self.log.writeToLog('XPRT-040') #for cid in self.components[cName].getSubcomponentList(cName) : #cidCName = self.components[cName].getRapumaCName(cid) #ptName = PT_Tools(self).formPTName(cName, cid) ## Test, no name = no success #if not ptName : #self.log.writeToLog('XPRT-010') #self.tools.dieNow() #target = os.path.join(path, ptName) #source = os.path.join(self.local.projComponentFolder, cidCName, cid + '.' + cType) ## If shutil.copy() spits anything back its bad news #if shutil.copy(source, target) : #self.log.writeToLog('XPRT-020', [self.tools.fName(target)]) #else : #fList.append(target) ## Start the main process here #if bundle : #archFile = os.path.join(path, cName + '_' + self.tools.ymd() + '.zip') ## Hopefully, this is a one time operation but if force is not True, ## we will expand the file name so nothing is lost. #if not force : #if os.path.isfile(archFile) : #archFile = os.path.join(path, cName + '_' + self.tools.fullFileTimeStamp() + '.zip') #myzip = zipfile.ZipFile(archFile, 'w', zipfile.ZIP_DEFLATED) #for f in fList : ## Create a string object from the contents of the file #strObj = StringIO.StringIO() #for l in open(f, "rb") : #strObj.write(l) ## Write out string object to zip #myzip.writestr(self.tools.fName(f), strObj.getvalue()) #strObj.close() ## Close out the zip and report #myzip.close() ## Clean out the folder #for f in fList : #os.remove(f) #self.log.writeToLog('XPRT-030', [self.tools.fName(archFile)]) #else : #self.log.writeToLog('XPRT-030', [path]) #return True ############################################################################### ########################## Text Processing Functions ########################## ############################################################################### ######################## Error Code Block Series = 1200 ####################### ############################################################################### def turnOnOffPreprocess (self, gid, onOff) : '''Turn on or off preprocessing on incoming component text.''' self.projectConfig['Groups'][gid]['usePreprocessScript'] = onOff self.tools.writeConfFile(self.projectConfig) self.log.writeToLog(self.errorCodes['1240'], [str(onOff), gid]) def checkForPreprocessScript (self, gid) : '''Check to see if a preprocess script is installed. If not, install the default script and give a warning that the script is not complete.''' # First make sure the Scripts folder is there if not os.path.isdir(self.local.projScriptFolder) : os.makedirs(self.local.projScriptFolder) # Check and copy if needed if not os.path.isfile(self.local.groupPreprocessFile) : shutil.copy(self.local.rpmPreprocessFile, self.local.groupPreprocessFile) self.tools.makeExecutable(self.local.groupPreprocessFile) self.log.writeToLog(self.errorCodes['1260']) else : self.log.writeToLog(self.errorCodes['1265']) def runProcessScript (self, target, scriptFile) : '''Run a text processing script on a component. This assumes the component and the script are valid and the component lock is turned off. If not, you cannot expect any good to come of this.''' # subprocess will fail if permissions are not set on the # script we want to run. The correct permission should have # been set when we did the installation. err = subprocess.call([scriptFile, target]) if err == 0 : self.log.writeToLog(self.errorCodes['1210'], [self.tools.fName(target), self.tools.fName(scriptFile)]) else : self.log.writeToLog(self.errorCodes['1220'], [self.tools.fName(target), self.tools.fName(scriptFile), str(err)]) return False return True def scriptInstall (self, source, target) : '''Install a script. A script can be a collection of items in a zip file or a single .py script file.''' scriptTargetFolder, fileName = os.path.split(target) if self.tools.isExecutable(source) : shutil.copy(source, target) self.tools.makeExecutable(target) elif self.tools.fName(source).split('.')[1].lower() == 'zip' : myZip = zipfile.ZipFile(source, 'r') for f in myZip.namelist() : data = myZip.read(f, source) # Pretty sure zip represents directory separator char as "/" regardless of OS myPath = os.path.join(scriptTargetFolder, f.split("/")[-1]) try : myFile = open(myPath, "wb") myFile.write(data) myFile.close() except : pass myZip.close() return True else : self.tools.dieNow('Script is an unrecognized type: ' + self.tools.fName(source) + ' Cannot continue with installation.') def installPostProcess (self, cType, script, force = None) : '''Install a post process script into the main components processing folder for a specified component type. This script will be run on every file of that type that is imported into the project. Some projects will have their own specially developed post process script. Use the "script" var to specify a process (which should be bundled in a system compatable way). If "script" is not specified we will copy in a default script that the user can modify. This is currently limited to Python scripts only which do in-place processes on the target files. The script needs to have the same name as the zip file it is bundled in, except the extention is .py instead of the bundle .zip extention.''' # Define some internal vars Ctype = cType.capitalize() oldScript = '' scriptName = os.path.split(script)[1] scriptSourceFolder = os.path.split(script)[0] scriptTarget = os.path.join(self.local.projScriptFolder, self.tools.fName(script).split('.')[0] + '.py') if scriptName in self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] : oldScript = scriptName # First check for prexsisting script record if not force : if oldScript : self.log.writeToLog('POST-080', [oldScript]) return False # In case this is a new project we may need to install a component # type and make a process (components) folder if not self.components[cType] : self.tools.addComponentType(self.projectConfig, self.local, cType) # Make the target folder if needed if not os.path.isdir(self.local.projScriptFolder) : os.makedirs(self.local.projScriptFolder) # First check to see if there already is a script file, return if there is if os.path.isfile(scriptTarget) and not force : self.log.writeToLog('POST-082', [self.tools.fName(scriptTarget)]) return False # No script found, we can proceed if not os.path.isfile(scriptTarget) : self.scriptInstall(script, scriptTarget) if not os.path.isfile(scriptTarget) : self.tools.dieNow('Failed to install script!: ' + self.tools.fName(scriptTarget)) self.log.writeToLog('POST-110', [self.tools.fName(scriptTarget)]) elif force : self.scriptInstall(script, scriptTarget) if not os.path.isfile(scriptTarget) : self.tools.dieNow('Failed to install script!: ' + self.tools.fName(scriptTarget)) self.log.writeToLog('POST-115', [self.tools.fName(scriptTarget)]) # Record the script with the cType post process scripts list scriptList = self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] if self.tools.fName(scriptTarget) not in scriptList : self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] = self.tools.addToList(scriptList, self.tools.fName(scriptTarget)) self.tools.writeConfFile(self.projectConfig) return True def removePostProcess (self, cType) : '''Remove (actually disconnect) a preprocess script from a component type. This will not actually remove the script. That would need to be done manually. Rather, this will remove the script name entry from the component type so the process cannot be accessed for this specific component type.''' Ctype = cType.capitalize() # Get old setting old = self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] # Reset the field to '' if old != '' : self.projectConfig['CompTypes'][Ctype]['postprocessScripts'] = '' self.tools.writeConfFile(self.projectConfig) self.log.writeToLog('POST-130', [old,Ctype]) else : self.log.writeToLog('POST-135', [cType.capitalize()]) return True
class Macro(object): def __init__(self, pid, cType, gid=None): """Do the primary initialization for this class.""" self.pid = pid self.gid = gid self.cType = cType self.user = UserConfig() self.userConfig = self.user.userConfig self.projHome = os.path.join(os.path.expanduser(self.userConfig["Resources"]["projects"]), self.pid) self.local = ProjLocal(pid, gid, cType) # import pdb; pdb.set_trace() self.proj_config = Config(pid) self.proj_config.getProjectConfig() self.projectConfig = self.proj_config.projectConfig self.layoutConfig = self.proj_config.layoutConfig self.tools = Tools() self.log = ProjLog(pid) # Create config placeholders self.layoutConfig = None self.illustrationConfig = None self.macroConfig = self.tools.loadConfig(self.local.macroConfFile, self.local.macroConfXmlFile) # Log messages for this module self.errorCodes = { "3010": ["ERR", "No macro package is registered for the [<<1>>] component type."], "3020": ["ERR", "Cannot update! No macro package is registered for the [<<1>>] component type."], "3050": ["ERR", "Macro package file not found: [<<1>>]"], "3100": [ "ERR", "Macro package: [<<1>>] already exists in the project. I am not allowed to copy over an existing package.", ], "3200": ["ERR", "Failed to install macro package: [<<1>>]"], "3300": ["MSG", "Installed macro package: [<<1>>], Reinitialized [<<2>>]"], "3310": ["ERR", "Failed to copy [<<1>>] to folder [<<2>>]."], "3400": ["MSG", "Removed macro package configuration settings for: [<<1>>] from the macro.conf file."], "3500": ["MSG", "Removed macro package [<<1>>] folder and all files contained."], "3600": ["MSG", "Updated component type [<<1>>] with macro package [<<2>>]"], "3650": ["ERR", "Failed to updated macro package [<<1>>]"], } ############################################################################### ###################### Macro Package Handling Functions ####################### ############################################################################### ######################## Error Code Block Series = 3000 ####################### ############################################################################### def createMacroFiles(self, macPackId): """Create all the necessary macro file names with their assigned paths.""" self.projMacPackFolder = os.path.join(self.local.projMacPackFolder, macPackId) texFileIds = { "preStyTexExtFile": "preSty-ext.tex", "macSettingsFile": "settings.tex", "extTexFile": "extension.tex", "grpExtTexFile": self.gid + "-extension.tex", "": "", "": "", "": "", "": "", "": "", } styFileIds = { "glbExtStyFile": "extension.sty", "grpExtStyFile": self.gid + "-extension.sty", "": "", "": "", "": "", "": "", "": "", } # <file> # <name>TeX lccode Definition File</name> # <description>The TeX file that contains lccode definitions and is linked with the hypenation exclusions file.</description> # <fileID>lccodeTexFile</fileID> # <fileName>[self:gid]-lccode.tex</fileName> # <filePath>[self:projGidFolder]</filePath> # <depends></depends> # <relies></relies> # <note>This file is located in the component group folder to allow more segregated processing.</note> # </file> # <file> # <name>TeX Group Hyphenation Exclusions File</name> # <description>The file that contains the hypenation words exclusions list for the current group that TeX will use to render the text.</description> # <fileID>grpHyphExcTexFile</fileID> # <fileName>[self:gid]-hyphenation.tex</fileName> # <filePath>[self:projGidFolder]</filePath> # <depends></depends> # <relies></relies> # <note>This file is located in the component group folder to allow more segregated processing.</note> # </file> def getMacPackIdFromFileName(self, fileName): """Return the macPack ID based on the file name""" # File name less ext is the ID parts = len(fileName.split(".")) return ".".join(fileName.split(".")[: parts - 1]) def getMacPackIdFromSource(self, source): """Return the macPack ID based on the complete path and file name.""" # Get the file name from the path fileName = self.tools.fName(source) # Return the ID return self.getMacPackIdFromFileName(fileName) def addMacPack(self, source): """Add a macro package to the project. It will not work if the same package is already present. Remove must be used to get rid of the existing one first.""" # import pdb; pdb.set_trace() macPackId = self.getMacPackIdFromSource(source) confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + ".xml") if not os.path.isfile(source): self.log.writeToLog(self.errorCodes["3050"], [source]) # Do not add/install if there seems to be a macro package there already if self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] and os.path.exists( self.local.macroConfFile ): self.log.writeToLog(self.errorCodes["3100"], [macPackId]) return False # Set the projectConf to the new/same package self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] = macPackId self.tools.writeConfFile(self.projectConfig) # If we got this far, install the a fresh copy of the macPack self.installMacPackOnly(source) # Move the style files and custom TeX files out of the macPack self.moveMacStyles(macPackId) self.moveMacTex(macPackId) # Create a fresh macro.conf file if it dosn't exist if not os.path.isfile(self.local.macroConfFile): self.macroConfig = self.tools.initNewConfig(self.local.macroConfFile, self.local.macroConfXmlFile) # Inject information from this particular macro package mInfo = self.tools.getXMLSettings(confXml) self.macroConfig["Macros"][macPackId] = mInfo.dict() # Save the settings now self.tools.writeConfFile(self.macroConfig) self.log.writeToLog(self.errorCodes["3300"], [macPackId, self.local.macroConfFileName]) return True def moveMacStyles(self, macPackId): """Move the default macro package styles out of the freshly installed project macro package folder to the project Style folder.""" # import pdb; pdb.set_trace() # Collect the style files to copy for f in self.getMacStyExtFiles(macPackId): source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f)) target = os.path.join(self.local.projStyleFolder, f) self.tools.makedirs(self.local.projStyleFolder) # Do not overwrite existing files unless force is used if not os.path.exists(target): shutil.copy(source, target) # Look for default and set to read-only defaultStyFile = os.path.join(self.local.projStyleFolder, macPackId + ".sty") if target == defaultStyFile: self.tools.makeReadOnly(defaultStyFile) # Remove the source to avoid confusion if os.path.exists(target): os.remove(source) else: self.log.writeToLog(self.errorCodes["3310"], [source, self.local.projStyleFolder]) def getMacStyExtFiles(self, macPackId): """Return a list of macro package style extention files.""" sFiles = [] macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId)) for f in macPackFiles: if f.split(".")[1].lower() == "sty": sFiles.append(f) return sFiles def moveMacTex(self, macPackId): """Move the custom macro package TeX out of the freshly installed project macro package folder to the project TeX folder.""" # Collect the TeX extention files to copy for f in self.getMacTexExtFiles(macPackId): source = os.path.join(os.path.join(self.local.projMacroFolder, macPackId, f)) target = os.path.join(self.local.projTexFolder, f) self.tools.makedirs(self.local.projTexFolder) # Do not overwrite existing files if not os.path.exists(target): shutil.copy(source, target) # Remove the source to avoid confusion if os.path.exists(target): os.remove(source) else: self.log.writeToLog(self.errorCodes["3310"], [source, self.local.projTexFolder]) def getMacTexExtFiles(self, macPackId): """Return a list of macro package TeX extention files.""" tFiles = [] macPackFiles = os.listdir(os.path.join(self.local.projMacroFolder, macPackId)) for f in macPackFiles: if f.find("ext.tex") > 0: tFiles.append(f) return tFiles def removeMacPack(self): """Remove the macro package from a component type""" # import pdb; pdb.set_trace() if self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] != "": macPackId = self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] else: self.log.writeToLog(self.errorCodes["3010"], [self.cType]) # Aquire target to delete target = os.path.join(self.local.projMacroFolder, macPackId) # Remove the macPack settings from the config file if it is there try: del self.macroConfig["Macros"][macPackId] # Save the settings now self.tools.writeConfFile(self.macroConfig) self.log.writeToLog(self.errorCodes["3400"], [macPackId]) except: pass # Now remove the macro folder (with all its contents) if os.path.exists(target): shutil.rmtree(target) self.log.writeToLog(self.errorCodes["3500"], [macPackId]) # Remove any style files associated with the macro styTarget = os.path.join(self.local.projStyleFolder, macPackId + ".sty") self.tools.makeExecutable(styTarget) self.tools.removeFile(styTarget) # Remove the reference for this macro package from the component type # that uses it. Normally that would probably be just be one of them. self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] = "" self.tools.writeConfFile(self.projectConfig) def updateMacPack(self, source): """Update a macro package with the latest version but do not touch the config file.""" # Do not update if no macro package is registered in the projectConfig if not self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"]: self.log.writeToLog(self.errorCodes["3020"], [self.cType.capitalize()]) return False macPackId = self.getMacPackIdFromSource(source) confXml = os.path.join(self.local.projMacroFolder, macPackId, macPackId + ".xml") oldMacPackId = self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] oldMacDir = os.path.join(self.local.projMacroFolder, oldMacPackId) newMacDir = os.path.join(self.local.projMacroFolder, macPackId) # Install the new macPack (but use the old settings) if self.installMacPackOnly(source): # The current macro system must have a "master" style file. # This is a version of the ParaText style file. Because # an update might include an update to the style file, # we need to copy that from the macro folder to the Style # folder. We will do that now. The name of the file should # be the macPackId + ".sty", in theory. srcStyleFile = os.path.join(self.local.projMacroFolder, macPackId, macPackId + ".sty") oldStyleFile = os.path.join(self.local.projStyleFolder, oldMacPackId + ".sty") newStyleFile = os.path.join(self.local.projStyleFolder, macPackId + ".sty") # Unlock the old one so it can be deleted. self.tools.makeExecutable(oldStyleFile) self.tools.removeFile(oldStyleFile) # Now copy in the new one. shutil.copy(srcStyleFile, newStyleFile) # The style file should never be edited by the user, relock it self.tools.makeReadOnly(newStyleFile) # Update exsisting extention file names. We never want to loose # any settings that the user may have added to the extention # files so we will rename the existing files to have the # new ID otherwise the system will not find them at render time. oldStyExtFile = os.path.join(self.local.projStyleFolder, oldMacPackId + "_ext.sty") oldTexExtFile = os.path.join(self.local.projTexFolder, oldMacPackId + "_ext.tex") oldTexPreStyExtFile = os.path.join(self.local.projTexFolder, oldMacPackId + "_preSty-ext.tex") newStyExtFile = os.path.join(self.local.projStyleFolder, macPackId + "_ext.sty") newTexExtFile = os.path.join(self.local.projTexFolder, macPackId + "_ext.tex") newTexPreStyExtFile = os.path.join(self.local.projTexFolder, macPackId + "_preSty-ext.tex") # By default, we protect any existing versions if os.path.exists(newStyExtFile): os.remove(oldStyExtFile) else: self.tools.renameFile(oldStyExtFile, newStyExtFile) if os.path.exists(newTexExtFile): os.remove(oldTexExtFile) else: self.tools.renameFile(oldTexExtFile, newTexExtFile) if os.path.exists(newTexPreStyExtFile): os.remove(oldTexPreStyExtFile) else: self.tools.renameFile(oldTexPreStyExtFile, newTexPreStyExtFile) # Remove un-needed sty and tex files from the newMacDir to # avoid confusion. The ext files never are updated because # they could contain custom project code that we don't want # to loose in an update. for f in self.getMacStyExtFiles(macPackId): source = os.path.join(newMacDir, f) if os.path.exists(source): os.remove(source) for f in self.getMacTexExtFiles(macPackId): source = os.path.join(newMacDir, f) if os.path.exists(source): os.remove(source) # Remove the old macPack folder shutil.rmtree(oldMacDir) # Merge new settings into old section (change name to new ID) # When updating, we are assuming the new macro is in the same # family as the old one. As such, settings should be almost # identical, but in case new settings are being added, we will # merge them in now. oldConfSettings = self.macroConfig["Macros"][oldMacPackId] newConfSettings = self.tools.getXMLSettings(confXml) # Now merge newConfSettings.merge(oldConfSettings) # Inject the new section # self.tools.buildConfSection(self.macroConfig, macPackId) self.macroConfig["Macros"][macPackId] = newConfSettings.dict() # Delete the old section del self.macroConfig["Macros"][oldMacPackId] # Save the changes self.tools.writeConfFile(self.macroConfig) # Assuming everything went well we will change the macPackID on the cType self.projectConfig["CompTypes"][self.cType.capitalize()]["macroPackage"] = macPackId self.tools.writeConfFile(self.projectConfig) # Report success self.log.writeToLog(self.errorCodes["3600"], [self.cType.capitalize(), macPackId]) return True # But if the install fails everything stays the same and we report else: self.log.writeToLog(self.errorCodes["3650"], [macPackId]) return False def installMacPackOnly(self, source): """Install the new macro package but only that.""" # import pdb; pdb.set_trace() if self.tools.pkgExtract(source, self.local.projMacroFolder, self.local.macroConfXmlFile): return True else: self.log.writeToLog(self.errorCodes["3200"], [self.tools.fName(source)]) return False