Compare commits
No commits in common. "e718ae905d486afeee1ad9313ad61ba8d106e101" and "22c080572e173fdd525ad0a25c288f4e2c575b78" have entirely different histories.
e718ae905d
...
22c080572e
7 changed files with 53 additions and 131 deletions
|
@ -52,7 +52,7 @@ update, delete, and be reminded of events with additional advanced features.
|
||||||
- User Event Association (Many to Many)
|
- User Event Association (Many to Many)
|
||||||
- User_id (string, required, foreign key)
|
- User_id (string, required, foreign key)
|
||||||
- Event_id (Integer, required, foreign key)
|
- Event_id (Integer, required, foreign key)
|
||||||
- Notifiyed Made (bool, default: false)
|
- Notification Made (bool, required)
|
||||||
|
|
||||||
|
|
||||||
# API EndPoints
|
# API EndPoints
|
||||||
|
|
19
app.py
19
app.py
|
@ -9,8 +9,6 @@ from middlewares.errorHandlers import handle_auth_error, handle_invalid_token
|
||||||
from flask_jwt_extended.exceptions import NoAuthorizationError
|
from flask_jwt_extended.exceptions import NoAuthorizationError
|
||||||
from jwt.exceptions import InvalidTokenError
|
from jwt.exceptions import InvalidTokenError
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from flask_apscheduler import APScheduler
|
|
||||||
from services.EventNotifyerService import EventNotifyerServer
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -20,15 +18,6 @@ class App:
|
||||||
self.set_up_jwt()
|
self.set_up_jwt()
|
||||||
self.register_blueprints()
|
self.register_blueprints()
|
||||||
self.setup_error_handlers()
|
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):
|
def set_config(self):
|
||||||
self.app.config.from_object(config.Config)
|
self.app.config.from_object(config.Config)
|
||||||
|
@ -55,7 +44,6 @@ class App:
|
||||||
def run(self):
|
def run(self):
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
self.setup_scheduler() # Setup scheduler
|
|
||||||
self.app.run(debug=True)
|
self.app.run(debug=True)
|
||||||
|
|
||||||
def print_endpoints(self):
|
def print_endpoints(self):
|
||||||
|
@ -66,13 +54,6 @@ class App:
|
||||||
print(f" Function: {function_name}")
|
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_class_instance = App()
|
||||||
app_instance = app_class_instance.app
|
app_instance = app_class_instance.app
|
||||||
|
|
||||||
|
|
7
infra/docker-compose.yml
Normal file
7
infra/docker-compose.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
|
@ -1,44 +0,0 @@
|
||||||
"""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,27 +1,28 @@
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
from sqlalchemy import Column, Integer, ForeignKey, String, Boolean
|
from sqlalchemy import Table, Column, Integer, ForeignKey, String
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
bcrypt = Bcrypt()
|
bcrypt = Bcrypt()
|
||||||
|
|
||||||
class UserEventAssociation(db.Model):
|
|
||||||
__tablename__ = 'user_event'
|
user_event_association = Table('user_event', db.Model.metadata,
|
||||||
user_id = Column(String(36), ForeignKey('user.id'), primary_key=True)
|
Column('user_id', String(36), ForeignKey('user.id')),
|
||||||
event_id = Column(Integer, ForeignKey('event.id'), primary_key=True)
|
Column('event_id', Integer, ForeignKey('event.id'))
|
||||||
notified = Column(Boolean, default=False)
|
)
|
||||||
|
|
||||||
class Event(db.Model):
|
class Event(db.Model):
|
||||||
id = Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
title = Column(db.String(100), nullable=False)
|
title = db.Column(db.String(100), nullable=False)
|
||||||
description = Column(db.String(200), nullable=False)
|
description = db.Column(db.String(200), nullable=False)
|
||||||
location = Column(db.String(100), nullable=False)
|
location = db.Column(db.String(100), nullable=False)
|
||||||
deleted = Column(Boolean, default=False)
|
deleted = db.Column(db.Boolean, default=False)
|
||||||
duedate = Column(db.DateTime, nullable=False)
|
duedate = db.Column(db.DateTime, nullable=False)
|
||||||
created_at = Column(db.DateTime, default=db.func.now())
|
created_at = db.Column(db.DateTime, default=db.func.now())
|
||||||
user_id = Column(String(36), db.ForeignKey('user.id'), nullable=False)
|
user_id = db.Column(db.String(36), db.ForeignKey('user.id'), nullable=False)
|
||||||
users = db.relationship('User', secondary='user_event', back_populates='events')
|
users = db.relationship('User', secondary=user_event_association, back_populates='events')
|
||||||
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
|
@ -35,12 +36,14 @@ class Event(db.Model):
|
||||||
}
|
}
|
||||||
|
|
||||||
class User(db.Model):
|
class User(db.Model):
|
||||||
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||||
name = Column(db.String(100), nullable=False)
|
name = db.Column(db.String(100), nullable=False)
|
||||||
email = Column(db.String(120), unique=True, nullable=False)
|
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||||
password_hash = Column(db.String(128), nullable=False)
|
password_hash = db.Column(db.String(128), nullable=False)
|
||||||
location = Column(db.String(100), nullable=False)
|
location = db.Column(db.String(100), nullable=False)
|
||||||
events = db.relationship('Event', secondary='user_event', back_populates='users')
|
events = db.relationship('Event', secondary=user_event_association, back_populates='users')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
self.password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||||
|
@ -55,3 +58,4 @@ class User(db.Model):
|
||||||
'email': self.email,
|
'email': self.email,
|
||||||
'location': self.location
|
'location': self.location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# 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,10 +1,9 @@
|
||||||
from sqlalchemy.sql import exists
|
from sqlalchemy.sql import exists
|
||||||
from models import db, Event, UserEventAssociation, User
|
from models import db, Event, user_event_association
|
||||||
from services.UserService import UserService
|
from services.UserService import UserService
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask import g
|
from flask import g
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
class EventService:
|
class EventService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -32,9 +31,7 @@ class EventService:
|
||||||
if sort_by == 'date':
|
if sort_by == 'date':
|
||||||
query = query.order_by(Event.duedate)
|
query = query.order_by(Event.duedate)
|
||||||
elif sort_by == 'popularity':
|
elif sort_by == 'popularity':
|
||||||
query = query.join(UserEventAssociation, Event.id == UserEventAssociation.event_id)\
|
query = query.outerjoin(user_event_association).group_by(Event.id).order_by(func.count(user_event_association.c.user_id).desc())
|
||||||
.group_by(Event.id)\
|
|
||||||
.order_by(func.count(UserEventAssociation.user_id).desc())
|
|
||||||
elif sort_by == 'creation':
|
elif sort_by == 'creation':
|
||||||
query = query.order_by(Event.created_at)
|
query = query.order_by(Event.created_at)
|
||||||
|
|
||||||
|
@ -85,26 +82,25 @@ class EventService:
|
||||||
return {'error': 'Event not found or already passed'}
|
return {'error': 'Event not found or already passed'}
|
||||||
|
|
||||||
# Check if the user is already associated with the event
|
# Check if the user is already associated with the event
|
||||||
is_already_attending = UserEventAssociation.query.filter_by(
|
is_already_attending = db.session.query(exists().where(
|
||||||
user_id=user_id,
|
user_event_association.c.user_id == user_id,
|
||||||
event_id=event_id
|
user_event_association.c.event_id == event_id
|
||||||
).first()
|
)).scalar()
|
||||||
|
|
||||||
if is_already_attending:
|
if is_already_attending:
|
||||||
return {'error': 'User already attending this event'}
|
return {'error': 'User already attending this event'}
|
||||||
|
|
||||||
# Add the user to the event
|
# Add the user to the event
|
||||||
user_event_association = UserEventAssociation(
|
user = UserService.get_user_by_id(user_id)
|
||||||
user_id=user_id,
|
if not user:
|
||||||
event_id=event_id
|
return {'error': 'User not found'}
|
||||||
)
|
|
||||||
db.session.add(user_event_association)
|
event.users.append(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return {'message': 'User successfully added to the event'}
|
return {'message': 'User successfully added to the event'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unattend_event(event_id):
|
def unattend_event(event_id):
|
||||||
user_id = g.user_id # Assuming user_id is stored in Flask's global g object
|
user_id = g.user_id # Assuming user_id is stored in Flask's global g object
|
||||||
|
@ -118,33 +114,21 @@ class EventService:
|
||||||
if not event:
|
if not event:
|
||||||
return {'error': 'Event not found or already passed'}
|
return {'error': 'Event not found or already passed'}
|
||||||
|
|
||||||
user_event_association = UserEventAssociation.query.filter_by(
|
# Check if the user is already associated with the event
|
||||||
user_id=user_id,
|
is_already_attending = db.session.query(exists().where(
|
||||||
event_id=event_id
|
user_event_association.c.user_id == user_id,
|
||||||
).first()
|
user_event_association.c.event_id == event_id
|
||||||
|
)).scalar()
|
||||||
|
|
||||||
|
if not is_already_attending:
|
||||||
if not user_event_association:
|
|
||||||
return {'error': 'User not attending this event'}
|
return {'error': 'User not attending this event'}
|
||||||
|
|
||||||
|
# Remove the user from the event
|
||||||
user = UserService.get_user_by_id(user_id)
|
user = UserService.get_user_by_id(user_id)
|
||||||
if not user:
|
if not user:
|
||||||
return {'error': 'User not found'}
|
return {'error': 'User not found'}
|
||||||
|
|
||||||
|
event.users.remove(user)
|
||||||
db.session.delete(user_event_association)
|
|
||||||
db.session.commit()
|
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