Skip to content

Commit

Permalink
Add POST endpoint for templates
Browse files Browse the repository at this point in the history
Closes #676

The changes here add the necessary schema, service methods, and endpoint for the creation of templates.

- Db connection updated so expire on commit is false. This prevents errors when trying to refresh the object for return to the api from service
- User model imported in timelog model explicitly. This solves occasional 'NoneType' error being thrown.
- Add template schema. Includes validation for column size, as well as check for is_global and user_id.
- Add template endpoint. Also add a 404 as a potential response type for the router.
- Add service to create template

Notes: For the schemas, I used the full-stack-fastapi-postgresql as a loose guide. Shared properties are all on the base model as optional. Then models that need other properties or for properties to be mandatory inherit and override the base.
  • Loading branch information
dmtrek14 committed Sep 27, 2023
1 parent ace6bce commit db990be
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 12 deletions.
2 changes: 1 addition & 1 deletion api/db/db_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def get_url():
SQLALCHEMY_DATABASE_URL = get_url()

engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, expire_on_commit=False)


def get_db():
Expand Down
3 changes: 2 additions & 1 deletion api/models/timelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from sqlalchemy.orm import relationship, Mapped
from sqlalchemy.ext.hybrid import hybrid_property
from models.project import Project
from models.user import User

from db.base_class import Base

Expand Down Expand Up @@ -79,7 +80,7 @@ class Template(Base):
init_time = Column(Integer, nullable=True)
end_time = Column(Integer, nullable=True)
customer_id = Column("customerid", Integer, ForeignKey("customer.id"), nullable=True)
user_id = Column("usrid", Integer, ForeignKey("usr.id"), nullable=True)
user_id = Column("usrid", Integer, ForeignKey(User.id), nullable=True)
project_id = Column("projectid", Integer, ForeignKey("project.id"), nullable=True)
is_global = Column(Boolean, nullable=False, default=False)

Expand Down
30 changes: 28 additions & 2 deletions api/routers/v1/timelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session

from schemas.timelog import TaskTypeItem, Template as TemplateSchema, Task as TaskSchema
from schemas.timelog import (
TaskTypeItem,
Template as TemplateSchema,
TemplateNew as TemplateNewSchema,
Task as TaskSchema,
)
from services.timelog import TaskTypeService, TemplateService, TaskService

from db.db_connection import get_db
from auth.auth_bearer import BearerToken

router = APIRouter(prefix="/timelog", tags=["timelog"])
router = APIRouter(prefix="/timelog", tags=["timelog"], responses={"404": {"description": "Not found"}})


@router.get("/task_types/", dependencies=[Depends(BearerToken())], response_model=List[TaskTypeItem])
Expand All @@ -24,6 +29,27 @@ async def get_user_templates(user_id: int, db: Session = Depends(get_db)):
return templates


@router.post("/templates", dependencies=[Depends(BearerToken())], response_model=TemplateSchema, status_code=201)
async def add_template(template: TemplateNewSchema, db: Session = Depends(get_db)):
"""
Create a template with all the information:
- **name***: each template must have a name
- **story**: the task story
- **description**: the task description
- **task type**: the task type
- **init time**: the task start time
- **end time**: the task end time
- **user id**: the user id (global templates should leave this null; user template should fill)
- **project id**: the project id
- **is global***: whether or not this template is global for all users (required)
\f
:param item: User input.
"""
result = TemplateService(db).create_template(template)
return result


@router.get("/tasks", dependencies=[Depends(BearerToken())], response_model=List[TaskSchema])
async def get_user_tasks(
user_id: int,
Expand Down
41 changes: 33 additions & 8 deletions api/schemas/timelog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import date
from pydantic import BaseModel
from pydantic import BaseModel, root_validator, constr
from typing import Optional


Expand All @@ -12,23 +12,48 @@ class Config:
orm_mode = True


class Template(BaseModel):
id: int
name: str
story: Optional[str]
description: Optional[str]
task_type: Optional[str]
# Shared properties
class TemplateBase(BaseModel):
name: Optional[constr(max_length=80)]
story: Optional[constr(max_length=80)]
description: Optional[constr(max_length=8192)]
task_type: Optional[constr(max_length=40)]
init_time: Optional[int]
end_time: Optional[int]
customer_id: Optional[int]
user_id: Optional[int]
project_id: Optional[int]
is_global: bool
is_global: Optional[bool]

@root_validator(pre=True)
def user_template_not_global(cls, values):
user_id, is_global = values.get("user_id"), values.get("is_global")
if is_global and user_id is not None:
raise ValueError("Global templates should not have a user_id.")
if not is_global and not user_id:
raise ValueError("Private templates should have a user_id.")
return values


# Properties to receive on creation
class TemplateNew(TemplateBase):
name: constr(max_length=80)
is_global: bool = False


# Properties shared by models stored in db
class TemplateInDb(TemplateBase):
id: int

class Config:
orm_mode = True


# Properties to return to client
class Template(TemplateInDb):
pass


class Task(BaseModel):
id: int
date: date
Expand Down
17 changes: 17 additions & 0 deletions api/services/timelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from services.main import AppService
from models.timelog import TaskType, Template, Task
from schemas.timelog import TemplateNew


class TaskTypeService(AppService):
Expand All @@ -19,6 +20,22 @@ def get_user_templates(self, user_id: int) -> List[Template]:
)
return templates

def create_template(self, template: TemplateNew) -> Template:
new_template = Template(
name=template.name,
story=template.story,
task_type=template.task_type,
init_time=template.init_time,
end_time=template.end_time,
user_id=template.user_id,
project_id=template.project_id,
is_global=template.is_global,
)
self.db.add(new_template)
self.db.commit()
self.db.refresh(new_template)
return new_template


class TaskService(AppService):
def get_user_tasks(self, user_id: int, offset: int, limit: int, start: date, end: date) -> List[Task]:
Expand Down

0 comments on commit db990be

Please sign in to comment.