From 3e4f7dc81e582f80c61cf5df0e273f5ee752bb94 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 08:11:34 -0700 Subject: [PATCH 01/13] add test for save_transifex_to_pofile --- licenses/tests/test_transifex.py | 49 ++++++++++++++++++++++++++++++++ licenses/transifex.py | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/licenses/tests/test_transifex.py b/licenses/tests/test_transifex.py index fc87bc32..f4a15db9 100644 --- a/licenses/tests/test_transifex.py +++ b/licenses/tests/test_transifex.py @@ -1333,6 +1333,55 @@ def test_diff_translations_same(self): ) self.helper.diff_entry.assert_not_called() + # Test: save_transifex_to_pofile ######################################### + + def test_save_transifex_to_pofile(self): + resource_slug = "x_slug_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" + pofile_path = "x_path_x" + pofile_obj = "x_pofile_obj_x" + self.helper.transifex_get_pofile_content = mock.Mock( + return_value=POFILE_CONTENT.encode("utf-8") + ) + + with self.assertLogs(self.helper.log) as log_context: + with mock.patch.object(polib.POFile, "save") as mock_pofile_save: + new_pofile_obj = self.helper.save_transifex_to_pofile( + resource_slug, + language_code, + transifex_code, + pofile_path, + pofile_obj, + ) + + self.assertTrue(log_context.output[0].startswith("INFO:")) + mock_pofile_save.assert_called_once() + + def test_save_transifex_to_pofile_dryrun(self): + self.helper.dryrun = True + resource_slug = "x_slug_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" + pofile_path = "x_path_x" + pofile_obj = "x_pofile_obj_x" + self.helper.transifex_get_pofile_content = mock.Mock( + return_value=POFILE_CONTENT.encode("utf-8") + ) + + with self.assertLogs(self.helper.log) as log_context: + with mock.patch.object(polib.POFile, "save") as mock_pofile_save: + new_pofile_obj = self.helper.save_transifex_to_pofile( + resource_slug, + language_code, + transifex_code, + pofile_path, + pofile_obj, + ) + + self.assertTrue(log_context.output[0].startswith("INFO:")) + mock_pofile_save.assert_not_called() + # Test: add_resource_to_transifex ######################################## def test_add_resource_to_transifex_present(self): diff --git a/licenses/transifex.py b/licenses/transifex.py index c2c54527..de35154a 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -1006,7 +1006,7 @@ def save_transifex_to_pofile( transifex_code, pofile_path, pofile_obj, - ): # pragma: no cover + ): # Get Transifex PO File transifex_pofile_content = self.transifex_get_pofile_content( resource_slug, transifex_code From 4022aed481357585635da193f5ec9e628e820077 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 08:46:13 -0700 Subject: [PATCH 02/13] remove unused variable --- licenses/tests/test_transifex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/licenses/tests/test_transifex.py b/licenses/tests/test_transifex.py index f4a15db9..2231c204 100644 --- a/licenses/tests/test_transifex.py +++ b/licenses/tests/test_transifex.py @@ -1347,7 +1347,7 @@ def test_save_transifex_to_pofile(self): with self.assertLogs(self.helper.log) as log_context: with mock.patch.object(polib.POFile, "save") as mock_pofile_save: - new_pofile_obj = self.helper.save_transifex_to_pofile( + self.helper.save_transifex_to_pofile( resource_slug, language_code, transifex_code, @@ -1371,7 +1371,7 @@ def test_save_transifex_to_pofile_dryrun(self): with self.assertLogs(self.helper.log) as log_context: with mock.patch.object(polib.POFile, "save") as mock_pofile_save: - new_pofile_obj = self.helper.save_transifex_to_pofile( + self.helper.save_transifex_to_pofile( resource_slug, language_code, transifex_code, From d46af4932f8567680f09c781e69efb9478e1e94b Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 08:47:14 -0700 Subject: [PATCH 03/13] add push translation and update --force messaging for compare --- .../management/commands/push_translation.py | 61 +++++++++++++ licenses/transifex.py | 85 ++++++++++++++++++- 2 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 licenses/management/commands/push_translation.py diff --git a/licenses/management/commands/push_translation.py b/licenses/management/commands/push_translation.py new file mode 100644 index 00000000..cccdc9e6 --- /dev/null +++ b/licenses/management/commands/push_translation.py @@ -0,0 +1,61 @@ +# Standard library +import logging +from argparse import ArgumentParser + +# Third-party +from django.conf.locale import LANG_INFO +from django.core.management import BaseCommand, CommandError +from git.exc import GitCommandError, RepositoryDirtyError +from requests.exceptions import HTTPError + +# First-party/Local +from licenses.transifex import TransifexHelper + +LOG = logging.getLogger(__name__) +LOG_LEVELS = { + 0: logging.ERROR, + 1: logging.WARNING, + 2: logging.INFO, + 3: logging.DEBUG, +} + + +class Command(BaseCommand): + def add_arguments(self, parser: ArgumentParser): + parser.add_argument( + "-n", + "--dryrun", + action="store_true", + help="dry run: do not make any changes", + ) + parser.add_argument( + "-d", + "--domain", + action="store", + required=True, + help="limit translation domain to specified domain", + ) + parser.add_argument( + "-l", + "--language", + action="store", + required=True, + help="limit translation language to specified Language Code", + ) + + def main(self, **options): + if options["language"] not in LANG_INFO: + raise CommandError(f"Invalid language code: {options['language']}") + LOG.setLevel(LOG_LEVELS[int(options["verbosity"])]) + transifex = TransifexHelper(dryrun=options["dryrun"], logger=LOG) + transifex.push_translation(options["domain"], options["language"]) + + def handle(self, **options): + try: + self.main(**options) + except GitCommandError as e: + raise CommandError(f"GitCommandError: {e}") + except HTTPError as e: + raise CommandError(f"HTTPError: {e}") + except RepositoryDirtyError as e: + raise CommandError(f"RepositoryDirtyError: {e}") diff --git a/licenses/transifex.py b/licenses/transifex.py index de35154a..e579bae6 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -331,6 +331,49 @@ def add_translation_to_transifex_resource( else: self.clear_transifex_stats() + def push_translation_to_transifex_resource( + self, + resource_slug, + language_code, + transifex_code, + pofile_obj, + ): + """ + Add translation to Transifex resource. + + Uses transifex-python + https://github.com/transifex/transifex-python/tree/devel/transifex/api + + Uses Transifex API 3.0: Resources Translations + https://transifex.github.io/openapi/index.html#tag/Resource-Translations + """ + pofile_content = get_pofile_content(pofile_obj) + language = self.api.Language.get(code=transifex_code) + resource = self.api.Resource.get( + project=self.api_project, slug=resource_slug + ) + self.log.info( + f"{self.nop}{resource_slug} {language_code} ({transifex_code}):" + " Pushing translation to Transifex." + ) + if not self.dryrun: + result = self.api.ResourceTranslationsAsyncUpload.upload( + content=pofile_content, + language=language.id, + resource=resource, + ) + results = "" + for key, value in result.items(): + results = f"{results}\n {key}: {value}" + self.log.info(f"Resource upload results:{results}") + if ( + not result["translations_created"] + and not result["translations_updated"] + ): + self.log.critical("Translation upload failed") + else: + self.clear_transifex_stats() + # def update_branch_for_legal_code(self, repo, legal_code, branch_object): # """ # Pull down the latest translation for the legal_code and update @@ -1341,8 +1384,6 @@ def compare_translations( ): # pragma: no cover self.check_data_repo_is_clean() local_data = self.get_local_data(limit_domain, limit_language) - if force: - self.log.critical("force not yet implimented") # Resources & Sources for resource_slug, resource in local_data.items(): @@ -1381,7 +1422,7 @@ def compare_translations( transifex_string_count, ) if force or not metadata_identical: - self.log.critical("diff not yet implimented") + self.log.critical("resource diff not yet implimented") # Translations for language_code, translation in resource["translations"].items(): @@ -1468,6 +1509,44 @@ def pull_translation( load_deeds_ux_translations() self.normalize_translations(limit_domain, limit_language) + def push_translation( + self, limit_domain, limit_language + ): # pragma: no cover + self.check_data_repo_is_clean() + local_data = self.get_local_data(limit_domain, limit_language) + + # Resources & Sources + for resource_slug, resource in local_data.items(): + resource_name = resource["name"] + + if not self.resource_present(resource_slug, resource_name): + continue + + # Translations + for language_code, translation in resource["translations"].items(): + transifex_code = map_django_to_transifex_language_code( + language_code + ) + pofile_obj = translation["pofile_obj"] + + if not self.translation_supported( + resource_slug, resource_name, transifex_code + ): + continue + + self.push_translation_to_transifex_resource( + resource_slug, + language_code, + transifex_code, + pofile_obj, + ) + + # Normalize local PO File to match newly updated Transifex translation + if not self.dryrun: + if limit_domain and limit_domain == "deeds_ux": + load_deeds_ux_translations() + self.normalize_translations(limit_domain, limit_language) + def check_for_translation_updates_with_repo_and_legal_codes( self, repo: git.Repo, From b7650b76a79bb06c50bb038e9d7b822556361f63 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 09:51:13 -0700 Subject: [PATCH 04/13] refactor to use single upload_translation_to_transifex_resource function with push_overwrite parameter --- licenses/tests/test_transifex.py | 171 +++++++++++++++++++++---------- licenses/transifex.py | 124 ++++++++-------------- 2 files changed, 160 insertions(+), 135 deletions(-) diff --git a/licenses/tests/test_transifex.py b/licenses/tests/test_transifex.py index 2231c204..590c8547 100644 --- a/licenses/tests/test_transifex.py +++ b/licenses/tests/test_transifex.py @@ -11,10 +11,7 @@ from django.test import TestCase, override_settings # First-party/Local -from i18n.utils import ( - get_pofile_content, - map_django_to_transifex_language_code, -) +from i18n.utils import get_pofile_content from licenses.models import LegalCode from licenses.tests.factories import LegalCodeFactory, LicenseFactory from licenses.transifex import ( @@ -1591,53 +1588,63 @@ def test_add_resource_to_transifex_dryrun(self): self.helper.api.Resource.get.assert_not_called() self.helper.api.ResourceStringsAsyncUpload.upload.assert_not_called() - # Test: add_translation_to_transifex_resource ############################ + # Test: upload_translation_to_transifex_resource ######################### - def test_add_translation_to_transifex_resource_is_source(self): + def test_upload_translation_to_transifex_resource_is_source(self): api = self.helper.api - language_code = settings.LANGUAGE_CODE resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = settings.LANGUAGE_CODE + transifex_code = settings.LANGUAGE_CODE pofile_path = "x_path_x" pofile_obj = "x_pofile_obj_x" + push_overwrite = False self.helper._resource_stats = {} self.helper._translation_stats = {} with self.assertRaises(ValueError) as cm: - self.helper.add_translation_to_transifex_resource( - language_code, + self.helper.upload_translation_to_transifex_resource( resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite, ) - self.assertIn("x_name_x (x_slug_x) en", str(cm.exception)) + self.assertIn( + f"{resource_slug} {language_code} ({transifex_code}):", + str(cm.exception), + ) self.assertIn("is for translations, not sources.", str(cm.exception)) api.Language.get.assert_not_called() api.Resource.get.assert_not_called() api.ResourceTranslationsAsyncUpload.upload.assert_not_called() - def test_add_translation_to_transifex_resource_missing_source(self): + def test_upload_translation_to_transifex_resource_missing_source(self): api = self.helper.api - language_code = "x_lang_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = "x_pofile_obj_x" + push_overwrite = False self.helper._resource_stats = {} self.helper._translation_stats = {} with self.assertRaises(ValueError) as cm: - self.helper.add_translation_to_transifex_resource( - language_code, + self.helper.upload_translation_to_transifex_resource( resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite, ) - self.assertIn("x_name_x (x_slug_x) x_lang_code_x", str(cm.exception)) + self.assertIn( + f"{resource_slug} {language_code} ({transifex_code}):", + str(cm.exception), + ) self.assertIn( "Transifex does not yet contain resource.", str(cm.exception) ) @@ -1645,85 +1652,90 @@ def test_add_translation_to_transifex_resource_missing_source(self): api.Resource.get.assert_not_called() api.ResourceTranslationsAsyncUpload.upload.assert_not_called() - def test_add_translation_to_transifex_present(self): + def test_upload_translation_to_transifex_resource_present(self): api = self.helper.api - language_code = "x_lang_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) + push_overwrite = False self.helper._resource_stats = {resource_slug: None} self.helper._translation_stats = { - resource_slug: {language_code: {"translated_strings": 99}} + resource_slug: {transifex_code: {"translated_strings": 99}} } - self.helper.add_translation_to_transifex_resource( - language_code, + self.helper.upload_translation_to_transifex_resource( resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite, ) api.Language.get.assert_not_called() api.Resource.get.assert_not_called() api.ResourceTranslationsAsyncUpload.upload.assert_not_called() - def test_add_translation_to_transifex_resource_dryrun(self): + def test_upload_translation_to_transifex_resource_dryrun(self): api = self.helper.api self.helper.dryrun = True - language_code = "x_lang_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) + push_overwrite = False self.helper._resource_stats = {resource_slug: None} self.helper._translation_stats = {resource_slug: {}} - self.helper.add_translation_to_transifex_resource( - language_code, + self.helper.upload_translation_to_transifex_resource( resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite, ) api.Language.get.assert_called_once() api.Resource.get.assert_called_once() api.ResourceTranslationsAsyncUpload.upload.assert_not_called() - def test_add_translation_to_transifex_missing_with_changes(self): + def test_upload_translation_to_transifex_resource_miss_with_changes(self): api = self.helper.api + resource_slug = "x_slug_x" language_code = "x_lang_code_x" - transifex_code = map_django_to_transifex_language_code(language_code) + transifex_code = "x_trans_code_x" + pofile_path = "x_path_x" + pofile_obj = polib.pofile(pofile=POFILE_CONTENT) + push_overwrite = False + pofile_content = get_pofile_content(pofile_obj) + self.helper._resource_stats = {resource_slug: {}} + self.helper._translation_stats = {resource_slug: {}} language = mock.Mock( id=f"l:{transifex_code}", ) self.helper.api.Language.get = mock.Mock(return_value=language) - resource_slug = "x_slug_x" - resource_name = "x_name_x" resource = mock.Mock( id=f"o:{TEST_ORG_SLUG}:p:{TEST_PROJ_SLUG}:r:{resource_slug}", attributes={"i18n_type": "PO"}, ) self.helper.api.Resource.get = mock.Mock(return_value=resource) - pofile_path = "x_path_x" - pofile_obj = polib.pofile(pofile=POFILE_CONTENT) - pofile_content = get_pofile_content(pofile_obj) - self.helper._resource_stats = {resource_slug: {}} - self.helper._translation_stats = {resource_slug: {}} api.ResourceTranslationsAsyncUpload.upload.return_value = { "translations_created": 1, "translations_updated": 1, } self.helper.clear_transifex_stats = mock.Mock() - self.helper.add_translation_to_transifex_resource( - language_code, + self.helper.upload_translation_to_transifex_resource( resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite, ) api.Language.get.assert_called_once() @@ -1736,39 +1748,86 @@ def test_add_translation_to_transifex_missing_with_changes(self): ) self.helper.clear_transifex_stats.assert_called_once() - def test_add_translation_to_transifex_missing_no_changes(self): + def test_upload_translation_to_transifex_resource_push(self): api = self.helper.api + resource_slug = "x_slug_x" language_code = "x_lang_code_x" - transifex_code = map_django_to_transifex_language_code(language_code) + transifex_code = "x_trans_code_x" + pofile_path = "x_path_x" + pofile_obj = polib.pofile(pofile=POFILE_CONTENT) + push_overwrite = True + pofile_content = get_pofile_content(pofile_obj) + self.helper._resource_stats = {} + self.helper._translation_stats = {} language = mock.Mock( id=f"l:{transifex_code}", ) self.helper.api.Language.get = mock.Mock(return_value=language) - resource_slug = "x_slug_x" - resource_name = "x_name_x" resource = mock.Mock( id=f"o:{TEST_ORG_SLUG}:p:{TEST_PROJ_SLUG}:r:{resource_slug}", attributes={"i18n_type": "PO"}, ) self.helper.api.Resource.get = mock.Mock(return_value=resource) + api.ResourceTranslationsAsyncUpload.upload.return_value = { + "translations_created": 1, + "translations_updated": 1, + } + self.helper.clear_transifex_stats = mock.Mock() + + self.helper.upload_translation_to_transifex_resource( + resource_slug, + language_code, + transifex_code, + pofile_path, + pofile_obj, + push_overwrite, + ) + + api.Language.get.assert_called_once() + api.Resource.get.assert_called_once() + api.ResourceTranslationsAsyncUpload.upload.assert_called_once() + api.ResourceTranslationsAsyncUpload.upload.assert_called_with( + resource=resource, + content=pofile_content, + language=language.id, + ) + self.helper.clear_transifex_stats.assert_called_once() + + def test_upload_translation_to_transifex_resource_no_changes(self): + api = self.helper.api + resource_slug = "x_slug_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) + push_overwrite = False pofile_content = get_pofile_content(pofile_obj) self.helper._resource_stats = {resource_slug: {}} self.helper._translation_stats = {resource_slug: {}} + language = mock.Mock( + id=f"l:{transifex_code}", + ) + self.helper.api.Language.get = mock.Mock(return_value=language) + resource = mock.Mock( + id=f"o:{TEST_ORG_SLUG}:p:{TEST_PROJ_SLUG}:r:{resource_slug}", + attributes={"i18n_type": "PO"}, + ) + self.helper.api.Resource.get = mock.Mock(return_value=resource) api.ResourceTranslationsAsyncUpload.upload.return_value = { "translations_created": 0, "translations_updated": 0, } self.helper.clear_transifex_stats = mock.Mock() - self.helper.add_translation_to_transifex_resource( - language_code, - resource_slug, - resource_name, - pofile_path, - pofile_obj, - ) + with self.assertLogs(self.helper.log) as log_context: + self.helper.upload_translation_to_transifex_resource( + resource_slug, + language_code, + transifex_code, + pofile_path, + pofile_obj, + push_overwrite, + ) api.Language.get.assert_called_once() api.Resource.get.assert_called_once() @@ -1778,6 +1837,8 @@ def test_add_translation_to_transifex_missing_no_changes(self): content=pofile_content, language=language.id, ) + self.assertTrue(log_context.output[2].startswith("CRITICAL:")) + self.assertIn("Translation upload failed", log_context.output[2]) self.helper.clear_transifex_stats.assert_not_called() # Test: normalize_pofile_language ######################################## diff --git a/licenses/transifex.py b/licenses/transifex.py index e579bae6..7aa3c054 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -257,16 +257,18 @@ def add_resource_to_transifex( else: self.clear_transifex_stats() - def add_translation_to_transifex_resource( + def upload_translation_to_transifex_resource( self, - language_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite=False, ): """ - Add translation to Transifex resource. + Upload translation to Transifex resource (defaults to only uploading + if translation does not yet exist on Transifex). Uses transifex-python https://github.com/transifex/transifex-python/tree/devel/transifex/api @@ -274,79 +276,37 @@ def add_translation_to_transifex_resource( Uses Transifex API 3.0: Resources Translations https://transifex.github.io/openapi/index.html#tag/Resource-Translations """ - transifex_code = map_django_to_transifex_language_code(language_code) - if language_code == settings.LANGUAGE_CODE: - raise ValueError( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: This function," - " add_translation_to_transifex_resource(), is for" - " translations, not sources." - ) - elif resource_slug not in self.resource_stats.keys(): - raise ValueError( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Transifex does not yet contain resource." - " The add_resource_to_transifex() function must be called" - " before this one: add_translation_to_transifex_resource()." - ) - elif ( - resource_slug in self.translation_stats - and transifex_code in self.translation_stats[resource_slug] - and self.translation_stats[resource_slug][transifex_code].get( - "translated_strings", 0 - ) - > 0 - ): - self.log.debug( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Transifex already contains translation." - ) - return - - pofile_content = get_pofile_content(pofile_obj) - language = self.api.Language.get(code=transifex_code) - resource = self.api.Resource.get( - project=self.api_project, slug=resource_slug - ) - self.log.info( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Transifex does not yet contain" - f" translation. Added using {pofile_path}." - ) - if not self.dryrun: - result = self.api.ResourceTranslationsAsyncUpload.upload( - content=pofile_content, - language=language.id, - resource=resource, - ) - results = "" - for key, value in result.items(): - results = f"{results}\n {key}: {value}" - self.log.info(f"Resource upload results:{results}") - if ( - not result["translations_created"] - and not result["translations_updated"] + if not push_overwrite: + if language_code == settings.LANGUAGE_CODE: + raise ValueError( + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): This function," + " upload_translation_to_transifex_resource(), is for" + " translations, not sources." + ) + elif resource_slug not in self.resource_stats.keys(): + raise ValueError( + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): Transifex does not yet contain" + " resource. The add_resource_to_transifex() function must" + " be called before this one " + " [upload_translation_to_transifex_resource()]." + ) + elif ( + resource_slug in self.translation_stats + and transifex_code in self.translation_stats[resource_slug] + and self.translation_stats[resource_slug][transifex_code].get( + "translated_strings", 0 + ) + > 0 ): - self.log.critical("Translation upload failed") - else: - self.clear_transifex_stats() - - def push_translation_to_transifex_resource( - self, - resource_slug, - language_code, - transifex_code, - pofile_obj, - ): - """ - Add translation to Transifex resource. - - Uses transifex-python - https://github.com/transifex/transifex-python/tree/devel/transifex/api + self.log.debug( + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): Transifex already contains" + " translation." + ) + return - Uses Transifex API 3.0: Resources Translations - https://transifex.github.io/openapi/index.html#tag/Resource-Translations - """ pofile_content = get_pofile_content(pofile_obj) language = self.api.Language.get(code=transifex_code) resource = self.api.Resource.get( @@ -354,7 +314,7 @@ def push_translation_to_transifex_resource( ) self.log.info( f"{self.nop}{resource_slug} {language_code} ({transifex_code}):" - " Pushing translation to Transifex." + f" Uploading translation to Transifex using: {pofile_path}." ) if not self.dryrun: result = self.api.ResourceTranslationsAsyncUpload.upload( @@ -1329,12 +1289,13 @@ def normalize_translations( continue # Ensure translation is on Transifex - self.add_translation_to_transifex_resource( - language_code, + self.upload_translation_to_transifex_resource( resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, + push_overwrite=False, ) t_stats = self.translation_stats[resource_slug][transifex_code] @@ -1527,6 +1488,7 @@ def push_translation( transifex_code = map_django_to_transifex_language_code( language_code ) + pofile_path = translation["pofile_path"] pofile_obj = translation["pofile_obj"] if not self.translation_supported( @@ -1534,11 +1496,13 @@ def push_translation( ): continue - self.push_translation_to_transifex_resource( + self.upload_translation_to_transifex_resource( resource_slug, language_code, transifex_code, + pofile_path, pofile_obj, + push_overwrite=True, ) # Normalize local PO File to match newly updated Transifex translation From a29cae2b2941718d272976310fcdd44234eaa366 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 10:19:11 -0700 Subject: [PATCH 05/13] remove duplicate space --- licenses/transifex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/licenses/transifex.py b/licenses/transifex.py index 7aa3c054..60a8d71a 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -314,7 +314,7 @@ def upload_translation_to_transifex_resource( ) self.log.info( f"{self.nop}{resource_slug} {language_code} ({transifex_code}):" - f" Uploading translation to Transifex using: {pofile_path}." + f" Uploading translation to Transifex using: {pofile_path}." ) if not self.dryrun: result = self.api.ResourceTranslationsAsyncUpload.upload( From c295f09bc5e3be8c0638e62291022cf9f1ae8219 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 10:43:05 -0700 Subject: [PATCH 06/13] improve identifiers in log message --- licenses/transifex.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/licenses/transifex.py b/licenses/transifex.py index 60a8d71a..6ba9c3f8 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -776,9 +776,9 @@ def normalize_pofile_dates( def resources_metadata_identical( self, - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_creation, pofile_revision, @@ -806,15 +806,15 @@ def resources_metadata_identical( if differ: differ = "".join(differ) self.log.error( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Resources differ:" + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): Resources differ:" f"\n PO File path: {pofile_path}{differ}" ) return False else: self.log.debug( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Resources appear to be identical" + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): Resources appear to be identical" " based on metadata" ) return True @@ -1371,9 +1371,9 @@ def compare_translations( transifex_string_count = r_stats["string_count"] metadata_identical = self.resources_metadata_identical( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_creation, pofile_revision, From 9913b21f43799704929a687041b8eb2b508db81e Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 10:54:21 -0700 Subject: [PATCH 07/13] improve identifiers in log message --- licenses/tests/test_transifex.py | 64 ++++++++++++++++---------------- licenses/transifex.py | 40 ++++++++++---------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/licenses/tests/test_transifex.py b/licenses/tests/test_transifex.py index 590c8547..28d25333 100644 --- a/licenses/tests/test_transifex.py +++ b/licenses/tests/test_transifex.py @@ -2225,9 +2225,9 @@ def test_normalize_pofile_metadata(self): def test_update_pofile_creation_datetime_dryrun(self): self.helper.dryrun = True - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) pofile_creation = "2021-01-01 01:01:01+00:00" @@ -2236,9 +2236,9 @@ def test_update_pofile_creation_datetime_dryrun(self): with mock.patch.object(polib.POFile, "save") as mock_pofile_save: self.helper.update_pofile_creation_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -2248,9 +2248,9 @@ def test_update_pofile_creation_datetime_dryrun(self): mock_pofile_save.assert_not_called() def test_update_pofile_creation_datetime_save(self): - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) pofile_creation = "2021-01-01 01:01:01+00:00" @@ -2259,9 +2259,9 @@ def test_update_pofile_creation_datetime_save(self): with mock.patch.object(polib.POFile, "save") as mock_pofile_save: new_pofile_obj = self.helper.update_pofile_creation_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -2277,9 +2277,9 @@ def test_update_pofile_creation_datetime_save(self): def test_update_pofile_revision_datetime_dryrun(self): self.helper.dryrun = True - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) pofile_revision = "2021-01-01 01:01:01+00:00" @@ -2288,9 +2288,9 @@ def test_update_pofile_revision_datetime_dryrun(self): with mock.patch.object(polib.POFile, "save") as mock_pofile_save: self.helper.update_pofile_revision_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_revision, @@ -2300,9 +2300,9 @@ def test_update_pofile_revision_datetime_dryrun(self): mock_pofile_save.assert_not_called() def test_update_pofile_revision_datetime_save(self): - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) pofile_revision = dateutil.parser.isoparse("2021-01-01 01:01:01+00:00") @@ -2313,9 +2313,9 @@ def test_update_pofile_revision_datetime_save(self): with mock.patch.object(polib.POFile, "save") as mock_pofile_save: new_pofile_obj = self.helper.update_pofile_revision_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_revision, @@ -2331,9 +2331,9 @@ def test_update_pofile_revision_datetime_save(self): # Test: normalize_pofile_dates ######################## def test_normalize_pofile_dates_update_pofile_dates_missing(self): - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" transifex_creation = dateutil.parser.isoparse( "2021-01-01 01:01:01+00:00" ) @@ -2356,9 +2356,9 @@ def test_normalize_pofile_dates_update_pofile_dates_missing(self): with mock.patch.object(polib.POFile, "save") as mock_pofile_save: new_pofile_obj = self.helper.normalize_pofile_dates( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -2378,9 +2378,9 @@ def test_normalize_pofile_dates_update_pofile_dates_missing(self): ) def test_normalize_pofile_dates_update_pofile_creation_differs(self): - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" transifex_creation = dateutil.parser.isoparse( "2021-01-01 01:01:01+00:00" ) @@ -2403,9 +2403,9 @@ def test_normalize_pofile_dates_update_pofile_creation_differs(self): with mock.patch.object(polib.POFile, "save") as mock_pofile_save: new_pofile_obj = self.helper.normalize_pofile_dates( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -2421,9 +2421,9 @@ def test_normalize_pofile_dates_update_pofile_creation_differs(self): ) def test_normalize_pofile_dates_update_revisions_differ_entries_same(self): - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" transifex_creation = dateutil.parser.isoparse( "2021-01-01 01:01:01+00:00" ) @@ -2452,9 +2452,9 @@ def test_normalize_pofile_dates_update_revisions_differ_entries_same(self): ) with mock.patch.object(polib.POFile, "save") as mock_pofile_save: new_pofile_obj = self.helper.normalize_pofile_dates( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -2472,9 +2472,9 @@ def test_normalize_pofile_dates_update_revisions_differ_entries_same(self): def test_normalize_pofile_dates_update_revisions_differ_entries_differ( self, ): - transifex_code = "x_trans_code_x" resource_slug = "x_slug_x" - resource_name = "x_name_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" transifex_creation = dateutil.parser.isoparse( "2021-01-01 01:01:01+00:00" ) @@ -2515,9 +2515,9 @@ def test_normalize_pofile_dates_update_revisions_differ_entries_differ( polib.POFile, "save" ) as mock_pofile_save: self.helper.normalize_pofile_dates( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, diff --git a/licenses/transifex.py b/licenses/transifex.py index 6ba9c3f8..b54b2632 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -624,9 +624,9 @@ def normalize_pofile_metadata( def update_pofile_creation_datetime( self, - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -635,7 +635,7 @@ def update_pofile_creation_datetime( pad = len(pofile_path) label = f"Transifex {resource_slug} {transifex_code}" self.log.info( - f"{self.nop}{resource_name} ({resource_slug}) {transifex_code}:" + f"{self.nop}{resource_slug} {language_code} ({transifex_code}):" " Correcting PO file 'POT-Creation-Date' to match Transifex:" f"\n{pofile_path}: {pofile_creation}" f"\n{label:>{pad}}: {transifex_creation}" @@ -648,9 +648,9 @@ def update_pofile_creation_datetime( def update_pofile_revision_datetime( self, - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_revision, @@ -659,7 +659,7 @@ def update_pofile_revision_datetime( pad = len(pofile_path) label = f"Transifex {resource_slug} {transifex_code}" self.log.info( - f"{self.nop}{resource_name} ({resource_slug}) {transifex_code}:" + f"{self.nop}{resource_slug} {language_code} ({transifex_code}):" " Correcting PO file 'PO-Revision-Date' to match Transifex:" f"\n{pofile_path}: {pofile_revision}" f"\n{label:>{pad}}: {transifex_revision}" @@ -672,9 +672,9 @@ def update_pofile_revision_datetime( def normalize_pofile_dates( self, - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -697,9 +697,9 @@ def normalize_pofile_dates( # (Transifex API 3.0 does not allow for modifcation of Transifex # creation datetimes) pofile_obj = self.update_pofile_creation_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -710,9 +710,9 @@ def normalize_pofile_dates( if pofile_revision is None: # Normalize Local PO File revision date if its empty or invalid pofile_obj = self.update_pofile_revision_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_revision, @@ -741,9 +741,9 @@ def normalize_pofile_dates( # We can only normalize dates in one direction (normalize PO # Files). pofile_obj = self.update_pofile_revision_datetime( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_revision, @@ -756,8 +756,8 @@ def normalize_pofile_dates( transifex_translated = t_stats["translated_strings"] transifex_untranslated = t_stats["untranslated_strings"] self.log.error( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: 'PO-Revision-Date' mismatch:" + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): 'PO-Revision-Date' mismatch:" # Transifex f"\n{transifex_label:>{pad}}: {transifex_revision}" f"\n{'translated strings':>{pad}}:" @@ -1251,9 +1251,9 @@ def normalize_translations( transifex_revision = parse_date(r_stats["datetime_modified"]) pofile_obj = self.normalize_pofile_dates( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, @@ -1329,9 +1329,9 @@ def normalize_translations( # Normalize Creation and Revision dates in local PO File pofile_obj = self.normalize_pofile_dates( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_obj, pofile_creation, From b48200ba8a1e6d622c2915c013d4db7100b1cfec Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Fri, 5 Nov 2021 11:35:48 -0700 Subject: [PATCH 08/13] improve identifiers in log message --- licenses/tests/test_transifex.py | 42 +++++++++++++++++++------------- licenses/transifex.py | 42 +++++++++++++++++--------------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/licenses/tests/test_transifex.py b/licenses/tests/test_transifex.py index 28d25333..c89638fa 100644 --- a/licenses/tests/test_transifex.py +++ b/licenses/tests/test_transifex.py @@ -1223,10 +1223,11 @@ def test_safesync_pofile_with_changes_dryrun(self): # Test: diff_entry ####################################################### - def test_diff_entries(self): - transifex_code = "x_trans_code_x" - resource_slug = "x_slug_x" + def test_diff_entry(self): resource_name = "x_name_x" + resource_slug = "x_slug_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) pofile_entry = pofile_obj[0] @@ -1238,9 +1239,10 @@ def test_diff_entries(self): with self.assertLogs(self.helper.log) as log_context: self.helper.diff_entry( - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_entry, transifex_entry, @@ -1248,8 +1250,9 @@ def test_diff_entries(self): self.assertTrue(log_context.output[0].startswith("WARNING:")) self.assertIn( - "--- x_name_x PO File x_path_x\n\n" - "+++ x_name_x Transifex x_slug_x x_trans_code_x\n\n", + f"--- {resource_name} PO File {pofile_path}\n\n" + f"+++ {resource_name} Transifex {resource_slug} {language_code}" + f" ({transifex_code})\n\n", log_context.output[0], ) self.assertIn( @@ -1261,9 +1264,10 @@ def test_diff_entries(self): # Test: diff_translations ############################################### def test_diff_translations_differences(self): - transifex_code = "x_trans_code_x" - resource_slug = "x_slug_x" resource_name = "x_name_x" + resource_slug = "x_slug_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) colordiff = False @@ -1280,9 +1284,10 @@ def test_diff_translations_differences(self): self.helper.diff_entry = mock.Mock() self.helper.diff_translations( - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_obj, colordiff, @@ -1294,9 +1299,10 @@ def test_diff_translations_differences(self): ) self.helper.diff_entry.assert_called_once() self.helper.diff_entry.assert_called_with( - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_entry, transifex_entry, @@ -1304,9 +1310,10 @@ def test_diff_translations_differences(self): ) def test_diff_translations_same(self): - transifex_code = "x_trans_code_x" - resource_slug = "x_slug_x" resource_name = "x_name_x" + resource_slug = "x_slug_x" + language_code = "x_lang_code_x" + transifex_code = "x_trans_code_x" pofile_path = "x_path_x" pofile_obj = polib.pofile(pofile=POFILE_CONTENT) colordiff = False @@ -1316,9 +1323,10 @@ def test_diff_translations_same(self): self.helper.diff_entry = mock.Mock() self.helper.diff_translations( - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_obj, colordiff, diff --git a/licenses/transifex.py b/licenses/transifex.py index b54b2632..61e0a53d 100644 --- a/licenses/transifex.py +++ b/licenses/transifex.py @@ -821,9 +821,9 @@ def resources_metadata_identical( def translations_metadata_identical( self, - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_creation, pofile_revision, @@ -852,15 +852,15 @@ def translations_metadata_identical( if differ: differ = "".join(differ) self.log.error( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Translations differ:" + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): Translations differ:" f"\n PO File path: {pofile_path}{differ}" ) return False else: self.log.debug( - f"{self.nop}{resource_name} ({resource_slug})" - f" {transifex_code}: Translations appear to be identical" + f"{self.nop}{resource_slug} {language_code}" + f" ({transifex_code}): Translations appear to be identical" " based on metadata" ) return True @@ -932,9 +932,10 @@ def safesync_pofile( def diff_entry( self, - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_entry, transifex_entry, @@ -948,7 +949,7 @@ def diff_entry( fromfile=f"{resource_name} PO File {pofile_path}", tofile=( f"{resource_name} Transifex {resource_slug}" - f" {transifex_code}" + f" {language_code} ({transifex_code})" ), # Number of lines of context (n) is set very high to ensure # that the all comments and the entire msgid are shown @@ -975,9 +976,10 @@ def diff_entry( def diff_translations( self, - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_obj, colordiff, @@ -993,9 +995,10 @@ def diff_translations( transifex_entry = transifex_pofile_obj[index] if pofile_entry != transifex_entry: self.diff_entry( - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_entry, transifex_entry, @@ -1307,9 +1310,9 @@ def normalize_translations( # Compare metadata if not self.translations_metadata_identical( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_creation, pofile_revision, @@ -1409,9 +1412,9 @@ def compare_translations( transifex_translated = t_stats["translated_strings"] metadata_identical = self.translations_metadata_identical( - transifex_code, resource_slug, - resource_name, + language_code, + transifex_code, pofile_path, pofile_creation, pofile_revision, @@ -1422,9 +1425,10 @@ def compare_translations( ) if force or not metadata_identical: self.diff_translations( - transifex_code, - resource_slug, resource_name, + resource_slug, + language_code, + transifex_code, pofile_path, pofile_obj, colordiff, From 942fba6590cb750371744316fa61a452e36e43f6 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Mon, 8 Nov 2021 08:26:20 -0800 Subject: [PATCH 09/13] update python dependencies --- Pipfile | 2 +- Pipfile.lock | 355 ++++++++++++++++++++++++++++----------------------- 2 files changed, 198 insertions(+), 159 deletions(-) diff --git a/Pipfile b/Pipfile index 56836362..84b62a80 100644 --- a/Pipfile +++ b/Pipfile @@ -20,7 +20,7 @@ transifex-python = "*" whitenoise = "*" [dev-packages] -black = "==21.9b0" +black = "==21.10b0" coverage = "*" django-debug-toolbar = "*" factory-boy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 6610b58a..d6405304 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e5a8697db33d8835171fbf4ad916b1001d7090d0da98883c896bca4156f85814" + "sha256": "54faba013b4135bcf7b3c0c3736d2f0a7723c3abccece61cac7c4c43da554a94" }, "pipfile-spec": 6, "requires": { @@ -143,57 +143,69 @@ }, "lxml": { "hashes": [ - "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d", - "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3", - "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2", - "sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae", - "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f", - "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927", - "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3", - "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7", - "sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59", - "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f", - "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade", - "sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96", - "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468", - "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b", - "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4", - "sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354", - "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83", - "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04", - "sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16", - "sha256:64812391546a18896adaa86c77c59a4998f33c24788cadc35789e55b727a37f4", - "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791", - "sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a", - "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51", - "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1", - "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a", - "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f", - "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee", - "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec", - "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969", - "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28", - "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a", - "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa", - "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106", - "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d", - "sha256:c1a40c06fd5ba37ad39caa0b3144eb3772e813b5fb5b084198a985431c2f1e8d", - "sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617", - "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4", - "sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92", - "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0", - "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4", - "sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24", - "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2", - "sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e", - "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0", - "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654", - "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2", - "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23", - "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586" + "sha256:08eb9200d88b376a8ed5e50f1dc1d1a45b49305169674002a3b5929943390591", + "sha256:0b12c95542f04d10cba46b3ff28ea52ea56995b78cf918f0b11b05e75812bb79", + "sha256:0c15e1cd55055956e77b0732270f1c6005850696bc3ef3e03d01e78af84eaa42", + "sha256:15d0381feb56f08f78c5cc4fc385ddfe0bde1456e37f54a9322833371aec4060", + "sha256:197b7cb7a753cf553a45115739afd8458464a28913da00f5c525063f94cd3f48", + "sha256:20d7c8d90d449c6a353b15ee0459abae8395dbe59ad01e406ccbf30cd81c6f98", + "sha256:240db6f3228d26e3c6f4fad914b9ddaaf8707254e8b3efd564dc680c8ec3c264", + "sha256:2901625f4a878a055d275beedc20ba9cb359cefc4386a967222fee29eb236038", + "sha256:2b06a91cf7b8acea7793006e4ae50646cef0fe35ce5acd4f5cb1c77eb228e4a1", + "sha256:2eb90f6ec3c236ef2f1bb38aee7c0d23e77d423d395af6326e7cca637519a4cb", + "sha256:351482da8dd028834028537f08724b1de22d40dcf3bb723b469446564f409074", + "sha256:35752ee40f7bbf6adc9ff4e1f4b84794a3593736dcce80db32e3c2aa85e294ac", + "sha256:38b9de0de3aa689fe9fb9877ae1be1e83b8cf9621f7e62049d0436b9ecf4ad64", + "sha256:433df8c7dde0f9e41cbf4f36b0829d50a378116ef5e962ba3881f2f5f025c7be", + "sha256:4341d135f5660db10184963d9c3418c3e28d7f868aaf8b11a323ebf85813f7f4", + "sha256:45fdb2899c755138722797161547a40b3e2a06feda620cc41195ee7e97806d81", + "sha256:4717123f7c11c81e0da69989e5a64079c3f402b0efeb4c6241db6c369d657bd8", + "sha256:47e955112ce64241fdb357acf0216081f9f3255b3ac9c502ca4b3323ec1ca558", + "sha256:48eaac2991b3036175b42ee8d3c23f4cca13f2be8426bf29401a690ab58c88f4", + "sha256:4aa349c5567651f34d4eaae7de6ed5b523f6d70a288f9c6fbac22d13a0784e04", + "sha256:4ba74afe5ee5cb5e28d83b513a6e8f0875fda1dc1a9aea42cc0065f029160d2a", + "sha256:4ec9a80dd5704ecfde54319b6964368daf02848c8954d3bacb9b64d1c7659159", + "sha256:50790313df028aa05cf22be9a8da033b86c42fa32523e4fd944827b482b17bf0", + "sha256:51a0e5d243687596f46e24e464121d4b232ad772e2d1785b2a2c0eb413c285d4", + "sha256:523f195948a1ba4f9f5b7294d83c6cd876547dc741820750a7e5e893a24bbe38", + "sha256:543b239b191bb3b6d9bef5f09f1fb2be5b7eb09ab4d386aa655e4d53fbe9ff47", + "sha256:5ff5bb2a198ea67403bb6818705e9a4f90e0313f2215428ec51001ce56d939fb", + "sha256:601f0ab75538b280aaf1e720eb9d68d4fa104ac274e1e9e6971df488f4dcdb0f", + "sha256:6020c70ff695106bf80651953a23e37718ef1fee9abd060dcad8e32ab2dc13f3", + "sha256:619c6d2b552bba00491e96c0518aad94002651c108a0f7364ff2d7798812c00e", + "sha256:6298f5b42a26581206ef63fffa97c754245d329414108707c525512a5197f2ba", + "sha256:662523cd2a0246740225c7e32531f2e766544122e58bee70e700a024cfc0cf81", + "sha256:6764998345552b1dfc9326a932d2bad6367c6b37a176bb73ada6b9486bf602f7", + "sha256:6d422b3c729737d8a39279a25fa156c983a56458f8b2f97661ee6fb22b80b1d6", + "sha256:72e730d33fe2e302fd07285f14624fca5e5e2fb2bb4fb2c3941e318c41c443d1", + "sha256:75d3c5bbc0ddbad03bb68b9be638599f67e4b98ed3dcd0fec9f6f39e41ee96cb", + "sha256:7ae7089d81fc502df4b217ad77f03c54039fe90dac0acbe70448d7e53bfbc57e", + "sha256:80d10d53d3184837445ff8562021bdd37f57c4cadacbf9d8726cc16220a00d54", + "sha256:877666418598f6cb289546c77ff87590cfd212f903b522b0afa0b9fb73b3ccfb", + "sha256:9b87727561c1150c0cc91c5d9d389448b37a7d15f0ba939ed3d1acb2f11bf6c5", + "sha256:9c91a73971a922c13070fd8fa5a114c858251791ba2122a941e6aa781c713e44", + "sha256:9db24803fa71e3305fe4a7812782b708da21a0b774b130dd1860cf40a6d7a3ee", + "sha256:a75c1ad05eedb1a3ff2a34a52a4f0836cfaa892e12796ba39a7732c82701eff4", + "sha256:a77a3470ba37e11872c75ca95baf9b3312133a3d5a5dc720803b23098c653976", + "sha256:ab6db93a2b6b66cbf62b4e4a7135f476e708e8c5c990d186584142c77d7f975a", + "sha256:afd60230ad9d8bcba005945ec3a343722f09e0b7f8ae804246e5d2cfc6bd71a6", + "sha256:b0ca0ada9d3bc18bd6f611bd001a28abdd49ab9698bd6d717f7f5394c8e94628", + "sha256:b567178a74a2261345890eac66fbf394692a6e002709d329f28a673ca6042473", + "sha256:b667c51682fe9b9788c69465956baa8b6999531876ccedcafc895c74ad716cd8", + "sha256:bbf2dc330bd44bfc0254ab37677ec60f7c7ecea55ad8ba1b8b2ea7bf20c265f5", + "sha256:bdc224f216ead849e902151112efef6e96c41ee1322e15d4e5f7c8a826929aee", + "sha256:cf201bf5594d1aab139fe53e3fca457e4f8204a5bbd65d48ab3b82a16f517868", + "sha256:d43bd68714049c84e297c005456a15ecdec818f7b5aa5868c8b0a865cfb78a44", + "sha256:daf9bd1fee31f1c7a5928b3e1059e09a8d683ea58fb3ffc773b6c88cb8d1399c", + "sha256:e678a643177c0e5ec947b645fa7bc84260dfb9b6bf8fb1fdd83008dfc2ca5928", + "sha256:e91d24623e747eeb2d8121f4a94c6a7ad27dc48e747e2dc95bfe88632bd028a2", + "sha256:e95da348d57eb448d226a44b868ff2ca5786fbcbe417ac99ff62d0a7d724b9c7", + "sha256:ee9e4b07b0eba4b6a521509e9e1877476729c1243246b6959de697ebea739643", + "sha256:f5dd358536b8a964bf6bd48de038754c1609e72e5f17f5d21efe2dda17594dbf", + "sha256:ffd65cfa33fed01735c82aca640fde4cc63f0414775cba11e06f84fae2085a6e" ], "index": "pypi", - "version": "==4.6.3" + "version": "==4.6.4" }, "parsimonious": { "hashes": [ @@ -218,6 +230,7 @@ "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90", "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a", "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e", + "sha256:1f6ca4a9068f5c5c57e744b4baa79f40e83e3746875cac3c45467b16326bab45", "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d", "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f", "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed", @@ -242,6 +255,7 @@ "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f", "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a", "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34", + "sha256:ebccf1123e7ef66efc615a68295bf6fdba875a75d5bba10a05073202598085fc", "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce", "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a", "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e" @@ -335,11 +349,11 @@ }, "soupsieve": { "hashes": [ - "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc", - "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b" + "sha256:617ffc4d0dfd39c66f4d1413a6e165663a34eca86be9b54f97b91756300ff6df", + "sha256:e4860f889dfa88774c07da0b276b70c073b6470fa1a4a8350800bb7bce3dcc76" ], "markers": "python_version >= '3.6'", - "version": "==2.2.1" + "version": "==2.3" }, "sqlparse": { "hashes": [ @@ -351,11 +365,11 @@ }, "toolz": { "hashes": [ - "sha256:1bc473acbf1a1db4e72a1ce587be347450e8f08324908b8a266b486f408f04d5", - "sha256:c7a47921f07822fe534fb1c01c9931ab335a4390c782bd28c6bcc7c2f71f3fbf" + "sha256:6b312d5e15138552f1bda8a4e66c30e236c831b612b2bf0005f8a1df10a4bc33", + "sha256:a5700ce83414c64514d82d60bcda8aabfde092d1c1a8663f9200c07fdcc6da8f" ], "markers": "python_version >= '3.5'", - "version": "==0.11.1" + "version": "==0.11.2" }, "transifex-python": { "hashes": [ @@ -425,11 +439,11 @@ }, "black": { "hashes": [ - "sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115", - "sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91" + "sha256:6eb7448da9143ee65b856a5f3676b7dda98ad9abe0f87fce8c59291f15e82a5b", + "sha256:a9952229092e325fe5f3dae56d81f639b23f7131eb840781947e4b2886030f33" ], "index": "pypi", - "version": "==21.9b0" + "version": "==21.10b0" }, "certifi": { "hashes": [ @@ -550,11 +564,11 @@ }, "faker": { "hashes": [ - "sha256:aad59a0cb82ac072f3109667fb2ba35ac45981ec22436c649d8fcdbf1c9e6927", - "sha256:d23cd665886d2e9680d90f3ce2af7a439f44de3c7becc78353ca2f49b0015352" + "sha256:22e53b8082890cca9b595ec22f9b01676b9d96c5f2f1890bcb49e4d612aa40a2", + "sha256:810182ef3597e0dfc4999a29f7cf17b99c70b361aae0f16743de6b926619ae21" ], "markers": "python_version >= '3.6'", - "version": "==9.7.1" + "version": "==9.8.0" }, "filelock": { "hashes": [ @@ -669,11 +683,11 @@ }, "identify": { "hashes": [ - "sha256:b9ffbeb7ed87e96ce017c66b80ca04fda3adbceb5c74e54fc7d99281d27d0859", - "sha256:ffab539d9121b386ffdea84628ff3eefda15f520f392ce11b393b0a909632cdf" + "sha256:4de55a93e0ba72bf917c840b3794eb1055a67272a1732351c557c88ec42011b1", + "sha256:595283a1c3a078ac5774ad4dc4d1bdd0c1602f60bcf11ae673b64cb2b1945762" ], "markers": "python_full_version >= '3.6.1'", - "version": "==2.3.3" + "version": "==2.3.4" }, "idna": { "hashes": [ @@ -693,11 +707,11 @@ }, "isort": { "hashes": [ - "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899", - "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2" + "sha256:1a18ccace2ed8910bd9458b74a3ecbafd7b2f581301b0ab65cfdd4338272d76f", + "sha256:e52ff6d38012b131628cf0f26c51e7bd3a7c81592eefe3ac71411e692f1b9345" ], "index": "pypi", - "version": "==5.9.3" + "version": "==5.10.0" }, "junit-xml": { "hashes": [ @@ -715,57 +729,69 @@ }, "lxml": { "hashes": [ - "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d", - "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3", - "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2", - "sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae", - "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f", - "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927", - "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3", - "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7", - "sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59", - "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f", - "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade", - "sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96", - "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468", - "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b", - "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4", - "sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354", - "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83", - "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04", - "sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16", - "sha256:64812391546a18896adaa86c77c59a4998f33c24788cadc35789e55b727a37f4", - "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791", - "sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a", - "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51", - "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1", - "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a", - "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f", - "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee", - "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec", - "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969", - "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28", - "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a", - "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa", - "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106", - "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d", - "sha256:c1a40c06fd5ba37ad39caa0b3144eb3772e813b5fb5b084198a985431c2f1e8d", - "sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617", - "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4", - "sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92", - "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0", - "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4", - "sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24", - "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2", - "sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e", - "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0", - "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654", - "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2", - "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23", - "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586" + "sha256:08eb9200d88b376a8ed5e50f1dc1d1a45b49305169674002a3b5929943390591", + "sha256:0b12c95542f04d10cba46b3ff28ea52ea56995b78cf918f0b11b05e75812bb79", + "sha256:0c15e1cd55055956e77b0732270f1c6005850696bc3ef3e03d01e78af84eaa42", + "sha256:15d0381feb56f08f78c5cc4fc385ddfe0bde1456e37f54a9322833371aec4060", + "sha256:197b7cb7a753cf553a45115739afd8458464a28913da00f5c525063f94cd3f48", + "sha256:20d7c8d90d449c6a353b15ee0459abae8395dbe59ad01e406ccbf30cd81c6f98", + "sha256:240db6f3228d26e3c6f4fad914b9ddaaf8707254e8b3efd564dc680c8ec3c264", + "sha256:2901625f4a878a055d275beedc20ba9cb359cefc4386a967222fee29eb236038", + "sha256:2b06a91cf7b8acea7793006e4ae50646cef0fe35ce5acd4f5cb1c77eb228e4a1", + "sha256:2eb90f6ec3c236ef2f1bb38aee7c0d23e77d423d395af6326e7cca637519a4cb", + "sha256:351482da8dd028834028537f08724b1de22d40dcf3bb723b469446564f409074", + "sha256:35752ee40f7bbf6adc9ff4e1f4b84794a3593736dcce80db32e3c2aa85e294ac", + "sha256:38b9de0de3aa689fe9fb9877ae1be1e83b8cf9621f7e62049d0436b9ecf4ad64", + "sha256:433df8c7dde0f9e41cbf4f36b0829d50a378116ef5e962ba3881f2f5f025c7be", + "sha256:4341d135f5660db10184963d9c3418c3e28d7f868aaf8b11a323ebf85813f7f4", + "sha256:45fdb2899c755138722797161547a40b3e2a06feda620cc41195ee7e97806d81", + "sha256:4717123f7c11c81e0da69989e5a64079c3f402b0efeb4c6241db6c369d657bd8", + "sha256:47e955112ce64241fdb357acf0216081f9f3255b3ac9c502ca4b3323ec1ca558", + "sha256:48eaac2991b3036175b42ee8d3c23f4cca13f2be8426bf29401a690ab58c88f4", + "sha256:4aa349c5567651f34d4eaae7de6ed5b523f6d70a288f9c6fbac22d13a0784e04", + "sha256:4ba74afe5ee5cb5e28d83b513a6e8f0875fda1dc1a9aea42cc0065f029160d2a", + "sha256:4ec9a80dd5704ecfde54319b6964368daf02848c8954d3bacb9b64d1c7659159", + "sha256:50790313df028aa05cf22be9a8da033b86c42fa32523e4fd944827b482b17bf0", + "sha256:51a0e5d243687596f46e24e464121d4b232ad772e2d1785b2a2c0eb413c285d4", + "sha256:523f195948a1ba4f9f5b7294d83c6cd876547dc741820750a7e5e893a24bbe38", + "sha256:543b239b191bb3b6d9bef5f09f1fb2be5b7eb09ab4d386aa655e4d53fbe9ff47", + "sha256:5ff5bb2a198ea67403bb6818705e9a4f90e0313f2215428ec51001ce56d939fb", + "sha256:601f0ab75538b280aaf1e720eb9d68d4fa104ac274e1e9e6971df488f4dcdb0f", + "sha256:6020c70ff695106bf80651953a23e37718ef1fee9abd060dcad8e32ab2dc13f3", + "sha256:619c6d2b552bba00491e96c0518aad94002651c108a0f7364ff2d7798812c00e", + "sha256:6298f5b42a26581206ef63fffa97c754245d329414108707c525512a5197f2ba", + "sha256:662523cd2a0246740225c7e32531f2e766544122e58bee70e700a024cfc0cf81", + "sha256:6764998345552b1dfc9326a932d2bad6367c6b37a176bb73ada6b9486bf602f7", + "sha256:6d422b3c729737d8a39279a25fa156c983a56458f8b2f97661ee6fb22b80b1d6", + "sha256:72e730d33fe2e302fd07285f14624fca5e5e2fb2bb4fb2c3941e318c41c443d1", + "sha256:75d3c5bbc0ddbad03bb68b9be638599f67e4b98ed3dcd0fec9f6f39e41ee96cb", + "sha256:7ae7089d81fc502df4b217ad77f03c54039fe90dac0acbe70448d7e53bfbc57e", + "sha256:80d10d53d3184837445ff8562021bdd37f57c4cadacbf9d8726cc16220a00d54", + "sha256:877666418598f6cb289546c77ff87590cfd212f903b522b0afa0b9fb73b3ccfb", + "sha256:9b87727561c1150c0cc91c5d9d389448b37a7d15f0ba939ed3d1acb2f11bf6c5", + "sha256:9c91a73971a922c13070fd8fa5a114c858251791ba2122a941e6aa781c713e44", + "sha256:9db24803fa71e3305fe4a7812782b708da21a0b774b130dd1860cf40a6d7a3ee", + "sha256:a75c1ad05eedb1a3ff2a34a52a4f0836cfaa892e12796ba39a7732c82701eff4", + "sha256:a77a3470ba37e11872c75ca95baf9b3312133a3d5a5dc720803b23098c653976", + "sha256:ab6db93a2b6b66cbf62b4e4a7135f476e708e8c5c990d186584142c77d7f975a", + "sha256:afd60230ad9d8bcba005945ec3a343722f09e0b7f8ae804246e5d2cfc6bd71a6", + "sha256:b0ca0ada9d3bc18bd6f611bd001a28abdd49ab9698bd6d717f7f5394c8e94628", + "sha256:b567178a74a2261345890eac66fbf394692a6e002709d329f28a673ca6042473", + "sha256:b667c51682fe9b9788c69465956baa8b6999531876ccedcafc895c74ad716cd8", + "sha256:bbf2dc330bd44bfc0254ab37677ec60f7c7ecea55ad8ba1b8b2ea7bf20c265f5", + "sha256:bdc224f216ead849e902151112efef6e96c41ee1322e15d4e5f7c8a826929aee", + "sha256:cf201bf5594d1aab139fe53e3fca457e4f8204a5bbd65d48ab3b82a16f517868", + "sha256:d43bd68714049c84e297c005456a15ecdec818f7b5aa5868c8b0a865cfb78a44", + "sha256:daf9bd1fee31f1c7a5928b3e1059e09a8d683ea58fb3ffc773b6c88cb8d1399c", + "sha256:e678a643177c0e5ec947b645fa7bc84260dfb9b6bf8fb1fdd83008dfc2ca5928", + "sha256:e91d24623e747eeb2d8121f4a94c6a7ad27dc48e747e2dc95bfe88632bd028a2", + "sha256:e95da348d57eb448d226a44b868ff2ca5786fbcbe417ac99ff62d0a7d724b9c7", + "sha256:ee9e4b07b0eba4b6a521509e9e1877476729c1243246b6959de697ebea739643", + "sha256:f5dd358536b8a964bf6bd48de038754c1609e72e5f17f5d21efe2dda17594dbf", + "sha256:ffd65cfa33fed01735c82aca640fde4cc63f0414775cba11e06f84fae2085a6e" ], "index": "pypi", - "version": "==4.6.3" + "version": "==4.6.4" }, "mccabe": { "hashes": [ @@ -883,44 +909,57 @@ }, "regex": { "hashes": [ - "sha256:0c186691a7995ef1db61205e00545bf161fb7b59cdb8c1201c89b333141c438a", - "sha256:0dcc0e71118be8c69252c207630faf13ca5e1b8583d57012aae191e7d6d28b84", - "sha256:0f7552429dd39f70057ac5d0e897e5bfe211629652399a21671e53f2a9693a4e", - "sha256:129472cd06062fb13e7b4670a102951a3e655e9b91634432cfbdb7810af9d710", - "sha256:13ec99df95003f56edcd307db44f06fbeb708c4ccdcf940478067dd62353181e", - "sha256:1f2b59c28afc53973d22e7bc18428721ee8ca6079becf1b36571c42627321c65", - "sha256:2b20f544cbbeffe171911f6ce90388ad36fe3fad26b7c7a35d4762817e9ea69c", - "sha256:2fb698037c35109d3c2e30f2beb499e5ebae6e4bb8ff2e60c50b9a805a716f79", - "sha256:34d870f9f27f2161709054d73646fc9aca49480617a65533fc2b4611c518e455", - "sha256:391703a2abf8013d95bae39145d26b4e21531ab82e22f26cd3a181ee2644c234", - "sha256:450dc27483548214314640c89a0f275dbc557968ed088da40bde7ef8fb52829e", - "sha256:45b65d6a275a478ac2cbd7fdbf7cc93c1982d613de4574b56fd6972ceadb8395", - "sha256:5095a411c8479e715784a0c9236568ae72509450ee2226b649083730f3fadfc6", - "sha256:530fc2bbb3dc1ebb17f70f7b234f90a1dd43b1b489ea38cea7be95fb21cdb5c7", - "sha256:56f0c81c44638dfd0e2367df1a331b4ddf2e771366c4b9c5d9a473de75e3e1c7", - "sha256:5e9c9e0ce92f27cef79e28e877c6b6988c48b16942258f3bc55d39b5f911df4f", - "sha256:6d7722136c6ed75caf84e1788df36397efdc5dbadab95e59c2bba82d4d808a4c", - "sha256:74d071dbe4b53c602edd87a7476ab23015a991374ddb228d941929ad7c8c922e", - "sha256:7b568809dca44cb75c8ebb260844ea98252c8c88396f9d203f5094e50a70355f", - "sha256:80bb5d2e92b2258188e7dcae5b188c7bf868eafdf800ea6edd0fbfc029984a88", - "sha256:8d1cdcda6bd16268316d5db1038965acf948f2a6f43acc2e0b1641ceab443623", - "sha256:9f665677e46c5a4d288ece12fdedf4f4204a422bb28ff05f0e6b08b7447796d1", - "sha256:a30513828180264294953cecd942202dfda64e85195ae36c265daf4052af0464", - "sha256:a7a986c45d1099a5de766a15de7bee3840b1e0e1a344430926af08e5297cf666", - "sha256:a940ca7e7189d23da2bfbb38973832813eab6bd83f3bf89a977668c2f813deae", - "sha256:ab7c5684ff3538b67df3f93d66bd3369b749087871ae3786e70ef39e601345b0", - "sha256:be04739a27be55631069b348dda0c81d8ea9822b5da10b8019b789e42d1fe452", - "sha256:c0938ddd60cc04e8f1faf7a14a166ac939aac703745bfcd8e8f20322a7373019", - "sha256:cb46b542133999580ffb691baf67410306833ee1e4f58ed06b6a7aaf4e046952", - "sha256:d134757a37d8640f3c0abb41f5e68b7cf66c644f54ef1cb0573b7ea1c63e1509", - "sha256:de557502c3bec8e634246588a94e82f1ee1b9dfcfdc453267c4fb652ff531570", - "sha256:ded0c4a3eee56b57fcb2315e40812b173cafe79d2f992d50015f4387445737fa", - "sha256:e1dae12321b31059a1a72aaa0e6ba30156fe7e633355e445451e4021b8e122b6", - "sha256:eb672217f7bd640411cfc69756ce721d00ae600814708d35c930930f18e8029f", - "sha256:ee684f139c91e69fe09b8e83d18b4d63bf87d9440c1eb2eeb52ee851883b1b29", - "sha256:f3f9a91d3cc5e5b0ddf1043c0ae5fa4852f18a1c0050318baf5fc7930ecc1f9c" - ], - "version": "==2021.10.23" + "sha256:0075fe4e2c2720a685fef0f863edd67740ff78c342cf20b2a79bc19388edf5db", + "sha256:0621c90f28d17260b41838b22c81a79ff436141b322960eb49c7b3f91d1cbab6", + "sha256:070336382ca92c16c45b4066c4ba9fa83fb0bd13d5553a82e07d344df8d58a84", + "sha256:075b0fdbaea81afcac5a39a0d1bb91de887dd0d93bf692a5dd69c430e7fc58cb", + "sha256:07e3755e0f070bc31567dfe447a02011bfa8444239b3e9e5cca6773a22133839", + "sha256:0ed3465acf8c7c10aa2e0f3d9671da410ead63b38a77283ef464cbb64275df58", + "sha256:17e095f7f96a4b9f24b93c2c915f31a5201a6316618d919b0593afb070a5270e", + "sha256:1d85ca137756d62c8138c971453cafe64741adad1f6a7e63a22a5a8abdbd19fa", + "sha256:20605bfad484e1341b2cbfea0708e4b211d233716604846baa54b94821f487cb", + "sha256:23f93e74409c210de4de270d4bf88fb8ab736a7400f74210df63a93728cf70d6", + "sha256:2bb7cae741de1aa03e3dd3a7d98c304871eb155921ca1f0d7cc11f5aade913fd", + "sha256:2e3ff69ab203b54ce5c480c3ccbe959394ea5beef6bd5ad1785457df7acea92e", + "sha256:30fe317332de0e50195665bc61a27d46e903d682f94042c36b3f88cb84bd7958", + "sha256:3576e173e7b4f88f683b4de7db0c2af1b209bb48b2bf1c827a6f3564fad59a97", + "sha256:35ed5714467fc606551db26f80ee5d6aa1f01185586a7bccd96f179c4b974a11", + "sha256:41c66bd6750237a8ed23028a6c9173dc0c92dc24c473e771d3bfb9ee817700c3", + "sha256:48b4f4810117a9072a5aa70f7fea5f86fa9efbe9a798312e0a05044bd707cc33", + "sha256:4abf35e16f4b639daaf05a2602c1b1d47370e01babf9821306aa138924e3fe92", + "sha256:4fba661a4966adbd2c3c08d3caad6822ecb6878f5456588e2475ae23a6e47929", + "sha256:5e85dcfc5d0f374955015ae12c08365b565c6f1eaf36dd182476a4d8e5a1cdb7", + "sha256:77f9d16f7970791f17ecce7e7f101548314ed1ee2583d4268601f30af3170856", + "sha256:7ee36d5113b6506b97f45f2e8447cb9af146e60e3f527d93013d19f6d0405f3b", + "sha256:7fab29411d75c2eb48070020a40f80255936d7c31357b086e5931c107d48306e", + "sha256:85289c25f658e3260b00178757c87f033f3d4b3e40aa4abdd4dc875ff11a94fb", + "sha256:886f459db10c0f9d17c87d6594e77be915f18d343ee138e68d259eb385f044a8", + "sha256:897c539f0f3b2c3a715be651322bef2167de1cdc276b3f370ae81a3bda62df71", + "sha256:8fbe1768feafd3d0156556677b8ff234c7bf94a8110e906b2d73506f577a3269", + "sha256:9267e4fba27e6dd1008c4f2983cc548c98b4be4444e3e342db11296c0f45512f", + "sha256:9486ebda015913909bc28763c6b92fcc3b5e5a67dee4674bceed112109f5dfb8", + "sha256:956187ff49db7014ceb31e88fcacf4cf63371e6e44d209cf8816cd4a2d61e11a", + "sha256:a56735c35a3704603d9d7b243ee06139f0837bcac2171d9ba1d638ce1df0742a", + "sha256:ab1fea8832976ad0bebb11f652b692c328043057d35e9ebc78ab0a7a30cf9a70", + "sha256:adf35d88d9cffc202e6046e4c32e1e11a1d0238b2fcf095c94f109e510ececea", + "sha256:af23b9ca9a874ef0ec20e44467b8edd556c37b0f46f93abfa93752ea7c0e8d1e", + "sha256:b3794cea825f101fe0df9af8a00f9fad8e119c91e39a28636b95ee2b45b6c2e5", + "sha256:bb11c982a849dc22782210b01d0c1b98eb3696ce655d58a54180774e4880ac66", + "sha256:be30cd315db0168063a1755fa20a31119da91afa51da2907553493516e165640", + "sha256:c6238d30dcff141de076344cf7f52468de61729c2f70d776fce12f55fe8df790", + "sha256:cb1e44d860345ab5d4f533b6c37565a22f403277f44c4d2d5e06c325da959883", + "sha256:d4bfe3bc3976ccaeb4ae32f51e631964e2f0e85b2b752721b7a02de5ce3b7f27", + "sha256:d8ee91e1c295beb5c132ebd78616814de26fedba6aa8687ea460c7f5eb289b72", + "sha256:e3c00cb5c71da655e1e5161481455479b613d500dd1bd252aa01df4f037c641f", + "sha256:e9cec3a62d146e8e122d159ab93ac32c988e2ec0dcb1e18e9e53ff2da4fbd30c", + "sha256:ef4e53e2fdc997d91f5b682f81f7dc9661db9a437acce28745d765d251902d85", + "sha256:f0148988af0182a0a4e5020e7c168014f2c55a16d11179610f7883dd48ac0ebe", + "sha256:f20f9f430c33597887ba9bd76635476928e76cad2981643ca8be277b8e97aa96", + "sha256:f5930d334c2f607711d54761956aedf8137f83f1b764b9640be21d25a976f3a4", + "sha256:f6a28e87ba69f3a4f30d775b179aac55be1ce59f55799328a0d9b6df8f16b39d", + "sha256:f9ee98d658a146cb6507be720a0ce1b44f2abef8fb43c2859791d91aace17cd5" + ], + "version": "==2021.11.2" }, "requests": { "hashes": [ @@ -940,11 +979,11 @@ }, "soupsieve": { "hashes": [ - "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc", - "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b" + "sha256:617ffc4d0dfd39c66f4d1413a6e165663a34eca86be9b54f97b91756300ff6df", + "sha256:e4860f889dfa88774c07da0b276b70c073b6470fa1a4a8350800bb7bce3dcc76" ], "markers": "python_version >= '3.6'", - "version": "==2.2.1" + "version": "==2.3" }, "sqlparse": { "hashes": [ From 621dfd0ba7792668181965e704a9c4a0923f195f Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Mon, 8 Nov 2021 08:43:27 -0800 Subject: [PATCH 10/13] reduce differences between dev and GitHub Actions --- .github/workflows/pre-commit.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 885752a7..9fb8109c 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -36,7 +36,7 @@ jobs: DEBIAN_FRONTEND: noninteractive run: | sudo apt-get update - sudo apt-get install -y pandoc + sudo apt-get install -y --no-install-recommends gcc gettext pandoc # https://github.com/actions/checkout - name: Checkout cc-licenses @@ -67,22 +67,22 @@ jobs: - name: Install Python dependencies run: | - pipenv sync --dev + pipenv sync --dev --system - name: Update Django database schema env: DATA_REPOSITORY_DIR: cc-licenses-data run: | - pipenv run ./manage.py migrate + ./manage.py migrate - name: Start Django development web server env: DATA_REPOSITORY_DIR: cc-licenses-data run: | - pipenv run ./manage.py runserver &>/dev/null & + ./manage.py runserver &>/dev/null & # https://github.com/pre-commit/action - - uses: pre-commit/action@v2.0.0 + - uses: pre-commit/action@v2.0.3 env: DATA_REPOSITORY_DIR: cc-licenses-data From c19896f2744e437626362d2867ac657efc7b97b1 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Mon, 8 Nov 2021 10:10:33 -0800 Subject: [PATCH 11/13] Use pipenv 2021.5.29 to circumvent errors on Ubuntu 20.04: https://github.com/pypa/pipenv/issues/4833 --- .github/workflows/pre-commit.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 9fb8109c..6163d82d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -61,9 +61,11 @@ jobs: python-version: "3.7" - name: Install pipenv + # Use pipenv 2021.5.29 to circumvent errors: + # https://github.com/pypa/pipenv/issues/4833 run: | - python -m pip install --upgrade pip - python -m pip install pipenv + python -m pip install --no-cache-dir --upgrade pip + python -m pip install --no-cache-dir pipenv==2021.5.29 - name: Install Python dependencies run: | From a10e2cc3206ad0ccd528f5e796dd0b94fef589a9 Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Mon, 8 Nov 2021 10:19:35 -0800 Subject: [PATCH 12/13] remove pipenv run (assume system install) --- .pre-commit-config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d21e9326..426895d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,25 +29,25 @@ repos: hooks: - id: black name: "Black" - entry: pipenv run black --check + entry: black --check language: system types: ["file", "python", "text"] - id: flake8 name: "Flake8" - entry: pipenv run flake8 + entry: flake8 language: system types: ["file", "python", "text"] - id: isort name: "isort" - entry: pipenv run isort --check --filter-files + entry: isort --check --filter-files language: system types: ["file", "python", "text"] - id: missing-migrations name: 'Missing migrations' - entry: pipenv run ./manage.py makemigrations --check + entry: ./manage.py makemigrations --check language: system types: ["file", "python", "text"] pass_filenames: false @@ -55,7 +55,7 @@ repos: # This test fails if web server is not running - id: coverage-test name: 'Run Django tests' - entry: pipenv run coverage run manage.py test --noinput --keepdb + entry: coverage run manage.py test --noinput --keepdb language: system types: ["file", "python", "text"] pass_filenames: false @@ -63,7 +63,7 @@ repos: - id: coverage-report name: 'Confirm test coverage' - entry: pipenv run coverage report + entry: coverage report language: system types: ["file", "python", "text"] pass_filenames: false From 4dfc9bb197521d6d2e323ef6d618b3dd800bbc3a Mon Sep 17 00:00:00 2001 From: Timid Robot Zehta Date: Mon, 8 Nov 2021 10:41:18 -0800 Subject: [PATCH 13/13] remove all but essential changes from pre-commit workflow --- .github/workflows/pre-commit.yml | 17 +++++++++-------- .pre-commit-config.yaml | 12 ++++++------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 6163d82d..baac2d00 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -36,7 +36,7 @@ jobs: DEBIAN_FRONTEND: noninteractive run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends gcc gettext pandoc + sudo apt-get install -y pandoc # https://github.com/actions/checkout - name: Checkout cc-licenses @@ -61,30 +61,31 @@ jobs: python-version: "3.7" - name: Install pipenv - # Use pipenv 2021.5.29 to circumvent errors: + # Use pipenv 2021.5.29 to circumvent errors with 2021.11.5.post0 on + # Ubuntu 20.04 # https://github.com/pypa/pipenv/issues/4833 run: | - python -m pip install --no-cache-dir --upgrade pip - python -m pip install --no-cache-dir pipenv==2021.5.29 + python -m pip install --upgrade pip + python -m pip install pipenv==2021.5.29 - name: Install Python dependencies run: | - pipenv sync --dev --system + pipenv sync --dev - name: Update Django database schema env: DATA_REPOSITORY_DIR: cc-licenses-data run: | - ./manage.py migrate + pipenv run ./manage.py migrate - name: Start Django development web server env: DATA_REPOSITORY_DIR: cc-licenses-data run: | - ./manage.py runserver &>/dev/null & + pipenv run ./manage.py runserver &>/dev/null & # https://github.com/pre-commit/action - - uses: pre-commit/action@v2.0.3 + - uses: pre-commit/action@v2.0.0 env: DATA_REPOSITORY_DIR: cc-licenses-data diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 426895d2..d21e9326 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,25 +29,25 @@ repos: hooks: - id: black name: "Black" - entry: black --check + entry: pipenv run black --check language: system types: ["file", "python", "text"] - id: flake8 name: "Flake8" - entry: flake8 + entry: pipenv run flake8 language: system types: ["file", "python", "text"] - id: isort name: "isort" - entry: isort --check --filter-files + entry: pipenv run isort --check --filter-files language: system types: ["file", "python", "text"] - id: missing-migrations name: 'Missing migrations' - entry: ./manage.py makemigrations --check + entry: pipenv run ./manage.py makemigrations --check language: system types: ["file", "python", "text"] pass_filenames: false @@ -55,7 +55,7 @@ repos: # This test fails if web server is not running - id: coverage-test name: 'Run Django tests' - entry: coverage run manage.py test --noinput --keepdb + entry: pipenv run coverage run manage.py test --noinput --keepdb language: system types: ["file", "python", "text"] pass_filenames: false @@ -63,7 +63,7 @@ repos: - id: coverage-report name: 'Confirm test coverage' - entry: coverage report + entry: pipenv run coverage report language: system types: ["file", "python", "text"] pass_filenames: false