diff --git a/.github/workflows/formatting_backend.yml b/.github/workflows/formatting_backend.yml index c033780..ff98abf 100644 --- a/.github/workflows/formatting_backend.yml +++ b/.github/workflows/formatting_backend.yml @@ -25,7 +25,5 @@ jobs: - name: Run Formatters run: | - flake8 --max-line-length=120 --ignore=E203,E266,E501,W503,F403,F401,E402,F841,C901,F722,F405,F811 - black --check . --line-length=120 --skip-string-normalization ruff check . working-directory: backend diff --git a/backend/.pre-commit-config.yaml b/backend/.pre-commit-config.yaml index 9dc331b..33343ef 100644 --- a/backend/.pre-commit-config.yaml +++ b/backend/.pre-commit-config.yaml @@ -29,20 +29,6 @@ repos: # Run the formatter. - id: ruff-format - - repo: https://github.com/psf/black - rev: 25.1.0 - hooks: - - id: black - language_version: python3.11 - args: [--line-length=120, --skip-string-normalization] - - - repo: https://github.com/pycqa/flake8 - rev: 7.1.1 - hooks: - - id: flake8 - args: - - --max-line-length=120 - - --ignore=E203,E266,E501,W503,F403,F401,E402,F841,C901,F722,F405,F811 # mypy # - repo: https://github.com/pre-commit/mirrors-mypy # rev: 'v1.17.0' diff --git a/backend/pyproject.toml b/backend/pyproject.toml index be6df32..6b591c5 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -55,48 +55,43 @@ exclude = [ '^.serverless/', ] -[tool.black] -line-length = 120 -skip-string-normalization = true -target-version = ['py311'] - [tool.ruff] line-length = 120 indent-width = 4 target-version = "py311" exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".ipynb_checkpoints", - ".mypy_cache", - ".nox", - ".pants.d", - ".pyenv", - ".pytest_cache", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - ".vscode", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "site-packages", - "venv", - ".serverless" +".bzr", +".direnv", +".eggs", +".git", +".git-rewrite", +".hg", +".ipynb_checkpoints", +".mypy_cache", +".nox", +".pants.d", +".pyenv", +".pytest_cache", +".pytype", +".ruff_cache", +".svn", +".tox", +".venv", +".vscode", +"pypackages", +"_build", +"buck-out", +"build", +"dist", +"node_modules", +"site-packages", +"venv", +".serverless" ] [tool.ruff.lint] ignore = [ - "E203", "E266", "E501", "F403", "F401", "E402", "F841", "C901", "F722", "F405", "F811" +"E203", "E266", "E501", "F403", "F401", "E402", "F841", "C901", "F722", "F405", "F811" ] select = ["E4", "E7", "E9", "F"] fixable = ["ALL"] diff --git a/backend/usecase/payment_tracking_usecase.py b/backend/usecase/payment_tracking_usecase.py index 76fd56d..4883547 100644 --- a/backend/usecase/payment_tracking_usecase.py +++ b/backend/usecase/payment_tracking_usecase.py @@ -1,5 +1,6 @@ from datetime import datetime, timezone from http import HTTPStatus +from typing import Optional import ulid from model.email.email import EmailIn, EmailType @@ -36,6 +37,7 @@ def process_payment_event(self, message_body: dict) -> None: registration_data = registration_details.registrationData event_id = registration_details.eventId entry_id = registration_details.entryId + recorded_registration_data = None if transaction_status == TransactionStatus.PENDING: logger.info(f'Skipping PENDING message for entryId: {entry_id}') @@ -57,15 +59,23 @@ def process_payment_event(self, message_body: dict) -> None: logger.info(f'Payment transaction status updated to {transaction_status} for entryId {entry_id}') - recorded_registration_data = self._create_and_save_registration(payment_tracking_body=payment_tracking_body) + if transaction_status == TransactionStatus.SUCCESS: + recorded_registration_data = self._create_and_save_registration( + payment_tracking_body=payment_tracking_body + ) + if not recorded_registration_data: + logger.error(f'Failed to save registration for entryId {entry_id}') self._send_email_notification( + first_name=registration_data.firstName, + email=registration_data.email, + transaction_id=entry_id, recorded_registration=recorded_registration_data, ticket_type=registration_data.ticketType.value, status=transaction_status, event_detail=event_detail, ) - logger.info(f'Successfully processed and saved registration for {registration_data.email}') + logger.info(f'Successfully processed registration for {registration_data.email}') except Exception as e: logger.error(f'Failed to process successful payment for entryId {registration_details.entryId}: {e}') @@ -138,106 +148,100 @@ def _create_and_save_registration(self, payment_tracking_body: PaymentTrackingBo def _send_email_notification( self, - recorded_registration: Registration, + email: str, + first_name: str, + transaction_id: str, ticket_type: str, - status: str, + status: TransactionStatus, event_detail: Event, is_pycon_event: bool = True, + recorded_registration: Optional[Registration] = None, ): - def _email_list_elements(elements: list[str]): + """ + Sends an email notification based on the transaction status and event type. + """ + if not event_detail: + logger.error('Event details are missing. Cannot send email.') + return + + def _email_list_elements(elements: list[str]) -> str: return '\n'.join([f'
  • {element}
  • ' for element in elements]) - def _email_bold_element(element: str): + def _email_bold_element(element: str) -> str: return f'{element}' - def _email_newline_element(): + def _email_newline_element() -> str: return '
    ' - pycon_email_templates = { - TransactionStatus.SUCCESS: { - 'subject': "You're all set for PyCon Davao 2025!", - 'salutation': f'Hi {recorded_registration.firstName},', - 'body': [ - "Thank you for registering for PyCon Davao 2025 by DurianPy! Your payment was successful, and we're excited to see you at the event.", - _email_bold_element('Below is a summary of your registration details:'), - _email_list_elements( - [ - f'Registration ID: {recorded_registration.registrationId}', - f'Ticket Type: {ticket_type.capitalize()}', - f'Sprint Day Participation: {"Yes" if recorded_registration.sprintDay else "No"}', - ( - f'Amount Paid: ₱{recorded_registration.amountPaid:.2f}' - if recorded_registration.amountPaid is not None - else 'Amount Paid: ₱0' - ), - ] - ), - _email_newline_element(), - 'See you there!', - ], - 'regards': ['Best,'], - }, - TransactionStatus.FAILED: { - 'subject': 'Issue with your PyCon Davao 2025 Payment', - 'salutation': f'Hi {recorded_registration.firstName},', - 'body': [ - 'There was an issue processing your payment for PyCon Davao 2025. Please check your payment details or try again.', - f'If the problem persists, please contact our support team at durianpy.davao@gmail.com and present your transaction ID: {recorded_registration.transactionId}', - ], - 'regards': ['Sincerely,'], - }, - } - - email_templates = { + def _create_success_body(reg_data: Registration, ticket: str) -> list[str]: + logger.info(f'Creating success email body for registration: {reg_data}') + base_body = [ + f"Thank you for registering for {event_detail.name}! Your payment was successful, and we're excited to see you at the event.", + _email_bold_element('Below is a summary of your registration details:'), + ] + + list_items = [ + f'Registration ID: {reg_data.registrationId if reg_data.registrationId else "N/A"}', + f'Ticket Type: {ticket.capitalize()}', + f'Sprint Day Participation: {"Yes" if reg_data.sprintDay else "No"}', + f'Amount Paid: ₱{reg_data.amountPaid:.2f}' if reg_data.amountPaid is not None else 'Amount Paid: ₱0', + f'Transaction ID: {reg_data.transactionId if reg_data.transactionId else "N/A"}', + ] + + if is_pycon_event: + base_body[ + 0 + ] = "Thank you for registering for PyCon Davao 2025 by DurianPy! Your payment was successful, and we're excited to see you at the event." + list_items.pop() + + base_body.append(_email_list_elements(list_items)) + base_body.append(_email_newline_element()) + base_body.append('See you there!') + + return base_body + + def _create_failed_body(name: str, transaction_id: str) -> list[str]: + return [ + f'There was an issue processing your payment for {name}. Please check your payment details or try again.', + f'If the problem persists, please contact our support team at durianpy.davao@gmail.com and present your transaction ID: {transaction_id}', + ] + + templates = { TransactionStatus.SUCCESS: { 'subject': f"You're all set for {event_detail.name}!", - 'salutation': f'Hi {recorded_registration.firstName},', - 'body': [ - f"Thank you for registering for {event_detail.name}! Your payment was successful, and we're excited to see you at the event.", - _email_bold_element('Below is a summary of your registration details:'), - _email_list_elements( - [ - f'Registration ID: {recorded_registration.registrationId}', - f'Ticket Type: {ticket_type.capitalize()}', - f'Sprint Day Participation: {"Yes" if recorded_registration.sprintDay else "No"}', - ( - f'Amount Paid: ₱{recorded_registration.amountPaid:.2f}' - if recorded_registration.amountPaid is not None - else 'Amount Paid: ₱0' - ), - f'Transaction ID: {recorded_registration.transactionId}', - ] - ), - _email_newline_element(), - 'See you there!', - ], + 'salutation': f'Hi {first_name},', + 'body': lambda: _create_success_body(recorded_registration, ticket_type), 'regards': ['Best,'], }, TransactionStatus.FAILED: { 'subject': f'Issue with your {event_detail.name} Payment', - 'salutation': f'Hi {recorded_registration.firstName},', - 'body': [ - f'There was an issue processing your payment for {event_detail.name}. Please check your payment details or try again.', - f'If the problem persists, please contact our support team at durianpy.davao@gmail.com and present your transaction ID: {recorded_registration.transactionId}', - ], + 'salutation': f'Hi {first_name},', + 'body': lambda: _create_failed_body(event_detail.name, transaction_id), 'regards': ['Sincerely,'], }, } - template_dict = pycon_email_templates if is_pycon_event else email_templates - template = template_dict.get(status) - - if template: - email_in = EmailIn( - to=[recorded_registration.email], - subject=template['subject'], - salutation=template['salutation'], - body=template['body'], - regards=template['regards'], - emailType=EmailType.REGISTRATION_EMAIL, - eventId=event_detail.eventId, - isDurianPy=True, - ) - self.email_usecase.send_email(email_in=email_in, event=event_detail) - else: + if is_pycon_event: + templates[TransactionStatus.SUCCESS]['subject'] = "You're all set for PyCon Davao 2025!" + templates[TransactionStatus.FAILED]['subject'] = 'Issue with your PyCon Davao 2025 Payment' + + template = templates.get(status) + + if not template: logger.error(f'No email template found for status: {status}') + return + + logger.info(f'Preparing to send email for event {event_detail.eventId} with status {status} to {email}.') + + email_in = EmailIn( + to=[email], + subject=template['subject'], + salutation=template['salutation'], + body=template['body'](), + regards=template['regards'], + emailType=EmailType.REGISTRATION_EMAIL, + eventId=event_detail.eventId, + isDurianPy=is_pycon_event, + ) + self.email_usecase.send_email(email_in=email_in, event=event_detail) + logger.info(f'Email notification sent for event {event_detail.eventId} with status {status}.')