B
    ADb                 @   s   d Z ddlmZ ddlmZ ddlZddlZddlmZ ddlm	Z	m
Z
 yddlmZ W n  ek
rx   dd	lmZ Y nX dd
lT ddlmZ dZdd ZeG dd de	ZdS )zDtreelib - Simple to use for you.

   Python 2/3 Tree Implementation
    )print_function)unicode_literalsN)deepcopy   )AbsTreesafe_get_keyword)StringIO)BytesIO)*)NodeZboycec             C   s<   t jd dkr8d| jkr&td| j | j| _dd | _| S )at  
    (slightly modified from :
        http://django.readthedocs.org/en/latest/_modules/django/utils/encoding.html)

    A decorator that defines __unicode__ and __str__ methods under Python 2.
    Under Python 3 it does nothing.

    To support Python 2 and 3 with a single code base, define a __str__ method
    returning text and apply this decorator to the class.
    r      __str__zY@python_2_unicode_compatible cannot be applied to %s because it doesn't define __str__().c             S   s   |   dS )Nzutf-8)__unicode__encode)self r   9G:\easytimepro\master/mysite/admin\tree_structure\tree.py<lambda>@       z-python_2_unicode_compatible.<locals>.<lambda>)sysversion_info__dict__
ValueError__name__r   r   )klassr   r   r   python_2_unicode_compatible/   s    


r   c            	   @   s  e Zd ZdZeed\ZZZZ	dd Z
daddZd	d
 Zdd Zdd Zdd Zdeddddddef	ddZdd Zdd Zdd Zdd Zdd Zdd  Zdbd!d"Zd#d$ Zd%d& Zd'd( Zd)d* Zdcd+d,Zddd-d.Zdedddfd/d0Zd1d2 Z d3d4 Z!d5d6 Z"ded7d8Z#dfd9d:Z$d;d< Z%d=d> Z&d?d@ Z'e(dAdB Z)dCdD Z*dgdEdFZ+dGdH Z,dIdJ Z-dKdL Z.dhdMdNZ/deddddddfdOdPZ0deddddddfdQdRZ1dSdT Z2didUdVZ3dWdX Z4djdYdZZ5dkd[d\Z6dld]d^Z7d_d` Z8dS )mTreez=Tree objects are made of Node(s) stored in _nodes dictionary.   c                s    fdd| j D S )zUReturn a list of the nodes'identitys matching the
        identity argument.
        c                s   g | ]}| kr|qS r   r   ).0node)identityr   r   
<listcomp>O   s    z%Tree.__contains__.<locals>.<listcomp>)_nodes)r   r    r   )r    r   __contains__K   s    zTree.__contains__NFc             C   sR   i | _ d| _|dk	rN|j| _|rFx,|j D ]}t|j | | j |< q(W n|j | _ dS )zVInitiate a new tree or copy another tree with a shallow or
        deep copy.
        N)r"   rootr   )r   Ztreedeepnidr   r   r   __init__R   s    zTree.__init__c             C   s0   y
| j | S  tk
r*   td| Y nX dS )zReturn _nodes[key]zNode '%s' is not in the treeN)r"   KeyErrorNodeIDAbsentError)r   keyr   r   r   __getitem__f   s    
zTree.__getitem__c             C   s
   t | jS )zReturn len(_nodes))lenr"   )r   r   r   r   __len__m   s    zTree.__len__c             C   s   | j ||i dS )zSet _nodes[key]N)r"   update)r   r*   itemr   r   r   __setitem__q   s    zTree.__setitem__c                s$   d _  fdd} j|d  j S )N c                s     j | dd 7  _ d S )Nzutf-8
)_readerdecode)line)r   r   r   writex   s    zTree.__str__.<locals>.write)func)r3   _Tree__print_backend)r   r6   r   )r   r   r   u   s    zTree.__str__Tzascii-exc
                s    r$|r fdd}
