Format everything

This commit is contained in:
Jacob Windsor 2025-02-19 16:25:48 +01:00
parent ed6c49a1b7
commit ecfef9a09d
22 changed files with 115 additions and 62 deletions

View File

@ -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"],
)

View File

@ -1,5 +1,5 @@
from pydantic import BaseModel
class HouseCreateResponse(BaseModel):
id: str

View File

@ -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]

View File

@ -1,7 +1,7 @@
from pydantic import BaseModel
class OwnerDetailResponse(BaseModel):
id: str
user_id: str
email: str

View File

@ -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]

View File

@ -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)

View File

@ -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")

View File

@ -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.",

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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
)

View File

@ -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)

View File

@ -1,5 +1,6 @@
import random
class InvestorPredictor():
class InvestorPredictor:
def is_investor(user: User) -> bool:
return random.random() < 0.5

View File

@ -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"))