Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CLI to pip #15

Merged
merged 36 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
590877b
Added command_line.py, default language file, setup file, and basic f…
Oct 23, 2024
9cf43de
fixed pathing error
Oct 23, 2024
afac5c4
commented code, cleaned up code, added skeleton for tests
Oct 23, 2024
b2f2c36
implemented cli for add and update
steph-feng Oct 24, 2024
c9f9dbe
tests
Oct 24, 2024
83dd85d
Merge branch 'main' into create_cli
Oct 24, 2024
dec7d3d
merged
Oct 25, 2024
67cb834
merge with mainMerge branch 'main' into create_cli
Oct 25, 2024
8b36bd8
feat: cli for delete
Oct 26, 2024
de8dcfc
fixed pathing issue with tests
Oct 31, 2024
e3fe661
Merge branch 'create_cli' of https://github.com/ubclaunchpad/localiza…
Oct 31, 2024
0f97045
add setup/teardown and test for add_word
steph-feng Oct 31, 2024
9251c39
fix differences
Oct 31, 2024
8fae35d
Merge branch 'create_cli' of https://github.com/ubclaunchpad/localiza…
Oct 31, 2024
9f4e3cc
Merge branch 'main' into create_cli
angelafeliciaa Nov 2, 2024
dfaa017
Merge branch 'create_cli' of https://github.com/ubclaunchpad/localiza…
Nov 2, 2024
b37d718
added init files
Nov 3, 2024
24f35bd
add test for update
steph-feng Nov 4, 2024
860a984
Merge branch 'main' into create_cli
Nov 16, 2024
536d4d0
refactored add-language, added test
Nov 16, 2024
0820c6b
refactor add_word, update tests
steph-feng Nov 16, 2024
abb20ec
edit add/update word tests
steph-feng Nov 16, 2024
55a0993
gitignore
angelafeliciaa Nov 16, 2024
7462306
refactor delete translation
angelafeliciaa Nov 16, 2024
ee14131
add tests for delete
angelafeliciaa Nov 16, 2024
fe1c049
delete pycache
angelafeliciaa Nov 16, 2024
e1a2e89
Revert "delete pycache"
angelafeliciaa Nov 16, 2024
3792274
remove egg info
angelafeliciaa Nov 17, 2024
b8951da
remove useless
angelafeliciaa Nov 17, 2024
0288632
move languages_dir to globals
angelafeliciaa Nov 17, 2024
688a46c
delete more egg info
angelafeliciaa Nov 17, 2024
499b6f6
Merge branch 'main' into create_cli
angelafeliciaa Nov 17, 2024
7d7f7aa
delete build files
angelafeliciaa Nov 19, 2024
140ad3c
Merge branch 'create_cli' of https://github.com/ubclaunchpad/localiza…
angelafeliciaa Nov 19, 2024
d341e65
delete pycache
angelafeliciaa Nov 19, 2024
f00e7ba
delete pycache
angelafeliciaa Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/django.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ jobs:
cd i18nilize
python3 -m tests.test_read_file
python3 -m tests.test_parse_json
angelafeliciaa marked this conversation as resolved.
Show resolved Hide resolved
python3 -m tests.test_api_helpers
python3 -m tests.test_cli
python3 -m tests.test_api_helpers
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
env/
**/__pycache__
myenv
venv
venv
i18nilize.egg-info
Empty file added i18nilize/__init__.py
Empty file.
Empty file added i18nilize/src/__init__.py
Empty file.
Binary file not shown.
47 changes: 47 additions & 0 deletions i18nilize/src/internationalize/command_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#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

def cli():
# initialize the parser
parser = argparse.ArgumentParser(description="internationalization for translation")
subparsers = parser.add_subparsers(dest='command')

# sub parser for add_language
add_lang_parser = subparsers.add_parser('add-language')
add_lang_parser.add_argument('language')

