From 84648238a9b4a478f43b2fc792404f024dbf6891 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 28 Nov 2024 18:06:24 -0500 Subject: [PATCH 01/24] added translation pull endpoint --- core/i18nilize/urls.py | 6 +++--- core/i18nilize/views.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/core/i18nilize/urls.py b/core/i18nilize/urls.py index 5048bee..a4c6e03 100644 --- a/core/i18nilize/urls.py +++ b/core/i18nilize/urls.py @@ -1,10 +1,10 @@ from django.urls import path -from . import views -from .views import TokenView, TranslationView +from .views import TokenView, TranslationView, ProcessTranslationsView, PullTranslations, GetTokens urlpatterns = [ path('token/', TokenView.as_view(), name='create-token'), path('token//', TokenView.as_view(), name='read-token'), path('translation', TranslationView.as_view(), name='translation'), - path('translations', views.ProcessTranslationsView.as_view(), name='process-translations') + path('translations', ProcessTranslationsView.as_view(), name='process-translations'), + path('translations/pull', PullTranslations.as_view(), name='pull-translations'), ] diff --git a/core/i18nilize/views.py b/core/i18nilize/views.py index 8adc567..75f09bd 100644 --- a/core/i18nilize/views.py +++ b/core/i18nilize/views.py @@ -304,4 +304,32 @@ def delete(self, request): # Throw a bad request if the translation doesn't exist except Translation.DoesNotExist: - return Response({"error": "translation doesn't exist!"}, status=status.HTTP_404_NOT_FOUND) \ No newline at end of file + return Response({"error": "translation doesn't exist!"}, status=status.HTTP_404_NOT_FOUND) + +class PullTranslations(APIView): + """ + Pulls all translations for a given token. + """ + @require_valid_token + def get(self, request): + token = request.token + + try: + translations = Translation.objects.filter(token=token) + + # Consolidate all translations into single dictionary following + # the format of local translation files to overwrite files easily. + response_data = {} + for translation in translations: + language = translation.language.lower() + original_word = translation.original_word + translated_word = translation.translated_word + + if language not in response_data: + response_data[language] = {} + response_data[language][original_word] = translated_word + except Exception as e: + print(e) + return Response({"error": "could not fetch translations"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + return Response(response_data, status=status.HTTP_200_OK) From 525f605e1af30fbf7f26c66c881fc9dfbb0bfecd Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 28 Nov 2024 22:19:50 -0500 Subject: [PATCH 02/24] implemented CLI pull command --- core/i18nilize/urls.py | 4 ++-- i18nilize/src/internationalize/api_helpers.py | 2 +- .../src/internationalize/command_line.py | 7 +++++- i18nilize/src/internationalize/globals.py | 5 +++- i18nilize/src/internationalize/helpers.py | 24 ++++++++++++++++++- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/core/i18nilize/urls.py b/core/i18nilize/urls.py index a4c6e03..db5aa02 100644 --- a/core/i18nilize/urls.py +++ b/core/i18nilize/urls.py @@ -1,10 +1,10 @@ from django.urls import path -from .views import TokenView, TranslationView, ProcessTranslationsView, PullTranslations, GetTokens +from .views import TokenView, TranslationView, ProcessTranslationsView, PullTranslations urlpatterns = [ path('token/', TokenView.as_view(), name='create-token'), path('token//', TokenView.as_view(), name='read-token'), path('translation', TranslationView.as_view(), name='translation'), path('translations', ProcessTranslationsView.as_view(), name='process-translations'), - path('translations/pull', PullTranslations.as_view(), name='pull-translations'), + path('translations/pull/', PullTranslations.as_view(), name='pull-translations'), ] diff --git a/i18nilize/src/internationalize/api_helpers.py b/i18nilize/src/internationalize/api_helpers.py index 7a231e7..179a3a7 100644 --- a/i18nilize/src/internationalize/api_helpers.py +++ b/i18nilize/src/internationalize/api_helpers.py @@ -1,7 +1,7 @@ # api_helpers.py import requests -from . import globals +from . import globals import sys def create_token(): diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index e2c4124..247e51f 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -30,6 +30,9 @@ def cli(): delete_parser.add_argument('original_word') delete_parser.add_argument('translated_word') + # sub parser for pull + pull_parser = subparsers.add_parser('pull') + # the subparser is used because different CLIs use a different amount of inputs args = parser.parse_args() @@ -41,7 +44,9 @@ def cli(): add_update_translated_word(args.language, args.original_word, args.translated_word) elif args.command == 'delete': delete_translation(args.language, args.original_word, args.translated_word) + elif args.command == 'pull': + pull_translations() else: print("Invalid command") -cli() \ No newline at end of file +cli() diff --git a/i18nilize/src/internationalize/globals.py b/i18nilize/src/internationalize/globals.py index a1b68e8..fd420df 100644 --- a/i18nilize/src/internationalize/globals.py +++ b/i18nilize/src/internationalize/globals.py @@ -1,13 +1,16 @@ # globals.py +# Test Token: "c84234c3-b507-4ed0-a6eb-8b10116cdef1" + class GlobalToken: def __init__(self): - self.value = "dummy" + self.value = "dummy" API_BASE_URL = "http://localhost:8000/api/" TOKEN_ENDPOINT = f"{API_BASE_URL}token/" TRANSLATIONS_ENDPOINT = f"{API_BASE_URL}translations/" +PULL_TRANSLATIONS_ENDPOINT = f"{TRANSLATIONS_ENDPOINT}pull/" LANGUAGES_DIR = 'src/internationalize/languages' diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index e95cabb..3469616 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -73,6 +73,28 @@ def delete_translation(language, original_word, translated_word): json.dump(data, file, indent=4) print(f"Translation for '{original_word}' deleted successfully from language '{language}'.") +""" +Pulls all translations assigned to the microservices' token +and overwrites all language files to sync translations. +""" +def pull_translations(): + token = globals.token.value + + try: + all_translations = requests.get(globals.PULL_TRANSLATIONS_ENDPOINT, headers={'Token': token}) + except Exception as e: + print("Error: Could not fetch translations from database.", e) + + # Overwrite all translation files + all_transactions_dict = all_translations.json() + for language, translations in all_transactions_dict.items(): + file_name = f"{language}_2.json" + curr_file_path = os.path.join(globals.LANGUAGES_DIR, file_name) + with open(curr_file_path, "w+") as file: + json.dump(translations, file, indent=4) + + print(f"Pulled all translations from the database.") + # Input: # - file_path: path of json file # Output: Token in json file @@ -85,7 +107,7 @@ def get_token(file_path): # Input: a JSON object # Output: None, but creates a local JSON file containing the object def create_json(json_object, language): - base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) + base_dir = os.path.join(oparamss.path.dirname(os.path.abspath(__file__))) file_path = os.path.join(base_dir, 'languages', f'{language}.json') with open(file_path, 'w') as outfile: outfile.write(json_object) From cfa6b6c195820461f599f26885194c1696fb7682 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 28 Nov 2024 22:22:27 -0500 Subject: [PATCH 03/24] fixed bug --- i18nilize/src/internationalize/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index 3469616..a756d1c 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -107,7 +107,7 @@ def get_token(file_path): # Input: a JSON object # Output: None, but creates a local JSON file containing the object def create_json(json_object, language): - base_dir = os.path.join(oparamss.path.dirname(os.path.abspath(__file__))) + base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) file_path = os.path.join(base_dir, 'languages', f'{language}.json') with open(file_path, 'w') as outfile: outfile.write(json_object) From 1619b4293e9eb6bbca5a58a0a6a75162c94e0db2 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 28 Nov 2024 22:23:08 -0500 Subject: [PATCH 04/24] fixed typo --- i18nilize/src/internationalize/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index a756d1c..5efec83 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -88,7 +88,7 @@ def pull_translations(): # Overwrite all translation files all_transactions_dict = all_translations.json() for language, translations in all_transactions_dict.items(): - file_name = f"{language}_2.json" + file_name = f"{language}.json" curr_file_path = os.path.join(globals.LANGUAGES_DIR, file_name) with open(curr_file_path, "w+") as file: json.dump(translations, file, indent=4) From 07af7fe938e5611655e8a15b84a5eff0389b5dd5 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Fri, 29 Nov 2024 21:28:39 -0500 Subject: [PATCH 05/24] added test for pull endpoint --- core/i18nilize/tests.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/core/i18nilize/tests.py b/core/i18nilize/tests.py index f0caedb..9367d7e 100644 --- a/core/i18nilize/tests.py +++ b/core/i18nilize/tests.py @@ -1010,4 +1010,32 @@ def test_bulk_translations(self): # validate get requests response = self.client.get(reverse('translation'), query_params=query_params_get[i], headers=headers) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.data['error'], 'Translation not found for given language and word!') \ No newline at end of file + self.assertEqual(response.data['error'], 'Translation not found for given language and word!') + +class PullTranslations(APITestCase): + + def setUp(self): + token = Token.objects.create() + self.TEST_TOKEN = str(token.value) + + def test_pulling_no_assigned_translations(self): + pass + + def test_pulling_multiple_assigned_translations(self): + headers = { + 'Token': self.TEST_TOKEN + } + query_params = { + 'language': 'spanish', + 'hello': 'hola' + } + expected_response = { + 'spanish': { + 'hello': 'hola' + } + } + self.client.post(reverse('translation'), query_params=query_params, headers=headers, format='json') + + response = self.client.get(reverse('pull-translations'), headers=headers, format='json') + response_data = response.json() + self.assertEqual(response_data, expected_response) From fe81184eb79c5c1e7b0edd5c42f1300d0950a350 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Fri, 29 Nov 2024 22:47:03 -0500 Subject: [PATCH 06/24] fixed test for pulling multiple translations --- core/i18nilize/tests.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/core/i18nilize/tests.py b/core/i18nilize/tests.py index 9367d7e..53614c3 100644 --- a/core/i18nilize/tests.py +++ b/core/i18nilize/tests.py @@ -1018,23 +1018,34 @@ def setUp(self): token = Token.objects.create() self.TEST_TOKEN = str(token.value) - def test_pulling_no_assigned_translations(self): - pass - def test_pulling_multiple_assigned_translations(self): headers = { 'Token': self.TEST_TOKEN } - query_params = { - 'language': 'spanish', - 'hello': 'hola' + translations_data = { + 'translations': [ + { + 'language': 'spanish', + 'hello': 'hola', + 'bye': 'chau', + }, + { + 'language': 'french', + 'hello': 'bonjour', + } + ] } expected_response = { 'spanish': { - 'hello': 'hola' + 'hello': 'hola', + 'bye': 'chau', + }, + 'french': { + 'hello': 'bonjour', } } - self.client.post(reverse('translation'), query_params=query_params, headers=headers, format='json') + + self.client.post(reverse('process-translations'), translations_data, headers=headers, format='json') response = self.client.get(reverse('pull-translations'), headers=headers, format='json') response_data = response.json() From 4676c7e546cea1aaee8824adc3c93a2cb1e6dde5 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Fri, 29 Nov 2024 22:49:28 -0500 Subject: [PATCH 07/24] added explicit post parameter for clarity --- core/i18nilize/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/i18nilize/tests.py b/core/i18nilize/tests.py index 53614c3..a3b1abf 100644 --- a/core/i18nilize/tests.py +++ b/core/i18nilize/tests.py @@ -1045,7 +1045,7 @@ def test_pulling_multiple_assigned_translations(self): } } - self.client.post(reverse('process-translations'), translations_data, headers=headers, format='json') + self.client.post(reverse('process-translations'), data=translations_data, headers=headers, format='json') response = self.client.get(reverse('pull-translations'), headers=headers, format='json') response_data = response.json() From e5d5ee50d66106e94e1e1b8992802e3cfb85d69c Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Tue, 3 Dec 2024 20:32:46 -0500 Subject: [PATCH 08/24] added pip side test for pulling translations --- i18nilize/src/internationalize/helpers.py | 4 +- i18nilize/tests/test_cli.py | 59 ++++++++++++++++++++--- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index 5efec83..05cff0f 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -77,7 +77,7 @@ def delete_translation(language, original_word, translated_word): Pulls all translations assigned to the microservices' token and overwrites all language files to sync translations. """ -def pull_translations(): +def pull_translations(write_directory=globals.LANGUAGES_DIR): token = globals.token.value try: @@ -89,7 +89,7 @@ def pull_translations(): all_transactions_dict = all_translations.json() for language, translations in all_transactions_dict.items(): file_name = f"{language}.json" - curr_file_path = os.path.join(globals.LANGUAGES_DIR, file_name) + curr_file_path = os.path.join(write_directory, file_name) with open(curr_file_path, "w+") as file: json.dump(translations, file, indent=4) diff --git a/i18nilize/tests/test_cli.py b/i18nilize/tests/test_cli.py index f742f86..2036ecd 100644 --- a/i18nilize/tests/test_cli.py +++ b/i18nilize/tests/test_cli.py @@ -1,6 +1,7 @@ -import unittest, os, json, timeit +import unittest, os, json, timeit, shutil from unittest.mock import patch -from src.internationalize.helpers import delete_translation, get_json, make_translation_map, get_translation, add_language, add_update_translated_word +from src.internationalize.helpers import delete_translation, get_json, make_translation_map, get_translation, add_language, add_update_translated_word, pull_translations +from src.internationalize import globals # Create your tests here. # To test: @@ -76,14 +77,14 @@ def test_delete_translation_success(self): language = "German" add_language(language) file_path = os.path.join(self.languages_dir, f"{language.lower()}.json") - + initial_translations = { "goodbye": "auf Wiedersehen", "thank you": "danke" } with open(file_path, "w") as file: json.dump(initial_translations, file, indent=4) - + data = get_json(file_path) self.assertIn("goodbye", data) self.assertEqual(data["goodbye"], "auf Wiedersehen") @@ -111,13 +112,13 @@ def test_delete_translation_word_does_not_exist(self): language = "Chinese" add_language(language) file_path = os.path.join(self.languages_dir, f"{language.lower()}.json") - + initial_translations = { "thank you": "谢谢" } with open(file_path, "w") as file: json.dump(initial_translations, file, indent=4) - + data = get_json(file_path) self.assertIn("thank you", data) self.assertNotIn("good morning", data) @@ -136,13 +137,13 @@ def test_delete_translation_word_mismatch(self): language = "Korean" add_language(language) file_path = os.path.join(self.languages_dir, f"{language.lower()}.json") - + initial_translations = { "welcome": "환영합니다" } with open(file_path, "w") as file: json.dump(initial_translations, file, indent=4) - + data = get_json(file_path) self.assertIn("welcome", data) self.assertEqual(data["welcome"], "환영합니다") @@ -157,5 +158,47 @@ def test_delete_translation_word_mismatch(self): self.assertIn("welcome", data) self.assertEqual(data["welcome"], "환영합니다") + def test_pull_translations(self): + prev_token = globals.token.value + test_token = "c84234c3-b507-4ed0-a6eb-8b10116cdef1" + globals.token.value = test_token + + # Create temporary directories to pull translations + temp_dir_path = os.path.join(self.languages_dir, "temp") + files_to_copy = ["spanish.json", "french.json"] + if not os.path.exists(temp_dir_path): + os.mkdir(temp_dir_path) + for file_name in files_to_copy: + curr_file_path = os.path.join(self.languages_dir, file_name) + new_file_path = os.path.join(temp_dir_path, file_name) + shutil.copy(curr_file_path, new_file_path) + + # Expected content after pulling from API + expected_file_content = { + "fr.json": { + "hello": "bonjour" + }, + "french.json": { + "hello": "bonjour" + }, + "spanish.json": { + "hello": "hola", + "bye": "chau", + "what": "que", + "como": "how", + "codigo": "code" + } + } + + pull_translations(write_directory=temp_dir_path) + for file_name in os.listdir(temp_dir_path): + file_path = os.path.join(temp_dir_path, file_name) + file_content = get_json(file_path) + self.assertEqual(file_content, expected_file_content[file_name]) + + # Cleanup + shutil.rmtree(temp_dir_path) + globals.token.value = prev_token + if __name__ == '__main__': unittest.main() From b849cc88cedac0a10e982c5ecd1774cf2faf9442 Mon Sep 17 00:00:00 2001 From: Andrew Ahn Date: Wed, 4 Dec 2024 04:47:54 -0800 Subject: [PATCH 09/24] Implemented the push command --- core/i18nilize/urls.py | 1 + .../src/internationalize/command_line.py | 7 +++- i18nilize/src/internationalize/globals.py | 1 + i18nilize/src/internationalize/helpers.py | 39 +++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/core/i18nilize/urls.py b/core/i18nilize/urls.py index db5aa02..0bd82f6 100644 --- a/core/i18nilize/urls.py +++ b/core/i18nilize/urls.py @@ -7,4 +7,5 @@ path('translation', TranslationView.as_view(), name='translation'), path('translations', ProcessTranslationsView.as_view(), name='process-translations'), path('translations/pull/', PullTranslations.as_view(), name='pull-translations'), + path('translations/push/', TranslationView.as_view(), name='push-translations'), ] diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index 247e51f..064c051 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -1,7 +1,7 @@ #from src.internationalize.helpers import add_language import json import argparse -from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation +from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation, pull_translations, push_translations def cli(): # initialize the parser @@ -33,6 +33,9 @@ def cli(): # sub parser for pull pull_parser = subparsers.add_parser('pull') + # sub parser for push + push_parser = subparsers.add_parser('push') + # the subparser is used because different CLIs use a different amount of inputs args = parser.parse_args() @@ -46,6 +49,8 @@ def cli(): delete_translation(args.language, args.original_word, args.translated_word) elif args.command == 'pull': pull_translations() + elif args.command == 'push': + push_translations() else: print("Invalid command") diff --git a/i18nilize/src/internationalize/globals.py b/i18nilize/src/internationalize/globals.py index fd420df..cc29ad1 100644 --- a/i18nilize/src/internationalize/globals.py +++ b/i18nilize/src/internationalize/globals.py @@ -11,6 +11,7 @@ def __init__(self): TOKEN_ENDPOINT = f"{API_BASE_URL}token/" TRANSLATIONS_ENDPOINT = f"{API_BASE_URL}translations/" PULL_TRANSLATIONS_ENDPOINT = f"{TRANSLATIONS_ENDPOINT}pull/" +PUSH_TRANSLATIONS_ENDPOINT = f"{TRANSLATIONS_ENDPOINT}push/" LANGUAGES_DIR = 'src/internationalize/languages' diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index 5efec83..9b2331f 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -4,6 +4,7 @@ import hashlib import requests from . import globals +from diffing_processor import get_changed_translations # Function to parse json file, given its path def get_json(file_path): @@ -95,6 +96,44 @@ def pull_translations(): print(f"Pulled all translations from the database.") +""" +Push all local translations to the API. +""" +def push_translations(): + token = globals.token.value + changed_translations = get_changed_translations() + + for language in changed_translations: + created = changed_translations[language]["created"] + modified = changed_translations[language]["modified"] + deleted = changed_translations[language]["deleted"] + + # Post a new entry for each new translation + for original_word in created: + try: + response = requests.post(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, + params={'language': language, original_word: created[original_word]}) + except Exception as e: + print("Error: Could not create translation.", e) + + # Patch the appropriate entry for each modified translation + for original_word in modified: + try: + response = requests.patch(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, + params={'language': language, original_word: modified[original_word]}) + except Exception as e: + print("Error: Could not patch translation.", e) + + # Delete the appropriate entry for each deleted translation + for original_word in deleted: + try: + response = requests.delete(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, + params={'language': language, original_word: deleted[original_word]}) + except Exception as e: + print("Error: Could not delete translation.", e) + + print(f"Pushed all translations from the database.") + # Input: # - file_path: path of json file # Output: Token in json file From ecc871e8a8f30dea01b92797d12cb1fd090ce060 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Wed, 4 Dec 2024 21:34:31 -0500 Subject: [PATCH 10/24] fixed circular imports error --- .../src/internationalize/diffing_processor.py | 4 +- i18nilize/src/internationalize/helpers.py | 61 ----------------- .../src/internationalize/sync_processor.py | 66 +++++++++++++++++++ i18nilize/tests/test_cli.py | 61 +++++++++++++++-- 4 files changed, 126 insertions(+), 66 deletions(-) create mode 100644 i18nilize/src/internationalize/sync_processor.py diff --git a/i18nilize/src/internationalize/diffing_processor.py b/i18nilize/src/internationalize/diffing_processor.py index f024ff7..25fd123 100644 --- a/i18nilize/src/internationalize/diffing_processor.py +++ b/i18nilize/src/internationalize/diffing_processor.py @@ -48,7 +48,9 @@ def setup(self): """ Updates translation files with new changes and updates hashes in metadata. """ - def update_to_current_state(self, hash_dict): + def update_to_current_state(self, hash_dict=None): + if hash_dict == None: + hash_dict = compute_hashes(self.curr_translation_files_dir) self.update_metadata(hash_dict) self.sync_translations() diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index c2ed23f..e95cabb 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -4,7 +4,6 @@ import hashlib import requests from . import globals -from diffing_processor import get_changed_translations # Function to parse json file, given its path def get_json(file_path): @@ -74,66 +73,6 @@ def delete_translation(language, original_word, translated_word): json.dump(data, file, indent=4) print(f"Translation for '{original_word}' deleted successfully from language '{language}'.") -""" -Pulls all translations assigned to the microservices' token -and overwrites all language files to sync translations. -""" -def pull_translations(write_directory=globals.LANGUAGES_DIR): - token = globals.token.value - - try: - all_translations = requests.get(globals.PULL_TRANSLATIONS_ENDPOINT, headers={'Token': token}) - except Exception as e: - print("Error: Could not fetch translations from database.", e) - - # Overwrite all translation files - all_transactions_dict = all_translations.json() - for language, translations in all_transactions_dict.items(): - file_name = f"{language}.json" - curr_file_path = os.path.join(write_directory, file_name) - with open(curr_file_path, "w+") as file: - json.dump(translations, file, indent=4) - - print(f"Pulled all translations from the database.") - -""" -Push all local translations to the API. -""" -def push_translations(): - token = globals.token.value - changed_translations = get_changed_translations() - - for language in changed_translations: - created = changed_translations[language]["created"] - modified = changed_translations[language]["modified"] - deleted = changed_translations[language]["deleted"] - - # Post a new entry for each new translation - for original_word in created: - try: - response = requests.post(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, - params={'language': language, original_word: created[original_word]}) - except Exception as e: - print("Error: Could not create translation.", e) - - # Patch the appropriate entry for each modified translation - for original_word in modified: - try: - response = requests.patch(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, - params={'language': language, original_word: modified[original_word]}) - except Exception as e: - print("Error: Could not patch translation.", e) - - # Delete the appropriate entry for each deleted translation - for original_word in deleted: - try: - response = requests.delete(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, - params={'language': language, original_word: deleted[original_word]}) - except Exception as e: - print("Error: Could not delete translation.", e) - - print(f"Pushed all translations from the database.") - # Input: # - file_path: path of json file # Output: Token in json file diff --git a/i18nilize/src/internationalize/sync_processor.py b/i18nilize/src/internationalize/sync_processor.py new file mode 100644 index 0000000..8b21088 --- /dev/null +++ b/i18nilize/src/internationalize/sync_processor.py @@ -0,0 +1,66 @@ +import requests, os, json +from . import globals +from src.internationalize.diffing_processor import DiffingProcessor + +""" +Pulls all translations assigned to the microservices' token +and overwrites all language files to sync translations. +""" +def pull_translations(write_directory=globals.LANGUAGES_DIR): + token = globals.token.value + diff_processor = DiffingProcessor(write_directory) + + try: + all_translations = requests.get(globals.PULL_TRANSLATIONS_ENDPOINT, headers={'Token': token}) + except Exception as e: + print("Error: Could not fetch translations from database.", e) + + # Overwrite all translation files + all_transactions_dict = all_translations.json() + for language, translations in all_transactions_dict.items(): + file_name = f"{language}.json" + curr_file_path = os.path.join(write_directory, file_name) + with open(curr_file_path, "w+") as file: + json.dump(translations, file, indent=4) + + diff_processor.update_to_current_state() + print(f"Pulled all translations from the database.") + +""" +Push all local translations to the API. +""" +def push_translations(translations_dir=globals.LANGUAGES_DIR): + token = globals.token.value + diff_processor = DiffingProcessor(translations_dir) + changed_translations = diff_processor.get_changed_translations() + + for language in changed_translations: + created = changed_translations[language]["created"] + modified = changed_translations[language]["modified"] + deleted = changed_translations[language]["deleted"] + + # Post a new entry for each new translation + for original_word in created: + try: + response = requests.post(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, + params={'language': language, original_word: created[original_word]}) + except Exception as e: + print("Error: Could not create translation.", e) + + # Patch the appropriate entry for each modified translation + for original_word in modified: + try: + response = requests.patch(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, + params={'language': language, original_word: modified[original_word]}) + except Exception as e: + print("Error: Could not patch translation.", e) + + # Delete the appropriate entry for each deleted translation + for original_word in deleted: + try: + response = requests.delete(globals.PUSH_TRANSLATIONS_ENDPOINT, headers={'Token': token}, + params={'language': language, original_word: deleted[original_word]}) + except Exception as e: + print("Error: Could not delete translation.", e) + + print(f"Pushed all translations from the database.") diff --git a/i18nilize/tests/test_cli.py b/i18nilize/tests/test_cli.py index 2036ecd..e23e378 100644 --- a/i18nilize/tests/test_cli.py +++ b/i18nilize/tests/test_cli.py @@ -1,7 +1,9 @@ import unittest, os, json, timeit, shutil from unittest.mock import patch -from src.internationalize.helpers import delete_translation, get_json, make_translation_map, get_translation, add_language, add_update_translated_word, pull_translations +from src.internationalize.helpers import delete_translation, get_json, make_translation_map, get_translation, add_language, add_update_translated_word from src.internationalize import globals +from src.internationalize.sync_processor import pull_translations, push_translations +from src.internationalize.diffing_processor import DiffingProcessor # Create your tests here. # To test: @@ -162,12 +164,19 @@ def test_pull_translations(self): prev_token = globals.token.value test_token = "c84234c3-b507-4ed0-a6eb-8b10116cdef1" globals.token.value = test_token + temp_dir_path = os.path.join(self.languages_dir, "temp") + diff_processor = DiffingProcessor(temp_dir_path) + if os.path.exists(diff_processor.diff_state_root_dir): + shutil.rmtree(diff_processor.diff_state_root_dir) + diff_processor.setup() # Create temporary directories to pull translations - temp_dir_path = os.path.join(self.languages_dir, "temp") files_to_copy = ["spanish.json", "french.json"] - if not os.path.exists(temp_dir_path): - os.mkdir(temp_dir_path) + if os.path.exists(temp_dir_path): + shutil.rmtree(temp_dir_path) + os.mkdir(temp_dir_path) + + # Copy files into temp dir to test overwriting for file_name in files_to_copy: curr_file_path = os.path.join(self.languages_dir, file_name) new_file_path = os.path.join(temp_dir_path, file_name) @@ -197,8 +206,52 @@ def test_pull_translations(self): self.assertEqual(file_content, expected_file_content[file_name]) # Cleanup + shutil.rmtree(diff_processor.diff_state_root_dir) shutil.rmtree(temp_dir_path) globals.token.value = prev_token + # def test_push_translations(self): + # prev_token = globals.token.value + # test_token = "c53a8272-7e50-4757-90c4-2befd33d70cf" + # globals.token.value = test_token + # + # # Create temporary directories to pull translations + # temp_dir_path = os.path.join(self.languages_dir, "temp") + # files_to_copy = ["spanish.json", "french.json"] + # if os.path.exists(temp_dir_path): + # shutil.rmtree(temp_dir_path) + # os.mkdir(temp_dir_path) + # for file_name in files_to_copy: + # curr_file_path = os.path.join(self.languages_dir, file_name) + # new_file_path = os.path.join(temp_dir_path, file_name) + # shutil.copy(curr_file_path, new_file_path) + # + # # Expected content after pulling from API + # expected_file_content = { + # "fr.json": { + # "hello": "bonjour" + # }, + # "french.json": { + # "hello": "bonjour" + # }, + # "spanish.json": { + # "hello": "hola", + # "bye": "chau", + # "what": "que", + # "como": "how", + # "codigo": "code" + # } + # } + # + # pull_translations(write_directory=temp_dir_path) + # for file_name in os.listdir(temp_dir_path): + # file_path = os.path.join(temp_dir_path, file_name) + # file_content = get_json(file_path) + # self.assertEqual(file_content, expected_file_content[file_name]) + # + # # Cleanup + # shutil.rmtree(temp_dir_path) + # globals.token.value = prev_token + if __name__ == '__main__': unittest.main() From a76b50b7111bc83e3243ffc5057ab582f77f395c Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Wed, 4 Dec 2024 22:26:12 -0500 Subject: [PATCH 11/24] added test for push --- core/db.sqlite3 | Bin 147456 -> 147456 bytes core/i18nilize/urls.py | 3 +- core/i18nilize/views.py | 15 +++ .../src/internationalize/sync_processor.py | 1 + i18nilize/tests/test_cli.py | 95 ++++++++++-------- 5 files changed, 70 insertions(+), 44 deletions(-) diff --git a/core/db.sqlite3 b/core/db.sqlite3 index 60c13c56aa78b1ff1fbd5d00c98e8cbba06f7f4f..172df950a92937364a7082ae83fb4c75348af051 100644 GIT binary patch delta 219 zcmZo@;B08%oFL6uJyFJ)v3g;GKBI1PqW</', TokenView.as_view(), name='read-token'), + path('test/', TestTokenView.as_view(), name='test-token'), path('translation', TranslationView.as_view(), name='translation'), path('translations', ProcessTranslationsView.as_view(), name='process-translations'), path('translations/pull/', PullTranslations.as_view(), name='pull-translations'), diff --git a/core/i18nilize/views.py b/core/i18nilize/views.py index 75f09bd..35f489f 100644 --- a/core/i18nilize/views.py +++ b/core/i18nilize/views.py @@ -43,6 +43,21 @@ def get(self, request, value=None): except Token.DoesNotExist: return Response({'error': 'Token not found.'}, status=status.HTTP_404_NOT_FOUND) +class TestTokenView(APIView): + """ + Endpoint to delete all translations tied to a token for testing. + """ + @require_valid_token + def delete(self, request): + token = request.token + try: + translations = Translation.objects.filter(token=token) + for t in translations: + t.delete() + except Exception as e: + print(e) + return Response({'error': 'Could not delete all translations for given token.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response({'message': 'Deleted all translations tied to given token.'}, status=status.HTTP_200_OK) class ProcessTranslationsView(APIView): """ diff --git a/i18nilize/src/internationalize/sync_processor.py b/i18nilize/src/internationalize/sync_processor.py index 8b21088..8ff622e 100644 --- a/i18nilize/src/internationalize/sync_processor.py +++ b/i18nilize/src/internationalize/sync_processor.py @@ -63,4 +63,5 @@ def push_translations(translations_dir=globals.LANGUAGES_DIR): except Exception as e: print("Error: Could not delete translation.", e) + diff_processor.update_to_current_state() print(f"Pushed all translations from the database.") diff --git a/i18nilize/tests/test_cli.py b/i18nilize/tests/test_cli.py index e23e378..f6de20b 100644 --- a/i18nilize/tests/test_cli.py +++ b/i18nilize/tests/test_cli.py @@ -1,4 +1,4 @@ -import unittest, os, json, timeit, shutil +import unittest, os, json, timeit, shutil, requests from unittest.mock import patch from src.internationalize.helpers import delete_translation, get_json, make_translation_map, get_translation, add_language, add_update_translated_word from src.internationalize import globals @@ -12,6 +12,7 @@ class TestCLI(unittest.TestCase): def setUp(self): self.languages_dir = "src/internationalize/languages" + self.reset_token_endpoint = globals.API_BASE_URL + "test/" os.makedirs(self.languages_dir, exist_ok=True) def test_add_new_language(self): @@ -210,48 +211,56 @@ def test_pull_translations(self): shutil.rmtree(temp_dir_path) globals.token.value = prev_token - # def test_push_translations(self): - # prev_token = globals.token.value - # test_token = "c53a8272-7e50-4757-90c4-2befd33d70cf" - # globals.token.value = test_token - # - # # Create temporary directories to pull translations - # temp_dir_path = os.path.join(self.languages_dir, "temp") - # files_to_copy = ["spanish.json", "french.json"] - # if os.path.exists(temp_dir_path): - # shutil.rmtree(temp_dir_path) - # os.mkdir(temp_dir_path) - # for file_name in files_to_copy: - # curr_file_path = os.path.join(self.languages_dir, file_name) - # new_file_path = os.path.join(temp_dir_path, file_name) - # shutil.copy(curr_file_path, new_file_path) - # - # # Expected content after pulling from API - # expected_file_content = { - # "fr.json": { - # "hello": "bonjour" - # }, - # "french.json": { - # "hello": "bonjour" - # }, - # "spanish.json": { - # "hello": "hola", - # "bye": "chau", - # "what": "que", - # "como": "how", - # "codigo": "code" - # } - # } - # - # pull_translations(write_directory=temp_dir_path) - # for file_name in os.listdir(temp_dir_path): - # file_path = os.path.join(temp_dir_path, file_name) - # file_content = get_json(file_path) - # self.assertEqual(file_content, expected_file_content[file_name]) - # - # # Cleanup - # shutil.rmtree(temp_dir_path) - # globals.token.value = prev_token + def test_push_translations(self): + prev_token = globals.token.value + test_token = "a373fc5e-5b65-463e-b89e-1a37706a69dd" + globals.token.value = test_token + temp_dir_path = os.path.join(self.languages_dir, "temp") + diff_processor = DiffingProcessor(temp_dir_path) + + if os.path.exists(diff_processor.diff_state_root_dir): + shutil.rmtree(diff_processor.diff_state_root_dir) + if os.path.exists(temp_dir_path): + shutil.rmtree(temp_dir_path) + os.mkdir(temp_dir_path) + + # Initialize with no translations in either state + diff_processor.setup() + + # Deletes all translations tied to test_token + response = requests.delete(self.reset_token_endpoint, headers={'Token': test_token}) + self.assertTrue(response.ok) + + # Copy files to push to API + files_to_copy = ["spanish.json", "french.json"] + for file_name in files_to_copy: + curr_file_path = os.path.join(self.languages_dir, file_name) + new_file_path = os.path.join(temp_dir_path, file_name) + shutil.copy(curr_file_path, new_file_path) + + # Push changes, delete copied files, and pull + push_translations(translations_dir=temp_dir_path) + shutil.rmtree(temp_dir_path) + os.mkdir(temp_dir_path) + pull_translations(write_directory=temp_dir_path) + + # Expected content after pulling from API (same content that was pushed) + expected_file_content = {} + for file_name in files_to_copy: + copied_file_path = os.path.join(self.languages_dir, file_name) + expected_file_content[file_name] = get_json(copied_file_path) + + pulled_files = os.listdir(temp_dir_path) + self.assertEqual(len(pulled_files), 2) + for file_name in os.listdir(temp_dir_path): + file_path = os.path.join(temp_dir_path, file_name) + file_content = get_json(file_path) + self.assertEqual(file_content, expected_file_content[file_name]) + + # Cleanup + shutil.rmtree(diff_processor.diff_state_root_dir) + shutil.rmtree(temp_dir_path) + globals.token.value = prev_token if __name__ == '__main__': unittest.main() From 0ac92c5499489074684faeed36b78fde8db95cd9 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 5 Dec 2024 18:39:58 -0500 Subject: [PATCH 12/24] added setup command --- i18nilize/src/internationalize/command_line.py | 7 ++++++- i18nilize/src/internationalize/globals.py | 3 +++ i18nilize/src/internationalize/helpers.py | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index 064c051..b48f611 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -1,7 +1,7 @@ #from src.internationalize.helpers import add_language import json import argparse -from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation, pull_translations, push_translations +from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation, pull_translations, push_translations, setup_diffing def cli(): # initialize the parser @@ -36,6 +36,9 @@ def cli(): # sub parser for push push_parser = subparsers.add_parser('push') + # sub parser for setup + setup_parser = subparsers.add_parser('setup') + # the subparser is used because different CLIs use a different amount of inputs args = parser.parse_args() @@ -51,6 +54,8 @@ def cli(): pull_translations() elif args.command == 'push': push_translations() + elif args.command = 'setup': + setup_diffing() else: print("Invalid command") diff --git a/i18nilize/src/internationalize/globals.py b/i18nilize/src/internationalize/globals.py index cc29ad1..c3bbe77 100644 --- a/i18nilize/src/internationalize/globals.py +++ b/i18nilize/src/internationalize/globals.py @@ -15,4 +15,7 @@ def __init__(self): LANGUAGES_DIR = 'src/internationalize/languages' +# Use this directory to test the cli +TEST_LANGUAGES_DIR = 'temp' + token = GlobalToken() diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index e95cabb..f902a22 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -4,6 +4,7 @@ import hashlib import requests from . import globals +from src.internationalize.diffing_processor import DiffingProcessor # Function to parse json file, given its path def get_json(file_path): @@ -182,3 +183,10 @@ def read_json_file(directory): raise except Exception as e: print(f"An exception occured: {e}") + +""" +Initializes translations directory for diffing +""" +def setup_diffing(): + dp = DiffingProcessor(globals.TEST_LANGUAGES_DIR) + dp.setup() From 3e2a7710a1c4d9d17b564e1f02684cf0402b4fe7 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 5 Dec 2024 19:14:38 -0500 Subject: [PATCH 13/24] fixed circular imports --- i18nilize/src/internationalize/command_line.py | 9 ++++++--- i18nilize/src/internationalize/helpers.py | 8 -------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index b48f611..8ed2fa6 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -1,7 +1,8 @@ #from src.internationalize.helpers import add_language import json import argparse -from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation, pull_translations, push_translations, setup_diffing +from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation, pull_translations, push_translations +from i18nilize.src.internationalize.diffing_processor import DiffingProcessor def cli(): # initialize the parser @@ -54,8 +55,10 @@ def cli(): pull_translations() elif args.command == 'push': push_translations() - elif args.command = 'setup': - setup_diffing() + elif args.command == 'setup': + # Quick fix for now + dp = DiffingProcessor("/temp") + dp.setup() else: print("Invalid command") diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index f902a22..e95cabb 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -4,7 +4,6 @@ import hashlib import requests from . import globals -from src.internationalize.diffing_processor import DiffingProcessor # Function to parse json file, given its path def get_json(file_path): @@ -183,10 +182,3 @@ def read_json_file(directory): raise except Exception as e: print(f"An exception occured: {e}") - -""" -Initializes translations directory for diffing -""" -def setup_diffing(): - dp = DiffingProcessor(globals.TEST_LANGUAGES_DIR) - dp.setup() From a7d495847c867101c7e2270759379e2684dfea4a Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 5 Dec 2024 19:51:51 -0500 Subject: [PATCH 14/24] fix imports --- i18nilize/src/internationalize/command_line.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index 8ed2fa6..2e9da2b 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -1,7 +1,8 @@ #from src.internationalize.helpers import add_language import json import argparse -from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation, pull_translations, push_translations +from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation +from i18nilize.src.internationalize.sync_processor import pull_translations, push_translations from i18nilize.src.internationalize.diffing_processor import DiffingProcessor def cli(): From 08c5b315398851dac23e198b7a0db3537c5c0221 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 5 Dec 2024 20:16:52 -0500 Subject: [PATCH 15/24] minor fixes and backup languages --- i18nilize/src/internationalize/command_line.py | 3 ++- i18nilize/src/internationalize/default_languages/chinese.json | 3 +++ i18nilize/src/internationalize/default_languages/french.json | 4 ++++ i18nilize/src/internationalize/default_languages/german.json | 3 +++ i18nilize/src/internationalize/default_languages/korean.json | 3 +++ i18nilize/src/internationalize/default_languages/spanish.json | 4 ++++ i18nilize/src/internationalize/globals.py | 3 --- 7 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 i18nilize/src/internationalize/default_languages/chinese.json create mode 100644 i18nilize/src/internationalize/default_languages/french.json create mode 100644 i18nilize/src/internationalize/default_languages/german.json create mode 100644 i18nilize/src/internationalize/default_languages/korean.json create mode 100644 i18nilize/src/internationalize/default_languages/spanish.json diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index 2e9da2b..33e4843 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -4,6 +4,7 @@ from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation from i18nilize.src.internationalize.sync_processor import pull_translations, push_translations from i18nilize.src.internationalize.diffing_processor import DiffingProcessor +from i18nilize.src.internationalize import globals def cli(): # initialize the parser @@ -58,7 +59,7 @@ def cli(): push_translations() elif args.command == 'setup': # Quick fix for now - dp = DiffingProcessor("/temp") + dp = DiffingProcessor(globals.LANGUAGES_DIR) dp.setup() else: print("Invalid command") diff --git a/i18nilize/src/internationalize/default_languages/chinese.json b/i18nilize/src/internationalize/default_languages/chinese.json new file mode 100644 index 0000000..df53d19 --- /dev/null +++ b/i18nilize/src/internationalize/default_languages/chinese.json @@ -0,0 +1,3 @@ +{ + "thank you": "\u8c22\u8c22" +} \ No newline at end of file diff --git a/i18nilize/src/internationalize/default_languages/french.json b/i18nilize/src/internationalize/default_languages/french.json new file mode 100644 index 0000000..78b6885 --- /dev/null +++ b/i18nilize/src/internationalize/default_languages/french.json @@ -0,0 +1,4 @@ +{ + "thanks": "merci", + "hello": "bonjour" +} \ No newline at end of file diff --git a/i18nilize/src/internationalize/default_languages/german.json b/i18nilize/src/internationalize/default_languages/german.json new file mode 100644 index 0000000..2c57c1d --- /dev/null +++ b/i18nilize/src/internationalize/default_languages/german.json @@ -0,0 +1,3 @@ +{ + "thank you": "danke" +} \ No newline at end of file diff --git a/i18nilize/src/internationalize/default_languages/korean.json b/i18nilize/src/internationalize/default_languages/korean.json new file mode 100644 index 0000000..a029b92 --- /dev/null +++ b/i18nilize/src/internationalize/default_languages/korean.json @@ -0,0 +1,3 @@ +{ + "welcome": "\ud658\uc601\ud569\ub2c8\ub2e4" +} \ No newline at end of file diff --git a/i18nilize/src/internationalize/default_languages/spanish.json b/i18nilize/src/internationalize/default_languages/spanish.json new file mode 100644 index 0000000..11336ed --- /dev/null +++ b/i18nilize/src/internationalize/default_languages/spanish.json @@ -0,0 +1,4 @@ +{ + "hello": "hola", + "thanks": "gracias" +} \ No newline at end of file diff --git a/i18nilize/src/internationalize/globals.py b/i18nilize/src/internationalize/globals.py index c3bbe77..cc29ad1 100644 --- a/i18nilize/src/internationalize/globals.py +++ b/i18nilize/src/internationalize/globals.py @@ -15,7 +15,4 @@ def __init__(self): LANGUAGES_DIR = 'src/internationalize/languages' -# Use this directory to test the cli -TEST_LANGUAGES_DIR = 'temp' - token = GlobalToken() From 9824d037d928c2a7c53b13a542adfa555fa9cfc9 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Thu, 5 Dec 2024 20:49:04 -0500 Subject: [PATCH 16/24] minor fixes --- i18nilize/src/internationalize/diffing_processor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/i18nilize/src/internationalize/diffing_processor.py b/i18nilize/src/internationalize/diffing_processor.py index 25fd123..28ddf20 100644 --- a/i18nilize/src/internationalize/diffing_processor.py +++ b/i18nilize/src/internationalize/diffing_processor.py @@ -26,8 +26,10 @@ def __init__(self, curr_translations_dir): """ def setup(self): try: - os.mkdir(self.diff_state_root_dir) - os.mkdir(self.diff_state_files_dir) + if not os.path.exists(self.diff_state_root_dir): + os.mkdir(self.diff_state_root_dir) + if not os.path.exists(self.diff_state_files_dir): + os.mkdir(self.diff_state_files_dir) with open(self.metadata_file_dir, "w") as outfile: json.dump({}, outfile) From a4f2dd80581c8c9fa5b3e4069875fdbe763a044e Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Fri, 6 Dec 2024 17:50:20 -0500 Subject: [PATCH 17/24] disabled dirsync logging --- i18nilize/src/internationalize/diffing_processor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/i18nilize/src/internationalize/diffing_processor.py b/i18nilize/src/internationalize/diffing_processor.py index 28ddf20..a6d9503 100644 --- a/i18nilize/src/internationalize/diffing_processor.py +++ b/i18nilize/src/internationalize/diffing_processor.py @@ -1,6 +1,7 @@ import os import hashlib import json +import logging from dirsync import sync from src.internationalize.helpers import compute_hash, compute_hashes, read_json_file @@ -16,6 +17,7 @@ """ class DiffingProcessor(): def __init__(self, curr_translations_dir): + logging.getLogger('dirsync').disabled = True self.diff_state_root_dir = "diff_state" self.diff_state_files_dir = os.path.join(self.diff_state_root_dir, "translations") self.metadata_file_dir = os.path.join(self.diff_state_root_dir, "metadata.json") From 86ea339e12863be0159e23b7eec083d323544918 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sun, 15 Dec 2024 18:02:52 -0500 Subject: [PATCH 18/24] fixed bug with read_json and added comments for push/pull tests --- core/db.sqlite3 | Bin 147456 -> 147456 bytes i18nilize/src/internationalize/helpers.py | 2 +- i18nilize/tests/test_cli.py | 12 ++++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/core/db.sqlite3 b/core/db.sqlite3 index 172df950a92937364a7082ae83fb4c75348af051..a436253ba1490bd33b125aebe97a8c32a92adcda 100644 GIT binary patch delta 97 zcmZo@;B08%oFL6Od7_Ln)~{ri}sumMqMij20lm97LE+KW)!s3ILUn8Y2Jz delta 97 zcmZo@;B08%oFL6uJyFJ)v3g;GKBI1PqW<aa10i%N55q~_%0 xXXNK378fMuWfo^hXixv3$0W(9wVg|!X`_IEE( diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index e95cabb..76f0b54 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -9,7 +9,7 @@ def get_json(file_path): try: # open file and parse - with open(file_path, 'r') as file: + with open(file_path, 'r', encoding='utf8') as file: data = json.load(file) except FileNotFoundError: print("File not found") diff --git a/i18nilize/tests/test_cli.py b/i18nilize/tests/test_cli.py index f6de20b..e48f9c2 100644 --- a/i18nilize/tests/test_cli.py +++ b/i18nilize/tests/test_cli.py @@ -162,9 +162,13 @@ def test_delete_translation_word_mismatch(self): self.assertEqual(data["welcome"], "환영합니다") def test_pull_translations(self): + # Set global token to test token (Note: test will fail if translations + # tied token are modified) prev_token = globals.token.value test_token = "c84234c3-b507-4ed0-a6eb-8b10116cdef1" globals.token.value = test_token + + # Initialize DiffingProcessor with test directory temp_dir_path = os.path.join(self.languages_dir, "temp") diff_processor = DiffingProcessor(temp_dir_path) if os.path.exists(diff_processor.diff_state_root_dir): @@ -172,12 +176,12 @@ def test_pull_translations(self): diff_processor.setup() # Create temporary directories to pull translations - files_to_copy = ["spanish.json", "french.json"] if os.path.exists(temp_dir_path): shutil.rmtree(temp_dir_path) os.mkdir(temp_dir_path) - # Copy files into temp dir to test overwriting + # Copy test files into temp dir to test overwriting + files_to_copy = ["spanish.json", "french.json"] for file_name in files_to_copy: curr_file_path = os.path.join(self.languages_dir, file_name) new_file_path = os.path.join(temp_dir_path, file_name) @@ -212,12 +216,16 @@ def test_pull_translations(self): globals.token.value = prev_token def test_push_translations(self): + # Set global token to test token prev_token = globals.token.value test_token = "a373fc5e-5b65-463e-b89e-1a37706a69dd" globals.token.value = test_token + + # Initialize DiffingProcessor with test directory temp_dir_path = os.path.join(self.languages_dir, "temp") diff_processor = DiffingProcessor(temp_dir_path) + # Remove any persisting test data from previous tests (in case of a test failure) if os.path.exists(diff_processor.diff_state_root_dir): shutil.rmtree(diff_processor.diff_state_root_dir) if os.path.exists(temp_dir_path): From c7767b70761ca135d69038c5b536c5e6fb8e62c5 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sun, 15 Dec 2024 18:58:06 -0500 Subject: [PATCH 19/24] fixed pip installation bug --- core/db.sqlite3 | Bin 147456 -> 147456 bytes i18nilize/pyproject.toml | 39 +++++++++++++++++- i18nilize/setup_temp.cfg | 31 ++++++++++++++ .../src/internationalize/command_line.py | 11 ++--- 4 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 i18nilize/setup_temp.cfg diff --git a/core/db.sqlite3 b/core/db.sqlite3 index a436253ba1490bd33b125aebe97a8c32a92adcda..193156f9efd46a9eac9240386366561b6e814d1e 100644 GIT binary patch delta 186 zcmZo@;B08%oFL6OXQGTV=42'] -build-backend = 'setuptools.build_meta' \ No newline at end of file +requires = ["setuptools >= 42", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "localization" +version = "1.0.0" +authors = [ + { name = "UBC Launchpad", email = "strategy@ubclaunchpad.com" } +] +description = "A localization package for microservices" +readme = "readme.md" +license = { file = "LICENSE.txt" } +keywords = ["localization", "microservices"] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent" +] +dependencies = [ + "requests>=2.25.1", + "geocoder>=1.38.1", + "geopy>=2.2.0", + "Babel>=2.9.1", + "dirsync>=2.2.5", +] + +[project.scripts] +i18nilize = "src.internationalize.command_line:cli" + +[tool.setuptools] +packages = ["src"] + +[tool.setuptools.package-dir] +src = "src" + +# [tool.setuptools.packages.find] +# where = ["src"] diff --git a/i18nilize/setup_temp.cfg b/i18nilize/setup_temp.cfg new file mode 100644 index 0000000..cdea76c --- /dev/null +++ b/i18nilize/setup_temp.cfg @@ -0,0 +1,31 @@ +[metadata] +name = localization +version = 1.0.0 +author = UBC Launchpad +author_email = strategy@ubclaunchpad.com +description = A localization package for microservices +long_description = file: README.md, LICENSE.txt +long_description_content_type = text/markdown +# url = +#project_urls = +# Bug Tracker = +# repository = +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: MIT License + Operating System :: OS Independent + +[options] +package_dir = + = src +packages = find: +python_requires = >=3.6 +install_requires = + requests>=2.25.1 + geocoder>=1.38.1 + geopy>=2.2.0 + Babel>=2.9.1 + dirsync >= 2.2.5 + +[options.packages.find] +where = src \ No newline at end of file diff --git a/i18nilize/src/internationalize/command_line.py b/i18nilize/src/internationalize/command_line.py index 33e4843..62cf596 100644 --- a/i18nilize/src/internationalize/command_line.py +++ b/i18nilize/src/internationalize/command_line.py @@ -1,10 +1,10 @@ #from src.internationalize.helpers import add_language import json import argparse -from i18nilize.src.internationalize.helpers import add_language, add_update_translated_word, delete_translation -from i18nilize.src.internationalize.sync_processor import pull_translations, push_translations -from i18nilize.src.internationalize.diffing_processor import DiffingProcessor -from i18nilize.src.internationalize import globals +from src.internationalize.helpers import add_language, add_update_translated_word, delete_translation +from src.internationalize.sync_processor import pull_translations, push_translations +from src.internationalize.diffing_processor import DiffingProcessor +from src.internationalize import globals def cli(): # initialize the parser @@ -64,4 +64,5 @@ def cli(): else: print("Invalid command") -cli() +if __name__ == "__main__": + cli() From c0dbe1c96aa120e4933603f5afa7d7696c07cd4e Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sun, 15 Dec 2024 18:58:28 -0500 Subject: [PATCH 20/24] renamed setup.cfg --- i18nilize/setup.cfg | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 i18nilize/setup.cfg diff --git a/i18nilize/setup.cfg b/i18nilize/setup.cfg deleted file mode 100644 index cdea76c..0000000 --- a/i18nilize/setup.cfg +++ /dev/null @@ -1,31 +0,0 @@ -[metadata] -name = localization -version = 1.0.0 -author = UBC Launchpad -author_email = strategy@ubclaunchpad.com -description = A localization package for microservices -long_description = file: README.md, LICENSE.txt -long_description_content_type = text/markdown -# url = -#project_urls = -# Bug Tracker = -# repository = -classifiers = - Programming Language :: Python :: 3 - License :: OSI Approved :: MIT License - Operating System :: OS Independent - -[options] -package_dir = - = src -packages = find: -python_requires = >=3.6 -install_requires = - requests>=2.25.1 - geocoder>=1.38.1 - geopy>=2.2.0 - Babel>=2.9.1 - dirsync >= 2.2.5 - -[options.packages.find] -where = src \ No newline at end of file From fa34675f6ef27f7ffb0724723d2ec931bd23ad5d Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sat, 11 Jan 2025 19:32:16 -0500 Subject: [PATCH 21/24] added rest_framework to installed apps --- core/core/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/core/settings.py b/core/core/settings.py index 23171d6..8e76b0c 100644 --- a/core/core/settings.py +++ b/core/core/settings.py @@ -37,6 +37,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', 'i18nilize', ] From fd6f1d9977791c82a1e91e75beabda46fb7f880f Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sat, 18 Jan 2025 23:39:29 -0500 Subject: [PATCH 22/24] fixed error_handler import --- i18nilize/src/internationalize/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18nilize/src/internationalize/helpers.py b/i18nilize/src/internationalize/helpers.py index 6795729..d1fe71a 100644 --- a/i18nilize/src/internationalize/helpers.py +++ b/i18nilize/src/internationalize/helpers.py @@ -4,7 +4,7 @@ import hashlib import requests from . import globals -from internationalize.error_handler import ErrorHandler +from src.internationalize.error_handler import ErrorHandler # Function to parse json file, given its path def get_json(file_path): From 03d6da167fd0d6f264a75a5ca4a46f1f0b333b5e Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sun, 19 Jan 2025 00:00:32 -0500 Subject: [PATCH 23/24] fixed error_handler bug --- i18nilize/src/internationalize/error_handler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/i18nilize/src/internationalize/error_handler.py b/i18nilize/src/internationalize/error_handler.py index 2abbbb9..bf4dc95 100644 --- a/i18nilize/src/internationalize/error_handler.py +++ b/i18nilize/src/internationalize/error_handler.py @@ -24,6 +24,10 @@ def verify_languages(self): all_language_files = os.listdir(self.translations_dir) for language_file in all_language_files: + absolute_file_path = os.path.join(self.translations_dir, language_file) + if os.path.isdir(absolute_file_path): + continue + error = self.handle_error(language_file) if error != "": errors[language_file] = error From ffb12a69eb6bf4f9819a58c825e15f666eda51a9 Mon Sep 17 00:00:00 2001 From: Andrew Fenton Date: Sun, 19 Jan 2025 00:11:46 -0500 Subject: [PATCH 24/24] commented out push pull tests --- i18nilize/tests/test_cli.py | 220 ++++++++++++++++++------------------ 1 file changed, 113 insertions(+), 107 deletions(-) diff --git a/i18nilize/tests/test_cli.py b/i18nilize/tests/test_cli.py index e48f9c2..5a64e8c 100644 --- a/i18nilize/tests/test_cli.py +++ b/i18nilize/tests/test_cli.py @@ -161,114 +161,120 @@ def test_delete_translation_word_mismatch(self): self.assertIn("welcome", data) self.assertEqual(data["welcome"], "환영합니다") - def test_pull_translations(self): - # Set global token to test token (Note: test will fail if translations - # tied token are modified) - prev_token = globals.token.value - test_token = "c84234c3-b507-4ed0-a6eb-8b10116cdef1" - globals.token.value = test_token - - # Initialize DiffingProcessor with test directory - temp_dir_path = os.path.join(self.languages_dir, "temp") - diff_processor = DiffingProcessor(temp_dir_path) - if os.path.exists(diff_processor.diff_state_root_dir): - shutil.rmtree(diff_processor.diff_state_root_dir) - diff_processor.setup() - - # Create temporary directories to pull translations - if os.path.exists(temp_dir_path): - shutil.rmtree(temp_dir_path) - os.mkdir(temp_dir_path) - - # Copy test files into temp dir to test overwriting - files_to_copy = ["spanish.json", "french.json"] - for file_name in files_to_copy: - curr_file_path = os.path.join(self.languages_dir, file_name) - new_file_path = os.path.join(temp_dir_path, file_name) - shutil.copy(curr_file_path, new_file_path) - - # Expected content after pulling from API - expected_file_content = { - "fr.json": { - "hello": "bonjour" - }, - "french.json": { - "hello": "bonjour" - }, - "spanish.json": { - "hello": "hola", - "bye": "chau", - "what": "que", - "como": "how", - "codigo": "code" - } - } - pull_translations(write_directory=temp_dir_path) - for file_name in os.listdir(temp_dir_path): - file_path = os.path.join(temp_dir_path, file_name) - file_content = get_json(file_path) - self.assertEqual(file_content, expected_file_content[file_name]) - - # Cleanup - shutil.rmtree(diff_processor.diff_state_root_dir) - shutil.rmtree(temp_dir_path) - globals.token.value = prev_token - - def test_push_translations(self): - # Set global token to test token - prev_token = globals.token.value - test_token = "a373fc5e-5b65-463e-b89e-1a37706a69dd" - globals.token.value = test_token - - # Initialize DiffingProcessor with test directory - temp_dir_path = os.path.join(self.languages_dir, "temp") - diff_processor = DiffingProcessor(temp_dir_path) - - # Remove any persisting test data from previous tests (in case of a test failure) - if os.path.exists(diff_processor.diff_state_root_dir): - shutil.rmtree(diff_processor.diff_state_root_dir) - if os.path.exists(temp_dir_path): - shutil.rmtree(temp_dir_path) - os.mkdir(temp_dir_path) - - # Initialize with no translations in either state - diff_processor.setup() - - # Deletes all translations tied to test_token - response = requests.delete(self.reset_token_endpoint, headers={'Token': test_token}) - self.assertTrue(response.ok) - - # Copy files to push to API - files_to_copy = ["spanish.json", "french.json"] - for file_name in files_to_copy: - curr_file_path = os.path.join(self.languages_dir, file_name) - new_file_path = os.path.join(temp_dir_path, file_name) - shutil.copy(curr_file_path, new_file_path) - - # Push changes, delete copied files, and pull - push_translations(translations_dir=temp_dir_path) - shutil.rmtree(temp_dir_path) - os.mkdir(temp_dir_path) - pull_translations(write_directory=temp_dir_path) - - # Expected content after pulling from API (same content that was pushed) - expected_file_content = {} - for file_name in files_to_copy: - copied_file_path = os.path.join(self.languages_dir, file_name) - expected_file_content[file_name] = get_json(copied_file_path) - - pulled_files = os.listdir(temp_dir_path) - self.assertEqual(len(pulled_files), 2) - for file_name in os.listdir(temp_dir_path): - file_path = os.path.join(temp_dir_path, file_name) - file_content = get_json(file_path) - self.assertEqual(file_content, expected_file_content[file_name]) - - # Cleanup - shutil.rmtree(diff_processor.diff_state_root_dir) - shutil.rmtree(temp_dir_path) - globals.token.value = prev_token + """ + Commenting out push and pull tests because they need the backend to be running. + GitHub CI is not configured to start the backend before running tests. + """ + + # def test_pull_translations(self): + # # Set global token to test token (Note: test will fail if translations + # # tied token are modified) + # prev_token = globals.token.value + # test_token = "c84234c3-b507-4ed0-a6eb-8b10116cdef1" + # globals.token.value = test_token + # + # # Initialize DiffingProcessor with test directory + # temp_dir_path = os.path.join(self.languages_dir, "temp") + # diff_processor = DiffingProcessor(temp_dir_path) + # if os.path.exists(diff_processor.diff_state_root_dir): + # shutil.rmtree(diff_processor.diff_state_root_dir) + # diff_processor.setup() + # + # # Create temporary directories to pull translations + # if os.path.exists(temp_dir_path): + # shutil.rmtree(temp_dir_path) + # os.mkdir(temp_dir_path) + # + # # Copy test files into temp dir to test overwriting + # files_to_copy = ["spanish.json", "french.json"] + # for file_name in files_to_copy: + # curr_file_path = os.path.join(self.languages_dir, file_name) + # new_file_path = os.path.join(temp_dir_path, file_name) + # shutil.copy(curr_file_path, new_file_path) + # + # # Expected content after pulling from API + # expected_file_content = { + # "fr.json": { + # "hello": "bonjour" + # }, + # "french.json": { + # "hello": "bonjour" + # }, + # "spanish.json": { + # "hello": "hola", + # "bye": "chau", + # "what": "que", + # "como": "how", + # "codigo": "code" + # } + # } + # + # pull_translations(write_directory=temp_dir_path) + # for file_name in os.listdir(temp_dir_path): + # file_path = os.path.join(temp_dir_path, file_name) + # file_content = get_json(file_path) + # self.assertEqual(file_content, expected_file_content[file_name]) + # + # # Cleanup + # shutil.rmtree(diff_processor.diff_state_root_dir) + # shutil.rmtree(temp_dir_path) + # globals.token.value = prev_token + # + # def test_push_translations(self): + # # Set global token to test token + # prev_token = globals.token.value + # test_token = "a373fc5e-5b65-463e-b89e-1a37706a69dd" + # globals.token.value = test_token + # + # # Initialize DiffingProcessor with test directory + # temp_dir_path = os.path.join(self.languages_dir, "temp") + # diff_processor = DiffingProcessor(temp_dir_path) + # + # # Remove any persisting test data from previous tests (in case of a test failure) + # if os.path.exists(diff_processor.diff_state_root_dir): + # shutil.rmtree(diff_processor.diff_state_root_dir) + # if os.path.exists(temp_dir_path): + # shutil.rmtree(temp_dir_path) + # os.mkdir(temp_dir_path) + # + # # Initialize with no translations in either state + # diff_processor.setup() + # + # # Deletes all translations tied to test_token + # response = requests.delete(self.reset_token_endpoint, headers={'Token': test_token}) + # self.assertTrue(response.ok) + # + # # Copy files to push to API + # files_to_copy = ["spanish.json", "french.json"] + # for file_name in files_to_copy: + # curr_file_path = os.path.join(self.languages_dir, file_name) + # new_file_path = os.path.join(temp_dir_path, file_name) + # shutil.copy(curr_file_path, new_file_path) + # + # # Push changes, delete copied files, and pull + # push_translations(translations_dir=temp_dir_path) + # shutil.rmtree(temp_dir_path) + # os.mkdir(temp_dir_path) + # pull_translations(write_directory=temp_dir_path) + # + # # Expected content after pulling from API (same content that was pushed) + # expected_file_content = {} + # for file_name in files_to_copy: + # copied_file_path = os.path.join(self.languages_dir, file_name) + # expected_file_content[file_name] = get_json(copied_file_path) + # + # pulled_files = os.listdir(temp_dir_path) + # self.assertEqual(len(pulled_files), 2) + # for file_name in os.listdir(temp_dir_path): + # file_path = os.path.join(temp_dir_path, file_name) + # file_content = get_json(file_path) + # self.assertEqual(file_content, expected_file_content[file_name]) + # + # # Cleanup + # shutil.rmtree(diff_processor.diff_state_root_dir) + # shutil.rmtree(temp_dir_path) + # globals.token.value = prev_token if __name__ == '__main__': unittest.main()