a
    Cg                     @   sR  d Z ddlZddlmZ ddlmZ ddlmZmZm	Z	 ddl
mZmZmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZmZmZ dd Zd&ddZd'ddZdd ZG dd dej j!Z"G dd dej#Z$G dd de%Z&G dd de&Z'G dd de&Z(G dd de&Z)G d d! d!e'Z*G d"d# d#e'Z+G d$d% d%eZ,dS )(zMaterialized Path Trees    N)reduce)serializers)modelstransaction
connection)FQValue)ConcatSubstr)gettext_noop)NumConv)Node)InvalidMoveToDescendantPathOverflowNodeAlreadySavedc                  O   s@   | dd }|dkr$dd| S |dkr6d| S d| S )Nvendormysqlz
CONCAT({}), 	microsoftz + z||)popformatjoin)argskwargsr    r   P/var/www/lab.imftr.de/x/nb_venv/lib/python3.9/site-packages/treebeard/mp_tree.py
sql_concat   s    
r   c                 C   s   |dkrd | S d | S )Nr   LEN({})z
LENGTH({}))r   )fieldr   r   r   r   
sql_length   s    
r    c                 K   sB   | dd }d}|rd}|dkr2|s.d| }d}|j| ||dS )Nr   zSUBSTR({field}, {pos})z SUBSTR({field}, {pos}, {length})r   r   z#SUBSTRING({field}, {pos}, {length}))r   poslength)r   r   )r   r!   r"   r   r   functionr   r   r   
sql_substr"   s    
r$   c                 C   s&   | j dj}| j j|kr| S |S dS )a  
    For the given model class, determine what class we should use for the
    nodes returned by its tree methods (such as get_children).

    Usually this will be trivially the same as the initial model class,
    but there are special cases when model inheritance is in use:

    * If the model extends another via multi-table inheritance, we need to
      use whichever ancestor originally implemented the tree behaviour (i.e.
      the one which defines the 'path' field). We can't use the
      subclass, because it's not guaranteed that the other nodes reachable
      from the current one will be instances of the same subclass.

    * If the model is a proxy model, the returned nodes should also use
      the proxy class.
    pathN)_meta	get_fieldmodelZproxy_for_model)clsZ
base_classr   r   r   get_result_class.   s    r*   c                       s,   e Zd ZdZ fddZde_de_  ZS )MP_NodeQuerySetzc
    Custom queryset for the tree node manager.

    Needed only for the custom delete method.
    c                    sN  i }|  ddD ]T}d}tdtt|j|j D ]"}||j|}||v r2d} qVq2|s|||j< qi }g }	| D ]\}}||j|jd }
|
r|
|vr|	d||
< ||
 }|r|j
dkr| j
d8  _
|  | r|	t|jd qv|	t|jd qvt| j}|	r.|jttj|	}n
|j }tt|j|i |S )	a  
        Custom delete method, will remove all descendant nodes to ensure a
        consistent tree (no orphans)

        :returns: tuple of the number of objects deleted and a dictionary 
                  with the number of deletions per object type
        depthr%   F   Tr   r%   path__startswith)order_byrangeintlenr%   steplen_get_basepathitemsr,   
get_parentnumchildsaveis_leafappendr   r*   r(   objectsfilterr   operatoror_nonesuperr+   delete)selfr   r   removednodefoundr,   r%   parentsZtoremove
parentpathparentr(   qset	__class__r   r   rC   M   s:    

zMP_NodeQuerySet.deleteT)__name__
__module____qualname____doc__rC   Zalters_dataZqueryset_only__classcell__r   r   rL   r   r+   F   s   5r+   c                   @   s   e Zd ZdZdd ZdS )MP_NodeManagerz5Custom manager for nodes in a Materialized Path tree.c                 C   s   t | jdS )z(Sets the custom queryset as the default.r%   )r+   r(   r1   rD   r   r   r   get_queryset   s    zMP_NodeManager.get_querysetN)rN   rO   rP   rQ   rU   r   r   r   r   rS      s   rS   c                   @   s   e Zd Zdd ZdS )MP_AddHandlerc                 C   s
   g | _ d S N)stmtsrT   r   r   r   __init__   s    zMP_AddHandler.__init__N)rN   rO   rP   rY   r   r   r   r   rV      s   rV   c                   @   s0   e Zd Zdd ZdddZddd	Zd
