feat: add scratchpad field to session model and schemas

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-02-04 02:47:22 -05:00
parent d488d2acc8
commit 7d0000827b
3 changed files with 64 additions and 2 deletions

View File

@@ -1,7 +1,8 @@
import uuid
from datetime import datetime, timezone
from typing import Optional, Any
from sqlalchemy import String, DateTime, ForeignKey, Boolean
from sqlalchemy import String, DateTime, ForeignKey, Boolean, Text
import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app.core.database import Base
@@ -44,6 +45,9 @@ class Session(Base):
ticket_number: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
client_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
exported: Mapped[bool] = mapped_column(Boolean, default=False)
scratchpad: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, server_default=sa.text("''")
)
# Relationships
tree: Mapped["Tree"] = relationship("Tree", back_populates="sessions")

View File

@@ -1,7 +1,7 @@
from datetime import datetime
from typing import Optional, Any
from uuid import UUID
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, validator
class DecisionRecord(BaseModel):
@@ -27,6 +27,7 @@ class SessionUpdate(BaseModel):
custom_steps: Optional[list[dict[str, Any]]] = None
ticket_number: Optional[str] = Field(None, max_length=100)
client_name: Optional[str] = Field(None, max_length=255)
scratchpad: Optional[str] = None
class SessionResponse(BaseModel):
@@ -42,6 +43,11 @@ class SessionResponse(BaseModel):
ticket_number: Optional[str] = None
client_name: Optional[str] = None
exported: bool
scratchpad: str = ""
@validator('scratchpad', pre=True, always=True)
def normalize_scratchpad(cls, v):
return v or ""
class Config:
from_attributes = True
@@ -51,3 +57,7 @@ class SessionExport(BaseModel):
format: str = Field(default="markdown", pattern="^(text|markdown|html)$")
include_timestamps: bool = True
include_tree_info: bool = True
class ScratchpadUpdate(BaseModel):
scratchpad: str

View File

@@ -315,3 +315,51 @@ class TestSessions:
data = response.json()
assert len(data) >= 1
assert all(s["completed_at"] is None for s in data)
@pytest.mark.asyncio
async def test_create_session_has_scratchpad(
self, client: AsyncClient, auth_headers: dict, test_tree: dict
):
"""Test that new sessions include scratchpad field."""
session_data = {
"tree_id": test_tree["id"],
}
response = await client.post(
"/api/v1/sessions",
json=session_data,
headers=auth_headers
)
assert response.status_code == 201
data = response.json()
assert "scratchpad" in data
assert data["scratchpad"] == ""
@pytest.mark.asyncio
async def test_update_scratchpad_via_put(
self, client: AsyncClient, auth_headers: dict, test_tree: dict
):
"""Test updating scratchpad through the existing PUT endpoint."""
# Create session
create_response = await client.post(
"/api/v1/sessions",
json={"tree_id": test_tree["id"]},
headers=auth_headers
)
session_id = create_response.json()["id"]
# Update scratchpad via PUT
update_data = {
"scratchpad": "- Server IP: 192.168.1.50\n- Error: 0x80070005"
}
response = await client.put(
f"/api/v1/sessions/{session_id}",
json=update_data,
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["scratchpad"] == update_data["scratchpad"]