a
    CgBb                     @   s   d Z ddlZddlmZ ddlmZ ddlmZmZ ddl	m
Z
 ddlmZ ddlmZmZ dd	lmZ 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dS )zNested Sets    N)reduce)serializers)
connectionmodels)Q)gettext_noop)InvalidMoveToDescendantNodeAlreadySaved)Nodec                 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 'lft'/'rgt' fields). 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.
    lftN)_meta	get_fieldmodelZproxy_for_model)clsZ
base_class r   P/var/www/lab.imftr.de/x/nb_venv/lib/python3.9/site-packages/treebeard/ns_tree.pyget_result_class   s    r   c                    s<    fddt  d t d B D } d d  |fS )zE
    Merge return values from Django's Queryset.delete() method.
    c                    s.   i | ]&}| d   |dd   |d qS )   r   )get).0keyc1c2r   r   
<dictcomp>+   s   z*merge_deleted_counters.<locals>.<dictcomp>r   r   )set)r   r   Zobject_countsr   r   r   merge_deleted_counters'   s    r   c                       s4   e Zd ZdZddd fdd
Zde_de_  ZS )NS_NodeQuerySetzg
    Custom queryset for the tree node manager.

    Needed only for the customized delete method.
    Nremoved_rangesdeleted_counterc                   sH  t | j}|du rdi f}|durt j|i |}t||}|d}t|ddD ](\}}	}
||	|
|\}}||| qTni }| 	ddD ]:}d}|
 D ]\}}||rd} qq|s|||j< qg }g }|
 D ]B\}}|t|j|jfd	t|jd
@  ||j|j|jf q|rD|jttj|j||d}|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
        Nr   writeT)reversetree_idr   F)
lft__range)r#   r   )r   r   superdeleter   _get_database_cursorsorted_get_close_gap_sqlexecuteorder_byitemsis_descendant_ofpkappendr   r   rgtr#   objectsfilterr   operatoror_)selfr   r    argskwargsr   resultcursorr#   drop_lftdrop_rgtsqlparamsremovednodefoundZridZrnodeZtoremoverangesid	__class__r   r   r&   9   sN    




zNS_NodeQuerySet.deleteT)__name__
__module____qualname____doc__r&   Zalters_dataZqueryset_only__classcell__r   r   rC   r   r   2   s   =r   c                   @   s   e Zd ZdZdd ZdS )NS_NodeManagerz/Custom manager for nodes in a Nested Sets tree.c                 C   s   t | jddS )z(Sets the custom queryset as the default.r#   r   )r   r   r+   r5   r   r   r   get_queryset}   s    zNS_NodeManager.get_querysetN)rE   rF   rG   rH   rL   r   r   r   r   rJ   z   s   rJ   c                   @   s6  e Zd ZdZg ZejddZejddZejddZ	ejddZ
e Zedd Zed5dd	Zed
d Zdd Zd6ddZd7ddZedd Zed8ddZdd Zdd Zdd Zdd Zdd  Zd!d" Zed9d#d$Zed:d%d&Zd'd( Zd)d* Zd+d, Z d-d. Z!d;d/d0Z"ed1d2 Z#G d3d4 d4Z$dS )<NS_Nodez4Abstract model to create your own Nested Sets Trees.T)Zdb_indexc                 K   s   |   }|r"|jr"|jdi |S |r2|jd }nd}t|dkrdd|v rd|d }|jjsvtdnt| f i |}d|_	||_d|_
d|_|  |S )zAdds a root node to the tree.sorted-siblingr   instance<Attempted to add a tree node that is already in the database   )rN   )get_last_root_nodenode_order_byadd_siblingr#   len_stateaddingr	   r   depthr   r0   save)r   r7   	last_rootZ
newtree_idnewobjr   r   r   add_root   s"    

