11from time import time_ns
22import grequests
33import requests
4- import itertools
54from functools import partial
65from statistics import fmean , stdev
76
87# Branch for test:
98
109READ_SPEC_CRM = {
11- "stage_id" : {
12- "fields" : {
13- "display_name" : {}
14- }
15- },
10+ "stage_id" : {"fields" : {"display_name" : {}}},
1611 "probability" : {},
1712 "active" : {},
18- "company_currency" : {
19- "fields" : {
20- "display_name" : {}
21- }
22- },
13+ "company_currency" : {"fields" : {"display_name" : {}}},
2314 "recurring_revenue_monthly" : {},
24- "team_id" : {
25- "fields" : {
26- "display_name" : {}
27- }
28- },
15+ "team_id" : {"fields" : {"display_name" : {}}},
2916 "won_status" : {},
3017 "color" : {},
3118 "name" : {},
3219 "expected_revenue" : {},
33- "partner_id" : {
34- "fields" : {
35- "display_name" : {}
36- }
37- },
38- "tag_ids" : {
39- "fields" : {
40- "display_name" : {},
41- "color" : {}
42- }
43- },
20+ "partner_id" : {"fields" : {"display_name" : {}}},
21+ "tag_ids" : {"fields" : {"display_name" : {}, "color" : {}}},
4422 "lead_properties" : {},
4523 "priority" : {},
46- "activity_ids" : {
47- "fields" : {}
48- },
24+ "activity_ids" : {"fields" : {}},
4925 "activity_exception_decoration" : {},
5026 "activity_exception_icon" : {},
5127 "activity_state" : {},
5228 "activity_summary" : {},
5329 "activity_type_icon" : {},
54- "activity_type_id" : {
55- "fields" : {
56- "display_name" : {}
57- }
58- },
59- "user_id" : {
60- "fields" : {
61- "display_name" : {}
62- }
63- }
30+ "activity_type_id" : {"fields" : {"display_name" : {}}},
31+ "user_id" : {"fields" : {"display_name" : {}}},
6432}
6533
66- READ_SPEC_TASK = {
67-
68- }
34+ READ_SPEC_TASK = {}
6935
70- READ_SPEC_PROJECT = {
71-
72- }
36+ READ_SPEC_PROJECT = {}
37+
38+
39+ SESSION_ID = ""
40+ if not SESSION_ID :
41+ print ("Missing " , SESSION_ID )
42+ exit ()
7343
74- BASE_URL = "http://127.0.0.1:8069"
7544COOKIES = {
76- "session_id" : "" ,
45+ "session_id" : SESSION_ID ,
7746}
78- MAX_AUTO_UNFOLD = 10
47+ BASE_URL = "http://127.0.0.1:8069"
7948MAX_PARALLEL_REQUEST = 6 # Max of chrome
8049
8150SCENARIOS = [
8251 # ------- crm.lead
8352 # Open CRM - Kanban - Filter: default (none) - all open lead are loaded
8453 {
85- 'name' : "Open CRM - Kanban - Filter: default (none) - all open lead are loaded" ,
86- 'model' : 'crm.lead' ,
87- 'domain' : [],
88- 'groupby' : ["create_date:month" ],
89- 'aggregates' : ["probability:avg" , "recurring_revenue_monthly:sum" , "color:sum" , "expected_revenue:sum" ],
90- 'read_specification' : READ_SPEC_CRM ,
91- 'auto_unfold' : True ,
92- 'unfold_read_default_limit' : 80 ,
54+ "name" : "Open CRM - Kanban - Filter: default (none) - all open lead are loaded" ,
55+ "model" : "crm.lead" ,
56+ "domain" : [["type" , "=" , "opportunity" ]],
57+ "groupby" : ["stage_id" ],
58+ "aggregates" : [
59+ "probability:avg" ,
60+ "recurring_revenue_monthly:sum" ,
61+ "color:sum" ,
62+ "expected_revenue:sum" ,
63+ ],
64+ "read_specification" : READ_SPEC_CRM ,
65+ "auto_unfold" : True ,
66+ "unfold_read_default_limit" : 80 ,
9367 },
9468 # Open CRM - Kanban - Filter: Creation Date = 2025 - Groupby month
9569 {
96-
70+ "name" : "Open CRM - Kanban - Filter: Creation Date = 2025 - Groupby month" ,
71+ "model" : "crm.lead" ,
72+ "domain" : [
73+ "&" ,
74+ ["type" , "=" , "opportunity" ],
75+ "&" ,
76+ ["create_date" , ">=" , "2024-12-31 23:00:00" ],
77+ ["create_date" , "<=" , "2025-12-31 22:59:59" ],
78+ ],
79+ "groupby" : ["create_date:month" ],
80+ "aggregates" : [
81+ "probability:avg" ,
82+ "recurring_revenue_monthly:sum" ,
83+ "color:sum" ,
84+ "expected_revenue:sum" ,
85+ ],
86+ "read_specification" : READ_SPEC_CRM ,
87+ "auto_unfold" : True ,
88+ "unfold_read_default_limit" : 80 ,
9789 },
9890 # Open CRM - Kanban - Filter: default (none) - groupby lang_code (related field)
99- {
100-
101- },
91+ {},
10292 # Open CRM - Kanban - Filter : search 'test' (default groupby)
103- {
104-
105- },
93+ {},
10694 # Open CRM - Kanban - Filter: included active - groupby active
107- {
108-
109- },
95+ {},
11096 # Open CRM - List view - with multiple group loaded, TODO
111- {
112-
113- },
97+ {},
11498 # Open CRM - List view - with multiple group loaded, TODO
115- {
116-
117- },
118-
99+ {},
119100 # ------- project.task
120101 # Kanban: Open Framework Python project - No filters
121- {
122-
123- },
102+ {},
124103 # Kanban: Open Help project - No filter
125- {
126-
127- },
128- # Kanban:
129-
130-
104+ {},
105+ # Kanban:
131106]
132107
133108
134109def get_url (model , method ):
135110 return f"{ BASE_URL } /web/dataset/call_kw/RYV/{ model } /{ method } "
136111
112+
137113def get_default_json_data (model , method ):
138114 return {
139115 "id" : 13 , # balec
@@ -148,40 +124,50 @@ def get_default_json_data(model, method):
148124 "allowed_company_ids" : [1 ],
149125 "lang" : "en_US" ,
150126 "tz" : "Europe/Brussels" ,
127+ "read_group_expand" : True ,
151128 },
152129 },
153130 },
154131 }
155132
133+
156134def old_way (scenario , new_groups , with_issue = True ):
157- model = scenario [' model' ]
135+ model = scenario [" model" ]
158136 url = get_url (model , "web_read_group" )
159137 json_data = get_default_json_data (model , "web_read_group" )
160138
161- json_data ["params" ]["kwargs" ].update ({
162- 'domain' : scenario ['domain' ],
163- 'groupby' : scenario ['groupby' ],
164- 'aggregates' : scenario ['aggregates' ],
165- })
139+ json_data ["params" ]["kwargs" ].update (
140+ {
141+ "domain" : scenario ["domain" ],
142+ "groupby" : scenario ["groupby" ],
143+ "aggregates" : scenario ["aggregates" ],
144+ }
145+ )
166146
167147 start = time_ns ()
168148 res = requests .post (url , json = json_data , cookies = COOKIES )
169- delay = time_ns () - start
149+ delay_user = time_ns () - start
150+ worker_time = res .elapsed .total_seconds () * 1000
151+
170152 groups = res .json ()["result" ]["groups" ]
171153
172154 def web_search_read_data (group ):
173155 data = get_default_json_data (model , "web_search_read" )
174- data ["params" ]["kwargs" ].update ({
175- 'domain' : scenario ['domain' ] + group ['__extra_domain' ],
176- 'specification' : scenario ['read_specification' ],
177- 'count_limit' : 10001 if with_issue else scenario ['unfold_read_default_limit' ],
178- 'limit' : scenario ['unfold_read_default_limit' ],
179- })
156+ data ["params" ]["kwargs" ].update (
157+ {
158+ "domain" : scenario ["domain" ] + group ["__extra_domain" ],
159+ "specification" : scenario ["read_specification" ],
160+ "count_limit" : 10001
161+ if with_issue
162+ else scenario ["unfold_read_default_limit" ],
163+ "limit" : scenario ["unfold_read_default_limit" ],
164+ }
165+ )
180166 return data
181167
182168 unfold_groups = []
183169 for new_group , old_group in zip (new_groups , groups ):
184- if ' __records' in new_group :
170+ if " __records" in new_group :
185171 unfold_groups .append (old_group )
186172
187173 parallel_web_search_read = [
@@ -195,55 +181,63 @@ def web_search_read_data(group):
195181
196182 start = time_ns ()
197183 results = grequests .map (parallel_web_search_read , size = MAX_PARALLEL_REQUEST )
198- delay += time_ns () - start
184+ delay_user += time_ns () - start
199185
200186 for group , res in zip (unfold_groups , results ):
201187 group ["__records" ] = res .json ()["result" ]["records" ]
188+ worker_time += res .elapsed .total_seconds () * 1000
202189
203- return groups , delay / 1_000_000
190+ return groups , ( delay_user / 1_000_000 , worker_time )
204191
205192
206193def new_way (method , scenario ):
207- model = scenario [' model' ]
194+ model = scenario [" model" ]
208195 json_data = get_default_json_data (model , method )
209- json_data ["params" ]["kwargs" ].update ({
210- 'domain' : scenario ['domain' ],
211- 'groupby' : scenario ['groupby' ],
212- 'aggregates' : scenario ['aggregates' ],
213- 'unfold_read_specification' : scenario ['read_specification' ],
214- 'unfold_read_default_limit' : scenario ['unfold_read_default_limit' ],
215- 'auto_unfold' : True ,
216- })
196+ json_data ["params" ]["kwargs" ].update (
197+ {
198+ "domain" : scenario ["domain" ],
199+ "groupby" : scenario ["groupby" ],
200+ "aggregates" : scenario ["aggregates" ],
201+ "unfold_read_specification" : scenario ["read_specification" ],
202+ "unfold_read_default_limit" : scenario ["unfold_read_default_limit" ],
203+ "auto_unfold" : True ,
204+ }
205+ )
217206 start = time_ns ()
218207 res = requests .post (get_url (model , method ), json = json_data , cookies = COOKIES )
219- delay = time_ns () - start
220- return res .json ()["result" ]["groups" ], (delay / 1_000_000 )
208+ delay_user = time_ns () - start
209+ return res .json ()["result" ]["groups" ], (
210+ delay_user / 1_000_000 ,
211+ res .elapsed .total_seconds () * 1000 ,
212+ )
213+
221214
215+ NB_TEST = 8 # min 4
222216
223- NB_TEST = 10
224217
225218def time_test (method ):
226- res = []
219+ res_user_time = []
220+ res_worker_time = []
227221 for _ in range (NB_TEST ):
228- _ , delay = method ()
229- res .append (delay )
222+ _ , [delay_user , delay_worker ] = method ()
223+ res_user_time .append (delay_user )
224+ res_worker_time .append (delay_worker )
230225
231- res .sort ()
232- return res [2 :- 2 ] # Remove outlier
226+ return sorted (res_user_time )[:- (NB_TEST // 2 )], sorted (res_worker_time )[:- (NB_TEST // 2 )]
233227
234228
235229if __name__ == "__main__" :
236230 # Warmup
237- NB_WARMUP = 2
231+ NB_WARMUP = 1
238232 for i in range (NB_WARMUP ):
239- print (f"Warmup worker { i } /{ NB_WARMUP } : " )
233+ print (f"Warmup worker { i } /{ NB_WARMUP } " )
240234
241235 for scenario in SCENARIOS :
242236 if not scenario :
243237 continue
244- new_groups , _ = new_way ("web_read_group_unity " , scenario )
245- new_groups , _ = new_way ("web_read_group_unity " , scenario )
246- new_groups , _ = new_way ("web_read_group_unity " , scenario )
238+ new_groups , _ = new_way ("web_read_group_unity_trivial " , scenario )
239+ new_groups , _ = new_way ("web_read_group_unity_union_all_simple " , scenario )
240+ new_groups , _ = new_way ("web_read_group_unity_cte " , scenario )
247241 old_groups , _ = old_way (scenario , new_groups , with_issue = True )
248242 old_groups , _ = old_way (scenario , new_groups , with_issue = False )
249243
@@ -260,24 +254,23 @@ def time_test(method):
260254 for scenario in SCENARIOS :
261255 if not scenario :
262256 continue
263- new_groups , _ = new_way ("web_read_group_unity " , scenario )
257+ new_groups , _ = new_way ("web_read_group_unity_trivial " , scenario )
264258 to_test = [
265- (
266- "web_read_group_unity" ,
267- partial (new_way , "web_read_group_unity" , scenario ),
268- ),
269- ("Old way (Fixed)" , partial (old_way , scenario , new_groups , with_issue = False )),
270- ("Old way" , partial (old_way , scenario , new_groups , with_issue = True )),
259+ ("Old" , partial (old_way , scenario , new_groups , with_issue = True )),
260+ ("Old (Fixed)" , partial (old_way , scenario , new_groups , with_issue = False )),
261+ ("Uni Trivial" , partial (new_way , "web_read_group_unity_trivial" , scenario )),
262+ ("Uni Union All" , partial (new_way , "web_read_group_unity_union_all_simple" , scenario )),
263+ ("Uni CTE" , partial (new_way , "web_read_group_unity_cte" , scenario )),
271264 ]
272265 print (f"For { scenario ['name' ]} :" )
273266 for name , method in to_test :
274- res = time_test (method )
267+ user_times , worker_times = time_test (method )
275268
276- avg_res = fmean (res )
277- stdev_res = stdev (res )
278- max_res = max ( res )
279- min_res = min ( res )
269+ avg_user_times = fmean (user_times )
270+ stdev_user_times = stdev (user_times )
271+ avg_worker_times = fmean ( worker_times )
272+ stdev_worker_times = stdev ( worker_times )
280273
281274 print (
282- f"\t - { name } : { avg_res : .3f} +- { stdev_res : .3f} ms / min= { min_res : .3f} , max= { max_res : .3f} ms"
275+ f"\t { name :<15 } > User time : { avg_user_times :10 .3f} +- { stdev_user_times :8 .3f} ms | Worker time: { avg_worker_times :10 .3f} +- { stdev_worker_times :8 .3f} ms"
283276 )
0 commit comments