
    *`mh                     n    d dl Z d dlmZ d dlZd dlT d dlT d dlmZ  G d dej	        j
                  ZdS )    N)defaultdict)*)timeitc            	       R   e Zd ZdZdZdZ eedddddd          Z eedd          Z	 ee
d	
          Z eedd          Z ee
d
          Z eed
          Z ee
d
          Z ee
d
          Z ee
d
          Z ee
d
          Z ee
d
          Z ee
d
          Zed+d            Zed,d            Zed+d            Ze	 d-d            Zed             Zej         G d d                      Zi ZdZ ed             Z!ed.d            Z"ed             Z#ed             Z$ed e%d!e&fd"            Z'ed/d e%d!e&d#e(j)        j*        fd$            Z+ed%             Z,ed&             Z-e e.de/j0        d'(          d)                         Z1e2d*             Z3dS )0RelationCacheu  
    Мастер таблица всех деревянных связей объектов. В т.ч. непрямых. Т.е. дерего развёрнутое.
    Не заменяет m2m таблицы.
    Работа с данными этой таблицы только через методы этой модели. Вся магия деревьев здесь.

    TODO: Мультиколоночные индексы id+field

    TODO: какой-нить лок на изменение дерева, т.к. одновременные изменения его поломают.
        Сейчас защита на select for update, но нет уверенности что этого достаточно.
    Fu)   Идентификатор объектаu3   Автоматически генерируетсяT)captioncommentnullableprimary_keyreadonlyvisibleu   ID родителя)r   indexu$   Поле связи родителя)r   u   ID потомкаu"   Поле связи потомкаu   Глубина связиu   Модель родителяu   Имя родителяu   Код родителяu   Модель потомкаu.   Опциональное имя потомкаu   Код потомкаNc                     dg}ddd|gdd|gg}|rddd|g|g}|r|g dz  }|                      |||	          }|r|S t          d
 |D                       S )u  
        full=True - возвращаем список экземпляров RelationCacheModel со всеми полями
        иначе только список child_id
        fixme: фильтр по child_field - нужен ли? Возможно
        r   AND	parent_id==parent_fieldchild_modelIN)
r   r   parent_modelparent_nameparent_codechild_fieldr   
child_name
child_codedepthfieldsfilter
for_updatec                 &    h | ]}|j         j        S  )child_idvalue.0rows     "./cmf/fields/cmf_relation_cache.py	<setcomp>z-RelationCache.get_children.<locals>.<setcomp>D   s    :::CS\':::    list)	clsr   parent_field_namechild_modelsfullr    r   query_filterresults	            r(   get_childrenzRelationCache.get_children0   s     T9=PTVg?hi 	V!M4#FUL 	S S S S SF TT 	M::6:::;;;r*   c                    t          t                    }|                     ||d|          D ]1}||j        j                                     |j        j                   2|sd |                                D             S |S )u   
        get_children с разбитием по классам, потом удобней фильтровать, подгружать.
        result = {"child_model1": ["child_id1", ...], ...}
        T)r0   r/   c                 4    i | ]\  }}|t          |          S r"   r+   )r&   
model_nameids_sets      r(   
<dictcomp>z3RelationCache.get_children_dict.<locals>.<dictcomp>Q   s%    XXX2E*gJWXXXr*   )r   setr3   r   r$   addr#   items)r-   r   r.   r/   
return_setr2   r'   s          r(   get_children_dictzRelationCache.get_children_dictF   s     S!!##I/@tZf#gg 	B 	BC3?()--cl.@AAAA 	YXXXXXXr*   c                     dg}ddd|gdd|gg}|rddd|g|g}|                      |||          }|r|S t          d	 |D                       S )
u   
        full=True - возвращаем список экземпляров RelationCacheModel со всеми полями
        иначе только список parent_id
        r   r   r#   r   r   r   r   r   c                 &    h | ]}|j         j        S r"   )r   r$   r%   s     r(   r)   z,RelationCache.get_parents.<locals>.<setcomp>c   s    ;;;SS](;;;r*   r+   )	r-   r#   child_field_nameparents_modelsr0   r    r   r1   r2   s	            r(   get_parentszRelationCache.get_parentsT   s     
D(;mTSc=de 	Y!ND.#I<XLTT 	M;;F;;;<<<r*   c	                    |r|s,t          d| d| d| d| d ||h|h|h           
          |                     ||||dddg          }	|	rd	S |                     ||dd
          }
|                     ||dd
          } | |||                    d          d         |||||                    d          d         ||d          }||gz   D ]}|
|gz   D ]}|j        |j        z   }|j        |j        f|j        |j        fk    r|dz  }|j        |j	        f|j        |j	        fk    r|dz  } | |j        |j        |j
        |j        |j        |j        |j	        |j        |j        |j        |                                           d	S )u   Добавим прямую связь между полями двух объектов, а также связи между родителями и детьми.z%add_relation: try add None relation, (z, z) -> r   Tr   r   r   r#   r   r   r    r   N)r0   r    :)r   r   r   r   r   r#   r   r   r   r   r      )CmfOrmErrorr,   r3   rB   	partitionr   r   r   r#   r   r   r   r   r   r   r   save)r-   r   r.   r#   r@   r   r   r   r   direct_linkschildrenparentsdirect_linkparentchildr   s                   r(   add_relationzRelationCache.add_relatione   sj     	W 	WVV V/V V3=V VAKV V y"3!4{mk]SSV VW W W
 xx.?+;cU   4 4  	 F ##H.>TVZ#[[//)->TVZ/[[ c.?iNaNabeNfNfghNi#+;I[I[\_I`I`abIc!j
 
 
 - 	 	F![M1  u{2$f&9:{?TVaVn>oooQJENE$56;;OQ\Qh:iiiQJE $.V=P_e_r & 2@R"^9J % 1$/E<L   $&&&&#	 	r*   c                 &   |                      ||||dddg          }|sdS t          |          dk    r/t          dt          |           d| d	| d
| d	| 
||||          |d         }|                     ||d          }|                     ||d          }||z   D ]}	||z   D ]}
|	j        |
j        z   }|	j        |	j        f|j        |j        fk    r|dz  }|
j        |
j	        f|j        |j	        fk    r|dz  }| 
                    |	j        |	j        |
j        |
j	        |ddg          }|r|                                 t          d|	j        j         d	|	j        j         d
|
j        j         d	|
j	        j         d| 
|	|
|          dS )u3   
        TODO: пакетный режим
        r   Tr   rE   NrG   ztoo many relation (z) z::z => )r0   zrelation absent z depth )r,   lenLookupErrorrB   r3   r   r   r   r#   r   getdeleteFileNotFoundErrorr$   )r-   r   r.   r#   r@   rK   rN   rM   rL   rO   rP   r   links                r(   remove_relationzRelationCache.remove_relation   su    xx.?+;cU   4 4  	JF ""6c,&7&7 6 66 6!26 66 6#36 6 ,h8H	J J J
 #1o//)->T/JJ##H.>T#JJ, 	. 	.F!L0 . .u{2$f&9:{?TVaVn>oooQJENE$56;;OQ\Qh:iiiQJE ww$.V=P"^9JD#  @ @  .KKMMMM ,_",2_ _6<6I6O_ _$~3_ _7<7H7N_ _W\_ _ u	. . .'.	. 	.r*   c                       e Zd ZU dZeed<    ej        e          Z	eed<    ej        e          Z
eed<   dZeed<   dS )	RelationCache.FieldCacheu~   Данные по полю модели Cmf, отражающие его связи и положение в иерархии.field_class)default_factoryrL   	right_forFrecursion_doneN)__name__
__module____qualname____doc__CmfTypeMeta__annotations__dataclassesfieldr,   rL   r^   r_   boolr"   r*   r(   
FieldCacher[      s|          	M  	M    **4@@@$@@@
 ,++DAAA	4AAA$$$$$$r*   ri   c                    t          |dg           }t          |dd          r|t          |d          gz  }t          |t                    r!|d |                                D             z  }t	          t          |                    S )u$   Список имён related_modelsmodelsmodelNc                     g | ]	}|j         
S r"   )
class_name)r&   	sub_models     r(   
<listcomp>z8RelationCache.get_field_models_names.<locals>.<listcomp>   s    "`"`"`I9#7"`"`"`r*   )getattr
issubclassCmfSubclassedGenericRelationrelated_modelsr,   r9   )r-   rg   field_models_namess      r(   get_field_models_namesz$RelationCache.get_field_models_names   s     %UHb995'4(( 	<75'#:#:";;e9:: 	a"`"`I]I]I_I_"`"`"``C*++,,,r*   c                    | j         |         j                            |           |rKd|vr |                    d          d          d| }| j         |         j                            ||f           | j         |         j        }t          |t                    rft          |dd          sW| j         |         j	        sGd| j         |         _	        | 
                    |          D ]!}|                     || d           dS dS dS dS )uA   Используется при создании cls.fields_cache.r   nested_fieldsNTz.id)_fields_cacherL   appendrI   r^   r\   rr   CmfRelationBaserq   r_   rv   