d ZdS )MP_ComplexAddMoveHandlerc                 C   s,   | j d}| jD ]\}}||| qd S )Nwrite)node_cls_get_database_cursorrX   execute)rD   cursorsqlvalsr   r   r   run_sql_stmts   s    z&MP_ComplexAddMoveHandler.run_sql_stmtsincc                 C   s6   dt jt| jjjddd| f }|g}||fS )z5:returns: The sql needed the numchild value of a nodez1UPDATE %s SET numchild=numchild%s1 WHERE path=%%s+-)rc   dec)r   ops
quote_namer*   r\   r&   db_table)rD   r%   Zincdecr`   ra   r   r   r   get_sql_update_numchild   s    z0MP_ComplexAddMoveHandler.get_sql_update_numchildNFc                 C   s\  |dks|dkrH||  krH|  }| }	|rD| j| ||	 n|du r| }|j|jd|j|jd|d| }| }
d|
|
d d| }| j	
|j||}	d}|rLt|t|	krL| j	|tt|| j	j d }| j	|	|d }||krL|rL|	|k rL|  }| }
| j	
|	||
d	 }| j| || g }|	}|D ]*}|j|krn q|| | }qX|  |D ]}| |j| \}}| j||f |r||jr|d
 |t|d
 d  }|j|jr|d
 |jt|d
 d  |_q|rT|r@| j| ||	 n| j| ||	 ||	fS )z
        Handles the reordering of nodes and branches when adding/moving
        nodes.

        :returns: A tuple containing the old path and the new path.
        last-siblingrightN)Z	path__gteZpath__gt)leftrl   first-siblingr-   )ro   rn   rl      r   )get_last_sibling	_inc_pathrX   r<   get_sql_newpath_in_branchesget_siblingsr>   r%   _get_lastpos_in_pathr\   	_get_pathr4   r6   r3   r5   reverse
startswith)rD   r!   newposnewdepthtargetsiblingsoldpathZ
movebranchlastnewpathZbasenumZtempnewpathZparentoldpathZparentnewpathZmovesiblingsZ	priorpathrF   r`   ra   r   r   r    reorder_nodes_before_add_or_move   s    





$z9MP_ComplexAddMoveHandler.reorder_nodes_before_add_or_movec           
      C   s   | j d}dtjt| j jjf }|dkr6d}ntdt	dd|d|d}d|f g}|t
|d	 g}t
|t
|kr|dkr|d
td|d d |f  ||t
|d	 | j jg d}||d g d|d||f }	|	|fS )z
        :returns: The sql needed to move a branch to another position.

        .. note::

           The generated sql will only update the depth values if needed.

        r[   zUPDATE %s SETr   zCONCAT(%s, SUBSTR(path, %s))z%sr%   r   zpath=%sr-   zdepth=/%%szWHERE path LIKE %s%z%s %s %sr   )r\   get_database_vendorr   rg   rh   r*   r&   ri   r   r$   r4   r<   r    extendr5   r   )
