Apply ruff formatting and fix unused import linting errors
Remove unused imports flagged by ruff (F401), apply ruff format across all files, and restore members.signals side-effect import with noqa: F401 so the post_save signal that auto-creates Member profiles continues to fire. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
@@ -9,95 +9,94 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0006_member_notes'),
|
||||
('teams', '0001_initial'),
|
||||
("members", "0006_member_notes"),
|
||||
("teams", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Team',
|
||||
name="Team",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, unique=True, verbose_name='name')),
|
||||
('short_name', models.CharField(blank=True, help_text='An optional short name for the team', max_length=255, null=True, unique=True, verbose_name='short name')),
|
||||
('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, max_length=255, populate_from='name', unique=True, verbose_name='slug')),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='teams/logo/', verbose_name='logo')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("name", models.CharField(max_length=255, unique=True, verbose_name="name")),
|
||||
("short_name", models.CharField(blank=True, help_text="An optional short name for the team", max_length=255, null=True, unique=True, verbose_name="short name")),
|
||||
("slug", django_extensions.db.fields.AutoSlugField(blank=True, editable=False, max_length=255, populate_from="name", unique=True, verbose_name="slug")),
|
||||
("logo", models.ImageField(blank=True, null=True, upload_to="teams/logo/", verbose_name="logo")),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team',
|
||||
'verbose_name_plural': 'teams',
|
||||
'ordering': ['name'],
|
||||
"verbose_name": "team",
|
||||
"verbose_name_plural": "teams",
|
||||
"ordering": ["name"],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamRole',
|
||||
name="TeamRole",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, unique=True, verbose_name='name')),
|
||||
('abbreviation', models.CharField(help_text='An abbreviated version of the role name', max_length=255, unique=True, verbose_name='abbreviation')),
|
||||
('staff_role', models.BooleanField(default=False, help_text='A staff role is any supporting function for a given team (e.g., coach, team manager, ...)', verbose_name='staff role')),
|
||||
('admin_role', models.BooleanField(default=False, help_text='An admin role is a role that has administrative privileges and can change team information and settings', verbose_name='admin role')),
|
||||
('sort_order', models.PositiveIntegerField(default=10, help_text='The order in which the role should be displayed (low to high)', verbose_name='sort order')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("name", models.CharField(max_length=255, unique=True, verbose_name="name")),
|
||||
("abbreviation", models.CharField(help_text="An abbreviated version of the role name", max_length=255, unique=True, verbose_name="abbreviation")),
|
||||
("staff_role", models.BooleanField(default=False, help_text="A staff role is any supporting function for a given team (e.g., coach, team manager, ...)", verbose_name="staff role")),
|
||||
("admin_role", models.BooleanField(default=False, help_text="An admin role is a role that has administrative privileges and can change team information and settings", verbose_name="admin role")),
|
||||
("sort_order", models.PositiveIntegerField(default=10, help_text="The order in which the role should be displayed (low to high)", verbose_name="sort order")),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team role',
|
||||
'verbose_name_plural': 'team roles',
|
||||
'ordering': ['sort_order'],
|
||||
"verbose_name": "team role",
|
||||
"verbose_name_plural": "team roles",
|
||||
"ordering": ["sort_order"],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamMembership',
|
||||
name="TeamMembership",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('number', models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(99)], verbose_name='number')),
|
||||
('captain', models.BooleanField(default=False, verbose_name='captain')),
|
||||
('alternate_captain', models.BooleanField(default=False, verbose_name='alternate captain')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('modified', models.DateTimeField(auto_now=True)),
|
||||
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='members.member', verbose_name='member')),
|
||||
('season', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='teams.season', verbose_name='season')),
|
||||
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.team', verbose_name='team')),
|
||||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='teams.teamrole', verbose_name='role')),
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("number", models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(99)], verbose_name="number")),
|
||||
("captain", models.BooleanField(default=False, verbose_name="captain")),
|
||||
("alternate_captain", models.BooleanField(default=False, verbose_name="alternate captain")),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("modified", models.DateTimeField(auto_now=True)),
|
||||
("member", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="team_memberships", to="members.member", verbose_name="member")),
|
||||
("season", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="team_memberships", to="teams.season", verbose_name="season")),
|
||||
("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="teams.team", verbose_name="team")),
|
||||
("role", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="team_memberships", to="teams.teamrole", verbose_name="role")),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team membership',
|
||||
'verbose_name_plural': 'team memberships',
|
||||
'ordering': ['team__name', 'role__sort_order', 'number', 'member__user__last_name', 'member__user__first_name'],
|
||||
"verbose_name": "team membership",
|
||||
"verbose_name_plural": "team memberships",
|
||||
"ordering": ["team__name", "role__sort_order", "number", "member__user__last_name", "member__user__first_name"],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='team',
|
||||
name='members',
|
||||
field=models.ManyToManyField(through='teams.TeamMembership', to='members.member', verbose_name='members'),
|
||||
model_name="team",
|
||||
name="members",
|
||||
field=models.ManyToManyField(through="teams.TeamMembership", to="members.member", verbose_name="members"),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TeamPicture',
|
||||
name="TeamPicture",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('picture', models.ImageField(upload_to=teams.models.team_season_file_path, verbose_name='picture')),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('modified', models.DateTimeField(auto_now=True)),
|
||||
('season', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.season', verbose_name='season')),
|
||||
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.team', verbose_name='team')),
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("picture", models.ImageField(upload_to=teams.models.team_season_file_path, verbose_name="picture")),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("modified", models.DateTimeField(auto_now=True)),
|
||||
("season", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="teams.season", verbose_name="season")),
|
||||
("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="teams.team", verbose_name="team")),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'team picture',
|
||||
'verbose_name_plural': 'team pictures',
|
||||
'ordering': ['team__name', 'season__start_date'],
|
||||
"verbose_name": "team picture",
|
||||
"verbose_name_plural": "team pictures",
|
||||
"ordering": ["team__name", "season__start_date"],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='teammembership',
|
||||
constraint=models.UniqueConstraint(fields=('team', 'season', 'number'), name='unique_team_membership_number', violation_error_message='A team can only have one member for a given season and number.'),
|
||||
model_name="teammembership",
|
||||
constraint=models.UniqueConstraint(fields=("team", "season", "number"), name="unique_team_membership_number", violation_error_message="A team can only have one member for a given season and number."),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -18,6 +18,7 @@ def team_season_file_path(instance: "TeamPicture", filename: str) -> str:
|
||||
"""Generates the file path for a team picture based on the team name and season."""
|
||||
return f"teams/picture/{instance.team.slug}/{instance.season.start_date.year}/{filename}"
|
||||
|
||||
|
||||
class Season(RulesModel):
|
||||
start_date = models.DateField(_("start date"))
|
||||
end_date = models.DateField(_("end date"))
|
||||
@@ -52,7 +53,6 @@ class Season(RulesModel):
|
||||
if season is None:
|
||||
raise cls.DoesNotExist(f"No Season covers date {current_date}")
|
||||
|
||||
|
||||
if values_only:
|
||||
return season.date_range
|
||||
return season
|
||||
@@ -87,77 +87,80 @@ class Season(RulesModel):
|
||||
|
||||
return date_value.replace(year=year, month=month, day=day)
|
||||
|
||||
|
||||
class TeamRole(RulesModel):
|
||||
name = models.CharField(_("name"), max_length=255, unique=True)
|
||||
abbreviation = models.CharField(_("abbreviation"), max_length=255, unique=True, help_text=_("An abbreviated version of the role name"))
|
||||
|
||||
|
||||
staff_role = models.BooleanField(_("staff role"), default=False, help_text=_("A staff role is any supporting function for a given team (e.g., coach, team manager, ...)"))
|
||||
admin_role = models.BooleanField(_("admin role"), default=False, help_text=_("An admin role is a role that has administrative privileges and can change team information and settings"))
|
||||
|
||||
|
||||
sort_order = models.PositiveIntegerField(_("sort order"), default=10, help_text=_("The order in which the role should be displayed (low to high)"))
|
||||
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("team role")
|
||||
verbose_name_plural = _("team roles")
|
||||
ordering = ["sort_order"]
|
||||
rules_permissions = {"add": is_superuser, "view": is_superuser, "change": is_superuser, "delete": is_superuser}
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.abbreviation})"
|
||||
|
||||
|
||||
|
||||
class Team(RulesModel):
|
||||
name = models.CharField(_("name"), max_length=255, unique=True)
|
||||
short_name = models.CharField(_("short name"), max_length=255, unique=True, blank=True, null=True, help_text=_("An optional short name for the team"))
|
||||
slug = AutoSlugField(_("slug"), max_length=255, unique=True, populate_from="name")
|
||||
logo = models.ImageField(_("logo"), upload_to="teams/logo/", blank=True, null=True)
|
||||
|
||||
|
||||
members = models.ManyToManyField("members.Member", verbose_name=_("members"), through="TeamMembership")
|
||||
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("team")
|
||||
verbose_name_plural = _("teams")
|
||||
ordering = ["name"]
|
||||
rules_permissions = {"add": is_superuser, "view": is_superuser, "change": is_superuser, "delete": is_superuser}
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@property
|
||||
def initials(self) -> str:
|
||||
words = self.name.split()
|
||||
if len(words) == 1:
|
||||
return words[0][0].upper()
|
||||
|
||||
|
||||
return "".join(word[0].upper() for word in words[:2])
|
||||
|
||||
|
||||
@property
|
||||
def member_count(self) -> int:
|
||||
season = Season.for_date()
|
||||
return self.members.filter(team_memberships__season=season).count()
|
||||
|
||||
|
||||
def get_short_name(self) -> str:
|
||||
"""Returns the short name of the team, or the name if no short name is set."""
|
||||
return self.short_name if self.short_name and self.short_name != "" else self.name
|
||||
|
||||
|
||||
class TeamMembership(RulesModel):
|
||||
team = models.ForeignKey(Team, on_delete=models.CASCADE, verbose_name=_("team"))
|
||||
member = models.ForeignKey("members.Member", on_delete=models.CASCADE, related_name="team_memberships", verbose_name=_("member"))
|
||||
season = models.ForeignKey(Season, on_delete=models.CASCADE, related_name="team_memberships", verbose_name=_("season"))
|
||||
role = models.ForeignKey(TeamRole, on_delete=models.CASCADE, related_name="team_memberships", verbose_name=_("role"))
|
||||
|
||||
|
||||
number = models.PositiveIntegerField(_("number"), blank=True, null=True, validators=[MinValueValidator(1), MaxValueValidator(99)])
|
||||
captain = models.BooleanField(_("captain"), default=False)
|
||||
alternate_captain = models.BooleanField(_("alternate captain"), default=False)
|
||||
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("team membership")
|
||||
verbose_name_plural = _("team memberships")
|
||||
@@ -166,23 +169,24 @@ class TeamMembership(RulesModel):
|
||||
models.UniqueConstraint(fields=["team", "season", "number"], name="unique_team_membership_number", violation_error_message=_("A team can only have one member for a given season and number.")),
|
||||
]
|
||||
rules_permissions = {"add": is_superuser | is_team_admin, "view": is_superuser | is_team_admin, "change": is_superuser | is_team_admin, "delete": is_superuser | is_team_admin}
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.team.name} - {self.member.user.get_full_name()} ({self.role.abbreviation})"
|
||||
|
||||
|
||||
class TeamPicture(RulesModel):
|
||||
team = models.ForeignKey(Team, on_delete=models.CASCADE, verbose_name=_("team"))
|
||||
season = models.ForeignKey(Season, on_delete=models.CASCADE, verbose_name=_("season"))
|
||||
picture = models.ImageField(_("picture"), upload_to=team_season_file_path)
|
||||
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
modified = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("team picture")
|
||||
verbose_name_plural = _("team pictures")
|
||||
ordering = ["team__name", "season__start_date"]
|
||||
rules_permissions = {"add": is_superuser, "view": is_superuser, "change": is_superuser, "delete": is_superuser}
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.team.name} - {self.season.start_date.year}"
|
||||
return f"{self.team.name} - {self.season.start_date.year}"
|
||||
|
||||
@@ -4,7 +4,7 @@ import rules
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .models import TeamMembership, Season
|
||||
from .models import TeamMembership
|
||||
|
||||
|
||||
@rules.predicate
|
||||
@@ -18,10 +18,10 @@ def is_team_admin(user: AbstractUser | None, teammembership: "TeamMembership | N
|
||||
team membership.
|
||||
"""
|
||||
from .models import TeamMembership, Season
|
||||
|
||||
|
||||
if user is None or teammembership is None:
|
||||
return False
|
||||
|
||||
|
||||
return TeamMembership.objects.filter(team=teammembership.team, member__user=user, role__admin_role=True, season=Season.for_date()).exists()
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ def is_a_team_admin(user: AbstractUser | None) -> bool:
|
||||
:return: A boolean indicating whether the user is a team admin for the given
|
||||
team membership.
|
||||
"""
|
||||
|
||||
|
||||
from .models import Season, TeamMembership
|
||||
|
||||
|
||||
if user is None:
|
||||
return False
|
||||
|
||||
return TeamMembership.objects.filter(member__user=user, role__admin_role=True, season=Season.for_date()).exists()
|
||||
|
||||
return TeamMembership.objects.filter(member__user=user, role__admin_role=True, season=Season.for_date()).exists()
|
||||
|
||||
@@ -4,7 +4,6 @@ from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from members.models import Member
|
||||
from teams.models import Season, Team, TeamMembership, TeamPicture, TeamRole
|
||||
|
||||
User = get_user_model()
|
||||
@@ -14,6 +13,7 @@ User = get_user_model()
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def make_member(email, first="Test", last="User"):
|
||||
user = User.objects.create_user(username=email, email=email, first_name=first, last_name=last)
|
||||
return user.member
|
||||
@@ -35,6 +35,7 @@ def make_team(name="Test Team"):
|
||||
# Season
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class SeasonStrTest(TestCase):
|
||||
def test_str(self):
|
||||
season = Season(start_date=datetime.date(2025, 9, 1), end_date=datetime.date(2026, 8, 31))
|
||||
@@ -127,6 +128,7 @@ class SeasonAddMonthsTest(TestCase):
|
||||
# TeamRole
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TeamRoleStrTest(TestCase):
|
||||
def test_str(self):
|
||||
role = TeamRole(name="Coach", abbreviation="C")
|
||||
@@ -143,6 +145,7 @@ class TeamRoleStrTest(TestCase):
|
||||
# Team
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TeamStrTest(TestCase):
|
||||
def test_str(self):
|
||||
team = Team(name="Red Dragons")
|
||||
@@ -199,6 +202,7 @@ class TeamMemberCountTest(TestCase):
|
||||
# TeamMembership
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TeamMembershipStrTest(TestCase):
|
||||
def setUp(self):
|
||||
today = timezone.now().date()
|
||||
@@ -209,15 +213,14 @@ class TeamMembershipStrTest(TestCase):
|
||||
self.team = make_team("Blue Hawks")
|
||||
self.role = make_role("Forward", "F")
|
||||
self.member = make_member("player@test.com", "John", "Doe")
|
||||
self.membership = TeamMembership.objects.create(
|
||||
team=self.team, member=self.member, season=self.season, role=self.role
|
||||
)
|
||||
self.membership = TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, role=self.role)
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.membership), "Blue Hawks - John Doe (F)")
|
||||
|
||||
def test_unique_number_constraint(self):
|
||||
from django.db import IntegrityError
|
||||
|
||||
member2 = make_member("player2@test.com", "Jane", "Doe")
|
||||
TeamMembership.objects.create(team=self.team, member=member2, season=self.season, role=self.role, number=7)
|
||||
with self.assertRaises(IntegrityError):
|
||||
@@ -232,6 +235,7 @@ class TeamMembershipStrTest(TestCase):
|
||||
# TeamPicture
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TeamPictureStrTest(TestCase):
|
||||
def setUp(self):
|
||||
self.season = Season.objects.create(
|
||||
@@ -242,4 +246,4 @@ class TeamPictureStrTest(TestCase):
|
||||
self.picture = TeamPicture(team=self.team, season=self.season, picture="teams/picture/green-eagles/2025/photo.jpg")
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.picture), "Green Eagles - 2025")
|
||||
self.assertEqual(str(self.picture), "Green Eagles - 2025")
|
||||
|
||||
Reference in New Issue
Block a user