Skip to content

Commit d93ad0d

Browse files
author
chris0chris
committed
feat: add teamOfficials and gameInfo to endpoint #163
also refactor gameinfo_wrapper.py to raise ValueError
1 parent f9afb42 commit d93ad0d

File tree

10 files changed

+113
-24
lines changed

10 files changed

+113
-24
lines changed

gamedays/api/game_views.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def get(self, request: Request, *args, **kwargs):
2323
try:
2424
gamelog = GameService(game_id).get_gamelog()
2525
return Response(json.loads(gamelog.as_json(), object_pairs_hook=OrderedDict))
26-
except Gameinfo.DoesNotExist:
26+
except ValueError:
2727
raise NotFound(detail=f'No game found for gameId {game_id}')
2828
gamelog = Gameinfo.objects.filter(id=game_id).annotate(
2929
home_id=GameresultHelper.get_gameresult_team_subquery(is_home=True, team_column='id'),
@@ -55,7 +55,7 @@ def post(self, request, *args, **kwargs):
5555
gamelog = game_service.create_gamelog(data.get('team'), data.get('event'), request.user, data.get('half'))
5656
game_service.update_score(gamelog)
5757
return Response(json.loads(gamelog.as_json(), object_pairs_hook=OrderedDict), status=HTTPStatus.CREATED)
58-
except Gameinfo.DoesNotExist:
58+
except ValueError:
5959
raise NotFound(detail=f'Could not create team logs ... gameId {request.data.get("gameId")} not found')
6060
except Team.DoesNotExist:
6161
raise NotFound(detail=f'Could not create team logs ... team {request.data.get("team")} not found')
@@ -67,7 +67,7 @@ def delete(self, request: Request, *args, **kwargs):
6767
gamelog = game_service.delete_gamelog(request.data.get('sequence'))
6868
game_service.update_score(gamelog)
6969
return Response(json.loads(gamelog.as_json(), object_pairs_hook=OrderedDict), status=HTTPStatus.OK)
70-
except Gameinfo.DoesNotExist:
70+
except ValueError:
7171
raise NotFound(detail=f'Could not delete team logs ... gameId {game_id} not found')
7272

7373

gamedays/service/wrapper/gameinfo_wrapper.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
from datetime import datetime
22

3-
from gamedays.models import Gameinfo
3+
from django.db.models import Subquery, OuterRef
4+
5+
from gamedays.models import Gameinfo, Gameresult
6+
from league_manager.utils.view_utils import UserRequestPermission
47

58

69
class GameinfoWrapper(object):
7-
def __init__(self, game_id):
8-
self.gameinfo = Gameinfo.objects.get(id=game_id)
10+
def __init__(self, game_id, user_permission: UserRequestPermission = UserRequestPermission()):
11+
self.user_permission = user_permission
12+
try:
13+
self.gameinfo = Gameinfo.objects.get(id=game_id)
14+
except Gameinfo.DoesNotExist:
15+
raise ValueError(f'No entry found for game_id: {game_id}')
916

1017
def set_halftime_to_now(self):
1118
self.gameinfo.status = '2. Halbzeit'
@@ -25,3 +32,18 @@ def set_game_finished_to_now(self):
2532
def update_team_in_possession(self, team_name):
2633
self.gameinfo.in_possession = team_name
2734
self.gameinfo.save()
35+
36+
def get_game_info_with_home_and_away(self):
37+
return Gameinfo.objects.filter(id=self.gameinfo.pk).distinct().annotate(
38+
home=self._get_gameresult_team_subquery(is_home=True, team_column='team__description'),
39+
away=self._get_gameresult_team_subquery(is_home=False, team_column='team__description'),
40+
)
41+
42+
# noinspection PyMethodMayBeStatic
43+
def _get_gameresult_team_subquery(self, is_home: bool, team_column: str):
44+
return Subquery(
45+
Gameresult.objects.filter(
46+
gameinfo=OuterRef('id'),
47+
isHome=is_home
48+
).values(team_column)[:1]
49+
)

gamedays/tests/service/test_game_service.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
class TestGameService(TestCase):
1212
def test_game_not_available(self):
13-
with self.assertRaises(Gameinfo.DoesNotExist):
13+
with self.assertRaises(ValueError):
1414
GameService(1)
1515

1616
def test_update_game_by_halftime(self):

officials/api/views.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from officials.api.serializers import OfficialTeamListScorecardSerializer
1010
from officials.models import Official
11+
from officials.service.official_service import OfficialService
1112

1213

1314
class OfficialsTeamListAPIView(APIView):
@@ -16,11 +17,8 @@ class OfficialsTeamListAPIView(APIView):
1617
# noinspection PyMethodMayBeStatic
1718
def get(self, request, **kwargs):
1819
team_id = kwargs.get('pk')
19-
officials = Official.objects.filter(team_id=team_id).order_by('first_name', 'last_name').values(
20-
*OfficialTeamListScorecardSerializer.ALL_FIELD_VALUES
21-
)
22-
serializer = OfficialTeamListScorecardSerializer(instance=officials, many=True)
23-
return Response(serializer.data, status=HTTPStatus.OK)
20+
official_service = OfficialService()
21+
return Response(official_service.get_team_officials_by_team_id(team_id), status=HTTPStatus.OK)
2422

2523

2624
class OfficialsSearchName(APIView):

officials/service/official_service.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from django.db.models import Sum
22

3+
from gamedays.models import Gameinfo
34
from gamedays.service.team_repository_service import TeamRepositoryService
4-
from officials.api.serializers import OfficialGameCountSerializer
5+
from officials.api.serializers import OfficialGameCountSerializer, OfficialTeamListScorecardSerializer
56
from officials.models import Official
67
from officials.service.game_official_entries import InternalGameOfficialEntry, ExternalGameOfficialEntry
78
from officials.service.moodle.moodle_service import MoodleService
@@ -68,3 +69,14 @@ def _aggregate_games(self, external_official_qs):
6869
all_external_games_count = external_official_qs.aggregate(num_games=Sum('number_games')).get('num_games',
6970
0) or 0
7071
return all_external_games_count
72+
73+
def get_team_officials_by_gameinfo(self, gameinfo_id):
74+
gameinfo = Gameinfo.objects.get(pk=gameinfo_id)
75+
return self.get_team_officials_by_team_id(gameinfo.officials.pk)
76+
77+
def get_team_officials_by_team_id(self, team_id):
78+
officials = Official.objects.filter(team_id=team_id).order_by('first_name', 'last_name').values(
79+
*OfficialTeamListScorecardSerializer.ALL_FIELD_VALUES
80+
)
81+
return OfficialTeamListScorecardSerializer(instance=officials, many=True).data
82+

scorecard2/api/serializers.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,39 @@ class ScorecardGameinfoSerializer(Serializer):
99
ID_C = 'id'
1010
FIELD_C = 'field'
1111
SCHEDULED_C = 'scheduled'
12+
STAGE_C = 'stage'
13+
STANDING_C = 'standing'
1214
GAME_FINISHED_C = 'gameFinished'
1315
OFFICIALS_ID_C = 'officials_id'
1416
OFFICIALS_DESC_C = 'officials__description'
1517
HOME_C = 'home'
1618
AWAY_C = 'away'
1719

18-
ALL_FIELD_VALUES = [ID_C, FIELD_C, SCHEDULED_C, OFFICIALS_ID_C, OFFICIALS_DESC_C, HOME_C, AWAY_C, GAME_FINISHED_C]
20+
ALL_FIELD_VALUES = [ID_C, FIELD_C, SCHEDULED_C, OFFICIALS_ID_C, OFFICIALS_DESC_C, HOME_C, STAGE_C, STANDING_C, AWAY_C, GAME_FINISHED_C]
21+
ALL_GAME_OVERVIEW_VALUES = [ID_C, FIELD_C, SCHEDULED_C, OFFICIALS_ID_C, OFFICIALS_DESC_C, HOME_C, AWAY_C, GAME_FINISHED_C]
22+
ALL_SETUP_VALUES = [FIELD_C, SCHEDULED_C, HOME_C, STAGE_C, STANDING_C, AWAY_C]
1923

2024
id = IntegerField()
2125
field = IntegerField()
26+
stage = CharField()
27+
standing = CharField()
2228
scheduled = TimeField(format='%H:%M')
2329
isFinished = SerializerMethodField()
2430
officialsId = IntegerField(source=OFFICIALS_ID_C)
2531
officials = CharField(source=OFFICIALS_DESC_C)
2632
home = CharField()
2733
away = CharField()
2834

35+
def __init__(self, *args, **kwargs):
36+
fields = kwargs.pop('fields', None)
37+
super().__init__(*args, **kwargs)
38+
39+
if fields:
40+
allowed = set(fields)
41+
existing = set(self.fields.keys())
42+
for field_name in existing - allowed:
43+
self.fields.pop(field_name)
44+
2945
def get_isFinished(self, obj: dict) -> bool:
3046
return obj.get(self.GAME_FINISHED_C) is not None
3147

@@ -41,7 +57,7 @@ class ScorecardGamedaySerializer(Serializer):
4157
id = IntegerField()
4258
date = DateField(format='%d.%m.%Y')
4359
name = CharField()
44-
games = ScorecardGameinfoSerializer(many=True)
60+
games = ScorecardGameinfoSerializer(many=True, fields=ScorecardGameinfoSerializer.ALL_GAME_OVERVIEW_VALUES)
4561

4662

4763
class GameOfficialSerializer(ModelSerializer):

scorecard2/api/urls.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from django.urls import path
22

33
from scorecard2.api.views import SpecificGamedayAndGamesToOfficiateAPIView, GameOfficialCreateOrUpdateView, \
4-
GamedaysAndGamesToOfficiateAPIView, ConfigKickoffGameAPIView
4+
GamedaysAndGamesToOfficiateAPIView, GameSetupAPIView
55

66
API_SCORECARD_SPECIFIC_GAMEDAY_AND_GAMES_TO_OFFICIATE = 'api-scorecard-specific-gameday-and-games-to-officiate'
77
API_SCORECARD_GAMEDAYS_AND_GAMES_TO_OFFICIATE = 'api-scorecard-gamedays-and-games-to-officiate'
88
API_SCORECARD_GAME_OFFICIALS = 'api-scorecard-game-officials'
9-
API_SCORECARD_CONFIG_KICKOFF_GAME = 'api-scorecard-config-kickoff-game'
9+
API_SCORECARD_GAME_SETUP = 'api-scorecard-game-setup'
1010

1111
urlpatterns = [
1212
path('gameday/list', GamedaysAndGamesToOfficiateAPIView.as_view(),
@@ -15,5 +15,5 @@
1515
name=API_SCORECARD_SPECIFIC_GAMEDAY_AND_GAMES_TO_OFFICIATE),
1616
path('game/<int:pk>/officials', GameOfficialCreateOrUpdateView.as_view(),
1717
name=API_SCORECARD_GAME_OFFICIALS),
18-
path('config/kickoff/game/<int:pk>', ConfigKickoffGameAPIView.as_view(), name=API_SCORECARD_CONFIG_KICKOFF_GAME)
18+
path('game/<int:pk>/setup', GameSetupAPIView.as_view(), name=API_SCORECARD_GAME_SETUP)
1919
]

scorecard2/api/views.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
from http import HTTPStatus
22

3+
from rest_framework import permissions
34
from rest_framework.exceptions import NotFound, PermissionDenied
45
from rest_framework.generics import RetrieveUpdateAPIView
56
from rest_framework.response import Response
67
from rest_framework.views import APIView
78

89
from gamedays.models import GameOfficial
910
from league_manager.utils.decorators import get_user_request_permission
11+
from officials.service.official_service import OfficialService
1012
from scorecard2.api.serializers import GameOfficialSerializer
11-
from scorecard2.service.scorecard_service import ScorecardGamedayService, ScorecardConfigService
13+
from scorecard2.service.scorecard_service import ScorecardGamedayService, ScorecardConfigService, ScorecardGameService
1214

1315

1416
class SpecificGamedayAndGamesToOfficiateAPIView(APIView):
@@ -58,14 +60,23 @@ def update(self, request, *args, **kwargs):
5860
return Response(response_data, status=HTTPStatus.OK)
5961

6062

61-
class ConfigKickoffGameAPIView(APIView):
63+
class GameSetupAPIView(APIView):
64+
permission_classes = (permissions.IsAuthenticated,)
65+
66+
@get_user_request_permission
6267
# noinspection PyMethodMayBeStatic
63-
def get(self, **kwargs):
68+
def get(self, request, **kwargs):
6469
gameinfo_id = kwargs.get('pk')
70+
user_permission = kwargs.get('user_permission')
6571
scorecard_config_service = ScorecardConfigService(gameinfo_id)
72+
official_service = OfficialService()
73+
gameinfo_service = ScorecardGameService(gameinfo_id, user_permission)
6674
try:
6775
return Response(
68-
scorecard_config_service.get_kickoff_config(),
76+
{"scorecard": scorecard_config_service.get_kickoff_config(),
77+
"teamOfficials": official_service.get_team_officials_by_gameinfo(gameinfo_id),
78+
"gameInfo": gameinfo_service.get_game_info(),
79+
},
6980
status=HTTPStatus.OK
7081
)
7182
except PermissionError as exception:

scorecard2/service/scorecard_service.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import datetime
22

33
from django.conf import settings
4+
from django.db.models import QuerySet
45

56
from gamedays.models import Gameday, Team, EmptyTeam, Gameinfo
67
from gamedays.service.gameday_service import GamedayService
78
from gamedays.service.team_service import TeamService
9+
from gamedays.service.wrapper.gameinfo_wrapper import GameinfoWrapper
810
from league_manager.utils.view_utils import UserRequestPermission
911
from scorecard2.api.serializers import ScorecardGameinfoSerializer, ScorecardGamedaySerializer, \
1012
ScorecardConfigSerializer
@@ -33,7 +35,7 @@ def _merge_gamedays_and_gameinfos(self, gamedays, gameinfo):
3335
for current_gameday in all_gamdays:
3436
current_gameday[ScorecardGamedaySerializer.GAMES_C] = list(
3537
gameinfo.filter(gameday=current_gameday[ScorecardGamedaySerializer.ID_C]).values(
36-
*ScorecardGameinfoSerializer.ALL_FIELD_VALUES)
38+
*ScorecardGameinfoSerializer.ALL_GAME_OVERVIEW_VALUES)
3739
)
3840
return all_gamdays
3941

