import datetime
import re
import time
from functools import wraps

import bleach
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils import timezone
from django.utils.translation import gettext as _
from html2text import html2text
from rest_framework import serializers

# from .decorators import check_object_permission
from .models import (
    Activity,
    Column,
    ColumnWorkflow,
    Comment,
    Course,
    CourseFlowUser,
    Discipline,
    Favourite,
    LiveAssignment,
    LiveProject,
    LiveProjectUser,
    Node,
    NodeLink,
    NodeWeek,
    ObjectPermission,
    ObjectSet,
    Outcome,
    OutcomeHorizontalLink,
    OutcomeNode,
    OutcomeOutcome,
    OutcomeWorkflow,
    Program,
    Project,
    User,
    UserAssignment,
    Week,
    WeekWorkflow,
    Workflow,
    title_max_length,
)
from .utils import (
    benchmark,
    dateTimeFormat,
    get_unique_outcomehorizontallinks,
    get_unique_outcomenodes,
    get_user_permission,
    get_user_role,
    linkIDMap,
    user_workflow_url,
)

bleach_allowed_attributes_description = {
    "a": ["href", "title", "target"],
    "abbr": ["title"],
    "acronym": ["title"],
}
bleach_allowed_tags_description = [
    "b",
    "u",
    "em",
    "i",
    "ul",
    "ol",
    "li",
    "br",
    "p",
    "a",
    "strong",
    "sub",
    "sup",
]

bleach_allowed_tags_title = [
    "b",
    "u",
    "em",
    "i",
]


# timing_results = {}
# def timing(f):
#     @wraps(f)
#     def wrap(*args, **kw):
#         ts = time.time()
#         result = f(*args, **kw)
#         te = time.time()
#         #print(f'Function {f.__name__} took {te-ts:2.4f} seconds')
#         if timing_results.get(f.__name__) is None:
#             timing_results[f.__name__] = te - ts
#         else:
#             timing_results[f.__name__] = timing_results[f.__name__] + te - ts
#         print(f'Function {f.__name__} has taken {timing_results[f.__name__]:2.4f} seconds')
#         return result
#     return wrap


def bleach_sanitizer(value, **kwargs):
    if value is not None:
        return bleach.clean(value, **kwargs)
    else:
        return None


class AuthorSerializerMixin:
    author = serializers.SerializerMethodField()

    def get_author(self, instance):
        user = self.context.get("user", None)
        if user is not None:
            if instance.author is None:
                return ""
            return str(instance.author.username)
        else:
            return _("a CourseFlow user")


class DescriptionSerializerMixin:
    description = serializers.SerializerMethodField()

    def get_description(self, instance):
        return bleach_sanitizer(
            instance.description,
            tags=bleach_allowed_tags_description,
            attributes=bleach_allowed_attributes_description,
        )

    def validate_description(self, value):
        if value is None:
            return None
        return bleach_sanitizer(
            value,
            tags=bleach_allowed_tags_description,
            attributes=bleach_allowed_attributes_description,
        )


class TitleSerializerMixin:
    title = serializers.SerializerMethodField()

    def get_title(self, instance):
        return bleach_sanitizer(instance.title, tags=bleach_allowed_tags_title)

    def validate_title(self, value):
        return bleach_sanitizer(value, tags=bleach_allowed_tags_title)[
            :title_max_length
        ]


class DescriptionSerializerTextMixin(serializers.Serializer):
    description = serializers.SerializerMethodField()

    def get_description(self, instance):
        if instance.description is None:
            return None
        returnval = html2text(
            bleach_sanitizer(
                instance.description, tags=bleach_allowed_tags_description
            )
        )
        return re.sub("\n\n$", "", returnval)


class TitleSerializerTextMixin(serializers.Serializer):
    title = serializers.SerializerMethodField()

    def get_title(self, instance):
        title = instance.title

        if self.get_type(instance) == "node":
            if (
                instance.linked_workflow is not None
                and instance.represents_workflow
            ):
                title = instance.linked_workflow.title

        if title is None or title == "":
            if self.get_type(instance) == "week":
                return (
                    instance.get_week_type_display()
                    + " "
                    + str(
                        WeekWorkflow.objects.filter(week=instance)
                        .first()
                        .get_display_rank()
                        + 1
                    )
                )
            else:
                return _("Untitled")
        returnval = html2text(
            bleach_sanitizer(title, tags=bleach_allowed_tags_title)
        )
        return re.sub("\n\n$", "", returnval)


class TimeRequiredSerializerMixin:
    time_required = serializers.SerializerMethodField()

    def get_time_required(self, instance):
        return bleach_sanitizer(instance.time_required, tags=[])

    def validate_time_required(self, value):
        return bleach_sanitizer(value, tags=[])


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "first_name",
            "last_name",
        ]

    first_name = serializers.SerializerMethodField()
    last_name = serializers.SerializerMethodField()

    def get_first_name(self, instance):
        courseflow_user = CourseFlowUser.objects.filter(user=instance).first()
        if courseflow_user is None:
            courseflow_user = CourseFlowUser.objects.create(
                first_name=instance.first_name,
                last_name=instance.last_name,
                user=instance,
            )
        return bleach_sanitizer(
            courseflow_user.first_name,
            tags=[],
            attributes=[],
        )

    def validate_first_name(self, value):
        if value is None:
            return None
        return bleach_sanitizer(
            value,
            tags=[],
            attributes=[],
        )

    def get_last_name(self, instance):
        courseflow_user = CourseFlowUser.objects.filter(user=instance).first()
        if courseflow_user is None:
            courseflow_user = CourseFlowUser.objects.create(
                first_name=instance.first_name,
                last_name=instance.last_name,
                user=instance,
            )
        return bleach_sanitizer(
            courseflow_user.last_name,
            tags=[],
            attributes=[],
        )

    def validate_last_name(self, value):
        if value is None:
            return None
        return bleach_sanitizer(
            value,
            tags=[],
            attributes=[],
        )


class DisciplineSerializer(serializers.ModelSerializer):
    class Meta:
        model = Discipline
        fields = ["id", "title"]