# sub parser for add
add_parser = subparsers.add_parser('add')
add_parser.add_argument('language')
add_parser.add_argument('original_word')
add_parser.add_argument('translated_word')

# sub parser for update
update_parser = subparsers.add_parser('update')
update_parser.add_argument('language')
update_parser.add_argument('original_word')
update_parser.add_argument('translated_word')

# sub parser for delete
delete_parser = subparsers.add_parser('delete')
delete_parser.add_argument('language')
delete_parser.add_argument('original_word')
delete_parser.add_argument('translated_word')

# the subparser is used because different CLIs use a different amount of inputs

args = parser.parse_args()

# depending on the command, do different things
if args.command == 'add-language':
add_language(args.language)
elif args.command == 'add' or args.command == 'update':
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)
else:
print("Invalid command")

cli()
4 changes: 3 additions & 1 deletion i18nilize/src/internationalize/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ def __init__(self):
TOKEN_ENDPOINT = f"{API_BASE_URL}token/"
TRANSLATIONS_ENDPOINT = f"{API_BASE_URL}translations/"

token = GlobalToken()
LANGUAGES_DIR = 'src/internationalize/languages'

token = GlobalToken()
57 changes: 53 additions & 4 deletions i18nilize/src/internationalize/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import json
import sys
import os
import requests
from geocoder import ip
from geopy.geocoders import Nominatim
from babel.languages import get_official_languages
from . import globals

# Function to parse json file, given its path
Expand All @@ -23,6 +21,57 @@ def get_json(file_path):
raise e
return data

# Adds a json file corresponding to the added language
def add_language(language):
os.makedirs(globals.LANGUAGES_DIR, exist_ok=True)
file_path = os.path.join(globals.LANGUAGES_DIR, f"{language.lower()}.json")

if os.path.exists(file_path):
return

initial_content = {}
with open(file_path, 'w') as file:
json.dump(initial_content, file, indent=4)
print(f"Language added.")

# Adds/updates a translated word under the given language in the default JSON file
def add_update_translated_word(language, original_word, translated_word):
file_path = os.path.join(globals.LANGUAGES_DIR, f"{language.lower()}.json")

if not os.path.exists(file_path):
print(f"Error: Language '{language}' does not exist. Add the language before adding a translation.")
sys.exit(1)

data = get_json(file_path)

data[original_word] = translated_word
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)
print(f"{original_word}: {translated_word} added to translations.")

# Deletes a translated word for the given language
def delete_translation(language, original_word, translated_word):
file_path = os.path.join(globals.LANGUAGES_DIR, f"{language.lower()}.json")

if not os.path.exists(file_path):
print(f"Error: Language '{language}' does not exist.")
sys.exit(1)

data = get_json(file_path)

if original_word not in data:
print(f"Error: Original word '{original_word}' does not exist in language '{language}'.")
sys.exit(1)

if data[original_word] != translated_word:
print(f"Error: Translated word for '{original_word}' does not match '{translated_word}'.")
sys.exit(1)

del data[original_word]
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)
print(f"Translation for '{original_word}' deleted successfully from language '{language}'.")

# Input:
# - file_path: path of json file
# Output: Token in json file
Expand Down Expand Up @@ -71,7 +120,7 @@ def generate_file(language, token):
return

file_content = response.json()

# transforms the dictionary object above into a JSON object
json_object = json.dumps(file_content, indent=4)
create_json(json_object, language)
Expand Down
1 change: 1 addition & 0 deletions i18nilize/src/internationalize/languages/korean.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Binary file not shown.
161 changes: 161 additions & 0 deletions i18nilize/tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import unittest, os, json, timeit
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

# Create your tests here.
# To test:
# In i18nilize directory, run python -m tests.test_cli

class TestCLI(unittest.TestCase):
def setUp(self):
self.languages_dir = "src/internationalize/languages"
os.makedirs(self.languages_dir, exist_ok=True)

