o
    hBb                     @   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 )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.
    lft)_meta	get_fieldmodelproxy_for_model)cls
base_class r   Q/var/www/html/ndineBlogger/venv/lib/python3.10/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   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rAt j|i |}t||}|d}t|ddD ]\}}	}
||	|
|\}}||| q*|S i }| 	ddD ]}d}|
 D ]\}}||r^d} nqQ|sf|||j< qIg }g }|
 D ]!\}}|t|j|jfd	t|jd
@  ||j|j|jf qo|r|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foundridrnodetoremoverangesid	__class__r   r   r)   9   sT   





zNS_NodeQuerySet.deleteT)__name__
__module____qualname____doc__r)   alters_dataqueryset_only__classcell__r   r   rI   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.   r8   r   r   r   get_queryset}   s   zNS_NodeManager.get_querysetN)rK   rL   rM   rN   rT   r   r   r   r   rR   z   s    rR   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d6ddZedd Zed7ddZdd Zdd Zdd Zdd Zdd  Zd!d" Zed8d#d$Zed6d%d&Zd'd( Zd)d* Zd+d, Z d-d. Z!d9d/d0Z"ed1d2 Z#G d3d4 d4Z$dS ):NS_Nodez4Abstract model to create your own Nested Sets Trees.T)db_indexc                 K   s   |   }|r|jr|jdi |S |r|jd }nd}t|dkr2d|v r2|d }|jjs1tdn	t| d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   N)rW   r   )get_last_root_nodenode_order_byadd_siblingr&   len_stateaddingr	   r   depthr   r3   save)r   r:   	last_root
newtree_idnewobjr   r   r   add_root   s$   
zNS_Node.add_rootFrZ   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)table
parent_rgtr&   lftopincdecr   ops
quote_namer   r   db_table)r   r&   r3   lftmoverk   rj   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)rh   r&   rl   )r   r&   r?   r   r   r   _move_tree_right   s   
zNS_Node._move_tree_rightc                 K   s   |   s| jr
d}nd}|  }| |_|j|fi |S | j| j| jdd\}}t	|dkr@d|v r@|d }|j
js?tdn
t| jd
i |}| j|_| jd |_| jd |_| jd |_|  jd7  _| |_| d}||| |  |S )zAdds a child to the node.rW   last-siblingFrZ   r   rX   rY   r$   Nr   )is_leafr\   get_last_child_cached_parent_objr]   rJ   rq   r&   r3   r^   r_   r`   r	   r   ra   r   r*   r-   rb   )r8   r:   pos
last_childr?   r@   re   r<   r   r   r   	add_child   s4   

zNS_Node.add_childNc                 K   sr  |  |}t|dkrd|v r|d }|jjstdn
t| jdi |}| j|_d}| }| rd|_	d|_
|dkrQt|| |}|rOd}|d }nd	}|j }|d	ksb|d
kri||kri|jd |_nd|j|jd d| }|j|\}}	||_n|j|_|dkrt|| |}|rd}|d }nd	}|dv rt| }|d
kr||d krd	}nd}d}
|D ]}|
r|} n||krd}
q|dkr||d krd}|dkr|d }| jj}|d	kr| j
}||j|dd\}}	n'|dkr|j	}||j|d dd\}}	n|dkr|j	}||j|dd\}}	||_	|d |_
|r3| d}|||	 |  |S )z8Adds a new node as a sibling to the current node object.r   rX   rY   NrZ   rW   leftr   rs   right)first-siblingrz   r{   rz   r{   r|   FTr|   r$   r   ) _prepare_pos_var_for_add_siblingr^   r_   r`   r	   r   rJ   ra   is_rootr   r3   listget_sorted_pos_querysetget_siblingsr[   r&   rr   rq   
get_parentr*   r-   rb   )r8   rw   r:   re   r?   targetsiblingsrc   newposr@   rC   rB   
move_rightr<   r   r   r   r]      s   








zNS_Node.add_siblingc              	   C   s  |  |}t| j}d}|dv r%| r|}d}n| }dddd| }|| r0ttd| |krN|dksL|d	v rB|| ksL|dkrN||	 krNdS |dkrgt
|| | }|red}|d
 }nd}|dv rt
| }|dkr||d kr~d}nd}d}|D ]}|r|} n||krd}q|dkr||d
 krd}|dkr|d
 }| d}|j}	| j| j d }