class NodeLinkSerializerShallow(
    serializers.ModelSerializer, TitleSerializerMixin
):
    class Meta:
        model = NodeLink
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "source_node",
            "target_node",
            "source_port",
            "target_port",
            "dashed",
            "text_position",
        ]

    deleted_on = serializers.DateTimeField(format=dateTimeFormat())

    def create(self, validated_data):
        return Node.objects.create(
            author=User.objects.get(username=self.initial_data["author"]),
            **validated_data
        )

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.dashed = validated_data.get("dashed", instance.dashed)
        instance.text_position = validated_data.get(
            "text_position", instance.text_position
        )
        instance.save()
        return instance


class NodeSerializerShallow(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
    TimeRequiredSerializerMixin,
):

    outgoing_links = serializers.SerializerMethodField()
    outcomenode_set = serializers.SerializerMethodField()
    outcomenode_unique_set = serializers.SerializerMethodField()
    columnworkflow = serializers.SerializerMethodField()
    column = serializers.SerializerMethodField()
    linked_workflow_data = serializers.SerializerMethodField()
    has_assignment = serializers.SerializerMethodField()

    node_type_display = serializers.CharField(source="get_node_type_display")

    class Meta:
        model = Node
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "column",
            "columnworkflow",
            "context_classification",
            "task_classification",
            "outcomenode_set",
            "outcomenode_unique_set",
            "outgoing_links",
            "node_type",
            "node_type_display",
            "has_autolink",
            "time_units",
            "time_required",
            "ponderation_theory",
            "ponderation_practical",
            "ponderation_individual",
            "time_general_hours",
            "time_specific_hours",
            "represents_workflow",
            "linked_workflow",
            "linked_workflow_data",
            # "is_dropped",
            "comments",
            "sets",
            "has_assignment",
        ]

    deleted_on = serializers.DateTimeField(format=dateTimeFormat())

    def get_columnworkflow(self, instance):
        if instance.column is None:
            instance.column = (
                instance.get_workflow()
                .columns.filter(deleted=False)
                .order_by("columnworkflow__rank")
                .first()
            )
            instance.save()
        if instance.column.deleted:
            workflow = instance.get_workflow()
            return (
                ColumnWorkflow.objects.filter(
                    workflow=workflow, column__deleted=False
                )
                .order_by("rank")
                .first()
                .id
            )
        else:
            return instance.column.columnworkflow_set.get(
                column=instance.column
            ).id

    def get_column(self, instance):
        if instance.column is None:
            instance.column = (
                instance.get_workflow()
                .columns.filter(deleted=False)
                .order_by("columnworkflow__rank")
                .first()
            )
            instance.save()
        if instance.column.deleted:
            workflow = instance.get_workflow()
            return (
                ColumnWorkflow.objects.filter(
                    workflow=workflow, column__deleted=False
                )
                .order_by("rank")
                .first()
                .column.id
            )
        else:
            return instance.column.id

    def get_outcomenode_set(self, instance):
        links = instance.outcomenode_set.exclude(
            Q(outcome__deleted=True)
            | Q(outcome__parent_outcomes__deleted=True)
            | Q(outcome__parent_outcomes__parent_outcomes__deleted=True)
        )
        return list(map(linkIDMap, links))

    def get_outgoing_links(self, instance):
        links = instance.outgoing_links.exclude(
            Q(deleted=True)
            | Q(target_node__deleted=True)
            | Q(target_node__week__deleted=True)
        )
        return list(map(linkIDMap, links))

    def get_outcomenode_unique_set(self, instance):
        return list(map(linkIDMap, get_unique_outcomenodes(instance)))

    def get_linked_workflow_data(self, instance):
        linked_workflow = instance.linked_workflow
        if linked_workflow is not None:
            return LinkedWorkflowSerializerShallow(
                linked_workflow,
                context={"user": self.context.get("user", None)},
            ).data

    def get_has_assignment(self, instance):
        user = self.context.get("user", None)
        if user is None or not user.is_authenticated:
            return False
        assignments = instance.liveassignment_set.all()
        if assignments.exists():
            return instance.liveassignment_set.filter(
                Q(userassignment__user=user)
                | Q(
                    liveproject__liveprojectuser__role_type=LiveProjectUser.ROLE_TEACHER
                )
            ).exists()
        return False

    def create(self, validated_data):
        return Node.objects.create(
            author=User.objects.get(username=self.initial_data["author"]),
            **validated_data
        )

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.description = validated_data.get(
            "description", instance.description
        )
        instance.task_classification = validated_data.get(
            "task_classification", instance.task_classification
        )
        instance.context_classification = validated_data.get(
            "context_classification", instance.context_classification
        )
        instance.represents_workflow = validated_data.get(
            "represents_workflow", instance.represents_workflow
        )
        instance.has_autolink = validated_data.get(
            "has_autolink", instance.has_autolink
        )
        instance.time_required = validated_data.get(
            "time_required", instance.time_required
        )
        instance.time_units = validated_data.get(
            "time_units", instance.time_units
        )
        instance.ponderation_theory = validated_data.get(
            "ponderation_theory", instance.ponderation_theory
        )
        instance.ponderation_practical = validated_data.get(
            "ponderation_practical", instance.ponderation_practical
        )
        instance.ponderation_individual = validated_data.get(
            "ponderation_individual", instance.ponderation_individual
        )
        instance.time_general_hours = validated_data.get(
            "time_general_hours", instance.time_general_hours
        )
        instance.time_specific_hours = validated_data.get(
            "time_specific_hours", instance.time_specific_hours
        )
        # instance.is_dropped = validated_data.get(
        #     "is_dropped", instance.is_dropped
        # )
        instance.save()
        return instance


class LinkedWorkflowSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = Workflow
        fields = [
            "id",
            "deleted",
            "deleted_on",
            "title",
            "description",
            "code",
            "time_required",
            "time_units",
            "ponderation_theory",
            "ponderation_practical",
            "ponderation_individual",
            "time_general_hours",
            "time_specific_hours",
            "type",
            "created_on",
            "url",
        ]

    deleted_on = serializers.DateTimeField(format=dateTimeFormat())
    url = serializers.SerializerMethodField()

    def get_url(self, instance):
        user = self.context.get("user", None)
        return user_workflow_url(instance, user)


class NodeWeekSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = NodeWeek
        fields = ["week", "node", "added_on", "rank", "id"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance


class ColumnSerializerShallow(
    serializers.ModelSerializer, TitleSerializerMixin
):

    column_type_display = serializers.CharField(
        source="get_column_type_display"
    )

    class Meta:
        model = Column
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "icon",
            "column_type",
            "column_type_display",
            "colour",
            "visible",
            "comments",
        ]

    deleted_on = serializers.DateTimeField(format=dateTimeFormat())

    def create(self, validated_data):
        return Column.objects.create(
            author=User.objects.get(username=self.initial_data["author"]),
            **validated_data
        )

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.colour = validated_data.get("colour", instance.colour)
        instance.icon = validated_data.get("icon", instance.icon)
        instance.save()
        return instance


class WeekSerializerShallow(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
):

    nodeweek_set = serializers.SerializerMethodField()

    week_type_display = serializers.CharField(source="get_week_type_display")

    class Meta:
        model = Week
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "default",
            "nodeweek_set",
            "week_type",
            "week_type_display",
            "is_strategy",
            "strategy_classification",
            "comments",
            # "is_dropped",
        ]

    deleted_on = serializers.DateTimeField(format=dateTimeFormat())

    def get_nodeweek_set(self, instance):
        links = instance.nodeweek_set.filter(node__deleted=False).order_by(
            "rank"
        )
        return list(map(linkIDMap, links))

    def create(self, validated_data):
        return Week.objects.create(
            author=User.objects.get(username=self.initial_data["author"]),
            **validated_data
        )

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.description = validated_data.get(
            "description", instance.description
        )
        instance.strategy_classification = validated_data.get(
            "strategy_classification", instance.strategy_classification
        )
        # instance.is_dropped = validated_data.get(
        #     "is_dropped", instance.is_dropped
        # )
        instance.save()
        return instance


