Skip to content

Commit

Permalink
Merge pull request #33 from it-goats/feature/ITG-105
Browse files Browse the repository at this point in the history
[ITG-105] feat: use enum task status
  • Loading branch information
tgargula committed May 5, 2022
2 parents 70f0a65 + 1e90657 commit 6dca464
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 31 deletions.
17 changes: 16 additions & 1 deletion bode/bode/models/task.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import enum
import uuid

from sqlalchemy import Enum
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.exc import IntegrityError, NoResultFound

Expand All @@ -9,14 +11,27 @@
from bode.models.utc_datetime import UTCDateTime


class TaskStatus(enum.Enum):
TODO = "TODO"
INDIRECTLY_DONE = "INDIRECTLY_DONE"
DONE = "DONE"

def __str__(self):
return self.value

@classmethod
def list(cls):
return [c.value for c in cls]


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)
is_done = db.Column(db.Boolean, nullable=False, server_default="false")
status = db.Column(Enum(TaskStatus), nullable=False, server_default="TODO")

tags = db.relationship("Tag", secondary=task_tag, back_populates="task")

Expand Down
16 changes: 7 additions & 9 deletions bode/bode/models/task_actions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from bode.app import db
from bode.models.task import Task
from bode.models.task import Task, TaskStatus
from bode.models.task_relation import RelationType, TaskRelation


Expand All @@ -23,30 +23,28 @@ def is_subtask_relation(relation):


def edit_task(task_id, **task_data):
"""Function edit task. If task is checked, all interchangable task will be checked."""
"""Function edits task. If task is checked, all interchangable tasks with status todo will be indirectly checked."""

def is_interchangable_relation(relation):
return relation.type == RelationType.Interchangable.value and str(relation.first_task_id) == task_id

task = Task.get(task_id)

task.title = task_data["title"]
task.description = task_data["description"]
task.due_date = task_data["due_date"]
task.is_done = task_data["is_done"]
for key, value in task_data.items():
setattr(task, key, value)

db.session.commit()

if task_data["is_done"]:
if task_data["status"] != TaskStatus.TODO.value:
for relation, related_task in TaskRelation.get_related_tasks(task_id):
if is_interchangable_relation(relation):
if related_task.is_done:
if related_task.status != TaskStatus.TODO:
continue
inter_task_data = {
"title": related_task.title,
"description": related_task.description,
"due_date": related_task.due_date,
"is_done": True,
"status": TaskStatus.INDIRECTLY_DONE.value,
}
edit_task(str(related_task.id), **inter_task_data)

Expand Down
2 changes: 1 addition & 1 deletion bode/bode/resources/tasks/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class Tasks(MethodView):
@blueprint.response(200, TaskSchema(many=True))
def get(self):
return Task.query.order_by(Task.is_done, Task.due_date).all()
return Task.query.order_by(Task.status, Task.due_date).all()

@blueprint.arguments(TaskInputSchema)
@blueprint.response(201, TaskSchema)
Expand Down
5 changes: 3 additions & 2 deletions bode/bode/resources/tasks/schemas.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from marshmallow import EXCLUDE, fields, validate

from bode.models.task import TaskStatus
from bode.resources.base_schema import BaseSchema
from bode.resources.tags.schemas import TagInputSchema, TagSchema

Expand All @@ -11,7 +12,7 @@ class Meta:
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)
is_done = fields.Boolean(default=False)
status = fields.String(validate=validate.OneOf(TaskStatus.list()), default=TaskStatus.TODO)
tags = fields.List(fields.Nested(TagInputSchema), default=[])