d}|j}|dkr|j}|	|j|d|
\}}nm| rd}|dkr|  d
 jd }nW|dkrd}|d\}}nI|dkr||j\}}n<|dkr| j}|	|j|d|
\}}n'|dkr)|j}|	|j|d d|
\}}n|dkr;|j}|	|j|d|
\}}|rD||| |jj| jd}|j|j }|rY|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   r|   rs   rW   z Can't move node to a descendant.rz   )r{   rs   r   r}   r{   r~   FTr$   r   r1   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)rh   	from_treetarget_treejump	depthdifffromlftfromrgt)!_prepare_pos_var_for_mover   rJ   rt   ru   r0   r   _get_last_siblingget_first_siblingr   r   r   r*   rq   r3   r   r&   r   r%   rr   r   r-   r4   r   r1   ra   r   rm   rn   r   ro   r,   )r8   r   rw   r   parentr   rC   rB   r<   r   gapr?   r   r   r@   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   )rh   gapsizer=   r&   rl   )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}|rz| \}|d  }	| ||	 |rA|| |	|< rS| jj	d}|j
d	i |	 n| jd	i |	 | j d|v rx| fdd|d ddd D  |s'|S )
z.Loads a list/dictionary structure to the tree.Nc                    s   g | ]} |fqS r   r   r   rB   )	parent_idr   r   
<listcomp>  s    z%NS_Node.load_bulk.<locals>.<listcomp>r~   datar   childrenc                    s   g | ]} j |fqS r   r   r   )node_objr   r   r     s    r   )r   r1   get_foreign_keysr   attnamepopcopy_process_foreign_keysr4   r   ry   rf   r2   extend)
r   	bulk_datar   keep_idsaddedstackforeign_keyspk_fieldnode_struct	node_datar   )r   r   r   	load_bulk  s2   
zNS_Node.load_bulkc                 C   s   |   j| jd dS )z/:returns: A queryset of all the node's childrenr   ra   )get_descendantsr5   ra   rS   r   r   r   get_children   s   zNS_Node.get_childrenc                 C   s   | j S )z':returns: the depth (level) of the noder   rS   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   r3   r   rS   r   r   r   rt   (  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   rJ   r4   r   r&   rS   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   rS   r   r   r   r   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_nodesr   r   rS   r   r   r   r   7  s   
zNS_Node.get_siblingsc                 C   s   |   |}g i }}| jjj}|D ]b}td|gd }|d }	|	d }
|	d= |	d= |	d= |	d= ||	v r9|	|= d|	i}|rE|d	 ||< |sK|
d
ksR|rX|
|jkrX|| n|	 }||j }d|vrig |d< |d | |||j< q|S )z/Dumps a tree branch to a python data structure.pythonr   fieldsra   r   r3   r&   r   r1   r   r   )
_get_serializable_modelget_treer   r1   r   r   	serializera   r2   r   )r   r   r   qsetretlnkr   pyobjserobjr   ra   re   	parentobj	parentserr   r   r   	dump_bulk@  s8   



zNS_Node.dump_bulkc                 C   sP   t | } |du r| j S | r| 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   r4   allrt   r5   r1   r&   r   r3   )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   )rt   r   rJ   r4   noner   excluder1   rS   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   rZ   r   rS   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&   lft__ltrgt__gt)	r   r   rJ   r4   r   r5   r&   r   r3   rS   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   r3   )r8   rB   r   r   r   r0     s
   

zNS_Node.is_descendant_ofc                 C   sN   |   rdS z|r| `n| jW S W n	 ty   Y nw |   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   )r   rv   AttributeErrorr   r%   )r8   updater   r   r   r     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   r4   r5   )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)rK   rL   rM   rN   abstractr   r   r   r   Meta  s    r   )FrZ   )N)NF)NT)F)%rK   rL   rM   rN   r\   r   PositiveIntegerFieldr   r3   r&   ra   rR   r4   classmethodrf   rq   rr   ry   r]   r   r,   r   r   r   rt   r   r   r   r   r   r   r   r   r0   r   r   r   r   r   r   r   rU      sP    
$
	
,
e 
%	#	

rU   )rN   r6   	functoolsr   django.corer   	django.dbr   r   django.db.modelsr   django.utils.translationr   r   treebeard.exceptionsr   r	   treebeard.modelsr
   r   r   queryQuerySetr    ManagerrR   rU   r   r   r   r   <module>   s    H