1- from typing import Dict , Generic , Iterable , List , Set , Tuple , TypeVar
1+ from collections import defaultdict
2+ from typing import Dict , Generic , Iterable , List , Set , Tuple , TypeVar , Union
23
34from ..dynamic_typing import DOptional , ModelMeta , ModelPtr
45
56Index = str
7+ ModelsStructureType = Tuple [List [dict ], Dict [ModelMeta , ModelMeta ]]
8+
69T = TypeVar ('T' )
710
811
@@ -26,40 +29,44 @@ def insert_before(self, value: T, *before: T):
2629 raise ValueError
2730 pos = min (ix )
2831 self .insert (pos , value )
32+ return pos
2933
3034 def insert_after (self , value : T , * after : T ):
3135 ix = self ._safe_indexes (* after )
3236 if not ix :
3337 raise ValueError
34- pos = max (ix )
35- self .insert (pos + 1 , value )
36-
38+ pos = max (ix ) + 1
39+ self .insert (pos , value )
40+ return pos
3741
38- def filter_pointers (model : ModelMeta ) -> Iterable [ModelPtr ]:
39- """
40- Return iterator over pointers with not None parent
41- """
42- return (ptr for ptr in model .pointers if ptr .parent )
4342
43+ class PositionsDict (defaultdict ):
44+ # Dict contains mapping Index -> position, where position is list index to insert nested element of Index
45+ INC = object ()
4446
45- def extract_root (model : ModelMeta ) -> Set [Index ]:
46- """
47- Return set of indexes of root models that are use given ``model`` directly or through another nested model.
48- """
49- seen : Set [Index ] = set ()
50- nodes : List [ModelPtr ] = list (filter_pointers (model ))
51- roots : Set [Index ] = set ()
52- while nodes :
53- node = nodes .pop ()
54- seen .add (node .type .index )
55- filtered = list (filter_pointers (node .parent ))
56- nodes .extend (ptr for ptr in filtered if ptr .type .index not in seen )
57- if not filtered :
58- roots .add (node .parent .index )
59- return roots
47+ def __init__ (self , default_factory = int , ** kwargs ):
48+ super ().__init__ (default_factory , ** kwargs )
6049
50+ def update_position (self , key : str , value : Union [object , int ]):
51+ """
52+ Shift all elements which are placed after updated one
6153
62- ModelsStructureType = Tuple [List [dict ], Dict [ModelMeta , ModelMeta ]]
54+ :param key: Index or "root"
55+ :param value: Could be position or PositionsDict.INC to perform quick increment (x+=1)
56+ :return:
57+ """
58+ if value is self .INC :
59+ value = self [key ] + 1
60+ if key in self :
61+ old_value = self [key ]
62+ delta = value - old_value
63+ else :
64+ old_value = value
65+ delta = 1
66+ for k , v in self .items ():
67+ if k != key and v >= old_value :
68+ self [k ] += delta
69+ self [key ] = value
6370
6471
6572def compose_models (models_map : Dict [str , ModelMeta ]) -> ModelsStructureType :
@@ -116,6 +123,84 @@ def compose_models(models_map: Dict[str, ModelMeta]) -> ModelsStructureType:
116123 return root_models , path_injections
117124
118125
126+ def compose_models_flat (models_map : Dict [Index , ModelMeta ]) -> ModelsStructureType :
127+ """
128+ Generate flat sorted (by nesting level, ASC) models structure for internal usage.
129+
130+ :param models_map: Mapping (model index -> model meta instance).
131+ :return: List of root models data, Map(child model -> root model) for absolute ref generation
132+ """
133+ root_models = ListEx ()
134+ positions : PositionsDict [Index , int ] = PositionsDict ()
135+ top_level_models : Set [Index ] = set ()
136+ structure_hash_table : Dict [Index , dict ] = {
137+ key : {
138+ "model" : model ,
139+ "nested" : ListEx (),
140+ "roots" : list (extract_root (model )), # Indexes of root level models
141+ } for key , model in models_map .items ()
142+ }
143+
144+ for key , model in models_map .items ():
145+ pointers = list (filter_pointers (model ))
146+ has_root_pointers = len (pointers ) != len (model .pointers )
147+ if not pointers :
148+ # Root level model
149+ if not has_root_pointers :
150+ raise Exception (f'Model { model .name } has no pointers' )
151+ root_models .insert (positions ["root" ], structure_hash_table [key ])
152+ top_level_models .add (key )
153+ positions .update_position ("root" , PositionsDict .INC )
154+ else :
155+ parents = {ptr .parent .index for ptr in pointers }
156+ struct = structure_hash_table [key ]
157+ # Model is using by other models
158+ if has_root_pointers or len (parents ) > 1 and len (struct ["roots" ]) >= 1 :
159+ # Model is using by different root models
160+ if parents & top_level_models :
161+ parents .add ("root" )
162+ parents_positions = {positions [parent_key ] for parent_key in parents
163+ if parent_key in positions }
164+ parents_joined = "#" .join (sorted (parents ))
165+ if parents_joined in positions :
166+ parents_positions .add (positions [parents_joined ])
167+ pos = max (parents_positions ) if parents_positions else len (root_models )
168+ positions .update_position (parents_joined , pos + 1 )
169+ else :
170+ # Model is using by only one model
171+ parent = next (iter (parents ))
172+ pos = positions .get (parent , len (root_models ))
173+ positions .update_position (parent , pos + 1 )
174+ positions .update_position (key , pos + 1 )
175+ root_models .insert (pos , struct )
176+
177+ return root_models , {}
178+
179+
180+ def filter_pointers (model : ModelMeta ) -> Iterable [ModelPtr ]:
181+ """
182+ Return iterator over pointers with not None parent
183+ """
184+ return (ptr for ptr in model .pointers if ptr .parent )
185+
186+
187+ def extract_root (model : ModelMeta ) -> Set [Index ]:
188+ """
189+ Return set of indexes of root models that are use given ``model`` directly or through another nested model.
190+ """
191+ seen : Set [Index ] = set ()
192+ nodes : List [ModelPtr ] = list (filter_pointers (model ))
193+ roots : Set [Index ] = set ()
194+ while nodes :
195+ node = nodes .pop ()
196+ seen .add (node .type .index )
197+ filtered = list (filter_pointers (node .parent ))
198+ nodes .extend (ptr for ptr in filtered if ptr .type .index not in seen )
199+ if not filtered :
200+ roots .add (node .parent .index )
201+ return roots
202+
203+
119204def sort_fields (model_meta : ModelMeta ) -> Tuple [List [str ], List [str ]]:
120205 """
121206 Split fields into required and optional groups
0 commit comments