2 Commits
Author SHA1 Message Date
Martin Bley 3322649f05 Code optimizsations 2026-09-19 09:04:26 +02:00
Martin Bley 6b99dd5250 venv is running 2026-09-19 07:58:45 +02:00
4 changed files with 563 additions and 421 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ WSGI_APPLICATION = 'TipPy.wsgi.application'
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases # https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql',
'NAME': 'tippy', 'NAME': 'tippy',
'USER': 'tippy', 'USER': 'tippy',
'PASSWORD': 'tippy', 'PASSWORD': 'tippy',
Executable
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TipPy.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
+19 -27
View File
@@ -23,7 +23,9 @@ import urllib.error
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from suds.client import Client from zeep import Client
from zeep.transports import Transport
from requests import Session
class OpenLiga(object): class OpenLiga(object):
@@ -40,54 +42,44 @@ class OpenLiga(object):
self.debug = debug self.debug = debug
# get a SUDS client object # get a SOAP client object
if self.proxyurl is None: wsdl_url = 'http://www.openligadb.de/Webservices/Sportsdata.asmx?WSDL'
try: try:
self.client = Client( if proxyurl is None:
'http://www.openligadb.de/' self.client = Client(wsdl=wsdl_url)
+ 'Webservices/Sportsdata.asmx?WSDL')
except (urllib.error.URLError):
self.error += "Connect to webservice failed."
else: else:
try: session = Session()
t = suds.transport.http.HttpTransport() session.proxies = {'http': proxyurl, 'https': proxyurl}
proxy = urllib.request.ProxyHandler({'http': proxyurl}) transport = Transport(session=session)
opener = urllib.request.build_opener(proxy) self.client = Client(wsdl=wsdl_url, transport=transport)
t.urlopener = opener except (urllib.error.URLError, Exception) as e:
self.client = suds.client.Client( self.error += f"Connect to webservice failed: {str(e)}\n"
'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"
def getSeason(self, season, league='bl1'): def getSeason(self, season, league='bl1'):
""" Get the whole season. """ Get the whole season.
Args: season and league shortcut (optional) Args: season and league shortcut (optional)
""" """
return (self.client.service.GetMatchdataByLeagueSaison( return self.client.service.GetMatchdataByLeagueSaison(
leagueShortcut=league, leagueSaison=season) leagueShortcut=league, leagueSaison=season)
)
def getMatchday(self, season, matchdaynumber, league='bl1'): def getMatchday(self, season, matchdaynumber, league='bl1'):
""" Get matchday. """ Get matchday.
Args: season, matchdaynumber and league shortcut (optional) Args: season, matchdaynumber and league shortcut (optional)
""" """
return (self.client.service.GetMatchdataByGroupLeagueSaison( return self.client.service.GetMatchdataByGroupLeagueSaison(
groupOrderID=matchdaynumber, groupOrderID=matchdaynumber,
leagueShortcut=league, leagueShortcut=league,
leagueSaison=season) leagueSaison=season)
)
def getTeams(self, season, league='bl1'): def getTeams(self, season, league='bl1'):
return (self.client.service.GetTeamsByLeagueSaison( return self.client.service.GetTeamsByLeagueSaison(
leagueShortcut=league, leagueShortcut=league,
leagueSaison=season) leagueSaison=season)
)
def getCurrentGroup(self, league): def getCurrentGroup(self, league):
return (self.client.service.GetCurrentGroup( return self.client.service.GetCurrentGroup(
leagueShortcut=league) leagueShortcut=league)
)
+482 -355
View File
@@ -21,474 +21,601 @@ along with TipPy. If not, see <http://www.gnu.org/licenses/>.
import os import os
from os.path import join as pjoin from os.path import join as pjoin
import django.shortcuts import logging
import pytz import pytz
from PIL import Image as PImage from PIL import Image as PImage
from django.conf import settings from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_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.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, EmptyPage, \ from django.core.files.storage import default_storage
PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Sum from django.db.models import Sum
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone from django.utils import timezone
from django.views.decorators.http import require_http_methods
from tipp.forms import * from tipp.forms import *
from tipp.models import * from tipp.models import *
from tipp.openliga 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): def about(request):
return django.shortcuts.render(request, 'about.html') """Render about page."""
return render(request, 'about.html')
@login_required @login_required
def home(request): def home(request):
"""Redirect to current matchday."""
ls = get_current_ls() ls = get_current_ls()
season = get_current_season() season = get_current_season()
md = get_current_md(ls) md = get_current_md(ls)
return django.shortcuts.redirect("matchday", return redirect(
'matchday',
ls=ls, ls=ls,
season=season, season=season,
matchday=str(md).zfill(2) 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 @login_required
@require_http_methods(["GET", "POST"])
def profile(request, pk): 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) 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': if request.method == 'POST':
return _handle_profile_post(request, user, profile)
else:
return _handle_profile_get(request, user, profile)
def _handle_profile_post(request, user, profile):
"""Handle POST request for profile update."""
user_form = UserForm(data=request.POST, instance=user) user_form = UserForm(data=request.POST, instance=user)
profile_form = UserProfileForm( profile_form = UserProfileForm(data=request.POST, instance=profile)
data=request.POST, instance=profile
)
if user_form.is_valid() and profile_form.is_valid(): if user_form.is_valid() and profile_form.is_valid():
user = user_form.save() user = user_form.save()
profile = profile_form.save(commit=False) profile = profile_form.save(commit=False)
profile.user = user profile.user = user
# Did the user provide a profile picture? # Handle avatar upload
if 'avatar' in request.FILES: if 'avatar' in request.FILES:
profile.avatar = request.FILES['avatar'] profile.avatar = request.FILES['avatar']
# Now we save the UserProfile model instance.
profile.save() profile.save()
# create thumbnail image_path = os.path.join(settings.MEDIA_ROOT, profile.avatar.name)
imfn = pjoin(settings.MEDIA_ROOT, profile.avatar.name) try:
im = PImage.open(imfn) _create_thumbnail(image_path)
im.thumbnail((50, 50), PImage.ANTIALIAS) except Exception as e:
im.save(settings.MEDIA_ROOT + profile.avatar.name) logger.error(f"Thumbnail creation failed: {str(e)}")
if 'avatar-clear' in request.POST:
if 'on' in request.POST['avatar-clear']: # Handle avatar deletion
image = pjoin(settings.MEDIA_ROOT, img) elif 'avatar-clear' in request.POST and request.POST['avatar-clear'] == 'on':
debug.append('Is the image ' + str(image) if profile.avatar:
+ ' a regular file? ' old_image_path = os.path.join(settings.MEDIA_ROOT, profile.avatar.name)
+ str(os.path.isfile(image)) _delete_avatar(old_image_path)
) profile.avatar = None
if os.path.isfile(image):
os.remove(image)
profile.save() profile.save()
return redirect('profile', pk=user.id)
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: else:
print( logger.warning(f"Form validation failed for user {user.id}: "
user_form.errors, f"User errors: {user_form.errors}, "
profile_form.errors f"Profile errors: {profile_form.errors}")
)
return django.shortcuts.redirect('/accounts/profile/' + str(user.id)) return _render_profile_form(request, user_form, profile_form, profile)
# Not a HTTP POST, so we render our form using two ModelForm
# instances. These forms will be blank, ready for user input. def _handle_profile_get(request, user, profile):
else: """Handle GET request for profile display."""
user_form = UserForm( user_form = UserForm(instance=user)
initial={'last_name': user.last_name,
'first_name': user.first_name,
'email': user.email},
instance=user
)
profile_form = UserProfileForm(instance=profile) profile_form = UserProfileForm(instance=profile)
return django.shortcuts.render(request, 'registration/profile.html', { 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, 'user_form': user_form,
'profile_form': profile_form, 'profile_form': profile_form,
'debug': debug, 'avatar': profile.avatar.name if profile.avatar else None,
'img': img} }
) return render(request, 'registration/profile.html', context)
@staff_member_required @staff_member_required
def staff(request): def staff(request):
season = get_current_season(), """Display available competitions for current season."""
""" get available competitions """ season = get_current_season()
competitions = Competition.objects.filter(season=season).order_by('leagueName') 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 @staff_member_required
def getSeason(request, ls, season): def getSeason(request, ls, season):
"""Fetch and import teams and matches for a season from OpenLiga."""
ol = OpenLiga() ol = OpenLiga()
teams = ol.getTeams(str(season), ls) teams_data = ol.getTeams(str(season), ls)
season = ol.getSeason(str(season), ls) season_data = ol.getSeason(str(season), ls)
return_vals = [] # Bulk import teams
for team in teams[0]: _import_teams(teams_data[0])
t = Team(
teamID=team['teamID'],
name=team['teamName'],
iconURL=team['teamIconURL']
)
""" # Bulk import matches
check if object exits and do insert if not _import_season_matches(season_data[0], ls)
"""
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]: return redirect(
m = Match(matchID=match['matchID'], 'matchday',
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, ls=ls,
season=get_current_season(), season=get_current_season(),
matchday=str(get_current_md(ls)).zfill(2) 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']
)
)
if matches:
Match.objects.bulk_create(matches, ignore_conflicts=True)
@login_required @login_required
def update(request, ls, season, cur_md): def update(request, ls, season, cur_md):
""" """Update match results and calculate user scores for current matchday."""
To do:
- update table tipp_competition
"""
ol = OpenLiga() ol = OpenLiga()
md_matches = ol.getMatchday(str(season), int(cur_md), ls) md_matches = ol.getMatchday(str(season), int(cur_md), ls)
# Achtung: DateTimeField prüfen # Update match results
for match in md_matches[0]: _update_match_results(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
except: # Calculate scores for all tips
pointsTeam1 = -1 _calculate_matchday_scores(request.user, ls, season, cur_md)
pointsTeam2 = -1
m = Match(matchID=match['matchID'], return redirect('matchday', ls=ls, season=season, matchday=cur_md)
matchDateTime=match['matchDateTime'],
pointsTeam1=pointsTeam1,
pointsTeam2=pointsTeam2, def _update_match_results(matches_data):
finished=bool(match['matchIsFinished']), """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']),
)
) )
m.save(update_fields=['matchDateTime', 'pointsTeam1', 'pointsTeam2', 'finished'])
pointsTeam1 = -1
pointsTeam2 = -1
# evaluate scores if matches_to_update:
""" get mandants current user is related """ Match.objects.bulk_update(
mandants = RelUserMandant.objects.filter(user=request.user). \ matches_to_update,
values_list('mandant', flat=True) ['matchDateTime', 'pointsTeam1', 'pointsTeam2', 'finished'],
batch_size=100
)
# scores = Score.objects.get(client=Mandant.objects.get(name='huse'))
scores = Score.objects.get(client=mandants[0]) 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_matches = Match.objects.filter(
finished=True, finished=True,
leagueShortcut=ls, leagueShortcut=ls,
season=season, season=season,
matchday=cur_md matchday=cur_md
) ).prefetch_related('tipp_set')
tipps_to_update = []
for match in finished_matches: for match in finished_matches:
tipps = Tipp.objects.filter(matchID=match.matchID) for tipp in match.tipp_set.all():
for tipp in tipps: score = _calculate_tipp_score(tipp, match, scores)
score = 0 tipp.score = score
tipps_to_update.append(tipp)
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 goals_total = match.pointsTeam1 + match.pointsTeam2
return scores.exact_high if goals_total >= 5 else scores.exact
# Tipp is acurate # Winner and difference
if (tipp.pointsTeam1 == match.pointsTeam1 winner_real = _get_winner(match.pointsTeam1, match.pointsTeam2)
and tipp.pointsTeam2 == match.pointsTeam2): winner_tipp = _get_winner(tipp.pointsTeam1, tipp.pointsTeam2)
if goals_total >= 5:
score = scores.exact_high 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: else:
score = scores.exact return scores.tendency
tipp.score = score return 0
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 def _get_winner(points_team1, points_team2):
if tipp.pointsTeam1 > tipp.pointsTeam2: """Determine match winner.
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
tipp.score = score
tipp.save()
continue
return django.shortcuts.redirect("matchday",
ls=ls,
season=season,
matchday=cur_md
)
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 @login_required
@require_http_methods(["GET", "POST"])
def matchday(request, ls, season, matchday, template_name='md.html'): def matchday(request, ls, season, matchday, template_name='md.html'):
update(request, ls, season, matchday) """Display matchday with user tips, match results, and mandant scores."""
debug = '' # Process form submissions first
debug += "Debugging: " _process_matchday_tips(request, ls, season, matchday)
has_refresh = True
""" get matches of the matchday """ user = request.user
md_matches = Match.objects.filter(leagueShortcut=ls, season=season, matches = _build_matchday_matches(request, user, ls, season, matchday)
matchday=int(matchday)).order_by('matchDateTime') mandant_dict = _build_mandant_scores(user, matches)
posts = _get_mandant_posts(user)
""" get the current user """ cur_group = matches[0]['md_name'] if matches else "N/A"
user = User.objects.get(username=request.user.username)
""" get mandants current user is related """ return render(request, template_name, {
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,
'cur_md': matchday, 'cur_md': matchday,
'md_name': cur_group, 'md_name': cur_group,
'matches': matches, 'matches': matches,
'ls': ls, 'ls': ls,
'season': season, 'season': season,
'username': user, 'username': user.username,
'mandant_dict': mandant_dict, 'mandant_dict': mandant_dict,
# 'tipp_mates': sorted(tipp_mates, key=lambda k: k['sum_score'],
# reverse=True),
'posts': posts, '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 @login_required
def charts(request, ls, season, pos='default', def charts(request, ls, season, pos='default',
template_name='charts.html'): template_name='charts.html'):