taiga-back/taiga/projects/serializers.py

458 lines
15 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext as _
from taiga.base.api import serializers
from taiga.base.fields import Field, MethodField, I18NField
from taiga.permissions import services as permissions_services
from taiga.users.services import get_photo_url, get_user_photo_url
from taiga.users.gravatar import get_gravatar_id, get_user_gravatar_id
from taiga.users.serializers import UserBasicInfoSerializer
from taiga.permissions.services import calculate_permissions
from taiga.permissions.services import is_project_admin, is_project_owner
from . import services
from .notifications.choices import NotifyLevel
######################################################
# Custom values for selectors
######################################################
class PointsSerializer(serializers.LightSerializer):
name = I18NField()
order = Field()
value = Field()
project = Field(attr="project_id")
class UserStoryStatusSerializer(serializers.LightSerializer):
name = I18NField()
slug = Field()
order = Field()
is_closed = Field()
is_archived = Field()
color = Field()
wip_limit = Field()
project = Field(attr="project_id")
class TaskStatusSerializer(serializers.LightSerializer):
name = I18NField()
slug = Field()
order = Field()
is_closed = Field()
color = Field()
project = Field(attr="project_id")
class SeveritySerializer(serializers.LightSerializer):
name = I18NField()
order = Field()
color = Field()
project = Field(attr="project_id")
class PrioritySerializer(serializers.LightSerializer):
name = I18NField()
order = Field()
color = Field()
project = Field(attr="project_id")
class IssueStatusSerializer(serializers.LightSerializer):
name = I18NField()
slug = Field()
order = Field()
is_closed = Field()
color = Field()
project = Field(attr="project_id")
class IssueTypeSerializer(serializers.LightSerializer):
name = I18NField()
order = Field()
color = Field()
project = Field(attr="project_id")
######################################################
# Members
######################################################
class MembershipDictSerializer(serializers.LightDictSerializer):
role_name = Field()
full_name = Field()
full_name_display = MethodField()
is_active = Field()
id = Field()
color = Field()
username = Field()
photo = MethodField()
gravatar_id = MethodField()
def get_full_name_display(self, obj):
return obj["full_name"] or obj["username"] or obj["email"]
def get_photo(self, obj):
return get_photo_url(obj['photo'])
def get_gravatar_id(self, obj):
return get_gravatar_id(obj['email'])
class MembershipSerializer(serializers.LightSerializer):
id = Field()
user = Field(attr="user_id")
project = Field(attr="project_id")
role = Field(attr="role_id")
is_admin = Field()
created_at = Field()
invited_by = Field(attr="invited_by_id")
invitation_extra_text = Field()
user_order = Field()
role_name = MethodField()
full_name = MethodField()
is_user_active = MethodField()
color = MethodField()
photo = MethodField()
gravatar_id = MethodField()
project_name = MethodField()
project_slug = MethodField()
invited_by = UserBasicInfoSerializer()
is_owner = MethodField()
def get_role_name(self, obj):
return obj.role.name if obj.role else None
def get_full_name(self, obj):
return obj.user.get_full_name() if obj.user else None
def get_is_user_active(self, obj):
return obj.user.is_active if obj.user else False
def get_color(self, obj):
return obj.user.color if obj.user else None
def get_photo(self, obj):
return get_user_photo_url(obj.user)
def get_gravatar_id(self, obj):
return get_user_gravatar_id(obj.user)
def get_project_name(self, obj):
return obj.project.name if obj and obj.project else ""
def get_project_slug(self, obj):
return obj.project.slug if obj and obj.project else ""
def get_is_owner(self, obj):
return (obj and obj.user_id and obj.project_id and obj.project.owner_id and
obj.user_id == obj.project.owner_id)
class MembershipAdminSerializer(MembershipSerializer):
email = Field()
user_email = MethodField()
def get_user_email(self, obj):
return obj.user.email if obj.user else None
# IMPORTANT: Maintain the MembershipSerializer Meta up to date
# with this info (excluding there user_email and email)
######################################################
# Projects
######################################################
class ProjectSerializer(serializers.LightSerializer):
id = Field()
name = Field()
slug = Field()
description = Field()
created_date = Field()
modified_date = Field()
owner = MethodField()
members = MethodField()
total_milestones = Field()
total_story_points = Field()
is_backlog_activated = Field()
is_kanban_activated = Field()
is_wiki_activated = Field()
is_issues_activated = Field()
videoconferences = Field()
videoconferences_extra_data = Field()
creation_template = Field(attr="creation_template_id")
is_private = Field()
anon_permissions = Field()
public_permissions = Field()
is_featured = Field()
is_looking_for_people = Field()
looking_for_people_note = Field()
blocked_code = Field()
totals_updated_datetime = Field()
total_fans = Field()
total_fans_last_week = Field()
total_fans_last_month = Field()
total_fans_last_year = Field()
total_activity = Field()
total_activity_last_week = Field()
total_activity_last_month = Field()
total_activity_last_year = Field()
tags = Field()
tags_colors = MethodField()
default_points = Field(attr="default_points_id")
default_us_status = Field(attr="default_us_status_id")
default_task_status = Field(attr="default_task_status_id")
default_priority = Field(attr="default_priority_id")
default_severity = Field(attr="default_severity_id")
default_issue_status = Field(attr="default_issue_status_id")
default_issue_type = Field(attr="default_issue_type_id")
my_permissions = MethodField()
i_am_owner = MethodField()
i_am_admin = MethodField()
i_am_member = MethodField()
notify_level = MethodField()
total_closed_milestones = MethodField()
is_watcher = MethodField()
total_watchers = MethodField()
logo_small_url = MethodField()
logo_big_url = MethodField()
is_fan = Field(attr="is_fan_attr")
def get_members(self, obj):
assert hasattr(obj, "members_attr"), "instance must have a members_attr attribute"
if obj.members_attr is None:
return []
return [m.get("id") for m in obj.members_attr if m["id"] is not None]
def get_i_am_member(self, obj):
assert hasattr(obj, "members_attr"), "instance must have a members_attr attribute"
if obj.members_attr is None:
return False
if "request" in self.context:
user = self.context["request"].user
user_ids = [m.get("id") for m in obj.members_attr if m["id"] is not None]
if not user.is_anonymous() and user.id in user_ids:
return True
return False
def get_tags_colors(self, obj):
return dict(obj.tags_colors)
def get_my_permissions(self, obj):
if "request" in self.context:
user = self.context["request"].user
return calculate_permissions(
is_authenticated=user.is_authenticated(),
is_superuser=user.is_superuser,
is_member=self.get_i_am_member(obj),
is_admin=self.get_i_am_admin(obj),
role_permissions=obj.my_role_permissions_attr,
anon_permissions=obj.anon_permissions,
public_permissions=obj.public_permissions)
return []
def get_owner(self, obj):
return UserBasicInfoSerializer(obj.owner).data
def get_i_am_owner(self, obj):
if "request" in self.context:
return is_project_owner(self.context["request"].user, obj)
return False
def get_i_am_admin(self, obj):
if "request" in self.context:
return is_project_admin(self.context["request"].user, obj)
return False
def get_total_closed_milestones(self, obj):
assert hasattr(obj, "closed_milestones_attr"), "instance must have a closed_milestones_attr attribute"
return obj.closed_milestones_attr
def get_is_watcher(self, obj):
assert hasattr(obj, "notify_policies_attr"), "instance must have a notify_policies_attr attribute"
np = self.get_notify_level(obj)
return np is not None and np != NotifyLevel.none
def get_total_watchers(self, obj):
assert hasattr(obj, "notify_policies_attr"), "instance must have a notify_policies_attr attribute"
if obj.notify_policies_attr is None:
return 0
valid_notify_policies = [np for np in obj.notify_policies_attr if np["notify_level"] != NotifyLevel.none]
return len(valid_notify_policies)
def get_notify_level(self, obj):
assert hasattr(obj, "notify_policies_attr"), "instance must have a notify_policies_attr attribute"
if obj.notify_policies_attr is None:
return None
if "request" in self.context:
user = self.context["request"].user
for np in obj.notify_policies_attr:
if np["user_id"] == user.id:
return np["notify_level"]
return None
def get_logo_small_url(self, obj):
return services.get_logo_small_thumbnail_url(obj)
def get_logo_big_url(self, obj):
return services.get_logo_big_thumbnail_url(obj)
class ProjectDetailSerializer(ProjectSerializer):
us_statuses = Field(attr="userstory_statuses_attr")
points = Field(attr="points_attr")
task_statuses = Field(attr="task_statuses_attr")
issue_statuses = Field(attr="issue_statuses_attr")
issue_types = Field(attr="issue_types_attr")
priorities = Field(attr="priorities_attr")
severities = Field(attr="severities_attr")
userstory_custom_attributes = Field(attr="userstory_custom_attributes_attr")
task_custom_attributes = Field(attr="task_custom_attributes_attr")
issue_custom_attributes = Field(attr="issue_custom_attributes_attr")
roles = Field(attr="roles_attr")
members = MethodField()
total_memberships = MethodField()
is_out_of_owner_limits = MethodField()
# Admin fields
is_private_extra_info = MethodField()
max_memberships = MethodField()
issues_csv_uuid = Field()
tasks_csv_uuid = Field()
userstories_csv_uuid = Field()
transfer_token = Field()
def to_value(self, instance):
# Name attributes must be translated
for attr in ["userstory_statuses_attr", "points_attr", "task_statuses_attr",
"issue_statuses_attr", "issue_types_attr", "priorities_attr",
"severities_attr", "userstory_custom_attributes_attr",
"task_custom_attributes_attr", "issue_custom_attributes_attr", "roles_attr"]:
assert hasattr(instance, attr), "instance must have a {} attribute".format(attr)
val = getattr(instance, attr)
if val is None:
continue
for elem in val:
elem["name"] = _(elem["name"])
ret = super().to_value(instance)
admin_fields = [
"is_private_extra_info", "max_memberships", "issues_csv_uuid",
"tasks_csv_uuid", "userstories_csv_uuid", "transfer_token"
]
is_admin_user = False
if "request" in self.context:
user = self.context["request"].user
is_admin_user = permissions_services.is_project_admin(user, instance)
if not is_admin_user:
for admin_field in admin_fields:
del(ret[admin_field])
return ret
def get_members(self, obj):
assert hasattr(obj, "members_attr"), "instance must have a members_attr attribute"
if obj.members_attr is None:
return []
return MembershipDictSerializer([m for m in obj.members_attr if m['id'] is not None], many=True).data
def get_total_memberships(self, obj):
if obj.members_attr is None:
return 0
return len(obj.members_attr)
def get_is_out_of_owner_limits(self, obj):
assert hasattr(obj, "private_projects_same_owner_attr"), "instance must have a private_projects_same_owner_attr attribute"
assert hasattr(obj, "public_projects_same_owner_attr"), "instance must have a public_projects_same_owner_attr attribute"
return services.check_if_project_is_out_of_owner_limits(
obj,
current_memberships=self.get_total_memberships(obj),
current_private_projects=obj.private_projects_same_owner_attr,
current_public_projects=obj.public_projects_same_owner_attr
)
def get_is_private_extra_info(self, obj):
assert hasattr(obj, "private_projects_same_owner_attr"), "instance must have a private_projects_same_owner_attr attribute"
assert hasattr(obj, "public_projects_same_owner_attr"), "instance must have a public_projects_same_owner_attr attribute"
return services.check_if_project_privacity_can_be_changed(
obj,
current_memberships=self.get_total_memberships(obj),
current_private_projects=obj.private_projects_same_owner_attr,
current_public_projects=obj.public_projects_same_owner_attr
)
def get_max_memberships(self, obj):
return services.get_max_memberships_for_project(obj)
######################################################
# Project Templates
######################################################
class ProjectTemplateSerializer(serializers.LightSerializer):
id = Field()
name = I18NField()
slug = Field()
description = I18NField()
order = Field()
created_date = Field()
modified_date = Field()
default_owner_role = Field()
is_backlog_activated = Field()
is_kanban_activated = Field()
is_wiki_activated = Field()
is_issues_activated = Field()
videoconferences = Field()
videoconferences_extra_data = Field()
default_options = Field()
us_statuses = Field()
points = Field()
task_statuses = Field()
issue_statuses = Field()
issue_types = Field()
priorities = Field()
severities = Field()
roles = Field()