Cleanup: simplify file name incrementing logic
[blender-addons.git] / blenderkit / ratings.py
blobc273959fa4efd71a590c698758f4b9b326e908e3
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 from blenderkit import paths, utils, rerequests, tasks_queue
21 import bpy
22 import requests, threading
23 import logging
25 bk_logger = logging.getLogger('blenderkit')
27 from bpy.props import (
28 IntProperty,
29 FloatProperty,
30 StringProperty,
31 EnumProperty,
32 BoolProperty,
33 PointerProperty,
35 from bpy.types import (
36 Operator,
37 Panel,
41 def pretty_print_POST(req):
42 """
43 pretty print a request
44 """
45 print('{}\n{}\n{}\n\n{}'.format(
46 '-----------START-----------',
47 req.method + ' ' + req.url,
48 '\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
49 req.body,
53 def upload_rating_thread(url, ratings, headers):
54 ''' Upload rating thread function / disconnected from blender data.'''
55 bk_logger.debug('upload rating ' + url + str(ratings))
56 for rating_name, score in ratings:
57 if (score != -1 and score != 0):
58 rating_url = url + rating_name + '/'
59 data = {
60 "score": score, # todo this kind of mixing is too much. Should have 2 bkit structures, upload, use
63 try:
64 r = rerequests.put(rating_url, data=data, verify=True, headers=headers)
66 except requests.exceptions.RequestException as e:
67 print('ratings upload failed: %s' % str(e))
70 def send_rating_to_thread_quality(url, ratings, headers):
71 '''Sens rating into thread rating, main purpose is for tasks_queue.
72 One function per property to avoid lost data due to stashing.'''
73 thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
74 thread.start()
77 def send_rating_to_thread_work_hours(url, ratings, headers):
78 '''Sens rating into thread rating, main purpose is for tasks_queue.
79 One function per property to avoid lost data due to stashing.'''
80 thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
81 thread.start()
84 def upload_review_thread(url, reviews, headers):
85 r = rerequests.put(url, data=reviews, verify=True, headers=headers)
87 # except requests.exceptions.RequestException as e:
88 # print('reviews upload failed: %s' % str(e))
91 def get_rating(asset_id):
92 # this function isn't used anywhere,should probably get removed.
93 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
94 api_key = user_preferences.api_key
95 headers = utils.get_headers(api_key)
96 rl = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
97 rtypes = ['quality', 'working_hours']
98 for rt in rtypes:
99 params = {
100 'rating_type': rt
102 r = rerequests.get(r1, params=data, verify=True, headers=headers)
103 print(r.text)
106 def update_ratings_quality(self, context):
107 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
108 api_key = user_preferences.api_key
110 headers = utils.get_headers(api_key)
111 asset = self.id_data
112 if asset:
113 bkit_ratings = asset.bkit_ratings
114 url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
115 else:
116 # this part is for operator rating:
117 bkit_ratings = self
118 url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
120 if bkit_ratings.rating_quality > 0.1:
121 ratings = [('quality', bkit_ratings.rating_quality)]
122 tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True)
125 def update_ratings_work_hours(self, context):
126 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
127 api_key = user_preferences.api_key
128 headers = utils.get_headers(api_key)
129 asset = self.id_data
130 if asset:
131 bkit_ratings = asset.bkit_ratings
132 url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
133 else:
134 # this part is for operator rating:
135 bkit_ratings = self
136 url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
138 if bkit_ratings.rating_work_hours > 0.45:
139 ratings = [('working_hours', round(bkit_ratings.rating_work_hours, 1))]
140 tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True)
143 def upload_rating(asset):
144 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
145 api_key = user_preferences.api_key
146 headers = utils.get_headers(api_key)
148 bkit_ratings = asset.bkit_ratings
149 # print('rating asset', asset_data['name'], asset_data['assetBaseId'])
150 url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/rating/'
152 ratings = [
156 if bkit_ratings.rating_quality > 0.1:
157 ratings = (('quality', bkit_ratings.rating_quality),)
158 tasks_queue.add_task((send_rating_to_thread_quality, (url, ratings, headers)), wait=2.5, only_last=True)
159 if bkit_ratings.rating_work_hours > 0.1:
160 ratings = (('working_hours', round(bkit_ratings.rating_work_hours, 1)),)
161 tasks_queue.add_task((send_rating_to_thread_work_hours, (url, ratings, headers)), wait=2.5, only_last=True)
163 thread = threading.Thread(target=upload_rating_thread, args=(url, ratings, headers))
164 thread.start()
166 url = paths.get_api_url() + 'assets/' + asset['asset_data']['id'] + '/review'
168 reviews = {
169 'reviewText': bkit_ratings.rating_compliments,
170 'reviewTextProblems': bkit_ratings.rating_problems,
172 if not (bkit_ratings.rating_compliments == '' and bkit_ratings.rating_compliments == ''):
173 thread = threading.Thread(target=upload_review_thread, args=(url, reviews, headers))
174 thread.start()
176 # the info that the user rated an item is stored in the scene
177 s = bpy.context.scene
178 s['assets rated'] = s.get('assets rated', {})
179 if bkit_ratings.rating_quality > 0.1 and bkit_ratings.rating_work_hours > 0.1:
180 s['assets rated'][asset['asset_data']['assetBaseId']] = True
183 def get_assets_for_rating():
185 gets assets from scene that could/should be rated by the user.
186 TODO this is only a draft.
189 assets = []
190 for ob in bpy.context.scene.objects:
191 if ob.get('asset_data'):
192 assets.append(ob)
193 for m in bpy.data.materials:
194 if m.get('asset_data'):
195 assets.append(m)
196 for b in bpy.data.brushes:
197 if b.get('asset_data'):
198 assets.append(b)
199 return assets
202 asset_types = (
203 ('MODEL', 'Model', 'set of objects'),
204 ('SCENE', 'Scene', 'scene'),
205 ('HDR', 'HDR', 'hdr'),
206 ('MATERIAL', 'Material', 'any .blend Material'),
207 ('TEXTURE', 'Texture', 'a texture, or texture set'),
208 ('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
209 ('ADDON', 'Addon', 'addnon'),
213 # TODO drop this operator, not needed anymore.
214 class UploadRatingOperator(bpy.types.Operator):
215 """Upload rating to the web db"""
216 bl_idname = "object.blenderkit_rating_upload"
217 bl_label = "Send Rating"
218 bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
220 # type of upload - model, material, textures, e.t.c.
221 # asset_type: EnumProperty(
222 # name="Type",
223 # items=asset_types,
224 # description="Type of asset",
225 # default="MODEL",
228 # @classmethod
229 # def poll(cls, context):
230 # return bpy.context.active_object != None and bpy.context.active_object.get('asset_id') is not None
231 def draw(self, context):
232 layout = self.layout
233 layout.label(text='Rating sent to server. Thanks for rating!')
235 def execute(self, context):
236 return {'FINISHED'}
238 def invoke(self, context, event):
239 wm = context.window_manager
240 asset = utils.get_active_asset()
241 upload_rating(asset)
242 return wm.invoke_props_dialog(self)
245 def stars_enum_callback(self, context):
246 '''regenerates the enum property used to display rating stars, so that there are filled/empty stars correctly.'''
247 items = []
248 for a in range(0, 10):
249 if self.rating_quality < a + 1:
250 icon = 'SOLO_OFF'
251 else:
252 icon = 'SOLO_ON'
253 # has to have something before the number in the value, otherwise fails on registration.
254 items.append((f'{a + 1}', f'{a + 1}', '', icon, a + 1))
255 return items
258 def update_quality_ui(self, context):
259 '''Converts the _ui the enum into actual quality number.'''
260 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
261 if user_preferences.api_key == '':
262 # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
263 # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
264 # return
265 bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
266 message='Please login/signup to rate assets. Clicking OK takes you to web login.')
267 # self.rating_quality_ui = '0'
268 self.rating_quality = int(self.rating_quality_ui)
271 def update_ratings_work_hours_ui(self, context):
272 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
273 if user_preferences.api_key == '':
274 # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
275 # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
276 # return
277 bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
278 message='Please login/signup to rate assets. Clicking OK takes you to web login.')
279 # self.rating_work_hours_ui = '0'
280 self.rating_work_hours = float(self.rating_work_hours_ui)
283 def update_ratings_work_hours_ui_1_5(self, context):
284 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
285 if user_preferences.api_key == '':
286 # ui_panels.draw_not_logged_in(self, message='Please login/signup to rate assets.')
287 # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_login_menu')
288 # return
289 bpy.ops.wm.blenderkit_login('INVOKE_DEFAULT',
290 message='Please login/signup to rate assets. Clicking OK takes you to web login.')
291 # self.rating_work_hours_ui_1_5 = '0'
292 # print('updating 1-5')
293 # print(float(self.rating_work_hours_ui_1_5))
294 self.rating_work_hours = float(self.rating_work_hours_ui_1_5)
297 class FastRateMenu(Operator):
298 """Fast rating of the assets directly in the asset bar - without need to download assets"""
299 bl_idname = "wm.blenderkit_menu_rating_upload"
300 bl_label = "Rate asset"
301 bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
303 message: StringProperty(
304 name="message",
305 description="message",
306 default="Rating asset",
307 options={'SKIP_SAVE'})
309 asset_id: StringProperty(
310 name="Asset Base Id",
311 description="Unique id of the asset (hidden)",
312 default="",
313 options={'SKIP_SAVE'})
315 asset_name: StringProperty(
316 name="Asset Name",
317 description="Name of the asset (hidden)",
318 default="",
319 options={'SKIP_SAVE'})
321 asset_type: StringProperty(
322 name="Asset type",
323 description="asset type",
324 default="",
325 options={'SKIP_SAVE'})
327 rating_quality: IntProperty(name="Quality",
328 description="quality of the material",
329 default=0,
330 min=-1, max=10,
331 # update=update_ratings_quality,
332 options={'SKIP_SAVE'})
334 # the following enum is only to ease interaction - enums support 'drag over' and enable to draw the stars easily.
335 rating_quality_ui: EnumProperty(name='rating_quality_ui',
336 items=stars_enum_callback,
337 description='Rating stars 0 - 10',
338 default=0,
339 update=update_quality_ui,
340 options={'SKIP_SAVE'})
342 rating_work_hours: FloatProperty(name="Work Hours",
343 description="How many hours did this work take?",
344 default=0.00,
345 min=0.0, max=300,
346 # update=update_ratings_work_hours,
347 options={'SKIP_SAVE'}
350 high_rating_warning = "This is a high rating, please be sure to give such rating only to amazing assets"
352 rating_work_hours_ui: EnumProperty(name="Work Hours",
353 description="How many hours did this work take?",
354 items=[('0', '0', ''),
355 ('.5', '0.5', ''),
356 ('1', '1', ''),
357 ('2', '2', ''),
358 ('3', '3', ''),
359 ('4', '4', ''),
360 ('5', '5', ''),
361 ('6', '6', ''),
362 ('8', '8', ''),
363 ('10', '10', ''),
364 ('15', '15', ''),
365 ('20', '20', ''),
366 ('30', '30', high_rating_warning),
367 ('50', '50', high_rating_warning),
368 ('100', '100', high_rating_warning),
369 ('150', '150', high_rating_warning),
370 ('200', '200', high_rating_warning),
371 ('250', '250', high_rating_warning),
373 default='0', update=update_ratings_work_hours_ui,
374 options = {'SKIP_SAVE'}
377 rating_work_hours_ui_1_5: EnumProperty(name="Work Hours",
378 description="How many hours did this work take?",
379 items=[('0', '0', ''),
380 ('.2', '0.2', ''),
381 ('.5', '0.5', ''),
382 ('1', '1', ''),
383 ('2', '2', ''),
384 ('3', '3', ''),
385 ('4', '4', ''),
386 ('5', '5', '')
388 default='0',
389 update=update_ratings_work_hours_ui_1_5,
390 options = {'SKIP_SAVE'}
393 @classmethod
394 def poll(cls, context):
395 # scene = bpy.context.scene
396 # ui_props = scene.blenderkitUI
397 return True # ui_props.active_index > -1
399 def draw(self, context):
400 layout = self.layout
401 col = layout.column()
403 # layout.template_icon_view(bkit_ratings, property, show_labels=False, scale=6.0, scale_popup=5.0)
404 col.label(text=self.message)
405 row = col.row()
406 row.prop(self, 'rating_quality_ui', expand=True, icon_only=True, emboss=False)
407 # row.label(text=str(self.rating_quality))
408 col.separator()
410 row = layout.row()
411 row.label(text=f"How many hours did this {self.asset_type} save you?")
413 if self.asset_type in ('model', 'scene'):
414 row = layout.row()
415 if utils.profile_is_validator():
416 col.prop(self, 'rating_work_hours')
417 row.prop(self, 'rating_work_hours_ui', expand=True, icon_only=False, emboss=True)
418 if float(self.rating_work_hours_ui) > 100:
419 utils.label_multiline(layout,
420 text=f"\nThat's huge! please be sure to give such rating only to godly {self.asset_type}s.\n",
421 width=500)
422 elif float(self.rating_work_hours_ui) > 18:
423 layout.separator()
425 utils.label_multiline(layout,
426 text=f"\nThat's a lot! please be sure to give such rating only to amazing {self.asset_type}s.\n",
427 width=500)
429 else:
432 row = layout.row()
433 row.prop(self, 'rating_work_hours_ui_1_5', expand=True, icon_only=False, emboss=True)
436 def execute(self, context):
437 user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
438 api_key = user_preferences.api_key
439 headers = utils.get_headers(api_key)
441 url = paths.get_api_url() + f'assets/{self.asset_id}/rating/'
443 rtgs = [
447 if self.rating_quality_ui == '':
448 self.rating_quality = 0
449 else:
450 self.rating_quality = float(self.rating_quality_ui)
452 if self.rating_quality > 0.1:
453 rtgs = (('quality', self.rating_quality),)
454 tasks_queue.add_task((send_rating_to_thread_quality, (url, rtgs, headers)), wait=2.5, only_last=True)
456 if self.rating_work_hours > 0.45:
457 rtgs = (('working_hours', round(self.rating_work_hours, 1)),)
458 tasks_queue.add_task((send_rating_to_thread_work_hours, (url, rtgs, headers)), wait=2.5, only_last=True)
459 return {'FINISHED'}
461 def invoke(self, context, event):
462 scene = bpy.context.scene
463 ui_props = scene.blenderkitUI
464 if ui_props.active_index > -1:
465 sr = bpy.context.window_manager['search results']
466 asset_data = dict(sr[ui_props.active_index])
467 self.asset_id = asset_data['id']
468 self.asset_type = asset_data['assetType']
470 self.message = f"Rate asset {self.asset_name}"
471 wm = context.window_manager
473 if self.asset_type in ('model','scene'):
474 # spawn a wider one for validators for the enum buttons
475 return wm.invoke_props_dialog(self, width=500)
476 else:
477 return wm.invoke_props_dialog(self)
480 def rating_menu_draw(self, context):
481 layout = self.layout
483 ui_props = context.scene.blenderkitUI
484 sr = bpy.context.window_manager['search results orig']
486 asset_search_index = ui_props.active_index
487 if asset_search_index > -1:
488 asset_data = dict(sr['results'][asset_search_index])
490 col = layout.column()
491 layout.label(text='Admin rating Tools:')
492 col.operator_context = 'INVOKE_DEFAULT'
494 op = col.operator('wm.blenderkit_menu_rating_upload', text='Rate')
495 op.asset_id = asset_data['id']
496 op.asset_name = asset_data['name']
497 op.asset_type = asset_data['assetType']
500 def register_ratings():
501 bpy.utils.register_class(UploadRatingOperator)
502 bpy.utils.register_class(FastRateMenu)
503 # bpy.types.OBJECT_MT_blenderkit_asset_menu.append(rating_menu_draw)
506 def unregister_ratings():
507 pass;
508 # bpy.utils.unregister_class(StarRatingOperator)
509 bpy.utils.unregister_class(UploadRatingOperator)
510 bpy.utils.unregister_class(FastRateMenu)