skimobiliaria / app /schemas.py
Carlexxx
xxx
8072ab3
Raw
History Blame Contribute Delete
6.13 kB
"""
Modelos Pydantic usados para validar entrada e formatar saída da API.
"""
import json
from datetime import datetime
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field, field_validator
class TipoNegocio(str, Enum):
venda = "Venda"
locacao = "Locação"
class TipoImovel(str, Enum):
casa = "Casa"
apartamento = "Apartamento"
terreno = "Terreno"
comercial = "Comercial"
sala_comercial = "Sala Comercial"
galpao = "Galpão"
chacara_sitio = "Chácara/Sítio"
cobertura = "Cobertura"
kitnet = "Kitnet/Studio"
class StatusAnuncio(str, Enum):
disponivel = "Disponível"
lancamento = "Lançamento"
vendido = "Vendido"
alugado = "Alugado"
reservado = "Reservado"
inativo = "Inativo"
class AnuncioBase(BaseModel):
tipo_negocio: TipoNegocio
tipo_imovel: TipoImovel
titulo: str = Field(..., min_length=3, max_length=150)
descricao: str = Field("", max_length=3000)
endereco: str = Field("", max_length=200)
bairro: str = Field("", max_length=100)
cidade: str = Field(..., min_length=2, max_length=100)
estado: str = Field(..., min_length=2, max_length=2, description="Sigla da UF, ex: PR")
cep: str = Field("", max_length=10)
preco: float = Field(..., ge=0)
condominio: Optional[float] = Field(0, ge=0)
iptu: Optional[float] = Field(0, ge=0)
area_util: Optional[float] = Field(0, ge=0)
area_total: Optional[float] = Field(0, ge=0)
quartos: Optional[int] = Field(0, ge=0)
suites: Optional[int] = Field(0, ge=0)
banheiros: Optional[int] = Field(0, ge=0)
vagas_garagem: Optional[int] = Field(0, ge=0)
status: StatusAnuncio = StatusAnuncio.disponivel
destaque: Optional[bool] = False
corretor_responsavel: str = Field("", max_length=100)
telefone_contato: str = Field("", max_length=30)
midias: Optional[str] = Field(
"[]",
description="JSON com a lista de URLs completas de imagens/vídeos, ex: [\"https://.../foto1.jpg\"]",
)
@field_validator("estado")
@classmethod
def uf_maiuscula(cls, v: str) -> str:
return v.upper()
@field_validator("midias", mode="before")
@classmethod
def midias_json_valido(cls, v) -> str:
"""Aceita string JSON de lista de URLs, lista Python direta, ou vazio -> normaliza sempre para JSON string."""
if v in (None, ""):
return "[]"
if isinstance(v, list):
return json.dumps([str(u) for u in v if u])
try:
parsed = json.loads(v)
except (json.JSONDecodeError, TypeError):
raise ValueError("midias deve ser uma lista JSON válida de URLs, ex: [\"https://.../foto1.jpg\"]")
if not isinstance(parsed, list):
raise ValueError("midias deve ser uma lista JSON de URLs")
return json.dumps([str(u) for u in parsed if u])
class AnuncioCreate(AnuncioBase):
pass
class AnuncioUpdate(BaseModel):
"""Todos os campos opcionais - usado no PATCH (atualização parcial)."""
tipo_negocio: Optional[TipoNegocio] = None
tipo_imovel: Optional[TipoImovel] = None
titulo: Optional[str] = Field(None, min_length=3, max_length=150)
descricao: Optional[str] = None
endereco: Optional[str] = None
bairro: Optional[str] = None
cidade: Optional[str] = None
estado: Optional[str] = Field(None, min_length=2, max_length=2)
cep: Optional[str] = None
preco: Optional[float] = Field(None, ge=0)
condominio: Optional[float] = Field(None, ge=0)
iptu: Optional[float] = Field(None, ge=0)
area_util: Optional[float] = Field(None, ge=0)
area_total: Optional[float] = Field(None, ge=0)
quartos: Optional[int] = Field(None, ge=0)
suites: Optional[int] = Field(None, ge=0)
banheiros: Optional[int] = Field(None, ge=0)
vagas_garagem: Optional[int] = Field(None, ge=0)
status: Optional[StatusAnuncio] = None
destaque: Optional[bool] = None
corretor_responsavel: Optional[str] = None
telefone_contato: Optional[str] = None
midias: Optional[str] = None
@field_validator("midias", mode="before")
@classmethod
def midias_json_valido(cls, v):
if v in (None, ""):
return v
if isinstance(v, list):
return json.dumps([str(u) for u in v if u])
try:
parsed = json.loads(v)
except (json.JSONDecodeError, TypeError):
raise ValueError("midias deve ser uma lista JSON válida de URLs")
if not isinstance(parsed, list):
raise ValueError("midias deve ser uma lista JSON de URLs")
return json.dumps([str(u) for u in parsed if u])
class AnuncioResponse(AnuncioBase):
id: int
data_cadastro: datetime
data_atualizacao: datetime
class OrdenacaoAnuncio(str, Enum):
mais_recentes = "mais_recentes"
mais_antigos = "mais_antigos"
preco_asc = "preco_asc"
preco_desc = "preco_desc"
area_asc = "area_asc"
area_desc = "area_desc"
class FacetaItem(BaseModel):
valor: str
quantidade: int
class FaixaValores(BaseModel):
min: float
max: float
class OpcoesFiltroResponse(BaseModel):
total_anuncios: int
tipos_negocio: list[FacetaItem]
tipos_imovel: list[FacetaItem]
status: list[FacetaItem]
cidades: list[FacetaItem]
bairros: list[FacetaItem]
faixa_preco: FaixaValores
faixa_area: FaixaValores
quartos_disponiveis: list[int]
vagas_disponiveis: list[int]
class FacetasBusca(BaseModel):
tipos_imovel: list[FacetaItem]
cidades: list[FacetaItem]
bairros: list[FacetaItem]
status: list[FacetaItem]
faixa_preco: FaixaValores
faixa_area: FaixaValores
class BuscaExploratoriaResponse(BaseModel):
total: int
skip: int
limit: int
ordenar_por: OrdenacaoAnuncio
resultados: list[AnuncioResponse]
facetas: FacetasBusca
class ResumoStats(BaseModel):
total_anuncios: int
total_venda: int
total_locacao: int
total_disponiveis: int
preco_medio_venda: float
preco_medio_locacao: float
por_tipo_imovel: dict
por_cidade: dict