a
    Cgt6                     @   sr   d Z ddl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G d	d
 d
ejZG dd deZdS )zAdjacency List    )serializers)models)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 'parent' 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.
    parentN)_meta	get_fieldmodelproxy_for_model)clsZ
base_class r   P/var/www/lab.imftr.de/x/nb_venv/lib/python3.9/site-packages/treebeard/al_tree.pyget_result_class
   s    r   c                       s    e Zd ZdZ fddZ  ZS )AL_NodeManagerz3Custom manager for nodes in an Adjacency List tree.c                    s4   | j jrdgt| j j }nddg}t  j| S )z(Sets the custom queryset as the default.r   	sib_order)r   node_order_bylistsuperget_querysetorder_by)selfr   	__class__r   r   r   $   s    zAL_NodeManager.get_queryset)__name__
__module____qualname____doc__r   __classcell__r   r   r   r   r   "   s   r   c                   @   s   e Zd ZdZe ZdZedd Zedd Z	d1dd	Z
d
d Zd2ddZdd Zdd Zdd Zed3ddZdd Zedd Zed4ddZdd Zdd  Zd!d" Zd5d#d$Zed%d& Zed'd( Zed)d* Zed+d, Zd6d-d.ZG d/d0 d0ZdS )7AL_Nodez7Abstract model to create your own Adjacency List Trees.Nc                 K   s   t |dkr.d|v r.|d }|jjs<tdn| f i |}d|_| jsz&t| jjdd	d
 d j}W n ty   d}Y n0 |d |_|  |S )zAdds a root node to the tree.   instance<Attempted to add a tree node that is already in the databaseTZparent__isnullr   r   )len_stateaddingr   _cached_depthr   r   objectsfilterr   reverser   
