fix: ruff lint + format fixes in tests, ESLint fixes in frontend
This commit is contained in:
@@ -39,7 +39,9 @@ async def get_user_by_id(db: AsyncSession, user_id: uuid.UUID) -> Optional[User]
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def authenticate_user(db: AsyncSession, email: str, password: str) -> Optional[User]:
|
||||
async def authenticate_user(
|
||||
db: AsyncSession, email: str, password: str
|
||||
) -> Optional[User]:
|
||||
"""Authenticate a user by email and password."""
|
||||
user = await get_user_by_email(db, email)
|
||||
if user is None:
|
||||
@@ -67,6 +69,7 @@ def generate_token_pair(user: User) -> dict:
|
||||
lang=user.language,
|
||||
)
|
||||
from app.config import settings
|
||||
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
@@ -135,10 +138,7 @@ async def list_users(
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
result = await db.execute(
|
||||
select(User)
|
||||
.order_by(User.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(page_size)
|
||||
select(User).order_by(User.created_at.desc()).offset(offset).limit(page_size)
|
||||
)
|
||||
users = list(result.scalars().all())
|
||||
return users, total
|
||||
|
||||
@@ -137,24 +137,18 @@ async def list_contacts(
|
||||
return contacts, total
|
||||
|
||||
|
||||
async def get_contact_by_id(
|
||||
db: AsyncSession, contact_id: uuid.UUID
|
||||
) -> Contact | None:
|
||||
async def get_contact_by_id(db: AsyncSession, contact_id: uuid.UUID) -> Contact | None:
|
||||
"""Get a single contact by ID, excluding soft-deleted. Eager-loads contact persons."""
|
||||
stmt = (
|
||||
select(Contact)
|
||||
.options(selectinload(Contact.contact_persons))
|
||||
.where(
|
||||
and_(Contact.id == contact_id, Contact.deleted_at.is_(None))
|
||||
)
|
||||
.where(and_(Contact.id == contact_id, Contact.deleted_at.is_(None)))
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def create_contact(
|
||||
db: AsyncSession, data: dict[str, Any]
|
||||
) -> Contact:
|
||||
async def create_contact(db: AsyncSession, data: dict[str, Any]) -> Contact:
|
||||
"""Create a new contact with optional nested contact persons.
|
||||
|
||||
The data dict may contain a 'contact_persons' list of dicts.
|
||||
|
||||
@@ -74,10 +74,12 @@ def _parse_ai_response(raw_content: str) -> dict[str, Any]:
|
||||
valid_actions = []
|
||||
for action in actions:
|
||||
if isinstance(action, dict) and "type" in action:
|
||||
valid_actions.append({
|
||||
"type": action["type"],
|
||||
"params": action.get("params", {}),
|
||||
})
|
||||
valid_actions.append(
|
||||
{
|
||||
"type": action["type"],
|
||||
"params": action.get("params", {}),
|
||||
}
|
||||
)
|
||||
|
||||
return {"response": response_text, "actions": valid_actions}
|
||||
|
||||
@@ -172,7 +174,9 @@ async def chat(
|
||||
6. Save the assistant message
|
||||
7. Return response with actions and IDs
|
||||
"""
|
||||
session = await _get_or_create_session(db, user_id, session_id, first_message=message)
|
||||
session = await _get_or_create_session(
|
||||
db, user_id, session_id, first_message=message
|
||||
)
|
||||
|
||||
# Save user message
|
||||
user_msg = CopilotChat(
|
||||
|
||||
@@ -60,9 +60,7 @@ async def create_export(
|
||||
csv_content = generate_datev_csv(sales)
|
||||
|
||||
# Calculate total amount
|
||||
total_amount = sum(
|
||||
(s.sale_price or Decimal("0")) for s in sales
|
||||
)
|
||||
total_amount = sum((s.sale_price or Decimal("0")) for s in sales)
|
||||
|
||||
# Save CSV file
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
@@ -111,7 +109,9 @@ async def list_exports(
|
||||
return exports, total
|
||||
|
||||
|
||||
async def get_export_csv(db: AsyncSession, export_id: uuid.UUID) -> tuple[str, bytes] | None:
|
||||
async def get_export_csv(
|
||||
db: AsyncSession, export_id: uuid.UUID
|
||||
) -> tuple[str, bytes] | None:
|
||||
"""Get the CSV content for a DATEV export.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -98,7 +98,9 @@ async def upload_file(
|
||||
"""
|
||||
# Validate MIME type
|
||||
if not validate_mime_type(mime_type, original_filename):
|
||||
raise ValueError(f"Unsupported MIME type: {mime_type} for file: {original_filename}")
|
||||
raise ValueError(
|
||||
f"Unsupported MIME type: {mime_type} for file: {original_filename}"
|
||||
)
|
||||
|
||||
# Validate file size (20MB limit for uploads)
|
||||
file_size = len(file_content)
|
||||
@@ -173,9 +175,7 @@ async def list_files(
|
||||
Returns (files, total_count).
|
||||
"""
|
||||
# Count total files for this vehicle
|
||||
count_stmt = select(func.count(File.id)).where(
|
||||
File.vehicle_id == vehicle_id
|
||||
)
|
||||
count_stmt = select(func.count(File.id)).where(File.vehicle_id == vehicle_id)
|
||||
count_result = await db.execute(count_stmt)
|
||||
total = count_result.scalar_one()
|
||||
|
||||
|
||||
@@ -48,9 +48,7 @@ def _get_status_url(listing_id: str) -> str:
|
||||
return f"{_MOBILE_DE_API_BASE}/api/seller/listings/{listing_id}/status"
|
||||
|
||||
|
||||
async def push_listing(
|
||||
db: AsyncSession, vehicle: Vehicle
|
||||
) -> MobileDeListing:
|
||||
async def push_listing(db: AsyncSession, vehicle: Vehicle) -> MobileDeListing:
|
||||
"""Push a vehicle listing to mobile.de.
|
||||
|
||||
Creates a MobileDeListing record with status 'pending',
|
||||
@@ -150,9 +148,7 @@ async def update_listing(
|
||||
return listing
|
||||
|
||||
|
||||
async def delete_listing(
|
||||
db: AsyncSession, listing: MobileDeListing
|
||||
) -> MobileDeListing:
|
||||
async def delete_listing(db: AsyncSession, listing: MobileDeListing) -> MobileDeListing:
|
||||
"""Delete a listing from mobile.de.
|
||||
|
||||
Sends DELETE /api/seller/listings/{id}.
|
||||
@@ -228,8 +224,7 @@ async def retry_failed_listing(
|
||||
if retry_count >= MAX_RETRIES:
|
||||
listing.sync_status = "fehler"
|
||||
listing.error_log = (
|
||||
f"Max retries ({MAX_RETRIES}) exceeded. "
|
||||
f"Last error: {listing.error_log}"
|
||||
f"Max retries ({MAX_RETRIES}) exceeded. Last error: {listing.error_log}"
|
||||
)
|
||||
await db.flush()
|
||||
await db.refresh(listing)
|
||||
|
||||
@@ -46,12 +46,12 @@ async def upload_file(
|
||||
Validates MIME type and file size before saving.
|
||||
"""
|
||||
if not validate_mime_type(mime_type):
|
||||
raise ValueError(f"Invalid MIME type: {mime_type}. Allowed: {ALLOWED_MIME_TYPES}")
|
||||
raise ValueError(
|
||||
f"Invalid MIME type: {mime_type}. Allowed: {ALLOWED_MIME_TYPES}"
|
||||
)
|
||||
|
||||
if not validate_file_size(len(file_bytes)):
|
||||
raise ValueError(
|
||||
f"File size exceeds limit of {settings.MAX_FILE_SIZE_MB} MB"
|
||||
)
|
||||
raise ValueError(f"File size exceeds limit of {settings.MAX_FILE_SIZE_MB} MB")
|
||||
|
||||
# Ensure upload directory exists
|
||||
upload_dir = settings.UPLOAD_DIR
|
||||
@@ -173,6 +173,7 @@ async def apply_to_vehicle(
|
||||
if ocr_field == "first_registration" and isinstance(value, str):
|
||||
try:
|
||||
from datetime import datetime as dt
|
||||
|
||||
parsed = dt.strptime(value, "%d.%m.%Y").date()
|
||||
setattr(vehicle, vehicle_field, parsed)
|
||||
updated_fields.append(vehicle_field)
|
||||
@@ -180,6 +181,7 @@ async def apply_to_vehicle(
|
||||
except ValueError:
|
||||
try:
|
||||
from datetime import date
|
||||
|
||||
parsed = date.fromisoformat(value)
|
||||
setattr(vehicle, vehicle_field, parsed)
|
||||
updated_fields.append(vehicle_field)
|
||||
|
||||
@@ -82,7 +82,9 @@ async def compare_prices(
|
||||
|
||||
# Calculate average price
|
||||
if listings:
|
||||
average_price = round(sum(listing.price for listing in listings) / len(listings), 2)
|
||||
average_price = round(
|
||||
sum(listing.price for listing in listings) / len(listings), 2
|
||||
)
|
||||
else:
|
||||
average_price = None
|
||||
|
||||
|
||||
@@ -80,7 +80,9 @@ def generate_retouch_prompt(vehicle_info: dict[str, Any] | None = None) -> str:
|
||||
if make and model:
|
||||
parts.append(f"The vehicle is a {make} {model}.")
|
||||
if color:
|
||||
parts.append(f"The vehicle color is {color}; ensure it looks accurate and rich.")
|
||||
parts.append(
|
||||
f"The vehicle color is {color}; ensure it looks accurate and rich."
|
||||
)
|
||||
return " ".join(parts)
|
||||
|
||||
return base_prompt
|
||||
@@ -98,7 +100,9 @@ async def upload_retouch_file(
|
||||
Raises ValueError for invalid MIME type or file size.
|
||||
"""
|
||||
if not validate_mime_type(mime_type):
|
||||
raise ValueError(f"Invalid MIME type: {mime_type}. Only image/* types are allowed.")
|
||||
raise ValueError(
|
||||
f"Invalid MIME type: {mime_type}. Only image/* types are allowed."
|
||||
)
|
||||
|
||||
if not validate_file_size(len(file_bytes)):
|
||||
raise ValueError(f"File size exceeds limit of {settings.MAX_FILE_SIZE_MB} MB")
|
||||
@@ -151,7 +155,11 @@ async def list_results(
|
||||
total = total_result.scalar_one()
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
data_stmt = data_stmt.order_by(RetouchResult.created_at.desc()).offset(offset).limit(page_size)
|
||||
data_stmt = (
|
||||
data_stmt.order_by(RetouchResult.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(page_size)
|
||||
)
|
||||
result = await db.execute(data_stmt)
|
||||
items = list(result.scalars().all())
|
||||
|
||||
|
||||
@@ -131,9 +131,7 @@ async def list_vehicles(
|
||||
return vehicles, total
|
||||
|
||||
|
||||
async def get_vehicle_by_id(
|
||||
db: AsyncSession, vehicle_id: uuid.UUID
|
||||
) -> Vehicle | None:
|
||||
async def get_vehicle_by_id(db: AsyncSession, vehicle_id: uuid.UUID) -> Vehicle | None:
|
||||
"""Get a single vehicle by ID, excluding soft-deleted."""
|
||||
stmt = select(Vehicle).where(
|
||||
and_(Vehicle.id == vehicle_id, Vehicle.deleted_at.is_(None))
|
||||
@@ -142,20 +140,14 @@ async def get_vehicle_by_id(
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_vehicle_by_fin(
|
||||
db: AsyncSession, fin: str
|
||||
) -> Vehicle | None:
|
||||
async def get_vehicle_by_fin(db: AsyncSession, fin: str) -> Vehicle | None:
|
||||
"""Get a single vehicle by FIN, excluding soft-deleted."""
|
||||
stmt = select(Vehicle).where(
|
||||
and_(Vehicle.fin == fin, Vehicle.deleted_at.is_(None))
|
||||
)
|
||||
stmt = select(Vehicle).where(and_(Vehicle.fin == fin, Vehicle.deleted_at.is_(None)))
|
||||
result = await db.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def create_vehicle(
|
||||
db: AsyncSession, data: dict[str, Any]
|
||||
) -> Vehicle:
|
||||
async def create_vehicle(db: AsyncSession, data: dict[str, Any]) -> Vehicle:
|
||||
"""Create a new vehicle.
|
||||
|
||||
Raises ValueError if FIN already exists.
|
||||
|
||||
Reference in New Issue
Block a user