def test_add_new_language(self):
new_language = "Japanese"
add_language(new_language)
new_language_path = os.path.join(self.languages_dir, f"{new_language.lower()}.json")
self.assertTrue(os.path.exists(new_language_path))

with open(new_language_path, "r") as file:
data = json.load(file)
self.assertEqual(data, {})

# clean up test
os.remove(new_language_path)

def test_add_word_language_exists(self):
existing_language = "French"
file_path = os.path.join(self.languages_dir, f"{existing_language.lower()}.json")

# save original state
with open(file_path, "r") as file:
original_translations = json.load(file)

data = get_json(file_path)
self.assertEqual(len(data), 2)

add_update_translated_word("French", "good", "bien")
data = get_json(file_path)
self.assertEqual(len(data), 3)
self.assertEqual(data["good"], "bien")

# clean up test
with open(file_path, 'w') as file:
json.dump(original_translations, file, indent=4)

def test_update_word_language_exists(self):
existing_language = "French"
file_path = os.path.join(self.languages_dir, f"{existing_language.lower()}.json")

# save original state
with open(file_path, "r") as file:
original_translations = json.load(file)

data = get_json(file_path)
self.assertEqual(len(data), 2)

add_update_translated_word("French", "thanks", "merc")
data = get_json(file_path)
self.assertEqual(len(data), 2)
self.assertEqual(data["thanks"], "merc")

# clean up test
with open(file_path, 'w') as file:
json.dump(original_translations, file, indent=4)

@patch('builtins.print')
def test_add_word_language_does_not_exist(self, mock_print):
with self.assertRaises(SystemExit) as context:
add_update_translated_word("NonExistentLanguage", "a", "b")
self.assertTrue(context.exception.code, 1)

mock_print.assert_called_once_with("Error: Language 'NonExistentLanguage' does not exist. Add the language before adding a translation.")

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")

delete_translation(language, "goodbye", "auf Wiedersehen")

# verify deletion
data = get_json(file_path)
self.assertNotIn("goodbye", data)
self.assertIn("thank you", data)

@patch('builtins.print')
def test_delete_translation_language_does_not_exist(self, mock_print):
language = "Russian"
original_word = "hello"
translated_word = "привет"

with self.assertRaises(SystemExit) as context:
delete_translation(language, original_word, translated_word)
self.assertEqual(context.exception.code, 1)

mock_print.assert_called_once_with(f"Error: Language '{language}' does not exist.")

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)

with patch('builtins.print') as mock_print, self.assertRaises(SystemExit) as context:
delete_translation(language, "good morning", "早上好")
self.assertEqual(context.exception.code, 1)
mock_print.assert_called_once_with(f"Error: Original word 'good morning' does not exist in language '{language}'.")

# ensure existing translations remain unchanged
data = get_json(file_path)
self.assertIn("thank you", data)
self.assertEqual(data["thank you"], "谢谢")

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"], "환영합니다")

with patch('builtins.print') as mock_print, self.assertRaises(SystemExit) as context:
delete_translation(language, "welcome", "환영해요")
self.assertEqual(context.exception.code, 1)
mock_print.assert_called_once_with(f"Error: Translated word for 'welcome' does not match '환영해요'.")

# ensure existing translations remain unchanged
data = get_json(file_path)
self.assertIn("welcome", data)
self.assertEqual(data["welcome"], "환영합니다")

if __name__ == '__main__':
unittest.main()
22 changes: 22 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import setuptools
from setuptools import find_packages

# setup file for the command line
# before testing, do the following command:
# pip install -e
# within the virtual environment, this will enable you to use i18nilize CLI
setuptools.setup(
# package name
name='i18nilize',

# arbitrary version #
version='1.0',

# downloads the necessary packages (ex. json)
packages=find_packages(),

# directs the script towards the function located in that file
entry_points = {
'console_scripts': ['i18nilize=i18nilize.src.internationalize.command_line:cli'],
},
)
Loading