Skip to content

Commit

Permalink
Merge branch 'main' into create_cli
Browse files Browse the repository at this point in the history
  • Loading branch information
angelafeliciaa authored Nov 17, 2024
2 parents 688a46c + 4143305 commit 499b6f6
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 23 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/django.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
- name: Install Core Dependencies
run: |
cd core
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Install Pip Package Dependencies
run: |
cd i18nilize
python -m pip install --upgrade pip
pip install -e .
- name: Run Django Tests
run: |
cd core
Expand All @@ -35,4 +40,5 @@ jobs:
cd i18nilize
python3 -m tests.test_read_file
python3 -m tests.test_parse_json
python3 -m tests.test_cli
python3 -m tests.test_cli
python3 -m tests.test_api_helpers
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ env/
**/__pycache__
myenv
venv
i18nilize.egg-info
i18nilize.egg-info
18 changes: 0 additions & 18 deletions core/i18nilize/services/locale.py

This file was deleted.

5 changes: 5 additions & 0 deletions i18nilize/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ 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

[options.packages.find]
where = src
52 changes: 52 additions & 0 deletions i18nilize/src/internationalize/api_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# api_helpers.py

import requests
from . import globals
import sys

def create_token():
"""
Creates a new token by making a POST request to the central API.
"""
url = globals.TOKEN_ENDPOINT
try:
response = requests.post(url)
if response.status_code == 201:
token_value = response.json().get("value")
globals.token.value = token_value
print("Token set.")
else:
raise Exception(f"Failed to retrieve token. Status code: {response.status_code}")
except requests.RequestException as e:
raise Exception(f"HTTP Request failed: {e}")

def fetch_translation_data(language):
"""
Fetches translation data for the specified language using the token.
"""
token = globals.token.value
if not token:
print("Token not found. Creating a new token...")
create_token()
token = globals.token.value
if not token:
raise Exception("Failed to create token.")

if not language:
raise Exception("Language parameter is required.")

url = f"{globals.TRANSLATIONS_ENDPOINT}?language={language}"
headers = {
'Authorization': f'Token {token}'
}
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
translations = response.json()
print(f"Generated translation data for language: {language}")
return translations
else:
print(f"Generated translation data for language: {language}")
raise Exception(f"No translations found for language: {language}")
except requests.RequestException as e:
raise Exception(f"HTTP Request failed: {e}")
15 changes: 14 additions & 1 deletion i18nilize/src/internationalize/globals.py
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
LANGUAGES_DIR = 'src/internationalize/languages'
# globals.py

class GlobalToken:
def __init__(self):
self.value = "dummy"

API_BASE_URL = "http://localhost:8000/api/"

TOKEN_ENDPOINT = f"{API_BASE_URL}token/"
TRANSLATIONS_ENDPOINT = f"{API_BASE_URL}translations/"

LANGUAGES_DIR = 'src/internationalize/languages'

token = GlobalToken()
20 changes: 19 additions & 1 deletion i18nilize/src/internationalize/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,28 @@ def create_json(json_object, language):
with open(file_path, 'w') as outfile:
outfile.write(json_object)

# Input: None
# Output: Default language based on user's IP address
def get_default_language():
# get user's coordinates based on IP address
g = ip('me')
coord = str(g.latlng[0]) + ", " + str(g.latlng[1])

# convert coordinates to country code
geolocator = Nominatim(user_agent="localization_launchpad")
location = geolocator.reverse(coord, exactly_one=True)
address = location.raw['address']
country_code = address["country_code"]

# pick the first (most popular) language
return get_official_languages(country_code)[0]

# Input: language
# Output: None, but creates a local JSON file containing translations
def generate_file(language, token):
url = 'http://localhost:8000/api/translations'
if not language:
language = get_default_language()
url = globals.TRANSLATIONS_ENDPOINT
params = {'language': language}
headers = {'token': token}
response = requests.get(url, params=params, headers=headers)
Expand Down
194 changes: 194 additions & 0 deletions i18nilize/tests/test_api_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# test_api_helpers.py

import unittest
from unittest.mock import patch, MagicMock, call
import os
import sys

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if project_root not in sys.path:
sys.path.append(project_root)

from internationalize.api_helpers import create_token, fetch_translation_data
from internationalize.globals import token

class TestAPIHelpers(unittest.TestCase):

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

# backup the original token value
self.original_token_value = token.value

def tearDown(self):
# restore the original token value
token.value = self.original_token_value

# clean up any created language files
if os.path.exists(self.languages_dir):
for filename in os.listdir(self.languages_dir):
if filename.endswith('.json'):
os.remove(os.path.join(self.languages_dir, filename))


@patch('internationalize.api_helpers.requests.post')
def test_create_token_success(self, mock_post):
"""
Test that create_token() successfully creates a token and sets it in globals.
"""
mock_response = MagicMock()
mock_response.status_code = 201
mock_response.json.return_value = {'value': 'test-token'}
mock_post.return_value = mock_response

with patch('builtins.print') as mock_print:
create_token()

mock_post.assert_called_once_with("http://localhost:8000/api/token/")
self.assertEqual(token.value, 'test-token')
mock_print.assert_called_once_with("Token set.")

