From 60d55652c09a768ea38fe2f4626bf880fbcb5336 Mon Sep 17 00:00:00 2001 From: Martin Bley Date: Wed, 20 Jan 2016 16:56:31 +0100 Subject: [PATCH] Initial commit --- templates/base.html | 60 +++ templates/blogindex.html | 72 +++ templates/blogpost.html | 32 ++ templates/charts.html | 38 ++ templates/login.html | 33 ++ templates/md.html | 109 ++++ templates/nav.html | 41 ++ templates/nav_login.html | 13 + templates/newblogpost.html | 49 ++ templates/registration/login.html | 33 ++ .../registration/password_change_done.html | 22 + .../registration/password_change_form.html | 59 +++ templates/registration/profile.html | 80 +++ .../registration/profile_change_done.html | 22 + tipp/__init__.py | 0 tipp/admin.py | 28 + tipp/forms.py | 40 ++ tipp/models.py | 111 ++++ tipp/openliga.py | 68 +++ tipp/tests.py | 16 + tipp/updateMatch.py | 3 + tipp/views.py | 486 ++++++++++++++++++ tippy/__init__.py | 0 tippy/settings.py | 188 +++++++ tippy/urls.py | 37 ++ tippy/wsgi.py | 28 + 26 files changed, 1668 insertions(+) create mode 100644 templates/base.html create mode 100644 templates/blogindex.html create mode 100644 templates/blogpost.html create mode 100644 templates/charts.html create mode 100644 templates/login.html create mode 100644 templates/md.html create mode 100644 templates/nav.html create mode 100644 templates/nav_login.html create mode 100644 templates/newblogpost.html create mode 100644 templates/registration/login.html create mode 100644 templates/registration/password_change_done.html create mode 100644 templates/registration/password_change_form.html create mode 100644 templates/registration/profile.html create mode 100644 templates/registration/profile_change_done.html create mode 100644 tipp/__init__.py create mode 100644 tipp/admin.py create mode 100644 tipp/forms.py create mode 100644 tipp/models.py create mode 100755 tipp/openliga.py create mode 100644 tipp/tests.py create mode 100644 tipp/updateMatch.py create mode 100644 tipp/views.py create mode 100644 tippy/__init__.py create mode 100644 tippy/settings.py create mode 100644 tippy/urls.py create mode 100644 tippy/wsgi.py diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..f219605 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,60 @@ +{% load static %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block head %}{% endblock %} + + + + + + + + + {% block nav %}{% endblock %} + +
+
+ +
+ {% block body %}{% endblock %} +
+
+
+
+

© Martin Bley, 2014-2015 | Dank an OpenLigaDB für + die Bereitstellung der Spielergebnisse.

+
+
+
+ + diff --git a/templates/blogindex.html b/templates/blogindex.html new file mode 100644 index 0000000..e4f318f --- /dev/null +++ b/templates/blogindex.html @@ -0,0 +1,72 @@ +{% extends "base.html" %} +{% load i18n static %} +{% load humanize %} +{% block head %} + TipPy Blog +{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +
+
+
+

TipPy Blog

+

+ Neue Nachricht +

+
+
+ {% for post, avatar in posts %} +
+
+
+
+ {{post.author.first_name}} schrieb {{post.created|naturaltime}}, + {{post.title}} +
+
+ {% if avatar %} + Profile Pic + {% else %} + Profile Pic + {% endif %} + {{post.content|safe}} +
+
+
+
+ {% endfor %} +
+
+ +
+
+
+{% endblock %} diff --git a/templates/blogpost.html b/templates/blogpost.html new file mode 100644 index 0000000..00b89b9 --- /dev/null +++ b/templates/blogpost.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% block head %} + TipPy Blog +{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +
+
+
+
+
+

{{post.title}}

+

+ geposted am + +

+
+

+ {{post.description}} +

+ {{post.content|safe}} +
+
+
+
+{% endblock %} diff --git a/templates/charts.html b/templates/charts.html new file mode 100644 index 0000000..0b094e9 --- /dev/null +++ b/templates/charts.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% block head %} + TipPy Charts +{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} + {% for mandant, users in mandant_dict.items %} +
+
+

