From 5ef64692408249e51e3593eee0f36ae88c9bacd4 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 11 Feb 2021 14:48:40 +0200 Subject: [PATCH 01/26] basic restoration of code from a deleted branch. includes- adding is_public attr to event model, a function that adds a user to existing event and test for that function. --- app/database/models.py | 2 +- app/routers/event.py | 25 +++++++++++++++++++++++++ tests/test_event.py | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/app/database/models.py b/app/database/models.py index 9f0f7f1a..19a62241 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -59,7 +59,7 @@ class Event(Base): color = Column(String, nullable=True) emotion = Column(String, nullable=True) invitees = Column(String) - + is_public = Column(Boolean, default=False) owner_id = Column(Integer, ForeignKey("users.id")) color = Column(String, nullable=True) category_id = Column(Integer, ForeignKey("categories.id")) diff --git a/app/routers/event.py b/app/routers/event.py index d0d4a06e..b5b5b05b 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -300,3 +300,28 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: except (AssertionError, AttributeError, TypeError) as e: logger.exception(e) return None + + +def add_user_to_event( + session: Session, user_id: int, event_id: int +): """this function gets an existing event and a user and adds the user to the participant list. + function won't add the user to the event if the user is already participates in it. + Function will return: + True if the connection was set successfuly + False if it didn't""" + user_already_connected = session.query(UserEvent).filter_by(event_id == event_id, user_id == user_id).all() + if user_already_connected: + """ if user is not registered to the event, the system will add him""" + try: + create_model( + db, UserEvent, + user_id=owner_id, + event_id=event.id + ) + return True + except: + return False + else: + """if the user has a connection to the event, + the function will recognize the duplicate and return false.""" + return False \ No newline at end of file diff --git a/tests/test_event.py b/tests/test_event.py index e8c4b588..dc498ccf 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -99,6 +99,29 @@ ] +#------------------------------------------------- +# fixtures +#------------------------------------------------- + +@pytest.fixture +def new_event(): + """this func is a valid event, made in order to test event functions""" + return add_new_event(TestApp.event_test_data, session) + + +#------------------------------------------------- +# tests +#------------------------------------------------- + +def test_joining_public_event(): + """test in order to make sure user is added the first time he asks to join event, + yet won't join the same user twice""" + first_join = add_user_to_event(session,new_user.id,new_event.id) + assert first_join + second_join = add_user_to_event(session,new_user.id,new_event.id) + assert not second_join + + def test_eventedit(event_test_client): response = event_test_client.get("/event/edit") assert response.ok From 885a84349c4ef48ccf053067cd049287444810ac Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 11 Feb 2021 14:54:46 +0200 Subject: [PATCH 02/26] added 2 functions- one to send an adaptable email, with email body content given as a var, and also a func that sends email to all participants in event. --- app/internal/email.py | 41 +++++++++++++++++++++++++++++++++++++++++ app/routers/email.py | 14 ++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/app/internal/email.py b/app/internal/email.py index 87092f7f..29a53733 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -52,6 +52,47 @@ def send( return True +def send_email_with_body( + session: Session, event_used: int, user_to_send: int, + title: str, body_content: str, background_tasks: BackgroundTasks = BackgroundTasks +) -> bool: + """This function is the altered version of the function above, + that allows adaptable email content. + It takes an event, user and email content and sends the event to the user. + + Args: + session(Session): The session to redirect to the database. + title (str): Title of the email that is being sent. + event_used (int): Id number of the event that is used. + user_to_send (int): Id number of user that we want to notify. + body_content (string): the content of the email sent to the user. + background_tasks (BackgroundTasks): Function from fastapi that lets + you apply tasks in the background. + + + Returns: + bool: Returns True if the email was sent, else returns False. + """ + event_used = session.query(Event).filter( + Event.id == event_used).first() + user_to_send = session.query(User).filter( + User.id == user_to_send).first() + if not user_to_send or not event_used: + return False + if not verify_email_pattern(user_to_send.email): + return False + + subject = f"{title} {event_used.title}" + recipients = {"email": [user_to_send.email]}.get("email") + body = f"begins at:{event_used.start} : {event_used.content} \n {body_content}" + + background_tasks.add_task(send_internal, + subject=subject, + recipients=recipients, + body=body) + return True + + def send_email_invitation(sender_name: str, recipient_name: str, recipient_mail: str, diff --git a/app/routers/email.py b/app/routers/email.py index 908c35a2..378e50b1 100644 --- a/app/routers/email.py +++ b/app/routers/email.py @@ -69,3 +69,17 @@ def send_invitation(invitation: InvitationParams, background_tasks=background_task): raise HTTPException(status_code=422, detail="Couldn't send the email!") return RedirectResponse(invitation.send_to, status_code=303) + + +def send_email_to_event_participants( + session: Session, + event_id: int, + email_content: str, + title: str): + """ this function sents emails to all of the event participants. + the function gets the event id and the content of the email, then sends emails. + even if the sending of one of the email fails the function will keep running.""" + all_participants = session.query(UserEvent).filter(event_id == event_id).all() + for person in all_participants: + send_email_with_body(session, event_id, person.user_id, title, email_content) + From 1f4b0844d186d3b65d7b477f05fa78ae09fbb779 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 11 Feb 2021 15:00:03 +0200 Subject: [PATCH 03/26] added front end button to allow users to join event, not connected to backend yet... --- app/templates/event/eventview.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/templates/event/eventview.html b/app/templates/event/eventview.html index 708195da..291d411a 100644 --- a/app/templates/event/eventview.html +++ b/app/templates/event/eventview.html @@ -44,6 +44,9 @@

{{ message }}