q: fdd}
n|r2dd }
ndd }
|dkrJdd }xN|  ||||||D ]6\}}|
|}|jrzd	nd
}|	d|||d q`W dS )aN  
        Another implementation of printing tree using Stack
        Print tree structure in hierarchy style.

        For example:
            Root
            |___ C01
            |    |___ C11
            |         |___ C111
            |         |___ C112
            |___ C02
            |___ C03
            |    |___ C31

        A more elegant way to achieve this function using Stack
        structure, for constructing the Nodes Stack push and pop nodes
        with additional level info.

        UPDATE: the @key @reverse is present to sort node at each
        level.
        c                s   t | j S )N)getattrdata)r   )data_propertyr   r   	get_label   s    z'Tree.__print_backend.<locals>.get_labelc                s   dt | j | jf S )Nz%s[%s])r9   r:   r    )r   )r;   r   r   r<      s    c             S   s   | j S )N)tag)r   r   r   r   r<      s    c             S   s   d| j | jf S )Nz%s[%s])r=   r    )r   r   r   r   r<      s    Nc             S   s   | S )Nr   )r   r   r   r   r*      s    z!Tree.__print_backend.<locals>.keyvxz{0}{1: <2}{2}zutf-8)
_Tree__getcheckedformatr   )r   r&   levelidhiddenfilterr*   reverse	line_typer;   r7   r<   Zprer   labelZmarkr   )r;   r   Z__print_backend~   s    
zTree.__print_backendc          	   C   s<   |d krdd }ddddddd	| }|  ||||||g S )
Nc             S   s   dS )NTr   )_noder   r   r   filter_   s    zTree.__get.<locals>.filter_)|z|-- z+-- )u   │u
   ├── u
   └── )u   │u
   ├── u
   ╰── )u   ║u
   ╠══ u
   ╚══ )u   ║u
   ╟── u
   ╙── )u   │u
   ╞══ u
   ╘══ )asciizascii-exz	ascii-exrzascii-emz	ascii-emvz	ascii-emh)_Tree__get_iter)r   r&   rC   rJ   r*   rF   rG   dtr   r   r   Z__get   s    z
Tree.__getc          
   #   sR  |\ }}	d}
|}|d kr j n|}|s:td| | }|jkrXd|fV  nBdtt fdd|dd }
|d r|	n|}|
| |fV  |rN|jrNfdd|jD }t	|d	 }|r|j
||d
 n|rt|}|d	7 }xVt|D ]J\}}|||k x(|j|||||D ]}|V  q0W |  q W d S )Nr1   zNode '%s' is not in the treec                s   | s d S dS )Nz   z    r   )r?   )dt_vliner   r   r      s    z!Tree.__get_iter.<locals>.<lambda>r   c                s    g | ]} | r| qS r   r   )r   i)rJ   r   r   r   r!      s    z#Tree.__get_iter.<locals>.<listcomp>r   )r*   rF   )r$   containsr)   ROOTjoinlistmapexpandedfpointerr,   sortreversed	enumerateappendrM   r    pop)r   r&   rC   rJ   r*   rF   rN   Zis_lastZdt_line_boxZdt_line_corZ_leadingZ_lastingr   childrenZidxlastidxchildr/   r   )rO   rJ   r   r   Z
__get_iter   s6    


zTree.__get_iterc             C   s   | |  | dS )zset self[nid].bpointerN)update_bpointer)r   r&   Z	parent_idr   r   r   Z__update_bpointer   s    zTree.__update_bpointerc             C   s    |d krd S | |  || d S )N)update_fpointer)r   r&   Zchild_idmoder   r   r   Z__update_fpointer   s    zTree.__update_fpointerc             C   s   dS )NTr   )r   Z_pr   r   r   Z__always_true   s    zTree.__always_truec             C   s   t |tr|j}n|}|S )N)
isinstancer   r    )r   r   r&   r   r   r   _node_2_nid   s    
zTree._node_2_nidc             C   s   t |tstd|j| jkr,td|j t |tr<|jn|}|dkrf| jdk	r\tdq||j| _n| |s|t	d| | j
|j|i | ||jtj | |j| dS )zk
        Add a new node to tree.
        The 'node' parameter refers to an instance of Class::Node
        z.First parameter must be object of Class::Node.zCan't create node with ID '%s'NzA tree takes one root merely.z#Parent node '%s' is not in the tree)rd   r   OSErrorr    r"   ZDuplicatedNodeIdErrorr$   ZMultipleRootErrorrR   r)   r.   _Tree__update_fpointerADD_Tree__update_bpointer)r   r   parentpidr   r   r   add_node   s     





zTree.add_nodec             C   s   t | j S )zReturn all nodes in a list)rU   r"   values)r   r   r   r   	all_nodes  s    zTree.all_nodesc             C   s
   | j  S )zS
        Returns all nodes in an iterator
        Added by William Rusnack
        )r"   rm   )r   r   r   r   all_nodes_itr  s    zTree.all_nodes_itrc                s"     |} fdd |D S )zn
        Return the children (Node) list of nid.
        Empty list is returned if nid does not exist
        c                s   g | ]} | qS r   r   )r   rQ   )r   r   r   r!   &  s    z!Tree.children.<locals>.<listcomp>)re   	is_branch)r   r   r&   r   )r   r   r^      s    
zTree.childrenc             C   s   |  |}|| jkrdS dS )z+Check if the tree contains node of given idTF)re   r"   )r   r   r&   r   r   r   rR   (  s    
zTree.containsc             C   s   t |||d}| || |S )z+Create a child node for given @parent node.)r=   r    r:   )r   rl   )r   r=   r    rj   r:   r   r   r   r   create_node-  s    zTree.create_nodec             C   sn   d}|dkr@|   }xT|D ] }| |j}||kr6|n|}qW n*| |}| |s`td| | |}|S )z
        Get the maximum level of this tree or the level of the given node

        @param node Node instance or identity
        @return int
        @throw NodeIDAbsentError
        r   NzNode '%s' is not in the tree)leavesrC   r    re   rR   r)   )r   r   Zretrr   ZleaverC   r&   r   r   r   depth3  s    



