From ecfef9a09d4da14e53809ade135f9c85b92bf24a Mon Sep 17 00:00:00 2001 From: Jacob Windsor Date: Wed, 19 Feb 2025 16:25:48 +0100 Subject: [PATCH] Format everything --- backend/app/dtos/house_create_request.py | 26 +++++++--- backend/app/dtos/house_create_response.py | 2 +- backend/app/dtos/houses_list_response.py | 2 + backend/app/dtos/owner_detail_response.py | 2 +- backend/app/dtos/owner_list_response.py | 3 +- backend/app/errors/base_error.py | 2 +- backend/app/errors/not_authenticated.py | 2 +- backend/app/main.py | 2 + backend/app/middleware/authenticate.py | 9 ++-- backend/app/models/house.py | 1 + backend/app/models/owner.py | 1 + backend/app/models/user.py | 1 + backend/app/providers/auth_provider.py | 9 ++-- backend/app/providers/db_provider.py | 12 ++--- backend/app/repositories/house_repository.py | 1 + backend/app/repositories/owner_repository.py | 1 + backend/app/repositories/user_repository.py | 3 +- backend/app/routers/houses.py | 47 ++++++++++++------- backend/app/routers/owners.py | 25 ++++++---- backend/app/services/house_price_predictor.py | 17 +++---- backend/app/services/investor_predictor.py | 3 +- backend/app/settings.py | 6 ++- 22 files changed, 115 insertions(+), 62 deletions(-) diff --git a/backend/app/dtos/house_create_request.py b/backend/app/dtos/house_create_request.py index 18a2ca7..0f7fc7c 100644 --- a/backend/app/dtos/house_create_request.py +++ b/backend/app/dtos/house_create_request.py @@ -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"], + ) diff --git a/backend/app/dtos/house_create_response.py b/backend/app/dtos/house_create_response.py index 2c60514..5f74154 100644 --- a/backend/app/dtos/house_create_response.py +++ b/backend/app/dtos/house_create_response.py @@ -1,5 +1,5 @@ from pydantic import BaseModel + class HouseCreateResponse(BaseModel): id: str - diff --git a/backend/app/dtos/houses_list_response.py b/backend/app/dtos/houses_list_response.py index fbf3274..6b55b6e 100644 --- a/backend/app/dtos/houses_list_response.py +++ b/backend/app/dtos/houses_list_response.py @@ -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] diff --git a/backend/app/dtos/owner_detail_response.py b/backend/app/dtos/owner_detail_response.py index f166374..b928c01 100644 --- a/backend/app/dtos/owner_detail_response.py +++ b/backend/app/dtos/owner_detail_response.py @@ -1,7 +1,7 @@ from pydantic import BaseModel + class OwnerDetailResponse(BaseModel): id: str user_id: str email: str - diff --git a/backend/app/dtos/owner_list_response.py b/backend/app/dtos/owner_list_response.py index 0740443..03d1aba 100644 --- a/backend/app/dtos/owner_list_response.py +++ b/backend/app/dtos/owner_list_response.py @@ -1,9 +1,10 @@ from pydantic import BaseModel + class OwnerResponse(BaseModel): id: str user_id: str class OwnerListResponse(BaseModel): - owners: list[OwnerResponse] \ No newline at end of file + owners: list[OwnerResponse] diff --git a/backend/app/errors/base_error.py b/backend/app/errors/base_error.py index e633d1a..29f72a2 100644 --- a/backend/app/errors/base_error.py +++ b/backend/app/errors/base_error.py @@ -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) \ No newline at end of file + super().__init__(status_code=self.status_code, detail=self.detail) diff --git a/backend/app/errors/not_authenticated.py b/backend/app/errors/not_authenticated.py index b5701ac..cc7930a 100644 --- a/backend/app/errors/not_authenticated.py +++ b/backend/app/errors/not_authenticated.py @@ -7,4 +7,4 @@ class NotAuthenticatedError(BaseError): status_code: int = status.HTTP_401_UNAUTHORIZED def __init__(self): - super().__init__("Not authenticated") \ No newline at end of file + super().__init__("Not authenticated") diff --git a/backend/app/main.py b/backend/app/main.py index 1311bfb..2272ae5 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -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.", diff --git a/backend/app/middleware/authenticate.py b/backend/app/middleware/authenticate.py index 8879d23..c4b0592 100644 --- a/backend/app/middleware/authenticate.py +++ b/backend/app/middleware/authenticate.py @@ -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 \ No newline at end of file + return True diff --git a/backend/app/models/house.py b/backend/app/models/house.py index 072b590..b37da38 100644 --- a/backend/app/models/house.py +++ b/backend/app/models/house.py @@ -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() diff --git a/backend/app/models/owner.py b/backend/app/models/owner.py index f46a715..6c54884 100644 --- a/backend/app/models/owner.py +++ b/backend/app/models/owner.py @@ -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) diff --git a/backend/app/models/user.py b/backend/app/models/user.py index 6c5c33b..929e744 100644 --- a/backend/app/models/user.py +++ b/backend/app/models/user.py @@ -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) diff --git a/backend/app/providers/auth_provider.py b/backend/app/providers/auth_provider.py index 3512541..3e4c5c1 100644 --- a/backend/app/providers/auth_provider.py +++ b/backend/app/providers/auth_provider.py @@ -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 diff --git a/backend/app/providers/db_provider.py b/backend/app/providers/db_provider.py index cc665eb..fa64ff2 100644 --- a/backend/app/providers/db_provider.py +++ b/backend/app/providers/db_provider.py @@ -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() - diff --git a/backend/app/repositories/house_repository.py b/backend/app/repositories/house_repository.py index 466a627..bc7855d 100644 --- a/backend/app/repositories/house_repository.py +++ b/backend/app/repositories/house_repository.py @@ -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 diff --git a/backend/app/repositories/owner_repository.py b/backend/app/repositories/owner_repository.py index c32c083..350bf23 100644 --- a/backend/app/repositories/owner_repository.py +++ b/backend/app/repositories/owner_repository.py @@ -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 diff --git a/backend/app/repositories/user_repository.py b/backend/app/repositories/user_repository.py index a01cc5a..28351a4 100644 --- a/backend/app/repositories/user_repository.py +++ b/backend/app/repositories/user_repository.py @@ -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) diff --git a/backend/app/routers/houses.py b/backend/app/routers/houses.py index d21b44c..21c3547 100644 --- a/backend/app/routers/houses.py +++ b/backend/app/routers/houses.py @@ -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 - ) \ No newline at end of file + + return HousesListResponse(houses=house_responses) diff --git a/backend/app/routers/owners.py b/backend/app/routers/owners.py index e2f1e84..b88b7e3 100644 --- a/backend/app/routers/owners.py +++ b/backend/app/routers/owners.py @@ -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 - ) \ No newline at end of file + id=str(owner.id), user_id=str(owner.user_id), email=user.email + ) diff --git a/backend/app/services/house_price_predictor.py b/backend/app/services/house_price_predictor.py index 62b394b..b6c66f7 100644 --- a/backend/app/services/house_price_predictor.py +++ b/backend/app/services/house_price_predictor.py @@ -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) diff --git a/backend/app/services/investor_predictor.py b/backend/app/services/investor_predictor.py index 7ed40cf..4ceec16 100644 --- a/backend/app/services/investor_predictor.py +++ b/backend/app/services/investor_predictor.py @@ -1,5 +1,6 @@ import random -class InvestorPredictor(): + +class InvestorPredictor: def is_investor(user: User) -> bool: return random.random() < 0.5 diff --git a/backend/app/settings.py b/backend/app/settings.py index 90df875..c6cd66d 100644 --- a/backend/app/settings.py +++ b/backend/app/settings.py @@ -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"))