From f4cdce4ac107904f710583f307467faa4f6f2b16 Mon Sep 17 00:00:00 2001 From: Lutfian Rahdiansyah <55054691+lutfianRhdn@users.noreply.github.com> Date: Fri, 25 Aug 2023 20:07:29 +0700 Subject: [PATCH 1/4] Update TicketService.py --- src/services/TicketService.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/TicketService.py b/src/services/TicketService.py index 6814c59..6f78510 100644 --- a/src/services/TicketService.py +++ b/src/services/TicketService.py @@ -122,7 +122,7 @@ def attendTicket(self,data,user_id): ticket = ticketRepository.getTicketByCode(data['ticket_code']) if(not ticket): return self.failedOrSuccessRequest('failed', 400, 'ticket not found') - if(ticket.user_id != user_id): + if(ticket.event.user_id != user_id): return self.failedOrSuccessRequest('failed', 400, 'ticket not belong to you') if(ticket.is_attended): return self.failedOrSuccessRequest('failed', 400, 'ticket already attended') @@ -132,4 +132,4 @@ def attendTicket(self,data,user_id): except ValueError as e: return self.failedOrSuccessRequest('failed', 400, errorHandler(e.errors())) except Exception as e: - return self.failedOrSuccessRequest('failed', 500, str(e)) \ No newline at end of file + return self.failedOrSuccessRequest('failed', 500, str(e)) From d2481b58431b5bcc1bfef3b462f538898d8d50ef Mon Sep 17 00:00:00 2001 From: Lutfian Rhdn Date: Sat, 26 Aug 2023 00:22:42 +0700 Subject: [PATCH 2/4] bugfix: eo reigster email:bug: --- src/services/AuthService.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/AuthService.py b/src/services/AuthService.py index 8e89f15..80926c4 100644 --- a/src/services/AuthService.py +++ b/src/services/AuthService.py @@ -25,7 +25,7 @@ def _sendNotification(self,data): sendMail( templates=templates, subject="Ticket Event", - to=data.user.email + to=data.email ) return True From 76922cf942a04bca8138e971a038b6db94bc9aa5 Mon Sep 17 00:00:00 2001 From: Lutfian Rhdn Date: Sat, 26 Aug 2023 01:26:33 +0700 Subject: [PATCH 3/4] refactor:change eo registation notification to admin :recycle: --- src/repositories/UserRepository.py | 3 ++- src/services/AuthService.py | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/repositories/UserRepository.py b/src/repositories/UserRepository.py index b3b1a4d..63d9be9 100644 --- a/src/repositories/UserRepository.py +++ b/src/repositories/UserRepository.py @@ -4,7 +4,8 @@ class UserRepository: def getAllUser(self): return User.query.all() - + def getUserByRole(self,role): + return User.query.filter_by(role=role).all() def getUserByEmail(self,email): return User.query.filter_by(email=email).first() def createNewUser(self,data): diff --git a/src/services/AuthService.py b/src/services/AuthService.py index 80926c4..e2d2fba 100644 --- a/src/services/AuthService.py +++ b/src/services/AuthService.py @@ -16,7 +16,7 @@ def failedOrSuccessRequest(status, code, data): "code": code, 'data': data, } - def _sendNotification(self,data): + def _sendNotification(self,data,to): templates = render_template( 'html/registeredEoNotification.html', name=data.name, @@ -25,7 +25,7 @@ def _sendNotification(self,data): sendMail( templates=templates, subject="Ticket Event", - to=data.email + to=to ) return True @@ -40,7 +40,8 @@ def registerUser(self, data): return self.failedOrSuccessRequest('failed', 400, 'Validation failed') newUser = user_repository.createNewUser(data) if(newUser.role == 'EVENT_ORGANIZER'): - self._sendNotification(newUser) + userAdmin = user_repository.getUserByRole('ADMIN') + self._sendNotification(newUser,userAdmin[0].email) return self.failedOrSuccessRequest('success', 201, queryResultToDict([newUser])[0]) except ValueError as e: From dac4043e2edda76919dae55eb6f76ae425e325ef Mon Sep 17 00:00:00 2001 From: Lutfian Rhdn Date: Sat, 26 Aug 2023 18:57:04 +0700 Subject: [PATCH 4/4] bugfix:fix relation and other :bug: --- ...e7_alter_table_add_status_change_at_on_.py | 32 ++++++++++++++ ...61_alter_table_add_ondelte_cascade_for_.py | 42 +++++++++++++++++++ src/config/permission.py | 2 +- src/models/Event.py | 1 + src/models/Ticket.py | 4 +- src/models/Transaction.py | 4 +- src/repositories/CategoryRepository.py | 2 + src/repositories/EventRepository.py | 1 + src/repositories/TicketRepository.py | 9 ++-- src/services/CategoryService.py | 4 ++ src/services/EventService.py | 6 +++ 11 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 migrations/versions/199b40c274e7_alter_table_add_status_change_at_on_.py create mode 100644 migrations/versions/21069897ab61_alter_table_add_ondelte_cascade_for_.py diff --git a/migrations/versions/199b40c274e7_alter_table_add_status_change_at_on_.py b/migrations/versions/199b40c274e7_alter_table_add_status_change_at_on_.py new file mode 100644 index 0000000..5bfa0ed --- /dev/null +++ b/migrations/versions/199b40c274e7_alter_table_add_status_change_at_on_.py @@ -0,0 +1,32 @@ +"""alter_table_add_status_change_at_on_event_table + +Revision ID: 199b40c274e7 +Revises: f391490d3fa9 +Create Date: 2023-08-26 18:08:17.283643 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '199b40c274e7' +down_revision = 'f391490d3fa9' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('events', schema=None) as batch_op: + batch_op.add_column(sa.Column('status_change_at', sa.DateTime(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('events', schema=None) as batch_op: + batch_op.drop_column('status_change_at') + + # ### end Alembic commands ### diff --git a/migrations/versions/21069897ab61_alter_table_add_ondelte_cascade_for_.py b/migrations/versions/21069897ab61_alter_table_add_ondelte_cascade_for_.py new file mode 100644 index 0000000..003ffe1 --- /dev/null +++ b/migrations/versions/21069897ab61_alter_table_add_ondelte_cascade_for_.py @@ -0,0 +1,42 @@ +"""alter_table_add_ondelte_cascade_for_event_ticket_table + +Revision ID: 21069897ab61 +Revises: 199b40c274e7 +Create Date: 2023-08-26 18:39:02.825930 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '21069897ab61' +down_revision = '199b40c274e7' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('tickets', schema=None) as batch_op: + batch_op.drop_constraint('tickets_ibfk_1', type_='foreignkey') + batch_op.create_foreign_key(None, 'events', ['event_id'], ['event_id'], ondelete='CASCADE') + + with op.batch_alter_table('transactions', schema=None) as batch_op: + batch_op.drop_constraint('transactions_ibfk_1', type_='foreignkey') + batch_op.create_foreign_key(None, 'tickets', ['ticket_id'], ['ticket_id'], ondelete='CASCADE') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('transactions', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.create_foreign_key('transactions_ibfk_1', 'tickets', ['ticket_id'], ['ticket_id']) + + with op.batch_alter_table('tickets', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.create_foreign_key('tickets_ibfk_1', 'events', ['event_id'], ['event_id']) + + # ### end Alembic commands ### diff --git a/src/config/permission.py b/src/config/permission.py index 2aff313..0f1f997 100644 --- a/src/config/permission.py +++ b/src/config/permission.py @@ -8,7 +8,6 @@ '/users/topup', '/users/withdraw', '/transactions/my', - '/tickets/my' ] admin_permission=[ @@ -20,6 +19,7 @@ ] user_permission=[ + '/tickets/my' ] diff --git a/src/models/Event.py b/src/models/Event.py index 9f12086..1fabba5 100644 --- a/src/models/Event.py +++ b/src/models/Event.py @@ -12,6 +12,7 @@ class Event(db.Model): description= db.Column(db.Text) poster_path = db.Column(db.Text) address= db.Column(db.Text) + status_change_at = db.Column(db.DateTime, nullable=True) user_id = db.Column(db.Integer, db.ForeignKey('users.user_id')) category_id = db.Column(db.Integer, db.ForeignKey('categories.category_id')) category = db.relationship('Category', backref='events') diff --git a/src/models/Ticket.py b/src/models/Ticket.py index 74b2f02..8b45aeb 100644 --- a/src/models/Ticket.py +++ b/src/models/Ticket.py @@ -4,12 +4,12 @@ class Ticket(db.Model): __tablename__ = 'tickets' ticket_id = db.Column(db.Integer, primary_key=True) - event_id = db.Column(db.Integer, db.ForeignKey('events.event_id')) + event_id = db.Column(db.Integer, db.ForeignKey('events.event_id',ondelete='CASCADE')) user_id = db.Column(db.Integer, db.ForeignKey('users.user_id')) ticket_code = db.Column(db.String(6)) is_attended = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, default=db.func.current_timestamp()) - event = db.relationship("Event", backref="tickets") + event = db.relationship("Event", backref="tickets", cascade="all, delete") user = db.relationship("User", back_populates="tickets") transactions = db.relationship("Transaction", back_populates="ticket") def __init__(self, event_id, user_id, ticket_code): diff --git a/src/models/Transaction.py b/src/models/Transaction.py index 4f63ab9..dbd8cfb 100644 --- a/src/models/Transaction.py +++ b/src/models/Transaction.py @@ -7,10 +7,10 @@ class Transaction(db.Model): type = db.Column(db.String(10), nullable=False) nominal = db.Column(db.Integer, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.user_id')) - ticket_id = db.Column(db.Integer, db.ForeignKey('tickets.ticket_id')) + ticket_id = db.Column(db.Integer, db.ForeignKey('tickets.ticket_id',ondelete='CASCADE')) created_at = db.Column(db.DateTime, default=db.func.current_timestamp()) user = db.relationship("User", back_populates="transactions") - ticket = db.relationship("Ticket", back_populates="transactions") + ticket = db.relationship("Ticket", back_populates="transactions", cascade="all, delete") def __init__(self, type, user_id, nominal): self.type = type self.user_id = user_id diff --git a/src/repositories/CategoryRepository.py b/src/repositories/CategoryRepository.py index e51d744..95faae5 100644 --- a/src/repositories/CategoryRepository.py +++ b/src/repositories/CategoryRepository.py @@ -26,3 +26,5 @@ def deleteCategory(self,id): db.session.delete(category) db.session.commit() return True + def getCategoryByName(self,name): + return Category.query.filter_by(name=name).first() \ No newline at end of file diff --git a/src/repositories/EventRepository.py b/src/repositories/EventRepository.py index bf14ea3..2cfd9c7 100644 --- a/src/repositories/EventRepository.py +++ b/src/repositories/EventRepository.py @@ -69,6 +69,7 @@ def updateStatus(self,event_id,status): event = Event.query.filter_by(event_id=event_id).first() if(not event) :return False event.status = status + event.change_status_at = datetime.now() db.session.commit() return event diff --git a/src/repositories/TicketRepository.py b/src/repositories/TicketRepository.py index e104853..cb3e309 100644 --- a/src/repositories/TicketRepository.py +++ b/src/repositories/TicketRepository.py @@ -1,13 +1,12 @@ from src.models.Ticket import Ticket,db +from sqlalchemy import and_ import string import random class TicketRepository: def getAllTickets(self): - return Ticket.query.all() - + return Ticket.query.filter(Ticket.event_id.isnot(None)).all() def getAllTicketByUserId(self,user_id): - return Ticket.query.filter_by(user_id=user_id).all() - + return Ticket.query.filter(and_(Ticket.user_id == user_id, Ticket.event_id.isnot(None))).all() def getAllTicketByEventId(self,event_id): return Ticket.query.filter_by(event_id=event_id).all() @@ -27,4 +26,4 @@ def attendTicket(self,code): db.session.commit() return ticket def getTicketByCode(self,code): - return Ticket.query.filter_by(ticket_code=code).first() \ No newline at end of file + return Ticket.query.filter(and_(Ticket.ticket_code == code, Ticket.event_id.isnot(None))).first() \ No newline at end of file diff --git a/src/services/CategoryService.py b/src/services/CategoryService.py index 846c45d..7ee2616 100644 --- a/src/services/CategoryService.py +++ b/src/services/CategoryService.py @@ -28,6 +28,10 @@ def createCategory(self,data): validate = CreateNewCategoryValidator(**data) if not validate: return self.failedOrSuccessRequest('failed', 400, 'Validation failed') + category = categoryRepository.getCategoryByName(data['name']) + if(category): + return self.failedOrSuccessRequest('failed', 400, 'category already exist') + newCategory = categoryRepository.createNewCategory(data) return self.failedOrSuccessRequest('success', 201, queryResultToDict([newCategory])[0]) except ValueError as e: diff --git a/src/services/EventService.py b/src/services/EventService.py index c5b0154..70c3561 100644 --- a/src/services/EventService.py +++ b/src/services/EventService.py @@ -56,6 +56,12 @@ def createEvent(self,data,file,user_id): return EventService.failedOrSuccessRequest('failed', 400, 'Validation failed') if not file['poster']: return EventService.failedOrSuccessRequest('failed', 400, 'poster is required') + if file['poster'].content_type not in ['image/jpeg','image/png']: + return EventService.failedOrSuccessRequest('failed', 400, 'poster must be image') + if sys.getsizeof(file['poster'].read()) > 2000000: + return EventService.failedOrSuccessRequest('failed', 400, 'poster must be less than 2mb') + # + poster = upload_file(file['poster']) newEvent = eventRepository.createNewEvent(**data,poster_path=poster,user_id=user_id) return EventService.failedOrSuccessRequest('success', 201, queryResultToDict([newEvent])[0])