rD   r}   r   r   Zsql1ZsqlpathZsql2ra   Zsql3r`   r   r   r   rs   
  s$    
 z4MP_ComplexAddMoveHandler.get_sql_newpath_in_branches)rc   )NF)rN   rO   rP   rb   rj   r   rs   r   r   r   r   rZ      s   
  
grZ   c                       s$   e Zd Z fddZdd Z  ZS )MP_AddRootHandlerc                    s   t    || _|| _d S rW   )rB   rY   r)   r   )rD   r)   r   rL   r   r   rY   2  s    
zMP_AddRootHandler.__init__c                 C   s   | j  }|r&|jr&|jdi | jS |r4| }n| j d dd}t| jdkrxd| jv rx| jd }|jj	st
dn| j f i | j}d|_||_|  |S )Nsorted-siblingr-   instance<Attempted to add a tree node that is already in the database)r   )r)   Zget_last_root_nodenode_order_byadd_siblingr   rr   rv   r4   _stateaddingr   r,   r%   r:   )rD   Z	last_rootr   newobjr   r   r   process7  s    




zMP_AddRootHandler.processrN   rO   rP   rY   r   rR   r   r   rL   r   r   1  s   r   c                       s$   e Zd Z fddZdd Z  ZS )MP_AddChildHandlerc                    s"   t    || _|j| _|| _d S rW   )rB   rY   rF   rM   r\   r   )rD   rF   r   rL   r   r   rY   Z  s    
zMP_AddChildHandler.__init__c                 C   s8  | j jr:| j s:| j jd7  _| j jd
i | jS t| jdkrnd| jv rn| jd }|j	j
stdn| j f i | j}| jjd |_| j r| j | jj|jd|_| j jdj}t|j|krttdn| j  |_t| j jj| jjdjtdd d	 | j jd7  _| j|_|  |S )Nr-   r   r   r   r%   zjThe new node is too deep in the tree, try increasing the path.max_length property and UPDATE your databaser.   r9   r9   )r   )r\   r   rF   r;   r9   get_last_childr   r   r4   r   r   r   r,   rv   r%   r&   r'   
max_lengthr   _rr   r*   r=   r>   updater   _cached_parent_objr:   )rD   r   r   r   r   r   r   `  s@    
 



zMP_AddChildHandler.processr   r   r   rL   r   r   Y  s   r   c                       s$   e Zd Z fddZdd Z  ZS )MP_AddSiblingHandlerc                    s(   t    || _|j| _|| _|| _d S rW   )rB   rY   rF   rM   r\   r!   r   )rD   rF   r!   r   rL   r   r   rY     s
    
zMP_AddSiblingHandler.__init__c              	   C   s0  | j | j| _t| jdkrDd| jv rD| jd }|jjsVtdn| jf i | j}| j j	|_	| jdkr| j 
| j  |}z| d  }W n ty   d }Y n0 |d u rd| _n
d g  }}| | j|| j j	| j |d d\}}| j || j j	d }|r| j| |d |   ||_|  |S )	Nr-   r   r   r   r   rk   Frc   )rF   Z _prepare_pos_var_for_add_siblingr!   r4   r   r   r   r   r\   r,   get_sorted_pos_querysetrt   allru   
IndexErrorr   r6   rX   r<   rj   rb   r%   r:   )rD   r   r|   ry   r   r   rI   r   r   r   r     s>    







zMP_AddSiblingHandler.processr   r   r   rL   r   r     s   r   c                       s>   e Zd Zd fdd	Zdd Zdd Zdd	 Zd
d Z  ZS )MP_MoveHandlerNc                    s(   t    || _|j| _|| _|| _d S rW   )rB   rY   rF   rM   r\   r{   r!   )rD   rF   r{   r!   rL   r   r   rY     s
    
zMP_MoveHandler.__init__c              	   C   s&  | j | j| _| j j}|  \}}}| j| j r@ttd|| jjkr| jdks| jdv rt| jj| j	 jks| jdkr| jj| j
 jkrd S | jdkr| j | j | j }z| d  }W n ty   d }Y n0 |d u rd| _| | j||| j||d\}}| || |   d S )	Nz Can't move node to a descendant.rn   )rl   rk   ro   r   r   rk   T)rF   Z_prepare_pos_var_for_mover!   r%   update_move_to_child_varsr{   is_descendant_ofr   r   rq   Zget_first_siblingr   rt   r   ru   r   r   sanity_updates_after_moverb   )rD   r}   rz   r|   ry   r   r   r   r   r     sF    
	


