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:
2026-06-05 08:55:28 +02:00
parent 6c0115d4a2
commit ef05a6523d
28 changed files with 194 additions and 204 deletions

View File

@@ -11,6 +11,6 @@ import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TeamForge.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TeamForge.settings")
application = get_asgi_application() application = get_asgi_application()

View File

@@ -14,7 +14,6 @@ from pathlib import Path
from decouple import Csv, config from decouple import Csv, config
from dj_database_url import parse as db_url from dj_database_url import parse as db_url
from django.template.context_processors import static
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent

View File

@@ -14,10 +14,11 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
urlpatterns = [ urlpatterns = [
path('backend/', include('backend.urls')), path("backend/", include("backend.urls")),
path('admin/', admin.site.urls), path("admin/", admin.site.urls),
] ]

View File

@@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TeamForge.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TeamForge.settings")
application = get_wsgi_application() application = get_wsgi_application()

View File

@@ -1,4 +1,4 @@
from django.urls import include, path from django.urls import path
from .views import MemberAddView, MemberDeleteView, MemberEditView, MemberListView, MemberLoadView from .views import MemberAddView, MemberDeleteView, MemberEditView, MemberListView, MemberLoadView

View File

@@ -4,6 +4,7 @@ from django.http import HttpResponse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django_htmx.http import HttpResponseClientRedirect, HttpResponseClientRefresh from django_htmx.http import HttpResponseClientRedirect, HttpResponseClientRefresh
class HTMXPartialMixin: class HTMXPartialMixin:
"""Mixin that automatically switches to a partial template when the request is made via HTMX.""" """Mixin that automatically switches to a partial template when the request is made via HTMX."""

View File

@@ -3,8 +3,4 @@ from django.urls import include, path
from .views import configuration, index from .views import configuration, index
app_name = "backend" app_name = "backend"
urlpatterns = [ urlpatterns = [path("", index, name="index"), path("members/", include("backend.members.urls")), path("configuration", configuration, name="configuration")]
path("", index, name="index"),
path("members/", include("backend.members.urls")),
path("configuration", configuration, name="configuration")
]

View File

@@ -1,22 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
"""Django's command-line utility for administrative tasks.""" """Django's command-line utility for administrative tasks."""
import os import os
import sys import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TeamForge.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TeamForge.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
raise ImportError( raise ImportError("Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?") from exc
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@@ -5,5 +5,4 @@ class MembersConfig(AppConfig):
name = "members" name = "members"
def ready(self): def ready(self):
# noinspection PyUnusedImports import members.signals # noqa: F401
import members.signals

View File

@@ -8,10 +8,18 @@ class MemberFilter(django_filters.FilterSet):
user__first_name = django_filters.CharFilter(field_name="user__first_name", label=_("First name"), lookup_expr="icontains") user__first_name = django_filters.CharFilter(field_name="user__first_name", label=_("First name"), lookup_expr="icontains")
user__last_name = django_filters.CharFilter(field_name="user__last_name", label=_("Last name"), lookup_expr="icontains") user__last_name = django_filters.CharFilter(field_name="user__last_name", label=_("Last name"), lookup_expr="icontains")
license = django_filters.CharFilter(label=_("License"), lookup_expr="icontains") license = django_filters.CharFilter(label=_("License"), lookup_expr="icontains")
user__is_active = django_filters.TypedChoiceFilter( field_name='user__is_active', label=_("Active?"), initial="true", choices=( ('', 'All users'), ('true', 'Active users'), ('false', 'Inactive users'), ), coerce=lambda x: x.lower() == 'true' ) user__is_active = django_filters.TypedChoiceFilter(
field_name="user__is_active",
label=_("Active?"),
initial="true",
choices=(
("", "All users"),
("true", "Active users"),
("false", "Inactive users"),
),
coerce=lambda x: x.lower() == "true",
)
class Meta: class Meta:
model = Member model = Member
fields = ["user__first_name", "user__last_name", "license", "user__is_active"] fields = ["user__first_name", "user__last_name", "license", "user__is_active"]

View File

@@ -7,7 +7,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("members", "0001_initial"), ("members", "0001_initial"),
] ]

