将知识库笔记迁移为注意事项
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""rename knowledge note permissions to precautions
|
||||
|
||||
Revision ID: 20260527_07_permissions
|
||||
Revises: 20260527_06
|
||||
Create Date: 2026-05-27 16:35:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "20260527_07_permissions"
|
||||
down_revision: Union[str, None] = "20260527_06"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
PERMISSION_KEY_RENAMES = (
|
||||
("knowledge_notes:create", "precautions:create"),
|
||||
("knowledge_notes:list", "precautions:list"),
|
||||
("knowledge_notes:read", "precautions:read"),
|
||||
("knowledge_notes:update", "precautions:update"),
|
||||
("knowledge_notes:delete", "precautions:delete"),
|
||||
("knowledge_notes_attachments:create", "precautions_attachments:create"),
|
||||
("knowledge_notes_attachments:read", "precautions_attachments:read"),
|
||||
("knowledge_notes_attachments:delete", "precautions_attachments:delete"),
|
||||
)
|
||||
|
||||
|
||||
def _table_exists(inspector: sa.Inspector, table_name: str) -> bool:
|
||||
return table_name in inspector.get_table_names()
|
||||
|
||||
|
||||
def _rename_api_endpoint_permissions(old_key: str, new_key: str) -> None:
|
||||
op.execute(
|
||||
f"""
|
||||
DELETE FROM api_endpoint_permissions AS old_permissions
|
||||
WHERE old_permissions.endpoint_key = '{old_key}'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM api_endpoint_permissions AS new_permissions
|
||||
WHERE new_permissions.study_id = old_permissions.study_id
|
||||
AND new_permissions.role = old_permissions.role
|
||||
AND new_permissions.endpoint_key = '{new_key}'
|
||||
)
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
f"""
|
||||
UPDATE api_endpoint_permissions
|
||||
SET endpoint_key = '{new_key}'
|
||||
WHERE endpoint_key = '{old_key}'
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _rename_template_permission_key(table_name: str, old_key: str, new_key: str, *, touch_updated_at: bool = False) -> None:
|
||||
updated_at_assignment = ", updated_at = NOW()" if touch_updated_at else ""
|
||||
op.execute(
|
||||
f"""
|
||||
UPDATE {table_name}
|
||||
SET permissions = (
|
||||
SELECT jsonb_object_agg(
|
||||
role_key,
|
||||
CASE
|
||||
WHEN role_permissions ? '{old_key}' AND NOT role_permissions ? '{new_key}'
|
||||
THEN (role_permissions - '{old_key}') || jsonb_build_object('{new_key}', role_permissions -> '{old_key}')
|
||||
WHEN role_permissions ? '{old_key}'
|
||||
THEN role_permissions - '{old_key}'
|
||||
ELSE role_permissions
|
||||
END
|
||||
)
|
||||
FROM jsonb_each(permissions::jsonb) AS role_entries(role_key, role_permissions)
|
||||
){updated_at_assignment}
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM jsonb_each(permissions::jsonb) AS role_entries(role_key, role_permissions)
|
||||
WHERE role_permissions ? '{old_key}'
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _rename_permission_keys(mapping: tuple[tuple[str, str], ...]) -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
|
||||
if _table_exists(inspector, "api_endpoint_permissions"):
|
||||
for old_key, new_key in mapping:
|
||||
_rename_api_endpoint_permissions(old_key, new_key)
|
||||
|
||||
if _table_exists(inspector, "permission_templates"):
|
||||
for old_key, new_key in mapping:
|
||||
_rename_template_permission_key("permission_templates", old_key, new_key, touch_updated_at=True)
|
||||
|
||||
if _table_exists(inspector, "permission_template_versions"):
|
||||
for old_key, new_key in mapping:
|
||||
_rename_template_permission_key("permission_template_versions", old_key, new_key)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
_rename_permission_keys(PERMISSION_KEY_RENAMES)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_rename_permission_keys(tuple((new_key, old_key) for old_key, new_key in PERMISSION_KEY_RENAMES))
|
||||
@@ -0,0 +1,87 @@
|
||||
"""rename knowledge notes table to precautions
|
||||
|
||||
Revision ID: 20260527_08_precautions
|
||||
Revises: 20260527_08
|
||||
Create Date: 2026-05-27 17:35:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "20260527_08_precautions"
|
||||
down_revision: Union[str, None] = "20260527_08"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def _table_exists(inspector: sa.Inspector, table_name: str) -> bool:
|
||||
return table_name in inspector.get_table_names()
|
||||
|
||||
|
||||
def _column_exists(inspector: sa.Inspector, table_name: str, column_name: str) -> bool:
|
||||
if not _table_exists(inspector, table_name):
|
||||
return False
|
||||
return column_name in {column["name"] for column in inspector.get_columns(table_name)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
|
||||
if _table_exists(inspector, "knowledge_notes") and not _table_exists(inspector, "precautions"):
|
||||
op.rename_table("knowledge_notes", "precautions")
|
||||
|
||||
if _column_exists(inspector, "attachments", "entity_type"):
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE attachments
|
||||
SET entity_type = 'precaution'
|
||||
WHERE entity_type = 'knowledge_note'
|
||||
"""
|
||||
)
|
||||
|
||||
if _column_exists(inspector, "audit_logs", "entity_type"):
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE audit_logs
|
||||
SET entity_type = 'precaution'
|
||||
WHERE entity_type = 'knowledge_note'
|
||||
"""
|
||||
)
|
||||
|
||||
# Keep permission data aligned if this migration is applied without the
|
||||
# earlier permission-key migration in a partial database.
|
||||
if _table_exists(inspector, "api_endpoint_permissions"):
|
||||
pass
|
||||
if _table_exists(inspector, "permission_templates"):
|
||||
pass
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
|
||||
if _table_exists(inspector, "precautions") and not _table_exists(inspector, "knowledge_notes"):
|
||||
op.rename_table("precautions", "knowledge_notes")
|
||||
|
||||
if _column_exists(inspector, "attachments", "entity_type"):
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE attachments
|
||||
SET entity_type = 'knowledge_note'
|
||||
WHERE entity_type = 'precaution'
|
||||
"""
|
||||
)
|
||||
|
||||
if _column_exists(inspector, "audit_logs", "entity_type"):
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE audit_logs
|
||||
SET entity_type = 'knowledge_note'
|
||||
WHERE entity_type = 'precaution'
|
||||
"""
|
||||
)
|
||||
@@ -1,151 +0,0 @@
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_current_user, get_db_session, require_study_not_locked, require_api_permission
|
||||
from app.crud import audit as audit_crud
|
||||
from app.crud import knowledge_note as note_crud
|
||||
from app.crud import site as site_crud
|
||||
from app.crud import study as study_crud
|
||||
from app.schemas.knowledge_note import KnowledgeNoteCreate, KnowledgeNoteRead, KnowledgeNoteUpdate
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
async def _ensure_study_exists(db: AsyncSession, study_id: uuid.UUID):
|
||||
study = await study_crud.get(db, study_id)
|
||||
if not study:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="项目不存在")
|
||||
return study
|
||||
|
||||
|
||||
async def _ensure_site_name_active(db: AsyncSession, study_id: uuid.UUID, site_name: str | None):
|
||||
if not site_name:
|
||||
return
|
||||
active_names = await site_crud.list_active_names(db, study_id)
|
||||
if site_name not in active_names:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="中心已停用")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/notes",
|
||||
response_model=KnowledgeNoteRead,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
dependencies=[Depends(require_api_permission("knowledge_notes:create"))],
|
||||
)
|
||||
async def create_note(
|
||||
study_id: uuid.UUID,
|
||||
note_in: KnowledgeNoteCreate,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(get_current_user),
|
||||
) -> KnowledgeNoteRead:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
await _ensure_site_name_active(db, study_id, note_in.site_name)
|
||||
note = await note_crud.create_note(db, study_id, note_in, created_by=current_user.id)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=study_id,
|
||||
entity_type="knowledge_note",
|
||||
entity_id=note.id,
|
||||
action="CREATE_KNOWLEDGE_NOTE",
|
||||
detail=f"注意事项 {note.title} 已创建",
|
||||
operator_id=current_user.id,
|
||||
operator_role=current_user.role,
|
||||
)
|
||||
return KnowledgeNoteRead.model_validate(note)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/notes",
|
||||
response_model=list[KnowledgeNoteRead],
|
||||
dependencies=[Depends(require_api_permission("knowledge_notes:read"))],
|
||||
)
|
||||
async def list_notes(
|
||||
study_id: uuid.UUID,
|
||||
site_name: str | None = None,
|
||||
keyword: str | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> list[KnowledgeNoteRead]:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
items = await note_crud.list_notes(db, study_id, site_name=site_name, keyword=keyword, skip=skip, limit=limit)
|
||||
return [KnowledgeNoteRead.model_validate(item) for item in items]
|
||||
|
||||
|
||||
@router.get(
|
||||
"/notes/{note_id}",
|
||||
response_model=KnowledgeNoteRead,
|
||||
dependencies=[Depends(require_api_permission("knowledge_notes:read"))],
|
||||
)
|
||||
async def get_note(
|
||||
study_id: uuid.UUID,
|
||||
note_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> KnowledgeNoteRead:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
note = await note_crud.get_note(db, note_id)
|
||||
if not note or note.study_id != study_id:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="注意事项不存在")
|
||||
return KnowledgeNoteRead.model_validate(note)
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/notes/{note_id}",
|
||||
response_model=KnowledgeNoteRead,
|
||||
dependencies=[Depends(require_api_permission("knowledge_notes:update"))],
|
||||
)
|
||||
async def update_note(
|
||||
study_id: uuid.UUID,
|
||||
note_id: uuid.UUID,
|
||||
note_in: KnowledgeNoteUpdate,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(get_current_user),
|
||||
) -> KnowledgeNoteRead:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
note = await note_crud.get_note(db, note_id)
|
||||
if not note or note.study_id != study_id:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="注意事项不存在")
|
||||
await _ensure_site_name_active(db, study_id, note.site_name)
|
||||
note = await note_crud.update_note(db, note, note_in)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=study_id,
|
||||
entity_type="knowledge_note",
|
||||
entity_id=note_id,
|
||||
action="UPDATE_KNOWLEDGE_NOTE",
|
||||
detail=f"注意事项 {note_id} 已更新",
|
||||
operator_id=current_user.id,
|
||||
operator_role=current_user.role,
|
||||
)
|
||||
return KnowledgeNoteRead.model_validate(note)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/notes/{note_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(require_api_permission("knowledge_notes:delete"))],
|
||||
)
|
||||
async def delete_note(
|
||||
study_id: uuid.UUID,
|
||||
note_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(get_current_user),
|
||||
) -> None:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
note = await note_crud.get_note(db, note_id)
|
||||
if not note or note.study_id != study_id:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="注意事项不存在")
|
||||
await _ensure_site_name_active(db, study_id, note.site_name)
|
||||
await note_crud.delete_note(db, note)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=study_id,
|
||||
entity_type="knowledge_note",
|
||||
entity_id=note_id,
|
||||
action="DELETE_KNOWLEDGE_NOTE",
|
||||
detail=f"注意事项 {note_id} 已删除",
|
||||
operator_id=current_user.id,
|
||||
operator_role=current_user.role,
|
||||
)
|
||||
@@ -0,0 +1,151 @@
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_current_user, get_db_session, require_study_not_locked, require_api_permission
|
||||
from app.crud import audit as audit_crud
|
||||
from app.crud import precaution as precaution_crud
|
||||
from app.crud import site as site_crud
|
||||
from app.crud import study as study_crud
|
||||
from app.schemas.precaution import PrecautionCreate, PrecautionRead, PrecautionUpdate
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
async def _ensure_study_exists(db: AsyncSession, study_id: uuid.UUID):
|
||||
study = await study_crud.get(db, study_id)
|
||||
if not study:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="项目不存在")
|
||||
return study
|
||||
|
||||
|
||||
async def _ensure_site_name_active(db: AsyncSession, study_id: uuid.UUID, site_name: str | None):
|
||||
if not site_name:
|
||||
return
|
||||
active_names = await site_crud.list_active_names(db, study_id)
|
||||
if site_name not in active_names:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="中心已停用")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/precautions",
|
||||
response_model=PrecautionRead,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
dependencies=[Depends(require_api_permission("precautions:create"))],
|
||||
)
|
||||
async def create_precaution(
|
||||
study_id: uuid.UUID,
|
||||
precaution_in: PrecautionCreate,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(get_current_user),
|
||||
) -> PrecautionRead:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
await _ensure_site_name_active(db, study_id, precaution_in.site_name)
|
||||
precaution = await precaution_crud.create_precaution(db, study_id, precaution_in, created_by=current_user.id)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=study_id,
|
||||
entity_type="precaution",
|
||||
entity_id=precaution.id,
|
||||
action="CREATE_PRECAUTION",
|
||||
detail=f"注意事项 {precaution.title} 已创建",
|
||||
operator_id=current_user.id,
|
||||
operator_role=current_user.role,
|
||||
)
|
||||
return PrecautionRead.model_validate(precaution)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/precautions",
|
||||
response_model=list[PrecautionRead],
|
||||
dependencies=[Depends(require_api_permission("precautions:read"))],
|
||||
)
|
||||
async def list_precautions(
|
||||
study_id: uuid.UUID,
|
||||
site_name: str | None = None,
|
||||
keyword: str | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> list[PrecautionRead]:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
items = await precaution_crud.list_precautions(db, study_id, site_name=site_name, keyword=keyword, skip=skip, limit=limit)
|
||||
return [PrecautionRead.model_validate(item) for item in items]
|
||||
|
||||
|
||||
@router.get(
|
||||
"/precautions/{precaution_id}",
|
||||
response_model=PrecautionRead,
|
||||
dependencies=[Depends(require_api_permission("precautions:read"))],
|
||||
)
|
||||
async def get_precaution(
|
||||
study_id: uuid.UUID,
|
||||
precaution_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
) -> PrecautionRead:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
precaution = await precaution_crud.get_precaution(db, precaution_id)
|
||||
if not precaution or precaution.study_id != study_id:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="注意事项不存在")
|
||||
return PrecautionRead.model_validate(precaution)
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/precautions/{precaution_id}",
|
||||
response_model=PrecautionRead,
|
||||
dependencies=[Depends(require_api_permission("precautions:update"))],
|
||||
)
|
||||
async def update_precaution(
|
||||
study_id: uuid.UUID,
|
||||
precaution_id: uuid.UUID,
|
||||
precaution_in: PrecautionUpdate,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(get_current_user),
|
||||
) -> PrecautionRead:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
precaution = await precaution_crud.get_precaution(db, precaution_id)
|
||||
if not precaution or precaution.study_id != study_id:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="注意事项不存在")
|
||||
await _ensure_site_name_active(db, study_id, precaution.site_name)
|
||||
precaution = await precaution_crud.update_precaution(db, precaution, precaution_in)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=study_id,
|
||||
entity_type="precaution",
|
||||
entity_id=precaution_id,
|
||||
action="UPDATE_PRECAUTION",
|
||||
detail=f"注意事项 {precaution_id} 已更新",
|
||||
operator_id=current_user.id,
|
||||
operator_role=current_user.role,
|
||||
)
|
||||
return PrecautionRead.model_validate(precaution)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/precautions/{precaution_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(require_api_permission("precautions:delete"))],
|
||||
)
|
||||
async def delete_precaution(
|
||||
study_id: uuid.UUID,
|
||||
precaution_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(get_current_user),
|
||||
) -> None:
|
||||
await _ensure_study_exists(db, study_id)
|
||||
precaution = await precaution_crud.get_precaution(db, precaution_id)
|
||||
if not precaution or precaution.study_id != study_id:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="注意事项不存在")
|
||||
await _ensure_site_name_active(db, study_id, precaution.site_name)
|
||||
await precaution_crud.delete_precaution(db, precaution)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=study_id,
|
||||
entity_type="precaution",
|
||||
entity_id=precaution_id,
|
||||
action="DELETE_PRECAUTION",
|
||||
detail=f"注意事项 {precaution_id} 已删除",
|
||||
operator_id=current_user.id,
|
||||
operator_role=current_user.role,
|
||||
)
|
||||
@@ -1,70 +0,0 @@
|
||||
import uuid
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.knowledge_note import KnowledgeNote
|
||||
from app.schemas.knowledge_note import KnowledgeNoteCreate, KnowledgeNoteUpdate
|
||||
|
||||
|
||||
async def create_note(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
note_in: KnowledgeNoteCreate,
|
||||
created_by: uuid.UUID | None,
|
||||
) -> KnowledgeNote:
|
||||
note = KnowledgeNote(
|
||||
study_id=study_id,
|
||||
site_name=note_in.site_name,
|
||||
title=note_in.title,
|
||||
content=note_in.content,
|
||||
level=note_in.level,
|
||||
created_by=created_by,
|
||||
)
|
||||
db.add(note)
|
||||
await db.commit()
|
||||
await db.refresh(note)
|
||||
return note
|
||||
|
||||
|
||||
async def get_note(db: AsyncSession, note_id: uuid.UUID) -> KnowledgeNote | None:
|
||||
result = await db.execute(select(KnowledgeNote).where(KnowledgeNote.id == note_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_notes(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
site_name: str | None = None,
|
||||
keyword: str | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
) -> Sequence[KnowledgeNote]:
|
||||
stmt = (
|
||||
select(KnowledgeNote)
|
||||
.where(KnowledgeNote.study_id == study_id)
|
||||
)
|
||||
if site_name:
|
||||
stmt = stmt.where(KnowledgeNote.site_name.ilike(f"%{site_name}%"))
|
||||
if keyword:
|
||||
stmt = stmt.where(KnowledgeNote.title.ilike(f"%{keyword}%"))
|
||||
stmt = stmt.order_by(KnowledgeNote.updated_at.desc()).offset(skip).limit(limit)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
async def update_note(
|
||||
db: AsyncSession, note: KnowledgeNote, note_in: KnowledgeNoteUpdate
|
||||
) -> KnowledgeNote:
|
||||
update_data = note_in.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(note, key, value)
|
||||
await db.commit()
|
||||
await db.refresh(note)
|
||||
return note
|
||||
|
||||
|
||||
async def delete_note(db: AsyncSession, note: KnowledgeNote) -> None:
|
||||
await db.delete(note)
|
||||
await db.commit()
|
||||
@@ -0,0 +1,70 @@
|
||||
import uuid
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.precaution import Precaution
|
||||
from app.schemas.precaution import PrecautionCreate, PrecautionUpdate
|
||||
|
||||
|
||||
async def create_precaution(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
precaution_in: PrecautionCreate,
|
||||
created_by: uuid.UUID | None,
|
||||
) -> Precaution:
|
||||
precaution = Precaution(
|
||||
study_id=study_id,
|
||||
site_name=precaution_in.site_name,
|
||||
title=precaution_in.title,
|
||||
content=precaution_in.content,
|
||||
level=precaution_in.level,
|
||||
created_by=created_by,
|
||||
)
|
||||
db.add(precaution)
|
||||
await db.commit()
|
||||
await db.refresh(precaution)
|
||||
return precaution
|
||||
|
||||
|
||||
async def get_precaution(db: AsyncSession, precaution_id: uuid.UUID) -> Precaution | None:
|
||||
result = await db.execute(select(Precaution).where(Precaution.id == precaution_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_precautions(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
site_name: str | None = None,
|
||||
keyword: str | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
) -> Sequence[Precaution]:
|
||||
stmt = (
|
||||
select(Precaution)
|
||||
.where(Precaution.study_id == study_id)
|
||||
)
|
||||
if site_name:
|
||||
stmt = stmt.where(Precaution.site_name.ilike(f"%{site_name}%"))
|
||||
if keyword:
|
||||
stmt = stmt.where(Precaution.title.ilike(f"%{keyword}%"))
|
||||
stmt = stmt.order_by(Precaution.updated_at.desc()).offset(skip).limit(limit)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
async def update_precaution(
|
||||
db: AsyncSession, precaution: Precaution, precaution_in: PrecautionUpdate
|
||||
) -> Precaution:
|
||||
update_data = precaution_in.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(precaution, key, value)
|
||||
await db.commit()
|
||||
await db.refresh(precaution)
|
||||
return precaution
|
||||
|
||||
|
||||
async def delete_precaution(db: AsyncSession, precaution: Precaution) -> None:
|
||||
await db.delete(precaution)
|
||||
await db.commit()
|
||||
@@ -11,8 +11,8 @@ from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.base_class import Base
|
||||
|
||||
|
||||
class KnowledgeNote(Base):
|
||||
__tablename__ = "knowledge_notes"
|
||||
class Precaution(Base):
|
||||
__tablename__ = "precautions"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
study_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("studies.id"), index=True, nullable=False)
|
||||
@@ -5,21 +5,21 @@ from typing import Optional
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class KnowledgeNoteCreate(BaseModel):
|
||||
class PrecautionCreate(BaseModel):
|
||||
site_name: str
|
||||
title: str
|
||||
content: str
|
||||
level: Optional[str] = None
|
||||
|
||||
|
||||
class KnowledgeNoteUpdate(BaseModel):
|
||||
class PrecautionUpdate(BaseModel):
|
||||
site_name: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
content: Optional[str] = None
|
||||
level: Optional[str] = None
|
||||
|
||||
|
||||
class KnowledgeNoteRead(BaseModel):
|
||||
class PrecautionRead(BaseModel):
|
||||
id: uuid.UUID
|
||||
study_id: uuid.UUID
|
||||
site_name: str
|
||||
Reference in New Issue
Block a user