#!/usr/bin/python
################################
##
## Transmogrifier: fcsxml
## A Final Cut Server import/export tool
##
## Written by: Beau Hunter beau@318.com
## 318 Inc 05/2009
##
## This class provides the primary interface for reading and interpreting FCS
## generated XML. This class provides primary methods which assist in the
## conversion of FCS XML and media files to a third party format.
##
## This code is made available via the GPL2 license as part of the Transmogrifier
## project available at:
## http://sourceforge.net/projects/transmogrifier/
##
#############################################################
import sys,os.path,shutil,subprocess
import re,datetime,time,tempfile,copy
import urllib, plistlib
import codecs
from ConfigParser import *
from xml.dom import minidom
version = '.96beta'
build = '2011040701'
[docs]class FCSBaseObject:
'''Our base object. Provides basic error logging capabilities'''
## Logging vars
log = []
lastError = ""
lastMSG = ""
debug = False
keepFiles = False
isError = False
logOffset = 1
printLogs = False
printLogDate = True
printClassInLog = False
timezoneOffset = 5
def __init__(self):
'''Our construct'''
self.log = []
self.lastError = ''
self.lastMSG = ''
self.debug = False
self.keepFiles = False
self.isError = False
self.logOffset = 1
self.printLogs = False
self.printLogDate = True
self.printClassInLog = False
[docs] def entityPath(self):
'''Returns the entityPath'''
if self.entityType and self.entityID:
entityPath = '/%s/%s' % (self.entityType,self.entityID)
else:
self.logger('Could not determine entityPath, entityType or entityID not set!','error')
return False
return entityPath
[docs] def logger(self, logMSG, logLevel='normal',printClassInLog='unset'):
'''(very) Basic Logging Function, we'll probably migrate to msg module'''
if (logLevel == 'error'
or logLevel == 'normal'
or logLevel == 'detailed'
or self.debug):
i = 0
headerText = ''
while i < self.logOffset:
headerText+=' '
i+=1
if logLevel.lower() == 'error':
headerText = ' ERROR : %s' % headerText
elif logLevel.lower() == 'debug':
headerText = ' DEBUG : %s' % headerText
elif logLevel.lower() == 'warning':
headerText = 'WARNING : %s' % headerText
elif logLevel.lower() == 'detailed':
headerText = ' DETAIL : %s' % headerText
else:
headerText = ' INFO : %s' % headerText
if self.printLogDate:
dateString = datetime.datetime.today().strftime("%b %d %H:%M:%S")
headerText = "%s: %s" % (dateString,headerText)
if self.printLogs:
if printClassInLog == 'unset':
printClassInLog = self.printClassInLog
if printClassInLog:
print '%s%s: %s' % (headerText,self.__class__.__name__,logMSG)
else:
print '%s%s' % (headerText,logMSG)
sys.stdout.flush()
self.lastMSG = logMSG
if logLevel == 'error':
self.lastError = logMSG
self.log.append({'logLevel' : logLevel, 'logMSG' : logMSG})
[docs] def logs(self,logLevel=''):
'''Returns an array of logs matching logLevel'''
returnedLogs = []
logs = self.log
for log in logs:
if logLevel and logLevel.lower() == log['logLevel'].lower():
returnedLogs.append(log)
[docs] def lastError(self):
'''Returns last error'''
errorLogs = self.logs('error')
return errorLogs[len(errorLogs)]
[docs]class FCSXMLField(FCSBaseObject):
'''Our FCSXMLField object, used to store value, field name, and db name'''
name = ''
dbname = ''
value = ''
dataType = ''
dbDataType = ''
dataTypeLoaded = False
valueLoaded = False
validDataTypes = { 'string' : 'KtString32','string2' : 'KtString','int':'KtInt',
'integer':'KtInt','int64':'KtInt64','bigint':'KtInt64','dateTime':'KtDateTime',
'timestamp':'KtDateTime','timecode':'PxTimecode','float':'KtReal','coords':'KtIntXY',
'fraction':'KtFraction','bool':'KtBool','list':'KtMdValueMap' }
def __init__(self, name='', value='', dataType='',dbname='',dbDataType=''):
'''Our construct, dbDataType overrides dataType'''
FCSBaseObject.__init__(self)
self.name = name
self.dbname = dbname
self.dataType = ''
self.dbDataType = ''
if dataType:
self.setDataType(dataType)
if dbDataType:
self.setDBDataType(dbDataType)
self.setValue(value)
self.dataTypeLoaded = False
self.valueLoaded = False
self.debug = False
def __str__(self):
'''Output our field name'''
return "FCS Field: %s" % self.name
[docs] def setDataType(self,dataType):
'''Method to set dataType'''
if dataType in self.validDataTypes:
if dataType == 'integer':
dataType = 'int'
elif dataType == 'timestamp':
dataType = 'dateTime'
self.dataType = dataType
self.dbDataType = self.validDataTypes[dataType]
self.dataTypeLoaded = True
else:
raise RuntimeError('dataType:%s is not defined!' % dataType)
[docs] def setDBDataType(self,dbDataType):
'''Method to set dbDataType'''
dataType = ''
for key,value in self.validDataTypes.iteritems():
if value == dbDataType:
dataType = key
if dataType:
self.dataType = dataType
self.dbDataType = dbDataType
self.dataTypeLoaded = True
else:
if dbDataType == 'KtAtom' or dbDataType == 'KtAtomList':
print 'Fields with type: %s are not currently supported!' % dbDataType
return False
else:
raise RuntimeError('dbDataType:%s is not defined!' % dbDataType)
[docs] def setValue(self,newValue):
'''Method to set internal value, performs validation and conformity'''
dataType = self.dataType
myDateTime = ''
## String can be either string or string2
if dataType[0:6] == 'string':
self.value = '%s' % newValue
elif dataType == 'int64':
if newValue:
self.value = int(newValue)
else:
self.value = 0
elif dataType == 'dateTime':
inDateTimeFormats = ['%Y-%m-%d %H:%M:%S+%f','%Y-%m-%dT%H:%M:%SZ']
outDateTimeFormat = '%Y-%m-%dT%H:%M:%SZ'
if newValue.__class__.__name__ == 'datetime':
myDateTime = newValue
else:
count = 0
while count < len(inDateTimeFormats) and not myDateTime:
self.logger('Testing Pattern: \'%s\' against string: \'%s\'' % (inDateTimeFormats[count],newValue),'debug')
try:
myDateTime = datetime.datetime.strptime('%s' % newValue,inDateTimeFormats[count])
except:
pass
count += 1
## Account for timezone changes (FCS stores timestamps in UTC)
if myDateTime:
myDateTime = myDateTime + datetime.timedelta(hours=self.timezoneOffset)
self.value = myDateTime.strftime(outDateTimeFormat)
elif dataType == 'bool':
##self.logger("setValue() BOOL FIELD:%s HIT WITH VALUE: %s " % (self.name,newValue),'debug')
if (type('') == type(newValue) or type(u'') == type(newValue)):
if (newValue.lower() == 'true'):
self.value = True
else:
self.value = False
elif type(1) == type(newValue):
if newValue > 0:
self.value = True
else:
self.value = False
elif type(True) == type(newValue):
self.value = newValue
else:
self.logger("setValue() ACK!! ERRORRRRRz: given type:%s" % type(newValue),'debug')
validationInfo = ('BOOL Field must be provided a boolean value, an '
'integer value (0 or 1) or a string value (\'true\' or \'false\')')
raise FCSValidationError(fieldName=self.name,
value=newValue,
dataType=self.dataType,
validationInfo=validationInfo)
self.logger("setValue() - USING VALUE: %s " % self.value,'debug')
else:
self.value = '%s' % newValue
self.valueLoaded = True
return True
[docs] def printInfo(self):
'''output basic field info'''
print 'FieldName: %s\n Value: %s\n Type: %s\n dbname: %s\n' % (self.name,self.value,self.dataType,self.dbname)
[docs]class FCSXMLObject(FCSBaseObject):
entityID = 0
entityType = 'asset'
path = ''
fcsXML = ''
fields = {}
deviceDict = {}
lastError = ''
log = []
overwriteExistingFiles = True
debug = False
def __init__(self,entityID=0):
'''construct function, can be called with a specified integer id'''
FCSBaseObject.__init__(self)
self.entityID = entityID
self.entityType = 'asset'
self.path = ''
self.fcsXML = ''
self.fields = {}
self.deviceDict = {}
self.lastError = ''
self.log = []
[docs] def setFile(self,filePath):
'''Historical method, use loadFromFile()'''
return self.loadFromFile(filePath)
[docs] def loadFromFile(self,filePath):
'''import file'''
filePath = os.path.abspath(os.path.realpath(os.path.expanduser(filePath)))
## todo: get entityType
if os.path.exists(filePath):
self.path = filePath
self.fcsXML = minidom.parse(filePath).documentElement
self.logger('Loading XML from File: %s' % filePath, 'info')
return self.loadXML()
else:
self.logger('File does not exist at path: %s, exiting!' % filePath, 'error')
def printLogs(self, logLevel='all'):
for log in self.log:
if logLevel == 'all' or logLevel == log['logLevel']:
print 'fcsvr_xml:%s:%s' % (log['logLevel'], log['logMSG'])
[docs] def getXMLNodeText(self, nodes):
'''returns text value for passed nodes'''
text = ''
for node in nodes:
if node.nodeType == node.TEXT_NODE:
text = text + node.data
return text
[docs] def loadFromFCSVRClient(self,fcsvrClient):
'''Loads our object from values stored in fcsvrClient'''
try:
self.entityID = fcsvrClient.entityID
self.entityType = fcsvrClient.entityType
self.fields = fcsvrClient.fields
except:
self.logger('An error occured loading from the object','error')
return False
return True
[docs] def loadXMLFromString(self,xmlString=''):
'''loads xml in from passed string'''
if xmlString:
try:
self.fcsXML = minidom.parseString(xmlString).documentElement
except:
self.logger('An error occurred reading in xmlString, could not create'
+ ' minidom object!','error')
self.logger("\n######## XML String ######\n'%s'\n###### END XML String #####" % xmlString,'debug')
return False
else:
self.logger('loadXMLFromString() failed! Provided string is empty!','error')
return False
return self.loadXML()
[docs] def loadXML(self, xml=''):
'''loads values from stored xml'''
self.logger('Loading XML!','debug')
if not xml:
if not self.fcsXML:
self.logger('loadXML() XML could not be loaded!', 'error')
return False
else:
xml = self.fcsXML
try:
entity = xml.getElementsByTagName('entity')[0]
self.entityType = entity.attributes['entityType'].value
self.entityID = re.sub(r'\/asset\/(\d*?)',r'\1',entity.attributes['entityId'].value)
except:
##self.logger('Error reading XML format, attempting to read FCS WriteXML format','warning')
#try:
entity = xml.getElementsByTagName('params')[0]
requestEntity = xml.getElementsByTagName('request')[0]
self.entityType = re.sub(r'\/(.*)\/(.*)',r'\1',requestEntity.attributes['entityId'].value)
self.entityID = re.sub(r'\/(.*)\/(.*)',r'\2',requestEntity.attributes['entityId'].value)
#except:
# self.logger('Failed reading FCS WriteXML format, cannot read XML','error')
# return False
fields = entity.getElementsByTagName('mdValue')
for field in fields:
if len(field.childNodes) > 0:
theField = FCSXMLField(name=field.attributes['fieldName'].value,
value=self.getXMLNodeText(field.childNodes),
dataType=field.attributes['dataType'].value)
else:
theField = FCSXMLField(name=field.attributes['fieldName'].value,
value='',
dataType=field.attributes['dataType'].value)
self.fields[field.attributes['fieldName'].value] = theField
##self.lastError = 'Loaded Key:', theField.name
return True
[docs] def valueForField(self, fieldName):
'''return value for the passed field name'''
if not fieldName in self.fields:
self.logger('valueForField() No Field with key: %s exists!' % fieldName, 'warning')
raise FCSFieldNotFoundError(fieldName)
else:
theField = self.fields[fieldName]
if theField.dataType == 'timecode':
## return timecode in seconds
tempValue = theField.value.replace( '.', ':' )
tempValue = tempValue.replace( ';', ':' )
tempValue = tempValue.replace(',',':')
tempValueArray = tempValue.split(':')
hours = int(tempValueArray[0])
minutes = int(tempValueArray[1])
seconds = int(tempValueArray[2])
frames = int(tempValueArray[3])
## need to extrapolate framerate to get seconds-per-frame
frameRate = float(self.valueForField('Video Frame Rate'))
if frameRate:
frameSeconds = (frames/frameRate)
totalSeconds = (hours * 60 * 60) + (minutes * 60) + seconds + frameSeconds
return totalSeconds
else:
return theField.value
[docs] def dataTypeForField(self, fieldName):
'''returns datatype for the passed field name'''
if not fieldName in self.fields:
self.logger('dataTypeForField() No Field with key: %s exists!' % fieldName, 'warning')
return False
else:
theField = self.fields[fieldName]
if not theField.dataType:
theField.dataType = 'string'
return theField.dataType
[docs] def fieldWithName(self, fieldName):
'''returns FCSXMLField object for the passed field name'''
if not fieldName in self.fields:
self.logger('fieldWithName() No Field with key: %s exists!' % fieldName, 'warning')
return False
else:
theField = self.fields[fieldName]
return theField
[docs] def setField(self, field):
'''Register FCSXMLField, replaces existing field object with same name'''
if field.__class__.__name__ != 'FCSXMLField':
self.logger('setField() Passed invalid data! Expects FCSXMLField', 'error')
return False
else:
self.fields[field.name] = field
[docs] def appendField(self, field):
'''Append FCSXMLField, fails if field object with same name already exists'''
if field.__class__.__name__ != 'FCSXMLField':
self.logger('appendField() Passed invalid data! Expects FCSXMLField', 'error')
return False
if field.name in self.fields:
self.logger('appendField() Field with name: %s already exists!' % field.name, 'error')
return False
else:
self.fields[field.name] = field
[docs] def removeFieldWithName(self, fieldName):
'''Remove FCSXMLField object registered with passed field name'''
if fieldName.__class__.__name__ == 'FCSXMLField':
fieldName = fieldName.name
if fieldName in self.fields:
del self.fields[fieldName]
self.logger('removeFieldWithName() Field with name: %s removed!' % fieldName, 'info')
return True
else:
return False
[docs] def xmlOut(self, filePath=''):
'''Write our XML out to a file'''
if not self.entityID > 0:
self.logger('xmlOut() entityID not set! Cannot generate XML.', 'warning')
if not len(self.fields) > 0:
self.logger('xmlOut() no fields set! Cannot generate XML.', 'error')
return False
if (filePath and (not os.path.exists(filePath) \
or (os.path.exists(filePath) and self.overwriteExistingFiles))
and os.path.isdir(os.path.dirname(filePath))) \
or not filePath :
## create our new xml doc, add our root FCS elements:
## <?xml version="1.0"?>
## <FinalCutServer>
## <getMdReply>
## <entity entityType="asset" entityId="/asset/587344">
## <metadata>
xmlDoc = minidom.Document()
fcsElement = xmlDoc.createElement('FinalCutServer')
xmlDoc.appendChild(fcsElement)
requestElement = xmlDoc.createElement('request')
requestElement.setAttribute('entityId', '/%s/%s' % (self.entityType,self.entityID))
requestElement.setAttribute('reqId', 'setMd')
fcsElement.appendChild(requestElement)
paramsElement = xmlDoc.createElement('params')
requestElement.appendChild(paramsElement)
## And then our individual fields.
for field in self.fields.itervalues():
theFieldElement = xmlDoc.createElement('mdValue')
theFieldElement.setAttribute('fieldName', field.name)
theFieldElement.setAttribute('dataType', field.dataType)
if field.dataType == 'bool':
if field.value:
fieldValue = 'True'
else:
fieldValue = 'False'
else:
fieldValue = field.value
if fieldValue:
theValueNode = xmlDoc.createTextNode(fieldValue)
theFieldElement.appendChild(theValueNode)
else:
theValueNode = ''
## Append our field element to our 'params' element i.e.
## <params>
## <mdValue fieldName="Size" dataType="int64">3798309</mdValue>
paramsElement.appendChild(theFieldElement)
del theFieldElement
if filePath:
theFile = open(filePath, 'w')
xmlDoc.writexml(theFile)
theFile.close()
else:
return xmlDoc.toxml()
##return xmlDoc.toprettyxml()
elif os.path.exists(filePath) and not self.overwriteExistingFiles:
self.logger('xmlOut() File already exists at path: %s, exiting!' % filePath, 'error')
return False
elif not os.path.exists(os.path.dirname(filePath)):
self.logger('xmlOut() Directory does not exist at path: %s, exiting!' % os.path.dirname(filePath), 'error')
return False
else:
self.logger('xmlOut() Unknown error writing XML', 'error')
return False
xmlDoc.unlink()
return True
[docs]class FCSVRClient(FCSBaseObject):
'''Our FCSVRClient object, for manipulating FCS via fcsvr_client'''
entityType = ''
entityID = ''
entityMetadataSet = ''
entityTitle = ''
defaultProductionMetadataSet = 'pa_production_package'
defaultAssetMetadataSet = 'pa_asset_media'
defaultThumbnailMetadataSet = 'pa_asset_thumbnail'
fields = {}
deviceDict = {}
supportDir = '/tmp'
overwriteExistingFiles = True
thumbnailPath = ''
posterFramePath = ''
proxyPath = ''
editProxyPath = ''
thumbnailDeviceName = ''
parentXMLObject = ''
childXMLObject = ''
FCSUID = 0 ## Our FCS User ID. If this is not set we will attempt
## to read it in from /Library/Preferences/com.apple.FinalCutServer.settings.plist
useSudo = False ## Call /usr/bin/sudo before calling fcsvr_client commands
## that require root access. You'll want to make sure that
## You have modified your sudoers file if you set this to true.
pathToFCSVRClient = '/Library/Application Support/Final Cut Server/Final Cut Server.bundle/Contents/MacOS/fcsvr_client'
devicesMap = ''
registeredEntities = ['asset','project','dev','field','mdgroup','group']
def __init__(self,entityType='asset',id='',entityPath='',cfgParser=''):
'''Our constructor'''
FCSBaseObject.__init__(self)
if entityPath:
pathMatch = re.match('\/(.*)\/(.*)',entityPath)
if pathMatch:
self.entityType = pathMatch.groups()[0]
self.entityID = pathMatch.groups()[1]
else:
self.entityType = entityType
self.entityID = id
self.entityMetadataSet = ''
self.overwriteExistingFiles = True
self.thumbnailPath = ''
self.parentXMLObject = ''
self.childXMLObject = ''
self.thumbnailDeviceName = 'Library'
self.debug = False
self.keepFiles = False
self.devicesMap = ''
self.fields = {}
self.deviceDict = {}
self.FCSUID = self.getFCSUID()
self.thumbnailPath = ''
self.posterFramePath = ''
self.proxyPath = ''
self.editProxyPath = ''
self.defaultProductionMetadataSet = 'pa_production_package'
self.defaultAssetMetadataSet = 'pa_asset_media'
self.useSudo = True
self.printClassInLog = True
self.debug = False
if cfgParser:
self.loadConfiguration(cfgParser)
if id and entityType == 'asset':
self.initWithAssetID(assetID=id)
[docs] def loadConfiguration(self, parser):
'''Load from configuration file, expects a ConfigParser type object. If you subclass,
you should call this function. If we return false then you should abort. or do your own sanity checks'''
if not isinstance(parser,ConfigParser):
self.logger('loadConfiguration() Not passed a valid ConfigParser Object!', 'error')
return False
try:
self.configParser = parser
## Get Debug status, first from FCSVRClient section, then from global
try:
self.printLogs = parser.getboolean('FCSVRClient','printLogs')
except:
try:
self.printLogs = parser.getboolean('GLOBAL','printLogs')
except:
pass
try:
self.debug = parser.getboolean('FCSVRClient','debug')
except:
try:
self.debug = parser.getboolean('GLOBAL','debug')
except:
pass
try:
self.keepFiles = parser.getboolean('FCSVRClient','keepFiles')
except:
try:
self.keepFiles = parser.getboolean('GLOBAL','keepFiles')
except:
pass
try:
self.defaultProductionMetadataSet = parser.get('FCSVRClient','defaultproductionmdset')
except:
pass
try:
self.defaultAssetMetadataSet = parser.get('FCSVRClient','defaultassetmdset')
except:
pass
try:
self.defaultThumbnailMetadataSet = parser.get('FCSVRClient','defaultthumbnailmdset')
except:
pass
try:
self.useSudo = parser.getboolean('FCSVRClient','usesudo')
except:
pass
except:
self.logger('loadConfiguration() Problem loading configuration records, please double check your configuration', 'error')
return True
[docs] def getFCSUID(self):
'''Determine the UID of the running FCS User, this is stored in
/Library/Preferences/com.apple.FinalCutServer.settings.plist'''
plistFile = '/Library/Preferences/com.apple.FinalCutServer.settings.plist'
UID = 0
if self.FCSUID:
return self.FCSUID
plistDict = {}
if os.path.isfile(plistFile):
try:
plistObj = plistlib.readPlist(plistFile)
except:
self.logger('loadFromFile() Error Reading File!','error')
return False
if 'USER_ID' in plistObj:
self.FCSUID = plistObj['USER_ID']
return self.FCSUID
[docs] def getDevicesMap(self,useCache=True):
'''Load device information from FCS'''
if self.devicesMap and len(self.devicesMap) > 0 and useCache:
return self.devicesMap
## Run our fcsvr_client command.
fcsvrCMDTXT = "'%s' search /dev --xml" % (self.pathToFCSVRClient)
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
if not fcsvrCMD.returncode == 0:
##self.logger('%s' % fcsvrCMD_STDOUT,'error')
##raise RuntimeError('Could not parse output from fcsvr_client')
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString='fcsvr_client %s' % cmdString)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client','error')
raise RuntimeError('Could not parse output from fcsvr_client')
devicesMap = {}
for rootValues in myDom.getElementsByTagName('values'):
deviceDict = {}
for rootValue in rootValues.getElementsByTagName('value'):
if rootValue.attributes['id'].value == 'METADATA':
for values in rootValue.getElementsByTagName('values'):
for value in values.getElementsByTagName('value'):
valueID = value.attributes['id'].value
if valueID == 'DEV_ROOT_PATH':
deviceDict['DEV_ROOT_PATH'] = value.childNodes[1].childNodes[0].data
deviceDict['FSPATH'] = value.childNodes[1].childNodes[0].data
elif valueID == 'DEVICE_TYPE':
deviceDict['DEVICE_TYPE'] = value.childNodes[1].childNodes[0].data
elif valueID == 'DEVICE_NAME':
deviceDict['DEVICE_NAME'] = value.childNodes[1].childNodes[0].data
elif valueID == 'DEVICE_ID':
deviceDict['DEVICE_ID'] = int(value.childNodes[1].childNodes[0].data)
deviceDict['DESC_DEVICE_ADDRESS'] = '/dev/%s' % deviceDict['DEVICE_ID']
elif valueID == 'DEV_ARCHIVE':
archiveValue = value.childNodes[1].childNodes[0].data
if archiveValue == 'true':
deviceDict['DEV_ARCHIVE'] = True
else:
deviceDict['DEV_ARCHIVE'] = False
if 'DEVICE_ID' in deviceDict and deviceDict['DEVICE_ID']:
devicesMap[deviceDict['DEVICE_ID']] = deviceDict
self.devicesMap = devicesMap
return self.devicesMap
[docs] def deviceWithID(self,id):
'''Return a dictionary containing pertinent device information'''
deviceIDMap = self.getDevicesMap()
self.logger('deviceWithID() Called with ID: %s' % id,'debug')
myID = int(id)
if myID in deviceIDMap:
return deviceIDMap[myID]
else:
raise FCSEntityNotFoundError(entityType='device',entityID=id)
#self.logger('DeviceID: %s has not been registered!' % id,'error')
#return False
[docs] def deviceWithName(self,id):
'''Return a dictionary containing pertinent device information based on device Name'''
deviceIDMap = self.getDevicesMap()
self.logger('deviceWithName() Called with Name: %s' % id,'debug')
if not deviceIDMap:
self.logger('Could not generate deviceMap, cannot continue!','error')
return False
for deviceID,deviceDict in deviceIDMap.iteritems():
if 'DEVICE_NAME' in deviceDict and deviceDict['DEVICE_NAME'] == id:
return deviceIDMap[deviceID]
## We are here if we found no match.
raise FCSEntityNotFoundError(entityType='device',entityTitle=id)
[docs] def deviceWithPath(self,path):
'''Return a dictionary containing pertinent device information based on a
POSIX filesystem path'''
deviceIDMap = self.getDevicesMap()
myDevice = {}
self.logger('deviceWithName() Called with path: %s' % path,'debug')
for deviceID,deviceDict in deviceIDMap.iteritems():
if 'FSPATH' in deviceDict:
subPath = path[0:len(deviceDict['FSPATH'])]
self.logger('deviceWithName() comparing subPath: %s to path:%s' % (subPath,deviceDict['FSPATH']),'debug')
if subPath == deviceDict['FSPATH']:
myDevice = deviceDict
break
if myDevice:
return myDevice
else:
raise FCSEntityNotFoundError(entityType='device',entityPath=path)
[docs] def valueForDBField(self,dbFieldName):
'''Return value for the passed DB field name'''
self.logger('valueForDBField() hit','debug')
## Create our FCSXMLField object
myField = self.loadField(FCSXMLField(dbname=dbFieldName))
return self.valueForField(myField.name)
[docs] def valueForField(self, fieldName):
'''Return value for the passed field name'''
self.logger('valueForField() hit','debug')
if not fieldName in self.fields:
self.logger('valueForField() No Field with key:\'%s\' exists! Attempting to load' % fieldName, 'warning')
if not self.loadFieldWithName(fieldName):
self.logger('valueForField() Could not load field with key:\'%s\'' % fieldName, 'warning')
raise FCSFieldNotFoundError(fieldName)
theField = self.fields[fieldName]
if theField.dataType == 'timecode':
## return timecode in seconds
tempValue = theField.value.replace( '.', ':' )
tempValue = tempValue.replace( ';', ':' )
tempValue = tempValue.replace(',',':')
tempValueArray = tempValue.split(':')
hours = int(tempValueArray[0])
minutes = int(tempValueArray[1])
seconds = int(tempValueArray[2])
frames = int(tempValueArray[3])
## need to extrapolate framerate to get seconds-per-frame
frameRate = float(self.valueForField('Video Frame Rate'))
if frameRate:
frameSeconds = (frames/frameRate)
totalSeconds = (hours * 60 * 60) + (minutes * 60) + seconds + frameSeconds
return totalSeconds
elif theField.dataType == 'bool':
if theField.value:
return True
elif theField.value:
return False
elif theField.dataType == 'string':
if not theField.value:
theField.value = ''
return theField.value
else:
return theField.value
[docs] def appendValueForField(self,fieldName,value,useTimestamp=False):
'''Appends a value to field, concatenating with the current field value'''
if not fieldName in self.fields:
self.logger('valueForField() No Field with key:\'%s\' exists! '
'Attempting to load' % fieldName, 'warning')
if not self.loadFieldWithName(fieldName):
self.logger('valueForField() Could not load field with key:\'%s\''
% fieldName, 'warning')
theField = self.fields[fieldName]
if theField.dataType == 'integer':
newValue = theField.value + value
else:
## Get current field and value
currentValue = theField.value
newValue = value
## Add a line return.
if currentValue:
currentValue += "\n"
## If we are set to use a timestamp, prepend it to the new value
if useTimestamp:
currentTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
newValue = '%s: %s' % (currentTime,newValue)
self.logger('appendValueForField() Appending timestamp to new value:%s'
% (newValue),'debug')
newValue = "%s%s\n" % (currentValue,newValue)
theField.setValue(newValue)
return
[docs] def dataTypeForField(self, field):
'''Returns datatype for the passed field (FCSXMLField) object.'''
dbname = field.dbname
name = field.name
dataType = field.dataType
if not dbname and not name:
##self.logger('dataTypeForField() Passed field has no name!', 'error')
raise RuntimeError('Could not determine Data Type: requested field has '
'no name specified');
elif name:
dbname = self.dbFieldNameForFieldName(name)
self.logger("Loading dataType for name:'%s' dbname:'%s'" % (name,dbname),
'debug')
dataType = self.dataTypeForDBFieldName(dbname)
field.dataType = dataType
return dataType
[docs] def dbDataTypeForField(self, field):
'''returns datatype for the passed field (FCSXMLField) object.'''
dbname = field.dbname
name = field.name
dataType = field.dataType
if not dbname and not name:
self.logger('dataTypeForField() Field has no name!', 'error')
raise RuntimeError('Could not determine Data Type: requested field has no'
' name specified');
elif name:
dbname = self.dbFieldNameForFieldName(name)
elif dbname:
name = self.fieldNameForField(dbname)
self.logger("Loading field name for name:'%s' dbname:'%s'" % (name,dbname),
'debug')
dbDataType = self.dbDataTypeForDBFieldName(dbname)
field.dbDataType = dbDataType
return dbDataType
[docs] def fieldWithName(self, fieldName):
'''returns FCSXMLField object for the passed field name'''
self.logger('fieldWithName() hit','debug')
if fieldName in self.fields:
theField = self.fields[fieldName]
return theField
else:
if self.loadFieldWithName(fieldName):
return self.fields[fieldName]
else:
self.logger("Could not Retrieving Field with name: %s" % fieldName,
'warning')
return False
[docs] def loadField(self,field):
'''Function which loads a field. If the field is not defined for our object,
we still will look up the dataType'''
self.logger('loadField() hit','debug')
dbFieldName = ''
fieldName = ''
if field.dbname:
dbFieldName = field.dbname
if field.name:
fieldName = field.name
if not fieldName and dbFieldName:
fieldName = self.fieldNameForDBFieldName(dbFieldName)
elif not dbFieldName and fieldName:
dbFieldName = self.dbFieldNameForFieldName(fieldName)
elif fieldName and dbFieldName:
testName = self.fieldNameForDBFieldName(dbFieldName)
testDBName = self.dbFieldNameForFieldName(fieldName)
if (not testName == fieldName or not testDBName == dbFieldName):
raise RuntimeError('Ambiguous data provided, please provide either a '
'name or dbname');
else:
self.logger('loadField() No fieldname provided!','error');
raise RuntimeError('Could not load field name: no data provided');
self.logger("loadField() Loading field with name:'%s' dbname:'%s'"
% (fieldName,dbFieldName),'debug')
## If we have a registered entityID, try to fetch the actual field value
if self.entityID:
## Run our fcsvr_client command.
fcsvrCMDTXT = "'%s' getmd /%s/%s --xml" % (self.pathToFCSVRClient,
self.entityType,self.entityID)
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client command: fcsvr_client getmd /%s/%s --xml"
% (self.entityType,self.entityID),'debug')
if not fcsvrCMD.returncode == 0:
##self.logger("%s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'error')
##return False
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
raise
self.logger("Could not parse output from fcsvr_client command: "
"fcsvr_client getmd /%s/%s --xml" % (self.entityType,
self.entityID),'error')
return False
try:
for rootElement in myDom.childNodes[0].childNodes:
##self.logger("Nodename: %s" %rootElement.nodeName,'debug')
if rootElement.nodeName == "values":
for value in rootElement.getElementsByTagName("value"):
valueID = value.attributes["id"].value
##self.logger("valueID: %s" % valueID,'debug')
if valueID == dbFieldName:
self.testNode = value
fieldValueNode = value.childNodes[1]
fieldType = fieldValueNode.nodeName
try:
self.logger('Found field: %s, reading value.' % dbFieldName,
'debug')
fieldData = fieldValueNode.childNodes[0].data
except Exception, inst:
self.logger('An error occured reading value for field: %s'
% inst,'error')
fieldData = ""
self.logger('Found field: \'%s\', with data: \'%s\''
% (dbFieldName,fieldData),'debug')
FCSField = FCSXMLField(name=fieldName,value=fieldData,
dataType=fieldType,dbname=dbFieldName)
self.fields[fieldName] = FCSField
return FCSField
except:
self.logger("Error extracting field:'%s' from fcsvr_client command: "
"fcsvr_client getmd /%s/%s --xml" % (dbFieldName,self.entityType,
self.entityID),'error')
raise
return False
## If we have gotten to this point, then the asset did not have a value for
## the requested field. If we can query the dataType for the field, return
## a bare FCSXMLField object
dataType = self.dataTypeForDBFieldName(dbFieldName)
dbDataType = self.dbDataTypeForDBFieldName(dbFieldName)
self.logger('Creating new field with name:\'%s\' dbname:\'%s\''
' dataType:\'%s\' dbDataType:%s'
% (fieldName,dbFieldName,dataType,dbDataType),'debug')
if (dataType):
FCSField = FCSXMLField(name=fieldName,dbname=dbFieldName,
dataType=dataType,dbDataType=dbDataType);
self.fields[fieldName] = FCSField
return FCSField
return False
[docs] def loadFieldWithName(self,name='',dbname=''):
'''Returns FCSField object for fieldName'''
self.logger('loadFieldWithName() hit','debug')
dbFieldName = ''
fieldName = ''
if dbname:
dbFieldName = dbname
if name:
fieldName = name
if not fieldName and dbFieldName:
fieldName = self.fieldNameForDBFieldName(dbFieldName)
elif not dbFieldName and fieldName:
dbFieldName = self.dbFieldNameForFieldName(fieldName)
elif fieldName and dbFieldName:
testName = self.fieldNameForDBFieldName(dbFieldName)
testDBName = self.dbFieldNameForFieldName(fieldName)
if (not testName == fieldName or not testDBName == dbFieldName):
raise RuntimeError('Ambiguous data provided, please provide either a'
' name or dbname');
else:
self.logger('loadFieldWithName() No fieldname provided!','error');
raise RuntimeError('Could not load field name: no data provided');
self.logger("Loading field name for name:'%s' dbname:'%s'"
% (fieldName,dbFieldName),'debug')
## If we have a registered entityID, try to fetch the actual field value
if self.entityID:
## Run our fcsvr_client command.
fcsvrCMDString = "'%s' getmd /%s/%s --xml" % (self.pathToFCSVRClient,
self.entityType,
self.entityID)
fcsvrCMD = subprocess.Popen(fcsvrCMDString,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client command: %s" % fcsvrCMDString,'debug')
if not fcsvrCMD.returncode == 0:
##errorString = '%s %s' % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR)
##self.logger(errorString,'error')
##raise FCSVRClientError('%s' % errorString,cmdString)
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
errorString = 'Could not parse output from fcsvr_client command.'
self.logger('%s fcsvr_client cmd:%s' % (errorString,fcsvrCMDString),'error')
raise FCSVRClientError(errorString,fcsvrCMDString)
try:
for rootElement in myDom.childNodes[0].childNodes:
##self.logger("loadFieldWithName() Nodename: %s" % rootElement.nodeName,'debug')
if rootElement.nodeName == "values":
for value in rootElement.getElementsByTagName("value"):
valueID = value.attributes["id"].value
##self.logger("loadFieldWithName() valueID: %s" % valueID,'debug')
if valueID == dbFieldName:
##self.logger('loadFieldWithName() found field with name:%s' % dbFieldName,'debug')
self.testNode = value
fieldValueNode = value.childNodes[1]
fieldType = fieldValueNode.nodeName
try:
fieldData = fieldValueNode.childNodes[0].data
except:
self.logger('loadFieldWithName() An error occured reading '
' data for field:%s' % dbFieldName,'debug')
fieldData = ''
self.logger('loadFieldWithName() Found field with dbname:\'%s\''
'value:\'%s\' type:\'%s\'' % (dbFieldName,fieldType,fieldData),'debug')
FCSField = FCSXMLField(name=fieldName,value=fieldData,
dataType=fieldType,dbname=dbFieldName)
self.fields[fieldName] = FCSField
return FCSField
except:
self.logger("Error extracting field:'%s' from fcsvr_client command: "
"fcsvr_client getmd /%s/%s --xml" % (dbFieldName,self.entityType,
self.entityID),'error')
raise
raise FCSVRFieldNotFound(fieldName)
## If we have gotten to this point, then the asset did not have a value for
## the requested field. If we can query the dataType for the field, return
## a bare FCSXMLField object
dataType = self.dataTypeForDBFieldName(dbFieldName)
dbDataType = self.dbDataTypeForDBFieldName(dbFieldName)
self.logger('loadFieldWithName() Creating new field with name:\'%s\' dbname:\'%s\' '
'dataType:\'%s\' dbDataType:%s'
% (fieldName,dbFieldName,dataType,dbDataType),'debug')
if (dataType):
FCSField = FCSXMLField(name=fieldName,dbname=dbFieldName,
dataType=dataType,dbDataType=dbDataType);
self.fields[name] = FCSField
return FCSField
raise FCSVRFieldNotFound(fieldName)
[docs] def setField(self, field):
'''Register FCSXMLField, replaces existing field object with same name'''
if field.__class__.__name__ != "FCSXMLField":
self.logger("setField() Passed invalid data! Expects FCSXMLField", 'error')
return False
fieldName = field.name
dbname = field.dbname
dataType = field.dataType;
if not fieldName and not dbname:
self.logger("setField() provided field has no fieldname or dbname!",'error')
return False
if not fieldName and dbname:
fieldName = self.fieldNameForDBFieldName(dbname)
field.name = fieldName
if not dbname and fieldName:
dbname = self.dbFieldNameForFieldName(fieldName)
field.dbname = dbname
if not dataType:
self.logger('setField() loading dataType for field: %s' %field.name,'debug')
dataType = self.dataTypeForField(field);
field.setDataType(dataType)
if not field.dataType:
self.logger('setField() could not produce dataType for field: %s' % fieldName,'error');
raise RuntimeError('Could not determine datatype!');
self.fields[field.name] = field
return True
[docs] def appendField(self, field):
'''Append FCSXMLField, fails if field object with same name already exists'''
if field.__class__.__name__ != "FCSXMLField":
self.logger("appendField() Passed invalid data! Expects FCSXMLField", 'error')
return False
if field.name in self.fields:
self.logger("appendField() Field with name: %s already exists!" % field.name, 'error')
return False
else:
self.fields[field.name] = field
[docs] def removeFieldWithName(self, fieldName):
'''Remove FCSXMLField object registered with passed field name'''
if fieldName.__class__.__name__ == "FCSXMLField":
fieldName = fieldName.name
if fieldName in self.fields:
del self.fields[fieldName]
self.logger("removeFieldWithName() Field with name: %s removed!" % fieldName, "info")
return True
else:
return False
[docs] def fieldNameForDBFieldName(self,dbname):
'''Returns a field name for a dbname
(FCS Read XML field name from FCSVR_CLIENT field name'''
self.logger("Retrieving field name for dbname:%s" % dbname,'debug')
## Run our fcsvr_client command.
fcsvrCMDTXT = "'%s' getmd /field/%s --xml" % (self.pathToFCSVRClient,dbname)
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client command: fcsvr_client getmd /field/%s --xml" % dbname,'debug')
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger("Could not parse output from fcsvr_client command: fcsvr_client getmd /field/%s --xml" % dbname,'error')
return False
try:
fieldName = ""
for rootElement in myDom.childNodes[0].childNodes:
self.logger("Nodename: %s" %rootElement.nodeName,'debug')
if rootElement.nodeName == "values":
for value in rootElement.getElementsByTagName("value"):
valueID = value.attributes["id"].value
#self.logger("valueID: %s" % valueID,'debug')
if valueID == "FIELD_NAME":
fieldName = value.childNodes[1].childNodes[0].data
break
except:
self.logger("Uncaught exception reading field name for dbname:%s" % dbname,'debug')
return False
if fieldName:
self.logger("Found fieldname: %s for dbname: %s"% (fieldName,dbname),'debug')
return fieldName
else:
return False
[docs] def dataTypeForDBFieldName(self,dbname):
'''Returns a dataType value for field with dbname (we use FCSXMLField Object
and self.dbDataTypeForDBFieldName for this)
'''
self.logger("dataTypeForDBFieldName() Retrieving dataType for dbname:'%s'" % (dbname),'debug')
myField = self.initFieldWithDBName(dbname)
return myField.dataType
[docs] def dbDataTypeForDBFieldName(self,dbname):
''' Returns a datatype for a dbname '''
self.logger("dbDataTypeForDBFieldName() Retrieving dbDataType for dbname:'%s'" % (dbname),'debug')
myField = self.initFieldWithDBName(dbname)
return myField.dbDataType
[docs] def dbFieldNameForFieldName(self,fieldName):
'''Returns a dbFieldName from a field name
(FCSVR_CLIENT field name from FCS Read XML field name'''
self.logger('Retrieving dbFieldName for field: %s' %fieldName,'debug')
theField = self.initFieldWithFieldName(fieldName)
if theField:
return theField.dbname
else:
raise FCSFieldNotFoundError(fieldName)
[docs] def initFieldWithDBName(self,dbname):
'''Returns a FCSXMLField object from a dbname.Uses fcsvr_client
to retrieve all the necessary values.'''
self.logger("initFieldWithDBName() Constructing field with dbname:%s" % dbname,'debug')
## URL encode our fieldname
encodedDBName = urllib.quote(dbname)
## Run our fcsvr_client command.
fcsvrCMDTXT = "'%s' getmd /field/%s --xml" % (self.pathToFCSVRClient,encodedDBName)
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("initFieldWithDBName() fcsvr_client command: fcsvr_client getmd /field/%s --xml" % dbname,'debug')
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger("Could not parse output from fcsvr_client command: fcsvr_client getmd /field/%s --xml" % dbname,'error')
return False
try:
myField = FCSXMLField()
for rootElement in myDom.childNodes[0].childNodes:
##self.logger("Nodename: %s" %rootElement.nodeName,'debug')
if rootElement.nodeName == "values":
for value in rootElement.getElementsByTagName("value"):
valueID = value.attributes["id"].value
#self.logger("valueID: %s" % valueID,'debug')
if valueID == "FIELD_NAME":
myField.name = value.childNodes[1].childNodes[0].data
if valueID == "FIELD_DATA_TYPE":
myField.setDBDataType(value.childNodes[1].childNodes[0].data)
if valueID == "FIELD_ID":
myField.dbname = value.childNodes[1].childNodes[0].data
except:
self.logger("Uncaught exception reading field name for dbname:%s" % dbname,'debug')
return False
if myField and myField.dbname == dbname:
self.logger("Found field for dbname: %s, dataType:%s"% (dbname,myField.dataType),'debug')
return myField
elif not myField.dbname == dbname:
raise RuntimeError("Constructed field does not match dbname:%s" % dbname)
else:
raise FCSFieldNotFoundError(dbname=dbname)
[docs] def initFieldWithFieldName(self,fieldName):
'''Returns a FCSXMLField object from a field name. Uses fcsvr_client
to retrieve all the necessary values.'''
self.logger("Constructing field with name:%s" % fieldName,'debug')
## Run our fcsvr_client command.
fcsvrCMDTXT = '"%s" search --crit "%s" /field --xml' % (self.pathToFCSVRClient,fieldName)
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command: fcsvr_client search --crit "%s" /field --xml' % fieldName,'debug')
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: fcsvr_client search --crit "%s" /field --xml' % fieldName,'error')
return False
didComplete = False
#try:
testFieldName = ""
searchRoot = myDom.childNodes[0]
searchResultCount = 0
exactMatches = []
caseInsensitiveMatches = []
partialMatches = []
for searchResult in searchRoot.childNodes:
## self.logger("initFieldWithFieldName() Searching nodename: %s" % searchResult.nodeName,'debug')
if searchResult.nodeName == "values":
didFindMatch = False
matchType = ""
dbFieldName = ""
for rootValue in searchResult.getElementsByTagName("value"):
## self.logger("initFieldWithFieldName() rootValue nodename: %s" % rootValue.nodeName,'debug')
rootValueID = rootValue.attributes["id"].value
## self.logger("initFieldWithFieldName() Searching rootValueID: %s" % rootValueID,'debug')
if rootValueID == "COMPLETE":
didComplete = rootValue.childNodes[1].childNodes[0].data
if not didComplete:
break;
searchResultCount += 1
elif rootValueID == "METADATA":
myField = FCSXMLField()
for value in rootValue.getElementsByTagName("values")[0].childNodes:
if not value.nodeType == 1:
##self.logger("initFieldWithFieldName() Skipping nodetype: %s" % value.nodeType,'debug')
continue
try:
valueID = value.attributes["id"].value
##self.logger("initFieldWithFieldName() - Found METADATA value node"
##" with ID: %s" % valueID,'debug')
except:
continue
if valueID == "FIELD_NAME":
myField.name = value.childNodes[1].childNodes[0].data
self.logger("initFieldWithFieldName() Testing field with name: %s" % myField.name,'debug')
if not myField.name:
break;
elif myField.name == fieldName:
didFindMatch = True
matchType = "exact"
elif myField.name.lower() == fieldName.lower():
didFindMatch = True
matchType = "caseinsensitive"
else:
if len(myField.name) > len(fieldName):
if testFieldName[0:len(fieldName)].lower() == fieldName.lower():
didFindMatch = True
matchType = "substring"
else:
if fieldName[0:len(myField.name)].lower() == myField.name.lower():
didFindMatch = True
matchType = "substring"
elif valueID == "FIELD_ID":
myField.dbname = value.childNodes[1].childNodes[0].data
elif valueID == "FIELD_DATA_TYPE":
dbDataType = value.childNodes[1].childNodes[0].data
## if datatype is ktAtom, then we are a system field.
if dbDataType == 'KtAtom' or dbDataType == 'KtAddress':
continue
try:
myField.setDBDataType(dbDataType)
except:
self.logger('An unknown error occurred setting dataType:%s'
', skipping field.)' % dbDataType,'warning')
continue
if didFindMatch and myField:
self.logger("initFieldWithFieldName() Found match:%s for fieldname: %s" % (matchType,myField.name),'debug')
if matchType == "exact":
exactMatches.append(myField)
elif matchType == "caseinsensitive":
caseInsensitiveMatches.append(myField)
elif matchType == "substring":
partialMatches.append(myField)
#except:
# self.logger("Uncaught exception reading field name for dbFieldName:%s" % dbFieldName,'debug')
# return False
myField = ""
## analyze our findings
if len(exactMatches) == 1:
self.logger("Found exact match for field name:%s, dbname:%s" % (fieldName,exactMatches[0].dbname),'warning')
myField = exactMatches[0]
elif len(exactMatches) > 1:
self.logger("Found %s exact matches for field name:%s, determining best result" % (len(exactMatches),fieldName),'warning')
result = ""
## Determine the most appropriate match based on DB Name, order of preference:
## PA_MD_CUST_, CUST_
currentResultPriority = 0
for field in exactMatches:
dbname = field.dbname
## First look for an exact match for our field, using our field name
## with caps.
idealPartialDBName = field.name.replace(" ","_").upper()
##self.logger('initFieldWithFieldName() idealPartialName:%s, %s'
##% (idealPartialDBName,dbname[0:5+len(idealPartialDBName)]),'debug')
if dbname[0:11+len(idealPartialDBName)] == "PA_MD_CUST_%s" % idealPartialDBName:
self.logger("Found match for ideal DB Name: %s for field name:%s"
% (field.dbname,field.name),'debug')
myResultPriority = 10
if myResultPriority > currentResultPriority:
result = field
currentResultPriority = myResultPriority
elif dbname[0:5+len(idealPartialDBName)] == "CUST_%s" % idealPartialDBName:
self.logger("Found match for ideal DB Name: %s for field name:%s"
% (dbname,field.name),'debug')
myResultPriority = 9
if myResultPriority > currentResultPriority:
result = field
currentResultPriority = myResultPriority
elif dbname[0:11] == "PA_MD_CUST_":
self.logger("Found PA_MD_CUST type field:%s for field name:%s"
% (dbname,field.name),'debug')
result = field
myResultPriority = 8
if myResultPriority > currentResultPriority:
result = field
currentResultPriority = myResultPriority
elif dbname[0:5] == "CUST_":
self.logger("Found CUST_ type field:%s for field name:%s"
% (dbname,field.name),'debug')
result = field
myResultPriority = 7
if myResultPriority > currentResultPriority:
result = field
currentResultPriority = myResultPriority
if result:
myField = result
else:
self.logger("Returning last result:%s for field name:'%s'" % (dbname,fieldName),'warning')
myField = field
elif len(caseInsensitiveMatches) == 1:
myField = caseInsensitiveMatches[0]
elif len(caseInsensitiveMatches) > 1:
self.logger("Found %s matches for field name:%s, returning first result!" % (len(exactMatches),fieldName),'warning')
myField = caseInsensitiveMatches[0]
elif len(partialMatches) == 1:
self.logger("Found more than one partial match for field name:%s, returning first result" % fieldName,'warning')
myField = partialMatches[0]
elif len(partialMatches) > 1:
self.logger("Found %s matches for field name:%s, returning first result!" % (len(exactMatches),fieldName),'warning')
myField = partialMatches[0]
else:
raise RuntimeError("An error occured while determining preferred value from matches for fieldName:%s!" % fieldName)
if myField:
self.logger("Found dbFieldName: %s for fieldName: %s" % (myField.dbname,myField.name),'debug')
return myField
else:
return False
[docs] def assetWithTitle(self,title,mdSet=''):
'''Returns a new FCSVRClient object based upon search results'''
title = title.replace('`',"'").replace('"',"'")
self.logger('Retrieving Asset for name:%s' % title,'debug')
## Run our fcsvr_client command.
'''
fcsvrCMDTXT = ('"%s" search --crit "%s" /asset --xml'
% (self.pathToFCSVRClient,title))
'''
#### Generate our search XML file
## Create our title field
myTitleField = FCSXMLField(name='Title',dbname='CUST_TITLE',value=title)
XMLSearchFilePath = self.generateSearchXML(fields=[myTitleField])
fcsvrCMDTXT = ('"%s" search /asset --xml --xmlcrit < "%s"'
% (self.pathToFCSVRClient,XMLSearchFilePath))
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command:\'%s\'' % fcsvrCMDTXT,'debug')
if not self.keepFiles:
os.remove(XMLSearchFilePath)
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Delete our temp search file
if not self.keepFiles:
os.remove(XMLSearchFilePath)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: \'%s\''
% fcsvrCMDTXT,'error')
raise RuntimeError('fcsvr_client returned unexpected results!')
didComplete = False
#try:
matches = []
searchRoot = myDom.childNodes[0]
searchResultCount = 0
## Iterate through the DOM and extract our results.
for searchResult in searchRoot.childNodes:
##self.logger("productionWithTitle() Searching nodename: %s" % searchResult.nodeName,'debug')
if searchResult.nodeName == 'values':
searchResultDict = {}
didFindMatch = False
for rootValue in searchResult.getElementsByTagName('value'):
## This is the result level, here for each result
##self.logger("productionWithTitle() rootValue nodename: %s" % rootValue.nodeName,'debug')
rootValueID = rootValue.attributes['id'].value
##self.logger('productionWithTitle() Searching rootValueID: %s' % rootValueID,'debug')
if rootValueID == 'COMPLETE':
didComplete = rootValue.childNodes[1].childNodes[0].data
if not didComplete:
break;
searchResultCount += 1
elif rootValueID == 'METADATA':
for value in rootValue.getElementsByTagName('value'):
valueID = value.attributes['id'].value
##self.logger('assetWithTitle() Searching value: %s' % valueID,'debug')
if valueID == 'CUST_TITLE':
testTitle = value.childNodes[1].childNodes[0].data
if not testTitle:
break;
elif testTitle == title:
didFindMatch = True
searchResultDict['matchType'] = 'exact'
elif testTitle.strip() == title.strip():
didFindMatch = True
searchResultDict['matchType'] = 'exact_whitespace'
elif testTitle.lower() == title.lower():
didFindMatch = True
searchResultDict['matchType'] = 'caseinsensitive'
elif testTitle.lower().strip() == title.lower().strip():
didFindMatch = True
searchResultDict['matchType'] = 'caseinsensitive_whitespace'
elif testTitle in title:
didFindMatch = True
searchResultDict['matchType'] = 'substring'
else:
if len(testTitle) > len(title):
if testTitle[0:len(title)].lower() == title.lower():
didFindMatch = True
searchResultDict['matchType'] = 'substring'
elif testTitle.lower() in title.lower():
didFindMatch = True
searchResultDict['matchType'] = 'substring'
else:
if title[0:len(testTitle)].lower() == testTitle.lower():
didFindMatch = True
searchResultDict['matchType'] = 'substring'
elif testTitle in title:
didFindMatch = True
searchResultDict['matchType'] = 'substring'
elif valueID == 'ASSET_TYPE':
searchResultDict['ASSET_TYPE'] = value.childNodes[1].childNodes[0].data
elif valueID == 'ASSET_NUMBER':
searchResultDict['ASSET_NUMBER'] = value.childNodes[1].childNodes[0].data
if didFindMatch and 'ASSET_NUMBER' in searchResultDict:
matches.append(searchResultDict)
#except:
# self.logger("Uncaught exception reading field name for dbFieldName:%s" % dbFieldName,'debug')
# return False
#### Analyze our findings
## If only one match was found, return it
theMatch = ''
if len(matches) == 1:
self.logger('Found Asset: /asset/%s for search string: \'%s\''
% (matches[0]['ASSET_NUMBER'],title),'detailed')
self.logger(' Match Type: %s' % matches[0]['matchType'],'debug')
theMatch = matches[0]
elif not matches:
self.logger('Found no assets matching search string: \'%s\'' % title,'error')
raise FCSEntityNotFoundError(entityType='asset',entityTitle=title)
elif matches > 1:
self.logger('Found %s assets matching search string: \'%s\''
% (len(matches),title),'detailed')
rankedMatches = []
topScore = 0
topScoreConflict = False
## Iterate through our matches for ranking.
for result in matches:
searchScore = 0
if result['matchType'] == 'exact':
searchScore += 50
elif result['matchType'] == 'exact_whitespace':
searchScore += 45
elif result['matchType'] == 'caseinsensitive':
searchScore += 25
elif result['matchType'] == 'caseinsensitive_whitespace':
searchScore += 20
elif result['matchType'] == 'substring_whitespace':
searchScore += 2
elif result['matchType'] == 'substring':
searchScore += 1
if 'ASSET_TYPE' in result and mdSet and result['ASSET_TYPE'].lower() == mdSet.lower():
searchScore += 50
result['searchScore'] = searchScore
## Insert it in the appropriate rank, based on searchScore, break when we
## get to an object who's score is less then ours
count=0
while count < len(rankedMatches):
if rankedMatches[count]['searchScore'] < result['searchScore']:
break
elif (rankedMatches[count]['searchScore'] == result['searchScore']
and result['searchScore'] == topScore):
self.logger('assetWithTitle() Found top score conflict: %s asset:'
' /project/%s' % (topScore,result['ASSET_NUMBER']),'debug')
topScoreConflict = True
count += 1
else:
count += 1
## insert our object
if count == 0:
if result['searchScore'] > topScore:
self.logger('assetWithTitle() Found new top score: %s asset: /asset/%s'
% (result['searchScore'],result['ASSET_NUMBER']),'debug')
topScore = result['searchScore']
topScoreConflict = False
rankedMatches.insert(count,result)
if topScoreConflict:
self.logger('Found more than one Asset with same search score: %s,'
' returning first result! (/asset/%s) '
% (topScore,rankedMatches[0]['ASSET_NUMBER']),'warning')
theMatch = rankedMatches[0]
else:
self.logger('Found more than one Asset, returning result with '
'highest search score:%s! (/asset/%s) '
% (rankedMatches[0]['searchScore'],
rankedMatches[0]['ASSET_NUMBER'])
,'detailed')
theMatch = rankedMatches[0]
if theMatch:
theAsset = FCSVRClient()
theAsset.entityType = 'asset'
theAsset.entityID = theMatch['ASSET_NUMBER']
theAsset.entityMetadataSet = theMatch['ASSET_TYPE']
else:
raise RuntimeError('No asset found for title: \'%s\'' % title)
return theAsset
[docs] def productionWithTitle(self,title,mdSet="",match='exact'):
'''Returns a new FCSVRClient object based upon search results. Matching
parameters can be defined as either "exact" or "substring"'''
title = title.replace("`","'").replace('"',"'")
if not title or title == " ":
msg = 'Could not load production, an empty title was provided!'
self.logger(msg,'error')
raise FCSProductionLoadError(msg)
self.logger("Retrieving Production for name:%s" % title,'debug')
## Run our fcsvr_client command.
##fcsvrCMDTXT = '"%s" search --crit "%s" /project --xml' % (self.pathToFCSVRClient,title)
#### Generate our search XML file
## Create our title field
myTitleField = FCSXMLField(name='Title',dbname='CUST_TITLE',value=title)
XMLSearchFilePath = self.generateSearchXML(fields=[myTitleField],match=match)
fcsvrCMDTXT = ('"%s" search /project --xml --xmlcrit < "%s"'
% (self.pathToFCSVRClient,XMLSearchFilePath))
fcsvrCMD = subprocess.Popen(fcsvrCMDTXT,shell=True,stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command: fcsvr_client search /project --xml'
' --xmlcrit < "%s"' % XMLSearchFilePath,'debug')
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: '
'fcsvr_client search /project --xml --xmlcrit < "%s"' % XMLSearchFilePath,
'error')
raise RuntimeError("fcsvr_client returned unexpected results!")
didComplete = False
#try:
matches = []
searchRoot = myDom.childNodes[0]
searchResultCount = 0
## Iterate through the DOM and extract our results.
for searchResult in searchRoot.childNodes:
##self.logger("productionWithTitle() Searching nodename: %s" % searchResult.nodeName,'debug')
if searchResult.nodeName == "values":
searchResultDict = {}
didFindMatch = False
for rootValue in searchResult.getElementsByTagName("value"):
## This is the result level, here for each result
##self.logger("productionWithTitle() rootValue nodename: %s" % rootValue.nodeName,'debug')
rootValueID = rootValue.attributes["id"].value
##self.logger("productionWithTitle() Searching rootValueID: %s" % rootValueID,'debug')
if rootValueID == "COMPLETE":
didComplete = rootValue.childNodes[1].childNodes[0].data
if not didComplete:
break;
searchResultCount += 1
elif rootValueID == "METADATA":
for value in rootValue.getElementsByTagName("value"):
valueID = value.attributes["id"].value
self.logger("productionWithTitle() Searching value: %s" % valueID,'debug')
if valueID == "CUST_TITLE":
testTitle = value.childNodes[1].childNodes[0].data
if not testTitle:
break;
elif testTitle == title:
didFindMatch = True
searchResultDict["matchType"] = "exact"
elif testTitle.lower() == title.lower():
didFindMatch = True
searchResultDict["matchType"] = "caseinsensitive"
else:
if len(testTitle) > len(title):
if testTitle[0:len(title)].lower() == title.lower():
didFindMatch = True
searchResultDict["matchType"] = "substring"
else:
if title[0:len(testTitle)].lower() == testTitle.lower():
didFindMatch = True
searchResultDict["matchType"] = "substring"
elif valueID == "PROJECT_TYPE":
searchResultDict["PROJECT_TYPE"] = value.childNodes[1].childNodes[0].data
elif valueID == "PROJECT_NUMBER":
searchResultDict["PROJECT_NUMBER"] = value.childNodes[1].childNodes[0].data
if didFindMatch and "PROJECT_NUMBER" in searchResultDict:
matches.append(searchResultDict)
#except:
# self.logger("Uncaught exception reading field name for dbFieldName:%s" % dbFieldName,'debug')
# return False
#### Analyze our findings
## If only one match was found, return it
theMatch = ""
if len(matches) == 1:
self.logger("Found Production: /project/%s for search string: '%s'" % (matches[0]["PROJECT_NUMBER"],title),"detailed")
theMatch = matches[0]
elif not matches:
self.logger("Found no productions matching search string: '%s'" % title,'error')
raise RuntimeError("No production found for title: '%s'" % title)
elif matches > 1:
self.logger("Found %s productions matching search string: '%s'" % (len(matches),title),"detailed")
rankedMatches = []
topScore = 0
topScoreConflict = False
## Iterate through our matches for ranking.
for result in matches:
searchScore = 0
if result["matchType"] == "exact":
searchScore += 50
elif result["matchType"] == "caseinsensitive":
searchScore += 25
elif result["matchType"] == "substring":
searchScore += 1
if "PROJECT_TYPE" in result and mdSet and result["PROJECT_TYPE"].lower() == mdSet.lower():
searchScore += 50
result["searchScore"] = searchScore
## Insert it in the appropriate rank, based on searchScore, break when we
## get to an object who's score is less then ours
count=0
while count < len(rankedMatches):
if rankedMatches[count]["searchScore"] < result["searchScore"]:
break
elif rankedMatches[count]["searchScore"] == result["searchScore"] and result["searchScore"] == topScore:
self.logger("productionWithTitle() Found top score conflict: %s production: /project/%s" % (topScore,result["PROJECT_NUMBER"]),'debug')
topScoreConflict = True
count += 1
else:
count += 1
## insert our object
if count == 0:
if result["searchScore"] > topScore:
self.logger("productionWithTitle() Found new top score: %s production: /project/%s" % (result["searchScore"],result["PROJECT_NUMBER"]),'debug')
topScore = result["searchScore"]
topScoreConflict = False
rankedMatches.insert(count,result)
if topScoreConflict:
self.logger("Found more than one Production with same search score: %s, returning first result! (/project/%s) " % (topScore,rankedMatches[0]["PROJECT_NUMBER"]),'warning')
theMatch = rankedMatches[0]
else:
self.logger("Found more than one Production, returning result with higest search score:%s! (/project/%s) " % (rankedMatches[0]["searchScore"],rankedMatches[0]["PROJECT_NUMBER"]),"detailed")
theMatch = rankedMatches[0]
if theMatch:
theProduction = FCSVRClient()
theProduction.entityType = "project"
theProduction.entityID = theMatch["PROJECT_NUMBER"]
theProduction.entityMetadataSet = theMatch["PROJECT_TYPE"]
else:
raise RuntimeError("No production found for title: '%s'" % title)
return theProduction
[docs] def createProduction(self,title="",mdSet="",parentProjectAddress="",setMD=False):
'''Creates a production. '''
## Method to create a production, If our entity type is not 'project',
## and we are not passed a title abort. If our entity type is 'project',
## try to read our title from our fields.
if not mdSet:
mdSet = self.defaultProductionMetadataSet
if not mdSet:
self.logger("Could not create production: no metadata set specified!",'error')
raise FCSProductionLoadError("Could not create production! No metadata set specified!")
if not title:
title = self.valueForField("Title")
if not title:
self.logger("Could not create production: no title specified!",'error')
raise FCSProductionLoadError("Could not create production! No title specified!")
## if we are currently a project type asset, load ourselves, otherwise,
## return a duplicated object.
if self.entityType == "project":
obj = self
else:
obj = copy.copy(self)
## If setMD is set to true and we have no fields at this point, throw a
## warning and change setMD to false
if setMD and not self.fields:
self.logger("Creating production with metadata, but no fields are loaded! (No metadata to set)",'warning')
setMD = False
## Set our title field
titleField = FCSXMLField(name="Title",dbname="CUST_TITLE",value=title)
obj.setField(titleField)
## Construct our fcsvr_client arguments
cmdArgs = ' create /project --type %s' % mdSet
if parentProjectAddress:
cmdArgs += ' --linkparentaddr %s --linkparentlinktype 16' % parentProjectAddress
## If we are importing MD, insert our file args
## note 2/7/09: this is temporarily disable until I fix the XML output
## to ensure proper formatting during asset creation, for now we're
## just calling setMD after the asset is created, which works fine.
'''
tempFilePath = ""
if setMD:
tempFilePath = self.generateTempXMLFile()
if os.path.isfile(tempFilePath):
cmdArgs += ' --xml "%s"' % tempFilePath
else:
self.logger("XML File does not exist at path:'%s', cannot import metadata!",'error')
cmdArgs += ' CUST_TITLE="%s"' % (title.replace('"',"'"))
else:
cmdArgs += ' CUST_TITLE="%s"' % (title.replace('"',"'"))
'''
cmdArgs += ' CUST_TITLE="%s"' % (title.replace('"',"'"))
if not os.geteuid() == 0:
if not self.useSudo:
self.logger("Could not set Metadata, root privileges are required!",'error')
raise FCSProductionLoadError("Updating metadata requires root privileges!")
useSudo = True
else:
useSudo = False
if useSudo:
fcsvrCMDTXT = "/usr/bin/sudo '%s' %s" % (self.pathToFCSVRClient,cmdArgs)
else:
fcsvrCMDTXT = "'%s' %s" % (self.pathToFCSVRClient,cmdArgs)
self.logger('fcsvrCMD:\n %s' % fcsvrCMDTXT,'debug')
## run fcsvr_client
fcsvrCMD = subprocess.Popen('%s' % fcsvrCMDTXT ,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client output: %s" % fcsvrCMD_STDOUT,'debug')
if not fcsvrCMD.returncode == 0:
errorString = '%s\n%s' % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR)
self.logger("FCSVRClient Error: %s" % errorString,'error')
raise FCSProductionLoadError('Could not create production!',errorString,fcsvrCMDTXT)
## Resolve our entity ID from STDOUT
try:
match = re.match("/project/(\d+)",fcsvrCMD_STDOUT)
projectID = match.group(1)
except:
self.logger("Could not retrieve project id from string: %s" % fcsvrCMD_STDOUT,'error')
raise FCSProductionLoadError('Could not find /project/id in results!',fcsvrCMD_STDOUT,'fcsvrCMDTXT')
self.logger("Created new Production with ID:%s" % projectID,'debug')
obj.entityID = projectID
obj.entityType = "project"
obj.entityMetadataSet = mdSet
## If we have a temp file, delete it
## Disabled for now in lieu of setMD
'''
if not self.debug:
os.remove(tempFilePath)
'''
if setMD:
obj.setMD()
return obj
[docs] def removeMemberFromProduction(self,member='',memberPath='',production='',
productionPath='',tempProductionMDSet=''):
''' Removes a member from a production. Accepts either an FCSVRClient object
(member) or a FCS entity path '/asset/10' (memberPath) in a addition
to a FCSVRClient object with project entityType (production) or a
production path '/project/10' (productionPath). If calling instance
is either a production or an asset, you can omit the appropriate arguments.
We also accept tempProductionMDSet: which is the md set used to create
a temporary production. (We do this because FCS can't explicitely delete an
existing link, but it CAN relocate links).'''
## Resolve our child (asset or production) and parent (production)
childObject = ''
parentObject = ''
## Resolve our child (asset or production)
if member:
childObject = member
elif memberPath:
self.logger("Resolving member from entityPath:%s" % memberPath,'debug')
pathMatch = re.match('\/(.*)\/(.*)',memberPath)
if pathMatch:
memberType = pathMatch.groups()[0]
memberID = pathMatch.groups()[1]
childObject = FCSVRClient(entityType=memberType,id=memberID)
else:
if (self.entityType == 'asset' or self.entityType == 'project') and self.entityID:
childObject = self
## Resolve our parent (production)
if production:
parentObject = production
elif productionPath:
self.logger("Resolving parent production from entityPath:%s" % productionPath,'debug')
pathMatch = re.match('\/(.*)\/(.*)',memberPath)
if pathMatch:
memberType = pathMatch.groups()[0]
memberID = pathMatch.groups()[1]
parentObject = FCSVRClient(entityType=memberType,id=memberID)
else:
raise RuntimeError('Could not remove member from production, parentPath:%s is invalid!')
else:
self.logger("Resolving parent production from local instance!",'debug')
if self.entityType == 'project' and self.entityID:
##print "self:%s" % self
parentObject = self
##print "parent:%s" % parentObject
##print "parentObject.entityPath:%s" % parentObject.debug
else:
raise RuntimeError('Could not remove member from production, parentPath:%s is invalid!')
## At this point we should have both a child and parent object, perform
## sanity checks.
## Verify we have a parent object and it is a production
if not parentObject:
self.logger('removeMemberFromProduction() cannot continue: could not resolve parent production!','error')
raise RuntimeError('Cannot continue: could not resolve parent production!')
elif parentObject.entityType != 'project':
self.logger('removeMemberFromProduction() cannot continue: provided parent is not a production!','error')
raise RuntimeError('Cannot continue: provided parent is not a production!')
## Verify we have a child object.
if not childObject:
self.logger('removeMemberFromProduction() cannot continue: could not resolve member!','error')
raise RuntimeError('removeMemberFromProduction() cannot continue: could not resolve member!','error')
## Verify the child and parent aren't referencing the same entity.
parentEntityPath = parentObject.entityPath()
childEntityPath = childObject.entityPath()
if parentEntityPath == childEntityPath:
self.logger('removeMemberFromProduction() cannot continue: provided '
' member and production are the same:%s!'
% parentObject.entityPath,'error')
raise RuntimeError('removeMemberFromProduction() cannot continue: provided '
' member and production reference the same entity: %s!'
% parentObject.entityPath,'error')
## At this point we have a legitimate child and parent, proceed to remove
## child from parent.
## Get current production members
try:
currentMembers = parentObject.productionMemberAddresses()
except:
currentMembers = []
if not childEntityPath in currentMembers:
self.logger('Could not remove entity:%s from production:%s, %s is not a '
'current member!' % (childEntityPath,
parentEntityPath,
childEntityPath),'error')
return False
## Get a list of all our child's current parents
childCurrentParents = childObject.parentProductions()
## Create a new temp production and use --movelink, which will destroy
## all of the assets production memberships,we then rebuild all of
## the memberships, short of the production which we are being removed
## from. Necessarily convoluted until fcsvr_client can explicitely remove
## links, but it works.
tempProd = FCSVRClient(entityType='project')
self.logger('removeMemberFromProduction() Creating temporary production'
' with mdset:%s' % tempProductionMDSet)
tempProd.createProduction(title='_temp_',mdSet=tempProductionMDSet)
tempProd.fcsvr_client_make_link(linkType=1,
parentPath=tempProd.entityPath(),
childPath=childEntityPath,
moveLink=True)
## Delete the temp production, the asset is now no longer a member of
## ANY productions.
tempProd.delete()
## Iterate through each pre-exsisting production link, and
## re-establish membership (provided it is not the provided parent
## production, which we will NOT rejoin). (We do this
## this way because fcsvr_client does not have support to explicitely
## remove links, all we have is --movelink)
for parentPath in childCurrentParents:
## if we match our current production, which we are being removed
## from, skip it.
if parentEntityPath == parentPath:
print "Removing asset:%s from production:%s" % (childEntityPath,
parentPath)
continue
## re-establish membership to the current parentPath
self.fcsvr_client_make_link(linkType=1,
parentPath=parentPath,
childPath=childEntityPath,
moveLink=False)
## Flush our caches
self.flushCaches()
return True
[docs] def addMemberToProduction(self,member="",memberPath="",production="",productionPath="",moveLink=False):
''' Adds entity to the specified production. Accepts either a FCSVRClient
object (member) or a FCS entity path '/asset/10' (memberPath) in addition
to a FCSVRClient object with entityType 'production', or a production
path '/project/10'. If the local entity is either the member to be added,
or is a production, then you can omit that information.
For instance, if we are a loaded production, I can call:
self.addMemberToProduction(memberPath='/asset/10'), then we will add
asset with ID 10 to our own membership. Likewise, if we call:
self.addMemberToProduction(productionPath='/project/12', then we will
add ourself (a production) as a child production to '/project/12'
'''
## If no member information is given, assume that we are the member
if not member and not memberPath:
theMemberPath = self.entityPath()
elif member and not memberPath:
theMemberPath = member.entityPath()
elif memberPath:
theMemberPath = memberPath
if not production and not productionPath and self.entityType == 'project':
theProductionPath = self.entityPath()
elif production and not productionPath and production.entityType == 'project':
theProductionPath = production.entityPath()
elif productionPath:
theProductionPath = productionPath
## Make sure that our memberPath references either an asset or project
## Address
try:
memberType = theMemberPath.split('/')[1]
if not memberType == "project" and not memberType == "asset":
raise
except:
self.logger('Member path: %s is not a project or asset, cannot continue!' % theMemberPath,'error')
return False
## Make sure that our productionPath references a project address
try:
productionType = theProductionPath.split('/')[1]
if not productionType == "project":
raise
except:
self.logger('Production path: %s is not a project or asset, cannot continue!' % theProductionPath,'error')
return False
## Make sure we never add ourself to ourself
if theProductionPath == theMemberPath:
self.logger('Cannot add Member Path: %s to Production path: %s, one cannot add one to oneself!' % (theMemberPath,theProductionPath),'error')
return False
''' movelink is now a passable argument.
## Create our link
if memberType == 'project':
moveLink = True
else:
moveLink = False
'''
return self.fcsvr_client_make_link(1,parentPath=theProductionPath,childPath=theMemberPath,moveLink=moveLink)
[docs] def addAssetToProduction(self,asset="",assetID="",assetPath="",productionPath="",productionID=""):
''' Adds an asset to production,
07/22/10: this is depricated, use addMemberToProduciton'''
## Resolve our asset information
if not asset and not assetID and not assetPath:
self.logger("addAssetToProduction() No asset information provided, aborting",'error')
return False
if asset:
if assetID and not asset.entityID == assetID:
self.logger("addAssetToProduction() provided conflicting asset:%s and assetID:%s" % (asset.entityID,assetID),'error')
return False
if assetPath and not asset.entityPath() == assetPath:
self.logger("addAssetToProduction() provided conflicting asset:%s and assetPath:%s" % (asset.entityPath(),assetPath),'error')
return False
assetPath = asset.entityPath()
assetID = asset.entityID
elif assetID:
if assetPath and not "/asset/%s" % assetID == assetPath:
self.logger("addAssetToProduction() provided conflicting assetID:%s and assetPath:%s" % (assetID,assetPath),'error')
return False
else:
assetPath = "/asset/%s" % assetID
## Resolve our production information
if not productionPath and not productionID and not self.entityType == "project":
self.logger("addAssetToProduction() No production information provided, aborting",'error')
return False
elif not productionPath and not productionID and self.entityType == "project":
productionID = self.entityID
productionPath = self.entityPath()
elif productionID:
if productionPath and not "/project/%s" % productionID == productionPath:
self.logger("addAssetToProduction() provided conflicting assetID:%s and assetPath:%s" % (assetID,assetPath),'error')
return False
assetPath = "/asset/%s" % assetID
## At this point we should have an assetPath and a productionPath
if not assetPath and not productionPath:
self.logger("addAssetToProduction() Could not resolve assetPath or productionPath,cannot continue!",'error')
return False
## Create our link
self.logger("Adding asset:%s to production:%s" % (assetPath,productionPath),'error')
return self.fcsvr_client_make_link(1,parentPath=productionPath,childPath=assetPath)
[docs] def assetsFromProduction(self,productionID="",productionTitle="",recurse=False,mdSet=""):
'''Function which returns a list of associated FCSVRClient asset objects. If
recurse is set to true, we will return assets for subproductions as well.
If mdSet is provided, we will return only assets which have the provided
metadataset.
'''
if not productionID and not productionTitle:
if self.entityType == "project" and self.entityID:
productionID = self.entityID
elif not self.entityType == "project":
self.logger("Loaded entity is not a project and no ID or Title specified!",'error')
raise RuntimeError("Loaded entity is not a project and no ID or Title specified!",'error')
else:
self.logger("Must provide production ID or Title to search!",'error')
raise RuntimeError("Loaded entity is not a project and no ID or Title specified!",'error')
elif not productionID and productionTitle:
try:
production = self.productionWithTitle(title=productionTitle)
productionID = production.entityID
except:
raise RuntimeError("Could not load production with Title: %s" % productionTitle)
if not productionID:
self.logger("")
raise RuntimeError("No production could be found!")
## Retrieve all assets in the production.
assetAddresses = self.productionAssetAddresses(productionID=productionID,recurse=recurse)
assets = []
## Iterate through each returned asset address and instantiate.
for assetAddress in assetAddresses:
regexMatch = re.match('/(.*)/(.*)',assetAddress)
memberType = regexMatch.group(1)
memberID = regexMatch.group(2)
myAsset = FCSVRClient(id=memberID)
myAsset.initWithAssetID(memberID)
## If no mdSet parameter was passed, otherwise ensure that our
## mdSet matches.
if not mdSet:
assets.append(myAsset)
elif mdSet and myAsset.entityMetadataSet == mdSet:
assets.append(myAsset)
elif mdSet and not myAsset.entityMetadataSet == mdSet:
self.logger('assetsFromProduction() Asset: %s with mdSet: %s does not match'
' mdSet: %s, excluding!'
% (myAsset.entityPath(),myAsset.entityMetadataSet,mdSet),'debug')
return assets
[docs] def productionAssetAddresses(self,productionID="",productionTitle="",recurse=False,processedAddresses=""):
'''Function which returns asset addresses '/asset/12' from a production.
If recurse is set to true, we will recurse any nested productions and
collate their addresses. The processedAddresses parameter is an array
of previously processed production addresses to prevent infinite recursion
scenarios'''
if not productionID and not productionTitle:
if self.entityType == "project" and self.entityID:
productionID = self.entityID
elif not self.entityType == "project":
self.logger("Loaded entity is not a project and no ID or Title specified!",'error')
raise RuntimeError("Loaded entity is not a project and no ID or Title specified!",'error')
else:
self.logger("Must provide production ID or Title to search!",'error')
raise RuntimeError("Loaded entity is not a project and no ID or Title specified!",'error')
elif not productionID and productionTitle:
try:
production = self.productionWithTitle(title=productionTitle)
productionID = production.entityID
except:
raise RuntimeError("Could not load production with Title: %s" % productionTitle)
if not productionID:
self.logger("")
raise RuntimeError("No production could be found!")
memberAddresses = self.productionMemberAddresses(productionID=productionID,
productionTitle=productionTitle)
assetAddresses = []
productionAddresses = []
processedProductionAddresses = ["/project/%s" % productionID]
for memberAddress in memberAddresses:
regexMatch = re.match('/(.*)/(.*)',memberAddress)
memberType = regexMatch.group(1)
memberID = regexMatch.group(2)
if memberType == "asset":
if memberAddress not in assetAddresses:
assetAddresses.append(memberAddress)
elif (memberType == "project" and recurse
and memberAddress not in processedProductionAddresses):
memberAddresses.extend(self.productionMemberAddresses(productionID=memberID))
return assetAddresses
[docs] def productionMemberAddresses(self,productionID="",productionTitle=""):
'''Function which returns a list of associated entity addresses:
["/asset/12", "/project/11"]
'''
if not productionID and not productionTitle:
if self.entityType == "project" and self.entityID:
productionID = self.entityID
elif not self.entityType == "project":
self.logger("Loaded entity is not a project and no ID or Title specified!",'error')
raise RuntimeError("Loaded entity is not a project and no ID or Title specified!",'error')
else:
self.logger("Must provide production ID or Title to search!",'error')
raise RuntimeError("Loaded entity is not a project and no ID or Title specified!",'error')
elif not productionID and productionTitle:
try:
production = self.productionWithTitle(title=productionTitle)
productionID = production.entityID
except:
raise RuntimeError("Could not load production with Title: %s" % productionTitle)
if not productionID:
self.logger("")
raise RuntimeError("No production could be found!")
## Get our asset members.
assetAddresses = self.getValueForLinkType(value="ADDRESS",linkType=1,id=productionID,type="project")
## Get our production members.
try:
projectAddresses = self.getValueForLinkType(value="ADDRESS",linkType=16,id=productionID,type="project")
except:
projectAddresses = []
memberAddresses = []
try:
memberAddresses.extend(assetAddresses)
memberAddresses.extend(projectAddresses)
except:
pass
if len(memberAddresses) == 0:
self.logger("No member addresses were found for production with id:'%s'" % productionID,'detailed')
return memberAddresses
[docs] def assetsFromProject(self,projectID="",mdSet=''):
'''Function which returns a list of associated FCSVRClient asset objects. For
the provided FCP project.'''
assetAddresses = self.projectElementAddresses(id=projectID,addressType='asset')
assets = []
for assetAddress in assetAddresses:
regexMatch = re.match('/(.*)/(.*)',assetAddress)
memberType = regexMatch.group(1)
memberID = regexMatch.group(2)
myAsset = FCSVRClient(id=memberID,cfgParser=self.cfgParser)
## If no mdSet parameter was passed add our asset to the list,
## otherwise ensure that our mdSet matches.
if not mdSet:
assets.append(myAsset)
elif mdSet and myAsset.entityMetadataSet == mdSet:
assets.append(myAsset)
elif mdSet and not myAsset.entityMetadataSet == mdSet:
self.logger('assetsFromProject() Asset: %s with mdSet: %s does not match'
' mdSet: %s, excluding!'
% (myAsset.entityPath(),myAsset.entityMetadataSet,mdSet),'debug')
return assets
[docs] def projectElementAddresses(self,id='',addressType='asset'):
'''Function which returns a list of element addresses associated to an FCP
project file. If we are a loaded asset with '.fcp' in the file name,
we will work of our loaded instance, otherwise we will create a new
object. An optional argument, addressType, can be provided.
When set to 'asset', we return /asset/ addresses for any linked elements.
if set to 'element', we return /element addresses for all linked alements.
'''
## If we aren't provided an ID, check to make sure we are a loaded asset
if not id and self.entityID and self.entityType == 'asset':
myObject = self
elif id and self.entityID == id:
myObject = self
elif id and not self.entityID == id:
myObject = FCSVRClient(entityType='asset',id=id)
else:
msg = ('Could not load element addresses: no asset id provided and '
' local instance is not a project file!')
self.logger('projectElementAddresses() %s' % msg,'error')
raise FCSObjectLoadError(msg)
## Make sure our resolved object is an FCP project asset (based on filename)
fileName = myObject.valueForField('File Name')
if not fileName[-4:] == '.fcp' and not fileName[-4:] == u'.fcp':
msg = ('Could not load element addresses: no asset id provided and '
' local instance is not a project file!')
self.logger('projectElementAddresses() %s' % msg,'error')
raise FCSObjectLoadError(msg)
## At this point we have our FCP asset loaded at myObject, fetch all
## child links with type 1, but first, flush our cache.
self.flushCaches()
elementList = self.getValueForLinkType(value="ADDRESS",
linkType=1,
id=myObject.entityID,
type=myObject.entityType)
## If we are not filtering our results, return our current links
if addressType == 'element':
return elementList
## Iterate through our element list, create separate memberList based
## upon which elements have linked media assets.
memberList = []
self.logger('projectElementAddresses() found elements: %s, processing!'
% ','.join(elementList),'debug')
for elementAddress in elementList:
self.logger('projectElementAddresses() processing element: %s'
% elementAddress,'debug')
try:
match = re.match('/(.*)/(.*)',elementAddress)
elementEntityID = match.groups()[1]
elementEntityType = match.groups()[0]
memberAddress = self.getValueForLinkType(value='ADDRESS',
linkType=12,
id=elementEntityID,
type=elementEntityType,
origin='child')
if memberAddress:
memberList.append(memberAddress[0])
else:
raise
except:
self.logger('projectElementAddresses() skiping element address:%s,'
' could not load asset address!' % elementAddress,'debug')
continue
return memberList
[docs] def parentProductions(self,id='',entityType='asset'):
'''Function which returns a list of associated project addresses:
["/project/11"] representative of all productions that an asset
is a member of, based on passed integer value assetID. assetID
can be omitted if we are a loaded asset.
'''
if not id and self.entityID and (self.entityType == 'asset'
or self.entityType == 'project'):
id = self.entityID
entityType = self.entityType
else:
msg = "Loaded entity is not an asset or project and no ID was provided!"
self.logger(msg,'error')
raise RuntimeError(msg,'error')
## Get our parent production
## Fetch our XML DOM for our child links.
childLinkDOM = self.getChildLinksXML(id=id,type=entityType)
if entityType == 'project':
linkType = 16
else:
linkType = 1
parentAddresses = self.getValueForLinkType(value="ADDRESS",
linkType=linkType,
xmlDOM=childLinkDOM,
id=id,
type=entityType)
## Make sure we have parents
if len(parentAddresses) == 0:
raise RuntimeError("No parent productions found for entity:'/%s/%s'" % (entityType,id))
return parentAddresses
[docs] def archive(self,deviceID='',deviceDict='',recurseProductions=False):
'''Archives the loaded entity. If this is a production, then we will
archive all assets which are members of the production. If recurseProductions
is true, then we will archive it's members as well. '''
## Make sure that we have a specified archive device, if none are
## specified, archive to the first archive device found.
if not deviceID and not deviceDict:
self.logger('No archive device was provided, searching for applicable device','detailed')
devicesMap = self.getDevicesMap()
for deviceName,deviceDict in devicesMap.iteritems():
if 'DEV_ARCHIVE' in deviceDict and deviceDict['DEV_ARCHIVE']:
deviceID = deviceDict['DEVICE_ID']
self.logger(' - Using archive device: %s' % deviceDict['DEVICE_NAME'],'detailed')
break
elif deviceID:
deviceID = deviceID
elif deviceDict:
deviceID = deviceDict['DEVICE_ID']
## If this is a production, restore all members of the production.
if self.entityType == 'project':
if recurseProductions:
members = self.productionMemberAddresses()
else:
members = self.productionAssetAddresses()
## Iterate through our members and restore them
for member in members:
memberObj = FCSVRClient(entityPath=member)
memberObj.loadConfiguration(self.configParser)
memberObj.archive(recurseProductions=recurseProductions)
## If this is an asset, simply archive it.
elif self.entityType == 'asset':
## Archive the asset
## Basic sanity checks.
if not self.entityID:
msg = 'Could not archive entity, no entityID is loaded.'
self.logger(msg,'error')
raise FCSError(msg)
## Run our fcsvr_client command.
if not os.geteuid() == 0:
if not self.useSudo:
self.logger('Could not archive entity: root privileges are required!','error')
raise FCSProductionLoadError("Archiving entities requires root privileges!")
useSudo = True
else:
useSudo = False
if useSudo:
fcsvrCMDString = ('/usr/bin/sudo "%s" archive %s /dev/%s'
% (self.pathToFCSVRClient,self.entityPath(),deviceID))
else:
fcsvrCMDString = ('"%s" archive %s /dev/%s'
% (self.pathToFCSVRClient,self.entityPath(),deviceID))
self.logger('fcsvrCMD:\n %s' % fcsvrCMDString,'debug')
fcsvrCMD = subprocess.Popen(fcsvrCMDString,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client output: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'debug')
if not fcsvrCMD.returncode == 0:
## If fcsvr_client reports an error, search the text for the string
## 'The asset is already offline' (we must do this because
## fcsvr_client archive does return xml)
if 'The asset is already offline' in fcsvrCMD_STDOUT:
raise FCSAssetOfflineError(assetID=self.entityID)
elif 'Archiving FCP Project assets is not supported' in fcsvrCMD_STDOUT:
self.logger('Archiving FCP Project assets is not supported!','error')
else:
self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDString)
[docs] def restore(self,recurseProductions=False):
'''Restores the loaded entity. If this is a production, then we will
restore all assets which are members of the production. If recurseProductions
is true, then we will restore it's members as well. '''
## If this is a production, restore all members of the production.
if self.entityType == 'project':
if recurseProductions:
members = self.productionMemberAddresses()
else:
members = self.productionAssetAddresses()
## Iterate through our members and restore them
for member in members:
memberObj = FCSVRClient(entityPath=member)
memberObj.loadConfiguration(self.configParser)
memberObj.restore(recurseProductions=recurseProductions)
## If this is an asset, simply restore it.
elif self.entityType == 'asset':
## Restore the asset
## Basic sanity checks.
if not self.entityID:
msg = 'Could not restore entity, no entityID is loaded.'
self.logger(msg,'error')
raise FCSError(msg)
## Run our fcsvr_client command.
if not os.geteuid() == 0:
if not self.useSudo:
self.logger('Could not restore entity: root privileges are required!','error')
raise FCSProductionLoadError("Restoring entities requires root privileges!")
useSudo = True
else:
useSudo = False
if useSudo:
fcsvrCMDString = ('/usr/bin/sudo "%s" restore %s'
% (self.pathToFCSVRClient,self.entityPath()))
else:
fcsvrCMDString = ('"%s" restore %s'
% (self.pathToFCSVRClient,self.entityPath()))
self.logger('fcsvrCMD:\n %s' % fcsvrCMDString,'debug')
fcsvrCMD = subprocess.Popen(fcsvrCMDString,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client output: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'debug')
if not fcsvrCMD.returncode == 0:
##self.logger("FCSVRClient Error: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'error')
##return False
try:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDString)
except FCSVRClientFileError:
self.logger('Could not restore asset, no file is not available on '
'archive device!','error')
return False
return True
[docs] def analyze(self,force=False,FCP=False,recurseProductions=False):
'''Analyzes the loaded entity.'''
## If this is a production, analyze all members of the production.
if self.entityType == 'project':
if recurseProductions:
members = self.productionMemberAddresses()
else:
members = self.productionAssetAddresses()
## Iterate through our members and analyze them
for member in members:
memberObj = FCSVRClient(entityPath=member)
memberObj.loadConfiguration(self.configParser)
memberObj.analyze(force=force,FCP=FCP)
## If this is an asset, simply restore it.
elif self.entityType == 'asset':
## Analyze the asset
## Basic sanity checks.
if not self.entityID:
msg = 'Could not analyze entity, no entityID is loaded.'
self.logger(msg,'error')
raise FCSError(msg)
## Run our fcsvr_client command.
cmdOpts = ''
if force:
cmdOpts += ' --force'
if FCP:
cmdOpts += ' --fcp'
fcsvrCMDString = ('"%s" analyze %s %s'
% (self.pathToFCSVRClient,cmdOpts,self.entityPath()))
self.logger('fcsvrCMD:\n %s' % fcsvrCMDString,'debug')
fcsvrCMD = subprocess.Popen(fcsvrCMDString,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client output: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'debug')
if not fcsvrCMD.returncode == 0:
self.logger("FCSVRClient Error: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'error')
return False
[docs] def delete(self):
'''Deletes the loaded entity'''
if not self.entityType or not self.entityID:
self.logger("Could not delete entity, entityType or entityID not set!",'error')
return False
## Delete the asset
## Run our fcsvr_client command.
if not os.geteuid() == 0:
if not self.useSudo:
self.logger('Could not delete entity: root privileges are required!','error')
raise FCSProductionLoadError("Deleting entities requires root privileges!")
useSudo = True
else:
useSudo = False
if useSudo:
fcsvrCMDString = '/usr/bin/sudo "%s" delete --confirm /%s/%s' % (self.pathToFCSVRClient,
self.entityType,
self.entityID)
else:
fcsvrCMDString = '"%s" delete --confirm /%s/%s' % (self.pathToFCSVRClient,
self.entityType,
self.entityID)
self.logger('fcsvrCMD:\n %s' % fcsvrCMDString,'debug')
fcsvrCMD = subprocess.Popen('%s' % fcsvrCMDString,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client output: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'debug')
if not fcsvrCMD.returncode == 0:
self.logger("FCSVRClient Error: %s %s" % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'error')
return False
[docs] def createAssetFromFSPath(self,path,deviceName="",deviceID="",mdSet="",relPath="",overwriteExistingFiles="ignore",setMD=True,backgroundAnalyze=True):
'''This function takes a filesystem path and will create an asset from the
file. Can be passed a deviceName or deviceID, metadata set, and an optional
relative path to be uploaded. An option overwriteExistingFiles determines
behavior if a file already exists, options are 'ignore','iterate', and 'overwrite'
'''
## Make sure our asset exists
if not os.path.exists(path):
self.logger("Cannot create asset, no file could be found at path: '%s'" % path)
return False
assetPath = path
assetName = os.path.basename(assetPath)
if not mdSet:
mdSet = self.defaultAssetMetadataSet
## Fetch our device
myDeviceDict = {}
if not deviceName and not deviceID:
self.logger("Cannot create asset, deviceName or deviceID not specified!",'error')
return False
if deviceName:
myDeviceDict = self.deviceWithName(deviceName)
if not myDeviceDict:
self.logger("Could not find device with name: %s!" % deviceName,'error')
return False
if deviceID:
if myDeviceDict:
if not myDeviceDict["DEVICE_ID"]:
self.logger("Device with name: %s has a deviceID: %s, which conflicts with specified deviceID: %s, cannot continue" % (deviceName,myDeviceDict["DEVICE_ID"],deviceID),'error')
return False
deviceName = myDeviceDict["DEVICE_NAME"]
else:
myDeviceDict = self.deviceWithID(deviceID)
## Construct or path
targetDir = myDeviceDict["FSPATH"]
fcsDir = myDeviceDict["DESC_DEVICE_ADDRESS"]
if not os.path.exists(targetDir):
self.logger("Path: %s for Device: %s does not exist, cannot continue!" %(targetDir,assetDeviceName),'error')
return False
## If we have a relative path, clean it up and append it
if relPath:
## Replace some problematic characters, this probably needs to be greatly expanded
relPath = relPath.replace("`","'").replace('"',"'")
if relPath[0:1] == "/":
relPath = relPath[1:]
targetDir = os.path.join(targetDir,relPath)
fcsDir = os.path.join(fcsDir,relPath)
## At this point we have passed validation, announce our presence
self.logger("Creating asset %s from filesystem path: '%s' to device:'%s' fcspath:'%s' "%(mdSet,assetPath,deviceName,fcsDir))
## Create our directory if it doesn't exist
if not os.path.exists(targetDir):
theDir = targetDir
try:
while not os.path.exists(targetDir):
theDir = targetDir
while not os.path.exists(theDir):
if not os.path.exists(theDir) and os.path.exists(os.path.dirname(theDir)):
self.logger("Creating directory:%s with userID:%s" % (theDir,self.FCSUID),'debug')
os.mkdir(theDir)
os.chown(theDir,self.FCSUID,-1)
break
else:
theDir = os.path.dirname(theDir)
except:
self.logger("Error creating directory: %s, cannot continue!" % theDir,'error')
return False
## If the target already exists, act based upon overwriteExistingFiles
targetPath = os.path.join(targetDir,assetName)
fcsPath = os.path.join(fcsDir,assetName)
copyFile = True
if os.path.exists(targetPath):
if overwriteExistingFiles.lower() == "ignore":
self.logger("File exists at path:'%s', skipping copy" % targetPath)
copyFile = False
elif overwriteExistingFiles.lower() == "iterate":
iterCount = 1
fileBase,fileExtension = os.path.splitext(assetName)
while os.path.exists(targetPath):
targetPath = os.path.join(targetDir,"%s-%s%s" % (fileBase,iterCount,fileExtension))
fcsPath = os.path.join(fcsDir,"%s-%s%s" % (fileBase,iterCount,fileExtension))
iterCount += 1
elif overwriteExistingFiles.lower() == "replace":
self.logger("Found existing file at path:'%s', removing!")
shutil.rmtree(targetPath)
else:
self.logger("Found existing object at path:'%s'," % targetPath()
+ " and invalid overwriteExistingFiles option:%s" % overwriteExistingFiles
+ " was specified. (expects 'ignore','iterate', or 'replace'), cannot continue" ,'error')
return False
if copyFile:
## Copy our file
self.logger(" - Copying File to '%s'" % targetPath)
shutil.copy(assetPath,targetPath)
## Set proper ownership
self.logger(" - Updating ownership to UID:%s" % self.FCSUID)
os.chown(targetPath,self.FCSUID,-1)
self.logger(" - Copy Finished, creating asset")
## URLEncode our fcsPath
## Todo: add more logic to ensure we don't double quote.
qoutedFCSPath = urllib.quote(fcsPath)
if self.fcsvr_client_createasset(qoutedFCSPath,
mdSet,
backgroundAnalyze=backgroundAnalyze,
setMD=setMD):
self.logger("Successfully Created Asset /asset/%s" % self.entityID)
return True
else:
return False
[docs] def createThumbnail(self,newThumbnailPath,assetID="",deviceName="",deviceID="",mdSet=""):
'''This is accomplished by creating an asset for the thumbnail,
getting it's thumbnail path, and then creating the link'''
linkType = 5
## Make sure the replacement file exists:
if not os.path.isfile(newThumbnailPath):
if os.path.exists(newThumbnailPath):
self.logger("Non-file object exists at path:%s" % newThumbnailPath,'error')
return False
else:
self.logger("File does not exist at path:%s" % newThumbnailPath,'error')
return False
if not assetID:
if self.entityID and self.entityType == "asset":
assetID = self.entityID
if not deviceName:
if self.thumbnailDeviceName:
deviceName = self.thumbnailDeviceName
if not deviceName:
self.logger("Cannot create thumbnail, no device was specified!",'error')
return False
if not mdSet:
mdSet = self.defaultThumbnailMetadataSet
#### Create the asset
## first, get our desired device path
myDeviceDict = self.deviceWithName(deviceName)
if not myDeviceDict:
self.logger("Could not find device with name: %s, cannot continue!" % deviceName,'error')
return False
## Determine our target location.
targetDir = myDeviceDict["FSPATH"]
fcsDir = myDeviceDict["DESC_DEVICE_ADDRESS"]
## Get our thumbnailname
thumbnailName = os.path.basename(newThumbnailPath)
thumbnailBaseName,thumbnailExtension = os.path.splitext(thumbnailName)
targetName = thumbnailName
i = 1
while os.path.exists(os.path.join(targetDir,targetName)):
targetName = "%s_%s%s" % (thumbnailBaseName,i,thumbnailExtension)
i += 1
targetPath = os.path.join(targetDir,targetName)
fcsPath = os.path.join(fcsDir,targetName)
## Copy the file
try:
shutil.copy(newThumbnailPath,targetPath)
## Set proper ownership
os.chown(targetPath,self.FCSUID,-1)
except:
self.logger("Error copying file:'%s' to destination:'%s'!, cannot create thumbnail" % (newThumbnailPath,targetPath),'error')
return False
## Import our asset into FCS, this will also update the asset's MD
self.logger("Importing thumbnail at FCS Path:'%s' FS Path:'%s'" % (fcsPath,targetPath),'debug')
## Create an FCSVRClient object for our thumbnail
myFCSVRClient = FCSVRClient()
##myFCSVRClient.fields = self.fields
if not myFCSVRClient.fcsvr_client_createasset(fcsPath=fcsPath,mdSet=mdSet,backgroundAnalyze=False,setMD=False):
self.logger("Error creating asset: %s" % myFCSVRClient.lastError,'error')
return False
time.sleep(5)
## Get the thumbnail for our thumbnail
createdFCSThumbnailPath = myFCSVRClient.getFCSPathForLinkType(linkType=linkType)
if not createdFCSThumbnailPath:
self.logger("Could not find thumbnail reference for imported thumbnail file!",'error')
return False
## Create our link
self.fcsvr_client_make_link(linkType,childPath=createdFCSThumbnailPath,moveLink=True)
## Delete the thumbnail asset
myFCSVRClient.delete()
##fcsvrCMD = subprocess.Popen("'%s' make_link --linktype %s --movelink /asset/%s %s" % (self.pathToFCSVRClient,linkType,self.entityID,createdFCSThumbnailPath),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
##fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
##self.logger("fcsvrCMD:\n fcsvr_client make_link --linktype %s --movelink /asset/%s %s" % (linkType,self.entityID,createdFCSThumbnailPath),'debug')
#if not fcsvrCMD.returncode == 0:
#self.logger("%s" % fcsvrCMD_STDOUT,'error')
#return False
return True
[docs] def replaceThumbnail(self,newThumbnailPath,assetID=""):
'''Method which will replace the thumbnail file for the specified/stored assetID
with file provided at newThumbnailPath. '''
## Make sure the replacement file exists:
if not os.path.isfile(newThumbnailPath):
if os.path.exists(newThumbnailPath):
self.logger("Non-file object exists at path:%s" % newThumbnailPath,'error')
return False
else:
self.logger("File does not exist at path:%s" % newThumbnailPath,'error')
return False
if not assetID:
if self.entityID and self.entityType == "asset":
assetID = self.entityID
## Get our old thumbnail path:
thumbnailPath = self.getThumbnailPath(assetID=assetID)
## Copy our thumbnail over the existing thumbnail
try:
shutil.copy(newThumbnailPath,thumbnailPath)
os.chown(thumbnailPath,self.FCSUID,-1)
except:
self.logger("Error copying new thumbnail from: %s to %s" % (newThumbnailPath,thumbnailPath),'error')
return False
return True
[docs] def generateSearchXML(self,fields,match='exact'):
'''Generates an XML file for use by fcsvr_client search --xmlcrit, should
be past a list of FCSXMLFields (with their appropriate values set). Returns
an absolute path to the XML file.'''
## Generate a string of our field names
fieldNames = ""
for field in fields:
if not fieldNames:
fieldNames = field
else:
fieldNames += ", %s" % field
self.logger('generateSearchXML() Generating search XML with fields:%s'
' match type:%s' % (fieldNames,match),'debug')
## create our new xml doc, add our root FCS elements:
## <?xml version="1.0"?>
## <session>
## <values>
## <value id="CRIT_TYPE">
## <int>3</int>
## </value>
## <value id="CRIT_INTERSECT">
## <valuesList>
## <values>
## <value id="CRIT_CMP_VALUE">
## <value id="CUST_TITLE">
## <string>02-Ducati-Camera A-Color</string>
## </value>
## </value>
## <value id="CRIT_CMP_OP">
## <atom>eq</atom>
## </value>
## <value id="CRIT_TYPE">
## <int>1</int>
## </value>
## </values>
## </valuesList>
## </value>
## </values>
## </session>
xmlDoc = minidom.Document()
fcsElement = xmlDoc.createElement('session')
xmlDoc.appendChild(fcsElement)
valuesElement = xmlDoc.createElement('values')
fcsElement.appendChild(valuesElement)
## <value id="CRIT_TYPE">
## <int>3</int>
## </value>
critTypeElement = xmlDoc.createElement('value')
critTypeElement.setAttribute('id', 'CRIT_TYPE')
critTypeValueElement = xmlDoc.createElement('int')
critTypeValueNode = xmlDoc.createTextNode('3')
critTypeValueElement.appendChild(critTypeValueNode)
critTypeElement.appendChild(critTypeValueElement)
valuesElement.appendChild(critTypeElement)
## <value id="CRIT_INTERSECT">
## <valuesList>
## <values>
## <value id="CRIT_CMP_VALUE">
critIntersectElement = xmlDoc.createElement('value')
critIntersectElement.setAttribute('id', 'CRIT_INTERSECT')
valuesListElement = xmlDoc.createElement('valuesList')
critIntersectElement.appendChild(valuesListElement)
critIntersectValuesElement = xmlDoc.createElement('values')
valuesListElement.appendChild(critIntersectValuesElement)
critCMPValueElement = xmlDoc.createElement('value')
critCMPValueElement.setAttribute('id', 'CRIT_CMP_VALUE')
critIntersectValuesElement.appendChild(critCMPValueElement)
valuesElement.appendChild(critIntersectElement)
## Iterate through our passed field for XML and add our individual field
## criteria.
for field in fields:
try:
fieldName = field.name
dbname = field.dbname
if not fieldName and not dbname:
self.logger('xmlOut() found field with no fieldname or dbname! skipping!','error')
continue
if not fieldName and dbname:
fieldName = self.fieldNameForDBFieldName(dbname)
field.name = fieldName
self.logger('xmlOut() fieldName not set for field:%s, resolved:%s' % (dbname,fieldName),'debug')
elif not dbname and fieldName:
dbname = self.dbFieldNameForFieldName(fieldName)
field.dbname = dbname
self.logger('xmlOut() dbname not set for field:%s, resolved:%s' % (fieldName,dbname),'debug')
if dbname and field.value:
theFieldElement = xmlDoc.createElement('value')
theFieldElement.setAttribute('id', field.dbname)
## generate our dataType specific XML nodes, if no
## dataType is set, assume string
if field.dataType[0:6] == 'string' or not field.dataType:
fieldSourceElement = xmlDoc.createElement('string')
fieldSourceElement.setAttribute('xml:space','preserve')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'dateTime':
fieldSourceElement = xmlDoc.createElement('timestamp')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'timecode':
fieldSourceElement = xmlDoc.createElement('timecode')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'float':
fieldSourceElement = xmlDoc.createElement('real')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'int64':
fieldSourceElement = xmlDoc.createElement('bigint')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif (field.dataType == 'int' or field.dataType == 'integer'):
fieldSourceElement = xmlDoc.createElement('int')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'fraction':
fieldSourceElement = xmlDoc.createElement('fraction')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'coords':
fieldSourceElement = xmlDoc.createElement('intpair')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'bool':
fieldSourceElement = xmlDoc.createElement('bool')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
else:
self.logger('Unknown dataType: \'%s\'' % field.dataType,'error')
continue
theFieldElement.appendChild(fieldSourceElement)
## Append our field element to our "values" element i.e.
## <values>
## <value id="MYFIELD">
## <bool>true</bool>
critCMPValueElement.appendChild(theFieldElement)
del theFieldElement
except:
theErrorMsg = 'An error occured processing field: \'%s\', skipping!' % field.name
self.logger(theErrorMsg,'error')
## <value id="CRIT_CMP_OP">
## <atom>eq</atom>
## </value>
critCMPOPElement = xmlDoc.createElement('value')
critCMPOPElement.setAttribute('id','CRIT_CMP_OP')
critCMPAtomElement = xmlDoc.createElement('atom')
if match == 'exact':
critCMPAtomElement.appendChild(xmlDoc.createTextNode('eq'))
else:
critCMPAtomElement.appendChild(xmlDoc.createTextNode('contains'))
critCMPOPElement.appendChild(critCMPAtomElement)
critIntersectValuesElement.appendChild(critCMPOPElement)
## <value id="CRIT_TYPE">
## <int>1</int>
## </value>
critTypeElement = xmlDoc.createElement('value')
critTypeElement.setAttribute('id','CRIT_TYPE')
critTypeIntElement = xmlDoc.createElement('int')
critTypeIntElement.appendChild(xmlDoc.createTextNode('1'))
critTypeElement.appendChild(critTypeIntElement)
critIntersectValuesElement.appendChild(critTypeElement)
## We have finished building our fields, at this point, write out our
## XML
tempFileHandle,tempFilePath = tempfile.mkstemp(dir=self.supportDir,
suffix="_search.xml")
self.logger("generateSearchXML() Using temp file: %s" % tempFilePath,'debug')
os.fdopen(tempFileHandle).close()
theFile = codecs.open(tempFilePath, 'w','utf-8')
xmlDoc.writexml(theFile)
theFile.close()
return tempFilePath
[docs] def generateTempXMLFile(self):
'''generates a temporary file constructed from self.fields, returns the file path'''
if not self.fields:
self.logger("No fields are set, cannot generate xml file!",'error')
return False
## Create a temporary directory
tempFileHandle,tempFilePath = tempfile.mkstemp(dir=self.supportDir,suffix="_%s%s.xml" % (self.entityType,self.entityID))
## Generate our XML,write to our temp file
if not self.xmlOut(tempFilePath):
self.logger("No XML was generated",'error')
tempFileHandle.close()
tempFilePath = False
## Close out our temp file.
try:
## Fetch and close our file descriptor
fd = os.fdopen(tempFileHandle)
fd.close()
except:
pass
return tempFilePath
[docs] def setMD(self):
'''If we have a valid entityType and ID, we will attempt to run an fcsvr_client
--setmd operation. This is performed by writing a temporary XML file and reading
it in with fcsvr_client --xml setmd'''
## Setting metadata with fcsvr_client requires root access. Make sure we're
## running under effective UID 0, or that we have useSudo set (requires
## modified sudoers file).
if not os.geteuid() == 0:
if not self.useSudo:
self.logger("Could not set Metadata",'error')
raise RuntimeError("Updating metadata requires root privileges!")
useSudo = True
else:
useSudo = False
if not self.entityType or not self.entityID:
self.logger("Could not create entity, id or type is not set!" % entityType,'error')
raise RuntimeError("Could not create entity, id or type is not set!")
tempFilePath = self.generateTempXMLFile()
if not tempFilePath:
return False
## Run our fcsvr_client command.
if useSudo:
fcsvrCMDTXT = "/usr/bin/sudo '%s' setmd /%s/%s --xml '%s'" % (self.pathToFCSVRClient,self.entityType,self.entityID,tempFilePath)
else:
fcsvrCMDTXT = "'%s' setmd /%s/%s --xml '%s'" % (self.pathToFCSVRClient,self.entityType,self.entityID,tempFilePath)
fcsvrCMD = subprocess.Popen("%s" % fcsvrCMDTXT,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger("fcsvr_client command: %s" % "%s" % fcsvrCMDTXT,'debug')
self.logger("fcsvr_client output: %s tempfilepath: %s" % (fcsvrCMD_STDOUT,tempFilePath),'debug')
## Remove the temp file
if not self.keepFiles:
os.remove(tempFilePath)
if not fcsvrCMD.returncode == 0:
self.logger("%s %s" % (fcsvrCMD_STDERR,fcsvrCMD_STDOUT),'error')
return False
return True
[docs] def xmlOut(self,filePath):
'''Output our XML to the specified file'''
if not self.entityType:
self.logger('Could not create entity, entityType is not set!' % entityType,'error')
return False
if not self.entityType in self.registeredEntities:
self.logger('Could not set metadata, entityType:% is not valid!' % entityType,'error')
return False
if not self.entityID > 0:
self.logger('xmlOut() entityID not set! Cannot generate XML.', 'warning')
if not len(self.fields) > 0:
self.logger('xmlOut() no fields set! Cannot generate XML.', 'error')
return False
if not os.path.exists(filePath) and not os.path.isdir(os.path.dirname(filePath)):
self.logger('xmlOut() could not create file at %s' % filePath)
return False
if os.path.exists(filePath) and not self.overwriteExistingFiles:
self.logger('xmlOut() file already exists at %s' % filePath)
return False
## create our new xml doc, add our root FCS elements:
## <?xml version="1.0"?>
## <session>
## <values>
## <value id="CUST_DESCRIPTION">
## <string xml:space="preserve">this is my new description</string>
## </value>
## </values>
##</session>
xmlDoc = minidom.Document()
fcsElement = xmlDoc.createElement('session')
xmlDoc.appendChild(fcsElement)
valuesElement = xmlDoc.createElement('values')
fcsElement.appendChild(valuesElement)
## And then our individual fields.
for field in self.fields.itervalues():
try:
fieldName = field.name
dbname = field.dbname
if not fieldName and not dbname:
self.logger('xmlOut() found field with no fieldname or dbname! skipping!','error')
continue
if not fieldName and dbname:
fieldName = self.fieldNameForDBFieldName(dbname)
field.name = fieldName
self.logger('xmlOut() fieldName not set for field:%s, resolved:%s' % (dbname,fieldName),'debug')
elif not dbname and fieldName:
dbname = self.dbFieldNameForFieldName(fieldName)
field.dbname = dbname
self.logger('xmlOut() dbname not set for field:%s, resolved:%s' % (fieldName,dbname),'debug')
if dbname and field.value:
theFieldElement = xmlDoc.createElement('value')
theFieldElement.setAttribute('id', field.dbname)
## generate our dataType specific XML nodes, if no
## dataType is set, assume string
if field.dataType[0:6] == 'string' or not field.dataType:
fieldSourceElement = xmlDoc.createElement('string')
fieldSourceElement.setAttribute('xml:space','preserve')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'dateTime':
fieldSourceElement = xmlDoc.createElement('timestamp')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'timecode':
fieldSourceElement = xmlDoc.createElement('timecode')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'float':
fieldSourceElement = xmlDoc.createElement('real')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'int64':
fieldSourceElement = xmlDoc.createElement('bigint')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif (field.dataType == 'int' or field.dataType == 'integer'):
fieldSourceElement = xmlDoc.createElement('int')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'fraction':
fieldSourceElement = xmlDoc.createElement('fraction')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'coords':
fieldSourceElement = xmlDoc.createElement('intpair')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
elif field.dataType == 'bool':
fieldSourceElement = xmlDoc.createElement('bool')
theValueNode = xmlDoc.createTextNode(field.value)
fieldSourceElement.appendChild(theValueNode)
else:
self.logger('Unknown dataType: \'%s\'' % field.dataType,'error')
continue
theFieldElement.appendChild(fieldSourceElement)
## Append our field element to our "values" element i.e.
## <values>
## <value id="MYFIELD">
## <bool>true</bool>
valuesElement.appendChild(theFieldElement)
del theFieldElement
except:
theErrorMsg = 'An error occured processing field: \'%s\', skipping!' % field.name
self.logger(theErrorMsg,'error')
## We have finished building our fields, at this point, write out our
## XML
theFile = open(filePath, 'w')
xmlDoc.writexml(theFile)
theFile.close()
return True
[docs] def getFilePath(self):
'''Returns the file path to the primary representation for the loaded asset'''
deviceDict = self.deviceWithName(self.valueForField('Stored On'))
location = self.valueForField('Location')
fileName = self.valueForField('File Name')
self.logger('getFilePath() Found deviceDict: %s' % deviceDict,'debug')
deviceFSPath = deviceDict['FSPATH']
## If our location has a leading /, remove it
if location[0:1] == "/":
location = location[1:]
assetPath = os.path.join(deviceFSPath,location,fileName)
return assetPath
[docs] def getFilePathForThumbnail(self,id='',xmlDOM=''):
'''Returns the filesystem thumbnail path for asset given ID,
loads from fcsvr_client'''
if not id:
if self.entityID and self.entityType == 'asset':
assetID = self.entityID
else:
self.logger('Asset ID was not provided and is not stored, cannot continue!','error')
raise RuntimeError('Asset ID was not provided and is not stored, cannot continue!')
else:
assetID = id
self.logger('Determining thumbnailpath for id:%s' % id,'debug')
## If our assetID lookup matches our entityID and we have a cached value, return it
if assetID == self.entityID and self.thumbnailPath and not xmlDOM:
return self.thumbnailPath
## if we have an xmlDOM, use it, otherwise grab it from our object
if not xmlDOM:
xmlDOM = self.getParentLinksXML(id=assetID)
if not xmlDOM:
self.logger('Could not generate xml!','error')
return False
thumbnailPath = self.getFSPathForLinkType(linkType=5,id=assetID,xmlDOM=xmlDOM)
if not thumbnailPath:
self.logger('Could not determine thumbnail path for asset:%s' % assetID,'error')
return False
## If our assetID lookup matches our entityID, cache our value
if assetID == self.entityID and thumbnailPath:
self.thumbnailPath = thumbnailPath
return thumbnailPath
[docs] def getFilePathForProxy(self,id='',xmlDOM=''):
'''Returns filesystem path to the associated proxy file'''
if not id:
if self.entityID and self.entityType == 'asset':
entityID = self.entityID
else:
self.logger('Asset ID was not provided and is not stored, cannot continue!','error')
raise RuntimeError('Asset ID was not provided and is not stored, cannot continue!')
else:
entityID = id
if not entityID:
self.logger('assetID is not set, cannot determine proxyPath','error')
return False
## If our assetID lookup matches our entityID and we have a cached value, return it
if entityID == self.entityID and self.proxyPath and not xmlDOM:
return self.proxyPath
## if we have an xmlDOM, use it, otherwise grab it from our object
if not xmlDOM:
xmlDOM = self.getParentLinksXML(id=entityID)
if not xmlDOM:
self.logger('Could not generate xml!','error')
return False
proxyPath = self.getFSPathForLinkType(linkType=4,id=entityID,xmlDOM=xmlDOM)
if not proxyPath:
self.logger('Could not determine proxy path for asset:%s' % entityID,'error')
return False
## If our assetID lookup matches our entityID, cache our value
if entityID == self.entityID and proxyPath:
self.proxyPath = proxyPath
return proxyPath
[docs] def getFilePathForPosterFrame(self,id='',xmlDOM=''):
'''Returns filesystem path to the associated poster frame'''
if not id:
if self.entityID and self.entityType == 'asset':
assetID = self.entityID
else:
self.logger('Asset ID was not provided and is not stored, cannot continue!','error')
raise RuntimeError('Asset ID was not provided and is not stored, cannot continue!')
else:
assetID = id
## If our assetID lookup matches our entityID and we have a cached value, return it
if assetID == self.entityID and self.posterFramePath and not xmlDOM:
return self.posterFramePath
## if we have an xmlDOM, use it, otherwise grab it from our object
if not xmlDOM:
xmlDOM = self.getParentLinksXML(id=assetID)
if not xmlDOM:
self.logger('Could not generate xml!','error')
return False
posterFramePath = self.getFSPathForLinkType(linkType=6,id=assetID,xmlDOM=xmlDOM)
if not posterFramePath:
self.logger('Could not determine poster frame path for asset:%s' % assetID,'error')
return False
## If our assetID lookup matches our entityID, cache our value
if assetID == self.entityID and posterFramePath:
self.posterFramePath = posterFramePath
return posterFramePath
[docs] def getFilePathForEditProxy(self,id='',xmlDOM=''):
'''Returns filesystem path to the associated thumbnail'''
if not id:
if self.entityID and self.entityType == 'asset':
assetID = self.entityID
else:
self.logger('Asset ID was not provided and is not stored, cannot continue!','error')
raise RuntimeError('Asset ID was not provided and is not stored, cannot continue!')
else:
assetID = id
## If our assetID lookup matches our entityID and we have a cached value, return it
if assetID == self.entityID and self.editProxyPath and not xmlDOM:
return self.editProxyPath
## if we have an xmlDOM, use it, otherwise grab it from our object
if not xmlDOM:
xmlDOM = self.getParentLinksXML(id=assetID)
if not xmlDOM:
self.logger('Could not generate xml!','error')
return False
editProxyPath = self.getFSPathForLinkType(linkType=5,id=assetID,xmlDOM=xmlDOM)
if not editProxyPath:
self.logger('Could not determine edit proxy path for asset:%s' % assetID,'error')
return False
## If our assetID lookup matches our entityID, cache our value
if assetID == self.entityID and editProxyPath:
self.editProxyPath = editProxyPath
return editProxyPath
[docs] def initWithProductionTitle(self,title,match='exact'):
'''Inits a production based on Title'''
## lookup our production with the requested title
theProduction = self.productionWithTitle(title,match=match)
self.entityID = theProduction.entityID
self.entityType = theProduction.entityType
self.entityTitle = theProduction.entityTitle
self.entityMetadataSet = theProduction.entityMetadataSet
self.fields = theProduction.fields
return True
[docs] def initWithProductionID(self,productionID):
'''Inits a production based on ID'''
## Set our id and type
self.entityID = productionID
self.entityType = 'project'
## Make sure we have a productionID, if not, bail
if not productionID:
raise FCSObjectLoadError('Could not init production: no production id was'
' provided')
## delete our stored fields
if self.fields:
self.fields = {}
## Get our project metadata
cmdString = 'getmd "/project/%s" --xml' % productionID
## todo: add timeout to detect if FCSVR is down
fcsvrCMD = subprocess.Popen('"%s" %s' % (self.pathToFCSVRClient,cmdString),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command: fcsvr_client %s' % cmdString,'debug')
if not fcsvrCMD.returncode == 0:
self.logger('ERROR:%s' % fcsvrCMD_STDOUT,'error')
return False
##self.logger("fcsvr_client output: %s fcsPath: %s" % (fcsvrCMD_STDOUT,self.pathToFCSVRClient),'debug')
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: fcsvr_client %s' % cmdString,'error')
return False
self.dom = myDom
#try:
sessionRoot = myDom.childNodes[0]
searchResultCount = 0
resolvedFileName = ''
resolvedMetadataSet = ''
resolvedTitle = ''
resolvedProjectID = ''
resolvedDBEntityID = ''
for value in sessionRoot.getElementsByTagName('value'):
#self.logger('dbFieldNameForFieldName() rootValue nodename: %s' % rootValue.nodeName,'debug')
valueID = value.attributes['id'].value
#self.logger('dbFieldNameForFieldName() Searching value: %s' % valueID,'debug')
if valueID == 'PROJECT_NUMBER':
try:
resolvedProjectID = value.childNodes[1].childNodes[0].data
except:
pass
elif valueID == 'DB_ENTITY_ID':
try:
resolvedDBEntityID = value.childNodes[1].childNodes[0].data
except:
pass
elif valueID == 'PROJECT_TYPE':
resolvedMetadataSet = value.childNodes[1].childNodes[0].data
elif valueID == 'CUST_TITLE':
try:
resolvedTitle = value.childNodes[1].childNodes[0].data
except:
resolvedTitle = ''
if (not resolvedDBEntityID or resolvedDBEntityID == '0'):
msg = ('Could not init production with id:%s, production does not exist!'
% productionID)
self.logger(msg,'error')
raise FCSObjectLoadError(msg)
self.logger('Finished init. projectID: %s, entityMetadataSet: %s,'
' entityTitle: %s DBEntityID: %s' % (resolvedProjectID,
resolvedMetadataSet,resolvedTitle,resolvedDBEntityID)
,'debug')
self.entityID = resolvedProjectID
self.entityMetadataSet = resolvedMetadataSet
self.entityTitle = resolvedTitle
if self.entityID:
return True
[docs] def initWithAssetID(self,assetID):
'''Inits an asset based upon id'''
## Make sure we have an assetID, if not, bail
if not assetID:
raise FCSObjectLoadError('Could not init asset: no asset id was'
' provided')
## Get our asset metadata
cmdString = 'getmd "/asset/%s" --xml' % assetID
## todo: add timeout to detect if FCSVR is down
fcsvrCMD = subprocess.Popen('"%s" %s' % (self.pathToFCSVRClient,cmdString),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command: fcsvr_client %s' % cmdString,'debug')
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString='fcsvr_client %s' % cmdString)
##self.logger("fcsvr_client output: %s fcsPath: %s" % (fcsvrCMD_STDOUT,self.pathToFCSVRClient),'debug')
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: fcsvr_client %s' % cmdString,'error')
return False
self.dom = myDom
#try:
sessionRoot = myDom.childNodes[0]
searchResultCount = 0
noMatch = False
resolvedRelPath = ''
resolvedDeviceID = ''
resolvedDevice = ''
resolvedFileName = ''
resolvedAssetID = ''
resolvedMetadataSet = ''
resolvedTitle = ''
resolvedDBEntityID = ''
for value in sessionRoot.getElementsByTagName('value'):
#self.logger('dbFieldNameForFieldName() rootValue nodename: %s' % rootValue.nodeName,'debug')
valueID = value.attributes['id'].value
##self.logger('dbFieldNameForFieldName() Searching value: %s' % valueID,'debug')
if valueID == 'CUST_LOCATION':
try:
resolvedRelPath = value.childNodes[1].childNodes[0].data
if not resolvedRelPath or not resolvedRelPath == relPath:
self.logger('Found Location: \'%s\' does not match our requested '
'location:\'%s\',skipping search result!'
% (resolvedRelPath,relPath),'debug')
noMatch = True
break;
except:
pass
elif valueID == 'ASSET_NUMBER':
resolvedAssetID = value.childNodes[1].childNodes[0].data
elif valueID == 'DB_ENTITY_ID':
try:
resolvedDBEntityID = value.childNodes[1].childNodes[0].data
except:
pass
elif valueID == 'PA_MD_CUST_FILENAME':
resolvedFileName = value.childNodes[1].childNodes[0].data
elif valueID == 'CUST_DEVICE':
resolvedDeviceName = value.childNodes[1].childNodes[0].data
resolvedDevice = self.deviceWithName(resolvedDeviceName)
resolvedDeviceID = resolvedDevice['DEVICE_ID']
elif valueID == 'ASSET_TYPE':
resolvedMetadataSet = value.childNodes[1].childNodes[0].data
elif valueID == 'CUST_TITLE':
try:
resolvedTitle = value.childNodes[1].childNodes[0].data
except:
resolvedTitle = ''
if not resolvedDBEntityID or resolvedDBEntityID == '0':
msg = ('Could not init asset with id:%s, asset does not exist!'
% assetID)
self.logger(msg,'error')
raise FCSEntityNotFoundError(entityType='asset',entityID=assetID)
self.logger('Finished init. entityID: %s, entityMetadataSet: %s,'
' entityTitle: %s' % (resolvedAssetID,resolvedMetadataSet,resolvedTitle)
,'debug')
self.deviceDict = resolvedDevice
self.entityID = resolvedAssetID
self.entityMetadataSet = resolvedMetadataSet
self.entityTitle = resolvedTitle
if self.entityID:
return True
[docs] def initWithAssetTitle(self,title):
'''Accepts a title in string form, and copies local data'''
## Get our asset with title.
myAsset = self.assetWithTitle(title=title)
self.entityID = myAsset.entityID
self.entityType = myAsset.entityType
self.entityTitle = myAsset.entityTitle
self.entityMetadataSet = myAsset.entityMetadataSet
self.fields = myAsset.fields
return self
[docs] def initWithAssetFromFSPath(self,FSPath):
'''Accepts a file system path, and loads basic information'''
## Get our FCSPath
self.logger('initWithAssetFromFSPath() looking up fcsPath for path:%s'
% FSPath,'debug')
try:
fcsPath = self.getFCSPathFromFSPath(FSPath)
except:
## Attempt to resolve from an archive path
onlinePath = self.getFSPathFromArchivePath(FSPath)
fcsPath = self.getFCSPathFromFSPath(onlinePath)
self.logger('initWithAssetFromFSPath() found fcsPath:%s for path:%s'
% (fcsPath,FSPath),'debug')
try:
return self.initWithAssetFromFCSPath(fcsPath)
except (FCSEntityNotFoundError,FCSVRClientError):
raise FCSEntityNotFoundError(entityPath=FSPath)
[docs] def initWithAssetFromFCSPath(self,FCSPath):
'''loads our object based upon a FCS path'''
self.logger('initFromFCSPath() initing as asset from FCSPath:\'%s\''
% FCSPath,'debug')
## Extract the asset name from the path
fileName = os.path.basename(FCSPath)
## Extract the devID and relPath from the path
partialPath = os.path.dirname(FCSPath)
try:
match = re.match('^/dev/(\d+)/(.*)$',partialPath)
deviceID = match.group(1)
relPath = match.group(2)
except:
try:
match = re.match('^/dev/(\d+)$',partialPath)
deviceID = match.group(1)
relPath = ''
except:
self.logger('Could not extract deviceID or partial path, path:\'%s\''
% partialPath)
return False
## Perform a search for our asset
cmdString = 'list_child_links %s --xml' % FCSPath
## todo: add timeout to detect if FCSVR is down
fcsvrCMD = subprocess.Popen('"%s" %s' % (self.pathToFCSVRClient,cmdString),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command: fcsvr_client %s' % cmdString,'debug')
## IF fcsvrCMD didn't return quickly, consult with fcsvr_client_error
## to determine the problem. If it throws exception FCSClientFileError,
## then we raise a FCSClientEntityNotFoundError
if not fcsvrCMD.returncode == 0:
try:
self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString='fcsvr_client %s' % cmdString)
except FCSVRClientFileError:
raise FCSEntityNotFoundError(entityPath=FCSPath)
##self.logger('fcsvr_client output: %s fcsPath: %s' % (fcsvrCMD_STDOUT,self.pathToFCSVRClient),'debug')
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: fcsvr_client %s' % cmdString,'error')
return False
self.dom = myDom
#try:
sessionRoot = myDom.childNodes[0]
resolvedEntityPath = ''
for value in sessionRoot.getElementsByTagName('value'):
#self.logger('dbFieldNameForFieldName() rootValue nodename: %s' % rootValue.nodeName,'debug')
valueID = value.attributes['id'].value
##self.logger('initWithAssetFromFCSPath() Searching value: %s' % valueID,'debug')
if valueID == 'ADDRESS':
try:
resolvedEntityPath = value.childNodes[1].childNodes[0].data
except:
pass
resolvedAssetID = resolvedEntityPath.split('/')[2]
self.logger('initWithAssetID() found entityPath:%s, assetID:%s for FCS Address:%s'
% (resolvedEntityPath,resolvedAssetID,FCSPath),'debug')
return self.initWithAssetID(resolvedAssetID)
[docs] def old_initAssetFromFCSPath(self,FCSPath):
'''loads our object based upon a FCS path'''
self.logger("initFromFCSPath() initing as asset from FCSPath:'%s'" % FCSPath,'debug')
## Extract the asset name from the path
fileName = os.path.basename(FCSPath)
## Extract the devID and relPath from the path
partialPath = os.path.dirname(FCSPath)
try:
match = re.match('^/dev/(\d+)/(.*)$',partialPath)
deviceID = match.group(1)
relPath = match.group(2)
except:
try:
match = re.match('^/dev/(\d+)$',partialPath)
deviceID = match.group(1)
relPath = ''
except:
self.logger("Could not extract deviceID or partial path, path:'%s'" % partialPath)
return False
## Perform a search for our asset
cmdString = 'search --crit "%s" /asset --xml' % fileName
## todo: add timeout to detect if FCSVR is down
fcsvrCMD = subprocess.Popen('"%s" %s' % (self.pathToFCSVRClient,cmdString),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command: fcsvr_client %s' % cmdString,'debug')
if not fcsvrCMD.returncode == 0:
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString='fcsvr_client %s' % cmdString)
##self.logger("fcsvr_client output: %s fcsPath: %s" % (fcsvrCMD_STDOUT,self.pathToFCSVRClient),'debug')
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client command: fcsvr_client %s' % cmdString,'error')
return False
self.dom = myDom
#try:
sessionRoot = myDom.childNodes[0]
searchResultCount = 0
noMatch = False
resolvedRelPath = ''
resolvedDeviceID = ''
resolvedFileName = ''
resolvedAssetID = ''
resolvedMetadataSet = ''
resolvedTitle = ''
for searchResult in sessionRoot.childNodes:
#self.logger('dbFieldNameForFieldName() Searching nodename: %s' % searchResult.nodeName,'debug')
if searchResult.nodeName == 'values':
resolvedRelPath = ''
resolvedDeviceID = ''
resolvedFileName = ''
resolvedAssetID = ''
resolvedMetadataSet = ''
resolvedTitle = ''
noMatch = False
for rootValue in searchResult.getElementsByTagName('value'):
#self.logger('dbFieldNameForFieldName() rootValue nodename: %s' % rootValue.nodeName,'debug')
rootValueID = rootValue.attributes['id'].value
#self.logger('dbFieldNameForFieldName() Searching rootValueID: %s' % rootValueID,'debug')
if rootValueID == 'COMPLETE':
didComplete = rootValue.childNodes[1].childNodes[0].data
if not didComplete:
break;
searchResultCount += 1
elif rootValueID == 'METADATA':
noMatch = False
for value in rootValue.getElementsByTagName('value'):
valueID = value.attributes['id'].value
##self.logger('dbFieldNameForFieldName() Searching value: %s' % valueID,'debug')
if valueID == 'CUST_LOCATION':
try:
resolvedRelPath = value.childNodes[1].childNodes[0].data
if not resolvedRelPath or not resolvedRelPath == relPath:
self.logger('Found Location: \'%s\' does not match our '
'requested location:\'%s\',skipping search result!'
% (resolvedRelPath,relPath),'debug')
noMatch = True
break;
except:
pass
elif valueID == 'ASSET_NUMBER':
resolvedAssetID = value.childNodes[1].childNodes[0].data
elif valueID == 'PA_MD_CUST_FILENAME':
resolvedFileName = value.childNodes[1].childNodes[0].data
if not resolvedFileName or not resolvedFileName == fileName:
self.logger('Found File Name: \'%s\' does not match our '
'requested filename:\'%s\',skipping search result!'
% (resolvedFileName,fileName),'debug')
noMatch = True
break
elif valueID == 'CUST_DEVICE':
resolvedDeviceName = value.childNodes[1].childNodes[0].data
resolvedDevice = self.deviceWithName(resolvedDeviceName)
resolvedDeviceID = resolvedDevice['DEVICE_ID']
try:
if not int(resolvedDeviceID) == int(deviceID):
self.logger('Found DeviceID: \'%s\' does not match our'
'requested Device ID:\'%s\',skipping search result!'
% (resolvedDeviceID,deviceID),'debug')
noMatch = True
break
except:
self.logger('Error converting \'%s\' or \'%s\' to int!'
% (resolvedDeviceID,deviceID),'debug')
elif valueID == 'ASSET_TYPE':
resolvedMetadataSet = value.childNodes[1].childNodes[0].data
elif valueID == 'CUST_TITLE':
try:
resolvedTitle = value.childNodes[1].childNodes[0].data
except:
resolvedTitle = ''
if not noMatch and resolvedAssetID:
resolvedPath = u'%s' % os.path.join('/dev','%s' % resolvedDeviceID,
resolvedRelPath,
resolvedFileName)
originalPath = u'%s' % FCSPath
if resolvedPath == originalPath:
self.deviceDict = resolvedDevice
self.entityID = resolvedAssetID
self.entityMetadataSet = resolvedMetadataSet
self.entityTitle = resolvedTitle
return True
else:
self.logger('Match failed! resolved path:\'%s\', requested path:\'%s\''
% (resolvedPath,FCSPath))
noMatch = True
return False
[docs] def getFCSPathFromFSPath(self,FSPath):
'''Resolves a device path from a filesystem path'''
newPath = ''
self.logger('getFCSPathFromFSPath() resolving path:%s' % FSPath,'debug')
devicesMap = self.getDevicesMap()
if not devicesMap or not len(devicesMap) > 0:
self.logger('getFCSPathFromFSPath() devicesMap could not be loaded!','error')
return False
for deviceID,device in devicesMap.iteritems():
if FSPath[0:len(device['DEV_ROOT_PATH'])] == device['DEV_ROOT_PATH']:
if device['DEVICE_TYPE'] == 'filesystem':
newPath = os.path.join(device['DESC_DEVICE_ADDRESS'],FSPath[len(device['DEV_ROOT_PATH']) + 1:])
elif device['DEVICE_TYPE'] == 'contentbase':
try:
parentDirName = os.path.basename(os.path.dirname(FSPath))
decimalNameVal = int(parentDirName,16)
newPath = os.path.join(device['DESC_DEVICE_ADDRESS'],'%s_%s' % (decimalNameVal,os.path.basename(FSPath)))
except:
self.logger('getFCSPathFromFSPath() Unexpected error converting contentbase path!','error')
return False
else:
self.logger('getFCSPathFromFSPath() Unexpected DEVICE_TYPE:%s' % device['DEVICE_TYPE'],'error')
return False
break;
self.logger('getFCSPathFromFSPath() resolved unquoted path:%s' % newPath,'debug')
## Encode FCS path with standard URL chars (i.e. ' ' = '%20')
quotedPath = self.quoteString(newPath)
self.logger('getFCSPathFromFSPath() resolved path:%s' % quotedPath,'debug')
return quotedPath
[docs] def getFSPathFromFCSPath(self,fcsPath):
'''Resolves a POSIX Path from an FCS path'''
self.logger('getFSPathFromFCSPath() resolving fcsPath:%s' % fcsPath,'debug')
## Blow up the FCS Path
pathArray = fcsPath.split('/')
##self.logger('pathArray:%s' % pathArray,'debug')
newPath = ''
## Ensure it's a /dev path
if not pathArray[1] == 'dev':
self.logger('Passed path:%s does not seem to be a valid FCS path!' % fcsPath,'error')
return False
## Abstract the device ID and dictionary
deviceID = pathArray[2]
self.logger('getFSPathFromFCSPath() found deviceID: %s' % pathArray[2],'debug')
deviceDict = self.deviceWithID(deviceID)
if not deviceDict:
self.logger('Could not load Device for id:%s, cannot continue!' % deviceID,'debug')
return False
deviceFSPath = deviceDict['DEV_ROOT_PATH']
## Get the remainder of the path:
if deviceDict['DEVICE_TYPE'] == 'contentbase':
if not len(pathArray) == 4:
self.logger('Unexpected element count in path: %s for ContentBase '
'device, expected 3 items, found %s '
% (fcsPath,len(pathArray)),'error')
return False
try:
regexMatch = re.match('^(\d+)_(.*)$',pathArray[3])
fileID = int(regexMatch.group(1))
fileHexID = '%016X' % fileID
fileName = regexMatch.group(2)
## Get Our subpaths from the hex substr
path1 = fileHexID[12:14]
path2 = fileHexID[10:12]
newPath = os.path.join(deviceFSPath,path1,path2,fileHexID,fileName)
except:
self.logger('Could not determine fileID from filename:%s' % pathArray[3],'error')
return False
elif deviceDict['DEVICE_TYPE'] == 'filesystem':
count=4
newPath = ''
while count < len(pathArray):
newPath = os.path.join(deviceFSPath,pathArray[count])
count += 1
else:
self.logger('Unexpected DEVICE_TYPE encountered:%s' % deviceDict['DEVICE_TYPE'],'error')
return False
unQuotedPath = self.unquoteString(newPath)
return unQuotedPath
[docs] def getFSPathFromArchivePath(self,archiveFilePath):
'''Resolves a POSIX 'online' Path from a POSIX archive PATH
i.e.: /MyArchiveDevicePath/4/myfile.mov -> /LibraryDevicePath/myfile.mov'''
## Determine our archive device
myArchiveDevice = self.deviceWithPath(archiveFilePath)
archiveBasePath = myArchiveDevice['FSPATH']
## Determine the non-archive path for the file, extract the archive portion
## of the path and determine original location of the file.
relFilePath = ''
if archiveFilePath[0:len(archiveBasePath)] == archiveBasePath:
## First get our relative path extracted from our archive-relative path
## (/4/testfolder/myfile.mov)
relFilePathTemp = archiveFilePath[len(archiveBasePath):]
## Extract the deviceID and save our device-relative path
deviceID = relFilePathTemp.split('/')[1]
relFilePath = relFilePathTemp[len('/%s' % deviceID):]
else:
self.logger('loadForFileAtPath() File Path:%s does not reside in Archive Path:%s','error')
raise FCSObjectLoadError('File Path:%s does not exist in Archive Path:%s')
## Fetch our device info from our FCSVRClient object
myDevice = self.deviceWithID(deviceID)
## Determine online asset path
resolvedPath = u"%s%s" % (myDevice['FSPATH'],relFilePath)
self.logger('loadForFileAtPath() determined relative path: %s deviceID: %s'
' resolvedPath: %s' % (relFilePath,deviceID,resolvedPath),'debug')
self.logger('loadForFileAtPath() - device path:%s' % myDevice['FSPATH'],'debug')
return resolvedPath
[docs] def quoteString(self,path):
'''This function returns a quoted filepath
acceptable for use by fcsvr_client'''
## Quote our string using urllib's quote function, skipping a few
## special escape characters.
quotedString = urllib.quote(path,'&()/*!\'~$')
quotedString = quotedString.replace('(','\(')
quotedString = quotedString.replace(')','\)')
quotedString = quotedString.replace('&','\&')
quotedString = quotedString.replace('*','\*')
quotedString = quotedString.replace('!','\!')
quotedString = quotedString.replace("'","\\'")
quotedString = quotedString.replace('~','\~')
quotedString = quotedString.replace('$','\$')
return quotedString
[docs] def unquoteString(self,path):
'''This function returns a quoted filepath
acceptable for use by fcsvr_client'''
## Quote our string using urllib's quote function, skipping a few
## special escape characters.
unquotedString = urllib.unquote(path)
unquotedString = unquotedString.replace('\(','(')
unquotedString = unquotedString.replace('\)',')')
unquotedString = unquotedString.replace('\&','&')
unquotedString = unquotedString.replace('\*','*')
unquotedString = unquotedString.replace('\!','!')
unquotedString = unquotedString.replace("\'","'")
unquotedString = unquotedString.replace('\~','~')
unquotedString = unquotedString.replace('\$','$')
return unquotedString
[docs] def loadFromFCSXMLObject(self,fcsXMLObject):
'''Loads our object from values stored in fcsXMLObject'''
if fcsXMLObject.entityID:
self.entityID = fcsXMLObject.entityID
if fcsXMLObject.entityType:
self.entityType = fcsXMLObject.entityType
if len(fcsXMLObject.fields) > 0:
self.fields = fcsXMLObject.fields
## Attempt to load to verify that the entity exists. An exception
## will be thrown if we fail.
myFCSVRClient = FCSVRClient()
if self.entityType == 'project':
myFCSVRClient.initWithProductionID(self.entityID)
else:
myFCSVRClient.initWithAssetID(self.entityID)
[docs] def getValueForLinkType(self,value,linkType,xmlDOM='',id='',type='',origin='parent'):
'''Returns the value of <value> node with id of value and LINK_TYPE of
linkType. If xmlDOM is provided, we will search it, otherwise we will
generate our own xmlDOM for the search using provided or stored values of
entityID and entityType. Argument 'origin' can be provided to denote whether
we are fetching parent or child links.'''
## Assign new var (for readability)
requestedValue = value
if not xmlDOM:
if not id and self.entityID:
entityID = self.entityID
elif not id:
self.logger('Could not determine ID to search!');
raise RuntimeError('Could not determine ID to search!')
else:
entityID = id
entityType = type
if origin == 'child':
xmlDOM = self.getChildLinksXML(id=entityID,type=entityType)
else:
xmlDOM = self.getParentLinksXML(id=entityID,type=entityType)
## Do our work on the DOM
matchedEntries = [] ## List of matching dicts
matchedValues = [] ## List of specific matching values
linkAddress = ''
for myValues in xmlDOM.getElementsByTagName('values'):
loopDied = False
assetDict = {}
for myValue in myValues.getElementsByTagName('value'):
valueID = myValue.attributes['id'].value
##self.logger('VALUE ID ATTRIB:%s' % valueID,'debug')
if valueID == 'LINK_TYPE':
## Check to see if it's the thumbnail (LINK_TYPE of 5)
assetDict['LINK_TYPE'] = int(myValue.childNodes[1].childNodes[0].data)
if not assetDict['LINK_TYPE'] == linkType:
loopDied = True
assetDict = {}
break
if valueID == requestedValue:
assetDict[requestedValue] = myValue.childNodes[1].childNodes[0].data
if (not loopDied and 'LINK_TYPE' in assetDict
and assetDict['LINK_TYPE'] == linkType
and requestedValue in assetDict):
self.logger("Found matched value:'%s' (linkType:%s RequestedValue:'%s')"
% (requestedValue,
assetDict['LINK_TYPE'],
assetDict[requestedValue])
,'debug')
matchedEntries.append(assetDict)
matchedValues.append(assetDict[requestedValue])
if not len(matchedValues) > 0:
##raise FCSError("Could not find entries with VALUE:%s LINK_TYPE:%s"
## " in XML object!" % (requestedValue,linkType))
return []
else:
self.logger('Found %s matching entries for LINK_TYPE:%s in XML object!'
% (len(matchedValues),linkType),'debug')
return matchedValues
[docs] def getFCSPathForLinkType(self,linkType,id='',xmlDOM=''):
'''Returns a Final Cut server relative path for linked assets'''
entityID = False
if not id:
if self.entityID and self.entityType == 'asset':
entityID = self.entityID
else:
entityID = id
if not entityID:
self.logger('Could not determine ID for asset, cannot continue!','error')
raise RuntimeError('Could not determine ID for asset, cannot continue!')
responses = self.getValueForLinkType(value='ADDRESS',linkType=linkType,
id=entityID,type='asset',
xmlDOM=xmlDOM)
if not len(responses) > 0:
self.logger('No responses returned for asset!','error')
return ''
return responses[0]
'''
## If we weren't passed our DOM, load it
if not xmlDOM:
xmlDOM = self.getParentLinksXML(id=id)
## If we still have no DOM, exit
if not xmlDOM:
self.logger('Could not retrieve FCSPath, no xmlDOM loaded!','error')
return False
assetDict = {}
linkAddress = ''
for values in xmlDOM.getElementsByTagName('values'):
loopDied = False
for value in values.getElementsByTagName('value'):
valueID = value.attributes['id'].value
##self.logger("VALUE ID ATTRIB:%s" % valueID,'debug')
if valueID == 'LINK_TYPE':
## Check to see if it's the thumbnail (LINK_TYPE of 5)
assetDict['LINK_TYPE'] = int(value.childNodes[1].childNodes[0].data)
if not assetDict['LINK_TYPE'] == linkType:
loopDied = True
break
if valueID == 'ADDRESS':
assetDict['ADDRESS'] = value.childNodes[1].childNodes[0].data
if valueID == 'DESC_DEVICE':
assetDict['DESC_DEVICE'] = value.childNodes[1].childNodes[0].data
if valueID == 'DESC_DEVICE_ADDRESS':
assetDict['DESC_DEVICE_ADDRESS'] = value.childNodes[1].childNodes[0].data
if not loopDied and 'LINK_TYPE' in assetDict and assetDict['LINK_TYPE'] == linkType:
self.logger('link type:%s Address: %s ' % (assetDict['LINK_TYPE'],assetDict['ADDRESS']),'debug')
break
if not 'ADDRESS' or not 'ADDRESS' in assetDict or 'ADDRESS' in assetDict and not assetDict['ADDRESS']:
self.logger('Could not find LINK_TYPE:%s in XML object!' % linkType,'error')
return False
return assetDict['ADDRESS']
'''
[docs] def getFSPathForLinkType(self,linkType,id='',xmlDOM=''):
'''Returns a filesystem path from a linked asset'''
## todo: document xmlDOM functionality.
if not id:
if self.entityID and self.entityType == 'asset':
assetID = self.entityID
else:
self.logger('Asset ID was not provided and is not stored, cannot continue!','error')
raise RuntimeError('Asset ID was not provided and is not stored, cannot continue!')
else:
assetID = id
if not xmlDOM:
xmlDOM = self.getParentLinksXML(id=assetID)
if not xmlDOM:
self.logger('Could not retrieve FCPath, no xmlDOM loaded!','error')
return False
## Get our FCS Address for the link
assetAddress = self.getFCSPathForLinkType(linkType,id=assetID,xmlDOM=xmlDOM)
if not assetAddress:
return False
## Convert the FCS Path to an FS Path
self.logger('Converting address: %s to known FileSystem address!'
% assetAddress)
assetPath = self.getFSPathFromFCSPath(assetAddress)
if assetPath:
self.logger('Using address: %s' % assetPath,'debug')
return assetPath
else:
self.logger('Could not convert address: %s' % assetAddress,'error')
return False
[docs] def getChildLinksXML(self,id='',type=''):
'''Gets an xml.dom.minidom object of child links data from fcsvr_client
sister object to getParentLinksXML.'''
## Determine our entityType
entityType = ''
if not type and self.entityType:
entityType = self.entityType
elif not self.entityType:
entityType = 'asset'
elif type:
entityType = type
## Determine our id
entityID = ''
if not id and self.entityType == entityType:
entityID = self.entityID
elif not id and not self.entityType == entityType:
self.logger('Requested entity type does not match stored values!')
raise RuntimeError('Requested entity type does not match stored values!')
elif id:
entityID = id
if not entityID or not entityType:
self.logger('Passed invalid entity data, type or id missing!')
raise RuntimeError('Passed invalid entity data, type or id missing!')
## If our assetID lookup matches our entityID and we have a cached value, return it
if entityID == self.entityID and type == self.entityType and self.childXMLObject:
return self.childXMLObject
## Fetch our data from FCSVRXML
fcsvrCMDString = "'%s' list_child_links /%s/%s --xml" % (self.pathToFCSVRClient,
entityType,
entityID)
fcsvrCMD = subprocess.Popen(fcsvrCMDString,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command:%s' % fcsvrCMDString,'debug')
##self.logger('fcsvr_client output: %s tempfilepath: %s' % (fcsvrCMD_STDOUT,tempFilePath),'debug')
if not fcsvrCMD.returncode == 0:
##self.logger('fcsvr_client reported an error: %s%s' % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR),'error')
##raise RuntimeError('fcsvr_client reported an error: %s' % (fcsvrCMD_STDOUT,fcsvrCMD_STDERR))
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDString)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client','error')
return False
if entityID == self.entityID:
self.parentXMLObject = myDom
return myDom
[docs] def getParentLinksXML(self,id='',type=''):
'''Returns an xml.dom.minidom object of parent links data returned from
fcsvr_client'''
## Determine our entityType
entityType = ''
if not type and self.entityType:
entityType = self.entityType
elif not self.entityType:
entityType = 'asset'
elif type:
entityType = type
## Determine our id
entityID = ''
if not id and self.entityType == entityType:
entityID = self.entityID
elif not id and not self.entityType == entityType:
self.logger('Requested entity type does not match stored values!')
raise RuntimeError('Requested entity type does not match stored values!')
elif id:
entityID = id
## If our assetID lookup matches our entityID and we have a cached value, return it
if entityID == self.entityID and self.parentXMLObject:
return self.parentXMLObject
## Fetch our data from FCSVRXML
fcsvrCMDString = '"%s" list_parent_links /%s/%s --xml' % (self.pathToFCSVRClient,
entityType,
entityID)
fcsvrCMD = subprocess.Popen(fcsvrCMDString,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvr_client command:%s' % fcsvrCMDString,'debug')
##self.logger('fcsvr_client output: %s tempfilepath: %s' % (fcsvrCMD_STDOUT,tempFilePath),'debug')
if not fcsvrCMD.returncode == 0:
##self.logger('fcsvr_client reported an error: %s' % fcsvrCMD_STDERR,'error')
##raise RuntimeError('fcsvr_client reported an error: %s' % fcsvrCMD_STDERR)
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDString)
## Create a dom object from our string:
try:
myDom = minidom.parseString(fcsvrCMD_STDOUT)
except:
self.logger('Could not parse output from fcsvr_client','error')
return False
if entityID == self.entityID:
self.parentXMLObject = myDom
return myDom
[docs] def getThumbnailPath(self,assetID='',xmlDOM=''):
'''DEPRICATED: use getFilePathForThumbnail'''
return self.getFilePathForThumbnail(id=assetID)
[docs] def flushCaches(self):
'''Flushes caches'''
self.parentXMLObject = ''
self.childXMLObject = ''
return True
[docs] def fcsvr_client_create(self,address='',entityType='',parentAddress='',parentLinkType=''):
'''This is a wrapper around fcsvr_client, if there are fields set in self.fields,
it will import with generate XML to a temp file and pass it during creation'''
## currently a place holder
return False
[docs] def fcsvr_client_createasset(self,fcsPath,mdSet='pa_asset_media',
backgroundAnalyze=False,projAddress='',setMD=True):
'''This is the createasset wrapper around fcsvr_client. If there are fields
set in self.fields, It will output XML to a temp file and then
call fcsvr_client to create the entity. If self.entityType is 'asset', then
we load from fcsvr_client and return our self, if our entityType is not asset,
we clone ourself and return a new FCSVRClient object with entityType 'asset'
WARNING: fcsPath should be a quoted path, as returned by quoteFSPath()
failure to comply with this CAN result in a database crash, or possible
corruption if fcsvr_client is passed unquoted special characters!!!
'''
if not self.entityType == 'asset':
self.logger('Object entityType is %s, not %s, cloning object!'
% entityType,'warning')
obj = copy.copy(self)
else:
obj = self
if not obj.entityType in obj.registeredEntities:
self.logger('Could not create entity, entityType:% is not valid!'
% entityType,'error')
return False
cmdArgs = ' createasset '
if backgroundAnalyze:
cmdArgs += '--background '
if projAddress:
if projAddress[0:9] == '/project/':
cmdArgs += '--projaddr %s' % projAddress
else:
self.logger('Found invalid project address: %s, cannot link!'
% projAddress,'warning')
cmdArgs += ' %s %s' % (mdSet,fcsPath)
## If we can escalate to root, do so
if self.useSudo and not os.geteuid() == 0:
fcsvrCMDTXT = '/usr/bin/sudo \'%s\' %s' % (self.pathToFCSVRClient,cmdArgs)
else:
if not os.geteuid() == 0:
self.logger('Could not create asset, permission denied!','error')
raise FCSVRClientPermissionDenied('createasset',cmdArgs)
else:
self.useSudo = False
fcsvrCMDTXT = '\'%s\' %s' % (self.pathToFCSVRClient,cmdArgs)
## Run our fcsvr_client command.
fcsvrCMD = subprocess.Popen('%s' % fcsvrCMDTXT,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvrCMD:\n %s' % "'%s' %s" % (obj.pathToFCSVRClient,cmdArgs),'debug')
self.logger('fcsvr_client output: %s fcsPath: %s' % (fcsvrCMD_STDOUT,fcsPath),'debug')
if not fcsvrCMD.returncode == 0:
##self.logger('FCSVRClient Error: %s\n%s\nCommand Syntax: %s'
##% (fcsvrCMD_STDOUT,fcsvrCMD_STDERR,fcsvrCMDTXT),'error')
##return False
if fcsvrCMD_STDOUT:
reportString = fcsvrCMD_STDOUT
else:
reportString = fcsvrCMD_STDERR
return self.fcsvr_client_error(errorString=reportString,
cmdString=fcsvrCMDTXT)
## Resolve our entity ID from STDOUT
try:
match = re.match('/asset/(\d+)',fcsvrCMD_STDOUT)
assetID = match.group(1)
except:
self.logger('Could not retrieve asset id from string: %s'
% fcsvrCMD_STDOUT,'error')
return False
self.logger('Created new Asset with ID:%s' % assetID,'debug')
obj.entityType = 'asset'
obj.entityID = assetID
if obj.fields and setMD:
obj.setMD()
return obj
[docs] def fcsvr_client_make_link(self,linkType,parentPath='',childPath='',moveLink=False):
'''This is a wrapper function around fcsvr_client make_link'''
if not childPath:
self.logger('fcsvr_client_make_link() no childPath provided!','error')
return False
if not parentPath:
if self.entityType and self.entityID:
parentPath = '/%s/%s' % (self.entityType,self.entityID)
else:
self.logger('fcsvr_client_make_link() cannot make link, parentPath '
'not provided, entityType,entityID not set!','error')
return False
try:
linkType = int(linkType)
except:
self.logger('fcsvr_client_make_link() provided non-int linkType:%s'
% linkType,'error')
return False
## Create our link
cmdArgs = 'make_link --linktype %s' % linkType
if moveLink:
cmdArgs += ' --movelink'
cmdArgs += ' \'%s\' \'%s\'' % (parentPath,childPath)
## Creating links with fcsvr_client requires root access. Make sure we're
## running under effective UID 0, or that we have useSudo set (requires
## modified sudoers file).
if self.useSudo and not os.geteuid() == 0:
fcsvrCMDTXT = '/usr/bin/sudo \'%s\' %s' % (self.pathToFCSVRClient,cmdArgs)
else:
if not os.geteuid() == 0:
self.logger('Could not create link, permission denied!','error')
raise FCSVRClientPermissionDenied('make_link',cmdArgs)
else:
self.useSudo = False
fcsvrCMDTXT = '\'%s\' %s' % (self.pathToFCSVRClient,cmdArgs)
## Run our fcsvr_client command.
fcsvrCMD = subprocess.Popen('%s' % fcsvrCMDTXT,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
fcsvrCMD_STDOUT,fcsvrCMD_STDERR = fcsvrCMD.communicate()
self.logger('fcsvrCMD:\n %s' % fcsvrCMDTXT,'debug')
if not fcsvrCMD.returncode == 0:
##errorString = 'Failed to create link! %s %s' % (fcsvrCMD_STDERR,fcsvrCMD_STDOUT)
##self.logger(errorString,'error')
##raise FCSVRClientError(errorString,fcsvrCMDTXT)
return self.fcsvr_client_error(errorString=fcsvrCMD_STDOUT,
cmdString=fcsvrCMDTXT)
## Flush our caches
self.flushCaches()
return True
[docs] def fcsvr_client_error(self,errorString='',cmdString=''):
'''This method interprets an fcsvr_client error string and will throw
the appropriate exception, it should be called if fcsvr_client returns
a non-0 exit code.'''
self.logger('fcsvr_client_error() Hit!','debug')
resolvedCode = ''
resolvedDesc = ''
resolvedNode = ''
resolvedSourceFile = ''
resolvedSourceLine = ''
## Create a dom object from our string:
try:
myDom = minidom.parseString(errorString)
sessionRoot = myDom.childNodes[0]
for value in sessionRoot.getElementsByTagName('value'):
#self.logger('dbFieldNameForFieldName() rootValue nodename: %s' % rootValue.nodeName,'debug')
valueID = value.attributes['id'].value
self.logger('fcsvr_client_error() Searching value: %s' % valueID,'debug')
if valueID == 'CODE':
resolvedCode = value.childNodes[1].childNodes[0].data
self.logger('fcsvr_client_error() found error code:%s'
% resolvedCode,'debug')
elif valueID == 'DESC':
resolvedDesc = value.childNodes[1].childNodes[0].data
elif valueID == 'NODE':
resolvedNode = value.childNodes[1].childNodes[0].data
elif valueID == 'SRC_FILE':
resolvedSourceFile = value.childNodes[1].childNodes[0].data
elif valueID == 'SRC_LINE':
resolvedSourceLine = value.childNodes[1].childNodes[0].data
except:
## If the DOM failed, attempt to extract from string (some fcsvr_client
## actions do not support -xml
try:
stringSearchResults = re.search('{.*CODE = (.*), DESC = (.*)'
', NODE = (.*), SRC_FILE = (.*), SRC_LINE = (.*) }',errorString)
resolvedCode = stringSearchResults.groups()[0]
resolvedDesc = stringSearchResults.groups()[1]
resolvedNode = stringSearchResults.groups()[2]
resolvedSourceFile = stringSearchResults.groups()[3]
resolvedSourceLine = stringSearchResults.groups()[4]
except:
raise FCSVRClientError(errorString='An Unknown error occurred, could '
'not parse fcsvr_client output: %s' % errorString,cmdString=cmdString)
#try:
if resolvedCode == 'E_COM':
if resolvedDesc:
msg = resolvedDesc
else:
msg = ('Server is not running!')
raise FCSVROfflineError(errorString=msg)
elif resolvedCode == 'E_FILE':
if resolvedDesc:
msg = resolvedDesc
else:
msg = ('No such file or directory!')
raise FCSVRClientFileError(msg)
elif resolvedCode == 'E_DUPLICATE':
if resolvedDesc:
msg = resolvedDesc
else:
msg = ('A duplicate action has been recorded: this action may have '
'already been applied!')
raise FCSDuplicateError(msg)
elif resolvedCode == 'E_NOTSUPP':
if resolvedDesc:
msg = resolvedDesc
else:
msg = ('Action is not supported!!')
raise FCSVRClientError(msg)
else:
raise FCSVRClientError(errorString='An Unknown error occurred. Code: \'%s\''
' Desc: \'%s\' ErrorString:\'%s\'' % (resolvedCode,resolvedDesc,errorString)
,cmdString=cmdString)
return False
#### BEGIN EXCEPTIONS ####
[docs]class FCSFieldNotFoundError(Exception):
def __init__(self, fieldName='',dbname=''):
self.fieldName = fieldName
self.dbname = dbname
def __str__(self):
if self.dbname:
returnString = 'No Field with dbkey:\'%s\' exists!' % self.dbname
return repr(returnString)
else:
returnString = 'No Field with key:\'%s\' exists!' % self.fieldName
return repr(returnString)
[docs]class FCSAssetOfflineError(Exception):
def __init__(self,assetID='',assetPath='',assetTitle=''):
self.assetID = assetID
self.assetPath = assetPath
self.assetTitle = assetTitle
def __str__(self):
returnString = 'Asset is offline.'
if self.assetID:
returnString += ' AssetID:%s' % self.assetID
if self.assetPath:
returnString += ' AssetPath:\'%s\'' % self.assetID
if self.assetTitle:
returnString += ' Title:\'%s\'' % self.assetTitle
return repr(returnString)
[docs]class FCSEntityNotFoundError(Exception):
def __init__(self,entityType='asset',entityID='',entityPath='',entityTitle=''):
self.entityType = entityType
self.entityID = entityID
self.entityPath = entityPath
self.entityTitle = entityTitle
def __str__(self):
if self.entityID:
return repr('No %s with id:\'%s\' exists!'
% (self.entityType,self.entityID))
elif self.entityPath:
return repr('No %s with path:\'%s\' exists!'
% (self.entityType,self.entityPath))
elif self.entityTitle:
return repr('No %s with Title:\'%s\' exists!'
% (self.entityType,self.entityTitle))
else:
return repr('No %s with id:\'%s\' exists!'
% (self.entityType,self.entityID))
[docs]class FCSValidationError(Exception):
def __init__(self, fieldName='', dataType='', value='', validationInfo=''):
self.fieldName = fieldName
self.dataType = dataType
self.value = value
self.validationInfo = validationInfo
def __str__(self):
string = ('Could not set new value for field:\'%s\', dataType:\'%s\', '
'value:\'%s\' did not pass validation! %s'
% (self.fieldName,self.dataType,self.value,self.validationInfo))
return repr(string)
[docs]class FCSDuplicateError(Exception):
def __init__(self, error=''):
self.error = error
def __str__(self):
return repr(self.error)
[docs]class FCSError(Exception):
def __init__(self, errorString):
self.errorString = errorString
def __str__(self):
return repr('FCS Error: %s\n' % errorString)
[docs]class FCSVRClientError(Exception):
def __init__(self,errorString,cmdString=''):
self.errorString = errorString
self.cmdString = cmdString
def __str__(self):
return repr('fcsvr_client Error: %s\nCommand Syntax:%s\n'
% (self.errorString,self.cmdString))
[docs]class FCSVRClientFileError(Exception):
def __init__(self,errorString,cmdString=''):
self.errorString = errorString
self.cmdString = cmdString
def __str__(self):
message = ('fcsvr_client Error: %s\nCommand Syntax:%s\n'
% (self.errorString,self.cmdString))
return repr(message)
[docs]class FCSVROfflineError(Exception):
def __init__(self,errorString):
self.errorString = errorString
def __str__(self):
return repr(self.errorString)
[docs]class FCSVRClientPermissionDenied(Exception):
def __init__(self,action,cmdString=''):
self.action = action
self.cmdString = cmdString
def __str__(self):
return repr('fcsvr_client Permission denied for action: %s cmdString: %s'
% (self.action,self.cmdString))
[docs]class FCSObjectLoadError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
[docs]class FCSProductionLoadError(Exception):
def __init__(self,errorString,retString,cmdString):
self.errorString = errorString
self.retString = retString
self.cmdString = cmdString
def __str__(self):
retString = self.errorString
if self.cmdString:
retString += '\nCommand: %s' % (self.cmdString)
if self.retString:
retString += '\nReturned Text: %s' % self.retString
return retString