zMP_MoveHandler.processc                 C   s   | j ddkr2t|t|kr2| j| | | j |}| j |}|sR|sb|rZ|rb||kr|rz| j| |d |r| j| |d dS )z
        Updates the list of sql statements needed after moving nodes.

        1. :attr:`depth` updates *ONLY* needed by mysql databases (*sigh*)
        2. update the number of children of parent nodes
        r[   r   rf   rc   N)r\   r   r4   rX   r<    get_mysql_update_depth_in_branch_get_parent_path_from_pathrj   )rD   r}   r   ZoldparentpathZnewparentpathr   r   r   r     s6    

z(MP_MoveHandler.sanity_updates_after_movec                 C   s   | j j}d}g }| jdv r|| j }|d7 }| j  rNd}d| _t| jj }n | j  | _ dddd| j | _| j	d7  _	|||fS )z=Update preliminar vars in :meth:`move` when moving to a childN)zfirst-childz
last-childzsorted-childr-   ro   rk   r   )
r{   r,   r!   r;   r*   r\   r=   rA   r   r9   )rD   rz   ry   r|   rJ   r   r   r   r     s&    

z(MP_MoveHandler.update_move_to_child_varsc                 C   sP   | j d}dtd|d d tjt| j jjf }| j j	|d g}||fS )zn
        :returns: The sql needed to update the depth of all the nodes in a
                  branch.
        r[   UPDATE %s SET depth=r%   r   z/%%s WHERE path LIKE %%sr   )
