From 5f67e55e24f2dedca451963d8f25a631b2e0a32a Mon Sep 17 00:00:00 2001 From: Azeez Muibi Date: Tue, 15 Apr 2025 17:19:07 +0100 Subject: [PATCH] update --- app/api/routes/routes.py | 11 +- app/api/services/__init__.py | 1 + app/api/services/dashboard_service.py | 115 +++++++++++++++++++ app/config.py | 2 +- app/swagger/digifi_swagger.json | 6 + app/swagger/paths/Dashboard.json | 33 ++++++ app/swagger/schemas/DashboardResponse.json | 126 +++++++++++++++++++++ 7 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 app/api/services/dashboard_service.py create mode 100644 app/swagger/paths/Dashboard.json create mode 100644 app/swagger/schemas/DashboardResponse.json diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py index 8298052..cd5b6d2 100644 --- a/app/api/routes/routes.py +++ b/app/api/routes/routes.py @@ -3,7 +3,8 @@ from app.api.services import ( AuthorizationService, TransactionService, LoanService, - AuthService + AuthService, + DashboardService ) from app.utils.logger import logger from app.api.middlewares import enforce_json, require_auth @@ -46,6 +47,14 @@ def login(): 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 @api.route("/transactions", methods=["GET"]) @jwt_required() diff --git a/app/api/services/__init__.py b/app/api/services/__init__.py index 70d8957..24c4a04 100644 --- a/app/api/services/__init__.py +++ b/app/api/services/__init__.py @@ -4,3 +4,4 @@ from app.api.services.authorization import AuthorizationService from app.api.services.transaction import TransactionService from app.api.services.loan import LoanService from app.api.services.auth_service import AuthService +from app.api.services.dashboard_service import DashboardService diff --git a/app/api/services/dashboard_service.py b/app/api/services/dashboard_service.py new file mode 100644 index 0000000..a728966 --- /dev/null +++ b/app/api/services/dashboard_service.py @@ -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 \ No newline at end of file diff --git a/app/config.py b/app/config.py index b0d43e4..d1f3065 100644 --- a/app/config.py +++ b/app/config.py @@ -26,7 +26,7 @@ class Config: 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", timedelta(days=30) ) diff --git a/app/swagger/digifi_swagger.json b/app/swagger/digifi_swagger.json index c973c48..3a4119b 100644 --- a/app/swagger/digifi_swagger.json +++ b/app/swagger/digifi_swagger.json @@ -61,6 +61,9 @@ "/login": { "$ref": "../swagger/paths/Login.json" }, + "/dashboard": { + "$ref": "../swagger/paths/Dashboard.json" + }, "/Authorize": { "$ref": "../swagger/paths/Authorize.json" }, @@ -97,6 +100,9 @@ "LoginResponse": { "$ref": "../swagger/schemas/LoginResponse.json" }, + "DashboardResponse": { + "$ref": "../swagger/schemas/DashboardResponse.json" + }, "LoansResponse": { "$ref": "../swagger/schemas/LoansResponse.json" }, diff --git a/app/swagger/paths/Dashboard.json b/app/swagger/paths/Dashboard.json new file mode 100644 index 0000000..beff334 --- /dev/null +++ b/app/swagger/paths/Dashboard.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" + } + } + } +} \ No newline at end of file diff --git a/app/swagger/schemas/DashboardResponse.json b/app/swagger/schemas/DashboardResponse.json new file mode 100644 index 0000000..771d306 --- /dev/null +++ b/app/swagger/schemas/DashboardResponse.json @@ -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" + } + } + } + } + } +} \ No newline at end of file -- 2.34.1