services api

This commit is contained in:
2025-07-31 17:26:30 +03:00
parent 479104a04f
commit 1f8db23f75
56 changed files with 1976 additions and 120 deletions

View 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"]

View 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.

View 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)

View 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

View 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",
]

View 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