diff --git a/package-mac/ebi-package.command b/package-mac/ebi-package.command
old mode 100644
new mode 100755
index e03ccbb..878dc08
--- a/package-mac/ebi-package.command
+++ b/package-mac/ebi-package.command
@@ -11,9 +11,8 @@ cp source/ebi/gtk-help.png dist/ebi.app/Contents/Resources/lib/.
cp source/ebi/gtk-stop.png dist/ebi.app/Contents/Resources/lib/.
cp source/ebi/options.png dist/ebi.app/Contents/Resources/lib/.
cp source/ebi/pause.png dist/ebi.app/Content
-frjikjhjkfdf021s/Resources/lib/.
cp ebi.command dist/ebi.app/Contents/MacOS/.
cd dist
hdiutil create -srcfolder ebi.app ebi.dmg
-hdiutil internet-enable -yes ebi.dmg
\ No newline at end of file
+hdiutil internet-enable -yes ebi.dmg
diff --git a/package-mac/ebi.command b/package-mac/ebi.command
old mode 100644
new mode 100755
diff --git a/package-mac/source/ebi/Engine.py b/package-mac/source/ebi/Engine.py
new file mode 100644
index 0000000..1f738e9
--- /dev/null
+++ b/package-mac/source/ebi/Engine.py
@@ -0,0 +1,3508 @@
+# Engine.py
+#
+# Author: Jim Kurian, Pearson plc.
+# Date: October 2014
+#
+# The CSV processing engine of the EQUELLA Bulk Importer. Loads a
+# CSV file and iterates through the rows creating items in EQUELLA
+# for each one. Utilizes equellaclient41.py for EQUELLA
+# communications. Invoked by Mainframe.py.
+
+from xml.dom import Node
+from equellaclient41 import *
+import time, datetime
+import zipfile, csv, codecs, cStringIO
+import sys, platform
+import traceback
+import random
+import os
+import zipfile, glob, time, getpass, uuid
+import wx
+
+class Engine():
+ def __init__(self, owner, Version, Copyright):
+ # constants
+ self.METADATA = 'Metadata'
+ self.ATTACHMENTLOCATIONS = 'Attachment Locations'
+ self.ATTACHMENTNAMES = 'Attachment Names'
+ self.CUSTOMATTACHMENTS = 'Custom Attachments'
+ self.RAWFILES = 'Raw Files'
+ self.URLS = 'URLs'
+ self.HYPERLINKNAMES = 'Hyperlink Names'
+ self.EQUELLARESOURCES = 'EQUELLA Resources'
+ self.EQUELLARESOURCENAMES = 'EQUELLA Resource Names'
+ self.COMMANDS = 'Commands'
+ self.TARGETIDENTIFIER = 'Target Identifier'
+ self.TARGETVERSION = 'Target Version'
+ self.COLLECTION = 'Collection'
+ self.OWNER = 'Owner'
+ self.COLLABORATORS = 'Collaborators'
+ self.ITEMID = 'Item ID'
+ self.ITEMVERSION = 'Item Version'
+ self.ROWERROR = 'Row Error'
+ self.THUMBNAILS = "Thumbnails"
+ self.SELECTEDTHUMBNAIL = "Selected Thumbnail"
+ self.IGNORE = 'Ignore'
+
+ self.COLUMN_POS = "Pos"
+ self.COLUMN_HEADING = "Column Heading"
+ self.COLUMN_DATATYPE = "Column Data Type"
+ self.COLUMN_DISPLAY = "Display"
+ self.COLUMN_SOURCEIDENTIFIER = "Source Identifier"
+ self.COLUMN_XMLFRAGMENT = "XML Fragment"
+ self.COLUMN_DELIMITER = "Delimiter"
+
+ self.CLEARMETA = 0
+ self.REPLACEMETA = 1
+ self.APPENDMETA = 2
+
+ self.OVERWRITENONE = 0
+ self.OVERWRITEEXISTING = 1
+ self.OVERWRITEALL = 2
+
+ self.pause = False
+ self.owner = owner
+
+ # default settings (can be overridden in ebi.properties)
+ self.debug = False
+ self.attachmentMetadataTargets = True
+ self.defaultChunkSize = (1024 * 2048)
+ self.chunkSize = self.defaultChunkSize
+ self.networkLogging = False
+ self.scormformatsupport = True
+
+ self.copyright = Copyright
+ self.rowFilter = ""
+ self.logfilesfolder = "logs"
+ self.logfilespath = ""
+ self.testItemfolder = "test_output"
+ self.receiptFolder = "receipts"
+ self.sessionName = ""
+ self.maxRetry = 5
+
+ # welcome message for command prompt and log files
+ self.welcomeLine1 = "EQUELLA Bulk Importer [EBI %s, %s]" % (Version, self.getPlatform())
+ self.welcomeLine2 = self.copyright + "\n"
+
+ print self.welcomeLine1
+ print self.welcomeLine2
+
+ # CSV and connection settings
+ self.institutionUrl = ""
+ self.username = ""
+ self.password = ""
+ self.collection = ""
+ self.csvFilePath = ""
+
+ # Options
+ self.proxy = ""
+ self.proxyUsername = ""
+ self.proxyPassword = ""
+ self.encoding = "utf8"
+ self.saveTestXML = False
+ self.saveAsDraft = False
+ self.saveTestXml = False
+ self.existingMetadataMode = self.CLEARMETA
+ self.appendAttachments = False
+ self.createNewVersions = False
+ self.useEBIUsername = False
+ self.ignoreNonexistentCollaborators = False
+ self.saveNonexistentUsernamesAsIDs = True
+ self.attachmentsBasepath = ""
+ self.absoluteAttachmentsBasepath = ""
+ self.export = False
+ self.includeNonLive = False
+ self.overwriteMode = self.OVERWRITENONE
+ self.whereClause = ""
+ self.startScript = ""
+ self.endScript = ""
+ self.preScript = ""
+ self.postScript = ""
+
+ self.ebiScriptObject = EbiScriptObject(self)
+
+ # data structures to store column settings
+ self.currentColumns = []
+ self.csvArray = []
+
+ self.successCount = 0
+ self.errorCount = 0
+
+
+ # enum for attachment types
+ self.attachmentTypeFile = 0
+ self.attachmentTypeZip = 1
+ self.attachmentTypeIMS = 2
+ self.attachmentTypeSCORM = 3
+
+ self.columnHeadings = []
+ self.StopProcessing = False
+ self.processingStoppedByScript = False
+ self.Skip = False
+ self.logFileName = ""
+ self.collectionIDs = {}
+
+ self.itemSystemNodes = [
+ "staging",
+ "name",
+ "description",
+ "itemdefid",
+ "datecreated",
+ "datemodified",
+ "dateforindex",
+ "owner",
+ "collaborativeowners",
+ "rating",
+ "badurls",
+ "moderation",
+ "newitem",
+ "attachments",
+ "navigationnodes",
+ "url",
+ "history",
+ "thumbnail"
+ ]
+ self.sourceIdentifierReceipts = {}
+ self.exportedFiles = []
+ self.eqVersionmm = ""
+ self.eqVersionmmr = ""
+ self.eqVersionDisplay = ""
+
+ def getPlatform(self):
+ ebiPlatform = "Python " + platform.python_version()
+ system = platform.system()
+ if system == "Windows":
+ ebiPlatform += ", Windows " + platform.release()
+ elif system == "Darwin":
+ ebiPlatform += ", Mac OS " + platform.mac_ver()[0]
+ elif system == "Linux":
+ ebiPlatform += ", " + platform.linux_distribution()[0] + " " + platform.linux_distribution()[1]
+ return ebiPlatform
+
+ def setDebug(self, debug):
+ self.debug = debug
+ if self.debug:
+ self.echo("debug = True")
+
+ def setLog(self, log):
+ self.log = log
+ self.log.AddLogText(self.welcomeLine1 + "\n", 1)
+ self.log.AddLogText(self.welcomeLine2 + "\n", 1)
+
+ def echo(self, entry, display = True, log = True, style = 0):
+
+ if log and self.logFileName != "":
+
+ # create/open log file
+ logfile = open(os.path.join(self.logfilespath, self.logFileName),"a")
+
+ # write entry
+ logfile.writelines(entry.encode(self.encoding) + "\n")
+
+ logfile.close()
+
+ if display:
+ self.log.AddLogText(entry.encode(self.encoding) + "\n", style)
+ print entry.encode(self.encoding)
+ return
+
+ def translateError(self, rawError, context = ""):
+
+ rawError = str(rawError)
+
+ # check if it is a SOAP error
+ if rawError.rfind('') != -1:
+ # Extract faultstring from 500 code and display/log
+ rawError = rawError[rawError.find('faultstring') + 12:rawError.rfind(' ")
+ # iterate through columns and check for validity
+ for n, columnHeading in enumerate(self.columnHeadings):
+ if self.currentColumns[n][self.COLUMN_DATATYPE] == self.METADATA:
+ if columnHeading == "":
+ raise Exception, "Blank column heading found on column requiring XPath '%s' (column %s)" % (columnHeading, n + 1)
+
+ if self.currentColumns[n][self.COLUMN_DATATYPE] == self.METADATA or \
+ (self.currentColumns[n][self.COLUMN_DATATYPE] in [self.ATTACHMENTLOCATIONS, self.URLS, self.EQUELLARESOURCES, self.CUSTOMATTACHMENTS] and \
+ columnHeading.strip() != "" and columnHeading.strip()[0] != "#"):
+ try:
+ # test xpath
+ testPB.validateXpath(columnHeading.strip())
+ except:
+ if self.debug:
+ raise
+ else:
+ exceptionValue = sys.exc_info()[1]
+ scriptErrorMsg = "Invalid column heading '%s' (column %s). %s" % (columnHeading, n + 1, exceptionValue)
+ raise Exception, scriptErrorMsg
+
+ # warn the user if any XPaths are attempting to overwrite system nodes
+ xpathParts = columnHeading.split("/")
+ if columnHeading.strip() == "item" or len(xpathParts) > 1:
+ if columnHeading.strip() == "item" or (xpathParts[0].strip() == "item" and xpathParts[1].strip() in self.itemSystemNodes):
+ self.echo("WARNING: XPath '%s' in column %s is writing to a system node" % (columnHeading, n + 1))
+
+ def getEquellaVersion(self):
+ # download and read version.properties
+ try:
+ versionUrl = self.institutionUrl + "/version.properties"
+ versionProperties = ""
+ versionProperties = self.tle.getText(versionUrl)
+ vpLines = versionProperties.split("\n")
+
+ for line in vpLines:
+ line = line.strip()
+ lineparts = line.split("=")
+ if lineparts[0] == "version.mm":
+ self.eqVersionmm = lineparts[1]
+ if lineparts[0] == "version.mmr":
+ self.eqVersionmmr = lineparts[1]
+ if lineparts[0] == "version.display":
+ self.eqVersionDisplay = lineparts[1]
+
+ except:
+ if self.debug:
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ self.echo(''.join(traceback.format_exception(exceptionType, exceptionValue, exceptionTraceback)))
+
+ def getContributableCollections(self):
+ try:
+ # connect to EQUELLA
+ self.tle = TLEClient(self, self.institutionUrl, self.username, self.password, self.proxy, self.proxyUsername, self.proxyPassword, self.debug)
+ self.getEquellaVersion()
+ except:
+ if self.debug:
+ raise
+ else:
+ raise Exception, self.translateError(str(sys.exc_info()[1]), "login")
+
+ try:
+ # get all contributable collections and their IDs
+ itemDefs = self.tle._enumerateItemDefs()
+ self.collectionIDs.clear()
+ for key, value in itemDefs.items():
+ self.collectionIDs[key] = value["uuid"]
+
+ self.tle.logout()
+
+ # return collection names (sorted)
+ return sorted(self.collectionIDs.keys())
+ except:
+ if self.debug:
+ raise
+ else:
+ raise Exception, self.translateError(str(sys.exc_info()[1]))
+
+ # find index of first matching instance of value, return -1 if not found
+ def lookupColumnIndex(self, columnProperty, value):
+ for index, column in enumerate(self.currentColumns):
+ if column[columnProperty] == value:
+
+ # value found
+ return index
+
+ # value not found
+ return -1
+
+ def tryPausing(self, message, newline = False):
+ progress = message
+ if self.pause:
+ self.log.Enable()
+ # add message to log
+ self.log.SetReadOnly(False)
+ if newline:
+ self.log.AppendText("\n")
+ self.log.AppendText(progress)
+ self.log.SetReadOnly(True)
+ self.log.GotoPos(self.log.GetLength())
+ statusOriginalText = self.owner.mainStatusBar.GetStatusText(0)
+ self.owner.mainStatusBar.SetStatusText("PAUSED...", 2)
+
+ # pause loop
+ count = 0
+ while self.pause:
+ wx.GetApp().Yield()
+ time.sleep(0.5)
+ if count == 0:
+ self.owner.mainStatusBar.SetStatusText("PAUSED.", 2)
+ count = 1
+ elif count == 1:
+ self.owner.mainStatusBar.SetStatusText("PAUSED..", 2)
+ count = 2
+ else:
+ self.owner.mainStatusBar.SetStatusText("PAUSED...", 2)
+ count = 0
+
+
+ # remove message
+ if message != "":
+ self.log.DocumentEnd()
+ self.log.SetReadOnly(False)
+ for i in range(len(progress)):
+ self.log.DeleteBack()
+ if newline:
+ self.log.DeleteBack()
+ self.log.SetReadOnly(True)
+ self.log.Disable()
+ self.owner.mainStatusBar.SetStatusText("", 2)
+
+ def runImport(self, owner, testOnly=False):
+ self.StopProcessing = False
+ self.pause = False
+ self.processingStoppedByScript = False
+
+ try:
+ # try opening csv file
+ if owner.txtCSVPath.GetValue() != "" and not os.path.isdir(self.csvFilePath):
+ f = open(self.csvFilePath, "rb")
+ f.close()
+ except:
+ owner.mainStatusBar.SetStatusText("Processing halted due to an error", 0)
+ raise Exception, "CSV file could not be opened, check path: %s" % self.csvFilePath
+
+ # specify folders
+ self.logfilespath = os.path.join(os.path.dirname(self.csvFilePath), self.logfilesfolder)
+ self.testItemfolder = os.path.join(os.path.dirname(self.csvFilePath), self.testItemfolder)
+ self.receiptFolder = os.path.join(os.path.dirname(self.csvFilePath), self.receiptFolder)
+
+ # specify log file name for this run
+ if not os.path.exists(self.logfilespath):
+ os.makedirs(self.logfilespath)
+ self.sessionName = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
+ self.logFileName = self.sessionName + '.txt'
+
+ self.echo(self.welcomeLine1, False)
+ self.echo(self.welcomeLine2, False)
+ if self.debug:
+ self.echo("Debug mode on\n", False)
+
+ # create objects for EBI scripts
+ self.logger = Logger(self)
+ self.process = Process(self)
+
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Opening a connection to EQUELLA at %s..." % self.institutionUrl)
+
+ try:
+
+ # set stats counters
+ self.successCount = 0
+ self.errorCount = 0
+
+ # connect to EQUELLA
+ owner.mainStatusBar.SetStatusText("Connecting...", 0)
+ wx.GetApp().Yield()
+ self.tle = TLEClient(self, self.institutionUrl, self.username, self.password, self.proxy, self.proxyUsername, self.proxyPassword, self.debug)
+
+ # get EQUELLA version
+ if self.eqVersionmm == "":
+ self.getEquellaVersion()
+ versionDisplay = ""
+ if self.eqVersionDisplay != "":
+ versionDisplay = " (%s)" % self.eqVersionDisplay
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Successfully connected to EQUELLA%s" % versionDisplay)
+
+ # Get Collection UUID
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Target collection: '" + self.collection + "'...")
+
+ # if not previously retreieved get all contributable collections and their IDs
+ if len(self.collectionIDs) == 0:
+ itemDefs = self.tle._enumerateItemDefs()
+ for key, value in itemDefs.items():
+ self.collectionIDs[key] = value["uuid"]
+
+ # get ID of selected collection
+ if self.collection in self.collectionIDs.keys():
+ itemdefuuid = self.collectionIDs[self.collection]
+ else:
+ raise Exception, "Collection '" + self.collection + "'" + " not found"
+
+ try:
+ if not os.path.isdir(self.csvFilePath):
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Parsing CSV file (" + self.csvFilePath + ")...")
+ else:
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "WARNING: No CSV specified. CSV path is " + self.csvFilePath)
+
+ if not owner.verifyCurrentColumnsMatchCSV():
+ raise Exception, 'CSV headings do not match the settings, update the settings to match the CSV column headings'
+
+ # determine the column indexes for the following column types
+ sourceIdentifierColumn = self.lookupColumnIndex(self.COLUMN_SOURCEIDENTIFIER, "YES")
+ targetIdentifierColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.TARGETIDENTIFIER)
+ targetVersionColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.TARGETVERSION)
+ commandOptionsColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.COMMANDS)
+ attachmentLocationsColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.ATTACHMENTLOCATIONS)
+ customAttachmentsColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.CUSTOMATTACHMENTS)
+ urlsColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.URLS)
+ resourcesColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.EQUELLARESOURCES)
+ itemIdColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.ITEMID)
+ versionColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.ITEMVERSION)
+ thumbnailsColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.THUMBNAILS)
+ selectedThumbnailColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.SELECTEDTHUMBNAIL)
+ rowErrorColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.ROWERROR)
+ collectionColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.COLLECTION)
+ ownerColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.OWNER)
+ collaboratorsColumn = self.lookupColumnIndex(self.COLUMN_DATATYPE, self.COLLABORATORS)
+
+ # ignore Source Identifier if column datatype is set to Ignore
+ if sourceIdentifierColumn != -1 and self.currentColumns[sourceIdentifierColumn][self.COLUMN_DATATYPE] == self.IGNORE:
+ sourceIdentifierColumn = -1
+
+ # parse CSV
+ self.csvParse(owner,
+ self.tle,
+ itemdefuuid,
+ testOnly,
+ sourceIdentifierColumn,
+ targetIdentifierColumn,
+ targetVersionColumn,
+ commandOptionsColumn,
+ attachmentLocationsColumn,
+ urlsColumn,
+ resourcesColumn,
+ customAttachmentsColumn,
+ itemIdColumn,
+ versionColumn,
+ rowErrorColumn,
+ collectionColumn,
+ thumbnailsColumn,
+ selectedThumbnailColumn,
+ ownerColumn,
+ collaboratorsColumn)
+
+ except:
+ owner.mainStatusBar.SetStatusText("Processing halted due to an error", 0)
+
+ err = str(sys.exc_info()[1])
+ exact_error = err
+
+ errorString = ""
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ if self.debug:
+ errorString = "\n" + ''.join(traceback.format_exception(exceptionType, exceptionValue, exceptionTraceback))
+
+ # check if it is a SOAP error
+ if err.rfind('') != -1:
+ # Extract faultstring from 500 code and display/log
+ exact_error = err[err.find('faultstring') + 12:err.rfind('') != -1:
+ # Extract faultstring from 500 code and display/log
+ exact_error = err[err.find('faultstring') + 12:err.rfind(' 0:
+ break
+ else:
+ del self.csvArray[-1]
+
+ # if CSV file is empty or non-existent populate the first row with column headings from settings
+ if len(self.csvArray) == 0:
+ self.csvArray.append([])
+ for columnHeading in self.columnHeadings:
+ self.csvArray[0].append(columnHeading)
+
+ def csvParse(self,
+ owner,
+ tle,
+ itemdefuuid,
+ testOnly,
+ sourceIdentifierColumn,
+ targetIdentifierColumn,
+ targetVersionColumn,
+ commandOptionsColumn,
+ attachmentLocationsColumn,
+ urlsColumn,
+ resourcesColumn,
+ customAttachmentsColumn,
+ itemIdColumn,
+ versionColumn,
+ rowErrorColumn,
+ collectionColumn,
+ thumbnailsColumn,
+ selectedThumbnailColumn,
+ ownerColumn,
+ collaboratorsColumn):
+
+ # if real form receipt filename and run check if receipts file is editable
+ receiptFilename = ""
+ if not self.export:
+ if itemIdColumn != -1:
+ # form receipts filename
+ if owner.txtCSVPath.GetValue() != "" and not os.path.isdir(self.csvFilePath):
+ receiptFilename = os.path.join(self.receiptFolder, os.path.basename(self.csvFilePath))
+ else:
+ receiptFilename = os.path.join(self.receiptFolder, "receipt.csv")
+
+ if os.path.exists(receiptFilename):
+ try:
+ # try opening file for editing
+ f = open(receiptFilename, "wb")
+ f.close()
+ except:
+ raise Exception, "Receipts file cannot be written to and may be in use: %s" % receiptFilename
+
+
+ # read the CSV and store the rows in an array
+ self.loadCSV(owner)
+
+ # warn if not using attachment metadata targets
+ if not self.attachmentMetadataTargets:
+ self.echo("\nWARNING: Not using attachments metadata targets (not suitable for EQUELLA 5.0 or higher)\n")
+
+ # calculate absolute appachments basepath for attachments
+ self.absoluteAttachmentsBasepath = os.path.join(os.path.dirname(self.csvFilePath), self.attachmentsBasepath.strip())
+ if self.debug:
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Absolute attachments basepath is " + self.absoluteAttachmentsBasepath)
+
+ # indicate what scripts, if any, are present
+ scriptsPresent = []
+ if self.startScript.strip() != "":
+ scriptsPresent.append("Start Script")
+ if self.preScript.strip() != "":
+ scriptsPresent.append("Row Pre-Script")
+ if self.postScript.strip() != "":
+ scriptsPresent.append("Row Post-Script")
+ if self.endScript.strip() != "":
+ scriptsPresent.append("End Script")
+ if len(scriptsPresent) > 0:
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Scripts present: " + ", ".join(scriptsPresent))
+
+ # set variables for scripts
+ self.scriptVariables = {}
+ if self.export:
+ action = 1
+ else:
+ action = 0
+
+ # run Start Script
+ if self.startScript.strip() != "" and not self.export:
+ try:
+ exec self.startScript in {
+ "IMPORT":0,
+ "EXPORT":1,
+ "mode":action,
+ "vars":self.scriptVariables,
+ "testOnly": testOnly,
+ "institutionUrl":tle.institutionUrl,
+ "collection":self.collection,
+ "csvFilePath":self.csvFilePath,
+ "username":self.username,
+ "logger":self.logger,
+ "columnHeadings":self.columnHeadings,
+ "columnSettings":self.currentColumns,
+ "successCount":self.successCount,
+ "errorCount":self.errorCount,
+ "process":self.process,
+ "basepath":self.absoluteAttachmentsBasepath,
+ "sourceIdentifierIndex":sourceIdentifierColumn,
+ "targetIdentifierIndex":targetIdentifierColumn,
+ "targetVersionIndex":targetVersionColumn,
+ "csvData":self.csvArray,
+ "ebi":self.ebiScriptObject,
+ "equella":tle,
+ }
+ except:
+ if self.debug:
+ raise
+ else:
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ formattedException = "".join(traceback.format_exception_only(exceptionType, exceptionValue))[:-1]
+ scriptErrorMsg = "An error occured in the Start Script:\n%s (line %s)" % (formattedException, traceback.extract_tb(exceptionTraceback)[-1][1])
+ raise Exception, scriptErrorMsg
+
+ if not self.export:
+ self.validateColumnHeadings()
+
+ # set all rows to be processed
+ scheduledRows = range(1, len(self.csvArray))
+ rowsToBeProcessedCount = len(self.csvArray) - 1
+ scheduledRowsLabel = "all rows to be processed"
+
+ # check if row filter applies
+ if self.rowFilter.strip() != "":
+ try:
+ scheduledRows = []
+
+ # populate scheduledRows based on rowsFilter
+ rowRanges = self.rowFilter.split(",")
+ for rowRange in rowRanges:
+ rows = rowRange.split("-")
+
+ if len(rows) == 1:
+
+ # single row number encountered
+ scheduledRows.append(int(rows[0]))
+
+ if len(rows) == 2:
+
+ # row range provided
+ if rows[1].strip() == "":
+
+ # no finish row so assume all remaining rows (e.g. "5-")
+ rows[1] = len(self.csvArray) - 1
+
+ scheduledRows.extend(range(int(rows[0]), int(rows[1]) + 1))
+
+ # remove any duplicates (preserving order)
+ scheduledRows = self.removeDuplicates(scheduledRows)
+
+ # determine how many rows to be processed
+ rowsToBeProcessedCount = 0
+ for rc in scheduledRows:
+ if rc < len(self.csvArray):
+ rowsToBeProcessedCount += 1
+
+ # form label for how many rows to be processed
+ scheduledRowsLabel = "%s to be processed [%s]" % (rowsToBeProcessedCount, self.rowFilter)
+
+ except:
+ if self.debug:
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ self.echo(''.join(traceback.format_exception(exceptionType, exceptionValue, exceptionTraceback)))
+ raise Exception, "Invalid row filter specified"
+
+ if not self.export:
+ # echo rows to be processed
+ if testOnly:
+ actionString = "%s row(s) found, %s (test only)" % (len(self.csvArray) - 1 if len(self.csvArray) > 0 else 0, scheduledRowsLabel)
+ else:
+ actionString = "%s row(s) found, %s" % (len(self.csvArray) - 1 if len(self.csvArray) > 0 else 0, scheduledRowsLabel)
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + actionString)
+
+ # echo draft and new version settings
+ actionString = ""
+ if sourceIdentifierColumn != -1 or targetIdentifierColumn != -1:
+ if self.saveAsDraft and self.createNewVersions:
+ actionString = "Options -> Create new versions of existing items in draft status"
+ elif self.createNewVersions:
+ actionString = "Options -> Create new versions of existing items"
+ elif self.saveAsDraft:
+ actionString = "Options -> Create new items in draft status (status of existing items will remain unchanged)"
+ elif self.saveAsDraft:
+ actionString = "Options -> Create items in draft status"
+ if actionString != "":
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + actionString)
+
+ # echo append/replace metadata settings
+ actionString = ""
+ if sourceIdentifierColumn != -1 or targetIdentifierColumn != -1:
+ if self.existingMetadataMode == self.APPENDMETA:
+ actionString = "Options -> Append metadata to existing items"
+ if self.existingMetadataMode == self.REPLACEMETA:
+ actionString = "Options -> Replace specified metadata in existing items"
+ if actionString != "":
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + actionString)
+
+ # echo append attachment settings
+ if self.appendAttachments:
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Options -> Append attachments to existing items")
+
+
+ # iterate through the rows of metadata from the CSV file creating an item in EQUELLA for each
+ rowReceipts = {}
+ processedCounter = 0
+ self.sourceIdentifierReceipts = {}
+
+ self.owner.progressGauge.SetRange(len(scheduledRows))
+ self.owner.progressGauge.SetValue(processedCounter)
+ self.owner.progressGauge.Show()
+
+ if not self.export:
+
+ # if Collection column spectifed check that all collection names resolve to collectionIDs
+ if collectionColumn != -1:
+ for rowCounter in scheduledRows:
+ collectionName = self.csvArray[rowCounter][collectionColumn]
+ if collectionName.strip() != "" and collectionName not in self.collectionIDs.keys():
+ raise Exception,"Unknown collection '%s' at row %s" % (collectionName, rowCounter)
+
+ rowCounter = 0
+ for rowCounter in scheduledRows:
+ if self.StopProcessing:
+ break
+
+ if rowCounter < len(self.csvArray):
+ self.Skip = False
+ processedCounter += 1
+
+ self.echo("---")
+
+ self.tryPausing("[Paused]")
+
+ # update UI and log
+ wx.GetApp().Yield()
+ owner.mainStatusBar.SetStatusText("Processing row %s [%s of %s]" % (rowCounter, processedCounter, rowsToBeProcessedCount), 0)
+
+ action = "Processing item..."
+ if testOnly:
+ action = "Validating item..."
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + " Row %s [%s of %s]: %s" % (rowCounter, processedCounter, rowsToBeProcessedCount, action))
+
+ # process row
+ savedItemID, savedItemVersion, sourceIdentifier, rowData, rowError = self.processRow(rowCounter,
+ self.csvArray[rowCounter],
+ self.tle,
+ itemdefuuid,
+ self.collectionIDs,
+ testOnly,
+ sourceIdentifierColumn,
+ targetIdentifierColumn,
+ targetVersionColumn,
+ commandOptionsColumn,
+ attachmentLocationsColumn,
+ urlsColumn,
+ resourcesColumn,
+ customAttachmentsColumn,
+ collectionColumn,
+ thumbnailsColumn,
+ selectedThumbnailColumn,
+ ownerColumn,
+ collaboratorsColumn)
+
+ # add to row receipts
+ rowReceipts[rowCounter] = (savedItemID, savedItemVersion)
+ if sourceIdentifierColumn != -1:
+ self.sourceIdentifierReceipts[sourceIdentifier] = (savedItemID, savedItemVersion)
+
+
+ # update row in CSV array for receipt and script-processed row data
+ if itemIdColumn != -1:
+ # assign itemID to receipt cell
+ rowData[itemIdColumn] = savedItemID
+ rowData[versionColumn] = str(savedItemVersion)
+ if rowErrorColumn != -1:
+ rowData[rowErrorColumn] = rowError
+ self.csvArray[rowCounter] = rowData
+
+ # update progress bar
+ self.owner.progressGauge.SetValue(processedCounter)
+
+ if self.StopProcessing:
+ self.echo("---")
+ if self.processingStoppedByScript:
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Processing halted")
+ else:
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Processing halted by user")
+
+ self.echo("---")
+
+ # run End Script
+ if self.endScript.strip() != "":
+ try:
+ exec self.endScript in {
+ "IMPORT":0,
+ "EXPORT":1,
+ "mode":action,
+ "vars":self.scriptVariables,
+ "rowCounter":rowCounter,
+ "testOnly": testOnly,
+ "institutionUrl":tle.institutionUrl,
+ "collection":self.collection,
+ "csvFilePath":self.csvFilePath,
+ "username":self.username,
+ "logger":self.logger,
+ "columnHeadings":self.columnHeadings,
+ "columnSettings":self.currentColumns,
+ "successCount":self.successCount,
+ "errorCount":self.errorCount,
+ "process":self.process,
+ "basepath":self.absoluteAttachmentsBasepath,
+ "sourceIdentifierIndex":sourceIdentifierColumn,
+ "targetIdentifierIndex":targetIdentifierColumn,
+ "targetVersionIndex":targetVersionColumn,
+ "csvData":self.csvArray,
+ "ebi":self.ebiScriptObject,
+ "equella":tle,
+ }
+ except:
+ if self.debug:
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ scriptErrorMsg = "An error occured in the End Script:\n" + ''.join(traceback.format_exception(exceptionType, exceptionValue, exceptionTraceback))
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + scriptErrorMsg, style=2)
+ else:
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ formattedException = "".join(traceback.format_exception_only(exceptionType, exceptionValue))[:-1]
+ scriptErrorMsg = "An error occured in the End Script:\n%s (line %s)" % (formattedException, traceback.extract_tb(exceptionTraceback)[-1][1])
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + scriptErrorMsg, style=2)
+
+
+
+ # output receipts if Item ID column specified and real run
+ if itemIdColumn != -1:
+
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + "Writing receipts file...")
+
+ # create receipts folder if one doesn't exist
+ if not os.path.exists(self.receiptFolder):
+ os.makedirs(self.receiptFolder)
+
+ # open csv writer and output orginal csv rows using self.columnHeadings as first row (instead of first row of self.csvArray)
+ f = open(receiptFilename, "wb")
+ writer = UnicodeWriter(f, self.encoding)
+ writer.writerow(list(self.columnHeadings))
+ for i in range(1, len(self.csvArray)):
+ writer.writerow(list(self.csvArray[i]))
+ f.close()
+
+ else:
+ # export
+ actionString = ""
+ if sourceIdentifierColumn == -1 and targetIdentifierColumn == -1:
+ if self.includeNonLive:
+ actionString = "Options -> Include non-live items in export"
+ if actionString != "":
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + actionString)
+
+ self.exportedFiles = []
+ self.exportCSV(owner,
+ self.tle,
+ itemdefuuid,
+ self.collectionIDs,
+ testOnly,
+ scheduledRows,
+ sourceIdentifierColumn,
+ targetIdentifierColumn,
+ targetVersionColumn,
+ commandOptionsColumn,
+ attachmentLocationsColumn,
+ collectionColumn,
+ rowsToBeProcessedCount)
+
+
+ # form outcome report
+ errorReport = ""
+ if self.errorCount > 0:
+ errorReport = " errors: %s" % (self.errorCount)
+ resultReport = "Processing complete (success: %s%s)" % (self.successCount, errorReport)
+
+ self.echo(time.strftime("%H:%M:%S: ", time.localtime(time.time())) + resultReport)
+
+ owner.mainStatusBar.SetStatusText(resultReport, 0)
+
+
+
+ # getEquellaResourceDetail() retrieves an item or item attachment details necessary for forming an EQUELLA resource-type attachment
+ def getEquellaResourceDetail(self, resourceUrl, itemdefuuid, collectionIDs, sourceIdentifierColumn, isCalHolding):
+
+ # break up resource url
+ resourceUrlParts = []
+ if resourceUrl[0] == "[" or resourceUrl[0] == "{":
+ if resourceUrl[-1] == "}":
+ resourceUrlParts.append(resourceUrl)
+ elif resourceUrl[-2:-1] == "}/":
+ resourceUrlParts.append(resourceUrl[:-1])
+ else:
+ resourceUrlParts.append(resourceUrl.split("}/")[0] + "}")
+ resourceUrlParts += resourceUrl.split("}/")[1].split("/")
+ else:
+ resourceUrlParts = resourceUrl.split("/")
+
+ # get item UUID
+ resourceItemUuid = resourceUrlParts[0]
+
+ # check if itemUUID is actually a sourceIdentifier
+ # format is {