diff --git a/backend/.config b/backend/.config deleted file mode 100644 index d31c4ed..0000000 --- a/backend/.config +++ /dev/null @@ -1,40 +0,0 @@ -import sys -from os.path import abspath, dirname -import logging.config -from alembic import context - -sys.path.insert(0, dirname(dirname(abspath(__file__)))) - -from config import DATABASE_URL, LOGGING_CONFIG -from database import Base, engine -import models - -#log-settings - -logging.config.dictConfig(LOGGING_CONFIG) - -target_metadata = Base.metadata - -def run_migrations_offline(): - context.configure( - url=DATABASE_URL, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - with context.begin_transaction(): - context.run_migrations() - -def run_migrations_online(): - with engine.connect() as connection: - context.configure( - connection=connection, - target_metadata=target_metadata, - ) - with context.begin_transaction(): - context.run_migrations() - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() \ No newline at end of file diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..b686fce --- /dev/null +++ b/backend/.env @@ -0,0 +1 @@ +DATABASE_URL=sqlite:///./wheel.db \ No newline at end of file diff --git a/backend/__pycache__/config.cpython-313.pyc b/backend/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..3a0cad9 Binary files /dev/null and b/backend/__pycache__/config.cpython-313.pyc differ diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..02039a9 Binary files /dev/null and b/backend/__pycache__/main.cpython-313.pyc differ diff --git a/backend/__pycache__/schemas.cpython-313.pyc b/backend/__pycache__/schemas.cpython-313.pyc new file mode 100644 index 0000000..069f2ab Binary files /dev/null and b/backend/__pycache__/schemas.cpython-313.pyc differ diff --git a/backend/alembic.ini b/backend/alembic.ini index 95cc891..63a6f19 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -5,7 +5,7 @@ # this is typically a path given in POSIX (e.g. forward slashes) # format, relative to the token %(here)s which refers to the location of this # ini file -script_location = alembic +script_location = %(here)s/alembic # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s # Uncomment the line below if you want the files to be prepended with date and time diff --git a/backend/alembic/__pycache__/env.cpython-313.pyc b/backend/alembic/__pycache__/env.cpython-313.pyc index 53a8725..2e9e97d 100644 Binary files a/backend/alembic/__pycache__/env.cpython-313.pyc and b/backend/alembic/__pycache__/env.cpython-313.pyc differ diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 209048d..c0a64ec 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -5,76 +5,18 @@ from sqlalchemy import pool from alembic import context -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. +from app.db.session import Base +from config import settings +from app.db import models + config = context.config -# Interpret the config file for Python logging. -# This line sets up loggers basically. +config.set_main_option( + "sqlalchemy.url", + settings.DATABASE_URL +) + if config.config_file_name is not None: fileConfig(config.config_file_name) -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -from database import Base, engine -import models -target_metadata = Base.metadata - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def run_migrations_offline() -> None: - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online() -> None: - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - connectable = engine_from_config( - config.get_section(config.config_ini_section, {}), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: - context.configure( - connection=connection, target_metadata=target_metadata - ) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() +target_metadata = Base.metadata \ No newline at end of file diff --git a/backend/alembic/versions/96dff3d4cf1c_initial.py b/backend/alembic/versions/96dff3d4cf1c_initial.py new file mode 100644 index 0000000..359eec9 --- /dev/null +++ b/backend/alembic/versions/96dff3d4cf1c_initial.py @@ -0,0 +1,28 @@ +"""initial + +Revision ID: 96dff3d4cf1c +Revises: +Create Date: 2026-05-17 03:07:55.231096 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '96dff3d4cf1c' +down_revision: Union[str, Sequence[str], None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + pass + + +def downgrade() -> None: + """Downgrade schema.""" + pass diff --git a/backend/alembic/versions/__pycache__/96dff3d4cf1c_initial.cpython-313.pyc b/backend/alembic/versions/__pycache__/96dff3d4cf1c_initial.cpython-313.pyc new file mode 100644 index 0000000..d7534f9 Binary files /dev/null and b/backend/alembic/versions/__pycache__/96dff3d4cf1c_initial.cpython-313.pyc differ diff --git a/backend/api/__pycache__/admin.cpython-313.pyc b/backend/api/__pycache__/admin.cpython-313.pyc new file mode 100644 index 0000000..7fd52b4 Binary files /dev/null and b/backend/api/__pycache__/admin.cpython-313.pyc differ diff --git a/backend/api/__pycache__/user.cpython-313.pyc b/backend/api/__pycache__/user.cpython-313.pyc new file mode 100644 index 0000000..891e14f Binary files /dev/null and b/backend/api/__pycache__/user.cpython-313.pyc differ diff --git a/backend/api/admin b/backend/api/admin deleted file mode 100644 index 612d640..0000000 --- a/backend/api/admin +++ /dev/null @@ -1,37 +0,0 @@ -from fastapi import FastAPI, Depends, HTTPException -from sqlalchemy.orm import Session -from fastapi.middleware.cors import CORSMiddleware - -import models -import schemas -import crud - -from database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - -# DELETE ITEM -@app.delete("/items/{item_id}") -def delete_item( - item_id: int, - db: Session = Depends(get_db) -): - item = crud.delete_item(db, item_id) - - if not item: - raise HTTPException( - status_code=404, - detail="Item not found" - ) - - return {"message": "Item deleted"} - - # CREATE ITEM -@app.post("/items", response_model=schemas.ItemResponse) -def create_item( - item: schemas.ItemCreate, - db: Session = Depends(get_db) -): - return crud.create_item(db, item.title) \ No newline at end of file diff --git a/backend/api/admin.py b/backend/api/admin.py new file mode 100644 index 0000000..79d823f --- /dev/null +++ b/backend/api/admin.py @@ -0,0 +1,64 @@ +from fastapi import APIRouter +from fastapi import Depends +from fastapi import HTTPException + +from sqlalchemy.orm import Session + +import schemas +from services import crud + +from app.db.session import SessionLocal + + +router = APIRouter() + + +def get_db(): + + db = SessionLocal() + + try: + yield db + + finally: + db.close() + + +# CREATE ITEM +@router.post( + "/items", + response_model=schemas.ItemResponse +) +def create_item( + item: schemas.ItemCreate, + db: Session = Depends(get_db) +): + + return crud.create_item( + db, + item.title + ) + + +# DELETE ITEM +@router.delete("/items/{item_id}") +def delete_item( + item_id: int, + db: Session = Depends(get_db) +): + + item = crud.delete_item( + db, + item_id + ) + + if not item: + + raise HTTPException( + status_code=404, + detail="Item not found" + ) + + return { + "message": "Item deleted" + } \ No newline at end of file diff --git a/backend/api/user b/backend/api/user deleted file mode 100644 index 43448c2..0000000 --- a/backend/api/user +++ /dev/null @@ -1,38 +0,0 @@ -from fastapi import FastAPI, Depends, HTTPException -from sqlalchemy.orm import Session -from fastapi.middleware.cors import CORSMiddleware - -import models -import schemas -import crud - -from database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -# SPIN -@app.post("/spin") -def spin(db: Session = Depends(get_db)): - - winner = crud.spin_wheel(db) - - if not winner: - raise HTTPException( - status_code=400, - detail="No items available" - ) - - return { - "winner": { - "id": winner.id, - "title": winner.title - } - } - - # GET ITEMS -@app.get("/items", response_model=list[schemas.ItemResponse]) -def get_items(db: Session = Depends(get_db)): - return crud.get_items(db) \ No newline at end of file diff --git a/backend/api/user.py b/backend/api/user.py new file mode 100644 index 0000000..9861a42 --- /dev/null +++ b/backend/api/user.py @@ -0,0 +1,59 @@ +from fastapi import APIRouter +from fastapi import Depends +from fastapi import HTTPException + +from sqlalchemy.orm import Session + +import schemas +from services import crud + +from app.db.session import SessionLocal + + +router = APIRouter() + + +def get_db(): + + db = SessionLocal() + + try: + yield db + + finally: + db.close() + + +# GET ITEMS +@router.get( + "/items", + response_model=list[schemas.ItemResponse] +) +def get_items( + db: Session = Depends(get_db) +): + + return crud.get_items(db) + + +# SPIN +@router.post("/spin") +def spin( + db: Session = Depends(get_db) +): + + winner = crud.spin_wheel(db) + + if not winner: + + raise HTTPException( + status_code=400, + detail="No items available" + ) + + return { + "winner": { + "id": winner.id, + "title": winner.title + } + } \ No newline at end of file diff --git a/backend/app/db/__init__.py b/backend/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/db/__pycache__/__init__.cpython-313.pyc b/backend/app/db/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..d03eb38 Binary files /dev/null and b/backend/app/db/__pycache__/__init__.cpython-313.pyc differ diff --git a/backend/app/db/__pycache__/model.cpython-313.pyc b/backend/app/db/__pycache__/model.cpython-313.pyc new file mode 100644 index 0000000..7eb1b50 Binary files /dev/null and b/backend/app/db/__pycache__/model.cpython-313.pyc differ diff --git a/backend/app/db/__pycache__/models.cpython-313.pyc b/backend/app/db/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000..4c2cd42 Binary files /dev/null and b/backend/app/db/__pycache__/models.cpython-313.pyc differ diff --git a/backend/app/db/__pycache__/session.cpython-313.pyc b/backend/app/db/__pycache__/session.cpython-313.pyc new file mode 100644 index 0000000..20b6f71 Binary files /dev/null and b/backend/app/db/__pycache__/session.cpython-313.pyc differ diff --git a/backend/app/db/model.py b/backend/app/db/model.py deleted file mode 100644 index 5d0491f..0000000 --- a/backend/app/db/model.py +++ /dev/null @@ -1,87 +0,0 @@ -from fastapi import FastAPI, Depends, HTTPException -from sqlalchemy.orm import Session -from fastapi.middleware.cors import CORSMiddleware - -import models -import schemas -import crud - -from database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - -# CORS -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -# Database Session -def get_db(): - db = SessionLocal() - - try: - yield db - finally: - db.close() - -# GET ITEMS -@app.get("/items", response_model=list[schemas.ItemResponse]) -def get_items(db: Session = Depends(get_db)): - return crud.get_items(db) - -# CREATE ITEM -@app.post("/items", response_model=schemas.ItemResponse) -def create_item( - item: schemas.ItemCreate, - db: Session = Depends(get_db) -): - return crud.create_item(db, item.title) - -# DELETE ITEM -@app.delete("/items/{item_id}") -def delete_item( - item_id: int, - db: Session = Depends(get_db) -): - item = crud.delete_item(db, item_id) - - if not item: - raise HTTPException( - status_code=404, - detail="Item not found" - ) - - return {"message": "Item deleted"} - -# SPIN -@app.post("/spin") -def spin(db: Session = Depends(get_db)): - - winner = crud.spin_wheel(db) - - if not winner: - raise HTTPException( - status_code=400, - detail="No items available" - ) - - return { - "winner": { - "id": winner.id, - "title": winner.title - } - } - - from sqlalchemy import Column, Integer, String -from database import Base - -class Item(Base): - __tablename__ = "items" - id = Column(Integer, primary_key=True, index=True) - title = Column(String, nullable=False) \ No newline at end of file diff --git a/backend/app/db/models.py b/backend/app/db/models.py new file mode 100644 index 0000000..8c90d5b --- /dev/null +++ b/backend/app/db/models.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column +from sqlalchemy import Integer +from sqlalchemy import String + +from app.db.session import Base + + +class Item(Base): + + __tablename__ = "items" + + id = Column( + Integer, + primary_key=True, + index=True + ) + + title = Column( + String, + nullable=False + ) \ No newline at end of file diff --git a/backend/app/db/session.py b/backend/app/db/session.py index 0393a31..555626d 100644 --- a/backend/app/db/session.py +++ b/backend/app/db/session.py @@ -1,11 +1,11 @@ from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import declarative_base from sqlalchemy.orm import sessionmaker -DATABASE_URL = "sqlite:///./wheel.db" +from config import settings engine = create_engine( - DATABASE_URL, + settings.DATABASE_URL, connect_args={"check_same_thread": False} ) @@ -15,13 +15,4 @@ SessionLocal = sessionmaker( bind=engine ) -# Database Session -def get_db(): - db = SessionLocal() - - try: - yield db - finally: - db.close() - Base = declarative_base() \ No newline at end of file diff --git a/backend/config.py b/backend/config.py new file mode 100644 index 0000000..67d7bac --- /dev/null +++ b/backend/config.py @@ -0,0 +1,10 @@ +from pydantic_settings import BaseSettings + +class Settings(BaseSettings): + + DATABASE_URL: str + + class Config: + env_file = ".env" + +settings = Settings() \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 5e55b40..f394234 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,18 +1,13 @@ -from fastapi import FastAPI, Depends, HTTPException -from sqlalchemy.orm import Session +from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -import models -import schemas -import crud +from api.admin import router as admin_router +from api.user import router as user_router -from database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) app = FastAPI() -# CORS + app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -22,6 +17,6 @@ app.add_middleware( ) +app.include_router(admin_router) -app.include_router(user_router) -app.include_router(admin_router) \ No newline at end of file +app.include_router(user_router) \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..da056c5 Binary files /dev/null and b/backend/requirements.txt differ diff --git a/backend/services/__pycache__/crud.cpython-313.pyc b/backend/services/__pycache__/crud.cpython-313.pyc new file mode 100644 index 0000000..a988918 Binary files /dev/null and b/backend/services/__pycache__/crud.cpython-313.pyc differ diff --git a/backend/services/crud.py b/backend/services/crud.py index f483ea2..b9545a4 100644 --- a/backend/services/crud.py +++ b/backend/services/crud.py @@ -1,32 +1,61 @@ -from sqlalchemy.orm import Session -from models import Item import random -def get_items(db: Session): +from sqlalchemy.orm import Session + +from app.db.models import Item + + +def get_items( + db: Session +): + return db.query(Item).all() -def create_item(db: Session, title: str): - item = Item(title=title) + +def create_item( + db: Session, + title: str +): + + item = Item( + title=title + ) db.add(item) + db.commit() + db.refresh(item) return item -def delete_item(db: Session, item_id: int): - item = db.query(Item).filter(Item.id == item_id).first() + +def delete_item( + db: Session, + item_id: int +): + + item = db.query(Item).filter( + Item.id == item_id + ).first() if item: + db.delete(item) + db.commit() return item -def spin_wheel(db: Session): + +def spin_wheel( + db: Session +): + items = db.query(Item).all() if not items: + return None return random.choice(items) \ No newline at end of file