Skip to content

Commit

Permalink
Add setup for tasks card
Browse files Browse the repository at this point in the history
  • Loading branch information
uittenbroekrobbert committed Jun 6, 2024
1 parent 51e9ad6 commit e756d61
Show file tree
Hide file tree
Showing 75 changed files with 5,915 additions and 287 deletions.
1 change: 1 addition & 0 deletions .devcontainer/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

pipx install poetry
poetry install
poetry run playwright install --with-deps
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ jobs:
- name: Install dependencies
run: poetry install

- name: Install Playwright browsers
run: poetry run playwright install --with-deps

- name: run ruff
run: poetry run ruff check --output-format=github

Expand Down Expand Up @@ -92,6 +95,9 @@ jobs:
- name: Install dependencies
run: poetry install

- name: Install Playwright browsers
run: poetry run playwright install --with-deps

- name: Run pytest
run: poetry run coverage run -m pytest

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ __pypackages__/

#mypyr
.mypy_cache/
/.idea/

# macos
.DS_Store
Expand Down
620 changes: 419 additions & 201 deletions poetry.lock

Large diffs are not rendered by default.

19 changes: 13 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ uvicorn = {extras = ["standard"], version = "^0.30.1"}
pytest = "^8.2.1"
coverage = "^7.5.3"
httpx = "^0.27.0"
urllib3 = "^2.2.1"
playwright = "^1.44.0"
pytest-playwright = "^0.5.0"

[tool.poetry.group.dev.dependencies]
ruff = "^0.4.7"
Expand Down Expand Up @@ -77,7 +80,8 @@ reportMissingImports = true
reportMissingTypeStubs = true
reportUnnecessaryIsInstance = false
exclude = [
"tad/migrations"
"tad/migrations",
".venv"
]

