style: apply black and isort formatting across entire codebase
- Standardize quote style (single to double quotes) - Reorder and group imports alphabetically - Fix line breaks and indentation for consistency - Apply PEP 8 formatting standards Also updated Makefile to exclude both venv and .venv from code quality checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,25 +3,27 @@ Code Quality API Endpoints
|
||||
RESTful API for architecture validation and violation management
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_api
|
||||
from app.core.database import get_db
|
||||
from app.services.code_quality_service import code_quality_service
|
||||
from app.api.deps import get_current_admin_api
|
||||
from models.database.user import User
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# Pydantic Models for API
|
||||
|
||||
|
||||
class ScanResponse(BaseModel):
|
||||
"""Response model for a scan"""
|
||||
|
||||
id: int
|
||||
timestamp: str
|
||||
total_files: int
|
||||
@@ -38,6 +40,7 @@ class ScanResponse(BaseModel):
|
||||
|
||||
class ViolationResponse(BaseModel):
|
||||
"""Response model for a violation"""
|
||||
|
||||
id: int
|
||||
scan_id: int
|
||||
rule_id: str
|
||||
@@ -61,6 +64,7 @@ class ViolationResponse(BaseModel):
|
||||
|
||||
class ViolationListResponse(BaseModel):
|
||||
"""Response model for paginated violations list"""
|
||||
|
||||
violations: list[ViolationResponse]
|
||||
total: int
|
||||
page: int
|
||||
@@ -70,34 +74,42 @@ class ViolationListResponse(BaseModel):
|
||||
|
||||
class ViolationDetailResponse(ViolationResponse):
|
||||
"""Response model for single violation with relationships"""
|
||||
|
||||
assignments: list = []
|
||||
comments: list = []
|
||||
|
||||
|
||||
class AssignViolationRequest(BaseModel):
|
||||
"""Request model for assigning a violation"""
|
||||
|
||||
user_id: int = Field(..., description="User ID to assign to")
|
||||
due_date: Optional[datetime] = Field(None, description="Due date for resolution")
|
||||
priority: str = Field("medium", description="Priority level (low, medium, high, critical)")
|
||||
priority: str = Field(
|
||||
"medium", description="Priority level (low, medium, high, critical)"
|
||||
)
|
||||
|
||||
|
||||
class ResolveViolationRequest(BaseModel):
|
||||
"""Request model for resolving a violation"""
|
||||
|
||||
resolution_note: str = Field(..., description="Note about the resolution")
|
||||
|
||||
|
||||
class IgnoreViolationRequest(BaseModel):
|
||||
"""Request model for ignoring a violation"""
|
||||
|
||||
reason: str = Field(..., description="Reason for ignoring")
|
||||
|
||||
|
||||
class AddCommentRequest(BaseModel):
|
||||
"""Request model for adding a comment"""
|
||||
|
||||
comment: str = Field(..., min_length=1, description="Comment text")
|
||||
|
||||
|
||||
class DashboardStatsResponse(BaseModel):
|
||||
"""Response model for dashboard statistics"""
|
||||
|
||||
total_violations: int
|
||||
errors: int
|
||||
warnings: int
|
||||
@@ -116,10 +128,10 @@ class DashboardStatsResponse(BaseModel):
|
||||
|
||||
# API Endpoints
|
||||
|
||||
|
||||
@router.post("/scan", response_model=ScanResponse)
|
||||
async def trigger_scan(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_api)
|
||||
):
|
||||
"""
|
||||
Trigger a new architecture scan
|
||||
@@ -127,7 +139,9 @@ async def trigger_scan(
|
||||
Requires authentication. Runs the validator script and stores results.
|
||||
"""
|
||||
try:
|
||||
scan = code_quality_service.run_scan(db, triggered_by=f"manual:{current_user.username}")
|
||||
scan = code_quality_service.run_scan(
|
||||
db, triggered_by=f"manual:{current_user.username}"
|
||||
)
|
||||
|
||||
return ScanResponse(
|
||||
id=scan.id,
|
||||
@@ -138,7 +152,7 @@ async def trigger_scan(
|
||||
warnings=scan.warnings,
|
||||
duration_seconds=scan.duration_seconds,
|
||||
triggered_by=scan.triggered_by,
|
||||
git_commit_hash=scan.git_commit_hash
|
||||
git_commit_hash=scan.git_commit_hash,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Scan failed: {str(e)}")
|
||||
@@ -148,7 +162,7 @@ async def trigger_scan(
|
||||
async def list_scans(
|
||||
limit: int = Query(30, ge=1, le=100, description="Number of scans to return"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Get scan history
|
||||
@@ -167,7 +181,7 @@ async def list_scans(
|
||||
warnings=scan.warnings,
|
||||
duration_seconds=scan.duration_seconds,
|
||||
triggered_by=scan.triggered_by,
|
||||
git_commit_hash=scan.git_commit_hash
|
||||
git_commit_hash=scan.git_commit_hash,
|
||||
)
|
||||
for scan in scans
|
||||
]
|
||||
@@ -175,15 +189,23 @@ async def list_scans(
|
||||
|
||||
@router.get("/violations", response_model=ViolationListResponse)
|
||||
async def list_violations(
|
||||
scan_id: Optional[int] = Query(None, description="Filter by scan ID (defaults to latest)"),
|
||||
severity: Optional[str] = Query(None, description="Filter by severity (error, warning)"),
|
||||
status: Optional[str] = Query(None, description="Filter by status (open, assigned, resolved, ignored)"),
|
||||
scan_id: Optional[int] = Query(
|
||||
None, description="Filter by scan ID (defaults to latest)"
|
||||
),
|
||||
severity: Optional[str] = Query(
|
||||
None, description="Filter by severity (error, warning)"
|
||||
),
|
||||
status: Optional[str] = Query(
|
||||
None, description="Filter by status (open, assigned, resolved, ignored)"
|
||||
),
|
||||
rule_id: Optional[str] = Query(None, description="Filter by rule ID"),
|
||||
file_path: Optional[str] = Query(None, description="Filter by file path (partial match)"),
|
||||
file_path: Optional[str] = Query(
|
||||
None, description="Filter by file path (partial match)"
|
||||
),
|
||||
page: int = Query(1, ge=1, description="Page number"),
|
||||
page_size: int = Query(50, ge=1, le=200, description="Items per page"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Get violations with filtering and pagination
|
||||
@@ -200,7 +222,7 @@ async def list_violations(
|
||||
rule_id=rule_id,
|
||||
file_path=file_path,
|
||||
limit=page_size,
|
||||
offset=offset
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
total_pages = (total + page_size - 1) // page_size
|
||||
@@ -223,14 +245,14 @@ async def list_violations(
|
||||
resolved_at=v.resolved_at.isoformat() if v.resolved_at else None,
|
||||
resolved_by=v.resolved_by,
|
||||
resolution_note=v.resolution_note,
|
||||
created_at=v.created_at.isoformat()
|
||||
created_at=v.created_at.isoformat(),
|
||||
)
|
||||
for v in violations
|
||||
],
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
total_pages=total_pages
|
||||
total_pages=total_pages,
|
||||
)
|
||||
|
||||
|
||||
@@ -238,7 +260,7 @@ async def list_violations(
|
||||
async def get_violation(
|
||||
violation_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Get single violation with details
|
||||
@@ -253,12 +275,12 @@ async def get_violation(
|
||||
# Format assignments
|
||||
assignments = [
|
||||
{
|
||||
'id': a.id,
|
||||
'user_id': a.user_id,
|
||||
'assigned_at': a.assigned_at.isoformat(),
|
||||
'assigned_by': a.assigned_by,
|
||||
'due_date': a.due_date.isoformat() if a.due_date else None,
|
||||
'priority': a.priority
|
||||
"id": a.id,
|
||||
"user_id": a.user_id,
|
||||
"assigned_at": a.assigned_at.isoformat(),
|
||||
"assigned_by": a.assigned_by,
|
||||
"due_date": a.due_date.isoformat() if a.due_date else None,
|
||||
"priority": a.priority,
|
||||
}
|
||||
for a in violation.assignments
|
||||
]
|
||||
@@ -266,10 +288,10 @@ async def get_violation(
|
||||
# Format comments
|
||||
comments = [
|
||||
{
|
||||
'id': c.id,
|
||||
'user_id': c.user_id,
|
||||
'comment': c.comment,
|
||||
'created_at': c.created_at.isoformat()
|
||||
"id": c.id,
|
||||
"user_id": c.user_id,
|
||||
"comment": c.comment,
|
||||
"created_at": c.created_at.isoformat(),
|
||||
}
|
||||
for c in violation.comments
|
||||
]
|
||||
@@ -287,12 +309,14 @@ async def get_violation(
|
||||
suggestion=violation.suggestion,
|
||||
status=violation.status,
|
||||
assigned_to=violation.assigned_to,
|
||||
resolved_at=violation.resolved_at.isoformat() if violation.resolved_at else None,
|
||||
resolved_at=(
|
||||
violation.resolved_at.isoformat() if violation.resolved_at else None
|
||||
),
|
||||
resolved_by=violation.resolved_by,
|
||||
resolution_note=violation.resolution_note,
|
||||
created_at=violation.created_at.isoformat(),
|
||||
assignments=assignments,
|
||||
comments=comments
|
||||
comments=comments,
|
||||
)
|
||||
|
||||
|
||||
@@ -301,7 +325,7 @@ async def assign_violation(
|
||||
violation_id: int,
|
||||
request: AssignViolationRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Assign violation to a developer
|
||||
@@ -315,17 +339,19 @@ async def assign_violation(
|
||||
user_id=request.user_id,
|
||||
assigned_by=current_user.id,
|
||||
due_date=request.due_date,
|
||||
priority=request.priority
|
||||
priority=request.priority,
|
||||
)
|
||||
|
||||
return {
|
||||
'id': assignment.id,
|
||||
'violation_id': assignment.violation_id,
|
||||
'user_id': assignment.user_id,
|
||||
'assigned_at': assignment.assigned_at.isoformat(),
|
||||
'assigned_by': assignment.assigned_by,
|
||||
'due_date': assignment.due_date.isoformat() if assignment.due_date else None,
|
||||
'priority': assignment.priority
|
||||
"id": assignment.id,
|
||||
"violation_id": assignment.violation_id,
|
||||
"user_id": assignment.user_id,
|
||||
"assigned_at": assignment.assigned_at.isoformat(),
|
||||
"assigned_by": assignment.assigned_by,
|
||||
"due_date": (
|
||||
assignment.due_date.isoformat() if assignment.due_date else None
|
||||
),
|
||||
"priority": assignment.priority,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
@@ -336,7 +362,7 @@ async def resolve_violation(
|
||||
violation_id: int,
|
||||
request: ResolveViolationRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Mark violation as resolved
|
||||
@@ -348,15 +374,17 @@ async def resolve_violation(
|
||||
db,
|
||||
violation_id=violation_id,
|
||||
resolved_by=current_user.id,
|
||||
resolution_note=request.resolution_note
|
||||
resolution_note=request.resolution_note,
|
||||
)
|
||||
|
||||
return {
|
||||
'id': violation.id,
|
||||
'status': violation.status,
|
||||
'resolved_at': violation.resolved_at.isoformat() if violation.resolved_at else None,
|
||||
'resolved_by': violation.resolved_by,
|
||||
'resolution_note': violation.resolution_note
|
||||
"id": violation.id,
|
||||
"status": violation.status,
|
||||
"resolved_at": (
|
||||
violation.resolved_at.isoformat() if violation.resolved_at else None
|
||||
),
|
||||
"resolved_by": violation.resolved_by,
|
||||
"resolution_note": violation.resolution_note,
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
@@ -369,7 +397,7 @@ async def ignore_violation(
|
||||
violation_id: int,
|
||||
request: IgnoreViolationRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Mark violation as ignored (won't fix)
|
||||
@@ -381,15 +409,17 @@ async def ignore_violation(
|
||||
db,
|
||||
violation_id=violation_id,
|
||||
ignored_by=current_user.id,
|
||||
reason=request.reason
|
||||
reason=request.reason,
|
||||
)
|
||||
|
||||
return {
|
||||
'id': violation.id,
|
||||
'status': violation.status,
|
||||
'resolved_at': violation.resolved_at.isoformat() if violation.resolved_at else None,
|
||||
'resolved_by': violation.resolved_by,
|
||||
'resolution_note': violation.resolution_note
|
||||
"id": violation.id,
|
||||
"status": violation.status,
|
||||
"resolved_at": (
|
||||
violation.resolved_at.isoformat() if violation.resolved_at else None
|
||||
),
|
||||
"resolved_by": violation.resolved_by,
|
||||
"resolution_note": violation.resolution_note,
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
@@ -402,7 +432,7 @@ async def add_comment(
|
||||
violation_id: int,
|
||||
request: AddCommentRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
current_user: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""
|
||||
Add comment to violation
|
||||
@@ -414,15 +444,15 @@ async def add_comment(
|
||||
db,
|
||||
violation_id=violation_id,
|
||||
user_id=current_user.id,
|
||||
comment=request.comment
|
||||
comment=request.comment,
|
||||
)
|
||||
|
||||
return {
|
||||
'id': comment.id,
|
||||
'violation_id': comment.violation_id,
|
||||
'user_id': comment.user_id,
|
||||
'comment': comment.comment,
|
||||
'created_at': comment.created_at.isoformat()
|
||||
"id": comment.id,
|
||||
"violation_id": comment.violation_id,
|
||||
"user_id": comment.user_id,
|
||||
"comment": comment.comment,
|
||||
"created_at": comment.created_at.isoformat(),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
@@ -430,8 +460,7 @@ async def add_comment(
|
||||
|
||||
@router.get("/stats", response_model=DashboardStatsResponse)
|
||||
async def get_dashboard_stats(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_admin_api)
|
||||
db: Session = Depends(get_db), current_user: User = Depends(get_current_admin_api)
|
||||
):
|
||||
"""
|
||||
Get dashboard statistics
|
||||
|
||||
Reference in New Issue
Block a user