@patch('internationalize.api_helpers.requests.post')
def test_create_token_failure(self, mock_post):
"""
Test that create_token() raises an exception when API call fails.
"""
mock_response = MagicMock()
mock_response.status_code = 400
mock_response.json.return_value = {'error': 'Bad Request'}
mock_post.return_value = mock_response

with patch('builtins.print') as mock_print:
with self.assertRaises(Exception) as context:
create_token()

mock_post.assert_called_once_with("http://localhost:8000/api/token/")
self.assertIn("Failed to retrieve token. Status code: 400", str(context.exception))
mock_print.assert_not_called()


@patch('internationalize.api_helpers.requests.get')
@patch('internationalize.api_helpers.create_token')
def test_fetch_translation_data_token_exists(self, mock_create_token, mock_get):
"""
Test that fetch_translation_data() fetches translations when token exists.
"""
token.value = 'existing-token'

mock_get_response = MagicMock()
mock_get_response.status_code = 200
mock_get_response.json.return_value = {'hello': 'hola'}
mock_get.return_value = mock_get_response

with patch('builtins.print') as mock_print:
translations = fetch_translation_data('Spanish')

mock_create_token.assert_not_called()
mock_get.assert_called_once_with(
"http://localhost:8000/api/translations/?language=Spanish",
headers={'Authorization': 'Token existing-token'}
)
mock_print.assert_called_once_with("Generated translation data for language: Spanish")
self.assertEqual(translations, {'hello': 'hola'})

@patch('internationalize.api_helpers.requests.get')
@patch('internationalize.api_helpers.requests.post')
def test_fetch_translation_data_token_missing_and_created_successfully(self, mock_post, mock_get):
"""
Test that fetch_translation_data() creates a token if missing and fetches translations successfully.
"""
token.value = ''

mock_post_response = MagicMock()
mock_post_response.status_code = 201
mock_post_response.json.return_value = {'value': 'new-token'}
mock_post.return_value = mock_post_response

mock_get_response = MagicMock()
mock_get_response.status_code = 200
mock_get_response.json.return_value = {'hello': 'hola'}
mock_get.return_value = mock_get_response

with patch('builtins.print') as mock_print:
translations = fetch_translation_data('Spanish')

mock_post.assert_called_once_with("http://localhost:8000/api/token/")
mock_get.assert_called_once_with(
"http://localhost:8000/api/translations/?language=Spanish",
headers={'Authorization': 'Token new-token'}
)
expected_calls = [
call("Token not found. Creating a new token..."),
call("Token set."),
call("Generated translation data for language: Spanish")
]
mock_print.assert_has_calls(expected_calls, any_order=False)
self.assertEqual(token.value, 'new-token')
self.assertEqual(translations, {'hello': 'hola'})

@patch('internationalize.api_helpers.requests.get')
@patch('internationalize.api_helpers.requests.post')
def test_fetch_translation_data_token_missing_and_creation_fails(self, mock_post, mock_get):
"""
Test that fetch_translation_data() raises an exception if token creation fails.
"""
token.value = ''

mock_post_response = MagicMock()
mock_post_response.status_code = 400
mock_post_response.json.return_value = {'error': 'Bad Request'}
mock_post.return_value = mock_post_response

with patch('builtins.print') as mock_print:
with self.assertRaises(Exception) as context:
fetch_translation_data('Spanish')

mock_post.assert_called_once_with("http://localhost:8000/api/token/")
mock_get.assert_not_called()
expected_calls = [
call("Token not found. Creating a new token...")
]
mock_print.assert_has_calls(expected_calls, any_order=False)
self.assertEqual(token.value, '') # token remains empty
self.assertIn("Failed to retrieve token. Status code: 400", str(context.exception))

def test_fetch_translation_data_missing_language(self):
"""
Test that fetch_translation_data() raises an exception when language parameter is missing.
"""
token.value = 'existing-token'

with patch('builtins.print') as mock_print:
with self.assertRaises(Exception) as context:
fetch_translation_data('') # missing language

self.assertIn("Language parameter is required.", str(context.exception))
mock_print.assert_not_called()

@patch('internationalize.api_helpers.requests.get')
def test_fetch_translation_data_no_translations_found(self, mock_get):
"""
Test that fetch_translation_data() raises an exception when no translations are found.
"""
token.value = 'existing-token'

mock_get_response = MagicMock()
mock_get_response.status_code = 404 # assuming 404 for no translations
mock_get_response.json.return_value = {'error': 'No translations found'}
mock_get.return_value = mock_get_response

with patch('builtins.print') as mock_print:
with self.assertRaises(Exception) as context:
fetch_translation_data('Spanish')

mock_get.assert_called_once_with(
"http://localhost:8000/api/translations/?language=Spanish",
headers={'Authorization': 'Token existing-token'}
)
mock_print.assert_called_once_with("Generated translation data for language: Spanish")
self.assertEqual(token.value, 'existing-token') # token remains unchanged
self.assertIn("No translations found for language: Spanish", str(context.exception))

if __name__ == '__main__':
unittest.main()

0 comments on commit 499b6f6

Please sign in to comment.