r\   r   r    r   rg   rh   r*   r&   ri   r5   )rD   r%   r   r`   ra   r   r   r   r   8  s    z/MP_MoveHandler.get_mysql_update_depth_in_branch)N)	rN   rO   rP   rY   r   r   r   r   rR   r   r   rL   r   r     s
   2r   c                   @   s  e Zd ZdZdZdZg ZejdddZ	e
 Zej
ddZd	Ze Zd
Zedd Zedd Zedd Zedd ZedTddZedd ZedUddZedd ZedVddZedd ZedWd d!Zd"d# Zd$d% Zd&d' Zd(d) Z d*d+ Z!d,d- Z"d.d/ Z#d0d1 Z$d2d3 Z%d4d5 Z&d6d7 Z'dXd8d9Z(d:d; Z)d<d= Z*d>d? Z+d@dA Z,dYdBdCZ-dZdDdEZ.edFdG Z/edHdI Z0dJdK Z1dLdM Z2edNdO Z3edPdQ Z4G dRdS dSZ5d
S )[MP_Nodez:Abstract model to create your own Materialized Path Trees.   Z$0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ   T)r   uniquer   )defaultr-   Nc                 C   s   |   |S rW   )numconv_objZint2strr)   numr   r   r   _int2strT  s    zMP_Node._int2strc                 C   s   |   |S rW   )r   Zstr2intr   r   r   r   _str2intX  s    zMP_Node._str2intc                 C   s$   | j d u rtt| j| j| _ | j S rW   )numconv_obj_r   r4   alphabetr)   r   r   r   r   \  s    
zMP_Node.numconv_objc                 K   s   t | fi | S )z
        Adds a root node to the tree.

        This method saves the node in database. The object is populated as if via:

        ```
        obj = cls(**kwargs)
        ```

        :raise PathOverflow: when no more root objects can be added
        )r   r   )r)   r   r   r   r   add_rootb  s    zMP_Node.add_rootc                 C   s  t | } |  j }|r(|j|jd}g i  }}| jjj}t	
d|D ]}|d }|d }	tt|	| j }
|d= |d= |d= ||v r||= d|i}|r|d ||< |s|
d	ks|rt|	t|jkr|| n6| |	|
d	 }|| }d
|vrg |d
< |d
 | |||	< qH|S )z/Dumps a tree branch to a python data structure.r/   pythonfieldsr%   r,   r9   datapkr-   children)r*   Z_get_serializable_modelr=   r   r>   r%   r&   r   Zattnamer   	serializer3   r4   r5   r<   r6   )r)   rJ   Zkeep_idsrK   retZlnkZpk_fieldZpyobjr   r%   r,   r   rI   Z	parentobjr   r   r   	dump_bulkq  s>    


zMP_Node.dump_bulkc              	   C   sR  t | } | d}g g g   }}}g g  }}| j D ]
}d}|jD ]"}	|	| jvrF||j d} qjqF|rpq6t|j| j	 r||j q6z|
d W n$ | jy   ||j Y q6Y n0 |jtt|j| j	 kr||j q6| jj| |jdjtd|dd | j	|jd f gd	 }
|
|jkr6||j q6q6|||||fS )
ah  
        Checks for problems in the tree structure, problems can occur when:

           1. your code breaks and you get incomplete transactions (always
              use transactions!)
           2. changing the ``steplen`` value in a model (you must
              :meth:`dump_bulk` first, change ``steplen`` and then
              :meth:`load_bulk`

        :returns: A tuple of five lists:

                  1. a list of ids of nodes with characters not found in the
                     ``alphabet``
                  2. a list of ids of nodes when a wrong ``path`` length
                     according to ``steplen``
                  3. a list of ids of orphaned nodes
                  4. a list of ids of nodes with the wrong depth value for
                     their path
                  5. a list of ids nodes that report a wrong number of children
        r[   FTpath__ranger%   r   z/%d=%dr-   )where)r*   r   r=   r   r%   r   r<   r   r4   r5   r8   ZDoesNotExistr,   r3   r>   _get_children_path_intervalextrar    countr9   )r)   r   Z
evil_charsZbad_steplenZorphansZwrong_depthZwrong_numchildrF   Zfound_errorcharZreal_numchildr   r   r   find_problems  sB    





 

zMP_Node.find_problemsFc                 C   s@  t | } | d}| d}dtd|d d td|d d tj| jjf }| j	| j	g}|
|| d| j	 g}| dd	krd
tdd|d d dtj| jji }nFdtdd|d d }d| d | }|dtj| jji }|d9 }|
|| ddtj| jji }| D ]"}|d |d g}|
|| q"|sR|r<t  dg}	|	r|	d\}
}| jj|
|ddddd}|j| jpdg }i }d}|D ]@}| |d | j	 d }|||< |du s||kr|}qt|D ]\}}|d }| |d | j	 d }||kr6n||}|r|d }|d7 }| |
||}t|t|kr| |
||d }ttd|f | || |||< ||= ||d< |d }| |
||}| || |||< ||= ||d< |d r|	|d |d f qqdW d   n1 s20    Y  dS )a  
        Solves some problems that can appear when transactions are not used and
        a piece of code breaks, leaving the tree in an inconsistent state.

        The problems this method solves are:

           1. Nodes with an incorrect ``depth`` or ``numchild`` values due to
              incorrect code and lack of database transactions.
           2. "Holes" in the tree. This is normal if you move/delete nodes a
              lot. Holes in a tree don't affect performance,
           3. Incorrect ordering of nodes when ``node_order_by`` is enabled.
              Ordering is enforced on *node insertion*, so if an attribute in
              ``node_order_by`` is modified after the node is inserted, the
              tree ordering will be inconsistent.

        :param fix_paths:

            A boolean value. If True, a slower, more complex fix_tree method
            will be attempted. If False (the default), it will use a safe (and
            fast!) fix approach, but it will only solve the ``depth`` and
            ``numchild`` nodes, it won't fix the tree holes or broken path
            ordering.

        :param destructive:

            Deprecated; alias for ``fix_paths``.
        r[   r   r%   r   z/%%s WHERE depth!=r   r   readr   z^SELECT tbn1.path, tbn1.numchild, (SELECT COUNT(1) FROM %(table)s AS tbn2 WHERE tbn2.path LIKE z	tbn1.pathz%%szO) AS real_numchild FROM %(table)s AS tbn1 HAVING tbn1.numchild != real_numchildtablez=(SELECT COUNT(1) FROM %(table)s AS tbn2 WHERE tbn2.path LIKE )z!SELECT tbn1.path, tbn1.numchild, z/ FROM %(table)s AS tbn1 WHERE tbn1.numchild != rp   z0UPDATE %(table)s SET numchild=%%s WHERE path=%%sr   ) r-   )r0   r,   r   r,   r9   Nr-   Path Overflow from: '%s')r*   r   r]   r    r   rg   rh   r&   ri   r5   r^   r   fetchallr   Zatomicr   r=   r>   valuesr1   r   r   	enumerategetrv   r4   r   r   _rewrite_node_pathr<   )r)   ZdestructiveZ	fix_pathsr   r_   r`   ra   Zsubquery	node_dataZchildren_to_fixparent_pathr,   r   Zdesired_sequenceZactual_sequenceZmax_positionitemZactual_positioniZdesired_positionZoccupantold_pathnew_pathZprevious_max_pathr   r   r   fix_tree  s    