[tool.coverage.run]
Expand Down Expand Up @@ -110,12 +114,15 @@ filterwarnings = [
level = "PARANOID"
dependencies = true
authorized_licenses = [
"BSD",
"Python Software Foundation",
"MIT",
"Apache Software",
"Artistic",
"BSD",
"GNU General Public License v2 or later (GPLv2+)",
"GNU General Public License (GPL)",
"GNU Library or Lesser General Public License (LGPL)",
"Mozilla Public License 2.0 (MPL 2.0)",
"MIT",
"The Unlicense (Unlicense)",
"ISC License (ISCL)"
"ISC License (ISCL)",
"Mozilla Public License 2.0 (MPL 2.0)",
"Python Software Foundation"
]
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ sonar.python.version=3.10,3.11,3.12

sonar.python.coverage.reportPaths=coverage.xml

sonar.coverage.exclusions=tad/migrations/**
sonar.coverage.exclusions=tad/migrations/**,tad/site/static/js/tad.js
19 changes: 0 additions & 19 deletions tad/api/deps.py

This file was deleted.

4 changes: 3 additions & 1 deletion tad/api/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from fastapi import APIRouter

from tad.api.routes import health, root
from tad.api.routes import health, pages, root, tasks

api_router = APIRouter()
api_router.include_router(root.router)
api_router.include_router(health.router, prefix="/health", tags=["health"])
api_router.include_router(pages.router, prefix="/pages", tags=["pages"])
api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"])
9 changes: 9 additions & 0 deletions tad/api/routes/deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from fastapi.templating import Jinja2Templates
from jinja2 import Environment

from tad.core.config import settings

env = Environment(
autoescape=True,
)
templates = Jinja2Templates(directory=settings.TEMPLATE_DIR, env=env)
24 changes: 24 additions & 0 deletions tad/api/routes/pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Annotated

from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse

from tad.api.routes.deps import templates
from tad.services.statuses import StatusesService
from tad.services.tasks import TasksService

router = APIRouter()


@router.get("/", response_class=HTMLResponse)
async def default_layout(
request: Request,
status_service: Annotated[StatusesService, Depends(StatusesService)],
tasks_service: Annotated[TasksService, Depends(TasksService)],
):
context = {
"page_title": "This is the page title",
"tasks_service": tasks_service,
"statuses_service": status_service,
}
return templates.TemplateResponse(request=request, name="default_layout.jinja", context=context)
15 changes: 10 additions & 5 deletions tad/api/routes/root.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi import APIRouter
from fastapi.responses import FileResponse, RedirectResponse

from tad.api.deps import templates
from tad.core.config import settings

router = APIRouter()


@router.get("/")
async def base(request: Request) -> HTMLResponse:
return templates.TemplateResponse(request=request, name="root/index.html")
async def base() -> RedirectResponse:
return RedirectResponse("/pages/")


@router.get("/favicon.ico", include_in_schema=False)
async def favicon():
return FileResponse(settings.STATIC_DIR + "/favicon.ico")
41 changes: 41 additions & 0 deletions tad/api/routes/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import Annotated

from fastapi import APIRouter, Depends, Request, status
from fastapi.responses import HTMLResponse

from tad.api.routes.deps import templates
from tad.schema.task import MovedTask
from tad.services.tasks import TasksService

router = APIRouter()


@router.patch("/")
async def move_task(
request: Request, moved_task: MovedTask, tasks_service: Annotated[TasksService, Depends(TasksService)]
) -> HTMLResponse:
"""
Move a task through an API call.
:param tasks_service: the task service
:param request: the request object
:param moved_task: the move task object
:return: a HTMLResponse object, in this case the html code of the card that was moved
"""
try:
# because htmx form always sends a value and siblings are optional, we use -1 for None and convert it here
if moved_task.next_sibling_id == -1:
moved_task.next_sibling_id = None
if moved_task.previous_sibling_id == -1:
moved_task.previous_sibling_id = None
task = tasks_service.move_task(
moved_task.id,
moved_task.status_id,
moved_task.previous_sibling_id,
moved_task.next_sibling_id,
)
# todo(Robbert) add error handling for input error or task error handling
return templates.TemplateResponse(request=request, name="task.jinja", context={"task": task})
except Exception:
return templates.TemplateResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, request=request, name="error.jinja"
)
4 changes: 2 additions & 2 deletions tad/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ def server_host(self) -> str:
PROJECT_NAME: str = "TAD"
PROJECT_DESCRIPTION: str = "Transparency of Algorithmic Decision making"

STATIC_DIR: str = "tad/static"
TEMPLATE_DIR: str = "tad/templates"
STATIC_DIR: str = "tad/site/static/"
TEMPLATE_DIR: str = "tad/site/templates"

# todo(berry): create submodel for database settings
APP_DATABASE_SCHEME: DatabaseSchemaType = "sqlite"
Expand Down
12 changes: 10 additions & 2 deletions tad/core/db.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
from sqlalchemy.engine.base import Engine
from sqlmodel import Session, create_engine, select

from tad.core.config import settings

engine = create_engine(settings.SQLALCHEMY_DATABASE_URI)
_engine: None | Engine = None


def get_engine() -> Engine:
global _engine
if _engine is None:
_engine = create_engine(settings.SQLALCHEMY_DATABASE_URI)
return _engine


async def check_db():
with Session(engine) as session:
with Session(get_engine()) as session:
session.exec(select(1))
7 changes: 7 additions & 0 deletions tad/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ def __init__(self, message: str = "Settings error"):
self.message: str = message
exception_name: str = self.__class__.__name__
super().__init__(f"{exception_name}: {self.message}")


class RepositoryError(TADError):
def __init__(self, message: str = "Repository error"):
self.message: str = message
exception_name: str = self.__class__.__name__
super().__init__(f"{exception_name}: {self.message}")
7 changes: 5 additions & 2 deletions tad/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
validation_exception_handler as tad_validation_exception_handler,
)
from tad.core.log import configure_logging
from tad.middleware.route_logging import RequestLoggingMiddleware
from tad.utils.mask import Mask

from .middleware.route_logging import RequestLoggingMiddleware

configure_logging(settings.LOGGING_LEVEL, settings.LOGGING_CONFIG)


logger = logging.getLogger(__name__)
mask = Mask(mask_keywords=["database_uri"])

Expand Down Expand Up @@ -52,7 +54,6 @@ async def lifespan(app: FastAPI):
)

app.add_middleware(RequestLoggingMiddleware)

app.mount("/static", StaticFiles(directory=settings.STATIC_DIR), name="static")


Expand All @@ -67,3 +68,5 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE


app.include_router(api_router)

# todo (robbert) add init code for example tasks and statuses
60 changes: 60 additions & 0 deletions tad/migrations/versions/eb2eed884ae9_a_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Create the user, status and task tables, drop table hero
Revision ID: eb2eed884ae9
Revises:
Create Date: 2024-05-14 13:36:23.551663
"""

from collections.abc import Sequence

import sqlalchemy as sa
import sqlmodel.sql.sqltypes
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "eb2eed884ae9"
down_revision: str | None = None
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("hero")
op.create_table(
"status",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("sort_order", sa.Float(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"user",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("avatar", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"task",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("title", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("sort_order", sa.Float(), nullable=False),
sa.Column("status_id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["user_id"],
["user.id"],
),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###


def downgrade() -> None:
# we do not delete any tables on a downgrade
pass
# ### commands auto generated by Alembic - please adjust! ###
# ### end Alembic commands ###
6 changes: 4 additions & 2 deletions tad/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .hero import Hero
from .status import Status
from .task import Task
from .user import User

__all__ = ["Hero"]
__all__ = ["Task", "Status", "User"]
7 changes: 7 additions & 0 deletions tad/models/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from sqlmodel import Field, SQLModel # type: ignore


class Status(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
sort_order: float
12 changes: 12 additions & 0 deletions tad/models/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from sqlmodel import Field as SQLField # type: ignore
from sqlmodel import SQLModel


class Task(SQLModel, table=True):
id: int | None = SQLField(default=None, primary_key=True)
title: str
description: str
sort_order: float
status_id: int | None = SQLField(default=None, foreign_key="status.id")
user_id: int | None = SQLField(default=None, foreign_key="user.id")
# todo(robbert) Tasks probably are grouped (and sub-grouped), so we probably need a reference to a group_id
3 changes: 2 additions & 1 deletion tad/models/hero.py → tad/models/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from sqlmodel import Field, SQLModel # type: ignore


class Hero(SQLModel, table=True):
class User(SQLModel, table=True):
id: int = Field(default=None, primary_key=True)
name: str
avatar: str | None
File renamed without changes.
10 changes: 10 additions & 0 deletions tad/repositories/deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from collections.abc import Generator

from sqlmodel import Session

from tad.core.db import get_engine


def get_session() -> Generator[Session, None, None]:
with Session(get_engine()) as session:
yield session
Loading

0 comments on commit e756d61

Please sign in to comment.