diff --git a/app/database/models.py b/app/database/models.py index 048fa53f..9263ae90 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -380,6 +380,16 @@ class Quote(Base): author = Column(String) +class OutOfOffice(Base): + __tablename__ = "out_of_office" + + id = Column(Integer, primary_key=True, index=True) + user_id = Column(Integer, ForeignKey("users.id")) + start_date = Column(DateTime, nullable=False) + end_date = Column(DateTime, nullable=False) + status = Column(String, nullable=False) + + class Comment(Base): __tablename__ = "comments" diff --git a/app/internal/event.py b/app/internal/event.py index 57b29d27..93739c13 100644 --- a/app/internal/event.py +++ b/app/internal/event.py @@ -1,6 +1,6 @@ import logging import re -from typing import List, Set +from typing import List, Set, Tuple from email_validator import EmailSyntaxError, validate_email from fastapi import HTTPException @@ -87,6 +87,7 @@ def get_messages( session: Session, event: Event, uninvited_contacts: Set[str], + out_of_office_users: List[Tuple[str, str]], ) -> List[str]: messages = [] if uninvited_contacts: @@ -100,4 +101,14 @@ def get_messages( f"Same event happened {weeks_diff} weeks before too. " f"Want to create another one {weeks_diff} after too?", ) + + if out_of_office_users: + username = 0 + email = 1 + messages.append("Out of office:") + for user in out_of_office_users: + messages.append( + f"Username: {user[username]}, " f"Email: {user[email]}", + ) + return messages diff --git a/app/internal/out_of_office.py b/app/internal/out_of_office.py new file mode 100644 index 00000000..caa5d858 --- /dev/null +++ b/app/internal/out_of_office.py @@ -0,0 +1,125 @@ +from datetime import datetime +from sqlalchemy.orm import Session +from typing import List + +from app.database.models import User, OutOfOffice + +DATETIME_FORMAT = "%Y-%m-%d %H:%M" +START_DATE = "start_date" +END_DATE = "end_date" +START_TIME = "start_time" +END_TIME = "end_time" + + +def get_who_is_out_of_office( + session: Session, + event_start_date: datetime, + invited_emails: List[str], +): + """ + Get who is out of office + + Args: + session: db session + event_start_date: event start date + invited_emails: invited emails + + Returns: + Users who are out of office at the event date + """ + out_of_office_users = ( + session.query(User.username, User.email) + .join(OutOfOffice) + .filter(User.email.in_(invited_emails)) + .filter( + OutOfOffice.start_date <= event_start_date, + OutOfOffice.end_date >= event_start_date, + ) + .filter(OutOfOffice.status == "On") + .all() + ) + return out_of_office_users + + +def insert_new_out_of_office(out_of_office_data, user: User, session: Session): + out = get_out_of_office_template( + user_id=user.id, + start_date=get_date_formatted( + out_of_office_data, + START_DATE, + START_TIME, + DATETIME_FORMAT, + ), + end_date=get_date_formatted( + out_of_office_data, + END_DATE, + END_TIME, + DATETIME_FORMAT, + ), + status="On", + ) + session.add(out) + + +def get_out_of_office_template( + user_id, + start_date=None, + end_date=None, + status="Off", +): + return OutOfOffice( + user_id=user_id, + start_date=start_date, + end_date=end_date, + status=status, + ) + + +def get_date_formatted(out_of_office_data, date, time, date_format): + return datetime.strptime( + out_of_office_data[date] + " " + out_of_office_data[time], + date_format, + ) + + +def update_out_of_office( + out_of_office_data_from_req, + out_of_office_data_from_db, +): + activate_out_of_office = "1" + + if out_of_office_data_from_req["outOfOffice"] == activate_out_of_office: + out_of_office_data_from_db.start_date = get_date_formatted( + out_of_office_data_from_req, + START_DATE, + START_TIME, + DATETIME_FORMAT, + ) + out_of_office_data_from_db.end_date = get_date_formatted( + out_of_office_data_from_req, + END_DATE, + END_TIME, + DATETIME_FORMAT, + ) + out_of_office_data_from_db.status = "On" + else: + out_of_office_data_from_db.status = "Off" + + +def update_out_of_office_status_to_off(out_of_office_data, session: Session): + """ + Update out of office status to off if out of office date passed + + Args: + out_of_office_data: Out of office data from db + session: db session + + Returns: + out_of_office_data object + """ + if out_of_office_data: + if out_of_office_data.status == "On": + if out_of_office_data.end_date < datetime.now(): + out_of_office_data.status = "Off" + session.commit() + return out_of_office_data diff --git a/app/routers/event.py b/app/routers/event.py index d87e206e..20268b34 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -24,6 +24,7 @@ ) from app.internal import comment as cmt from app.internal.emotion import get_emotion +from app.internal.out_of_office import get_who_is_out_of_office from app.internal.privacy import PrivacyKinds from app.internal.utils import create_model, get_current_user from app.routers.categories import get_user_categories @@ -31,6 +32,7 @@ EVENT_DATA = Tuple[Event, List[Dict[str, str]], str] TIME_FORMAT = "%Y-%m-%d %H:%M" START_FORMAT = "%A, %d/%m/%Y %H:%M" + UPDATE_EVENTS_FIELDS = { "title": str, "start": dt, @@ -44,7 +46,6 @@ "category_id": (int, type(None)), } - router = APIRouter( prefix="/event", tags=["event"], @@ -153,7 +154,17 @@ async def create_new_event( privacy=privacy, ) - messages = get_messages(session, event, uninvited_contacts) + out_of_office_users = get_who_is_out_of_office( + session, + start, + invited_emails, + ) + messages = get_messages( + session, + event, + uninvited_contacts, + out_of_office_users, + ) return RedirectResponse( router.url_path_for("eventview", event_id=event.id) + f'?messages={"---".join(messages)}', diff --git a/app/routers/profile.py b/app/routers/profile.py index e1473048..b3ecc617 100644 --- a/app/routers/profile.py +++ b/app/routers/profile.py @@ -8,11 +8,20 @@ from sqlalchemy.exc import SQLAlchemyError from app import config -from app.database.models import User +from app.database.models import User, OutOfOffice + from app.dependencies import get_db, MEDIA_PATH, templates, GOOGLE_ERROR +from app.internal.security.dependancies import current_user, schema +from app.internal.import_holidays import ( + get_holidays_from_file, + save_holidays_to_db, +) from app.internal.on_this_day_events import get_on_this_day_events -from app.internal.import_holidays import (get_holidays_from_file, - save_holidays_to_db) +from app.internal.out_of_office import ( + insert_new_out_of_office, + update_out_of_office, + update_out_of_office_status_to_off, +) from app.internal.privacy import PrivacyKinds PICTURE_EXTENSION = config.PICTURE_EXTENSION @@ -27,20 +36,21 @@ def get_placeholder_user(): return User( - username='new_user', - email='my@email.po', - password='1a2s3d4f5g6', - full_name='My Name', + username="new_user", + email="my@email.po", + password="1a2s3d4f5g6", + full_name="My Name", language_id=1, - telegram_id='', + telegram_id="", ) @router.get("/") async def profile( - request: Request, - session=Depends(get_db), - new_user=Depends(get_placeholder_user)): + request: Request, + session=Depends(get_db), + new_user=Depends(get_placeholder_user), +): # Get relevant data from database upcoming_events = range(5) user = session.query(User).filter_by(id=1).first() @@ -49,28 +59,50 @@ async def profile( session.commit() user = session.query(User).filter_by(id=1).first() - signs = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', - 'Virgo', 'Libra', 'Scorpio', 'Sagittarius', - 'Capricorn', 'Aquarius', 'Pisces'] + out_of_office_data = ( + session.query(OutOfOffice).filter_by(id=user.id).first() + ) + out_of_office_updated_data = update_out_of_office_status_to_off( + out_of_office_data, + session, + ) + + signs = [ + "Aries", + "Taurus", + "Gemini", + "Cancer", + "Leo", + "Virgo", + "Libra", + "Scorpio", + "Sagittarius", + "Capricorn", + "Aquarius", + "Pisces", + ] on_this_day_data = get_on_this_day_events(session) - return templates.TemplateResponse("profile.html", { - "request": request, - "user": user, - "events": upcoming_events, - "signs": signs, - "google_error": GOOGLE_ERROR, - "on_this_day_data": on_this_day_data, - "privacy": PrivacyKinds - }) + return templates.TemplateResponse( + "profile.html", + { + "request": request, + "user": user, + "events": upcoming_events, + "signs": signs, + "google_error": GOOGLE_ERROR, + "on_this_day_data": on_this_day_data, + "out_of_office_data": out_of_office_updated_data, + "privacy": PrivacyKinds, + }, + ) @router.post("/update_user_fullname") -async def update_user_fullname( - request: Request, session=Depends(get_db)): +async def update_user_fullname(request: Request, session=Depends(get_db)): user = session.query(User).filter_by(id=1).first() data = await request.form() - new_fullname = data['fullname'] + new_fullname = data["fullname"] # Update database user.full_name = new_fullname @@ -81,11 +113,10 @@ async def update_user_fullname( @router.post("/update_user_email") -async def update_user_email( - request: Request, session=Depends(get_db)): +async def update_user_email(request: Request, session=Depends(get_db)): user = session.query(User).filter_by(id=1).first() data = await request.form() - new_email = data['email'] + new_email = data["email"] # Update database user.email = new_email @@ -96,11 +127,10 @@ async def update_user_email( @router.post("/update_user_description") -async def update_profile( - request: Request, session=Depends(get_db)): +async def update_profile(request: Request, session=Depends(get_db)): user = session.query(User).filter_by(id=1).first() data = await request.form() - new_description = data['description'] + new_description = data["description"] # Update database user.description = new_description @@ -112,7 +142,9 @@ async def update_profile( @router.post("/upload_user_photo") async def upload_user_photo( - file: UploadFile = File(...), session=Depends(get_db)): + file: UploadFile = File(...), + session=Depends(get_db), +): user = session.query(User).filter_by(id=1).first() pic = await file.read() @@ -127,11 +159,10 @@ async def upload_user_photo( @router.post("/update_telegram_id") -async def update_telegram_id( - request: Request, session=Depends(get_db)): +async def update_telegram_id(request: Request, session=Depends(get_db)): user = session.query(User).filter_by(id=1).first() data = await request.form() - new_telegram_id = data['telegram_id'] + new_telegram_id = data["telegram_id"] # Update database user.telegram_id = new_telegram_id @@ -142,13 +173,10 @@ async def update_telegram_id( @router.post("/privacy") -async def update_calendar_privacy( - request: Request, - session=Depends(get_db) -): +async def update_calendar_privacy(request: Request, session=Depends(get_db)): user = session.query(User).filter_by(id=1).first() data = await request.form() - new_privacy = data['privacy'] + new_privacy = data["privacy"] # Update database user.privacy = new_privacy @@ -160,9 +188,12 @@ async def update_calendar_privacy( @router.get("/holidays/import") def import_holidays(request: Request): - return templates.TemplateResponse("import_holidays.html", { - "request": request, - }) + return templates.TemplateResponse( + "import_holidays.html", + { + "request": request, + }, + ) async def process_image(image, user): @@ -184,9 +215,50 @@ def get_image_crop_area(width, height): return 0, delta, width, width + delta +@router.post("/out_of_office") +async def out_of_office( + request: Request, + session=Depends(get_db), + user: schema.CurrentUser = Depends(current_user), +): + activate_out_of_office = "1" + user_db = session.query(User).filter_by(id=user.user_id).first() + + # TODO: Check if the user exist + + out_of_office_data_from_req = await request.form() + + out_of_office_data_from_db = ( + session.query(OutOfOffice).filter_by(id=user_db.id).first() + ) + + # insert new out of office + if not out_of_office_data_from_db: + if ( + out_of_office_data_from_req["outOfOffice"] + == activate_out_of_office + ): + insert_new_out_of_office( + out_of_office_data_from_req, + user_db, + session, + ) + + # update out of office + else: + update_out_of_office( + out_of_office_data_from_req, + out_of_office_data_from_db, + ) + + session.commit() + + url = router.url_path_for("profile") + return RedirectResponse(url=url, status_code=HTTP_302_FOUND) + + @router.post("/holidays/update") -async def update( - file: UploadFile = File(...), session=Depends(get_db)): +async def update(file: UploadFile = File(...), session=Depends(get_db)): icsfile = await file.read() holidays = get_holidays_from_file(icsfile.decode(), session) try: diff --git a/app/static/out_of_office/__init__.py b/app/static/out_of_office/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/static/out_of_office/out_of_office.js b/app/static/out_of_office/out_of_office.js new file mode 100644 index 00000000..481824b0 --- /dev/null +++ b/app/static/out_of_office/out_of_office.js @@ -0,0 +1,16 @@ +function findselected() { + let deactivate = 0; + let result = document.querySelector('input[name="outOfOffice"]:checked').value; + if (result == deactivate) { + document.getElementById("start_date").setAttribute('disabled', true); + document.getElementById("end_date").setAttribute('disabled', true); + document.getElementById("start_time").setAttribute('disabled', true); + document.getElementById("end_time").setAttribute('disabled', true); + } + else { + document.getElementById("start_date").removeAttribute('disabled'); + document.getElementById("end_date").removeAttribute('disabled'); + document.getElementById("start_time").removeAttribute('disabled'); + document.getElementById("end_time").removeAttribute('disabled'); + } +} \ No newline at end of file diff --git a/app/templates/partials/user_profile/sidebar_left/features_card.html b/app/templates/partials/user_profile/sidebar_left/features_card.html index 946d1040..9eccfea5 100644 --- a/app/templates/partials/user_profile/sidebar_left/features_card.html +++ b/app/templates/partials/user_profile/sidebar_left/features_card.html @@ -8,11 +8,11 @@