Format everything
This commit is contained in:
parent
ed6c49a1b7
commit
ecfef9a09d
@ -1,9 +1,23 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class HouseCreateRequest(BaseModel):
|
||||
address: str = Field(..., min_length=1, max_length=255, description="House address", examples=["123 Main St"])
|
||||
city: str = Field(..., description="City where the house is located", examples=["Springfield"])
|
||||
country: str = Field(..., description="Country where the house is located", examples=["USA"])
|
||||
price: float = Field(..., description="Price of the house", examples=[250000.00])
|
||||
description: str = Field(..., description="Description of the house", examples=["A beautiful 3-bedroom house"])
|
||||
|
||||
class HouseCreateRequest(BaseModel):
|
||||
address: str = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
max_length=255,
|
||||
description="House address",
|
||||
examples=["123 Main St"],
|
||||
)
|
||||
city: str = Field(
|
||||
..., description="City where the house is located", examples=["Springfield"]
|
||||
)
|
||||
country: str = Field(
|
||||
..., description="Country where the house is located", examples=["USA"]
|
||||
)
|
||||
price: float = Field(..., description="Price of the house", examples=[250000.00])
|
||||
description: str = Field(
|
||||
...,
|
||||
description="Description of the house",
|
||||
examples=["A beautiful 3-bedroom house"],
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class HouseCreateResponse(BaseModel):
|
||||
id: str
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class HouseResponse(BaseModel):
|
||||
id: str
|
||||
description: str
|
||||
@ -8,5 +9,6 @@ class HouseResponse(BaseModel):
|
||||
country: str
|
||||
price: float
|
||||
|
||||
|
||||
class HousesListResponse(BaseModel):
|
||||
houses: list[HouseResponse]
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class OwnerDetailResponse(BaseModel):
|
||||
id: str
|
||||
user_id: str
|
||||
email: str
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class OwnerResponse(BaseModel):
|
||||
id: str
|
||||
user_id: str
|
||||
|
||||
|
||||
class OwnerListResponse(BaseModel):
|
||||
owners: list[OwnerResponse]
|
||||
owners: list[OwnerResponse]
|
||||
|
||||
@ -6,4 +6,4 @@ class BaseError(HTTPException):
|
||||
|
||||
def __init__(self, detail: str):
|
||||
self.detail = detail
|
||||
super().__init__(status_code=self.status_code, detail=self.detail)
|
||||
super().__init__(status_code=self.status_code, detail=self.detail)
|
||||
|
||||
@ -7,4 +7,4 @@ class NotAuthenticatedError(BaseError):
|
||||
status_code: int = status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("Not authenticated")
|
||||
super().__init__("Not authenticated")
|
||||
|
||||
@ -8,11 +8,13 @@ from .middleware.authenticate import authenticate
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_app: FastAPI):
|
||||
create_db_and_tables()
|
||||
yield
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Fair Housing API",
|
||||
description="Provides access to core functionality for the fair housing platform.",
|
||||
|
||||
@ -3,7 +3,10 @@ from typing import Annotated
|
||||
from ..settings import get_settings
|
||||
from ..repositories.user_repository import UserRepository
|
||||
|
||||
async def authenticate(request: Request, user_repository: Annotated[UserRepository, Depends()]) -> bool:
|
||||
|
||||
async def authenticate(
|
||||
request: Request, user_repository: Annotated[UserRepository, Depends()]
|
||||
) -> bool:
|
||||
"""
|
||||
Authenticate the current request.
|
||||
"""
|
||||
@ -11,6 +14,6 @@ async def authenticate(request: Request, user_repository: Annotated[UserReposito
|
||||
|
||||
if not mocked_user:
|
||||
raise Exception("Mock user not found.")
|
||||
|
||||
|
||||
request.state.user = mocked_user
|
||||
return True
|
||||
return True
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from sqlmodel import SQLModel, Field
|
||||
from uuid import uuid4, UUID
|
||||
|
||||
|
||||
class House(SQLModel, table=True):
|
||||
id: UUID = Field(primary_key=True, default_factory=uuid4)
|
||||
address: str = Field()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from sqlmodel import SQLModel, Field
|
||||
from uuid import uuid4, UUID
|
||||
|
||||
|
||||
class Owner(SQLModel, table=True):
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
user_id: UUID = Field(foreign_key="user.id", unique=True)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from sqlmodel import SQLModel, Field
|
||||
from uuid import uuid4, UUID
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
id: UUID = Field(default_factory=lambda: uuid4(), primary_key=True)
|
||||
email: str = Field(unique=True, nullable=False)
|
||||
|
||||
@ -6,6 +6,7 @@ from ..repositories.user_repository import UserRepository
|
||||
from typing import Annotated
|
||||
from fastapi import Depends, Request
|
||||
|
||||
|
||||
class AuthContext:
|
||||
"""
|
||||
Provides authentication context for the current request.
|
||||
@ -13,11 +14,13 @@ class AuthContext:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
request: Request,
|
||||
request: Request,
|
||||
) -> None:
|
||||
if not get_settings().environment == "development":
|
||||
raise NotImplementedError("AuthProvider is only implemented for development environment.")
|
||||
|
||||
raise NotImplementedError(
|
||||
"AuthProvider is only implemented for development environment."
|
||||
)
|
||||
|
||||
self._authenticated_user = request.state.user
|
||||
|
||||
@property
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
from sqlalchemy import Engine
|
||||
@ -21,9 +20,9 @@ settings = get_settings()
|
||||
|
||||
async def _get_async_engine() -> AsyncEngine:
|
||||
return create_async_engine(
|
||||
f"postgresql+asyncpg://{settings.db.username}:{settings.db.password}@{settings.db.host}:{settings.db.port}/{settings.db.db_name}",
|
||||
future=True,
|
||||
)
|
||||
f"postgresql+asyncpg://{settings.db.username}:{settings.db.password}@{settings.db.host}:{settings.db.port}/{settings.db.db_name}",
|
||||
future=True,
|
||||
)
|
||||
|
||||
|
||||
async def get_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
@ -64,7 +63,9 @@ def _seed_db():
|
||||
|
||||
session = Session(_get_engine())
|
||||
|
||||
existing_user = session.query(User).filter(User.email == settings.app.mock_user_email).first()
|
||||
existing_user = (
|
||||
session.query(User).filter(User.email == settings.app.mock_user_email).first()
|
||||
)
|
||||
if not existing_user:
|
||||
mock_user = User(
|
||||
email=settings.app.mock_user_email,
|
||||
@ -80,4 +81,3 @@ def create_db_and_tables():
|
||||
engine = _get_engine()
|
||||
SQLModel.metadata.create_all(engine)
|
||||
_seed_db()
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ from sqlmodel import select
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class HouseRepository:
|
||||
def __init__(self, session: Annotated[AsyncSession, Depends(get_session)]) -> None:
|
||||
self.session = session
|
||||
|
||||
@ -7,6 +7,7 @@ from sqlmodel import select
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class OwnerRepository:
|
||||
def __init__(self, session: Annotated[AsyncSession, Depends(get_session)]) -> None:
|
||||
self.session = session
|
||||
|
||||
@ -7,6 +7,7 @@ from sqlmodel import select
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class UserRepository:
|
||||
def __init__(self, session: Annotated[AsyncSession, Depends(get_session)]) -> None:
|
||||
self.session = session
|
||||
@ -15,7 +16,7 @@ class UserRepository:
|
||||
statement = select(User).where(User.id == user_id)
|
||||
result = await self.session.execute(statement)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_by_email(self, email: str):
|
||||
statement = select(User).where(User.email == email)
|
||||
result = await self.session.execute(statement)
|
||||
|
||||
@ -11,14 +11,18 @@ from ..dtos.houses_list_response import HousesListResponse, HouseResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("")
|
||||
async def create_house(body: HouseCreateRequest, auth: Annotated[AuthContext, Depends()], house_repository: Annotated[HouseRepository, Depends()], owner_repository: Annotated[OwnerRepository, Depends()]) -> HouseCreateResponse:
|
||||
async def create_house(
|
||||
body: HouseCreateRequest,
|
||||
auth: Annotated[AuthContext, Depends()],
|
||||
house_repository: Annotated[HouseRepository, Depends()],
|
||||
owner_repository: Annotated[OwnerRepository, Depends()],
|
||||
) -> HouseCreateResponse:
|
||||
owner = await owner_repository.get_by_id(auth.user.id)
|
||||
|
||||
|
||||
if not owner:
|
||||
new_owner = Owner(
|
||||
user_id=auth.user.id
|
||||
)
|
||||
new_owner = Owner(user_id=auth.user.id)
|
||||
|
||||
await owner_repository.save(new_owner)
|
||||
|
||||
@ -28,24 +32,33 @@ async def create_house(body: HouseCreateRequest, auth: Annotated[AuthContext, De
|
||||
city=body.city,
|
||||
country=body.country,
|
||||
price=body.price,
|
||||
description=body.description
|
||||
description=body.description,
|
||||
)
|
||||
|
||||
await house_repository.save(house)
|
||||
|
||||
return HouseCreateResponse(
|
||||
id=str(house.id)
|
||||
)
|
||||
|
||||
return HouseCreateResponse(id=str(house.id))
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def get_all_houses(house_repository: Annotated[HouseRepository, Depends()], limit: int = 100, offset: int = 0) -> HousesListResponse:
|
||||
async def get_all_houses(
|
||||
house_repository: Annotated[HouseRepository, Depends()],
|
||||
limit: int = 100,
|
||||
offset: int = 0,
|
||||
) -> HousesListResponse:
|
||||
all_houses = await house_repository.get_all(offset=offset, limit=limit)
|
||||
|
||||
house_responses = ([HouseResponse(id=str(house.id), description=house.description, address=house.address, city=house.city, country=house.country, price=house.price) for house in all_houses])
|
||||
|
||||
house_responses = [
|
||||
HouseResponse(
|
||||
id=str(house.id),
|
||||
description=house.description,
|
||||
address=house.address,
|
||||
city=house.city,
|
||||
country=house.country,
|
||||
price=house.price,
|
||||
)
|
||||
for house in all_houses
|
||||
]
|
||||
print(house_responses)
|
||||
|
||||
return HousesListResponse(
|
||||
houses=house_responses
|
||||
)
|
||||
|
||||
return HousesListResponse(houses=house_responses)
|
||||
|
||||
@ -10,22 +10,27 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def get_owners(owner_repository: Annotated[OwnerRepository, Depends()]) -> OwnerListResponse:
|
||||
async def get_owners(
|
||||
owner_repository: Annotated[OwnerRepository, Depends()],
|
||||
) -> OwnerListResponse:
|
||||
owners = await owner_repository.get_all()
|
||||
|
||||
owners_response = [OwnerResponse(id=str(owner.id), user_id=str(owner.user_id)) for owner in owners]
|
||||
owners_response = [
|
||||
OwnerResponse(id=str(owner.id), user_id=str(owner.user_id)) for owner in owners
|
||||
]
|
||||
|
||||
return OwnerListResponse(owners=owners_response)
|
||||
|
||||
return OwnerListResponse(
|
||||
owners=owners_response
|
||||
)
|
||||
|
||||
@router.get("/{id}")
|
||||
async def get_owner(id: str, owner_repository: Annotated[OwnerRepository, Depends()], user_repository: Annotated[UserRepository, Depends()]) -> OwnerDetailResponse:
|
||||
async def get_owner(
|
||||
id: str,
|
||||
owner_repository: Annotated[OwnerRepository, Depends()],
|
||||
user_repository: Annotated[UserRepository, Depends()],
|
||||
) -> OwnerDetailResponse:
|
||||
owner = await owner_repository.get_by_id(id)
|
||||
user = await user_repository.get_by_id(owner.user_id)
|
||||
|
||||
return OwnerDetailResponse(
|
||||
id=str(owner.id),
|
||||
user_id=str(owner.user_id),
|
||||
email=user.email
|
||||
)
|
||||
id=str(owner.id), user_id=str(owner.user_id), email=user.email
|
||||
)
|
||||
|
||||
@ -15,12 +15,14 @@ class HousePricePredictor:
|
||||
Mock ML model that predicts house prices.
|
||||
In a real scenario, this would load a trained model.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
# Mock initialization - in reality would load model weights
|
||||
pass
|
||||
|
||||
def predict(self, square_feet: float, bedrooms: int, bathrooms: float) -> Prediction:
|
||||
|
||||
def predict(
|
||||
self, square_feet: float, bedrooms: int, bathrooms: float
|
||||
) -> Prediction:
|
||||
"""
|
||||
Mock prediction method that returns:
|
||||
- predicted price
|
||||
@ -31,14 +33,13 @@ class HousePricePredictor:
|
||||
base_price = square_feet * 200
|
||||
bedroom_value = bedrooms * 25000
|
||||
bathroom_value = bathrooms * 15000
|
||||
|
||||
|
||||
predicted_price = base_price + bedroom_value + bathroom_value
|
||||
|
||||
|
||||
# Add some randomness to make it interesting
|
||||
confidence_score = random.uniform(0.8, 0.99)
|
||||
similar_listings = [
|
||||
predicted_price * random.uniform(0.9, 1.1)
|
||||
for _ in range(3)
|
||||
predicted_price * random.uniform(0.9, 1.1) for _ in range(3)
|
||||
]
|
||||
|
||||
|
||||
return Prediction(predicted_price, confidence_score, similar_listings)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import random
|
||||
|
||||
class InvestorPredictor():
|
||||
|
||||
class InvestorPredictor:
|
||||
def is_investor(user: User) -> bool:
|
||||
return random.random() < 0.5
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
from pydantic import Field
|
||||
@ -9,12 +8,15 @@ import os
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class _BaseConfig(BaseSettings):
|
||||
environment: str = Field(default=os.getenv("ENVIRONMENT", "development"))
|
||||
|
||||
|
||||
|
||||
class _AppSettings(_BaseConfig):
|
||||
mock_user_email: str = "test@test.com"
|
||||
|
||||
|
||||
class _DbSettings(_BaseConfig):
|
||||
username: str = Field(default=os.getenv("PG_USER"))
|
||||
password: str = Field(default=os.getenv("PG_PASSWORD"))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user