Home > Django > djangofantalega: model Player e model Evaluation

djangofantalega: model Player e model Evaluation

14 Novembre 2016

7 – Models: Player ed Evaluation

Creare i modelli Player ed Evaluation.

Per Evaluation si intendono i voti che settimanalmente vengono
pubblicati e con i quali si effettuano i conteggi delle squadre.
La fantalega in oggetto si basa sulla creazione di squadre tramite Asta
(Auction) pertanto un giocatore, potrà appartenere solo ad una squadra.
Per questo la relazione Team-Player sarà di tipo one-to-many.
Lo stesso Player avrà una relazione one-to-many con Evaluation.

add_player

Aggiungere nel file fantalega/models.py i models Player e Evaluation:

...
from .models import Player, Evaluation


...
class Player(models.Model):
    code = models.IntegerField()
    name = models.CharField(max_length=32)
    real_team = models.CharField(max_length=3)
    cost = models.IntegerField()
    auction_value = models.IntegerField()
    role = models.CharField(max_length=16)
    team = models.ForeignKey(Team, null=True)
    season = models.ForeignKey(Season, related_name='players')

    def __unicode__(self):
        return self.name

    @staticmethod
    def get_by_code(code, season):
        return Player.objects.filter(code=int(code), season=season).first()

    @staticmethod
    def code_to_role(code):
        if int(code) < 200:
            return 'goalkeeper'
        elif 200 < int(code) < 500:
            return 'defender'
        elif 500 < int(code) < 800:
            return 'midfielder'
        elif int(code) > 800:
            return 'forward'
        else:
            return 'unknown'


class Evaluation(models.Model):
    season = models.ForeignKey(Season, related_name='evaluations')
    player = models.ForeignKey(Player, related_name='player_votes')
    day = models.IntegerField()
    net_value = models.FloatField()
    fanta_value = models.FloatField()
    cost = models.IntegerField()

    def __unicode__(self):
        return "[%s] %s" % (self.day, self.player.name)

    @staticmethod
    def get_evaluations(day, code, season):
        """get_evaluations(self, day, code, season) -> fanta_value, net_value
           code: Player.code
           day: lineup.day + league.offset
           season: season object
        """
        player = Player.objects.filter(code=int(code), season=season).first()
        evaluation = Evaluation.objects.filter(day=day, player=player).first()
        if evaluation and player:
            return code, evaluation.fanta_value, evaluation.net_value
        else:
            return code, None, None

    @staticmethod
    def upload(path, day, season):
        # with open(path) as data:  # for shell string-file-path upload
        with path as data:  # for InMemoryUploadedFile object upload
            for record in data:  # nnn|PLAYER_NAME|REAL_TEAM|x|y|n
                code, name, real_team, fv, v, cost = record.strip().split("|")
                player = Player.get_by_code(code, season)
                role = Player.code_to_role(code.strip())
                if not player:
                    player = Player(name=name, code=code, role=role,
                                    real_team=real_team, cost=cost,
                                    auction_value=0, season=season)
                    print "[INFO] Creating %s %s" % (code, name)
                else:
                    player.cost = cost
                    player.real_team = real_team
                    print "[INFO] Upgrading %s %s" % (code, name)
                player.save()
                # storing evaluation
                evaluation = Evaluation.objects.filter(day=day, season=season,
                                                       player=player).first()
                if evaluation:
                    evaluation.net_value = v
                    evaluation.fanta_value = fv
                    evaluation.cost = cost
                    evaluation.save()
                    print "[INFO] Upgrading values day: %s player %s [%s]" % (
                        day, player.name, season.name)
                else:
                    Evaluation.objects.create(day=day, player=player, cost=cost,
                                              net_value=v, fanta_value=fv,
                                              season=season)
                    print "[INFO] Creating values day: %s player %s [%s]" % (
                        day, player.name, season.name)
        print "[INFO] Evaluation uploading done!"

Tenendo traccia anche di stagioni precedenti, Evaluation avrà una relazione
many-to-one con Season. Stesso discorso per Player. Ogni stagione (Season)
avrà i suoi giocatori (Player) di riferimento quindi la relazione
Season-Player sarà di tipo one-to-many.

