Compare commits

..

2 Commits

Author SHA1 Message Date
Lino Mallevaey
0b3c755e5c feat: Addition of the main database models 2025-08-18 21:08:16 +02:00
Lino Mallevaey
6f4dc2244a fix: Cleaning up the code base 2025-08-18 21:07:24 +02:00
8 changed files with 135 additions and 43 deletions

View File

@@ -6,12 +6,12 @@ ENV=dev # dev, prod
# =========================
# DATABASE CONFIGURATION
# =========================
# URL de connexion SQLAlchemy
# Pour PyMySQL (synchrone)
DATABASE_URL=mysql+pymysql://user:password@localhost:3306/db_name
# Pour aiomysql (async)
# DATABASE_URL=mysql+aiomysql://user:password@localhost:3306/mokpyo
# connexion SQLAlchemy
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_NAME=mokpyo
# Pooling / Options SQLAlchemy (optionnel)
# MAX_CONNECTIONS=10

3
.gitignore vendored
View File

@@ -179,3 +179,6 @@ cython_debug/
# git
.git/
# custom tests files
*.test.py

View File

@@ -13,32 +13,3 @@ router = APIRouter()
@router.get("/ping", tags=["Test"])
def ping():
return {"message": "pong"}
# =========================
# EXEMPLE UTILISATEUR
# =========================
@router.get("/me", tags=["User"])
def read_current_user(current_user: dict = Depends(get_current_user)):
"""
Retourne les infos de l'utilisateur connecté
"""
return {"user": current_user}
# =========================
# EXEMPLE AUTH
# =========================
@router.post("/hash-password", tags=["Auth"])
def test_hash_password(password: str):
"""
Exemple simple pour hasher un mot de passe
"""
hashed = hash_password(password)
return {"password": password, "hashed": hashed}
@router.post("/verify-password", tags=["Auth"])
def test_verify_password(password: str, hashed: str):
"""
Vérifie qu'un mot de passe correspond à un hash
"""
valid = verify_password(password, hashed)
return {"valid": valid}

View File

@@ -10,7 +10,12 @@ class Settings(BaseSettings):
# =========================
# DATABASE
# =========================
DATABASE_URL: str
DATABASE_HOST: str
DATABASE_PORT: int
DATABASE_USER: str
DATABASE_PASSWORD: str
DATABASE_NAME: str
DATABASE_DRIVER: str
# =========================
# FASTAPI
@@ -61,6 +66,10 @@ class Settings(BaseSettings):
# =========================
ORJSON_STRICT: bool = True
@property
def database_url(self) -> str:
return f"mysql+<driver>://{self.DATABASE_USER}:{self.DATABASE_PASSWORD}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
@property
def access_token_expire(self) -> timedelta:
return timedelta(minutes=self.ACCESS_TOKEN_EXPIRE_MINUTES)
@@ -70,6 +79,4 @@ class Settings(BaseSettings):
env_file_encoding = "utf-8"
# Instance globale
settings = Settings()
print("test")
settings = Settings()

View File

@@ -8,3 +8,4 @@ __version__ = "1.0.0"
from .engine import engine, async_engine
from .session import SessionLocal, AsyncSessionLocal, get_db, get_async_db
from .models import Base

View File

@@ -1,13 +1,13 @@
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine
from sqlalchemy import create_engine
from sqlalchemy import create_engine, Engine
from app.core.config import settings
# =========================
# SYNCHRONOUS ENGINE
# =========================
# Pour opérations sync classiques
engine = create_engine(
settings.DATABASE_URL.replace("+aiomysql", ""), # remove async part if present
engine: Engine = create_engine(
settings.database_url.replace('<driver>', "pymysql"),
echo=settings.DEBUG,
future=True
)
@@ -17,7 +17,7 @@ engine = create_engine(
# =========================
# Pour opérations async avec async SQLAlchemy
async_engine: AsyncEngine = create_async_engine(
settings.DATABASE_URL if "+aiomysql" in settings.DATABASE_URL else settings.DATABASE_URL.replace("mysql+pymysql", "mysql+aiomysql"),
settings.database_url.replace('<driver>', "aiomysql"),
echo=settings.DEBUG,
future=True
)

110
app/db/models.py Normal file
View File

@@ -0,0 +1,110 @@
from datetime import date
from sqlalchemy import (
sa,
Column,
Integer,
String,
Boolean,
Date,
Enum,
ForeignKey
)
from sqlalchemy.orm import DeclarativeBase, relationship
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
email = Column(String, unique=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
stats = Column(String)
is_active = Column(Boolean, default=True)
tasks = relationship("Item", back_populates="owner")
events = relationship("Event", back_populates="owner")
goals = relationship("Goal", back_populates="owner")
sub_goals = relationship("SubGoal", back_populates="owner")
notes = relationship("Note", back_populates="owner")
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
description = Column(String, index=True, nullable=False)
frequency = Column(Integer, default=4223, nullable=False)
reward = Column(Integer, nullable=True)
goal_id = Column(Integer, ForeignKey("goals.id"), default=None, nullable=True)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
start_date = Column(Date, server_default=sa.text("CURRENT_DATE"), nullable=False)
next_date = Column(Date, nullable=True)
owner = relationship("User", back_populates="tasks")
goal = relationship("Goal", back_populates="tasks")
class Event(Base):
__tablename__ = "events"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
name = Column(String, index=True, nullable=False)
description = Column(String, index=True, nullable=True)
frequency = Column(Integer, default=4223, nullable=False)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
owner = relationship("User", back_populates="events")
class Goal(Base):
__tablename__ = "goals"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, index=True, nullable=False)
description = Column(String, index=True, nullable=True)
state = Column(Enum("in_progress", "successful", "failed", "aborted"), default="in_progress", nullable=False)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
tasks = relationship("Task", back_populates="goal")
notes = relationship("Note", back_populates="goal")
sub_goals = relationship("SubGoal", back_populates="goal")
owner = relationship("User", back_populates="goals")
class SubGoal(Base):
__tablename__ = "sub_goals"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, index=True, nullable=False)
description = Column(String, index=True, nullable=True)
state = Column(Enum("in_progress", "successful", "failed", "aborted"), default="in_progress", nullable=False)
reward = Column(Integer, nullable=True)
goal_id = Column(Integer, ForeignKey("goals.id"), nullable=False)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
goal = relationship("Goal", back_populates="sub_goals")
owner = relationship("User", back_populates="sub_goals")
class Note(Base):
__tablename__ = "notes"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, index=True, nullable=False)
description = Column(String, index=True, nullable=True)
content = Column(String, index=True, nullable=True)
goal_id = Column(Integer, ForeignKey("goals.id"), nullable=True)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
created_at = Column(Date, server_default=sa.text("CURRENT_DATE"), nullable=False)
updated_at = Column(Date, server_default=sa.text("CURRENT_DATE"), nullable=False)
goal = relationship("Goal", back_populates="notes")
owner = relationship("User", back_populates="notes")

View File