zMP_Node.fix_treec                 C   s2   | j j|djtt|tdt|d d d S )Nr/   r%   r-   r.   )r=   r>   r   r
   r	   r   r4   )r)   r   r   r   r   r   r   n  s    zMP_Node._rewrite_node_pathc                 C   sL   t | } |du r| j S | r2| jj|jdS | jj|j|jddS )z
        :returns:

            A *queryset* of nodes ordered as DFS, including the parent.
            If no parent is given, the entire tree is returned.
        Nr   )r0   Z
depth__gter%   )	r*   r=   r   r;   r>   r   r%   r,   r1   )r)   rJ   r   r   r   get_treew  s    
zMP_Node.get_treec                 C   s   t | jjdddS )z;:returns: A queryset containing the root nodes in the tree.r-   r,   r%   )r*   r=   r>   r1   r   r   r   r   get_root_nodes  s    zMP_Node.get_root_nodesc              
   C   s   t | } | d}|r2|jd }| |j}d}nd}g }d}tddd|d}d	| d
 | d tj| j	j
|| j ||d }| d}||| g }	dd |jD }
| D ]8}| f i tt|
|dd }|d |_|	| q|	S )z
        Helper for a very common case: get a group of siblings and the number
        of *descendants* in every sibling.
        r[   r-   zAND path BETWEEN %s AND %sr   r%   1z%(subpathlen)sr   z5SELECT * FROM %(table)s AS t1 INNER JOIN  (SELECT    zi AS subpath,    COUNT(1)-1 AS count    FROM %(table)s    WHERE depth >= %(depth)s %(extrand)s   GROUP BY z0) AS t2  ON t1.path=t2.subpath  ORDER BY t1.path)r   Z
