Coverage for src/backend/InvenTree/part/helpers.py: 82%

34 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""Various helper functions for the part app.""" 

2 

3import os 

4 

5from django.conf import settings 

6 

7import structlog 

8from jinja2.sandbox import SandboxedEnvironment 

9 

10from common.settings import get_global_setting 

11 

12logger = structlog.get_logger('inventree') 

13 

14 

15# Compiled template for rendering the 'full_name' attribute of a Part 

16_part_full_name_template = None 

17_part_full_name_template_string = '' 

18 

19 

20def compile_full_name_template(*args, **kwargs): 

21 """Recompile the template for rendering the 'full_name' attribute of a Part. 

22 

23 This function is called whenever the 'PART_NAME_FORMAT' setting is changed. 

24 """ 

25 global _part_full_name_template 

26 global _part_full_name_template_string 

27 

28 template_string = get_global_setting('PART_NAME_FORMAT', cache=True) 

29 

30 # Skip if the template string has not changed 

31 if ( 

32 template_string == _part_full_name_template_string 

33 and _part_full_name_template is not None 

34 ): 

35 return _part_full_name_template 

36 

37 # Cache the template string 

38 _part_full_name_template_string = template_string 

39 

40 env = SandboxedEnvironment(variable_start_string='{{', variable_end_string='}}') 

41 

42 # Compile the template 

43 try: 

44 _part_full_name_template = env.from_string(template_string) 

45 except Exception: 

46 _part_full_name_template = None 

47 

48 return _part_full_name_template 

49 

50 

51def render_part_full_name(part) -> str: 

52 """Render the 'full_name' attribute of a Part. 

53 

54 To improve render efficiency, we re-compile the template whenever the setting is changed 

55 

56 Args: 

57 part: The Part object to render 

58 """ 

59 template = compile_full_name_template() 

60 

61 if template: 61 ↛ 72line 61 didn't jump to line 72 because the condition on line 61 was always true

62 try: 

63 return template.render(part=part) 

64 except Exception as e: 

65 logger.warning( 

66 'exception while trying to create full name for part %s: %s', 

67 part.name, 

68 e, 

69 ) 

70 

71 # Fallback to the default format 

72 elements = [el for el in [part.IPN, part.name, part.revision] if el] 

73 return ' | '.join(elements) 

74 

75 

76# Subdirectory for storing part images 

77PART_IMAGE_DIR = 'part_images' 

78 

79 

80def get_part_image_directory() -> str: 

81 """Return the directory where part images are stored. 

82 

83 Returns: 

84 str: Directory where part images are stored 

85 

86 TODO: Future work may be needed here to support other storage backends, such as S3 

87 """ 

88 part_image_directory = os.path.abspath( 

89 os.path.join(settings.MEDIA_ROOT, PART_IMAGE_DIR) 

90 ) 

91 

92 # Create the directory if it does not exist 

93 if not os.path.exists(part_image_directory): 

94 os.makedirs(part_image_directory) 

95 

96 return part_image_directory