)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":23279,"name":"Seongsoo Cho","display_name":"Seongsoo Cho","email":"ppiyakk2@printf.kr","username":"seongsoo.cho"},"change_message_id":"2798eb99853151b71a3d693065e4466e24568aa6","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"d8c945b9_4ccf4682","updated":"2026-07-07 15:26:16.000000000","message":"Thank you for creating these Weblate management tools! \n\nHere are a few minor comments and suggestions for improvement:\n\n1. Folder Name Typo \u0026 Clarity\nIt looks like \"weblate_component_tools\" was intended, but there is currently a typo in the path (componet). Also, it might be worth reconsidering the folder name itself. It\u0027s not immediately intuitive what its exact role is just by looking at the name.\n\n2. Enhancing Output Messages\nCould we make the CLI output a bit more user-friendly? For example, when a project is created, it currently only shows \"Created Project: (name)\". It would be great if it could print the URLs of the created projects/categories as well, similar to how `git review` provides detailed output.\n\n3. Improving Error Messages\nWhen trying to create a category in a non-existent project, the raw 404 client error is printed directly. Instead of dumping the raw API response, it would be much cleaner to handle this and display a meaningful error message to the user.\n\n4. Python Packaging \u0026 Entry Points\nI understand this code is intended to be provided as a CLI once the i18n repository is packaged. If so, the package code can stay here, but the executable should ideally be managed via entry points. Please consider adding an entry_points configuration to setup.cfg with an appropriate CLI binary name.","commit_id":"3416cbce00ab62fd23052179a929a9b0e8b30436"},{"author":{"_account_id":37966,"name":"DaGyeong Kim","display_name":"S0okJu","email":"damekyung98@gmail.com","username":"S0okJu"},"change_message_id":"067eb9a9b044aba15d52f3e8fd5ba91211808103","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"ed1eed80_0a31d30e","in_reply_to":"2d7f5cbb_c27a2f86","updated":"2026-07-24 11:24:50.000000000","message":"@ppiyakk2@printf.kr\n\nImproved both the output and error messages based on the feedback.\n\nFor error messages, added a `--debug` flag so the response value can be inspected when needed.","commit_id":"3416cbce00ab62fd23052179a929a9b0e8b30436"},{"author":{"_account_id":37966,"name":"DaGyeong Kim","display_name":"S0okJu","email":"damekyung98@gmail.com","username":"S0okJu"},"change_message_id":"78f43698d41723e7fe2ba05fc5144c20d116ffd2","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"2d7f5cbb_c27a2f86","in_reply_to":"d8c945b9_4ccf4682","updated":"2026-07-21 16:12:42.000000000","message":"Thanks for the review! @ppiyakk2@printf.kr\n\nI\u0027ll go through your comments one by one.\n\n1. Regarding the tool name, how about `openstack-weblate-client`?\nThis client is specific to OpenStack\u0027s use of Weblate, so I added the `openstack-` prefix to make that scope clear.","commit_id":"3416cbce00ab62fd23052179a929a9b0e8b30436"},{"author":{"_account_id":37966,"name":"DaGyeong Kim","display_name":"S0okJu","email":"damekyung98@gmail.com","username":"S0okJu"},"change_message_id":"a649dae1ad283ba2d5897e635f074cfa10009b13","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"491145b5_1bd3ca9e","in_reply_to":"ed1eed80_0a31d30e","updated":"2026-07-24 11:29:00.000000000","message":"\u003e @ppiyakk2@printf.kr\n\u003e \n\u003e Improved both the output and error messages based on the feedback.\n\u003e \n\u003e For error messages, added a `--debug` flag so the response value can be inspected when needed.\n\nI\u0027ll work on the Python Packaging \u0026 Entry Points feedback after the message-related changes are in.","commit_id":"3416cbce00ab62fd23052179a929a9b0e8b30436"}],"tools/weblate/openstack_weblate_client/cli.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":115,"context_line":"        web_url \u003d category.get(\"web_url\")"},{"line_number":116,"context_line":"        if web_url is None:"},{"line_number":117,"context_line":"            web_url \u003d f\"{client.url}/projects/{project_slug}/{category_slug}/\""},{"line_number":118,"context_line":"        print(f\u0027Created category \"{category_slug}\" in project \"{project_slug}\".\u0027)"},{"line_number":119,"context_line":"        print(f\"Web URL: {web_url}\")"},{"line_number":120,"context_line":"        return 0"},{"line_number":121,"context_line":""}],"source_content_type":"text/x-python","patch_set":7,"id":"0776e264_8fbb03ff","line":118,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":138,"context_line":"            )"},{"line_number":139,"context_line":"        elif status in (400, 422):"},{"line_number":140,"context_line":"            _print_error("},{"line_number":141,"context_line":"                f\u0027failed to create category \"{category_slug}\". Weblate rejected \u0027"},{"line_number":142,"context_line":"                \"the request.\","},{"line_number":143,"context_line":"                \"Run with --debug for details.\","},{"line_number":144,"context_line":"            )"}],"source_content_type":"text/x-python","patch_set":7,"id":"aaed1f4c_5b514b45","line":141,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":144,"context_line":"            )"},{"line_number":145,"context_line":"        else:"},{"line_number":146,"context_line":"            _print_error("},{"line_number":147,"context_line":"                f\u0027failed to create category \"{category_slug}\". Weblate returned \u0027"},{"line_number":148,"context_line":"                f\"HTTP {status or \u0027unknown\u0027}.\""},{"line_number":149,"context_line":"            )"},{"line_number":150,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"7631c864_d62e7549","line":147,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":209,"context_line":"        )"},{"line_number":210,"context_line":"        component \u003d component.get(\"result\", component)"},{"line_number":211,"context_line":"        print("},{"line_number":212,"context_line":"            f\u0027Created component \"{component_slug}\" in project \"{project_slug}\" \u0027"},{"line_number":213,"context_line":"            f\u0027and category \"{category_slug}\".\u0027"},{"line_number":214,"context_line":"        )"},{"line_number":215,"context_line":"        if component.get(\"web_url\"):"}],"source_content_type":"text/x-python","patch_set":7,"id":"5bbc0072_fff2c46a","line":212,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (80 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":232,"context_line":"            )"},{"line_number":233,"context_line":"        elif status in (400, 422):"},{"line_number":234,"context_line":"            _print_error("},{"line_number":235,"context_line":"                f\u0027failed to create component \"{component_slug}\". Weblate rejected \u0027"},{"line_number":236,"context_line":"                \"the request.\","},{"line_number":237,"context_line":"                \"Run with --debug for details.\","},{"line_number":238,"context_line":"            )"}],"source_content_type":"text/x-python","patch_set":7,"id":"419849ed_77da31ab","line":235,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (83 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":238,"context_line":"            )"},{"line_number":239,"context_line":"        else:"},{"line_number":240,"context_line":"            _print_error("},{"line_number":241,"context_line":"                f\u0027failed to create component \"{component_slug}\". Weblate returned \u0027"},{"line_number":242,"context_line":"                f\"HTTP {status or \u0027unknown\u0027}.\""},{"line_number":243,"context_line":"            )"},{"line_number":244,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"8a726340_2c08d648","line":241,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (83 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":246,"context_line":"    except OSError as exc:"},{"line_number":247,"context_line":"        LOG.debug(\"Could not read the POT file\", exc_info\u003dTrue)"},{"line_number":248,"context_line":"        _print_error("},{"line_number":249,"context_line":"            f\u0027failed to create component \"{component_slug}\". Could not read POT \u0027"},{"line_number":250,"context_line":"            f\u0027file \"{pot_path}\" ({exc})\u0027"},{"line_number":251,"context_line":"        )"},{"line_number":252,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"32e4fe23_0c19399c","line":249,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":277,"context_line":"            raise"},{"line_number":278,"context_line":"        if status \u003d\u003d 404:"},{"line_number":279,"context_line":"            _print_error("},{"line_number":280,"context_line":"                f\u0027failed to update source for component \"{component_slug}\". It \u0027"},{"line_number":281,"context_line":"                \"was not found.\""},{"line_number":282,"context_line":"            )"},{"line_number":283,"context_line":"        elif status \u003d\u003d 409:"}],"source_content_type":"text/x-python","patch_set":7,"id":"78b2d86d_3f659b90","line":280,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (80 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":282,"context_line":"            )"},{"line_number":283,"context_line":"        elif status \u003d\u003d 409:"},{"line_number":284,"context_line":"            _print_error("},{"line_number":285,"context_line":"                f\u0027failed to update source for component \"{component_slug}\". Its \u0027"},{"line_number":286,"context_line":"                \"current state conflicts with this operation.\""},{"line_number":287,"context_line":"            )"},{"line_number":288,"context_line":"        elif status in (400, 422):"}],"source_content_type":"text/x-python","patch_set":7,"id":"a8352aec_bf806361","line":285,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":301,"context_line":"    except OSError as exc:"},{"line_number":302,"context_line":"        LOG.debug(\"Could not read the POT file\", exc_info\u003dTrue)"},{"line_number":303,"context_line":"        _print_error("},{"line_number":304,"context_line":"            f\u0027failed to update source for component \"{component_slug}\". Could not \u0027"},{"line_number":305,"context_line":"            f\u0027read POT file \"{pot_path}\" ({exc})\u0027"},{"line_number":306,"context_line":"        )"},{"line_number":307,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"319a1d46_5592c88f","line":304,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (83 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":336,"context_line":"            raise"},{"line_number":337,"context_line":"        if status \u003d\u003d 404:"},{"line_number":338,"context_line":"            _print_error("},{"line_number":339,"context_line":"                f\u0027failed to download translations for component \"{component_slug}\". \u0027"},{"line_number":340,"context_line":"                \"It was not found.\""},{"line_number":341,"context_line":"            )"},{"line_number":342,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":7,"id":"1ba37046_e193e878","line":339,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (85 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":341,"context_line":"            )"},{"line_number":342,"context_line":"        else:"},{"line_number":343,"context_line":"            _print_error("},{"line_number":344,"context_line":"                f\u0027failed to download translations for component \"{component_slug}\". \u0027"},{"line_number":345,"context_line":"                f\"Weblate returned HTTP {status or \u0027unknown\u0027}.\""},{"line_number":346,"context_line":"            )"},{"line_number":347,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"7e43a50d_135fac5f","line":344,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (85 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":349,"context_line":"    except OSError as exc:"},{"line_number":350,"context_line":"        LOG.debug(\"Could not write the translation archive\", exc_info\u003dTrue)"},{"line_number":351,"context_line":"        _print_error("},{"line_number":352,"context_line":"            f\u0027failed to download translations for component \"{component_slug}\". \u0027"},{"line_number":353,"context_line":"            f\u0027Could not write to \"{output_dir}\" ({exc})\u0027"},{"line_number":354,"context_line":"        )"},{"line_number":355,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"8f84b7de_032b7a3d","line":352,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"}],"tools/weblate/openstack_weblate_client/common.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":1,"context_line":"import argparse"},{"line_number":2,"context_line":"import re"},{"line_number":3,"context_line":""},{"line_number":4,"context_line":""}],"source_content_type":"text/x-python","patch_set":7,"id":"ae765077_96d06833","line":1,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: H102: Apache 2.0 license header not found","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"}],"tools/weblate/openstack_weblate_client/main.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5bb03b568d7bd558cb1e001f5d8d3fec24ccbd09","unresolved":false,"context_lines":[{"line_number":94,"context_line":""},{"line_number":95,"context_line":"    except ValueError as exc:"},{"line_number":96,"context_line":"        print("},{"line_number":97,"context_line":"            f\u0027Error: failed to load Weblate configuration \"{args.config}\". {exc}\u0027,"},{"line_number":98,"context_line":"            file\u003dsys.stderr,"},{"line_number":99,"context_line":"        )"},{"line_number":100,"context_line":"        return 1"}],"source_content_type":"text/x-python","patch_set":7,"id":"794e4e85_0d30f3f1","line":97,"updated":"2026-07-24 09:07:17.000000000","message":"pep8: E501 line too long (82 \u003e 79 characters)","commit_id":"189d911cb5c4892ce967f3736e0d02dad4245bb0"}],"tools/weblate/weblate_componet_tools/README.rst":[{"author":{"_account_id":23279,"name":"Seongsoo Cho","display_name":"Seongsoo Cho","email":"ppiyakk2@printf.kr","username":"seongsoo.cho"},"change_message_id":"19cec031e6518feb6055546441e1dc71fae8a871","unresolved":true,"context_lines":[{"line_number":98,"context_line":""},{"line_number":99,"context_line":"The preferred config format is::"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    [weblate]"},{"line_number":102,"context_line":"    url \u003d https://weblate.example.org"},{"line_number":103,"context_line":"    key \u003d your-api-token"},{"line_number":104,"context_line":""}],"source_content_type":"text/x-rst","patch_set":3,"id":"fd625171_bba82f90","line":101,"updated":"2026-06-03 13:40:41.000000000","message":"This uses the old Weblate config format.\n\nPlease update it to the latest format described here:\nhttps://docs.weblate.org/en/latest/wlc.html#getting-started\n\nFollowing the new format, config.py should also be updated accordingly.","commit_id":"3dad8028103d3c5b5cd67d96c0837d551de55cf1"},{"author":{"_account_id":37966,"name":"DaGyeong Kim","display_name":"S0okJu","email":"damekyung98@gmail.com","username":"S0okJu"},"change_message_id":"9944099ab35860c7a4eb7893c7e1a4104d082f44","unresolved":false,"context_lines":[{"line_number":98,"context_line":""},{"line_number":99,"context_line":"The preferred config format is::"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    [weblate]"},{"line_number":102,"context_line":"    url \u003d https://weblate.example.org"},{"line_number":103,"context_line":"    key \u003d your-api-token"},{"line_number":104,"context_line":""}],"source_content_type":"text/x-rst","patch_set":3,"id":"ed0a457a_df3505b0","line":101,"in_reply_to":"fd625171_bba82f90","updated":"2026-06-20 05:30:50.000000000","message":"@ppiyakk2@printf.kr\nI updated the configuration format and documented it in the README.\n\nThank you.","commit_id":"3dad8028103d3c5b5cd67d96c0837d551de55cf1"}],"tools/weblate/weblate_componet_tools/config.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"c42b67f68cdbca95377e82e409f5f2dae6a0ae70","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        if not config.read(path):"},{"line_number":40,"context_line":"            raise FileNotFoundError(f\"Config file not found: {path}\")"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"        configured_url \u003d self._read_required(config, self.WEBLATE_SECTION, \"url\")"},{"line_number":43,"context_line":"        self.url \u003d self._client_url_from_config_url(configured_url)"},{"line_number":44,"context_line":"        self.key \u003d self._read_key_for_url(config, configured_url)"},{"line_number":45,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"4c3f5fd9_1b94bac6","line":42,"updated":"2026-06-20 05:19:09.000000000","message":"pep8: E501 line too long (81 \u003e 79 characters)","commit_id":"3b22e93375d5a6d9503de1f56de30361260d6132"}]}
