From 62f11dbc8c0bb3fb4ba20b4cb3fa5415e2e86e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Wed, 18 Sep 2024 12:45:54 +0200 Subject: [PATCH] fix(download): correctly download objects specified in configuration - add tests for configuration based download - reuse download_components for single component download - fixed download of categorized components - consistent test data for categorization of components - make Component always have category even if it None Fixes #653 --- CHANGES.rst | 4 ++- wlc/__init__.py | 26 +++++++++++++++++- wlc/main.py | 23 ++++++++-------- wlc/test_data/api/components-hello-android | 1 + wlc/test_data/api/components-hello-weblate | 1 - wlc/test_data/wlc | 8 ++++++ wlc/test_main.py | 32 ++++++++++++++++++++++ wlc/test_wlc.py | 1 + 8 files changed, 82 insertions(+), 14 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 02ef3c56..ca7e2bac 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,9 @@ -1.15 +1.16 ---- * Not yet released. +* Fixed downloading components based on configration. +* Improved handling of categorized components. 1.15 ---- diff --git a/wlc/__init__.py b/wlc/__init__.py index ad0f17d4..7967e2b3 100644 --- a/wlc/__init__.py +++ b/wlc/__init__.py @@ -368,6 +368,7 @@ class LazyObject(dict): PARAMS: ClassVar[tuple[str, ...]] = () OPTIONALS: ClassVar[set[str]] = set() + NULLS: ClassVar[set[str]] = set() MAPPINGS: ClassVar[dict[str, Any]] = {} ID: ClassVar[str] = "url" @@ -431,6 +432,8 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as error: + if name in self.NULLS: + return None raise AttributeError(name) from error def setattrvalue(self, name, value): @@ -451,7 +454,11 @@ def keys(self): if len(self._data) <= 1: self.refresh() for param in self.PARAMS: - if param not in self.OPTIONALS or param in self._data: + if ( + param not in self.OPTIONALS + or param in self._data + or param in self.NULLS + ): yield param def items(self): @@ -604,6 +611,9 @@ def delete(self): def create_component(self, **kwargs): return self.weblate.create_component(self.slug, **kwargs) + def full_slug(self): + return self.slug + class Category(LazyObject): """Category object.""" @@ -611,6 +621,14 @@ class Category(LazyObject): PARAMS: ClassVar[tuple[str, ...]] = ("category", "name", "project", "slug", "url") MAPPINGS: ClassVar[dict[str, Any]] = {"project": Project} + def full_slug(self): + current = self + slugs = [self.project.slug, self.slug] + while current.category: + current = current.category + slugs.insert(1, current.slug) + return "/".join(slugs) + class Component(LazyObject, RepoObjectMixin): """Component object.""" @@ -645,6 +663,7 @@ class Component(LazyObject, RepoObjectMixin): "category", "linked_component", } + NULLS: ClassVar[set[str]] = {"category"} ID: ClassVar[str] = "slug" MAPPINGS: ClassVar[dict[str, Any]] = { "category": Category, @@ -653,6 +672,11 @@ class Component(LazyObject, RepoObjectMixin): } REPOSITORY_CLASS = Repository + def full_slug(self): + if self.category: + return f"{self.category.full_slug()}/{self.slug}" + return f"{self.project.full_slug()}/{self.slug}" + def list(self): """List translations in the component.""" self.ensure_loaded("translations_url") diff --git a/wlc/main.py b/wlc/main.py index 1b3fecc1..9dadd305 100644 --- a/wlc/main.py +++ b/wlc/main.py @@ -668,8 +668,9 @@ def download_components(self, iterable): if getattr(component, "is_glossary", False) and self.args.no_glossary: continue self.download_component(component) - item = f"{component.project.slug}/{component.slug}" - self.println(f"downloaded translations for component: {item}") + self.println( + f"downloaded translations for component: {component.full_slug()}" + ) def run(self): """Executor.""" @@ -687,20 +688,20 @@ def run(self): # All translations for a component if isinstance(obj, wlc.Component): - for component in self.wlc.list_components(): - # Only download for the component we scoped - if obj.slug == component.slug: - self.download_component(component) - self.println( - f"downloaded translations for component: {self.args.object[0]}" - ) - + # Only download for the component we scoped + self.download_components( + [ + component + for component in self.wlc.list_components() + if obj.full_slug() == component.full_slug() + ] + ) return # All translations for a project if isinstance(obj, wlc.Project): self.download_components(obj.list()) - self.println(f"downloaded translations for project: {self.args.object[0]}") + self.println(f"downloaded translations for project: {obj.full_slug()}") return self.download_components(self.wlc.list_components()) diff --git a/wlc/test_data/api/components-hello-android b/wlc/test_data/api/components-hello-android index 45a08f29..a5f8887a 100644 --- a/wlc/test_data/api/components-hello-android +++ b/wlc/test_data/api/components-hello-android @@ -2,6 +2,7 @@ "branch": "main", "file_format": "aresource", "filemask": "android/values-*/strings.xml", + "category": "http://127.0.0.1:8000/api/categories/1/", "git_export": "", "license": "", "license_url": "", diff --git a/wlc/test_data/api/components-hello-weblate b/wlc/test_data/api/components-hello-weblate index 7ebbe30f..d5fbf30d 100644 --- a/wlc/test_data/api/components-hello-weblate +++ b/wlc/test_data/api/components-hello-weblate @@ -1,7 +1,6 @@ { "agreement": "", "branch": "main", - "category": "http://127.0.0.1:8000/api/categories/1/", "file_format": "po", "filemask": "po/*.po", "git_export": "", diff --git a/wlc/test_data/wlc b/wlc/test_data/wlc index c4ac294c..d5c64c45 100644 --- a/wlc/test_data/wlc +++ b/wlc/test_data/wlc @@ -9,3 +9,11 @@ timeout = 30 [withkey] url = http://127.0.0.1:8000/api/ key = KEY + +[withcomponent] +url = http://127.0.0.1:8000/api/ +translation = hello/weblate + +[withproject] +url = http://127.0.0.1:8000/api/ +translation = hello diff --git a/wlc/test_main.py b/wlc/test_main.py index dd9fb45c..eb0c7d8e 100644 --- a/wlc/test_main.py +++ b/wlc/test_main.py @@ -454,6 +454,38 @@ def test_download(self): ) self.assertEqual(os.listdir(tmpdirname), ["output"]) + def test_download_config(self): + with TemporaryDirectory() as tmpdirname: + execute( + [ + "--config", + TEST_CONFIG, + "--config-section", + "withcomponent", + "download", + "--output", + tmpdirname, + ], + settings=False, + ) + self.assertEqual(os.listdir(tmpdirname), ["hello-weblate.zip"]) + with TemporaryDirectory() as tmpdirname: + execute( + [ + "--config", + TEST_CONFIG, + "--config-section", + "withproject", + "download", + "--output", + tmpdirname, + ], + settings=False, + ) + self.assertEqual( + os.listdir(tmpdirname), ["hello-weblate.zip", "hello-android.zip"] + ) + def test_upload(self): """Translation file uploads.""" msg = "Error: Failed to upload translations!\nNot found.\n" diff --git a/wlc/test_wlc.py b/wlc/test_wlc.py index 7fd9cbe2..58966c79 100644 --- a/wlc/test_wlc.py +++ b/wlc/test_wlc.py @@ -552,6 +552,7 @@ def test_keys(self): obj.keys(), [ "agreement", + "category", "branch", "file_format", "filemask",