Home > Django > djangofantalega: Asta

djangofantalega: Asta

14 Novembre 2016

8 – Asta

Una fantalega che si rispetti inizia sempre con una Asta:
ogni giocatore (Player) viene associato ad una squadra (Team).
Nella template della Lega, è stato previsto un bottone

<input type="submit" class="btn" value="Start Auction" name="auction">

questo bottone va collegato alle urls, alle views e alle templates necessarie
allo scopo.

Aggiungere per prima cosa le urls relative all’asta (auction) nel file fantalega\urls.py:

...
    # auction urls
    url(r'^leagues/(?P<league_id>[0-9]+)/auction$', views.auction,
        name='auction'),
    url(r'^leagues/(?P<league_id>[0-9]+)/auction/summary$',
        views.auction_summary, name='auction_summary'),
]

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

...
@login_required
def auction(request, league_id):
    league = get_object_or_404(League, pk=int(league_id))
    if request.GET.get('back_to_teams'):
        return redirect('league_details', league.id)
    if request.GET.get('auction_summary'):
        return redirect('auction_summary', league.id)
    free_players = [(p.code, p.name) for p in
                    Player.objects.filter(season=league.season).all()
                    if not p.team]
    league_teams = [(team.id, team.name) for team in league.team_set.all()]
    if request.method == "POST":
        form = AuctionPlayer(request.POST, initial={'players': free_players,
                                                    'teams': league_teams,
                                                    'league': league})
        if form.is_valid():
            player_code = form.cleaned_data['player']
            player = Player.get_by_code(int(player_code), season=league.season)
            auction_value = form.cleaned_data['auction_value']
            team_id = form.cleaned_data['team']
            team = Team.objects.get(pk=int(team_id))
            if team.player_set.count() == league.max_players():
                messages.warning(request,
                                 '%s has reached max player number'
                                 ' the operation is not valid' % team.name)
                return redirect('auction', league.id)
            remaining_players = league.max_players() - team.player_set.count()
            budget_remaining = int(team.budget) - int(auction_value)
            if budget_remaining >= 0 and budget_remaining >= \
                    (remaining_players - 1):
                player.team = team
                player.auction_value = auction_value
                player.save()
                team.budget -= auction_value
                team.save()
                messages.success(request,
                                 'Auction operation [ %s - %s ] stored!' %
                                 (player.name, team.name))
            else:
                messages.error(request,
                               "Not enough budget: budget: %s, "
                               "auction price %s, remaining players %s" %
                               (team.budget, auction_value, remaining_players))
            return redirect('auction', league.id)
    else:
        form = AuctionPlayer(initial={'players': free_players,
                                      'teams': league_teams, 'league': league})
    return render(request, 'fantalega/auction.html',
                  {'form': form, 'players': free_players,
                   'teams': league_teams, 'league': league}, )


@login_required
def auction_summary(request, league_id):
    league = get_object_or_404(League, pk=int(league_id))
    league_teams = league.team_set.all()
    if request.GET.get('back_to_auction'):
        return redirect('auction', league.id)
    context = {'league': league, 'teams': league_teams}
    return render(request, 'fantalega/auction_summary.html', context)

il form che permetterà l’associazione Player-Team (fantalega/forms.py) sarà:

...
class AuctionPlayer(forms.Form):
    def __init__(self, *args, **kwargs):
        self.dict_values = kwargs.pop('initial')
        super(AuctionPlayer, self).__init__(*args, **kwargs)

        self.fields['player'] = forms.ChoiceField(
            label=u'player', choices=self.dict_values['players'],
            widget=forms.Select(), required=False)
        self.fields['auction_value'] = forms.IntegerField()
        self.fields['team'] = forms.ChoiceField(
            label=u'team', choices=self.dict_values['teams'],
            widget=forms.Select(), required=False)

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

from .forms import AuctionPlayer
...

La logica è semplice:
si chiama a turno un giocatore, si fa un’asta poi nel form, si immetteranno
il nome della squadra che si è aggiudicata il giocatore e il valore d’asta,
che verrà scalato dal budget totale di squadra.
Viene anche effettuato un controllo sul valore d’asta, per assicurarsi che
ogni squadra abbia la disponibilità sufficiente per rilanciare.
E’ prevista anche una view/template per il riassunto dell’asta (auction_summary).
Ora le template legate alle views suddette.

templates/auction.html

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

{% block content %}
    <form action="#" method="get">
     <input type="submit" class="btn" value="back to {{ league.name }} teams"
            name="back_to_teams">
     <input type="submit" class="btn" value="view auction summary"
            name="auction_summary">
    </form>

    <b>New Auction Operation for lega
        <font color="green">{{ league.name }}</font></b><br><br>
    <form method="POST" class="form">
		{% csrf_token %}
        {% bootstrap_form form %}
            {% buttons %}
                <button type="submit" class="btn btn-primary">
                {% bootstrap_icon "save" %} Submit</button>
			{% endbuttons %}
    </form>
{% endblock %}

il pulsante che rimanderà al sommario è appunto:

<input type="submit" class="btn" value="view auction summary"
       name="auction_summary">

templates/auction_summary.html

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

