diff --git a/.gitignore b/.gitignore index 90a57429..a1f5f510 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ docker-stack*.yml geckodriver.log hbscorez.log src/hbscorez/settings_docker_stack*.py +src/mails/ \ No newline at end of file diff --git a/src/associations/admin.py b/src/associations/admin.py index 05985675..e4d15d07 100644 --- a/src/associations/admin.py +++ b/src/associations/admin.py @@ -7,4 +7,5 @@ @admin.register(Association) class AssociationAdmin(admin.ModelAdmin): - search_fields = ASSOCIATION_SEARCH_FIELDS + list_display = ['bhv_id', 'abbreviation', 'name'] + search_fields = ['name', 'abbreviation', 'bhv_id'] diff --git a/src/associations/apps.py b/src/associations/apps.py index 38418ffe..1d45decb 100644 --- a/src/associations/apps.py +++ b/src/associations/apps.py @@ -3,3 +3,4 @@ class AssociationsConfig(AppConfig): name = 'associations' + verbose_name = 'Verbände' diff --git a/src/associations/management/commands/import_associations.py b/src/associations/management/commands/import_associations.py index bf612e4b..a636f628 100644 --- a/src/associations/management/commands/import_associations.py +++ b/src/associations/management/commands/import_associations.py @@ -48,17 +48,23 @@ def scrape_association(url: str, options): html = http.get_text(url) dom = parsing.html_dom(html) - abbreviation = parsing.parse_association_abbreviation(url) - name = parsing.parse_association_name(dom) bhv_id = parsing.parse_association_bhv_id(dom) + name = parsing.parse_association_name(dom) + + association_api_url = f'{settings.API_URL_TEMPLATE}cmd=po&og={bhv_id}' + abbreviation = parsing.parse_association_abbreviation(association_api_url) if options['associations'] and bhv_id not in options['associations']: LOGGER.debug('SKIPPING Association (options): %s %s', bhv_id, name) return - association = Association.objects.filter(bhv_id=bhv_id).first() - if association is None: - association = Association.objects.create(name=name, abbreviation=abbreviation, bhv_id=bhv_id, source_url=url) + try: + association = Association.objects.get(bhv_id=bhv_id) + except Association.MultipleObjectsReturned: + LOGGER.debug('Database contains multiple association objects with same bhv_id.') + return + except Association.DoesNotExist: + association = Association.objects.create(name=name, abbreviation=abbreviation, bhv_id=bhv_id) LOGGER.info('CREATED Association: %s', association) return diff --git a/src/associations/migrations/0003_alter_association_options_and_more.py b/src/associations/migrations/0003_alter_association_options_and_more.py new file mode 100644 index 00000000..e9df130f --- /dev/null +++ b/src/associations/migrations/0003_alter_association_options_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.4 on 2023-10-20 10:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("associations", "0002_association_source_url"), + ] + + operations = [ + migrations.AlterModelOptions( + name="association", + options={"verbose_name": "Verband", "verbose_name_plural": "Verbände"}, + ), + migrations.RemoveField( + model_name="association", + name="source_url", + ), + migrations.AlterField( + model_name="association", + name="abbreviation", + field=models.CharField(max_length=255, verbose_name="Abkürzung"), + ), + migrations.AlterField( + model_name="association", + name="bhv_id", + field=models.IntegerField(unique=True, verbose_name="ID"), + ), + migrations.AlterField( + model_name="association", + name="name", + field=models.CharField(max_length=255, unique=True, verbose_name="Name"), + ), + ] diff --git a/src/associations/models.py b/src/associations/models.py index ad13b712..daeb7db2 100644 --- a/src/associations/models.py +++ b/src/associations/models.py @@ -4,10 +4,13 @@ class Association(models.Model): - name = models.TextField(unique=True) - abbreviation = models.TextField() - bhv_id = models.IntegerField(unique=True) - source_url = models.TextField() + bhv_id = models.IntegerField('ID', unique=True) + name = models.CharField('Name', max_length=255, unique=True) + abbreviation = models.CharField('Abkürzung', max_length=255) + + class Meta: + verbose_name = 'Verband' + verbose_name_plural = 'Verbände' def __str__(self): return f'{self.bhv_id} {self.abbreviation}' diff --git a/src/base/admin.py b/src/base/admin.py index 0d531daa..84dd3a7a 100644 --- a/src/base/admin.py +++ b/src/base/admin.py @@ -2,5 +2,9 @@ from base.models import Env, GlobalMessage -admin.site.register(Env) admin.site.register(GlobalMessage) + + +@admin.register(Env) +class EnvAdmin(admin.ModelAdmin): + list_display = ('name', 'value') diff --git a/src/base/http.py b/src/base/http.py index 736a42ad..416cd2fc 100644 --- a/src/base/http.py +++ b/src/base/http.py @@ -12,6 +12,14 @@ def get_text(url) -> str: response.encoding = 'utf-8' return response.text +def get_json(url) -> list[dict]: + response = _http.get(url, timeout=5) + response.encoding = 'utf-8' + try: + return response.json() + except requests.exceptions.JSONDecodeError: + return dict() + class EmptyResponseError(RequestException): """The response payload was empty.""" diff --git a/src/base/migrations/0003_alter_env_options_alter_env_name.py b/src/base/migrations/0003_alter_env_options_alter_env_name.py new file mode 100644 index 00000000..c73ececd --- /dev/null +++ b/src/base/migrations/0003_alter_env_options_alter_env_name.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-10-23 20:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("base", "0002_globalmessage"), + ] + + operations = [ + migrations.AlterModelOptions( + name="env", + options={ + "verbose_name": "Umgebungsvariable", + "verbose_name_plural": "Umgebungsvariablen", + }, + ), + migrations.AlterField( + model_name="env", + name="name", + field=models.CharField(max_length=255, unique=True), + ), + ] diff --git a/src/base/models.py b/src/base/models.py index c9130728..662072da 100644 --- a/src/base/models.py +++ b/src/base/models.py @@ -9,9 +9,13 @@ class Value(Enum): class Env(models.Model): - name = models.TextField(unique=True) + name = models.CharField(unique=True, max_length=255) value = models.TextField() + class Meta: + verbose_name = 'Umgebungsvariable' + verbose_name_plural = 'Umgebungsvariablen' + def set_value(self, value: Value): self.value = value.value self.save() diff --git a/src/base/parsing.py b/src/base/parsing.py index 9317a221..bea4af56 100644 --- a/src/base/parsing.py +++ b/src/base/parsing.py @@ -11,6 +11,7 @@ from teams.models import Team +from base import http def html_dom(html_text: str) -> _Element: return html.fromstring(html_text) @@ -26,8 +27,8 @@ def parse_association_urls(dom: _Element) -> list[str]: return cast(list[str], dom.xpath('//ul[@id="main-navi"]/li[contains(@class, "active")]//li/a/@href')) -def parse_association_abbreviation(association_url: str) -> str: - return association_url.rsplit('/', 1)[1].upper() +def parse_association_abbreviation(association_url: str) -> str | None: + return http.get_json(association_url)[0].get('head', {}).get('sname', None) def parse_association_name(dom: _Element) -> str: diff --git a/src/districts/admin.py b/src/districts/admin.py index ba71e22c..2ce23d6e 100644 --- a/src/districts/admin.py +++ b/src/districts/admin.py @@ -1,4 +1,8 @@ from django.contrib import admin +from django.utils.html import format_html +from django.urls import reverse +from django.forms import CheckboxSelectMultiple +from django.db import models from associations.admin import ASSOCIATION_SEARCH_FIELDS from districts.models import District @@ -9,4 +13,20 @@ @admin.register(District) class DistrictAdmin(admin.ModelAdmin): + list_display = ('bhv_id', 'name', 'get_associations') + list_display_links = ('bhv_id', 'name') search_fields = DISTRICT_SEARCH_FIELDS + + formfield_overrides = { + models.ManyToManyField: {'widget': CheckboxSelectMultiple} + } + + @admin.display(description='Verband') + def get_associations(self, obj: District) -> str: + association_links = [ + format_html('{}', + reverse('admin:associations_association_change', args=[a.pk]), + a.name + ) for a in obj.associations.all() + ] + return ', '.join(association_links) diff --git a/src/districts/apps.py b/src/districts/apps.py index eb8a8e07..b0881b3d 100644 --- a/src/districts/apps.py +++ b/src/districts/apps.py @@ -3,3 +3,4 @@ class DistrictsConfig(AppConfig): name = 'districts' + verbose_name = 'Bezirke' diff --git a/src/districts/migrations/0002_alter_district_options_alter_district_associations_and_more.py b/src/districts/migrations/0002_alter_district_options_alter_district_associations_and_more.py new file mode 100644 index 00000000..ca8600a0 --- /dev/null +++ b/src/districts/migrations/0002_alter_district_options_alter_district_associations_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.4 on 2023-10-23 20:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("associations", "0003_alter_association_options_and_more"), + ("districts", "0001_initial"), + ] + + operations = [ + migrations.AlterModelOptions( + name="district", + options={ + "ordering": ("bhv_id",), + "verbose_name": "Bezirk", + "verbose_name_plural": "Bezirke", + }, + ), + migrations.AlterField( + model_name="district", + name="associations", + field=models.ManyToManyField( + to="associations.association", verbose_name="Verbände" + ), + ), + migrations.AlterField( + model_name="district", + name="bhv_id", + field=models.IntegerField(unique=True, verbose_name="ID"), + ), + migrations.AlterField( + model_name="district", + name="name", + field=models.CharField(max_length=255, unique=True, verbose_name="Name"), + ), + ] diff --git a/src/districts/models.py b/src/districts/models.py index 7cd4b398..5132f9d2 100644 --- a/src/districts/models.py +++ b/src/districts/models.py @@ -8,9 +8,14 @@ class District(models.Model): - name = models.TextField(unique=True) - associations = models.ManyToManyField(Association) - bhv_id = models.IntegerField(unique=True) + bhv_id = models.IntegerField(verbose_name='ID', unique=True) + name = models.CharField('Name', max_length=255, unique=True) + associations = models.ManyToManyField(Association, verbose_name='Verbände') + + class Meta: + verbose_name = 'Bezirk' + verbose_name_plural = 'Bezirke' + ordering = ('bhv_id',) def __str__(self): return f'{self.bhv_id} {self.name}' diff --git a/src/games/admin.py b/src/games/admin.py index 82b29d95..36be2e7f 100644 --- a/src/games/admin.py +++ b/src/games/admin.py @@ -1,6 +1,8 @@ from django.contrib import admin +from django.utils.html import format_html +from django.urls import reverse -from games.models import Game +from games.models import Game, GameOutcome from leagues.admin import LEAGUE_SEARCH_FIELDS from teams.admin import TEAM_SEARCH_FIELDS @@ -12,4 +14,49 @@ @admin.register(Game) class GameAdmin(admin.ModelAdmin): + list_display = ('number', 'league_year', 'show_opening_whistle', 'league_name', + 'home_team_name', 'guest_team_name', 'report', 'home_goals', 'guest_goals', 'spectators') + list_filter = ('league__season',) search_fields = GAME_SEARCH_FIELDS + + @admin.display(description='Heimmannschaft') + def home_team_name(self, obj: Game) -> str: + url = reverse('admin:teams_team_change', args=(obj.home_team.pk,)) + name = obj.home_team.name + if obj.outcome() == GameOutcome.HOME_WIN: + return format_html('{}', url, name) + if obj.outcome() == GameOutcome.TIE: + return format_html('{}', url, name) + return format_html('{}', url, name) + + @admin.display(description='Gastmannschaft') + def guest_team_name(self, obj: Game) -> str: + url = reverse('admin:teams_team_change', args=(obj.guest_team.pk,)) + name = obj.guest_team.name + if obj.outcome() == GameOutcome.AWAY_WIN: + return format_html('{}', url, name) + if obj.outcome() == GameOutcome.TIE: + return format_html('{}', url, name) + return format_html('{}', url, name) + + @admin.display(description='Saison', ordering='league__season__start_year') + def league_year(self, obj: Game) -> str: + return str(obj.league.season) + + @admin.display(description='Liga') + def league_name(self, obj: Game) -> str: + return str(obj.league.name) + + @admin.display(description='Anpfiff', ordering='opening_whistle') + def show_opening_whistle(self, obj: Game) -> str | None: + if not obj.opening_whistle: + return None + return obj.opening_whistle.strftime('%d.%m.%Y %H:%M') + + @admin.display(description='Bericht') + def report(self, obj: Game) -> str | None: + report_nr = obj.report_number + if report_nr is None: + return None + url = obj.report_source_url() + return format_html('{}', url, report_nr) diff --git a/src/games/apps.py b/src/games/apps.py index b74f62c9..77911d2c 100644 --- a/src/games/apps.py +++ b/src/games/apps.py @@ -3,3 +3,4 @@ class GamesConfig(AppConfig): name = 'games' + verbose_name = 'Spiele' diff --git a/src/games/migrations/0003_alter_game_options_alter_game_guest_goals_and_more.py b/src/games/migrations/0003_alter_game_options_alter_game_guest_goals_and_more.py new file mode 100644 index 00000000..4bf5b645 --- /dev/null +++ b/src/games/migrations/0003_alter_game_options_alter_game_guest_goals_and_more.py @@ -0,0 +1,95 @@ +# Generated by Django 4.2.4 on 2023-10-23 20:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ( + "sports_halls", + "0002_alter_sportshall_options_alter_sportshall_address_and_more", + ), + ("teams", "0003_alter_team_options_alter_team_bhv_id_and_more"), + ("leagues", "0004_alter_league_options_alter_season_options_and_more"), + ("games", "0002_game_spectators"), + ] + + operations = [ + migrations.AlterModelOptions( + name="game", + options={"verbose_name": "Spiel", "verbose_name_plural": "Spiele"}, + ), + migrations.AlterField( + model_name="game", + name="guest_goals", + field=models.IntegerField(blank=True, null=True, verbose_name="Gast"), + ), + migrations.AlterField( + model_name="game", + name="guest_team", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="guest_team", + to="teams.team", + verbose_name="Gastmannschaft", + ), + ), + migrations.AlterField( + model_name="game", + name="home_goals", + field=models.IntegerField(blank=True, null=True, verbose_name="Heim"), + ), + migrations.AlterField( + model_name="game", + name="home_team", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="home_team", + to="teams.team", + verbose_name="Heimmannschaft", + ), + ), + migrations.AlterField( + model_name="game", + name="league", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="leagues.league", + verbose_name="Liga", + ), + ), + migrations.AlterField( + model_name="game", + name="number", + field=models.IntegerField(verbose_name="Spiel-Nr."), + ), + migrations.AlterField( + model_name="game", + name="opening_whistle", + field=models.DateTimeField(blank=True, null=True, verbose_name="Anpfiff"), + ), + migrations.AlterField( + model_name="game", + name="report_number", + field=models.IntegerField( + blank=True, null=True, unique=True, verbose_name="Bericht-Nr." + ), + ), + migrations.AlterField( + model_name="game", + name="spectators", + field=models.IntegerField(blank=True, null=True, verbose_name="Zuschauer"), + ), + migrations.AlterField( + model_name="game", + name="sports_hall", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="sports_halls.sportshall", + verbose_name="Sporthalle", + ), + ), + ] diff --git a/src/games/migrations/0004_alter_game_guest_goals_alter_game_guest_team_and_more.py b/src/games/migrations/0004_alter_game_guest_goals_alter_game_guest_team_and_more.py new file mode 100644 index 00000000..669322b4 --- /dev/null +++ b/src/games/migrations/0004_alter_game_guest_goals_alter_game_guest_team_and_more.py @@ -0,0 +1,79 @@ +# Generated by Django 4.2.4 on 2023-10-31 16:20 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("teams", "0003_alter_team_options_alter_team_bhv_id_and_more"), + ("leagues", "0004_alter_league_options_alter_season_options_and_more"), + ( + "sports_halls", + "0002_alter_sportshall_options_alter_sportshall_address_and_more", + ), + ("games", "0003_alter_game_options_alter_game_guest_goals_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="game", + name="guest_goals", + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name="game", + name="guest_team", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="guest_team", + to="teams.team", + ), + ), + migrations.AlterField( + model_name="game", + name="home_goals", + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name="game", + name="home_team", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="home_team", + to="teams.team", + ), + ), + migrations.AlterField( + model_name="game", + name="league", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="leagues.league" + ), + ), + migrations.AlterField( + model_name="game", + name="number", + field=models.IntegerField(), + ), + migrations.AlterField( + model_name="game", + name="opening_whistle", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AlterField( + model_name="game", + name="report_number", + field=models.IntegerField(blank=True, null=True, unique=True), + ), + migrations.AlterField( + model_name="game", + name="sports_hall", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="sports_halls.sportshall", + ), + ), + ] diff --git a/src/games/models.py b/src/games/models.py index f9bbf448..28f47a3c 100644 --- a/src/games/models.py +++ b/src/games/models.py @@ -42,13 +42,15 @@ class Game(models.Model): report_number = models.IntegerField(blank=True, null=True, unique=True) forfeiting_team = models.ForeignKey(Team, on_delete=models.CASCADE, blank=True, null=True, related_name='forfeiting_team') - spectators = models.IntegerField(blank=True, null=True) + spectators = models.IntegerField('Zuschauer', blank=True, null=True) class Meta: - unique_together = ('number', 'league') + verbose_name = 'Spiel' + verbose_name_plural = 'Spiele' + unique_together = ['number', 'league'] def __str__(self): - return f'{self.number} {self.league} {self.home_team.short_name} vs. {self.guest_team.short_name}' + return f'{self.number} | {self.league}: {self.home_team.short_name} vs. {self.guest_team.short_name}' @staticmethod def build_report_source_url(report_number): diff --git a/src/hbscorez/settings.py b/src/hbscorez/settings.py index 76e1d4cc..5e902e9f 100644 --- a/src/hbscorez/settings.py +++ b/src/hbscorez/settings.py @@ -210,4 +210,6 @@ NEW_ROOT_SOURCE_URL = 'https://www.handball4all.de/' +API_URL_TEMPLATE = 'https://spo.handball4all.de/service/if_g_json.php?' + BROWSER_TIMEOUT = 3 diff --git a/src/leagues/admin.py b/src/leagues/admin.py index 48a2ac13..9234fdb6 100644 --- a/src/leagues/admin.py +++ b/src/leagues/admin.py @@ -12,11 +12,15 @@ @ admin.register(Season) class SeasonAdmin(admin.ModelAdmin): + list_display = ('__str__',) search_fields = SEASON_SEARCH_FIELDS @ admin.register(League) class LeagueAdmin(admin.ModelAdmin): + list_display = ('bhv_id', 'name', 'season') + list_display_links = ('bhv_id', 'name') + list_filter = ('season',) search_fields = LEAGUE_SEARCH_FIELDS diff --git a/src/leagues/apps.py b/src/leagues/apps.py index 4abf4fa9..facaffed 100644 --- a/src/leagues/apps.py +++ b/src/leagues/apps.py @@ -3,3 +3,4 @@ class LeaguesConfig(AppConfig): name = 'leagues' + verbose_name = 'Ligen' diff --git a/src/leagues/migrations/0004_alter_league_options_alter_season_options_and_more.py b/src/leagues/migrations/0004_alter_league_options_alter_season_options_and_more.py new file mode 100644 index 00000000..8c8f6e54 --- /dev/null +++ b/src/leagues/migrations/0004_alter_league_options_alter_season_options_and_more.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.4 on 2023-10-23 20:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ( + "districts", + "0002_alter_district_options_alter_district_associations_and_more", + ), + ("leagues", "0003_leaguename"), + ] + + operations = [ + migrations.AlterModelOptions( + name="league", + options={"verbose_name": "Liga", "verbose_name_plural": "Ligen"}, + ), + migrations.AlterModelOptions( + name="season", + options={ + "ordering": ("start_year",), + "verbose_name": "Saison", + "verbose_name_plural": "Saisons", + }, + ), + migrations.AlterField( + model_name="league", + name="abbreviation", + field=models.CharField(max_length=255), + ), + migrations.AlterField( + model_name="league", + name="bhv_id", + field=models.IntegerField(unique=True, verbose_name="ID"), + ), + migrations.AlterField( + model_name="league", + name="district", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="districts.district", + verbose_name="Bezirk", + ), + ), + migrations.AlterField( + model_name="league", + name="name", + field=models.CharField(max_length=255, verbose_name="Name"), + ), + migrations.AlterField( + model_name="league", + name="season", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="leagues.season", + verbose_name="Saison", + ), + ), + ] diff --git a/src/leagues/models.py b/src/leagues/models.py index 8fcf42f8..cf2a1b53 100644 --- a/src/leagues/models.py +++ b/src/leagues/models.py @@ -11,18 +11,25 @@ class Season(models.Model): validators.MinValueValidator(1990), validators.MaxValueValidator(2050)]) + class Meta: + ordering = ('start_year',) + verbose_name = 'Saison' + verbose_name_plural = 'Saisons' + def __str__(self): return f'{self.start_year}/{self.start_year + 1}' class League(models.Model): - name = models.TextField() - abbreviation = models.TextField() - district = models.ForeignKey(District, on_delete=models.CASCADE) - season = models.ForeignKey(Season, on_delete=models.CASCADE) - bhv_id = models.IntegerField(unique=True) + bhv_id = models.IntegerField(verbose_name='ID', unique=True) + name = models.CharField(verbose_name='Name', max_length=255) + abbreviation = models.CharField(max_length=255) + district = models.ForeignKey(District, verbose_name='Bezirk', on_delete=models.CASCADE) + season = models.ForeignKey(Season, verbose_name='Saison', on_delete=models.CASCADE) class Meta: + verbose_name = 'Liga' + verbose_name_plural = 'Ligen' unique_together = (('name', 'district', 'season'), ('abbreviation', 'district', 'season')) def __str__(self): diff --git a/src/sports_halls/admin.py b/src/sports_halls/admin.py index 22c62cee..66841f7f 100644 --- a/src/sports_halls/admin.py +++ b/src/sports_halls/admin.py @@ -5,4 +5,10 @@ @admin.register(SportsHall) class SportsHallAdmin(admin.ModelAdmin): - search_fields = ('number', 'name', 'address', 'bhv_id') + list_display = ('bhv_id', 'number', 'name', 'address', 'location') + list_display_links = ('bhv_id', 'number', 'name') + search_fields = ('bhv_id', 'number', 'name', 'address') + + @admin.display(description='Ort') + def location(self, obj: SportsHall) -> str: + return obj.address.rpartition(' ')[2] diff --git a/src/sports_halls/apps.py b/src/sports_halls/apps.py index 1c49f06d..34be9e4e 100644 --- a/src/sports_halls/apps.py +++ b/src/sports_halls/apps.py @@ -3,3 +3,4 @@ class SportsHallsConfig(AppConfig): name = 'sports_halls' + verbose_name = 'Sporthallen' diff --git a/src/sports_halls/migrations/0002_alter_sportshall_options_alter_sportshall_address_and_more.py b/src/sports_halls/migrations/0002_alter_sportshall_options_alter_sportshall_address_and_more.py new file mode 100644 index 00000000..42b21397 --- /dev/null +++ b/src/sports_halls/migrations/0002_alter_sportshall_options_alter_sportshall_address_and_more.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.4 on 2023-10-23 20:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("sports_halls", "0001_initial"), + ] + + operations = [ + migrations.AlterModelOptions( + name="sportshall", + options={ + "verbose_name": "Sporthalle", + "verbose_name_plural": "Sporthallen", + }, + ), + migrations.AlterField( + model_name="sportshall", + name="address", + field=models.CharField(max_length=255, verbose_name="Adresse"), + ), + migrations.AlterField( + model_name="sportshall", + name="bhv_id", + field=models.IntegerField(unique=True, verbose_name="ID"), + ), + migrations.AlterField( + model_name="sportshall", + name="name", + field=models.CharField(max_length=255, verbose_name="Name"), + ), + migrations.AlterField( + model_name="sportshall", + name="number", + field=models.IntegerField(unique=True, verbose_name="Hallennummer"), + ), + migrations.AlterField( + model_name="sportshall", + name="phone_number", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="Telefonnummer" + ), + ), + ] diff --git a/src/sports_halls/models.py b/src/sports_halls/models.py index 883bbea0..2ee2624d 100644 --- a/src/sports_halls/models.py +++ b/src/sports_halls/models.py @@ -3,13 +3,17 @@ class SportsHall(models.Model): - number = models.IntegerField(unique=True) - name = models.TextField() - address = models.TextField() - phone_number = models.TextField(blank=True, null=True) + bhv_id = models.IntegerField(verbose_name='ID', unique=True) + number = models.IntegerField(verbose_name='Hallennummer', unique=True) + name = models.CharField(verbose_name='Name', max_length=255) + address = models.CharField(verbose_name='Adresse', max_length=255) + phone_number = models.CharField(verbose_name='Telefonnummer', max_length=255, blank=True, null=True) latitude = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=6) longitude = models.DecimalField(blank=True, null=True, max_digits=9, decimal_places=6) - bhv_id = models.IntegerField(unique=True) + + class Meta: + verbose_name = 'Sporthalle' + verbose_name_plural = 'Sporthallen' def __str__(self): return f"{self.number} {self.name}" diff --git a/src/teams/admin.py b/src/teams/admin.py index 732259b7..e1b8085b 100644 --- a/src/teams/admin.py +++ b/src/teams/admin.py @@ -9,4 +9,14 @@ @admin.register(Team) class TeamAdmin(admin.ModelAdmin): + list_display = ('name', 'season', 'get_league') + list_filter = ('league__season',) search_fields = TEAM_SEARCH_FIELDS + + @admin.display(description='Saison') + def season(self, obj: Team) -> str: + return str(obj.league.season) + + @admin.display(description='Liga') + def get_league(self, obj: Team) -> str: + return str(obj.league.name) diff --git a/src/teams/apps.py b/src/teams/apps.py index 17954d66..1b23d616 100644 --- a/src/teams/apps.py +++ b/src/teams/apps.py @@ -3,3 +3,4 @@ class TeamsConfig(AppConfig): name = 'teams' + verbose_name = 'Mannschaften' diff --git a/src/teams/migrations/0003_alter_team_options_alter_team_bhv_id_and_more.py b/src/teams/migrations/0003_alter_team_options_alter_team_bhv_id_and_more.py new file mode 100644 index 00000000..2eb74794 --- /dev/null +++ b/src/teams/migrations/0003_alter_team_options_alter_team_bhv_id_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.4 on 2023-10-23 20:08 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("leagues", "0004_alter_league_options_alter_season_options_and_more"), + ("teams", "0002_team_retirement"), + ] + + operations = [ + migrations.AlterModelOptions( + name="team", + options={ + "verbose_name": "Mannschaft", + "verbose_name_plural": "Mannschaften", + }, + ), + migrations.AlterField( + model_name="team", + name="bhv_id", + field=models.IntegerField(unique=True, verbose_name="ID"), + ), + migrations.AlterField( + model_name="team", + name="league", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="leagues.league", + verbose_name="Liga", + ), + ), + migrations.AlterField( + model_name="team", + name="name", + field=models.CharField(max_length=255, verbose_name="Name"), + ), + migrations.AlterField( + model_name="team", + name="retirement", + field=models.DateField(blank=True, null=True, verbose_name="Abgemeldet am"), + ), + migrations.AlterField( + model_name="team", + name="short_name", + field=models.CharField(max_length=255, verbose_name="Abkürzung"), + ), + ] diff --git a/src/teams/models.py b/src/teams/models.py index d6e0ae5b..46a31603 100644 --- a/src/teams/models.py +++ b/src/teams/models.py @@ -10,20 +10,22 @@ class Team(models.Model): - name = models.TextField() - short_name = models.TextField() - league = models.ForeignKey(League, on_delete=models.CASCADE) - bhv_id = models.IntegerField(unique=True) - retirement = models.DateField(blank=True, null=True) + bhv_id = models.IntegerField(verbose_name='ID', unique=True) + name = models.CharField(verbose_name='Name', max_length=255) + short_name = models.CharField(verbose_name='Abkürzung', max_length=255) + league = models.ForeignKey(League, verbose_name='Liga', on_delete=models.CASCADE) + retirement = models.DateField(verbose_name='Abgemeldet am', blank=True, null=True) class Meta: + verbose_name = 'Mannschaft' + verbose_name_plural = 'Mannschaften' unique_together = (('name', 'league'), ('short_name', 'league')) def __str__(self): - return f'{self.bhv_id} {self.short_name}' + return f'{self.bhv_id} {self.short_name} ({self.league.season})' def get_absolute_url(self): - return reverse('teams:detail', kwargs={'bhv_id': self.bhv_id, }) + return reverse('teams:detail', kwargs={'bhv_id': self.bhv_id}) @staticmethod def build_source_url(league_bhv_id, team_bhv_id):