[Backport] Fixing contacts and stats endpoints for users API
parent
56a347e608
commit
29b75dc78a
|
@ -111,7 +111,7 @@ class UsersViewSet(ModelCrudViewSet):
|
||||||
def stats(self, request, *args, **kwargs):
|
def stats(self, request, *args, **kwargs):
|
||||||
user = get_object_or_404(models.User, **kwargs)
|
user = get_object_or_404(models.User, **kwargs)
|
||||||
self.check_permissions(request, "stats", user)
|
self.check_permissions(request, "stats", user)
|
||||||
return response.Ok(services.get_stats_for_user(user))
|
return response.Ok(services.get_stats_for_user(user, request.user))
|
||||||
|
|
||||||
@list_route(methods=["POST"])
|
@list_route(methods=["POST"])
|
||||||
def password_recovery(self, request, pk=None):
|
def password_recovery(self, request, pk=None):
|
||||||
|
|
|
@ -15,30 +15,14 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from taiga.base.filters import PermissionBasedFilterBackend
|
from taiga.base.filters import PermissionBasedFilterBackend
|
||||||
|
from . import services
|
||||||
|
|
||||||
class ContactsFilterBackend(PermissionBasedFilterBackend):
|
class ContactsFilterBackend(PermissionBasedFilterBackend):
|
||||||
permission = "view_project"
|
|
||||||
|
|
||||||
def filter_queryset(self, user, request, queryset, view):
|
def filter_queryset(self, user, request, queryset, view):
|
||||||
qs = queryset.filter(is_active=True)
|
qs = queryset.filter(is_active=True)
|
||||||
Membership = apps.get_model('projects', 'Membership')
|
project_ids = services.get_visible_project_ids(user, request.user)
|
||||||
memberships_qs = Membership.objects.filter(user=user)
|
qs = qs.filter(memberships__project_id__in=project_ids)
|
||||||
|
|
||||||
# Authenticated
|
|
||||||
if request.user.is_authenticated():
|
|
||||||
# if super user we don't need to filter anything
|
|
||||||
if not request.user.is_superuser:
|
|
||||||
memberships_qs = memberships_qs.filter(Q(role__permissions__contains=[self.permission]) |
|
|
||||||
Q(is_owner=True))
|
|
||||||
|
|
||||||
# Anonymous
|
|
||||||
else:
|
|
||||||
memberships_qs = memberships_qs.filter(project__anon_permissions__contains=[self.permission])
|
|
||||||
|
|
||||||
projects_list = [membership.project_id for membership in memberships_qs]
|
|
||||||
qs = qs.filter(memberships__project_id__in=projects_list)
|
|
||||||
qs = qs.exclude(id=user.id)
|
qs = qs.exclude(id=user.id)
|
||||||
return qs.distinct()
|
return qs.distinct()
|
||||||
|
|
|
@ -89,21 +89,51 @@ def get_big_photo_or_gravatar_url(user):
|
||||||
else:
|
else:
|
||||||
return get_gravatar_url(user.email, size=settings.DEFAULT_BIG_AVATAR_SIZE)
|
return get_gravatar_url(user.email, size=settings.DEFAULT_BIG_AVATAR_SIZE)
|
||||||
|
|
||||||
def get_stats_for_user(user):
|
|
||||||
"""Get the user stats"""
|
|
||||||
|
|
||||||
project_ids = user.memberships.values_list("project__id", flat=True).distinct()
|
def get_visible_project_ids(from_user, by_user):
|
||||||
total_num_projects = project_ids.count()
|
"""Calculate the project_ids from one user visible by another"""
|
||||||
roles = [_(r) for r in user.memberships.values_list("role__name", flat=True)]
|
required_permissions = ["view_project"]
|
||||||
|
#Or condition for membership filtering, the basic one is the access to projects allowing anonymous visualization
|
||||||
|
member_perm_conditions = Q(project__anon_permissions__contains=required_permissions)
|
||||||
|
|
||||||
|
# Authenticated
|
||||||
|
if by_user.is_authenticated():
|
||||||
|
#Calculating the projects wich from_user user is member
|
||||||
|
by_user_project_ids = by_user.memberships.values_list("project__id", flat=True)
|
||||||
|
#Adding to the condition two OR situations:
|
||||||
|
#- The from user has a role that allows access to the project
|
||||||
|
#- The to user is the owner
|
||||||
|
member_perm_conditions |= \
|
||||||
|
Q(project__id__in=by_user_project_ids, role__permissions__contains=required_permissions) |\
|
||||||
|
Q(project__id__in=by_user_project_ids, is_owner=True)
|
||||||
|
|
||||||
|
Membership = apps.get_model('projects', 'Membership')
|
||||||
|
#Calculating the user memberships adding the permission filter for the by user
|
||||||
|
memberships_qs = Membership.objects.filter(member_perm_conditions, user=from_user)
|
||||||
|
project_ids = memberships_qs.values_list("project__id", flat=True)
|
||||||
|
return project_ids
|
||||||
|
|
||||||
|
|
||||||
|
def get_stats_for_user(from_user, by_user):
|
||||||
|
"""Get the user stats"""
|
||||||
|
project_ids = get_visible_project_ids(from_user, by_user)
|
||||||
|
|
||||||
|
total_num_projects = len(project_ids)
|
||||||
|
|
||||||
|
roles = [_(r) for r in from_user.memberships.filter(project__id__in=project_ids).values_list("role__name", flat=True)]
|
||||||
roles = list(set(roles))
|
roles = list(set(roles))
|
||||||
|
|
||||||
User = apps.get_model('users', 'User')
|
User = apps.get_model('users', 'User')
|
||||||
total_num_contacts = User.objects.filter(memberships__project__id__in=project_ids)\
|
total_num_contacts = User.objects.filter(memberships__project__id__in=project_ids)\
|
||||||
.exclude(id=user.id)\
|
.exclude(id=from_user.id)\
|
||||||
.distinct()\
|
.distinct()\
|
||||||
.count()
|
.count()
|
||||||
|
|
||||||
UserStory = apps.get_model('userstories', 'UserStory')
|
UserStory = apps.get_model('userstories', 'UserStory')
|
||||||
total_num_closed_userstories = UserStory.objects.filter(is_closed=True, assigned_to=user).count()
|
total_num_closed_userstories = UserStory.objects.filter(
|
||||||
|
is_closed=True,
|
||||||
|
project__id__in=project_ids,
|
||||||
|
assigned_to=from_user).count()
|
||||||
|
|
||||||
project_stats = {
|
project_stats = {
|
||||||
'total_num_projects': total_num_projects,
|
'total_num_projects': total_num_projects,
|
||||||
|
|
|
@ -57,13 +57,6 @@ def test_add_to_objects_timeline():
|
||||||
service.push_to_timeline(None, project, "test")
|
service.push_to_timeline(None, project, "test")
|
||||||
|
|
||||||
|
|
||||||
def test_modify_created_timeline_entry():
|
|
||||||
timeline = Timeline()
|
|
||||||
timeline.pk = 3
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
timeline.save()
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_impl_key_from_model():
|
def test_get_impl_key_from_model():
|
||||||
assert service._get_impl_key_from_model(Timeline, "test") == "timeline.timeline.test"
|
assert service._get_impl_key_from_model(Timeline, "test") == "timeline.timeline.test"
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
|
|
Loading…
Reference in New Issue