z
Tree.depthc             #   s  |dkrj n|}|s.td|  dkr<jn   | r|V   fdd| jD }|jjgkr|j||d xt|r|d j	V   fdd|d jD }|j||d |jkr||dd  }q|jkr|dd | }qW n|j
krg }	|  | }
}d	}xn|
r fd
d|
d jD }|
dj	V  |rr|  || }n||	 }	|
s,| }|r|	n|}
q,W dS )a  
        Python generator. Loosly based on an algorithm from
        'Essential LISP' by John R. Anderson, Albert T. Corbett, and
        Brian J. Reiser, page 239-241

        UPDATE: the @filter function is performed on Node object during
        traversing. In this manner, the traversing will not continue to
        following children of node whose condition does not pass the filter.

        UPDATE: the @key and @reverse are present to sort nodes at each
        level.
        NzNode '%s' is not in the treec                s    g | ]} | r| qS r   r   )r   rQ   )rE   r   r   r   r!   _  s    z$Tree.expand_tree.<locals>.<listcomp>)r*   rF   r   c                s    g | ]} | r| qS r   r   )r   rQ   )rE   r   r   r   r!   d  s    r   Fc                s    g | ]} | r| qS r   r   )r   rQ   )rE   r   r   r   r!     s    )r$   re   rR   r)   _Tree__always_truerX   DEPTHWIDTHrY   r    ZIGZAGrF   r]   )r   r   rc   rE   r*   rF   r&   ZqueueZ	expansionZstack_fwstackZstack_bw	directionr   )rE   r   r   expand_treeJ  s@    



zTree.expand_treec             C   s   t ||  S )a  
        Filters all nodes by function
        function is passed one node as an argument and that node is included if function returns true
        returns a filter iterator of the node in python 3 or a list of the nodes in python 2
        Added William Rusnack
        )rE   ro   )r   r7   r   r   r   filter_nodes  s    zTree.filter_nodesc             C   s   ddl m} |dks| |s|t}y0x*| j| jdD ]}|| | j | q8W W n tk
rj   dS X ||krxdS || d }| j	| S )zPReturn the node with `nid_or_tag`. None returned if `nid_or_tag` does not exist.r   )defaultdictN)rc   )
collectionsr|   rR   rU   rz   rv   r=   r\   r)   r"   )r   Z
nid_or_tagr|   Zall_tagidr   r   r   get_node  s    zTree.get_nodec             C   s\   |  |}|dkrtd| |s0td| y| | j}W n tk
rV   g }Y nX |S )zl
        Return the children (ID) list of nid.
        Empty list is returned if nid does not exist
        NzFirst parameter can't be NonezNode '%s' is not in the tree)re   rf   rR   r)   rX   r(   )r   r   r&   rX   r   r   r   rp     s    


