Beispiel #1
0
    def init(self, **kwargs):
        logMsg(log='all')

        self.__userid = kwargs.pop("user", "")
        self.__passwd = kwargs.pop("password", "")

        bShotgun = kwargs.pop("shotgun", True)
        bDamas = kwargs.pop("damas", True)

        bExists = self._instanceExists()
        if bExists and self.isAuthenticated():
            return True

        print "<{}> Initializing...".format(self)

        if bShotgun:
            self.__initShotgun()
            if not bDamas:
                self.__authClass = ShotgunAuth

        if bDamas:
            self.__initDamas()
            if not bShotgun:
                self.__authClass = DamasAuth

        if bShotgun and bDamas:
            self.__authClass = DualAuth

        if not self.authenticate(renew=True):
            return False

        if inDevMode() and kwargs.pop("checkTemplates", True):
            sMissingPathList = []
            self._checkTemplatePaths(sMissingPathList)
            if sMissingPathList:
                msg = "Missing template paths:\n    " + '\n    '.join(sMissingPathList)
                logMsg(msg , warning=True)
                #return False

        self._db = DrcDb(self)

        return True
Beispiel #2
0
class DamProject(object):

    _instancesDct = {}

    @classmethod
    def fromPath(cls, p, fail=False, **kwargs):

        sProjName = projectNameFromPath(p)
        if not sProjName:
            if fail:
                raise ValueError("Path NOT part of a KNOWN PROJECT: '{}'"
                                 .format(p))
            return None

        return cls(sProjName, **kwargs)

    def __new__(cls, sProjName, **kwargs):
        logMsg(cls.__name__ , log='all')

        sProjName = sProjName.lower()

        bExists = True
        proj = cls._instancesDct.get(sProjName)
        if not proj:
            bExists = False

            proj = object.__new__(cls)
            proj.name = sProjName

            libClass = DrcLibrary
            if "maya" in hostApp():
                try:
                    from davos_maya.core.mrclibrary import MrcLibrary
                except ImportError:
                    pass
                else:
                    libClass = MrcLibrary

            proj.__libClass = libClass#kwargs.pop("libraryType", DrcLibrary)

            proj.reset()

        if not kwargs.pop("empty", False):

            if not proj.init(**kwargs):
                return None

            proj.loadLibraries()

        if not bExists:
            cls._instancesDct[sProjName] = proj

        #print id(proj), proj

        return proj

    def reset(self):
        logMsg(log='all')

        self._damasdb = None
        self._shotgundb = None
        self._db = None
        self._authobj = None
        self._itemmodel = None
        self.__loggedUser = None
        self.__userid = ""
        self.__passwd = ""
        self.__authClass = None
        self.authenticated = False
        self.loadedLibraries = {}

        self.mayaLoadReferences = True
        self.showPrivateLibraries = os.environ.get("DAVOS_SHOW_PRIV_LIBS", False)

        self.loadConfigModule()

        self.__confLibraries = self.getVar("project", "libraries")

    def init(self, **kwargs):
        logMsg(log='all')

        self.__userid = kwargs.pop("user", "")
        self.__passwd = kwargs.pop("password", "")

        bShotgun = kwargs.pop("shotgun", True)
        bDamas = kwargs.pop("damas", True)

        bExists = self._instanceExists()
        if bExists and self.isAuthenticated():
            return True

        print "<{}> Initializing...".format(self)

        if bShotgun:
            self.__initShotgun()
            if not bDamas:
                self.__authClass = ShotgunAuth

        if bDamas:
            self.__initDamas()
            if not bShotgun:
                self.__authClass = DamasAuth

        if bShotgun and bDamas:
            self.__authClass = DualAuth

        if not self.authenticate(renew=True):
            return False

        if inDevMode() and kwargs.pop("checkTemplates", True):
            sMissingPathList = []
            self._checkTemplatePaths(sMissingPathList)
            if sMissingPathList:
                msg = "Missing template paths:\n    " + '\n    '.join(sMissingPathList)
                logMsg(msg , warning=True)
                #return False

        self._db = DrcDb(self)

        return True

    def loadConfigModule(self):

        try:
            self._confobj = PyConfParser(getConfigModule(self.name))
        except ImportError as e:
            #if kwargs.pop("warn", True):
            logMsg(e.message , warning=True)
            return False

    def _instanceExists(self):
        return id(self.__class__._instancesDct.get(self.name)) == id(self)

    def authenticate(self, renew=False):

        if renew or (not self._authobj):
            self._authobj = self.getAuthenticator()

        userData = self._authobj.authenticate(user=self.__userid,
                                              password=self.__passwd)

        if not self.isAuthenticated():
            return False

        self.__loggedUser = DamUser(self, userData)

        sUsrLogin = self.__loggedUser.loginName
        self.loadEnviron()

        self._authobj.userLogin = sUsrLogin

        return True

    def getAuthenticator(self):