Aggiornare il database inserendo la nuova tabella.

(venv) >python manage.py makemigrations
Migrations for 'fantalega':
  fantalega\migrations\0005_auto_20161110_1407.py:
    - Create model Evaluation
    - Create model Player
    - Add field player to evaluation
    - Add field season to evaluation

confermare con:

(venv) C:\tmp\djangosite>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, fantalega, log, sessions
Running migrations:
  Applying fantalega.0005_auto_20161110_1407... OK

Sistemare l’interfaccia di admin per i model Player ed Evaluation:
file fantalega/admin.py:

# noinspection PyUnresolvedReferences
...
from .models import Player, Evaluation


# Register your models here.
...
class PlayersInline(admin.TabularInline):
    model = Player
    extra = 0
    classes = ('collapse',)

...
class EvaluationAdmin(admin.ModelAdmin):
    list_display = ('day', 'player', 'fanta_value', 'net_value', 'cost')
    list_filter = ('day', 'player', 'season')
    list_per_page = 50


class PlayerAdmin(admin.ModelAdmin):
    ordering = ('code', )
    list_display = ('code', 'name', 'real_team', 'cost')
    list_filter = ('season', 'role', 'team')
    list_per_page = 20

...
admin.site.register(Player, PlayerAdmin)
admin.site.register(Evaluation, EvaluationAdmin)

La classe PlayersInline la facciamo apparire nell’interfaccia di
admin di Team, poichè è fondamentale poter vedere da quali giocatori
è composta la rosa della squadra, quindi, sempre in admin:

...
class PlayersInline(admin.TabularInline):
    model = Player
    extra = 0
    classes = ('collapse',)


class TeamAdmin(admin.ModelAdmin):
    inlines = [LeaguesInline, PlayersInline]  # TabularInLine added
    list_display = ('name', 'budget', 'max_trades')
    ordering = ('-budget', )

Ora aggiungere gli urls inerenti Player ed Evaluation nel file fantalega\urls.py:

...
    # players urls
    url(r'^players/$', views.players, name='players'),
    url(r'^players/(?P<player_id>[0-9]+)/$', views.player_details,
        name='player_details'),
    # votes urls
    url(r'^leagues/(?P<league_id>[0-9]+)/votes/(?P<day>[0-9]+)/$',
        views.vote, name='vote'),
    url(r'^leagues/(?P<league_id>[0-9]+)/upload$', views.upload_votes,
        name='upload_votes'),
]

Aggiungere nel file fantalega\views.py le nuove viste:

...
from .models import Player, Evaluation
...
@login_required
def players(request):
    sorted_players = Player.objects.order_by('code')
    context = {'players': sorted_players}
    return render(request, 'fantalega/players.html', context)


@login_required
def player_details(request, player_id):
    player = Player.objects.get(id=int(player_id))
    votes = player.player_votes.all()
    context = {'player': player, 'votes': votes}
    return render(request, 'fantalega/player.html', context)


@login_required
def vote(request, league_id, day):
    league = get_object_or_404(League, pk=int(league_id))
    if request.GET.get('back_to_teams'):
        return redirect('league_details', league.id)
    votes = Evaluation.objects.filter(season=league.season, day=day).all()
    context = {'votes': votes, 'day': day, 'league': league}
    return render(request, 'fantalega/vote.html', context)


@login_required
def upload_votes(request, league_id):
    league = get_object_or_404(League, pk=int(league_id))
    seasons = enumerate([season.name for season in Season.objects.all()])


    if request.GET.get('back_to_teams'):
        return redirect('league_details', league.id)
    if request.method == "POST":
        form = UploadVotesForm(request.POST, request.FILES,
                               initial={'seasons': seasons})
        if form.is_valid():
            day = form.cleaned_data['day']
            dict_season = dict(form.fields['season'].choices)
            season = dict_season[int(form.cleaned_data['season'])]
            obj_season = get_object_or_404(Season, name=season)
            file_in = request.FILES['file_in']
            Evaluation.upload(path=file_in, day=day, season=obj_season)
            messages.success(request, 'votes uploaded!')
            return redirect('league_details', league.id)
    else:
        form = UploadVotesForm(initial={'seasons': seasons})
    return render(request, 'fantalega/upload_votes.html',
                  {'form': form, 'league': league})

