Pipelinen parannuksia building blockeilla
This commit is contained in:
16
zipit/loop_runs/baseline_v1/Dockerfile
Normal file
16
zipit/loop_runs/baseline_v1/Dockerfile
Normal 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"]
|
||||
76
zipit/loop_runs/baseline_v1/main.py
Normal file
76
zipit/loop_runs/baseline_v1/main.py
Normal 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
|
||||
23
zipit/loop_runs/baseline_v1/models.py
Normal file
23
zipit/loop_runs/baseline_v1/models.py
Normal 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)
|
||||
32
zipit/loop_runs/baseline_v1/pyproject.toml
Normal file
32
zipit/loop_runs/baseline_v1/pyproject.toml
Normal 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"
|
||||
105
zipit/loop_runs/baseline_v1/report.json
Normal file
105
zipit/loop_runs/baseline_v1/report.json
Normal 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"
|
||||
}
|
||||
34
zipit/loop_runs/baseline_v1/requirements
Normal file
34
zipit/loop_runs/baseline_v1/requirements
Normal 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.
|
||||
16
zipit/loop_runs/baseline_v1/schemas.py
Normal file
16
zipit/loop_runs/baseline_v1/schemas.py
Normal 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
|
||||
59
zipit/loop_runs/baseline_v1/test_main.py
Normal file
59
zipit/loop_runs/baseline_v1/test_main.py
Normal 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
|
||||
18
zipit/loop_runs/v2_strict/Dockerfile
Normal file
18
zipit/loop_runs/v2_strict/Dockerfile
Normal 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"]
|
||||
```
|
||||
64
zipit/loop_runs/v2_strict/main.py
Normal file
64
zipit/loop_runs/v2_strict/main.py
Normal 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
|
||||
18
zipit/loop_runs/v2_strict/models.py
Normal file
18
zipit/loop_runs/v2_strict/models.py
Normal 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)
|
||||
13
zipit/loop_runs/v2_strict/pyproject.toml
Normal file
13
zipit/loop_runs/v2_strict/pyproject.toml
Normal 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"
|
||||
]
|
||||
113
zipit/loop_runs/v2_strict/report.json
Normal file
113
zipit/loop_runs/v2_strict/report.json
Normal 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"
|
||||
}
|
||||
32
zipit/loop_runs/v2_strict/requirements
Normal file
32
zipit/loop_runs/v2_strict/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v2_strict/schemas.py
Normal file
18
zipit/loop_runs/v2_strict/schemas.py
Normal 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
|
||||
57
zipit/loop_runs/v2_strict/test_main.py
Normal file
57
zipit/loop_runs/v2_strict/test_main.py
Normal 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
|
||||
16
zipit/loop_runs/v3_docker/Dockerfile
Normal file
16
zipit/loop_runs/v3_docker/Dockerfile
Normal 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"]
|
||||
10
zipit/loop_runs/v3_docker/docker-compose.yml
Normal file
10
zipit/loop_runs/v3_docker/docker-compose.yml
Normal 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
|
||||
64
zipit/loop_runs/v3_docker/main.py
Normal file
64
zipit/loop_runs/v3_docker/main.py
Normal 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()
|
||||
18
zipit/loop_runs/v3_docker/models.py
Normal file
18
zipit/loop_runs/v3_docker/models.py
Normal 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)
|
||||
16
zipit/loop_runs/v3_docker/pyproject.toml
Normal file
16
zipit/loop_runs/v3_docker/pyproject.toml
Normal 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"
|
||||
]
|
||||
134
zipit/loop_runs/v3_docker/report.json
Normal file
134
zipit/loop_runs/v3_docker/report.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
30
zipit/loop_runs/v3_docker/requirements
Normal file
30
zipit/loop_runs/v3_docker/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v3_docker/schemas.py
Normal file
18
zipit/loop_runs/v3_docker/schemas.py
Normal 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
|
||||
57
zipit/loop_runs/v3_docker/test_main.py
Normal file
57
zipit/loop_runs/v3_docker/test_main.py
Normal 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
|
||||
11
zipit/loop_runs/v4_docker/Dockerfile
Normal file
11
zipit/loop_runs/v4_docker/Dockerfile
Normal 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"]
|
||||
58
zipit/loop_runs/v4_docker/main.py
Normal file
58
zipit/loop_runs/v4_docker/main.py
Normal 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()
|
||||
18
zipit/loop_runs/v4_docker/models.py
Normal file
18
zipit/loop_runs/v4_docker/models.py
Normal 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)
|
||||
13
zipit/loop_runs/v4_docker/pyproject.toml
Normal file
13
zipit/loop_runs/v4_docker/pyproject.toml
Normal 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"
|
||||
]
|
||||
145
zipit/loop_runs/v4_docker/report.json
Normal file
145
zipit/loop_runs/v4_docker/report.json
Normal 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"
|
||||
}
|
||||
30
zipit/loop_runs/v4_docker/requirements
Normal file
30
zipit/loop_runs/v4_docker/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v4_docker/schemas.py
Normal file
18
zipit/loop_runs/v4_docker/schemas.py
Normal 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
|
||||
59
zipit/loop_runs/v4_docker/test_main.py
Normal file
59
zipit/loop_runs/v4_docker/test_main.py
Normal 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
|
||||
11
zipit/loop_runs/v5_feedback/Dockerfile
Normal file
11
zipit/loop_runs/v5_feedback/Dockerfile
Normal 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"]
|
||||
74
zipit/loop_runs/v5_feedback/main.py
Normal file
74
zipit/loop_runs/v5_feedback/main.py
Normal 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))
|
||||
25
zipit/loop_runs/v5_feedback/models.py
Normal file
25
zipit/loop_runs/v5_feedback/models.py
Normal 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)
|
||||
13
zipit/loop_runs/v5_feedback/pyproject.toml
Normal file
13
zipit/loop_runs/v5_feedback/pyproject.toml
Normal 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"
|
||||
]
|
||||
163
zipit/loop_runs/v5_feedback/report.json
Normal file
163
zipit/loop_runs/v5_feedback/report.json
Normal 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"
|
||||
}
|
||||
40
zipit/loop_runs/v5_feedback/requirements
Normal file
40
zipit/loop_runs/v5_feedback/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v5_feedback/schemas.py
Normal file
18
zipit/loop_runs/v5_feedback/schemas.py
Normal 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
|
||||
51
zipit/loop_runs/v5_feedback/test_main.py
Normal file
51
zipit/loop_runs/v5_feedback/test_main.py
Normal 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
|
||||
11
zipit/loop_runs/v6_stable/Dockerfile
Normal file
11
zipit/loop_runs/v6_stable/Dockerfile
Normal 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"]
|
||||
62
zipit/loop_runs/v6_stable/main.py
Normal file
62
zipit/loop_runs/v6_stable/main.py
Normal 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
|
||||
23
zipit/loop_runs/v6_stable/models.py
Normal file
23
zipit/loop_runs/v6_stable/models.py
Normal 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)
|
||||
15
zipit/loop_runs/v6_stable/pyproject.toml
Normal file
15
zipit/loop_runs/v6_stable/pyproject.toml
Normal 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"
|
||||
]
|
||||
126
zipit/loop_runs/v6_stable/report.json
Normal file
126
zipit/loop_runs/v6_stable/report.json
Normal 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"
|
||||
}
|
||||
32
zipit/loop_runs/v6_stable/requirements
Normal file
32
zipit/loop_runs/v6_stable/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v6_stable/schemas.py
Normal file
18
zipit/loop_runs/v6_stable/schemas.py
Normal 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
|
||||
58
zipit/loop_runs/v6_stable/test_main.py
Normal file
58
zipit/loop_runs/v6_stable/test_main.py
Normal 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
|
||||
11
zipit/loop_runs/v7_enum_fix/Dockerfile
Normal file
11
zipit/loop_runs/v7_enum_fix/Dockerfile
Normal 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"]
|
||||
10
zipit/loop_runs/v7_enum_fix/docker-compose.yml
Normal file
10
zipit/loop_runs/v7_enum_fix/docker-compose.yml
Normal 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
|
||||
63
zipit/loop_runs/v7_enum_fix/main.py
Normal file
63
zipit/loop_runs/v7_enum_fix/main.py
Normal 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
|
||||
18
zipit/loop_runs/v7_enum_fix/models.py
Normal file
18
zipit/loop_runs/v7_enum_fix/models.py
Normal 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)
|
||||
17
zipit/loop_runs/v7_enum_fix/pyproject.toml
Normal file
17
zipit/loop_runs/v7_enum_fix/pyproject.toml
Normal 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"
|
||||
]
|
||||
123
zipit/loop_runs/v7_enum_fix/report.json
Normal file
123
zipit/loop_runs/v7_enum_fix/report.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
32
zipit/loop_runs/v7_enum_fix/requirements
Normal file
32
zipit/loop_runs/v7_enum_fix/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v7_enum_fix/schemas.py
Normal file
18
zipit/loop_runs/v7_enum_fix/schemas.py
Normal 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
|
||||
56
zipit/loop_runs/v7_enum_fix/test_main.py
Normal file
56
zipit/loop_runs/v7_enum_fix/test_main.py
Normal 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
|
||||
11
zipit/loop_runs/v8_cache_fix/Dockerfile
Normal file
11
zipit/loop_runs/v8_cache_fix/Dockerfile
Normal 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"]
|
||||
10
zipit/loop_runs/v8_cache_fix/docker-compose.yml
Normal file
10
zipit/loop_runs/v8_cache_fix/docker-compose.yml
Normal 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
|
||||
63
zipit/loop_runs/v8_cache_fix/main.py
Normal file
63
zipit/loop_runs/v8_cache_fix/main.py
Normal 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
|
||||
18
zipit/loop_runs/v8_cache_fix/models.py
Normal file
18
zipit/loop_runs/v8_cache_fix/models.py
Normal 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)
|
||||
17
zipit/loop_runs/v8_cache_fix/pyproject.toml
Normal file
17
zipit/loop_runs/v8_cache_fix/pyproject.toml
Normal 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"
|
||||
]
|
||||
123
zipit/loop_runs/v8_cache_fix/report.json
Normal file
123
zipit/loop_runs/v8_cache_fix/report.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
32
zipit/loop_runs/v8_cache_fix/requirements
Normal file
32
zipit/loop_runs/v8_cache_fix/requirements
Normal 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.
|
||||
18
zipit/loop_runs/v8_cache_fix/schemas.py
Normal file
18
zipit/loop_runs/v8_cache_fix/schemas.py
Normal 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
|
||||
56
zipit/loop_runs/v8_cache_fix/test_main.py
Normal file
56
zipit/loop_runs/v8_cache_fix/test_main.py
Normal 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
|
||||
Reference in New Issue
Block a user