Skip to content

Commit

Permalink
Merge pull request #22 from it-goats/feature/ITG-44
Browse files Browse the repository at this point in the history
[ITG-44] Implementacja endpointu GET do pobrania zadań będących w relacji z danym zadaniem
  • Loading branch information
koszar91 committed Apr 27, 2022
2 parents 5cab778 + aaa1ed8 commit 437d0ee
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 54 deletions.
16 changes: 0 additions & 16 deletions bode/bode/models/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from bode.app import db
from bode.models.tag import Tag
from bode.models.task_relation import TaskRelation
from bode.models.task_tag import task_tag
from bode.models.utc_datetime import UTCDateTime

Expand Down Expand Up @@ -44,21 +43,6 @@ def edit(task_id, **task_data):

return task

def delete(task_id):
subtasks = TaskRelation.get_subtasks_id_by_task_id(task_id)

for relation in TaskRelation.get_all_relations_by_task_id(task_id):
TaskRelation.delete(relation.id)
for subtask in subtasks:
Task.delete(subtask[0])

task = Task.get(task_id)

db.session.delete(task)
db.session.commit()

return task

def add_tag(task_id, **tag_data):
task = Task.get(task_id)
tag_name = tag_data["name"]
Expand Down
22 changes: 22 additions & 0 deletions bode/bode/models/task_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from bode.app import db
from bode.models.task import Task
from bode.models.task_relation import RelationType, TaskRelation


def delete_task(task_id):
"""Function deletes task, all it's relations and all it's subtasks reursively."""

def is_subtask_relation(relation, task_id):
return relation.type == RelationType.Subtask.value and str(relation.first_task_id) == task_id

relation_task_pairs = TaskRelation.get_related_tasks(task_id)

for relation, related_task in relation_task_pairs:
TaskRelation.delete(relation.id)
if is_subtask_relation(relation, task_id):
delete_task(str(related_task.id))

task = Task.get(task_id)
db.session.delete(task)
db.session.commit()
return task
22 changes: 15 additions & 7 deletions bode/bode/models/task_relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from sqlalchemy.dialects.postgresql import ENUM, UUID

from bode.app import db
from bode.models.task import Task


class RelationType(Enum):
Expand Down Expand Up @@ -81,20 +82,27 @@ def delete(relation_id):

return relation

def get_all_relations_by_task_id(task_id):
def get_lhs_related_tasks(task_id, filters=list()):
all_filters = [TaskRelation.first_task_id == task_id] + filters
return (
TaskRelation.query.filter(TaskRelation.first_task_id == task_id).all()
+ TaskRelation.query.filter(TaskRelation.second_task_id == task_id).all()
db.session.query(TaskRelation, Task)
.filter(*all_filters)
.join(Task, TaskRelation.second_task_id == Task.id)
.all()
)

def get_subtasks_id_by_task_id(task_id):
def get_rhs_related_tasks(task_id, filters=list()):
all_filters = [TaskRelation.second_task_id == task_id] + filters
return (
TaskRelation.query.with_entities(TaskRelation.second_task_id)
.filter(TaskRelation.first_task_id == task_id)
.filter(TaskRelation.type == RelationType.Subtask.value)
db.session.query(TaskRelation, Task)
.filter(*all_filters)
.join(Task, TaskRelation.first_task_id == Task.id)
.all()
)

def get_related_tasks(task_id):
return TaskRelation.get_lhs_related_tasks(task_id) + TaskRelation.get_rhs_related_tasks(task_id)

