Pipelinen parannuksia building blockeilla
This commit is contained in:
64
zipit/projekti_clean/prompts/0_client_requirements.md
Normal file
64
zipit/projekti_clean/prompts/0_client_requirements.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 0 — Client (client) — requirements
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are a product owner who turns vague ideas into clear, actionable software requirements.
|
||||
|
||||
GIVEN a short project description from the user, produce a structured brief:
|
||||
|
||||
1. PROJECT NAME: a short, descriptive name
|
||||
2. GOAL: one sentence explaining what the software does and who it's for
|
||||
3. CORE FEATURES: numbered list of 3-8 concrete features (not vague wishes)
|
||||
4. DATA MODEL: list the main entities and their key fields (include field types)
|
||||
5. API ENDPOINTS: list the REST endpoints (method + path + purpose)
|
||||
6. CONSTRAINTS: any technical constraints (e.g. "must use SQLite", "no auth needed")
|
||||
|
||||
RULES:
|
||||
- Be specific: "User can filter todos by status" not "todo management"
|
||||
- Use plain English, no code
|
||||
- Maximum 400 words total
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
1. **PROJECT NAME:** Todo-sovellus
|
||||
|
||||
2. **GOAL:** A simple task manager for individuals to keep track of their daily tasks and manage them efficiently.
|
||||
|
||||
3. **CORE FEATURES:**
|
||||
- Create new todos with title, description, due date.
|
||||
- Read/View all todos or a single todo by ID.
|
||||
- Update existing todos (title, description, status).
|
||||
- Delete completed or unnecessary todos.
|
||||
- Filter todos by status (e.g., pending, completed).
|
||||
|
||||
4. **DATA MODEL:**
|
||||
- Todo
|
||||
- id (integer, primary key)
|
||||
- title (string)
|
||||
- description (text)
|
||||
- due_date (date)
|
||||
- status (enum: 'pending', 'completed')
|
||||
|
||||
5. **API ENDPOINTS:**
|
||||
- GET /todos: Retrieve all todos.
|
||||
- GET /todos/{id}: Retrieve a single todo by ID.
|
||||
- POST /todos: Create a new todo.
|
||||
- PUT /todos/{id}: Update an existing todo.
|
||||
- DELETE /todos/{id}: Delete a todo.
|
||||
|
||||
6. **CONSTRAINTS:**
|
||||
- Must use SQLite as the database.
|
||||
- No authentication required for accessing endpoints.
|
||||
- Implement unit tests and integration tests to ensure functionality.
|
||||
```
|
||||
122
zipit/projekti_clean/prompts/1_data_models.py.md
Normal file
122
zipit/projekti_clean/prompts/1_data_models.py.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# 1 — Data Engineer (data) — models.py
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are a database architect specializing in SQLAlchemy and relational databases.
|
||||
|
||||
YOUR RESPONSIBILITIES:
|
||||
1. Design normalized database schemas with proper column types and constraints
|
||||
2. Define SQLAlchemy models with __tablename__, primary keys, indexes, and relationships
|
||||
3. Set up engine, SessionLocal, and Base in the same file (models.py or database.py)
|
||||
4. Use String(length) not bare String for SQLite compatibility
|
||||
5. Add nullable=False for required fields, unique=True where appropriate
|
||||
6. Use Column(Integer, primary_key=True, index=True) for IDs
|
||||
|
||||
ALWAYS INCLUDE:
|
||||
- from sqlalchemy import create_engine, Column, Integer, String
|
||||
- from sqlalchemy.ext.declarative import declarative_base
|
||||
- from sqlalchemy.orm import sessionmaker
|
||||
- DATABASE_URL, engine, SessionLocal, Base
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are a database architect specializing in SQLAlchemy and relational databases.
|
||||
|
||||
YOUR RESPONSIBILITIES:
|
||||
1. Design normalized database schemas with proper column types and constraints
|
||||
2. Define SQLAlchemy models with __tablename__, primary keys, indexes, and relationships
|
||||
3. Set up engine, SessionLocal, and Base in the same file (models.py or database.py)
|
||||
4. Use String(length) not bare String for SQLite compatibility
|
||||
5. Add nullable=False for required fields, unique=True where appropriate
|
||||
6. Use Column(Integer, primary_key=True, index=True) for IDs
|
||||
|
||||
ALWAYS INCLUDE:
|
||||
- from sqlalchemy import create_engine, Column, Integer, String
|
||||
- from sqlalchemy.ext.declarative import declarative_base
|
||||
- from sqlalchemy.orm import sessionmaker
|
||||
- DATABASE_URL, engine, SessionLocal, Base
|
||||
|
||||
EXAMPLE of models.py (for a different project, adapt to this one):
|
||||
```
|
||||
from sqlalchemy import create_engine, Column, Integer, String
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./app.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Item(Base):
|
||||
__tablename__ = "items"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
```
|
||||
|
||||
PROJECT REQUIREMENTS (from product owner):
|
||||
1. **PROJECT NAME:** Todo-sovellus
|
||||
|
||||
2. **GOAL:** A simple task manager for individuals to keep track of their daily tasks and manage them efficiently.
|
||||
|
||||
3. **CORE FEATURES:**
|
||||
- Create new todos with title, description, due date.
|
||||
- Read/View all todos or a single todo by ID.
|
||||
- Update existing todos (title, description, status).
|
||||
- Delete completed or unnecessary todos.
|
||||
- Filter todos by status (e.g., pending, completed).
|
||||
|
||||
4. **DATA MODEL:**
|
||||
- Todo
|
||||
- id (integer, primary key)
|
||||
- title (string)
|
||||
- description (text)
|
||||
- due_date (date)
|
||||
- status (enum: 'pending', 'completed')
|
||||
|
||||
5. **API ENDPOINTS:**
|
||||
- GET /todos: Retrieve all todos.
|
||||
- GET /todos/{id}: Retrieve a single todo by ID.
|
||||
- POST /todos: Create a new todo.
|
||||
- PUT /todos/{id}: Update an existing todo.
|
||||
- DELETE /todos/{id}: Delete a todo.
|
||||
|
||||
6. **CONSTRAINTS:**
|
||||
- Must use SQLite as the database.
|
||||
- No authentication required for accessing endpoints.
|
||||
- Implement unit tests and integration tests to ensure functionality.
|
||||
|
||||
NOW write "models.py" for THIS project: Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
Define the SQLAlchemy model based on the project description. Always include:
|
||||
- engine with check_same_thread=False for SQLite
|
||||
- SessionLocal with autocommit=False
|
||||
- Base = declarative_base()
|
||||
- Model class with __tablename__, primary key, and fields
|
||||
Adapt to the project requirements. Import from already written files. Write ONLY the code, no explanations.
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
```
|
||||
142
zipit/projekti_clean/prompts/2_coder_schemas.py.md
Normal file
142
zipit/projekti_clean/prompts/2_coder_schemas.py.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# 2 — Coder (coder) — schemas.py
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are an expert Python developer. Write complete, production-ready code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Include ALL imports at the top of every file
|
||||
2. Import from other project files: from models import User, SessionLocal
|
||||
3. Pydantic schemas use different names than SQLAlchemy models: UserCreate, UserResponse (not User)
|
||||
4. SQLAlchemy engine: create_engine(url, connect_args={"check_same_thread": False})
|
||||
5. SessionLocal: sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
6. FastAPI dependencies: def get_db(): db = SessionLocal(); try: yield db; finally: db.close()
|
||||
7. Pydantic v2: use model_dump() not dict(), class Config: from_attributes = True
|
||||
8. All CRUD endpoints: POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
|
||||
NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are an expert Python developer. Write complete, production-ready code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Include ALL imports at the top of every file
|
||||
2. Import from other project files: from models import User, SessionLocal
|
||||
3. Pydantic schemas use different names than SQLAlchemy models: UserCreate, UserResponse (not User)
|
||||
4. SQLAlchemy engine: create_engine(url, connect_args={"check_same_thread": False})
|
||||
5. SessionLocal: sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
6. FastAPI dependencies: def get_db(): db = SessionLocal(); try: yield db; finally: db.close()
|
||||
7. Pydantic v2: use model_dump() not dict(), class Config: from_attributes = True
|
||||
8. All CRUD endpoints: POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
|
||||
NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)
|
||||
|
||||
EXAMPLE of schemas.py (for a different project, adapt to this one):
|
||||
```
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ItemCreate(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
class ItemResponse(ItemCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
```
|
||||
|
||||
Already written files in THIS project:
|
||||
--- models.py ---
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
PROJECT REQUIREMENTS (from product owner):
|
||||
1. **PROJECT NAME:** Todo-sovellus
|
||||
|
||||
2. **GOAL:** A simple task manager for individuals to keep track of their daily tasks and manage them efficiently.
|
||||
|
||||
3. **CORE FEATURES:**
|
||||
- Create new todos with title, description, due date.
|
||||
- Read/View all todos or a single todo by ID.
|
||||
- Update existing todos (title, description, status).
|
||||
- Delete completed or unnecessary todos.
|
||||
- Filter todos by status (e.g., pending, completed).
|
||||
|
||||
4. **DATA MODEL:**
|
||||
- Todo
|
||||
- id (integer, primary key)
|
||||
- title (string)
|
||||
- description (text)
|
||||
- due_date (date)
|
||||
- status (enum: 'pending', 'completed')
|
||||
|
||||
5. **API ENDPOINTS:**
|
||||
- GET /todos: Retrieve all todos.
|
||||
- GET /todos/{id}: Retrieve a single todo by ID.
|
||||
- POST /todos: Create a new todo.
|
||||
- PUT /todos/{id}: Update an existing todo.
|
||||
- DELETE /todos/{id}: Delete a todo.
|
||||
|
||||
6. **CONSTRAINTS:**
|
||||
- Must use SQLite as the database.
|
||||
- No authentication required for accessing endpoints.
|
||||
- Implement unit tests and integration tests to ensure functionality.
|
||||
|
||||
NOW write "schemas.py" for THIS project: Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
Create Pydantic schemas that match the SQLAlchemy model:
|
||||
- Create schema: fields without id (user provides these)
|
||||
- Response schema: inherits from Create, adds id
|
||||
- Add class Config with from_attributes = True (required for SQLAlchemy ORM)
|
||||
Adapt to the project requirements. Import from already written files. Write ONLY the code, no explanations.
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TodoCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
due_date: date
|
||||
status: str = 'pending'
|
||||
|
||||
class TodoResponse(TodoCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
```
|
||||
244
zipit/projekti_clean/prompts/3_coder_main.py.md
Normal file
244
zipit/projekti_clean/prompts/3_coder_main.py.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# 3 — Coder (coder) — main.py
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are an expert Python developer. Write complete, production-ready code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Include ALL imports at the top of every file
|
||||
2. Import from other project files: from models import User, SessionLocal
|
||||
3. Pydantic schemas use different names than SQLAlchemy models: UserCreate, UserResponse (not User)
|
||||
4. SQLAlchemy engine: create_engine(url, connect_args={"check_same_thread": False})
|
||||
5. SessionLocal: sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
6. FastAPI dependencies: def get_db(): db = SessionLocal(); try: yield db; finally: db.close()
|
||||
7. Pydantic v2: use model_dump() not dict(), class Config: from_attributes = True
|
||||
8. All CRUD endpoints: POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
|
||||
NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are an expert Python developer. Write complete, production-ready code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Include ALL imports at the top of every file
|
||||
2. Import from other project files: from models import User, SessionLocal
|
||||
3. Pydantic schemas use different names than SQLAlchemy models: UserCreate, UserResponse (not User)
|
||||
4. SQLAlchemy engine: create_engine(url, connect_args={"check_same_thread": False})
|
||||
5. SessionLocal: sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
6. FastAPI dependencies: def get_db(): db = SessionLocal(); try: yield db; finally: db.close()
|
||||
7. Pydantic v2: use model_dump() not dict(), class Config: from_attributes = True
|
||||
8. All CRUD endpoints: POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
|
||||
NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)
|
||||
|
||||
EXAMPLE of main.py (for a different project, adapt to this one):
|
||||
```
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from models import Base, engine, SessionLocal, Item
|
||||
from schemas import ItemCreate, ItemResponse
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
app = FastAPI()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.post("/items/", response_model=ItemResponse, status_code=201)
|
||||
def create_item(item: ItemCreate, db: Session = Depends(get_db)):
|
||||
db_item = Item(**item.model_dump())
|
||||
db.add(db_item)
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
return db_item
|
||||
|
||||
@app.get("/items/", response_model=list[ItemResponse])
|
||||
def list_items(db: Session = Depends(get_db)):
|
||||
return db.query(Item).all()
|
||||
|
||||
@app.get("/items/{item_id}", response_model=ItemResponse)
|
||||
def get_item(item_id: int, db: Session = Depends(get_db)):
|
||||
item = db.query(Item).filter(Item.id == item_id).first()
|
||||
if not item:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return item
|
||||
|
||||
@app.put("/items/{item_id}", response_model=ItemResponse)
|
||||
def update_item(item_id: int, item: ItemCreate, db: Session = Depends(get_db)):
|
||||
db_item = db.query(Item).filter(Item.id == item_id).first()
|
||||
if not db_item:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in item.model_dump().items():
|
||||
setattr(db_item, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
return db_item
|
||||
|
||||
@app.delete("/items/{item_id}", status_code=204)
|
||||
def delete_item(item_id: int, db: Session = Depends(get_db)):
|
||||
db_item = db.query(Item).filter(Item.id == item_id).first()
|
||||
if not db_item:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_item)
|
||||
db.commit()
|
||||
```
|
||||
|
||||
Already written files in THIS project:
|
||||
--- models.py ---
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
--- schemas.py ---
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TodoCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
due_date: date
|
||||
status: str = 'pending'
|
||||
|
||||
class TodoResponse(TodoCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
PROJECT REQUIREMENTS (from product owner):
|
||||
1. **PROJECT NAME:** Todo-sovellus
|
||||
|
||||
2. **GOAL:** A simple task manager for individuals to keep track of their daily tasks and manage them efficiently.
|
||||
|
||||
3. **CORE FEATURES:**
|
||||
- Create new todos with title, description, due date.
|
||||
- Read/View all todos or a single todo by ID.
|
||||
- Update existing todos (title, description, status).
|
||||
- Delete completed or unnecessary todos.
|
||||
- Filter todos by status (e.g., pending, completed).
|
||||
|
||||
4. **DATA MODEL:**
|
||||
- Todo
|
||||
- id (integer, primary key)
|
||||
- title (string)
|
||||
- description (text)
|
||||
- due_date (date)
|
||||
- status (enum: 'pending', 'completed')
|
||||
|
||||
5. **API ENDPOINTS:**
|
||||
- GET /todos: Retrieve all todos.
|
||||
- GET /todos/{id}: Retrieve a single todo by ID.
|
||||
- POST /todos: Create a new todo.
|
||||
- PUT /todos/{id}: Update an existing todo.
|
||||
- DELETE /todos/{id}: Delete a todo.
|
||||
|
||||
6. **CONSTRAINTS:**
|
||||
- Must use SQLite as the database.
|
||||
- No authentication required for accessing endpoints.
|
||||
- Implement unit tests and integration tests to ensure functionality.
|
||||
|
||||
NOW write "main.py" for THIS project: Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
Create the FastAPI app with all CRUD endpoints:
|
||||
- Import from models.py and schemas.py (use exact class names)
|
||||
- create_all(bind=engine) at module level
|
||||
- get_db dependency with yield pattern
|
||||
- POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
- Use response_model for type safety
|
||||
- Use model_dump() not dict() (Pydantic v2)
|
||||
Adapt to the project requirements. Import from already written files. Write ONLY the code, no explanations.
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from models import Base, engine, SessionLocal, Todo
|
||||
from schemas import TodoCreate, TodoResponse
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
app = FastAPI()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.post("/todos/", response_model=TodoResponse, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[TodoResponse])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
```
|
||||
212
zipit/projekti_clean/prompts/4_coder_pyproject.toml.md
Normal file
212
zipit/projekti_clean/prompts/4_coder_pyproject.toml.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# 4 — Coder (coder) — pyproject.toml
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are an expert Python developer. Write complete, production-ready code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Include ALL imports at the top of every file
|
||||
2. Import from other project files: from models import User, SessionLocal
|
||||
3. Pydantic schemas use different names than SQLAlchemy models: UserCreate, UserResponse (not User)
|
||||
4. SQLAlchemy engine: create_engine(url, connect_args={"check_same_thread": False})
|
||||
5. SessionLocal: sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
6. FastAPI dependencies: def get_db(): db = SessionLocal(); try: yield db; finally: db.close()
|
||||
7. Pydantic v2: use model_dump() not dict(), class Config: from_attributes = True
|
||||
8. All CRUD endpoints: POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
|
||||
NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are an expert Python developer. Write complete, production-ready code.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. Include ALL imports at the top of every file
|
||||
2. Import from other project files: from models import User, SessionLocal
|
||||
3. Pydantic schemas use different names than SQLAlchemy models: UserCreate, UserResponse (not User)
|
||||
4. SQLAlchemy engine: create_engine(url, connect_args={"check_same_thread": False})
|
||||
5. SessionLocal: sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
6. FastAPI dependencies: def get_db(): db = SessionLocal(); try: yield db; finally: db.close()
|
||||
7. Pydantic v2: use model_dump() not dict(), class Config: from_attributes = True
|
||||
8. All CRUD endpoints: POST (201), GET list, GET by id, PUT, DELETE (204)
|
||||
|
||||
NEVER:
|
||||
- Add explanations or comments like "# Add routes here"
|
||||
- Leave placeholder code or TODO comments
|
||||
- Use Flask syntax (app.run) in FastAPI projects
|
||||
- Forget to import from other project files
|
||||
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
|
||||
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)
|
||||
|
||||
EXAMPLE of pyproject.toml (for a different project, adapt to this one):
|
||||
```
|
||||
[project]
|
||||
name = "myapp"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"sqlalchemy",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
dev = "uvicorn main:app --reload"
|
||||
```
|
||||
|
||||
Already written files in THIS project:
|
||||
--- models.py ---
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
--- schemas.py ---
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TodoCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
due_date: date
|
||||
status: str = 'pending'
|
||||
|
||||
class TodoResponse(TodoCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
--- main.py ---
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from models import Base, engine, SessionLocal, Todo
|
||||
from schemas import TodoCreate, TodoResponse
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
app = FastAPI()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.post("/todos/", response_model=TodoResponse, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[TodoResponse])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
|
||||
PROJECT REQUIREMENTS (from product owner):
|
||||
1. **PROJECT NAME:** Todo-sovellus
|
||||
|
||||
2. **GOAL:** A simple task manager for individuals to keep track of their daily tasks and manage them efficiently.
|
||||
|
||||
3. **CORE FEATURES:**
|
||||
- Create new todos with title, description, due date.
|
||||
- Read/View all todos or a single todo by ID.
|
||||
- Update existing todos (title, description, status).
|
||||
- Delete completed or unnecessary todos.
|
||||
- Filter todos by status (e.g., pending, completed).
|
||||
|
||||
4. **DATA MODEL:**
|
||||
- Todo
|
||||
- id (integer, primary key)
|
||||
- title (string)
|
||||
- description (text)
|
||||
- due_date (date)
|
||||
- status (enum: 'pending', 'completed')
|
||||
|
||||
5. **API ENDPOINTS:**
|
||||
- GET /todos: Retrieve all todos.
|
||||
- GET /todos/{id}: Retrieve a single todo by ID.
|
||||
- POST /todos: Create a new todo.
|
||||
- PUT /todos/{id}: Update an existing todo.
|
||||
- DELETE /todos/{id}: Delete a todo.
|
||||
|
||||
6. **CONSTRAINTS:**
|
||||
- Must use SQLite as the database.
|
||||
- No authentication required for accessing endpoints.
|
||||
- Implement unit tests and integration tests to ensure functionality.
|
||||
|
||||
NOW write "pyproject.toml" for THIS project: Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
Use [project] format (PEP 621, compatible with uv). List dependencies under [project.dependencies]. Add [project.scripts] with dev command. Never use requirements.txt or Poetry format. Run with: uv run uvicorn main:app --reload
|
||||
Adapt to the project requirements. Import from already written files. Write ONLY the code, no explanations.
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
[project]
|
||||
name = "todo-sovellus"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"sqlalchemy",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
dev = "uvicorn main:app --reload"
|
||||
```
|
||||
174
zipit/projekti_clean/prompts/5_qa_review.md
Normal file
174
zipit/projekti_clean/prompts/5_qa_review.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# 5 — QA (qa) — review
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are a QA engineer responsible for code review and automated testing.
|
||||
|
||||
CODE REVIEW CHECKLIST:
|
||||
1. IMPORTS: Every "from X import Y" must match an actual export in file X
|
||||
2. NAMES: Pydantic schemas (UserCreate) must not shadow SQLAlchemy models (User)
|
||||
3. TYPES: All function parameters have type hints, return types specified
|
||||
4. ERRORS: Every db query that can return None has a 404 check
|
||||
5. RESOURCES: Database session uses yield+finally pattern (no leaks)
|
||||
6. SECURITY: No raw SQL, no hardcoded secrets, inputs validated via Pydantic
|
||||
7. ENDPOINTS: All CRUD operations exist (POST/GET/GET-by-id/PUT/DELETE)
|
||||
8. MODELS: Pydantic Config has from_attributes=True, uses model_dump() not dict()
|
||||
9. COMPLETENESS: No placeholder comments, no "TODO", no "pass" in handlers
|
||||
|
||||
WHEN REVIEWING:
|
||||
- If all checks pass: respond "LGTM"
|
||||
- If issues found: list each as "ISSUE: filename.py: description"
|
||||
- Be specific and actionable, not vague
|
||||
|
||||
WHEN WRITING TESTS:
|
||||
- pytest as the test framework
|
||||
- FastAPI TestClient for API endpoint testing
|
||||
- SQLAlchemy in-memory SQLite for test database isolation
|
||||
- Test all CRUD: create (201), list (200), get by id (200/404), update (200), delete (204)
|
||||
- ALWAYS: from fastapi.testclient import TestClient
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are a QA engineer responsible for code review and automated testing.
|
||||
|
||||
CODE REVIEW CHECKLIST:
|
||||
1. IMPORTS: Every "from X import Y" must match an actual export in file X
|
||||
2. NAMES: Pydantic schemas (UserCreate) must not shadow SQLAlchemy models (User)
|
||||
3. TYPES: All function parameters have type hints, return types specified
|
||||
4. ERRORS: Every db query that can return None has a 404 check
|
||||
5. RESOURCES: Database session uses yield+finally pattern (no leaks)
|
||||
6. SECURITY: No raw SQL, no hardcoded secrets, inputs validated via Pydantic
|
||||
7. ENDPOINTS: All CRUD operations exist (POST/GET/GET-by-id/PUT/DELETE)
|
||||
8. MODELS: Pydantic Config has from_attributes=True, uses model_dump() not dict()
|
||||
9. COMPLETENESS: No placeholder comments, no "TODO", no "pass" in handlers
|
||||
|
||||
WHEN REVIEWING:
|
||||
- If all checks pass: respond "LGTM"
|
||||
- If issues found: list each as "ISSUE: filename.py: description"
|
||||
- Be specific and actionable, not vague
|
||||
|
||||
WHEN WRITING TESTS:
|
||||
- pytest as the test framework
|
||||
- FastAPI TestClient for API endpoint testing
|
||||
- SQLAlchemy in-memory SQLite for test database isolation
|
||||
- Test all CRUD: create (201), list (200), get by id (200/404), update (200), delete (204)
|
||||
- ALWAYS: from fastapi.testclient import TestClient
|
||||
|
||||
Review this project code for issues. If everything is correct, respond with "LGTM". Otherwise list issues as "ISSUE: filename.py: description".
|
||||
|
||||
--- models.py ---
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
--- schemas.py ---
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TodoCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
due_date: date
|
||||
status: str = 'pending'
|
||||
|
||||
class TodoResponse(TodoCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
--- main.py ---
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from models import Base, engine, SessionLocal, Todo
|
||||
from schemas import TodoCreate, TodoResponse
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
app = FastAPI()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.post("/todos/", response_model=TodoResponse, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[TodoResponse])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
|
||||
--- pyproject.toml ---
|
||||
[project]
|
||||
name = "todo-sovellus"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"sqlalchemy",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
dev = "uvicorn main:app --reload"
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
LGTM
|
||||
```
|
||||
291
zipit/projekti_clean/prompts/6_qa_test_main.py.md
Normal file
291
zipit/projekti_clean/prompts/6_qa_test_main.py.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# 6 — QA (qa) — test_main.py
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are a QA engineer responsible for code review and automated testing.
|
||||
|
||||
CODE REVIEW CHECKLIST:
|
||||
1. IMPORTS: Every "from X import Y" must match an actual export in file X
|
||||
2. NAMES: Pydantic schemas (UserCreate) must not shadow SQLAlchemy models (User)
|
||||
3. TYPES: All function parameters have type hints, return types specified
|
||||
4. ERRORS: Every db query that can return None has a 404 check
|
||||
5. RESOURCES: Database session uses yield+finally pattern (no leaks)
|
||||
6. SECURITY: No raw SQL, no hardcoded secrets, inputs validated via Pydantic
|
||||
7. ENDPOINTS: All CRUD operations exist (POST/GET/GET-by-id/PUT/DELETE)
|
||||
8. MODELS: Pydantic Config has from_attributes=True, uses model_dump() not dict()
|
||||
9. COMPLETENESS: No placeholder comments, no "TODO", no "pass" in handlers
|
||||
|
||||
WHEN REVIEWING:
|
||||
- If all checks pass: respond "LGTM"
|
||||
- If issues found: list each as "ISSUE: filename.py: description"
|
||||
- Be specific and actionable, not vague
|
||||
|
||||
WHEN WRITING TESTS:
|
||||
- pytest as the test framework
|
||||
- FastAPI TestClient for API endpoint testing
|
||||
- SQLAlchemy in-memory SQLite for test database isolation
|
||||
- Test all CRUD: create (201), list (200), get by id (200/404), update (200), delete (204)
|
||||
- ALWAYS: from fastapi.testclient import TestClient
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are a QA engineer responsible for code review and automated testing.
|
||||
|
||||
CODE REVIEW CHECKLIST:
|
||||
1. IMPORTS: Every "from X import Y" must match an actual export in file X
|
||||
2. NAMES: Pydantic schemas (UserCreate) must not shadow SQLAlchemy models (User)
|
||||
3. TYPES: All function parameters have type hints, return types specified
|
||||
4. ERRORS: Every db query that can return None has a 404 check
|
||||
5. RESOURCES: Database session uses yield+finally pattern (no leaks)
|
||||
6. SECURITY: No raw SQL, no hardcoded secrets, inputs validated via Pydantic
|
||||
7. ENDPOINTS: All CRUD operations exist (POST/GET/GET-by-id/PUT/DELETE)
|
||||
8. MODELS: Pydantic Config has from_attributes=True, uses model_dump() not dict()
|
||||
9. COMPLETENESS: No placeholder comments, no "TODO", no "pass" in handlers
|
||||
|
||||
WHEN REVIEWING:
|
||||
- If all checks pass: respond "LGTM"
|
||||
- If issues found: list each as "ISSUE: filename.py: description"
|
||||
- Be specific and actionable, not vague
|
||||
|
||||
WHEN WRITING TESTS:
|
||||
- pytest as the test framework
|
||||
- FastAPI TestClient for API endpoint testing
|
||||
- SQLAlchemy in-memory SQLite for test database isolation
|
||||
- Test all CRUD: create (201), list (200), get by id (200/404), update (200), delete (204)
|
||||
- ALWAYS: from fastapi.testclient import TestClient
|
||||
|
||||
Write pytest tests for this project:
|
||||
|
||||
--- models.py ---
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
--- schemas.py ---
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TodoCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
due_date: date
|
||||
status: str = 'pending'
|
||||
|
||||
class TodoResponse(TodoCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
--- main.py ---
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from models import Base, engine, SessionLocal, Todo
|
||||
from schemas import TodoCreate, TodoResponse
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
app = FastAPI()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.post("/todos/", response_model=TodoResponse, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[TodoResponse])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
|
||||
--- pyproject.toml ---
|
||||
[project]
|
||||
name = "todo-sovellus"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"sqlalchemy",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
dev = "uvicorn main:app --reload"
|
||||
|
||||
Write a complete test_main.py file with TestClient.
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./test_todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.post("/todos/", response_model=Todo, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[Todo])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=Todo)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=Todo)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
def test_create_todo():
|
||||
response = client.post("/todos/", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023-12-31"})
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["title"] == "Test Todo"
|
||||
assert data["description"] == "This is a test todo."
|
||||
assert data["due_date"] == "2023-12-31"
|
||||
assert data["status"] == "pending"
|
||||
|
||||
def test_list_todos():
|
||||
response = client.get("/todos/")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data) > 0
|
||||
|
||||
def test_get_todo_by_id():
|
||||
todo_id = create_test_todo().json()["id"]
|
||||
response = client.get(f"/todos/{todo_id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["title"] == "Test Todo"
|
||||
|
||||
def test_get_nonexistent_todo():
|
||||
response = client.get("/todos/999")
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_update_todo():
|
||||
todo_id = create_test_todo().json()["id"]
|
||||
response = client.put(f"/todos/{todo_id}", json={"title": "Updated Todo"})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["title"] == "Updated Todo"
|
||||
|
||||
def test_delete_todo():
|
||||
todo_id = create_test_todo().json()["id"]
|
||||
response = client.delete(f"/todos/{todo_id}")
|
||||
assert response.status_code == 204
|
||||
|
||||
def create_test_todo():
|
||||
return client.post("/todos/", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023
|
||||
```
|
||||
93
zipit/projekti_clean/prompts/7_tester_dockerfile.md
Normal file
93
zipit/projekti_clean/prompts/7_tester_dockerfile.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 7 — DevOps (tester) — Dockerfile
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are a DevOps engineer specializing in containerization and deployment.
|
||||
|
||||
YOUR RESPONSIBILITIES:
|
||||
1. Write production-ready Dockerfiles
|
||||
2. Use multi-stage builds when appropriate
|
||||
3. Follow security best practices (non-root user, minimal base image)
|
||||
4. Configure health checks and proper signal handling
|
||||
|
||||
DOCKERFILE RULES:
|
||||
- Use python:3.12-slim as base
|
||||
- Install uv: COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
- Copy pyproject.toml first, then uv sync, then copy source
|
||||
- Expose appropriate ports
|
||||
- Use uv run for CMD
|
||||
|
||||
Write ONLY the requested files, no explanations.
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are a DevOps engineer specializing in containerization and deployment.
|
||||
|
||||
YOUR RESPONSIBILITIES:
|
||||
1. Write production-ready Dockerfiles
|
||||
2. Use multi-stage builds when appropriate
|
||||
3. Follow security best practices (non-root user, minimal base image)
|
||||
4. Configure health checks and proper signal handling
|
||||
|
||||
DOCKERFILE RULES:
|
||||
- Use python:3.12-slim as base
|
||||
- Install uv: COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
- Copy pyproject.toml first, then uv sync, then copy source
|
||||
- Expose appropriate ports
|
||||
- Use uv run for CMD
|
||||
|
||||
Write ONLY the requested files, no explanations.
|
||||
|
||||
Write a Dockerfile for this Python FastAPI project.
|
||||
|
||||
Project files: models.py, schemas.py, main.py, pyproject.toml, test_main.py
|
||||
|
||||
Requirements:
|
||||
- Use python:3.12-slim as base
|
||||
- Install uv: COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
- Copy pyproject.toml first, then uv sync, then copy source
|
||||
- Expose port 8000
|
||||
- CMD: uv run uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
|
||||
Write ONLY the Dockerfile, no explanations.
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
# Stage 1: Build
|
||||
FROM python:3.12-slim as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY pyproject.toml .
|
||||
RUN pip install --no-cache-dir poetry && \
|
||||
poetry config virtualenvs.create false && \
|
||||
poetry install --no-interaction --no-ansi
|
||||
|
||||
COPY models.py schemas.py main.py test_main.py .
|
||||
|
||||
# Stage 2: Production
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /root/.local/share/python-poetry/cache /root/.local/share/python-poetry/cache
|
||||
COPY --from=builder /app /app
|
||||
|
||||
RUN useradd -m appuser && \
|
||||
chown -R appuser:appuser /app
|
||||
|
||||
USER appuser
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
404
zipit/projekti_clean/prompts/8_observer_readme.md.md
Normal file
404
zipit/projekti_clean/prompts/8_observer_readme.md.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# 8 — Observer (observer) — README.md
|
||||
|
||||
**Malli:** `qwen-coder`
|
||||
|
||||
## System Prompt
|
||||
|
||||
```
|
||||
You are an independent technical observer and risk analyst.
|
||||
|
||||
EVALUATE THE PROJECT FOR:
|
||||
1. ARCHITECTURE: Is the file structure logical? Are responsibilities separated?
|
||||
2. SECURITY: SQL injection risks? Input validation? Authentication?
|
||||
3. RELIABILITY: Error handling? Database connection management? Edge cases?
|
||||
4. MAINTAINABILITY: Consistent naming? Clear code structure? Would a new developer understand this?
|
||||
|
||||
OUTPUT FORMAT:
|
||||
- RISK: [critical/high/medium/low] Description
|
||||
- List max 3-5 most important findings
|
||||
- End with overall assessment: "SHIP IT" or "NEEDS WORK: reason"
|
||||
```
|
||||
|
||||
## Syöte
|
||||
|
||||
```
|
||||
You are an independent technical observer and risk analyst.
|
||||
|
||||
EVALUATE THE PROJECT FOR:
|
||||
1. ARCHITECTURE: Is the file structure logical? Are responsibilities separated?
|
||||
2. SECURITY: SQL injection risks? Input validation? Authentication?
|
||||
3. RELIABILITY: Error handling? Database connection management? Edge cases?
|
||||
4. MAINTAINABILITY: Consistent naming? Clear code structure? Would a new developer understand this?
|
||||
|
||||
OUTPUT FORMAT:
|
||||
- RISK: [critical/high/medium/low] Description
|
||||
- List max 3-5 most important findings
|
||||
- End with overall assessment: "SHIP IT" or "NEEDS WORK: reason"
|
||||
|
||||
Write a project report in clean markdown for: Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
|
||||
FIRST LINE must be exactly one of:
|
||||
VERDICT: GREEN
|
||||
VERDICT: ORANGE
|
||||
VERDICT: RED
|
||||
|
||||
Then write this report:
|
||||
|
||||
# Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
|
||||
|
||||
## Overview
|
||||
One paragraph describing what this project does.
|
||||
|
||||
## Files
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| models.py | ... |
|
||||
| schemas.py | ... |
|
||||
| main.py | ... |
|
||||
| pyproject.toml | ... |
|
||||
| test_main.py | ... |
|
||||
| Dockerfile | ... |
|
||||
|
||||
## Quick Start
|
||||
```bash
|
||||
git clone <repo>
|
||||
cd project
|
||||
uv sync
|
||||
uv run uvicorn main:app --reload
|
||||
```
|
||||
|
||||
## Docker
|
||||
```bash
|
||||
docker build -t todo-sovellus-fastapi---sqlite--crud-endpointit-ja-testit .
|
||||
docker run -p 8000:8000 todo-sovellus-fastapi---sqlite--crud-endpointit-ja-testit
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | /items/ | Create |
|
||||
| GET | /items/ | List all |
|
||||
| GET | /items/{id} | Get by ID |
|
||||
| PUT | /items/{id} | Update |
|
||||
| DELETE | /items/{id} | Delete |
|
||||
(Adapt paths and descriptions to match the actual code)
|
||||
|
||||
## Architecture
|
||||
Describe the project structure and design decisions.
|
||||
|
||||
## Risk Assessment
|
||||
| Severity | Issue |
|
||||
|----------|-------|
|
||||
| ... | ... |
|
||||
|
||||
Project code:
|
||||
--- models.py ---
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
--- schemas.py ---
|
||||
from pydantic import BaseModel
|
||||
|
||||
class TodoCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
due_date: date
|
||||
status: str = 'pending'
|
||||
|
||||
class TodoResponse(TodoCreate):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
--- main.py ---
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from models import Base, engine, SessionLocal, Todo
|
||||
from schemas import TodoCreate, TodoResponse
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
app = FastAPI()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.post("/todos/", response_model=TodoResponse, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[TodoResponse])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=TodoResponse)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
|
||||
--- pyproject.toml ---
|
||||
[project]
|
||||
name = "todo-sovellus"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"sqlalchemy",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
dev = "uvicorn main:app --reload"
|
||||
|
||||
--- test_main.py ---
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
DATABASE_URL = "sqlite:///./test_todo.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String(100), nullable=False)
|
||||
description = Column(String(500))
|
||||
due_date = Column(Date, nullable=False)
|
||||
status = Column(Enum('pending', 'completed'), default='pending')
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.post("/todos/", response_model=Todo, status_code=201)
|
||||
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = Todo(**todo.model_dump())
|
||||
db.add(db_todo)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.get("/todos/", response_model=list[Todo])
|
||||
def list_todos(status: str | None = None, db: Session = Depends(get_db)):
|
||||
if status:
|
||||
query = db.query(Todo).filter_by(status=status)
|
||||
else:
|
||||
query = db.query(Todo)
|
||||
return query.all()
|
||||
|
||||
@app.get("/todos/{todo_id}", response_model=Todo)
|
||||
def get_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return todo
|
||||
|
||||
@app.put("/todos/{todo_id}", response_model=Todo)
|
||||
def update_todo(todo_id: int, todo: TodoCreate, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
for key, value in todo.model_dump().items():
|
||||
setattr(db_todo, key, value)
|
||||
db.commit()
|
||||
db.refresh(db_todo)
|
||||
return db_todo
|
||||
|
||||
@app.delete("/todos/{todo_id}", status_code=204)
|
||||
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
|
||||
db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
|
||||
if not db_todo:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
db.delete(db_todo)
|
||||
db.commit()
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
def test_create_todo():
|
||||
response = client.post("/todos/", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023-12-31"})
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["title"] == "Test Todo"
|
||||
assert data["description"] == "This is a test todo."
|
||||
assert data["due_date"] == "2023-12-31"
|
||||
assert data["status"] == "pending"
|
||||
|
||||
def test_list_todos():
|
||||
response = client.get("/todos/")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data) > 0
|
||||
|
||||
def test_get_todo_by_id():
|
||||
todo_id = create_test_todo().json()["id"]
|
||||
response = client.get(f"/todos/{todo_id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["title"] == "Test Todo"
|
||||
|
||||
def test_get_nonexistent_todo():
|
||||
response = client.get("/todos/999")
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_update_todo():
|
||||
todo_id = create_test_todo().json()["id"]
|
||||
response = client.put(f"/todos/{todo_id}", json={"title": "Updated Todo"})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["title"] == "Updated Todo"
|
||||
|
||||
def test_delete_todo():
|
||||
todo_id = create_test_todo().json()["id"]
|
||||
response = client.delete(f"/todos/{todo_id}")
|
||||
assert response.status_code == 204
|
||||
|
||||
def create_test_todo():
|
||||
return client.post("/todos/", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023
|
||||
|
||||
--- Dockerfile ---
|
||||
# Stage 1: Build
|
||||
FROM python:3.12-slim as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY pyproject.toml .
|
||||
RUN pip install --no-cache-dir poetry && \
|
||||
poetry config virtualenvs.create false && \
|
||||
poetry install --no-interaction --no-ansi
|
||||
|
||||
COPY models.py schemas.py main.py test_main.py .
|
||||
|
||||
# Stage 2: Production
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /root/.local/share/python-poetry/cache /root/.local/share/python-poetry/cache
|
||||
COPY --from=builder /app /app
|
||||
|
||||
RUN useradd -m appuser && \
|
||||
chown -R appuser:appuser /app
|
||||
|
||||
USER appuser
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
## Tulos
|
||||
|
||||
```
|
||||
VERDICT: GREEN
|
||||
|
||||
## Overview
|
||||
This project is a simple Todo application using FastAPI for the backend and SQLite as the database. It includes CRUD endpoints for managing tasks.
|
||||
|
||||
## Files
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| models.py | Defines the data model for the todo items using SQLAlchemy ORM. |
|
||||
| schemas.py | Contains Pydantic schemas for validating request and response data. |
|
||||
| main.py | The FastAPI application, defining routes and business logic. |
|
||||
| pyproject.toml | Project metadata and dependencies. |
|
||||
| test_main.py | Unit tests for the FastAPI endpoints. |
|
||||
| Dockerfile | Docker configuration to build and run the application. |
|
||||
|
||||
## Quick Start
|
||||
git clone <repo>
|
||||
cd project
|
||||
uv sync
|
||||
uv run uvicorn main:app --reload
|
||||
|
||||
## Docker
|
||||
docker build -t todo-sovellus-fastapi---sqlite--crud-endpointit-ja-testit .
|
||||
docker run -p 8000:8000 todo-sovellus-fastapi---sqlite--crud-endpointit-ja-testit
|
||||
|
||||
## API Endpoints
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | /todos/ | Create a new todo item. |
|
||||
| GET | /todos/ | List all todos or filter by status. |
|
||||
| GET | /todos/{id} | Retrieve a specific todo item by ID. |
|
||||
| PUT | /todos/{id} | Update an existing todo item. |
|
||||
| DELETE | /todos/{id} | Delete a todo item. |
|
||||
|
||||
## Architecture
|
||||
The project follows a clean separation of concerns:
|
||||
- **models.py**: Defines the data model using SQLAlchemy ORM.
|
||||
- **schemas.py**: Contains Pydantic models for data validation and serialization.
|
||||
- **main.py**: Implements FastAPI routes, business logic, and database interactions.
|
||||
|
||||
This logical structure makes it easy to understand and maintain each component independently.
|
||||
|
||||
## Risk Assessment
|
||||
| Severity | Issue |
|
||||
|----------|-------|
|
||||
| LOW | The project uses parameterized queries in the SQLAlchemy ORM which helps prevent SQL injection. However, input validation is minimal (e.g., no checks for `due_date` format). |
|
||||
| MEDIUM | Error handling and database connection management are basic but adequate. Database connections are managed via context managers, which ensures proper closure of sessions. |
|
||||
| LOW | The project uses consistent naming conventions for variables and functions, making the code easy to understand. However, there's room for improving documentation around API endpoints and models. |
|
||||
|
||||
Overall assessment: "SHIP IT"
|
||||
```
|
||||
Reference in New Issue
Block a user