Copmletely rejigger sports to accomodate tennis

This commit is contained in:
2023-01-22 18:27:33 -05:00
parent 506de848d7
commit 49bf57dd58
11 changed files with 604 additions and 54 deletions

View File

@ -119,7 +119,7 @@ def create_jellyfin_scrobble_dict(data_dict: dict, user_id: int) -> dict:
"timestamp": parse(data_dict.get("UtcTimestamp")),
"playback_position_ticks": playback_position_ticks,
"playback_position": playback_position,
"source": data_dict.get("ClientName", "Jellyfin"),
"source": data_dict.get("ClientName", "Vrobbler"),
"source_id": data_dict.get('MediaSourceId'),
"jellyfin_status": jellyfin_status,
}

View File

@ -4,7 +4,7 @@ from dateutil.parser import parse
from django.conf import settings
from django.utils import timezone
from pysportsdb import TheSportsDbClient
from sports.models import SportEvent
from sports.models import Sport
logger = logging.getLogger(__name__)
@ -15,18 +15,23 @@ client = TheSportsDbClient(api_key=API_KEY)
def lookup_event_from_thesportsdb(event_id: str) -> dict:
event = client.lookup_event(event_id)['events'][0]
if not event or type(event) != dict:
return {}
league = {} # client.lookup_league(league_id=event.get('idLeague'))
event_type = SportEvent.Type.GAME
run_time_seconds = 11700
run_time_ticks = run_time_seconds * 1000
event_type = "Game"
sport = Sport.objects.filter(thesportsdb_id=event.get('strSport')).first()
logger.debug(event)
data_dict = {
"ItemType": event_type,
"Name": event.get('strEventAlternate'),
"ItemType": sport.default_event_type,
"Name": event.get('strEvent'),
"AltName": event.get('strEventAlternate'),
"Start": parse(event.get('strTimestamp')),
"Provider_thesportsdb": event.get('idEvent'),
"RunTime": run_time_seconds,
"RunTimeTicks": run_time_ticks,
"RunTime": sport.default_event_run_time,
"RunTimeTicks": sport.default_event_run_time_ticks,
"Sport": event.get('strSport'),
"Season": event.get('strSeason'),
"LeagueId": event.get('idLeague'),
"LeagueName": event.get('strLeague'),
"HomeTeamId": event.get('idHomeTeam'),
@ -39,6 +44,7 @@ def lookup_event_from_thesportsdb(event_id: str) -> dict:
"UtcTimestamp": timezone.now().strftime('%Y-%m-%d %H:%M:%S.%f%z'),
"IsPaused": False,
"PlayedToCompletion": False,
"Source": "Vrobbler",
}
return data_dict

View File

@ -1,10 +1,24 @@
from django.contrib import admin
from sports.models import League, SportEvent, Team
from sports.models import (
League,
Player,
Round,
Season,
Sport,
SportEvent,
Team,
)
from scrobbles.admin import ScrobbleInline
@admin.register(Sport)
class SportAdmin(admin.ModelAdmin):
date_hierarchy = "created"
ordering = ("name",)
@admin.register(League)
class LeagueAdmin(admin.ModelAdmin):
date_hierarchy = "created"
@ -12,6 +26,27 @@ class LeagueAdmin(admin.ModelAdmin):
ordering = ("name",)
@admin.register(Player)
class PlayerAdmin(admin.ModelAdmin):
date_hierarchy = "created"
list_display = ("name", "league", "team")
ordering = ("name",)
@admin.register(Season)
class SeasonAdmin(admin.ModelAdmin):
date_hierarchy = "created"
list_display = ("name", "league")
ordering = ("name",)
@admin.register(Round)
class RoundAdmin(admin.ModelAdmin):
date_hierarchy = "created"
list_display = ("name", "season")
ordering = ("name",)
@admin.register(Team)
class TeamAdmin(admin.ModelAdmin):
date_hierarchy = "created"
@ -26,12 +61,17 @@ class SportEventAdmin(admin.ModelAdmin):
"title",
"event_type",
"start",
"home_team",
"away_team",
"season",
"comp_str",
"round",
)
list_filter = ("season", "home_team", "away_team")
list_filter = ("round__season", "home_team", "away_team")
ordering = ("-created",)
inlines = [
ScrobbleInline,
]
def comp_str(self, obj):
if obj.home_team:
return f'{obj.away_team} @ {obj.home_team}'
if obj.player_one:
return f'{obj.player_one} v {obj.player_two}'

