Coverage for app/venv/lib/python3.14/site-packages/weblate/trans/management/commands/import_json.py: 0%

83 statements  

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

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4from __future__ import annotations 

5 

6import json 

7from pathlib import Path 

8from typing import TYPE_CHECKING 

9 

10from django.core.exceptions import ValidationError 

11from django.core.management.base import CommandError 

12from django.utils.text import slugify 

13 

14from weblate.trans.models import Component, Project 

15from weblate.utils.management.base import BaseCommand 

16 

17if TYPE_CHECKING: 

18 from django.core.management.base import CommandParser 

19 

20 

21class Command(BaseCommand): 

22 """Command for mass importing of repositories into Weblate based on JSON data.""" 

23 

24 help = "imports projects based on JSON data" 

25 

26 def add_arguments(self, parser: CommandParser) -> None: 

27 super().add_arguments(parser) 

28 parser.add_argument( 

29 "--project", default=None, required=True, help="Project where to operate" 

30 ) 

31 parser.add_argument( 

32 "--ignore", 

33 default=False, 

34 action="store_true", 

35 help="Ignore already existing entries", 

36 ) 

37 parser.add_argument( 

38 "--update", 

39 default=False, 

40 action="store_true", 

41 help="Update already existing entries", 

42 ) 

43 parser.add_argument( 

44 "--main-component", 

45 default=None, 

46 help="Define which component will be used as main for the VCS repository", 

47 ) 

48 parser.add_argument( 

49 "json-file", 

50 type=Path, 

51 help="JSON file containing component definition", 

52 ) 

53 

54 def handle(self, *args, **options) -> None: # noqa: C901 

55 """Automatic import of components.""" 

56 # Get project 

57 try: 

58 project = Project.objects.get(slug=options["project"]) 

59 except Project.DoesNotExist as error: 

60 msg = "Project does not exist!" 

61 raise CommandError(msg) from error 

62 

63 # Get main component 

64 main_component = None 

65 if options["main_component"]: 

66 try: 

67 main_component = Component.objects.get( 

68 project=project, slug=options["main_component"] 

69 ) 

70 except Component.DoesNotExist as error: 

71 msg = "Main component does not exist!" 

72 raise CommandError(msg) from error 

73 try: 

74 with options["json-file"].open("r") as handle: 

75 try: 

76 data = json.load(handle) 

77 except json.JSONDecodeError as error: 

78 msg = "Could not parse JSON file!" 

79 raise CommandError(msg) from error 

80 except OSError as error: 

81 msg = f"Could not open file: {error}" 

82 raise CommandError(msg) from error 

83 

84 allfields = { 

85 field.name 

86 for field in Component._meta.get_fields() # noqa: SLF001 

87 if field.editable and not field.is_relation 

88 } 

89 

90 # Handle dumps from API 

91 if "results" in data: 

92 data = data["results"] 

93 

94 for item in data: 

95 if "filemask" not in item or "name" not in item: 

96 msg = "Missing required fields in JSON!" 

97 raise CommandError(msg) 

98 

99 if "slug" not in item: 

100 item["slug"] = slugify(item["name"]) 

101 

102 if "repo" not in item: 

103 if main_component is None: 

104 msg = "No main component and no repository URL!" 

105 raise CommandError(msg) 

106 item["repo"] = main_component.get_repo_link_url() 

107 

108 try: 

109 component = Component.objects.get(slug=item["slug"], project=project) 

110 except Component.DoesNotExist: 

111 params = {key: item[key] for key in allfields if key in item} 

112 component = Component(project=project, **params) 

113 try: 

114 component.full_clean() 

115 except ValidationError as error: 

116 for key, value in error.message_dict.items(): 

117 self.stderr.write( 

118 "Error in {}: {}".format(key, ", ".join(value)) 

119 ) 

120 msg = "Component failed validation!" 

121 raise CommandError(msg) from error 

122 component.save(force_insert=True) 

123 self.stdout.write( 

124 f"Imported {component} with {component.translation_set.count()} translations" 

125 ) 

126 else: 

127 self.stderr.write(f"Component {component} already exists") 

128 if options["ignore"]: 

129 continue 

130 if options["update"]: 

131 for key in item: 

132 if key not in allfields or key == "slug": 

133 continue 

134 setattr(component, key, item[key]) 

135 component.save() 

136 continue 

137 msg = "Component already exists, use --ignore or --update!" 

138 raise CommandError(msg)