Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ITG-77] Add more task properties #7

Merged
merged 4 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion bode/bode/models/task.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import uuid

from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import NoResultFound

from bode.app import db
from bode.models.utc_datetime import UTCDateTime


class Task(db.Model):
__tablename__ = "tasks"

id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
title = db.Column(db.String(80), nullable=False)
description = db.Column(db.String(1024), nullable=False, server_default="")
due_date = db.Column(UTCDateTime(), nullable=True)

def create(**kwargs):
task = Task(**kwargs)
Expand Down
22 changes: 22 additions & 0 deletions bode/bode/models/utc_datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import datetime

from sqlalchemy.types import DateTime, TypeDecorator


class UTCDateTime(TypeDecorator):
impl = DateTime
cache_ok = True

def process_bind_param(self, value, _):
if value is not None:
if not value.tzinfo:
raise TypeError("tzinfo is required")
value = value.astimezone(datetime.timezone.utc).replace(
tzinfo=None
)
return value

def process_result_value(self, value, _):
if value is not None:
value = value.replace(tzinfo=datetime.timezone.utc)
return value
john-sonz marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions bode/bode/resources/base_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from marshmallow import Schema


def camelcase(s):
parts = iter(s.split("_"))
return next(parts) + "".join(i.title() for i in parts)


class BaseSchema(Schema):
"""Schema that uses camel-case for its external representation
and snake-case for its internal representation.
"""

def on_bind_field(self, field_name, field_obj):
field_obj.data_key = camelcase(field_obj.data_key or field_name)
john-sonz marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 4 additions & 5 deletions bode/bode/resources/tasks/api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from flask import abort
from flask.views import MethodView
from flask_smorest import Blueprint
from sqlalchemy.orm.exc import NoResultFound
from flask_smorest import Blueprint, abort
from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound

from bode.models.task import Task
from bode.resources.tasks.schemas import TaskSchema
from bode.resources.tasks.schemas import TaskInputSchema, TaskSchema

blueprint = Blueprint("tasks", "tasks", url_prefix="/tasks")

Expand All @@ -16,7 +15,7 @@ class Tasks(MethodView):
def get(self):
return Task.query.all()

@blueprint.arguments(TaskSchema)
@blueprint.arguments(TaskInputSchema)
@blueprint.response(201, TaskSchema)
def post(self, task_data):
return Task.create(**task_data)
Expand Down
16 changes: 13 additions & 3 deletions bode/bode/resources/tasks/schemas.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from marshmallow import Schema, fields, validate
from marshmallow import fields, validate

from bode.resources.base_schema import BaseSchema

class TaskSchema(Schema):

class TaskInputSchema(BaseSchema):
title = fields.String(validate=validate.Length(1, 80), required=True)
description = fields.String(validate=validate.Length(0, 1024), default="")
due_date = fields.DateTime(allow_none=True)


class TaskSchema(BaseSchema):
id = fields.UUID(dump_only=True)
title = fields.String(validate=validate.Length(1, 80))
title = fields.String()
description = fields.String()
due_date = fields.DateTime()
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Add due date and description to task model

Revision ID: 9b8806b556b8
Revises: 4daafbe3fd77
Create Date: 2022-04-03 19:53:56.792053

"""
import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision = "9b8806b556b8"
down_revision = "4daafbe3fd77"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"tasks",
sa.Column(
"description",
sa.String(length=1024),
server_default="",
nullable=False,
),
)
op.add_column(
"tasks",
sa.Column("due_date", sa.DateTime(timezone=False), nullable=True),
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("tasks", "due_date")
op.drop_column("tasks", "description")
# ### end Alembic commands ###
Loading