diff --git a/app/config.py.example b/app/config.py.example index 57cfa211..2fc21168 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -33,3 +33,6 @@ email_conf = ConnectionConfig( MAIL_SSL=False, USE_CREDENTIALS=True, ) + +# PATHS +STATIC_ABS_PATH = os.path.abspath("static") diff --git a/app/routers/__init__.py b/app/routers/__init__.py index e69de29b..7170f990 100644 --- a/app/routers/__init__.py +++ b/app/routers/__init__.py @@ -0,0 +1,4 @@ +import nltk + + +nltk.download('punkt') diff --git a/app/routers/event_images.py b/app/routers/event_images.py new file mode 100644 index 00000000..a04536a4 --- /dev/null +++ b/app/routers/event_images.py @@ -0,0 +1,217 @@ +from app import config +from functools import lru_cache +from nltk.tokenize import word_tokenize +from typing import Optional +from word_forms.lemmatizer import lemmatize + +import re + + +FLAIRS_EXTENSION = '.jpg' +FLAIRS_REL_PATH = f'{config.STATIC_ABS_PATH}\\event_flairs' +IMAGES_RELATED_WORDS_MAP = { + 'birthday': 'birthday', + 'coffee': 'coffee', + 'coffees': 'coffee', + 'concert': 'concert', + 'gig': 'concert', + 'concerts': 'concert', + 'gigs': 'concert', + 'bicycle': 'cycle', + 'cycling': 'cycle', + 'bike': 'cycle', + 'bicycles': 'cycle', + 'bikes': 'cycle', + 'biking': 'cycle', + 'dentist': 'dentist', + 'dentistry': 'dentist', + 'dental': 'dentist', + 'dinner': 'food', + 'dinners': 'food', + 'restaurant': 'food', + 'restaurants': 'food', + 'family meal': 'food', + 'lunch': 'food', + 'lunches': 'food', + 'luncheon': 'food', + 'cocktail': 'drank', + 'drinks': 'drank', + 'cocktails': 'drank', + 'golf': 'golf', + 'graduation': 'graduate', + 'gym': 'gym', + 'workout': 'gym', + 'workouts': 'gym', + 'haircut': 'haircut', + 'hair': 'haircut', + 'halloween': 'halloween', + 'helloween': 'halloween', + "hallowe'en": 'halloween', + 'allhalloween': 'halloween', + "all hallows' eve": 'halloween', + "all saints' Eve": 'halloween', + 'hiking': 'hike', + 'hike': 'hike', + 'hikes': 'hike', + 'kayaking': 'kayak', + 'piano': 'music', + 'singing': 'music', + 'music class': 'music', + 'choir practice': 'music', + 'flute': 'music', + 'orchestra': 'music', + 'oboe': 'music', + 'clarinet': 'music', + 'saxophone': 'music', + 'cornett': 'music', + 'trumpet': 'music', + 'contrabass': 'music', + 'cello': 'music', + 'trombone': 'music', + 'tuba': 'music', + 'music ensemble': 'music', + 'string quartett': 'music', + 'guitar lesson': 'music', + 'classical music': 'music', + 'choir': 'music', + 'manicure': 'manicure', + 'pedicure': 'manicure', + 'manicures': 'manicure', + 'pedicures': 'manicure', + 'massage': 'massage', + 'back rub': 'massage', + 'backrub': 'massage', + 'massages': 'massage', + 'pills': 'pill', + 'medicines': 'pill', + 'medicine': 'pill', + 'drug': 'pill', + 'drugs': 'pill', + 'ping pong': 'pingpong', + 'table tennis': 'pingpong', + 'ping-pong': 'pingpong', + 'pingpong': 'pingpong', + 'plan week': 'plan', + 'plan quarter': 'plan', + 'plan day': 'plan', + 'plan vacation': 'plan', + 'week planning': 'plan', + 'vacation planning': 'plan', + 'pokemon': 'pokemon', + 'reading': 'read', + 'newspaper': 'read', + 'fridge repair': 'repair', + 'handyman': 'repair', + 'electrician': 'repair', + 'diy': 'repair', + 'jog': 'ran', + 'jogging': 'ran', + 'running': 'ran', + 'jogs': 'ran', + 'runs': 'ran', + 'sail': 'sail', + 'sailing': 'sail', + 'boat cruise': 'sail', + 'sailboat': 'sail', + 'santa claus': 'santa', + 'father christmas': 'santa', + 'skiing': 'ski', + 'ski': 'ski', + 'skis': 'ski', + 'snowboarding': 'ski', + 'snowshoeing': 'ski', + 'snow shoe': 'ski', + 'snow boarding': 'ski', + 'soccer': 'soccer', + 'swim': 'swam', + 'swimming': 'swam', + 'swims': 'swam', + 'tennis': 'tennis', + 'thanksgiving': 'thanksgiving', + 'wedding': 'wed', + 'wedding eve': 'wed', + 'wedding-eve party': 'wed', + 'weddings': 'wed', + 'christmas': 'christmas', + 'xmas': 'christmas', + 'x-mas': 'christmas', + 'yoga': 'yoga', +} + + +def generate_flare_link_from_lemmatized_word(lemmatized_word: str) -> str: + """Generate a link to a flair by a given lemmatized word. + + Args: + lemmatized_word (str): The lemmatized word. + + Returns: + str: The suitable link. + """ + return f'{FLAIRS_REL_PATH}\\{lemmatized_word}{FLAIRS_EXTENSION}' + + +def remove_non_alphabet_chars(text: str) -> str: + """Remove non-alphabet chars from a given string + + Args: + text (str): The string to remove the non-alphabet chars from. + + Returns: + str: The string after the removal. + """ + regex = re.compile('[^a-zA-Z]') + return regex.sub('', text) + + +def get_image_name(related_word: str) -> Optional[str]: + """Search the key of a given value in IMAGES_RELATED_WORDS_MAP dictionary. + + Args: + related_word (str): The value to search its key. + + Returns: + str: The value's key in IMAGES_RELATED_WORDS_MAP dictionary. + """ + shrunken = remove_non_alphabet_chars(related_word).lower() + return IMAGES_RELATED_WORDS_MAP.get(shrunken) + + +@lru_cache(maxsize=32) +def search_token_in_related_words(token: str) -> Optional[str]: + """Search a token in IMAGES_RELATED_WORDS_MAP dictionary. + + Args: + token (str): The token to search. + + Returns: + str: The link to the suitable image of the given token. + """ + key = get_image_name(token) + if key: + return generate_flare_link_from_lemmatized_word(key) + + +def attach_image_to_event(event_content: str) -> str: + """Get a link to the suitable image of a given token content. + + Args: + event_content (str): The event content. + + Returns: + str: The link to the suitable image of a given token content. + """ + event_tokens = word_tokenize(event_content) + for token in event_tokens: + if token.isalnum(): + try: + base_word = lemmatize(remove_non_alphabet_chars(token).lower()) + except ValueError: + base_word = token + if base_word in IMAGES_RELATED_WORDS_MAP.values(): + return generate_flare_link_from_lemmatized_word(base_word) + link = search_token_in_related_words(token) + if link: + return link + link = '#' + return link diff --git a/app/static/event_flairs/birthday.jpg b/app/static/event_flairs/birthday.jpg new file mode 100644 index 00000000..da5da73f Binary files /dev/null and b/app/static/event_flairs/birthday.jpg differ diff --git a/app/static/event_flairs/christmas.jpg b/app/static/event_flairs/christmas.jpg new file mode 100644 index 00000000..5aa06898 Binary files /dev/null and b/app/static/event_flairs/christmas.jpg differ diff --git a/app/static/event_flairs/coffee.jpg b/app/static/event_flairs/coffee.jpg new file mode 100644 index 00000000..aec0c0e8 Binary files /dev/null and b/app/static/event_flairs/coffee.jpg differ diff --git a/app/static/event_flairs/concert.jpg b/app/static/event_flairs/concert.jpg new file mode 100644 index 00000000..5d383cbf Binary files /dev/null and b/app/static/event_flairs/concert.jpg differ diff --git a/app/static/event_flairs/cycle.jpg b/app/static/event_flairs/cycle.jpg new file mode 100644 index 00000000..62a3509a Binary files /dev/null and b/app/static/event_flairs/cycle.jpg differ diff --git a/app/static/event_flairs/dentist.jpg b/app/static/event_flairs/dentist.jpg new file mode 100644 index 00000000..e4075202 Binary files /dev/null and b/app/static/event_flairs/dentist.jpg differ diff --git a/app/static/event_flairs/drank.jpg b/app/static/event_flairs/drank.jpg new file mode 100644 index 00000000..517ab704 Binary files /dev/null and b/app/static/event_flairs/drank.jpg differ diff --git a/app/static/event_flairs/food.jpg b/app/static/event_flairs/food.jpg new file mode 100644 index 00000000..80a39832 Binary files /dev/null and b/app/static/event_flairs/food.jpg differ diff --git a/app/static/event_flairs/golf.jpg b/app/static/event_flairs/golf.jpg new file mode 100644 index 00000000..2d787446 Binary files /dev/null and b/app/static/event_flairs/golf.jpg differ diff --git a/app/static/event_flairs/graduate.jpg b/app/static/event_flairs/graduate.jpg new file mode 100644 index 00000000..b3820132 Binary files /dev/null and b/app/static/event_flairs/graduate.jpg differ diff --git a/app/static/event_flairs/gym.jpg b/app/static/event_flairs/gym.jpg new file mode 100644 index 00000000..0ee0f8cd Binary files /dev/null and b/app/static/event_flairs/gym.jpg differ diff --git a/app/static/event_flairs/haircut.jpg b/app/static/event_flairs/haircut.jpg new file mode 100644 index 00000000..b7ec8c32 Binary files /dev/null and b/app/static/event_flairs/haircut.jpg differ diff --git a/app/static/event_flairs/halloween.jpg b/app/static/event_flairs/halloween.jpg new file mode 100644 index 00000000..24aed91f Binary files /dev/null and b/app/static/event_flairs/halloween.jpg differ diff --git a/app/static/event_flairs/hike.jpg b/app/static/event_flairs/hike.jpg new file mode 100644 index 00000000..183fafc1 Binary files /dev/null and b/app/static/event_flairs/hike.jpg differ diff --git a/app/static/event_flairs/kayak.jpg b/app/static/event_flairs/kayak.jpg new file mode 100644 index 00000000..abcd0e6e Binary files /dev/null and b/app/static/event_flairs/kayak.jpg differ diff --git a/app/static/event_flairs/manicure.jpg b/app/static/event_flairs/manicure.jpg new file mode 100644 index 00000000..62513be8 Binary files /dev/null and b/app/static/event_flairs/manicure.jpg differ diff --git a/app/static/event_flairs/massage.jpg b/app/static/event_flairs/massage.jpg new file mode 100644 index 00000000..db95454d Binary files /dev/null and b/app/static/event_flairs/massage.jpg differ diff --git a/app/static/event_flairs/music.jpg b/app/static/event_flairs/music.jpg new file mode 100644 index 00000000..5a722d93 Binary files /dev/null and b/app/static/event_flairs/music.jpg differ diff --git a/app/static/event_flairs/pill.jpg b/app/static/event_flairs/pill.jpg new file mode 100644 index 00000000..71ae31b1 Binary files /dev/null and b/app/static/event_flairs/pill.jpg differ diff --git a/app/static/event_flairs/pingpong.jpg b/app/static/event_flairs/pingpong.jpg new file mode 100644 index 00000000..47a4dd66 Binary files /dev/null and b/app/static/event_flairs/pingpong.jpg differ diff --git a/app/static/event_flairs/plan.jpg b/app/static/event_flairs/plan.jpg new file mode 100644 index 00000000..34a9c009 Binary files /dev/null and b/app/static/event_flairs/plan.jpg differ diff --git a/app/static/event_flairs/pokemon.jpg b/app/static/event_flairs/pokemon.jpg new file mode 100644 index 00000000..9ddda581 Binary files /dev/null and b/app/static/event_flairs/pokemon.jpg differ diff --git a/app/static/event_flairs/ran.jpg b/app/static/event_flairs/ran.jpg new file mode 100644 index 00000000..e2ad3e06 Binary files /dev/null and b/app/static/event_flairs/ran.jpg differ diff --git a/app/static/event_flairs/read.jpg b/app/static/event_flairs/read.jpg new file mode 100644 index 00000000..cb66e0d7 Binary files /dev/null and b/app/static/event_flairs/read.jpg differ diff --git a/app/static/event_flairs/repair.jpg b/app/static/event_flairs/repair.jpg new file mode 100644 index 00000000..59731a10 Binary files /dev/null and b/app/static/event_flairs/repair.jpg differ diff --git a/app/static/event_flairs/sail.jpg b/app/static/event_flairs/sail.jpg new file mode 100644 index 00000000..00c6fba5 Binary files /dev/null and b/app/static/event_flairs/sail.jpg differ diff --git a/app/static/event_flairs/santa.jpg b/app/static/event_flairs/santa.jpg new file mode 100644 index 00000000..6b5a8794 Binary files /dev/null and b/app/static/event_flairs/santa.jpg differ diff --git a/app/static/event_flairs/ski.jpg b/app/static/event_flairs/ski.jpg new file mode 100644 index 00000000..dae2fa32 Binary files /dev/null and b/app/static/event_flairs/ski.jpg differ diff --git a/app/static/event_flairs/soccer.jpg b/app/static/event_flairs/soccer.jpg new file mode 100644 index 00000000..31de5b38 Binary files /dev/null and b/app/static/event_flairs/soccer.jpg differ diff --git a/app/static/event_flairs/swam.jpg b/app/static/event_flairs/swam.jpg new file mode 100644 index 00000000..04aba94b Binary files /dev/null and b/app/static/event_flairs/swam.jpg differ diff --git a/app/static/event_flairs/tennis.jpg b/app/static/event_flairs/tennis.jpg new file mode 100644 index 00000000..cc015542 Binary files /dev/null and b/app/static/event_flairs/tennis.jpg differ diff --git a/app/static/event_flairs/thanksgiving.jpg b/app/static/event_flairs/thanksgiving.jpg new file mode 100644 index 00000000..25dff4b2 Binary files /dev/null and b/app/static/event_flairs/thanksgiving.jpg differ diff --git a/app/static/event_flairs/wed.jpg b/app/static/event_flairs/wed.jpg new file mode 100644 index 00000000..61fe9559 Binary files /dev/null and b/app/static/event_flairs/wed.jpg differ diff --git a/app/static/event_flairs/yoga.jpg b/app/static/event_flairs/yoga.jpg new file mode 100644 index 00000000..a0993c5b Binary files /dev/null and b/app/static/event_flairs/yoga.jpg differ diff --git a/requirements.txt b/requirements.txt index ba9af619..dc96da2b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,45 @@ aiofiles==0.6.0 +aioredis==1.3.1 +aiosmtpd==1.2.2 +aiosmtplib==1.1.4 apipkg==1.5 arrow==0.17.0 +async-timeout==3.0.1 atomicwrites==1.4.0 +atpublic==2.1.2 attrs==20.3.0 beautifulsoup4==4.9.3 +blinker==1.4 certifi==2020.12.5 chardet==4.0.0 click==7.1.2 colorama==0.4.4 coverage==5.3.1 +dnspython==2.1.0 +email-validator==1.1.2 execnet==1.7.1 +Faker==5.6.2 +fakeredis==1.4.5 fastapi==0.63.0 -fastapi_mail==0.3.3.1 -faker==5.6.2 +fastapi-mail==0.3.3.1 frozendict==1.2 -smtpdfix==0.2.6 h11==0.12.0 h2==4.0.0 +hiredis==1.1.0 hpack==4.0.0 +httpcore==0.12.2 +httpx==0.16.1 hyperframe==6.0.0 icalendar==4.0.7 idna==2.10 importlib-metadata==3.3.0 +inflect==4.1.0 iniconfig==1.1.1 Jinja2==2.11.2 +joblib==1.0.0 +lazy-object-proxy==1.5.2 MarkupSafe==1.1.1 +nltk==3.5 packaging==20.8 Pillow==8.1.0 pluggy==0.13.1 @@ -42,18 +57,27 @@ 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 requests-mock==1.8.0 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 SQLAlchemy==1.3.22 starlette==0.13.6 +text-unidecode==1.3 toml==0.10.2 +tqdm==4.56.0 typing-extensions==3.7.4.3 urllib3==1.26.2 uvicorn==0.13.3 watchgod==0.6 websockets==8.1 +word-forms==2.1.0 wsproto==1.0.0 zipp==3.4.0 diff --git a/tests/test_event_images.py b/tests/test_event_images.py new file mode 100644 index 00000000..ba7df635 --- /dev/null +++ b/tests/test_event_images.py @@ -0,0 +1,77 @@ +from app.routers.event_images import attach_image_to_event,\ + generate_flare_link_from_lemmatized_word, get_image_name,\ + remove_non_alphabet_chars, search_token_in_related_words +from app import config +import pytest + + +static = config.STATIC_ABS_PATH + + +lemmatized_words = [ + ("ran", f'{static}\\event_flairs\\ran.jpg'), + ("food", f'{static}\\event_flairs\\food.jpg'), + ("i", f'{static}\\event_flairs\\i.jpg'), + ("drank", f'{static}\\event_flairs\\drank.jpg'), +] + + +@pytest.mark.parametrize('lemmatized, link', lemmatized_words) +def test_generate_flare_link_from_lemmatized_word(lemmatized, link): + assert generate_flare_link_from_lemmatized_word(lemmatized) == link + + +contents = [ + (r"it's my birthday!", r"itsmybirthday"), + (r"iT's my birthday!!!", r"iTsmybirthday"), + (r"its my birthday", r"itsmybirthday"), + (r"it's-my-birthday!1990", r"itsmybirthday"), +] + + +@pytest.mark.parametrize('content, alphabet_only', contents) +def test_remove_non_alphabet_chars(content, alphabet_only): + assert remove_non_alphabet_chars(content) == alphabet_only + + +values = [ + (r"backrub", r"massage"), + (r"--MedicineS", r"pill"), + (r"restaurants", r"food"), + (r"pikachu", None), + (r"Pokemon", r"pokemon"), +] + + +@pytest.mark.parametrize('related_word, key', values) +def test_get_image_name(related_word, key): + assert get_image_name(related_word) == key + + +tokens = [ + (r"backrub", f'{static}\\event_flairs\\massage.jpg'), + (r"--MedicineS", f'{static}\\event_flairs\\pill.jpg'), + (r"restaurants", f'{static}\\event_flairs\\food.jpg'), + (r"pikachu", None), + (r"Pokemon", f'{static}\\event_flairs\\pokemon.jpg'), +] + + +@pytest.mark.parametrize('token, link', tokens) +def test_search_token_in_related_words(token, link): + assert search_token_in_related_words(token) == link + + +event_contents = [ + (r"memo backrub and medicines!!", f'{static}\\event_flairs\\massage.jpg'), + (r"Dont forget medicines & backrub!", f'{static}\\event_flairs\\pill.jpg'), + (r"Its important to drink", f'{static}\\event_flairs\\drank.jpg'), + (r"call Jim about tennis friday", f'{static}\\event_flairs\\tennis.jpg'), + (r"have to check on pikachu", r'#'), + (r"-~new pokemon episode 19:00~!", f'{static}\\event_flairs\\pokemon.jpg'), +] + + +@pytest.mark.parametrize('event_content, link', event_contents) +def test_attach_image_to_event(event_content, link): + assert attach_image_to_event(event_content) == link