Compare commits
3 commits
22c080572e
...
e718ae905d
Author | SHA1 | Date | |
---|---|---|---|
e718ae905d | |||
a63b31da04 | |||
7778626bcb |
7 changed files with 131 additions and 53 deletions
|
@ -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
|
||||
|
|
19
app.py
19
app.py
|
@ -9,6 +9,8 @@ from middlewares.errorHandlers import handle_auth_error, handle_invalid_token
|
|||
from flask_jwt_extended.exceptions import NoAuthorizationError
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
from datetime import timedelta
|
||||
from flask_apscheduler import APScheduler
|
||||
from services.EventNotifyerService import EventNotifyerServer
|
||||
|
||||
class App:
|
||||
def __init__(self):
|
||||
|
@ -18,6 +20,15 @@ class App:
|
|||
self.set_up_jwt()
|
||||
self.register_blueprints()
|
||||
self.setup_error_handlers()
|
||||
self.scheduler = APScheduler()
|
||||
|
||||
def setup_scheduler(self):
|
||||
self.app.config.from_object(self.SchadulerConfig())
|
||||
self.scheduler.init_app(self.app)
|
||||
self.scheduler.start()
|
||||
# Schedule the job
|
||||
self.scheduler.add_job(id='locate_upcoming_events', func=self.locate_upcoming_events, trigger='interval', seconds=1)
|
||||
|
||||
|
||||
def set_config(self):
|
||||
self.app.config.from_object(config.Config)
|
||||
|
@ -44,6 +55,7 @@ class App:
|
|||
def run(self):
|
||||
with self.app.app_context():
|
||||
db.create_all()
|
||||
self.setup_scheduler() # Setup scheduler
|
||||
self.app.run(debug=True)
|
||||
|
||||
def print_endpoints(self):
|
||||
|
@ -54,6 +66,13 @@ class App:
|
|||
print(f" Function: {function_name}")
|
||||
|
||||
|
||||
def SchadulerConfig(object):
|
||||
SCHEDULER_API_ENABLED = True
|
||||
|
||||
def locate_upcoming_events(self):
|
||||
with self.app.app_context():
|
||||
print(EventNotifyerServer.locate_upcoming_events())
|
||||
|
||||
app_class_instance = App()
|
||||
app_instance = app_class_instance.app
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis:latest
|
||||
ports:
|
||||
- "6379:6379"
|
44
migrations/versions/04e41f293ec2_adda_col_notifyed_bool.py
Normal file
44
migrations/versions/04e41f293ec2_adda_col_notifyed_bool.py
Normal file
|
@ -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 ###
|
46
models.py
46
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
|
||||
}
|
||||
|
||||
|
|
10
services/EventNotifyerService.py
Normal file
10
services/EventNotifyerService.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
# this Class is for the scheduler
|
||||
#this class will have a function that locates the upcomming events Using the Event service.
|
||||
from services.EventService import EventService
|
||||
|
||||
class EventNotifyerServer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def locate_upcoming_events():
|
||||
return EventService.get_all_upcomming_events()
|
|
@ -1,9 +1,10 @@
|
|||
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
|
||||
from sqlalchemy import func
|
||||
from datetime import timedelta
|
||||
|
||||
class EventService:
|
||||
@staticmethod
|
||||
|
@ -31,7 +32,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 +85,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 +118,33 @@ 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'}
|
||||
return {'message': 'User successfully removed from the event'}
|
||||
|
||||
@staticmethod
|
||||
def get_all_upcomming_events():
|
||||
now = datetime.now()
|
||||
upcoming_deadline = now + timedelta(minutes=30)
|
||||
events = Event.query.filter(
|
||||
|
||||
Event.duedate <= upcoming_deadline,
|
||||
Event.deleted == False
|
||||
).all()
|
||||
|
||||
return [event.to_dict() for event in events]
|
Loading…
Reference in a new issue