diff --git a/.env.example b/.env.example index 2f099a8..52cc427 100644 --- a/.env.example +++ b/.env.example @@ -3,3 +3,10 @@ PORT=5000 DEBUG=True DATABASE_URL="mysql+pymysql://username:password@localhost:3306/se_ticket" + +MAIL_SERVER='smtp.gmail.com' +MAIL_PORT = 587 +MAIL_USERNAME = 'compest15team5@gmail.com' +MAIL_PASSWORD = 'duewujqfideibbkm' +MAIL_USE_TLS = False +MAIL_USE_SSL = True \ No newline at end of file diff --git a/migrations/versions/734664dd0b82_create_ticket_table.py b/migrations/versions/734664dd0b82_create_ticket_table.py new file mode 100644 index 0000000..a537f3d --- /dev/null +++ b/migrations/versions/734664dd0b82_create_ticket_table.py @@ -0,0 +1,44 @@ +"""Create_Ticket_table + +Revision ID: 734664dd0b82 +Revises: c6f3e7ccf086 +Create Date: 2023-08-21 16:05:02.108956 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '734664dd0b82' +down_revision = 'c6f3e7ccf086' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('tickets', + sa.Column('ticket_id', sa.Integer(), nullable=False), + sa.Column('event_id', sa.Integer(), nullable=True), + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('ticket_code', sa.String(length=6), nullable=True), + sa.Column('is_attended', sa.Boolean(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['event_id'], ['events.event_id'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.user_id'], ), + sa.PrimaryKeyConstraint('ticket_id') + ) + with op.batch_alter_table('events', schema=None) as batch_op: + batch_op.drop_column('created_at') + + # ### 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.add_column(sa.Column('created_at', sa.DATE(), nullable=True)) + + op.drop_table('tickets') + # ### end Alembic commands ### diff --git a/public/mail.html b/public/mail.html new file mode 100644 index 0000000..b0a4f2f --- /dev/null +++ b/public/mail.html @@ -0,0 +1,77 @@ + + + + + + Event Information Ticket + + + +
+

Your Event Information Ticket

+

Dear [Recipient's Name],

+

We are excited to have you join us for our upcoming event. Here are the details you'll need:

+ +
+

Event Details

+

Event Name: [Event Name]

+

Date: [Event Date]

+

Time: [Event Time]

+

Location: [Event Location]

+
+ +

If you have any questions or need further assistance, please don't hesitate to contact us.

+

Thank you for being a part of this event!

+ +

Your Ticket Code:

+
+ {{ code }} +
+ +

Best Regards,
Your Event Team

+ +

Get Directions

+
+ + diff --git a/requirement.txt b/requirement.txt index ed78b1a..8f69acf 100644 --- a/requirement.txt +++ b/requirement.txt @@ -8,4 +8,5 @@ pydantic==2.1.1 pydantic[email]==2.1.1 werkzeug==2.3.3 pyjwt==2.8.0 -Flask-Migrate==4.0.4 \ No newline at end of file +Flask-Migrate==4.0.4 +Flask-Mail==0.9.1 diff --git a/src/config/config.py b/src/config/config.py index 79e5888..c088369 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -3,7 +3,17 @@ BASE_URL = env["BASE_URL"] or "http://localhost" PORT= env['PORT'] or 5000 DEBUG= env['DEBUG'] or True + DATABASE_URL = env['DATABASE_URL'] or "mysq;://root:root@localhost:3306/se_ticket" + JWT_ACCESS_TOKEN_EXPIRES = env['JWT_ACCESS_TOKEN_EXPIRES'] or 60 * 60 * 24 * 7 JWT_ACCESS_TOKEN_SECRET = env['JWT_ACCESS_TOKEN_SECRET'] or "secret" JWT_ACCESS_TOKEN_ALGORITHM = env['JWT_ACCESS_TOKEN_ALGORITHM'] or "HS256" + + +MAIL_SERVER = env['MAIL_SERVER'] or 'sandbox.smtp.mailtrap.io' +MAIL_PORT = env['MAIL_PORT'] or 2525 +MAIL_USE_TLS = env['MAIL_USE_TLS'] or True +MAIL_USE_SSL = env['MAIL_USE_SSL'] or False +MAIL_USERNAME = env['MAIL_USERNAME'] or '1190969a29319c' +MAIL_PASSWORD = env['MAIL_PASSWORD'] or '39c02575b88bd3' \ No newline at end of file diff --git a/src/config/mail.py b/src/config/mail.py new file mode 100644 index 0000000..c5fb10f --- /dev/null +++ b/src/config/mail.py @@ -0,0 +1,13 @@ +from flask_mail import Mail +from src.config.config import MAIL_PORT,MAIL_SERVER,MAIL_USE_SSL,MAIL_USE_TLS,MAIL_USERNAME,MAIL_PASSWORD +def mailConfig(app): + print(MAIL_SERVER,MAIL_PORT,MAIL_USE_SSL,MAIL_USE_TLS,MAIL_USERNAME,MAIL_PASSWORD) + print(type(MAIL_SERVER),type(MAIL_PORT),type(MAIL_USE_SSL),type(MAIL_USE_TLS),type(MAIL_USERNAME),type(MAIL_PASSWORD)) + app.config['MAIL_SERVER']= MAIL_SERVER + app.config['MAIL_PORT'] = MAIL_PORT + app.config['MAIL_USE_TLS'] = MAIL_USE_TLS == 'True' + app.config['MAIL_USE_SSL'] = MAIL_USE_SSL == 'True' + app.config['MAIL_USERNAME'] = MAIL_USERNAME + app.config['MAIL_PASSWORD'] = MAIL_PASSWORD + return Mail(app) + \ No newline at end of file diff --git a/src/config/permission.py b/src/config/permission.py index d4755dc..9bfc109 100644 --- a/src/config/permission.py +++ b/src/config/permission.py @@ -3,6 +3,7 @@ '/auth/logout', '/categories', '/events/', + '/tickets/' ] admin_permission=[ diff --git a/src/controllers/TicketController.py b/src/controllers/TicketController.py new file mode 100644 index 0000000..293459c --- /dev/null +++ b/src/controllers/TicketController.py @@ -0,0 +1,41 @@ +from flask import Blueprint +from flask import request,g +from src.services.TicketService import TicketService +from src.middlewares.AuthMiddleware import isAuthenticated +import src.utils.getResponse as Response + +TicketApp = Blueprint('TicketApp', __name__,) +ticketService = TicketService() + +@TicketApp.route('/', methods=['GET']) +@isAuthenticated +def index(): + + result = ticketService.getAllTickets() + return Response.success(result['data'],"success get all Tickets") + +@TicketApp.route('/', methods=['POST']) +@isAuthenticated +def store(): + result = ticketService.createNewTicket(request.json,g.user['user_id']) + if(result['status'] == 'failed'): + return Response.error(result['data'],result['code']) + return Response.success(result['data'],"success create new event") + +@TicketApp.route('/my', methods=['GET']) +@isAuthenticated +def myTicket(): + result = ticketService.getTicketByUserId(g.user['user_id']) + if(result['status'] == 'failed'): + return Response.error(result['data'],result['code']) + return Response.success(result['data'],"success get all my ticket") + + +@TicketApp.route('//delete', methods=['DELETE']) +@isAuthenticated +def delete(id): + result = ticketService.deleteCategory(id) + if(result['status'] == 'failed'): + return Response.error(result['data'],result['code']) + + return Response.success(result['data'],"success delete event") diff --git a/src/middlewares/AuthMiddleware.py b/src/middlewares/AuthMiddleware.py index 8aa38dd..20e7126 100644 --- a/src/middlewares/AuthMiddleware.py +++ b/src/middlewares/AuthMiddleware.py @@ -4,6 +4,7 @@ from flask import request, g from src.repositories.UserRepository import UserRepository from src.utils.permission import check_role_is_have_access +from src.utils.convert import queryResultToDict user_repository = UserRepository() def isAuthenticated(func): @@ -19,7 +20,8 @@ def wrapper(*args, **kwargs): is_have_access = check_role_is_have_access(user.role, request.path) if not is_have_access: return response.error(message="Forbidden", errors=None, status_code=403) - g.user = dict(user) + print(user) + g.user = queryResultToDict([user])[0] return func(*args, **kwargs) except jwt.jwt.InvalidKeyError as e: return response.error(message="Unauthorized", errors=None, status_code=401) diff --git a/src/models/Category.py b/src/models/Category.py index d23ef4f..7564f97 100644 --- a/src/models/Category.py +++ b/src/models/Category.py @@ -8,19 +8,4 @@ class Category(db.Model): def __init__(self,name ): self.name = name - - def __repr__(self): - - return f"" - - - def toDict(self): - return { - 'category_id': self.category_id, - 'name': self.name, - } - - def __iter__(self): - yield 'category_id', self.category_id - yield 'name', self.name - \ No newline at end of file + \ No newline at end of file diff --git a/src/models/Event.py b/src/models/Event.py index e66ef5e..5dff9b5 100644 --- a/src/models/Event.py +++ b/src/models/Event.py @@ -14,7 +14,8 @@ class Event(db.Model): address= db.Column(db.Text) 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') + user = db.relationship('User', backref='events') def __init__(self, title, description, price, date_of_event, number_of_ticket,user_id,poster_path,address,category_id ): self.title= title @@ -27,35 +28,4 @@ def __init__(self, title, description, price, date_of_event, number_of_ticket,us self.status = 'PENDING' self.category_id = category_id self.address = address - - def __repr__(self): - - return f"" - - - def toDict(self): - return { - 'event_id': self.event_id, - 'title': self.title, - 'description': self.description, - 'price': self.price, - 'date_of_event': self.date_of_event, - 'number_of_tickets': self.number_of_ticket, - 'user_id': self.user_id, - 'poster_path': self.poster_path, - 'status': self.status, - 'category_id': self.category_id, - } - - def __iter__(self): - yield 'event_id', self.event_id - yield 'title', self.title - yield 'description', self.description - yield 'price', self.price - yield 'date_of_event', self.date_of_event - yield 'number_of_ticket', self.number_of_ticket - yield 'user_id', self.user_id - yield 'poster_path', self.poster_path - yield 'status', self.status - yield 'category_id', self.category_id - yield 'address', self.address \ No newline at end of file + \ No newline at end of file diff --git a/src/models/Ticket.py b/src/models/Ticket.py new file mode 100644 index 0000000..2d74159 --- /dev/null +++ b/src/models/Ticket.py @@ -0,0 +1,18 @@ +from src.server.main import db,main_app +from src.config.database import generateDatabase +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')) + 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") + user = db.relationship("User", back_populates="tickets") + def __init__(self, event_id, user_id, ticket_code): + self.event_id = event_id + self.user_id = user_id + self.ticket_code = ticket_code + \ No newline at end of file diff --git a/src/models/User.py b/src/models/User.py index cc53119..0856a0a 100644 --- a/src/models/User.py +++ b/src/models/User.py @@ -10,7 +10,7 @@ class User(db.Model): password = db.Column(db.String(255)) balance = db.Column(db.Float, default=0) status = db.Column(db.String(255), default='INACTIVE') - + tickets = db.relationship('Ticket', back_populates='user') def __init__(self, email, name, role, password, status, balance =0): self.name = name self.email = email @@ -18,25 +18,6 @@ def __init__(self, email, name, role, password, status, balance =0): self.password = password self.balance = balance self.status = status - - def __repr__(self): - return f"" - - - def toDict(self): - return { - 'user_id': self.user_id, - 'email': self.email, - 'name': self.name, - 'role': self.role, - 'balance': self.balance, - 'status': self.status - } - - def __iter__(self): - yield 'user_id', self.user_id - yield 'email', self.email - yield 'name', self.name - yield 'role', self.role - yield 'balance', self.balance - yield 'status', self.status + # def __repr__(self): + # return '' % self.name + \ No newline at end of file diff --git a/src/repositories/CategoryRepository.py b/src/repositories/CategoryRepository.py index f13ec12..e51d744 100644 --- a/src/repositories/CategoryRepository.py +++ b/src/repositories/CategoryRepository.py @@ -8,7 +8,7 @@ def createNewCategory(self,data): newCategory = Category(name=data['name']) db.session.add(newCategory) db.session.commit() - return dict(newCategory) + return newCategory def getCategoryById(self,category_id): return Category.query.filter_by(category_id=category_id).first() @@ -18,7 +18,7 @@ def updateCategory(self,id,data): if(not category) :return False category.name = data['name'] db.session.commit() - return dict(category) + return category def deleteCategory(self,id): category = Category.query.filter_by(category_id=id).first() diff --git a/src/repositories/EventRepository.py b/src/repositories/EventRepository.py index 7e5eed9..4b5520a 100644 --- a/src/repositories/EventRepository.py +++ b/src/repositories/EventRepository.py @@ -40,22 +40,24 @@ def createNewEvent(self,title,description,price,date_of_event,number_of_ticket,u ) db.session.add(newEvent) db.session.commit() - return dict(newEvent) + return newEvent def getEventById(self,event_id): return Event.query.filter_by(event_id=event_id).first() - def updateEvent(self,event_id,title,description,price,date_of_event,number_of_tickets,poster_path): + def updateEvent(self,event_id,title,description,price,date_of_event,number_of_ticket,poster_path,address,category_id): event = Event.query.filter_by(event_id=event_id).first() if(not event) :return False event.title = title + event.address = address + event.category_id = category_id event.description = description event.price = price event.date_of_event = date_of_event - event.number_of_ticket = number_of_tickets + event.number_of_ticket = number_of_ticket event.poster_path = poster_path db.session.commit() - return dict(event) + return event def deleteEvent(self,event_id): event = Event.query.filter_by(event_id=event_id).first() @@ -69,7 +71,7 @@ def updateStatus(self,event_id,status): if(not event) :return False event.status = status db.session.commit() - return dict(event) + return event def getAllEventByUserId(self,user_id): return Event.query.filter_by(user_id=user_id).all() \ No newline at end of file diff --git a/src/repositories/TicketRepository.py b/src/repositories/TicketRepository.py new file mode 100644 index 0000000..d366446 --- /dev/null +++ b/src/repositories/TicketRepository.py @@ -0,0 +1,23 @@ +from src.models.Ticket import Ticket,db +import string +import random +class TicketRepository: + def getAllTickets(self): + return Ticket.query.all() + + def getAllTicketByUserId(self,user_id): + return Ticket.query.filter_by(user_id=user_id).all() + + def getAllTicketByEventId(self,event_id): + return Ticket.query.filter_by(event_id=event_id).all() + + def createNewTicket(self,data,user_id): + newTicket = Ticket( + event_id=data['event_id'], + user_id=user_id, + ticket_code=''.join(random.choices(string.ascii_uppercase + string.digits, k = 6)) , + ) + db.session.add(newTicket) + db.session.commit() + return newTicket\ + \ No newline at end of file diff --git a/src/repositories/UserRepository.py b/src/repositories/UserRepository.py index be85064..664f50a 100644 --- a/src/repositories/UserRepository.py +++ b/src/repositories/UserRepository.py @@ -20,7 +20,7 @@ def createNewUser(self,data): ) db.session.add(newUser) db.session.commit() - return dict(newUser) + return newUser def getUserById(self,user_id): return User.query.filter_by(user_id=user_id).first() def verifyUser(self,user_id): @@ -28,4 +28,4 @@ def verifyUser(self,user_id): if(not user) :return False user.status = 'ACTIVE' db.session.commit() - return dict(user) \ No newline at end of file + return user \ No newline at end of file diff --git a/src/routes/main.py b/src/routes/main.py index c39d11f..9c3d9df 100644 --- a/src/routes/main.py +++ b/src/routes/main.py @@ -2,9 +2,11 @@ from src.controllers.AuthController import AuthApp from src.controllers.EventController import EventApp from src.controllers.CategoryController import CategoryApp +from src.controllers.TicketController import TicketApp routes = [ { "url": "/users", "name": UserApp }, {"url": "/auth", "name": AuthApp}, {"url":"/categories","name":CategoryApp}, {"url":"/events","name":EventApp}, + {"url":"/tickets","name":TicketApp} ] diff --git a/src/server/main.py b/src/server/main.py index e99d12b..e3acc1a 100644 --- a/src/server/main.py +++ b/src/server/main.py @@ -1,12 +1,14 @@ -from flask import Flask +from flask import Flask,g from flask_cors import CORS from flask_migrate import Migrate from src.config.config import BASE_URL, PORT, DEBUG +from src.config.mail import mailConfig from src.config.database import database -main_app = Flask(__name__,static_folder='../../public', static_url_path='/') - +main_app = Flask(__name__,static_folder='../../public', static_url_path='/',template_folder='../../templates') +mail = mailConfig(main_app) CORS(main_app) + db = database(main_app) migrate = Migrate(main_app, db) diff --git a/src/services/AuthService.py b/src/services/AuthService.py index 5ef42b3..8053376 100644 --- a/src/services/AuthService.py +++ b/src/services/AuthService.py @@ -3,6 +3,7 @@ from src.repositories.UserRepository import UserRepository import bcrypt import src.utils.jwt as jwt +from src.utils.convert import queryResultToDict user_repository = UserRepository() from src.services.Service import Service class AuthService(Service): @@ -26,7 +27,7 @@ def registerUser(self, data): newUser = user_repository.createNewUser(data) - return self.failedOrSuccessRequest('success', 201, newUser) + return self.failedOrSuccessRequest('success', 201, queryResultToDict([newUser])[0]) except ValueError as e: return self.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) @@ -37,12 +38,14 @@ def login(self,data): return self.failedOrSuccessRequest('failed', 400, 'Validation failed') user = user_repository.getUserByEmail(data['email']) + if not user: + return self.failedOrSuccessRequest('failed', 400, 'user not found') isPasswordMatch = bcrypt.checkpw(data['password'].encode('utf-8'), user.password.encode('utf-8') ) if not user or not isPasswordMatch: return self.failedOrSuccessRequest('failed', 400, 'user not found') print(user.status) if(user.status == 'INACTIVE'):return self.failedOrSuccessRequest('failed', 400, 'user not verified') - user_dict = dict(user) + user_dict = queryResultToDict([user])[0] user_dict['token'] = jwt.encode(user_dict) return self.failedOrSuccessRequest('success', 200,user_dict) @@ -57,7 +60,7 @@ def verifyEO(self,data): user.status = 'ACTIVE' user_repository.commit() - return self.failedOrSuccessRequest('success', 200, dict(user)) + return self.failedOrSuccessRequest('success', 200, queryResultToDict([user])[0]) except ValueError as e: return self.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) def verify(self,data): @@ -68,6 +71,6 @@ def verify(self,data): user = user_repository.verifyUser(data['user_id']) if not user: return self.failedOrSuccessRequest('failed', 400, 'user not found') - return self.failedOrSuccessRequest('success', 200, dict(user)) + return self.failedOrSuccessRequest('success', 200, queryResultToDict([user])[0]) except ValueError as e: return self.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) \ No newline at end of file diff --git a/src/services/CategoryService.py b/src/services/CategoryService.py index 5da28b6..846c45d 100644 --- a/src/services/CategoryService.py +++ b/src/services/CategoryService.py @@ -1,6 +1,6 @@ from src.repositories.CategoryRepository import CategoryRepository -from src.utils.convert import transformToDictList +from src.utils.convert import queryResultToDict from src.services.Service import Service from src.utils.validator.CategoryValidator import CreateNewCategoryValidator,UpdateCategoryValidator,DeleteCategoryValidator @@ -19,7 +19,7 @@ def failedOrSuccessRequest(status, code, data): def getAllCategories(self): try: data = categoryRepository.getAllCategories() - return CategoryService.failedOrSuccessRequest('success', 200, transformToDictList(data)) + return CategoryService.failedOrSuccessRequest('success', 200, queryResultToDict(data)) except Exception as e: return CategoryService.failedOrSuccessRequest('failed', 500, str(e)) @@ -29,7 +29,7 @@ def createCategory(self,data): if not validate: return self.failedOrSuccessRequest('failed', 400, 'Validation failed') newCategory = categoryRepository.createNewCategory(data) - return self.failedOrSuccessRequest('success', 201, newCategory) + return self.failedOrSuccessRequest('success', 201, queryResultToDict([newCategory])[0]) except ValueError as e: return self.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) except Exception as e: @@ -44,7 +44,7 @@ def updateCategory(self,id,data): if not event: return self.failedOrSuccessRequest('failed', 404, 'Category not found') categoryUpdated = categoryRepository.updateCategory(id,data) - return self.failedOrSuccessRequest('success', 201, categoryUpdated) + return self.failedOrSuccessRequest('success', 201, queryResultToDict([categoryUpdated])[0]) except ValueError as e: return self.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) except Exception as e: diff --git a/src/services/EventService.py b/src/services/EventService.py index ac66779..c39b911 100644 --- a/src/services/EventService.py +++ b/src/services/EventService.py @@ -1,6 +1,6 @@ from src.repositories.EventRepository import EventRepository -from src.utils.convert import transformToDictList +from src.utils.convert import queryResultToDict from src.services.Service import Service from src.utils.uploadFile import upload_file,delete_file from src.utils.validator.EventValidator import CreateNewEventValidator,UpdateEventValidator,DeleteEventValidator,VerifyEventValidator @@ -23,7 +23,7 @@ def getAllEvent(self,filter): data = eventRepository.getAllEventFiltered(filter) else: data = eventRepository.getAllEvent() - return EventService.failedOrSuccessRequest('success', 200, transformToDictList(data)) + return EventService.failedOrSuccessRequest('success', 200, queryResultToDict(data,['user','category'])) except Exception as e: print(e) return EventService.failedOrSuccessRequest('failed', 500, str(e)) @@ -38,7 +38,7 @@ def createEvent(self,data,file,user_id): return EventService.failedOrSuccessRequest('failed', 400, 'poster is required') poster = upload_file(file['poster']) newEvent = eventRepository.createNewEvent(**data,poster_path=poster,user_id=user_id) - return EventService.failedOrSuccessRequest('success', 201, newEvent) + return EventService.failedOrSuccessRequest('success', 201, queryResultToDict([newEvent])[0]) except ValueError as e: return EventService.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) except Exception as e: @@ -52,19 +52,18 @@ def updateEvent(self,id,data,file ,user_id): if not event: return EventService.failedOrSuccessRequest('failed', 404, 'Event not found') data_dict = data.to_dict() - event_copied = event.toDict().copy() + event_copied = queryResultToDict([event])[0] if file: - delete_file(event['poster_path']) + print(event) event_copied['poster_path'] = upload_file(file['poster']) event_copied.update(data_dict) event_copied.pop('user_id') event_copied.pop('status') - print('event',event_copied) - + print(event_copied,file=sys.stderr) eventUpdated = eventRepository.updateEvent(**event_copied) - return EventService.failedOrSuccessRequest('success', 201, eventUpdated) + return EventService.failedOrSuccessRequest('success', 201, queryResultToDict([eventUpdated])[0]) except ValueError as e: return EventService.failedOrSuccessRequest('failed', 500, errorHandler(e.errors())) except Exception as e: diff --git a/src/services/TicketService.py b/src/services/TicketService.py new file mode 100644 index 0000000..e6518df --- /dev/null +++ b/src/services/TicketService.py @@ -0,0 +1,55 @@ + +from src.repositories.TicketRepository import TicketRepository +from src.utils.convert import queryResultToDict +from src.services.Service import Service +from src.utils.validator.TicketValidator import CreateNewTicketValidator +from src.utils.errorHandler import errorHandler +from src.utils.sendMail import sendMail + +ticketRepository = TicketRepository() + +class TicketService(Service): + @staticmethod + def failedOrSuccessRequest(status, code, data): + return { + 'status': status, + "code": code, + 'data': data, + } + + def getAllTickets(self): + try: + data = ticketRepository.getAllTickets() + return self.failedOrSuccessRequest('success', 200, queryResultToDict(data,['user','event'])) + except Exception as e: + return self.failedOrSuccessRequest('failed', 500, str(e)) + + def createNewTicket(self,data,user_id): + try: + validate = CreateNewTicketValidator(**data) + if(not validate): + return self.failedOrSuccessRequest('failed', 400, validate.errors()) + + data = ticketRepository.createNewTicket(data,user_id) + sendMail( + name=data.user.name, + code=data.ticket_code, + date=data.event.date_of_event, + event_name=data.event.title, + location=data.event.address, + subject="Ticket Event", + to=data.user.email + ) + + return self.failedOrSuccessRequest('success', 200, queryResultToDict([data])[0]) + except ValueError as e: + return self.failedOrSuccessRequest('failed', 400, errorHandler(e.errors())) + except Exception as e: + return self.failedOrSuccessRequest('failed', 500, str(e)) + + def getTicketByUserId(self,user_id): + try: + data = ticketRepository.getAllTicketByUserId(user_id) + return self.failedOrSuccessRequest('success', 200, queryResultToDict(data,['user','event'])) + except Exception as e: + return self.failedOrSuccessRequest('failed', 500, str(e)) \ No newline at end of file diff --git a/src/services/UserService.py b/src/services/UserService.py index c75f6cf..72bb656 100644 --- a/src/services/UserService.py +++ b/src/services/UserService.py @@ -1,6 +1,6 @@ from src.repositories.UserRepository import UserRepository -from src.utils.convert import transformToDictList +from src.utils.convert import queryResultToDict from src.services.Service import Service userRepository = UserRepository() @@ -16,7 +16,7 @@ def failedOrSuccessRequest(status, code, data): def getAllUser(self): try: data = userRepository.getAllUser() - return UserService.failedOrSuccessRequest('success', 200, transformToDictList(data)) + return UserService.failedOrSuccessRequest('success', 200, queryResultToDict(data)) except Exception as e: return UserService.failedOrSuccessRequest('failed', 500, str(e)) diff --git a/src/utils/convert.py b/src/utils/convert.py index 5c9b20a..835412e 100644 --- a/src/utils/convert.py +++ b/src/utils/convert.py @@ -1,3 +1,23 @@ def transformToDictList(data: list): return list(map(lambda x: dict(x), data)) - \ No newline at end of file + +def queryResultToDict(query_result,related_tables=None): + result_list = [] + for row in query_result: + row_dict = {} + for column in row.__table__.columns: + row_dict[column.name] = getattr(row, column.name) + + # Handle related tables + if related_tables: + for related_table in related_tables: + related_data = getattr(row, related_table, None) + if related_data is not None: + if isinstance(related_data, list): + related_data = queryResultToDict(related_data) + else: + related_data = queryResultToDict([related_data])[0] + row_dict[related_table] = related_data + + result_list.append(row_dict) + return result_list \ No newline at end of file diff --git a/src/utils/convertResponse.py b/src/utils/convertResponse.py new file mode 100644 index 0000000..ea507d8 --- /dev/null +++ b/src/utils/convertResponse.py @@ -0,0 +1,7 @@ +def query_result_to_dict(query_result): + result_list = [] + for row in query_result: + row_dict = dict(row.__dict__) + row_dict.pop('_sa_instance_state', None) # Remove SQLAlchemy state + result_list.append(row_dict) + return result_list diff --git a/src/utils/sendMail.py b/src/utils/sendMail.py new file mode 100644 index 0000000..b48ee78 --- /dev/null +++ b/src/utils/sendMail.py @@ -0,0 +1,23 @@ +from flask_mail import Message +from flask import render_template +from src.server.main import mail +from datetime import datetime +def sendMail(to,subject,code,event_name,location, date:datetime,name): + # split date and time + event_date = date.strftime("%d %B %Y") + event_time = date.strftime("%H:%M") + templates = render_template('html/mail.html',code=code,event_name=event_name,location=location,name=name, date=event_date,time=event_time) + msg = Message( + subject, + recipients=[to], + html=templates, + sender="Ticket@gmail.com" + ) + try: + print("sending mail") + mail.send(msg) + return True + except Exception as e: + print(e) + print("failed to send mail : ",e.with_traceback) + return False \ No newline at end of file diff --git a/src/utils/validator/TicketValidator.py b/src/utils/validator/TicketValidator.py new file mode 100644 index 0000000..1a9ef8f --- /dev/null +++ b/src/utils/validator/TicketValidator.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + +class CreateNewTicketValidator(BaseModel): + event_id: int + \ No newline at end of file diff --git a/templates/html/mail.html b/templates/html/mail.html new file mode 100644 index 0000000..ee25c1d --- /dev/null +++ b/templates/html/mail.html @@ -0,0 +1,76 @@ + + + + + + Event Information Ticket + + + +
+

Your Event Information Ticket

+

Dear {{name}},

+

We are excited to have you join us for our upcoming event. Here are the details you'll need:

+ +
+

Event Details

+

Event Name: {{event_name}}

+

Date: {{date}}

+

Time: {{time}}

+

Location: {{location}}

+
+ +

If you have any questions or need further assistance, please don't hesitate to contact us.

+

Thank you for being a part of this event!

+ +

Your Ticket Code:

+
+ {{ code }} +
+ +

Best Regards,
SeTicket Team

+ +
+ +