zNS_Node.add_rootFrQ   c                 C   s:   |r
d}nd}dt jt| jj||||d }|g fS )Nz>=>a0  UPDATE %(table)s  SET lft = CASE WHEN lft %(lftop)s %(parent_rgt)d                 THEN lft %(incdec)+d                 ELSE lft END,      rgt = CASE WHEN rgt >= %(parent_rgt)d                 THEN rgt %(incdec)+d                 ELSE rgt END  WHERE rgt >= %(parent_rgt)d AND        tree_id = %(tree_id)s)tableZ
parent_rgtr#   lftopincdecr   ops
quote_namer   r   db_table)r   r#   r0   Zlftmover`   r_   r<   r   r   r   _move_right   s    	
zNS_Node._move_rightc                 C   s&   dt jt| jj|d }|g fS )NzGUPDATE %(table)s  SET tree_id = tree_id+1  WHERE tree_id >= %(tree_id)d)r^   r#   ra   )r   r#   r<   r   r   r   _move_tree_right   s    
zNS_Node._move_tree_rightc                 K   s   |   s8| jrd}nd}|  }| |_|j|fi |S | j| j| jdd\}}t	|dkrd|v r|d }|j
jstdnt| jf i |}| j|_| jd |_| jd |_| jd |_|  jd7  _| |_| d}||| |  |S )	zAdds a child to the node.rN   last-siblingFrQ   r   rO   rP   r!   )is_leafrS   get_last_child_cached_parent_objrT   rD   re   r#   r0   rU   rV   rW   r	   r   rX   r   r'   r*   rY   )r5   r7   posZ
last_childr<   r=   r[   r9   r   r   r   	add_child   s2    


zNS_Node.add_childNc                 K   s  |  |}t|dkr8d|v r8|d }|jjsLtdnt| jf i |}| j|_d}| }| rd|_	d|_
|dkrt|| |}|rd}|d }nd	}|j }|d	ks|d
kr||kr|jd |_n.d|j|jd d| }|j|\}}	||_nd|j|_|dkrDt|| |}|r@d}|d }nd	}|dv rt| }|d
kr||d krxd	}n4d}d}
|D ]&}|
r|} qn||krd}
q|dkr||d krd}|dkr|d }| jj}|d	kr| j
}||j|dd\}}	nN|dkr6|j	}||j|d dd\}}	n$|dkrZ|j	}||j|dd\}}	||_	|d |_
|r| d}|||	 |  |S )z8Adds a new node as a sibling to the current node object.r   rO   rP   NrQ   rN   leftr   rg   right)first-siblingrm   rn   rm   rn   ro   FTro   r!   )Z _prepare_pos_var_for_add_siblingrU   rV   rW   r	   r   rD   rX   is_rootr   r0   listget_sorted_pos_querysetget_siblingsrR   r#   rf   re   
get_parentr'   r*   rY   )r5   rk   r7   r[   r<   targetsiblingsrZ   newposr=   r@   r?   
move_rightr9   r   r   r   rT      s    


