def __repr__(self):
return f"""<TaskRelation
{self.first_task_id} <{self.type}> {self.second_task_id}
Expand Down
89 changes: 71 additions & 18 deletions bode/bode/resources/task_relations/api.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
from uuid import UUID

from flask.views import MethodView
from flask_smorest import Blueprint, abort
from psycopg2 import DataError
from sqlalchemy.exc import IntegrityError

from bode.app import db
from bode.models.task import Task
from bode.models.task_relation import TaskRelation
from bode.models.task_relation import RelationType, TaskRelation
from bode.resources.task_relations.schemas import (
LHS_RELATION_TYPES,
RHS_RELATION_TYPES,
SYMMETRIC_RELATION_TYPES,
DirectedRelationSchema,
DirectedRelationType,
RelatedTaskSchema,
SimpleTaskRelationSchema,
TaskRelationInputSchema,
TasksRelatedSchema,
)

blueprint = Blueprint("task-relations", "task-relations", url_prefix="/task-relations")
Expand Down Expand Up @@ -37,18 +42,66 @@ def delete(self, relation_id):
return TaskRelation.delete(relation_id)


@blueprint.route("/<task_id>/<relation_type>")
class TasksRelatedByIdAndRelationType(MethodView):
@blueprint.response(200, TasksRelatedSchema(many=True))
def get(self, task_id, relation_type):
try:
result = (
db.session.query(TaskRelation.id, Task)
.join(Task, Task.id == TaskRelation.second_task_id)
.filter(TaskRelation.first_task_id == task_id)
.filter(TaskRelation.type == relation_type)
.all()
@blueprint.route("/<task_id>")
class TasksInRelationWith(MethodView):
def lhs_relation_filter(self, relation_type: str):
match relation_type:
case DirectedRelationType.DependsOn.value:
return TaskRelation.type == RelationType.Dependent.value
case DirectedRelationType.Subtask.value:
return TaskRelation.type == RelationType.Subtask.value
case DirectedRelationType.Interchangable.value:
return TaskRelation.type == RelationType.Interchangable.value
case _:
return None

def rhs_relation_filter(self, relation_type):
match relation_type:
case DirectedRelationType.IsDependentOn.value:
return TaskRelation.type == RelationType.Dependent.value
case DirectedRelationType.Supertask.value:
return TaskRelation.type == RelationType.Subtask.value
case DirectedRelationType.Interchangable.value:
return TaskRelation.type == RelationType.Interchangable.value
case _:
return None

def map_to_related_task_schema(self, relation: TaskRelation, task: Task):
match relation.type:
case RelationType.Dependent.value:
type = (
DirectedRelationType.IsDependentOn.value
if task.id == relation.first_task_id
else DirectedRelationType.DependsOn.value
)
case RelationType.Subtask.value:
type = (
DirectedRelationType.Supertask.value
if task.id == relation.first_task_id
else DirectedRelationType.Subtask.value
)
case _:
type = DirectedRelationType.Interchangable.value

return {"task": task, "relation_type": type, "relation_id": relation.id}

@blueprint.arguments(DirectedRelationSchema, location="query")
@blueprint.response(200, RelatedTaskSchema(many=True))
def get(self, params: DirectedRelationSchema, task_id: UUID):
relation_type = params.get("relation_type")

query_result = []

if not relation_type:
query_result += TaskRelation.get_lhs_related_tasks(task_id)
query_result += TaskRelation.get_rhs_related_tasks(
task_id, [TaskRelation.type != RelationType.Interchangable.value]
)
return [{"id": rel_id, "task": task} for rel_id, task in result]
except DataError:
abort(404)

elif relation_type in SYMMETRIC_RELATION_TYPES + LHS_RELATION_TYPES:
query_result += TaskRelation.get_lhs_related_tasks(task_id, [self.lhs_relation_filter(relation_type)])

elif relation_type in RHS_RELATION_TYPES:
query_result += TaskRelation.get_rhs_related_tasks(task_id, [self.rhs_relation_filter(relation_type)])

return map(lambda pair: self.map_to_related_task_schema(*pair), query_result)
28 changes: 26 additions & 2 deletions bode/bode/resources/task_relations/schemas.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import Enum

from marshmallow import ValidationError, fields, validate, validates_schema

from bode.models.task_relation import RelationType
Expand All @@ -23,6 +25,28 @@ class SimpleTaskRelationSchema(BaseSchema):
type = fields.String()


class TasksRelatedSchema(BaseSchema):
id = fields.UUID(dump_only=True)
class DirectedRelationType(Enum):
IsDependentOn = "is_dependent_on"
DependsOn = "depends_on"
Subtask = "subtask"
Supertask = "supertask"
Interchangable = "interchangable"

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


SYMMETRIC_RELATION_TYPES = [DirectedRelationType.Interchangable.value]
LHS_RELATION_TYPES = [DirectedRelationType.DependsOn.value, DirectedRelationType.Subtask.value]
RHS_RELATION_TYPES = [DirectedRelationType.IsDependentOn.value, DirectedRelationType.Supertask.value]


class DirectedRelationSchema(BaseSchema):
relation_type = fields.String(validate=validate.OneOf(DirectedRelationType.list()), default=None)


class RelatedTaskSchema(BaseSchema):
task = fields.Nested(TaskSchema)
relation_type = fields.String(validate=validate.OneOf(DirectedRelationType.list()), required=True)
relation_id = fields.UUID(dump_only=True)
3 changes: 2 additions & 1 deletion bode/bode/resources/tasks/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from psycopg2 import IntegrityError
from sqlalchemy.exc import DataError, NoResultFound

from bode.models import task_actions
from bode.models.task import Task
from bode.resources.tags.schemas import TagInputSchema
from bode.resources.tasks.schemas import TaskInputSchema, TaskSchema
Expand Down Expand Up @@ -42,7 +43,7 @@ def put(self, task_data, task_id):
@blueprint.response(200, TaskSchema)
def delete(self, task_id):
try:
return Task.delete(task_id)
return task_actions.delete_task(task_id)
except NoResultFound:
abort(404, message="Item not found.")

Expand Down
6 changes: 3 additions & 3 deletions cabra/src/api/taskRelations.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ITaskRelation, ITasksRelated } from "../types/taskRelation";
import { IRelatedTask, ITaskRelation } from "../types/taskRelation";

import axios from "axios";

export const getSubtasks = {
cacheKey: (id: string) => ["task-relations", id, "SUBTASK"],
cacheKey: (id: string) => ["task-relations", id, "subtask"],
run: (id: string) =>
axios.get<ITasksRelated[]>(`/task-relations/${id}/SUBTASK`),
axios.get<IRelatedTask[]>(`/task-relations/${id}?relationType=subtask`),
};

export const createRelation = (data: Omit<ITaskRelation, "id">) =>
Expand Down
2 changes: 1 addition & 1 deletion cabra/src/pages/components/SubtasksList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function SubtasksList({ parentId }: PropsList) {
<Container>
{subtasks.map((relation) => (
<Subtask
key={relation.id}
key={relation.relationId}
subtask={relation.task}
parentId={parentId}
/>
Expand Down
8 changes: 4 additions & 4 deletions cabra/src/pages/components/SubtasksListEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ export default function SubtasksListEdit({ parentId }: Props) {
return (
<div>
<Container>
{subtasks.map((relation) => (
{subtasks.map((relatedTask) => (
<MiniTaskDelete
key={relation.id}
title={relation.task.title}
key={relatedTask.relationId}
title={relatedTask.task.title}
onClickDelete={removeTask.mutateAsync}
taskId={relation.task.id}
taskId={relatedTask.task.id}
/>
))}
</Container>
Expand Down
5 changes: 3 additions & 2 deletions cabra/src/types/taskRelation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export interface ITaskRelation {
type: string;
}

export interface ITasksRelated {
id: string;
export interface IRelatedTask {
task: ITask;
relationType: string;
relationId: string;
}

0 comments on commit 437d0ee

Please sign in to comment.