Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
1 change: 1 addition & 0 deletions app/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class User(Base):
email = Column(String, unique=True, nullable=False)
password = Column(String, nullable=False)
full_name = Column(String)
language = Column(String)
description = Column(String, default="Happy new user!")
avatar = Column(String, default="profile.png")
telegram_id = Column(String, unique=True)
Expand Down
77 changes: 77 additions & 0 deletions app/internal/translation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from typing import Optional

from iso639 import languages
from sqlalchemy.exc import SQLAlchemyError
from textblob import TextBlob, download_corpora
from textblob.exceptions import NotTranslated

from app.database.database import SessionLocal
from app.database.models import User

download_corpora.download_all()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change it to a lazy call? (so we will call it in the first time it's needed and not always)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the same as other nltk uses in other pull requests
also will only take time on the first time
after that is fast



def translate_text(text: str,
target_lang: str,
original_lang: Optional[str] = None
) -> str:
"""
Translate text to the target language
optionally given the original language
"""
if len(text) <= 0:
return "No text to translate"
if original_lang is None:
original_lang = _detect_text_language(text)
else:
original_lang = _lang_full_to_short(original_lang)

if original_lang == _lang_full_to_short(target_lang):
return text

try:
return str(TextBlob(text).translate(
from_lang=original_lang,
to=_lang_full_to_short(target_lang)))
except NotTranslated:
return text


def _detect_text_language(text: str) -> str:
"""
Gets some text and returns the language it is in
Uses external API
"""
return str(TextBlob(text).detect_language())


def _get_user_language(user_id: int, session: SessionLocal) -> str:
"""
Gets a user-id and returns the language he speaks
Uses the DB"""
try:
user = (
session.query(User).filter(User.id == user_id).first()
)
language_user = user.language
except SQLAlchemyError:
return ""
else:
return language_user


def translate_text_for_user(text: str,
session: SessionLocal,
user_id: int) -> str:
"""
Gets a text and a user-id and returns the text,
translated to the language the user speaks
"""
target_lang = _get_user_language(user_id, session)
if not target_lang:
return text
return translate_text(text, target_lang)


def _lang_full_to_short(full_lang: str) -> str:
return languages.get(name=full_lang.capitalize()).alpha2
1 change: 0 additions & 1 deletion app/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import nltk


nltk.download('punkt')
8 changes: 5 additions & 3 deletions app/routers/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def get_placeholder_user():
email='my@email.po',
password='1a2s3d4f5g6',
full_name='My Name',
telegram_id=''
telegram_id='',
language='english',
)