il form che permetterà l’upload dei voti e dei giocatori (fantalega/forms.py) sarà:

...
class UploadVotesForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.dict_values = kwargs.pop('initial')
        super(UploadVotesForm, self).__init__(*args, **kwargs)
        self.fields['day'] = forms.IntegerField()
        self.fields['season'] = forms.ChoiceField(label=u'season',
                                   choices=self.dict_values['seasons'],
                                   widget=forms.Select(),)
        self.fields['file_in'] = forms.FileField()

e andrà importato nel file fantalega/views.py:

from .forms import UploadVotesForm
...

Ora le template legate alle views suddette.

templates/players.html

{% extends 'fantalega/base.html' %}

{% block content %}
        <font color="green"><b>List of all players</b></font><br><br>
    {% if players %}
        <table class="table table-striped" width="100%">
          <tr>
              <th>code</th>
              <th>name</th>
              <th>team</th>
              <th>fanta team</th>
              <th>season</th>
          </tr>
          <tr>
              {% for player in players %}
                  <td>{{ player.code }}</td>
                  <td><a href="{% url 'player_details' player.id %}">
                      {{ player.name }}</a></td>
                  <td>{{ player.real_team }}</td>
                  <td>{{ player.team.name }}</td>
                  <td>{{ player.season }}</td>
          </tr>
              {% endfor %}
        </table>
    {% else %}
      <font color="red"><b>No players found.</b></font>
    {% endif %}
{% endblock %}

templates/player.html

{% extends 'fantalega/base.html' %}
{% load app_filters %}

{% block content %}
		<h1><font color="green">{{ player.name }}</font></h1>

	<table class="table table-striped" width="100%">
	  <tr>
		  <th>code</th>
		  <th>real team</th>
		  <th>role</th>
		  <th>cost</th>
		  <th>auction value</th>
		  <th>season</th>
	  </tr>
	  <tr>
		  <td>{{ player.code }}</td>
		  <td>{{ player.real_team }}</td>
		  <td>{{ player.role }}</td>
		  <td>{{ player.cost }}</td>
		  <td>{{ player.auction_value }}</td>
		  <td>{{ player.season }}</td>
	  </tr>
	</table>
    <br>
    <b><font color="orange">votes</font></b>
    <br>
	<table class="table table-striped" width="100%">
	  <tr>
		  <th>day</th>
		  <th>fanta value</th>
		  <th>value</th>
		  <th>cost</th>
	  </tr>
        {% for vote in votes %}
          <tr>
			  <td>{{ vote.day }}</td>
			  <td>{{ vote.fanta_value }}</td>
			  <td>{{ vote.net_value }}</td>
			  <td>{{ vote.cost }}</td>
    	  </tr>
	   {% endfor %}
	</table>
    <br>
    <b><font color="orange">presenze</font></b>: {{player|get_played:player.code}}
    <br>
    <b><font color="orange">fv avg</font></b>: {{player|get_avg:player.code}}


{% endblock %}

templates/vote.html

{% extends 'fantalega/base.html' %}

{% block content %}
      <font color="green"><b>{{ league.name }}</b></font>: list of evaluations for day
      <font color="purple"><b>{{ day }}</b></font><br><br>
    <form action="#" method="get">
    <input type="submit" class="btn"
           value="back to {{ league.name }} teams" name="back_to_teams">
    </form>
    <br>
    <table class="table table-striped" width="100%">
      <tr>
          <th>code</th>
          <th>name</th>
          <th>team</th>
          <th>fanta value</th>
          <th>value</th>
          <th>cost</th>
      </tr>
      <tr>
          {% for vote in votes %}
              <td>{{ vote.player.code }}</td>
              <td><a href="{% url 'player_details' vote.player.id %}">
                  {{ vote.player.name }}</a></td>
              <td>{{ vote.player.real_team }}</td>
              <td>{{ vote.fanta_value }}</td>
              <td>{{ vote.net_value }}  </td>
              <td>{{ vote.cost }}</td></tr>
          {% endfor %}
    </table>
{% endblock %}

