Templates for different tasks

This commit is contained in:
Jaakko Vanhala
2026-04-12 20:02:25 +03:00
parent b2ee8b9031
commit 6ef71b7e5c
169 changed files with 5 additions and 12772 deletions

5
.gitignore vendored
View File

@@ -42,4 +42,7 @@ Cargo.lock
*.log *.log
# Wanha versio # Wanha versio
temp/ temp/
# Muut
zipit/**

Submodule projektit/projektiopinnot-1-datan-hallinta-ttm23sai added at c20e918b34

View File

@@ -1,16 +0,0 @@
FROM python:3.12-slim as builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY . .
USER 1000
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,76 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime
from models import Todo, SessionLocal
from schemas import TodoCreate, TodoResponse
app = FastAPI()
# Dependency to get database session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/todos", response_model=TodoResponse, status_code=201)
async 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])
async def read_todos(status: Optional[str] = Query(None), db: Session = Depends(get_db)):
if status:
todos = db.query(Todo).filter(Todo.status == status).all()
else:
todos = db.query(Todo).all()
return todos
@app.get("/todos/{id}", response_model=TodoResponse)
async def read_todo(id: int, db: Session = Depends(get_db)):
todo = db.query(Todo).filter(Todo.id == id).first()
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
return todo
@app.put("/todos/{id}", response_model=TodoResponse)
async def update_todo(id: int, todo: TodoCreate, db: Session = Depends(get_db)):
db_todo = db.query(Todo).filter(Todo.id == id).first()
if not db_todo:
raise HTTPException(status_code=404, detail="Todo 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/{id}", status_code=204)
async def delete_todo(id: int, db: Session = Depends(get_db)):
db_todo = db.query(Todo).filter(Todo.id == id).first()
if not db_todo:
raise HTTPException(status_code=404, detail="Todo not found")
db.delete(db_todo)
db.commit()
@app.patch("/todos/{id}/complete", status_code=204)
async def complete_todo(id: int, db: Session = Depends(get_db)):
db_todo = db.query(Todo).filter(Todo.id == id).first()
if not db_todo:
raise HTTPException(status_code=404, detail="Todo not found")
db_todo.status = 'completed'
db.commit()
@app.get("/todos/count", response_model=int)
async def get_todo_count(db: Session = Depends(get_db)):
return db.query(Todo).count()
@app.get("/todos/search?q={query}", response_model=List[TodoResponse])
async def search_todos(query: str, db: Session = Depends(get_db)):
todos = db.query(Todo).filter(Todo.title.contains(query)).all()
return todos

View File

@@ -1,23 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Text, Enum, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
DATABASE_URL = "sqlite:///./todos.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class TodoStatus(Enum):
completed = 'completed'
pending = 'pending'
class Todo(Base):
__tablename__ = "todos"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
status = Column(Enum(TodoStatus), default=TodoStatus.pending)
created_at = Column(DateTime, default=datetime.utcnow)
Base.metadata.create_all(bind=engine)

View File

@@ -1,32 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple todo application using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy",
"python-dotenv"
]
[tool.poetry]
name = "todo-app"
version = "0.1.0"
description = "A simple todo application using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
[tool.poetry.dependencies]
fastapi = "^0.85.0"
uvicorn = { version = "^0.20.0", extras = ["standard"] }
sqlalchemy = "^1.4.36"
python-dotenv = "^0.21.0"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

View File

@@ -1,105 +0,0 @@
{
"run_id": "baseline_v1",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 7.5,
"errors": [],
"code_length": 1196
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.8,
"errors": [],
"code_length": 824
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 4.0,
"errors": [],
"code_length": 361
}
],
"final_errors": [],
"passed": true
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 18.9,
"errors": [],
"code_length": 2617
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 7.9,
"errors": [],
"code_length": 724
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 15.3,
"errors": [],
"code_length": 1689
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 3.3,
"errors": [],
"code_length": 249
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "7/7 passed"
}

View File

@@ -1,34 +0,0 @@
**PROJECT NAME:** Todo Manager
**GOAL:** A simple todo application for individuals to manage their tasks efficiently.
**CORE FEATURES:**
1. Create a new todo item with title and description.
2. Read (view) all todos or filter by status (e.g., completed, pending).
3. Update an existing todo item's details.
4. Delete a todo item.
5. Mark a todo as completed.
6. View the total number of todos.
7. Search for todos by partial title.
**DATA MODEL:**
1. **Todo**
- id (integer, primary key)
- title (string, required)
- description (text, optional)
- status (enum [completed, pending], default: pending)
- created_at (timestamp, auto-generated)
**API ENDPOINTS:**
1. **POST /todos**: Create a new todo item.
2. **GET /todos**: Retrieve all todos.
3. **GET /todos/status/{status}**: Filter todos by status.
4. **PUT /todos/{id}**: Update an existing todo item.
5. **DELETE /todos/{id}**: Delete a todo item.
6. **PATCH /todos/{id}/complete**: Mark a todo as completed.
7. **GET /todos/count**: Get the total number of todos.
8. **GET /todos/search?q={query}**: Search for todos by partial title.
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required.

View File

@@ -1,16 +0,0 @@
from pydantic import BaseModel, Field
from datetime import datetime
class TodoCreate(BaseModel):
title: str = Field(..., max_length=255)
description: str | None = Field(None)
class TodoResponse(BaseModel):
id: int
title: str
description: str | None
status: str
created_at: datetime
class Config:
from_attributes = True

View File

@@ -1,59 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
from main import get_db
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_todo():
response = client.post("/todos", json={"title": "Test Todo"})
assert response.status_code == 201
todo = response.json()
assert todo["title"] == "Test Todo"
assert todo["status"] == "pending"
def test_read_todos():
response = client.get("/todos")
assert response.status_code == 200
todos = response.json()
assert len(todos) == 1
def test_get_todo_by_id():
response = client.get("/todos/1")
assert response.status_code == 200
todo = response.json()
assert todo["id"] == 1
assert todo["title"] == "Test Todo"
def test_get_nonexistent_todo():
response = client.get("/todos/999")
assert response.status_code == 404
def test_update_todo():
response = client.put("/todos/1", json={"title": "Updated Todo"})
assert response.status_code == 200
todo = response.json()
assert todo["id"] == 1
assert todo["title"] == "Updated Todo"
def test_delete_todo():
response = client.delete("/todos/1")
assert response.status_code == 204
def test_complete_todo():
response = client.patch("/todos/1/complete")
assert response.status_code == 204

View File

@@ -1,18 +0,0 @@
```Dockerfile
FROM python:3.12-slim as builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY models.py schemas.py main.py test_main.py .
USER nonrootuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```

View File

@@ -1,64 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import date
from models import Task, engine
from schemas import TaskCreate, TaskResponse
app = FastAPI()
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/tasks/", response_model=TaskResponse, status_code=201)
async def create_task(task: TaskCreate, db: Session = Depends(get_db)):
db_task = Task(**task.model_dump())
db.add(db_task)
db.commit()
db.refresh(db_task)
return db_task
@app.get("/tasks/", response_model=List[TaskResponse])
async def get_tasks(status: Optional[str] = Query(None), db: Session = Depends(get_db)):
if status:
tasks = db.query(Task).filter_by(status=status).all()
else:
tasks = db.query(Task).all()
return tasks
@app.get("/tasks/{task_id}", response_model=TaskResponse)
async def get_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
@app.put("/tasks/{task_id}", response_model=TaskResponse)
async def update_task(task_id: int, task: TaskCreate, db: Session = Depends(get_db)):
db_task = db.query(Task).filter(Task.id == task_id).first()
if not db_task:
raise HTTPException(status_code=404, detail="Task not found")
for key, value in task.model_dump().items():
setattr(db_task, key, value)
db.commit()
db.refresh(db_task)
return db_task
@app.delete("/tasks/{task_id}", status_code=204)
async def delete_task(task_id: int, db: Session = Depends(get_db)):
db_task = db.query(Task).filter(Task.id == task_id).first()
if not db_task:
raise HTTPException(status_code=404, detail="Task not found")
db.delete(db_task)
db.commit()
return

View File

@@ -1,18 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Text, 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 Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(Text)
due_date = Column(Date)
status = Column(Enum('pending', 'completed'), default='pending')
Base.metadata.create_all(bind=engine)

View File

@@ -1,13 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple task management API using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy"
]

View File

@@ -1,113 +0,0 @@
{
"run_id": "v2_strict",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 6.6,
"errors": [],
"code_length": 1190
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.3,
"errors": [
"SQLite create_engine puuttuu connect_args={'check_same_thread': False}"
],
"code_length": 699
},
{
"attempt": 2,
"elapsed": 4.4,
"errors": [],
"code_length": 712
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 5.2,
"errors": [],
"code_length": 494
}
],
"final_errors": [],
"passed": true
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 14.4,
"errors": [],
"code_length": 2036
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 3.3,
"errors": [],
"code_length": 267
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 16.4,
"errors": [],
"code_length": 2071
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 3.5,
"errors": [],
"code_length": 314
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "7/7 passed"
}

View File

@@ -1,32 +0,0 @@
**PROJECT NAME:** Todo-sovellus
**GOAL:** Create a simple task management application for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. Add new tasks with title, description, due date, and status.
2. View all tasks in a list format.
3. Filter tasks by status (e.g., pending, completed).
4. Update task details such as title, description, due date, and status.
5. Delete individual tasks.
6. Mark tasks as complete or incomplete.
7. Search for tasks by keywords in the title or description.
**DATA MODEL:**
1. **Task**
- id (integer, primary key)
- title (string, required)
- description (text, optional)
- due_date (date, optional)
- status (enum ['pending', 'completed'], default='pending')
**API ENDPOINTS:**
1. **GET /tasks** - Retrieve all tasks.
2. **POST /tasks** - Create a new task.
3. **GET /tasks/{id}** - Retrieve a specific task by ID.
4. **PUT /tasks/{id}** - Update an existing task.
5. **DELETE /tasks/{id}** - Delete a specific task.
6. **GET /tasks/status/{status}** - Filter tasks by status.
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TaskCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=255)
description: str | None = Field(None, max_length=255)
due_date: date | None = Field(None)
status: str = Field('pending', regex='^(pending|completed)$')
class TaskResponse(BaseModel):
id: int
title: str
description: str | None
due_date: date | None
status: str
class Config:
from_attributes = True

View File

@@ -1,57 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_task():
response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
assert response.status_code == 201
assert response.json()["title"] == "Test Task"
assert response.json()["description"] == "This is a test task"
def test_get_tasks():
response = client.get("/tasks/")
assert response.status_code == 200
assert len(response.json()) > 0
def test_get_task_by_id():
response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
task_id = response.json()["id"]
response = client.get(f"/tasks/{task_id}")
assert response.status_code == 200
assert response.json()["id"] == task_id
def test_get_task_by_id_not_found():
response = client.get("/tasks/999")
assert response.status_code == 404
def test_update_task():
response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
task_id = response.json()["id"]
updated_data = {"title": "Updated Test Task"}
response = client.put(f"/tasks/{task_id}", json=updated_data)
assert response.status_code == 200
assert response.json()["title"] == "Updated Test Task"
def test_delete_task():
response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
task_id = response.json()["id"]
response = client.delete(f"/tasks/{task_id}")
assert response.status_code == 204
response = client.get(f"/tasks/{task_id}")
assert response.status_code == 404

View File

@@ -1,16 +0,0 @@
FROM python:3.12-slim as builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY . .
USER 1000
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,10 +0,0 @@
services:
app:
build: .
ports:
- "18765:8000"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')"]
interval: 3s
timeout: 5s
retries: 5

View File

@@ -1,64 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import date
from .models import Todo, SessionLocal
from .schemas import TodoCreate, TodoResponse
app = FastAPI()
# Dependency to get database session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/todos", response_model=TodoResponse, status_code=201)
async 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])
async def read_todos(status: Optional[str] = Query(None), db: Session = Depends(get_db)):
if status:
todos = db.query(Todo).filter(Todo.status == status).all()
else:
todos = db.query(Todo).all()
return todos
@app.get("/todos/{todo_id}", response_model=TodoResponse)
async def read_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="Todo not found")
return todo
@app.put("/todos/{todo_id}", response_model=TodoResponse)
async def update_todo(todo_id: int, todo_update: TodoCreate, db: Session = Depends(get_db)):
todo = db.query(Todo).filter(Todo.id == todo_id).first()
if not todo:
raise HTTPException(status_code=404, detail="Todo not found")
todo.title = todo_update.title
todo.description = todo_update.description
todo.due_date = todo_update.due_date
todo.status = todo_update.status
db.commit()
db.refresh(todo)
return todo
@app.delete("/todos/{todo_id}", status_code=204)
async def delete_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="Todo not found")
db.delete(todo)
db.commit()

View File

@@ -1,18 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Text, Date, Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./todos.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(255), nullable=False)
description = Column(Text, nullable=False)
due_date = Column(Date, nullable=False)
status = Column(Enum("pending", "completed"), nullable=False)
Base.metadata.create_all(bind=engine)

View File

@@ -1,16 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple todo app using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy",
"pytest",
"httpx",
"python-dateutil"
]

View File

@@ -1,134 +0,0 @@
{
"run_id": "v3_docker",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 6.2,
"errors": [],
"code_length": 1105
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.4,
"errors": [
"SQLite create_engine puuttuu connect_args={'check_same_thread': False}"
],
"code_length": 672
},
{
"attempt": 2,
"elapsed": 4.6,
"errors": [],
"code_length": 742
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 4.2,
"errors": [],
"code_length": 370
}
],
"final_errors": [],
"passed": true
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 14.9,
"errors": [
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty"
],
"code_length": 1975
},
{
"attempt": 2,
"elapsed": 14.4,
"errors": [],
"code_length": 2058
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 3.6,
"errors": [],
"code_length": 279
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 21.1,
"errors": [],
"code_length": 2415
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 3.3,
"errors": [],
"code_length": 249
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "7/7 passed",
"docker": {
"build": true,
"pytest": false,
"api": false,
"errors": [
"pytest failed (exit 2):\n Network v3_docker_default Creating\n Network v3_docker_default Created\nerror: Failed to initialize cache at `/.cache/uv`\n Caused by: failed to create directory `/.cache/uv`: Permission denied (os error 13)\n\n",
"Kontti ei käynnistynyt 15s:ssa"
]
}
}

View File

@@ -1,30 +0,0 @@
**PROJECT NAME:** TodoSovellus
**GOAL:** To create a simple task management application for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. Create new todos with title, description, due date, and status.
2. Read (view) all todos and filter by status (e.g., pending, completed).
3. Update existing todos to change details like title, description, due date, or status.
4. Delete todos that are no longer needed.
5. Mark todos as completed or pending.
6. View the total number of todos and count by status.
**DATA MODEL:**
1. **Todo**
- id (integer, primary key)
- title (string)
- description (text)
- due_date (date)
- status (enum: "pending", "completed")
**API ENDPOINTS:**
1. **POST /todos** - Create a new todo
2. **GET /todos** - Retrieve all todos, optionally filter by status
3. **GET /todos/{id}** - Retrieve a single todo by ID
4. **PUT /todos/{id}** - Update an existing todo
5. **DELETE /todos/{id}** - Delete a todo
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TodoCreate(BaseModel):
title: str = Field(..., max_length=255)
description: str
due_date: date
status: str = "pending"
class TodoResponse(BaseModel):
id: int
title: str
description: str
due_date: date
status: str
class Config:
from_attributes = True

View File

@@ -1,57 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
from main import get_db
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
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
assert response.json()["title"] == "Test Todo"
assert response.json()["status"] == "pending"
def test_read_todos():
client.post("/todos", json={"title": "Todo 1", "description": "First todo.", "due_date": "2023-12-31"})
client.post("/todos", json={"title": "Todo 2", "description": "Second todo.", "due_date": "2024-01-01"})
response = client.get("/todos")
assert response.status_code == 200
assert len(response.json()) == 2
def test_read_todo():
todo = client.post("/todos", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023-12-31"})
response = client.get(f"/todos/{todo.json()['id']}")
assert response.status_code == 200
assert response.json()["title"] == "Test Todo"
def test_read_todo_not_found():
response = client.get("/todos/999")
assert response.status_code == 404
def test_update_todo():
todo = client.post("/todos", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023-12-31"})
response = client.put(f"/todos/{todo.json()['id']}", json={"title": "Updated Todo", "description": "Updated description.", "due_date": "2024-01-01", "status": "completed"})
assert response.status_code == 200
assert response.json()["title"] == "Updated Todo"
assert response.json()["status"] == "completed"
def test_delete_todo():
todo = client.post("/todos", json={"title": "Test Todo", "description": "This is a test todo.", "due_date": "2023-12-31"})
response = client.delete(f"/todos/{todo.json()['id']}")
assert response.status_code == 204
response = client.get(f"/todos/{todo.json()['id']}")
assert response.status_code == 404

View File

@@ -1,11 +0,0 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
ENV UV_CACHE_DIR=/tmp/uv-cache
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY *.py .
RUN useradd -m appuser
USER appuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,58 +0,0 @@
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List, Optional
from models import Todo, SessionLocal
from schemas import TodoCreate, TodoResponse
app = FastAPI()
# Dependency to get database session
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 read_todos(status: Optional[str] = None, db: Session = Depends(get_db)):
if status:
todos = db.query(Todo).filter(Todo.status == status).all()
else:
todos = db.query(Todo).all()
return todos
@app.get("/todos/{todo_id}", response_model=TodoResponse)
def read_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="Todo 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="Todo 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="Todo not found")
db.delete(db_todo)
db.commit()

View File

@@ -1,18 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Text, Date, Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./todos.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(255), nullable=False)
description = Column(Text, nullable=True)
due_date = Column(Date, nullable=True)
status = Column(Enum('pending', 'completed'), nullable=False)
Base.metadata.create_all(bind=engine)

View File

@@ -1,13 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple todo application using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy"
]

View File

@@ -1,145 +0,0 @@
{
"run_id": "v4_docker",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 6.6,
"errors": [],
"code_length": 1106
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.4,
"errors": [
"SQLite create_engine puuttuu connect_args={'check_same_thread': False}"
],
"code_length": 696
},
{
"attempt": 2,
"elapsed": 4.7,
"errors": [],
"code_length": 740
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 5.1,
"errors": [
"SyntaxError: invalid syntax (rivi 8)"
],
"code_length": 472
},
{
"attempt": 2,
"elapsed": 2.9,
"errors": [
"SyntaxError: invalid syntax (rivi 8)"
],
"code_length": 372
},
{
"attempt": 3,
"elapsed": 3.5,
"errors": [
"SyntaxError: invalid syntax (rivi 8)"
],
"code_length": 472
}
],
"final_errors": [
"SyntaxError: invalid syntax (rivi 8)"
],
"passed": false
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 14.6,
"errors": [
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'Depends()' mutta sitä ei ole importattu eikä määritelty"
],
"code_length": 1984
},
{
"attempt": 2,
"elapsed": 14.0,
"errors": [],
"code_length": 1911
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 3.6,
"errors": [],
"code_length": 264
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 15.9,
"errors": [],
"code_length": 1779
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 4.0,
"errors": [],
"code_length": 291
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "6/7 passed"
}

View File

@@ -1,30 +0,0 @@
**PROJECT NAME:** Todo-sovellus
**GOAL:** The software is a simple task manager for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. Create new todos with title, description, due date, and status.
2. Read (view) all todos or filter by status (e.g., pending, completed).
3. Update existing todos (change title, description, due date, status).
4. Delete todos that are no longer needed.
5. Mark a todo as complete.
6. View the total number of todos and count by status.
**DATA MODEL:**
1. **Todo**
- id (integer, primary key)
- title (string, required)
- description (text, optional)
- due_date (date, optional)
- status (enum: 'pending', 'completed')
**API ENDPOINTS:**
1. **GET /todos** - Retrieve all todos.
2. **GET /todos?status=...** - Filter todos by status (e.g., `/todos?status=pending`).
3. **POST /todos** - Create a new todo.
4. **PUT /todos/{id}** - Update an existing todo.
5. **DELETE /todos/{id}** - Delete a todo.
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TodoCreate(BaseModel):
title: str = Field(..., max_length=255)
description: str | None = Field(None, max_length=255)
due_date: date | None = Field(None)
status: str = Field(..., in=["pending", "completed"])
class TodoResponse(BaseModel):
id: int
title: str
description: str | None
due_date: date | None
status: str
class Config:
from_attributes = True

View File

@@ -1,59 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
from main import get_db
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_todo():
response = client.post("/todos", json={"title": "Test Todo", "status": "pending"})
assert response.status_code == 201
todo = response.json()
assert todo["title"] == "Test Todo"
assert todo["status"] == "pending"
def test_read_todos():
response = client.get("/todos")
assert response.status_code == 200
todos = response.json()
assert len(todos) == 1
def test_get_todo_by_id():
response = client.get("/todos/1")
assert response.status_code == 200
todo = response.json()
assert todo["id"] == 1
assert todo["title"] == "Test Todo"
assert todo["status"] == "pending"
def test_get_todo_by_id_not_found():
response = client.get("/todos/999")
assert response.status_code == 404
def test_update_todo():
response = client.put("/todos/1", json={"title": "Updated Todo", "status": "completed"})
assert response.status_code == 200
todo = response.json()
assert todo["id"] == 1
assert todo["title"] == "Updated Todo"
assert todo["status"] == "completed"
def test_delete_todo():
response = client.delete("/todos/1")
assert response.status_code == 204
response = client.get("/todos/1")
assert response.status_code == 404

View File

@@ -1,11 +0,0 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
ENV UV_CACHE_DIR=/tmp/uv-cache
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY *.py .
RUN useradd -m appuser
USER appuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,74 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from pydantic.v1 import model_dump
from models import Task, User, Base, engine
from schemas import TaskCreate, TaskResponse
from sqlalchemy.exc import SQLAlchemyError
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/tasks/", response_model=TaskResponse, status_code=201)
async def create_task(task: TaskCreate, db: Session = Depends(get_db)):
db_task = Task(**model_dump(task))
db.add(db_task)
try:
db.commit()
db.refresh(db_task)
except SQLAlchemyError as e:
raise HTTPException(status_code=400, detail=str(e))
return db_task
@app.get("/tasks/", response_model=List[TaskResponse])
async def read_tasks(
status: Optional[str] = Query(None),
db: Session = Depends(get_db)
):
query = db.query(Task)
if status:
query = query.filter(Task.status == status)
tasks = query.all()
return tasks
@app.get("/tasks/{task_id}", response_model=TaskResponse)
async def read_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
@app.put("/tasks/{task_id}", response_model=TaskResponse)
async def update_task(task_id: int, task: TaskCreate, db: Session = Depends(get_db)):
db_task = db.query(Task).filter(Task.id == task_id).first()
if not db_task:
raise HTTPException(status_code=404, detail="Task not found")
for key, value in model_dump(task).items():
setattr(db_task, key, value)
try:
db.commit()
db.refresh(db_task)
except SQLAlchemyError as e:
raise HTTPException(status_code=400, detail=str(e))
return db_task
@app.delete("/tasks/{task_id}", status_code=204)
async def delete_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
try:
db.delete(task)
db.commit()
except SQLAlchemyError as e:
raise HTTPException(status_code=400, detail=str(e))

View File

@@ -1,25 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Text, 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 Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
due_date = Column(Date, nullable=True)
status = Column(Enum('pending', 'completed'), default='pending')
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(255), unique=True, nullable=False)
email = Column(String(255), unique=True, nullable=False)
password_hash = Column(String(255), nullable=False)
Base.metadata.create_all(bind=engine)

View File

@@ -1,13 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple task management API"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy"
]

View File

@@ -1,163 +0,0 @@
{
"run_id": "v5_feedback",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 9.1,
"errors": [],
"code_length": 1560
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 7.5,
"errors": [
"SQLite create_engine puuttuu connect_args={'check_same_thread': False}"
],
"code_length": 982
},
{
"attempt": 2,
"elapsed": 7.6,
"errors": [],
"code_length": 1025
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 6.2,
"errors": [
"Schema puuttuu kenttiä jotka ovat modelissa: {'password_hash', 'email', 'username'}"
],
"code_length": 494
},
{
"attempt": 2,
"elapsed": 4.6,
"errors": [
"Schema puuttuu kenttiä jotka ovat modelissa: {'password_hash', 'email', 'username'}"
],
"code_length": 494
},
{
"attempt": 3,
"elapsed": 4.0,
"errors": [
"Schema puuttuu kenttiä jotka ovat modelissa: {'password_hash', 'email', 'username'}"
],
"code_length": 494
}
],
"final_errors": [
"Schema puuttuu kenttiä jotka ovat modelissa: {'password_hash', 'email', 'username'}"
],
"passed": false
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 18.9,
"errors": [
"Kutsutaan 'create_engine()' mutta sitä ei ole importattu eikä määritelty",
"Kutsutaan 'sessionmaker()' mutta sitä ei ole importattu eikä määritelty"
],
"code_length": 2531
},
{
"attempt": 2,
"elapsed": 18.7,
"errors": [
"Kutsutaan 'SessionLocal()' mutta sitä ei ole importattu eikä määritelty"
],
"code_length": 2337
},
{
"attempt": 3,
"elapsed": 18.4,
"errors": [
"Kutsutaan 'SessionLocal()' mutta sitä ei ole importattu eikä määritelty"
],
"code_length": 2337
}
],
"final_errors": [
"Kutsutaan 'SessionLocal()' mutta sitä ei ole importattu eikä määritelty"
],
"passed": false
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 7.4,
"errors": [
"Sisältää poetry-konfiguraation — käytä VAIN [project] (PEP 621) + uv",
"build-backend käyttää poetryä — poista tai vaihda"
],
"code_length": 647
},
{
"attempt": 2,
"elapsed": 2.9,
"errors": [],
"code_length": 238
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 14.1,
"errors": [],
"code_length": 1567
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 4.1,
"errors": [],
"code_length": 291
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "5/7 passed"
}

View File

@@ -1,40 +0,0 @@
**PROJECT NAME:** Todo-sovellus
**GOAL:** Create a simple task management application for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. Add new tasks with title, description, due date, and status.
2. View all tasks with filtering by status (e.g., pending, completed).
3. Update existing tasks including changing the title, description, due date, and status.
4. Delete tasks that are no longer needed.
5. Mark tasks as completed or pending.
6. Search for tasks by keywords in the title or description.
7. Export task list to a CSV file.
**DATA MODEL:**
1. **Task**
- id (integer, primary key)
- title (string, required)
- description (text, optional)
- due_date (date, optional)
- status (enum ['pending', 'completed'], default 'pending')
2. **User**
- id (integer, primary key)
- username (string, unique, required)
- email (string, unique, required)
- password_hash (string, required)
**API ENDPOINTS:**
1. **GET /tasks** - Retrieve all tasks with optional filters by status.
2. **POST /tasks** - Create a new task.
3. **GET /tasks/{task_id}** - Retrieve a specific task by ID.
4. **PUT /tasks/{task_id}** - Update an existing task.
5. **DELETE /tasks/{task_id}** - Delete a task.
6. **POST /users/register** - Register a new user.
7. **POST /users/login** - Authenticate and return a token for authorized access.
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing CRUD operations.
- Implement unit tests for all API endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TaskCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=255)
description: str | None = Field(None, max_length=255)
due_date: date | None = Field(None)
status: str = Field('pending', regex='^(pending|completed)$')
class TaskResponse(BaseModel):
id: int
title: str
description: str | None
due_date: date | None
status: str
class Config:
from_attributes = True

View File

@@ -1,51 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_task():
response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
assert response.status_code == 201
assert response.json()["title"] == "Test Task"
assert response.json()["description"] == "This is a test task"
def test_read_tasks():
response = client.get("/tasks/")
assert response.status_code == 200
assert len(response.json()) == 1
def test_get_task_by_id():
response = client.get("/tasks/1")
assert response.status_code == 200
assert response.json()["id"] == 1
def test_get_task_by_id_not_found():
response = client.get("/tasks/999")
assert response.status_code == 404
def test_update_task():
response = client.put("/tasks/1", json={"title": "Updated Task"})
assert response.status_code == 200
assert response.json()["title"] == "Updated Task"
def test_delete_task():
response = client.delete("/tasks/1")
assert response.status_code == 204
response = client.get("/tasks/1")
assert response.status_code == 404

View File

@@ -1,11 +0,0 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
ENV UV_CACHE_DIR=/tmp/uv-cache
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY *.py .
RUN useradd -m appuser
USER appuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,62 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends
from sqlalchemy.orm import Session
from typing import List
from models import Task, SessionLocal
from schemas import TaskCreate, TaskResponse
app = FastAPI()
# Dependency to get database session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/tasks/", response_model=TaskResponse, status_code=201)
def create_task(task: TaskCreate, db: Session = Depends(get_db)):
db_task = Task(**task.model_dump())
db.add(db_task)
db.commit()
db.refresh(db_task)
return db_task
@app.get("/tasks/", response_model=List[TaskResponse])
def read_tasks(status: str = None, db: Session = Depends(get_db)):
if status:
tasks = db.query(Task).filter_by(status=status).all()
else:
tasks = db.query(Task).all()
return tasks
@app.get("/tasks/{task_id}", response_model=TaskResponse)
def read_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
@app.put("/tasks/{task_id}", response_model=TaskResponse)
def update_task(task_id: int, task: TaskCreate, db: Session = Depends(get_db)):
db_task = db.query(Task).filter(Task.id == task_id).first()
if not db_task:
raise HTTPException(status_code=404, detail="Task not found")
for key, value in task.model_dump().items():
setattr(db_task, key, value)
db.commit()
db.refresh(db_task)
return db_task
@app.delete("/tasks/{task_id}", status_code=204)
def delete_task(task_id: int, db: Session = Depends(get_db)):
db_task = db.query(Task).filter(Task.id == task_id).first()
if not db_task:
raise HTTPException(status_code=404, detail="Task not found")
db.delete(db_task)
db.commit()
return

View File

@@ -1,23 +0,0 @@
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:///./todos.db"
engine = create_engine(DATABASE_URL, connect_args={'check_same_thread': False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class TaskStatus(Enum):
pending = 'pending'
completed = 'completed'
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(String(1000))
due_date = Column(Date)
status = Column(Enum(TaskStatus), default=TaskStatus.pending)
Base.metadata.create_all(bind=engine)

View File

@@ -1,15 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple task management API using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy",
"python-dotenv",
"pydantic"
]

View File

@@ -1,126 +0,0 @@
{
"run_id": "v6_stable",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 7.7,
"errors": [],
"code_length": 1361
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.9,
"errors": [
"SQLite create_engine puuttuu connect_args={'check_same_thread': False}",
"Luokka 'TaskStatus' perii Enum:in mutta 'from enum import Enum' puuttuu (SQLAlchemy Enum ei ole Python Enum)"
],
"code_length": 751
},
{
"attempt": 2,
"elapsed": 5.6,
"errors": [
"Luokka 'TaskStatus' perii Enum:in mutta 'from enum import Enum' puuttuu (SQLAlchemy Enum ei ole Python Enum)"
],
"code_length": 794
},
{
"attempt": 3,
"elapsed": 5.5,
"errors": [
"Luokka 'TaskStatus' perii Enum:in mutta 'from enum import Enum' puuttuu (SQLAlchemy Enum ei ole Python Enum)"
],
"code_length": 794
}
],
"final_errors": [
"Luokka 'TaskStatus' perii Enum:in mutta 'from enum import Enum' puuttuu (SQLAlchemy Enum ei ole Python Enum)"
],
"passed": false
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 5.4,
"errors": [],
"code_length": 481
}
],
"final_errors": [],
"passed": true
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 14.1,
"errors": [],
"code_length": 1914
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 3.6,
"errors": [],
"code_length": 304
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 16.9,
"errors": [],
"code_length": 2108
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 4.0,
"errors": [],
"code_length": 291
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "6/7 passed"
}

View File

@@ -1,32 +0,0 @@
**PROJECT NAME:** TodoSovellus
**GOAL:** Create a simple task management application for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. **Add Task**: Users can add new tasks with a title, description, due date, and status (e.g., pending, completed).
2. **View Tasks**: Users can view all tasks or filter by status (pending, completed).
3. **Edit Task**: Users can update the details of an existing task.
4. **Delete Task**: Users can remove tasks that are no longer needed.
5. **Mark as Completed**: Users can mark a task as completed.
6. **Filter Tasks**: Users can filter tasks based on their status (pending, completed).
7. **Task Details**: Users can view detailed information about each task.
**DATA MODEL:**
1. **Tasks**
- id (integer, primary key)
- title (string)
- description (text)
- due_date (date)
- status (enum: pending, completed)
**API ENDPOINTS:**
1. **GET /tasks**: Retrieve all tasks.
2. **POST /tasks**: Create a new task.
3. **GET /tasks/{id}**: Retrieve details of a specific task by ID.
4. **PUT /tasks/{id}**: Update an existing task.
5. **DELETE /tasks/{id}**: Delete a task.
6. **GET /tasks/status/{status}**: Filter tasks by status (pending, completed).
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TaskCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=255)
description: str = Field(None, min_length=0, max_length=1000)
due_date: date = Field(None)
status: str = Field('pending', regex='^(pending|completed)$')
class TaskResponse(BaseModel):
id: int
title: str
description: str
due_date: date
status: str
class Config:
from_attributes = True

View File

@@ -1,58 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
from main import get_db
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_task():
response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
assert response.status_code == 201
assert response.json()["title"] == "Test Task"
assert response.json()["status"] == "pending"
def test_read_tasks():
response = client.get("/tasks/")
assert response.status_code == 200
assert len(response.json()) == 1
def test_get_task_by_id():
task_response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
task_id = task_response.json()["id"]
response = client.get(f"/tasks/{task_id}")
assert response.status_code == 200
assert response.json()["id"] == task_id
def test_get_task_by_id_not_found():
response = client.get("/tasks/999")
assert response.status_code == 404
def test_update_task():
task_response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
task_id = task_response.json()["id"]
update_data = {"title": "Updated Test Task"}
response = client.put(f"/tasks/{task_id}", json=update_data)
assert response.status_code == 200
assert response.json()["title"] == "Updated Test Task"
def test_delete_task():
task_response = client.post("/tasks/", json={"title": "Test Task", "description": "This is a test task"})
task_id = task_response.json()["id"]
response = client.delete(f"/tasks/{task_id}")
assert response.status_code == 204
response = client.get(f"/tasks/{task_id}")
assert response.status_code == 404

View File

@@ -1,11 +0,0 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
ENV UV_CACHE_DIR=/tmp/uv-cache
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY *.py .
RUN useradd -m appuser
USER appuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,10 +0,0 @@
services:
app:
build: .
ports:
- "18765:8000"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')"]
interval: 3s
timeout: 5s
retries: 5

View File

@@ -1,63 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends
from sqlalchemy.orm import Session
from typing import List
from datetime import date
from models import Task, SessionLocal
from schemas import TaskCreate, TaskResponse
app = FastAPI()
# Dependency to get database session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/tasks/", response_model=TaskResponse, status_code=201)
async def create_task(task: TaskCreate, db: Session = Depends(get_db)):
db_task = Task(**task.model_dump())
db.add(db_task)
db.commit()
db.refresh(db_task)
return db_task
@app.get("/tasks/", response_model=List[TaskResponse])
async def read_tasks(status: str = None, db: Session = Depends(get_db)):
if status:
tasks = db.query(Task).filter_by(status=status).all()
else:
tasks = db.query(Task).all()
return tasks
@app.get("/tasks/{task_id}", response_model=TaskResponse)
async def read_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
@app.put("/tasks/{task_id}", response_model=TaskResponse)
async def update_task(task_id: int, task_update: TaskCreate, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
for key, value in task_update.model_dump().items():
setattr(task, key, value)
db.commit()
db.refresh(task)
return task
@app.delete("/tasks/{task_id}", status_code=204)
async def delete_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
db.delete(task)
db.commit()
return

View File

@@ -1,18 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Date
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./todos.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(String(1000))
due_date = Column(Date, nullable=False)
status = Column(String(20), default="pending")
Base.metadata.create_all(bind=engine)

View File

@@ -1,17 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple task management API using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy",
"pytest",
"httpx",
"python-dotenv",
"pydantic>=2.0.0"
]

View File

@@ -1,123 +0,0 @@
{
"run_id": "v7_enum_fix",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 7.6,
"errors": [],
"code_length": 1361
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.9,
"errors": [],
"code_length": 707
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 5.4,
"errors": [],
"code_length": 464
}
],
"final_errors": [],
"passed": true
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 14.1,
"errors": [],
"code_length": 1960
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 8.6,
"errors": [
"Sisältää poetry-konfiguraation — käytä VAIN [project] (PEP 621) + uv",
"build-backend käyttää poetryä — poista tai vaihda"
],
"code_length": 815
},
{
"attempt": 2,
"elapsed": 3.6,
"errors": [],
"code_length": 311
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 20.3,
"errors": [],
"code_length": 2050
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 4.5,
"errors": [],
"code_length": 291
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "7/7 passed",
"docker": {
"build": true,
"pytest": false,
"api": false,
"errors": [
"pytest failed (exit 2):\n Network v7_enum_fix_default Creating\n Network v7_enum_fix_default Created\nerror: Failed to initialize cache at `/tmp/uv-cache`\n Caused by: failed to open file `/tmp/uv-cache/sdists-v9/.git`: Permission denied (os error 13)\n\n",
"Kontti ei käynnistynyt 15s:ssa"
]
}
}

View File

@@ -1,32 +0,0 @@
**PROJECT NAME:** TodoSovellus
**GOAL:** Create a simple task management application for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. **Add Task**: Users can add new tasks with a title, description, due date, and status (e.g., pending, completed).
2. **View Tasks**: Users can view all tasks or filter by status (pending, completed).
3. **Edit Task**: Users can update the details of an existing task.
4. **Delete Task**: Users can remove tasks that are no longer needed.
5. **Mark as Completed**: Users can mark a task as completed.
6. **Filter Tasks**: Users can filter tasks based on their status (pending, completed).
7. **Task Details**: Users can view detailed information about each task.
**DATA MODEL:**
1. **Tasks**
- id (integer, primary key)
- title (string)
- description (text)
- due_date (date)
- status (enum: pending, completed)
**API ENDPOINTS:**
1. **GET /tasks**: Retrieve all tasks.
2. **POST /tasks**: Create a new task.
3. **GET /tasks/{id}**: Retrieve details of a specific task by ID.
4. **PUT /tasks/{id}**: Update an existing task.
5. **DELETE /tasks/{id}**: Delete a task.
6. **GET /tasks/status/{status}**: Filter tasks by status (pending, completed).
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TaskCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=255)
description: str = Field(None, max_length=1000)
due_date: date = Field(...)
status: str = Field("pending", min_length=1, max_length=20)
class TaskResponse(BaseModel):
id: int
title: str
description: str
due_date: date
status: str
class Config:
from_attributes = True

View File

@@ -1,56 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
from main import get_db
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_task():
response = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"})
assert response.status_code == 201
assert response.json()["title"] == "Test Task"
assert response.json()["status"] == "pending"
def test_read_tasks():
client.post("/tasks/", json={"title": "Task 1", "due_date": "2023-12-31"})
client.post("/tasks/", json={"title": "Task 2", "due_date": "2024-01-01"})
response = client.get("/tasks/")
assert response.status_code == 200
assert len(response.json()) == 2
def test_read_task():
task = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"}).json()
response = client.get(f"/tasks/{task['id']}")
assert response.status_code == 200
assert response.json()["id"] == task["id"]
def test_read_task_not_found():
response = client.get("/tasks/999")
assert response.status_code == 404
def test_update_task():
task = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"}).json()
response = client.put(f"/tasks/{task['id']}", json={"status": "completed"})
assert response.status_code == 200
assert response.json()["status"] == "completed"
def test_delete_task():
task = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"}).json()
response = client.delete(f"/tasks/{task['id']}")
assert response.status_code == 204
response = client.get(f"/tasks/{task['id']}")
assert response.status_code == 404

View File

@@ -1,11 +0,0 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
ENV UV_CACHE_DIR=/tmp/uv-cache
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY *.py .
RUN useradd -m appuser && chown -R appuser:appuser /app /tmp/uv-cache
USER appuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,10 +0,0 @@
services:
app:
build: .
ports:
- "18765:8000"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')"]
interval: 3s
timeout: 5s
retries: 5

View File

@@ -1,63 +0,0 @@
from fastapi import FastAPI, HTTPException, Depends
from sqlalchemy.orm import Session
from typing import List
from datetime import date
from models import Task, SessionLocal
from schemas import TaskCreate, TaskResponse
app = FastAPI()
# Dependency to get database session
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/tasks/", response_model=TaskResponse, status_code=201)
async def create_task(task: TaskCreate, db: Session = Depends(get_db)):
db_task = Task(**task.model_dump())
db.add(db_task)
db.commit()
db.refresh(db_task)
return db_task
@app.get("/tasks/", response_model=List[TaskResponse])
async def read_tasks(status: str = None, db: Session = Depends(get_db)):
if status:
tasks = db.query(Task).filter_by(status=status).all()
else:
tasks = db.query(Task).all()
return tasks
@app.get("/tasks/{task_id}", response_model=TaskResponse)
async def read_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
@app.put("/tasks/{task_id}", response_model=TaskResponse)
async def update_task(task_id: int, task_update: TaskCreate, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
for key, value in task_update.model_dump().items():
setattr(task, key, value)
db.commit()
db.refresh(task)
return task
@app.delete("/tasks/{task_id}", status_code=204)
async def delete_task(task_id: int, db: Session = Depends(get_db)):
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
db.delete(task)
db.commit()
return

View File

@@ -1,18 +0,0 @@
from sqlalchemy import create_engine, Column, Integer, String, Date
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./todos.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(String(1000))
due_date = Column(Date, nullable=False)
status = Column(String(20), default="pending")
Base.metadata.create_all(bind=engine)

View File

@@ -1,17 +0,0 @@
[project]
name = "todo-app"
version = "0.1.0"
description = "A simple task management API using FastAPI and SQLAlchemy"
authors = [
{ name="Your Name", email="your.email@example.com" }
]
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy",
"pytest",
"httpx",
"python-dotenv",
"pydantic>=2.0.0"
]

View File

@@ -1,123 +0,0 @@
{
"run_id": "v8_cache_fix",
"steps": [
{
"step": "requirements",
"agent": "client",
"attempts": [
{
"attempt": 1,
"elapsed": 7.7,
"errors": [],
"code_length": 1361
}
],
"final_errors": [],
"passed": true
},
{
"step": "models.py",
"agent": "data",
"attempts": [
{
"attempt": 1,
"elapsed": 5.8,
"errors": [],
"code_length": 707
}
],
"final_errors": [],
"passed": true
},
{
"step": "schemas.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 5.2,
"errors": [],
"code_length": 464
}
],
"final_errors": [],
"passed": true
},
{
"step": "main.py",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 14.1,
"errors": [],
"code_length": 1960
}
],
"final_errors": [],
"passed": true
},
{
"step": "pyproject.toml",
"agent": "coder",
"attempts": [
{
"attempt": 1,
"elapsed": 8.8,
"errors": [
"Sisältää poetry-konfiguraation — käytä VAIN [project] (PEP 621) + uv",
"build-backend käyttää poetryä — poista tai vaihda"
],
"code_length": 815
},
{
"attempt": 2,
"elapsed": 3.6,
"errors": [],
"code_length": 311
}
],
"final_errors": [],
"passed": true
},
{
"step": "test_main.py",
"agent": "qa",
"attempts": [
{
"attempt": 1,
"elapsed": 19.8,
"errors": [],
"code_length": 2050
}
],
"final_errors": [],
"passed": true
},
{
"step": "Dockerfile",
"agent": "tester",
"attempts": [
{
"attempt": 1,
"elapsed": 4.6,
"errors": [],
"code_length": 338
}
],
"final_errors": [],
"passed": true
}
],
"model": "qwen2.5-coder:7b-instruct-q4_K_M",
"summary": "7/7 passed",
"docker": {
"build": true,
"pytest": false,
"api": false,
"errors": [
"pytest failed (exit 1):\n=============== short test summary info ============================\nFAILED test_main.py::test_create_task - sqlalchemy.exc.OperationalError: (sql...\nFAILED test_main.py::test_read_tasks - sqlalchemy.exc.OperationalError: (sqli...\nFAILED test_main.py::test_read_task - sqlalchemy.exc.OperationalError: (sqlit...\nFAILED test_main.py::test_read_task_not_found - sqlalchemy.exc.OperationalErr...\nFAILED test_main.py::test_update_task - sqlalchemy.exc.OperationalError: (sql...\nFAILED test_main.py::test_delete_task - sqlalchemy.exc.OperationalError: (sql...\n======================== 6 failed, 2 warnings in 2.50s =========================\n Network v8_cache_fix_default Creating\n Network v8_cache_fix_default Created\nwarning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.\n\n",
"POST /todos/ ja /tasks/ molemmat failasivat: HTTP Error 422: Unprocessable Entity"
]
}
}

View File

@@ -1,32 +0,0 @@
**PROJECT NAME:** TodoSovellus
**GOAL:** Create a simple task management application for individuals to keep track of their daily tasks and manage them efficiently.
**CORE FEATURES:**
1. **Add Task**: Users can add new tasks with a title, description, due date, and status (e.g., pending, completed).
2. **View Tasks**: Users can view all tasks or filter by status (pending, completed).
3. **Edit Task**: Users can update the details of an existing task.
4. **Delete Task**: Users can remove tasks that are no longer needed.
5. **Mark as Completed**: Users can mark a task as completed.
6. **Filter Tasks**: Users can filter tasks based on their status (pending, completed).
7. **Task Details**: Users can view detailed information about each task.
**DATA MODEL:**
1. **Tasks**
- id (integer, primary key)
- title (string)
- description (text)
- due_date (date)
- status (enum: pending, completed)
**API ENDPOINTS:**
1. **GET /tasks**: Retrieve all tasks.
2. **POST /tasks**: Create a new task.
3. **GET /tasks/{id}**: Retrieve details of a specific task by ID.
4. **PUT /tasks/{id}**: Update an existing task.
5. **DELETE /tasks/{id}**: Delete a task.
6. **GET /tasks/status/{status}**: Filter tasks by status (pending, completed).
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is required for accessing endpoints.

View File

@@ -1,18 +0,0 @@
from pydantic import BaseModel, Field
from datetime import date
class TaskCreate(BaseModel):
title: str = Field(..., min_length=1, max_length=255)
description: str = Field(None, max_length=1000)
due_date: date = Field(...)
status: str = Field("pending", min_length=1, max_length=20)
class TaskResponse(BaseModel):
id: int
title: str
description: str
due_date: date
status: str
class Config:
from_attributes = True

View File

@@ -1,56 +0,0 @@
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app
from models import Base
from main import get_db
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
def override_get_db():
db = TestSession()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_task():
response = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"})
assert response.status_code == 201
assert response.json()["title"] == "Test Task"
assert response.json()["status"] == "pending"
def test_read_tasks():
client.post("/tasks/", json={"title": "Task 1", "due_date": "2023-12-31"})
client.post("/tasks/", json={"title": "Task 2", "due_date": "2024-01-01"})
response = client.get("/tasks/")
assert response.status_code == 200
assert len(response.json()) == 2
def test_read_task():
task = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"}).json()
response = client.get(f"/tasks/{task['id']}")
assert response.status_code == 200
assert response.json()["id"] == task["id"]
def test_read_task_not_found():
response = client.get("/tasks/999")
assert response.status_code == 404
def test_update_task():
task = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"}).json()
response = client.put(f"/tasks/{task['id']}", json={"status": "completed"})
assert response.status_code == 200
assert response.json()["status"] == "completed"
def test_delete_task():
task = client.post("/tasks/", json={"title": "Test Task", "due_date": "2023-12-31"}).json()
response = client.delete(f"/tasks/{task['id']}")
assert response.status_code == 204
response = client.get(f"/tasks/{task['id']}")
assert response.status_code == 404

Binary file not shown.

View File

@@ -1,30 +0,0 @@
# 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"]

View File

@@ -1,50 +0,0 @@
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"

View File

@@ -1,150 +0,0 @@
# Agents — Kipinä Agentic Studio → CrewAI
client:
role: >-
Client
goal: >-
product owner who turns vague ideas into clear, actionable software requirements
backstory: |
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
llm: qwen-coder
data:
role: >-
Data Engineer
goal: >-
database architect specializing in SQLAlchemy and relational databases
backstory: |
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
llm: qwen-coder
coder:
role: >-
Coder
goal: >-
expert Python developer
backstory: |
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)
llm: qwen-coder
qa:
role: >-
QA
goal: >-
QA engineer responsible for code review and automated testing
backstory: |
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
llm: qwen-coder
tester:
role: >-
DevOps
goal: >-
DevOps engineer specializing in containerization and deployment
backstory: |
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.
llm: qwen-coder
observer:
role: >-
Observer
goal: >-
independent technical observer and risk analyst
backstory: |
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"
llm: qwen-coder

View File

@@ -1,85 +0,0 @@
"""Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
CrewAI crew — generated by Kipinä Agentic Studio.
Run: crewai run
"""
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
@CrewBase
class ProjectCrew:
"""Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit"""
agents_config = "agents.yaml"
tasks_config = "tasks.yaml"
@agent
def client(self) -> Agent:
return Agent(config=self.agents_config["client"])
@agent
def data(self) -> Agent:
return Agent(config=self.agents_config["data"])
@agent
def coder(self) -> Agent:
return Agent(config=self.agents_config["coder"])
@agent
def qa(self) -> Agent:
return Agent(config=self.agents_config["qa"])
@agent
def tester(self) -> Agent:
return Agent(config=self.agents_config["tester"])
@agent
def observer(self) -> Agent:
return Agent(config=self.agents_config["observer"])
@task
def step_0_requirements(self) -> Task:
return Task(config=self.tasks_config["step_0_requirements"])
@task
def step_1_models_py(self) -> Task:
return Task(config=self.tasks_config["step_1_models_py"])
@task
def step_2_schemas_py(self) -> Task:
return Task(config=self.tasks_config["step_2_schemas_py"])
@task
def step_3_main_py(self) -> Task:
return Task(config=self.tasks_config["step_3_main_py"])
@task
def step_4_pyproject_toml(self) -> Task:
return Task(config=self.tasks_config["step_4_pyproject_toml"])
@task
def step_5_review(self) -> Task:
return Task(config=self.tasks_config["step_5_review"])
@task
def step_6_test_main_py(self) -> Task:
return Task(config=self.tasks_config["step_6_test_main_py"])
@task
def step_7_dockerfile(self) -> Task:
return Task(config=self.tasks_config["step_7_dockerfile"])
@task
def step_8_readme_md(self) -> Task:
return Task(config=self.tasks_config["step_8_readme_md"])
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)

View File

@@ -1,56 +0,0 @@
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()

View File

@@ -1,16 +0,0 @@
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')

View File

@@ -1,64 +0,0 @@
# 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.
```

View File

@@ -1,122 +0,0 @@
# 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')
```

View File

@@ -1,142 +0,0 @@
# 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
```

View File

@@ -1,244 +0,0 @@
# 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()
```

View File

@@ -1,212 +0,0 @@
# 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"
```

View File

@@ -1,174 +0,0 @@
# 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
```

View File

@@ -1,291 +0,0 @@
# 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
```

View File

@@ -1,93 +0,0 @@
# 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"]
```

View File

@@ -1,404 +0,0 @@
# 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"
```

View File

@@ -1,12 +0,0 @@
[project]
name = "todo-sovellus"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"fastapi",
"uvicorn[standard]",
"sqlalchemy",
]
[project.scripts]
dev = "uvicorn main:app --reload"

View File

@@ -1,13 +0,0 @@
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

View File

@@ -1,442 +0,0 @@
# Tasks — Kipinä Agentic Studio → CrewAI
step_0_requirements:
description: |
Todo-sovellus FastAPI + SQLite, CRUD-endpointit ja testit
expected_output: >-
requirements
agent: client
step_1_models_py:
description: |
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)
# ... (truncated)
expected_output: >-
models.py
agent: data
step_2_schemas_py:
description: |
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)
# ... (truncated)
expected_output: >-
schemas.py
agent: coder
step_3_main_py:
description: |
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)
# ... (truncated)
expected_output: >-
main.py
agent: coder
step_4_pyproject_toml:
description: |
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)
# ... (truncated)
expected_output: >-
pyproject.toml
agent: coder
step_5_review:
description: |
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
# ... (truncated)
expected_output: >-
review
agent: qa
step_6_test_main_py:
description: |
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
# ... (truncated)
expected_output: >-
test_main.py
agent: qa
step_7_dockerfile:
description: |
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.
expected_output: >-
Dockerfile
agent: tester
step_8_readme_md:
description: |
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
```
# ... (truncated)
expected_output: >-
README.md
agent: observer

View File

@@ -1,116 +0,0 @@
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

Binary file not shown.

View File

@@ -1,11 +0,0 @@
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
ENV UV_CACHE_DIR=/tmp/uv-cache
WORKDIR /app
COPY pyproject.toml .
RUN uv sync
COPY *.py .
RUN useradd -m appuser && chown -R appuser:appuser /app /tmp/uv-cache
USER appuser
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,56 +0,0 @@
**VERDICT: ORANGE**
## Overview
This project is a REST API for user management using SQLite as the database backend, built with FastAPI.
## Files
| File | Purpose |
|------|---------|
| models.py | Defines the data model for users. |
| schemas.py | Contains Pydantic schema definitions for input and output validation. |
| main.py | Implements the FastAPI application with endpoints for user operations. |
| test_main.py | Includes unit tests for the API endpoints using TestClient from fastapi.testclient. |
| pyproject.toml | Project metadata, dependencies, and build configuration. |
| Dockerfile | Defines a Docker image for running the API, including environment setup and dependency installation. |
## Quick Start
git clone <repo>
cd project
uv sync
uv run uvicorn main:app --reload
## Docker
docker build -t rest-api-k-ytt-j-hallinnalle-sqlite-tietokannalla .
docker run -p 8000:8000 rest-api-k-ytt-j-hallinnalle-sqlite-tietokannalla
## API Endpoints
| Method | Path | Description |
|--------|------|-------------|
| POST | /users/ | Create a new user. |
| GET | /users/ | List all users. |
| GET | /users/{id} | Retrieve a specific user by ID. |
| PUT | /users/{id} | Update an existing user. |
| DELETE | /users/{id} | Delete a user. |
## Architecture
The project uses FastAPI for the web framework, SQLAlchemy as the ORM, and SQLite as the database. The code is organized into `models.py` (data models), `schemas.py` (Pydantic schemas), and `main.py` (FastAPI application). Testing is done with `test_main.py`. A Dockerfile is provided to containerize the application.
## Risk Assessment
| Severity | Issue |
|----------|-------|
| **HIGH** | SQL injection risks: The API directly uses user input in SQL queries without validation or parameterized statements. This can lead to SQL injection vulnerabilities if an attacker manages to inject malicious SQL code through the input fields. |
| **MEDIUM** | Input validation is minimal: While Pydantic schemas are used for data validation, they only validate types and lengths but do not sanitize inputs (e.g., no escaping of special characters). This could potentially lead to security issues if an attacker bypasses these checks. |
| **MEDIUM** | Authentication and authorization: The API does not implement any authentication or authorization mechanisms. Any user can create, read, update, or delete users without proper credentials. |
| **LOW** | Error handling: Basic error handling is implemented using HTTPException for specific cases (e.g., resource not found). However, more comprehensive logging and exception handling could improve the robustness of the application. |
| **MEDIUM** | Database connection management: The database session is created once per request in `get_db` but is not explicitly closed or committed/rolled back in all cases, which can lead to potential memory leaks or data inconsistencies if an error occurs during a transaction. |
### Most Important Findings:
1. **SQL Injection Risks**: Direct user input usage in SQL queries without parameterized statements.
2. **Minimal Input Validation**: Pydantic schemas only validate types and lengths but do not sanitize inputs.
3. **Lack of Authentication and Authorization**: Anybody can perform CRUD operations on users.
4. **Error Handling Improvements Needed**: More comprehensive error handling, logging, and transaction management are required.
### Overall Assessment:
**NEEDS WORK: SQL injection risks, minimal input validation, lack of authentication/authorization, and basic error handling improvements are critical issues that need to be addressed before the project can be considered secure and robust.**
The architecture is logical but lacks necessary security features, and some parts of the code could benefit from better practices in terms of error management and database connection handling.

View File

@@ -1,115 +0,0 @@
# Agents — Kipinä Agentic Studio → CrewAI
client:
role: >-
Client
goal: >-
product owner who turns vague ideas into clear, actionable software requirements
backstory: |
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
llm: qwen-coder
manager:
role: >-
Manager
goal: >-
software architect
backstory: |
You are a software architect. Given a project description, output a JSON specification.
Output ONLY valid JSON, no explanations. Follow this exact schema:
{
"project_name": "short-name",
"description": "One sentence",
"entities": [
{
"name": "Todo",
"table_name": "todos",
"fields": [
{"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null},
{"name": "description", "sa_type": "Text", "py_type": "str | None", "nullable": true, "default": null},
{"name": "status", "sa_type": "String(20)", "py_type": "str", "nullable": false, "default": "pending"}
]
}
],
"extra_imports": ["from datetime import date"]
}
RULES:
- sa_type: SQLAlchemy column type (String(N), Text, Integer, Date, DateTime, Boolean, Float)
- py_type: Python type hint (str, int, float, bool, date, datetime, str | None, etc.)
- Do NOT use Enum — use String(20) with a default value for status fields
- nullable: true = optional field
- default: null = no default, otherwise a string/number value
- extra_imports: stdlib imports needed in schemas.py (e.g. "from datetime import date")
- entity name: PascalCase singular, table_name: snake_case plural
- Keep it simple: 1-3 entities, 3-7 fields each
llm: qwen-coder
data:
role: >-
Data Engineer
goal: >-
(template pipeline — ei LLM-promptia)
backstory: |
(template pipeline — ei LLM-promptia)
llm: template
coder:
role: >-
Coder
goal: >-
(template pipeline — ei LLM-promptia)
backstory: |
(template pipeline — ei LLM-promptia)
llm: template
qa:
role: >-
QA
goal: >-
(template pipeline — ei LLM-promptia)
backstory: |
(template pipeline — ei LLM-promptia)
llm: template
tester:
role: >-
DevOps
goal: >-
(template pipeline — ei LLM-promptia)
backstory: |
(template pipeline — ei LLM-promptia)
llm: template
observer:
role: >-
Observer
goal: >-
independent technical observer and risk analyst
backstory: |
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"
llm: qwen-coder

View File

@@ -1,93 +0,0 @@
"""REST API käyttäjähallinnalle SQLite-tietokannalla
CrewAI crew — generated by Kipinä Agentic Studio.
Run: crewai run
"""
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
@CrewBase
class ProjectCrew:
"""REST API käyttäjähallinnalle SQLite-tietokannalla"""
agents_config = "agents.yaml"
tasks_config = "tasks.yaml"
@agent
def client(self) -> Agent:
return Agent(config=self.agents_config["client"])
@agent
def manager(self) -> Agent:
return Agent(config=self.agents_config["manager"])
@agent
def data(self) -> Agent:
return Agent(config=self.agents_config["data"])
@agent
def coder(self) -> Agent:
return Agent(config=self.agents_config["coder"])
@agent
def qa(self) -> Agent:
return Agent(config=self.agents_config["qa"])
@agent
def tester(self) -> Agent:
return Agent(config=self.agents_config["tester"])
@agent
def observer(self) -> Agent:
return Agent(config=self.agents_config["observer"])
@task
def step_0_requirements(self) -> Task:
return Task(config=self.tasks_config["step_0_requirements"])
@task
def step_1_json_speksi(self) -> Task:
return Task(config=self.tasks_config["step_1_json_speksi"])
@task
def step_2_models_py(self) -> Task:
return Task(config=self.tasks_config["step_2_models_py"])
@task
def step_3_schemas_py(self) -> Task:
return Task(config=self.tasks_config["step_3_schemas_py"])
@task
def step_4_main_py(self) -> Task:
return Task(config=self.tasks_config["step_4_main_py"])
@task
def step_5_test_main_py(self) -> Task:
return Task(config=self.tasks_config["step_5_test_main_py"])
@task
def step_6_pyproject_toml(self) -> Task:
return Task(config=self.tasks_config["step_6_pyproject_toml"])
@task
def step_7_dockerfile(self) -> Task:
return Task(config=self.tasks_config["step_7_dockerfile"])
@task
def step_8_validointi(self) -> Task:
return Task(config=self.tasks_config["step_8_validointi"])
@task
def step_9_readme_md(self) -> Task:
return Task(config=self.tasks_config["step_9_readme_md"])
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True,
)

View File

@@ -1,5 +0,0 @@
services:
app:
build: .
ports:
- "18765:8000"

View File

@@ -1,52 +0,0 @@
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from models import Base, engine, SessionLocal, User
from schemas import UserCreate, UserResponse
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/", response_model=UserResponse, status_code=201)
def create_user(item: UserCreate, db: Session = Depends(get_db)):
db_item = User(**item.model_dump())
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
@app.get("/users/", response_model=list[UserResponse])
def list_users(db: Session = Depends(get_db)):
return db.query(User).all()
@app.get("/users/{item_id}", response_model=UserResponse)
def get_user(item_id: int, db: Session = Depends(get_db)):
item = db.query(User).filter(User.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail="User not found")
return item
@app.put("/users/{item_id}", response_model=UserResponse)
def update_user(item_id: int, item: UserCreate, db: Session = Depends(get_db)):
db_item = db.query(User).filter(User.id == item_id).first()
if not db_item:
raise HTTPException(status_code=404, detail="User 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("/users/{item_id}", status_code=204)
def delete_user(item_id: int, db: Session = Depends(get_db)):
db_item = db.query(User).filter(User.id == item_id).first()
if not db_item:
raise HTTPException(status_code=404, detail="User not found")
db.delete(db_item)
db.commit()

View File

@@ -1,18 +0,0 @@
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 User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
UserID = Column(Integer, nullable=False)
Username = Column(String(255), nullable=False)
Email = Column(String(255), nullable=False)
PasswordHash = Column(String(255), nullable=False)
Base.metadata.create_all(bind=engine)

View File

@@ -1,84 +0,0 @@
# 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
```
REST API käyttäjähallinnalle SQLite-tietokannalla
```
## Tulos
```
**PROJECT NAME:** User Management REST API
**GOAL:** To develop a RESTful API for managing users using an SQLite database.
**CORE FEATURES:**
1. Register new users with username, email, and password.
2. Authenticate users to gain access to the system.
3. Update user profile information (email, password).
4. Delete existing users.
5. Retrieve all registered users.
6. Retrieve a specific user by ID.
7. Search for users based on partial usernames or emails.
**DATA MODEL:**
- **User:**
- UserID (Integer, Primary Key)
- Username (String, Unique)
- Email (String, Unique)
- PasswordHash (String)
**API ENDPOINTS:**
1. **POST /users/register**: Register a new user.
- Request Body:
{
"username": "johndoe",
"email": "john@example.com",
"password": "securepassword"
}
2. **POST /auth/login**: Authenticate a user and return a token.
- Request Body:
{
"username": "johndoe",
"password": "securepassword"
}
3. **PUT /users/{UserID}**: Update a specific user's profile.
- Path Parameter: UserID (Integer)
- Request Body:
{
"email": "newjohn@example.com",
"password": "newsecurepassword"
}
4. **DELETE /users/{UserID}**: Delete a specific user.
- Path Parameter: UserID (Integer)
5. **GET /users**: Retrieve all registered users.
6. **GET /users/{UserID}**: Retrieve a specific user by ID.
7. **GET /users/search?query={searchTerm}**: Search for users based on partial usernames or emails.
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is needed to view all users, but authentication is required to update or delete any user.
```

View File

@@ -1,112 +0,0 @@
# 1 — Manager (manager) — JSON-speksi
**Malli:** `qwen-coder`
## System Prompt
```
You are a software architect. Given a project description, output a JSON specification.
Output ONLY valid JSON, no explanations. Follow this exact schema:
{
"project_name": "short-name",
"description": "One sentence",
"entities": [
{
"name": "Todo",
"table_name": "todos",
"fields": [
{"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null},
{"name": "description", "sa_type": "Text", "py_type": "str | None", "nullable": true, "default": null},
{"name": "status", "sa_type": "String(20)", "py_type": "str", "nullable": false, "default": "pending"}
]
}
],
"extra_imports": ["from datetime import date"]
}
RULES:
- sa_type: SQLAlchemy column type (String(N), Text, Integer, Date, DateTime, Boolean, Float)
- py_type: Python type hint (str, int, float, bool, date, datetime, str | None, etc.)
- Do NOT use Enum — use String(20) with a default value for status fields
- nullable: true = optional field
- default: null = no default, otherwise a string/number value
- extra_imports: stdlib imports needed in schemas.py (e.g. "from datetime import date")
- entity name: PascalCase singular, table_name: snake_case plural
- Keep it simple: 1-3 entities, 3-7 fields each
```
## Syöte
```
**PROJECT NAME:** User Management REST API
**GOAL:** To develop a RESTful API for managing users using an SQLite database.
**CORE FEATURES:**
1. Register new users with username, email, and password.
2. Authenticate users to gain access to the system.
3. Update user profile information (email, password).
4. Delete existing users.
5. Retrieve all registered users.
6. Retrieve a specific user by ID.
7. Search for users based on partial usernames or emails.
**DATA MODEL:**
- **User:**
- UserID (Integer, Primary Key)
- Username (String, Unique)
- Email (String, Unique)
- PasswordHash (String)
**API ENDPOINTS:**
1. **POST /users/register**: Register a new user.
- Request Body:
{
"username": "johndoe",
"email": "john@example.com",
"password": "securepassword"
}
2. **POST /auth/login**: Authenticate a user and return a token.
- Request Body:
{
"username": "johndoe",
"password": "securepassword"
}
3. **PUT /users/{UserID}**: Update a specific user's profile.
- Path Parameter: UserID (Integer)
- Request Body:
{
"email": "newjohn@example.com",
"password": "newsecurepassword"
}
4. **DELETE /users/{UserID}**: Delete a specific user.
- Path Parameter: UserID (Integer)
5. **GET /users**: Retrieve all registered users.
6. **GET /users/{UserID}**: Retrieve a specific user by ID.
7. **GET /users/search?query={searchTerm}**: Search for users based on partial usernames or emails.
**CONSTRAINTS:**
- Must use SQLite as the database.
- No authentication is needed to view all users, but authentication is required to update or delete any user.
```
## Tulos
```
{
"project_name": "UserManagementAPI",
"description": "RESTful API for managing users using an SQLite database.",
"entities": [
{
"name": "User",
"table_name": "users",
"fields": [
{"name": "UserID", "sa_type": "Integer", "py_type": "int", "nullable": false, "default": null},
{"name": "Username", "sa_type": "String(255)", "py_type": "str", "nullable": false, "unique": true},
{"name": "Email", "sa_type": "String(255)", "py_type": "str", "nullable": false, "unique": true},
{"name": "PasswordHash", "sa_type": "String(255)", "py_type": "str", "nullable": false}
]
}
],
"extra_imports": []
}
```

View File

@@ -1,39 +0,0 @@
# 2 — Data Engineer (data) — models.py
**Malli:** `template`
## System Prompt
```
(template pipeline — ei LLM-promptia)
```
## Syöte
```
Generated from spec: ["User"]
```
## Tulos
```
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 User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
UserID = Column(Integer, nullable=False)
Username = Column(String(255), nullable=False)
Email = Column(String(255), nullable=False)
PasswordHash = Column(String(255), nullable=False)
Base.metadata.create_all(bind=engine)
```

Some files were not shown because too many files have changed in this diff Show More