{% block content %}
    <b><font color="green">{{ league.name }}</font>:
        <font color="purple">Auction summary</font></b><br>
    <br>
    <form action="#" method="get">
      <input type="submit" class="btn" value="back to auction"
             name="back_to_auction">
    </form>

  {% if league.team_set.all %}
      {% for team in league.team_set.all %}
        <font color="brown"><b>{{team.name}}</b></font>
        <font color="darkgrey"> {{team.user.username}}</font><br>
        {% for player in team.player_set.all %}
          {{player|color_by_role}}:
          <font color="darkgrey"> {{player.role}}</font><br>
        {% endfor %}
        <font color="red">budget remain: <b>{{team.budget}}</b><br></font>
        total goalkeepers to buy: {{team|gk_to_buy:league}}<br>
        total defenders to buy: {{team|def_to_buy:league}}<br>
        total players to buy: {{team|mid_to_buy:league}}<br>
        total players to buy: {{team|forw_to_buy:league}}<br>
        total players to buy: {{team|total_to_buy:league}}
      <br>
      <br>
      {% endfor %}

  {% else %}
    <p>No team found.</p>
  {% endif %}
{% endblock %}

Collegare il pulsante ‘auction’ alla view ‘league_details’,
file fantalega.views.py:

@login_required
def league_details(request, league_id):
    ...
    if request.GET.get('auction'):
        return redirect('auction', league.id)
    context = {'league': league, 'teams': league_teams,
               'days': days, 'user': request.user}
    ...

Nella template auction_summary.html vengono utilizzati alcuni custom_filters,
che vanno inseriti nel file templatetags/app_filters.py

from django.utils.safestring import mark_safe
...
@register.filter(name='color_by_role')
def color_by_role(player):
    if player.role.lower() == 'goalkeeper':
        color = 'blue'
    elif player.role.lower() == 'defender':
        color = 'orange'
    elif player.role.lower() == 'midfielder':
        color = 'green'
    else:
        color = 'purple'
    new_string = '<font color="%s">%s</font>' % (color, player.name.lower())
    return mark_safe(new_string)


@register.filter(name='gk_to_buy')
def gk_to_buy(team, league):
    gk_bought = team.player_set.filter(role='goalkeeper').count()
    remain = league.max_goalkeepers - gk_bought
    color = "green" if not remain else "red"
    return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain))


@register.filter(name='def_to_buy')
def def_to_buy(team, league):
    def_bought = team.player_set.filter(role='defender').count()
    remain = league.max_defenders - def_bought
    color = "green" if not remain else "red"
    return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain))


@register.filter(name='mid_to_buy')
def mid_to_buy(team, league):
    mid_bought = team.player_set.filter(role='midfielder').count()
    remain = league.max_midfielders - mid_bought
    color = "green" if not remain else "red"
    return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain))


@register.filter(name='forw_to_buy')
def forw_to_buy(team, league):
    forw_bought = team.player_set.filter(role='forward').count()
    remain = league.max_forwards - forw_bought
    color = "green" if not remain else "red"
    return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain))


@register.filter(name='total_to_buy')
def total_to_buy(team, league):
    player_bought = team.player_set.count()
    remain = league.max_players() - player_bought
    color = "green" if not remain else "red"
    return mark_safe('<b><font color="%s">%s</font></b>' % (color, remain))

Per comodità aggiungere il metodo max_players al model League,
metodo che torna utile durante l’asta per il controllo del tetto massimo
di giocatori.

fantalega/models.py

class League(models.Model):
    ...
    def max_players(self):
        return self.max_goalkeepers + self.max_defenders + \
            self.max_midfielders + self.max_forwards

Avviare il server e recarsi nella pagina relativa alla lega principale,
premere sul pulsante ‘Start Auction’ ed effettuare un acquisto, ricordandosi
il valore d’asta.

auction

Ad operazione ultimata recarsi alla pagina
‘view auction summary’ ed appariranno tutti i resoconti di ogni squadra.

E’ probabile che l’asta venga effettuata con l’ausilio di altri strumenti,
forniamo quindi il model League, di un metodo che permetta di importare
il risultato di un’asta direttamente da file csv:

fantalega/models.py

class League(models.Model):
   ... 
   @staticmethod
    def auction_upload(path):
        """This method import Auction directly from a csv file
           with this format:
           'name;auction_value;team_name
           'team_name' is the name of the object Team
        """
        with open(path) as data:
            for record in data:
                name, auction_value, team_name = record.strip().split(";")
                player = Player.objects.filter(name=name.upper()).first()
                if player:
                    player.auction_value = int(auction_value)
                    team = Team.objects.filter(name=team_name.strip()).first()
                    if team:
                        if player not in team.player_set.all():
                            player.team = team
                            team.budget -= player.auction_value
                            player.save()
                            team.save()
                    else:
                        print "[ERROR] Team %s not found" % team_name
                else:
                    print "[ERROR] Player %s not found" % name
            print "[INFO] Auction upload done!"

Ora è suffieciente aprire la shell

python manage.py shell
>>> League.auction_upload('fantalega/static/teams.csv')
>>> from fantalega.models import League
...

Salvare gli avanzamenti su github:

git add --all
git commit -m "Auction added"
git push -u origin master

La prossima sezione sarà piuttosto complicata, riguardando le formazioni (Lineup).

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
7 – Models: Player

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

Categorie:Django Tag:
I commenti sono chiusi.