<commit_before>from datetime import datetime
import logging

from sqlalchemy import or_

import adhocracy.model as model
from adhocracy.model import Delegation

log = logging.getLogger(__name__)

# REFACT: Rename: DelegationTraverser? Maybe thats also a new object that does the traversing in different ways
# That may become a Strategy object on how the delegation should be traversed
# REFACT: Extract: DelegationUserGraph, DelegationTopicGraph, perhaps DelegationGraph as common supercluss
# This object should represent a whole delegation graph from different points of view
# One Goal would be to be able to load the whole subgraph from the db in as few queries as possible
# Optimally just one...
# Maybe there will just be one DelegationGraph with different Strategies of how create/ traverse/ filter it attached
# Also it needs to be possible to ask the graph for the status at a specific time (all objects have a deleted_at property or it can be determined when they are overridden by a later choice)

# Some problems with this class:
# - many of the methods work across multiple nodes and layers of the graph
# - many of the methods take additional parameters to determine when to filter this graph for different criteria
# - there are methods that work on the whole graph (some as class-methods)
# - Forward and backward traversal are in the same object
# - it is oblivious as to why the graph is traversed, either to allow all agents to place their vote
#   or to find out what the delegation wheight of a specific user is in a specific context
class DelegationNode(object):
    """
    A ``DelegationNode`` describes a part of the voting delegation graph
    sorrounding a ``Delegateable`` (i.e. a ``Category``, ``Issue`` or 
    ``Proposal``) and a ``User``. 
    
    Right now the delegation graph is a graph of incomming and outgoing 
    delegations on multiple levels - one level per scope.
    
    Each DelegationNode represents the incomming and outgoing delegations
    of one user on one level (scope/delegateable) in this graph.
    
    **TODO:** Developing a good caching strategy for this class would be 
    useful in order to cache the delegation graph to memcached.
    
    :param user: The ``User`` at the center of this ``DelegationNode``.
    :param delegateable: A ``Delegateable``.   
    """
    
    def __init__(self, user, delegateable):
        self.user = user
        self.delegateable = delegateable
    
    def _query_traverse(self, querymod, recurse, at_time=None):
        if not at_time: # shouldn't this be if at_time is None: ?
            at_time = datetime.utcnow()
        
        query = model.meta.Session.query(Delegation)
        query = query.filter(Delegation.scope==self.delegateable)
        query = query.filter(Delegation.create_time<=at_time)
        query = query.filter(or_(Delegation.revoke_time == None,
                                 Delegation.revoke_time > at_time))
        query = querymod(query)
        delegations = query.all()
        if recurse:
            for parent in self.delegateable.parents:
                node = DelegationNode(self.user, parent)
                delegations += node._query_traverse(querymod, recurse, at_time)
        return delegations
    
    def inbound(self, recurse=True, at_time=None, should_filter=True):
        """
        Retrieve all inbound delegations (i.e. those that the user has received
        from other users in order to vote on their behalf) that apply to the 
        ``Delegateable``. 
        
        :param recurse: if ``True``, search will include delegations on parent 
            ``Delegateables`` in breadth-first traversal order.
        :param at_time: return the delegation graph at the given time, defaults
            to the current time. 
        :param should_filter: if ``True`` will filter out all overriden delegations
        :returns: list of ``Delegation``
        """
        delegations = self._query_traverse(lambda q: q.filter(Delegation.agent==self.user),
                                           recurse, at_time)
        print 'inbound no filter', delegations, 'should_filter', should_filter
        # filter out delegations that are overriden
        if should_filter:
            by_principal = dict()
            for delegation in set(delegations):
                by_principal[delegation.principal] = by_principal.get(delegation.principal, []) + [delegation]
            delegations = [self.filter_delegations(ds)[0] for ds in by_principal.values()]
        print 'inbound after should_filter', delegations
        delegations = self._filter_out_overridden_delegations(delegations)
        print 'inbound after _filter_out_overridden_delegations', delegations
        delegations = self._filter_out_overrides_by_direct_vote(delegations)
        print 'inbound after _filter_out_overrides_by_direct_vote', delegations
        return delegations
    <commit_msg>[svn r552] removed accidentally commited print statements<commit_after>from datetime import datetime
