Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/formatting_backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 0 additions & 14 deletions backend/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
61 changes: 28 additions & 33 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
172 changes: 88 additions & 84 deletions backend/usecase/payment_tracking_usecase.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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}')
Expand All @@ -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}')
Expand Down Expand Up @@ -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'<li>{element}</li>' for element in elements])

def _email_bold_element(element: str):
def _email_bold_element(element: str) -> str:
return f'<b>{element}</b>'

def _email_newline_element():
def _email_newline_element() -> str:
return '<br/>'

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}.')