templates/upload_votes.html

{% extends 'fantalega/base.html' %}
{% load bootstrap3 %}

{% block content %}
    <h1>Upload votes for <font color="green">{{ league.name }}</font></h1><br>
    <form action="#" method="get">
    <input type="submit" class="btn"
           value="back to {{ league.name }} teams" name="back_to_teams">
    </form>
    <form enctype="multipart/form-data" method="POST" class="form">
		{% csrf_token %}
        {% bootstrap_form form %}
            {% buttons %}
                <button type="submit" class="btn btn-primary">
                {% bootstrap_icon "upload" %} Upload</button>
			{% endbuttons %}
    </form>
{% endblock %}

Avviare il server e recarsi nella pagina relativa ai giocatori:
http://127.0.0.1:8000/fantalega/players/
Come si nota non ci sono ancora giocatori, pertanto vanno caricati.
Abilitare il pulsante ‘upload votes’ nella view league_details:

fantalega/views.py

@login_required
def league_details(request, league_id):
    league = get_object_or_404(League, pk=int(league_id))
    league_teams = league.team_set.all()
    if request.GET.get('calendar'):
        return redirect('calendar', league.id)
    if request.GET.get('matches'):
        return redirect('matches', league.id)
    if request.GET.get('upload votes'):
        return redirect('upload_votes', league.id)
    context = {'league': league, 'teams': league_teams,
               'user': request.user}
    return render(request, 'fantalega/league.html', context)

I file dei voti sono i soliti MCCxx.txt e si dovranno trovare
nella directory fantalega/static

Nella template player.html si fa uso di alcuni custom filter, pertanto
aggiungerli al file fantalega/templatetags/app_filters.py

...
from fantalega.models import Player, Evaluation
...
@register.filter(name='get_played')
def get_played(player, code):
    obj_player = Player.objects.filter(name=player, code=code).first()
    played = [e for e in Evaluation.objects.filter(player=obj_player).all()
              if e.fanta_value > 0.0 ]
    return len(played)


@register.filter(name='get_avg')
def get_avg(player, code):
    obj_player = Player.objects.filter(name=player, code=code).first()
    fanta_values = [e.fanta_value for e
                    in Evaluation.objects.filter(player=obj_player).all()
                    if e.fanta_value > 0.0 ]
    try:
        return sum(fanta_values)/len(fanta_values)
    except ZeroDivisionError:
        return 0.0

Siccome la pagina dei giocatori deve essere facilmente consultabile da ogni dove,
sarebbe bene aggiungerla nella template base.html (come navbar) in modo che venga
ereditata da tutte.

fantalega/templates/fantalega/base.html

...
      <div class="navbar-header">
        <a class="navbar-brand" href="{% url 'seasons' %}">Seasons</a></div>
      <div class="navbar-header">
        <a class="navbar-brand" href="{% url 'leagues' %}">Leagues</a></div>
      <div class="navbar-header">
        <a class="navbar-brand" href="{% url 'players' %}">Players</a></div>
...

Il server è già avviato, recarsi all’interno della lega e
premere sul pulsante ‘upload votes’. Una volta terminato
l’upload dei dati, recarsi nella pagina http://127.0.0.1:8000/fantalega/players/
ed appariranno tutti i giocatori.
Su ognuno sarà possibile cliccare entrando nella pagina relativa al singolo
giocatore con tutti i dettagli.

Le Squadre e i giocatori ci sono, il prossimo step è quello di definire l’asta (Auction)

Salvare ora gli avanzamenti su github:

git add --all
git commit -m "Player and Evaluation added"
git push -u origin master

articoli precedenti
0 – indice
1 – Virtualenv e Git
2 – Models: Season
3 – Admin: Login e Logout
4 – Models: League
5 – Models: Team
6 – Models: Match

articoli successivi
8 – Asta
9 – Models: Lineup
10 – Models: Trade
11 – Asta di riparazione
12 – Classifica

Categorie:Django Tag:
I commenti sono chiusi.