class WeekWorkflowSerializerShallow(serializers.ModelSerializer):

    week_type = serializers.SerializerMethodField()

    class Meta:
        model = WeekWorkflow
        fields = ["workflow", "week", "rank", "id", "week_type"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance

    def get_week_type(self, instance):
        return instance.week.week_type


class ColumnWorkflowSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = ColumnWorkflow
        fields = ["workflow", "column", "rank", "id"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance


class OutcomeWorkflowSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = OutcomeWorkflow
        fields = ["workflow", "outcome", "rank", "id"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance


class WorkflowSerializerFinder(serializers.ModelSerializer):
    class Meta:
        model = Workflow
        fields = ["id", "type"]

    def update(self, instance, validated_data):
        return instance


class ProjectSerializerShallow(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
    AuthorSerializerMixin,
):
    class Meta:
        model = Project
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "author",
            "author_id",
            "published",
            "created_on",
            "last_modified",
            "workflowproject_set",
            "disciplines",
            "type",
            "object_sets",
            "favourite",
            "liveproject",
            "object_permission",
        ]

    created_on = serializers.DateTimeField(format=dateTimeFormat())
    last_modified = serializers.DateTimeField(format=dateTimeFormat())
    workflowproject_set = serializers.SerializerMethodField()
    object_sets = serializers.SerializerMethodField()
    favourite = serializers.SerializerMethodField()
    deleted_on = serializers.DateTimeField(format=dateTimeFormat())
    author = serializers.SerializerMethodField()
    object_permission = serializers.SerializerMethodField()

    def get_favourite(self, instance):
        user = self.context.get("user")
        if user is None or not user.is_authenticated:
            return False
        if Favourite.objects.filter(
            user=user,
            content_type=ContentType.objects.get_for_model(instance),
            object_id=instance.id,
        ):
            return True
        else:
            return False

    def get_object_sets(self, instance):
        return [
            {
                "id": object_set.id,
                "term": object_set.term,
                "title": object_set.title,
                "translation_plural": object_set.translation_plural,
            }
            for object_set in instance.object_sets.all()
        ]

    def get_workflowproject_set(self, instance):
        links = instance.workflowproject_set.filter(
            workflow__deleted=False
        ).order_by("rank")
        return list(map(linkIDMap, links))

    def get_object_permission(self, instance):
        user = self.context.get("user")
        if user is None or not user.is_authenticated:
            return 0
        object_permission = ObjectPermission.objects.filter(
            user=user,
            content_type=ContentType.objects.get_for_model(instance),
            object_id=instance.id,
        ).first()
        if object_permission is None:
            return None
        return {
            "permission_type": object_permission.permission_type,
            "last_viewed": object_permission.last_viewed,
        }

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.description = validated_data.get(
            "description", instance.description
        )
        instance.published = validated_data.get(
            "published", instance.published
        )
        instance.disciplines.set(
            validated_data.get("disciplines", instance.disciplines.all())
        )
        instance.save()
        return instance


class OutcomeSerializerShallow(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
):
    class Meta:
        model = Outcome
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "code",
            "description",
            "child_outcome_links",
            "outcome_horizontal_links",
            "outcome_horizontal_links_unique",
            # "is_dropped",
            "depth",
            "type",
            "comments",
            "sets",
        ]

        # read_only_fields = [
        #     "id",
        #     "child_outcome_links",
        #     "outcome_horizontal_links",
        #     "outcome_horizontal_links_unique",
        #     "depth",
        #     "type",
        #     "comments",
        #     "sets",
        # ]

    child_outcome_links = serializers.SerializerMethodField()
    outcome_horizontal_links = serializers.SerializerMethodField()
    outcome_horizontal_links_unique = serializers.SerializerMethodField()
    type = serializers.SerializerMethodField()
    deleted_on = serializers.DateTimeField(format=dateTimeFormat())

    def get_type(self, instance):
        my_type = self.context.get("type", None)
        if my_type is None:
            my_type = instance.get_workflow().type + " outcome"
        return my_type

    def get_outcome_horizontal_links(self, instance):
        if len(instance.outcome_horizontal_links.all()) == 0:
            return []
        return list(
            map(
                linkIDMap,
                instance.outcome_horizontal_links.exclude(
                    Q(parent_outcome__deleted=True)
                    | Q(parent_outcome__parent_outcomes__deleted=True)
                    | Q(
                        parent_outcome__parent_outcomes__parent_outcomes__deleted=True
                    )
                ),
            )
        )

    def get_outcome_horizontal_links_unique(self, instance):
        if len(instance.outcome_horizontal_links.all()) == 0:
            return []
        return list(
            map(linkIDMap, get_unique_outcomehorizontallinks(instance))
        )

    def get_child_outcome_links(self, instance):
        links = instance.child_outcome_links.filter(child__deleted=False)
        if len(links) == 0:
            return []
        links = links.order_by("rank")
        return list(map(linkIDMap, links))

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.code = validated_data.get("code", instance.code)
        instance.description = validated_data.get(
            "description", instance.description
        )
        # instance.is_dropped = validated_data.get(
        #     "is_dropped", instance.is_dropped
        # )
        instance.save()
        return instance


class OutcomeOutcomeSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = OutcomeOutcome
        fields = ["parent", "child", "rank", "id"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance


class OutcomeNodeSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = OutcomeNode
        fields = ["node", "outcome", "rank", "id", "degree"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance


class OutcomeHorizontalLinkSerializerShallow(serializers.ModelSerializer):
    class Meta:
        model = OutcomeHorizontalLink
        fields = ["outcome", "parent_outcome", "rank", "degree", "id"]

    def update(self, instance, validated_data):
        instance.rank = validated_data.get("rank", instance.rank)
        instance.save()
        return instance


class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = ["id", "user", "created_on", "text"]

    user = serializers.SerializerMethodField()
    created_on = serializers.DateTimeField(format=dateTimeFormat())

    def get_user(self, instance):
        return UserSerializer(instance.user).data

    def update(self, instance, validated_data):
        instance.text = validated_data.get("text", instance.text)
        instance.save()
        return instance


class WorkflowSerializerShallow(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
    AuthorSerializerMixin,
):

    author_id = serializers.SerializerMethodField()

    class Meta:
        model = Workflow
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "code",
            "author",
            "created_on",
            "last_modified",
            "columnworkflow_set",
            "weekworkflow_set",
            "is_original",
            "parent_workflow",
            "outcomes_type",
            "outcomes_sort",
            "outcomeworkflow_set",
            "author_id",
            "is_strategy",
            "strategy_icon",
            "published",
            "time_required",
            "time_units",
            "ponderation_theory",
            "ponderation_practical",
            "ponderation_individual",
            "time_general_hours",
            "time_specific_hours",
            "edit_count",
            "favourite",
            "condensed",
            "importing",
            "public_view",
            "url",
        ]

    created_on = serializers.DateTimeField(format=dateTimeFormat())
    last_modified = serializers.DateTimeField(format=dateTimeFormat())
    weekworkflow_set = serializers.SerializerMethodField()
    columnworkflow_set = serializers.SerializerMethodField()
    outcomeworkflow_set = serializers.SerializerMethodField()
    favourite = serializers.SerializerMethodField()
    deleted_on = serializers.DateTimeField(format=dateTimeFormat())
    author = serializers.SerializerMethodField()
    outcomes_sort = serializers.SerializerMethodField()

    strategy_icon = serializers.SerializerMethodField()

    url = serializers.SerializerMethodField()

    # Although we'll hang onto outcomes_sort as a field for now, this should just reset to 0
    def get_outcomes_sort(self, instance):
        return 0

    def get_url(self, instance):
        user = self.context.get("user", None)
        return user_workflow_url(instance, user)

    def get_author_id(self, instance):
        if instance.author is not None:
            return instance.author.id
        return None

    def get_favourite(self, instance):
        user = self.context.get("user", None)
        if user is None or not user.is_authenticated:
            return False
        if user is None:
            return False
        if Favourite.objects.filter(
            user=user,
            content_type=ContentType.objects.get_for_model(
                instance.get_subclass()
            ),
            object_id=instance.id,
        ):
            return True
        else:
            return False

    def get_strategy_icon(self, instance):
        if instance.is_strategy:
            return instance.weeks.first().strategy_classification
        else:
            return None

    def get_weekworkflow_set(self, instance):
        links = instance.weekworkflow_set.filter(week__deleted=False).order_by(
            "rank"
        )
        return list(map(linkIDMap, links))

    def get_columnworkflow_set(self, instance):
        links = instance.columnworkflow_set.filter(
            column__deleted=False
        ).order_by("rank")
        return list(map(linkIDMap, links))

    def get_outcomeworkflow_set(self, instance):
        links = instance.outcomeworkflow_set.filter(
            outcome__deleted=False
        ).order_by("rank")
        return list(map(linkIDMap, links))

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.description = validated_data.get(
            "description", instance.description
        )
        instance.code = validated_data.get("code", instance.code)
        instance.outcomes_type = validated_data.get(
            "outcomes_type", instance.outcomes_type
        )
        instance.outcomes_sort = validated_data.get(
            "outcomes_sort", instance.outcomes_sort
        )
        instance.published = validated_data.get(
            "published", instance.published
        )
        instance.time_required = validated_data.get(
            "time_required", instance.time_required
        )
        instance.time_units = validated_data.get(
            "time_units", instance.time_units
        )
        instance.ponderation_theory = validated_data.get(
            "ponderation_theory", instance.ponderation_theory
        )
        instance.ponderation_practical = validated_data.get(
            "ponderation_practical", instance.ponderation_practical
        )
        instance.ponderation_individual = validated_data.get(
            "ponderation_individual", instance.ponderation_individual
        )
        instance.time_general_hours = validated_data.get(
            "time_general_hours", instance.time_general_hours
        )
        instance.time_specific_hours = validated_data.get(
            "time_specific_hours", instance.time_specific_hours
        )
        instance.condensed = validated_data.get(
            "condensed", instance.condensed
        )
        instance.public_view = validated_data.get(
            "public_view", instance.public_view
        )
        instance.save()
        return instance


class ProgramSerializerShallow(WorkflowSerializerShallow):

    author_id = serializers.SerializerMethodField()

    class Meta:
        model = Program
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "code",
            "author",
            "author_id",
            "created_on",
            "last_modified",
            "columnworkflow_set",
            "weekworkflow_set",
            "is_original",
            "parent_workflow",
            "outcomes_type",
            "outcomes_sort",
            "outcomeworkflow_set",
            "is_strategy",
            "published",
            "type",
            "DEFAULT_COLUMNS",
            "DEFAULT_CUSTOM_COLUMN",
            "time_required",
            "time_units",
            "ponderation_theory",
            "ponderation_practical",
            "ponderation_individual",
            "time_general_hours",
            "time_specific_hours",
            "favourite",
            "condensed",
            "importing",
            "public_view",
            "url",
        ]

    def get_author_id(self, instance):
        if instance.author is not None:
            return instance.author.id
        return None

    def create(self, validated_data):
        return Program.objects.create(
            author=User.objects.get(username=self.initial_data["author"]),
            **validated_data
        )


class CourseSerializerShallow(WorkflowSerializerShallow):

    author_id = serializers.SerializerMethodField()

    class Meta:
        model = Course
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "code",
            "author",
            "author_id",
            "created_on",
            "last_modified",
            "weekworkflow_set",
            "columnworkflow_set",
            "is_original",
            "parent_workflow",
            "outcomes_type",
            "outcomes_sort",
            "outcomeworkflow_set",
            "is_strategy",
            "published",
            "type",
            "DEFAULT_COLUMNS",
            "DEFAULT_CUSTOM_COLUMN",
            "time_required",
            "time_units",
            "ponderation_theory",
            "ponderation_practical",
            "ponderation_individual",
            "time_general_hours",
            "time_specific_hours",
            "favourite",
            "condensed",
            "importing",
            "public_view",
            "url",
        ]

    def get_author_id(self, instance):
        if instance.author is not None:
            return instance.author.id
        return None

    def create(self, validated_data):
        return Course.objects.create(
            author=User.objects.get(username=self.initial_data["author"]),
            **validated_data
        )