View File

@@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("members", "0002_member_family"), ("members", "0002_member_family"),
] ]
@@ -29,8 +28,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="member", model_name="member",
name="family", name="family",
field=models.ManyToManyField( field=models.ManyToManyField(blank=True, to="members.member", verbose_name="family"),
blank=True, to="members.member", verbose_name="family"
),
), ),
] ]

View File

@@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("members", "0003_member_created_member_updated_alter_member_family"), ("members", "0003_member_created_member_updated_alter_member_family"),
] ]
@@ -14,9 +13,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="member", model_name="member",
name="access_token", name="access_token",
field=models.CharField( field=models.CharField(blank=True, max_length=255, null=True, verbose_name="access token"),
blank=True, max_length=255, null=True, verbose_name="access token"
),
), ),
migrations.AddField( migrations.AddField(
model_name="member", model_name="member",
@@ -37,9 +34,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="member", model_name="member",
name="license", name="license",
field=models.CharField( field=models.CharField(blank=True, max_length=20, null=True, verbose_name="license"),
blank=True, max_length=20, null=True, verbose_name="license"
),
), ),
migrations.AddField( migrations.AddField(
model_name="member", model_name="member",

View File

@@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("members", "0004_member_access_token_member_birthday_and_more"), ("members", "0004_member_access_token_member_birthday_and_more"),
] ]
@@ -17,8 +16,6 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="member", model_name="member",
name="family_members", name="family_members",
field=models.ManyToManyField( field=models.ManyToManyField(blank=True, to="members.member", verbose_name="family members"),
blank=True, to="members.member", verbose_name="family members"
),
), ),
] ]

View File

@@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('members', '0005_remove_member_family_member_family_members'), ("members", "0005_remove_member_family_member_family_members"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='member', model_name="member",
name='notes', name="notes",
field=models.TextField(blank=True, null=True, verbose_name='notes'), field=models.TextField(blank=True, null=True, verbose_name="notes"),
), ),
] ]

View File

@@ -66,14 +66,7 @@ class Member(RulesModel):
else: else:
# First check to see if a user already exists in the system # First check to see if a user already exists in the system
user, created = get_user_model().objects.get_or_create( user, created = get_user_model().objects.get_or_create(username=email, defaults={"first_name": first_name, "last_name": last_name, "email": email})
username=email,
defaults={
"first_name": first_name,
"last_name": last_name,
"email": email
}
)
if not created: if not created:
user.first_name = first_name user.first_name = first_name

View File

@@ -6,4 +6,4 @@ from django.contrib.auth.models import AbstractUser
@rules.predicate @rules.predicate
def is_member_manager(user: Optional[AbstractUser]) -> bool: def is_member_manager(user: Optional[AbstractUser]) -> bool:
return user.has_perm('members.member_manager') return user.has_perm("members.member_manager")

View File

@@ -1,3 +1 @@
from django.contrib import admin
# Register your models here. # Register your models here.

View File

@@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [] dependencies = []

View File

