feat: add scratchpad field to session model and schemas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Optional, Any
|
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.orm import Mapped, mapped_column, relationship
|
||||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||||
from app.core.database import Base
|
from app.core.database import Base
|
||||||
@@ -44,6 +45,9 @@ class Session(Base):
|
|||||||
ticket_number: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
ticket_number: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||||
client_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
client_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||||
exported: Mapped[bool] = mapped_column(Boolean, default=False)
|
exported: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||||
|
scratchpad: Mapped[Optional[str]] = mapped_column(
|
||||||
|
Text, nullable=True, server_default=sa.text("''")
|
||||||
|
)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
tree: Mapped["Tree"] = relationship("Tree", back_populates="sessions")
|
tree: Mapped["Tree"] = relationship("Tree", back_populates="sessions")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field, validator
|
||||||
|
|
||||||
|
|
||||||
class DecisionRecord(BaseModel):
|
class DecisionRecord(BaseModel):
|
||||||
@@ -27,6 +27,7 @@ class SessionUpdate(BaseModel):
|
|||||||
custom_steps: Optional[list[dict[str, Any]]] = None
|
custom_steps: Optional[list[dict[str, Any]]] = None
|
||||||
ticket_number: Optional[str] = Field(None, max_length=100)
|
ticket_number: Optional[str] = Field(None, max_length=100)
|
||||||
client_name: Optional[str] = Field(None, max_length=255)
|
client_name: Optional[str] = Field(None, max_length=255)
|
||||||
|
scratchpad: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class SessionResponse(BaseModel):
|
class SessionResponse(BaseModel):
|
||||||
@@ -42,6 +43,11 @@ class SessionResponse(BaseModel):
|
|||||||
ticket_number: Optional[str] = None
|
ticket_number: Optional[str] = None
|
||||||
client_name: Optional[str] = None
|
client_name: Optional[str] = None
|
||||||
exported: bool
|
exported: bool
|
||||||
|
scratchpad: str = ""
|
||||||
|
|
||||||
|
@validator('scratchpad', pre=True, always=True)
|
||||||
|
def normalize_scratchpad(cls, v):
|
||||||
|
return v or ""
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
@@ -51,3 +57,7 @@ class SessionExport(BaseModel):
|
|||||||
format: str = Field(default="markdown", pattern="^(text|markdown|html)$")
|
format: str = Field(default="markdown", pattern="^(text|markdown|html)$")
|
||||||
include_timestamps: bool = True
|
include_timestamps: bool = True
|
||||||
include_tree_info: bool = True
|
include_tree_info: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
class ScratchpadUpdate(BaseModel):
|
||||||
|
scratchpad: str
|
||||||
|
|||||||
@@ -315,3 +315,51 @@ class TestSessions:
|
|||||||
data = response.json()
|
data = response.json()
|
||||||
assert len(data) >= 1
|
assert len(data) >= 1
|
||||||
assert all(s["completed_at"] is None for s in data)
|
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"]
|
||||||
|
|||||||
Reference in New Issue
Block a user