class ActivitySerializerShallow(WorkflowSerializerShallow):

    author_id = serializers.SerializerMethodField()

    class Meta:
        model = Activity
        fields = [
            "deleted",
            "deleted_on",
            "id",
            "title",
            "description",
            "code",
            "author",
            "author_id",
            "created_on",
            "last_modified",
            "columnworkflow_set",
            "weekworkflow_set",
            "is_original",
            "parent_workflow",
            "outcomes_sort",
            "outcomes_type",
            "outcomeworkflow_set",
            "is_strategy",
            "published",
            "type",
            "DEFAULT_COLUMNS",
            "DEFAULT_CUSTOM_COLUMN",
            "time_required",
            "time_units",
            "ponderation_theory",
            "ponderation_practical",
            "ponderation_individual",
            "time_general_hours",
            "time_specific_hours",
            "favourite",
            "condensed",
            "importing",
            "public_view",
            "url",
        ]

    def get_author_id(self, instance):
        if instance.author is not None:
            return instance.author.id
        return None

    def create(self, validated_data):
        if User.objects.filter(username=self.initial_data["author"]).exists():
            author = User.objects.get(username=self.initial_data["author"])
        else:
            author = None
        activity = Activity.objects.create(author=author, **validated_data)

        return activity


class ObjectSetSerializerShallow(
    serializers.ModelSerializer,
    TitleSerializerMixin,
):
    class Meta:
        model = ObjectSet
        fields = ["id", "title", "translation_plural", "term"]

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.translation_plural = validated_data.get(
            "translation_plural", instance.translation_plural
        )
        instance.save()
        return instance


class InfoBoxSerializer(
    serializers.Serializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
    AuthorSerializerMixin,
):

    deleted = serializers.ReadOnlyField()
    id = serializers.ReadOnlyField()
    created_on = serializers.DateTimeField(format=dateTimeFormat())
    last_modified = serializers.DateTimeField(format=dateTimeFormat())
    title = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    type = serializers.ReadOnlyField()
    favourite = serializers.SerializerMethodField()
    is_owned = serializers.SerializerMethodField()
    is_strategy = serializers.ReadOnlyField()
    published = serializers.ReadOnlyField()
    author = serializers.SerializerMethodField()
    title = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    project_title = serializers.SerializerMethodField()
    # url = serializers.SerializerMethodField()
    # can_edit = serializers.SerializerMethodField()
    object_permission = serializers.SerializerMethodField()
    has_liveproject = serializers.SerializerMethodField()
    workflow_count = serializers.SerializerMethodField()
    is_linked = serializers.SerializerMethodField()
    is_visible = serializers.SerializerMethodField()

    def get_workflow_count(self, instance):
        if instance.type == "project":
            return instance.workflows.all().count()
        return None

    def get_url(self, instance):
        if instance.type in ["project", "liveproject"]:
            return None
        user = self.context.get("user", None)
        return user_workflow_url(instance, user)

    def get_project_title(self, instance):
        if instance.type in ["project", "liveproject"]:
            return None
        if instance.get_project() is None:
            return None
        return instance.get_project().title

    def get_is_owned(self, instance):
        user = self.context.get("user")
        if user == instance.author:
            return True
        else:
            return False

    def get_favourite(self, instance):
        user = self.context.get("user")
        if user is None or not user.is_authenticated:
            return False
        if Favourite.objects.filter(
            user=user,
            content_type=ContentType.objects.get_for_model(
                instance.get_permission_objects()[0]
            ),
            object_id=instance.id,
        ):
            return True
        else:
            return False

    def get_object_permission(self, instance):
        user = self.context.get("user")
        if user is None or not user.is_authenticated:
            return 0
        object_permission = ObjectPermission.objects.filter(
            user=user,
            content_type=ContentType.objects.get_for_model(
                instance.get_permission_objects()[0]
            ),
            object_id=instance.id,
        ).first()
        object_role = get_user_role(instance, user)
        if object_permission is None:
            return {
                "permission_type": ObjectPermission.PERMISSION_VIEW,
                "last_viewed": None,
                "role_type": object_role,
            }
        return {
            "permission_type": object_permission.permission_type,
            "last_viewed": object_permission.last_viewed,
            "role_type": object_role,
        }

    def get_has_liveproject(self, instance):
        if instance.type == "project":
            if LiveProject.objects.filter(project=instance).count() > 0:
                return True
        return False

    def get_is_linked(self, instance):
        if instance.type not in ["project", "liveproject"]:
            return len(Node.objects.filter(linked_workflow=instance)) > 0
        return False

    def get_is_visible(self, instance):
        if instance.type in ["project", "liveproject"]:
            return False
        return len(LiveProject.objects.filter(visible_workflows=instance)) > 0


def analyticsDateTimeFormat():
    return "%Y %m"