@@ -9,95 +9,94 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('members', '0006_member_notes'), ("members", "0006_member_notes"),
('teams', '0001_initial'), ("teams", "0001_initial"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Team', name="Team",
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("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')), ("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')), ("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')), ("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')), ("logo", models.ImageField(blank=True, null=True, upload_to="teams/logo/", verbose_name="logo")),
('created', models.DateTimeField(auto_now_add=True)), ("created", models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)), ("updated", models.DateTimeField(auto_now=True)),
], ],
options={ options={
'verbose_name': 'team', "verbose_name": "team",
'verbose_name_plural': 'teams', "verbose_name_plural": "teams",
'ordering': ['name'], "ordering": ["name"],
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model), bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name='TeamRole', name="TeamRole",
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("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')), ("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')), ("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')), ("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')), ("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')), ("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)), ("created", models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)), ("updated", models.DateTimeField(auto_now=True)),
], ],
options={ options={
'verbose_name': 'team role', "verbose_name": "team role",
'verbose_name_plural': 'team roles', "verbose_name_plural": "team roles",
'ordering': ['sort_order'], "ordering": ["sort_order"],
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model), bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name='TeamMembership', name="TeamMembership",
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("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')), ("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')), ("captain", models.BooleanField(default=False, verbose_name="captain")),
('alternate_captain', models.BooleanField(default=False, verbose_name='alternate captain')), ("alternate_captain", models.BooleanField(default=False, verbose_name="alternate captain")),
('created', models.DateTimeField(auto_now_add=True)), ("created", models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=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')), ("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')), ("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')), ("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')), ("role", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="team_memberships", to="teams.teamrole", verbose_name="role")),
], ],
options={ options={
'verbose_name': 'team membership', "verbose_name": "team membership",
'verbose_name_plural': 'team memberships', "verbose_name_plural": "team memberships",
'ordering': ['team__name', 'role__sort_order', 'number', 'member__user__last_name', 'member__user__first_name'], "ordering": ["team__name", "role__sort_order", "number", "member__user__last_name", "member__user__first_name"],
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model), bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.AddField( migrations.AddField(
model_name='team', model_name="team",
name='members', name="members",
field=models.ManyToManyField(through='teams.TeamMembership', to='members.member', verbose_name='members'), field=models.ManyToManyField(through="teams.TeamMembership", to="members.member", verbose_name="members"),
), ),
migrations.CreateModel( migrations.CreateModel(
name='TeamPicture', name="TeamPicture",
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ("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')), ("picture", models.ImageField(upload_to=teams.models.team_season_file_path, verbose_name="picture")),
('created', models.DateTimeField(auto_now_add=True)), ("created", models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)), ("modified", models.DateTimeField(auto_now=True)),
('season', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teams.season', verbose_name='season')), ("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')), ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="teams.team", verbose_name="team")),
], ],
options={ options={
'verbose_name': 'team picture', "verbose_name": "team picture",
'verbose_name_plural': 'team pictures', "verbose_name_plural": "team pictures",
'ordering': ['team__name', 'season__start_date'], "ordering": ["team__name", "season__start_date"],
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model), bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.AddConstraint( migrations.AddConstraint(
model_name='teammembership', 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.'), 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."),
), ),
] ]

View File

