services api
This commit is contained in:
26
ServicesBank/RoutineEmail/Dockerfile
Normal file
26
ServicesBank/RoutineEmail/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/* && pip install --no-cache-dir poetry
|
||||
|
||||
COPY /ServicesBank/RoutineEmail/pyproject.toml ./pyproject.toml
|
||||
|
||||
RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi --no-root --only main && pip cache purge && rm -rf ~/.cache/pypoetry
|
||||
RUN apt-get update && apt-get install -y cron
|
||||
|
||||
COPY /ServicesBank/RoutineEmail /
|
||||
|
||||
RUN chmod +x /run_app.sh
|
||||
|
||||
COPY /ServicesBank/RoutineEmail /
|
||||
COPY /ServicesApi/Controllers /ServicesApi/Controllers
|
||||
COPY /ServicesBank/Depends/config.py /ServicesBank/Depends/config.py
|
||||
COPY /ServicesBank/Depends/template_accounts.html /template_accounts.html
|
||||
|
||||
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
RUN touch /var/log/cron.log
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
69
ServicesBank/RoutineEmail/README.md
Normal file
69
ServicesBank/RoutineEmail/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Routine Email Service
|
||||
|
||||
## Overview
|
||||
This service sends automated email reports about account records at scheduled times using cron. It retrieves account records from a PostgreSQL database, formats them into an HTML email, and sends them to specified recipients.
|
||||
|
||||
## Environment Setup
|
||||
The service requires the following environment variables:
|
||||
|
||||
### Email Configuration
|
||||
- `EMAIL_HOST`: SMTP server address (e.g., "10.10.2.34")
|
||||
- `EMAIL_USERNAME`: Email sender address (e.g., "example@domain.com")
|
||||
- `EMAIL_PASSWORD`: Email password (sensitive)
|
||||
- `EMAIL_PORT`: SMTP port (e.g., 587)
|
||||
- `EMAIL_SEND`: Flag to enable/disable email sending (1 = enabled)
|
||||
|
||||
### Database Configuration
|
||||
- `DB_HOST`: PostgreSQL server address (e.g., "10.10.2.14")
|
||||
- `DB_USER`: Database username (e.g., "postgres")
|
||||
- `DB_PASSWORD`: Database password (sensitive)
|
||||
- `DB_PORT`: Database port (e.g., 5432)
|
||||
- `DB_NAME`: Database name (e.g., "postgres")
|
||||
|
||||
## Cron Job Configuration
|
||||
The service is configured to run daily at 11:00 Istanbul Time (08:00 UTC). This is set up in the entrypoint.sh script.
|
||||
|
||||
## Docker Container Setup
|
||||
|
||||
### Key Files
|
||||
|
||||
1. **Dockerfile**: Defines the container image with Python and cron
|
||||
|
||||
2. **entrypoint.sh**: Container entrypoint script that:
|
||||
- Creates an environment file (/env.sh) with all configuration variables
|
||||
- Sets up the crontab to run run_app.sh at the scheduled time
|
||||
- Starts the cron service
|
||||
- Tails the log file for monitoring
|
||||
|
||||
3. **run_app.sh**: Script executed by cron that:
|
||||
- Sources the environment file to get all configuration
|
||||
- Exports variables to make them available to the Python script
|
||||
- Runs the Python application
|
||||
- Logs environment and execution results
|
||||
|
||||
### Environment Variable Handling
|
||||
Cron jobs run with a minimal environment that doesn't automatically include Docker container environment variables. Our solution:
|
||||
|
||||
1. Captures all environment variables from Docker to a file at container startup
|
||||
2. Has the run_app.sh script source this file before execution
|
||||
3. Explicitly exports all variables to ensure they're available to the Python script
|
||||
|
||||
## Logs
|
||||
Logs are written to `/var/log/cron.log` and can be viewed with:
|
||||
```bash
|
||||
docker exec routine_email_service tail -f /var/log/cron.log
|
||||
```
|
||||
|
||||
## Manual Execution
|
||||
To run the service manually:
|
||||
```bash
|
||||
docker exec routine_email_service /run_app.sh
|
||||
```
|
||||
|
||||
## Docker Compose Configuration
|
||||
In the docker-compose.yml file, the service needs an explicit entrypoint configuration:
|
||||
```yaml
|
||||
entrypoint: ["/entrypoint.sh"]
|
||||
```
|
||||
|
||||
This ensures the entrypoint script runs when the container starts.
|
||||
157
ServicesBank/RoutineEmail/app.py
Normal file
157
ServicesBank/RoutineEmail/app.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import arrow
|
||||
from typing import List, Any
|
||||
|
||||
from Schemas import AccountRecords
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from Controllers.Email.send_email import EmailSendModel, EmailService
|
||||
|
||||
|
||||
def render_email_template(
|
||||
headers: List[str], rows: List[List[Any]], balance_error: bool, bank_balance: str
|
||||
) -> str:
|
||||
"""
|
||||
Render the HTML email template with the provided data.
|
||||
|
||||
Args:
|
||||
headers: List of column headers for the table
|
||||
rows: List of data rows for the table
|
||||
balance_error: Flag indicating if there's a balance discrepancy
|
||||
bank_balance: Current bank balance formatted as string
|
||||
|
||||
Returns:
|
||||
Rendered HTML template as string
|
||||
"""
|
||||
try:
|
||||
# Look for template in ServiceDepends directory
|
||||
env = Environment(loader=FileSystemLoader("/"))
|
||||
template = env.get_template("template_accounts.html")
|
||||
|
||||
# Render template with variables
|
||||
return template.render(
|
||||
headers=headers,
|
||||
rows=rows,
|
||||
bank_balance=bank_balance,
|
||||
balance_error=balance_error,
|
||||
today=str(arrow.now().date()),
|
||||
)
|
||||
except Exception as e:
|
||||
print("Exception render template:", e)
|
||||
raise
|
||||
|
||||
|
||||
def send_email_to_given_address(send_to: str, html_template: str) -> bool:
|
||||
"""
|
||||
Send email with the rendered HTML template to the specified address.
|
||||
|
||||
Args:
|
||||
send_to: Email address of the recipient
|
||||
html_template: Rendered HTML template content
|
||||
|
||||
Returns:
|
||||
Boolean indicating if the email was sent successfully
|
||||
"""
|
||||
today = arrow.now()
|
||||
subject = f"{str(today.date())} Gunes Apt. Cari Durum Bilgilendirme Raporu"
|
||||
|
||||
# Create email parameters using EmailSendModel
|
||||
email_params = EmailSendModel(
|
||||
subject=subject,
|
||||
html=html_template,
|
||||
receivers=[send_to],
|
||||
text=f"Gunes Apt. Cari Durum Bilgilendirme Raporu - {today.date()}",
|
||||
)
|
||||
|
||||
try:
|
||||
# Use the context manager to handle connection errors
|
||||
with EmailService.new_session() as email_session:
|
||||
# Send email through the service
|
||||
return EmailService.send_email(email_session, email_params)
|
||||
except Exception as e:
|
||||
print(f"Exception send email: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def set_account_records_to_send_email() -> bool:
|
||||
"""
|
||||
Retrieve account records from the database, format them, and send an email report.
|
||||
|
||||
Usage:
|
||||
from app import set_account_records_to_send_email
|
||||
|
||||
Returns:
|
||||
Boolean indicating if the process completed successfully
|
||||
"""
|
||||
# Get database session and retrieve records
|
||||
with AccountRecords.new_session() as db_session:
|
||||
account_records_query = AccountRecords.filter_all(db=db_session).query
|
||||
|
||||
# Get the 3 most recent records
|
||||
account_records: List[AccountRecords] = (
|
||||
account_records_query.order_by(
|
||||
AccountRecords.bank_date.desc(),
|
||||
AccountRecords.iban.desc(),
|
||||
)
|
||||
.limit(3)
|
||||
.all()
|
||||
)
|
||||
|
||||
# Check if we have enough records
|
||||
if len(account_records) < 2:
|
||||
print(f"Not enough records found: {len(account_records)}")
|
||||
return False
|
||||
|
||||
# Check for balance discrepancy
|
||||
first_record, second_record = account_records[0], account_records[1]
|
||||
expected_second_balance = (
|
||||
first_record.bank_balance - first_record.currency_value
|
||||
)
|
||||
balance_error = expected_second_balance != second_record.bank_balance
|
||||
|
||||
if balance_error:
|
||||
print(
|
||||
f"Balance error detected {expected_second_balance} != {second_record.bank_balance}"
|
||||
)
|
||||
|
||||
# Format rows for the email template
|
||||
list_of_rows = []
|
||||
for record in account_records:
|
||||
list_of_rows.append(
|
||||
[
|
||||
record.bank_date.strftime("%d/%m/%Y %H:%M"),
|
||||
record.process_comment,
|
||||
f"{record.currency_value:,.2f}",
|
||||
f"{record.bank_balance:,.2f}",
|
||||
]
|
||||
)
|
||||
# Get the most recent bank balance
|
||||
last_bank_balance = sorted(
|
||||
account_records, key=lambda x: x.bank_date, reverse=True
|
||||
)[0].bank_balance
|
||||
# Define headers for the table
|
||||
headers = [
|
||||
"Ulaştığı Tarih",
|
||||
"Banka Transaksiyonu Ek Bilgi",
|
||||
"Aktarım Değeri",
|
||||
"Banka Bakiyesi",
|
||||
]
|
||||
|
||||
# Recipient email address
|
||||
send_to = "karatay@mehmetkaratay.com.tr"
|
||||
|
||||
# Render email template
|
||||
html_template = render_email_template(
|
||||
headers=headers,
|
||||
rows=list_of_rows,
|
||||
balance_error=balance_error,
|
||||
bank_balance=f"{last_bank_balance:,.2f}",
|
||||
)
|
||||
|
||||
# Send the email
|
||||
return send_email_to_given_address(send_to=send_to, html_template=html_template)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = set_account_records_to_send_email()
|
||||
print("Email sent successfully" if success else "Failed to send email")
|
||||
exit_code = 0 if success else 1
|
||||
exit(exit_code)
|
||||
29
ServicesBank/RoutineEmail/entrypoint.sh
Normal file
29
ServicesBank/RoutineEmail/entrypoint.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create environment file that will be available to cron jobs
|
||||
echo "# Environment variables for cron jobs" > /env.sh
|
||||
echo "EMAIL_HOST=\"$EMAIL_HOST\"" >> /env.sh
|
||||
echo "EMAIL_USERNAME=\"$EMAIL_USERNAME\"" >> /env.sh
|
||||
echo "EMAIL_PASSWORD=\"$EMAIL_PASSWORD\"" >> /env.sh
|
||||
echo "EMAIL_PORT=$EMAIL_PORT" >> /env.sh
|
||||
echo "EMAIL_SEND=$EMAIL_SEND" >> /env.sh
|
||||
echo "DB_HOST=\"$DB_HOST\"" >> /env.sh
|
||||
echo "DB_USER=\"$DB_USER\"" >> /env.sh
|
||||
echo "DB_PASSWORD=\"$DB_PASSWORD\"" >> /env.sh
|
||||
echo "DB_PORT=$DB_PORT" >> /env.sh
|
||||
echo "DB_NAME=\"$DB_NAME\"" >> /env.sh
|
||||
|
||||
# Add Python environment variables
|
||||
echo "PYTHONPATH=/" >> /env.sh
|
||||
echo "PYTHONUNBUFFERED=1" >> /env.sh
|
||||
echo "PYTHONDONTWRITEBYTECODE=1" >> /env.sh
|
||||
|
||||
# Make the environment file available to cron
|
||||
echo "0 8 * * * /run_app.sh >> /var/log/cron.log 2>&1" > /tmp/crontab_list
|
||||
crontab /tmp/crontab_list
|
||||
|
||||
# Start cron
|
||||
cron
|
||||
|
||||
# Tail the log file
|
||||
tail -f /var/log/cron.log
|
||||
17
ServicesBank/RoutineEmail/pyproject.toml
Normal file
17
ServicesBank/RoutineEmail/pyproject.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[project]
|
||||
name = "routineemailservice"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"arrow>=1.3.0",
|
||||
"redbox>=0.2.1",
|
||||
"redis>=5.2.1",
|
||||
"pydantic-settings>=2.8.1",
|
||||
"sqlalchemy-mixins>=2.0.5",
|
||||
"fastapi>=0.115.11",
|
||||
"jinja2>=3.1.6",
|
||||
"psycopg2-binary>=2.9.10",
|
||||
"redmail>=0.6.0",
|
||||
]
|
||||
24
ServicesBank/RoutineEmail/run_app.sh
Normal file
24
ServicesBank/RoutineEmail/run_app.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Source the environment file directly
|
||||
. /env.sh
|
||||
|
||||
# Re-export all variables to ensure they're available to the Python script
|
||||
export EMAIL_HOST
|
||||
export EMAIL_USERNAME
|
||||
export EMAIL_PASSWORD
|
||||
export EMAIL_PORT
|
||||
export EMAIL_SEND
|
||||
export DB_HOST
|
||||
export DB_USER
|
||||
export DB_PASSWORD
|
||||
export DB_PORT
|
||||
export DB_NAME
|
||||
|
||||
# Python environment variables
|
||||
export PYTHONPATH
|
||||
export PYTHONUNBUFFERED
|
||||
export PYTHONDONTWRITEBYTECODE
|
||||
|
||||
env >> /var/log/cron.log
|
||||
/usr/local/bin/python /app.py
|
||||
0
ServicesBank/RoutineEmail/templates/a.txt
Normal file
0
ServicesBank/RoutineEmail/templates/a.txt
Normal file
Reference in New Issue
Block a user