class AnalyticsSerializer(
    serializers.Serializer,
):
    created_on = serializers.DateTimeField(format=analyticsDateTimeFormat())
    type = serializers.ReadOnlyField()
    User = serializers.SerializerMethodField()
    nodes = serializers.SerializerMethodField()
    email = serializers.SerializerMethodField()

    def get_nodes(self, instance):
        if instance.type == "project":
            return Node.objects.filter(
                week__workflow__project__id=instance.id
            ).count()
        else:
            return Node.objects.filter(week__workflow=instance.id).count()
        return 0

    def get_User(self, instance):
        if instance.author is not None:
            active = " (active)"
            if (
                timezone.now() - instance.author.last_login
                > datetime.timedelta(days=31)
            ):
                active = " (inactive)"
            return str(instance.author.pk) + active

    def get_email(self, instance):
        if instance.author is not None:
            return instance.author.email


# class RefreshSerializerWeek(serializers.Serializer):
#    class Meta:
#        model = Week
#        fields = [
#            "id",
#            "nodeweek_set",
#        ]
#
#    nodeweek_set = serializers.SerializerMethodField()
#
#    def get_nodeweek_set(self, instance):
#        links = instance.nodeweek_set.filter(node__deleted=False).order_by("rank")
#        return list(map(linkIDMap, links))
#
#
#
# class RefreshSerializerWorkflow(serializers.ModelSerializer):
#
#    author_id = serializers.SerializerMethodField()
#
#    class Meta:
#        model = Workflow
#        fields = [
#            "id",
#            "columnworkflow_set",
#            "weekworkflow_set",
#            "outcomeworkflow_set",
#        ]
#
#    weekworkflow_set = serializers.SerializerMethodField()
#    columnworkflow_set = serializers.SerializerMethodField()
#    outcomeworkflow_set = serializers.SerializerMethodField()
#
#
#
#    def get_weekworkflow_set(self, instance):
#        links = instance.weekworkflow_set.filter(week__deleted=False).order_by("rank")
#        return list(map(linkIDMap, links))
#
#    def get_columnworkflow_set(self, instance):
#        links = instance.columnworkflow_set.filter(column__deleted=False).order_by("rank")
#        return list(map(linkIDMap, links))
#
#    def get_outcomeworkflow_set(self, instance):
#        links = instance.outcomeworkflow_set.filter(outcome__deleted=False).order_by("rank")
#        return list(map(linkIDMap, links))
#
#


class RefreshSerializerOutcome(serializers.ModelSerializer):
    class Meta:
        model = Outcome
        fields = [
            "id",
            "outcome_horizontal_links",
            "outcome_horizontal_links_unique",
        ]

    outcome_horizontal_links = serializers.SerializerMethodField()
    outcome_horizontal_links_unique = serializers.SerializerMethodField()

    def get_outcome_horizontal_links(self, instance):
        if len(instance.outcome_horizontal_links.all()) == 0:
            return []
        return list(
            map(
                linkIDMap,
                instance.outcome_horizontal_links.exclude(
                    Q(parent_outcome__deleted=True)
                    | Q(parent_outcome__parent_outcomes__deleted=True)
                    | Q(
                        parent_outcome__parent_outcomes__parent_outcomes__deleted=True
                    )
                ),
            )
        )

    def get_outcome_horizontal_links_unique(self, instance):
        if len(instance.outcome_horizontal_links.all()) == 0:
            return []
        return list(
            map(linkIDMap, get_unique_outcomehorizontallinks(instance))
        )


class RefreshSerializerNode(serializers.ModelSerializer):

    outcomenode_set = serializers.SerializerMethodField()
    outcomenode_unique_set = serializers.SerializerMethodField()

    class Meta:
        model = Node
        fields = [
            "id",
            "outcomenode_set",
            "outcomenode_unique_set",
        ]

    def get_outcomenode_set(self, instance):
        links = instance.outcomenode_set.exclude(
            Q(outcome__deleted=True)
            | Q(outcome__parent_outcomes__deleted=True)
            | Q(outcome__parent_outcomes__parent_outcomes__deleted=True)
        )
        return list(map(linkIDMap, links))

    def get_outcomenode_unique_set(self, instance):
        return list(map(linkIDMap, get_unique_outcomenodes(instance)))


class OutcomeExportSerializer(
    serializers.ModelSerializer,
    TitleSerializerTextMixin,
    DescriptionSerializerTextMixin,
):
    class Meta:
        model = Outcome
        fields = [
            "id",
            "title",
            "code",
            "description",
            "depth",
        ]

    code = serializers.SerializerMethodField()

    def get_code(self, instance):
        if instance.depth == 0:
            outcomeworkflow = OutcomeWorkflow.objects.filter(
                outcome=instance
            ).first()
            if instance.code is None or instance.code == "":
                return str(outcomeworkflow.get_display_rank() + 1)
            else:
                return instance.code
        else:
            outcomeoutcome = OutcomeOutcome.objects.filter(
                child=instance
            ).first()
            if instance.code is None or instance.code == "":
                return (
                    self.get_code(outcomeoutcome.parent)
                    + "."
                    + str(outcomeoutcome.get_display_rank() + 1)
                )
            else:
                return (
                    self.get_code(outcomeoutcome.parent) + "." + instance.code
                )

    def get_type(self, instance):
        return "outcome"


class WeekExportSerializer(
    serializers.ModelSerializer,
    TitleSerializerTextMixin,
    DescriptionSerializerTextMixin,
):
    class Meta:
        model = Week
        fields = [
            "id",
            "title",
            "description",
            "type",
        ]

    objectType = "week"

    type = serializers.SerializerMethodField()

    def get_type(self, instance):
        return "week"

class NodeExportSerializer(
    serializers.ModelSerializer,
    TitleSerializerTextMixin,
    DescriptionSerializerTextMixin,
):
    class Meta:
        model = Node
        fields = [
            "id",
            "code",
            "title",
            "description",
            "column_order",
            "type",
        ]

    column_order = serializers.SerializerMethodField()
    type = serializers.SerializerMethodField()
    code = serializers.SerializerMethodField()

    def get_column_order(self, instance):
        return str(ColumnWorkflow.objects.get(column=instance.column).rank)

    def get_type(self, instance):
        return "node"

    def get_code(self, instance):
        if instance.linked_workflow and instance.linked_workflow.code is not None:
            return instance.linked_workflow.code
        else:
            return ""


