update
This commit was merged in pull request #3.
This commit is contained in:
@@ -3,7 +3,8 @@ from app.api.services import (
|
|||||||
AuthorizationService,
|
AuthorizationService,
|
||||||
TransactionService,
|
TransactionService,
|
||||||
LoanService,
|
LoanService,
|
||||||
AuthService
|
AuthService,
|
||||||
|
DashboardService
|
||||||
)
|
)
|
||||||
from app.utils.logger import logger
|
from app.utils.logger import logger
|
||||||
from app.api.middlewares import enforce_json, require_auth
|
from app.api.middlewares import enforce_json, require_auth
|
||||||
@@ -46,6 +47,14 @@ def login():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# Dashboard endpoint
|
||||||
|
@api.route("/dashboard", methods=["GET"])
|
||||||
|
@jwt_required()
|
||||||
|
def get_dashboard():
|
||||||
|
response = DashboardService.get_dashboard_data()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# Get All Transactions Endpoint
|
# Get All Transactions Endpoint
|
||||||
@api.route("/transactions", methods=["GET"])
|
@api.route("/transactions", methods=["GET"])
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ from app.api.services.authorization import AuthorizationService
|
|||||||
from app.api.services.transaction import TransactionService
|
from app.api.services.transaction import TransactionService
|
||||||
from app.api.services.loan import LoanService
|
from app.api.services.loan import LoanService
|
||||||
from app.api.services.auth_service import AuthService
|
from app.api.services.auth_service import AuthService
|
||||||
|
from app.api.services.dashboard_service import DashboardService
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
from flask import jsonify
|
||||||
|
from app.utils.logger import logger
|
||||||
|
from app.api.services.base_service import BaseService
|
||||||
|
from app.models.transaction import Transaction
|
||||||
|
from app.models.loan import Loan
|
||||||
|
from sqlalchemy import func, desc
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from app.extensions import db
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardService(BaseService):
|
||||||
|
@staticmethod
|
||||||
|
def get_dashboard_data():
|
||||||
|
"""
|
||||||
|
Get dashboard summary data.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A standardized response with dashboard data.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get current date and start of the week
|
||||||
|
now = datetime.now()
|
||||||
|
start_of_week = now - timedelta(days=now.weekday())
|
||||||
|
start_of_week = start_of_week.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
# Get loans data for the current week
|
||||||
|
loans_this_week = db.session.query(
|
||||||
|
func.sum(Loan.initial_loan_amount)
|
||||||
|
).filter(
|
||||||
|
Loan.created_at >= start_of_week
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
# Get payments data for the current week
|
||||||
|
# Assuming payments are transactions with type 'PAYMENT'
|
||||||
|
payments_this_week = db.session.query(
|
||||||
|
func.count(Transaction.id)
|
||||||
|
).filter(
|
||||||
|
Transaction.created_at >= start_of_week,
|
||||||
|
Transaction.type == 'PAYMENT'
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
# Get request summary counts
|
||||||
|
# These are placeholders - needed to adjust based on your actual data model
|
||||||
|
eligibility_check_count = db.session.query(
|
||||||
|
func.count(Transaction.id)
|
||||||
|
).filter(
|
||||||
|
Transaction.type == 'ELIGIBILITY_CHECK'
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
select_offer_count = db.session.query(
|
||||||
|
func.count(Transaction.id)
|
||||||
|
).filter(
|
||||||
|
Transaction.type == 'SELECT_OFFER'
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
provide_loan_count = db.session.query(
|
||||||
|
func.count(Transaction.id)
|
||||||
|
).filter(
|
||||||
|
Transaction.type == 'PROVIDE_LOAN'
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
repayment_count = db.session.query(
|
||||||
|
func.count(Transaction.id)
|
||||||
|
).filter(
|
||||||
|
Transaction.type == 'REPAYMENT'
|
||||||
|
).scalar() or 0
|
||||||
|
|
||||||
|
# Get recent transactions
|
||||||
|
recent_transactions = Transaction.query.order_by(
|
||||||
|
Transaction.id.desc()
|
||||||
|
).limit(15).all()
|
||||||
|
|
||||||
|
# Format recent transactions
|
||||||
|
recent_transactions_data = []
|
||||||
|
for transaction in recent_transactions:
|
||||||
|
recent_transactions_data.append({
|
||||||
|
'id': transaction.id,
|
||||||
|
'transaction_id': transaction.transaction_id,
|
||||||
|
'account_id': transaction.account_id,
|
||||||
|
'type': transaction.type,
|
||||||
|
'channel': transaction.channel,
|
||||||
|
'created_at': transaction.created_at.isoformat() if transaction.created_at else None,
|
||||||
|
'updated_at': transaction.updated_at.isoformat() if transaction.updated_at else None
|
||||||
|
})
|
||||||
|
|
||||||
|
# Prepare response data
|
||||||
|
dashboard_data = {
|
||||||
|
"loans": {
|
||||||
|
"value": float(loans_this_week),
|
||||||
|
"currency": "Naira",
|
||||||
|
"currency_text": "₦",
|
||||||
|
"text": "this week"
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"value": payments_this_week,
|
||||||
|
"currency": "Naira",
|
||||||
|
"currency_text": "₦",
|
||||||
|
"text": "this week"
|
||||||
|
},
|
||||||
|
"request_summary": {
|
||||||
|
"eligibility_check": {"Eligibility": eligibility_check_count},
|
||||||
|
"select_offer": {"Offers": select_offer_count},
|
||||||
|
"provide_loan": {"Loans": provide_loan_count},
|
||||||
|
"repayment": {"Repayments": repayment_count}
|
||||||
|
},
|
||||||
|
"recent_transactions": recent_transactions_data
|
||||||
|
}
|
||||||
|
|
||||||
|
return dashboard_data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"An error occurred while getting dashboard data: {str(e)}", exc_info=True)
|
||||||
|
return jsonify({
|
||||||
|
"message": "Internal Server Error"
|
||||||
|
}), 500
|
||||||
+1
-1
@@ -26,7 +26,7 @@ class Config:
|
|||||||
|
|
||||||
|
|
||||||
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "secret-key")
|
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "secret-key")
|
||||||
JWT_ACCESS_TOKEN_EXPIRES = os.getenv("JWT_ACCESS_TOKEN_EXPIRES", timedelta(hours=1))
|
JWT_ACCESS_TOKEN_EXPIRES = os.getenv("JWT_ACCESS_TOKEN_EXPIRES", timedelta(minutes=15)) # 15 minutes expiration
|
||||||
JWT_REFRESH_TOKEN_EXPIRES = os.getenv(
|
JWT_REFRESH_TOKEN_EXPIRES = os.getenv(
|
||||||
"JWT_REFRESH_TOKEN_EXPIRES", timedelta(days=30)
|
"JWT_REFRESH_TOKEN_EXPIRES", timedelta(days=30)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,6 +61,9 @@
|
|||||||
"/login": {
|
"/login": {
|
||||||
"$ref": "../swagger/paths/Login.json"
|
"$ref": "../swagger/paths/Login.json"
|
||||||
},
|
},
|
||||||
|
"/dashboard": {
|
||||||
|
"$ref": "../swagger/paths/Dashboard.json"
|
||||||
|
},
|
||||||
"/Authorize": {
|
"/Authorize": {
|
||||||
"$ref": "../swagger/paths/Authorize.json"
|
"$ref": "../swagger/paths/Authorize.json"
|
||||||
},
|
},
|
||||||
@@ -97,6 +100,9 @@
|
|||||||
"LoginResponse": {
|
"LoginResponse": {
|
||||||
"$ref": "../swagger/schemas/LoginResponse.json"
|
"$ref": "../swagger/schemas/LoginResponse.json"
|
||||||
},
|
},
|
||||||
|
"DashboardResponse": {
|
||||||
|
"$ref": "../swagger/schemas/DashboardResponse.json"
|
||||||
|
},
|
||||||
"LoansResponse": {
|
"LoansResponse": {
|
||||||
"$ref": "../swagger/schemas/LoansResponse.json"
|
"$ref": "../swagger/schemas/LoansResponse.json"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Dashboard"
|
||||||
|
],
|
||||||
|
"summary": "Get dashboard data",
|
||||||
|
"description": "Retrieve summary data for the dashboard including loans, payments, request summary, and recent transactions",
|
||||||
|
"operationId": "getDashboard",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../schemas/DashboardResponse.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal server error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loans": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"value": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 50000.0
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Naira"
|
||||||
|
},
|
||||||
|
"currency_text": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "₦"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "this week"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"value": {
|
||||||
|
"type": "number",
|
||||||
|
"example": 25
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Naira"
|
||||||
|
},
|
||||||
|
"currency_text": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "₦"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "this week"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request_summary": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"eligibility_check": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Eligibility": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"select_offer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Offers": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 85
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"provide_loan": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Loans": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repayment": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Repayments": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recent_transactions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"transaction_id": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "TRX123456"
|
||||||
|
},
|
||||||
|
"account_id": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "ACC456"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "PAYMENT"
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "MOBILE"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"example": "2023-01-15T10:30:00Z"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"example": "2023-01-15T10:30:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user