Expand All @@ -20,5 +21,5 @@ class TaskSchema(BaseSchema):
title = fields.String()
description = fields.String()
due_date = fields.DateTime()
is_done = fields.Boolean()
status = fields.String()
tags = fields.List(fields.Nested(TagSchema))
47 changes: 47 additions & 0 deletions bode/migrations/versions/5c592f0ac54b_use_task_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Use task status
Revision ID: 5c592f0ac54b
Revises: d71419c8a49a
Create Date: 2022-05-05 21:13:36.734542
"""
import sqlalchemy as sa

from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "5c592f0ac54b"
down_revision = "d71419c8a49a"
branch_labels = None
depends_on = None


task_status = postgresql.ENUM("TODO", "INDIRECTLY_DONE", "DONE", name="taskstatus")


def upgrade():
task_status.create(op.get_bind())
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"tasks",
sa.Column(
"status",
sa.Enum("TODO", "INDIRECTLY_DONE", "DONE", name="taskstatus"),
server_default="TODO",
nullable=False,
),
)
op.drop_column("tasks", "is_done")
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"tasks",
sa.Column("is_done", sa.BOOLEAN(), server_default=sa.text("false"), autoincrement=False, nullable=False),
)
op.drop_column("tasks", "status")
# ### end Alembic commands ###
task_status.drop(op.get_bind())
4 changes: 2 additions & 2 deletions cabra/src/pages/components/CreateTaskForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ITask, TaskStatus } from "../../types/task";
import { createTask, getTasks } from "../../api/tasks";
import { useMutation, useQueryClient } from "react-query";

import { ITask } from "../../types/task";
import TaskForm from "./TaskForm";
import { routeHelpers } from "../../routes";
import { useNavigate } from "react-router-dom";
Expand All @@ -10,7 +10,7 @@ const emptyTask: Omit<ITask, "id"> = {
description: "",
dueDate: null,
title: "",
isDone: false,
status: TaskStatus.TODO,
tags: [],
};

Expand Down
7 changes: 4 additions & 3 deletions cabra/src/pages/components/RelatedTask.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import "twin.macro";

import { ITask, TaskStatus } from "../../types/task";
import { getTask, getTasks, updateTask } from "../../api/tasks";
import { useMutation, useQueryClient } from "react-query";

import Checkbox from "./CheckBox";
import { DirectedRelationType } from "../../types/taskRelation";
import { ITask } from "../../types/task";
import { getRelatedTasks } from "../../api/taskRelations";
import { useState } from "react";

Expand Down Expand Up @@ -37,7 +37,8 @@ export default function RelatedTask({
try {
const updatedTask = {
...task,
isDone: !task.isDone,
status:
task.status === TaskStatus.DONE ? TaskStatus.TODO : TaskStatus.DONE,
};
await editTask.mutateAsync(updatedTask);
} catch (error) {
Expand All @@ -56,7 +57,7 @@ export default function RelatedTask({
<p tw="place-self-end">
<Checkbox
id={task.id}
checked={task.isDone}
checked={task.status !== TaskStatus.TODO}
onChange={handleIsDoneChange}
/>
</p>
Expand Down
4 changes: 2 additions & 2 deletions cabra/src/pages/components/SubtasksListEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DirectedRelationType, ITaskRelation } from "../../types/taskRelation";
import { FormEvent, useState } from "react";
import { ITask, TaskStatus } from "../../types/task";
import tw, { styled } from "twin.macro";
import { useMutation, useQuery } from "react-query";

import { ITask } from "../../types/task";
import MiniTaskDelete from "./MiniTaskDelete";
import { createTask } from "../../api/tasks";
import { getRelatedTasks } from "../../api/taskRelations";
Expand All @@ -21,7 +21,7 @@ const emptyTask: Omit<ITask, "id"> = {
description: "",
dueDate: null,
title: "",
isDone: false,
status: TaskStatus.TODO,
tags: [],
};

Expand Down
3 changes: 2 additions & 1 deletion cabra/src/pages/components/TaskDetailsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DirectedRelationType } from "../../types/taskRelation";
import { Link } from "react-router-dom";
import NavigationButton from "./NavigationButton";
import RelatedTasksList from "./RelatedTaskList";
import { TaskStatus } from "../../types/task";
import { formatDateTime } from "../../utils/dates";
import { routeHelpers } from "../../routes";
import styled from "@emotion/styled";
Expand Down Expand Up @@ -48,7 +49,7 @@ export default function TaskDetails({ id }: Props) {
</CardField>
<Ctas align="center">
<CheckBox
checked={task.isDone}
checked={task.status !== TaskStatus.TODO}
id={`task-${task.id}`}
onChange={handleStatusChange}
/>
Expand Down
8 changes: 5 additions & 3 deletions cabra/src/pages/components/TaskListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ITask, TaskStatus } from "../../types/task";
import { getTask, getTasks, updateTask } from "../../api/tasks";
import tw, { styled } from "twin.macro";
import { useMutation, useQueryClient } from "react-query";

import { ArrowRightIcon } from "@heroicons/react/solid";
import CheckBox from "./CheckBox";
import { ITask } from "../../types/task";
import { Link } from "react-router-dom";
import NavigationButton from "./NavigationButton";
import { formatDateTime } from "../../utils/dates";
Expand All @@ -29,10 +29,12 @@ export default function TaskListItem({ task }: Props) {
},
});
const handleIsDoneChange = async () => {
const newStatus =
task.status === TaskStatus.DONE ? TaskStatus.TODO : TaskStatus.DONE;
try {
const updatedTask = {
...task,
isDone: !task.isDone,
status: newStatus,
};
await editTask.mutateAsync(updatedTask);
} catch (error) {
Expand Down Expand Up @@ -62,7 +64,7 @@ export default function TaskListItem({ task }: Props) {
<div>
<Card tw="flex justify-center items-center gap-4">
<CheckBox
checked={task.isDone}
checked={task.status !== TaskStatus.TODO}
id={`task-${task.id}`}
onChange={handleIsDoneChange}
size="sm"
Expand Down
8 changes: 4 additions & 4 deletions cabra/src/pages/components/TaskRelationsEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ITask, TaskStatus } from "../../types/task";
import tw, { styled } from "twin.macro";

import { DirectedRelationType } from "../../types/taskRelation";
import { ITask } from "../../types/task";
import RelationListEdit from "./RelationListEdit";
import Select from "react-select";
import { getRelatedTasks } from "../../api/taskRelations";
Expand Down Expand Up @@ -53,16 +53,16 @@ export default function TaskRelationsEdit({ taskId }: Props) {

const subtasks = dataSubtasks?.data;

const potentialRelative = (isDone: boolean, id: string) => {
const potentialRelative = (status: TaskStatus, id: string) => {
return (
!isDone &&
status === TaskStatus.TODO &&
id !== taskId &&
!subtasks?.map(({ task: subtask }) => subtask.id).includes(id)
);
};

const formattedRelativesToBe: TaskOption[] = data.data
.filter(({ isDone, id }) => potentialRelative(isDone, id))
.filter(({ status, id }) => potentialRelative(status, id))
.map((task) => ({ value: task, label: task.title }));

const toggleShowSelected = () => {
Expand Down
5 changes: 3 additions & 2 deletions cabra/src/pages/hooks/useTaskDetails.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ITask, TaskStatus } from "../../types/task";
import { deleteTask, getTask, getTasks, updateTask } from "../../api/tasks";
import { useMutation, useQuery, useQueryClient } from "react-query";

import { ITask } from "../../types/task";
import { routeHelpers } from "../../routes";
import { useNavigate } from "react-router-dom";
import { useState } from "react";
Expand Down Expand Up @@ -30,7 +30,8 @@ const useTask = (id: string) => {
const handleStatusChange = async () => {
const updatedTask = {
...task,
isDone: !task?.isDone,
status:
task.status === TaskStatus.DONE ? TaskStatus.TODO : TaskStatus.DONE,
};
editTask.mutate(updatedTask);
};
Expand Down
8 changes: 7 additions & 1 deletion cabra/src/types/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ export interface ITag {
name: string;
}

export enum TaskStatus {
TODO = "TODO",
INDIRECTLY_DONE = "INDIRECTLY_DONE",
DONE = "DONE",
}

export interface ITask {
id: string;
title: string;
description: string;
dueDate: string | null;
isDone: boolean;
status: TaskStatus;
tags: ITag[];
}

0 comments on commit 6dca464

Please sign in to comment.