From b5129bfa18478fa4fa79c6c0cdf09688922e7703 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Tue, 11 Feb 2025 10:14:33 +0000 Subject: [PATCH 1/3] removed analysis tables --- backend/src/api/endpoints/analysis_table.py | 139 ------------------ backend/src/app/core/data/crud/__init__.py | 2 - .../src/app/core/data/crud/analysis_table.py | 46 ------ .../src/app/core/data/dto/analysis_table.py | 44 ------ .../src/app/core/data/orm/analysis_table.py | 40 ----- backend/src/app/core/data/orm/project.py | 5 - backend/src/app/core/data/orm/user.py | 5 - backend/src/app/core/db/import_all_orms.py | 1 - backend/src/main.py | 2 - 9 files changed, 284 deletions(-) delete mode 100644 backend/src/api/endpoints/analysis_table.py delete mode 100644 backend/src/app/core/data/crud/analysis_table.py delete mode 100644 backend/src/app/core/data/dto/analysis_table.py delete mode 100644 backend/src/app/core/data/orm/analysis_table.py diff --git a/backend/src/api/endpoints/analysis_table.py b/backend/src/api/endpoints/analysis_table.py deleted file mode 100644 index 9129d7482..000000000 --- a/backend/src/api/endpoints/analysis_table.py +++ /dev/null @@ -1,139 +0,0 @@ -from typing import List - -from fastapi import APIRouter, Depends -from sqlalchemy.orm import Session - -from api.dependencies import get_current_user, get_db_session -from app.core.authorization.authz_user import AuthzUser -from app.core.data.crud import Crud -from app.core.data.crud.analysis_table import crud_analysis_table -from app.core.data.dto.analysis_table import ( - AnalysisTableCreate, - AnalysisTableCreateIntern, - AnalysisTableRead, - AnalysisTableUpdate, -) - -router = APIRouter( - prefix="/analysisTable", - dependencies=[Depends(get_current_user)], - tags=["analysisTable"], -) - - -@router.put( - "", - response_model=AnalysisTableRead, - summary="Creates an AnalysisTable", -) -def create( - *, - db: Session = Depends(get_db_session), - analysis_table: AnalysisTableCreate, - authz_user: AuthzUser = Depends(), -) -> AnalysisTableRead: - authz_user.assert_in_project(analysis_table.project_id) - - return AnalysisTableRead.model_validate( - crud_analysis_table.create( - db=db, - create_dto=AnalysisTableCreateIntern( - **analysis_table.model_dump(), user_id=authz_user.user.id - ), - ) - ) - - -@router.get( - "/{analysis_table_id}", - response_model=AnalysisTableRead, - summary="Returns the AnalysisTable with the given ID if it exists", -) -def get_by_id( - *, - db: Session = Depends(get_db_session), - analysis_table_id: int, - authz_user: AuthzUser = Depends(), -) -> AnalysisTableRead: - authz_user.assert_in_same_project_as(Crud.ANALYSIS_TABLE, analysis_table_id) - - db_obj = crud_analysis_table.read(db=db, id=analysis_table_id) - return AnalysisTableRead.model_validate(db_obj) - - -@router.get( - "/project/{project_id}/user", - response_model=List[AnalysisTableRead], - summary="Returns the AnalysisTable of the Project with the given ID and the logged-in User if it exists", -) -def get_by_project_and_user( - *, - db: Session = Depends(get_db_session), - project_id: int, - authz_user: AuthzUser = Depends(), -) -> List[AnalysisTableRead]: - # No need to authorize against the user: - # all users can see all analysis tables in the project - # at the moment. - authz_user.assert_in_project(project_id) - - db_objs = crud_analysis_table.read_by_project_and_user( - db=db, project_id=project_id, user_id=authz_user.user.id - ) - return [AnalysisTableRead.model_validate(db_obj) for db_obj in db_objs] - - -@router.patch( - "/{analysis_table_id}", - response_model=AnalysisTableRead, - summary="Updates the Analysis Table with the given ID if it exists", -) -def update_by_id( - *, - db: Session = Depends(get_db_session), - analysis_table_id: int, - analysis_table: AnalysisTableUpdate, - authz_user: AuthzUser = Depends(), -) -> AnalysisTableRead: - authz_user.assert_in_same_project_as(Crud.ANALYSIS_TABLE, analysis_table_id) - - db_obj = crud_analysis_table.update( - db=db, id=analysis_table_id, update_dto=analysis_table - ) - return AnalysisTableRead.model_validate(db_obj) - - -@router.post( - "/duplicate/{analysis_table_id}", - response_model=AnalysisTableRead, - summary="Duplicate the Analysis Table with the given ID if it exists", -) -def duplicate_by_id( - *, - db: Session = Depends(get_db_session), - analysis_table_id: int, - authz_user: AuthzUser = Depends(), -) -> AnalysisTableRead: - authz_user.assert_in_same_project_as(Crud.ANALYSIS_TABLE, analysis_table_id) - - db_obj = crud_analysis_table.duplicate_by_id( - db=db, analysis_table_id=analysis_table_id, user_id=authz_user.user.id - ) - return AnalysisTableRead.model_validate(db_obj) - - -@router.delete( - "/{analysis_table_id}", - response_model=AnalysisTableRead, - summary="Removes the AnalysisTable with the given ID if it exists", -) -def delete_by_id( - *, - db: Session = Depends(get_db_session), - analysis_table_id: int, - authz_user: AuthzUser = Depends(), -) -> AnalysisTableRead: - authz_user.assert_in_same_project_as(Crud.ANALYSIS_TABLE, analysis_table_id) - - db_obj = crud_analysis_table.remove(db=db, id=analysis_table_id) - return AnalysisTableRead.model_validate(db_obj) diff --git a/backend/src/app/core/data/crud/__init__.py b/backend/src/app/core/data/crud/__init__.py index c7d0aa9d3..e161a0e2e 100644 --- a/backend/src/app/core/data/crud/__init__.py +++ b/backend/src/app/core/data/crud/__init__.py @@ -1,6 +1,5 @@ from enum import Enum -from app.core.data.crud.analysis_table import crud_analysis_table from app.core.data.crud.annotation_document import crud_adoc from app.core.data.crud.bbox_annotation import crud_bbox_anno from app.core.data.crud.code import crud_code @@ -26,7 +25,6 @@ class Crud(Enum): - ANALYSIS_TABLE = crud_analysis_table ANNOTATION_DOCUMENT = crud_adoc BBOX_ANNOTATION = crud_bbox_anno CODE = crud_code diff --git a/backend/src/app/core/data/crud/analysis_table.py b/backend/src/app/core/data/crud/analysis_table.py deleted file mode 100644 index 8e5f7b238..000000000 --- a/backend/src/app/core/data/crud/analysis_table.py +++ /dev/null @@ -1,46 +0,0 @@ -from typing import List - -from sqlalchemy.orm import Session - -from app.core.data.crud.crud_base import CRUDBase -from app.core.data.dto.analysis_table import ( - AnalysisTableCreateIntern, - AnalysisTableUpdate, -) -from app.core.data.orm.analysis_table import AnalysisTableORM -from app.core.data.table_type import TableType - - -class CRUDAnalysisTable( - CRUDBase[AnalysisTableORM, AnalysisTableCreateIntern, AnalysisTableUpdate] -): - def read_by_project_and_user( - self, db: Session, *, project_id: int, user_id: int - ) -> List[AnalysisTableORM]: - db_obj = ( - db.query(self.model) - .filter( - self.model.project_id == project_id, - self.model.user_id == user_id, - ) - .all() - ) - return db_obj - - def duplicate_by_id( - self, db: Session, *, analysis_table_id: int, user_id: int - ) -> AnalysisTableORM: - db_obj = self.read(db, id=analysis_table_id) - return self.create( - db, - create_dto=AnalysisTableCreateIntern( - project_id=db_obj.project_id, - user_id=user_id, - title=db_obj.title + " (Copy)", - content=db_obj.content, - table_type=TableType(db_obj.table_type), - ), - ) - - -crud_analysis_table = CRUDAnalysisTable(AnalysisTableORM) diff --git a/backend/src/app/core/data/dto/analysis_table.py b/backend/src/app/core/data/dto/analysis_table.py deleted file mode 100644 index fce66e446..000000000 --- a/backend/src/app/core/data/dto/analysis_table.py +++ /dev/null @@ -1,44 +0,0 @@ -from datetime import datetime -from typing import Optional - -from pydantic import BaseModel, ConfigDict, Field - -from app.core.data.dto.dto_base import UpdateDTOBase -from app.core.data.table_type import TableType - - -# Properties shared across all DTOs -class AnalysisTableBaseDTO(BaseModel): - title: str = Field(description="Title of the AnalysisTable") - content: str = Field(description="Content of the AnalysisTable") - table_type: TableType = Field(description="TABLETYPE of the AnalysisTable") - - -# Properties for creation -class AnalysisTableCreate(AnalysisTableBaseDTO): - project_id: int = Field(description="Project the AnalysisTable belongs to") - - -class AnalysisTableCreateIntern(AnalysisTableCreate): - user_id: int = Field(description="User the AnalysisTable belongs to") - - -# Properties for updating -class AnalysisTableUpdate(AnalysisTableBaseDTO, UpdateDTOBase): - title: Optional[str] = Field(description="Title of the AnalysisTable", default=None) - content: Optional[str] = Field( - description="Content of the AnalysisTable", default=None - ) - table_type: Optional[TableType] = Field( - description="TABLETYPE of the AnalysisTable", default=None - ) - - -# Properties for reading (as in ORM) -class AnalysisTableRead(AnalysisTableBaseDTO): - id: int = Field(description="ID of the AnalysisTable") - project_id: int = Field(description="Project the AnalysisTable belongs to") - user_id: int = Field(description="User the AnalysisTable belongs to") - created: datetime = Field(description="Created timestamp of the AnalysisTable") - updated: datetime = Field(description="Updated timestamp of the AnalysisTable") - model_config = ConfigDict(from_attributes=True) diff --git a/backend/src/app/core/data/orm/analysis_table.py b/backend/src/app/core/data/orm/analysis_table.py deleted file mode 100644 index 9cdc204a8..000000000 --- a/backend/src/app/core/data/orm/analysis_table.py +++ /dev/null @@ -1,40 +0,0 @@ -from datetime import datetime -from typing import TYPE_CHECKING - -from sqlalchemy import DateTime, ForeignKey, Integer, String, func -from sqlalchemy.orm import Mapped, mapped_column, relationship - -from app.core.data.orm.orm_base import ORMBase - -if TYPE_CHECKING: - from app.core.data.orm.project import ProjectORM - from app.core.data.orm.user import UserORM - - -class AnalysisTableORM(ORMBase): - id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) - created: Mapped[datetime] = mapped_column( - DateTime, server_default=func.now(), index=True - ) - updated: Mapped[datetime] = mapped_column( - DateTime, server_default=func.now(), onupdate=func.current_timestamp() - ) - - title: Mapped[str] = mapped_column(String, nullable=False, index=False) - content: Mapped[str] = mapped_column(String, nullable=False, index=False) - table_type: Mapped[str] = mapped_column(String, nullable=False, index=False) - - project_id: Mapped[int] = mapped_column( - Integer, - ForeignKey("project.id", ondelete="CASCADE"), - nullable=False, - index=True, - ) - project: Mapped["ProjectORM"] = relationship( - "ProjectORM", back_populates="analysis_tables" - ) - - user_id: Mapped[int] = mapped_column( - Integer, ForeignKey("user.id", ondelete="CASCADE"), nullable=False, index=True - ) - user: Mapped["UserORM"] = relationship("UserORM", back_populates="analysis_tables") diff --git a/backend/src/app/core/data/orm/project.py b/backend/src/app/core/data/orm/project.py index 71db1db0e..3aa5404d6 100644 --- a/backend/src/app/core/data/orm/project.py +++ b/backend/src/app/core/data/orm/project.py @@ -7,7 +7,6 @@ from app.core.data.orm.orm_base import ORMBase if TYPE_CHECKING: - from app.core.data.orm.analysis_table import AnalysisTableORM from app.core.data.orm.code import CodeORM from app.core.data.orm.document_tag import DocumentTagORM from app.core.data.orm.memo import MemoORM @@ -52,10 +51,6 @@ class ProjectORM(ORMBase): "DocumentTagORM", back_populates="project", passive_deletes=True ) - analysis_tables: Mapped[List["AnalysisTableORM"]] = relationship( - "AnalysisTableORM", back_populates="project", passive_deletes=True - ) - whiteboards: Mapped[List["WhiteboardORM"]] = relationship( "WhiteboardORM", back_populates="project", passive_deletes=True ) diff --git a/backend/src/app/core/data/orm/user.py b/backend/src/app/core/data/orm/user.py index 3e9ca4cb4..eea94fec7 100644 --- a/backend/src/app/core/data/orm/user.py +++ b/backend/src/app/core/data/orm/user.py @@ -8,7 +8,6 @@ from app.core.data.orm.timeline_analysis import TimelineAnalysisORM if TYPE_CHECKING: - from app.core.data.orm.analysis_table import AnalysisTableORM from app.core.data.orm.annotation_document import AnnotationDocumentORM from app.core.data.orm.concept_over_time_analysis import ConceptOverTimeAnalysisORM from app.core.data.orm.memo import MemoORM @@ -45,10 +44,6 @@ class UserORM(ORMBase): "MemoORM", back_populates="user", passive_deletes=True ) - analysis_tables: Mapped[List["AnalysisTableORM"]] = relationship( - "AnalysisTableORM", back_populates="user", passive_deletes=True - ) - timeline_analysis: Mapped[List["TimelineAnalysisORM"]] = relationship( "TimelineAnalysisORM", back_populates="user", passive_deletes=True ) diff --git a/backend/src/app/core/db/import_all_orms.py b/backend/src/app/core/db/import_all_orms.py index 28148b8d4..6459cd5a2 100644 --- a/backend/src/app/core/db/import_all_orms.py +++ b/backend/src/app/core/db/import_all_orms.py @@ -2,7 +2,6 @@ # ruff: noqa: F401 """we import all ORM here so that SQLAlchemy knows about them to generate the SQL tables""" -from app.core.data.orm.analysis_table import AnalysisTableORM from app.core.data.orm.annotation_document import AnnotationDocumentORM from app.core.data.orm.bbox_annotation import BBoxAnnotationORM from app.core.data.orm.code import CodeORM diff --git a/backend/src/main.py b/backend/src/main.py index 0cb70600a..5df11e27c 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -35,7 +35,6 @@ from api.endpoints import ( analysis, - analysis_table, annoscaling, authentication, bbox_annotation, @@ -259,7 +258,6 @@ def invalid_error_handler(_, exc: InvalidError): app.include_router(prepro.router) app.include_router(export.router) app.include_router(crawler.router) -app.include_router(analysis_table.router) app.include_router(annoscaling.router) app.include_router(whiteboard.router) app.include_router(project_metadata.router) From b16311f636820f2e4725de18c43a5cafafaa9f60 Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Tue, 11 Feb 2025 10:15:27 +0000 Subject: [PATCH 2/3] removed analysis tables --- frontend/package.json | 2 - frontend/src/api/QueryKey.ts | 6 - frontend/src/api/TableHooks.ts | 125 ------ .../api/openapi/models/AnalysisTableCreate.ts | 23 - .../api/openapi/models/AnalysisTableRead.ts | 39 -- .../api/openapi/models/AnalysisTableUpdate.ts | 19 - frontend/src/api/openapi/models/TableType.ts | 10 - .../openapi/services/AnalysisTableService.ts | 121 ----- frontend/src/openapi.json | 229 ---------- frontend/src/router/routes.tsx | 10 - frontend/src/views/analysis/Analysis.tsx | 2 - .../Table/Renderer/AnnotationBlock.tsx | 25 -- .../analysis/Table/Renderer/CodeBlock.tsx | 26 -- .../Table/Renderer/CustomHTMLCellRenderer.tsx | 72 --- .../analysis/Table/Renderer/SdocBlock.tsx | 27 -- .../views/analysis/Table/TableDashboard.tsx | 160 ------- .../src/views/analysis/Table/TableView.tsx | 54 --- .../views/analysis/Table/TableViewContent.tsx | 415 ------------------ .../Table/Toolbar/AddAnnotationDialog.tsx | 110 ----- .../analysis/Table/Toolbar/AddCodeDialog.tsx | 58 --- .../Table/Toolbar/AddDocumentDialog.tsx | 67 --- .../Table/Toolbar/PageNavigationButton.tsx | 57 --- .../src/views/analysis/Table/templates.ts | 69 --- 23 files changed, 1726 deletions(-) delete mode 100644 frontend/src/api/TableHooks.ts delete mode 100644 frontend/src/api/openapi/models/AnalysisTableCreate.ts delete mode 100644 frontend/src/api/openapi/models/AnalysisTableRead.ts delete mode 100644 frontend/src/api/openapi/models/AnalysisTableUpdate.ts delete mode 100644 frontend/src/api/openapi/models/TableType.ts delete mode 100644 frontend/src/api/openapi/services/AnalysisTableService.ts delete mode 100644 frontend/src/views/analysis/Table/Renderer/AnnotationBlock.tsx delete mode 100644 frontend/src/views/analysis/Table/Renderer/CodeBlock.tsx delete mode 100644 frontend/src/views/analysis/Table/Renderer/CustomHTMLCellRenderer.tsx delete mode 100644 frontend/src/views/analysis/Table/Renderer/SdocBlock.tsx delete mode 100644 frontend/src/views/analysis/Table/TableDashboard.tsx delete mode 100644 frontend/src/views/analysis/Table/TableView.tsx delete mode 100644 frontend/src/views/analysis/Table/TableViewContent.tsx delete mode 100644 frontend/src/views/analysis/Table/Toolbar/AddAnnotationDialog.tsx delete mode 100644 frontend/src/views/analysis/Table/Toolbar/AddCodeDialog.tsx delete mode 100644 frontend/src/views/analysis/Table/Toolbar/AddDocumentDialog.tsx delete mode 100644 frontend/src/views/analysis/Table/Toolbar/PageNavigationButton.tsx delete mode 100644 frontend/src/views/analysis/Table/templates.ts diff --git a/frontend/package.json b/frontend/package.json index d6817a139..932e69913 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,6 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@fontsource/roboto": "^5.1.0", - "@handsontable/react": "^14.6.0", "@hookform/error-message": "^2.0.1", "@mui/icons-material": "^6.1.2", "@mui/lab": "^6.0.0-beta.10", @@ -35,7 +34,6 @@ "@tanstack/react-virtual": "^3.10.8", "d3": "^7.9.0", "dayjs": "^1.11.13", - "handsontable": "^14.6.0", "html-react-parser": "^5.1.18", "html-to-image": "^1.11.11", "lodash": "^4.17.21", diff --git a/frontend/src/api/QueryKey.ts b/frontend/src/api/QueryKey.ts index aa8388101..30c4d6b4a 100644 --- a/frontend/src/api/QueryKey.ts +++ b/frontend/src/api/QueryKey.ts @@ -146,10 +146,4 @@ export const QueryKey = { LLM_JOB: "llmJob", // all llm jobs of a Project (by project id) PROJECT_LLM_JOBS: "projectLLMJobs", - - // TO BE DELETED: - // a single TABLE (by TABLE id) - TABLE: "table", - // all tables of the project of the logged-in user (by project id) - TABLES_PROJECT_USER: "tablesProjectUser", }; diff --git a/frontend/src/api/TableHooks.ts b/frontend/src/api/TableHooks.ts deleted file mode 100644 index e0ee34996..000000000 --- a/frontend/src/api/TableHooks.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { useMutation, useQuery } from "@tanstack/react-query"; -import queryClient from "../plugins/ReactQueryClient.ts"; -import { QueryKey } from "./QueryKey.ts"; -import { AnalysisTableRead } from "./openapi/models/AnalysisTableRead.ts"; -import { AnalysisTableService } from "./openapi/services/AnalysisTableService.ts"; - -export type TablePage = { - id: string; - name: string; - content: string[][]; -}; - -export type TableRead = Omit & { content: TablePage[] }; - -const useGetTable = (tableId: number | null | undefined) => - useQuery({ - queryKey: [QueryKey.TABLE, tableId], - queryFn: async () => { - const data = await AnalysisTableService.getById({ analysisTableId: tableId! }); - const content = JSON.parse(data.content) as TablePage[]; - return { ...data, content }; - }, - retry: false, - enabled: !!tableId, - }); - -const useGetUserTables = (projectId: number | null | undefined) => - useQuery({ - queryKey: [QueryKey.TABLES_PROJECT_USER, projectId], - queryFn: async () => { - const data = await AnalysisTableService.getByProjectAndUser({ projectId: projectId! }); - return data.map((table) => { - const content = JSON.parse(table.content) as TablePage[]; - return { ...table, content }; - }); - }, - retry: false, - enabled: !!projectId, - }); - -const useCreateTable = () => - useMutation({ - mutationFn: AnalysisTableService.create, - onSettled(data, _error, variables) { - if (data) { - queryClient.setQueryData([QueryKey.TABLES_PROJECT_USER, data.project_id], (prevTables) => { - const newTable = { - ...data, - content: [], - }; - if (!prevTables) return [newTable]; - return [...prevTables, newTable]; - }); - queryClient.invalidateQueries({ queryKey: [QueryKey.TABLE, data.id] }); - } - queryClient.invalidateQueries({ - queryKey: [QueryKey.TABLES_PROJECT_USER, variables.requestBody.project_id], - }); - }, - }); - -const useUpdateTable = () => - useMutation({ - mutationFn: AnalysisTableService.updateById, - onSettled(data, _error, variables) { - if (data) { - const updatedTable = { - ...data, - content: JSON.parse(data.content) as TablePage[], - }; - queryClient.setQueryData([QueryKey.TABLES_PROJECT_USER, data.project_id], (prevTables) => { - if (!prevTables) return [updatedTable]; - const index = prevTables.findIndex((table) => table.id === data.id); - if (index === -1) { - return prevTables; - } - return [...prevTables.slice(0, index), updatedTable, ...prevTables.slice(index + 1)]; - }); - queryClient.invalidateQueries({ queryKey: [QueryKey.TABLES_PROJECT_USER, data.project_id] }); - } - queryClient.invalidateQueries({ queryKey: [QueryKey.TABLE, variables.analysisTableId] }); - }, - }); - -const useDuplicateTable = () => - useMutation({ - mutationFn: AnalysisTableService.duplicateById, - onSuccess(data) { - queryClient.setQueryData([QueryKey.TABLES_PROJECT_USER, data.project_id], (prevTables) => { - const newTable = { - ...data, - content: JSON.parse(data.content) as TablePage[], - }; - if (!prevTables) return [newTable]; - return [...prevTables, newTable]; - }); - queryClient.invalidateQueries({ queryKey: [QueryKey.TABLES_PROJECT_USER, data.project_id] }); - }, - }); - -const useDeleteTable = () => - useMutation({ - mutationFn: AnalysisTableService.deleteById, - onSettled(data, _error, variables) { - if (data) { - queryClient.setQueryData([QueryKey.TABLES_PROJECT_USER, data.project_id], (prevTables) => { - if (!prevTables) return []; - return prevTables.filter((table) => table.id !== data.id); - }); - queryClient.invalidateQueries({ queryKey: [QueryKey.TABLES_PROJECT_USER, data.project_id] }); - } - queryClient.invalidateQueries({ queryKey: [QueryKey.TABLE, variables.analysisTableId] }); - }, - }); - -const TableHooks = { - useGetTable, - useGetUserTables, - useCreateTable, - useUpdateTable, - useDuplicateTable, - useDeleteTable, -}; - -export default TableHooks; diff --git a/frontend/src/api/openapi/models/AnalysisTableCreate.ts b/frontend/src/api/openapi/models/AnalysisTableCreate.ts deleted file mode 100644 index f8351862c..000000000 --- a/frontend/src/api/openapi/models/AnalysisTableCreate.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { TableType } from "./TableType"; -export type AnalysisTableCreate = { - /** - * Title of the AnalysisTable - */ - title: string; - /** - * Content of the AnalysisTable - */ - content: string; - /** - * TABLETYPE of the AnalysisTable - */ - table_type: TableType; - /** - * Project the AnalysisTable belongs to - */ - project_id: number; -}; diff --git a/frontend/src/api/openapi/models/AnalysisTableRead.ts b/frontend/src/api/openapi/models/AnalysisTableRead.ts deleted file mode 100644 index 86398ff3d..000000000 --- a/frontend/src/api/openapi/models/AnalysisTableRead.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { TableType } from "./TableType"; -export type AnalysisTableRead = { - /** - * Title of the AnalysisTable - */ - title: string; - /** - * Content of the AnalysisTable - */ - content: string; - /** - * TABLETYPE of the AnalysisTable - */ - table_type: TableType; - /** - * ID of the AnalysisTable - */ - id: number; - /** - * Project the AnalysisTable belongs to - */ - project_id: number; - /** - * User the AnalysisTable belongs to - */ - user_id: number; - /** - * Created timestamp of the AnalysisTable - */ - created: string; - /** - * Updated timestamp of the AnalysisTable - */ - updated: string; -}; diff --git a/frontend/src/api/openapi/models/AnalysisTableUpdate.ts b/frontend/src/api/openapi/models/AnalysisTableUpdate.ts deleted file mode 100644 index dd91b2603..000000000 --- a/frontend/src/api/openapi/models/AnalysisTableUpdate.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { TableType } from "./TableType"; -export type AnalysisTableUpdate = { - /** - * Title of the AnalysisTable - */ - title?: string | null; - /** - * Content of the AnalysisTable - */ - content?: string | null; - /** - * TABLETYPE of the AnalysisTable - */ - table_type?: TableType | null; -}; diff --git a/frontend/src/api/openapi/models/TableType.ts b/frontend/src/api/openapi/models/TableType.ts deleted file mode 100644 index a36345222..000000000 --- a/frontend/src/api/openapi/models/TableType.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export enum TableType { - CUSTOM = "custom", - SITUATION = "situation", - PHENOMENON = "phenomenon", - INTERPRETATION = "interpretation", -} diff --git a/frontend/src/api/openapi/services/AnalysisTableService.ts b/frontend/src/api/openapi/services/AnalysisTableService.ts deleted file mode 100644 index cf88f6ba6..000000000 --- a/frontend/src/api/openapi/services/AnalysisTableService.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { AnalysisTableCreate } from "../models/AnalysisTableCreate"; -import type { AnalysisTableRead } from "../models/AnalysisTableRead"; -import type { AnalysisTableUpdate } from "../models/AnalysisTableUpdate"; -import type { CancelablePromise } from "../core/CancelablePromise"; -import { OpenAPI } from "../core/OpenAPI"; -import { request as __request } from "../core/request"; -export class AnalysisTableService { - /** - * Creates an AnalysisTable - * @returns AnalysisTableRead Successful Response - * @throws ApiError - */ - public static create({ requestBody }: { requestBody: AnalysisTableCreate }): CancelablePromise { - return __request(OpenAPI, { - method: "PUT", - url: "/analysisTable", - body: requestBody, - mediaType: "application/json", - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Returns the AnalysisTable with the given ID if it exists - * @returns AnalysisTableRead Successful Response - * @throws ApiError - */ - public static getById({ analysisTableId }: { analysisTableId: number }): CancelablePromise { - return __request(OpenAPI, { - method: "GET", - url: "/analysisTable/{analysis_table_id}", - path: { - analysis_table_id: analysisTableId, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Updates the Analysis Table with the given ID if it exists - * @returns AnalysisTableRead Successful Response - * @throws ApiError - */ - public static updateById({ - analysisTableId, - requestBody, - }: { - analysisTableId: number; - requestBody: AnalysisTableUpdate; - }): CancelablePromise { - return __request(OpenAPI, { - method: "PATCH", - url: "/analysisTable/{analysis_table_id}", - path: { - analysis_table_id: analysisTableId, - }, - body: requestBody, - mediaType: "application/json", - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Removes the AnalysisTable with the given ID if it exists - * @returns AnalysisTableRead Successful Response - * @throws ApiError - */ - public static deleteById({ analysisTableId }: { analysisTableId: number }): CancelablePromise { - return __request(OpenAPI, { - method: "DELETE", - url: "/analysisTable/{analysis_table_id}", - path: { - analysis_table_id: analysisTableId, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Returns the AnalysisTable of the Project with the given ID and the logged-in User if it exists - * @returns AnalysisTableRead Successful Response - * @throws ApiError - */ - public static getByProjectAndUser({ projectId }: { projectId: number }): CancelablePromise> { - return __request(OpenAPI, { - method: "GET", - url: "/analysisTable/project/{project_id}/user", - path: { - project_id: projectId, - }, - errors: { - 422: `Validation Error`, - }, - }); - } - /** - * Duplicate the Analysis Table with the given ID if it exists - * @returns AnalysisTableRead Successful Response - * @throws ApiError - */ - public static duplicateById({ analysisTableId }: { analysisTableId: number }): CancelablePromise { - return __request(OpenAPI, { - method: "POST", - url: "/analysisTable/duplicate/{analysis_table_id}", - path: { - analysis_table_id: analysisTableId, - }, - errors: { - 422: `Validation Error`, - }, - }); - } -} diff --git a/frontend/src/openapi.json b/frontend/src/openapi.json index c46713673..37e951315 100644 --- a/frontend/src/openapi.json +++ b/frontend/src/openapi.json @@ -3675,166 +3675,6 @@ } } }, - "/analysisTable": { - "put": { - "tags": ["analysisTable"], - "summary": "Creates an AnalysisTable", - "operationId": "create", - "requestBody": { - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableCreate" } } }, - "required": true - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableRead" } } } - }, - "422": { - "description": "Validation Error", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } - } - }, - "security": [{ "OAuth2PasswordBearer": [] }] - } - }, - "/analysisTable/{analysis_table_id}": { - "get": { - "tags": ["analysisTable"], - "summary": "Returns the AnalysisTable with the given ID if it exists", - "operationId": "get_by_id", - "security": [{ "OAuth2PasswordBearer": [] }], - "parameters": [ - { - "name": "analysis_table_id", - "in": "path", - "required": true, - "schema": { "type": "integer", "title": "Analysis Table Id" } - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableRead" } } } - }, - "422": { - "description": "Validation Error", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } - } - } - }, - "patch": { - "tags": ["analysisTable"], - "summary": "Updates the Analysis Table with the given ID if it exists", - "operationId": "update_by_id", - "security": [{ "OAuth2PasswordBearer": [] }], - "parameters": [ - { - "name": "analysis_table_id", - "in": "path", - "required": true, - "schema": { "type": "integer", "title": "Analysis Table Id" } - } - ], - "requestBody": { - "required": true, - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableUpdate" } } } - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableRead" } } } - }, - "422": { - "description": "Validation Error", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } - } - } - }, - "delete": { - "tags": ["analysisTable"], - "summary": "Removes the AnalysisTable with the given ID if it exists", - "operationId": "delete_by_id", - "security": [{ "OAuth2PasswordBearer": [] }], - "parameters": [ - { - "name": "analysis_table_id", - "in": "path", - "required": true, - "schema": { "type": "integer", "title": "Analysis Table Id" } - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableRead" } } } - }, - "422": { - "description": "Validation Error", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } - } - } - } - }, - "/analysisTable/project/{project_id}/user": { - "get": { - "tags": ["analysisTable"], - "summary": "Returns the AnalysisTable of the Project with the given ID and the logged-in User if it exists", - "operationId": "get_by_project_and_user", - "security": [{ "OAuth2PasswordBearer": [] }], - "parameters": [ - { - "name": "project_id", - "in": "path", - "required": true, - "schema": { "type": "integer", "title": "Project Id" } - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { "$ref": "#/components/schemas/AnalysisTableRead" }, - "title": "Response Analysistable-Get By Project And User" - } - } - } - }, - "422": { - "description": "Validation Error", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } - } - } - } - }, - "/analysisTable/duplicate/{analysis_table_id}": { - "post": { - "tags": ["analysisTable"], - "summary": "Duplicate the Analysis Table with the given ID if it exists", - "operationId": "duplicate_by_id", - "security": [{ "OAuth2PasswordBearer": [] }], - "parameters": [ - { - "name": "analysis_table_id", - "in": "path", - "required": true, - "schema": { "type": "integer", "title": "Analysis Table Id" } - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalysisTableRead" } } } - }, - "422": { - "description": "Validation Error", - "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } - } - } - } - }, "/annoscaling/suggest": { "post": { "tags": ["annoscaling"], @@ -5355,70 +5195,6 @@ }, "components": { "schemas": { - "AnalysisTableCreate": { - "properties": { - "title": { "type": "string", "title": "Title", "description": "Title of the AnalysisTable" }, - "content": { "type": "string", "title": "Content", "description": "Content of the AnalysisTable" }, - "table_type": { "$ref": "#/components/schemas/TableType", "description": "TABLETYPE of the AnalysisTable" }, - "project_id": { - "type": "integer", - "title": "Project Id", - "description": "Project the AnalysisTable belongs to" - } - }, - "type": "object", - "required": ["title", "content", "table_type", "project_id"], - "title": "AnalysisTableCreate" - }, - "AnalysisTableRead": { - "properties": { - "title": { "type": "string", "title": "Title", "description": "Title of the AnalysisTable" }, - "content": { "type": "string", "title": "Content", "description": "Content of the AnalysisTable" }, - "table_type": { "$ref": "#/components/schemas/TableType", "description": "TABLETYPE of the AnalysisTable" }, - "id": { "type": "integer", "title": "Id", "description": "ID of the AnalysisTable" }, - "project_id": { - "type": "integer", - "title": "Project Id", - "description": "Project the AnalysisTable belongs to" - }, - "user_id": { "type": "integer", "title": "User Id", "description": "User the AnalysisTable belongs to" }, - "created": { - "type": "string", - "format": "date-time", - "title": "Created", - "description": "Created timestamp of the AnalysisTable" - }, - "updated": { - "type": "string", - "format": "date-time", - "title": "Updated", - "description": "Updated timestamp of the AnalysisTable" - } - }, - "type": "object", - "required": ["title", "content", "table_type", "id", "project_id", "user_id", "created", "updated"], - "title": "AnalysisTableRead" - }, - "AnalysisTableUpdate": { - "properties": { - "title": { - "anyOf": [{ "type": "string" }, { "type": "null" }], - "title": "Title", - "description": "Title of the AnalysisTable" - }, - "content": { - "anyOf": [{ "type": "string" }, { "type": "null" }], - "title": "Content", - "description": "Content of the AnalysisTable" - }, - "table_type": { - "anyOf": [{ "$ref": "#/components/schemas/TableType" }, { "type": "null" }], - "description": "TABLETYPE of the AnalysisTable" - } - }, - "type": "object", - "title": "AnalysisTableUpdate" - }, "AnnoscalingConfirmSuggest": { "properties": { "project_id": { "type": "integer", "title": "Project Id", "description": "Project to apply suggestions" }, @@ -9190,11 +8966,6 @@ "enum": ["STRING_CONTAINS", "STRING_EQUALS", "STRING_NOT_EQUALS", "STRING_STARTS_WITH", "STRING_ENDS_WITH"], "title": "StringOperator" }, - "TableType": { - "type": "string", - "enum": ["custom", "situation", "phenomenon", "interpretation"], - "title": "TableType" - }, "TagStat": { "properties": { "tag": { "$ref": "#/components/schemas/DocumentTagRead", "description": "The counted document tag." }, diff --git a/frontend/src/router/routes.tsx b/frontend/src/router/routes.tsx index 557e64f3c..445603b27 100644 --- a/frontend/src/router/routes.tsx +++ b/frontend/src/router/routes.tsx @@ -14,8 +14,6 @@ import CotaDashboard from "../views/analysis/ConceptsOverTime/CotaDashboard.tsx" import CotaView from "../views/analysis/ConceptsOverTime/CotaView.tsx"; import SentAnnotationAnalysis from "../views/analysis/SentAnnotationAnalysis/SentAnnotationAnalysis.tsx"; import SpanAnnotationAnalysis from "../views/analysis/SpanAnnotationAnalysis/SpanAnnotationAnalysis.tsx"; -import TableDashboard from "../views/analysis/Table/TableDashboard.tsx"; -import TableView from "../views/analysis/Table/TableView.tsx"; import TimelineAnalysis from "../views/analysis/TimelineAnalysis/TimelineAnalysis.tsx"; import TimelineAnalysisDashboard from "../views/analysis/TimelineAnalysis/TimelineAnalysisDashboard.tsx"; import WordFrequency from "../views/analysis/WordFrequency/WordFrequency.tsx"; @@ -138,14 +136,6 @@ const router = createBrowserRouter([ path: "/project/:projectId/analysis/word-frequency", element: , }, - { - path: "/project/:projectId/analysis/table", - element: , - }, - { - path: "/project/:projectId/analysis/table/:tableId", - element: , - }, { path: "/project/:projectId/analysis/concepts-over-time-analysis", element: , diff --git a/frontend/src/views/analysis/Analysis.tsx b/frontend/src/views/analysis/Analysis.tsx index f246eec83..5db9f7744 100644 --- a/frontend/src/views/analysis/Analysis.tsx +++ b/frontend/src/views/analysis/Analysis.tsx @@ -64,8 +64,6 @@ function Analysis() { description={"Semi-automatically scale annotations"} color={"#77dd77"} /> - - ); diff --git a/frontend/src/views/analysis/Table/Renderer/AnnotationBlock.tsx b/frontend/src/views/analysis/Table/Renderer/AnnotationBlock.tsx deleted file mode 100644 index 50ed089fb..000000000 --- a/frontend/src/views/analysis/Table/Renderer/AnnotationBlock.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import CodeBlock from "./CodeBlock.tsx"; -import SdocBlock from "./SdocBlock.tsx"; - -interface AnnotationBlockProps { - sdocId: number; - codeId: number; - text: string | React.JSX.Element | React.JSX.Element[]; -} - -function AnnotationBlock({ sdocId, codeId, text }: AnnotationBlockProps) { - return ( - <> - {'"'} - {text} - {'"'} - {" ("} - - {", "} - - {")"} - - ); -} - -export default AnnotationBlock; diff --git a/frontend/src/views/analysis/Table/Renderer/CodeBlock.tsx b/frontend/src/views/analysis/Table/Renderer/CodeBlock.tsx deleted file mode 100644 index 3a043f2b8..000000000 --- a/frontend/src/views/analysis/Table/Renderer/CodeBlock.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Stack, Box } from "@mui/material"; -import CodeHooks from "../../../../api/CodeHooks.ts"; - -interface CodeBlockProps { - codeId: number; -} - -function CodeBlock({ codeId }: CodeBlockProps) { - const code = CodeHooks.useGetCode(codeId); - - if (code.isSuccess) { - return ( - - - {code.data.name} - - ); - } else { - return Loading...; - } -} - -export default CodeBlock; diff --git a/frontend/src/views/analysis/Table/Renderer/CustomHTMLCellRenderer.tsx b/frontend/src/views/analysis/Table/Renderer/CustomHTMLCellRenderer.tsx deleted file mode 100644 index 4d32b5a9d..000000000 --- a/frontend/src/views/analysis/Table/Renderer/CustomHTMLCellRenderer.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import parse, { DOMNode, Element, HTMLReactParserOptions, domToReact } from "html-react-parser"; -import { useMemo } from "react"; -import AnnotationBlock from "./AnnotationBlock.tsx"; -import CodeBlock from "./CodeBlock.tsx"; -import SdocBlock from "./SdocBlock.tsx"; - -const processingInstructions = (col: number, row: number): HTMLReactParserOptions => { - const options: HTMLReactParserOptions = { - replace(domNode, index) { - if (domNode instanceof Element && domNode.attribs) { - // code - if (domNode.name === "code") { - if (domNode.attribs.id) { - const codeId = parseInt(domNode.attribs.id); - return ; - } else { - return Code ?; - } - } - // sdoc - else if (domNode.name === "sdoc") { - if (domNode.attribs.id) { - const sdocId = parseInt(domNode.attribs.id); - return ; - } else { - return Document ?; - } - } - // annotation - else if (domNode.name === "annotation") { - if (domNode.attribs.sdocid && domNode.attribs.codeid) { - const codeId = parseInt(domNode.attribs.codeid); - const sdocId = parseInt(domNode.attribs.sdocid); - return ( - - ); - } else { - return Annotation ?; - } - } - // anything else - else { - return domToReact(domNode.children as DOMNode[], options); - } - } - }, - }; - return options; -}; - -interface CustomHTMLCellRendererProps { - col?: number; - row?: number; - value?: string; -} - -function CustomHTMLCellRenderer(props: CustomHTMLCellRendererProps) { - const renderedTokens = useMemo(() => { - if (!props.col || !props.row || !props.value) return null; - - return parse(props.value, processingInstructions(props.col, props.row)); - }, [props.col, props.row, props.value]); - - return <>{renderedTokens}; -} - -export default CustomHTMLCellRenderer; diff --git a/frontend/src/views/analysis/Table/Renderer/SdocBlock.tsx b/frontend/src/views/analysis/Table/Renderer/SdocBlock.tsx deleted file mode 100644 index ef202956c..000000000 --- a/frontend/src/views/analysis/Table/Renderer/SdocBlock.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import DescriptionIcon from "@mui/icons-material/Description"; -import { Stack } from "@mui/material"; -import { Link } from "react-router-dom"; -import SdocHooks from "../../../../api/SdocHooks.ts"; - -interface SdocBlockProps { - sdocId: number; -} - -function SdocBlock({ sdocId }: SdocBlockProps) { - const sdoc = SdocHooks.useGetDocument(sdocId); - - if (sdoc.isSuccess) { - return ( - - - - {sdoc.data.name} - - - ); - } else { - return Loading...; - } -} - -export default SdocBlock; diff --git a/frontend/src/views/analysis/Table/TableDashboard.tsx b/frontend/src/views/analysis/Table/TableDashboard.tsx deleted file mode 100644 index cfe54b565..000000000 --- a/frontend/src/views/analysis/Table/TableDashboard.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { MRT_Row, MRT_TableOptions } from "material-react-table"; -import { useParams } from "react-router"; -import { v4 as uuidv4 } from "uuid"; -import TableHooks from "../../../api/TableHooks.ts"; -import { TableType } from "../../../api/openapi/models/TableType.ts"; -import ConfirmationAPI from "../../../components/ConfirmationDialog/ConfirmationAPI.ts"; -import { useOpenSnackbar } from "../../../components/SnackbarDialog/useOpenSnackbar.ts"; -import AnalysisDashboard from "../AnalysisDashboard/AnalysisDashboard.tsx"; -import { - AnaylsisDashboardRow, - HandleCreateAnalysis, - useAnalysisDashboardTable, -} from "../AnalysisDashboard/useAnalysisDashboardTable.tsx"; -import { TableType2Template } from "./templates.ts"; - -function TableDashboard() { - // global client state - const projectId = parseInt((useParams() as { projectId: string }).projectId); - - // global server state - const { - data: userTables, - isLoading: isLoadingTables, - isFetching: isFetchingTables, - isError: isLoadingTablesError, - } = TableHooks.useGetUserTables(projectId); - - // mutations - const { mutate: createTable, isPending: isCreatingTable } = TableHooks.useCreateTable(); - const { mutate: deleteTable, isPending: isDeletingTable, variables: deletingVariables } = TableHooks.useDeleteTable(); - const { - mutate: duplicateTable, - isPending: isDuplicatingTable, - variables: duplicatingVariables, - } = TableHooks.useDuplicateTable(); - const { mutate: updateTable, isPending: isUpdatingTable } = TableHooks.useUpdateTable(); - - // snackbar - const openSnackbar = useOpenSnackbar(); - - // CRUD actions - const handleCreateAnalysis: HandleCreateAnalysis = - (createOption) => - ({ values, table }) => { - if (!createOption) return; - - const tableType = createOption.option as TableType; - const content = [{ id: uuidv4(), name: `Table sheet 1`, content: TableType2Template[tableType] }]; - createTable( - { - requestBody: { - project_id: projectId, - title: values.title, - content: JSON.stringify(content), - table_type: tableType, - }, - }, - { - onSuccess(data) { - openSnackbar({ - text: `Created new table '${data.title}'`, - severity: "success", - }); - table.setCreatingRow(null); //exit creating mode - }, - }, - ); - }; - - const handleDuplicateAnalysis = (row: MRT_Row) => { - duplicateTable( - { - analysisTableId: row.original.id, - }, - { - onSuccess(data) { - openSnackbar({ - text: `Duplicated table '${data.title}'`, - severity: "success", - }); - }, - }, - ); - }; - - const handleDeleteAnalysis = (row: MRT_Row) => { - ConfirmationAPI.openConfirmationDialog({ - text: `Do you really want to remove the table ${row.original.id}? This action cannot be undone!`, - onAccept: () => { - deleteTable( - { - analysisTableId: row.original.id, - }, - { - onSuccess(data) { - openSnackbar({ - text: `Deleted table '${data.title}'`, - severity: "success", - }); - }, - }, - ); - }, - }); - }; - - const handleEditAnalysis: MRT_TableOptions["onEditingRowSave"] = ({ values, table, row }) => { - updateTable( - { - analysisTableId: row.original.id, - requestBody: { - title: values.title, - }, - }, - { - onSuccess(data) { - openSnackbar({ - text: `Updated table '${data.title}'`, - severity: "success", - }); - table.setEditingRow(null); //exit editing mode - }, - }, - ); - }; - - // table - const table = useAnalysisDashboardTable({ - analysisName: "Table", - data: userTables || [], - isLoadingData: isLoadingTables, - isFetchingData: isFetchingTables, - isLoadingDataError: isLoadingTablesError, - isCreatingAnalysis: isCreatingTable, - isDeletingAnalysis: isDeletingTable, - isDuplicatingAnalysis: isDuplicatingTable, - isUpdatingAnalysis: isUpdatingTable, - deletingAnalysisId: deletingVariables?.analysisTableId, - duplicatingAnalysisId: duplicatingVariables?.analysisTableId, - onOpenAnalysis: (analysisId) => console.log("Opening Table " + analysisId), - handleCreateAnalysis, - handleEditAnalysis, - handleDeleteAnalysis, - handleDuplicateAnalysis, - analysisCreateOptions: Object.values(TableType).map((key) => { - return { option: key, label: key.toLocaleUpperCase() }; - }), - }); - - return ( - - ); -} - -export default TableDashboard; diff --git a/frontend/src/views/analysis/Table/TableView.tsx b/frontend/src/views/analysis/Table/TableView.tsx deleted file mode 100644 index f855a6478..000000000 --- a/frontend/src/views/analysis/Table/TableView.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { CircularProgress, Portal } from "@mui/material"; -import { useContext } from "react"; -import { useParams } from "react-router-dom"; -import TableHooks from "../../../api/TableHooks.ts"; -import EditableTypography from "../../../components/EditableTypography.tsx"; -import { AppBarContext } from "../../../layouts/TwoBarLayout.tsx"; -import TableViewContent from "./TableViewContent.tsx"; - -function TableView() { - // global client state - const appBarContainerRef = useContext(AppBarContext); - const urlParams = useParams() as { projectId: string; tableId: string }; - const projectId = parseInt(urlParams.projectId); - const tableId = parseInt(urlParams.tableId); - - // global server state - const updateTableMutation = TableHooks.useUpdateTable(); - const table = TableHooks.useGetTable(tableId); - - const handleChange = (value: string) => { - if (!table.data || table.data.title === value) return; - - updateTableMutation.mutate({ - analysisTableId: table.data.id, - requestBody: { - title: value, - table_type: table.data.table_type, - content: JSON.stringify(table.data.content), - }, - }); - }; - - return ( - <> - - - - {table.isSuccess ? ( - - ) : table.isLoading ? ( - - ) : table.isError ? ( -
ERROR: {table.error.message}
- ) : null} - - ); -} - -export default TableView; diff --git a/frontend/src/views/analysis/Table/TableViewContent.tsx b/frontend/src/views/analysis/Table/TableViewContent.tsx deleted file mode 100644 index 1fe590323..000000000 --- a/frontend/src/views/analysis/Table/TableViewContent.tsx +++ /dev/null @@ -1,415 +0,0 @@ -import AddIcon from "@mui/icons-material/Add"; -import ContentCopyIcon from "@mui/icons-material/ContentCopy"; -import DeleteIcon from "@mui/icons-material/Delete"; -import EditIcon from "@mui/icons-material/Edit"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; -import SaveIcon from "@mui/icons-material/Save"; - -import { - Box, - Button, - IconButton, - ListItemIcon, - ListItemText, - Menu, - MenuItem, - Stack, - Tab, - Tabs, - TextField, - Toolbar, - Tooltip, - tabsClasses, -} from "@mui/material"; -import { useEffect, useMemo, useRef, useState } from "react"; -import { useBlocker } from "react-router-dom"; -import { v4 as uuidv4 } from "uuid"; -import TableHooks, { TableRead } from "../../../api/TableHooks.ts"; - -// register Handsontable's modules -import type { HotTableClass } from "@handsontable//react/hotTableClass.d.ts"; -import { HotTable } from "@handsontable/react"; -import Handsontable from "handsontable/base"; -import "handsontable/dist/handsontable.full.min.css"; -import { registerAllModules } from "handsontable/registry"; -import { CodeRead } from "../../../api/openapi/models/CodeRead.ts"; -import { useOpenSnackbar } from "../../../components/SnackbarDialog/useOpenSnackbar.ts"; -import CustomHTMLCellRenderer from "./Renderer/CustomHTMLCellRenderer.tsx"; -import AddAnnotationDialog from "./Toolbar/AddAnnotationDialog.tsx"; -import AddCodeDialog from "./Toolbar/AddCodeDialog.tsx"; -import AddDocumentDialog from "./Toolbar/AddDocumentDialog.tsx"; -import PageNavigationButton from "./Toolbar/PageNavigationButton.tsx"; -import { TableType2Template } from "./templates.ts"; - -registerAllModules(); - -// table actions -const isCellSelected = (hot: Handsontable | null | undefined) => { - if (!hot) return false; - - const selectedCells = hot.getSelected(); - const selectedCell = selectedCells ? selectedCells[0] : undefined; - const x = selectedCell ? selectedCell[0] : undefined; - const y = selectedCell ? selectedCell[1] : undefined; - - const shouldOpen = x !== undefined && y !== undefined && x !== -1 && y !== -1; - if (!shouldOpen) { - alert("Please select a cell."); - } - return shouldOpen; -}; - -type CellInfo = { - x: number; - y: number; - data: string; -}; - -const getSelectedCellInfo = (hot: Handsontable): CellInfo => { - const selectedCell = hot.getSelected()![0]!; - return { - x: selectedCell[1], - y: selectedCell[0], - data: hot.getDataAtCell(selectedCell[0], selectedCell[1]), - }; -}; - -const addData = (hot: Handsontable, cell: CellInfo, dataToAdd: string[], addAsRows: boolean) => { - if (addAsRows) { - addDataBelowCellAsRows(hot, cell, dataToAdd); - } else { - addDataToCell(hot, cell, dataToAdd); - } -}; - -const addDataToCell = (hot: Handsontable, cell: CellInfo, dataToAdd: string[]) => { - const newLine = cell.data.length > 0 ? "\n" : ""; - hot.setDataAtCell(cell.y, cell.x, cell.data + newLine + dataToAdd.join("\n")); -}; - -const addDataBelowCellAsRows = (hot: Handsontable, cell: CellInfo, dataToAdd: string[]) => { - hot.alter("insert_row_below", cell.y, dataToAdd.length); - dataToAdd.forEach((dta, i) => { - hot.setDataAtCell(cell.y + 1 + i, cell.x, dta); - }); -}; - -interface TableViewContentProps { - table: TableRead; -} - -function TableViewContent({ table }: TableViewContentProps) { - // local client state - const hotRef = useRef(null); - const [tablePages, setTablePages] = useState(table.content); // page data - const [hasChanged, setHasChanged] = useState(false); - - // table pages tabs - const [currentPageId, setCurrentPageId] = useState(table.content[0].id); // pages tab - const handlePageChange = (_event: React.SyntheticEvent, newValue: string) => { - setCurrentPageId(newValue); - }; - - const currentPage = useMemo(() => { - return tablePages.find((p) => p.id === currentPageId); - }, [currentPageId, tablePages]); - - // toolbar - const [selectedPageId, setSelectedPageId] = useState(""); // the page that was right clicked to open the context menu - const [renamingPageId, setRenamingPageId] = useState(""); // the page that is being renamed - - // global server state - const updateTable = TableHooks.useUpdateTable(); - - // snackbar - const openSnackbar = useOpenSnackbar(); - - // context menu - const [anchorEl, setAnchorEl] = useState(null); - const isOpen = Boolean(anchorEl); - const handleTabMenuClick = (page: string) => (event: React.MouseEvent) => { - event.preventDefault(); - setAnchorEl(event.currentTarget); - setSelectedPageId(page); - setCurrentPageId(page); - }; - - // block navigation if we have changes - const [oldData, setOldData] = useState(JSON.stringify(table.content)); - useEffect(() => { - setOldData(JSON.stringify(table.content)); - }, [table.content]); - useBlocker(() => { - if (oldData !== JSON.stringify(tablePages)) { - return !window.confirm("You have unsaved changes! Are you sure you want to leave?"); - } - return false; - }); - - const handleSaveTable = () => { - updateTable.mutate( - { - analysisTableId: table.id, - requestBody: { - table_type: table.table_type, - title: table.title, - content: JSON.stringify(tablePages), - }, - }, - { - onSuccess(data) { - openSnackbar({ - text: `Saved table '${data.title}'`, - severity: "success", - }); - setHasChanged(false); - }, - }, - ); - }; - - // page actions: add action - const handleAddPage = () => { - const id = uuidv4(); - setTablePages((pages) => [ - ...pages, - { id: id, name: `Table sheet ${pages.length + 1}`, content: TableType2Template[table.table_type] }, - ]); - setCurrentPageId(id); - }; - - // page actions: delete action - const handleClickDelete = () => { - if (tablePages.length <= 1 || selectedPageId === "") return; // you cannot delete the last page - - const newPages = [...tablePages]; - const deleteIndex = newPages.findIndex((p) => p.id === selectedPageId); - newPages.splice(deleteIndex, 1); - - setAnchorEl(null); - setTablePages(newPages); - setCurrentPageId(newPages[0].id); - setSelectedPageId(""); - }; - - // page actions: copy action - const handleClickCopy = () => { - if (selectedPageId === "") return; - - const newPages = [...tablePages]; - const copyIndex = newPages.findIndex((p) => p.id === selectedPageId); - newPages.splice(copyIndex + 1, 0, { - id: uuidv4(), - name: tablePages[copyIndex].name + " (copy)", - content: tablePages[copyIndex].content, - }); - - setAnchorEl(null); - setTablePages(newPages); - setSelectedPageId(""); - }; - - // page actions: rename action - const handleClickRename = () => { - if (selectedPageId === "") return; - - setAnchorEl(null); - setRenamingPageId(selectedPageId); - }; - useEffect(() => { - document.getElementById("rename-tab-" + renamingPageId + "-input")?.focus(); - hotRef.current?.hotInstance?.deselectCell(); - }, [renamingPageId]); - const handleRename = () => { - if (renamingPageId === "") return; - - const newName = (document.getElementById("rename-tab-" + renamingPageId + "-input") as HTMLInputElement).value; - - const newPages = [...tablePages]; - const renameIndex = newPages.findIndex((p) => p.id === renamingPageId); - newPages[renameIndex].name = newName; - - setTablePages(newPages); - setRenamingPageId(""); - setSelectedPageId(""); - }; - - // table actions: add codes - const onAddCodes = (codes: CodeRead[], addRows: boolean) => { - const hot = hotRef.current?.hotInstance; - if (!hot) return; - - const cellInfo = getSelectedCellInfo(hot); - const dataToAdd = codes.map((code) => `${code.name}`); - addData(hot, cellInfo, dataToAdd, addRows); - }; - - // table actions: add annotations - const onAddSpanAnnotations = (spanAnnotationIds: number[], addRows: boolean) => { - const hot = hotRef.current?.hotInstance; - if (!hot) return; - - const cellInfo = getSelectedCellInfo(hot); - const dataToAdd = spanAnnotationIds.map((spanAnnotationId) => ``); - addData(hot, cellInfo, dataToAdd, addRows); - }; - - // table actions: add documents - const onAddDocuments = (sdocIds: number[], addRows: boolean) => { - const hot = hotRef.current?.hotInstance; - if (!hot) return; - - const cellInfo = getSelectedCellInfo(hot); - const dataToAdd = sdocIds.map((sdocId) => ``); - addData(hot, cellInfo, dataToAdd, addRows); - }; - - return ( - - - - - - - - - - {tablePages.map((pageData) => { - if (pageData.id !== renamingPageId) { - return ( - - {pageData.name} - - - - - } - value={pageData.id} - style={{ - padding: "6px 0px 6px 12px", - }} - /> - ); - } else { - return ( - handleRename()} - onKeyDown={(event) => (event.key === "Enter" || event.key === "Escape") && handleRename()} - inputProps={{ - id: `rename-tab-${pageData.id}-input`, - style: { - padding: "6px", - }, - }} - /> - } - /> - ); - } - })} - - setAnchorEl(null)}> - handleClickDelete()} disabled={tablePages.length === 1}> - - - - Delete - - handleClickRename()}> - - - - Rename - - handleClickCopy()}> - - - - Duplicate - - - - - isCellSelected(hotRef.current?.hotInstance)} - onConfirmSelection={onAddCodes} - buttonProps={{ variant: "outlined" }} - /> - isCellSelected(hotRef.current?.hotInstance)} - onConfirmSelection={onAddSpanAnnotations} - buttonProps={{ variant: "outlined" }} - /> - isCellSelected(hotRef.current?.hotInstance)} - onConfirmSelection={onAddDocuments} - buttonProps={{ variant: "outlined" }} - /> - - - - - {currentPage && ( - { - if (!hasChanged && source === "edit") { - setHasChanged(true); - } - }} - > - - - )} - - - ); -} - -export default TableViewContent; diff --git a/frontend/src/views/analysis/Table/Toolbar/AddAnnotationDialog.tsx b/frontend/src/views/analysis/Table/Toolbar/AddAnnotationDialog.tsx deleted file mode 100644 index 4941d13df..000000000 --- a/frontend/src/views/analysis/Table/Toolbar/AddAnnotationDialog.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { Box, Button, ButtonProps, CircularProgress, Dialog } from "@mui/material"; -import { MRT_RowSelectionState, MRT_SortingState, MRT_VisibilityState } from "material-react-table"; -import { useState } from "react"; -import MetadataHooks from "../../../../api/MetadataHooks.ts"; -import { ProjectMetadataRead } from "../../../../api/openapi/models/ProjectMetadataRead.ts"; -import SpanAnnotationTable from "../../../../components/SpanAnnotation/SpanAnnotationTable/SpanAnnotationTable.tsx"; - -const filterName = "spanAnnotationDialogTable"; - -interface AddAnnotationDialogProps { - projectId: number; - shouldOpen: () => boolean; - onConfirmSelection: (spanAnnotationIds: number[], addRows: boolean) => void; - buttonProps?: Omit; -} - -function AddAnnotationDialog({ projectId, buttonProps, shouldOpen, ...props }: AddAnnotationDialogProps) { - // local state - const [open, setOpen] = useState(false); - - // global server state - const metadata = MetadataHooks.useGetProjectMetadataList(); - - // actions - const handleOpen = () => { - setOpen(shouldOpen()); - }; - - const handleClose = () => { - setOpen(false); - }; - - return ( - <> - - - {metadata.isSuccess ? ( - - ) : metadata.isLoading ? ( - - ) : ( -
An error occured
- )} -
- - ); -} - -interface AddAnnotationDialogContentProps { - metadata: ProjectMetadataRead[]; - projectId: number; - onConfirmSelection: (spanAnnotationIds: number[], addRows: boolean) => void; - onClose: () => void; -} - -function AddAnnotationDialogContent({ - metadata, - projectId, - onConfirmSelection, - onClose, -}: AddAnnotationDialogContentProps) { - const [rowSelectionModel, setRowSelectionModel] = useState({}); - const [sortingModel, setSortingModel] = useState([]); - const [visibilityModel, setVisibilityModel] = useState(() => - metadata.reduce((acc, curr) => { - return { - ...acc, - [curr.id]: false, - }; - }, {}), - ); - const selectedAnnotationIds = Object.keys(rowSelectionModel).map((id) => parseInt(id)); - - const handleConfirmSelection = (addRows: boolean) => () => { - onConfirmSelection(selectedAnnotationIds, addRows); - onClose(); - }; - - return ( - >} - cardProps={{ elevation: 2, className: "myFlexFillAllContainer myFlexContainer" }} - renderBottomToolbarCustomActions={() => ( - <> - - - - - - )} - /> - ); -} - -export default AddAnnotationDialog; diff --git a/frontend/src/views/analysis/Table/Toolbar/AddCodeDialog.tsx b/frontend/src/views/analysis/Table/Toolbar/AddCodeDialog.tsx deleted file mode 100644 index 6574df943..000000000 --- a/frontend/src/views/analysis/Table/Toolbar/AddCodeDialog.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Box, Button, ButtonProps, Dialog, DialogTitle, Stack } from "@mui/material"; -import { MRT_RowSelectionState } from "material-react-table"; -import { useState } from "react"; -import { CodeRead } from "../../../../api/openapi/models/CodeRead.ts"; -import CodeTable from "../../../../components/Code/CodeTable.tsx"; - -export interface AddCodeDialogProps { - projectId: number; - shouldOpen: () => boolean; - onConfirmSelection: (codes: CodeRead[], addRows: boolean) => void; - buttonProps?: Omit; -} - -function AddCodeDialog({ projectId, shouldOpen, onConfirmSelection, buttonProps }: AddCodeDialogProps) { - // local state - const [open, setOpen] = useState(false); - const [rowSelectionModel, setRowSelectionModel] = useState({}); - - const onOpenDialogClick = () => { - setOpen(shouldOpen()); - }; - - const handleClose = () => { - setOpen(false); - setRowSelectionModel({}); - }; - - const handleConfirmSelection = (codes: CodeRead[], isAddCodesToCell: boolean) => { - onConfirmSelection(codes, !isAddCodesToCell); - handleClose(); - }; - - return ( - <> - - setOpen(false)} open={open} maxWidth="lg" fullWidth> - Select codes to add to table - ( - - - - - - - )} - /> - - - ); -} - -export default AddCodeDialog; diff --git a/frontend/src/views/analysis/Table/Toolbar/AddDocumentDialog.tsx b/frontend/src/views/analysis/Table/Toolbar/AddDocumentDialog.tsx deleted file mode 100644 index c58945462..000000000 --- a/frontend/src/views/analysis/Table/Toolbar/AddDocumentDialog.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { Box, Button, ButtonProps, Dialog, DialogTitle } from "@mui/material"; -import { MRT_RowSelectionState, MRT_SortingState } from "material-react-table"; -import { useState } from "react"; -import SdocTable from "../../../../components/SourceDocument/SdocTable/SdocTable.tsx"; - -const filterName = "sdocDialogTable"; - -export interface AddDocumentDialogProps { - projectId: number; - shouldOpen: () => boolean; - onConfirmSelection: (sddocIds: number[], addRows: boolean) => void; - buttonProps?: Omit; -} - -function AddDocumentDialog({ projectId, shouldOpen, onConfirmSelection, buttonProps }: AddDocumentDialogProps) { - const [open, setOpen] = useState(false); - const [rowSelectionModel, setRowSelectionModel] = useState({}); - const [sortingModel, setSortingModel] = useState([]); - const selectedSdocIds = Object.keys(rowSelectionModel).map((id) => parseInt(id)); - - const handleOpenDialogClick = () => { - setOpen(shouldOpen()); - }; - - const handleClose = () => { - setOpen(false); - setRowSelectionModel({}); - }; - - const handleConfirmSelection = (addRows: boolean) => { - onConfirmSelection(selectedSdocIds, addRows); - handleClose(); - }; - - return ( - <> - - - Select documents to add to table - ( - <> - - - - - - )} - /> - - - ); -} - -export default AddDocumentDialog; diff --git a/frontend/src/views/analysis/Table/Toolbar/PageNavigationButton.tsx b/frontend/src/views/analysis/Table/Toolbar/PageNavigationButton.tsx deleted file mode 100644 index 319ec7602..000000000 --- a/frontend/src/views/analysis/Table/Toolbar/PageNavigationButton.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { IconButton, Menu, MenuItem, ListItemIcon, ListItemText, Tooltip } from "@mui/material"; -import { useState } from "react"; -import MenuIcon from "@mui/icons-material/Menu"; -import CheckIcon from "@mui/icons-material/Check"; -import { TablePage } from "../../../../api/TableHooks.ts"; - -interface PageNavigationButtonProps { - tablePages: TablePage[]; - currentPageId: string; - onPageIdChange: (pageId: string) => void; -} - -function PageNavigationButton({ tablePages, currentPageId, onPageIdChange }: PageNavigationButtonProps) { - // local client state - const [pageNavigationMenuAnchorEl, setPageNavigationMenuAnchorEl] = useState(null); - const pageNavigationMenuOpen = Boolean(pageNavigationMenuAnchorEl); - - // events - const handleOpenPageNavigationMenu = (event: React.MouseEvent) => { - setPageNavigationMenuAnchorEl(event.currentTarget); - }; - const handleClosePageNavigationMenu = () => { - setPageNavigationMenuAnchorEl(null); - }; - const handleClickPageNavigationMenuItem = (pageId: string) => { - setPageNavigationMenuAnchorEl(null); - onPageIdChange(pageId); - }; - - return ( - <> - - - - - - - {tablePages.map((tablePage) => ( - handleClickPageNavigationMenuItem(tablePage.id)}> - {tablePage.id === currentPageId ? ( - <> - - - - {tablePage.name} - - ) : ( - {tablePage.name} - )} - - ))} - - - ); -} - -export default PageNavigationButton; diff --git a/frontend/src/views/analysis/Table/templates.ts b/frontend/src/views/analysis/Table/templates.ts deleted file mode 100644 index 9fc97d245..000000000 --- a/frontend/src/views/analysis/Table/templates.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { TableType } from "../../../api/openapi/models/TableType.ts"; - -const CUSTOM_TEMPLATE = [ - ["", "", "", "", ""], - ["", "", "", "", ""], - ["", "", "", "", ""], - ["", "", "", "", ""], - ["", "", "", "", ""], -]; - -const SITUATIONMAP_TEMPLATE = [ - ["

INDIVIDUELLE MENSCHLICHE ELEMENTE/AKTEURE

", "

NICHTMENSCHLICHE ELEMENTE/ AKTANTEN

"], - ["", ""], - ["

KOLLEKTIVE MENSCHLICHE ELEMENTE/AKTEURE

", "

IMPLIZIERTE/STUMME AKTEURE/AKTANTEN

"], - ["", ""], - [ - "

DISKURSIVE KONSTRUKTIONEN INDIVIDUELLER UND/ODER KOLLEKTIVER MENSCHLICHER AKTEURE

", - "

DISKURSIVE KONSTRUKTION NICHTMENSCHLICHER AKTANTEN

", - ], - ["", ""], - ["

POLITISCHE/WIRTSCHAFTLICHE ELEMENTE

", "

SOZIO-KULTURELLE/SYMBOLISCHE ELEMENTE

"], - ["", ""], - ["

ZEITLICHE ELEMENTE

", "

RÄUMLICHE ELEMENTE

"], - ["", ""], - [ - "

HAUPTTHEMEN/DEBATTEN (MEIST UMSTRITTEN)

", - "

VERWANDTE DISKURSE (HISTORISCHE, NARRATIVE UND/ODER VISUELLE)

", - ], - ["", ""], -]; - -const INTERPRETATION_TEMPLATE = [ - [ - "Fundstelle", - "Deutungsmuster", - "Akteur:in", - "Ursache", - "Hypothese", - "Konsequenz", - "Präzisierungen", - "Argumentation", - ], - ["Code", "", "", "", "", "", "", ""], - ["", "", "", "", "", "", "", ""], -]; - -const PHENOMENON_TEMPLATE = [ - ["Dimension", "Inhaltliche Ausführung", "Theoretisierung"], - ["Diskursthema", "", ""], - ["Ursachen", "", ""], - ["Folgen", "", ""], - ["Handlungsbedarf", "", ""], - ["Problemlösung", "", ""], - ["Akteur:innen", "", ""], - ["Verantwortung", "", ""], - ["Handlungsstrategien", "", ""], - ["Aushandlungsstrategien", "", ""], - ["Selbstpositionierung", "", ""], - ["Fremdpositionierung", "", ""], - ["Dingkultur", "", ""], - ["Wertbezug", "", ""], -]; - -export const TableType2Template = { - [TableType.CUSTOM]: CUSTOM_TEMPLATE, - [TableType.SITUATION]: SITUATIONMAP_TEMPLATE, - [TableType.PHENOMENON]: PHENOMENON_TEMPLATE, - [TableType.INTERPRETATION]: INTERPRETATION_TEMPLATE, -}; From 4840a150c5255ad79c334e7e8a4eab83a327d00a Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Tue, 11 Feb 2025 10:17:22 +0000 Subject: [PATCH 3/3] added migration to delete analysistable --- .../241cfa625db2_remove_analysistable.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 backend/src/alembic/versions/241cfa625db2_remove_analysistable.py diff --git a/backend/src/alembic/versions/241cfa625db2_remove_analysistable.py b/backend/src/alembic/versions/241cfa625db2_remove_analysistable.py new file mode 100644 index 000000000..a00700a39 --- /dev/null +++ b/backend/src/alembic/versions/241cfa625db2_remove_analysistable.py @@ -0,0 +1,81 @@ +"""remove analysistable + +Revision ID: 241cfa625db2 +Revises: 970c55224a39 +Create Date: 2025-02-11 10:16:29.438178 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "241cfa625db2" +down_revision: Union[str, None] = "970c55224a39" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index("ix_analysistable_created", table_name="analysistable") + op.drop_index("ix_analysistable_id", table_name="analysistable") + op.drop_index("ix_analysistable_project_id", table_name="analysistable") + op.drop_index("ix_analysistable_user_id", table_name="analysistable") + op.drop_table("analysistable") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "analysistable", + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "created", + postgresql.TIMESTAMP(), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "updated", + postgresql.TIMESTAMP(), + server_default=sa.text("now()"), + autoincrement=False, + nullable=False, + ), + sa.Column("title", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("content", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("table_type", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("project_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("user_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint( + ["project_id"], + ["project.id"], + name="analysistable_project_id_fkey", + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["user_id"], + ["user.id"], + name="analysistable_user_id_fkey", + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name="analysistable_pkey"), + ) + op.create_index( + "ix_analysistable_user_id", "analysistable", ["user_id"], unique=False + ) + op.create_index( + "ix_analysistable_project_id", "analysistable", ["project_id"], unique=False + ) + op.create_index("ix_analysistable_id", "analysistable", ["id"], unique=False) + op.create_index( + "ix_analysistable_created", "analysistable", ["created"], unique=False + ) + # ### end Alembic commands ###