@@ -58,6 +60,18 @@ def _get_gamedays_for_team(self, officiating_team: Team):
5860
return gamedays
5961

6062

63+
class ScorecardGameService:
64+
def __init__(self, gameinfo_id: int, user_permission: UserRequestPermission):
65+
self.user_permission = user_permission
66+
self.gameinfo_wrapper = GameinfoWrapper(gameinfo_id, user_permission)
67+
68+
def get_game_info(self):
69+
gameinfo: QuerySet[Gameinfo] = self.gameinfo_wrapper.get_game_info_with_home_and_away()
70+
return ScorecardGameinfoSerializer(
71+
instance=gameinfo.values(*ScorecardGameinfoSerializer.ALL_SETUP_VALUES).first(),
72+
fields=ScorecardGameinfoSerializer.ALL_SETUP_VALUES).data
73+
74+
6175
class ScorecardConfigService:
6276
def __init__(self, gameinfo_id):
6377
self.gameinfo_id = gameinfo_id

scorecard2/tests/service/test_scorecard_service.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from gamedays.tests.setup_factories.db_setup import DBSetup
55
from league_manager.utils.view_utils import UserRequestPermission
66
from scorecard2.api.serializers import ScorecardConfigSerializer
7-
from scorecard2.service.scorecard_service import ScorecardGamedayService, ScorecardConfigService
7+
from scorecard2.service.scorecard_service import ScorecardGamedayService, ScorecardConfigService, ScorecardGameService
88
from scorecard2.tests.setup_factories.db_setup import DbSetupScorecard
99

1010

@@ -84,3 +84,19 @@ def test_get_kickoff_config_success(self):
8484

8585
expected_data = ScorecardConfigSerializer(instance=scorecard_config).data
8686
self.assertEqual(result, expected_data)
87+
88+
89+
class ScorecardGameServiceTest(TestCase):
90+
def test_game_info_correct_retrieved(self):
91+
DBSetup().g62_status_empty()
92+
gameinfo = Gameinfo.objects.first()
93+
scorecard_game_service = ScorecardGameService(gameinfo_id=gameinfo.pk,
94+
user_permission=UserRequestPermission(is_staff=True))
95+
result = scorecard_game_service.get_game_info()
96+
assert result == {'field': 1, 'stage': 'Vorrunde', 'standing': 'Gruppe 1', 'scheduled': '10:00',
97+
'home': 'AAAAAAA1', 'away': 'AAAAAAA2'}
98+
99+
def test_raises_value_error_if_gameinfo_id_not_found(self):
100+
with self.assertRaises(ValueError) as error:
101+
ScorecardGameService(gameinfo_id=-1, user_permission=UserRequestPermission(is_staff=True))
102+
assert str(error.exception) == 'No entry found for game_id: -1'

0 commit comments

Comments
 (0)