def computeAssignments(self):

        LOGGER = logging.getLogger('main')

        from .model.node import NoRenderNodeAvailable, NoLicenseAvailableForTask
        # if no rendernodes available, return
        if not any(rn.isAvailable() for rn in self.dispatchTree.renderNodes.values()):
            return []

        # first create a set of entrypoints that are not done nor cancelled nor blocked nor paused and that have at least one command ready
        # FIXME: hack to avoid getting the 'graphs' poolShare node in entryPoints, need to avoid it more nicely...
        entryPoints = set([poolShare.node for poolShare in self.dispatchTree.poolShares.values()
                                if poolShare.node.status not in (NODE_BLOCKED, NODE_DONE, NODE_CANCELED, NODE_PAUSED) and poolShare.node.readyCommandCount > 0 and poolShare.node.name != 'graphs'])

        # don't proceed to the calculation if no render nodes available in the requested pools
        isRenderNodesAvailable = False
        for pool, jobsIterator in groupby(entryPoints, lambda x: x.mainPoolShare().pool):
            renderNodesAvailable = set([rn for rn in pool.renderNodes if rn.status not in [RN_UNKNOWN, RN_PAUSED, RN_WORKING]])
            if len(renderNodesAvailable):
                isRenderNodesAvailable = True
                break
        if not isRenderNodesAvailable:
            return []

        # Log time updating max rn
        prevTimer = time.time()

        # sort by pool for the groupby
        entryPoints = sorted(entryPoints, key=lambda node: node.mainPoolShare().pool)

        # update the value of the maxrn for the poolshares (parallel dispatching)
        for pool, jobsIterator in groupby(entryPoints, lambda x: x.mainPoolShare().pool):

            # we are treating every active job of the pool
            jobsList = [job for job in jobsIterator]

            # the new maxRN value is calculated based on the number of active jobs of the pool, and the number of online rendernodes of the pool
            onlineRenderNodes = set([rn for rn in pool.renderNodes if rn.status not in [RN_UNKNOWN, RN_PAUSED]])
            nbOnlineRenderNodes = len(onlineRenderNodes)
            # LOGGER.debug("@   - nb rns awake:%r" % (nbOnlineRenderNodes) )

            # if we have a userdefined maxRN for some nodes, remove them from the list and substracts their maxRN from the pool's size
            l = jobsList[:]  # duplicate the list to be safe when removing elements
            for job in l:
                # LOGGER.debug("@   - checking userDefMaxRN: %s -> %r maxRN=%d" % (job.name, job.mainPoolShare().userDefinedMaxRN, job.mainPoolShare().maxRN ) )
                if job.mainPoolShare().userDefinedMaxRN and job.mainPoolShare().maxRN not in [-1, 0]:
                    # LOGGER.debug("@     removing: %s -> maxRN=%d" % (job.name, job.mainPoolShare().maxRN ) )
                    jobsList.remove(job)
                    nbOnlineRenderNodes -= job.mainPoolShare().maxRN

            # LOGGER.debug("@   - nb rns awake after maxRN:%d" % (nbOnlineRenderNodes) )
            if len(jobsList) == 0:
                continue

            # Prepare updatedMaxRN with dispatch key proportions
            # list of dks (integer only)
            dkList = [job.dispatchKey for job in jobsList]
            nbJobs = len(jobsList)     # number of jobs in the current pool
            nbRNAssigned = 0            # number of render nodes assigned for this pool

            dkMin = min(dkList)
            # dkPositiveList: Shift all dks values in order that each min value of dk becomes 1
            dkPositiveList = map(lambda x: x-dkMin+1, dkList)  # dk values start at 1
            dkSum = sum(dkPositiveList)

            # sort by id (fifo)
            jobsList = sorted(jobsList, key=lambda x: x.id)

            # then sort by dispatchKey (priority)
            jobsList = sorted(jobsList, key=lambda x: x.dispatchKey, reverse=True)

            for dk, jobIterator in groupby(jobsList, lambda x: x.dispatchKey):

                jobs = [job for job in jobIterator]
                # dkPositive: Shift all dks values in order that each min value of dk becomes 1
                dkPositive = dk - dkMin + 1

                # Proportion of render nodes for
                updatedmaxRN = int(round(nbOnlineRenderNodes * (dkPositive / float(dkSum))))

                for job in jobs:
                    job.mainPoolShare().maxRN = updatedmaxRN
                    nbRNAssigned += updatedmaxRN

            # PRA: Here is the main choice!
            # Add remaining RNs to most important jobs (to fix rounding errors)
            unassignedRN = nbOnlineRenderNodes - nbRNAssigned
            while unassignedRN > 0:
                for job in jobsList:
                    if unassignedRN <= 0:
                        break
                    job.mainPoolShare().maxRN += 1
                    unassignedRN -= 1

        if singletonconfig.get('CORE','GET_STATS'):
            singletonstats.theStats.assignmentTimers['update_max_rn'] = time.time() - prevTimer
        LOGGER.info( "%8.2f ms --> .... updating max RN values", (time.time() - prevTimer)*1000 )

        # now, we are treating every nodes
        # sort by id (fifo)
        entryPoints = sorted(entryPoints, key=lambda node: node.id)
        # then sort by dispatchKey (priority)
        entryPoints = sorted(entryPoints, key=lambda node: node.dispatchKey, reverse=True)

        # Put nodes with a userDefinedMaxRN first
        userDefEntryPoints = ifilter(lambda node: node.mainPoolShare().userDefinedMaxRN, entryPoints)
        standardEntryPoints = ifilter(lambda node: not node.mainPoolShare().userDefinedMaxRN, entryPoints)
        scoredEntryPoints = chain(userDefEntryPoints, standardEntryPoints)

        # Log time dispatching RNs
        prevTimer = time.time()

        # Iterate over each entryPoint to get an assignment
        assignments = []  # list of (renderNode, Command)
        for entryPoint in scoredEntryPoints:
            # If we have dedicated render nodes for this poolShare
            if not any([poolShare.hasRenderNodesAvailable() for poolShare in entryPoint.poolShares.values()]):
                continue
	    
	    try:
	        for (rn, com) in entryPoint.dispatchIterator(lambda: self.queue.qsize() > 0):
        	    assignments.append((rn, com))
        	    # increment the allocatedRN for the poolshare
        	    entryPoint.mainPoolShare().allocatedRN += 1
        	    # save the active poolshare of the rendernode
        	    rn.currentpoolshare = entryPoint.mainPoolShare()
	    except NoRenderNodeAvailable:
                 pass
 	    except NoLicenseAvailableForTask:
                 LOGGER.info("Missing license for node \"%s\" (other commands can start anyway)." % entryPoint.name)
		 pass

        assignmentDict = collections.defaultdict(list)
        for (rn, com) in assignments:
            assignmentDict[rn].append(com)

        if singletonconfig.get('CORE','GET_STATS'):
            singletonstats.theStats.assignmentTimers['dispatch_command'] = time.time() - prevTimer
        LOGGER.info( "%8.2f ms --> .... dispatching commands", (time.time() - prevTimer)*1000  )

        #
        # Check replacements
        #
        # - faire une passe pour les jobs n'ayant pas leur part de gateau
        #     - identifier dans leur pool les jobs killable
        #     - pour chaque ressource, si match : on jette le job en cours ET on desactive son attribut killable


        #
        # Backfill
        #
        # TODO refaire une passe pour les jobs ayant un attribut "killable" et au moins une pool additionnelle

        return assignmentDict.items()
Computes and returns a list of (rendernode, command) assignments.
dataset/ETHPy150Open mikrosimage/OpenRenderManagement/src/octopus/dispatcher/dispatcher.py/Dispatcher.computeAssignments