import logging

from sqlalchemy import or_

import adhocracy.model as model
from adhocracy.model import Delegation

log = logging.getLogger(__name__)

# REFACT: Rename: DelegationTraverser? Maybe thats also a new object that does the traversing in different ways
# That may become a Strategy object on how the delegation should be traversed
# REFACT: Extract: DelegationUserGraph, DelegationTopicGraph, perhaps DelegationGraph as common supercluss
# This object should represent a whole delegation graph from different points of view
# One Goal would be to be able to load the whole subgraph from the db in as few queries as possible
# Optimally just one...
# Maybe there will just be one DelegationGraph with different Strategies of how create/ traverse/ filter it attached
# Also it needs to be possible to ask the graph for the status at a specific time (all objects have a deleted_at property or it can be determined when they are overridden by a later choice)

# Some problems with this class:
# - many of the methods work across multiple nodes and layers of the graph
# - many of the methods take additional parameters to determine when to filter this graph for different criteria
# - there are methods that work on the whole graph (some as class-methods)
# - Forward and backward traversal are in the same object
# - it is oblivious as to why the graph is traversed, either to allow all agents to place their vote
#   or to find out what the delegation wheight of a specific user is in a specific context
class DelegationNode(object):
    """
    A ``DelegationNode`` describes a part of the voting delegation graph
    sorrounding a ``Delegateable`` (i.e. a ``Category``, ``Issue`` or 
    ``Proposal``) and a ``User``. 
    
    Right now the delegation graph is a graph of incomming and outgoing 
    delegations on multiple levels - one level per scope.
    
    Each DelegationNode represents the incomming and outgoing delegations
    of one user on one level (scope/delegateable) in this graph.
    
    **TODO:** Developing a good caching strategy for this class would be 
    useful in order to cache the delegation graph to memcached.
    
    :param user: The ``User`` at the center of this ``DelegationNode``.
    :param delegateable: A ``Delegateable``.   
    """
    
    def __init__(self, user, delegateable):
        self.user = user
        self.delegateable = delegateable
    
    def _query_traverse(self, querymod, recurse, at_time=None):
        if not at_time: # shouldn't this be if at_time is None: ?
            at_time = datetime.utcnow()
        
        query = model.meta.Session.query(Delegation)
        query = query.filter(Delegation.scope==self.delegateable)
        query = query.filter(Delegation.create_time<=at_time)
        query = query.filter(or_(Delegation.revoke_time == None,
                                 Delegation.revoke_time > at_time))
        query = querymod(query)
        delegations = query.all()
        if recurse:
            for parent in self.delegateable.parents:
                node = DelegationNode(self.user, parent)
                delegations += node._query_traverse(querymod, recurse, at_time)
        return delegations
    
    def inbound(self, recurse=True, at_time=None, should_filter=True):
        """
        Retrieve all inbound delegations (i.e. those that the user has received
        from other users in order to vote on their behalf) that apply to the 
        ``Delegateable``. 
        
        :param recurse: if ``True``, search will include delegations on parent 
            ``Delegateables`` in breadth-first traversal order.
        :param at_time: return the delegation graph at the given time, defaults
            to the current time. 
        :param should_filter: if ``True`` will filter out all overriden delegations
        :returns: list of ``Delegation``
        """
        delegations = self._query_traverse(lambda q: q.filter(Delegation.agent==self.user),
                                           recurse, at_time)
        # filter out delegations that are overriden
        if should_filter:
            by_principal = dict()
            for delegation in set(delegations):
                by_principal[delegation.principal] = by_principal.get(delegation.principal, []) + [delegation]
            delegations = [self.filter_delegations(ds)[0] for ds in by_principal.values()]
        delegations = self._filter_out_overridden_delegations(delegations)
        delegations = self._filter_out_overrides_by_direct_vote(delegations)
        return delegations
    
{'fraction_non_alphanumeric': 0.06559766763848396, 'fraction_numerical': 0.0005206164098292378, 'mean_word_length': 3.010438413361169, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5313426', 'n_tokens_mistral': 2557, 'n_tokens_neox': 2523, 'n_words': 1032}