+ {% if event.is_public %} + + {% endif %} From 8e0d1ee4de24afbe6cb55569760db2983d265c93 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 11 Feb 2021 20:04:59 +0200 Subject: [PATCH 04/26] adding testing fixtures --- app/routers/event.py | 9 ++------- tests/test_event.py | 14 +++++++++----- tests/test_user.py | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index b5b5b05b..a1ac21c3 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -302,13 +302,8 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: return None -def add_user_to_event( - session: Session, user_id: int, event_id: int -): """this function gets an existing event and a user and adds the user to the participant list. - function won't add the user to the event if the user is already participates in it. - Function will return: - True if the connection was set successfuly - False if it didn't""" +def add_user_to_event(session: Session, user_id: int, event_id: int): + user_already_connected = session.query(UserEvent).filter_by(event_id == event_id, user_id == user_id).all() if user_already_connected: """ if user is not registered to the event, the system will add him""" diff --git a/tests/test_event.py b/tests/test_event.py index dc498ccf..c71aa763 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -11,7 +11,10 @@ from app.main import app from app.routers.event import (_delete_event, _update_event, add_new_event, by_id, check_change_dates_allowed, delete_event, - is_date_before, update_event) + create_event, is_date_before, update_event) +from app.routers.event import add_user_to_event +from tests.test_user import new_user + CORRECT_EVENT_FORM_DATA = { 'title': 'test title', @@ -104,21 +107,22 @@ #------------------------------------------------- @pytest.fixture -def new_event(): +def new_event(session): """this func is a valid event, made in order to test event functions""" - return add_new_event(TestApp.event_test_data, session) + event = create_event(db, **CORRECT_EVENT_FORM_DATA) + return event #------------------------------------------------- # tests #------------------------------------------------- -def test_joining_public_event(): +def test_joining_public_event(session): """test in order to make sure user is added the first time he asks to join event, yet won't join the same user twice""" first_join = add_user_to_event(session,new_user.id,new_event.id) assert first_join - second_join = add_user_to_event(session,new_user.id,new_event.id) + second_join = add_user_to_event(session,new_user.id, new_event.id) assert not second_join diff --git a/tests/test_user.py b/tests/test_user.py index a5c3411a..7cf37657 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,5 +1,29 @@ +import pytest + from app.routers.user import create_user, does_user_exist, get_users +# ----------------------------------------------------- +# Fixtures +# ----------------------------------------------------- + +@pytest.fixture +def new_user(session): + user = create_user( + session=session, + username='new_test_username', + password='new_test_password', + email='new_test.email@gmail.com', + language='english' + ) + + return user + + + +# ----------------------------------------------------- +# Tests +# ----------------------------------------------------- + class TestUser: From 5a8adeb5790f8d3fa2c10712aeff8fb1659e37a5 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 11 Feb 2021 23:59:54 +0200 Subject: [PATCH 05/26] adding working user and event fixture, adding is_public param to event creation. fixing tests. tests are not working properly yet! --- app/database/models.py | 4 ++-- app/routers/event.py | 23 ++++++++++++----------- tests/test_event.py | 14 ++++++++------ tests/test_user.py | 2 +- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index 19a62241..c145ac33 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -86,8 +86,8 @@ class UserEvent(Base): __tablename__ = "user_event" id = Column(Integer, primary_key=True, index=True) - user_id = Column('user_id', Integer, ForeignKey('users.id')) - event_id = Column('event_id', Integer, ForeignKey('events.id')) + user_id = Column('user_id', Integer, ForeignKey('users.id'), nullable=False) + event_id = Column('event_id', Integer, ForeignKey('events.id'), nullable=False) events = relationship("Event", back_populates="participants") participants = relationship("User", back_populates="events") diff --git a/app/routers/event.py b/app/routers/event.py index a1ac21c3..b37dd041 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -199,6 +199,7 @@ def create_event(db: Session, title: str, start, end, owner_id: int, content: str = None, location: str = None, invitees: List[str] = None, + is_public: bool = False, category_id: int = None): """Creates an event and an association.""" @@ -214,6 +215,7 @@ def create_event(db: Session, title: str, start, end, owner_id: int, location=location, emotion=get_emotion(title, content), invitees=invitees_concatenated, + is_public=is_public, category_id=category_id, ) create_model( @@ -304,18 +306,17 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: def add_user_to_event(session: Session, user_id: int, event_id: int): - user_already_connected = session.query(UserEvent).filter_by(event_id == event_id, user_id == user_id).all() - if user_already_connected: + user_already_connected = session.query(UserEvent).filter(event_id == event_id, user_id == user_id).all() + print(user_already_connected) + if not user_already_connected: """ if user is not registered to the event, the system will add him""" - try: - create_model( - db, UserEvent, - user_id=owner_id, - event_id=event.id - ) - return True - except: - return False + + create_model( + session, UserEvent, + user_id=user_id, + event_id=event_id + ) + return True else: """if the user has a connection to the event, the function will recognize the duplicate and return false.""" diff --git a/tests/test_event.py b/tests/test_event.py index c71aa763..768946a4 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -106,10 +106,11 @@ # fixtures #------------------------------------------------- + + @pytest.fixture -def new_event(session): - """this func is a valid event, made in order to test event functions""" - event = create_event(db, **CORRECT_EVENT_FORM_DATA) +def new_event(session, new_user): + event = add_new_event(TestApp.event_test_data, session) return event @@ -117,12 +118,13 @@ def new_event(session): # tests #------------------------------------------------- -def test_joining_public_event(session): +def test_joining_public_event(session, new_event, new_user): """test in order to make sure user is added the first time he asks to join event, yet won't join the same user twice""" - first_join = add_user_to_event(session,new_user.id,new_event.id) + print(f"user {new_user.id} event {new_event.id}") + first_join = add_user_to_event(session,new_event.id, new_user.id) assert first_join - second_join = add_user_to_event(session,new_user.id, new_event.id) + second_join = add_user_to_event(session,new_event.id, new_user.id) assert not second_join diff --git a/tests/test_user.py b/tests/test_user.py index 7cf37657..c9d60b75 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -13,7 +13,7 @@ def new_user(session): username='new_test_username', password='new_test_password', email='new_test.email@gmail.com', - language='english' + language_id='english' ) return user From 59532d62a4a2713d40a5704112574bc7efe9bc6a Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 12 Feb 2021 00:05:49 +0200 Subject: [PATCH 06/26] subtle changes for debugging add_user_to_event failing tests. --- app/routers/event.py | 4 ++-- tests/test_event.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index b37dd041..c5724375 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -305,8 +305,8 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: def add_user_to_event(session: Session, user_id: int, event_id: int): - - user_already_connected = session.query(UserEvent).filter(event_id == event_id, user_id == user_id).all() + print(f'inside adding func: event {event_id} user {user_id}') + user_already_connected = session.query(UserEvent).filter(event_id == event_id, user_id == user_id).one() print(user_already_connected) if not user_already_connected: """ if user is not registered to the event, the system will add him""" diff --git a/tests/test_event.py b/tests/test_event.py index 768946a4..178074fa 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -122,9 +122,9 @@ def test_joining_public_event(session, new_event, new_user): """test in order to make sure user is added the first time he asks to join event, yet won't join the same user twice""" print(f"user {new_user.id} event {new_event.id}") - first_join = add_user_to_event(session,new_event.id, new_user.id) + first_join = add_user_to_event(session,event_id=new_event.id, user_id=new_user.id) assert first_join - second_join = add_user_to_event(session,new_event.id, new_user.id) + second_join = add_user_to_event(session,event_id=new_event.id, user_id=new_user.id) assert not second_join From 235c4b2c02d03e4a23d8fdc7968fb93323695df5 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 12 Feb 2021 16:32:22 +0200 Subject: [PATCH 07/26] added something that prevents adding null event_id to userevent objects. --- app/routers/event.py | 12 ++++++------ tests/test_event.py | 39 +++++++++++++++++++++++++++------------ tests/test_user.py | 24 ------------------------ 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index c5724375..ea3338ce 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -16,7 +16,7 @@ raise_if_zoom_link_invalid, ) from app.internal.emotion import get_emotion -from app.internal.utils import create_model +from app.internal.utils import create_model, save from app.routers.user import create_user TIME_FORMAT = '%Y-%m-%d %H:%M' @@ -306,18 +306,18 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: def add_user_to_event(session: Session, user_id: int, event_id: int): print(f'inside adding func: event {event_id} user {user_id}') - user_already_connected = session.query(UserEvent).filter(event_id == event_id, user_id == user_id).one() + user_already_connected = session.query(UserEvent)\ + .filter_by(event_id=event_id, user_id=user_id).all() print(user_already_connected) if not user_already_connected: """ if user is not registered to the event, the system will add him""" - - create_model( - session, UserEvent, + association = UserEvent( user_id=user_id, event_id=event_id ) + save(session, association) return True else: """if the user has a connection to the event, the function will recognize the duplicate and return false.""" - return False \ No newline at end of file + return False diff --git a/tests/test_event.py b/tests/test_event.py index 178074fa..45070c2a 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -11,9 +11,9 @@ from app.main import app from app.routers.event import (_delete_event, _update_event, add_new_event, by_id, check_change_dates_allowed, delete_event, - create_event, is_date_before, update_event) -from app.routers.event import add_user_to_event -from tests.test_user import new_user + is_date_before, update_event, + add_user_to_event) +from app.routers.user import create_user CORRECT_EVENT_FORM_DATA = { @@ -102,10 +102,9 @@ ] -#------------------------------------------------- +# ------------------------------------------------- # fixtures -#------------------------------------------------- - +# ------------------------------------------------- @pytest.fixture @@ -114,17 +113,33 @@ def new_event(session, new_user): return event -#------------------------------------------------- +@pytest.fixture +def new_user(session): + user = create_user( + session=session, + username='new_test_username', + password='new_test_password', + email='new_test.email@gmail.com', + language_id='english' + ) + + return user + + +# ------------------------------------------------- # tests -#------------------------------------------------- +# ------------------------------------------------- + def test_joining_public_event(session, new_event, new_user): - """test in order to make sure user is added the first time he asks to join event, - yet won't join the same user twice""" + """test in order to make sure user is added the first time + he asks to join event, yet won't join the same user twice""" print(f"user {new_user.id} event {new_event.id}") - first_join = add_user_to_event(session,event_id=new_event.id, user_id=new_user.id) + first_join = add_user_to_event( + session, event_id=2, user_id=1) assert first_join - second_join = add_user_to_event(session,event_id=new_event.id, user_id=new_user.id) + second_join = add_user_to_event( + session, event_id=2, user_id=1) assert not second_join diff --git a/tests/test_user.py b/tests/test_user.py index c9d60b75..a5c3411a 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,29 +1,5 @@ -import pytest - from app.routers.user import create_user, does_user_exist, get_users -# ----------------------------------------------------- -# Fixtures -# ----------------------------------------------------- - -@pytest.fixture -def new_user(session): - user = create_user( - session=session, - username='new_test_username', - password='new_test_password', - email='new_test.email@gmail.com', - language_id='english' - ) - - return user - - - -# ----------------------------------------------------- -# Tests -# ----------------------------------------------------- - class TestUser: From d32ca25ba5cbdfa723bc8a0e7d331d9d3f9714cb Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 12 Feb 2021 16:44:36 +0200 Subject: [PATCH 08/26] fixed a small bug that made one of the tests fail --- app/routers/event.py | 2 -- tests/test_event.py | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index ea3338ce..45ef9dfd 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -305,10 +305,8 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: def add_user_to_event(session: Session, user_id: int, event_id: int): - print(f'inside adding func: event {event_id} user {user_id}') user_already_connected = session.query(UserEvent)\ .filter_by(event_id=event_id, user_id=user_id).all() - print(user_already_connected) if not user_already_connected: """ if user is not registered to the event, the system will add him""" association = UserEvent( diff --git a/tests/test_event.py b/tests/test_event.py index 45070c2a..857b8d5b 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -134,12 +134,11 @@ def new_user(session): def test_joining_public_event(session, new_event, new_user): """test in order to make sure user is added the first time he asks to join event, yet won't join the same user twice""" - print(f"user {new_user.id} event {new_event.id}") first_join = add_user_to_event( - session, event_id=2, user_id=1) + session, event_id=new_event.id, user_id=new_user.id) assert first_join second_join = add_user_to_event( - session, event_id=2, user_id=1) + session, event_id=new_event.id, user_id=new_user.id) assert not second_join From ef0f679e6d4d883cf449fa7a42137969899f0188 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 12 Feb 2021 19:24:30 +0200 Subject: [PATCH 09/26] added func that send email to participants given event id- there are still bugs inside --- app/database/models.py | 6 ++++-- app/internal/email.py | 41 ++++++++++++++++++++--------------------- app/routers/email.py | 23 ++++++++++++----------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index c145ac33..3d79fc78 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -86,8 +86,10 @@ class UserEvent(Base): __tablename__ = "user_event" id = Column(Integer, primary_key=True, index=True) - user_id = Column('user_id', Integer, ForeignKey('users.id'), nullable=False) - event_id = Column('event_id', Integer, ForeignKey('events.id'), nullable=False) + user_id = Column('user_id', Integer, ForeignKey( + 'users.id'), nullable=False) + event_id = Column('event_id', Integer, ForeignKey( + 'events.id'), nullable=False) events = relationship("Event", back_populates="participants") participants = relationship("User", back_populates="events") diff --git a/app/internal/email.py b/app/internal/email.py index 29a53733..aeb184ba 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -9,7 +9,7 @@ from app.config import (CALENDAR_HOME_PAGE, CALENDAR_REGISTRATION_PAGE, CALENDAR_SITE_NAME, email_conf, templates) -from app.database.models import Event, User +from app.database.models import Event, User, UserEvent mail = FastMail(email_conf) @@ -52,39 +52,38 @@ def send( return True -def send_email_with_body( - session: Session, event_used: int, user_to_send: int, - title: str, body_content: str, background_tasks: BackgroundTasks = BackgroundTasks +def send_email_to_event_participants( + session: Session, event_id: int, email_to_send: List[str], + title: str, content: str, + background_tasks: BackgroundTasks = BackgroundTasks ) -> bool: - """This function is the altered version of the function above, - that allows adaptable email content. - It takes an event, user and email content and sends the event to the user. + """This function is being used to send emails in the background. + It takes an event and a user and it sends the event to the user. Args: session(Session): The session to redirect to the database. title (str): Title of the email that is being sent. event_used (int): Id number of the event that is used. user_to_send (int): Id number of user that we want to notify. - body_content (string): the content of the email sent to the user. background_tasks (BackgroundTasks): Function from fastapi that lets you apply tasks in the background. - Returns: - bool: Returns True if the email was sent, else returns False. + bool: Returns True if emails were sent, False if none. """ - event_used = session.query(Event).filter( - Event.id == event_used).first() - user_to_send = session.query(User).filter( - User.id == user_to_send).first() - if not user_to_send or not event_used: - return False - if not verify_email_pattern(user_to_send.email): + mailing_list = session.query(User.email).join( + UserEvent, User.id == UserEvent.user_id + ).filter( + event_id == event_id).all() + # making sure app doesn't crash cause emails are invalid + valid_mailing_list = list(filter(verify_email_pattern, mailing_list)) + + if not valid_mailing_list: return False - - subject = f"{title} {event_used.title}" - recipients = {"email": [user_to_send.email]}.get("email") - body = f"begins at:{event_used.start} : {event_used.content} \n {body_content}" + event = session.query(Event).get(event_id) + subject = f"{event.title}: {title}" + recipients = valid_mailing_list + body = content background_tasks.add_task(send_internal, subject=subject, diff --git a/app/routers/email.py b/app/routers/email.py index 378e50b1..926fe50c 100644 --- a/app/routers/email.py +++ b/app/routers/email.py @@ -71,15 +71,16 @@ def send_invitation(invitation: InvitationParams, return RedirectResponse(invitation.send_to, status_code=303) -def send_email_to_event_participants( - session: Session, - event_id: int, - email_content: str, - title: str): - """ this function sents emails to all of the event participants. - the function gets the event id and the content of the email, then sends emails. - even if the sending of one of the email fails the function will keep running.""" - all_participants = session.query(UserEvent).filter(event_id == event_id).all() - for person in all_participants: - send_email_with_body(session, event_id, person.user_id, title, email_content) +# def send_email_to_event_participants( +# session: Session, +# event_id: int, +# email_content: str, +# title: str): +# """ this function sents emails to all of the event participants. +# the function gets the event id and the content of the email, +# then sends emails. even if the sending of +# one of the email fails the function will keep running.""" +# for person in get_participants_emails_by_event(session, event_id): +# send_email_with_body(session, event_id, person.user_id, +# title, email_content) From c6477a53806da8ec4a080af172d4d56ec4c93553 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 12 Feb 2021 19:34:19 +0200 Subject: [PATCH 10/26] email not working- last commit before rebuild --- app/internal/email.py | 77 ++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/app/internal/email.py b/app/internal/email.py index aeb184ba..d8eb9b64 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -52,44 +52,45 @@ def send( return True -def send_email_to_event_participants( - session: Session, event_id: int, email_to_send: List[str], - title: str, content: str, - background_tasks: BackgroundTasks = BackgroundTasks -) -> bool: - """This function is being used to send emails in the background. - It takes an event and a user and it sends the event to the user. - - Args: - session(Session): The session to redirect to the database. - title (str): Title of the email that is being sent. - event_used (int): Id number of the event that is used. - user_to_send (int): Id number of user that we want to notify. - background_tasks (BackgroundTasks): Function from fastapi that lets - you apply tasks in the background. - - Returns: - bool: Returns True if emails were sent, False if none. - """ - mailing_list = session.query(User.email).join( - UserEvent, User.id == UserEvent.user_id - ).filter( - event_id == event_id).all() - # making sure app doesn't crash cause emails are invalid - valid_mailing_list = list(filter(verify_email_pattern, mailing_list)) - - if not valid_mailing_list: - return False - event = session.query(Event).get(event_id) - subject = f"{event.title}: {title}" - recipients = valid_mailing_list - body = content - - background_tasks.add_task(send_internal, - subject=subject, - recipients=recipients, - body=body) - return True +# def send_email_to_event_participants( +# session: Session, event_id: int, email_to_send: List[str], +# title: str, content: str, +# background_tasks: BackgroundTasks = BackgroundTasks +# ) -> bool: +# """This function is being used to send emails in the background. +# It takes an event and a user and it sends the event to the user. + +# Args: +# session(Session): The session to redirect to the database. +# title (str): Title of the email that is being sent. +# event_id (int): Id number of the event that is used. +# content (str): body of email sent. +# background_tasks (BackgroundTasks): Function from fastapi that lets +# you apply tasks in the background. + +# Returns: +# bool: Returns True if emails were sent, False if none. +# """ +# mailing_list = session.query(User.email).join( +# UserEvent, User.id == UserEvent.user_id +# ).filter( +# event_id == event_id).all() +# # making sure app doesn't crash cause emails are invalid +# valid_mailing_list = list(filter(verify_email_pattern, mailing_list)) + +# if not valid_mailing_list: +# return False +# event = session.query(Event).get(event_id) +# subject = f"{event.title}: {title}" +# recipients = valid_mailing_list +# body = content +# for r in recipients: +# print(r) +# background_tasks.add_task(send_internal, +# subject=subject, +# recipients=r, +# body=body) +# return True def send_email_invitation(sender_name: str, From 49ac8e7b9b488521ee1896c279198fe5290ed40c Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Mon, 15 Feb 2021 00:23:41 +0200 Subject: [PATCH 11/26] improved documentation --- app/internal/email.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/internal/email.py b/app/internal/email.py index 4d59296d..86b88e65 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -58,6 +58,7 @@ def send_email_to_event_participants( background_tasks: BackgroundTasks = BackgroundTasks ) -> bool: """This function is being used to send emails in the background. + it uses the main elements of the func writen above. It takes an event and a user and it sends the event to the user. Args: session(Session): The session to redirect to the database. @@ -73,7 +74,7 @@ def send_email_to_event_participants( UserEvent, User.id == UserEvent.user_id ).filter( event_id == event_id).all() - # making sure app doesn't crash cause emails are invalid + # making sure app doesn't crash if emails are invalid valid_mailing_list = list(filter(verify_email_pattern, mailing_list)) if not valid_mailing_list: return False From e111110742198023cc3c0537e553a132497601a1 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Mon, 15 Feb 2021 00:34:07 +0200 Subject: [PATCH 12/26] adds feature that makes sure only the event owner can send email to all participants --- app/internal/email.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/internal/email.py b/app/internal/email.py index 86b88e65..384ea146 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -10,7 +10,7 @@ from app.config import (CALENDAR_HOME_PAGE, CALENDAR_REGISTRATION_PAGE, CALENDAR_SITE_NAME, email_conf, templates) from app.database.models import Event, User, UserEvent - +from app.internal.utils import get_current_user mail = FastMail(email_conf) @@ -53,7 +53,7 @@ def send( def send_email_to_event_participants( - session: Session, event_id: int, email_to_send: List[str], + session: Session, event_id: int, title: str, content: str, background_tasks: BackgroundTasks = BackgroundTasks ) -> bool: @@ -70,14 +70,18 @@ def send_email_to_event_participants( Returns: bool: Returns True if emails were sent, False if none. """ + event_owner = session.query(Event.owner).filter(id == event_id).first() + if event_owner != get_current_user(session): + return False + # makes sure only event owner can send an email via this func. mailing_list = session.query(User.email).join( UserEvent, User.id == UserEvent.user_id ).filter( event_id == event_id).all() - # making sure app doesn't crash if emails are invalid valid_mailing_list = list(filter(verify_email_pattern, mailing_list)) if not valid_mailing_list: return False + # making sure app doesn't crash if emails are invalid event = session.query(Event).get(event_id) subject = f"{event.title}: {title}" recipients = valid_mailing_list From f515361ea479823293e41431ca6e42d86b3c597e Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 18 Feb 2021 18:58:14 +0200 Subject: [PATCH 13/26] trying to add is_public feature to telegram create event bot --- app/telegram/handlers.py | 36 +++++++++++++++++++++++++++++++- tests/test_a_telegram_asyncio.py | 6 ++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/telegram/handlers.py b/app/telegram/handlers.py index 8b2a6719..fbb9ddc0 100644 --- a/app/telegram/handlers.py +++ b/app/telegram/handlers.py @@ -134,10 +134,14 @@ async def process_new_event(self, memo_dict): return await self._process_content(memo_dict) elif 'location' not in memo_dict: return await self._process_location(memo_dict) + elif 'is_public' not in memo_dict: + return await self._process_is_public(memo_dict) elif 'start' not in memo_dict: return await self._process_start_date(memo_dict) elif 'end' not in memo_dict: return await self._process_end_date(memo_dict) + elif 'end' not in memo_dict: + return await self._process_end_date(memo_dict) elif self.chat.message == 'create': return await self._submit_new_event(memo_dict) @@ -184,7 +188,36 @@ async def _process_content(self, memo_dict): async def _process_location(self, memo_dict): memo_dict['location'] = self.chat.message answer = f'Location:\n{memo_dict["location"]}\n\n' - answer += 'When does it start?' + answer += 'is the event public? (yes\\no)' + await telegram_bot.send_message( + chat_id=self.chat.user_id, + text=answer, + reply_markup=field_kb) + return answer + + async def _process_is_public(self, memo_dict): + is_public_response = self.chat.message.lower + if is_public_response in ['yes', 'no']: + return await self._add_is_public() + return await self._process_bad_bool_input() + + async def _process_bad_bool_input(self): + answer = '❗️ Please, enter an answer of yes/no.' + await telegram_bot.send_message( + chat_id=self.chat.user_id, + text=answer, + reply_markup=field_kb) + return answer + + async def _add_is_public(self, memo_dict): + if self.chat.message.lower == 'yes': + memo_dict['is_public'] = True + answer = r'public\\private event:\npublic event\n\n' + else: + answer = 'private event' + memo_dict['is_public'] = False + answer = r'public\\private event:\nprivate event\n\n' + answer += '\nWhen does it start?' await telegram_bot.send_message( chat_id=self.chat.user_id, text=answer, @@ -248,6 +281,7 @@ async def _submit_new_event(self, memo_dict): content=memo_dict['content'], owner_id=self.user.id, location=memo_dict['location'], + is_public=memo_dict['is_public'], ) # Delete current session del telegram_bot.MEMORY[self.chat.user_id] diff --git a/tests/test_a_telegram_asyncio.py b/tests/test_a_telegram_asyncio.py index ec58f989..239905b0 100644 --- a/tests/test_a_telegram_asyncio.py +++ b/tests/test_a_telegram_asyncio.py @@ -245,6 +245,12 @@ async def test_process_new_event(self): chat = Chat(gen_message('Universe')) message = MessageHandler(chat, self.TEST_USER) answer = 'Location:\nUniverse\n\n' + answer += 'is the event public? (yes\\no)' + assert await message.process_callback() == answer + + chat = Chat(gen_message('yes')) + message = MessageHandler(chat, self.TEST_USER) + answer = r'public\\private event:\npublic event\n\n' answer += 'When does it start?' assert await message.process_callback() == answer From e4f2ee04615f9bde7a2f7accc60dfbced0dd8f13 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:29:10 +0200 Subject: [PATCH 14/26] creating more changes to enable public feature on create event func on telegram --- app/telegram/handlers.py | 5 +++-- tests/test_a_telegram_asyncio.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/telegram/handlers.py b/app/telegram/handlers.py index fbb9ddc0..8aff7c71 100644 --- a/app/telegram/handlers.py +++ b/app/telegram/handlers.py @@ -198,6 +198,7 @@ async def _process_location(self, memo_dict): async def _process_is_public(self, memo_dict): is_public_response = self.chat.message.lower if is_public_response in ['yes', 'no']: + print('yasss') return await self._add_is_public() return await self._process_bad_bool_input() @@ -212,11 +213,11 @@ async def _process_bad_bool_input(self): async def _add_is_public(self, memo_dict): if self.chat.message.lower == 'yes': memo_dict['is_public'] = True - answer = r'public\\private event:\npublic event\n\n' + answer = 'public\\private event:\npublic event\n\n' else: answer = 'private event' memo_dict['is_public'] = False - answer = r'public\\private event:\nprivate event\n\n' + answer = 'public\\private event:\nprivate event\n\n' answer += '\nWhen does it start?' await telegram_bot.send_message( chat_id=self.chat.user_id, diff --git a/tests/test_a_telegram_asyncio.py b/tests/test_a_telegram_asyncio.py index 239905b0..c9cb8659 100644 --- a/tests/test_a_telegram_asyncio.py +++ b/tests/test_a_telegram_asyncio.py @@ -250,7 +250,7 @@ async def test_process_new_event(self): chat = Chat(gen_message('yes')) message = MessageHandler(chat, self.TEST_USER) - answer = r'public\\private event:\npublic event\n\n' + answer = 'public\\private event:\npublic event\n\n' answer += 'When does it start?' assert await message.process_callback() == answer From 9ffa3ea36db40bc987f54ed37c83bd74454af397 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:29:47 +0200 Subject: [PATCH 15/26] last chance to add telegram feature- will be deleted soon... --- app/telegram/handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/telegram/handlers.py b/app/telegram/handlers.py index 8aff7c71..af069213 100644 --- a/app/telegram/handlers.py +++ b/app/telegram/handlers.py @@ -196,7 +196,7 @@ async def _process_location(self, memo_dict): return answer async def _process_is_public(self, memo_dict): - is_public_response = self.chat.message.lower + is_public_response = self.chat.message.lower() if is_public_response in ['yes', 'no']: print('yasss') return await self._add_is_public() From e8571f1ccce73fde9cadb53ab161d6a4997ab3ae Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:44:46 +0200 Subject: [PATCH 16/26] reverting changes made on telegram --- app/telegram/handlers.py | 37 +------------------------------- tests/test_a_telegram_asyncio.py | 6 ------ 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/app/telegram/handlers.py b/app/telegram/handlers.py index af069213..8b2a6719 100644 --- a/app/telegram/handlers.py +++ b/app/telegram/handlers.py @@ -134,14 +134,10 @@ async def process_new_event(self, memo_dict): return await self._process_content(memo_dict) elif 'location' not in memo_dict: return await self._process_location(memo_dict) - elif 'is_public' not in memo_dict: - return await self._process_is_public(memo_dict) elif 'start' not in memo_dict: return await self._process_start_date(memo_dict) elif 'end' not in memo_dict: return await self._process_end_date(memo_dict) - elif 'end' not in memo_dict: - return await self._process_end_date(memo_dict) elif self.chat.message == 'create': return await self._submit_new_event(memo_dict) @@ -188,37 +184,7 @@ async def _process_content(self, memo_dict): async def _process_location(self, memo_dict): memo_dict['location'] = self.chat.message answer = f'Location:\n{memo_dict["location"]}\n\n' - answer += 'is the event public? (yes\\no)' - await telegram_bot.send_message( - chat_id=self.chat.user_id, - text=answer, - reply_markup=field_kb) - return answer - - async def _process_is_public(self, memo_dict): - is_public_response = self.chat.message.lower() - if is_public_response in ['yes', 'no']: - print('yasss') - return await self._add_is_public() - return await self._process_bad_bool_input() - - async def _process_bad_bool_input(self): - answer = '❗️ Please, enter an answer of yes/no.' - await telegram_bot.send_message( - chat_id=self.chat.user_id, - text=answer, - reply_markup=field_kb) - return answer - - async def _add_is_public(self, memo_dict): - if self.chat.message.lower == 'yes': - memo_dict['is_public'] = True - answer = 'public\\private event:\npublic event\n\n' - else: - answer = 'private event' - memo_dict['is_public'] = False - answer = 'public\\private event:\nprivate event\n\n' - answer += '\nWhen does it start?' + answer += 'When does it start?' await telegram_bot.send_message( chat_id=self.chat.user_id, text=answer, @@ -282,7 +248,6 @@ async def _submit_new_event(self, memo_dict): content=memo_dict['content'], owner_id=self.user.id, location=memo_dict['location'], - is_public=memo_dict['is_public'], ) # Delete current session del telegram_bot.MEMORY[self.chat.user_id] diff --git a/tests/test_a_telegram_asyncio.py b/tests/test_a_telegram_asyncio.py index c9cb8659..ec58f989 100644 --- a/tests/test_a_telegram_asyncio.py +++ b/tests/test_a_telegram_asyncio.py @@ -245,12 +245,6 @@ async def test_process_new_event(self): chat = Chat(gen_message('Universe')) message = MessageHandler(chat, self.TEST_USER) answer = 'Location:\nUniverse\n\n' - answer += 'is the event public? (yes\\no)' - assert await message.process_callback() == answer - - chat = Chat(gen_message('yes')) - message = MessageHandler(chat, self.TEST_USER) - answer = 'public\\private event:\npublic event\n\n' answer += 'When does it start?' assert await message.process_callback() == answer From 71ee772aa6359ed8fed5193d0b59e4dcae4a5e12 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 19 Feb 2021 15:55:23 +0200 Subject: [PATCH 17/26] changed email sending function to depend on send function so its more efficient. --- app/internal/email.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/app/internal/email.py b/app/internal/email.py index 314664b2..ea85a5b0 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -16,7 +16,8 @@ def send( session: Session, event_used: int, user_to_send: int, - title: str, background_tasks: BackgroundTasks = BackgroundTasks + title: str, content: str = "", + background_tasks: BackgroundTasks = BackgroundTasks ) -> bool: """This function is being used to send emails in the background. It takes an event and a user and it sends the event to the user. @@ -48,25 +49,20 @@ def send( background_tasks.add_task(send_internal, subject=subject, recipients=recipients, - body=body) + body=body + content) return True def send_email_to_event_participants( session: Session, event_id: int, - title: str, content: str, - background_tasks: BackgroundTasks = BackgroundTasks -) -> bool: - """This function is being used to send emails in the background. - it uses the main elements of the func writen above. - It takes an event and a user and it sends the event to the user. + title: str, content: str) -> bool: + """This function sends emails to a mailing list of all event participants. + it uses the function send above to do this and avoid double codes.. Args: session(Session): The session to redirect to the database. - title (str): Title of the email that is being sent. event_id (int): Id number of the event that is used. + title (str): Title of the email that is being sent. content (str): body of email sent. - background_tasks (BackgroundTasks): Function from fastapi that lets - you apply tasks in the background. Returns: bool: Returns True if emails were sent, False if none. """ @@ -74,21 +70,20 @@ def send_email_to_event_participants( if event_owner != get_current_user(session): return False # makes sure only event owner can send an email via this func. - mailing_list = session.query(User.email).join( + mailing_list = session.query(User.id, User.email).join( UserEvent, User.id == UserEvent.user_id ).filter( event_id == event_id).all() - valid_mailing_list = list(filter(verify_email_pattern, mailing_list)) + valid_mailing_list = list(filter(verify_email_pattern, mailing_list.email)) if not valid_mailing_list: return False # making sure app doesn't crash if emails are invalid + event = session.query(Event).get(event_id) subject = f"{event.title}: {title}" for r in valid_mailing_list: - background_tasks.add_task(send_internal, - subject=subject, - recipients=r, - body=content) + send(session, event, r.id, subject, content) + # sends the send email function parameters to send on the mailing list return True From 9cf15b1c8c330d7dde6aaad6d7f1f81e4c8511dd Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Sat, 20 Feb 2021 14:17:29 +0200 Subject: [PATCH 18/26] moving user and event fixtures to conftest.py so we can use them globally --- tests/conftest.py | 57 +++++++++++++++++++++++++++ tests/test_email.py | 15 ++++++- tests/test_user.py | 96 ++++++++++----------------------------------- 3 files changed, 90 insertions(+), 78 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4923a614..c80366bb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import calendar +from datetime import datetime import pytest from sqlalchemy import create_engine @@ -6,6 +7,8 @@ from app.config import PSQL_ENVIRONMENT from app.database.models import Base +from app.routers.event import create_event +from app.routers.user import create_user pytest_plugins = [ 'tests.user_fixture', @@ -79,3 +82,57 @@ def sqlite_engine(): @pytest.fixture def Calendar(): return calendar.Calendar(0) + + +@pytest.fixture +def no_event_user(session): + # a user made for testing who doesn't own any event. + user = create_user( + session=session, + username='new_test_username', + password='new_test_password', + email='new2_test.email@gmail.com', + language_id='english' + ) + + return user + + +@pytest.fixture +def event_owning_user(session): + # a user made for testing who already owns an event. + user = create_user( + session=session, + username='new_test_username2', + password='new_test_password2', + email='new_test_love231.email@gmail.com', + language_id='english' + ) + + data = { + 'title': 'event_owning_user event', + 'start': datetime.strptime('2021-05-05 14:59', '%Y-%m-%d %H:%M'), + 'end': datetime.strptime('2021-05-05 15:01', '%Y-%m-%d %H:%M'), + 'location': 'https://us02web.zoom.us/j/875384596', + 'content': 'content', + 'owner_id': user.id, + } + + create_event(session, **data) + + return user + + +@pytest.fixture +def event_example(session, event_owning_user): + data = { + 'title': 'test event title', + 'start': datetime.strptime('2021-05-05 14:59', '%Y-%m-%d %H:%M'), + 'end': datetime.strptime('2021-05-05 15:01', '%Y-%m-%d %H:%M'), + 'location': 'https://us02web.zoom.us/j/87538459r6', + 'content': 'content', + 'owner_id': event_owning_user.id, + } + + event = create_event(session, **data) + return event diff --git a/tests/test_email.py b/tests/test_email.py index 37138239..516f3b91 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -2,10 +2,10 @@ import pytest from sqlalchemy.orm import Session -from app.database.models import User +from app.database.models import User, UserEvent from app.internal.email import (mail, send, send_email_file, send_email_invitation, verify_email_pattern) -from app.internal.utils import create_model, delete_instance +from app.internal.utils import create_model, delete_instance, save def test_email_send(client, user, event, smtpd): @@ -240,3 +240,14 @@ def test_send(session, bad_user, event): "test@mail-com"]) def test_verify_email_pattern(email): assert not verify_email_pattern(email) + + +def test_sending_mailing_list(session, no_event_user, + event_owning_user, + event_example): + + association = UserEvent( + user_id=no_event_user.id, + event_id=event_example.id + ) + save(session, association) diff --git a/tests/test_user.py b/tests/test_user.py index 213e7589..8d58064a 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,5 +1,4 @@ from datetime import datetime -import pytest from app.routers.user import ( create_user, does_user_exist, get_users @@ -7,109 +6,54 @@ from app.internal.user.availability import disable, enable from app.internal.utils import save from app.database.models import UserEvent, Event -from app.routers.event import create_event -@pytest.fixture -def user1(session): - # a user made for testing who doesn't own any event. - user = create_user( - session=session, - username='new_test_username', - password='new_test_password', - email='new2_test.email@gmail.com', - language_id='english' - ) - - return user - - -@pytest.fixture -def user2(session): - # a user made for testing who already owns an event. - user = create_user( - session=session, - username='new_test_username2', - password='new_test_password2', - email='new_test_love231.email@gmail.com', - language_id='english' - ) - - data = { - 'title': 'user2 event', - 'start': datetime.strptime('2021-05-05 14:59', '%Y-%m-%d %H:%M'), - 'end': datetime.strptime('2021-05-05 15:01', '%Y-%m-%d %H:%M'), - 'location': 'https://us02web.zoom.us/j/875384596', - 'content': 'content', - 'owner_id': user.id, - } - - create_event(session, **data) - - return user - - -@pytest.fixture -def event1(session, user2): - data = { - 'title': 'test event title', - 'start': datetime.strptime('2021-05-05 14:59', '%Y-%m-%d %H:%M'), - 'end': datetime.strptime('2021-05-05 15:01', '%Y-%m-%d %H:%M'), - 'location': 'https://us02web.zoom.us/j/87538459r6', - 'content': 'content', - 'owner_id': user2.id, - } - - event = create_event(session, **data) - return event - - -def test_disabling_no_event_user(session, user1): +def test_disabling_no_event_user(session, no_event_user): # users without any future event can disable themselves - disable(session, user1.id) - assert user1.disabled + disable(session, no_event_user.id) + assert no_event_user.disabled future_events = list(session.query(Event.id) .join(UserEvent) .filter( - UserEvent.user_id == user1.id, + UserEvent.user_id == no_event_user.id, Event.start > datetime .now())) assert not future_events # making sure that after disabling the user he can be easily enabled. - enable(session, user1.id) - assert not user1.disabled + enable(session, no_event_user.id) + assert not no_event_user.disabled def test_disabling_user_participating_event( - session, user1, event1): + session, no_event_user, event_example): """making sure only users who only participate in events can disable and enable themselves.""" association = UserEvent( - user_id=user1.id, - event_id=event1.id + user_id=no_event_user.id, + event_id=event_example.id ) save(session, association) - disable(session, user1.id) - assert user1.disabled + disable(session, no_event_user.id) + assert no_event_user.disabled future_events = list(session.query(Event.id) .join(UserEvent) .filter( - UserEvent.user_id == user1.id, + UserEvent.user_id == no_event_user.id, Event.start > datetime.now(), - Event.owner_id == user1.id)) + Event.owner_id == no_event_user.id)) assert not future_events - enable(session, user1.id) - assert not user1.disabled + enable(session, no_event_user.id) + assert not no_event_user.disabled deleted_user_event_connection = session.query(UserEvent).filter( - UserEvent.user_id == user1.id, - UserEvent.event_id == event1.id).first() + UserEvent.user_id == no_event_user.id, + UserEvent.event_id == event_example.id).first() session.delete(deleted_user_event_connection) -def test_disabling_event_owning_user(session, user2): +def test_disabling_event_owning_user(session, event_owning_user): # making sure user owning events can't disable itself - disable(session, user2.id) - assert not user2.disabled + disable(session, event_owning_user.id) + assert not event_owning_user.disabled class TestUser: From 70be01988960ce435379cb1b5f684c980548a1e7 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Sat, 20 Feb 2021 14:39:07 +0200 Subject: [PATCH 19/26] changing mailing list sender to return the number of emails sent instead of bool of whether emails were send. tried simple tests for it --- app/internal/email.py | 11 ++++++----- tests/conftest.py | 17 +++++++++++++++-- tests/test_email.py | 15 ++++++++++++++- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/internal/email.py b/app/internal/email.py index ea85a5b0..83b35a32 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -55,7 +55,7 @@ def send( def send_email_to_event_participants( session: Session, event_id: int, - title: str, content: str) -> bool: + title: str, content: str) -> int: """This function sends emails to a mailing list of all event participants. it uses the function send above to do this and avoid double codes.. Args: @@ -64,11 +64,12 @@ def send_email_to_event_participants( title (str): Title of the email that is being sent. content (str): body of email sent. Returns: - bool: Returns True if emails were sent, False if none. + int: Returns the number of emails sent + (number of valid emails in event's participants) """ event_owner = session.query(Event.owner).filter(id == event_id).first() if event_owner != get_current_user(session): - return False + return 0 # makes sure only event owner can send an email via this func. mailing_list = session.query(User.id, User.email).join( UserEvent, User.id == UserEvent.user_id @@ -76,7 +77,7 @@ def send_email_to_event_participants( event_id == event_id).all() valid_mailing_list = list(filter(verify_email_pattern, mailing_list.email)) if not valid_mailing_list: - return False + return 0 # making sure app doesn't crash if emails are invalid event = session.query(Event).get(event_id) @@ -84,7 +85,7 @@ def send_email_to_event_participants( for r in valid_mailing_list: send(session, event, r.id, subject, content) # sends the send email function parameters to send on the mailing list - return True + return len(valid_mailing_list) def send_email_invitation(sender_name: str, diff --git a/tests/conftest.py b/tests/conftest.py index c80366bb..bcd048c7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -86,7 +86,7 @@ def Calendar(): @pytest.fixture def no_event_user(session): - # a user made for testing who doesn't own any event. + """a user made for testing who doesn't own any event.""" user = create_user( session=session, username='new_test_username', @@ -100,7 +100,7 @@ def no_event_user(session): @pytest.fixture def event_owning_user(session): - # a user made for testing who already owns an event. + """a user made for testing who already owns an event.""" user = create_user( session=session, username='new_test_username2', @@ -123,6 +123,19 @@ def event_owning_user(session): return user +@pytest.fixture +def user1(session): + """another user made for testing""" + user = create_user( + session=session, + username='user2user2', + password='verynicepass', + email='trulyyours1.email@gmail.com', + language_id='english' + ) + return user + + @pytest.fixture def event_example(session, event_owning_user): data = { diff --git a/tests/test_email.py b/tests/test_email.py index 516f3b91..e794c783 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -4,7 +4,9 @@ from app.database.models import User, UserEvent from app.internal.email import (mail, send, send_email_file, - send_email_invitation, verify_email_pattern) + send_email_invitation, + send_email_to_event_participants, + verify_email_pattern) from app.internal.utils import create_model, delete_instance, save @@ -244,6 +246,7 @@ def test_verify_email_pattern(email): def test_sending_mailing_list(session, no_event_user, event_owning_user, + user1, event_example): association = UserEvent( @@ -251,3 +254,13 @@ def test_sending_mailing_list(session, no_event_user, event_id=event_example.id ) save(session, association) + + association2 = UserEvent( + user_id=user1.id, + event_id=event_example.id + ) + save(session, association2) + + num_emails_send = send_email_to_event_participants( + session, event_example.id, 'this mail example', 'booboo') + assert num_emails_send == 2 From 1bc712c51427f287b287330cceeffdd2553179f8 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Sat, 20 Feb 2021 14:40:00 +0200 Subject: [PATCH 20/26] making test to assert failure of sending mailing list with no logged user --- tests/test_email.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_email.py b/tests/test_email.py index e794c783..d693c579 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -244,7 +244,7 @@ def test_verify_email_pattern(email): assert not verify_email_pattern(email) -def test_sending_mailing_list(session, no_event_user, +def test_sending_mailing_list_with_no_user(session, no_event_user, event_owning_user, user1, event_example): @@ -263,4 +263,4 @@ def test_sending_mailing_list(session, no_event_user, num_emails_send = send_email_to_event_participants( session, event_example.id, 'this mail example', 'booboo') - assert num_emails_send == 2 + assert num_emails_send == 0 From 05310b712245023e238fca7aea57e988b20480f8 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Sat, 20 Feb 2021 15:48:15 +0200 Subject: [PATCH 21/26] splitting mailing list send to 2 tests- one where no user is logged (sending is supposed to fail) and the second where event owner sends email --- tests/test_email.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/test_email.py b/tests/test_email.py index 15e12773..bd9c0b64 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -1,8 +1,10 @@ from fastapi import BackgroundTasks, status import pytest from sqlalchemy.orm import Session +# from starlette.status import HTTP_302_FOUND from app.database.models import User, UserEvent +from app.internal.security.dependancies import current_user from app.internal.email import (mail, send, send_email_file, send_email_invitation, send_email_to_event_participants, @@ -248,7 +250,7 @@ def test_sending_mailing_list_with_no_user(session, no_event_user, event_owning_user, user1, event_example): - + """this test assures a wrong user won't be able to use the mailing list""" association = UserEvent( user_id=no_event_user.id, event_id=event_example.id @@ -264,3 +266,24 @@ def test_sending_mailing_list_with_no_user(session, no_event_user, num_emails_send = send_email_to_event_participants( session, event_example.id, 'this mail example', 'booboo') assert num_emails_send == 0 + + +def test_sending_mailing_list_from_event_owner(session, no_event_user, + event_owning_user, + user1, + event_example, client, + security_test_client): + """this test assures mailing list is sent successfuly from + the event owner. assiciations were created already at the test above.""" + logged_user_data = {'username': event_owning_user.username, + 'password': event_owning_user.password} + + # res = security_test_client.post( + # security_test_client.app.url_path_for('login'), + # data=logged_user_data) + # TODO: log in event_owning_user to assure successful mailing list send + + # if res.status_code == HTTP_302_FOUND: + # num_emails_send = send_email_to_event_participants( + # session, event_example.id, 'this mail example', 'booboo') + # assert num_emails_send == 2 From c02ffd0019e624cc833356aaa48d9ed7ff618105 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Sat, 20 Feb 2021 17:31:13 +0200 Subject: [PATCH 22/26] small pep changes --- tests/test_email.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_email.py b/tests/test_email.py index bd9c0b64..d494838b 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -4,7 +4,7 @@ # from starlette.status import HTTP_302_FOUND from app.database.models import User, UserEvent -from app.internal.security.dependancies import current_user +# from app.internal.security.dependancies import current_user from app.internal.email import (mail, send, send_email_file, send_email_invitation, send_email_to_event_participants, @@ -275,9 +275,9 @@ def test_sending_mailing_list_from_event_owner(session, no_event_user, security_test_client): """this test assures mailing list is sent successfuly from the event owner. assiciations were created already at the test above.""" - logged_user_data = {'username': event_owning_user.username, - 'password': event_owning_user.password} - + # logged_user_data = {'username': event_owning_user.username, + # 'password': event_owning_user.password} + pass # res = security_test_client.post( # security_test_client.app.url_path_for('login'), # data=logged_user_data) From b6ff758ea615637a96856e428b582eab3b366633 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Sun, 21 Feb 2021 19:25:57 +0200 Subject: [PATCH 23/26] deleting some visual comments according to yam's commends + hooks --- app/routers/event.py | 21 ++++++++++----------- app/templates/eventview.html | 15 --------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 12cdd465..5d2beed1 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -519,20 +519,19 @@ def add_new_event(values: dict, db: Session) -> Optional[Event]: def add_user_to_event(session: Session, user_id: int, event_id: int): - user_already_connected = session.query(UserEvent)\ - .filter_by(event_id=event_id, user_id=user_id).all() + user_already_connected = ( + session.query(UserEvent) + .filter_by(event_id=event_id, user_id=user_id) + .all() + ) if not user_already_connected: - """ if user is not registered to the event, the system will add him""" - association = UserEvent( - user_id=user_id, - event_id=event_id - ) + """if user is not registered to the event, the system will add him""" + association = UserEvent(user_id=user_id, event_id=event_id) save(session, association) return True - else: - # if the user has a connection to the event, - # the function will recognize the duplicate and return false. - return False + # if the user has a connection to the event, + # the function will recognize the duplicate and return false. + return False def get_template_to_share_event( diff --git a/app/templates/eventview.html b/app/templates/eventview.html index 2d4bad9c..3d219975 100644 --- a/app/templates/eventview.html +++ b/app/templates/eventview.html @@ -32,19 +32,4 @@ ADD INCLUDE HERE --> -<<<<<<< HEAD - - - - - -
- - - - {% if event.is_public %} - - {% endif %} -======= ->>>>>>> 8cd3a6e3c8c94ff0e9c86c9a6ed15c9856f757f9
From 093881b93ea2a2f6f16359ae8f3cf2a7ba75070d Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Tue, 23 Feb 2021 20:10:26 +0200 Subject: [PATCH 24/26] trying to add precommit hooks --- app/internal/email.py | 126 ++++++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 49 deletions(-) diff --git a/app/internal/email.py b/app/internal/email.py index 83b35a32..3017f6c4 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -7,17 +7,26 @@ from pydantic.errors import EmailError from sqlalchemy.orm.session import Session -from app.config import (CALENDAR_HOME_PAGE, CALENDAR_REGISTRATION_PAGE, - CALENDAR_SITE_NAME, email_conf, templates) +from app.config import ( + CALENDAR_HOME_PAGE, + CALENDAR_REGISTRATION_PAGE, + CALENDAR_SITE_NAME, + email_conf, + templates, +) from app.database.models import Event, User, UserEvent from app.internal.utils import get_current_user + mail = FastMail(email_conf) def send( - session: Session, event_used: int, user_to_send: int, - title: str, content: str = "", - background_tasks: BackgroundTasks = BackgroundTasks + session: Session, + event_used: int, + user_to_send: int, + title: str, + content: str = "", + background_tasks: BackgroundTasks = BackgroundTasks, ) -> bool: """This function is being used to send emails in the background. It takes an event and a user and it sends the event to the user. @@ -33,10 +42,8 @@ def send( Returns: bool: Returns True if the email was sent, else returns False. """ - event_used = session.query(Event).filter( - Event.id == event_used).first() - user_to_send = session.query(User).filter( - User.id == user_to_send).first() + event_used = session.query(Event).filter(Event.id == event_used).first() + user_to_send = session.query(User).filter(User.id == user_to_send).first() if not user_to_send or not event_used: return False if not verify_email_pattern(user_to_send.email): @@ -46,16 +53,21 @@ def send( recipients = {"email": [user_to_send.email]}.get("email") body = f"begins at:{event_used.start} : {event_used.content}" - background_tasks.add_task(send_internal, - subject=subject, - recipients=recipients, - body=body + content) + background_tasks.add_task( + send_internal, + subject=subject, + recipients=recipients, + body=body + content, + ) return True def send_email_to_event_participants( - session: Session, event_id: int, - title: str, content: str) -> int: + session: Session, + event_id: int, + title: str, + content: str, +) -> int: """This function sends emails to a mailing list of all event participants. it uses the function send above to do this and avoid double codes.. Args: @@ -71,10 +83,12 @@ def send_email_to_event_participants( if event_owner != get_current_user(session): return 0 # makes sure only event owner can send an email via this func. - mailing_list = session.query(User.id, User.email).join( - UserEvent, User.id == UserEvent.user_id - ).filter( - event_id == event_id).all() + mailing_list = ( + session.query(User.id, User.email) + .join(UserEvent, User.id == UserEvent.user_id) + .filter(event_id == event_id) + .all() + ) valid_mailing_list = list(filter(verify_email_pattern, mailing_list.email)) if not valid_mailing_list: return 0 @@ -88,11 +102,12 @@ def send_email_to_event_participants( return len(valid_mailing_list) -def send_email_invitation(sender_name: str, - recipient_name: str, - recipient_mail: str, - background_tasks: BackgroundTasks = BackgroundTasks - ) -> bool: +def send_email_invitation( + sender_name: str, + recipient_name: str, + recipient_mail: str, + background_tasks: BackgroundTasks = BackgroundTasks, +) -> bool: """ This function takes as parameters the sender's name, the recipient's name and his email address, configuration, and @@ -117,28 +132,35 @@ def send_email_invitation(sender_name: str, return False template = templates.get_template("invite_mail.html") - html = template.render(recipient=recipient_name, sender=sender_name, - site_name=CALENDAR_SITE_NAME, - registration_link=CALENDAR_REGISTRATION_PAGE, - home_link=CALENDAR_HOME_PAGE, - addr_to=recipient_mail) + html = template.render( + recipient=recipient_name, + sender=sender_name, + site_name=CALENDAR_SITE_NAME, + registration_link=CALENDAR_REGISTRATION_PAGE, + home_link=CALENDAR_HOME_PAGE, + addr_to=recipient_mail, + ) subject = "Invitation" recipients = [recipient_mail] body = html subtype = "html" - background_tasks.add_task(send_internal, - subject=subject, - recipients=recipients, - body=body, - subtype=subtype) + background_tasks.add_task( + send_internal, + subject=subject, + recipients=recipients, + body=body, + subtype=subtype, + ) return True -def send_email_file(file_path: str, - recipient_mail: str, - background_tasks: BackgroundTasks = BackgroundTasks): +def send_email_file( + file_path: str, + recipient_mail: str, + background_tasks: BackgroundTasks = BackgroundTasks, +): """ his function takes as parameters the file's path, the recipient's email address, configuration, and @@ -162,19 +184,23 @@ def send_email_file(file_path: str, body = "file" file_attachments = [file_path] - background_tasks.add_task(send_internal, - subject=subject, - recipients=recipients, - body=body, - file_attachments=file_attachments) + background_tasks.add_task( + send_internal, + subject=subject, + recipients=recipients, + body=body, + file_attachments=file_attachments, + ) return True -async def send_internal(subject: str, - recipients: List[str], - body: str, - subtype: Optional[str] = None, - file_attachments: Optional[List[str]] = None): +async def send_internal( + subject: str, + recipients: List[str], + body: str, + subtype: Optional[str] = None, + file_attachments: Optional[List[str]] = None, +): if file_attachments is None: file_attachments = [] @@ -183,8 +209,10 @@ async def send_internal(subject: str, recipients=[EmailStr(recipient) for recipient in recipients], body=body, subtype=subtype, - attachments=[UploadFile(file_attachment) - for file_attachment in file_attachments]) + attachments=[ + UploadFile(file_attachment) for file_attachment in file_attachments + ], + ) return await send_internal_internal(message) From 76db81a99e5deb57c3448a453f084d82baa3fbe0 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 26 Feb 2021 16:46:53 +0200 Subject: [PATCH 25/26] added guards and small changes --- app/routers/event.py | 14 ++++++++------ tests/test_email.py | 2 -- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/routers/event.py b/app/routers/event.py index 2d49aa2c..c7196ca6 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -625,14 +625,16 @@ def add_user_to_event(session: Session, user_id: int, event_id: int): .filter_by(event_id=event_id, user_id=user_id) .all() ) - if not user_already_connected: - """if user is not registered to the event, the system will add him""" - association = UserEvent(user_id=user_id, event_id=event_id) - save(session, association) - return True + # if the user has a connection to the event, # the function will recognize the duplicate and return false. - return False + + if user_already_connected: + return False + # if user is not registered to the event, the system will add him + association = UserEvent(user_id=user_id, event_id=event_id) + save(session, association) + return True def extract_shared_list_from_data( diff --git a/tests/test_email.py b/tests/test_email.py index 2ab34cfc..6e4279dd 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -13,8 +13,6 @@ ) from app.internal.utils import create_model, delete_instance, save -# from starlette.status import HTTP_302_FOUND - def test_email_send(client, user, event, smtpd): mail.config.SUPPRESS_SEND = 1 From 94c4a32492be92df75bfd0467648f2564ba0e555 Mon Sep 17 00:00:00 2001 From: Noam <63012023+noam-y@users.noreply.github.com> Date: Fri, 26 Feb 2021 17:17:04 +0200 Subject: [PATCH 26/26] first try of adding koby's user system to event mailing list send --- app/internal/email.py | 7 ++++--- app/routers/email.py | 40 +++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/app/internal/email.py b/app/internal/email.py index 82c9d640..71f30ad8 100644 --- a/app/internal/email.py +++ b/app/internal/email.py @@ -1,7 +1,7 @@ import os from typing import List, Optional -from fastapi import BackgroundTasks, UploadFile +from fastapi import BackgroundTasks, Depends, UploadFile from fastapi_mail import FastMail, MessageSchema from pydantic import EmailStr from pydantic.errors import EmailError @@ -16,8 +16,8 @@ ) from app.database.models import Event, User, UserEvent from app.dependencies import templates +from app.internal.security.dependencies import current_user from app.internal.security.schema import ForgotPassword -from app.internal.utils import get_current_user mail = FastMail(email_conf) @@ -69,6 +69,7 @@ def send_email_to_event_participants( event_id: int, title: str, content: str, + user_logged: User = Depends(current_user), ) -> int: """This function sends emails to a mailing list of all event participants. it uses the function send above to do this and avoid double codes.. @@ -82,7 +83,7 @@ def send_email_to_event_participants( (number of valid emails in event's participants) """ event_owner = session.query(Event.owner).filter(id == event_id).first() - if event_owner != get_current_user(session): + if event_owner != user_logged: return 0 # makes sure only event owner can send an email via this func. mailing_list = ( diff --git a/app/routers/email.py b/app/routers/email.py index 3cf9072d..13b0bad5 100644 --- a/app/routers/email.py +++ b/app/routers/email.py @@ -8,7 +8,6 @@ from app.internal.email import send as internal_send from app.internal.email import send_email_invitation - router = APIRouter( prefix="/email", tags=["email"], @@ -18,17 +17,20 @@ @router.post("/send") async def send( - db: Session = Depends(get_db), - send_to: str = "/", - title: str = Form(...), - event_used: str = Form(...), - user_to_send: str = Form(...), - background_tasks: BackgroundTasks = BackgroundTasks + db: Session = Depends(get_db), + send_to: str = "/", + title: str = Form(...), + event_used: str = Form(...), + user_to_send: str = Form(...), + background_tasks: BackgroundTasks = BackgroundTasks, ) -> RedirectResponse: if not internal_send( - title=title, event_used=event_used, - user_to_send=user_to_send, - background_tasks=background_tasks, session=db): + title=title, + event_used=event_used, + user_to_send=user_to_send, + background_tasks=background_tasks, + session=db, + ): raise HTTPException(status_code=404, detail="Couldn't send the email!") return RedirectResponse(send_to, status_code=303) @@ -45,8 +47,10 @@ class InvitationParams(BaseModel): @router.post("/invitation/") -def send_invitation(invitation: InvitationParams, - background_task: BackgroundTasks): +def send_invitation( + invitation: InvitationParams, + background_task: BackgroundTasks, +): """ This function sends the recipient an invitation to his email address in the format HTML. @@ -61,12 +65,14 @@ def send_invitation(invitation: InvitationParams, except EmailError: raise HTTPException( status_code=422, - detail=INVALID_EMAIL_ADDRESS_ERROR_MESSAGE) + detail=INVALID_EMAIL_ADDRESS_ERROR_MESSAGE, + ) if not send_email_invitation( - sender_name=invitation.sender_name, - recipient_name=invitation.recipient_name, - recipient_mail=invitation.recipient_mail, - background_tasks=background_task): + sender_name=invitation.sender_name, + recipient_name=invitation.recipient_name, + recipient_mail=invitation.recipient_mail, + background_tasks=background_task, + ): raise HTTPException(status_code=422, detail="Couldn't send the email!") return RedirectResponse(invitation.send_to, status_code=303)