zNS_Node.add_siblingc              	   C   s6  |  |}t| j}d}|dv rJ| r2|}d}n| }dddd| }|| r`ttd| |kr|dks|d	v r|| ks|dkr||	 krdS |dkrt
|| | }|rd}|d
 }nd}|dv rdt
| }|dkr6||d krd}n4d}d}|D ]&}|r$|} q6n||krd}q|dkrR||d
 krRd}|dkrd|d
 }| d}|j}	| j| j d }
d}|j}|dkr|j}|	|j|d|
\}}n| rd}|dkr|  d
 jd }n8|dkrd}|d\}}n|dkr||j\}}nx|dkrH| j}|	|j|d|
\}}nN|dkrr|j}|	|j|d d|
\}}n$|dkr|j}|	|j|d|
\}}|r||| |jj| jd}|j|j }|r|d7 }dtj|jj|j|||j ||j|jd }||g  | |j|j|j\}}||| dS )zu
        Moves the current node and all it's descendants to a new position
        relative to another node.
        N)zfirst-child
last-childzsorted-childr{   ro   rg   rN   z Can't move node to a descendant.rm   )rn   rg   r   rp   rn   rq   FTr!   r   r.   zUPDATE %(table)s  SET tree_id = %(target_tree)d,      lft = lft + %(jump)d ,      rgt = rgt + %(jump)d ,      depth = depth + %(depthdiff)d  WHERE tree_id = %(from_tree)d AND      lft BETWEEN %(fromlft)d AND %(fromrgt)d)r^   Z	from_treetarget_treeZjump	depthdiffZfromlftZfromrgt)!Z_prepare_pos_var_for_mover   rD   rh   ri   r-   r   _Zget_last_siblingZget_first_siblingrs   rt   ru   r'   re   r0   r   r#   rr   r"   rf   rv   r*   r1   r   r.   rX   r   rb   rc   r   rd   r)   )r5   rw   rk   r   parentrx   r@   r?   r9   rz   Zgapr<   r}   ry   r=   Zfromobjr~   r   r   r   moved  s    






















zNS_Node.movec                 C   s2   dt jt| jj|| d ||d }|g fS )Na?  UPDATE %(table)s  SET lft = CASE            WHEN lft > %(drop_lft)d            THEN lft - %(gapsize)d            ELSE lft END,      rgt = CASE            WHEN rgt > %(drop_lft)d            THEN rgt - %(gapsize)d            ELSE rgt END  WHERE (lft > %(drop_lft)d      OR rgt > %(drop_lft)d) AND      tree_id=%(tree_id)dr   )r^   Zgapsizer:   r#   ra   )r   r:   r;   r#   r<   r   r   r   r)     s    

zNS_Node._get_close_gap_sqlc           
         s   t | } g }|r|jndfdd|ddd D }|  }| jjj}|r| \}|d  }	| ||	 |r|| |	|< r| jj	d}|j
f i |	 n| jf i |	 | j d|v rJ| fdd|d ddd D  qJ|S )	z.Loads a list/dictionary structure to the tree.Nc                    s   g | ]} |fqS r   r   r   r?   )	parent_idr   r   
<listcomp>      z%NS_Node.load_bulk.<locals>.<listcomp>rq   datar|   childrenc                    s   g | ]} j |fqS r   r|   r   )node_objr   r   r     s   )r   r.   Zget_foreign_keysr   attnamepopcopyZ_process_foreign_keysr1   r   rl   r\   r/   extend)
r   Z	bulk_datar   keep_idsaddedstackZforeign_keyspk_fieldZnode_structZ	node_datar   )r   r   r   	load_bulk  s0    

zNS_Node.load_bulkc                 C   s   |   j| jd dS )z/:returns: A queryset of all the node's childrenr   rX   )get_descendantsr2   rX   rK   r   r   r   get_children   s    zNS_Node.get_childrenc                 C   s   | j S )z':returns: the depth (level) of the noder   rK   r   r   r   	get_depth$  s    zNS_Node.get_depthc                 C   s   | j | j dkS )z?:returns: True if the node is a leaf node (else, returns False)r   r0   r   rK   r   r   r   rh   (  s    zNS_Node.is_leafc                 C   s&   | j dkr| S t| jjj| jddS )z4:returns: the root node for the current node object.r   )r#   r   )r   r   rD   r1   r   r#   rK   r   r   r   get_root,  s
    
zNS_Node.get_rootc                 C   s
   | j dkS )z?:returns: True if the node is a root node (else, returns False)r   r   rK   r   r   r   rr   3  s    zNS_Node.is_rootc                 C   s    | j dkr|  S | d S )zi
        :returns: A queryset of all the node's siblings, including the node
            itself.
        r   T)r   get_root_nodesrv   r   rK   r   r   r   ru   7  s    
zNS_Node.get_siblingsc                 C   s   |   |}g i  }}| jjj}|D ]}td|gd }|d }	|	d }
|	d= |	d= |	d= |	d= ||	v rr|	|= d|	i}|r|d	 ||< |s|
d
ks|r|
|jkr|| n0|	 }||j }d|vrg |d< |d | |||j< q&|S )z/Dumps a tree branch to a python data structure.pythonr   fieldsrX   r   r0   r#   r   r.   r   r   )
Z_get_serializable_modelget_treer   r.   r   r   	serializerX   r/   rv   )r   r   r   ZqsetretZlnkr   ZpyobjZserobjr   rX   r[   Z	parentobjZ	parentserr   r   r   	dump_bulk@  s:    


zNS_Node.dump_bulkc                 C   sP   t | } |du r| j S | r2| jj|jdS | jj|j|j|jd fdS )z
        :returns:

            A *queryset* of nodes ordered as DFS, including the parent.
            If no parent is given, all trees are returned.
        Nr|   r   )r#   r$   )	r   r1   allrh   r2   r.   r#   r   r0   )r   r   r   r   r   r   d  s    
zNS_Node.get_treec                 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|   )rh   r   rD   r1   noner   excluder.   rK   r   r   r   r   w  s    zNS_Node.get_descendantsc                 C   s   | j | j d d S )z.:returns: the number of descendants of a node.r   rQ   r   rK   r   r   r   get_descendant_count  s    zNS_Node.get_descendant_countc                 C   s6   |   rt| jj S t| jjj| j| j| jdS )z
        :returns: A queryset containing the current node object's ancestors,
            starting by the root node and descending to the parent.
        )r#   Zlft__ltZrgt__gt)	rr   r   rD   r1   r   r2   r#   r   r0   rK   r   r   r   get_ancestors  s    zNS_Node.get_ancestorsc                 C   s$   | j |j ko"| j|jko"| j|jk S )z
        :returns: ``True`` if the node if a descendant of another node given
            as an argument, else, returns ``False``
        )r#   r   r0   )r5   r?   r   r   r   r-     s
    

zNS_Node.is_descendant_ofc                 C   sN   |   rdS z|r| `n| jW S W n ty4   Y n0 |   d | _| jS )z
        :returns: the parent node of the current node object.
            Caches the result in the object itself to help in loops.
        Nr   )rr   rj   AttributeErrorr   r"   )r5   updater   r   r   rv     s    zNS_Node.get_parentc                 C   s   t | jjddS )z;:returns: A queryset containing the root nodes in the tree.r   r   )r   r1   r2   )r   r   r   r   r     s    zNS_Node.get_root_nodesc                   @   s   e Zd ZdZdZdS )zNS_Node.MetazAbstract model.TN)rE   rF   rG   rH   Zabstractr   r   r   r   Meta  s   r   )FrQ   )N)N)NF)NT)N)F)%rE   rF   rG   rH   rS   r   ZPositiveIntegerFieldr   r0   r#   rX   rJ   r1   classmethodr\   re   rf   rl   rT   r   r)   r   r   r   rh   r   rr   ru   r   r   r   r   r   r-   rv   r   r   r   r   r   r   rM      sN   
$
	,
e
 
%	#	

rM   )rH   r3   	functoolsr   Zdjango.corer   Z	django.dbr   r   Zdjango.db.modelsr   Zdjango.utils.translationr   r   Ztreebeard.exceptionsr   r	   Ztreebeard.modelsr
   r   r   queryZQuerySetr   ManagerrJ   rM   r   r   r   r   <module>   s   H