Flusso valutazione
This commit is contained in:
@@ -0,0 +1,607 @@
|
||||
import math
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
||||
|
||||
from app.core.deps import get_db, get_current_user
|
||||
from app.models.user import User, UserRole
|
||||
from app.models.valuation import Valuation, ValuationComment, ValuationHistory, ValuationPhoto, ValuationStatus, ValuationPriority
|
||||
from app.models.vehicle import VehicleRegistry, VehicleVersion, PlateVersionCandidate, ApiUsageLog
|
||||
from app.models.motornet_valuation import MotornetValuation
|
||||
from app.schemas.valuation import (
|
||||
CommentCreate,
|
||||
CommentResponse,
|
||||
HistoryResponse,
|
||||
MotornetValuationResponse,
|
||||
PaginatedValuations,
|
||||
PhotoResponse,
|
||||
ValuationCreate,
|
||||
ValuationListItem,
|
||||
ValuationResponse,
|
||||
VehicleInfo,
|
||||
)
|
||||
from app.services import storage
|
||||
from app.integrations.motornet import valuate_vehicle, MotornetUnavailableError
|
||||
|
||||
import structlog
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
PRIORITY_RANK = {
|
||||
ValuationPriority.contratto: 1,
|
||||
ValuationPriority.preventivo: 2,
|
||||
ValuationPriority.valutazione: 3,
|
||||
}
|
||||
|
||||
SELLER_ROLES = {UserRole.venditore}
|
||||
EVALUATOR_ROLES = {UserRole.valutatore, UserRole.admin}
|
||||
ALL_ALLOWED = SELLER_ROLES | EVALUATOR_ROLES | {UserRole.backoffice}
|
||||
|
||||
|
||||
def _check_access(valuation: Valuation, user: User) -> None:
|
||||
if user.role in EVALUATOR_ROLES:
|
||||
return
|
||||
if user.role in {UserRole.backoffice}:
|
||||
return
|
||||
if valuation.user_id == user.id:
|
||||
return
|
||||
if user.group_id and valuation.group_id == user.group_id:
|
||||
return
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Accesso non consentito")
|
||||
|
||||
|
||||
async def _build_vehicle_info(plate: str, motornet_code: str | None, db: AsyncSession) -> VehicleInfo | None:
|
||||
result = await db.execute(select(VehicleRegistry).where(VehicleRegistry.plate == plate))
|
||||
reg = result.scalar_one_or_none()
|
||||
if not reg:
|
||||
return VehicleInfo(
|
||||
plate=plate,
|
||||
vin=None,
|
||||
vehicle_type=None,
|
||||
registration_date=None,
|
||||
version_label=None,
|
||||
brand_name=None,
|
||||
model_description=None,
|
||||
gamma_description=None,
|
||||
list_price=None,
|
||||
)
|
||||
|
||||
version_label = None
|
||||
brand_name = None
|
||||
model_description = None
|
||||
gamma_description = None
|
||||
list_price = None
|
||||
|
||||
code = motornet_code or reg.selected_motornet_code
|
||||
if code:
|
||||
vr = await db.execute(select(VehicleVersion).where(VehicleVersion.motornet_code == code))
|
||||
ver = vr.scalar_one_or_none()
|
||||
if ver:
|
||||
version_label = ver.version_label
|
||||
brand_name = ver.brand_name
|
||||
model_description = ver.model_description
|
||||
gamma_description = ver.gamma_description
|
||||
list_price = ver.list_price
|
||||
|
||||
return VehicleInfo(
|
||||
plate=reg.plate,
|
||||
vin=reg.vin,
|
||||
vehicle_type=reg.vehicle_type,
|
||||
registration_date=reg.registration_date,
|
||||
version_label=version_label,
|
||||
brand_name=brand_name,
|
||||
model_description=model_description,
|
||||
gamma_description=gamma_description,
|
||||
list_price=list_price,
|
||||
)
|
||||
|
||||
|
||||
async def _build_list_item(valuation: Valuation, db: AsyncSession) -> ValuationListItem:
|
||||
brand_name = None
|
||||
model_description = None
|
||||
version_label = None
|
||||
|
||||
code = valuation.motornet_code
|
||||
vr = await db.execute(select(VehicleRegistry).where(VehicleRegistry.plate == valuation.plate))
|
||||
reg = vr.scalar_one_or_none()
|
||||
if not code and reg:
|
||||
code = reg.selected_motornet_code
|
||||
|
||||
if code:
|
||||
vres = await db.execute(select(VehicleVersion).where(VehicleVersion.motornet_code == code))
|
||||
ver = vres.scalar_one_or_none()
|
||||
if ver:
|
||||
brand_name = ver.brand_name
|
||||
model_description = ver.model_description
|
||||
version_label = ver.version_label
|
||||
|
||||
return ValuationListItem(
|
||||
id=valuation.id,
|
||||
plate=valuation.plate,
|
||||
status=valuation.status.value,
|
||||
priority=valuation.priority.value,
|
||||
priority_rank=valuation.priority_rank,
|
||||
mileage=valuation.mileage,
|
||||
is_frozen=valuation.is_frozen,
|
||||
created_at=valuation.created_at,
|
||||
updated_at=valuation.updated_at,
|
||||
brand_name=brand_name,
|
||||
model_description=model_description,
|
||||
version_label=version_label,
|
||||
final_value=valuation.final_value,
|
||||
)
|
||||
|
||||
|
||||
async def _try_motornet_valuation(
|
||||
valuation: Valuation,
|
||||
db: AsyncSession,
|
||||
) -> MotornetValuation | None:
|
||||
motornet_code = valuation.motornet_code
|
||||
if not motornet_code:
|
||||
logger.info("motornet_valuation_skip_no_code", valuation_id=valuation.id)
|
||||
return None
|
||||
|
||||
reg_result = await db.execute(
|
||||
select(VehicleRegistry).where(VehicleRegistry.plate == valuation.plate)
|
||||
)
|
||||
reg = reg_result.scalar_one_or_none()
|
||||
registration_date = reg.registration_date if reg else None
|
||||
|
||||
if not registration_date:
|
||||
logger.info("motornet_valuation_skip_no_date", valuation_id=valuation.id, plate=valuation.plate)
|
||||
return None
|
||||
|
||||
anno = registration_date.year
|
||||
mese = registration_date.month
|
||||
|
||||
try:
|
||||
data = await valuate_vehicle(
|
||||
motornet_code=motornet_code,
|
||||
anno=anno,
|
||||
mese=mese,
|
||||
km=valuation.mileage,
|
||||
targa=valuation.plate,
|
||||
)
|
||||
except MotornetUnavailableError as exc:
|
||||
logger.warning("motornet_valuation_failed", valuation_id=valuation.id, error=str(exc))
|
||||
return None
|
||||
|
||||
val_data = data.get("valutazione") or {}
|
||||
valutazioni_disponibili: int | None = data.get("valutazioniDisponibili")
|
||||
|
||||
usage_log = ApiUsageLog(
|
||||
source="motornet_valuation",
|
||||
plate=valuation.plate,
|
||||
hit_cache=False,
|
||||
remaining_queries=valutazioni_disponibili,
|
||||
called_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(usage_log)
|
||||
|
||||
alimentazione_desc: str | None = None
|
||||
alim = val_data.get("alimentazione")
|
||||
if alim and isinstance(alim, dict):
|
||||
alimentazione_desc = alim.get("descrizione")
|
||||
|
||||
brand_name: str | None = None
|
||||
marca = val_data.get("marca")
|
||||
if marca and isinstance(marca, dict):
|
||||
brand_name = marca.get("nome")
|
||||
|
||||
def _dec(v) -> Decimal | None:
|
||||
if v is None:
|
||||
return None
|
||||
try:
|
||||
return Decimal(str(v))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
mn_val = MotornetValuation(
|
||||
valuation_id=valuation.id,
|
||||
plate=valuation.plate,
|
||||
motornet_code=motornet_code,
|
||||
fetched_at=datetime.now(timezone.utc),
|
||||
registration_year=anno,
|
||||
registration_month=mese,
|
||||
mileage=valuation.mileage,
|
||||
motornet_id=val_data.get("id"),
|
||||
ediz_dati=val_data.get("edizDati"),
|
||||
brand_name=brand_name,
|
||||
model_description=val_data.get("modello"),
|
||||
allestimento=val_data.get("allestimento"),
|
||||
alimentazione=alimentazione_desc,
|
||||
immagine_url=val_data.get("immagine"),
|
||||
xml_url=val_data.get("xml"),
|
||||
percorrenza_media_km=val_data.get("percorrenzaMediaKm"),
|
||||
quotazione_blu=_dec(val_data.get("quotazioneEurotaxBlu")),
|
||||
quotazione_blu_km=_dec(val_data.get("quotazioneEurotaxBluKm")),
|
||||
quotazione_giallo=_dec(val_data.get("quotazioneEurotaxGiallo")),
|
||||
quotazione_giallo_km=_dec(val_data.get("quotazioneEurotaxGialloKm")),
|
||||
quotazione_blu_totale=_dec(val_data.get("quotazioneEurotaxBluTotale")),
|
||||
quotazione_giallo_totale=_dec(val_data.get("quotazioneEurotaxGialloTotale")),
|
||||
variazione_km=_dec(val_data.get("variazioneKm")),
|
||||
prezzo_listino=_dec(val_data.get("prezzoListino")),
|
||||
prezzo_accessori=_dec(val_data.get("prezzoAccessori")),
|
||||
totale_riparazioni_carrozzeria=_dec(val_data.get("totaleRiparazioniCarrozzeria")),
|
||||
totale_riparazioni_meccanica=_dec(val_data.get("totaleRiparazioniMeccanica")),
|
||||
raw_response=data,
|
||||
)
|
||||
db.add(mn_val)
|
||||
await db.commit()
|
||||
await db.refresh(mn_val)
|
||||
return mn_val
|
||||
|
||||
|
||||
def _build_motornet_response(mn: MotornetValuation) -> MotornetValuationResponse:
|
||||
return MotornetValuationResponse(
|
||||
id=mn.id,
|
||||
valuation_id=mn.valuation_id,
|
||||
plate=mn.plate,
|
||||
motornet_code=mn.motornet_code,
|
||||
fetched_at=mn.fetched_at,
|
||||
registration_year=mn.registration_year,
|
||||
registration_month=mn.registration_month,
|
||||
mileage=mn.mileage,
|
||||
motornet_id=mn.motornet_id,
|
||||
ediz_dati=mn.ediz_dati,
|
||||
brand_name=mn.brand_name,
|
||||
model_description=mn.model_description,
|
||||
allestimento=mn.allestimento,
|
||||
alimentazione=mn.alimentazione,
|
||||
immagine_url=mn.immagine_url,
|
||||
xml_url=mn.xml_url,
|
||||
percorrenza_media_km=mn.percorrenza_media_km,
|
||||
quotazione_blu=mn.quotazione_blu,
|
||||
quotazione_blu_km=mn.quotazione_blu_km,
|
||||
quotazione_giallo=mn.quotazione_giallo,
|
||||
quotazione_giallo_km=mn.quotazione_giallo_km,
|
||||
quotazione_blu_totale=mn.quotazione_blu_totale,
|
||||
quotazione_giallo_totale=mn.quotazione_giallo_totale,
|
||||
variazione_km=mn.variazione_km,
|
||||
prezzo_listino=mn.prezzo_listino,
|
||||
prezzo_accessori=mn.prezzo_accessori,
|
||||
totale_riparazioni_carrozzeria=mn.totale_riparazioni_carrozzeria,
|
||||
totale_riparazioni_meccanica=mn.totale_riparazioni_meccanica,
|
||||
raw_response=mn.raw_response,
|
||||
)
|
||||
|
||||
|
||||
@router.post("", response_model=ValuationResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_valuation(
|
||||
body: ValuationCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
if current_user.role not in {UserRole.venditore, UserRole.valutatore, UserRole.admin}:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permessi insufficienti")
|
||||
|
||||
try:
|
||||
priority_enum = ValuationPriority(body.priority)
|
||||
except ValueError:
|
||||
priority_enum = ValuationPriority.valutazione
|
||||
|
||||
priority_rank = PRIORITY_RANK[priority_enum]
|
||||
|
||||
normalized_plate = body.plate.upper().strip()
|
||||
reg_stmt = pg_insert(VehicleRegistry).values(
|
||||
plate=normalized_plate,
|
||||
source="manual",
|
||||
fetched_at=datetime.now(timezone.utc),
|
||||
).on_conflict_do_nothing(index_elements=["plate"])
|
||||
await db.execute(reg_stmt)
|
||||
|
||||
valuation = Valuation(
|
||||
plate=body.plate.upper().strip(),
|
||||
user_id=current_user.id,
|
||||
group_id=current_user.group_id,
|
||||
motornet_code=body.motornet_code,
|
||||
mileage=body.mileage,
|
||||
retake_regime=body.retake_regime,
|
||||
expected_return_date=body.expected_return_date,
|
||||
interest_fuel=body.interest_fuel,
|
||||
priority=priority_enum,
|
||||
priority_rank=priority_rank,
|
||||
notes=body.notes,
|
||||
status=ValuationStatus.inviata,
|
||||
is_frozen=True,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
updated_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(valuation)
|
||||
await db.flush()
|
||||
|
||||
history_entry = ValuationHistory(
|
||||
valuation_id=valuation.id,
|
||||
changed_by=current_user.id,
|
||||
field_name="status",
|
||||
old_value=None,
|
||||
new_value=ValuationStatus.inviata.value,
|
||||
changed_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(history_entry)
|
||||
await db.commit()
|
||||
await db.refresh(valuation)
|
||||
|
||||
mn_record = await _try_motornet_valuation(valuation, db)
|
||||
|
||||
vehicle = await _build_vehicle_info(valuation.plate, valuation.motornet_code, db)
|
||||
|
||||
motornet_valuations = []
|
||||
if mn_record:
|
||||
motornet_valuations = [_build_motornet_response(mn_record)]
|
||||
|
||||
return ValuationResponse(
|
||||
id=valuation.id,
|
||||
plate=valuation.plate,
|
||||
user_id=valuation.user_id,
|
||||
group_id=valuation.group_id,
|
||||
motornet_code=valuation.motornet_code,
|
||||
mileage=valuation.mileage,
|
||||
retake_regime=valuation.retake_regime.value if valuation.retake_regime else None,
|
||||
expected_return_date=valuation.expected_return_date,
|
||||
interest_fuel=valuation.interest_fuel,
|
||||
priority=valuation.priority.value,
|
||||
notes=valuation.notes,
|
||||
status=valuation.status.value,
|
||||
final_value=valuation.final_value,
|
||||
evaluator_notes=valuation.evaluator_notes,
|
||||
is_frozen=valuation.is_frozen,
|
||||
priority_rank=valuation.priority_rank,
|
||||
created_at=valuation.created_at,
|
||||
updated_at=valuation.updated_at,
|
||||
vehicle=vehicle,
|
||||
photos=[],
|
||||
comments=[],
|
||||
history=[
|
||||
HistoryResponse(
|
||||
id=history_entry.id,
|
||||
field_name=history_entry.field_name,
|
||||
old_value=history_entry.old_value,
|
||||
new_value=history_entry.new_value,
|
||||
changed_by=history_entry.changed_by,
|
||||
changed_by_name=current_user.full_name,
|
||||
changed_at=history_entry.changed_at,
|
||||
)
|
||||
],
|
||||
motornet_valuations=motornet_valuations,
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=PaginatedValuations)
|
||||
async def list_valuations(
|
||||
plate: str | None = Query(None),
|
||||
status_filter: str | None = Query(None, alias="status"),
|
||||
priority: str | None = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
query = select(Valuation)
|
||||
|
||||
if current_user.role == UserRole.venditore:
|
||||
if current_user.group_id:
|
||||
query = query.where(Valuation.group_id == current_user.group_id)
|
||||
else:
|
||||
query = query.where(Valuation.user_id == current_user.id)
|
||||
|
||||
if plate:
|
||||
query = query.where(Valuation.plate.ilike(f"%{plate.upper()}%"))
|
||||
if status_filter:
|
||||
query = query.where(Valuation.status == status_filter)
|
||||
if priority:
|
||||
query = query.where(Valuation.priority == priority)
|
||||
|
||||
count_q = select(func.count()).select_from(query.subquery())
|
||||
total_result = await db.execute(count_q)
|
||||
total = total_result.scalar_one()
|
||||
|
||||
query = query.order_by(Valuation.created_at.desc())
|
||||
query = query.offset((page - 1) * page_size).limit(page_size)
|
||||
|
||||
result = await db.execute(query)
|
||||
valuations = result.scalars().all()
|
||||
|
||||
items = []
|
||||
for v in valuations:
|
||||
items.append(await _build_list_item(v, db))
|
||||
|
||||
return PaginatedValuations(
|
||||
items=items,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
pages=max(1, math.ceil(total / page_size)),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{valuation_id}", response_model=ValuationResponse)
|
||||
async def get_valuation(
|
||||
valuation_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(Valuation)
|
||||
.options(
|
||||
selectinload(Valuation.photos),
|
||||
selectinload(Valuation.comments),
|
||||
selectinload(Valuation.history),
|
||||
selectinload(Valuation.motornet_valuations),
|
||||
)
|
||||
.where(Valuation.id == valuation_id)
|
||||
)
|
||||
valuation = result.scalar_one_or_none()
|
||||
if not valuation:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Valutazione non trovata")
|
||||
|
||||
_check_access(valuation, current_user)
|
||||
|
||||
vehicle = await _build_vehicle_info(valuation.plate, valuation.motornet_code, db)
|
||||
|
||||
user_ids = {valuation.user_id}
|
||||
user_ids.update(c.user_id for c in valuation.comments)
|
||||
user_ids.update(h.changed_by for h in valuation.history)
|
||||
user_ids.update(p.uploaded_by for p in valuation.photos)
|
||||
|
||||
users_result = await db.execute(select(User).where(User.id.in_(user_ids)))
|
||||
users_map = {u.id: u.full_name for u in users_result.scalars().all()}
|
||||
|
||||
comments = []
|
||||
for c in sorted(valuation.comments, key=lambda x: x.created_at):
|
||||
if not c.visible_to_all and current_user.role not in EVALUATOR_ROLES:
|
||||
continue
|
||||
comments.append(
|
||||
CommentResponse(
|
||||
id=c.id,
|
||||
valuation_id=c.valuation_id,
|
||||
user_id=c.user_id,
|
||||
user_full_name=users_map.get(c.user_id),
|
||||
content=c.content,
|
||||
visible_to_all=c.visible_to_all,
|
||||
created_at=c.created_at,
|
||||
)
|
||||
)
|
||||
|
||||
photos = []
|
||||
for p in sorted(valuation.photos, key=lambda x: x.uploaded_at):
|
||||
url = storage.get_presigned_url(p.storage_path)
|
||||
photos.append(
|
||||
PhotoResponse(
|
||||
id=p.id,
|
||||
valuation_id=p.valuation_id,
|
||||
filename=p.filename,
|
||||
storage_path=p.storage_path,
|
||||
uploaded_by=p.uploaded_by,
|
||||
uploaded_at=p.uploaded_at,
|
||||
url=url,
|
||||
)
|
||||
)
|
||||
|
||||
history = []
|
||||
for h in sorted(valuation.history, key=lambda x: x.changed_at):
|
||||
history.append(
|
||||
HistoryResponse(
|
||||
id=h.id,
|
||||
field_name=h.field_name,
|
||||
old_value=h.old_value,
|
||||
new_value=h.new_value,
|
||||
changed_by=h.changed_by,
|
||||
changed_by_name=users_map.get(h.changed_by),
|
||||
changed_at=h.changed_at,
|
||||
)
|
||||
)
|
||||
|
||||
motornet_valuations = sorted(valuation.motornet_valuations, key=lambda x: x.fetched_at)
|
||||
motornet_responses = [_build_motornet_response(mn) for mn in motornet_valuations]
|
||||
|
||||
return ValuationResponse(
|
||||
id=valuation.id,
|
||||
plate=valuation.plate,
|
||||
user_id=valuation.user_id,
|
||||
group_id=valuation.group_id,
|
||||
motornet_code=valuation.motornet_code,
|
||||
mileage=valuation.mileage,
|
||||
retake_regime=valuation.retake_regime.value if valuation.retake_regime else None,
|
||||
expected_return_date=valuation.expected_return_date,
|
||||
interest_fuel=valuation.interest_fuel,
|
||||
priority=valuation.priority.value,
|
||||
notes=valuation.notes,
|
||||
status=valuation.status.value,
|
||||
final_value=valuation.final_value,
|
||||
evaluator_notes=valuation.evaluator_notes,
|
||||
is_frozen=valuation.is_frozen,
|
||||
priority_rank=valuation.priority_rank,
|
||||
created_at=valuation.created_at,
|
||||
updated_at=valuation.updated_at,
|
||||
vehicle=vehicle,
|
||||
photos=photos,
|
||||
comments=comments,
|
||||
history=history,
|
||||
motornet_valuations=motornet_responses,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{valuation_id}/photos", response_model=PhotoResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def upload_photo(
|
||||
valuation_id: int,
|
||||
file: Annotated[UploadFile, File(...)],
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(select(Valuation).where(Valuation.id == valuation_id))
|
||||
valuation = result.scalar_one_or_none()
|
||||
if not valuation:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Valutazione non trovata")
|
||||
|
||||
_check_access(valuation, current_user)
|
||||
|
||||
storage_path = await storage.upload_photo(file, valuation_id)
|
||||
filename = file.filename or "foto"
|
||||
|
||||
photo = ValuationPhoto(
|
||||
valuation_id=valuation_id,
|
||||
filename=filename,
|
||||
storage_path=storage_path,
|
||||
uploaded_by=current_user.id,
|
||||
uploaded_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(photo)
|
||||
await db.commit()
|
||||
await db.refresh(photo)
|
||||
|
||||
url = storage.get_presigned_url(storage_path)
|
||||
return PhotoResponse(
|
||||
id=photo.id,
|
||||
valuation_id=photo.valuation_id,
|
||||
filename=photo.filename,
|
||||
storage_path=photo.storage_path,
|
||||
uploaded_by=photo.uploaded_by,
|
||||
uploaded_at=photo.uploaded_at,
|
||||
url=url,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{valuation_id}/comments", response_model=CommentResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def add_comment(
|
||||
valuation_id: int,
|
||||
body: CommentCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(select(Valuation).where(Valuation.id == valuation_id))
|
||||
valuation = result.scalar_one_or_none()
|
||||
if not valuation:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Valutazione non trovata")
|
||||
|
||||
_check_access(valuation, current_user)
|
||||
|
||||
comment = ValuationComment(
|
||||
valuation_id=valuation_id,
|
||||
user_id=current_user.id,
|
||||
content=body.content,
|
||||
visible_to_all=body.visible_to_all,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(comment)
|
||||
await db.commit()
|
||||
await db.refresh(comment)
|
||||
|
||||
return CommentResponse(
|
||||
id=comment.id,
|
||||
valuation_id=comment.valuation_id,
|
||||
user_id=comment.user_id,
|
||||
user_full_name=current_user.full_name,
|
||||
content=comment.content,
|
||||
visible_to_all=comment.visible_to_all,
|
||||
created_at=comment.created_at,
|
||||
)
|
||||
Reference in New Issue
Block a user