diff --git a/README.md b/README.md index 8549d0d..1e31a26 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ update, delete, and be reminded of events with additional advanced features. - User Event Association (Many to Many) - User_id (string, required, foreign key) - Event_id (Integer, required, foreign key) - - Notification Made (bool, required) + - Notifiyed Made (bool, default: false) # API EndPoints diff --git a/migrations/versions/04e41f293ec2_adda_col_notifyed_bool.py b/migrations/versions/04e41f293ec2_adda_col_notifyed_bool.py new file mode 100644 index 0000000..2ece980 --- /dev/null +++ b/migrations/versions/04e41f293ec2_adda_col_notifyed_bool.py @@ -0,0 +1,44 @@ +"""AddA_col_Notifyed_Bool + +Revision ID: 04e41f293ec2 +Revises: 902a6851ae27 +Create Date: 2024-01-08 21:11:13.952001 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '04e41f293ec2' +down_revision = '902a6851ae27' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('user_event', schema=None) as batch_op: + batch_op.add_column(sa.Column('notified', sa.Boolean(), nullable=True)) + batch_op.alter_column('user_id', + existing_type=sa.VARCHAR(length=36), + nullable=False) + batch_op.alter_column('event_id', + existing_type=sa.INTEGER(), + nullable=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('user_event', schema=None) as batch_op: + batch_op.alter_column('event_id', + existing_type=sa.INTEGER(), + nullable=True) + batch_op.alter_column('user_id', + existing_type=sa.VARCHAR(length=36), + nullable=True) + batch_op.drop_column('notified') + + # ### end Alembic commands ### diff --git a/models.py b/models.py index 52849f1..855a8bf 100644 --- a/models.py +++ b/models.py @@ -1,28 +1,27 @@ from flask_sqlalchemy import SQLAlchemy from flask_bcrypt import Bcrypt -from sqlalchemy import Table, Column, Integer, ForeignKey, String +from sqlalchemy import Column, Integer, ForeignKey, String, Boolean import uuid db = SQLAlchemy() bcrypt = Bcrypt() - -user_event_association = Table('user_event', db.Model.metadata, - Column('user_id', String(36), ForeignKey('user.id')), - Column('event_id', Integer, ForeignKey('event.id')) -) +class UserEventAssociation(db.Model): + __tablename__ = 'user_event' + user_id = Column(String(36), ForeignKey('user.id'), primary_key=True) + event_id = Column(Integer, ForeignKey('event.id'), primary_key=True) + notified = Column(Boolean, default=False) class Event(db.Model): - id = db.Column(db.Integer, primary_key=True) - title = db.Column(db.String(100), nullable=False) - description = db.Column(db.String(200), nullable=False) - location = db.Column(db.String(100), nullable=False) - deleted = db.Column(db.Boolean, default=False) - duedate = db.Column(db.DateTime, nullable=False) - created_at = db.Column(db.DateTime, default=db.func.now()) - user_id = db.Column(db.String(36), db.ForeignKey('user.id'), nullable=False) - users = db.relationship('User', secondary=user_event_association, back_populates='events') - + id = Column(db.Integer, primary_key=True) + title = Column(db.String(100), nullable=False) + description = Column(db.String(200), nullable=False) + location = Column(db.String(100), nullable=False) + deleted = Column(Boolean, default=False) + duedate = Column(db.DateTime, nullable=False) + created_at = Column(db.DateTime, default=db.func.now()) + user_id = Column(String(36), db.ForeignKey('user.id'), nullable=False) + users = db.relationship('User', secondary='user_event', back_populates='events') def to_dict(self): return { @@ -36,14 +35,12 @@ class Event(db.Model): } class User(db.Model): - id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4())) - name = db.Column(db.String(100), nullable=False) - email = db.Column(db.String(120), unique=True, nullable=False) - password_hash = db.Column(db.String(128), nullable=False) - location = db.Column(db.String(100), nullable=False) - events = db.relationship('Event', secondary=user_event_association, back_populates='users') - - + id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) + name = Column(db.String(100), nullable=False) + email = Column(db.String(120), unique=True, nullable=False) + password_hash = Column(db.String(128), nullable=False) + location = Column(db.String(100), nullable=False) + events = db.relationship('Event', secondary='user_event', back_populates='users') def set_password(self, password): self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8') @@ -58,4 +55,3 @@ class User(db.Model): 'email': self.email, 'location': self.location } - diff --git a/services/EventService.py b/services/EventService.py index ccc1352..c5408ff 100644 --- a/services/EventService.py +++ b/services/EventService.py @@ -1,5 +1,5 @@ from sqlalchemy.sql import exists -from models import db, Event, user_event_association +from models import db, Event, UserEventAssociation, User from services.UserService import UserService from datetime import datetime from flask import g @@ -31,7 +31,9 @@ class EventService: if sort_by == 'date': query = query.order_by(Event.duedate) elif sort_by == 'popularity': - query = query.outerjoin(user_event_association).group_by(Event.id).order_by(func.count(user_event_association.c.user_id).desc()) + query = query.join(UserEventAssociation, Event.id == UserEventAssociation.event_id)\ + .group_by(Event.id)\ + .order_by(func.count(UserEventAssociation.user_id).desc()) elif sort_by == 'creation': query = query.order_by(Event.created_at) @@ -82,25 +84,26 @@ class EventService: return {'error': 'Event not found or already passed'} # Check if the user is already associated with the event - is_already_attending = db.session.query(exists().where( - user_event_association.c.user_id == user_id, - user_event_association.c.event_id == event_id - )).scalar() + is_already_attending = UserEventAssociation.query.filter_by( + user_id=user_id, + event_id=event_id + ).first() if is_already_attending: return {'error': 'User already attending this event'} # Add the user to the event - user = UserService.get_user_by_id(user_id) - if not user: - return {'error': 'User not found'} - - event.users.append(user) + user_event_association = UserEventAssociation( + user_id=user_id, + event_id=event_id + ) + db.session.add(user_event_association) db.session.commit() return {'message': 'User successfully added to the event'} + @staticmethod def unattend_event(event_id): user_id = g.user_id # Assuming user_id is stored in Flask's global g object @@ -114,21 +117,21 @@ class EventService: if not event: return {'error': 'Event not found or already passed'} - # Check if the user is already associated with the event - is_already_attending = db.session.query(exists().where( - user_event_association.c.user_id == user_id, - user_event_association.c.event_id == event_id - )).scalar() + user_event_association = UserEventAssociation.query.filter_by( + user_id=user_id, + event_id=event_id + ).first() + - if not is_already_attending: + if not user_event_association: return {'error': 'User not attending this event'} - # Remove the user from the event user = UserService.get_user_by_id(user_id) if not user: return {'error': 'User not found'} - event.users.remove(user) + + db.session.delete(user_event_association) db.session.commit() return {'message': 'User successfully removed from the event'} \ No newline at end of file