View File

@ -0,0 +1,71 @@
# Generated by Django 4.1.5 on 2023-01-21 04:21
from django.db import migrations, models
import django.db.models.deletion
import django_extensions.db.fields
import uuid
class Migration(migrations.Migration):
dependencies = [
('sports', '0002_rename_start_utc_sportevent_start'),
]
operations = [
migrations.CreateModel(
name='Sport',
fields=[
(
'id',
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'created',
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name='created'
),
),
(
'modified',
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name='modified'
),
),
('name', models.CharField(max_length=255)),
(
'uuid',
models.UUIDField(
blank=True,
default=uuid.uuid4,
editable=False,
null=True,
),
),
('thesportsdb_id', models.IntegerField(blank=True, null=True)),
(
'default_event_run_time',
models.IntegerField(blank=True, null=True),
),
],
options={
'get_latest_by': 'modified',
'abstract': False,
},
),
migrations.AddField(
model_name='league',
name='sport',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.sport',
),
),
]

View File

@ -0,0 +1,254 @@
# Generated by Django 4.1.5 on 2023-01-22 20:20
from django.db import migrations, models
import django.db.models.deletion
import django_extensions.db.fields
import uuid
class Migration(migrations.Migration):
dependencies = [
('sports', '0003_sport_league_sport'),
]
operations = [
migrations.AlterModelOptions(
name='league',
options={},
),
migrations.AlterModelOptions(
name='sport',
options={},
),
migrations.AlterModelOptions(
name='team',
options={},
),
migrations.RemoveField(
model_name='sportevent',
name='league',
),
migrations.AlterField(
model_name='league',
name='thesportsdb_id',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='sport',
name='thesportsdb_id',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='team',
name='thesportsdb_id',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.CreateModel(
name='Season',
fields=[
(
'id',
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'created',
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name='created'
),
),
(
'modified',
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name='modified'
),
),
('name', models.CharField(max_length=255)),
(
'uuid',
models.UUIDField(
blank=True,
default=uuid.uuid4,
editable=False,
null=True,
),
),
(
'thesportsdb_id',
models.CharField(blank=True, max_length=255, null=True),
),
(
'league',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.league',
),
),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Round',
fields=[
(
'id',
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'created',
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name='created'
),
),
(
'modified',
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name='modified'
),
),
('name', models.CharField(max_length=255)),
(
'uuid',
models.UUIDField(
blank=True,
default=uuid.uuid4,
editable=False,
null=True,
),
),
(
'thesportsdb_id',
models.CharField(blank=True, max_length=255, null=True),
),
(
'season',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.season',
),
),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Player',
fields=[
(
'id',
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'created',
django_extensions.db.fields.CreationDateTimeField(
auto_now_add=True, verbose_name='created'
),
),
(
'modified',
django_extensions.db.fields.ModificationDateTimeField(
auto_now=True, verbose_name='modified'
),
),
('name', models.CharField(max_length=255)),
(
'uuid',
models.UUIDField(
blank=True,
default=uuid.uuid4,
editable=False,
null=True,
),
),
(
'thesportsdb_id',
models.CharField(blank=True, max_length=255, null=True),
),
(
'league',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.league',
),
),
(
'team',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.team',
),
),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='sportevent',
name='player_one',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name='player_one_set',
to='sports.player',
),
),
migrations.AddField(
model_name='sportevent',
name='player_two',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name='player_two_set',
to='sports.player',
),
),
migrations.AddField(
model_name='sportevent',
name='round',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.round',
),
),
migrations.AlterField(
model_name='sportevent',
name='season',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sports.season',
),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 4.1.5 on 2023-01-22 20:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('sports', '0004_alter_league_options_alter_sport_options_and_more'),
]
operations = [
migrations.RemoveField(
model_name='sportevent',
name='season',
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.1.5 on 2023-01-22 21:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sports', '0005_remove_sportevent_season'),
]
operations = [
migrations.AlterField(
model_name='sportevent',
name='event_type',
field=models.CharField(
choices=[('UK', 'Unknown'), ('GA', 'Game'), ('MA', 'Match')],
default='UK',
max_length=2,
),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.1.5 on 2023-01-22 22:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sports', '0006_alter_sportevent_event_type'),
]
operations = [
migrations.AddField(
model_name='sport',
name='default_event_type',
field=models.CharField(
choices=[('UK', 'Unknown'), ('GA', 'Game'), ('MA', 'Match')],
default='UK',
max_length=2,
),
),
]

View File

@ -7,52 +7,93 @@ from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django_extensions.db.models import TimeStampedModel
from sqlalchemy import update
from scrobbles.mixins import ScrobblableMixin
from vrobbler.apps.sports.utils import (
get_players_from_event,
get_round_name_from_event,
)
logger = logging.getLogger(__name__)
BNULL = {"blank": True, "null": True}
class League(TimeStampedModel):
class SportEventType(models.TextChoices):
UNKNOWN = 'UK', _('Unknown')
GAME = 'GA', _('Game')
MATCH = 'MA', _('Match')
class TheSportsDbMixin(TimeStampedModel):
name = models.CharField(max_length=255)
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
logo = models.ImageField(upload_to="sports/league-logos/", **BNULL)
abbreviation_str = models.CharField(max_length=10, **BNULL)
thesportsdb_id = models.IntegerField(**BNULL)
thesportsdb_id = models.CharField(max_length=255, **BNULL)
class Meta:
abstract = True
def __str__(self):
return self.name
class Sport(TheSportsDbMixin):
default_event_run_time = models.IntegerField(**BNULL)
default_event_type = models.CharField(
max_length=2,
choices=SportEventType.choices,
default=SportEventType.UNKNOWN,
)
# TODO Add these to the default run_time for Football
# run_time_seconds = 11700
# run_time_ticks = run_time_seconds * 1000
@property
def default_event_run_time_ticks(self):
return self.default_event_run_time * 1000
class League(TheSportsDbMixin):
logo = models.ImageField(upload_to="sports/league-logos/", **BNULL)
abbreviation_str = models.CharField(max_length=10, **BNULL)
sport = models.ForeignKey(Sport, on_delete=models.DO_NOTHING, **BNULL)
@property
def abbreviation(self):
return self.abbreviation_str
class Team(TimeStampedModel):
name = models.CharField(max_length=255)
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
class Season(TheSportsDbMixin):
league = models.ForeignKey(League, on_delete=models.DO_NOTHING, **BNULL)
thesportsdb_id = models.IntegerField(**BNULL)
def __str__(self):
return self.name
return f'{self.name} season of {self.league}'
class Team(TheSportsDbMixin):
league = models.ForeignKey(League, on_delete=models.DO_NOTHING, **BNULL)
class Player(TheSportsDbMixin):
league = models.ForeignKey(League, on_delete=models.DO_NOTHING, **BNULL)
team = models.ForeignKey(Team, on_delete=models.DO_NOTHING, **BNULL)
class Round(TheSportsDbMixin):
season = models.ForeignKey(Season, on_delete=models.DO_NOTHING, **BNULL)
def __str__(self):
return f'{self.name} of {self.season}'
class SportEvent(ScrobblableMixin):
COMPLETION_PERCENT = getattr(settings, 'SPORT_COMPLETION_PERCENT', 90)
class Type(models.TextChoices):
UNKNOWN = 'UK', _('Unknown')
GAME = 'GA', _('Game')
MATCH = 'MA', _('Match')
MEET = 'ME', _('Meet')
event_type = models.CharField(
max_length=2,
choices=Type.choices,
default=Type.UNKNOWN,
choices=SportEventType.choices,
default=SportEventType.UNKNOWN,
)
league = models.ForeignKey(League, on_delete=models.DO_NOTHING, **BNULL)
round = models.ForeignKey(Round, on_delete=models.DO_NOTHING, **BNULL)
start = models.DateTimeField(**BNULL)
home_team = models.ForeignKey(
Team,
@ -66,10 +107,21 @@ class SportEvent(ScrobblableMixin):
related_name='away_event_set',
**BNULL,
)
season = models.CharField(max_length=255, **BNULL)
player_one = models.ForeignKey(
Player,
on_delete=models.DO_NOTHING,
related_name='player_one_set',
**BNULL,
)
player_two = models.ForeignKey(
Player,
on_delete=models.DO_NOTHING,
related_name='player_two_set',
**BNULL,
)
def __str__(self):
return f"{self.start.date()} - {self.league.abbreviation} - {self.home_team} v {self.away_team}"
return f"{self.start.date()} - {self.round} - {self.home_team} v {self.away_team}"
def get_absolute_url(self):
return reverse("sports:event_detail", kwargs={'slug': self.uuid})
@ -81,33 +133,88 @@ class SportEvent(ScrobblableMixin):
exist.
"""
league_dict = {
"abbreviation_str": data_dict.get("LeagueName", ""),
"thesportsdb_id": data_dict.get("LeagueId", ""),
}
league, _created = League.objects.get_or_create(**league_dict)
# Find or create our Sport
sid = data_dict.get("Sport")
sport, s_created = Sport.objects.get_or_create(thesportsdb_id=sid)
if s_created:
sport.name = sid
sport.save(update_fields=['name'])
home_team_dict = {
"name": data_dict.get("HomeTeamName", ""),
"thesportsdb_id": data_dict.get("HomeTeamId", ""),
"league": league,
}
home_team, _created = Team.objects.get_or_create(**home_team_dict)
# Find or create our League
lid = data_dict.get("LeagueId")
league, l_created = League.objects.get_or_create(
thesportsdb_id=lid, sport=sport
)
if l_created:
league.sport = sport
league.name = data_dict.get("LeagueName", "")
league.save(update_fields=['sport', 'name'])
away_team_dict = {
"name": data_dict.get("AwayTeamName", ""),
"thesportsdb_id": data_dict.get("AwayTeamId", ""),
"league": league,
}
away_team, _created = Team.objects.get_or_create(**away_team_dict)
# Find or create our Season
seid = data_dict.get('Season')
season, se_created = Season.objects.get_or_create(
thesportsdb_id=seid, league=league
)
if se_created:
season.name = seid
season.save(update_fields['name'])
# Find or create our Round
rid = data_dict.get('RoundId')
round, r_created = Round.objects.get_or_create(
thesportsdb_id=rid, season=season
)
if r_created:
round.season = season
round.save(update_fields=['season'])
# Set some special data for Tennis
player_one = None
player_two = None
if data_dict.get('Sport') == 'Tennis':
event_name = data_dict.get('Name', '')
if not round.name:
round.name = get_round_name_from_event(event_name)
round.save(update_fields=['name'])
players_list = get_players_from_event(event_name)
player_one = Player.objects.filter(
name__icontains=players_list[0]
).first()
if not player_one:
player_one = Player.objects.create(name=players_list[0])
player_two = Player.objects.filter(
name__icontains=players_list[1]
).first()
if not player_two:
player_two = Player.objects.create(name=players_list[1])
home_team = None
away_team = None
if data_dict.get("HomeTeamName"):
home_team_dict = {
"name": data_dict.get("HomeTeamName", ""),
"thesportsdb_id": data_dict.get("HomeTeamId", ""),
"league": league,
}
home_team, _created = Team.objects.get_or_create(**home_team_dict)
away_team_dict = {
"name": data_dict.get("AwayTeamName", ""),
"thesportsdb_id": data_dict.get("AwayTeamId", ""),
"league": league,
}
away_team, _created = Team.objects.get_or_create(**away_team_dict)
event_dict = {
"title": data_dict.get("Name"),
"event_type": data_dict.get("ItemType"),
"event_type": sport.default_event_type,
"home_team": home_team,
"away_team": away_team,
"player_one": player_one,
"player_two": player_two,
"start": data_dict['Start'],
"league": league,
"round": round,
"run_time_ticks": data_dict.get("RunTimeTicks"),
"run_time": data_dict.get("RunTime", ""),
}

View File

@ -0,0 +1,11 @@
def get_round_name_from_event(event: str) -> str:
return ' '.join(event.split(' ')[:2])
def get_players_from_event(event: str) -> list[str]:
players = []
event_name = get_round_name_from_event(event)
players_list = event.split(event_name)[1:][0].split('vs')
players.append(players_list[0].strip())
players.append(players_list[1].strip())
return players

View File

@ -206,7 +206,7 @@
{% if scrobble.track %}<em>{{scrobble.track.artist}}</em><br/>{% endif %}
{% if scrobble.podcast_episode%}<em>{{scrobble.podcast_episode.podcast}}</em><br/>{% endif %}
{% if scrobble.video.tv_series %}<em>{{scrobble.video.tv_series }}</em><br/>{% endif %}
{% if scrobble.sport_event %}<em>{{scrobble.sport_event.league.abbreviation}}</em><br/>{% endif %}
{% if scrobble.sport_event %}<em>{{scrobble.sport_event.round.season.league}}</em><br/>{% endif %}
<small>{{scrobble.timestamp|naturaltime}}<br/>
from {{scrobble.source}}</small>
<div class="progress-bar" style="margin-right:5px;">