_add_child)r-   rO   rP   backrefr   field_model_names         r(   r}   zRelationCache._add_child   sO    	&!*11%888 	I'!!"__S11!4@@w@@g&077HHH '.:k?33 	@_dCC	@)%0?	@ 7;Ce$3$'$>$>{$K$K @ @ u)9&>&>&>????	@ 	@ 	@ 	@ 	@ 	@@ @r*   c                 t   t           }t          t                              |                    | _        | j        D ]}}|j                                        D ]a}t          |t          j	        j
        j                  s|j        dk    s2|j         d|j         }|                     |          | j        |<   b~t          | j                  }|D ]}| j        |         }|j        }t          |t                    r|j        r|                     |          }t          |t&          t(          f          rt|D ]p}	d}
|j        D ]9}|                    |	dz             r|                     |||j                   d}
:|
s)|                     ||	 d|j        pd |j                   qt1          d|          d	S )
ud   
        Вызывается после загрузки моделей при создании app)model_filteridrx   )r\   F)r~   Tz#Unsupported nested_fields for fieldN)rh   r,   cmfutiliter_models_models_listr   valuesrr   cmfrk   
base_model
CmfRelBasern   ri   rz   sortedr\   ry   rv   CmfBackrefBase
CmfM2MBase
startswithr}   r~   	TypeError)r-   r   rl   rg   field_full_namesorted_fields
field_namefield_cachefield_modelsfield_modelfoundnested_field_names               r(   build_fields_cachez RelationCache.build_fields_cache  s!       3 3 3 N NOO
 % 	W 	WE,,.. W W"5#**?*JKK uO_cgOgOg%*%5"J"J8H"J"J58^^PU^5V5V!/22	W s011' 	W 	WJ+J7K+E %,, W1D W"99%@@enj%ABB W'3 	m 	m %161D - --0;;KOLL - #z;LV[Vc d d d(,$ mNN *{,T,TU]=Rd,T,T^c^k + m m m	m $$I:VVV'	W 	Wr*   c                     | j                             |          }|r3|j        rdS |j        r%t	          |j        t          t          f          rdS d S d S d S )NT)rz   rU   r^   rL   rr   r\   r|   r   )r-   r   fcs      r(   _is_tree_fieldzRelationCache._is_tree_fieldA  st    ""?33 	| t{ z".?J:WXX t	 	
   r*   
inst_field	target_idc                    |j         j        }|j        }| d| }| j        |         }|j        }|                    d          d         }|j        D ]\  }	}
|
                    d          \  }}}|	                    d          \  }}}t          |t          t          f          r.||k    r'| 	                    |||j         j
        j        |           t          d|          |j        D ]t}|                    d          \  }}}t          |t          t          f          r.||k    r'| 	                    |j         j
        j        |||           et          d|          dS )uk  
        Рвём связь между field.instance и target_id, если она существует
        По field и fields_cache мы должны понять:
          - есть ли такая связь в кеше
          - поля, между которыми связь
          - кто родитель и кто потомок
        rx   rF   r   Unsupported right_for for fieldUnsupported children for fieldN)instancern   rz   r\   rI   r^   rr   r|   r   rY   r   r$   r   rL   )r-   r   r   r   r   r   r   rg   target_model_nameparent_field_full_namechild_field_full_namechild_model_name_r@   parent_model_namer.   child_full_field_names                    r(   remove_field_referencez$RelationCache.remove_field_referenceL  s    &.9*
-<<
<</%//44Q7 >@\ 	T 	T9"$94I4S4STW4X4X1a!16L6V6VWZ6[6[3q"3%/:!>?? T$(999''	3DjFYF\Fbdtuuu A?SSS &([ 	S 	S!4I4S4STW4X4X1a!1%/:!>?? S$(888''
(;(>(DjR[]mnnn @/RRR	S 	Sr*   targetc                    d}d}|rK|j         j        }t          |t          j        j                  r|j        j        }|j        j        }|j        }n|	                    d          d         \  }}}|j
        j        }|j        }	| d|	 }