Bestenliste + {% if pos == 'rueck' %} + (Rückrunde)

+

Hinrunde anzeigen

+ {% elif pos == 'hin' %} + (Hinrunde) +

Rückrunde anzeigen

+ {% endif %} +

Tippgemeinschaft: {{ mandant }}

+ + + + + + {% for user, score in users %} + + + + + {% endfor %} +
SpielerPunkte
{{ user }}{{ score }}
+
+
+ {% endfor %} +{% endblock %} diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..1e8be5c --- /dev/null +++ b/templates/login.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} +{% load url from future %} +{% comment %} + Main page to authenticate users. +{% endcomment %} + +{% block head %} + TipPy | Login +{% endblock %} + +{% block nav %} +{% include "nav_login.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +{% if form.errors %} +
+

Benutzename und Passwort passen nicht zusammen. Bitte versuche es noch einmal.

+
+{% endif %} +
+
+ +
+
+{% endblock %} diff --git a/templates/md.html b/templates/md.html new file mode 100644 index 0000000..9edcd98 --- /dev/null +++ b/templates/md.html @@ -0,0 +1,109 @@ +{% extends "base.html" %} +{% load i18n static %} +{% load humanize %} +{% block head %} + +{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +
+
+
+

Neueste Nachrichten

+

+ zum Blog +

+ {% for post, avatar in posts %} +
+
+ {{post.author.first_name}} schrieb {{post.created|naturaltime}}, + {{post.title}} +
+
+ {% if avatar %} + Profile Pic + {% else %} + Profile Pic + {% endif %} + {{post.content|safe}} +
+
+ {% endfor %} +
+
+
+
+

Deine Tipps

+ +
{% csrf_token %} +
+ + + + + + + + + {% for match in matches %} + {% if match.started and not match.finished %} + {% else %}{% endif %} + + + + + + + {% endfor %} +
AnstoßHeimGastErg.>Tipp
{{ match.matchDateTime }} + + {{ match.abbrTeam1 }} + + + {{ match.abbrTeam2 }} + {{ match.matchResult }}{{ match.tippFormTeam1 }}{{ match.tippFormTeam2 }}
+
+ +
+
+
+
+
+

Mitspieler

+
+ + + + + {% for match in matches %} + + {% endfor %} + + {% for mate in tipp_mates %} + + + + {% for tipp in mate.tipps %} + + {% endfor %} + + {% endfor %} +
TipperGesamt

+
{{ mate.mate }}{{ mate.sum_score }}{{ tipp }}
+
+
+
+
+{% endblock %} diff --git a/templates/nav.html b/templates/nav.html new file mode 100644 index 0000000..7bbe514 --- /dev/null +++ b/templates/nav.html @@ -0,0 +1,41 @@ +{% load i18n static %} + + diff --git a/templates/nav_login.html b/templates/nav_login.html new file mode 100644 index 0000000..cfbc19e --- /dev/null +++ b/templates/nav_login.html @@ -0,0 +1,13 @@ + diff --git a/templates/newblogpost.html b/templates/newblogpost.html new file mode 100644 index 0000000..d1f60cc --- /dev/null +++ b/templates/newblogpost.html @@ -0,0 +1,49 @@ +{% extends "base.html" %} +{% load i18n static %} +{% block head %} + TipPy Blog +{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +
+ {% if form.errors %} +
+

+ {% if form.errors.items|length == 1 %} + {% trans "Please correct the error below." %} + {% else %} + {% trans "Please correct the errors below." %} + {% endif %}

+
+ {% endif %} +
+
+
{% csrf_token %} +
+ {{form.title.errors}} + +
+ +
+
+
+ {{form.content.errors}} + +
+ +
+
+
+
+ +
+
+
+
+
+
+{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html new file mode 100644 index 0000000..1e8be5c --- /dev/null +++ b/templates/registration/login.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} +{% load url from future %} +{% comment %} + Main page to authenticate users. +{% endcomment %} + +{% block head %} + TipPy | Login +{% endblock %} + +{% block nav %} +{% include "nav_login.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +{% if form.errors %} +
+

Benutzename und Passwort passen nicht zusammen. Bitte versuche es noch einmal.

+
+{% endif %} +
+
+ +
+
+{% endblock %} diff --git a/templates/registration/password_change_done.html b/templates/registration/password_change_done.html new file mode 100644 index 0000000..3dba3ae --- /dev/null +++ b/templates/registration/password_change_done.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}{% trans 'Password change successful' %}{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} + + +
+
+
+

{% trans 'Password change successful' %}

+

{% trans 'Your password was changed.' %}

+
+
+
+ +{% endblock %} diff --git a/templates/registration/password_change_form.html b/templates/registration/password_change_form.html new file mode 100644 index 0000000..8642f38 --- /dev/null +++ b/templates/registration/password_change_form.html @@ -0,0 +1,59 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}{% trans 'Password change' %}{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +
+
+ {% if form.errors %} +

+ {% if form.errors.items|length == 1 %} + {% trans "Please correct the error below." %} + {% else %} + {% trans "Please correct the errors below." %} + {% endif %}

+ {% endif %} +
+ +
+
+ +

{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}

+
{% csrf_token %} +
+ {{ form.old_password.errors }} + +
+ +
+
+
+ {{ form.new_password1.errors }} + +
+ +
+
+
+ {{ form.new_password2.errors }} + +
+ +
+
+
+
+ +
+
+ +
+
+
+
+{% endblock %} diff --git a/templates/registration/profile.html b/templates/registration/profile.html new file mode 100644 index 0000000..89c3b0a --- /dev/null +++ b/templates/registration/profile.html @@ -0,0 +1,80 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}{% trans 'Password change' %}{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} +
+
+ {% if form.errors %} +

+ {% if form.errors.items|length == 1 %} + {% trans "Please correct the error below." %} +

{{ form.errors }} + {% else %} + {% trans "Please correct the errors below." %} + {% endif %}

+ {% endif %} +
+
+
+ + {% if img %} +

Profile Pic

+ {% endif %} +
{% csrf_token %} +
+ +
+ {% if img %} +
+ +
+ Ändern: + {% else %} + + {% endif %} +
+
+
+ +
+ {{ user_form.first_name }} +
+
+
+ +
+ {{ user_form.last_name }} +
+
+
+ +
+ {{ user_form.email }} +
+
+
+
+ +
+
+
+
+
+ {% if debug %} +
+
+ + {% for line in debug %} +

{{ line }}

+ {% endfor %} +
+
+ {% endif %} +
+{% endblock %} diff --git a/templates/registration/profile_change_done.html b/templates/registration/profile_change_done.html new file mode 100644 index 0000000..d0d0234 --- /dev/null +++ b/templates/registration/profile_change_done.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}{% trans 'Profile change successful' %}{% endblock %} + +{% block nav %} +{% include "nav.html" with ls=ls season=season %} +{% endblock %} + +{% block body %} + + +
+
+
+

{% trans 'Profile change successful' %}

+

{% trans 'Your profile was changed.' %}

+
+
+
+ +{% endblock %} diff --git a/tipp/__init__.py b/tipp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tipp/admin.py b/tipp/admin.py new file mode 100644 index 0000000..6e36dc3 --- /dev/null +++ b/tipp/admin.py @@ -0,0 +1,28 @@ +from tipp.models import Team, Match, Tipp, Score, Mandant, RelUserMandant, Competition, Post, UserProfile +from django.contrib import admin + +class PostAdmin(admin.ModelAdmin): + # fields display on change list + list_display = ['title'] + # fields to filter the change list with + list_filter = ['published', 'created'] + # fields to search in change list + search_fields = ['title', 'content'] + # enable the date drill down on change list + date_hierarchy = 'created' + # enable the save buttons on top on change form + save_on_top = True + # prepopulate the slug from the title - big timesaver! + prepopulated_fields = {"slug": ("title",)} + + + +admin.site.register(Team) +admin.site.register(Match) +admin.site.register(Tipp) +admin.site.register(Mandant) +admin.site.register(Score) +admin.site.register(RelUserMandant) +admin.site.register(Competition) +admin.site.register(Post, PostAdmin) + diff --git a/tipp/forms.py b/tipp/forms.py new file mode 100644 index 0000000..b242e8d --- /dev/null +++ b/tipp/forms.py @@ -0,0 +1,40 @@ +from django import forms +from django.forms import ModelForm +from django.contrib.auth.models import User +from django.core.files.images import get_image_dimensions +from tipp.models import Match, Competition, Post, UserProfile + +class NumberInput(forms.TextInput): + input_type = 'number' + +class TippForm(forms.Form): + tippTeam1 = forms.IntegerField(required=True,widget=NumberInput(attrs={ + 'cols': '2', + 'min': '0', 'max': '99', 'step': '1', + 'class':'form-inline', + 'role': 'form'} + )) + tippTeam2 = forms.IntegerField(required=True,widget=NumberInput(attrs={ + 'cols': '2', + 'min': '0', 'max': '99', 'step': '1', + 'class':'form-inline', + 'role': 'form'} + )) + tippTeam1.widget.attrs['style'] = "width:35px" + tippTeam2.widget.attrs['style'] = "width:35px" + +class BlogpostForm(ModelForm): + class Meta: + model = Post + fields = ['title', 'content'] + +class UserForm(forms.ModelForm): + class Meta: + model = User + fields = ['email', 'first_name', 'last_name'] + +class UserProfileForm(forms.ModelForm): + class Meta: + model = UserProfile + fields = ['avatar'] + diff --git a/tipp/models.py b/tipp/models.py new file mode 100644 index 0000000..3759ec9 --- /dev/null +++ b/tipp/models.py @@ -0,0 +1,111 @@ +from django.db import models +from django.core.urlresolvers import reverse +from django.contrib.auth.models import User + +class UserProfile(models.Model): + user = models.OneToOneField(User) + avatar = models.ImageField(upload_to="profiles/", blank=True) + + def __unicode__(self): + return self.user.username + +class Competition(models.Model): + leagueShortcut = models.CharField(max_length=32) + leagueName = models.CharField(max_length=128) + season = models.CharField(max_length=4) + + def __unicode__(self): + return(self.leagueShortcut) + +class Team(models.Model): + teamID = models.IntegerField(unique=True, primary_key=True) + name = models.CharField(max_length=200) + icon = models.FileField(upload_to="images/team_logos/") + abbr = models.CharField(max_length=3) + + def __unicode__(self): + return self.name + +class Match(models.Model): + matchID = models.IntegerField(unique=True,primary_key=True) + matchDateTime = models.DateTimeField() + group = models.IntegerField() + matchday = models.IntegerField() + idTeam1 = models.ForeignKey(Team, related_name='+') + idTeam2 = models.ForeignKey(Team, related_name='+') + pointsTeam1 = models.SmallIntegerField() + pointsTeam2 = models.SmallIntegerField() + finished = models.BooleanField() + season = models.CharField(max_length=4) + leagueShortcut = models.CharField(max_length=12) + + def __unicode__(self): + """ + return (str(self.matchID) + + str(Team.objects.get(teamID=int(self.idTeam1)).values(name)) + " - " + + str(Team.objects.get(teamID=int(self.idTeam2)).values(name))) + """ + return (str(self.matchID)) + +class Tipp(models.Model): + matchID = models.ForeignKey(Match) + tipperID = models.ForeignKey(User) + pointsTeam1 = models.PositiveSmallIntegerField() + pointsTeam2 = models.PositiveSmallIntegerField() + score = models.PositiveIntegerField(blank=True, null=True) + + class Meta: + unique_together = ("matchID", "tipperID") + +class Mandant(models.Model): + name = models.CharField(max_length=32, unique=True) + description = models.CharField(max_length=255) + def __unicode__(self): + return self.name + +class Score(models.Model): + client = models.ForeignKey(Mandant) + exact_high = models.PositiveSmallIntegerField() + exact = models.PositiveSmallIntegerField() + diff = models.PositiveSmallIntegerField() + tendency = models.PositiveSmallIntegerField() + def __unicode__(self): + return str(self.client) + +class chart(models.Model): + team = models.ForeignKey(Team) + competition = models.ForeignKey(Competition) + points = models.PositiveSmallIntegerField() + diff = models.SmallIntegerField() + games = models.PositiveSmallIntegerField() + + class Meta: + unique_together = ("team", "competition") + +class RelUserMandant(models.Model): + user = models.ForeignKey(User) + mandant = models.ForeignKey(Mandant) + + class Meta: + unique_together = ("user", "mandant") + def __unicode__(self): + return (str(self.user) + " -> " + str(self.mandant)) + +class Post(models.Model): + title = models.CharField(max_length=255) + slug = models.SlugField(unique=True, max_length=255) + content = models.TextField() + published = models.BooleanField(default=True) + created = models.DateTimeField(auto_now_add=True) + author = models.ForeignKey(User) + + class Meta: + ordering = ['-created'] + + def __unicode__(self): + return u'%s' % self.title + + def get_absolute_url(self): + return reverse('tipp.views.blogpost', args=[self.slug]) + + diff --git a/tipp/openliga.py b/tipp/openliga.py new file mode 100755 index 0000000..2eb9b48 --- /dev/null +++ b/tipp/openliga.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# Copyright 2013 by Martin Bley +# +# tippy.py 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.py 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.py. If not, see . +# + +import sys, locale, suds, urllib2, psycopg2 + +class OpenLiga(object): + version = "0.1" + error = "" + proxyurl = None + debug = None + client = None + + def __init__(self, proxyurl=None, debug=False): + + # set locale to environment (for dispaying localized dates) + locale.setlocale(locale.LC_TIME, "") + + self.debug = debug + + # get a SUDS client object + if self.proxyurl is None: + try: + self.client = suds.client.Client('http://www.openligadb.de/Webservices/Sportsdata.asmx?WSDL') + except (urllib2.URLError): + self.error += "Connect to webservice failed." + else: + try: + t = suds.transport.http.HttpTransport() + proxy = urllib2.ProxyHandler({'http':proxyurl}) + opener = urllib2.build_opener(proxy) + t.urlopener = opener + self.client = suds.client.Client('http://www.openligadb.de/Webservices/Sportsdata.asmx?WSDL', transport=t) + except urllib2.URLError as e: + self.error += "Connect to webservice failed (via proxy " + proxyurl + "): " + str(e) + "\n" + + def getSeason(self, season, league='bl1'): + """ Get the whole season. + + Args: season and league shortcut (optional) + """ + 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(groupOrderID=matchdaynumber,leagueShortcut=league,leagueSaison=season)) + + def getTeams(self, season, league='bl1'): + return(self.client.service.GetTeamsByLeagueSaison(leagueShortcut=league,leagueSaison=season)) + + diff --git a/tipp/tests.py b/tipp/tests.py new file mode 100644 index 0000000..501deb7 --- /dev/null +++ b/tipp/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/tipp/updateMatch.py b/tipp/updateMatch.py new file mode 100644 index 0000000..a3bc37b --- /dev/null +++ b/tipp/updateMatch.py @@ -0,0 +1,3 @@ +from tipp.models import Match + + diff --git a/tipp/views.py b/tipp/views.py new file mode 100644 index 0000000..773798c --- /dev/null +++ b/tipp/views.py @@ -0,0 +1,486 @@ +# -*- coding: utf-8 -*- +from django.http import HttpResponse +from django.shortcuts import render_to_response, redirect, render, get_object_or_404 +from django.contrib.auth import authenticate, login +from django.contrib.auth.models import User +from django.contrib.auth.decorators import login_required +from django.contrib.auth.views import password_reset, password_reset_confirm +from django.core.context_processors import csrf +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse +from django.core.paginator import Paginator, InvalidPage, EmptyPage, PageNotAnInteger +from django.utils import timezone +from django.utils.text import slugify +from django.db.models import Sum +from django.conf import settings +from os.path import join as pjoin +from tipp.models import * +from tipp.forms import * +from openliga import * +from datetime import datetime +from PIL import Image as PImage +import urllib2, pytz, os + +timezoneLocal = pytz.timezone('Europe/Berlin') + +@login_required +def home(request): + ls = get_current_ls() + season = get_current_season() + + return redirect("matchday", + ls = ls, + season = season, + matchday=str(get_current_md(ls, season)).zfill(2) + ) + +@login_required +def profile(request, pk): + user = User.objects.get(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': + # Attempt to grab information from the raw form information. + # Note that we make use of both UserForm and UserProfileForm. + user_form = UserForm(data=request.POST, instance=user) + profile_form = UserProfileForm(data=request.POST, instance=profile) + + # If the two forms are valid... + if user_form.is_valid() and profile_form.is_valid(): + + # Save the user's form data to the database. + user = user_form.save() + + # Now sort out the UserProfile instance. + # Since we need to set the user attribute ourselves, we set commit=False. + # This delays saving the model until we're ready to avoid integrity problems. + profile = profile_form.save(commit=False) + profile.user = user + + # Did the user provide a profile picture? + # If so, we need to get it from the input form and put it in the UserProfile model. + 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 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. + 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 render(request, 'registration/profile.html', { + 'user_form': user_form, + 'profile_form': profile_form, + 'debug': debug, + 'img': img} + ) + +@login_required +def update(request, ls, season, cur_md): + """ + To do: + - update table tipp_competition + """ + + 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'] == "Endergebnis": + pointsTeam1=int(result['pointsTeam1']) + pointsTeam2=int(result['pointsTeam2']) + elif result['resultName'] == "Halbzeitergebnis": + pointsTeam1=int(result['pointsTeam1']) + pointsTeam2=int(result['pointsTeam2']) + except: + pointsTeam1=-1 + pointsTeam2=-1 + + m = Match(matchID=unicode(match['matchID']), + matchDateTime=datetime.strptime(unicode(match['matchDateTime']), '%Y-%m-%d %H:%M:%S'), + group=int(match['groupID']), + matchday=int(match['groupOrderID']), + idTeam1=Team.objects.get(teamID=int(match['idTeam1'])), + idTeam2=Team.objects.get(teamID=int(match['idTeam2'])), + pointsTeam1=pointsTeam1, + pointsTeam2=pointsTeam2, + finished=bool(match['matchIsFinished']), + season=match['leagueSaison'], + leagueShortcut=match['leagueShortcut'] + ) + m.save() + pointsTeam1=-1 + pointsTeam2=-1 + + """ evaluate scores """ + scores = Score.objects.get(client=Mandant.objects.get(name='huse')) + finished_matches=Match.objects.filter(finished=True) + 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 + + tipp.score = score + tipp.save() + continue + + return redirect("matchday", ls=ls, season=season, matchday=cur_md) + +@login_required +def matchday(request, ls, season, matchday, template_name='md.html'): + debug = '' + debug += "Debugging: " + has_refresh = True + + """ get matchday closest to current date """ + 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 """ + tipp_mates = [] + for m in mandants: + rs = RelUserMandant.objects.filter(mandant=m) + for r in rs: + tipp_mates.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['nameTeam1']= str(match.idTeam1) + item['abbrTeam1']= str(match.idTeam1.abbr) + item['iconTeam2'] = str(match.idTeam2.icon) + item['nameTeam2'] = str(match.idTeam2) + item['abbrTeam2']= str(match.idTeam2.abbr) + item['started'] = match_started + item['finished'] = match.finished + + 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 + + for mate in tipp_mates: + try: + matetipp = Tipp.objects.get(tipperID=mate['mate'],matchID=match.matchID) + if match_started is True: + mate['tipps'].append(str(matetipp.pointsTeam1) + ":" + str(matetipp.pointsTeam2) + "(" + str(matetipp.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') + + matches.append(item) + + # get the newest blogposts + posts = [] + for post in Post.objects.filter(published=True)[:2]: + try: + avatar = UserProfile.objects.get(user_id=post.author_id).avatar.name + except: + avatar = None + posts.append( (post, avatar) ) + + return render(request, 'md.html', { + #'debug': debug, + #'mds_season': mds_in_season, + 'cur_md': matchday, + 'matches': matches, + 'ls': ls, + 'season': season, + 'username': user, + 'tipp_mates': sorted(tipp_mates, key=lambda k: k['sum_score'], reverse=True), + 'posts': posts, + 'has_refresh': has_refresh + }) + +@login_required +def charts(request, ls, season, pos='default', template_name='charts.html'): + debug = '' + + if pos == 'default': + if get_current_md(ls, season) < 18: + pos = 'hin' + matches = Match.objects.filter(leagueShortcut=ls, season=season, matchday__lte=17) + else: + pos = 'rueck' + matches = Match.objects.filter(leagueShortcut=ls, season=season, matchday__gte=18) + elif pos == 'hin': + matches = Match.objects.filter(leagueShortcut=ls, season=season, matchday__lte=17) + elif pos == 'rueck': + matches = Match.objects.filter(leagueShortcut=ls, season=season, matchday__gte=18) + else: + matches = Match.objects.filter(leagueShortcut=ls, season=season) + + # get mandants of current user + mandants = RelUserMandant.objects.filter(user=request.user).values_list('mandant', flat=True) + + # main loop + mandant_dict = {} + for m in mandants: + mandant = Mandant.objects.get(id=m) + + # get users + users = RelUserMandant.objects.filter(mandant=m).values_list('user', flat=True) + + user_dict = {} + for userid in users: + # get user object, then fetch matches for user + user = User.objects.get(id=userid) + score = Tipp.objects.filter(matchID__in=matches, tipperID=user).aggregate(Sum('score')) + + user_dict[user.first_name] = score['score__sum'] + + mandant_dict[mandant.name] = sorted(user_dict.items(), key=lambda x: x[1], reverse=True) + + return render(request, template_name, { + #'debug': debug, + 'mandant_dict': mandant_dict, + 'season': season, + 'ls': ls, + 'pos': pos + }) + + +@login_required +def blogindex(request, page): + # get the blog posts that are published + post_list = [] + for post in Post.objects.filter(published=True): + try: + avatar = UserProfile.objects.get(user_id=post.author_id).avatar.name + except: + avatar = None + post_list.append( (post, avatar) ) + + p = Paginator(post_list, 5) + + try: + posts = p.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + posts = p.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + posts = p.page(p.num_pages) + + # now return the rendered template + return render(request, 'blogindex.html', { + 'posts': posts, + 'ls': get_current_ls(), + 'season': get_current_season(), + 'page': page + }) + +@login_required +def blogpost(request, slug): + # get the Post object + post = get_object_or_404(Post, slug=slug) + # now return the rendered template + return render(request, 'blogpost.html', { + 'post': post, + 'ls': get_current_ls(), + 'season': get_current_season() + }) + +@login_required +def newBlogpost(request): + """ + if this is a POST request we need to process the form data + """ + if request.method == 'POST': + # create form instance and populate it with data from the request + form = BlogpostForm(request.POST) + # check whether it's valid: + if form.is_valid(): + # process the data + data = form.cleaned_data + slug = slugify(data['title']) + p = Post(author_id=request.user.id, slug=slug, title=data['title'], content=data['content']) + p.save() + + # ... + # redirect to a new URL: + return redirect( '/blog/1') + + # if a GET (or any other method) we'll create a blank form + else: + blogpostForm = BlogpostForm() + return render(request, 'newblogpost.html', { + 'form': blogpostForm, + 'ls': get_current_ls(), + 'season': get_current_season() + }) + +def logout(request): + """ + Log users out and re-direct them to the main page. + """ + logout(request) + return HttpResponseRedirect('/') + +def get_current_md(ls,season): + """ get current matchday """ + current_md = Match.objects.filter(leagueShortcut=ls, season=season, finished=False).order_by('matchday').values_list('matchday', flat=True).distinct()[0] + return(current_md) + +def get_current_ls(): + return("bl1") + +def get_current_season(): + return("2015") + diff --git a/tippy/__init__.py b/tippy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tippy/settings.py b/tippy/settings.py new file mode 100644 index 0000000..564c53c --- /dev/null +++ b/tippy/settings.py @@ -0,0 +1,188 @@ +""" +Django settings for tippy project. + +For more information on this file, see +https://docs.djangoproject.com/en/1.7/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.7/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + +# Django settings for tippy project. +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + ('Martin Bley', 'martin@mb-oss.de'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'django_tippy', + 'USER': 'tippy', + 'PASSWORD': 'tippy', + 'HOST': 'localhost', + 'PORT': '5432', + } +} + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/1.4/ref/settings/#allowed-hosts +ALLOWED_HOSTS = ['*'] + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# In a Windows environment this must be set to your system time zone. +TIME_ZONE = 'Europe/Berlin' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'de-de' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale. +USE_L10N = True + +# If you set this to False, Django will not use timezone-aware datetimes. +USE_TZ = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '/media/' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +#STATIC_ROOT = '/opt/TipPy/TipPy/static/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'static/') + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + #'/opt/TipPy/TipPy/tipp/static/', +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = '-7%ge^nl1b0t0694%ws&0$ft_lo)j4l1f-qv#ku^=$)s(sw6us' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + # Uncomment the next line for simple clickjacking protection: + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', +) + +ROOT_URLCONF = 'tippy.urls' + +# Python dotted path to the WSGI application used by Django's runserver. +WSGI_APPLICATION = 'tippy.wsgi.application' + +TEMPLATE_DIRS = ( + [os.path.join(BASE_DIR, 'templates')] +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.humanize', + # Uncomment the next line to enable the admin: + 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + 'django.contrib.admindocs', + 'tipp', +) + +# URL of the login page. +LOGIN_URL = '/login' + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} + +EMAIL_USE_TLS = True +EMAIL_HOST = 'hermes.mb-oss.de' +EMAIL_PORT = 25 +EMAIL_HOST_USER = 'tippy@mb-oss.de' +#EMAIL_HOST_PASSWORD = 'p@ssword' + +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER +SERVER_EMAIL = EMAIL_HOST_USER + +SESSION_COOKIE_AGE = 7200 + +AUTH_PROFILE_MODULE = 'tippy.UserProfile' + + diff --git a/tippy/urls.py b/tippy/urls.py new file mode 100644 index 0000000..a30e989 --- /dev/null +++ b/tippy/urls.py @@ -0,0 +1,37 @@ +from django.conf.urls import patterns, include, url +from django.conf import settings + +# Uncomment the next two lines to enable the admin: +from django.contrib import admin +admin.autodiscover() + +urlpatterns = patterns('', + # Examples: + url(r'^$', 'tipp.views.home', name='home'), + url(r'^login$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}), + url(r'^logout$', 'django.contrib.auth.views.logout', {'next_page': '/'}), + url(r'^update/(?P.*)/(?P\d{4})/(?P\d{2})$', 'tipp.views.update', name='update'), + url(r'^matchday/(?P.{3,})/(?P\d{4})/(?P\d{2})$', 'tipp.views.matchday', name='matchday'), + url(r'^charts/(?P\w{3,})/(?P\d{4})$', 'tipp.views.charts', name='charts'), + url(r'^charts/(?P\w{3,})/(?P\d{4})/(?P\w{1,})$', 'tipp.views.charts', name='charts'), + url(r'^accounts/profile/(\d+)', 'tipp.views.profile'), + url(r'^accounts/', include('django.contrib.auth.urls')), + url(r'^blog/newpost$', 'tipp.views.newBlogpost'), + url(r'^blog/(?P\d)$', 'tipp.views.blogindex'), + url(r'^blog/(?P[\w\-]+)', 'tipp.views.blogpost'), + + # Uncomment the admin/doc line below to enable admin documentation: + # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), + + # Uncomment the next line to enable the admin: + url(r'^admin/', include(admin.site.urls)), +) + +if settings.DEBUG: + urlpatterns += patterns('', + url(r'^media/(?P.*)$', 'django.views.static.serve', { + 'document_root': settings.MEDIA_ROOT, + }), +) + + diff --git a/tippy/wsgi.py b/tippy/wsgi.py new file mode 100644 index 0000000..1c590e4 --- /dev/null +++ b/tippy/wsgi.py @@ -0,0 +1,28 @@ +""" +WSGI config for tippy project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tippy.settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application)