@@ -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.""" """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}" return f"teams/picture/{instance.team.slug}/{instance.season.start_date.year}/{filename}"
class Season(RulesModel): class Season(RulesModel):
start_date = models.DateField(_("start date")) start_date = models.DateField(_("start date"))
end_date = models.DateField(_("end date")) end_date = models.DateField(_("end date"))
@@ -52,7 +53,6 @@ class Season(RulesModel):
if season is None: if season is None:
raise cls.DoesNotExist(f"No Season covers date {current_date}") raise cls.DoesNotExist(f"No Season covers date {current_date}")
if values_only: if values_only:
return season.date_range return season.date_range
return season return season
@@ -87,6 +87,7 @@ class Season(RulesModel):
return date_value.replace(year=year, month=month, day=day) return date_value.replace(year=year, month=month, day=day)
class TeamRole(RulesModel): class TeamRole(RulesModel):
name = models.CharField(_("name"), max_length=255, unique=True) 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")) abbreviation = models.CharField(_("abbreviation"), max_length=255, unique=True, help_text=_("An abbreviated version of the role name"))
@@ -108,6 +109,7 @@ class TeamRole(RulesModel):
def __str__(self): def __str__(self):
return f"{self.name} ({self.abbreviation})" return f"{self.name} ({self.abbreviation})"
class Team(RulesModel): class Team(RulesModel):
name = models.CharField(_("name"), max_length=255, unique=True) 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")) short_name = models.CharField(_("short name"), max_length=255, unique=True, blank=True, null=True, help_text=_("An optional short name for the team"))
@@ -145,6 +147,7 @@ class Team(RulesModel):
"""Returns the short name of the team, or the name if no short name is set.""" """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 return self.short_name if self.short_name and self.short_name != "" else self.name
class TeamMembership(RulesModel): class TeamMembership(RulesModel):
team = models.ForeignKey(Team, on_delete=models.CASCADE, verbose_name=_("team")) 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")) member = models.ForeignKey("members.Member", on_delete=models.CASCADE, related_name="team_memberships", verbose_name=_("member"))
@@ -170,6 +173,7 @@ class TeamMembership(RulesModel):
def __str__(self): def __str__(self):
return f"{self.team.name} - {self.member.user.get_full_name()} ({self.role.abbreviation})" return f"{self.team.name} - {self.member.user.get_full_name()} ({self.role.abbreviation})"
class TeamPicture(RulesModel): class TeamPicture(RulesModel):
team = models.ForeignKey(Team, on_delete=models.CASCADE, verbose_name=_("team")) team = models.ForeignKey(Team, on_delete=models.CASCADE, verbose_name=_("team"))
season = models.ForeignKey(Season, on_delete=models.CASCADE, verbose_name=_("season")) season = models.ForeignKey(Season, on_delete=models.CASCADE, verbose_name=_("season"))

View File

@@ -4,7 +4,7 @@ import rules
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
if TYPE_CHECKING: if TYPE_CHECKING:
from .models import TeamMembership, Season from .models import TeamMembership
@rules.predicate @rules.predicate

View File

@@ -4,7 +4,6 @@ from django.contrib.auth import get_user_model
from django.test import TestCase from django.test import TestCase
from django.utils import timezone from django.utils import timezone
from members.models import Member
from teams.models import Season, Team, TeamMembership, TeamPicture, TeamRole from teams.models import Season, Team, TeamMembership, TeamPicture, TeamRole
User = get_user_model() User = get_user_model()
@@ -14,6 +13,7 @@ User = get_user_model()
# Helpers # Helpers
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def make_member(email, first="Test", last="User"): def make_member(email, first="Test", last="User"):
user = User.objects.create_user(username=email, email=email, first_name=first, last_name=last) user = User.objects.create_user(username=email, email=email, first_name=first, last_name=last)
return user.member return user.member
@@ -35,6 +35,7 @@ def make_team(name="Test Team"):
# Season # Season
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class SeasonStrTest(TestCase): class SeasonStrTest(TestCase):
def test_str(self): def test_str(self):
season = Season(start_date=datetime.date(2025, 9, 1), end_date=datetime.date(2026, 8, 31)) season = Season(start_date=datetime.date(2025, 9, 1), end_date=datetime.date(2026, 8, 31))
@@ -127,6 +128,7 @@ class SeasonAddMonthsTest(TestCase):
# TeamRole # TeamRole
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class TeamRoleStrTest(TestCase): class TeamRoleStrTest(TestCase):
def test_str(self): def test_str(self):
role = TeamRole(name="Coach", abbreviation="C") role = TeamRole(name="Coach", abbreviation="C")
@@ -143,6 +145,7 @@ class TeamRoleStrTest(TestCase):
# Team # Team
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class TeamStrTest(TestCase): class TeamStrTest(TestCase):
def test_str(self): def test_str(self):
team = Team(name="Red Dragons") team = Team(name="Red Dragons")
@@ -199,6 +202,7 @@ class TeamMemberCountTest(TestCase):
# TeamMembership # TeamMembership
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class TeamMembershipStrTest(TestCase): class TeamMembershipStrTest(TestCase):
def setUp(self): def setUp(self):
today = timezone.now().date() today = timezone.now().date()
@@ -209,15 +213,14 @@ class TeamMembershipStrTest(TestCase):
self.team = make_team("Blue Hawks") self.team = make_team("Blue Hawks")
self.role = make_role("Forward", "F") self.role = make_role("Forward", "F")
self.member = make_member("player@test.com", "John", "Doe") self.member = make_member("player@test.com", "John", "Doe")
self.membership = TeamMembership.objects.create( self.membership = TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, role=self.role)
team=self.team, member=self.member, season=self.season, role=self.role
)
def test_str(self): def test_str(self):
self.assertEqual(str(self.membership), "Blue Hawks - John Doe (F)") self.assertEqual(str(self.membership), "Blue Hawks - John Doe (F)")
def test_unique_number_constraint(self): def test_unique_number_constraint(self):
from django.db import IntegrityError from django.db import IntegrityError
member2 = make_member("player2@test.com", "Jane", "Doe") member2 = make_member("player2@test.com", "Jane", "Doe")
TeamMembership.objects.create(team=self.team, member=member2, season=self.season, role=self.role, number=7) TeamMembership.objects.create(team=self.team, member=member2, season=self.season, role=self.role, number=7)
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
@@ -232,6 +235,7 @@ class TeamMembershipStrTest(TestCase):
# TeamPicture # TeamPicture
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class TeamPictureStrTest(TestCase): class TeamPictureStrTest(TestCase):
def setUp(self): def setUp(self):
self.season = Season.objects.create( self.season = Season.objects.create(

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class ThemeConfig(AppConfig): class ThemeConfig(AppConfig):
name = 'theme' name = "theme"

View File

@@ -5,13 +5,15 @@ from django import template
register = template.Library() register = template.Library()
def calculate_brightness(background_color: dict) -> float: def calculate_brightness(background_color: dict) -> float:
"""Calculates the brightness of a background image.""" """Calculates the brightness of a background image."""
r_coefficient = 0.241 r_coefficient = 0.241
g_coefficient = 0.691 g_coefficient = 0.691
b_coefficient = 0.068 b_coefficient = 0.068
return sqrt(r_coefficient ** 2 * background_color["R"] + g_coefficient ** 2 * background_color["G"] + b_coefficient ** 2 * background_color["B"]) * 100 return sqrt(r_coefficient**2 * background_color["R"] + g_coefficient**2 * background_color["G"] + b_coefficient**2 * background_color["B"]) * 100
def foreground(background_color: dict) -> dict: def foreground(background_color: dict) -> dict:
"""Calculates the foreground color based on the background.""" """Calculates the foreground color based on the background."""
@@ -20,6 +22,7 @@ def foreground(background_color: dict) -> dict:
return black if calculate_brightness(background_color) > 210 else white return black if calculate_brightness(background_color) > 210 else white
def background(text: str) -> dict: def background(text: str) -> dict:
"""Calculates the background color based on the text.""" """Calculates the background color based on the text."""
hash_value = md5(text.encode("utf-8")).hexdigest() hash_value = md5(text.encode("utf-8")).hexdigest()
@@ -28,6 +31,7 @@ def background(text: str) -> dict:
return {"R": background_color[0], "G": background_color[1], "B": background_color[2]} return {"R": background_color[0], "G": background_color[1], "B": background_color[2]}
@register.inclusion_tag("templatetags/avatar.html") @register.inclusion_tag("templatetags/avatar.html")
def avatar(first_name: str = "", last_name: str = "", initials: str = "", width: str = "md", button: bool = False) -> dict: def avatar(first_name: str = "", last_name: str = "", initials: str = "", width: str = "md", button: bool = False) -> dict:
if initials: if initials:

View File

@@ -4,6 +4,7 @@ from typing import Optional
register = template.Library() register = template.Library()
@register.inclusion_tag("templatetags/field.html") @register.inclusion_tag("templatetags/field.html")
def form_field(field: BoundField, label: Optional[str] = None, help_text: Optional[str] = None, show_label: bool = True, show_help_text: bool = True, show_placeholder: bool = True, show_as_toggle: bool = False, size: str = "full") -> dict: def form_field(field: BoundField, label: Optional[str] = None, help_text: Optional[str] = None, show_label: bool = True, show_help_text: bool = True, show_placeholder: bool = True, show_as_toggle: bool = False, size: str = "full") -> dict:
if label is not None: if label is not None:

View File

@@ -5,6 +5,7 @@ from django.http import HttpRequest
register = template.Library() register = template.Library()
@register.simple_tag @register.simple_tag
def url_replace(request: HttpRequest, field: str, value: str | int, default_field: Optional[str] = None, default_value: Optional[str | int] = None) -> str: def url_replace(request: HttpRequest, field: str, value: str | int, default_field: Optional[str] = None, default_value: Optional[str | int] = None) -> str:
"""Updates the given field in the GET parameters with the supplied field. If it does not exist, the field is added.""" """Updates the given field in the GET parameters with the supplied field. If it does not exist, the field is added."""