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-44] Implementacja endpointu GET do pobrania zadań będących w relacji z danym zadaniem #22

Merged
merged 12 commits into from
Apr 27, 2022
21 changes: 20 additions & 1 deletion 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 @@ -71,7 +72,25 @@ def delete(relation_id):

return relation

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

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

def __repr__(self):
return f"""<TaskRelation
{self.first_task_id} <{self.relationship}> {self.second_task_id}
{self.first_task_id} <{self.type}> {self.second_task_id}
>"""
76 changes: 75 additions & 1 deletion bode/bode/resources/task_relations/api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
from uuid import UUID

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

from bode.models.task_relation import TaskRelation
from bode.models.task import Task
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,
)
Expand All @@ -27,3 +36,68 @@ class TasksRelationsById(MethodView):
@blueprint.response(200, SimpleTaskRelationSchema)
def delete(self, relation_id):
return TaskRelation.delete(relation_id)


@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]
)

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)
32 changes: 32 additions & 0 deletions bode/bode/resources/task_relations/schemas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from enum import Enum

from marshmallow import ValidationError, fields, validate, validates_schema

from bode.models.task_relation import RelationType
from bode.resources.base_schema import BaseSchema
from bode.resources.tasks.schemas import TaskSchema


class TaskRelationInputSchema(BaseSchema):
Expand All @@ -20,3 +23,32 @@ class SimpleTaskRelationSchema(BaseSchema):
first_task_id = fields.UUID()
second_task_id = fields.UUID()
type = fields.String()


class DirectedRelationType(Enum):
"""Enum containing strings used for requesting relation types. It is used to handle unsymmetric relations."""

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)