zTree.is_branchc             C   sp   g }|dkr4x^| j  D ]}| r|| qW n8| |}x,| |D ]}| |  rJ|| |  qJW |S )z*Get leaves of the whole tree of a subtree.N)r"   rm   is_leafr\   re   rz   )r   r   rr   rI   r&   r   r   r   rr     s    
zTree.leavesc             C   s(   |  |}tdd | ||D d S )a  
        Get the node level in this tree.
        The level is an integer starting with '0' at the root.
        In other words, the root lives at level '0';

        Update: @filter params is added to calculate level passing
        exclusive nodes.
        c             S   s   g | ]}|qS r   r   )r   nr   r   r   r!     s    zTree.level.<locals>.<listcomp>r   )re   r,   rsearch)r   r   rE   r&   r   r   r   rC     s    	
z
Tree.levelc             C   s   |  |}| |s td| | j|kr2td| | | j }x"| | jD ]}| | |j qLW | j| | j7  _|j	||j
d | j|= dS )z
        Delete a node by linking past it.

        For example, if we have a -> b -> c and delete node b, we are left
        with a -> c
        zNode '%s' is not in the treez<Cannot link past the root node, delete it with remove_node())rc   N)re   rR   r)   r$   ZLinkPastRootNodeErrorbpointerrX   ra   r    rb   DELETEr"   )r   r   r&   rj   r`   r   r   r   link_past_node  s    


zTree.link_past_nodec             C   sd   |  |r|  |stn| ||r*t| | j}| ||tj | ||tj | 	|| dS )zc
        Move a node indicated by @source parameter to be a child of
        @destination.
        N)
