From ec4aa946771433d29cf43a8f27378bb4f4fb713e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vil=C3=A9m=20Duha?= <vilda.novak@gmail.com> Date: Tue, 26 Jan 2021 15:44:56 +0100 Subject: [PATCH] BlenderKit: fix search results storage Storing results in scenes increased file size too much. Now the search reasults are stored in window manager and thus not saved. Also this makes them shared between more scenes. --- blenderkit/__init__.py | 33 +++++++++++++++------------ blenderkit/asset_bar_op.py | 21 ++++++++--------- blenderkit/download.py | 10 ++++----- blenderkit/ratings.py | 4 ++-- blenderkit/search.py | 46 +++++++++++++++++++++++++++++--------- blenderkit/ui.py | 32 +++++++++++++------------- blenderkit/ui_panels.py | 14 ++++++------ blenderkit/upload.py | 20 ++++++++--------- blenderkit/utils.py | 14 ++++++------ 9 files changed, 110 insertions(+), 84 deletions(-) diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index 494b530e7..90a50e6e7 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -138,7 +138,10 @@ from bpy.types import ( @persistent def scene_load(context): + print('loading in background') + print(bpy.context.window_manager) if not bpy.app.background: + search.load_previews() ui_props = bpy.context.scene.blenderkitUI ui_props.assetbar_on = False @@ -249,37 +252,39 @@ thumbnail_resolutions = ( def udate_down_up(self, context): """Perform a search if results are empty.""" s = context.scene + wm = bpy.context.window_manager props = s.blenderkitUI - if s['search results'] == None and props.down_up == 'SEARCH': + if wm['search results'] == None and props.down_up == 'SEARCH': search.search() def switch_search_results(self, context): s = bpy.context.scene + wm = bpy.context.window_manager props = s.blenderkitUI if props.asset_type == 'MODEL': - s['search results'] = s.get('bkit model search') - s['search results orig'] = s.get('bkit model search orig') + wm['search results'] = wm.get('bkit model search') + wm['search results orig'] = wm.get('bkit model search orig') elif props.asset_type == 'SCENE': - s['search results'] = s.get('bkit scene search') - s['search results orig'] = s.get('bkit scene search orig') + wm['search results'] = wm.get('bkit scene search') + wm['search results orig'] = wm.get('bkit scene search orig') elif props.asset_type == 'HDR': - s['search results'] = s.get('bkit hdr search') - s['search results orig'] = s.get('bkit hdr search orig') + wm['search results'] = wm.get('bkit hdr search') + wm['search results orig'] = wm.get('bkit hdr search orig') elif props.asset_type == 'MATERIAL': - s['search results'] = s.get('bkit material search') - s['search results orig'] = s.get('bkit material search orig') + wm['search results'] = wm.get('bkit material search') + wm['search results orig'] = wm.get('bkit material search orig') elif props.asset_type == 'TEXTURE': - s['search results'] = s.get('bkit texture search') - s['search results orig'] = s.get('bkit texture search orig') + wm['search results'] = wm.get('bkit texture search') + wm['search results orig'] = wm.get('bkit texture search orig') elif props.asset_type == 'BRUSH': - s['search results'] = s.get('bkit brush search') - s['search results orig'] = s.get('bkit brush search orig') + wm['search results'] = wm.get('bkit brush search') + wm['search results orig'] = wm.get('bkit brush search orig') if not(context.sculpt_object or context.image_paint_object): ui.add_report( 'Switch to paint or sculpt mode to search in BlenderKit brushes.') search.load_previews() - if s['search results'] == None and props.down_up == 'SEARCH': + if wm['search results'] == None and props.down_up == 'SEARCH': search.search() diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py index 612f58852..2c445ad4b 100644 --- a/blenderkit/asset_bar_op.py +++ b/blenderkit/asset_bar_op.py @@ -25,8 +25,8 @@ from bpy.props import ( def draw_callback_tooltip(self, context): if self.draw_tooltip: - s = bpy.context.scene - sr = s.get('search results') + wm = bpy.context.window_manager + sr = wm.get('search results') r = sr[self.active_index] ui.draw_tooltip_with_author(r, 0, 500) @@ -269,7 +269,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.wcount = math.floor( (self.bar_width) / (self.button_size)) - search_results = bpy.context.scene.get('search results') + search_results = bpy.context.window_manager.get('search results') if search_results is not None and self.wcount > 0: self.hcount = min(user_preferences.max_assetbar_rows, math.ceil(len(search_results) / self.wcount)) else: @@ -311,8 +311,6 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.panel = BL_UI_Drag_Panel(0, 0, self.bar_width, self.bar_height) self.panel.bg_color = (0.0, 0.0, 0.0, 0.5) - sr = bpy.context.scene['search results'] - for a in range(0, self.wcount): for b in range(0, self.hcount): asset_x = self.assetbar_margin + a * (self.button_size) @@ -478,7 +476,8 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): if self.active_index != widget.search_index: scene = bpy.context.scene - sr = scene['search results'] + wm = bpy.context.window_manager + sr = wm['search results'] asset_data = sr[widget.search_index + self.scroll_offset] self.active_index = widget.search_index @@ -514,11 +513,11 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu') def search_more(self): - sro = bpy.context.scene.get('search results orig') + sro = bpy.context.window_manager.get('search results orig') if sro is not None and sro.get('next') is not None: blenderkit.search.search(get_next=True) def update_images(self): - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] for asset_button in self.asset_buttons: asset_button.asset_index = asset_button.button_index + self.scroll_offset @@ -548,7 +547,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): asset_button.validation_icon.visible = False def scroll_update(self): - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] self.scroll_offset = min(self.scroll_offset, len(sr) - (self.wcount * self.hcount)) self.scroll_offset = max(self.scroll_offset, 0) self.update_images() @@ -556,7 +555,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.search_more() def search_by_author(self, asset_index): - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[asset_index] a = asset_data['author']['id'] if a is not None: @@ -573,12 +572,10 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): return False def scroll_up(self, widget): - sr = bpy.context.scene['search results'] self.scroll_offset += self.wcount * self.hcount self.scroll_update() def scroll_down(self, widget): - sr = bpy.context.scene['search results'] self.scroll_offset -= self.wcount * self.hcount self.scroll_update() diff --git a/blenderkit/download.py b/blenderkit/download.py index 71cc6239e..7a233c6f5 100644 --- a/blenderkit/download.py +++ b/blenderkit/download.py @@ -280,7 +280,7 @@ def udpate_asset_data_in_dicts(asset_data): scene['assets rated'] = scene.get('assets rated', {}) id = asset_data['assetBaseId'] scene['assets rated'][id] = scene['assets rated'].get(id, False) - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] if not sr: return; for i, r in enumerate(sr): @@ -577,7 +577,7 @@ def timer_update(): downloaders = [] if t.is_alive(): # set downloader size - sr = bpy.context.scene.get('search results') + sr = bpy.context.window_manager.get('search results') if sr is not None: for r in sr: if asset_data['id'] == r['id']: @@ -646,8 +646,8 @@ def timer_update(): tcom.passargs['retry_counter'] = tcom.passargs.get('retry_counter', 0) + 1 download(asset_data, **tcom.passargs) - if bpy.context.scene['search results'] is not None and done: - for sres in bpy.context.scene['search results']: + if bpy.context.window_manager['search results'] is not None and done: + for sres in bpy.context.window_manager['search results']: if asset_data['id'] == sres['id']: sres['downloaded'] = 100 @@ -1282,7 +1282,7 @@ class BlenderkitDownloadOperator(bpy.types.Operator): if self.asset_index > -1: # either get the data from search results - sr = s['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ self.asset_index].to_dict() # TODO CHECK ALL OCCURRENCES OF PASSING BLENDER ID PROPS TO THREADS! asset_base_id = asset_data['assetBaseId'] diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py index 4ef194c69..86d204e29 100644 --- a/blenderkit/ratings.py +++ b/blenderkit/ratings.py @@ -429,7 +429,7 @@ class FastRateMenu(Operator): scene = bpy.context.scene ui_props = scene.blenderkitUI if ui_props.active_index > -1: - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = dict(sr[ui_props.active_index]) self.asset_id = asset_data['id'] self.asset_type = asset_data['assetType'] @@ -448,7 +448,7 @@ def rating_menu_draw(self, context): layout = self.layout ui_props = context.scene.blenderkitUI - sr = bpy.context.scene['search results orig'] + sr = bpy.context.window_manager['search results orig'] asset_search_index = ui_props.active_index if asset_search_index > -1: diff --git a/blenderkit/search.py b/blenderkit/search.py index 00f8eda00..e362a928d 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -144,6 +144,26 @@ def update_assets_data(): # updates assets data on scene load. # bpy.context.scene['assets used'][ad] = ad +def purge_search_results(): + ''' clean up search results on save/load.''' + + s = bpy.context.scene + + sr_props = [ + 'search results', + 'search results orig', + ] + print('purge search') + asset_types = ['model', 'material', 'scene', 'hdr', 'brush'] + for at in asset_types: + sr_props.append('bkit {at} search') + sr_props.append('bkit {at} search orig') + for sr_prop in sr_props: + if s.get(sr_prop): + print(sr_prop) + del (s[sr_prop]) + + @persistent def scene_load(context): ''' @@ -151,6 +171,7 @@ def scene_load(context): Should (probably)also update asset data from server (after user consent) ''' wm = bpy.context.window_manager + purge_search_results() fetch_server_data() categories.load_categories() if not bpy.app.timers.is_registered(refresh_token_timer): @@ -323,7 +344,7 @@ def timer_update(): first_time = False if preferences.show_on_start: # TODO here it should check if there are some results, and only open assetbar if this is the case, not search. - # if bpy.context.scene.get('search results') is None: + # if bpy.context.window_manager.get('search results') is None: search() # preferences.first_run = False if preferences.tips_on_start: @@ -353,7 +374,7 @@ def timer_update(): icons_dir = thread[1] scene = bpy.context.scene # these 2 lines should update the previews enum and set the first result as active. - s = bpy.context.scene + wm = bpy.context.window_manager asset_type = thread[2] if asset_type == 'model': props = scene.blenderkit_models @@ -372,7 +393,7 @@ def timer_update(): # json_filepath = os.path.join(icons_dir, 'brush_searchresult.json') search_name = f'bkit {asset_type} search' - s[search_name] = [] + wm[search_name] = [] global reports if reports != '': @@ -384,6 +405,7 @@ def timer_update(): result_field = [] ok, error = check_errors(rdata) if ok: + bpy.ops.object.run_assetbar_fix_context() for r in rdata['results']: asset_data = parse_result(r) @@ -391,10 +413,10 @@ def timer_update(): result_field.append(asset_data) # results = rdata['results'] - s[search_name] = result_field - s['search results'] = result_field - s[search_name + ' orig'] = copy.deepcopy(rdata) - s['search results orig'] = s[search_name + ' orig'] + wm[search_name] = result_field + wm['search results'] = result_field + wm[search_name + ' orig'] = copy.deepcopy(rdata) + wm['search results orig'] = wm[search_name + ' orig'] load_previews() ui_props = bpy.context.scene.blenderkitUI @@ -402,8 +424,8 @@ def timer_update(): ui_props.scrolloffset = 0 props.is_searching = False props.search_error = False - props.report = 'Found %i results. ' % (s['search results orig']['count']) - if len(s['search results']) == 0: + props.report = 'Found %i results. ' % (wm['search results orig']['count']) + if len(wm['search results']) == 0: tasks_queue.add_task((ui.add_report, ('No matching results found.',))) # undo push bpy.ops.wm.undo_push_context(message='Get BlenderKit search') @@ -425,7 +447,7 @@ def load_previews(): props = scene.blenderkitUI directory = paths.get_temp_dir('%s_search' % props.asset_type.lower()) s = bpy.context.scene - results = s.get('search results') + results = bpy.context.window_manager.get('search results') # if results is not None: inames = [] @@ -664,6 +686,8 @@ def generate_tooltip(mdata): t += f"Quality rating: {int(mdata['ratingsAverage']['quality']) * '*'}\n" t += f"Hours saved rating: {int(mdata['ratingsAverage']['workingHours'])}\n" if utils.profile_is_validator(): + t += f"Score: {int(mdata['score'])}\n" + t += f"Ratings count {rc['quality']}*/{rc['workingHours']}wh value " \ f"{mdata['ratingsAverage']['quality']}*/{mdata['ratingsAverage']['workingHours']}wh\n" if len(t.split('\n')) < 11: @@ -1311,7 +1335,7 @@ def get_search_simple(parameters, filepath=None, page_size=100, max_results=1000 if not filepath: return results - with open(filepath, 'w', encoding = 'utf-8') as s: + with open(filepath, 'w', encoding='utf-8') as s: json.dump(results, s, ensure_ascii=False, indent=4) bk_logger.info(f'retrieved {len(results)} assets from elastic search') return results diff --git a/blenderkit/ui.py b/blenderkit/ui.py index 8d39b56f7..844d87c5b 100644 --- a/blenderkit/ui.py +++ b/blenderkit/ui.py @@ -138,10 +138,11 @@ class Report(): def get_asset_under_mouse(mousex, mousey): s = bpy.context.scene + wm = bpy.context.window_manager ui_props = bpy.context.scene.blenderkitUI r = bpy.context.region - search_results = s.get('search results') + search_results = wm.get('search results') if search_results is not None: h_draw = min(ui_props.hcount, math.ceil(len(search_results) / ui_props.wcount)) @@ -741,8 +742,8 @@ def draw_asset_bar(self, context): # img, # 1) if not ui_props.dragging and ui_props.hcount > 0 and ui_props.wcount > 0: - search_results = s.get('search results') - search_results_orig = s.get('search results orig') + search_results = bpy.context.window_manager.get('search results') + search_results_orig = bpy.context.window_manager.get('search results orig') if search_results == None: return h_draw = min(ui_props.hcount, math.ceil(len(search_results) / ui_props.wcount)) @@ -1078,10 +1079,9 @@ def mouse_in_area(mx, my, x, y, w, h): def mouse_in_asset_bar(mx, my): - s = bpy.context.scene ui_props = bpy.context.scene.blenderkitUI - # search_results = s.get('search results') + # search_results = bpy.context.window_manager.get('search results') # if search_results == None: # return False # @@ -1127,7 +1127,7 @@ def update_ui_size(area, region): ui.wcount = math.floor( (ui.bar_width - 2 * ui.drawoffset) / (ui.thumb_size + ui.margin)) - search_results = bpy.context.scene.get('search results') + search_results = bpy.context.window_manager.get('search results') if search_results != None and ui.wcount > 0: ui.hcount = min(user_preferences.max_assetbar_rows, math.ceil(len(search_results) / ui.wcount)) else: @@ -1220,7 +1220,7 @@ class AssetBarOperator(bpy.types.Operator): return properties.tooltip def search_more(self): - sro = bpy.context.scene.get('search results orig') + sro = bpy.context.window_manager.get('search results orig') if sro is not None and sro.get('next') is not None: search.search(get_next=True) @@ -1336,8 +1336,8 @@ class AssetBarOperator(bpy.types.Operator): # TODO add one more condition here to take less performance. r = self.region s = bpy.context.scene - sr = s.get('search results') - search_results_orig = s.get('search results orig') + sr = bpy.context.window_manager.get('search results') + search_results_orig = bpy.context.window_manager.get('search results orig') # If there aren't any results, we need no interaction(yet) if sr is None: return {'PASS_THROUGH'} @@ -1424,7 +1424,7 @@ class AssetBarOperator(bpy.types.Operator): bpy.context.window.cursor_set("DEFAULT") return {'PASS_THROUGH'} - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] if not ui_props.dragging: bpy.context.window.cursor_set("DEFAULT") @@ -1672,7 +1672,7 @@ class AssetBarOperator(bpy.types.Operator): return {'RUNNING_MODAL'} if event.type == 'W' and ui_props.active_index > -1: - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] a = bpy.context.window_manager['bkit authors'].get(asset_data['author']['id']) if a is not None: @@ -1681,7 +1681,7 @@ class AssetBarOperator(bpy.types.Operator): bpy.ops.wm.url_open(url=a['aboutMeUrl']) return {'RUNNING_MODAL'} if event.type == 'A' and ui_props.active_index > -1: - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] a = asset_data['author']['id'] if a is not None: @@ -1694,7 +1694,7 @@ class AssetBarOperator(bpy.types.Operator): if event.type == 'X' and ui_props.active_index > -1: # delete downloaded files for this asset - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] bk_logger.info('delete asset from local drive:' + asset_data['name']) paths.delete_asset_debug(asset_data) @@ -1734,9 +1734,9 @@ class AssetBarOperator(bpy.types.Operator): ui_props.assetbar_on = True ui_props.turn_off = False - sr = bpy.context.scene.get('search results') + sr = bpy.context.window_manager.get('search results') if sr is None: - bpy.context.scene['search results'] = [] + bpy.context.window_manager['search results'] = [] if context.area.type != 'VIEW_3D': self.report({'WARNING'}, "View3D not found, cannot run operator") @@ -2003,7 +2003,7 @@ class AssetDragOperator(bpy.types.Operator): object = None self.matrix = None - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] self.asset_data = sr[self.asset_search_index] context.window_manager.modal_handler_add(self) diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index 921d04530..deb3c0bb0 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -1244,7 +1244,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): op.invoke_resolution = True o = utils.get_active_model() if o and o.get('asset_data'): - if o['asset_data']['assetBaseId'] == bpy.context.scene['search results'][ui_props.active_index]: + if o['asset_data']['assetBaseId'] == bpy.context.window_manager['search results'][ui_props.active_index]: op.model_location = o.location op.model_rotation = o.rotation_euler else: @@ -1339,9 +1339,9 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): # def draw(self, context): # ui_props = context.scene.blenderkitUI # -# # sr = bpy.context.scene['search results'] +# # sr = bpy.context.window_manager['search results'] # -# # sr = bpy.context.scene['search results'] +# # sr = bpy.context.window_manager['search results'] # # asset_data = sr[ui_props.active_index] # # for k in resolutions.resolution_props_to_server.keys(): @@ -1355,13 +1355,13 @@ class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu): def draw(self, context): ui_props = context.scene.blenderkitUI - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] draw_asset_context_menu(self.layout, context, asset_data, from_panel=False) # ui_props = context.scene.blenderkitUI # - # sr = bpy.context.scene['search results'] + # sr = bpy.context.window_manager['search results'] # asset_data = sr[ui_props.active_index] # layout = self.layout # row = layout.row() @@ -1403,7 +1403,7 @@ class AssetPopupCard(bpy.types.Operator): def draw(self, context): ui_props = context.scene.blenderkitUI - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] layout = self.layout row = layout.row() @@ -1442,7 +1442,7 @@ class AssetPopupCard(bpy.types.Operator): wm = context.window_manager ui_props = context.scene.blenderkitUI ui_props.draw_tooltip = False - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = sr[ui_props.active_index] self.img = ui.get_large_thumbnail_image(asset_data) # self.tex = utils.get_hidden_texture(self.img) diff --git a/blenderkit/upload.py b/blenderkit/upload.py index 2cd23f727..8a905cecc 100644 --- a/blenderkit/upload.py +++ b/blenderkit/upload.py @@ -524,8 +524,8 @@ def patch_individual_metadata(asset_id, metadata_dict, api_key): # layout = self.layout # ui_props = context.scene.blenderkitUI # -# # sr = bpy.context.scene['search results'] -# sr = bpy.context.scene['search results'] +# # sr = bpy.context.window_manager['search results'] +# sr = bpy.context.window_manager['search results'] # asset_data = sr[ui_props.active_index] # categories = bpy.context.window_manager['bkit_categories'] # wm = bpy.context.win @@ -654,10 +654,10 @@ class FastMetadata(bpy.types.Operator): scene = bpy.context.scene ui_props = scene.blenderkitUI if ui_props.active_index > -1: - sr = bpy.context.scene['search results'] + sr = bpy.context.window_manager['search results'] asset_data = dict(sr[ui_props.active_index]) else: - for result in bpy.context.scene['search results']: + for result in bpy.context.window_manager['search results']: if result['id'] == self.asset_id: asset_data = dict(result) @@ -1196,12 +1196,12 @@ class AssetDebugPrint(Operator): def execute(self, context): preferences = bpy.context.preferences.addons['blenderkit'].preferences - if not bpy.context.scene['search results']: + if not bpy.context.window_manager['search results']: print('no search results found') return {'CANCELLED'}; # update status in search results for validator's clarity - sr = bpy.context.scene['search results'] - sro = bpy.context.scene['search results orig']['results'] + sr = bpy.context.window_manager['search results'] + sro = bpy.context.window_manager['search results orig']['results'] result = None for r in sr: @@ -1250,11 +1250,11 @@ class AssetVerificationStatusChange(Operator): def execute(self, context): preferences = bpy.context.preferences.addons['blenderkit'].preferences - if not bpy.context.scene['search results']: + if not bpy.context.window_manager['search results']: return {'CANCELLED'}; # update status in search results for validator's clarity - sr = bpy.context.scene['search results'] - sro = bpy.context.scene['search results orig']['results'] + sr = bpy.context.window_manager['search results'] + sro = bpy.context.window_manager['search results orig']['results'] for r in sr: if r['id'] == self.asset_id: diff --git a/blenderkit/utils.py b/blenderkit/utils.py index 94f795c14..adf5bd03f 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -780,13 +780,13 @@ def get_fake_context(context, area_type='VIEW_3D'): C_dict = {} # context.copy() #context.copy was a source of problems - incompatibility with addons that also define context C_dict.update(region='WINDOW') - try: - context = context.copy() - # print('bk context copied successfully') - except Exception as e: - print(e) - print('BlenderKit: context.copy() failed. Can be a colliding addon.') - context = {} + # try: + # context = context.copy() + # # print('bk context copied successfully') + # except Exception as e: + # print(e) + # print('BlenderKit: context.copy() failed. Can be a colliding addon.') + context = {} if context.get('area') is None or context.get('area').type != area_type: w, a, r = get_largest_area(area_type=area_type) -- GitLab