class NodeExportSerializerWithTime(NodeExportSerializer):
    class Meta:
        model = Node
        fields = [
            "id",
            "title",
            "description",
            "column_order",
            "type",
            "code",
            "ponderation_theory",
            "ponderation_individual",
            "ponderation_practical",
            "time_required",
        ]

    code = serializers.SerializerMethodField()
    ponderation_theory = serializers.SerializerMethodField()
    ponderation_individual = serializers.SerializerMethodField()
    ponderation_practical = serializers.SerializerMethodField()
    time_required = serializers.SerializerMethodField()

    def get_code(self, instance):
        if (
            instance.represents_workflow
            and instance.linked_workflow is not None
        ):
            return instance.linked_workflow.code
        else:
            return None

    def get_ponderation_theory(self, instance):
        if (
            instance.represents_workflow
            and instance.linked_workflow is not None
        ):
            return instance.linked_workflow.ponderation_theory
        else:
            return instance.ponderation_theory

    def get_ponderation_individual(self, instance):
        if (
            instance.represents_workflow
            and instance.linked_workflow is not None
        ):
            return instance.linked_workflow.ponderation_individual
        else:
            return instance.ponderation_individual

    def get_ponderation_practical(self, instance):
        if (
            instance.represents_workflow
            and instance.linked_workflow is not None
        ):
            return instance.linked_workflow.ponderation_practical
        else:
            return instance.ponderation_practical

    def get_time_required(self, instance):
        if (
            instance.represents_workflow
            and instance.linked_workflow is not None
        ):
            return instance.linked_workflow.time_required
        else:
            return instance.time_required


class WorkflowExportSerializer(
    serializers.ModelSerializer,
    TitleSerializerTextMixin,
    DescriptionSerializerTextMixin,
):
    class Meta:
        model = Workflow
        fields = [
            "id",
            "title",
            "description",
            "type",
        ]

    type = serializers.SerializerMethodField()

    def get_type(self, instance):
        return "workflow"


class UpdateNotificationSerializer(
    serializers.ModelSerializer,
    TitleSerializerMixin,
):
    class Meta:
        model = Workflow
        fields = [
            "id",
            "title",
        ]


"""
Live Project Serializers
"""


class LiveProjectSerializer(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
):
    class Meta:
        model = LiveProject
        fields = [
            "title",
            "description",
            "pk",
            "type",
            "created_on",
            "default_self_reporting",
            "default_assign_to_all",
            "default_single_completion",
            "default_all_workflows_visible",
            "registration_hash",
            "id",
        ]

    title = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    id = serializers.SerializerMethodField()

    created_on = serializers.DateTimeField(format=dateTimeFormat())

    registration_hash = serializers.SerializerMethodField()

    def get_registration_hash(self, instance):
        user = self.context.get("user", None)
        if user is None:
            return None
        if instance.project.author == user:
            return instance.project.registration_hash()
        return None

    def get_title(self, instance):
        return super().get_title(instance.project)

    def get_description(self, instance):
        return super().get_description(instance.project)

    def get_id(self, instance):
        return instance.pk

    def update(self, instance, validated_data):
        instance.default_self_reporting = validated_data.get(
            "default_self_reporting", instance.default_self_reporting
        )
        instance.default_assign_to_all = validated_data.get(
            "default_assign_to_all", instance.default_assign_to_all
        )
        instance.default_single_completion = validated_data.get(
            "default_single_completion", instance.default_single_completion
        )
        instance.default_all_workflows_visible = validated_data.get(
            "default_all_workflows_visible",
            instance.default_all_workflows_visible,
        )

        instance.save()
        return instance


class LiveAssignmentSerializer(
    serializers.ModelSerializer,
    AuthorSerializerMixin,
):
    class Meta:
        model = LiveAssignment
        fields = [
            "id",
            "author",
            "created_on",
            "self_reporting",
            "single_completion",
            "task",
            "start_date",
            "end_date",
            "parent_workflow_id",
            "workflow_access",
            "linked_workflow_access",
            "user_assignment",
            "liveproject",
        ]

    task = serializers.SerializerMethodField()
    user_assignment = serializers.SerializerMethodField()
    workflow_access = serializers.SerializerMethodField()
    linked_workflow_access = serializers.SerializerMethodField()
    parent_workflow_id = serializers.SerializerMethodField()
    created_on = serializers.DateTimeField(format=dateTimeFormat())

    def get_task(self, instance):
        node = instance.task
        if node is not None:
            return NodeSerializerForAssignments(
                node, context={"user": self.context.get("user", None)}
            ).data

    def get_workflow_access(self, instance):
        try:
            workflow = instance.task.get_workflow()
            if workflow in instance.liveproject.visible_workflows.all():
                return True
        except AttributeError:
            return False
        return False

    def get_linked_workflow_access(self, instance):
        try:
            linked_workflow = instance.task.linked_workflow
            if linked_workflow in instance.liveproject.visible_workflows.all():
                return True
        except AttributeError:
            return False
        return False

    def get_parent_workflow_id(self, instance):
        try:
            parent_workflow = instance.task.get_workflow()
            return parent_workflow.id
        except AttributeError:
            return False
        return False

    def get_user_assignment(self, instance):
        if instance.single_completion:
            if instance.userassignment_set.filter(completed=True).count() > 0:
                userassignment = (
                    instance.userassignment_set.filter(completed=True)
                    .order_by("completed_on")
                    .first()
                )
                return UserAssignmentSerializerWithUser(userassignment).data
        try:
            userassignment = UserAssignment.objects.filter(
                user=self.context["user"], assignment=instance
            ).first()
            if userassignment is not None:
                return UserAssignmentSerializerWithUser(userassignment).data
        except AttributeError:
            return None
        return None

    def update(self, instance, validated_data):
        instance.self_reporting = validated_data.get(
            "self_reporting", instance.self_reporting
        )
        instance.single_completion = validated_data.get(
            "single_completion", instance.single_completion
        )
        instance.start_date = validated_data.get(
            "start_date", instance.start_date
        )
        instance.end_date = validated_data.get("end_date", instance.end_date)
        instance.save()
        return instance