rR   r)   is_ancestorZ	LoopErrorr   rg   r   r   rh   ri   )r   sourceZdestinationrj   r   r   r   	move_node  s    
zTree.move_nodec             C   s@   | | j }|}x,|d k	r:||kr$dS | | j }| | j }qW dS )NTF)r   )r   ZancestorZ
grandchildrj   r`   r   r   r   r     s    


zTree.is_ancestorc             C   s   | j S )z:Return a dict form of nodes in a tree: {id: node_instance})r"   )r   r   r   r   nodes  s    z
Tree.nodesc             C   sH   |  |}| |s td| | | j}|dks<| |s@dS | | S )z"Get parent node object of given idzNode '%s' is not in the treeN)re   rR   r)   r   )r   r   r&   rk   r   r   r   rj   
  s    


zTree.parentc             C   s   t |tst| |}|dkr(td| |s>td| t|jt| j@ }|rft	dt
| |rx6|jD ]}| j|t|| i qrW n| j|j | ||jtj | |j| dS )z
        Paste a @new_tree to the original one by linking the root
        of new tree to given node (nid).

        Update: add @deep copy of pasted tree.
        NzFirst parameter can't be NonezNode '%s' is not in the treezDuplicated nodes %s exists.)rd   r   AssertionErrorre   rf   rR   r)   setr"   r   rU   r.   r   rg   r$   r   rh   ri   )r   r   Znew_treer%   r&   Z	set_jointr   r   r   paste  s    

z
Tree.pastec             C   s@   g }x6|   D ]*}|dd | |jD ddd  qW |S )ag  
        Use this function to get the identitys allowing to go from the root
        nodes to each leaf.
        Return a list of list of identitys, root being not omitted.

        For example :
            Harry
            |___ Bill
            |___ Jane
            |    |___ Diane
            |         |___ George
            |              |___ Jill
            |         |___ Mary
            |    |___ Mark

        expected result :
        [['harry', 'jane', 'diane', 'mary'],
         ['harry', 'jane', 'mark'],
         ['harry', 'jane', 'diane', 'george', 'jill'],
         ['harry', 'bill']]
        c             S   s   g | ]}|qS r   r   )r   r&   r   r   r   r!   K  s    z(Tree.paths_to_leaves.<locals>.<listcomp>NrP   )rr   r\   r   r    )r   resZleafr   r   r   paths_to_leaves2  s    *zTree.paths_to_leavesc             C   s   g }|dkrdS |  |s&td| | | j}x| |D ]}|| q<W t|}x|D ]}| j|= q\W | ||tj	 |S )z
        Remove a node indicated by 'identity'; all the successors are
        removed as well.

        Return the number of removed nodes.
        Nr   zNode '%s' is not in the tree)
rR   r)   r   rz   r\   r,   r"   rg   r   r   )r   r    removedrj   r~   Zcntr   r   r   remove_nodeO  s    


zTree.remove_nodec             C   s   t  }| |}|dkr|S | |s2td| ||_| | j}d| | _g }x| |D ]}|| q\W x$|D ]}|j	|| j
|i qtW | ||tj |S )a  
        Return a subtree deleted from this tree. If nid is None, an
        empty tree is returned.
        For the original tree, this method is similar to
        `remove_node(self,nid)`, because given node and its children
        are removed from the original tree in both methods.
        For the returned value and performance, these two methods are
        different:

            `remove_node` returns the number of deleted nodes;
            `remove_subtree` returns a subtree of deleted nodes;

        You are always suggested to use `remove_node` if your only to
        delete nodes from a tree, as the other one need memory
        allocation to store the new tree.
        NzNode '%s' is not in the tree)r   re   rR   r)   r$   r   rz   r\   r"   r.   r]   rg   r   r   )r   r   str&   rj   r   r~   r   r   r   remove_subtreel  s     




zTree.remove_subtreec             c   s~   |dkrdS |  |}| |s,td| |dkr:| jn|}|}x6|dk	rx|| | r^|V  | j|krr| | jnd}qDW dS )zk
        Traverse the tree branch along the branch from nid to its
        ancestors (until root).
        NzNode '%s' is not in the tree)re   rR   r)   rt   r$   r   )r   r   rE   r&   currentr   r   r   r     s    


zTree.rsearchc
                s@   dd   fdd}
|  |}| j||||||||	|
d	 dS )z9Update 20/05/13: Save tree into file for offline analysisc             S   s   | | d  d S )N   
)r6   )r5   fr   r   r   _write_line  s    z#Tree.save2file.<locals>._write_linec                s    | t dS )Nab)open)r?   )r   filenamer   r   r     r   z Tree.save2file.<locals>.<lambda>)r7   N)re   r8   )r   r   r   rC   rD   rE   r*   rF   rG   r;   Zhandlerr&   r   )r   r   r   	save2file  s
    
zTree.save2filec	                s^   d _  |}	 fdd}
y  j|	||||||||
d	 W n tk
rX   td Y nX d S )Nr1   c                s     j | dd 7  _ d S )Nzutf-8r2   )r3   r4   )r5   )r   r   r   r6     s    zTree.show.<locals>.write)r7   zTree is empty)r3   re   r8   r)   print)r   r   rC   rD   rE   r*   rF   rG   r;   r&   r6   r   )r   r   show  s    
z	Tree.showc                s@   g } |  jkr<  j} fdd| jD }|S )z
        Return the siblings of given @nid.

        If @nid is root or there are no siblings, an empty list is returned.
        c                s   g | ]}| kr| qS r   r   )r   rQ   )r&   r   r   r   r!     s    z!Tree.siblings.<locals>.<listcomp>)re   r$   r   rX   )r   r   siblingsrk   r   )r&   r   r   r     s    


zTree.siblingsc                sX    dkrt jS y$t  t  fdd D S    tdt  Y nX dS )aH  
        Get the number of nodes of the whole tree if @level is not
        given. Otherwise, the total number of nodes at specific level
        is returned.

        @param level The level number in the tree. It must be between
        [0, tree.depth].

        Otherwise, InvalidLevelNumber exception will be raised.
        Nc                s    g | ]} |j kr|qS r   )rC   r    )r   r   )rC   r   r   r   r!     s    zTree.size.<locals>.<listcomp>z*level should be an integer instead of '%s')r,   r"   intro   	TypeErrortype)r   rC   r   )rC   r   r   size  s    
z	Tree.sizec             C   sj   t  }|dkr|S | |}| |s2td| ||_x,| |D ]}|j| | j| | i qDW |S )ad  
        Return a shallow COPY of subtree with nid being the new root.
        If nid is None, return an empty tree.
        If you are looking for a deepcopy, please create a new tree
        with this shallow copy,

        e.g.
            new_tree = Tree(t.subtree(t.root), deep=True)

        This line creates a deep copy of the entire tree.
        NzNode '%s' is not in the tree)	r   re   rR   r)   r$   rz   r"   r.   r    )r   r   r   r&   Znode_nr   r   r   subtree  s    

zTree.subtreec          	      s  t d  td|}t ddtd|}t ddgfdtd|\}}	i }
|dkr\ jn |}x j j|dD ]~} | }|j}|j	} | || krqz|
 }||j| |jd	d	d
|
|< |r|f|
| d< |jr~|	r~ fdd | jD }|dkrdd n|}|r4|j||d xH|D ]@}|
|jd}|dkrZq:d|d< |d dkr:d	|d< q:W |jrz|rz |}|rz|jszx\ |}y|
| }W n tk
r   P Y nX |d s|d sd|d<  |}qW qzW |s|
| |
 S )z!transform self into a simple datarC   )defaultsZexpect_typehas_rootTparent_cascadechildren_cascadeN)rc   r   F)r~   nameZisParentrA   r   	halfCheckrk   c                s   g | ]} | qS r   r   )r   rQ   )r   r   r   r!   #  s    z'Tree.to_simple_data.<locals>.<listcomp>c             S   s   | S )Nr   )r?   r   r   r   r   $  r   z%Tree.to_simple_data.<locals>.<lambda>)r*   rF   rA   r   )rC   )r   )r   rs   r   boolr$   re   rz   rv   r    r   r   r=   rA   rX   rY   getZ	_identityrj   r(   r]   rm   )r   r   r*   rY   rF   kwargsZlevel_limitr   r   r   Ztree_node_datar&   Z_nidZ_idZ_pidr   children_queuecZ_c_nodeZ_parentZ_parent_nidrI   r   )r   r   to_simple_data  s`    






