Spaces:
Paused
Paused
| """ | |
| 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\"]", | |
| ) | |
| def uf_maiuscula(cls, v: str) -> str: | |
| return v.upper() | |
| 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 | |
| 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 | |