| j        |
         }|j        }|j
        }d}d}t          |t          j        j                  r|j        j        }|j        j        }|j        D ]\  }}|	                    d          \  }}}|	                    d          \  }}}t          |t          t           f          r.||k    r'|                     |||j         j        |||||           t%          d|
          |j        D ]t}|	                    d          \  }}}t          |t          t           f          r.||k    r'|                     |j         j        |	||||||           et%          d|
          dS )u`  
        Создаём связь между field.instance и target_id, если нужно
        По field и fields_cache мы должны понять:
          - есть ли такая связь в кеше
          - поля, между которыми связь
          - кто родитель и кто потомок
        NrF   r   rx   r   r   r   r   r   r   )r   r$   
isinstancer   rk   	CmfEntitynamecodern   rI   r   rz   r\   r^   rr   r|   r   rQ   r   rL   )r-   r   r   r   target_nametarget_coder   r   r   r   r   r   rg   r   instance_nameinstance_coder   r   r   r@   r   r.   r   s                          r(   add_field_referencez!RelationCache.add_field_referencer  sh     	B	I&#*"677 0$k/$k/ & 1&/&9&9#&>&>q&A#q!%.9*
-<<
<</&h
 455 	0$M/M$M/M >@\ 	T 	T9"$94I4S4STW4X4X1a!16L6V6VWZ6[6[3q"3%/:!>?? T$(999$$!#4hk6GIY$/[#0] % L L L
   A?SSS &([ 
	S 
	S!4I4S4STW4X4X1a!1%/:!>?? S$(888$$ ):yBR$1}#.; % H H H
   @/RRR
	S 
	Sr*   c                    d }d }t          |t          j        j                  r|j        j        }|j        j        }|                    d          \  }}}| j        |         }|j	        }	t          ||	j                  }
|
sd S d }d }t          |
t                    rFt          |
j        t          j        j                  r"|
j        j        j        }|
j        j        j        }|j        D ]\  }}|                    d          \  }}}|                    d          \  }}}t          |	t                    rG|
j        j        |k    r6|                     |
j        j        j        ||j        j        |||||           t          |	t"                    rt%          d|          |j        D ]}|                    d          \  }}}t          |	t                    rG|
j        j        |k    r6|                     |j        j        ||
j        j        j        |||||           xt          |	t"                    r|
j        D ]y}|j        |k    rld }d }t          |t          j        j                  r|j        j        }|j        j        }|                     |j        j        ||j        j        |||||           zt%          d|          d S )Nrx   r   r   r   )r   r   rk   r   r   r$   r   rI   rz   r\   rq   rn   r|   r^   rr   rQ   r   r   r   rL   )r-   r   inst	inst_name	inst_coder   r   r   r   fr   inst_field_nameinst_field_coder   r   r   r@   r   r.   r   rel_instrel_inst_namerel_inst_codes                          r(   _make_field_relationsz#RelationCache._make_field_relations  s1   
 		dCJ011 	(	I	I*9*C*CC*H*H'!Z/NT1<00
 	Fj/22 	:z*BRTWT^Th7i7i 	:(.39O(.39O >@\ 	T 	T9"$94I4S4STW4X4X1a!16L6V6VWZ6[6[3q"3!_-- T#.2CCC $$"(+13D'7$3#,	 % D D D Az** T A?SSS%'[ 	S 	S!4I4S4STW4X4X1a!1!_-- S#.2BBB $$z"(+13C$-9#2	 % P P P Az** S * 0 P PH*.>>>(,(,%h
0DEE @,4M,?M,4M,?M (( GM:$K-/?(1y'4	 ) P P PP   @/RRR;	S 	Sr*   c           	      H   d}d}d}| j         D ]}t          |t          j        j                  s#t          d|j                    |dz  }|j        }|j        	                    |          
                    |j        j                            d                                                     }||j        	                    |          
                    |j        j                            d                                                     z  }t                      }g }|j        	                    |j        |j        |j                  D ]C\  }	}
}|
|f|v r |dz  }|dz  }|                    |	           ,|                    |
|f           D|j        	                    |          
                    |j                            |                                        d           |r"|dz  }t          |j         d| d| d           t          | d	| d
           d S )Nr   zClear rG   F)synchronize_session z
 deleted, z duplicate refs/z m2m tables cleared)r   rr   r   rk   BaseM2MModelprintrn   dp_modeldpquery_deprecatedr   r   isnotrV   root_idr9   r   left_idright_idr{   r:   in_)r-   models_count	dup_countclearedrl   r   deleteduniq_reldup_idsrel_idr   r   s               r(   clear_old_m2m_treez RelationCache.clear_old_m2m_tree  s2    	% !	[ !	[EeSZ%<== -5+--...AL~Hh//99@@AYA_A_`dAeAeffmmooGux00::AA%.BXB^B^_cBdBdeellnnnG uuHG-2X-F-Fx{T\Tdfnfw-x-x 6 6)X&(22qLGNINN6****LL'8!45555H%%h//66x{w7O7OPPWWlqWrrr [1)YYGYYyYYYZZZ<<<<<<=====r*   <   	log_startalarm_levelalarm_thresholdc                      t           j                            d          }dt           j        d<   t          d            j                             j                                                  }t          d| d                                             t          dt          j        d           fd	            } j        D ] |j                                                     |t           j        d= d
S |t           j        d<   d
S )u  
        Актуализация данных в таблице. Требуется монопольный режим для обеспечения целостности.
            TODO: Лок на таблицу
        Пока лучше выключать срм на время ребилда:
            systemctl stop eva-app
            cd /opt/eva-app
            python3 ./manage.py shell <<<"models.RelationCache.recalculate()"
        Есть смысл делать при обновлении, после наката миграций.

        TODO: clear=True - предварительно очистить таблицу.
        TODO: выборочный пересчёт
        NO_CACHE1zClear relation_cachez  z ClearedFrG   r   c                    d}g }g }	j                                         D ]W}	j         d|j         }                    |          r/|                    |           |                    |j                   X|sd S 	                    dddg|          D ]"}|dz  }|D ]}                    ||           #t          d	j         d	| d
           d S )Nr   rx   r   r   r   r   rG   zBuild relations from z done, z objects)r   r   rn   r   r{   r,   r   r   )
_model_namecount_tree_fields_full_namestree_fields_namesrg   r   r   r   r-   rl   s
           r(   create_model_relationsz9RelationCache.recalculate.<locals>.create_model_relationsB  s+    F%'" ",,.. ? ?%*%5"J"J8H"J"J%%o66 ?*11/BBB%,,U-=>>>) !JJtVV.XFW.XJYY D D!"8 D DJ--j(CCCCDS%*:SS6SSSTTTTTr*   N)osenvironrU   r   r   r   r   rV   r   r   loggingWARNINGr   rn   fix_relation_locations)r-   	cache_oldcountr   rl   s   `   @r(   recalculatezRelationCache.recalculate$  s/     JNN:..	!$
:
 	$%%%''55<<>>"5"""### 	   	%W_a	P	P	P	U 	U 	U 	U 	U 
Q	P	U. % 	5 	5E""5#34444 	""$$$
:&&&%.BJz"""r*   c            
         ddl m} m} ddlm} t          |                                          D ]}t          |t                    st          ||           r-t          d|j                    d}d}d}|                    dg          D ]}|dz  }|j        r|j        sP|dz  }t          d|j         d	|j        j         d
|j        j                    |                                 |dz  }e|j        j                            d          d         }|j        j                            d          d         }	|j        | |	 k    r"|dz  }t          d|j         d	| d
|	            t          | d| d| d           t          ||          rKt          d|j                    d}d}d}|                    dg          D ]}|dz  }|j        j                            d          d         }
|j        j                            d          d         }|j        j                            d          d         }	|j                            |          sn|dz  }t          d|j         d	| d
|	            t)          ||
d          r"t)          ||d          rt)          ||d          s|                                 |dz  }t          | d| d| d           dS )u6  
        TODO: временно, после патчинга баз - убрать.
        Из-за бага, м2м связи могут быть в 'чужой' таблице. Найдём их и исправим.
        python3 ./manage.py shell "models.RelationCache.fix_relation_locations()"
        r   )CmfM2MModelCmfGM2MModel)rk   zCheck Model r   r   rG   zInvalid z: z - rF   r   z rows invalid, z fixedN)
cmf.modelsr   r   cmf.includerk   varsr   r   typerr   r   rn   r,   r   r   r   r$   rV   rI   r   rq   )r   r   rk   rl   	all_countinvalid_countfixed_countr   left_model_nameright_model_nameid_model_names              r(   r   z$RelationCache.fix_relation_locationsf  s    	98888888&&&&&&&\\((** *	X *	XEeT** %-- X7U%577888	 ! %

3%
 8 8 ` `HNI#+ !83D !%*lll8H8NllS[SdSjllmmm )))#q( &.&6&<&F&Fs&K&KA&NO'/'8'>'H'H'M'Ma'P$'o+Q?O+Q+QQQ%*^^^^^L\^^___VVVV;VVVWWW%.. X7U%577888	 ! %

3%
 8 8 - -HNI$,K$5$?$?$D$DQ$GM&.&6&<&F&Fs&K&KA&NO'/'8'>'H'H'M'Ma'P$ +66GG -%*^^^^^L\^^___&v}dCC -'.v'M'M-'.v'M'M- %OO---'1,KVVVV;VVVWWWU*	X *	Xr*   )NNN)NF)NNNN)N)NN)4r`   ra   rb   rc   abstract	api_allowFieldCmfTUUIDr   r   CmfTextr   r#   r   CmfIntr   r   r   r   r   r   r   classmethodr3   r=   rB   rQ   rY   rf   	dataclassri   rz   r   rv   r}   r   r   r   strr   r   rk   CmfModelr   r   r   r   r   r   r   staticmethodr   r"   r*   r(   r   r   
   s       	 	 HI	E  PED4
H 
H 
HB h(=TJJJI5*PQQQLuX':$GGGH%)MNNNKE&"=>>>E 5*IJJJL%)BCCCK%)BCCCK%)FGGGKw(XYYYJw(?@@@J < < < [<* 
 
 
 [
 = = = [=   MQ> > > [>@ 7. 7. [7.x 	% 	% 	% 	% 	% 	% 	% 	% ML- - [- @ @ @ [@$ (W (W [(W^   [ #S
 #Ss #S #S #S [#SJ 8S 8SZ 8SC 8SX[XbXk 8S 8S 8S [8St JS JS [JSX '> '> ['>R Ve"MMM=/ =/ NM [=/@ 3X 3X \3X 3X 3Xr*   r   )rf   collectionsr   cmf.models.base_modelr   cmf.fields.base_fieldsr   cmf.cmf_profiler   rk   	BaseModelr   r"   r*   r(   <module>r
     s        # # # # # #     $ $ $ $     " " " " " "P
X P
X P
X P
X P
XCJ( P
X P
X P
X P
X P
Xr*   