subpathlenr,   extrandc                 S   s   g | ]}|d  qS r   r   ).0r   r   r   r   
<listcomp>      z7MP_Node.get_descendants_group_count.<locals>.<listcomp>N)r*   r   r,   r   r%   r$   r   rg   rh   r&   ri   r5   r]   r^   descriptionr   dictzipZdescendants_countr<   )r)   rJ   r   r,   paramsr   subpathr`   r_   r   field_namesr   rF   r   r   r   get_descendants_group_count  sD    



 
z#MP_Node.get_descendants_group_countc                 C   s   | j S )z':returns: the depth (level) of the noder   rT   r   r   r   	get_depth  s    zMP_Node.get_depthc                 C   sP   t | jjj| jdd}| jdkrL| | j| jd }|j| |d}|S )zi
        :returns: A queryset of all the node's siblings, including the node
            itself.
        r   r%   r-   r   )	r*   rM   r=   r>   r,   r1   r6   r%   r   )rD   rK   rI   r   r   r   rt     s    
zMP_Node.get_siblingsc                 C   sB   |   rt| jj S t| jjj| jd | | jd	dS )z/:returns: A queryset of all the node's childrenr-   )r,   r   r%   )
r;   r*   rM   r=   rA   r>   r,   r   r%   r1   rT   r   r   r   get_children  s    
zMP_Node.get_childrenc                 C   s2   z|   j| jdd W S  ty,   Y dS 0 dS )zi
        :returns: The next node's sibling, or None if it was the rightmost
            sibling.
        rm   r   N)rt   r>   r%   r   rT   r   r   r   get_next_sibling  s    zMP_Node.get_next_siblingc                 C   s.   |   rt| jj S | j| j| jdS )zx
        :returns: A queryset of all the node's descendants as DFS, doesn't
            include the node itself
        r   )r;   r*   rM   r=   rA   r   excluder   rT   r   r   r   get_descendants  s    zMP_Node.get_descendantsc                 C   s6   z|   j| jd d W S  ty0   Y dS 0 dS )zl
        :returns: The previous node's sibling, or None if it was the leftmost
            sibling.
        )Zpath__ltr   N)rt   r>   r%   rw   r   rT   r   r   r   get_prev_sibling
  s    zMP_Node.get_prev_siblingc                 C   s   | j S )zr
        :returns: The number the node's children, calculated in the most
        efficient possible way.
        r   rT   r   r   r   get_children_count  s    zMP_Node.get_children_countc                 C   sB   | j |j k}|r>| j dkr>| | j| j d }|o<|j|S |S )z
        :returns: ``True`` if the node is a sibling of another node given as an
            argument, else, returns ``False``
        r-   )r,   r6   r%   rx   )rD   rF   ZauxrI   r   r   r   is_sibling_of  s
    zMP_Node.is_sibling_ofc                 C   s   | j |j o| j|jd kS )z
        :returns: ``True`` is the node if a child of another node given as an
            argument, else, returns ``False``
        r-   r%   rx   r,   rD   rF   r   r   r   is_child_of(  s    zMP_Node.is_child_ofc                 C   s   | j |j o| j|jkS )z
        :returns: ``True`` if the node is a descendant of another node given
            as an argument, else, returns ``False``
        r   r   r   r   r   r   0  s    zMP_Node.is_descendant_ofc                 K   s   t | fi | S )a  
        Adds a child to the node.

        This method saves the node in database. The object is populated as if via:

        ```
        obj = self.__class__(**kwargs)
        ```

        :raise PathOverflow: when no more child nodes can be added
        )r   r   )rD   r   r   r   r   	add_child7  s    zMP_Node.add_childc                 K   s   t | |fi | S )aD  
        Adds a new node as a sibling to the current node object.

        This method saves the node in database. The object is populated as if via:

        ```
        obj = self.__class__(**kwargs)
        ```

        :raise PathOverflow: when the library can't make room for the
           node's new position
        )r   r   )rD   r!   r   r   r   r   r   E  s    zMP_Node.add_siblingc                 C   s    t | jjj| jd| j dS )z4:returns: the root node for the current node object.r   r.   )r*   rM   r=   r   r%   r5   rT   r   r   r   get_rootT  s    zMP_Node.get_rootc                 C   s
   | j dkS )z?:returns: True if the node is a root node (else, returns False)r-   r   rT   r   r   r   is_rootY  s    zMP_Node.is_rootc                 C   s
   | j dkS )z?:returns: True if the node is a leaf node (else, returns False)r   r   rT   r   r   r   r;   ]  s    zMP_Node.is_leafc                    s\      rt jj S  fddtdt j jdd D }t jjj	|d
dS )z
        :returns: A queryset containing the current node object's ancestors,
            starting by the root node and descending to the parent.
        c                    s   g | ]} j d | qS r   r.   )r   r!   rT   r   r   r   i  s   z)MP_Node.get_ancestors.<locals>.<listcomp>r   r-   N)Zpath__inr,   )r   r*   rM   r=   rA   r2   r4   r%   r5   r>   r1   )rD   pathsr   rT   r   get_ancestorsa  s    
zMP_Node.get_ancestorsc                 C   sx   t t| j| j }|dkr dS z|r,| `n| jW S W n tyH   Y n0 | | j|d }t| jj	j
|d| _| jS )z
        :returns: the parent node of the current node object.
            Caches the result in the object itself to help in loops.
        r-   Nr.   )r3   r4   r%   r5   r   AttributeErrorr6   r*   rM   r=   r   )rD   r   r,   rI   r   r   r   r8   p  s     zMP_Node.get_parentc                 C   s   t | || S )z
        Moves the current node and all it's descendants to a new position
        relative to another node.

        :raise PathOverflow: when the library can't make room for the
           node's new position
        )r   r   )rD   r{   r!   r   r   r   move  s    zMP_Node.movec                 C   s   |r|d|| j   S dS )z;:returns: The base path of another path up to a given depthr   r   )r5   )r)   r%   r,   r   r   r   r6     s    zMP_Node._get_basepathc                 C   s<   |  ||d }| |}d|| jd | jt|  |S )z
        Builds a path given some values

        :param path: the base path
        :param depth: the depth of the  node
        :param newstep: the value (integer) of the new step
        r-   	{0}{1}{2}r   )r6   r   r   r   r5   r4   )r)   r%   r,   ZnewsteprI   keyr   r   r   rv     s    	
