diff --git a/sync/admin.py b/sync/admin.py index 0b73192..b2cc6fd 100644 --- a/sync/admin.py +++ b/sync/admin.py @@ -3,13 +3,22 @@ from .models import BaseModel, ModelLog, DataAccess from django.utils import timezone -class AutoUpdateAdmin(admin.ModelAdmin): +class SyncedObjectAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): if isinstance(obj, BaseModel): obj.last_updated_by = request.user obj.last_update = timezone.now() super().save_model(request, obj, form, change) + def delete_model(self, request, obj): + obj.delete_dependencies() # object must implement delete_dependencies(self) + obj.delete() + + def delete_queryset(self, request, queryset): + for obj in queryset: + obj.delete_dependencies() # object must implement delete_dependencies(self) + queryset.delete() + class ModelLogAdmin(admin.ModelAdmin): list_display = ['get_users', 'date', 'operation', 'model_id', 'model_name'] list_filter = ['users'] @@ -19,7 +28,7 @@ class ModelLogAdmin(admin.ModelAdmin): def get_users(self, obj): return ", ".join([str(item) for item in obj.users.all()]) -class DataAccessAdmin(AutoUpdateAdmin): +class DataAccessAdmin(SyncedObjectAdmin): list_display = ['related_user', 'get_shared_users', 'model_name', 'model_id'] list_filter = ['related_user', 'shared_with'] ordering = ['-granted_at'] diff --git a/tournaments/admin.py b/tournaments/admin.py index 0ca5702..da58ced 100644 --- a/tournaments/admin.py +++ b/tournaments/admin.py @@ -7,7 +7,7 @@ from .models import Club, TeamScore, Tournament, CustomUser, Event, Round, Group from .forms import CustomUserCreationForm, CustomUserChangeForm from .filters import TeamScoreTournamentListFilter, MatchTournamentListFilter, SimpleTournamentListFilter, MatchTypeListFilter, SimpleIndexListFilter -from sync.admin import AutoUpdateAdmin +from sync.admin import SyncedObjectAdmin class CustomUserAdmin(UserAdmin): form = CustomUserChangeForm @@ -38,76 +38,76 @@ class CustomUserAdmin(UserAdmin): obj.last_update = timezone.now() super().save_model(request, obj, form, change) -class EventAdmin(admin.ModelAdmin): +class EventAdmin(SyncedObjectAdmin): list_display = ['creation_date', 'name', 'club', 'creator', 'tenup_id'] list_filter = ['creator', 'tenup_id'] ordering = ['-creation_date'] -class TournamentAdmin(admin.ModelAdmin): +class TournamentAdmin(SyncedObjectAdmin): list_display = ['display_name', 'event', 'is_private', 'start_date', 'payment', 'creator'] list_filter = ['is_deleted', 'event__creator'] ordering = ['-start_date'] -class TeamRegistrationAdmin(AutoUpdateAdmin): +class TeamRegistrationAdmin(SyncedObjectAdmin): list_display = ['player_names', 'group_stage_position', 'name', 'tournament'] list_filter = [SimpleTournamentListFilter] search_fields = ['id'] -class TeamScoreAdmin(AutoUpdateAdmin): +class TeamScoreAdmin(SyncedObjectAdmin): list_display = ['team_registration', 'score', 'walk_out', 'match'] list_filter = [TeamScoreTournamentListFilter] -class RoundAdmin(AutoUpdateAdmin): +class RoundAdmin(SyncedObjectAdmin): list_display = ['tournament', 'name', 'index', 'parent'] list_filter = [SimpleTournamentListFilter, SimpleIndexListFilter] search_fields = ['id'] ordering = ['parent'] -class PlayerRegistrationAdmin(AutoUpdateAdmin): +class PlayerRegistrationAdmin(SyncedObjectAdmin): list_display = ['first_name', 'last_name', 'licence_id', 'rank'] search_fields = ('first_name', 'last_name') list_filter = [TeamScoreTournamentListFilter] ordering = ['last_name', 'first_name'] -class MatchAdmin(admin.ModelAdmin): +class MatchAdmin(SyncedObjectAdmin): list_display = ['__str__', 'round', 'group_stage', 'start_date', 'end_date', 'index'] list_filter = [MatchTypeListFilter, MatchTournamentListFilter, SimpleIndexListFilter] ordering = ['-group_stage', 'round'] -class GroupStageAdmin(admin.ModelAdmin): +class GroupStageAdmin(SyncedObjectAdmin): list_display = ['tournament', 'start_date', 'index'] list_filter = [SimpleTournamentListFilter] ordering = ['-start_date', 'index'] -class ClubAdmin(AutoUpdateAdmin): +class ClubAdmin(SyncedObjectAdmin): list_display = ['name', 'acronym', 'city', 'creator', 'events_count', 'broadcast_code'] search_fields = ('name', 'acronym', 'city') ordering = ['creator'] -class PurchaseAdmin(admin.ModelAdmin): +class PurchaseAdmin(SyncedObjectAdmin): list_display = ['id', 'user', 'product_id', 'quantity', 'purchase_date', 'revocation_date', 'expiration_date'] list_filter = ['user'] ordering = ['-purchase_date'] -class CourtAdmin(AutoUpdateAdmin): +class CourtAdmin(SyncedObjectAdmin): list_display = ['index', 'name', 'club'] ordering = ['club'] -class DateIntervalAdmin(AutoUpdateAdmin): +class DateIntervalAdmin(SyncedObjectAdmin): list_display = ['court_index', 'event'] -class FailedApiCallAdmin(AutoUpdateAdmin): +class FailedApiCallAdmin(admin.ModelAdmin): list_display = ['date', 'user', 'type', 'error'] list_filter = ['user'] -class LogAdmin(AutoUpdateAdmin): +class LogAdmin(admin.ModelAdmin): list_display = ['date', 'user', 'message'] list_filter = ['user'] -class DeviceTokenAdmin(AutoUpdateAdmin): +class DeviceTokenAdmin(admin.ModelAdmin): list_display = ['user', 'value'] -class DrawLogAdmin(admin.ModelAdmin): +class DrawLogAdmin(SyncedObjectAdmin): list_display = ['tournament', 'draw_date', 'draw_seed', 'draw_match_index', 'draw_team_position'] list_filter = [SimpleTournamentListFilter] ordering = ['draw_date'] diff --git a/tournaments/migrations/0099_alter_court_club_alter_dateinterval_event_and_more.py b/tournaments/migrations/0099_alter_court_club_alter_dateinterval_event_and_more.py new file mode 100644 index 0000000..73ea45e --- /dev/null +++ b/tournaments/migrations/0099_alter_court_club_alter_dateinterval_event_and_more.py @@ -0,0 +1,79 @@ +# Generated by Django 5.1 on 2025-01-28 14:54 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0098_alter_drawlog_tournament'), + ] + + operations = [ + migrations.AlterField( + model_name='court', + name='club', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='courts', to='tournaments.club'), + ), + migrations.AlterField( + model_name='dateinterval', + name='event', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='date_intervals', to='tournaments.event'), + ), + migrations.AlterField( + model_name='drawlog', + name='tournament', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='draw_logs', to='tournaments.tournament'), + ), + migrations.AlterField( + model_name='groupstage', + name='tournament', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='group_stages', to='tournaments.tournament'), + ), + migrations.AlterField( + model_name='match', + name='group_stage', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='matches', to='tournaments.groupstage'), + ), + migrations.AlterField( + model_name='match', + name='round', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='matches', to='tournaments.round'), + ), + migrations.AlterField( + model_name='playerregistration', + name='team_registration', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='player_registrations', to='tournaments.teamregistration'), + ), + migrations.AlterField( + model_name='round', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='tournaments.round'), + ), + migrations.AlterField( + model_name='round', + name='tournament', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rounds', to='tournaments.tournament'), + ), + migrations.AlterField( + model_name='teamregistration', + name='tournament', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_registrations', to='tournaments.tournament'), + ), + migrations.AlterField( + model_name='teamscore', + name='match', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_scores', to='tournaments.match'), + ), + migrations.AlterField( + model_name='teamscore', + name='team_registration', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_scores', to='tournaments.teamregistration'), + ), + migrations.AlterField( + model_name='tournament', + name='event', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tournaments', to='tournaments.event'), + ), + ] diff --git a/tournaments/models/club.py b/tournaments/models/club.py index 1268efd..0c71209 100644 --- a/tournaments/models/club.py +++ b/tournaments/models/club.py @@ -25,6 +25,11 @@ class Club(BaseModel): court_count = models.IntegerField(default=2) broadcast_code = models.CharField(max_length=10, null=True, blank=True, unique=True) + def delete_dependencies(self): + for court in self.courts.all(): + # court.delete_dependencies() + court.delete() + def __str__(self): return self.name diff --git a/tournaments/models/court.py b/tournaments/models/court.py index e601e6a..1fcbe52 100644 --- a/tournaments/models/court.py +++ b/tournaments/models/court.py @@ -10,6 +10,9 @@ class Court(BaseModel): exit_allowed = models.BooleanField(default=False) indoor = models.BooleanField(default=False) + def delete_dependencies(self): + pass + def __str__(self): if self.name: return self.name diff --git a/tournaments/models/date_interval.py b/tournaments/models/date_interval.py index c06f573..a54fb4a 100644 --- a/tournaments/models/date_interval.py +++ b/tournaments/models/date_interval.py @@ -8,3 +8,6 @@ class DateInterval(BaseModel): court_index = models.IntegerField() start_date = models.DateTimeField() end_date = models.DateTimeField() + + def delete_dependencies(self): + pass diff --git a/tournaments/models/draw_log.py b/tournaments/models/draw_log.py index 4714018..114a228 100644 --- a/tournaments/models/draw_log.py +++ b/tournaments/models/draw_log.py @@ -11,6 +11,9 @@ class DrawLog(SideStoreModel): draw_team_position = models.IntegerField() draw_type = models.IntegerField(default=0) + def delete_dependencies(self): + pass + def __str__(self): return f'{self.draw_date}' diff --git a/tournaments/models/event.py b/tournaments/models/event.py index b5bba03..6e446f4 100644 --- a/tournaments/models/event.py +++ b/tournaments/models/event.py @@ -12,6 +12,14 @@ class Event(BaseModel): tenup_id = models.CharField(max_length=20, null=True, blank=True) creator_full_name = models.CharField(max_length=200, null=True, blank=True) + def delete_dependencies(self): + for tournament in self.tournaments.all(): + tournament.delete_dependencies() + tournament.delete() + for date_interval in self.date_intervals.all(): + date_interval.delete_dependencies() + date_interval.delete() + def __str__(self): return self.display_name() diff --git a/tournaments/models/group_stage.py b/tournaments/models/group_stage.py index b17a124..592438a 100644 --- a/tournaments/models/group_stage.py +++ b/tournaments/models/group_stage.py @@ -1,15 +1,15 @@ -from asyncio.streams import StreamReaderProtocol +# from asyncio.streams import StreamReaderProtocol from django.db import models from . import SideStoreModel, Tournament, FederalMatchCategory import uuid from ..utils.extensions import format_seconds -from datetime import datetime, timedelta +# from datetime import datetime, timedelta from django.utils import timezone class GroupStage(SideStoreModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='group_stages', null=True) + tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='group_stages', null=True, blank=True) index = models.IntegerField(default=0) size = models.IntegerField(default=4) format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) @@ -17,6 +17,13 @@ class GroupStage(SideStoreModel): name = models.CharField(max_length=200, null=True, blank=True) step = models.IntegerField(default=0) + def delete_dependencies(self): + for team_registration in self.team_registrations.all(): + team_registration.delete_dependencies() + team_registration.delete() + for match in self.matches.all(): + match.delete_dependencies() + match.delete() def __str__(self): return self.display_name() diff --git a/tournaments/models/match.py b/tournaments/models/match.py index f6cb0e2..bb42e92 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -1,5 +1,5 @@ from django.db import models -from tournaments.models import group_stage +# from tournaments.models import group_stage from . import SideStoreModel, Round, GroupStage, FederalMatchCategory from django.utils import timezone, formats from datetime import datetime, timedelta @@ -27,6 +27,11 @@ class Match(SideStoreModel): court_index = models.IntegerField(null=True, blank=True) confirmed = models.BooleanField(default=False) + def delete_dependencies(self): + for team_score in self.team_scores.all(): + # team_score.delete_dependencies() + team_score.delete() + def __str__(self): names = " / ".join(self.player_names()) return f"{self.stage_name()} #{self.index}: {names}" diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index 687f071..7c22faa 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -34,6 +34,9 @@ class PlayerRegistration(SideStoreModel): source = models.IntegerField(choices=PlayerDataSource.choices, null=True, blank=True) has_arrived = models.BooleanField(default=False) + def delete_dependencies(self): + pass + def __str__(self): return self.name() diff --git a/tournaments/models/purchase.py b/tournaments/models/purchase.py index 1803418..f8e8974 100644 --- a/tournaments/models/purchase.py +++ b/tournaments/models/purchase.py @@ -12,5 +12,8 @@ class Purchase(models.Model): revocation_date = models.DateTimeField(null=True, blank=True) expiration_date = models.DateTimeField(null=True, blank=True) + def delete_dependencies(self): + pass + def __str__(self): return f"{self.id} > {self.product_id} - {self.purchase_date} - {self.user.username}" diff --git a/tournaments/models/round.py b/tournaments/models/round.py index 4a58db6..4e9ddac 100644 --- a/tournaments/models/round.py +++ b/tournaments/models/round.py @@ -12,6 +12,14 @@ class Round(SideStoreModel): group_stage_loser_bracket = models.BooleanField(default=False) loser_bracket_mode = models.IntegerField(default=0) + def delete_dependencies(self): + for round in self.children.all(): + round.delete_dependencies() + round.delete() + for match in self.matches.all(): + match.delete_dependencies() + match.delete() + def __str__(self): if self.parent: return f"LB: {self.name()}" diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py index d38ba4d..ec876c9 100644 --- a/tournaments/models/team_registration.py +++ b/tournaments/models/team_registration.py @@ -30,6 +30,14 @@ class TeamRegistration(SideStoreModel): final_ranking = models.IntegerField(null=True, blank=True) points_earned = models.IntegerField(null=True, blank=True) + def delete_dependencies(self): + for player_registration in self.player_registrations.all(): + # player_registration.delete_dependencies() + player_registration.delete() + for team_score in self.team_scores.all(): + # match.delete_dependencies() + team_score.delete() + def __str__(self): if self.name: return self.name diff --git a/tournaments/models/team_score.py b/tournaments/models/team_score.py index e5c37be..43b40b2 100644 --- a/tournaments/models/team_score.py +++ b/tournaments/models/team_score.py @@ -10,6 +10,9 @@ class TeamScore(SideStoreModel): walk_out = models.IntegerField(null=True, blank=True) # TODO type of WO: forfeit, injury... lucky_loser = models.IntegerField(null=True, blank=True) + def delete_dependencies(self): + pass + def __str__(self): return f"{self.match.stage_name()} #{self.match.index}: {self.player_names()}" diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py index 7132a41..52c53d7 100644 --- a/tournaments/models/tournament.py +++ b/tournaments/models/tournament.py @@ -66,6 +66,20 @@ class Tournament(BaseModel): initial_seed_round = models.IntegerField(default=0) initial_seed_count = models.IntegerField(default=0) + def delete_dependencies(self): + for team_registration in self.team_registrations.all(): + team_registration.delete_dependencies() + team_registration.delete() + for gs in self.group_stages.all(): + gs.delete_dependencies() + gs.delete() + for round in self.rounds.all(): + round.delete_dependencies() + round.delete() + for draw_log in self.draw_logs.all(): + # draw_log.delete_dependencies() + draw_log.delete() + def __str__(self): if self.name: return self.name @@ -166,7 +180,10 @@ class Tournament(BaseModel): return self.end_date is None def creator(self): - return self.event.creator.username + if self.event and self.event.creator: + return self.event.creator.username + else: + return "--" def private_label(self): if self.is_private: