From 1fbff58bcd256b5e85314c44495f760ba03012fe Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:55:02 +0200 Subject: [PATCH 01/32] migrate models.py to models directory --- csgomatches/models/__init__.py | 1 + .../{models.py => models/global_models.py} | 27 ++++++++------ csgomatches/utils/scrapers/hltv.py | 37 +++++++++++-------- 3 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 csgomatches/models/__init__.py rename csgomatches/{models.py => models/global_models.py} (96%) diff --git a/csgomatches/models/__init__.py b/csgomatches/models/__init__.py new file mode 100644 index 0000000..6956e54 --- /dev/null +++ b/csgomatches/models/__init__.py @@ -0,0 +1 @@ +from .global_models import * diff --git a/csgomatches/models.py b/csgomatches/models/global_models.py similarity index 96% rename from csgomatches/models.py rename to csgomatches/models/global_models.py index 5fb8933..fd5d119 100644 --- a/csgomatches/models.py +++ b/csgomatches/models/global_models.py @@ -1,16 +1,17 @@ import os import requests +import importlib.resources import twitter + from django.contrib.sites.models import Site from django.db import models -from django.db.models import QuerySet -# Create your models here. +from django.db.models import QuerySet, Manager from django.urls import reverse from django.utils import timezone from django.utils.text import slugify -from . import managers -from .utils.publishing import twitter_api +from csgomatches import managers +from csgomatches.utils.publishing import twitter_api def get_flags_choices()-> list[tuple[str, str]]: @@ -18,12 +19,12 @@ def get_flags_choices()-> list[tuple[str, str]]: returns a list of tuples of all available flags by looking at png files in 'static/csgomatches/flags' """ choices: list[tuple[str, str]] = [] - base_pth = os.path.dirname(os.path.abspath(__file__)) - flags_pth = os.path.join(base_pth, 'static/csgomatches/flags') - for fn in os.listdir(flags_pth): - if fn.endswith('.png'): - short_fn = fn.replace('.png', '') - choices.append((short_fn, short_fn)) + with importlib.resources.path('csgomatches', 'static') as static_folder_path: + flags_folder_path = os.path.join(static_folder_path, 'csgomatches', 'flags') + for fn in os.listdir(flags_folder_path): + if fn.endswith('.png'): + short_fn = fn.replace('.png', '') + choices.append((short_fn, short_fn)) choices.sort(key=lambda x: x[0]) return choices @@ -47,12 +48,12 @@ class Team(models.Model): hltv_id = models.IntegerField(null=True, blank=True) esea_team_id = models.IntegerField(null=True, blank=True) - lineup_set: QuerySet['Lineup'] + lineup_set: managers.LineupQuerySet objects = managers.TeamManager() def get_hltv_id_from_name(self): - from .utils.scrapers.hltv import get_hltv_id_from_team_name + from csgomatches.utils.scrapers.hltv import get_hltv_id_from_team_name return get_hltv_id_from_team_name(team_mdl=self) def get_hltv_team_link(self): @@ -93,6 +94,8 @@ class Lineup(models.Model): active_from = models.DateTimeField(help_text='Set -10 Days to avoid multiple Lineup creations') is_active = models.BooleanField(default=True) + lineupplayer_set: Manager['LineupPlayer'] + objects = managers.LineupQuerySet.as_manager() def get_previous_lineup(self) -> 'Lineup | None': diff --git a/csgomatches/utils/scrapers/hltv.py b/csgomatches/utils/scrapers/hltv.py index 824a586..541b4a7 100644 --- a/csgomatches/utils/scrapers/hltv.py +++ b/csgomatches/utils/scrapers/hltv.py @@ -3,9 +3,10 @@ import requests import websockets +import websockets.asyncio.client from bs4 import BeautifulSoup -from ... import models +from csgomatches.models import Lineup, LineupPlayer, Player, Team def get_hltv_team_name_from_id(hltv_id: int): @@ -25,7 +26,7 @@ def get_hltv_team_name_from_id(hltv_id: int): return team_name -def get_hltv_id_from_team_name(team_mdl: models.Team, return_team_json=False): +def get_hltv_id_from_team_name(team_mdl: Team, return_team_json=False): db_names = [] if team_mdl: db_names.append(team_mdl.name) @@ -39,21 +40,21 @@ def get_hltv_id_from_team_name(team_mdl: models.Team, return_team_json=False): response = requests.get(url=url) response_json = response.json() teams = response_json[0].get("teams") - for team in teams: - hltv_name = team['name'] + for team_dict in teams: + hltv_name = team_dict['name'] print(f"[get_hltv_id_from_team_name] checking if hltv_name={hltv_name} in db_names={db_names}") if hltv_name in db_names: if return_team_json: - return team - return team['id'] + return team_dict + return team_dict['id'] if hltv_name.lower() in db_names_lower: if return_team_json: - return team - return team['id'] + return team_dict + return team_dict['id'] -def build_players(team_mdl: models.Team): +def build_players(team_mdl: Team): """ Player-Dict: { @@ -67,6 +68,9 @@ def build_players(team_mdl: models.Team): :return: """ current_lineup = team_mdl.lineup_set.active_lineups().first() + if not isinstance(current_lineup, Lineup): + raise ValueError(f"current_lineup returned by team.lineup_set.active_lineups().first() is not a Lineup instance: {current_lineup}") + if team_mdl.hltv_id: team_dict = get_hltv_id_from_team_name(team_mdl=team_mdl, return_team_json=True) if not team_dict: @@ -82,7 +86,7 @@ def build_players(team_mdl: models.Team): ingame_name = player_data.get('nickName') hltv_id_url = player_data.get('location') hltv_id = int(hltv_id_url.split("/")[2]) - player, player_created = models.Player.objects.get_or_create( + player, player_created = Player.objects.get_or_create( ingame_name=ingame_name, defaults={ 'first_name': first_name, @@ -105,7 +109,7 @@ def build_players(team_mdl: models.Team): print("existing_lineup_players", existing_lineup_players) if existing_lineup_players.count() == 0: for player in player_instances: - lu_player = models.LineupPlayer( + lu_player = LineupPlayer( player=player, lineup=current_lineup ) @@ -137,7 +141,7 @@ async def get_hlvt_score(match_id: int = 2338003): ws_str2 = '61:42["readyForMatch","{\\"token\\":\\"\\",\\"listID\\":\\"' + str(match_id) + '"\\}"]' ws_str2 = '61:42["readyForScores","{\\"token\\":\\"\\",\\"listIds\\":[' + str(match_id) + ']}"]' results = {} - async with websockets.connect(uri) as websocket: + async with websockets.asyncio.client.connect(uri) as websocket: ret = await websocket.recv() if DEBUGGING: @@ -151,7 +155,8 @@ async def get_hlvt_score(match_id: int = 2338003): if DEBUGGING: print(5, "Send", s) print("Waiting for data") - ret = await asyncio.wait_for(websocket.recv(), timeout=1) + ret = await asyncio.wait_for(websocket.recv(decode=True), timeout=1) + ret = str(ret) if ret.startswith("42["): ret = ret.replace("42[", "[") results = json.loads(ret) @@ -176,8 +181,8 @@ def get_map_name(json_result_list: list = [], map_nr: int = 1): if __name__ == '__main__': r = asyncio.get_event_loop().run_until_complete(get_hlvt_score(2337996)) if not DEBUGGING: - print("Map #1", convert_to_score(r, map_nr=1)) - print("Map #2", convert_to_score(r, map_nr=2)) - print("Map #3", convert_to_score(r, map_nr=3)) + print("Map #1", convert_to_score(r, map_nr=1)) # type: ignore + print("Map #2", convert_to_score(r, map_nr=2)) # type: ignore + print("Map #3", convert_to_score(r, map_nr=3)) # type: ignore else: print("HLTV-Score", r) From 706aa5f912a458e566d3cca379ae66651c99f835 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 10:56:22 +0200 Subject: [PATCH 02/32] migrate Player class to CsPlayer and add abstract Player class --- csgomatches/admin.py | 2 +- csgomatches/drf_api/ser.py | 2 +- .../migrations/0049_rename_player_csplayer.py | 17 +++++++++++++++++ csgomatches/models/__init__.py | 1 + csgomatches/models/cs_models.py | 8 ++++++++ csgomatches/models/global_models.py | 7 ++++--- csgomatches/utils/scrapers/esea.py | 4 ++-- csgomatches/utils/scrapers/hltv.py | 4 ++-- 8 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 csgomatches/migrations/0049_rename_player_csplayer.py create mode 100644 csgomatches/models/cs_models.py diff --git a/csgomatches/admin.py b/csgomatches/admin.py index 527f727..347ce69 100644 --- a/csgomatches/admin.py +++ b/csgomatches/admin.py @@ -244,7 +244,7 @@ def save_global(modeladmin, request, queryset): admin.site.register(models.Map) admin.site.register(models.Match, MatchAdmin) admin.site.register(models.MatchMap, MatchMapAdmin) -admin.site.register(models.Player, PlayerAdmin) +admin.site.register(models.CsPlayer, PlayerAdmin) admin.site.register(models.PlayerRole) admin.site.register(models.Tournament, TournamentAdmin) admin.site.register(models.Game, GameAdmin) diff --git a/csgomatches/drf_api/ser.py b/csgomatches/drf_api/ser.py index 870d77c..98fb0ba 100644 --- a/csgomatches/drf_api/ser.py +++ b/csgomatches/drf_api/ser.py @@ -18,7 +18,7 @@ class Meta: class CSGOPlayerShortSerializer(serializers.ModelSerializer): class Meta: - model = apps.get_model('csgomatches.Player') + model = apps.get_model('csgomatches.CsPlayer') fields = ['ingame_name',] diff --git a/csgomatches/migrations/0049_rename_player_csplayer.py b/csgomatches/migrations/0049_rename_player_csplayer.py new file mode 100644 index 0000000..1dc47b9 --- /dev/null +++ b/csgomatches/migrations/0049_rename_player_csplayer.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1 on 2024-09-28 15:31 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0048_alter_match_options'), + ] + + operations = [ + migrations.RenameModel( + old_name='Player', + new_name='CsPlayer', + ), + ] diff --git a/csgomatches/models/__init__.py b/csgomatches/models/__init__.py index 6956e54..e5a8cf8 100644 --- a/csgomatches/models/__init__.py +++ b/csgomatches/models/__init__.py @@ -1 +1,2 @@ from .global_models import * +from .cs_models import * diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py new file mode 100644 index 0000000..070e91c --- /dev/null +++ b/csgomatches/models/cs_models.py @@ -0,0 +1,8 @@ +from django.db import models + +from csgomatches.models import Player + + +class CsPlayer(Player): + hltv_id = models.IntegerField(null=True, blank=True) + esea_user_id = models.IntegerField(null=True, blank=True) diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index fd5d119..bc2dd64 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -71,8 +71,9 @@ class Player(models.Model): ingame_name = models.CharField(max_length=255) first_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255) - hltv_id = models.IntegerField(null=True, blank=True) - esea_user_id = models.IntegerField(null=True, blank=True) + + class Meta: + abstract = True def __str__(self): return f'{self.first_name} "{self.ingame_name}" {self.last_name}' @@ -135,7 +136,7 @@ class Meta: class LineupPlayer(models.Model): - player = models.ForeignKey(Player, on_delete=models.CASCADE) + player = models.ForeignKey('CsPlayer', on_delete=models.CASCADE) role = models.ForeignKey(PlayerRole, on_delete=models.CASCADE, null=True, blank=True) lineup = models.ForeignKey(Lineup, on_delete=models.CASCADE) diff --git a/csgomatches/utils/scrapers/esea.py b/csgomatches/utils/scrapers/esea.py index 1e14f7a..1fa78ef 100644 --- a/csgomatches/utils/scrapers/esea.py +++ b/csgomatches/utils/scrapers/esea.py @@ -17,7 +17,7 @@ """ Run this local, not on a hosted server -Usage: +Usage: /home/christian/workspace/venvs/wannbigspielt/bin/python /home/christian/workspace/src/github/ckw-csgo/csgomatches/utils/scrapers/esea.py """ @@ -335,7 +335,7 @@ def get_esea_team_schedule(): # crawl player pages for wsb_team in apps.get_model('csgomatches.Team').objects.filter(id__in=TEAM_A_MAPPINGS.values()): - first_player_with_esea_user_id = apps.get_model('csgomatches.Player').objects.filter( + first_player_with_esea_user_id = apps.get_model('csgomatches.CsPlayer').objects.filter( esea_user_id__isnull=False, lineupplayer__lineup__team=wsb_team, lineupplayer__lineup__is_active=True, diff --git a/csgomatches/utils/scrapers/hltv.py b/csgomatches/utils/scrapers/hltv.py index 541b4a7..e64a780 100644 --- a/csgomatches/utils/scrapers/hltv.py +++ b/csgomatches/utils/scrapers/hltv.py @@ -6,7 +6,7 @@ import websockets.asyncio.client from bs4 import BeautifulSoup -from csgomatches.models import Lineup, LineupPlayer, Player, Team +from csgomatches.models import Lineup, LineupPlayer, CsPlayer, Team def get_hltv_team_name_from_id(hltv_id: int): @@ -86,7 +86,7 @@ def build_players(team_mdl: Team): ingame_name = player_data.get('nickName') hltv_id_url = player_data.get('location') hltv_id = int(hltv_id_url.split("/")[2]) - player, player_created = Player.objects.get_or_create( + player, player_created = CsPlayer.objects.get_or_create( ingame_name=ingame_name, defaults={ 'first_name': first_name, From 970693242151d59d102b44436e56ccf38109930b Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:01:56 +0200 Subject: [PATCH 03/32] Add note to PlayerRole class --- csgomatches/models/global_models.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index bc2dd64..031d678 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -80,13 +80,19 @@ def __str__(self): class PlayerRole(models.Model): + """ + The role of a player. + For CS i.e. Fragger, Support, Leader, AWPer, Lurker, (Coach). + Will not be reasonable for some games. + Maybe it's better to implement this as an Enum. + """ name = models.CharField(max_length=255) - # i.e. Fragger, Support, Leader, AWPer, Lurker, Coach - def __str__(self): return self.name + #TODO: Implement checking on LineupPlayer class, whether PlayerRole is reasonable for game. + class Lineup(models.Model): game = models.ForeignKey(Game, null=True, on_delete=models.SET_NULL) From 32a3edd72a63e18466050529240ad9fe177b81c3 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:04:09 +0200 Subject: [PATCH 04/32] move comment to correct class --- csgomatches/models/global_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 031d678..23455d3 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -91,7 +91,6 @@ class PlayerRole(models.Model): def __str__(self): return self.name - #TODO: Implement checking on LineupPlayer class, whether PlayerRole is reasonable for game. class Lineup(models.Model): @@ -151,6 +150,9 @@ def __str__(self): return f'{self.player.ingame_name} ({self.role.name})' return f'{self.player.ingame_name} @ {self.lineup.team.name}' + def save(self, *args, **kwargs): + #TODO: Implement checking, whether PlayerRole is reasonable for game. + super().save(*args, **kwargs) class Tournament(models.Model): name = models.CharField(max_length=255) From 885dcd66247581af6a586ca7ae47c653f8048255 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:17:41 +0200 Subject: [PATCH 05/32] rename Match -> CsMatch and Tournament -> CsTournament --- csgomatches/admin.py | 7 ++- csgomatches/drf_api/ser.py | 10 ++-- csgomatches/drf_api/views.py | 8 +-- ..._csmatch_rename_tournament_cstournament.py | 21 +++++++ csgomatches/models/cs_models.py | 55 ++++++++++++++++++- csgomatches/models/global_models.py | 50 ++--------------- 6 files changed, 91 insertions(+), 60 deletions(-) create mode 100644 csgomatches/migrations/0050_rename_match_csmatch_rename_tournament_cstournament.py diff --git a/csgomatches/admin.py b/csgomatches/admin.py index 347ce69..b16c0e1 100644 --- a/csgomatches/admin.py +++ b/csgomatches/admin.py @@ -48,7 +48,8 @@ def is_live(self, obj): is_live.boolean = True -class TournamentAdmin(admin.ModelAdmin): +# Currently no generic implementation of ModelAdmin for Tournament class +class CsTournamentAdmin(admin.ModelAdmin): search_fields = ['name', 'name_alt', 'name_hltv', 'name_99dmg'] list_display = ['name', 'name_alt', 'name_hltv', 'name_99dmg'] actions = ['cleanup', 'merge_two'] @@ -242,11 +243,11 @@ def save_global(modeladmin, request, queryset): admin.site.register(models.LineupPlayer, LineupPlayerAdmin) admin.site.register(models.ExternalLink, ExternalLinkAdmin) admin.site.register(models.Map) -admin.site.register(models.Match, MatchAdmin) +admin.site.register(models.CsMatch, MatchAdmin) admin.site.register(models.MatchMap, MatchMapAdmin) admin.site.register(models.CsPlayer, PlayerAdmin) admin.site.register(models.PlayerRole) -admin.site.register(models.Tournament, TournamentAdmin) +admin.site.register(models.CsTournament, CsTournamentAdmin) admin.site.register(models.Game, GameAdmin) admin.site.register(models.StaticPage) diff --git a/csgomatches/drf_api/ser.py b/csgomatches/drf_api/ser.py index 98fb0ba..9af80e6 100644 --- a/csgomatches/drf_api/ser.py +++ b/csgomatches/drf_api/ser.py @@ -6,7 +6,7 @@ class CSGOTournamentSerializer(serializers.ModelSerializer): class Meta: - model = apps.get_model('csgomatches.Tournament') + model = apps.get_model('csgomatches.CsTournament') fields = ['name', 'name_alt', 'name_hltv', 'name_99dmg', 'id'] @@ -70,15 +70,15 @@ def get_livescore_url(self, obj): if obj.hltv_match_id: url = reverse('match_livescore-detail', kwargs={'pk': obj.hltv_match_id}) request = self.context.get('request') - return request.build_absolute_uri(url) + return request.build_absolute_uri(url) # type: ignore def get_html_detail_url(self, obj): url = obj.get_absolute_url() request = self.context.get('request') - return request.build_absolute_uri(url) + return request.build_absolute_uri(url) # type: ignore class Meta: - model = apps.get_model('csgomatches.Match') + model = apps.get_model('csgomatches.CsMatch') fields = ['tournament', 'lineup_a', 'lineup_b', 'slug', 'bestof', 'first_map_at', 'cancelled', 'hltv_match_id', 'livescore_url', 'html_detail_url', 'matchmaps'] @@ -117,7 +117,7 @@ def get_api_match_url(self, obj): if csgo_match: url = reverse('match_all-detail', kwargs={'pk': csgo_match.pk}) request = self.context.get('request') - return request.build_absolute_uri(url) + return request.build_absolute_uri(url) # type: ignore def create(self, validated_data): hltv_match_id = validated_data.get('hltv_match_id') diff --git a/csgomatches/drf_api/views.py b/csgomatches/drf_api/views.py index 49f7f32..0dc762b 100644 --- a/csgomatches/drf_api/views.py +++ b/csgomatches/drf_api/views.py @@ -67,7 +67,7 @@ def get_view_name(self): class TournamentViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): - queryset = apps.get_model('csgomatches.Tournament').objects.all() + queryset = apps.get_model('csgomatches.CsTournament').objects.all() serializer_class = ser.CSGOTournamentSerializer def get_view_name(self): @@ -75,7 +75,7 @@ def get_view_name(self): class MatchViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): - queryset = apps.get_model('csgomatches.Match').objects.all() + queryset = apps.get_model('csgomatches.CsMatch').objects.all() serializer_class = ser.CSGOMatchSerializer def get_view_name(self): @@ -87,7 +87,7 @@ def list(self, request, *args, **kwargs): class MatchUpcomingViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): - queryset = apps.get_model('csgomatches.Match').objects.filter(first_map_at__gte=timezone.now()) + queryset = apps.get_model('csgomatches.CsMatch').objects.filter(first_map_at__gte=timezone.now()) serializer_class = ser.CSGOMatchSerializer def get_view_name(self): @@ -120,7 +120,7 @@ def get_view_name(self): def get_queryset(self): last_7_days = timezone.now() - timezone.timedelta(days=7) - return apps.get_model('csgomatches.Match').objects.filter( + return apps.get_model('csgomatches.CsMatch').objects.filter( hltv_match_id__isnull=False, first_map_at__gte=last_7_days ) diff --git a/csgomatches/migrations/0050_rename_match_csmatch_rename_tournament_cstournament.py b/csgomatches/migrations/0050_rename_match_csmatch_rename_tournament_cstournament.py new file mode 100644 index 0000000..0fd112e --- /dev/null +++ b/csgomatches/migrations/0050_rename_match_csmatch_rename_tournament_cstournament.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1 on 2024-09-29 09:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0049_rename_player_csplayer'), + ] + + operations = [ + migrations.RenameModel( + old_name='Match', + new_name='CsMatch', + ), + migrations.RenameModel( + old_name='Tournament', + new_name='CsTournament', + ), + ] diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index 070e91c..5d41ab1 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -1,8 +1,59 @@ from django.db import models +from django.urls import reverse +import requests -from csgomatches.models import Player +import csgomatches.models.global_models as global_models -class CsPlayer(Player): +class CsPlayer(global_models.Player): hltv_id = models.IntegerField(null=True, blank=True) esea_user_id = models.IntegerField(null=True, blank=True) + +class CsTournament(global_models.Tournament): + name_hltv = models.CharField(max_length=255, null=True, blank=True) + name_99dmg = models.CharField(max_length=255, null=True, blank=True) + esea_bracket_id = models.IntegerField(null=True, blank=True) + esea_bracket_team_ids = models.CharField(max_length=255, null=True, blank=True, help_text='Comma Separated') + +class CsMatch(global_models.Match): + tournament = models.ForeignKey(CsTournament, on_delete=models.CASCADE) + + hltv_match_id = models.CharField(max_length=20, null=True, blank=True, help_text='For HLTV Livescore during match') + esea_match_id = models.CharField(max_length=255, null=True, blank=True) + enable_99dmg = models.BooleanField(default=False) + enable_hltv = models.BooleanField(default=True) + + def get_livescore_url(self, request): + if self.hltv_match_id: + url = reverse('match_livescore-detail', kwargs={'pk': self.hltv_match_id}) + return request.build_absolute_uri(url) + + def update_hltv_livescore(self, request) -> None: + # Guard clause in case lineup_a is None + if not self.lineup_a: + return + + url = self.get_livescore_url(request=request) + if url: + response = requests.get(url=url, params={'format': 'json'}).json() + maps = response.get('maps', []) + for map_data in maps: + map_nr = map_data.get('map_nr') + mm_obj = self.matchmap_set.filter(map_nr=map_nr).first() + if mm_obj: + mm_obj.played_map = global_models.Map.objects.filter( + models.Q(name=map_data.get('map_name')) | + models.Q(cs_name=map_data.get('map_name')) + ).first() + score_a, score_b = map_data.get('score_a'), map_data.get('score_b') + swap_score = False + team_a_hltv_id = response.get('team_a_id') + if team_a_hltv_id != self.lineup_a.team.hltv_id: + swap_score = True + + if swap_score: + score_b, score_a = score_a, score_b + + mm_obj.rounds_won_team_a = score_a + mm_obj.rounds_won_team_b = score_b + mm_obj.save() diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 23455d3..4cde84d 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -1,5 +1,4 @@ import os -import requests import importlib.resources import twitter @@ -157,10 +156,6 @@ def save(self, *args, **kwargs): class Tournament(models.Model): name = models.CharField(max_length=255) name_alt = models.CharField(max_length=255, null=True, blank=True) - name_hltv = models.CharField(max_length=255, null=True, blank=True) - name_99dmg = models.CharField(max_length=255, null=True, blank=True) - esea_bracket_id = models.IntegerField(null=True, blank=True) - esea_bracket_team_ids = models.CharField(max_length=255, null=True, blank=True, help_text='Comma Separated') match_set: QuerySet['Match'] @@ -170,6 +165,7 @@ def __str__(self): return self.name class Meta: + abstract = True ordering = ['name'] @@ -202,17 +198,14 @@ class Match(models.Model): (2, 'Defwin in favour of team B'), ) ) - hltv_match_id = models.CharField(max_length=20, null=True, blank=True, help_text='For HLTV Livescore during match') - esea_match_id = models.CharField(max_length=255, null=True, blank=True) enable_tweet = models.BooleanField(default=True) last_tweet = models.DateTimeField(null=True, blank=True) last_tweet_id = models.CharField(max_length=255, null=True, blank=True) - enable_99dmg = models.BooleanField(default=False) - enable_hltv = models.BooleanField(default=True) matchmap_set: QuerySet['MatchMap'] class Meta: + abstract = True ordering = ['-first_map_at'] verbose_name_plural = "matches" @@ -306,44 +299,9 @@ def save(self, *args, **kwargs): def get_absolute_url(self) -> str: return reverse('match_details', kwargs={'slug': self.slug}) - def get_livescore_url(self, request): - if self.hltv_match_id: - url = reverse('match_livescore-detail', kwargs={'pk': self.hltv_match_id}) - return request.build_absolute_uri(url) - - def update_hltv_livescore(self, request) -> None: - # Guard clause in case lineup_a is None - if not self.lineup_a: - return - - url = self.get_livescore_url(request=request) - if url: - response = requests.get(url=url, params={'format': 'json'}).json() - maps = response.get('maps', []) - for map_data in maps: - map_nr = map_data.get('map_nr') - mm_obj = self.matchmap_set.filter(map_nr=map_nr).first() - if mm_obj: - mm_obj.played_map = Map.objects.filter( - models.Q(name=map_data.get('map_name')) | - models.Q(cs_name=map_data.get('map_name')) - ).first() - score_a, score_b = map_data.get('score_a'), map_data.get('score_b') - swap_score = False - team_a_hltv_id = response.get('team_a_id') - if team_a_hltv_id != self.lineup_a.team.hltv_id: - swap_score = True - - if swap_score: - score_b, score_a = score_a, score_b - - mm_obj.rounds_won_team_a = score_a - mm_obj.rounds_won_team_b = score_b - mm_obj.save() - class MatchMap(models.Model): - match = models.ForeignKey(Match, on_delete=models.CASCADE) + match = models.ForeignKey("CsMatch", on_delete=models.CASCADE) played_map = models.ForeignKey(Map, on_delete=models.CASCADE, null=True, blank=True) rounds_won_team_a = models.IntegerField(default=0) rounds_won_team_b = models.IntegerField(default=0) @@ -453,7 +411,7 @@ class Meta: class ExternalLink(models.Model): - match = models.ForeignKey(Match, on_delete=models.CASCADE) + match = models.ForeignKey("CsMatch", on_delete=models.CASCADE) link_type = models.CharField(max_length=255, choices=( ('hltv_match', 'HLTV'), ('esea_match', 'ESEA Match'), From f94e7d059d5d648b34cf657a1cdd3b8d76138e8b Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:19:41 +0200 Subject: [PATCH 06/32] Rename CSGOSiteSetting -> SiteSetting --- .../0051_rename_csgositesetting_sitesetting.py | 18 ++++++++++++++++++ csgomatches/models/global_models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 csgomatches/migrations/0051_rename_csgositesetting_sitesetting.py diff --git a/csgomatches/migrations/0051_rename_csgositesetting_sitesetting.py b/csgomatches/migrations/0051_rename_csgositesetting_sitesetting.py new file mode 100644 index 0000000..af1047d --- /dev/null +++ b/csgomatches/migrations/0051_rename_csgositesetting_sitesetting.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1 on 2024-09-29 09:19 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0050_rename_match_csmatch_rename_tournament_cstournament'), + ('sites', '0002_alter_domain_unique'), + ] + + operations = [ + migrations.RenameModel( + old_name='CSGOSiteSetting', + new_name='SiteSetting', + ), + ] diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 4cde84d..271f4ab 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -441,7 +441,7 @@ class Meta: ordering = ['match', 'link_flag', 'link_type'] -class CSGOSiteSetting(models.Model): +class SiteSetting(models.Model): site = models.ForeignKey(Site, on_delete=models.CASCADE) main_team = models.ForeignKey('csgomatches.Team', on_delete=models.CASCADE, related_name='main_team_settings') second_team = models.ForeignKey('csgomatches.Team', on_delete=models.CASCADE, related_name='sec_team_settings') From 2babcec318c7d0a00b812ff90d8ad72a48f56f89 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:36:11 +0200 Subject: [PATCH 07/32] finish migration Match -> CsMatch --- .../blocks/{match.html => csmatch.html} | 2 +- ...ve_year.html => csmatch_archive_year.html} | 4 +- ...{match_detail.html => csmatch_detail.html} | 42 +++++++++---------- .../{match_list.html => csmatch_list.html} | 4 +- csgomatches/views.py | 9 ++-- 5 files changed, 29 insertions(+), 32 deletions(-) rename csgomatches/templates/csgomatches/blocks/{match.html => csmatch.html} (98%) rename csgomatches/templates/csgomatches/{match_archive_year.html => csmatch_archive_year.html} (91%) rename csgomatches/templates/csgomatches/{match_detail.html => csmatch_detail.html} (59%) rename csgomatches/templates/csgomatches/{match_list.html => csmatch_list.html} (94%) diff --git a/csgomatches/templates/csgomatches/blocks/match.html b/csgomatches/templates/csgomatches/blocks/csmatch.html similarity index 98% rename from csgomatches/templates/csgomatches/blocks/match.html rename to csgomatches/templates/csgomatches/blocks/csmatch.html index cbab549..19d00ba 100644 --- a/csgomatches/templates/csgomatches/blocks/match.html +++ b/csgomatches/templates/csgomatches/blocks/csmatch.html @@ -29,7 +29,7 @@

{{ link.get_link_type_display }} {% endfor %} {% if user.is_staff %} - + {% if match.enable_tweet %} 🐦 {% else %} diff --git a/csgomatches/templates/csgomatches/match_archive_year.html b/csgomatches/templates/csgomatches/csmatch_archive_year.html similarity index 91% rename from csgomatches/templates/csgomatches/match_archive_year.html rename to csgomatches/templates/csgomatches/csmatch_archive_year.html index 785e027..226c300 100644 --- a/csgomatches/templates/csgomatches/match_archive_year.html +++ b/csgomatches/templates/csgomatches/csmatch_archive_year.html @@ -10,7 +10,7 @@

Match-Archiv {{ year.year }}

{% for match in object_list %} - {% include 'csgomatches/blocks/match.html' %} + {% include 'csgomatches/blocks/csmatch.html' %} {% empty %}

Keine Matches im Archiv von {{ year.year }} gefunden.

@@ -26,4 +26,4 @@

Match-Archiv {{ year.year }}

  • {{ date|date:"Y" }}
  • {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/csgomatches/templates/csgomatches/match_detail.html b/csgomatches/templates/csgomatches/csmatch_detail.html similarity index 59% rename from csgomatches/templates/csgomatches/match_detail.html rename to csgomatches/templates/csgomatches/csmatch_detail.html index 0cc7683..a365f18 100644 --- a/csgomatches/templates/csgomatches/match_detail.html +++ b/csgomatches/templates/csgomatches/csmatch_detail.html @@ -3,15 +3,15 @@ {% load static %} {% block page_title %} - {{ match }} - {{ match.first_map_at|date:"SHORT_DATE_FORMAT" }} - {{ match.tournament.name }} - Matchdetails - Wann spielt BIG? + {{ csmatch }} - {{ csmatch.first_map_at|date:"SHORT_DATE_FORMAT" }} - {{ csmatch.tournament.name }} - Matchdetails - Wann spielt BIG? {% endblock %} {% block meta %} - {% if match.is_live %} + {% if csmatch.is_live %} {% if update_seconds %} {% endif %} - {% elif match.is_upcoming %} + {% elif csmatch.is_upcoming %} {% endif %} {% endblock %} @@ -23,51 +23,51 @@ {% block content %}

    - {{ match.first_map_at|date:'d.m.y H:i' }} - {{ match.lineup_a.team }} - {% if match.lineup_b %} - {{ score.0 }}: - {{ score.1 }} - {{ match.lineup_b.team }} + {{ csmatch.first_map_at|date:'d.m.y H:i' }} + {{ csmatch.lineup_a.team }} + {% if csmatch.lineup_b %} + {{ score.0 }}: + {{ score.1 }} + {{ csmatch.lineup_b.team }} {% else %} - at {{ match.tournament.name }} + at {{ csmatch.tournament.name }} {% endif %} - {{ match.tournament.name }} + {{ csmatch.tournament.name }}

    -

    {{ match.tournament.name }}

    +

    {{ csmatch.tournament.name }}

    {% include 'csgomatches/blocks/box_matchrow_detail.html' %} - {% if match.lineup_a and match.lineup_a.lineupplayer_set.exists %} + {% if csmatch.lineup_a and csmatch.lineup_a.lineupplayer_set.exists %}
    -

    {% if match.lineup_a.game %}{{ match.lineup_a.game.name }}-{% endif %}Lineup {{ match.lineup_a.team.name }}

    - {% for lp in match.lineup_a.lineupplayer_set.all %} +

    {% if csmatch.lineup_a.game %}{{ csmatch.lineup_a.game.name }}-{% endif %}Lineup {{ csmatch.lineup_a.team.name }}

    + {% for lp in csmatch.lineup_a.lineupplayer_set.all %} {{ lp.player.ingame_name }} {% endfor %}
    {% endif %} - {% if match.lineup_b and match.lineup_b.lineupplayer_set.exists %} + {% if csmatch.lineup_b and csmatch.lineup_b.lineupplayer_set.exists %}
    -

    Lineup {{ match.lineup_b.team.name }}

    - {% for lp in match.lineup_b.lineupplayer_set.all %} +

    Lineup {{ csmatch.lineup_b.team.name }}

    + {% for lp in csmatch.lineup_b.lineupplayer_set.all %} {{ lp.player.ingame_name }} {% endfor %}
    {% endif %} - {% with matchlinks=match.externallink_set.visible %} + {% with matchlinks=csmatch.externallink_set.visible %} {% if matchlinks.exists %} diff --git a/csgomatches/utils/scrapers/esea.py b/csgomatches/utils/scrapers/esea.py index 1fa78ef..3170dde 100644 --- a/csgomatches/utils/scrapers/esea.py +++ b/csgomatches/utils/scrapers/esea.py @@ -293,8 +293,8 @@ def process_api_match_data(matches_data): if team_a_score > first_matchmap.rounds_won_team_a or \ team_b_score > first_matchmap.rounds_won_team_b or \ first_map_at_changed or \ - first_matchmap.played_map != map_instance: - first_matchmap.played_map = map_instance + first_matchmap.map != map_instance: + first_matchmap.map = map_instance first_matchmap.rounds_won_team_a = team_a_score first_matchmap.rounds_won_team_b = team_b_score first_matchmap.starting_at = match.first_map_at From f084736b795c82c47ef901efa6413a1f4d08973a Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:02:06 +0200 Subject: [PATCH 22/32] move parts of Match and MatchMap to respecive OneOnOne abtract model classes --- csgomatches/models/cs_models.py | 2 +- csgomatches/models/global_models.py | 65 +++++++++++++++-------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index 3fa2b02..62ace14 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -33,7 +33,7 @@ class Meta: verbose_name_plural = "CS Maps" -class CsMatch(global_models.Match): +class CsMatch(global_models.OneOnOneMatch): tournament = models.ForeignKey(CsTournament, on_delete=models.CASCADE, related_name='match_set') hltv_match_id = models.CharField(max_length=20, null=True, blank=True, help_text='For HLTV Livescore during match') diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 43659a8..dd278a6 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -171,8 +171,6 @@ def __str__(self): class Match(models.Model): tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) slug = models.SlugField(unique=True, allow_unicode=False, null=True, blank=True, max_length=255) - lineup_a = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_a_set', null=True, blank=True) - lineup_b = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_b_set', null=True, blank=True) bestof = models.IntegerField(choices=( (1, 'BO1'), (2, 'BO2'), @@ -193,20 +191,13 @@ class Match(models.Model): last_tweet = models.DateTimeField(null=True, blank=True) last_tweet_id = models.CharField(max_length=255, null=True, blank=True) - matchmap_set: QuerySet['MatchMap'] + matchmap_set: QuerySet['OneOnOneMatchMap'] class Meta: abstract = True ordering = ['-first_map_at'] verbose_name_plural = "matches" - def __str__(self) -> str: - if self.lineup_a and self.lineup_b: - return f'{self.lineup_a.team.name} vs {self.lineup_b.team.name}' - elif self.lineup_a and not self.lineup_b: - return f'{self.lineup_a.team.name}' - return 'TBA vs TBA' - def get_first_matchmap(self) -> 'MatchMap | None': return self.matchmap_set.order_by('starting_at').first() @@ -231,16 +222,32 @@ def has_ended(self) -> bool: if last_map: if last_map.has_ended(): return True - # if last_map.starting_at - team_a, team_b = self.get_overall_score() - if team_a > team_b or team_b > team_a: - return True + else: + raise ValueError(f"No last map found for Match {self}") return False def is_upcoming(self) -> bool | None: if self.first_map_at: return self.first_map_at > timezone.now() + def get_absolute_url(self) -> str: + return reverse('match_details', kwargs={'slug': self.slug}) + + +class OneOnOneMatch(Match): + lineup_a = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_a_set', null=True, blank=True) + lineup_b = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_b_set', null=True, blank=True) + + class Meta: + abstract = True + + def __str__(self) -> str: + if self.lineup_a and self.lineup_b: + return f'{self.lineup_a.team.name} vs {self.lineup_b.team.name}' + elif self.lineup_a and not self.lineup_b: + return f'{self.lineup_a.team.name}' + return 'TBA vs TBA' + def get_overall_score(self) -> tuple[int, int]: lineup_a_mapwins = 0 lineup_b_mapwins = 0 @@ -289,18 +296,12 @@ def save(self, *args, **kwargs): self.slug = slugify(f"id-{self.pk}") super(Match, self).save(*args, **kwargs) - def get_absolute_url(self) -> str: - return reverse('match_details', kwargs={'slug': self.slug}) - class MatchMap(models.Model): match = models.ForeignKey(Match, on_delete=models.CASCADE) map = models.ForeignKey(Map, on_delete=models.CASCADE, null=True, blank=True) - rounds_won_team_a = models.IntegerField(default=0) - rounds_won_team_b = models.IntegerField(default=0) starting_at = models.DateTimeField() map_nr = models.IntegerField(null=True) - map_pick_of = models.ForeignKey(Lineup, null=True, blank=True, on_delete=models.CASCADE) unplayed = models.BooleanField(default=False) # defwin_reason = models.CharField(max_length=255, null=True, blank=True) # defwin = models.BooleanField(default=False) @@ -336,6 +337,19 @@ def is_live(self) -> bool: calc_end = self.starting_at + timezone.timedelta(minutes=100) return self.starting_at < timezone.now() < calc_end + def __str__(self) -> str: + return f'{self.match} - {self.starting_at.date()} Map #{self.map_nr} (ID = {self.pk if self.pk else "-"})' + + +class OneOnOneMatchMap(MatchMap): + match = models.ForeignKey(OneOnOneMatch, on_delete=models.CASCADE) + rounds_won_team_a = models.IntegerField(default=0) + rounds_won_team_b = models.IntegerField(default=0) + map_pick_of = models.ForeignKey(Lineup, null=True, blank=True, on_delete=models.CASCADE) + + class Meta: + abstract = True + def team_a_won(self) -> bool: return (self.rounds_won_team_a > self.rounds_won_team_b) and self.has_ended() @@ -345,9 +359,6 @@ def is_draw(self) -> bool: def team_b_won(self) -> bool: return (self.rounds_won_team_a < self.rounds_won_team_b) and self.has_ended() - def __str__(self) -> str: - return f'{self.match} - {self.starting_at.date()} Map #{self.map_nr} (ID = {self.pk if self.pk else "-"})' - def send_tweet(self, prev_instance=None, interval=180.) -> None: # Guard clause in case either lineup_a or lineup_b are None if not self.match.lineup_a or not self.match.lineup_b: @@ -409,14 +420,6 @@ def save(self, *args, **kwargs): self.match.save() self.send_tweet(prev_instance=prev_instance) -class OneOnOneMatchMap(MatchMap): - """ - Abstract Match between two opponnents - """ - - class Meta: - abstract = True - class ExternalLink(models.Model): match = models.ForeignKey("CsMatch", on_delete=models.CASCADE) From 5b61fd9beee160b44f43bdd347dcac7b0d82b3e6 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:15:22 +0200 Subject: [PATCH 23/32] Rename Lineup -> CsLineup --- csgomatches/admin.py | 2 +- csgomatches/drf_api/ser.py | 2 +- csgomatches/drf_api/views.py | 2 +- ..._lineup_cslineup_alter_cslineup_options.py | 21 +++++++++++++++++++ csgomatches/models/cs_models.py | 12 +++++++++++ csgomatches/models/global_models.py | 7 +++---- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 csgomatches/migrations/0062_rename_lineup_cslineup_alter_cslineup_options.py diff --git a/csgomatches/admin.py b/csgomatches/admin.py index 3b4fa6c..4240652 100644 --- a/csgomatches/admin.py +++ b/csgomatches/admin.py @@ -239,7 +239,7 @@ def save_global(modeladmin, request, queryset): admin.site.register(models.Team, TeamAdmin) -admin.site.register(models.Lineup, LineupAdmin) +admin.site.register(models.CsLineup, LineupAdmin) admin.site.register(models.CsLineupPlayer, CsLineupPlayerAdmin) admin.site.register(models.ExternalLink, ExternalLinkAdmin) admin.site.register(models.CsMap) diff --git a/csgomatches/drf_api/ser.py b/csgomatches/drf_api/ser.py index 3b6b42c..65f3a19 100644 --- a/csgomatches/drf_api/ser.py +++ b/csgomatches/drf_api/ser.py @@ -54,7 +54,7 @@ class CSGOLineupSerializer(serializers.ModelSerializer): players = CSGOLineupPlayerSerializer(many=True, read_only=True, source='lineupplayer_set') class Meta: - model = apps.get_model('csgomatches.Lineup') + model = apps.get_model('csgomatches.CsLineup') fields = ['team', 'team_logo_url', 'active_from', 'players', 'id'] class CSGOMapSerializer(serializers.ModelSerializer): diff --git a/csgomatches/drf_api/views.py b/csgomatches/drf_api/views.py index 433bd0b..cba5d1a 100644 --- a/csgomatches/drf_api/views.py +++ b/csgomatches/drf_api/views.py @@ -99,7 +99,7 @@ def list(self, request, *args, **kwargs): class LineupViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): - queryset = apps.get_model('csgomatches.Lineup').objects.all() + queryset = apps.get_model('csgomatches.CsLineup').objects.all() serializer_class = ser.CSGOLineupSerializer def get_view_name(self): diff --git a/csgomatches/migrations/0062_rename_lineup_cslineup_alter_cslineup_options.py b/csgomatches/migrations/0062_rename_lineup_cslineup_alter_cslineup_options.py new file mode 100644 index 0000000..be4889e --- /dev/null +++ b/csgomatches/migrations/0062_rename_lineup_cslineup_alter_cslineup_options.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1 on 2024-09-29 16:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0061_rename_played_map_csmatchmap_map'), + ] + + operations = [ + migrations.RenameModel( + old_name='Lineup', + new_name='CsLineup', + ), + migrations.AlterModelOptions( + name='cslineup', + options={'ordering': ['team__name', '-active_from'], 'verbose_name': 'CS Lineup', 'verbose_name_plural': 'CS Lineups'}, + ), + ] diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index 62ace14..72aef8e 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -27,6 +27,14 @@ class Meta: verbose_name_plural = "CS Tournaments" +class CsLineup(global_models.Lineup): + class Meta: + verbose_name = "CS Lineup" + verbose_name_plural = "CS Lineups" + ordering = ['team__name', '-active_from'] + unique_together = ('team', 'active_from') + + class CsMap(global_models.Map): class Meta: verbose_name = "CS Map" @@ -35,6 +43,8 @@ class Meta: class CsMatch(global_models.OneOnOneMatch): tournament = models.ForeignKey(CsTournament, on_delete=models.CASCADE, related_name='match_set') + lineup_a = models.ForeignKey(CsLineup, on_delete=models.CASCADE, related_name='matches_as_lineup_a_set', null=True, blank=True) + lineup_b = models.ForeignKey(CsLineup, on_delete=models.CASCADE, related_name='matches_as_lineup_b_set', null=True, blank=True) hltv_match_id = models.CharField(max_length=20, null=True, blank=True, help_text='For HLTV Livescore during match') esea_match_id = models.CharField(max_length=255, null=True, blank=True) @@ -96,6 +106,7 @@ def choices(cls): class CsLineupPlayer(global_models.LineupPlayer): player = models.ForeignKey(CsPlayer, on_delete=models.CASCADE) + lineup = models.ForeignKey(CsLineup, on_delete=models.CASCADE) role = models.CharField( max_length=20, choices=CSLineupPlayerRole.choices(), @@ -118,6 +129,7 @@ def save(self, *args, **kwargs): class CsMatchMap(global_models.OneOnOneMatchMap): match = models.ForeignKey(CsMatch, on_delete=models.CASCADE) map = models.ForeignKey(CsMap, on_delete=models.CASCADE, null=True, blank=True) + map_pick_of = models.ForeignKey(CsLineup, null=True, blank=True, on_delete=models.CASCADE) class Meta: verbose_name = "CS Match Map" diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index dd278a6..64abbc5 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -92,6 +92,9 @@ class Lineup(models.Model): objects = managers.LineupQuerySet.as_manager() + class Meta: + abstract = True + def get_previous_lineup(self) -> 'Lineup | None': return self.team.lineup_set.filter( active_from__lt=self.active_from @@ -123,10 +126,6 @@ def save(self, *args, **kwargs): prev_lu.save() super(Lineup, self).save(*args, **kwargs) - class Meta: - ordering = ['team__name', '-active_from'] - unique_together = ('team', 'active_from') - class LineupPlayer(models.Model): player = models.ForeignKey(Player, on_delete=models.CASCADE) From 7e6189daff14594236e98a43b39f0b1f10555b72 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:17:08 +0200 Subject: [PATCH 24/32] move all meta options to implementations instead of abstract models --- ...tions_alter_csmatchmap_options_and_more.py | 25 +++++++++++++++++++ csgomatches/models/cs_models.py | 3 +++ csgomatches/models/global_models.py | 4 --- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 csgomatches/migrations/0063_alter_csmatch_options_alter_csmatchmap_options_and_more.py diff --git a/csgomatches/migrations/0063_alter_csmatch_options_alter_csmatchmap_options_and_more.py b/csgomatches/migrations/0063_alter_csmatch_options_alter_csmatchmap_options_and_more.py new file mode 100644 index 0000000..3e37c32 --- /dev/null +++ b/csgomatches/migrations/0063_alter_csmatch_options_alter_csmatchmap_options_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1 on 2024-09-29 16:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0062_rename_lineup_cslineup_alter_cslineup_options'), + ] + + operations = [ + migrations.AlterModelOptions( + name='csmatch', + options={'ordering': ['-first_map_at'], 'verbose_name': 'CS Match', 'verbose_name_plural': 'CS Matches'}, + ), + migrations.AlterModelOptions( + name='csmatchmap', + options={'ordering': ['starting_at'], 'verbose_name': 'CS Match Map', 'verbose_name_plural': 'CS Match Maps'}, + ), + migrations.AlterModelOptions( + name='cstournament', + options={'ordering': ['name'], 'verbose_name': 'CS Tournament', 'verbose_name_plural': 'CS Tournaments'}, + ), + ] diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index 72aef8e..8abc665 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -23,6 +23,7 @@ class CsTournament(global_models.Tournament): esea_bracket_team_ids = models.CharField(max_length=255, null=True, blank=True, help_text='Comma Separated') class Meta: + ordering = ['name'] verbose_name = "CS Tournament" verbose_name_plural = "CS Tournaments" @@ -54,6 +55,7 @@ class CsMatch(global_models.OneOnOneMatch): matchmap_set: QuerySet["CsMatchMap"] class Meta: + ordering = ['-first_map_at'] verbose_name = "CS Match" verbose_name_plural = "CS Matches" @@ -132,6 +134,7 @@ class CsMatchMap(global_models.OneOnOneMatchMap): map_pick_of = models.ForeignKey(CsLineup, null=True, blank=True, on_delete=models.CASCADE) class Meta: + ordering = ['starting_at'] verbose_name = "CS Match Map" verbose_name_plural = "CS Match Maps" diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 64abbc5..165508c 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -144,7 +144,6 @@ class Tournament(models.Model): class Meta: abstract = True - ordering = ['name'] # this will not actually exist, as the Tournament class is abstract # it's just here for type checking @@ -194,8 +193,6 @@ class Match(models.Model): class Meta: abstract = True - ordering = ['-first_map_at'] - verbose_name_plural = "matches" def get_first_matchmap(self) -> 'MatchMap | None': return self.matchmap_set.order_by('starting_at').first() @@ -308,7 +305,6 @@ class MatchMap(models.Model): class Meta: abstract = True - ordering = ['starting_at'] def get_prev_map(self) -> 'Self | None': # Filter using self.__class__ to ensure we are working with the subclass From 9f5ed23a5112c9e5851327268ed7a53e88c45605 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:27:12 +0200 Subject: [PATCH 25/32] properly inherit abstract Meta classes https://docs.djangoproject.com/en/5.1/topics/db/models/#meta-inheritance --- csgomatches/models/cs_models.py | 13 ++++--------- csgomatches/models/global_models.py | 9 +++++++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index 8abc665..3821343 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -22,18 +22,15 @@ class CsTournament(global_models.Tournament): esea_bracket_id = models.IntegerField(null=True, blank=True) esea_bracket_team_ids = models.CharField(max_length=255, null=True, blank=True, help_text='Comma Separated') - class Meta: - ordering = ['name'] + class Meta(global_models.Tournament.Meta): verbose_name = "CS Tournament" verbose_name_plural = "CS Tournaments" class CsLineup(global_models.Lineup): - class Meta: + class Meta(global_models.Lineup.Meta): verbose_name = "CS Lineup" verbose_name_plural = "CS Lineups" - ordering = ['team__name', '-active_from'] - unique_together = ('team', 'active_from') class CsMap(global_models.Map): @@ -54,8 +51,7 @@ class CsMatch(global_models.OneOnOneMatch): matchmap_set: QuerySet["CsMatchMap"] - class Meta: - ordering = ['-first_map_at'] + class Meta(global_models.OneOnOneMatch.Meta): verbose_name = "CS Match" verbose_name_plural = "CS Matches" @@ -133,8 +129,7 @@ class CsMatchMap(global_models.OneOnOneMatchMap): map = models.ForeignKey(CsMap, on_delete=models.CASCADE, null=True, blank=True) map_pick_of = models.ForeignKey(CsLineup, null=True, blank=True, on_delete=models.CASCADE) - class Meta: - ordering = ['starting_at'] + class Meta(global_models.OneOnOneMatchMap.Meta): verbose_name = "CS Match Map" verbose_name_plural = "CS Match Maps" diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 165508c..884dfb0 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -94,6 +94,8 @@ class Lineup(models.Model): class Meta: abstract = True + ordering = ['team__name', '-active_from'] + unique_together = ('team', 'active_from') def get_previous_lineup(self) -> 'Lineup | None': return self.team.lineup_set.filter( @@ -144,6 +146,7 @@ class Tournament(models.Model): class Meta: abstract = True + ordering = ['name'] # this will not actually exist, as the Tournament class is abstract # it's just here for type checking @@ -193,6 +196,7 @@ class Match(models.Model): class Meta: abstract = True + ordering = ['-first_map_at'] def get_first_matchmap(self) -> 'MatchMap | None': return self.matchmap_set.order_by('starting_at').first() @@ -234,7 +238,7 @@ class OneOnOneMatch(Match): lineup_a = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_a_set', null=True, blank=True) lineup_b = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_b_set', null=True, blank=True) - class Meta: + class Meta(Match.Meta): abstract = True def __str__(self) -> str: @@ -305,6 +309,7 @@ class MatchMap(models.Model): class Meta: abstract = True + ordering = ['starting_at'] def get_prev_map(self) -> 'Self | None': # Filter using self.__class__ to ensure we are working with the subclass @@ -342,7 +347,7 @@ class OneOnOneMatchMap(MatchMap): rounds_won_team_b = models.IntegerField(default=0) map_pick_of = models.ForeignKey(Lineup, null=True, blank=True, on_delete=models.CASCADE) - class Meta: + class Meta(MatchMap.Meta): abstract = True def team_a_won(self) -> bool: From 76762cba4b6fffcbde484b4e9d663982e978fe67 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:34:46 +0200 Subject: [PATCH 26/32] Serializers and Views: Rename CSGO -> Cs --- csgomatches/drf_api/ser.py | 36 ++++++++++++++--------------- csgomatches/drf_api/views.py | 44 ++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/csgomatches/drf_api/ser.py b/csgomatches/drf_api/ser.py index 65f3a19..a880aa7 100644 --- a/csgomatches/drf_api/ser.py +++ b/csgomatches/drf_api/ser.py @@ -5,25 +5,25 @@ from . import ser_objects from csgomatches.models.cs_models import CSLineupPlayerRole -class CSGOTournamentSerializer(serializers.ModelSerializer): +class CsTournamentSerializer(serializers.ModelSerializer): class Meta: model = apps.get_model('csgomatches.CsTournament') fields = ['name', 'name_alt', 'name_hltv', 'name_99dmg', 'id'] -class CSGOTeamSerializer(serializers.ModelSerializer): +class TeamSerializer(serializers.ModelSerializer): class Meta: model = apps.get_model('csgomatches.Team') fields = ['name', 'name_long', 'name_alt', 'hltv_id', 'id'] -class CSGOPlayerShortSerializer(serializers.ModelSerializer): +class CsPlayerShortSerializer(serializers.ModelSerializer): class Meta: model = apps.get_model('csgomatches.CsPlayer') fields = ['ingame_name',] -class CSGOLineupPlayerSerializer(serializers.ModelSerializer): - player = CSGOPlayerShortSerializer() +class CsLineupPlayerSerializer(serializers.ModelSerializer): + player = CsPlayerShortSerializer() role = serializers.SerializerMethodField() class Meta: @@ -49,35 +49,35 @@ def to_internal_value(self, data): return internal_value -class CSGOLineupSerializer(serializers.ModelSerializer): - team = CSGOTeamSerializer(read_only=True) - players = CSGOLineupPlayerSerializer(many=True, read_only=True, source='lineupplayer_set') +class CsLineupSerializer(serializers.ModelSerializer): + team = TeamSerializer(read_only=True) + players = CsLineupPlayerSerializer(many=True, read_only=True, source='lineupplayer_set') class Meta: model = apps.get_model('csgomatches.CsLineup') fields = ['team', 'team_logo_url', 'active_from', 'players', 'id'] -class CSGOMapSerializer(serializers.ModelSerializer): +class CsMapSerializer(serializers.ModelSerializer): class Meta: model = apps.get_model('csgomatches.CsMap') fields = ['name', 'cs_name', 'id'] -class CSGOMatchMapSerializer(serializers.ModelSerializer): - map_pick_of = CSGOLineupSerializer(read_only=True) - played = CSGOMapSerializer(read_only=True) +class CsMatchMapSerializer(serializers.ModelSerializer): + map_pick_of = CsLineupSerializer(read_only=True) + played = CsMapSerializer(read_only=True) class Meta: model = apps.get_model('csgomatches.CsMatchMap') fields = ['rounds_won_team_a', 'rounds_won_team_b', 'starting_at', 'map_pick_of', 'map', 'id'] -class CSGOMatchSerializer(serializers.ModelSerializer): - tournament = CSGOTournamentSerializer(read_only=True) - lineup_a = CSGOLineupSerializer(read_only=True) - lineup_b = CSGOLineupSerializer(read_only=True) +class CsMatchSerializer(serializers.ModelSerializer): + tournament = CsTournamentSerializer(read_only=True) + lineup_a = CsLineupSerializer(read_only=True) + lineup_b = CsLineupSerializer(read_only=True) livescore_url = serializers.SerializerMethodField(read_only=True, source='get_livescore_url') html_detail_url = serializers.SerializerMethodField(read_only=True, source='get_html_detail_url') - matchmaps = CSGOMatchMapSerializer(many=True, source='matchmap_set') + matchmaps = CsMatchMapSerializer(many=True, source='matchmap_set') def get_livescore_url(self, obj): if obj.hltv_match_id: @@ -95,7 +95,7 @@ class Meta: fields = ['tournament', 'lineup_a', 'lineup_b', 'slug', 'bestof', 'first_map_at', 'cancelled', 'hltv_match_id', 'livescore_url', 'html_detail_url', 'matchmaps'] -class CSGOMatchMapUpdateSerializer(serializers.ModelSerializer): +class CsMatchMapUpdateSerializer(serializers.ModelSerializer): class Meta: model = apps.get_model('csgomatches.CsMatchMap') fields = ['map_nr', 'rounds_won_team_a', 'rounds_won_team_b',] diff --git a/csgomatches/drf_api/views.py b/csgomatches/drf_api/views.py index cba5d1a..6d340bb 100644 --- a/csgomatches/drf_api/views.py +++ b/csgomatches/drf_api/views.py @@ -13,16 +13,16 @@ from csgomatches.utils.scrapers.faceit import check_hubs_for_matches -class CSGOPagination(pagination.LimitOffsetPagination): +class CsPagination(pagination.LimitOffsetPagination): max_limit = 50 default_limit = 20 -class CSGOView(views.APIView): - pagination_class = CSGOPagination +class CsView(views.APIView): + pagination_class = CsPagination def get_renderers(self): - rend = super(CSGOView, self).get_renderers() + rend = super(CsView, self).get_renderers() for r in rend: if isinstance(r, renderers.BrowsableAPIRenderer): r.template = 'csgomatches/rest_framework/csgo_api.html' @@ -34,7 +34,7 @@ def name(self): return "API für " + self.request.build_absolute_uri() -class CSGOAPIRootView(CSGOView, routers.APIRootView): +class CsAPIRootView(CsView, routers.APIRootView): def get_view_description(self, html=False): if html: return mark_safe('

    Willkommen! API made by ' @@ -47,7 +47,7 @@ def get_view_name(self): return "Browse API" def get_renderers(self): - rend = super(CSGOAPIRootView, self).get_renderers() + rend = super(CsAPIRootView, self).get_renderers() for r in rend: if isinstance(r, renderers.BrowsableAPIRenderer): r.template = 'csgomatches/rest_framework/csgo_api.html' @@ -55,28 +55,28 @@ def get_renderers(self): return rend -class TeamViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): +class TeamViewSet(CsView, viewsets.ReadOnlyModelViewSet): """ - CSGO Teams + Cs Teams """ queryset = apps.get_model('csgomatches.Team').objects.all() - serializer_class = ser.CSGOTeamSerializer + serializer_class = ser.TeamSerializer def get_view_name(self): return 'Teams' -class TournamentViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): +class TournamentViewSet(CsView, viewsets.ReadOnlyModelViewSet): queryset = apps.get_model('csgomatches.CsTournament').objects.all() - serializer_class = ser.CSGOTournamentSerializer + serializer_class = ser.CsTournamentSerializer def get_view_name(self): return 'Tournaments' -class MatchViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): +class MatchViewSet(CsView, viewsets.ReadOnlyModelViewSet): queryset = apps.get_model('csgomatches.CsMatch').objects.all() - serializer_class = ser.CSGOMatchSerializer + serializer_class = ser.CsMatchSerializer def get_view_name(self): return 'Matches (all)' @@ -86,9 +86,9 @@ def list(self, request, *args, **kwargs): return super(MatchViewSet, self).list(request, *args, **kwargs) -class MatchUpcomingViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): +class MatchUpcomingViewSet(CsView, viewsets.ReadOnlyModelViewSet): queryset = apps.get_model('csgomatches.CsMatch').objects.filter(first_map_at__gte=timezone.now()) - serializer_class = ser.CSGOMatchSerializer + serializer_class = ser.CsMatchSerializer def get_view_name(self): return 'Matches (upcoming)' @@ -98,9 +98,9 @@ def list(self, request, *args, **kwargs): return super(MatchUpcomingViewSet, self).list(request, *args, **kwargs) -class LineupViewSet(CSGOView, viewsets.ReadOnlyModelViewSet): +class LineupViewSet(CsView, viewsets.ReadOnlyModelViewSet): queryset = apps.get_model('csgomatches.CsLineup').objects.all() - serializer_class = ser.CSGOLineupSerializer + serializer_class = ser.CsLineupSerializer def get_view_name(self): return 'Team Lineups' @@ -110,7 +110,7 @@ def list(self, request, *args, **kwargs): return super(LineupViewSet, self).list(request, *args, **kwargs) -class HLTVLiveScoreViewSet(CSGOView, mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet): +class HLTVLiveScoreViewSet(CsView, mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet): """ Get HLTV Live Score from any Match """ @@ -127,7 +127,7 @@ def get_queryset(self): def get_serializer(self, *args, **kwargs): ser_by_action = { - 'list': ser.CSGOMatchSerializer, + 'list': ser.CsMatchSerializer, 'retrieve': ser.HLTVMatchSerializer, 'create': ser.HLTVMatchSerializer, } @@ -147,13 +147,13 @@ def get_object(self): hltv_match_instance = ser_objects.HLTVMatch(hltv_match_id=hltv_match_id) return hltv_match_instance -class MatchMapUpdateView(CSGOView, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): +class MatchMapUpdateView(CsView, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): permission_classes = (permissions.IsAdminUser, ) authentication_classes = (authentication.BasicAuthentication, ) queryset = apps.get_model('csgomatches.CsMatchMap').objects.all() - serializer_class = ser.CSGOMatchMapUpdateSerializer + serializer_class = ser.CsMatchMapUpdateSerializer -class FaceitProLeagueMatchesView(CSGOView, viewsets.ViewSet): +class FaceitProLeagueMatchesView(CsView, viewsets.ViewSet): serializer_class = ser.FaceitProLeagueMatchesSerializer def get_instances(self): From cd2b68ed6b38f77b0626336931da38067485082b Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:36:47 +0100 Subject: [PATCH 27/32] fix class name --- csgomatches/drf_api/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csgomatches/drf_api/urls.py b/csgomatches/drf_api/urls.py index 792e11b..3652d08 100644 --- a/csgomatches/drf_api/urls.py +++ b/csgomatches/drf_api/urls.py @@ -2,7 +2,7 @@ from csgomatches.drf_api import views router = routers.DefaultRouter() -router.APIRootView = views.CSGOAPIRootView +router.APIRootView = views.CsAPIRootView router.register(r'team', viewset=views.TeamViewSet) router.register(r'tournament', viewset=views.TournamentViewSet) router.register(r'lineup', viewset=views.LineupViewSet) From 11c37fe8a18617ed8a8f5d05b554f39b131cfdb4 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:37:27 +0100 Subject: [PATCH 28/32] fix related_name property for lineup_set and matchmap_set in CS implementations --- .../migrations/0064_alter_csmatchmap_match.py | 19 +++++++++++++++++++ .../migrations/0065_alter_cslineup_team.py | 19 +++++++++++++++++++ csgomatches/models/cs_models.py | 5 +++-- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 csgomatches/migrations/0064_alter_csmatchmap_match.py create mode 100644 csgomatches/migrations/0065_alter_cslineup_team.py diff --git a/csgomatches/migrations/0064_alter_csmatchmap_match.py b/csgomatches/migrations/0064_alter_csmatchmap_match.py new file mode 100644 index 0000000..77d7d80 --- /dev/null +++ b/csgomatches/migrations/0064_alter_csmatchmap_match.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1 on 2024-12-02 11:33 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0063_alter_csmatch_options_alter_csmatchmap_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='csmatchmap', + name='match', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='matchmap_set', to='csgomatches.csmatch'), + ), + ] diff --git a/csgomatches/migrations/0065_alter_cslineup_team.py b/csgomatches/migrations/0065_alter_cslineup_team.py new file mode 100644 index 0000000..4553143 --- /dev/null +++ b/csgomatches/migrations/0065_alter_cslineup_team.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1 on 2024-12-02 11:35 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('csgomatches', '0064_alter_csmatchmap_match'), + ] + + operations = [ + migrations.AlterField( + model_name='cslineup', + name='team', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lineup_set', to='csgomatches.team'), + ), + ] diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index 3821343..b6a1ef5 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -28,6 +28,7 @@ class Meta(global_models.Tournament.Meta): class CsLineup(global_models.Lineup): + team = models.ForeignKey(global_models.Team, on_delete=models.CASCADE, related_name='lineup_set') class Meta(global_models.Lineup.Meta): verbose_name = "CS Lineup" verbose_name_plural = "CS Lineups" @@ -49,7 +50,7 @@ class CsMatch(global_models.OneOnOneMatch): enable_99dmg = models.BooleanField(default=False) enable_hltv = models.BooleanField(default=True) - matchmap_set: QuerySet["CsMatchMap"] + matchmap_set: QuerySet['CsMatchMap'] class Meta(global_models.OneOnOneMatch.Meta): verbose_name = "CS Match" @@ -125,7 +126,7 @@ def save(self, *args, **kwargs): super().save(*args, **kwargs) class CsMatchMap(global_models.OneOnOneMatchMap): - match = models.ForeignKey(CsMatch, on_delete=models.CASCADE) + match = models.ForeignKey(CsMatch, on_delete=models.CASCADE, related_name='matchmap_set') map = models.ForeignKey(CsMap, on_delete=models.CASCADE, null=True, blank=True) map_pick_of = models.ForeignKey(CsLineup, null=True, blank=True, on_delete=models.CASCADE) From 823deb448abc780ff8437b9ec7c646dc41119bd8 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:38:06 +0100 Subject: [PATCH 29/32] fix matchmap_set type annotation in Match model --- csgomatches/models/global_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 884dfb0..04738a3 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -192,7 +192,7 @@ class Match(models.Model): last_tweet = models.DateTimeField(null=True, blank=True) last_tweet_id = models.CharField(max_length=255, null=True, blank=True) - matchmap_set: QuerySet['OneOnOneMatchMap'] + matchmap_set: QuerySet['MatchMap'] class Meta: abstract = True @@ -238,6 +238,8 @@ class OneOnOneMatch(Match): lineup_a = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_a_set', null=True, blank=True) lineup_b = models.ForeignKey(Lineup, on_delete=models.CASCADE, related_name='matches_as_lineup_b_set', null=True, blank=True) + matchmap_set: QuerySet['OneOnOneMatchMap'] + class Meta(Match.Meta): abstract = True From 2a4eb1b68dc58cf6bfcfd34537800177499e9582 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:55:55 +0100 Subject: [PATCH 30/32] fix save method to use the correct model type for fetching previous instance --- csgomatches/models/global_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csgomatches/models/global_models.py b/csgomatches/models/global_models.py index 04738a3..8636813 100644 --- a/csgomatches/models/global_models.py +++ b/csgomatches/models/global_models.py @@ -413,7 +413,7 @@ def send_tweet(self, prev_instance=None, interval=180.) -> None: def save(self, *args, **kwargs): prev_instance = None if self.pk: - prev_instance = MatchMap.objects.get(pk=self.pk) + prev_instance = type(self).objects.get(pk=self.pk) super(MatchMap, self).save(*args, **kwargs) first_matchmap = self.match.get_first_matchmap() From 4f1908ea31344c8955e65b3ea19678df0f6adb0c Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:56:11 +0100 Subject: [PATCH 31/32] migrate context_data scorelines to CS2 values --- .../templates/csgomatches/base/base.html | 8 ++++---- csgomatches/views.py | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/csgomatches/templates/csgomatches/base/base.html b/csgomatches/templates/csgomatches/base/base.html index 0301af0..b8272ea 100644 --- a/csgomatches/templates/csgomatches/base/base.html +++ b/csgomatches/templates/csgomatches/base/base.html @@ -691,10 +691,10 @@

    {% block content_header_h1 %}Wann spiel {% if statistics %}

    Fun-Facts:


    {% endif %} diff --git a/csgomatches/views.py b/csgomatches/views.py index bbccc5b..38e9b1b 100644 --- a/csgomatches/views.py +++ b/csgomatches/views.py @@ -35,25 +35,25 @@ def get_context_data(self, *args, **kwargs): ctx = super(IndexView, self).get_context_data(*args, **kwargs) big = models.Team.objects.get(name="BIG") statistics = { - 'last_sixteen_zero': models.MatchMap.objects.filter( + 'last_thirteen_zero': models.CsMatchMap.objects.filter( match__lineup_a__team=big, - rounds_won_team_a=16, + rounds_won_team_a=13, rounds_won_team_b=0 ).order_by('-starting_at').first(), - 'last_zero_sixteen': models.MatchMap.objects.filter( + 'last_zero_thirteen': models.CsMatchMap.objects.filter( match__lineup_a__team=big, rounds_won_team_a=0, - rounds_won_team_b=16 + rounds_won_team_b=13 ).order_by('-starting_at').first(), - 'last_sixteen_fourteen': models.MatchMap.objects.filter( + 'last_thirteen_eleven': models.CsMatchMap.objects.filter( match__lineup_a__team=big, - rounds_won_team_a=16, - rounds_won_team_b=14 + rounds_won_team_a=13, + rounds_won_team_b=11 ).order_by('-starting_at').first(), - 'last_fourteen_sixteen': models.MatchMap.objects.filter( + 'last_eleven_thirteen': models.CsMatchMap.objects.filter( match__lineup_a__team=big, - rounds_won_team_a=14, - rounds_won_team_b=16 + rounds_won_team_a=11, + rounds_won_team_b=13 ).order_by('-starting_at').first(), } ctx.update({ From 1d81700eb8ca6062c065e8d3c8190c9a2220c332 Mon Sep 17 00:00:00 2001 From: Ian Paul Falatik <63957260+ifalatik@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:58:27 +0100 Subject: [PATCH 32/32] add new line --- csgomatches/models/cs_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/csgomatches/models/cs_models.py b/csgomatches/models/cs_models.py index b6a1ef5..3e9eb77 100644 --- a/csgomatches/models/cs_models.py +++ b/csgomatches/models/cs_models.py @@ -125,6 +125,7 @@ def __str__(self): def save(self, *args, **kwargs): super().save(*args, **kwargs) + class CsMatchMap(global_models.OneOnOneMatchMap): match = models.ForeignKey(CsMatch, on_delete=models.CASCADE, related_name='matchmap_set') map = models.ForeignKey(CsMap, on_delete=models.CASCADE, null=True, blank=True)