Refactor client API endpoints for DCA dashboard: Update endpoint structure to focus on client-specific functionalities, including dashboard summary, transaction history, and analytics. Enhance code readability with improved formatting and add support for exporting transaction data in CSV format.
This commit is contained in:
parent
edb0b4d05e
commit
32e8f31b82
2 changed files with 131 additions and 65 deletions
14
__init__.py
14
__init__.py
|
|
@ -15,7 +15,9 @@ logger.debug(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
satmachineclient_ext: APIRouter = APIRouter(prefix="/satmachineclient", tags=["DCA Client"])
|
satmachineclient_ext: APIRouter = APIRouter(
|
||||||
|
prefix="/satmachineclient", tags=["DCA Client"]
|
||||||
|
)
|
||||||
satmachineclient_ext.include_router(satmachineclient_generic_router)
|
satmachineclient_ext.include_router(satmachineclient_generic_router)
|
||||||
satmachineclient_ext.include_router(satmachineclient_api_router)
|
satmachineclient_ext.include_router(satmachineclient_api_router)
|
||||||
|
|
||||||
|
|
@ -39,11 +41,15 @@ def satmachineclient_stop():
|
||||||
|
|
||||||
def satmachineclient_start():
|
def satmachineclient_start():
|
||||||
# Start invoice listener task
|
# Start invoice listener task
|
||||||
invoice_task = create_permanent_unique_task("ext_satmachineclient", wait_for_paid_invoices)
|
invoice_task = create_permanent_unique_task(
|
||||||
|
"ext_satmachineclient", wait_for_paid_invoices
|
||||||
|
)
|
||||||
scheduled_tasks.append(invoice_task)
|
scheduled_tasks.append(invoice_task)
|
||||||
|
|
||||||
# Start hourly transaction polling task
|
# Start hourly transaction polling task
|
||||||
polling_task = create_permanent_unique_task("ext_satmachineclient_polling", hourly_transaction_polling)
|
polling_task = create_permanent_unique_task(
|
||||||
|
"ext_satmachineclient_polling", hourly_transaction_polling
|
||||||
|
)
|
||||||
scheduled_tasks.append(polling_task)
|
scheduled_tasks.append(polling_task)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
182
views_api.py
182
views_api.py
|
|
@ -1,87 +1,147 @@
|
||||||
# Description: This file contains the extensions API endpoints.
|
# Description: Client-focused API endpoints for DCA dashboard
|
||||||
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import Optional
|
from typing import List, Optional
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Request
|
from fastapi import APIRouter, Depends, Query
|
||||||
from lnbits.core.crud import get_user
|
|
||||||
from lnbits.core.models import WalletTypeInfo
|
from lnbits.core.models import WalletTypeInfo
|
||||||
from lnbits.core.services import create_invoice
|
from lnbits.decorators import require_invoice_key
|
||||||
from lnbits.decorators import require_admin_key
|
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
from .crud import (
|
from .crud import (
|
||||||
# DCA CRUD operations
|
get_client_dashboard_summary,
|
||||||
create_dca_client,
|
get_client_transactions,
|
||||||
get_dca_client,
|
get_client_analytics,
|
||||||
get_all_deposits,
|
update_client_dca_settings,
|
||||||
get_deposit,
|
get_client_by_user_id,
|
||||||
get_client_balance_summary,
|
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
# DCA models
|
ClientDashboardSummary,
|
||||||
CreateDcaClientData,
|
ClientTransaction,
|
||||||
DcaClient,
|
ClientAnalytics,
|
||||||
DcaDeposit,
|
UpdateClientSettings,
|
||||||
ClientBalanceSummary,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
satmachineclient_api_router = APIRouter()
|
satmachineclient_api_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
################ DCA API ENDPOINTS ################
|
############## CLIENT DASHBOARD API ###############
|
||||||
###################################################
|
###################################################
|
||||||
|
|
||||||
# DCA Client Endpoints
|
@satmachineclient_api_router.get("/api/v1/dashboard/summary")
|
||||||
# Note: Client creation/update
|
async def api_get_dashboard_summary(
|
||||||
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
|
) -> ClientDashboardSummary:
|
||||||
|
"""Get client dashboard summary metrics"""
|
||||||
|
summary = await get_client_dashboard_summary(wallet.user)
|
||||||
|
if not summary:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
|
detail="Client data not found"
|
||||||
|
)
|
||||||
|
return summary
|
||||||
|
|
||||||
|
|
||||||
@satmachineclient_api_router.post("/api/v1/dca/clients", status_code=HTTPStatus.CREATED)
|
@satmachineclient_api_router.get("/api/v1/dashboard/transactions")
|
||||||
async def api_create_test_dca_client(
|
async def api_get_client_transactions(
|
||||||
data: CreateDcaClientData,
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
limit: int = Query(50, ge=1, le=1000),
|
||||||
) -> DcaClient:
|
offset: int = Query(0, ge=0),
|
||||||
return await create_dca_client(data)
|
transaction_type: Optional[str] = Query(None),
|
||||||
|
start_date: Optional[datetime] = Query(None),
|
||||||
|
end_date: Optional[datetime] = Query(None),
|
||||||
|
) -> List[ClientTransaction]:
|
||||||
|
"""Get client's DCA transaction history with filtering"""
|
||||||
|
return await get_client_transactions(
|
||||||
|
wallet.user,
|
||||||
|
limit=limit,
|
||||||
|
offset=offset,
|
||||||
|
transaction_type=transaction_type,
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@satmachineclient_api_router.get("/api/v1/dca/clients/{client_id}/balance")
|
@satmachineclient_api_router.get("/api/v1/dashboard/analytics")
|
||||||
async def api_get_client_balance(
|
async def api_get_client_analytics(
|
||||||
client_id: str,
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
time_range: str = Query("30d", regex="^(7d|30d|90d|1y|all)$"),
|
||||||
) -> ClientBalanceSummary:
|
) -> ClientAnalytics:
|
||||||
"""Get client balance summary"""
|
"""Get client performance analytics and cost basis data"""
|
||||||
client = await get_dca_client(client_id)
|
analytics = await get_client_analytics(wallet.user, time_range)
|
||||||
|
if not analytics:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
|
detail="Analytics data not available"
|
||||||
|
)
|
||||||
|
return analytics
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineclient_api_router.put("/api/v1/dashboard/settings")
|
||||||
|
async def api_update_client_settings(
|
||||||
|
settings: UpdateClientSettings,
|
||||||
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
|
) -> dict:
|
||||||
|
"""Update client DCA settings (mode, limits, status)"""
|
||||||
|
client = await get_client_by_user_id(wallet.user)
|
||||||
if not client:
|
if not client:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="DCA client not found."
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
|
detail="Client profile not found"
|
||||||
)
|
)
|
||||||
|
|
||||||
return await get_client_balance_summary(client_id)
|
success = await update_client_dca_settings(client.id, settings)
|
||||||
|
if not success:
|
||||||
|
|
||||||
# DCA Deposit Endpoints
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: to Claude - modify this so it only gets the deposits for the user! important security
|
|
||||||
@satmachineclient_api_router.get("/api/v1/dca/deposits")
|
|
||||||
async def api_get_deposits(
|
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
|
||||||
) -> list[DcaDeposit]:
|
|
||||||
"""Get all deposits"""
|
|
||||||
return await get_all_deposits()
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: does the client have any need to get sepcific deposits?
|
|
||||||
@satmachineclient_api_router.get("/api/v1/dca/deposits/{deposit_id}")
|
|
||||||
async def api_get_deposit(
|
|
||||||
deposit_id: str,
|
|
||||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
|
||||||
) -> DcaDeposit:
|
|
||||||
"""Get a specific deposit"""
|
|
||||||
deposit = await get_deposit(deposit_id)
|
|
||||||
if not deposit:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.NOT_FOUND, detail="Deposit not found."
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail="Failed to update settings"
|
||||||
)
|
)
|
||||||
return deposit
|
|
||||||
|
return {"message": "Settings updated successfully"}
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineclient_api_router.get("/api/v1/dashboard/export/transactions")
|
||||||
|
async def api_export_transactions(
|
||||||
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
|
format: str = Query("csv", regex="^(csv|json)$"),
|
||||||
|
start_date: Optional[datetime] = Query(None),
|
||||||
|
end_date: Optional[datetime] = Query(None),
|
||||||
|
):
|
||||||
|
"""Export client transaction history"""
|
||||||
|
transactions = await get_client_transactions(
|
||||||
|
wallet.user,
|
||||||
|
limit=10000, # Large limit for export
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date
|
||||||
|
)
|
||||||
|
|
||||||
|
if format == "csv":
|
||||||
|
# Return CSV response
|
||||||
|
from io import StringIO
|
||||||
|
import csv
|
||||||
|
|
||||||
|
output = StringIO()
|
||||||
|
writer = csv.writer(output)
|
||||||
|
writer.writerow(['Date', 'Amount (Sats)', 'Amount (Fiat)', 'Exchange Rate', 'Type', 'Status'])
|
||||||
|
|
||||||
|
for tx in transactions:
|
||||||
|
writer.writerow([
|
||||||
|
tx.created_at.isoformat(),
|
||||||
|
tx.amount_sats,
|
||||||
|
tx.amount_fiat / 100, # Convert centavos to full currency
|
||||||
|
tx.exchange_rate,
|
||||||
|
tx.transaction_type,
|
||||||
|
tx.status
|
||||||
|
])
|
||||||
|
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
output.seek(0)
|
||||||
|
return StreamingResponse(
|
||||||
|
iter([output.getvalue()]),
|
||||||
|
media_type="text/csv",
|
||||||
|
headers={"Content-Disposition": "attachment; filename=dca_transactions.csv"}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return {"transactions": transactions}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue