Spaces:
Sleeping
Sleeping
File size: 6,500 Bytes
456b2e2 d4444b8 456b2e2 367d57f 456b2e2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
"""
Export API - Data Export Endpoints
Endpoints for exporting entity data to CSV/Excel.
Available entities: tickets, customers, subscriptions, field_agents, sales_orders
Authorization:
- platform_admin, client_admin, contractor_admin: Full access
- project_manager, sales_manager, dispatcher: Project-scoped access
- field_agent, sales_agent: No access
"""
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from typing import List
import logging
from datetime import datetime
from app.api.deps import get_db, get_current_user
from app.models.user import User
from app.schemas.export import (
ExportRequest,
AvailableColumnsResponse,
ColumnMetadata
)
from app.services.export_service import ExportService
from app.config.export_columns import get_available_columns
router = APIRouter()
logger = logging.getLogger(__name__)
# ============================================
# GET AVAILABLE COLUMNS
# ============================================
@router.get("/{entity}/columns", response_model=AvailableColumnsResponse)
async def get_available_export_columns(
entity: str,
current_user: User = Depends(get_current_user)
):
"""
Get list of available columns for export.
Returns column metadata including:
- key: Column identifier for API requests
- label: Human-readable header
- requires_join: Whether column needs database join (affects performance)
Available entities:
- tickets: Work orders/tickets
- customers: Customer records
- subscriptions: Active service subscriptions
- field_agents: Field agent users
- sales_orders: Sales orders
Authorization: project_manager+, dispatcher+, admins
"""
# Check authorization
if not ExportService.can_user_export(current_user):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You do not have permission to export data"
)
# Get columns for entity
columns_data = get_available_columns(entity)
if not columns_data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Entity '{entity}' not found or has no exportable columns"
)
# Convert to schema
columns = [ColumnMetadata(**col_data) for col_data in columns_data]
return AvailableColumnsResponse(
entity=entity,
columns=columns
)
# ============================================
# EXPORT DATA
# ============================================
@router.post("/{entity}/export")
async def export_entity_data(
entity: str,
request: ExportRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
Export entity data to CSV/Excel.
Request body:
- columns: List of column keys to export (get from /columns endpoint)
- filters: Optional filters to apply (project_id, status, etc.)
- format: Export format ('csv' or 'excel')
Response: File download
Limits:
- Max file size: 1MB
- If file exceeds limit, reduce columns or add filters
Example filters:
- tickets: project_id, status, priority, ticket_type, region_id
- customers: client_id, is_active, region_id
- subscriptions: project_id, status, service_type, region_id
- field_agents: contractor_id, is_active, status
- sales_orders: project_id, status, is_processed, region_id
Authorization: project_manager+, dispatcher+, admins
"""
try:
# Ensure entity in request matches path parameter
if request.entity != entity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Entity mismatch: path='{entity}', body='{request.entity}'"
)
# Call export service
file_buffer = ExportService.export_entity(
db=db,
entity=request.entity,
columns=request.columns,
filters=request.filters,
format=request.format,
current_user=current_user,
calculated_fields=request.calculated_fields,
aggregations=request.aggregations,
group_by=request.group_by
)
# Prepare filename
timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
filename = f"{entity}_export_{timestamp}.{request.format}"
# Determine media type
if request.format == 'csv':
media_type = 'text/csv'
elif request.format == 'excel':
media_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
else:
media_type = 'application/octet-stream'
# Return file as streaming response
return StreamingResponse(
file_buffer,
media_type=media_type,
headers={
'Content-Disposition': f'attachment; filename="{filename}"',
'Content-Length': str(file_buffer.getbuffer().nbytes)
}
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Export failed: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Export failed: {str(e)}"
)
# ============================================
# EXPORT PRESETS (Future Feature)
# ============================================
@router.get("/{entity}/presets")
async def get_export_presets(
entity: str,
current_user: User = Depends(get_current_user)
):
"""
Get saved export presets for entity.
Presets are pre-configured column selections for common export scenarios.
Example presets for tickets:
- basic: id, ticket_name, status, priority, customer_name
- detailed: All fields
- analytics: status, priority, created_at, completed_at, sla_violated
Future feature: Currently returns empty list.
Authorization: project_manager+, dispatcher+, admins
"""
# Check authorization
if not ExportService.can_user_export(current_user):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You do not have permission to export data"
)
# TODO: Implement saved presets (future feature)
return {
"entity": entity,
"presets": []
}
|