#        sAuthFullName = self.getVar("project", "authenticator_class", "")
#        if not sAuthFullName:
#            return HellAuth()
#        else:
#            sAuthMod, sAuthClass = sAuthFullName.rsplit(".", 1)
#            exec("from {} import {}".format(sAuthMod, sAuthClass))
#
#            return eval(sAuthClass)(self)

        if not self.__authClass:
            return HellAuth()

        return self.__authClass(self)

    def isAuthenticated(self):

        if not self._authobj:
            return False

        bAuth = self._authobj.authenticated

        if not bAuth:
            logMsg("The project is not authenticated.", warning=True)

        return bAuth

    def loggedUser(self, **kwargs):
        logMsg(log='all')

        bForce = kwargs.get("force", False)

        if bForce and (not self.isAuthenticated()):
            self.authenticate()

        return self.__loggedUser

    def loadLibraries(self, noError=False):
        logMsg(log='all')

        bExists = self._instanceExists()
        if bExists and self.loadedLibraries:
            return

        if not self.isAuthenticated():
            return

        if not self._checkLibraryPaths(noError=noError):
            return

        print "<{}> Loading libraries...".format(self)

        for drcLib in self.iterLibraries(dbNode=True, weak=False):
            if drcLib.primeProperty().viewItems:
                drcLib.updModelRow()
            else:
                drcLib.addModelRow()

    def iterLibraries(self, dbNode=True, weak=False, remember=True):

        for sSpace, sLibSection in self._iterLibrariesSpaceAndSection():
            drcLib = self.getLibrary(sSpace, sLibSection, dbNode=dbNode, weak=weak,
                                     remember=remember)
            if drcLib:
                yield drcLib

    def getLibrary(self, sSpace, sLibSection, owner="", dbNode=True, weak=False,
                   remember=True, tokens=None):
        logMsg(log='all')

        self._assertSpaceAndSection(sSpace, sLibSection)

        sFullLibName = DrcLibrary.makeFullName(owner, sSpace, sLibSection)
        drcLib = self.loadedLibraries.get(sFullLibName, None)

        if not drcLib:
            if tokens:
                sLibPath = self.getPath(sSpace, sLibSection, resVars=False, tokens=tokens)
            else:
                sLibPath = self.getPath(sSpace, sLibSection)

            drcLib = self.__libClass(sLibSection, sLibPath, sSpace, owner=owner,
                                     project=self, dbNode=dbNode, remember=remember)
            if weak:
                return drcLib

            if osp.isdir(sLibPath):
                return drcLib
            else:
                logMsg("No such '{}': '{}'.".format(sFullLibName, sLibPath),
                       warning=True)
                return None

        return drcLib

    def makeCurrent(self):

        self.loadEnviron(force=True)
        m = sys.modules["__main__"]
        m.DAVOS_CURRENT_PROJECT = self

    def loadEnviron(self, force=False):

        sMsg = "\nLoading '{}' environment:".format(self.name)

        envFunc = hostSetEnvFunc()
        if envFunc:
            print sMsg, "(using {}.{})".format(envFunc.__module__, envFunc.__name__)
        else:
            print sMsg

        if self.__loggedUser:
            sUsrLogin = self.__loggedUser.loginName
            updEnv("DAVOS_USER", sUsrLogin, conflict="replace", usingFunc=envFunc)
            if inDevMode() and sUsrLogin == "sebastienc":
                updEnv("ZOMB_TOOL_PATH", r"\\ZOMBIWALK\Z2K_RnD\tool_testing",
                       conflict="replace")
                updEnv("IN_SEB_MODE", "1", conflict="replace")
            else:
                updEnv("IN_SEB_MODE", "", conflict="replace")

        sConflict = "replace" if force else "keep"
        for sSpace, sLibSection in self._iterLibrariesSpaceAndSection():

            sEnvVars = self.getVar(sLibSection, sSpace + "_" + "path_envars", default=())

            for sVar in sEnvVars:
                updEnv(sVar, self.getPath(sSpace, sLibSection, resEnvs=False),
                       conflict=sConflict, usingFunc=envFunc)

        self.loadConfigModule()

    def getAsset(self, sAstName):
        return DamAsset(self, name=sAstName)

    def getShot(self, sShotName):
        return DamShot(self, name=sShotName)

    def getPath(self, sSpace, sSection, pathVar="", tokens=None, default="NoEntry", **kwargs):

        bResVars = kwargs.get("resVars", True)
        bResEnvs = kwargs.get("resEnvs", True)

        sRcPath = ""
        if sSpace:
            sRcPath = self.getVar(sSection, sSpace + "_path", default=default,
                                  resVars=bResVars)
            if not sRcPath:
                return sRcPath

        if pathVar:
            try:
                sRcPath = pathJoin(sRcPath, self.getVar(sSection, pathVar))
            except AttributeError:
                if default != "NoEntry":
                    return default
                raise

        if bResEnvs:
            sRcPath = pathResolve(sRcPath)

        sFieldSet = set()
        if bResVars:
            # resolve vars from config
            sFieldSet = set(findFmtFields(sRcPath))
            if sFieldSet:

                confTokens = self.getVar(sSection, pathVar + "_tokens", default={})
                sConfFieldSet = set(confTokens.iterkeys())

                for sField in sFieldSet:

                    if sField in confTokens:
                        continue

                    value = self.getVar(sSection, sField, "")
                    if value:
                        sConfFieldSet.add(sField)
                    else:
                        value = '{' + sField + '}'

                    confTokens[sField] = value

                if confTokens:
                    sRcPath = sRcPath.format(**confTokens)

                sFieldSet -= sConfFieldSet

        # resolve remaining vars from input tokens
        if tokens:
            if not isinstance(tokens, dict):
                raise TypeError("argument 'tokens' must be of type <dict>. Got {}"
                                .format(type(tokens)))

            sFieldSet = sFieldSet - set(tokens.iterkeys())
            if sFieldSet:
                msg = ("Cannot resolve path: '{}'. \n\tMissing tokens: {}"
                        .format(sRcPath, list(sFieldSet)))
                raise RuntimeError(msg)

            sRcPath = sRcPath.format(**tokens)

        return pathNorm(sRcPath, keepEndSlash=True)

    def getVar(self, sSection, sVarName, default="NoEntry", **kwargs):
        return self._confobj.getVar(sSection, sVarName, default=default, **kwargs)

    def getRcParam(self, sSection, sRcName, sParam, default="NoEntry"):

        rcSettings = self.getVar(sSection, "resource_settings", {})

        if default == "NoEntry":
            return rcSettings[sRcName][sParam]
        else:
            return rcSettings.get(sRcName, {}).get(sParam, default)

    def getResource(self, sSpace, sRcSection, sRcName="", tokens=None,
                    default="NoEntry", fail=False, **kwargs):

        sRcPath = self.getPath(sSpace, sRcSection,
                               pathVar=sRcName,
                               tokens=tokens,
                               default=default,
                               )

        drcEntry = self.entryFromPath(sRcPath, **kwargs)

        if (not drcEntry) and fail:
            raise RuntimeError("No such resource path: '{}'".format(sRcPath))

        return drcEntry

    def isEditableResource(self, sAbsPath, assertion=False):

        p = pathNorm(sAbsPath)

        sFileName = osp.basename(p)
        if not (sFileName and osp.splitext(p)[1]):
            if assertion:
                raise EnvironmentError("Invalid filename: '{}'".format(sFileName))
            return False

        bPatternOk = False
        sPatrnList = self.getVar("project", "editable_file_patterns", ())
        for sPatrn in sPatrnList:
            if fnmatch(sFileName, sPatrn):
                bPatternOk = True
                break

        if not bPatternOk:
            if assertion:
                raise EnvironmentError("Not in project's editable files: {}."
                                       .format(" ".join(sPatrnList)))
            return False

        drcEntry = self.entryFromPath(sAbsPath, dbNode=False)
        if drcEntry and drcEntry.isFile():
            if drcEntry.parentDir().freeToPublish():
                return True

        ctxData = self.contextFromPath(p)

        sSection = ctxData.get("section")
        sRcName = ctxData.get("resource")

        if not sRcName:
            if assertion:
                raise EnvironmentError("Not a configured resource.")
            return False

        bEditable = self.getRcParam(sSection, sRcName, "editable", default=True)
        if not bEditable:
            if assertion:
                raise EnvironmentError("Resource configured as non-editable: '{}.{}' ."
                                       .format(sSection, sRcName))
            return False

        return True

    def rcFileFromPath(self, sFilePath, space="", library=None, fail=False, **kwargs):
        return self.entryFromPath(sFilePath, space=space, library=library,
                                  fail=fail, isFile=True, **kwargs)

    def entryFromPath(self, sEntryPath, space="", library=None, fail=False, **kwargs):

        bWarn = kwargs.pop("warn", True)
        bFile = kwargs.pop("isFile", False)
        bWeak = kwargs.pop("weak", False)

        if library:
            drcLib = library
        else:
            drcLib = self.libraryFromPath(sEntryPath, space=space)
            if not drcLib:
                sLibType = space.upper() if space else "KNOWN"
                sMsg = "Path NOT from {} library: '{}'".format(sLibType, sEntryPath)

                if fail:
                    raise ValueError(sMsg)
                else:
                    if bWarn:
                        logMsg(sMsg, warning=True)
                    return None

        try:
            if bFile:
                rcFile = drcLib._weakFile(sEntryPath, **kwargs)
                if bWeak:
                    return rcFile
                return rcFile if rcFile.isFile() else None

            return drcLib.getEntry(sEntryPath, **kwargs)

        except Exception as e:
            if fail:
                raise
            else:
                if bWarn:
                    logMsg(toStr(e), warning=True)
                return None

    def entryFromDbNode(self, dbnode, library=None, **kwargs):

        sDbPath = dbnode.getField('file')
        if library is None:
            library = self.libraryFromDbPath(sDbPath)
        drcEntry = library.entryFromDbPath(sDbPath, dbNode=False, **kwargs)
        if drcEntry:
            library._addDbNodeToCache(dbnode)
            drcEntry.refresh(simple=True, dbNode=False)

        return drcEntry

    def libraryFromDbPath(self, sDbPath):

        pubLibIter = (l for l in self.loadedLibraries.itervalues() if l.isPublic())

        for pubLib in pubLibIter:
            try:
                pubLib.dbToAbsPath(sDbPath)
            except ValueError:
                continue

            return pubLib

        return None

    def libraryFromPath(self, sAbsPath, space="", anyUser=False):

        sAbsPath = pathNorm(sAbsPath)

        if space:
            rcLibIter = (l for l in self.loadedLibraries.itervalues()
                          if (l.space == space))
        else:
            rcLibIter = self.loadedLibraries.itervalues()

        for rcLib in rcLibIter:
            if rcLib.contains(sAbsPath):
                return rcLib

        if (space != "public") and anyUser:

            for sSpace, sLibSection in self._iterLibrariesSpaceAndSection(space="private"):

                sRawLibPath = self.getPath(sSpace, sLibSection, resVars=False)

                parseRes = pathParse(sRawLibPath, sAbsPath)

                if parseRes and parseRes.named:
                    sOwner = parseRes.named["user_name"]
                    return self.getLibrary(sSpace, sLibSection, owner=sOwner, tokens=parseRes.named,
                                           dbNode=False, weak=True, remember=True)
        return None

    def entityFromPath(self, sRcPath, fail=False, library=None):
        try:
            ctxData = self.contextFromPath(sRcPath, library=library)
            return self._entityFromContext(ctxData, fail=fail)
        except:
            if fail:
                raise
            else:
                return None

    def _entityFromContext(self, ctxData, fail=True):

        err = ValueError("Path does NOT match any entity: '{}'"
                         .format(ctxData["abs_path"]))

        if "dam_entity" in ctxData:
            entity = ctxData["dam_entity"]
            if entity:
                return entity
            if fail:
                raise err
            else:
                return None

        if "section" not in ctxData:
            if fail:
                raise err
            else:
                return None

        entity = None
        sSection = ctxData['section']
        if fail:
            sEntityCls = self.getVar(sSection, "entity_class")
        else:
            sEntityCls = self.getVar(sSection, "entity_class", default="")

        if sEntityCls:
            cls = importClass(sEntityCls, globals(), locals())
            try:
                entity = cls(self, **ctxData)
            except:
                if fail:
                    raise

        return entity

    def contextFromPath(self, entryObjOrPath, library=None, warn=True):

        if isinstance(entryObjOrPath, DrcEntry):
            drcEntry = entryObjOrPath
            library = drcEntry.library
        else:
            drcEntry = self.entryFromPath(entryObjOrPath, library=library,
                                          dbNode=False, weak=True, warn=warn)
            if not drcEntry:
                logMsg("no entry found for '{}'".format(entryObjOrPath), log="debug")
                return {"abs_path":entryObjOrPath, "rc_entry":None}
            elif not library:
                library = drcEntry.library

        sAbsPath = drcEntry.absPath()
        ctxData = {"abs_path":sAbsPath, "rc_entry":drcEntry}
        sFoundRcName = ""
        bExactRc = False
        if drcEntry == library:
            sSection = library.sectionName
            sSpace = library.space
        else:
            sSpace, sSection = self.sectionFromPath(sAbsPath, library=library)
            if not sSection:
                logMsg("no section found for '{}'".format(sAbsPath), log="debug")
                return ctxData

            bIsFile = isinstance(drcEntry, DrcFile)
            if not drcEntry.isPublic():
                if bIsFile:
                    pubEntry = drcEntry.getPublicFile(fail=True, weak=True, dbNode=False)
                else:
                    pubEntry = drcEntry.getHomonym("public", weak=True)
            else:
                pubEntry = drcEntry

            pubLib = pubEntry.library

            if bIsFile and pubEntry.isVersionFile():
                ctxData["version"] = pubEntry.versionFromName()
                pubEntry = pubEntry.getHeadFile(fail=True, dbNode=False)

            sPublicPath = pubEntry.absPath()
            sPubPathDirs = pathSplitDirs(sPublicPath)
            numPubDirs = len(sPubPathDirs)

            def sortValue(sRcPath):
                n = len(pathSplitDirs(sRcPath))
                if n == numPubDirs:
                    return sys.maxint
                else:
                    return n

            sRcPathList = sorted(self.iterRcPaths("public", sSection, resVars=True),
                                 key=lambda x: sortValue(x[1]), reverse=True)

            sSectionPath = self.getPath("public", sSection, default="", resVars=False)
            if sSectionPath:
                sRcPathList.append(("", sSectionPath))

            parseRes = None
            sCurPubPath = sPublicPath
            numCurPubDirs = numPubDirs
            while (not sFoundRcName) and (not pathEqual(sCurPubPath, pubLib.absPath())):

                for sRcName, sRcPath in sRcPathList:
                    numRcDirs = len(pathSplitDirs(sRcPath))
                    if numRcDirs != numCurPubDirs:
                        continue

                    parseRes = pathParse(sRcPath, sCurPubPath)
                    #print "\n", sRcPath, sCurPubPath, parseRes, sRcName
                    if parseRes and parseRes.named:

                        sFoundRcName = sRcName
                        if numRcDirs == numPubDirs:
                            bExactRc = True
                            #print sRcName
                        break

                sCurPubPath = osp.dirname(sCurPubPath)
                numCurPubDirs = len(pathSplitDirs(sCurPubPath))

            if not parseRes:
                logMsg("no parsing results for '{}'".format(sCurPubPath), log="debug")
                return ctxData

            ctxData.update(parseRes.named)

        ctxData["section"] = sSection
        ctxData["space"] = sSpace
        ctxData["library"] = library

        if sFoundRcName:
            sRawRcPath = self.getPath("public", sSection, sFoundRcName, resVars=False)
            parseRes = pathParse(sRawRcPath, sPublicPath)
            ctxData.update((k, v) for k, v in parseRes.named.iteritems()
                                    if k not in ctxData)
            if bExactRc:
                ctxData["resource"] = sFoundRcName

        sStepDir = ctxData.get("step", "")
        if sStepDir:
            sSgStepDct = self.getVar(sSection, "sg_step_map", default={})
            if sStepDir in sSgStepDct:
                ctxData['sg_step'] = sSgStepDct[sStepDir]

        damEntity = None
        if "name" in ctxData:
            damEntity = self._entityFromContext(ctxData, fail=False)

        ctxData["dam_entity"] = damEntity

        return ctxData

    def sectionFromPath(self, sEntryPath, library=None):

        sEntryPath = normCase(sEntryPath)
        sEntryPathDirs = pathSplitDirs(normCase(sEntryPath))

        sectionDataList = sorted(self.iterSectionPaths(library=library),
                                 key=lambda x: len(x[2]),
                                 reverse=True)

        for sSpace, sSection, sSectionPath in sectionDataList:
            if pathStartsWith(sEntryPath, sSectionPath, pathSplits=sEntryPathDirs):
                return sSpace, sSection

        return "", ""

    def iterSectionPaths(self, library=None):

        sSpaceList = LIBRARY_SPACES
        #sLibPath = ""
        if library:
            sSpaceList = (library.space,)
            #sLibPath = normCase(library.absPath())

        for sSection, _ in self._confobj.listSections():

            if sSection == "project":
                continue

            for sSpace in sSpaceList:

                sSectionPath = self.getPath(sSpace, sSection, default="")
                if sSectionPath:

                    if library:
                        if not library.contains(sSectionPath):
                            continue

                    yield sSpace, sSection, sSectionPath

    def versionFileFromPrivatePath(self, sPrivFilePath, fail=False):

        privFile = self.entryFromPath(sPrivFilePath, space="private")
        if not privFile:
            raise ValueError("No such private file: '{}'".format(sPrivFilePath))

        v = privFile.versionFromName()
        if v is None:
            raise ValueError("Could not get version number from filename: '{}'"
                             .format(privFile.name))

        pubFile = privFile.getPublicFile(fail=True)

        return pubFile.getVersionFile(v, fail=fail)

    def publishEditedVersion(self, sSrcFilePath, **kwargs):

        bReturnDict = kwargs.pop("returnDict", False)
        bUploadApart = kwargs.pop("uploadApart", True)

        res = self.assertEditedVersion(sSrcFilePath)
        mainPubFile = res["public_file"]
        mainPrivFile = res["private_file"]
        privOutcomeDct = dict(res["outcome_files"])

        mainPubFile.ensureLocked()

        privOutcomeList = privOutcomeDct.values()

        pubOutcomeList = tuple(f.getPublicFile(fail=True) for f in privOutcomeList)
        outcomePairs = zip(pubOutcomeList, privOutcomeList)

        sUploadedRc = mainPubFile.getParam("sg_uploaded_movie", "")
        sMoviePathRc = mainPubFile.getParam("sg_path_to_movie", "")
        if privOutcomeDct:
            if sUploadedRc and (sUploadedRc not in privOutcomeDct):
                sMsg = (u"Resource('{}') to upload to shotgun is NOT in outcomes: {}"
                        .format(sUploadedRc, privOutcomeDct.keys()))
                logMsg(sMsg, warning=True)

            if sMoviePathRc and (sMoviePathRc not in privOutcomeDct):
                sMsg = (u"Resource('{}') to upload to shotgun is NOT in outcomes: {}"
                        .format(sUploadedRc, privOutcomeDct.keys()))
                logMsg(sMsg, warning=True)

        sgVersion = None
        iNxtVers = mainPubFile.currentVersion + 1
        try:
            for pubFile, privFile in outcomePairs:

                pubFile.assertEditedVersion(privFile, version=iNxtVers, outcomes=False)
                pubFile.ensureLocked(autoLock=True)

            mainVersFile, sgVersion = mainPubFile.publishEditedFile(mainPrivFile, checkLock=False,
                                                                    **kwargs)
        except:
            for pubFile in pubOutcomeList:
                pubFile.restoreLockState()
            raise

        uploadMovieFile = privOutcomeDct.get(sUploadedRc, None)
        pathToMovieFile = privOutcomeDct.get(sMoviePathRc, None)

        for pubFile, privFile in outcomePairs:

            outcomeVersFile, _ = pubFile.publishEditedFile(privFile, comment=mainPubFile.comment,
                                                           version=iNxtVers, checkLock=False)
            if pathToMovieFile and (pathToMovieFile == privFile):
                pathToMovieFile = outcomeVersFile

        if sgVersion:
            if pathToMovieFile:
                self.updateSgEntity(sgVersion, sg_path_to_movie=pathToMovieFile.envPath())

            if uploadMovieFile:
                self.uploadSgVersion(sgVersion, uploadMovieFile.absPath(), apart=bUploadApart)

        if not bReturnDict:
            return mainPrivFile, privOutcomeDct
        else:
            return {"private_file":mainPrivFile,
                    "private_outcome_files":privOutcomeDct,
                    "public_file":mainPubFile,
                    "public_outcome_files":pubOutcomeList,
                    "version_file":mainVersFile,
                    "sg_version":sgVersion,
                    }


    def assertEditedVersion(self, sSrcFilePath, **kwargs):

        privFile = self.entryFromPath(sSrcFilePath, space="private", fail=True)
        pubFile = privFile.getPublicFile(fail=True)
        res = {"private_file":privFile, "public_file":pubFile}

        res.update(pubFile.assertEditedVersion(privFile, **kwargs))

        return res

    def getDependencyTypes(self, sSection, sRcName=""):

        dependDct = self.getVar(sSection, "dependency_types", {})

        if not sRcName:
            return dependDct

        rcsSettings = self.getVar(sSection, "resource_settings")
        if sRcName in rcsSettings:
            rcDepDct = rcsSettings[sRcName].get("dependency_types", {})

            conflicts = set(dependDct.iterkeys()) & set(rcDepDct.iterkeys())
            if conflicts:
                sMsg = ("Dependencies defined twice for '{}' section and '{}' resource: {}"
                        .format(sSection, sRcName, ", ".join(conflicts)))
                raise EnvironmentError(sMsg)

            dependDct.update(rcDepDct)

        return dependDct

    def iterSgSteps(self, sEntityType=""):

        stepList = self._shotgundb.getSteps()
        for stepInfo in stepList:
            if sEntityType and (stepInfo['entity_type'] == sEntityType):
                yield stepInfo

    def createSgVersion(self, sVersionName, sgEntity, sgTask, sComment, sFilePath, **kwargs):

        shotgundb = self._shotgundb
        if not shotgundb:
            return None

        sgVersion = shotgundb.createVersion(sgEntity["type"], sgEntity,
                                            sVersionName, sgTask,
                                            sComment, sFilePath, **kwargs)
        return sgVersion

    @setWaitCursor
    def uploadSgVersion(self, sgVersion, sMediaPath, **kwargs):
        return self._shotgundb.uploadVersion(sgVersion, sMediaPath, **kwargs)

    def publishSgVersions(self, versionFilesList):

        headFileSet = set()
        for versFile in versionFilesList:
            headFile = versFile.getHeadFile(fail=True)
            headFileSet.add(headFile)

        if len(headFileSet) > 1:
            raise ValueError("Given files should be versions of the same file ! Got {}."
                             .format(tuple(headFileSet)))

        headFile = headFileSet.pop()

        ctxData = headFile.contextFromPath()
        sSection = ctxData.get("section")
        sRcName = ctxData.get("resource")
        if not sRcName:
            raise AssertionError("No such configured resource: {}".format(headFile))

        bSgVersion = self.getRcParam(sSection, sRcName, 'create_sg_version', default=False)
        if not bSgVersion:
            raise RuntimeError("Public file not configured to have shotgun versions: {} !"
                               .format(headFile))

        damEntity = headFile.getEntity(fail=True, ctxData=ctxData)
        sgVersList = damEntity.findSgVersions(resourceName=sRcName)
        sSgVersPathList = tuple(d["sg_source_file"] for d in sgVersList)

        sgVersionData = headFile._beginPublishSgVersion()
        sgTask = sgVersionData.get("sg_task")
        if (not sgTask):
            raise RuntimeError("No Shotgun Task given or selected !")

        newSgVersions = versionFilesList[:]
        for i, versionFile in enumerate(versionFilesList):

            sgVersionData = headFile._beginPublishSgVersion(versionFile, sgVersionData)

            sgVersion = None
            if versionFile.envPath() in sSgVersPathList:
                print "Shotgun Version already exists for {}.".format(versionFile)
            else:
                sgVersion = versionFile.createSgVersion(sgVersionData)
                print "created shotgun version", sgVersion

            newSgVersions[i] = sgVersion

        return newSgVersions

    def getSgVersion(self, versionFile):

        shotgundb = self._shotgundb
        if not shotgundb:
            return None

        filters = [['project', 'is', shotgundb.getProjectInfo()],
                   ['sg_source_file', 'is', versionFile.envPath()]]

        fields = ['code', 'sg_current_release_version', 'sg_task', 'sg_source_file',
                  'sg_status_list']

        return shotgundb.sg.find_one("Version", filters, fields)

    def findSgVersions(self, sgEntity=None, rcEntry=None, sgTask=None, moreFilters=None,
                       moreFields=None, limit=0):

        shotgundb = self._shotgundb

        if rcEntry:
            assert rcEntry.isPublic(), "Given file must be public."
            #assert not rcEntry.isVersionFile(), "Given file must not be a version."

        filters = [['project', 'is', shotgundb.getProjectInfo()],
                   ]

        if sgEntity is not None:
            filters.append(['entity', 'is', sgEntity])

        order = [{'field_name':'created_at', 'direction':'desc'}]

        if sgTask:
            filters.append(['task', 'is', sgTask])

        if rcEntry:
            if rcEntry.isVersionFile():
                versFile = rcEntry
            else:
                versFile = rcEntry.getVersionFile(0, weak=True)

            sEnvPath = versFile.envPath()
            sBasePath, sExt = osp.splitext(sEnvPath)
            sBasePath = sBasePath.rsplit('-v', 1)[0] + '-v'

            filters += [['sg_source_file', 'starts_with', sBasePath],
                        ['sg_source_file', 'ends_with', sExt]]

            order = [{'field_name':'sg_source_file', 'direction':'desc'}]

        if moreFilters:
            filters.extend(moreFilters)

        fields = ['code', 'sg_current_release_version', 'sg_task', 'sg_source_file',
                  'sg_status_list']
        if moreFields:
            fields.extend(moreFields)

        return shotgundb.sg.find("Version", filters, fields,
                                 order=order,
                                 limit=limit)

    def updateSgEntity(self, sgEntity, **data):
        return self._shotgundb.updateEntity(sgEntity, **data)

    def findDbNodes(self, sQuery="", **kwargs):

        sBaseQuery = u"file:/^{}/i"

        sDamasPath = self.getVar("project", "damas_root_path")
        sBaseQuery = sBaseQuery.format(sDamasPath)
        sFullQuery = " ".join((sBaseQuery, sQuery))

        return self._db.findNodes(sFullQuery, **kwargs)

    def linkResourceFiles(self, targetFile, sourceFileList, data=None):

        drcFileDct = {}
        def storeFile(drcFile):
            drcFileDct[osp.normcase(drcFile.dbPath())] = drcFile

        drcFileList = [targetFile] + sourceFileList

        headFileList = drcFileList[:]

        for i, drcFile in enumerate(drcFileList):
            if drcFile.isVersionFile():
                headFile = None
                try:
                    headFile = drcFile.getHeadFile(dbNode=False, fail=True)
                except RuntimeError as e:
                    print toStr(e)

                headFileList[i] = headFile
                if headFile:
                    storeFile(headFile)

        self.dbNodesFromEntries(headFileList)


        versFileList = drcFileList[:]

        for i, drcFile in enumerate(drcFileList):

            if drcFile.isVersionFile():
                continue

            try:
                versFile = drcFile.assertLatestFile(refresh=False, returnVersion=True)
            except EnvironmentError as e:
                logMsg(e.message, warning=True)
                continue

            if versFile:
                versFileList[i] = versFile

        self.dbNodesFromEntries(versFileList)

        headTrgtId = headFileList[0]._dbnode.id_
        versTrgtId = versFileList[0]._dbnode.id_

        targetIds = (headTrgtId, versTrgtId)
        graphNodes = self._damasdb.graph(targetIds)

        srcIds = {}
        for n in graphNodes:
            if ('src_id' in n) and ('tgt_id' in n):
                srcIds.setdefault(n['tgt_id'], set()).add(n['src_id'])

        def prunedDbPaths(fileList):
            for i, f in enumerate(fileList):
                dbNode = f.loadDbNode(fromDb=False)
                if i == 0:
                    trgtId = dbNode.id_
                else:
                    if not dbNode:
                        continue
                    if dbNode.id_ in srcIds.get(trgtId, []):
                        print "already linked '{}' -> '{}'".format(dbNode.file, fileList[0]._dbnode.file)
                        continue
                yield f.dbPath()

#        linkIds = tuple(n["_id"] for d in graphNodes if ('src_id' in d) and ('tgt_id' in d))
#        for id_ in linkIds:
#            self._damasdb.delete(id_)

        if data is None:
            data = {"link_type":"undefined"}

        links = {}
        sDbPathList = list(prunedDbPaths(headFileList))
        if len(sDbPathList) > 1:
            res = self._damasdb.link(sDbPathList[0], sDbPathList[1:], data)
            if res:
                links.update(res)

        sDbPathList = list(prunedDbPaths(versFileList))
        if len(sDbPathList) > 1:
            res = self._damasdb.link(sDbPathList[0], sDbPathList[1:], data)
            if res:
                links.update(res)

        print "done", links

        return targetIds

    def dbNodesFromEntries(self, rcEntryList, load=True):

        dbNodeList = len(rcEntryList) * [None]

        entryDct = {}
        for i, drcEntry in enumerate(rcEntryList):
            entryDct.setdefault(drcEntry.dbPath(), []).append((i, drcEntry))

#        sQueryList = list("REGEX_^{}".format(p) for p in entryDct.iterkeys())
#        for s in sQueryList:
#            print s

        sQueryList = entryDct.keys()

        mgQuery = {"file": {"$in":sQueryList}}
        ids = self._damasdb.search_mongo(mgQuery, None, None, None)
        if not ids:
            return dbNodeList

        for dbNode in self._db.nodeForIds(ids):
            for i, each in enumerate(entryDct.get(dbNode.file, [])):
                inIdx, drcEntry = each
                if load and i == 0:
                    drcEntry._cacheDbNode(dbNode)
                dbNodeList[inIdx] = dbNode

        return dbNodeList

    def iterConfigOutcomes(self, sSection):

        rcsSettings = self.getVar(sSection, "resource_settings")
        for sRcName, settings in rcsSettings.iteritems():
            outcomes = settings.get("outcomes")
            if outcomes:
                yield sRcName, outcomes

    def _evalSyncRules(self, in_sRuleList):

        sAllSites = set(self.listAllSites())

        sRuleList = in_sRuleList
        if len(sRuleList) == 1:
            sRule = sRuleList[0]
            if sRule == "all_sites":
                sRuleList = sAllSites
            elif sRule == "no_sync":
                sRuleList = []
            elif sRule not in sAllSites:
                raise ValueError("Invalid rule: '{}'".format(sRule))

        sSiteList = set(sRuleList)
        if sSiteList:
            sBadSites = sSiteList - sAllSites
            if sBadSites:
                raise ValueError("Unknown sites: {}".format(",".join(sBadSites)))

        syncData = dict((s, "1" if s in sSiteList else None) for s in sAllSites)

        return syncData

    def listAllSites(self):
        return list(self.getVar("project", "all_sync_sites"))

    def getCurrentSite(self):
        sSite = os.environ.get("DAVOS_SITE")
        if not sSite:
            raise EnvironmentError("DAVOS_SITE not defined.")
        else:
            sSiteList = self.listAllSites()
            if sSite not in sSiteList:
                raise EnvironmentError("Invalid DAVOS_SITE: '{}'. Expected one of: {}."
                                       .format(sSite, ", ".join(sSiteList)))
        return sSite

    def listAllSgAssets(self, includeOmitted=False, moreFilters=None, moreFields=None):

        shotgundb = self._shotgundb
        sg = shotgundb.sg

        sSectionList = []
        confobj = self._confobj
        for sSection, _ in confobj.listSections():
            if not confobj.getVar(sSection, "template_dir", ""):
                continue

            sSectionList.extend(confobj.getVar(sSection, "aliases", ()))

        filters = [["project", "is", shotgundb.getProjectInfo()],
                   ["sg_asset_type", "in", sSectionList]
                   ]

        if not includeOmitted:
            filters += [["sg_status_list", "is_not", "omt"],
                        ]

        if moreFilters:
            filters.extend(moreFilters)

        fields = ["code", "sg_asset_type", "sg_status_list"]
        if moreFields:
            fields.extend(moreFields)

        allSgAstList = sg.find("Asset", filters, fields,
                               [{'field_name':'sg_asset_type', 'direction':'asc'},
                                {'field_name':'code', 'direction':'asc'}])

        return allSgAstList

    def listAllSgShots(self, includeOmitted=False, moreFilters=None, moreFields=None):

        shotgundb = self._shotgundb
        sg = shotgundb.sg

        filters = [["project", "is", shotgundb.getProjectInfo()],
                   ["sg_sequence", "is_not", None],
                   ]

        if not includeOmitted:
            filters += [["sg_status_list", "is_not", "omt"],
                        ["sg_sequence.Sequence.sg_status_list", "is_not", "omt"]
                        ]

        if moreFilters:
            filters.extend(moreFilters)

        fields = ["code", "sg_sequence", "sg_status_list",
                  "sg_cut_in", "sg_cut_out", "sg_cut_duration"]
        if moreFields:
            fields.extend(moreFields)

        allSgShotList = sg.find("Shot", filters, fields,
                               [{'field_name':'sg_sequence', 'direction':'asc'},
                                {'field_name':'code', 'direction':'asc'}])

        return allSgShotList

    def iterShotDirsNotInSg(self):

        allSgShots = self.listAllSgShots(includeOmitted=True)
        sSgShotCodeList = tuple(sh["code"] for sh in allSgShots)
        shotLib = self.getLibrary("public", "shot_lib")

        for seqDir in shotLib.iterChildren():
            for shotDir in seqDir.iterChildren():
                if shotDir.name not in sSgShotCodeList:
                    yield shotDir

    def iterAssetPrefixes(self):
        for sSection in self.getVar("asset_lib", "asset_types"):
            sAstPrefix = self.getVar(sSection, "prefix", "")
            if sAstPrefix:
                yield sAstPrefix

    def listUiClasses(self):
        return DrcLibrary.listUiClasses()

    def setItemModel(self, model):
        self._itemmodel = model

    def iterChildren(self):
        return self.loadedLibraries.itervalues()

    def iterRcPaths(self, sSpace, sSection, tokens=None, **kwargs):

        allTreeVars = self.getVar(sSection, "all_tree_vars", ())
        #print sSection, len(allTreeVars), len(set(allTreeVars)), set(allTreeVars)
        for sTreeVar in allTreeVars:
            p = self.getPath(sSpace, sSection, pathVar=sTreeVar,
                             tokens=tokens, **kwargs)
            if not p:
                continue

            yield (sTreeVar, p)

    def _checkLibraryPaths(self, noError=False):

        sMissingPathList = []

        sSamePathDct = {}

        for sSpace, sLibSection in self._iterLibrariesSpaceAndSection():

            sLibFullName = DrcLibrary.makeFullName(sSpace, sLibSection)
            sLibPath = self.getPath(sSpace, sLibSection)

            sSamePathDct.setdefault(normCase(sLibPath), []).append(sLibFullName)

            if not osp.isdir(sLibPath):

                if sSpace == "public":
                    msg = u"No such '{}': '{}'.".format(sLibFullName, sLibPath)
                    if noError:
                        logMsg(msg, warning=True)
                    else:
                        raise EnvironmentError(msg)
                elif sSpace == "private":
                    sMissingPathList.append((sLibFullName, sLibPath))

        sSamePathList = tuple((p, libs) for p, libs in sSamePathDct.iteritems() if len(libs) > 1)
        if sSamePathList:
            msgIter = (u"'{}': {}".format(p, libs) for p, libs in sSamePathList)
            msg = u"Libraries using the same path:\n\n    " + u"\n    ".join(msgIter)
            raise EnvironmentError(msg)

        if sMissingPathList:

            msgIter = (u"'{}': '{}'".format(n, p) for n, p in sMissingPathList)
            msg = u"No such libraries:\n" + u"\n".join(msgIter)

            sConfirm = confirmDialog(title='WARNING !',
                                     message=msg + u"\n\nShould I create them ?",
                                     button=['Yes', 'No'],
                                     defaultButton='No',
                                     cancelButton='No',
                                     dismissString='No',
                                     icon="warning")

            if sConfirm == 'No':
                return False

            for _, p in sMissingPathList:
                os.makedirs(p)

        return True

    def getTemplatePath(self, sSection, pathVar="" , **kwargs):

        sTemplateDir = self.getPath("template", sSection, "template_dir", default="", **kwargs)
        if not sTemplateDir:
            return ""

        sEntityDir = self.getPath("template", sSection, "entity_dir", **kwargs)

        p = self.getPath("template", sSection, pathVar=pathVar, **kwargs)
        p = pathRedir(p, sEntityDir, sTemplateDir)
        return p

    def _checkTemplatePaths(self, out_invalidPaths, sSection="project"):

        sTemplateDir = self.getTemplateDir(sSection)
        if sTemplateDir:

            sEntityDir = self.getPath("template", sSection, "entity_dir")

            for _, sRcPath in self.iterRcPaths("template", sSection):

                if not pathStartsWith(sRcPath, sEntityDir):
                    continue

                sRcPath = pathRedir(sRcPath, sEntityDir, sTemplateDir)

                if osp.exists(sRcPath):
                    continue

                out_invalidPaths.append(sRcPath)

        for sChildSection in self.getVar(sSection, "child_sections", ()):
            self._checkTemplatePaths(out_invalidPaths, sChildSection)

    def getTemplateDir(self, sSection):
        return self.getPath("template", sSection, "template_dir", default="")

    def _assertSpaceAndSection(self, sSpace, sLibSection):

        if sSpace not in LIBRARY_SPACES:
            raise ValueError("No such space: '{}'. Expected: {}"
                            .format(sSpace, LIBRARY_SPACES))

        if sLibSection not in self.__confLibraries:
            msg = ("No such library: '{}'. \n\n\tKnown libraries: {}"
                   .format(sLibSection, self.__confLibraries))
            raise ValueError(msg)

    def assertMayaVersion(self, iCurMayaVersion):

        iProjMayaVersion = self.getVar("project", "maya_version")
        iCurMayaVersion /= 100

        if iCurMayaVersion != iProjMayaVersion:
            sMsg = ("{} requires Maya {}, but you're running Maya {} !"
                    .format(self, iProjMayaVersion, iCurMayaVersion))
            raise EnvironmentError(sMsg)

    def _iterLibrariesSpaceAndSection(self, space=LIBRARY_SPACES, fullName=False):

        sSpaceList = argToTuple(space)

        for sLibSection in self.__confLibraries:
            for sSpace in sSpaceList:
                if fullName:
                    yield DrcLibrary.makeFullName(sSpace, sLibSection)
                else:
                    yield (sSpace, sLibSection)

    def _inSebMode(self):

        if not self._loggedUser:
            return False

        sUsrLogin = self.__loggedUser.loginName
        return (inDevMode() and sUsrLogin == "sebastienc")

    def __initShotgun(self):

#        sFullName = self.getVar("project", "shotgun_class", "")
#        if not sFullName:
#            return

        #sgCls = importClass(sFullName, globals(), locals())
        from zomblib.shotgunengine import ShotgunEngine

        print "connecting to shotgun..."

        self._shotgundb = ShotgunEngine(self.name)

    def __initDamas(self):

        sDamasServerAddr = self.getVar("project", "damas_server_addr", "")

        from .dbtypes import DummyDbCon
        dummydb = DummyDbCon(sDamasServerAddr)

        if not sDamasServerAddr:
            self._damasdb = dummydb
            return

        if inDevMode():
            print "connecting to damas server:", sDamasServerAddr
        else:
            print "connecting to damas..."

        import damas
        damasdb = damas.http_connection(sDamasServerAddr)

        try:
            damasdb.verify()
        except IOError as e:
            logMsg(toStr(e), warning=True)
            self._damasdb = dummydb
        else:
            self._damasdb = damasdb

    def __repr__(self):

        cls = self.__class__

        try:
            sRepr = ("{0}('{1}')".format(cls.__name__, self.name))
        except AttributeError:
            sRepr = cls.__name__

        return sRepr