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
|
||||
Reference in New Issue
Block a user