diff --git a/tipp/openliga.py b/tipp/openliga.py
index 03b1403..8a3ce99 100644
--- a/tipp/openliga.py
+++ b/tipp/openliga.py
@@ -2,28 +2,30 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015-2016 by Martin Bley (martin@mb-oss.de)
-#
+#
# This file is part of TipPy.
-#
+#
# TipPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# TipPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with TipPy. If not, see .
-#
+#
import locale
import urllib.error
import urllib.parse
import urllib.request
-from suds.client import Client
+from zeep import Client
+from zeep.transports import Transport
+from requests import Session
class OpenLiga(object):
@@ -40,54 +42,44 @@ class OpenLiga(object):
self.debug = debug
- # get a SUDS client object
- if self.proxyurl is None:
- try:
- self.client = Client(
- 'http://www.openligadb.de/'
- + 'Webservices/Sportsdata.asmx?WSDL')
- except (urllib.error.URLError):
- self.error += "Connect to webservice failed."
- else:
- try:
- t = suds.transport.http.HttpTransport()
- proxy = urllib.request.ProxyHandler({'http': proxyurl})
- opener = urllib.request.build_opener(proxy)
- t.urlopener = opener
- self.client = suds.client.Client(
- 'http://www.openligadb.de/Webservices/'
- + 'Sportsdata.asmx?WSDL', transport=t)
- except urllib.error.URLError as e:
- self.error += "Connect to webservice failed " \
- + "(via proxy " + proxyurl + "): " + str(e) + "\n"
+ # get a SOAP client object
+ wsdl_url = 'http://www.openligadb.de/Webservices/Sportsdata.asmx?WSDL'
+
+ try:
+ if proxyurl is None:
+ self.client = Client(wsdl=wsdl_url)
+ else:
+ session = Session()
+ session.proxies = {'http': proxyurl, 'https': proxyurl}
+ transport = Transport(session=session)
+ self.client = Client(wsdl=wsdl_url, transport=transport)
+ except (urllib.error.URLError, Exception) as e:
+ self.error += f"Connect to webservice failed: {str(e)}\n"
def getSeason(self, season, league='bl1'):
""" Get the whole season.
Args: season and league shortcut (optional)
"""
- return (self.client.service.GetMatchdataByLeagueSaison(
+ return self.client.service.GetMatchdataByLeagueSaison(
leagueShortcut=league, leagueSaison=season)
- )
def getMatchday(self, season, matchdaynumber, league='bl1'):
""" Get matchday.
Args: season, matchdaynumber and league shortcut (optional)
"""
- return (self.client.service.GetMatchdataByGroupLeagueSaison(
+ return self.client.service.GetMatchdataByGroupLeagueSaison(
groupOrderID=matchdaynumber,
leagueShortcut=league,
leagueSaison=season)
- )
def getTeams(self, season, league='bl1'):
- return (self.client.service.GetTeamsByLeagueSaison(
+ return self.client.service.GetTeamsByLeagueSaison(
leagueShortcut=league,
leagueSaison=season)
- )
def getCurrentGroup(self, league):
- return (self.client.service.GetCurrentGroup(
+ return self.client.service.GetCurrentGroup(
leagueShortcut=league)
- )
+
diff --git a/tipp/views.py b/tipp/views.py
index 6217d0f..7da8563 100644
--- a/tipp/views.py
+++ b/tipp/views.py
@@ -21,474 +21,601 @@ along with TipPy. If not, see .
import os
from os.path import join as pjoin
-import django.shortcuts
+import logging
import pytz
from PIL import Image as PImage
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
-from django.core.paginator import Paginator, EmptyPage, \
- PageNotAnInteger
+from django.core.files.storage import default_storage
+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Sum
from django.http import HttpResponseRedirect
+from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
+from django.views.decorators.http import require_http_methods
from tipp.forms import *
from tipp.models import *
from tipp.openliga import *
-timezoneLocal = pytz.timezone('Europe/Berlin')
+TIMEZONE_LOCAL = pytz.timezone('Europe/Berlin')
+THUMBNAIL_SIZE = (50, 50)
+logger = logging.getLogger(__name__)
def about(request):
- return django.shortcuts.render(request, 'about.html')
+ """Render about page."""
+ return render(request, 'about.html')
@login_required
def home(request):
+ """Redirect to current matchday."""
ls = get_current_ls()
season = get_current_season()
md = get_current_md(ls)
- return django.shortcuts.redirect("matchday",
- ls=ls,
- season=season,
- matchday=str(md).zfill(2)
- )
+ return redirect(
+ 'matchday',
+ ls=ls,
+ season=season,
+ matchday=str(md).zfill(2)
+ )
+
+def _create_thumbnail(image_path):
+ """Create a 50x50 thumbnail from the original image."""
+ try:
+ im = PImage.open(image_path)
+ im.thumbnail(THUMBNAIL_SIZE, PImage.Resampling.LANCZOS)
+ im.save(image_path)
+ except Exception as e:
+ logger.error(f"Failed to create thumbnail: {str(e)}")
+ raise
+
+
+def _delete_avatar(image_path):
+ """Delete avatar image file safely."""
+ if os.path.isfile(image_path):
+ try:
+ os.remove(image_path)
+ except OSError as e:
+ logger.error(f"Failed to delete avatar: {str(e)}")
@login_required
+@require_http_methods(["GET", "POST"])
def profile(request, pk):
- user = User.objects.get(id=pk)
+ """Display and edit user profile."""
+ user = get_object_or_404(User, id=pk)
profile, created = UserProfile.objects.get_or_create(user=user)
- if profile.avatar:
- img = profile.avatar.name
- else:
- img = None
-
- debug = []
- debug.append('avatar: ' + str(img))
-
- # If it's a HTTP POST, we're interested in processing form data.
if request.method == 'POST':
- user_form = UserForm(data=request.POST, instance=user)
- profile_form = UserProfileForm(
- data=request.POST, instance=profile
- )
-
- if user_form.is_valid() and profile_form.is_valid():
-
- user = user_form.save()
-
- profile = profile_form.save(commit=False)
- profile.user = user
-
- # Did the user provide a profile picture?
- if 'avatar' in request.FILES:
- profile.avatar = request.FILES['avatar']
- # Now we save the UserProfile model instance.
- profile.save()
-
- # create thumbnail
- imfn = pjoin(settings.MEDIA_ROOT, profile.avatar.name)
- im = PImage.open(imfn)
- im.thumbnail((50, 50), PImage.ANTIALIAS)
- im.save(settings.MEDIA_ROOT + profile.avatar.name)
- if 'avatar-clear' in request.POST:
- if 'on' in request.POST['avatar-clear']:
- image = pjoin(settings.MEDIA_ROOT, img)
- debug.append('Is the image ' + str(image)
- + ' a regular file? '
- + str(os.path.isfile(image))
- )
- if os.path.isfile(image):
- os.remove(image)
- profile.save()
-
- for item in request.POST:
- debug.append(str(item) + " -> "
- + str(request.POST[item])
- )
-
- # Invalid form or forms - mistakes or something else?
- # Print problems to the terminal.
- # They'll also be shown to the user.
- else:
- print(
- user_form.errors,
- profile_form.errors
- )
-
- return django.shortcuts.redirect('/accounts/profile/' + str(user.id))
-
- # Not a HTTP POST, so we render our form using two ModelForm
- # instances. These forms will be blank, ready for user input.
+ return _handle_profile_post(request, user, profile)
else:
- user_form = UserForm(
- initial={'last_name': user.last_name,
- 'first_name': user.first_name,
- 'email': user.email},
- instance=user
- )
- profile_form = UserProfileForm(instance=profile)
+ return _handle_profile_get(request, user, profile)
- return django.shortcuts.render(request, 'registration/profile.html', {
+
+def _handle_profile_post(request, user, profile):
+ """Handle POST request for profile update."""
+ user_form = UserForm(data=request.POST, instance=user)
+ profile_form = UserProfileForm(data=request.POST, instance=profile)
+
+ if user_form.is_valid() and profile_form.is_valid():
+ user = user_form.save()
+ profile = profile_form.save(commit=False)
+ profile.user = user
+
+ # Handle avatar upload
+ if 'avatar' in request.FILES:
+ profile.avatar = request.FILES['avatar']
+ profile.save()
+
+ image_path = os.path.join(settings.MEDIA_ROOT, profile.avatar.name)
+ try:
+ _create_thumbnail(image_path)
+ except Exception as e:
+ logger.error(f"Thumbnail creation failed: {str(e)}")
+
+ # Handle avatar deletion
+ elif 'avatar-clear' in request.POST and request.POST['avatar-clear'] == 'on':
+ if profile.avatar:
+ old_image_path = os.path.join(settings.MEDIA_ROOT, profile.avatar.name)
+ _delete_avatar(old_image_path)
+ profile.avatar = None
+
+ profile.save()
+ return redirect('profile', pk=user.id)
+ else:
+ logger.warning(f"Form validation failed for user {user.id}: "
+ f"User errors: {user_form.errors}, "
+ f"Profile errors: {profile_form.errors}")
+
+ return _render_profile_form(request, user_form, profile_form, profile)
+
+
+def _handle_profile_get(request, user, profile):
+ """Handle GET request for profile display."""
+ user_form = UserForm(instance=user)
+ profile_form = UserProfileForm(instance=profile)
+
+ return _render_profile_form(request, user_form, profile_form, profile)
+
+
+def _render_profile_form(request, user_form, profile_form, profile):
+ """Render profile form template."""
+ context = {
'user_form': user_form,
'profile_form': profile_form,
- 'debug': debug,
- 'img': img}
- )
+ 'avatar': profile.avatar.name if profile.avatar else None,
+ }
+ return render(request, 'registration/profile.html', context)
@staff_member_required
def staff(request):
- season = get_current_season(),
- """ get available competitions """
- competitions = Competition.objects.filter(season=season).order_by('leagueName')
+ """Display available competitions for current season."""
+ season = get_current_season()
+ competitions = Competition.objects.filter(
+ season=season
+ ).order_by('leagueName')
- return django.shortcuts.render(request, 'staff.html', {'competitions': competitions})
+ return render(request, 'staff.html', {'competitions': competitions})
@staff_member_required
def getSeason(request, ls, season):
+ """Fetch and import teams and matches for a season from OpenLiga."""
ol = OpenLiga()
- teams = ol.getTeams(str(season), ls)
- season = ol.getSeason(str(season), ls)
+ teams_data = ol.getTeams(str(season), ls)
+ season_data = ol.getSeason(str(season), ls)
- return_vals = []
- for team in teams[0]:
- t = Team(
- teamID=team['teamID'],
- name=team['teamName'],
- iconURL=team['teamIconURL']
+ # Bulk import teams
+ _import_teams(teams_data[0])
+
+ # Bulk import matches
+ _import_season_matches(season_data[0], ls)
+
+ return redirect(
+ 'matchday',
+ ls=ls,
+ season=get_current_season(),
+ matchday=str(get_current_md(ls)).zfill(2)
+ )
+
+
+def _import_teams(teams_data):
+ """Import teams, update existing ones with icon URL if missing."""
+ teams_to_create = []
+ teams_to_update = []
+
+ for team_data in teams_data:
+ team_id = int(team_data['teamID'])
+
+ try:
+ existing_team = Team.objects.get(teamID=team_id)
+ if existing_team.iconURL is None:
+ existing_team.iconURL = team_data['teamIconURL']
+ teams_to_update.append(existing_team)
+ except Team.DoesNotExist:
+ teams_to_create.append(
+ Team(
+ teamID=team_id,
+ name=team_data['teamName'],
+ iconURL=team_data['teamIconURL']
+ )
+ )
+
+ if teams_to_create:
+ Team.objects.bulk_create(teams_to_create, ignore_conflicts=True)
+
+ if teams_to_update:
+ Team.objects.bulk_update(teams_to_update, ['iconURL'], batch_size=100)
+
+
+def _import_season_matches(season_data, ls):
+ """Import all matches from season data."""
+ matches = []
+
+ for match_data in season_data:
+ try:
+ team1 = Team.objects.get(teamID=int(match_data['idTeam1']))
+ team2 = Team.objects.get(teamID=int(match_data['idTeam2']))
+ except Team.DoesNotExist:
+ logger = logging.getLogger(__name__)
+ logger.warning(f"Team not found for match {match_data['matchID']}")
+ continue
+
+ matches.append(
+ Match(
+ matchID=match_data['matchID'],
+ matchDateTime=match_data['matchDateTime'],
+ group=int(match_data['groupID']),
+ matchday=int(match_data['groupOrderID']),
+ matchday_name=match_data['groupName'],
+ idTeam1=team1,
+ idTeam2=team2,
+ pointsTeam1=match_data['pointsTeam1'],
+ pointsTeam2=match_data['pointsTeam2'],
+ finished=bool(match_data['matchIsFinished']),
+ season=match_data['leagueSaison'],
+ leagueShortcut=match_data['leagueShortcut']
+ )
)
- """
- check if object exits and do insert if not
- """
- try:
- to = Team.objects.get(teamID=int(t.teamID))
- if to.iconURL is None:
- t.save(update_fields=['iconURL'])
- except Team.DoesNotExist:
- t.save()
-
- for match in season[0]:
- m = Match(matchID=match['matchID'],
- matchDateTime=match['matchDateTime'],
- group=int(match['groupID']),
- matchday=int(match['groupOrderID']),
- matchday_name=match['groupName'],
- idTeam1=Team.objects.get(teamID=int(match['idTeam1'])),
- idTeam2=Team.objects.get(teamID=int(match['idTeam2'])),
- pointsTeam1=match['pointsTeam1'],
- pointsTeam2=match['pointsTeam2'],
- finished=bool(match['matchIsFinished']),
- season=match['leagueSaison'],
- leagueShortcut=match['leagueShortcut']
- )
- m.save()
-
- return django.shortcuts.redirect("matchday",
- ls=ls,
- season=get_current_season(),
- matchday=str(get_current_md(ls)).zfill(2)
- )
+ if matches:
+ Match.objects.bulk_create(matches, ignore_conflicts=True)
@login_required
def update(request, ls, season, cur_md):
- """
- To do:
- - update table tipp_competition
- """
-
+ """Update match results and calculate user scores for current matchday."""
ol = OpenLiga()
md_matches = ol.getMatchday(str(season), int(cur_md), ls)
- # Achtung: DateTimeField prüfen
- for match in md_matches[0]:
- # Endergebnis holen
- matchResults = match['matchResults']
- try:
- for result in matchResults[0]:
- if result['resultName'] == "Halbzeit":
- pointsTeam1 = int(result['pointsTeam1'])
- pointsTeam2 = int(result['pointsTeam2'])
- elif result['resultName'] == "Endergebnis":
- pointsTeam1 = int(result['pointsTeam1'])
- pointsTeam2 = int(result['pointsTeam2'])
- break
- elif str(result['resultName']) == u"nach Verlängerung":
- pointsTeam1 = int(result['pointsTeam1'])
- pointsTeam2 = int(result['pointsTeam2'])
- else:
- pointsTeam1 = -1
- pointsTeam2 = -1
+ # Update match results
+ _update_match_results(md_matches[0])
- except:
- pointsTeam1 = -1
- pointsTeam2 = -1
+ # Calculate scores for all tips
+ _calculate_matchday_scores(request.user, ls, season, cur_md)
- m = Match(matchID=match['matchID'],
- matchDateTime=match['matchDateTime'],
- pointsTeam1=pointsTeam1,
- pointsTeam2=pointsTeam2,
- finished=bool(match['matchIsFinished']),
- )
- m.save(update_fields=['matchDateTime', 'pointsTeam1', 'pointsTeam2', 'finished'])
- pointsTeam1 = -1
- pointsTeam2 = -1
+ return redirect('matchday', ls=ls, season=season, matchday=cur_md)
- # evaluate scores
- """ get mandants current user is related """
- mandants = RelUserMandant.objects.filter(user=request.user). \
- values_list('mandant', flat=True)
- # scores = Score.objects.get(client=Mandant.objects.get(name='huse'))
- scores = Score.objects.get(client=mandants[0])
+def _update_match_results(matches_data):
+ """Update match results from OpenLiga data."""
+ matches_to_update = []
+
+ for match_data in matches_data:
+ points_team1, points_team2 = _extract_match_result(match_data['matchResults'])
+
+ matches_to_update.append(
+ Match(
+ matchID=match_data['matchID'],
+ matchDateTime=match_data['matchDateTime'],
+ pointsTeam1=points_team1,
+ pointsTeam2=points_team2,
+ finished=bool(match_data['matchIsFinished']),
+ )
+ )
+
+ if matches_to_update:
+ Match.objects.bulk_update(
+ matches_to_update,
+ ['matchDateTime', 'pointsTeam1', 'pointsTeam2', 'finished'],
+ batch_size=100
+ )
+
+
+def _extract_match_result(match_results):
+ """Extract final match result from match results data.
+
+ Returns:
+ tuple: (pointsTeam1, pointsTeam2), defaults to (-1, -1) if not found.
+ """
+ if not match_results or not match_results[0]:
+ return -1, -1
+
+ try:
+ for result in match_results[0]:
+ result_name = result['resultName']
+
+ # Prefer final result, but accept extra time if available
+ if result_name in ("Endergebnis", "nach Verlängerung"):
+ return int(result['pointsTeam1']), int(result['pointsTeam2'])
+
+ # Fallback: half-time result if final not found
+ for result in match_results[0]:
+ if result['resultName'] == "Halbzeit":
+ return int(result['pointsTeam1']), int(result['pointsTeam2'])
+
+ except (KeyError, ValueError, TypeError) as e:
+ logger = logging.getLogger(__name__)
+ logger.warning(f"Error extracting match result: {str(e)}")
+
+ return -1, -1
+
+
+def _calculate_matchday_scores(user, ls, season, cur_md):
+ """Calculate scores for all user tips on finished matches."""
+ try:
+ mandant = RelUserMandant.objects.filter(user=user).values_list(
+ 'mandant', flat=True
+ ).first()
+
+ if not mandant:
+ logger = logging.getLogger(__name__)
+ logger.warning(f"No mandant found for user {user.id}")
+ return
+
+ scores = Score.objects.get(client=mandant)
+ except Score.DoesNotExist:
+ logger = logging.getLogger(__name__)
+ logger.warning(f"No score configuration found for mandant {mandant}")
+ return
finished_matches = Match.objects.filter(
finished=True,
leagueShortcut=ls,
season=season,
matchday=cur_md
- )
+ ).prefetch_related('tipp_set')
+
+ tipps_to_update = []
+
for match in finished_matches:
- tipps = Tipp.objects.filter(matchID=match.matchID)
- for tipp in tipps:
- score = 0
- goals_total = match.pointsTeam1 + match.pointsTeam2
-
- # Tipp is acurate
- if (tipp.pointsTeam1 == match.pointsTeam1
- and tipp.pointsTeam2 == match.pointsTeam2):
- if goals_total >= 5:
- score = scores.exact_high
- else:
- score = scores.exact
-
- tipp.score = score
- tipp.save()
- continue
-
- # who won?
- winnerReal = None
- if match.pointsTeam1 > match.pointsTeam2:
- winnerReal = 1
- elif match.pointsTeam2 > match.pointsTeam1:
- winnerReal = 2
- elif match.pointsTeam1 == match.pointsTeam2:
- winnerReal = 0
-
- winnerTipp = None
- if tipp.pointsTeam1 > tipp.pointsTeam2:
- winnerTipp = 1
- elif tipp.pointsTeam2 > tipp.pointsTeam1:
- winnerTipp = 2
- elif tipp.pointsTeam1 == tipp.pointsTeam2:
- winnerTipp = 0
-
- diffReal = match.pointsTeam1 - match.pointsTeam2
- diffTipp = tipp.pointsTeam1 - tipp.pointsTeam2
-
- if winnerTipp == winnerReal:
- if diffTipp == diffReal:
- score = scores.diff
- else:
- score = scores.tendency
-
- tipp.score = score
- tipp.save()
- continue
-
+ for tipp in match.tipp_set.all():
+ score = _calculate_tipp_score(tipp, match, scores)
tipp.score = score
- tipp.save()
- continue
+ tipps_to_update.append(tipp)
- return django.shortcuts.redirect("matchday",
- ls=ls,
- season=season,
- matchday=cur_md
- )
+ if tipps_to_update:
+ Tipp.objects.bulk_update(tipps_to_update, ['score'], batch_size=100)
+def _calculate_tipp_score(tipp, match, scores):
+ """Calculate score for a single tip based on match result.
+
+ Returns:
+ int: Points earned for this tip.
+ """
+ # Exact result
+ if tipp.pointsTeam1 == match.pointsTeam1 and tipp.pointsTeam2 == match.pointsTeam2:
+ goals_total = match.pointsTeam1 + match.pointsTeam2
+ return scores.exact_high if goals_total >= 5 else scores.exact
+
+ # Winner and difference
+ winner_real = _get_winner(match.pointsTeam1, match.pointsTeam2)
+ winner_tipp = _get_winner(tipp.pointsTeam1, tipp.pointsTeam2)
+
+ if winner_tipp == winner_real:
+ diff_real = match.pointsTeam1 - match.pointsTeam2
+ diff_tipp = tipp.pointsTeam1 - tipp.pointsTeam2
+
+ if diff_tipp == diff_real:
+ return scores.diff
+ else:
+ return scores.tendency
+
+ return 0
+
+
+def _get_winner(points_team1, points_team2):
+ """Determine match winner.
+
+ Returns:
+ int: 1 for team1 win, 2 for team2 win, 0 for draw, -1 for invalid.
+ """
+ if points_team1 > points_team2:
+ return 1
+ elif points_team2 > points_team1:
+ return 2
+ elif points_team1 == points_team2:
+ return 0
+ return -1
+
@login_required
+@require_http_methods(["GET", "POST"])
def matchday(request, ls, season, matchday, template_name='md.html'):
- update(request, ls, season, matchday)
- debug = ''
- debug += "Debugging: "
- has_refresh = True
+ """Display matchday with user tips, match results, and mandant scores."""
+ # Process form submissions first
+ _process_matchday_tips(request, ls, season, matchday)
+
+ user = request.user
+ matches = _build_matchday_matches(request, user, ls, season, matchday)
+ mandant_dict = _build_mandant_scores(user, matches)
+ posts = _get_mandant_posts(user)
+
+ cur_group = matches[0]['md_name'] if matches else "N/A"
- """ get matches of the matchday """
- md_matches = Match.objects.filter(leagueShortcut=ls, season=season,
- matchday=int(matchday)).order_by('matchDateTime')
-
- """ get the current user """
- user = User.objects.get(username=request.user.username)
-
- """ get mandants current user is related """
- mandants = RelUserMandant.objects.filter(user=request.user). \
- values_list('mandant', flat=True)
-
- """ get mates """
- mandant_dict = {}
- for m in mandants:
- mandant = Mandant.objects.get(id=m)
- mandant_dict[mandant.description] = []
- rs = RelUserMandant.objects.filter(mandant=m)
-
- for r in rs:
- mandant_dict[mandant.description].append({
- 'mate': r.user,
- 'tipps': [],
- 'sum_score': 0
- })
-
- matches = []
- for match in md_matches:
- """
- create a form for each tipp
- """
- form = TippForm(request.POST or None, prefix=str(match.matchID))
-
- """
- check if Tipp already exists
- """
- try:
- tipp = Tipp.objects.get(tipperID=user.id,
- matchID=match.matchID)
- except ObjectDoesNotExist:
- tipp = None
-
- """
- if request is a POST, process form data, else just show form
- """
- if form.is_valid():
- data = form.cleaned_data
-
- if tipp is not None:
- """ Tipp exists an needs an UPDATE"""
- tipp = Tipp(id=tipp.pk,
- matchID=match,
- tipperID=user,
- pointsTeam1=data['tippTeam1'],
- pointsTeam2=data['tippTeam2']
- )
- tipp.save()
-
- else:
- """ do INSERT """
- tipp = Tipp(matchID=match,
- tipperID=user,
- pointsTeam1=data['tippTeam1'],
- pointsTeam2=data['tippTeam2'])
- tipp.save()
-
- match_started = timezone.now() >= match.matchDateTime
-
- if tipp is not None:
- f = TippForm(prefix=str(match.matchID),
- initial={'tippTeam1': tipp.pointsTeam1,
- 'tippTeam2': tipp.pointsTeam2}
- )
- else:
- f = TippForm(prefix=str(match.matchID))
-
- item = {}
- item['matchDateTime'] = (match.matchDateTime.
- astimezone(timezoneLocal).strftime('%a %d.%m, %H:%M'))
-
- item['iconTeam1'] = str(match.idTeam1.icon)
- item['iconURLTeam1'] = str(match.idTeam1.iconURL)
- item['nameTeam1'] = str(match.idTeam1.name)
- item['abbrTeam1'] = str(match.idTeam1.abbr)
- item['iconTeam2'] = str(match.idTeam2.icon)
- item['iconURLTeam2'] = str(match.idTeam2.iconURL)
- item['nameTeam2'] = str(match.idTeam2.name)
- item['abbrTeam2'] = str(match.idTeam2.abbr)
- item['started'] = match_started
- item['finished'] = match.finished
- item['md_name'] = match.matchday_name
-
- if (match.pointsTeam1 == -1 or match.pointsTeam2 == -1):
- item['matchResult'] = "-:-"
- else:
- item['matchResult'] = str(match.pointsTeam1) + ":" \
- + str(match.pointsTeam2)
-
- item['tippFormTeam1'] = f['tippTeam1']
- item['tippFormTeam2'] = f['tippTeam2']
-
- if match_started is True:
- f.fields['tippTeam1'].widget.attrs['disabled'] = True
- f.fields['tippTeam2'].widget.attrs['disabled'] = True
-
- """ get tipps from mates """
- for mandant in mandant_dict:
- for mate in mandant_dict[mandant]:
- try:
- matetipp = Tipp.objects.get(tipperID=mate['mate'],
- matchID=match.matchID)
- if match_started is True:
- if matetipp.score is None:
- score = u'\u231B'
- else:
- score = matetipp.score
-
- mate['tipps'].append(str(matetipp.pointsTeam1)
- + ":" + str(matetipp.pointsTeam2)
- + "(" + str(score) + ")")
- if match.finished is True:
- mate['sum_score'] += matetipp.score
- else:
- mate['tipps'].append(u'\u2714')
- except Exception as e:
- mate['tipps'].append(u'\u2717')
-
- """ sort items in mandant_dict by score """
- mandant_dict[mandant] = sorted(mandant_dict[mandant],
- key=lambda k: k['sum_score'], reverse=True)
-
- matches.append(item)
-
- # get the newest blogposts
- pm = RelPostMandant.objects.filter(mandant__in=mandants). \
- values_list('post', flat=True)
- posts = []
- for post in Post.objects.filter(id__in=pm, published=True)[:5]:
- try:
- avatar = UserProfile.objects.get(user_id=post.author_id). \
- avatar.name
- except:
- avatar = None
- posts.append((post, avatar))
-
- try:
- cur_group = matches[0]['md_name']
- except:
- cur_group = "N/A"
-
- debug += str(mandant_dict)
-
- return django.shortcuts.render(request, 'md.html', {
- 'debug': debug,
- # 'mds_season': mds_in_season,
+ return render(request, template_name, {
'cur_md': matchday,
'md_name': cur_group,
'matches': matches,
'ls': ls,
'season': season,
- 'username': user,
+ 'username': user.username,
'mandant_dict': mandant_dict,
- # 'tipp_mates': sorted(tipp_mates, key=lambda k: k['sum_score'],
- # reverse=True),
'posts': posts,
- 'has_refresh': has_refresh
+ 'has_refresh': True
})
+def _process_matchday_tips(request, ls, season, matchday):
+ """Process and save tip form submissions."""
+ if request.method != 'POST':
+ return
+
+ md_matches = Match.objects.filter(
+ leagueShortcut=ls,
+ season=season,
+ matchday=int(matchday)
+ )
+
+ for match in md_matches:
+ form = TippForm(request.POST, prefix=str(match.matchID))
+
+ if form.is_valid():
+ data = form.cleaned_data
+ Tipp.objects.update_or_create(
+ tipperID=request.user,
+ matchID=match,
+ defaults={
+ 'pointsTeam1': data['tippTeam1'],
+ 'pointsTeam2': data['tippTeam2']
+ }
+ )
+
+
+def _build_matchday_matches(request, user, ls, season, matchday):
+ """Build match data with forms and results for template."""
+ md_matches = Match.objects.filter(
+ leagueShortcut=ls,
+ season=season,
+ matchday=int(matchday)
+ ).select_related('idTeam1', 'idTeam2').order_by('matchDateTime')
+
+ # Fetch all user tips in one query
+ user_tipps = {
+ tipp.matchID_id: tipp
+ for tipp in Tipp.objects.filter(tipperID=user, matchID__in=md_matches)
+ }
+
+ matches = []
+ for match in md_matches:
+ match_started = timezone.now() >= match.matchDateTime
+ tipp = user_tipps.get(match.id)
+
+ # Build form with initial data
+ initial = {}
+ if tipp:
+ initial = {
+ 'tippTeam1': tipp.pointsTeam1,
+ 'tippTeam2': tipp.pointsTeam2
+ }
+
+ form = TippForm(prefix=str(match.matchID), initial=initial)
+
+ if match_started:
+ form.fields['tippTeam1'].widget.attrs['disabled'] = True
+ form.fields['tippTeam2'].widget.attrs['disabled'] = True
+
+ match_result = _format_match_result(match.pointsTeam1, match.pointsTeam2)
+
+ item = {
+ 'matchDateTime': match.matchDateTime.astimezone(TIMEZONE_LOCAL).strftime(
+ '%a %d.%m, %H:%M'
+ ),
+ 'iconTeam1': str(match.idTeam1.icon),
+ 'iconURLTeam1': str(match.idTeam1.iconURL),
+ 'nameTeam1': str(match.idTeam1.name),
+ 'abbrTeam1': str(match.idTeam1.abbr),
+ 'iconTeam2': str(match.idTeam2.icon),
+ 'iconURLTeam2': str(match.idTeam2.iconURL),
+ 'nameTeam2': str(match.idTeam2.name),
+ 'abbrTeam2': str(match.idTeam2.abbr),
+ 'started': match_started,
+ 'finished': match.finished,
+ 'md_name': match.matchday_name,
+ 'matchResult': match_result,
+ 'tippFormTeam1': form['tippTeam1'],
+ 'tippFormTeam2': form['tippTeam2'],
+ 'match_id': match.id,
+ }
+
+ matches.append(item)
+
+ return matches
+
+
+def _format_match_result(points_team1, points_team2):
+ """Format match result for display."""
+ if points_team1 == -1 or points_team2 == -1:
+ return "-:-"
+ return f"{points_team1}:{points_team2}"
+
+
+def _build_mandant_scores(user, matches):
+ """Build mandant dict with user scores and tips."""
+ mandants = RelUserMandant.objects.filter(user=user).values_list(
+ 'mandant', flat=True
+ )
+
+ if not mandants:
+ return {}
+
+ mandant_dict = {}
+
+ # Fetch all mandants and related users
+ mandant_users = {}
+ for mandant in mandants:
+ mandant_obj = get_object_or_404(Mandant, id=mandant)
+ mandant_users[mandant] = RelUserMandant.objects.filter(
+ mandant=mandant
+ ).select_related('user')
+
+ # Get all tips for all matches and users
+ match_ids = [m['match_id'] for m in matches]
+ user_ids = []
+ for mandant in mandant_users:
+ user_ids.extend([rel.user_id for rel in mandant_users[mandant]])
+
+ all_tipps = {
+ (tipp.tipperID_id, tipp.matchID_id): tipp
+ for tipp in Tipp.objects.filter(
+ tipperID_id__in=user_ids,
+ matchID_id__in=match_ids
+ )
+ }
+
+ # Build mandant dict
+ for mandant in mandants:
+ mandant_obj = Mandant.objects.get(id=mandant)
+ mandant_dict[mandant_obj.description] = []
+
+ for rel in mandant_users[mandant]:
+ mate_data = {
+ 'mate': rel.user,
+ 'tipps': [],
+ 'sum_score': 0
+ }
+
+ # Get tips for this mate across all matches
+ for match in matches:
+ tipp = all_tipps.get((rel.user_id, match['match_id']))
+
+ if tipp:
+ if match['started']:
+ score = tipp.score if tipp.score is not None else '\u231B'
+ tip_str = f"{tipp.pointsTeam1}:{tipp.pointsTeam2}({score})"
+ mate_data['tipps'].append(tip_str)
+
+ if match['finished']:
+ mate_data['sum_score'] += tipp.score or 0
+ else:
+ mate_data['tipps'].append('\u2714')
+ else:
+ mate_data['tipps'].append('\u2717')
+
+ mandant_dict[mandant_obj.description].append(mate_data)
+
+ # Sort by score
+ mandant_dict[mandant_obj.description] = sorted(
+ mandant_dict[mandant_obj.description],
+ key=lambda k: k['sum_score'],
+ reverse=True
+ )
+
+ return mandant_dict
+
+
+def _get_mandant_posts(user):
+ """Get latest published blog posts for user's mandants."""
+ mandants = RelUserMandant.objects.filter(user=user).values_list(
+ 'mandant', flat=True
+ )
+
+ post_ids = RelPostMandant.objects.filter(
+ mandant__in=mandants
+ ).values_list('post', flat=True)
+
+ posts = []
+ for post in Post.objects.filter(
+ id__in=post_ids,
+ published=True
+ ).select_related('author__userprofile')[:5]:
+ avatar = None
+ try:
+ avatar = post.author.userprofile.avatar.name
+ except (ObjectDoesNotExist, AttributeError):
+ pass
+
+ posts.append((post, avatar))
+
+ return posts
+
+
@login_required
def charts(request, ls, season, pos='default',
template_name='charts.html'):