Pipelinen parannuksia building blockeilla

This commit is contained in:
Jaakko Vanhala
2026-04-12 18:48:14 +03:00
parent c1a5f8aff5
commit b2ee8b9031
175 changed files with 13311 additions and 237 deletions

View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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