zTree.to_simple_datac          	      s   |dkr j n |} | j}|dg ii}|rD | j|| d<  | jr fdd | jD }	|dkrvdd n|}|r|	j||d x.|	D ]&}
|| d  j|
j	|||d	 qW t
|| d d
kr|sވ | jn|d | jii}|S dS )ztransform self into a dictNr^   r:   c                s   g | ]} | qS r   r   )r   rQ   )r   r   r   r!   M  s    z Tree.to_dict.<locals>.<listcomp>c             S   s   | S )Nr   )r?   r   r   r   r   N  r   zTree.to_dict.<locals>.<lambda>)r*   rF   )	with_datarY   rF   r   )r$   re   r=   r:   rW   rX   rY   r\   to_dictr    r,   )r   r   r*   rY   rF   r   r&   ZntagZ	tree_dictr   elemr   )r   r   r   B  s"    


zTree.to_dictc             C   s   t | j|||dS )z,Return the json string corresponding to self)r   rY   rF   )jsondumpsr   )r   r   rY   rF   r   r   r   to_jsonZ  s    zTree.to_jsonc             K   s   |  |}| | }x| D ]\}}|dkr| j|}t|d| || j|< |jdk	rp| |j j||tjd x|j	D ]}| | j
|d qxW | j|kr|| _qt||| qW dS )z
        Update node's attributes.
        :param nid: the identity of modified node
        :param attrs: attribute pairs recognized by Node object
        :return: None
        r    N)r   replacerc   )r   )re   itemsr"   r]   setattrr   rb   r   ZREPLACErX   ra   r$   )r   r   Zattrsr&   Zcnattrvalfpr   r   r   update_node^  s    



zTree.update_node)NF)N)NNNN)N)N)N)F)N)N)NNTF)NNTFF)FTF)9r   
__module____qualname____doc__rU   rangerS   ru   rv   rw   r#   r'   r+   r-   r0   r   r   r8   r@   rM   ri   rg   rt   re   rl   rn   ro   r^   rR   rq   rs   rz   r{   r   rp   rr   rC   r   r   r   propertyr   rj   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   D   sh   
	2"


C	


%


C

r   )r   Z
__future__r   r   r   r   copyr   baser   r   r   r	   ImportErrorio
exceptionsr   r   
__author__r   r   r   r   r   r   <module>   s    