class LiveAssignmentWithCompletionSerializer(LiveAssignmentSerializer):
    class Meta:
        model = LiveAssignment
        fields = [
            "id",
            "author",
            "created_on",
            "self_reporting",
            "single_completion",
            "task",
            "start_date",
            "end_date",
            "parent_workflow_id",
            "workflow_access",
            "linked_workflow_access",
            "liveproject",
            "completion_info",
        ]

    completion_info = serializers.SerializerMethodField()

    def get_completion_info(self, instance):
        userassignments = UserAssignment.objects.filter(assignment=instance)
        num_total = userassignments.count()
        num_completed = userassignments.filter(completed=True).count()
        if instance.single_completion and num_completed > 0:
            return str(num_total) + "/" + str(num_total)
        return str(num_completed) + "/" + str(num_total)


class UserAssignmentSerializer(
    serializers.ModelSerializer,
):
    class Meta:
        model = UserAssignment
        fields = [
            "id",
            "user",
            "assignment",
            "completed",
        ]


class UserAssignmentSerializerWithUser(
    serializers.ModelSerializer,
):
    class Meta:
        model = UserAssignment
        fields = [
            "id",
            "liveprojectuser",
            "assignment",
            "completed",
            "completed_on",
        ]

    liveprojectuser = serializers.SerializerMethodField()

    def get_liveprojectuser(self, instance):
        return LiveProjectUserSerializer(
            LiveProjectUser.objects.filter(
                user=instance.user, liveproject=instance.assignment.liveproject
            ).first()
        ).data


class LiveProjectUserSerializer(
    serializers.ModelSerializer,
):
    class Meta:
        model = LiveProjectUser
        fields = [
            "id",
            "user",
            "role_type",
            "role_type_display",
        ]

    role_type_display = serializers.CharField(source="get_role_type_display")
    user = serializers.SerializerMethodField()

    def get_user(self, instance):
        return UserSerializer(instance.user).data


class LiveProjectUserSerializerWithCompletion(
    LiveProjectUserSerializer,
):
    class Meta:
        model = LiveProjectUser
        fields = [
            "id",
            "user",
            "role_type",
            "role_type_display",
            "completion",
        ]

    completion = serializers.SerializerMethodField()

    def get_completion(self, instance):
        assignments = UserAssignment.objects.filter(
            assignment__liveproject=instance.liveproject,
            user=instance.user,
        )
        return (
            str(
                assignments.filter(
                    Q(completed=True)
                    | Q(
                        assignment__single_completion=True,
                        assignment__userassignment__completed=True,
                    )
                )
                .distinct()
                .count()
            )
            + "/"
            + str(assignments.count())
        )


class WorkflowSerializerForAssignments(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
    AuthorSerializerMixin,
):
    class Meta:
        model = Workflow
        fields = [
            "id",
            "title",
            "description",
            "author",
            "created_on",
            "code",
            "type",
            "deleted",
            "weeks",
            "url",
        ]

    created_on = serializers.DateTimeField(format=dateTimeFormat())
    author = serializers.SerializerMethodField()
    title = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    weeks = serializers.SerializerMethodField()
    url = serializers.SerializerMethodField()

    def get_url(self, instance):
        user = self.context.get("user", None)
        return user_workflow_url(instance, user)

    def get_type(self, instance):
        return instance.type

    def get_weeks(self, instance):
        return WeekSerializerForAssignments(
            Week.objects.filter(workflow=instance, deleted=False).order_by(
                "weekworkflow__rank"
            ),
            context={"user": self.context.get("user", None)},
            many=True,
        ).data


class WeekSerializerForAssignments(
    serializers.ModelSerializer,
    TitleSerializerMixin,
):
    class Meta:
        model = Week
        fields = [
            "id",
            "title",
            "deleted",
            "nodes",
            "week_type",
            "week_type_display",
        ]

    title = serializers.SerializerMethodField()
    nodes = serializers.SerializerMethodField()
    week_type_display = serializers.CharField(source="get_week_type_display")

    def get_type(self, instance):
        return instance.type

    def get_nodes(self, instance):
        return NodeSerializerForAssignments(
            Node.objects.filter(week=instance, deleted=False).order_by(
                "nodeweek__rank"
            ),
            context={"user": self.context.get("user", None)},
            many=True,
        ).data


class NodeSerializerForAssignments(
    serializers.ModelSerializer,
    TitleSerializerMixin,
    DescriptionSerializerMixin,
):
    class Meta:
        model = Node
        fields = [
            "id",
            "title",
            "description",
            "deleted",
            "linked_workflow",
            "linked_workflow_data",
            "colour",
            "column_type",
        ]

    title = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    linked_workflow_data = serializers.SerializerMethodField()
    colour = serializers.SerializerMethodField()
    column_type = serializers.SerializerMethodField()

    def get_colour(self, instance):
        return instance.column.colour

    def get_column_type(self, instance):
        return instance.column.column_type

    def get_type(self, instance):
        return instance.type

    def get_linked_workflow_data(self, instance):
        linked_workflow = instance.linked_workflow
        if linked_workflow is not None:
            return LinkedWorkflowSerializerShallow(
                linked_workflow,
                context={"user": self.context.get("user", None)},
            ).data


#
# serializer_lookups = {
#    "node": NodeSerializer,
#    "week": WeekSerializer,
#    "column": ColumnSerializer,
#    "activity": ActivitySerializer,
#    "course": CourseSerializer,
#    "program": ProgramSerializer,
# }


serializer_lookups_shallow = {
    "nodelink": NodeLinkSerializerShallow,
    "node": NodeSerializerShallow,
    "nodeweek": NodeWeekSerializerShallow,
    "week": WeekSerializerShallow,
    "weekworkflow": WeekWorkflowSerializerShallow,
    "column": ColumnSerializerShallow,
    "columnworkflow": ColumnWorkflowSerializerShallow,
    "workflow": WorkflowSerializerShallow,
    "activity": ActivitySerializerShallow,
    "course": CourseSerializerShallow,
    "program": ProgramSerializerShallow,
    "project": ProjectSerializerShallow,
    "outcome": OutcomeSerializerShallow,
    "outcomeoutcome": OutcomeOutcomeSerializerShallow,
    "outcomeworkflow": OutcomeWorkflowSerializerShallow,
    "objectset": ObjectSetSerializerShallow,
    "liveassignment": LiveAssignmentSerializer,
    "liveproject": LiveProjectSerializer,
}