IndexErrorsave)r   kwargsnewobjmaxr   r   r   add_root3   s(    




zAL_Node.add_rootc                 C   s   t | jjddS )z;:returns: A queryset containing the root nodes in the tree.Tr$   )r   r)   r*   )r   r   r   r   get_root_nodesL   s    zAL_Node.get_root_nodesFc                 C   s^   | j du rdS z|r| `n| jW S W n ty6   Y n0 d}| }|rT|j}|d7 }q@|| _|S )z
        :returns: the depth (level) of the node
            Caches the result in the object itself to help in loops.

        :param update: Updates the cached value.
        Nr!   r   )	parent_idr(   AttributeErrorr   )r   updatedepthnoder   r   r   	get_depthQ   s    

zAL_Node.get_depthc                 C   s   t | jjj| dS )z/:returns: A queryset of all the node's childrenr   )r   r   r)   r*   r   r   r   r   get_childrenl   s    zAL_Node.get_childrenc                 C   s4   | j jr*| jdu rdS | jjj| jdS n| jS dS )z5:returns: the parent node of the current node object.Npk)r	   r   r3   r   r)   getr   )r   r5   r   r   r   
get_parentp   s
    
zAL_Node.get_parentc                 C   s^   g }| j jr<| j}| }|jrZ|jj|jd}|d| qn| j}|rZ|d| |j}qB|S )z
        :returns: A *list* containing the current node object's ancestors,
            starting by the root node and descending to the parent.
        r<   r   )r	   r   r   r3   r)   r>   insertr   )r   	ancestorsr   r7   r   r   r   get_ancestors~   s    zAL_Node.get_ancestorsc                 C   s   |   }|r|d S | S )z4:returns: the root node for the current node object.r   )rB   )r   rA   r   r   r   get_root   s    zAL_Node.get_rootc                 C   s   | j dd | D v S )z
        :returns: ``True`` if the node if a descendant of another node given
            as an argument, else, returns ``False``
        c                 S   s   g | ]
}|j qS r   r<   ).0objr   r   r   
<listcomp>       z,AL_Node.is_descendant_of.<locals>.<listcomp>)r=   get_descendants)r   r7   r   r   r   is_descendant_of   s    zAL_Node.is_descendant_ofTc                 C   s  |   }|r.|| kr.|j|kr.|jj|jd}||}g i  }}| jjj}t|t	
d|D ]\}}	| }
|	d }|d= d|v r|d= ||v r||= d|i}|r|	d ||< |s|
dks|r|
| kr|| n(||j }d	|vrg |d	< |d	 | |||j< q^|S )
z/Dumps a tree branch to a python data structure.r<   pythonfieldsr   r   datar=   r!   children)Z_get_serializable_modelr   r)   r>   r=   get_treer	   Zattnamezipr   	serializer8   appendr3   )r   r   Zkeep_idsZserializable_clsobjsretZlnkZpk_fieldr7   Zpyobjr6   rK   r/   Z	parentobjr   r   r   	dump_bulk   sD    




zAL_Node.dump_bulkc                 K   s   t | j}t|dkr8d|v r8|d }|jjsFtdn|f i |}z| jd |_W n tyh   Y n0 |jsz|j	j
| d d j}W n ty   d}Y n0 |d |_| |_|  |S )zAdds a child to the node.r!   r"   r#   r9   r   )r   r   r%   r&   r'   r   r(   r4   r   r)   r*   r+   r   r,   r   r-   )r   r.   r   r/   r0   r   r   r   	add_child   s*    




zAL_Node.add_childc                 C   sF   |r|  }n|  }|D ]&}||_|| | |||d  qd S Nr!   )r;   r2   r(   rQ   _get_tree_recursively)r   resultsr   r6   nodesr7   r   r   r   rW      s    

zAL_Node._get_tree_recursivelyc                 C   s2   |r|  d }|g}nd}g }| ||| |S )z
        :returns: A list of nodes ordered as DFS, including the parent. If
                  no parent is given, the entire tree is returned.
        r!   )r8   rW   )r   r   r6   rX   r   r   r   rN      s    zAL_Node.get_treec                 C   s   | j j| ddd S )zo
        :returns: A *list* of all the node's descendants, doesn't
            include the node itself
        r9   r!   N)r   rN   r:   r   r   r   rH     s    zAL_Node.get_descendantsc                 C   s   t |  S )z.:returns: the number of descendants of a nodee)r%   rH   r:   r   r   r   get_descendant_count  s    zAL_Node.get_descendant_countc                 C   s&   | j rt| jjj| j dS | j S )zi
        :returns: A queryset of all the node's siblings, including the node
            itself.
        r9   )r   r   r   r)   r*   r2   r:   r   r   r   get_siblings  s
    zAL_Node.get_siblingsc                 K   sv   |  |}t|dkr8d|v r8|d }|jjsLtdnt| jf i |}| jsb| j|| |_	| j
|_
|  |S )z8Adds a new node as a sibling to the current node object.r!   r"   r#   )Z _prepare_pos_var_for_add_siblingr%   r&   r'   r   r   r   r   _get_new_sibling_orderr   r3   r-   )r   posr.   r/   r   r   r   add_sibling  s    

zAL_Node.add_siblingc                 C   s   |dkp|dko||  kS )Nlast-siblingright)get_last_sibling)r   r]   targetr   r   r   _is_target_pos_the_last_sibling/  s    z'AL_Node._is_target_pos_the_last_siblingc                 C   sP   t | jj|d}| r(|jdd}n|j|jd}|jtdd d d S )NZsib_order__gteTr$   r9   r   r!   )r   )r   r)   r*   Zis_rootr   r5   r   F)r   mintarget_nodeZqsetr   r   r   _make_hole_in_db4  s
    zAL_Node._make_hole_in_dbc                 C   s   |  }|j|jd|j|jd|d| }|j|jd dd| }z|dd j}W n tyn   d}Y n0 |r| || |S )Nrd   )Zsib_order__gt)leftr`   first-siblingr!   r   r   )r[   r*   r   r   r,   rh   )r   r]   rg   Zsiblingsr   rf   r   r   r    _make_hole_and_get_sibling_order=  s(    
z(AL_Node._make_hole_and_get_sibling_orderc                 C   s,   |  ||r| jd }n| ||}|S rV   )rc   ra   r   rk   )r   r]   rg   r   r   r   r   r\   R  s    zAL_Node._get_new_sibling_orderc                 C   s  |  |}d}d}|dv rV| s<| }dddd| }n|}|dkrNd}nd}d}|| rlttd| |kr|d	ks|d
v r|| ks|dkr|| krdS |dkr|r|| _q|j| _n0|r|| _	n| j
||| _	|r|| _n|j| _|   dS )zu
        Moves the current node and all it's descendants to a new position
        relative to another node.
        N)zfirst-childz
last-childsorted-childrj   r_   zsorted-siblingrl   r!   z Can't move node to a descendant.ri   )r`   r_   )Z_prepare_pos_var_for_moveZis_leafZget_last_childrI   r   _ra   Zget_first_siblingr   r   r   r\   r-   )r   rb   r]   r   r   r   r   r   moveZ  s\    




zAL_Node.movec                   @   s   e Zd ZdZdZdS )zAL_Node.MetazAbstract model.TN)r   r   r   r   Zabstractr   r   r   r   Meta  s   ro   )F)F)NT)N)N)N)r   r   r   r   r   r)   r   classmethodr1   r2   r8   r;   r?   rB   rC   rI   rT   rU   rW   rN   rH   rZ   r[   r^   rc   rh   rk   r\   rn   ro   r   r   r   r   r    -   sD   



+








9r    N)r   Zdjango.corer   Z	django.dbr   Zdjango.utils.translationr   rm   Ztreebeard.exceptionsr   r   Ztreebeard.modelsr   r   Managerr   r    r   r   r   r   <module>   s   