Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1e9e39fe5 | |||
| b412313fc5 | |||
| cba6eac501 | |||
| 443c6262b7 | |||
| 2ba97cee4a | |||
| 8c714cb45c | |||
| c93e1a8bdd | |||
| 6d8fe24718 | |||
| deaddd8132 |
@@ -12,6 +12,7 @@ DATABASE_PASSWORD=FirstAdvance!
|
||||
DATABASE_HOST=dev-data.simbrellang.net
|
||||
DATABASE_PORT=10532
|
||||
DATABASE_NAME=firstadvancedev
|
||||
#SQLALCHEMY_DATABASE_URI_FULL="oracle+oracledb://FIRSTADVSTG:Pchanged_56789@10.2.110.30:1521/?service_name=firstadv"
|
||||
|
||||
# DATABASE_HOST=10.20.30.60
|
||||
# DATABASE_USER=firstadvance
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
from flask import Flask
|
||||
from app.extensions import mail
|
||||
from app.utils.mail import send_report_email, get_report_data
|
||||
from app.config import settings
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(settings)
|
||||
|
||||
mail.init_app(app)
|
||||
|
||||
with app.app_context():
|
||||
report_data = get_report_data()
|
||||
recipients = ["vdagbue@gmail.com"]
|
||||
result = send_report_email(report_data, recipients)
|
||||
print(result)
|
||||
+5
-1
@@ -1,4 +1,5 @@
|
||||
from flask import Flask
|
||||
from flask_mail import Mail
|
||||
import os
|
||||
from flask_swagger_ui import get_swaggerui_blueprint
|
||||
from flask_cors import CORS
|
||||
@@ -7,7 +8,7 @@ from app.api.routes import api
|
||||
from app.errors import register_error_handlers
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from app.extensions import db, migrate
|
||||
from app.extensions import db, migrate, mail
|
||||
from flask_jwt_extended import (
|
||||
JWTManager,
|
||||
jwt_required,
|
||||
@@ -47,6 +48,9 @@ def create_app():
|
||||
|
||||
from . import models
|
||||
|
||||
# Initialize Flask-Mail
|
||||
mail.init_app(app)
|
||||
|
||||
# Database and Migrations
|
||||
db.init_app(app)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from flask import jsonify
|
||||
from marshmallow import ValidationError
|
||||
import logging
|
||||
from app.api.integrations import KafkaIntegration
|
||||
from app.utils.mail import send_report_email, get_report_data
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -173,3 +174,6 @@ class BaseService:
|
||||
# return {"rate": 0, "fee": 0, "due_days": 0}
|
||||
|
||||
|
||||
@classmethod
|
||||
def send_mail(cls, report_data, recipients):
|
||||
send_report_email(report_data, recipients)
|
||||
|
||||
@@ -87,13 +87,10 @@ class OfferAnalysis:
|
||||
|
||||
|
||||
if failed_true_rules or failed_false_rules or not salaries:
|
||||
if failed_true_rules:
|
||||
logger.warning(f"RAC analysis failed: TRUE rules failed: {failed_true_rules}")
|
||||
if failed_false_rules:
|
||||
logger.warning(f"RAC analysis failed: FALSE rules failed: {failed_false_rules}")
|
||||
if not salaries:
|
||||
logger.warning("RAC analysis failed: No salary records found in RAC response.")
|
||||
raise ValueError("RAC analysis failed")
|
||||
logger.warning(f"Failed TRUE rules: {failed_true_rules}")
|
||||
logger.warning(f"Failed FALSE rules: {failed_false_rules}")
|
||||
logger.warning("No salary records found in RAC response.")
|
||||
raise ValueError(f"RAC analysis failed")
|
||||
|
||||
|
||||
|
||||
|
||||
+13
-2
@@ -24,7 +24,10 @@ class Config:
|
||||
# Database Connection
|
||||
# SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DATABASE_USER}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}"
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = (f"oracle+oracledb://{DATABASE_USER}:{DATABASE_PASSWORD}@{DNS}")
|
||||
SQLALCHEMY_DATABASE_URI_INTERNAL = (f"oracle+oracledb://{DATABASE_USER}:{DATABASE_PASSWORD}@{DNS}")
|
||||
SQLALCHEMY_DATABASE_URI = os.getenv("SQLALCHEMY_DATABASE_URI_FULL", SQLALCHEMY_DATABASE_URI_INTERNAL)
|
||||
|
||||
#SQLALCHEMY_DATABASE_URI_FULL = 'oracle+oracledb://FIRSTADVSTG:Pchanged_56789@10.2.110.30:1521/?service_name=firstadv'
|
||||
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
@@ -85,7 +88,15 @@ class Config:
|
||||
"salarypaymenT_6"
|
||||
]
|
||||
|
||||
|
||||
MAIL_SERVER = os.getenv('MAIL_SERVER','smtp.zoho.com')
|
||||
MAIL_PORT = 587
|
||||
MAIL_USERNAME = os.getenv('MAIL_USERNAME', 'firstadvance@dynamikservices.tech')
|
||||
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
|
||||
MAIL_USE_TLS = True
|
||||
MAIL_USE_SSL = False
|
||||
MAIL_DEFAULT_SENDER = ('FirstAdvance', 'firstadvance@dynamikservices.tech')
|
||||
MAIL_RECEIVER= os.getenv('MAIL_RECEIVER', 'vdagbue@gmail.com')
|
||||
|
||||
|
||||
|
||||
settings = Config()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_mail import Mail
|
||||
|
||||
mail = Mail()
|
||||
db = SQLAlchemy()
|
||||
migrate = Migrate()
|
||||
@@ -17,6 +17,11 @@ class LoanRepaymentSchedule(db.Model):
|
||||
total_repayment_amount = db.Column(db.Float, default=0.0)
|
||||
paid = db.Column(db.Boolean, default=False)
|
||||
paid_at = db.Column(db.DateTime, nullable=True)
|
||||
due_process_date = db.Column(db.DateTime, nullable=True)
|
||||
due_process_count = db.Column(db.Integer, default=0)
|
||||
paid_status = db.Column(db.String(20), nullable=True)
|
||||
repay_description = db.Column(db.String(255), nullable=True)
|
||||
partial_balance = db.Column(db.Float, default=0.0)
|
||||
|
||||
created_at = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = db.Column(db.DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from flask_mail import Message
|
||||
from flask import current_app
|
||||
from app.extensions import mail
|
||||
import pandas as pd
|
||||
from io import BytesIO
|
||||
|
||||
def get_report_data():
|
||||
"""
|
||||
Fetch and return loan summary data.
|
||||
"""
|
||||
return [
|
||||
{"Type": "Disbursement", "Count": 45},
|
||||
{"Type": "Repayment", "Count": 32},
|
||||
]
|
||||
def send_report_email(report_data: list, recipients: list):
|
||||
"""
|
||||
Sends an HTML + Excel report to the given email recipients.
|
||||
"""
|
||||
df = pd.DataFrame(report_data)
|
||||
output = BytesIO()
|
||||
df.to_excel(output, index=False)
|
||||
output.seek(0)
|
||||
|
||||
html_table = df.to_html(index=False, border=1)
|
||||
|
||||
msg = Message(
|
||||
subject="Loan Report Summary",
|
||||
recipients=recipients,
|
||||
html=f"<h3>Loan Report Summary</h3>{html_table}",
|
||||
)
|
||||
msg.attach(
|
||||
"loan_report.xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
output.read()
|
||||
)
|
||||
|
||||
with current_app.app_context():
|
||||
mail.send(msg)
|
||||
|
||||
return "Report email sent"
|
||||
@@ -0,0 +1,45 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 30b45df851fa
|
||||
Revises: d59bfb9ead82
|
||||
Create Date: 2025-08-26 13:48:27.458593
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '30b45df851fa'
|
||||
down_revision = 'd59bfb9ead82'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column(
|
||||
'loan_repayment_schedules',
|
||||
sa.Column('paid_status', sa.String(length=20), nullable=True),
|
||||
)
|
||||
|
||||
op.add_column(
|
||||
'loan_repayment_schedules',
|
||||
sa.Column('repay_description', sa.String(length=255), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
op.add_column(
|
||||
'loan_repayment_schedules',
|
||||
sa.Column('partial_balance', sa.Float(), nullable=True),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('loan_repayment_schedules', 'partial_balance')
|
||||
op.drop_column('loan_repayment_schedules', 'repay_description')
|
||||
op.drop_column('loan_repayment_schedules', 'paid_status')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,38 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: d59bfb9ead82
|
||||
Revises: 05b5494ad406
|
||||
Create Date: 2025-08-21 14:22:19.220158
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd59bfb9ead82'
|
||||
down_revision = '05b5494ad406'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
op.add_column(
|
||||
'loan_repayment_schedules',
|
||||
sa.Column('due_process_date', sa.DateTime(), nullable=True)
|
||||
)
|
||||
|
||||
# Add due_process_count column
|
||||
op.add_column(
|
||||
'loan_repayment_schedules',
|
||||
sa.Column('due_process_count', sa.Integer(), nullable=True)
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('loan_repayment_schedules', 'due_process_date')
|
||||
op.drop_column('loan_repayment_schedules', 'due_process_count')
|
||||
# ### end Alembic commands ###
|
||||
@@ -40,3 +40,6 @@ confluent-kafka==1.9.2
|
||||
|
||||
python-dateutil
|
||||
|
||||
Flask-Mail==0.10.0
|
||||
pandas==2.1.3
|
||||
openpyxl==3.1.5
|
||||
Reference in New Issue
Block a user