extensions-website/teams/models.py
Oleg Komarov b4aac8b791 Intitial teams support (#147)
This PR adds new features described in #106:

- new `Assign Team` field in extension edit form that can be set only by extension maintainers (original author)
- it is now possible to leave a team
- all team members can edit/upload new versions for team extensions (except for changing the team assignment)
- team extensions are listed in My Extensions for all team members

Co-authored-by: Márton Lente <marton@blender.org>
Reviewed-on: #147
Reviewed-by: Anna Sirota <railla@noreply.localhost>
2024-05-23 19:43:53 +02:00

88 lines
2.9 KiB
Python

import logging
from django.contrib.auth import get_user_model
from django.db import models, transaction
from django.urls import reverse
from common.model_mixins import CreatedModifiedMixin
from constants.base import (
TEAM_ROLE_CHOICES,
TEAM_ROLE_MANAGER,
TEAM_ROLE_MEMBER,
)
import utils
User = get_user_model()
logger = logging.getLogger(__name__)
class Team(CreatedModifiedMixin, models.Model):
slug = models.SlugField(unique=True, null=False, blank=False, editable=True)
name = models.CharField(max_length=128, null=False, blank=False)
users = models.ManyToManyField(User, through='TeamsUsers', related_name='teams')
def clean(self) -> None:
if not self.slug:
self.slug = utils.slugify(self.name)
super().clean()
def __str__(self) -> str:
return f'<Team {self.name}>'
def save(self, *args, **kwargs):
self.clean()
return super().save(*args, **kwargs)
def get_absolute_url(self) -> str:
return reverse('extensions:by-team', kwargs={'team_slug': self.slug})
class TeamsUsers(CreatedModifiedMixin, models.Model):
class Meta:
verbose_name = 'Team member'
verbose_name_plural = 'Team members'
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='team_users')
role = models.SmallIntegerField(default=TEAM_ROLE_MEMBER, choices=TEAM_ROLE_CHOICES)
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='team_users')
@property
def is_manager(self) -> bool:
return self.role == TEAM_ROLE_MANAGER
@transaction.atomic
def delete(self):
# This runs when a user is leaving a team.
# If the user had authored an extension, other team members shouldn't have access to it,
# unless the extension has another maintainer who is still on that team.
for extension in self.user.extensions.filter(team=self.team).all():
# assuming small datasets, not optimizing db access
authors = extension.authors.all()
has_other_authors_from_the_team = False
for author in authors:
if author.pk == self.user.pk:
continue
if self.team in author.teams.all():
has_other_authors_from_the_team = True
break
if not has_other_authors_from_the_team:
extension.team = None
extension.save(update_fields={'team'})
return super().delete()
@property
def may_leave(self) -> bool:
nr_of_managers = TeamsUsers.objects.filter(role=TEAM_ROLE_MANAGER, team=self.team).count()
user_is_manager = (
TeamsUsers.objects.filter(
role=TEAM_ROLE_MANAGER,
team=self.team,
user=self.user,
).first()
is not None
)
if user_is_manager and nr_of_managers < 2:
return False
return True