Expand Down Expand Up @@ -110,6 +111,7 @@ async def upload_user_photo(
# Save to database
user.avatar = await process_image(pic, user)
session.commit()

finally:
url = router.url_path_for("profile")
return RedirectResponse(url=url, status_code=HTTP_302_FOUND)
Expand Down Expand Up @@ -145,6 +147,6 @@ async def process_image(image, user):
def get_image_crop_area(width, height):
if width > height:
delta = (width - height) // 2
return (delta, 0, width - delta, height)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the sograim are marked redundant, but has no effect on code

return delta, 0, width - delta, height
delta = (height - width) // 2
return (0, delta, width, width + delta)
return 0, delta, width, width + delta
3 changes: 2 additions & 1 deletion app/routers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
from app.internal.utils import save


def create_user(username, password, email, session: Session) -> User:
def create_user(username, password, email, language, session: Session) -> User:
"""Creates and saves a new user."""

user = User(
username=username,
password=password,
email=email,
language=language
)
save(user, session=session)
return user
Expand Down
34 changes: 16 additions & 18 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
Jinja2==2.11.2
MarkupSafe==1.1.1
Pillow==8.1.0
PyYAML==5.3.1
SQLAlchemy==1.3.22
aiofiles==0.6.0
aioredis==1.3.1
aiosmtpd==1.2.2
aiosmtplib==1.1.4
apipkg==1.5
arrow==0.17.0
asyncio==3.4.3
async-timeout==3.0.1
asyncio==3.4.3
atomicwrites==1.4.0
atpublic==2.1.2
attrs==20.3.0
blinker==1.4
beautifulsoup4==4.9.3
blinker==1.4
certifi==2020.12.5
Expand All @@ -20,12 +24,11 @@ coverage==5.3.1
dnspython==2.1.0
email-validator==1.1.2
execnet==1.7.1
Faker==5.6.2
faker==5.6.2
fakeredis==1.4.5
fastapi-mail==0.3.3.1
fastapi==0.63.0
flake8==3.8.4
faker==5.6.2
fastapi-mail==0.3.3.1
frozendict==1.2
h11==0.12.0
h2==4.0.0
Expand All @@ -39,17 +42,15 @@ idna==2.10
importlib-metadata==3.3.0
inflect==4.1.0
iniconfig==1.1.1
Jinja2==2.11.2
mccabe==0.6.1
iso-639==0.4.5
joblib==1.0.0
lazy-object-proxy==1.5.2
loguru==0.5.3
mypy==0.790
mccabe==0.6.1
mypy-extensions==0.4.3
MarkupSafe==1.1.1
mypy==0.790
nltk==3.5
packaging==20.8
Pillow==8.1.0
pluggy==0.13.1
priority==1.3.0
psycopg2==2.8.6
Expand All @@ -58,33 +59,30 @@ pycodestyle==2.6.0
pydantic==1.7.3
pyflakes==2.2.0
pyparsing==2.4.7
pytest==6.2.1
pytest-asyncio==0.14.0
pytest-cov==2.10.1
pytest-mock==3.5.1
pytest-forked==1.3.0
pytest-mock==3.5.1
pytest-xdist==2.2.0
pytest==6.2.1
python-dateutil==2.8.1
python-dotenv==0.15.0
python-multipart==0.0.5
pytz==2020.5
PyYAML==5.3.1
redis==3.5.3
regex==2020.11.13
requests==2.25.1
rfc3986==1.4.0
requests-mock==1.8.0
requests==2.25.1
responses==0.12.1
rfc3986==1.4.0
six==1.15.0
smtpdfix==0.2.6
sniffio==1.2.0
sortedcontainers==2.3.0
soupsieve==2.1
sortedcontainers==2.3.0
SQLAlchemy==1.3.22
starlette==0.13.6
text-unidecode==1.3
textblob>=0.15.3
toml==0.10.2
tqdm==4.56.0
typed-ast==1.4.2
Expand All @@ -95,4 +93,4 @@ watchgod==0.6
websockets==8.1
word-forms==2.1.0
wsproto==1.0.0
zipp==3.4.0
zipp==3.4.0
128 changes: 128 additions & 0 deletions tests/test_translation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import pytest
from iso639 import languages
from textblob import TextBlob

from app.internal.translation import (
translate_text,
translate_text_for_user,
_get_user_language,
_lang_full_to_short,
_detect_text_language
)


@pytest.mark.parametrize("text, target_lang, original_lang",
[("Привет мой друг", "english", "russian"),
("Hola mi amigo", "english", "spanish"),
("Bonjour, mon ami", "english", "french"),
("Hallo, mein Freund", "english", "german"),
])
def test_translate_text_with_original_lang(text, target_lang, original_lang):
answer = translate_text(text, target_lang, original_lang)
assert "Hello my friend" == answer
assert TextBlob(text).detect_language() == languages.get(
name=original_lang.capitalize()).alpha2
assert TextBlob(answer).detect_language() == languages.get(
name=target_lang.capitalize()).alpha2


@pytest.mark.parametrize("text, target_lang",
[("Привет мой друг", "english"),
("Bonjour, mon ami", "english"),
("Hallo, mein Freund", "english"),
])
def test_translate_text_without_original_lang(text, target_lang):
answer = translate_text(text, target_lang)
assert "Hello my friend" == answer
assert TextBlob(answer).detect_language() == languages.get(
name=target_lang.capitalize()).alpha2


@pytest.mark.parametrize("text, target_lang, original_lang",
[("Привет мой друг", "russian", "russian"),
("Hola mi amigo", "spanish", "spanish"),
("Bonjour, mon ami", "french", "french"),
("Hallo, mein Freund", "german", "german"),
("Ciao amico", "italian", "italian")
])
def test_translate_text_with_same_original_target_lang_with_original_lang(
text,
target_lang,
original_lang):
answer = translate_text(text, target_lang, original_lang)
assert answer == text


@pytest.mark.parametrize("text, target_lang",
[("Привет мой друг", "russian"),
("Hola mi amigo", "spanish"),
("Bonjour, mon ami", "french"),
("Hallo, mein Freund", "german"),
("Ciao amico", "italian")
])
def test_translate_text_with_same_original_target_lang_without_original_lang(
text,
target_lang):
answer = translate_text(text, target_lang)
assert answer == text


def test_translate_text_without_text_with_original_target_lang():
answer = translate_text("", "english", "russian")
assert answer == "No text to translate"


def test_translate_text_without_text_without_original_lang():
answer = translate_text("", "english")
assert answer == "No text to translate"


def test_lang_short_to_full():
answer = _lang_full_to_short("english")
assert answer == "en"


def test_get_user_language(user, session):
user_id = user.id
answer = _get_user_language(user_id, session=session)
assert user_id == 1
assert answer.lower() == "english"


@pytest.mark.parametrize("text", ["Привет мой друг",
"Bonjour, mon ami",
"Hello my friend"]
)
def test_translate_text_for_user(text, user, session):
user_id = user.id
answer = translate_text_for_user(text, session, user_id)
assert answer == "Hello my friend"


def test_detect_text_language():
answer = _detect_text_language("Hello my friend")
assert answer == "en"


@pytest.mark.parametrize("text, target_lang, original_lang",
[("Hoghhflaff", "english", "spanish"),
("Bdonfdjourr", "english", "french"),
("Hafdllnnc", "english", "german"),
])
def test_translate_text_with_text_impossible_to_translate(
text,
target_lang,
original_lang):
answer = translate_text(text, target_lang, original_lang)
assert answer == text


@pytest.mark.parametrize("text, target_lang, original_lang",
[("@Здравствуй#мой$друг!", "english", "russian"),
("@Hola#mi$amigo!", "english", "spanish"),
("@Bonjour#mon$ami!", "english", "french"),
("@Hallo#mein$Freund!", "english", "german"),
])
def test_translate_text_with_symbols(text, target_lang, original_lang):
answer = translate_text(text, target_lang, original_lang)
assert "@ Hello # my $ friend!" == answer
2 changes: 2 additions & 0 deletions tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ def test_create_user(self, session):
username='new_test_username',
password='new_test_password',
email='new_test.email@gmail.com',
language='english'
)
assert user.username == 'new_test_username'
assert user.password == 'new_test_password'
assert user.email == 'new_test.email@gmail.com'
assert user.language == 'english'
session.delete(user)
session.commit()

Expand Down
2 changes: 2 additions & 0 deletions tests/user_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def user(session: Session) -> User:
username='test_username',
password='test_password',
email='test.email@gmail.com',
language='english'
)
yield test_user
delete_instance(session, test_user)
Expand All @@ -24,6 +25,7 @@ def sender(session: Session) -> User:
username='sender_username',
password='sender_password',
email='sender.email@gmail.com',
language='english'
)
yield sender
delete_instance(session, sender)