zMP_Node._get_pathc                 C   sx   |  | j| j d d }| |}t|| jkrHttd| jf d| jd| j  | jd | jt|  |S )z<:returns: The path of the next sibling of a given node path.Nr-   r   r   r   )	r   r%   r5   r   r4   r   r   r   r   )rD   ry   r   r   r   r   rr     s    
zMP_Node._inc_pathc                 C   s   |  | j| j d S )z7:returns: The integer value of the last step in a path.N)r   r%   r5   rT   r   r   r   ru     s    zMP_Node._get_lastpos_in_pathc                 C   s   |r|dt || j  S dS )z*:returns: The parent path for a given pathr   r   )r4   r5   r)   r%   r   r   r   r     s    z"MP_Node._get_parent_path_from_pathc                 C   s(   || j d | j  || j d | j  fS )z@:returns: An interval of all possible children paths for a node.r   r   )r   r5   r   r   r   r   r     s    z#MP_Node._get_children_path_intervalc                   @   s   e Zd ZdZdZdS )zMP_Node.MetazAbstract model.TN)rN   rO   rP   rQ   Zabstractr   r   r   r   Meta  s   r   )NT)FF)N)N)N)F)N)6rN   rO   rP   rQ   r5   r   r   r   Z	CharFieldr%   ZPositiveIntegerFieldr,   r9   ZgaprS   r=   r   classmethodr   r   r   r   r   r   r   r   r   r   r   r   rt   r   r   r   r   r   r   r   r   r   r   r   r   r;   r   r8   r   r6   rv   rr   ru   r   r   r   r   r   r   r   r   E  s|   



*
; 

D
	








r   )N)N)-rQ   r?   	functoolsr   Zdjango.corer   Z	django.dbr   r   r   Zdjango.db.modelsr   r   r	   Zdjango.db.models.functionsr
   r   Zdjango.utils.translationr   r   Ztreebeard.numconvr   Ztreebeard.modelsr   Ztreebeard.exceptionsr   r   r   r   r    r$   r*   queryZQuerySetr+   ManagerrS   objectrV   rZ   r   r   r   r   r   r   r   r   r   <module>   s2   	

?  (55 