Archivio

Archivio per la categoria ‘Fantacalcio’

FantaStat 2.0

22 Novembre 2017 Commenti chiusi

Introduzione

Fstat 2.0 è una semplice applicazione per visualizzare le statistiche
dei giocatori di fantacalcio.
La precedente versione serviva a visualizzare i file.txt veccio formato, quelli che indicavano anche i goal segnati, gli assist ecc.
I file più recenti indicano solo il fanta voto, voto senza bonus malus e la quotazione:

code|NAME|TEAM|fanta voto|voto|quotazione

Link utili

Il codice dell’applicazione è reperibile nel repo corrispondente di github.
I binari win con database players.db qui.
I file txt delle giornate di fantacampionato sono reperibili sul blog.

Moduli utilizzati
Python 2.7.8
grafica: wx 3.0.2.0
ORM: Django 1.10.1

L’utilizzo è molto semplice e di seguito viene descritto:

Creazione database

Qualora ci si dimenticasse di creare il database per primo, al lancio dell’applicazione si verrà avvisati da un messaggio.

Ogni baco segnalato sarà ben gradito.

Creare quindi il database con il seguente comando:

python manage.py makemigrations players

Le migrations iniziali dovrebbero già essere presenti nel repo di github, quindi potrebbero non risultare modifiche, procedere quindi con il secondo comando:

python manage.py migrate

Avviare Fstat2.0

Per avviare l’applicazione eseguire lo script main.py con il comando:

python main.py

Non avendo ancora importato nessun giocatore, verremo avvisati da un messaggio che è necessario importare il primo file:

Le prime giornata disputate sono già fornite con il repo in posizione players/days, oppure sono sempre reperibili qui.
Dal menu Import-> Import players importare il file desiderato, solitamente la giornata 1 (MCC1.txt)

Il file dei giocatori va importato solo la prima volta, dopodichè il menu non sarà più attivo.
Da qui in avanti si importeranno solo i file dei voti.
Il primo sarà nuovamente MCC1.txt. Dal menu Import -> Import Evaluations, procedere con l’importazione.

Finita un’importazione eseguire un refresh dall’apposito pulsante.
L’interfaccia si presenta come in figura:

I radio-buttons permettono di filtrare i giocatori per ruolo, mentre cliccando sulle colonne della lista,
l’ordinamento avverrà in base alla colonna cliccata. Ad esempio cliccando su real_team, i giocatori
saranno ordinati per ruolo e per squadra di appartenenza.

Cliccando invece sulla “riga” di un determinato giocatore, sarà possibile modificarlo.

Dal menu Player -> Summary si accede alla lista riassuntiva del giocatori, poichè la visualizzazione della schermata del frame principale, mostra le medie dei giocatori e non i valori iniziali.
Da qui cliccando sui giocatori si possono modificare come in precedenza.

Qualora nei file non risultasse un giocatore, sarà possibile crearlo dal menu Players -> New Player

Stesso discorso vale per le valutazioni; esiste un sommario accessibile dal menu Evaluations -> Summary

dove cliccando sulla riga di una valutazione, sarà possibile modificarla

Come per i giocatori, sarà possibile aggiungere una valutazione ad hoc, dal menu
Evaluations -> New Evaluation

Come ultima operazione, è possibile cancellare tutti i dati, dal menu Reset.

Fantamanager 2.2

20 Settembre 2017 Commenti chiusi

Diversamente dalla versione precedente FantaLega 2.1 e dalla versione Light, qui è possibile gestire un campionato di n.squadre, a scontri diretti (con più di 8/10 squadre).
Il giocatore non potrà più appartenere a più squadre ma è prevista un’asta.

Opzioni:
Asta
Costruzione Squadre
Generazione calendario
Modifica Squadre
Consegna formazioni
Modifica Formazioni
Importazione Giocatori e Voti Gazzetta
Modifica Giocatori
Mercato tra squadre
Calcolo punteggio formazioni
Classifica Squadre

Grafica: WX
Django per l’ORM

Utilizzare virtualenv ed installare i requirements:

pip install -r requirements.txt

Creare il database:

python manage.py makemigrations fantalega
python manage.py migrate

lanciare l’applicazione con il comando:

python main.py

Il link di github:
repo su github

Fantalega: gestore Asta

1 Settembre 2017 Commenti chiusi

Ieri è terminata ufficialmente la sessione di calciomercato.
Molti fanta-allenatori hanno rischiosamente già effettuato le loro aste,
molti altri (noi), NO.

Ecco un tool parecchio spartano che velocizzerà le annotazioni
squadra-giocatore durante l’asta.

Auction1.0 è un gestore di aste di fantacalcio scritto in python.
Per la grafica sono state utilizzate le librerie wx, mentre per la
gestione del database e l’ORM, è stato utilizzato django.

Requisiti: installare django e wxpython

Su github è presente una prima bozza di repo.

Per prima cosa creare i database con i due comandi:

python manage.py makemigrations auction
python manage.py migrate

dopo queste operazioni che creeranno il database con le relative tabelle, è
possibile avviare la app:

python main.py

per prima cosa importiamo i giocatori utilizzando i soliti file txt in
formato Gazzetta, reperibili qui:

E’ possibile importare i giocatori più e più volte in modo da aggiornare
la lista dei calciatori.
Importati i giocatori, è necessario creare le squadre.

Dopo aver creato le squadre, si comincia l’asta.

Si seleziona il giocatore per squadra reale di appartenenza,
si utilizzano i filtri di ruolo e si assegna un costo d’asta e la squadra
che si aggiudica il giocatore. Durante l’asta è possibile avere un resoconto
delle squadre, con budget rimanente e giocatori ancora da comprare

Dai menu Teams e Players è possibile editare le squadre e i giocatori
per apportare delle modifiche. E’ anche possibile aggiungere giocatori che,
in fase d’asta, non sono ancora stati ufficializzati da Gazzetta e quindi
non presenti nei file.

Terminata l’asta è possibile effettuare scambi tra fanta-allenatori con il menu
“Trades”.

Nota: la casella max trades del pannello edit-team indica le massime operazioni
di mercato concesse durante la fantalega, mutuate dall’applicazione completa.
Queste operazioni (Trades) in fase di asta, sono invece illimitate.

Come ultima operazione, è possibile esportare un file in formato csv (menu Auction).

Fantagazzetta parser

3 Dicembre 2014 Commenti chiusi

un piccolo script per scrivere su un foglio di calcolo, i voti di Fantagazzetta.com.
Il file scaricato dal sito è un html mascherato, pertanto il parsing viene fatto con BeautyfulSoap e riscritto con xlwt:

import xlwt
import wx
import os
from BeautifulSoup import BeautifulSoup as bsoup


class FileBrowser(wx.FileDialog):
    '''Class for file browser'''
    def __init__(self):
        super(FileBrowser, self).__init__(None, "XLS file", os.getcwd(),
                "", "Excel file (*.xls)|*.xls|" "All files (*.*)|*.*",
                wx.OPEN)
        if self.ShowModal() == wx.ID_OK:  
            self.file = self.GetPath()
        else:
            self.file = ''
        self.Destroy()
        

def parse_html(path):
    voti = []

    with open(path) as html_file:
        data = html_file.read()

    soup = bsoup(''.join(data))
    for row in soup.findAll('tr'):
        cells = row.findAll('td')
        try:
            name= cells[2].text
            v = float('%.1f' % float(cells[3].text.replace(',', '.')))
            gf = int(cells[4].text)
            gs = int(cells[5].text)
            rp = int(cells[6].text)
            rno = int(cells[7].text)
            rok = int(cells[8].text)
            ag = int(cells[9].text)
            am = int(cells[10].text)
            es = int(cells[11].text)
            ass = int(cells[12].text)
            asf = int(cells[13].text)

        except (IndexError, ValueError):
            pass

        else:
            if v == '6*':
                fv = '0.0'
            else:
                fv = v + 3*gf - gs + 3*rp - 3*rno + 3*rok - 3*ag \
                     - 0.5*am - es + ass + asf
            voti.append((name, fv))
    return voti


def write_to_excel(iterable):
    wb = xlwt.Workbook()
    ws = wb.add_sheet('Voti')
    for n in range(len(iterable)):
        ws.write(n, 1, iterable[n][0])
        ws.write(n, 3, iterable[n][1])
    wb.save('VotiUltimaGiornataHtml.xls')


def main():
    '''app starter'''
    app = wx.PySimpleApp()
    path = FileBrowser().file
    if path:
        voti = parse_html(path)
        print 'processing...'
        write_to_excel(voti)
        print '...Done!'
    else:
        print 'Aborted!'
    app.MainLoop()

if __name__ == '__main__':
    main()
Categorie:Fantacalcio, python Tag:

python: calendario fantalega

1 Settembre 2014 Commenti chiusi

Ecco una applicazione per creare un calendario con n squadre Pari.
Non tiene conto della giornata di riposo in caso di squadre dispari!
Testandola, dovrebbe tenere conto anche del “casa”, “fuoricasa”.

codice view:

# -*- coding: utf-8 -*-#
'''VIEW Module for FantaCalendario:
Graphic library: wx
Bindings are defined under CONTROLLER Module.
''' 
import wx, os

class FrameCal(wx.Frame):
    '''Main frame for FantaPython application'''
    def __init__(self, *args, **kwargs):
        self.nsq = kwargs.pop('nsq')
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self)
        self.panel.SetBackgroundColour('Pink')
        sq_cont = 1
        xpos, ypos = 100, 20
        while sq_cont <= self.nsq:
            wx.StaticText(self.panel, wx.ID_NEW, 'squadra%s' % sq_cont,
                          pos = ((xpos - 85), ypos), size = (70, 25))
            wx.TextCtrl(self.panel, wx.ID_NEW,
                        pos = (xpos, ypos), size = (140, 25))
            ypos += 25
            sq_cont += 1
        
        self.btn_create = wx.Button(self.panel, wx.ID_NEW, 'Crea Calendario',
                                    size = (240, 25), pos = (0, (ypos)))
        self.SetSizeWH(250, (ypos + 65))
        self.Centre()
        self.Show()

class InfoMessage(wx.MessageDialog):
    '''Simple message Dialog'''
    def __init__(self, parent, message):
        wx.MessageDialog.__init__(self, parent, message, 'core info', wx.OK |
                                  wx.ICON_EXCLAMATION)
    def get_choice(self):
        '''get the state of the user choice'''
        if self.ShowModal() == wx.ID_OK:
            self.Destroy()

class ChoiceMessage(wx.MessageDialog):
    '''Simple choice message Dialog'''
    def __init__(self, parent, message):
        wx.MessageDialog.__init__(self, parent, message, 'Core question',
                                  wx.YES_NO | wx.ICON_QUESTION)
    def get_yes(self):
        '''get True if YES is clicked'''
        if self.ShowModal() == wx.ID_YES:
            return True
        else:
            self.Destroy()

class EntryDialog(wx.TextEntryDialog):            
    '''Simple Text Entry Dialog'''
    def __init__(self, parent, msg, value):
        wx.TextEntryDialog.__init__(self, parent, msg, 'Core request',
                                    defaultValue = value, style = wx.OK)
    def get_choice(self):
        '''get the state of the user choice'''
        if self.ShowModal() == wx.ID_OK:
            response = self.GetValue()
            self.Destroy()
            return response

class FileSaver(wx.FileDialog):
    '''Class for file Saver'''
    def __init__(self):
        self.f_save = None
        wildcard = "File Calendario (*.txt)|*.txt|" \
            "Tutti i files (*.*)|*.*"
        wx.FileDialog.__init__(self, None, "salva il file", os.getcwd(),
                               "", wildcard, wx.SAVE)
        if self.ShowModal() == wx.ID_OK:  
            print "...salvo il file in: %s" % (self.GetPath())
            self.output = self.GetPath()
            self.f_save = open(self.output, 'w')
        else:
            print "operazione salvataggio annullata"
            self.f_save = None
            self.Destroy()
        self.Destroy()

def main():
    '''app starter'''
    app = wx.PySimpleApp()
    n_sq = 8 # number of teams
    FrameCal(None, wx.ID_NEW, "Calendario 1.1", nsq = n_sq)
    app.MainLoop()

if __name__ == '__main__':
    main()

codice controller:

'''FantaCalendario v 2.0
   Controller Module for FantaCalendario Application:
   All the binding method to frame widget are defined here'''

import wx

from View import (FrameCal, EntryDialog, InfoMessage, FileSaver)
from copy import copy


class Team(object):
    '''A team class'''
    def __init__(self, name, status=True):
        self.name = name
        self.casa = status
        
    def set_casa(self):
        """set the status 'casa' to True"""
        self.casa = True

    def set_fuori(self):
        """set the status 'fuori' to True"""
        self.casa = False


class Controller(object):
    '''Controller class for MVC-like pattern'''
    def __init__(self):
        n_sq = int(EntryDialog(None, "Quante Squadre partecipano?",
                                     '8').get_choice())
        self.gui = FrameCal(None, wx.ID_NEW, "Calendario 2.0", nsq=n_sq)
        self.gui.Bind(wx.EVT_BUTTON, self.on_create)

    def on_create(self, evt):
        '''create every "team_x vs team_y" combinations possible'''
        panel = evt.GetEventObject().GetParent()
        teams = []
        err = 0
        for widget in panel.GetChildren():
            if isinstance(widget, wx.TextCtrl):
                if widget.GetValue() != '':
                    teams.append(Team(widget.GetValue()))
                else:
                    err += 1
        if err > 0:
            msg = "mancano uno o piu' nomi squadra"
            InfoMessage(panel.GetParent(), msg).get_choice()
        else:
            print teams
            crea_calendario(teams)
            self.gui.Close()


def crea_calendario(lista):
    '''crea gli accoppiamenti da una lista'''
    matrix = []
    cont = 0
    while cont < len(lista):
        matrix.append([None] * len(lista))
        cont += 1
    matrix[0] = lista  # intestazione

    # Riga Intestazione invertita meno l'ultima squadra
    row2 = copy(lista)
    row2.pop()
    row2.reverse()
    matrix[1][0:(len(lista) - 1)] = row2[0:(len(lista) - 1)]

    # Composizione tabella prima FASE
    i = 1
    while i < len(lista):
        k = 1
        for item in matrix[i]:
            try:
                matrix[i + 1][k] = item
                matrix[i + 1][0] = matrix[i + 1][(len(lista) - 1)]
                matrix[i + 1][(len(lista) - 1)] = None
                k += 1
            except IndexError:
                break
        i += 1

    # Composizione tabella seconda FASE
    row_m = 1
    while row_m < len(lista):
        for item_a in matrix[0]:
            for item_b in matrix[row_m]:
                if matrix[0].index(item_a) == matrix[row_m].index(item_b):
                    if item_a == item_b:
                        matrix[row_m][matrix[row_m].index(item_b)] = lista[-1]
                        matrix[row_m][(len(lista) - 1)] = item_b
        row_m += 1

    # Stampa giornata
    cont = 1
    fbs = FileSaver()
    outputfile = fbs.f_save
    while cont < len(lista):
        outputfile.write("\nGiornata n. " + str(cont) + "\n")
        andata = []
        for sq_a in matrix[0]:
            for sq_b in matrix[cont]:
                if matrix[0].index(sq_a) == matrix[cont].index(sq_b):
                    if sq_b not in andata or sq_a not in andata:
                        if sq_a.casa == True:
                            andata.append(sq_a)
                            andata.append(sq_b)
                            outputfile.write(sq_a.name + "-" + sq_b.name + "\n")
                            sq_a.set_fuori()
                            sq_b.set_casa()
                        else:
                            andata.append(sq_b)
                            andata.append(sq_a)
                            outputfile.write(sq_b.name + "-" + sq_a.name + "\n")
                            sq_a.set_casa()
                            sq_b.set_fuori()
        cont += 1
    outputfile.close()


def main():
    '''starter'''
    app = wx.PySimpleApp()
    Controller()
    app.MainLoop()

if __name__ == '__main__':
    main()

il funzionamento è semplicissimo:
si avvia, si sceglie il numero di squadre:
cal_1
si mettono i nomi delle squadre:
cal_2
si preme il pulsante crea e si sceglie un nome per la destinazione in txt, che poi potrà essere importata ad esempio in excel.

Giornata n. 1
A-C
B-D

Giornata n. 2
D-A
C-B

Giornata n. 3
A-B
D-C

link binario (exe)

Categorie:FantaLega, python, wxPython Tag:

Statistiche Fantacalcio: Flask-SQLAlchemy – view (parte 2)

20 Settembre 2013 Commenti chiusi

ecco una UI veloce veloce e molto migliorabile:

import model

class TextualViewer(object):
    def show_main_menu(self):
        menu = """\n
                 * Scegli un'opzione
                 ----------------------------------
                 1 - Miglior giocatore del torneo
                 2 - Miglior portiere del torneo
                 3 - Miglior difensore del torneo
                 4 - Miglior ccampista del torneo
                 5 - Miglior attaccante del torneo
                 6 - Miglior giocatore per squadra
                 7 - Media giocatori per squadra
                 8 - Trova giocatore che comincia per...
                 9 - Tutti i voti di un giocatore
                 0 - EXIT
                """
        print menu

    def show_player_result(self, player):
        obj, avg = player
        print "\n\n"
        print " ---<<<   Miglior Giocatore del torneo   >>>---\n"
        print "     {} ({})".format(obj.name, obj.team)
        print "     media voto: {}".format(avg)
        
    def show_avg_result(self, iterable):
        print "\n\n"
        print " ---<<<   Media voto Giocatori decrescente  >>>---\n"
        for player in iterable:
            print '{}  ({}) : media voto: {}'.format(player[0], player[1],
                                                     player[2])
    def show_player_grades(self, iterable, player):
        print "\n\n"
        print " ---<<<     Voti giornate %s      >>>---\n" % player.name
        for record in iterable:
            print 'giornata {}    voto: {}'.format(record[0], record[1])

    def show_player_name_starts_with(self, iterable, prefix):
        print "\n\n"
        print " ---<<<     Nomi Giocatori con '%s'     >>>---\n" % prefix
        for player in iterable:
            print '{} ({})'.format(player.name, player.team)

Poi creeremo un controller, che per ora, per una questione di debug utilizzo nello stesso
file view.py, a runtime.
La classe controller la creerò quando avrò 5 minuti.

if __name__ == '__main__':
    m = model.Model()
    cases = {1: m.get_best_player,
             2: m.get_best_role_player,
             3: m.get_best_role_player,
             4: m.get_best_role_player,
             5: m.get_best_role_player,
             6: m.get_best_team_player,
             7: m.get_avg_players,
             8: m.get_players_name_starts_with,
             9: m.get_player_fgrades,
            }
    tv = TextualViewer()
    while True:
        tv.show_main_menu()
        choice = raw_input('--> ')
        if choice == '0'.strip(): break
        elif choice == '1':
            player = cases.get(int(choice))()
            tv.show_player_result(player)
        elif choice == '2':
            player = cases.get(int(choice))('portiere')
            tv.show_player_result(player)
        elif choice == '3':
            player = cases.get(int(choice))('difensore')
            tv.show_player_result(player)
        elif choice == '4':
            player = cases.get(int(choice))('centrocampista')
            tv.show_player_result(player)
        elif choice == '5':
            player = cases.get(int(choice))('attaccante')
            tv.show_player_result(player)
        elif choice == '6':
            team = raw_input('quale squadra? ')
            player = cases.get(int(choice))(team)
            tv.show_player_result(player)
        elif choice == '7':
            team = raw_input('quale squadra? ')
            players = m.get_players_by_team(team)
            result = cases.get(int(choice))(players)
            tv.show_avg_result(result)
        elif choice == '8':
            prefix = raw_input('comincia per? ')
            result = cases.get(int(choice))(prefix)
            tv.show_player_name_starts_with(result, prefix)
        elif choice == '9':
            pl = raw_input('quale giocatore? ')
            player = m.get_player_by_name(pl)
            result = cases.get(int(choice))(player)
            tv.show_player_grades(result, player)

link utili:
model

Categorie:Fantacalcio, FantaLega, python Tag:

Statistiche Fantacalcio: la mia prima applicazione con Flask-SQLAlchemy

19 Settembre 2013 Commenti chiusi

Django ed il suo orm, già li ho usati. Stupendo, ma volendo un’applicazione
che lavori sia in locale che su web, il magnifico orm di django (e tutto il suo mondo),
mi diventa pesantuccio da portare appresso.

SQlAlchemy è comodissimo, ma non è compatibile con django (o meglio se devi usare
django e rinunciare al suo orm, lasciamo perdere).
Web2py come WF funziona nativamente con sqla, ma non mi ci trovo granchè.

Invece Flask, essendo un light-web-framework (che ancora non so utilizzare…),
dovrebbe fare a caso mio per il secondo caso… e soprattutto si
appoggia ad SQLAlchemy. Se quindi impostassi la mia app in locale
utilizzando l’estensione di Flask, appunto Flask-SQLAlchemy, nel momento
in cui dovrò trasferire il tutto su web, dovrei essere a posto e soprattutto leggero.

Cominciamo quindi con l’impostare l’applicazione dalle basi.

Come spesso accade sul mio blog, imposto i miei esercizi ed i miei esperimenti,
basandomi sul fantacalcio. Comodissimo per creare un database con più di una tabella
e soprattutto con relazioni che siano almeno di 1 a molti.

Questa volta vorrei un’applicazione che prendesse tutti i calciatori della serie A,
che immagazzinasse tutti i voti che questi prendono ogni giornata e mi restituisse la
media voto.
Mi servirò del magico sqlite per il database e di un pattern MVC per l’app vera e propria.
Inizialmente avremo una UI testuale, poi in futuro faremo anche una GUI.

Iniziamo quindi dal Model, creando un file model.py:

Ci saranno 2 classi (tabelle):

Player (calciatore)
Grade (voto)

collegate tra loro con una relazione 1-a-molti, poichè ovviamente un giocatore
alla fine del campionato riceverà un voto per ogni giornata giocata.

tables
fig.1

come da guida di Flask-SQLAlchemy, creiamo le classi:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///stat.db'
db = SQLAlchemy(app)

class Player(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.Integer(3), unique=True)
    name = db.Column(db.String(80), unique=True)
    team = db.Column(db.String(3))

    def __init__(self, code, name, team):
        self.code = code
        self.name = name
        self.team = team

    def __repr__(self):
        return '<Player %r>' % self.name


class Grade(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    day = db.Column(db.Integer(3))
    f_grade = db.Column(db.Float(4))  # grade with bonus-malus
    grade = db.Column(db.Float(4))  # naked grade (without bonus-malus)
    value = db.Column(db.Integer(3))
    # Relationship
    player_id = db.Column(db.Integer, db.ForeignKey('player.id'))
    player = db.relationship('Player',
        backref=db.backref('grades', lazy='dynamic'))

    def __init__(self, day, f_grade, grade, value, player):
        self.day = day
        self.f_grade = f_grade
        self.grade = grade
        self.value = value
        self.player = player

    def __repr__(self):
        return '<Grade day %r: %r>' % (self.day, self.player)

per creare il database:

db.create_all()

Class Model:

Craieamo ora la classe Model, che conterrà tutte le operazioni che faremo sul database.
I print che appaiono nel codice hanno funzione di debug.
Le vere operazioni di visualizzazione, verranno inserite nel modulo view.py, come
da pattern. Il controller farà il resto.
Comunque, qui di seguito la classe Model con i suoi metodi, tutti per la verità
abbastanza autoesplicativi:

class Model(object):
    '''Model class for MVC pattern.
       It creates, destroies, fills and queries the database "stat.db".
    '''

    def __init__(self):
        self.day = None

    def _database_exist(self):
        '''Return True if database already exists'''
        return 'stat.db' in os.listdir(os.getcwd())

    def _delete_database(self):
        '''Delete database and all data'''
        ans = raw_input("I'm going to remove database, are you sure? (Y/N): ")
        if ans.upper() == 'Y':
            db.session.close()
            os.remove('stat.db')
        else:
            print 'operation canceled!'

    def _import_all_txt(self):
        pass

    def get_import_day(self, path):
        '''Get the day number from path file'''
        pattern = 'MCC(\d{2}).txt'
        try:
            day = re.compile(pattern).search(path).group(1)
        except AttributeError:
            print 'File name must follow "MCCnn_s.txt" pattern! Rename it.'
        return day

    def _import_file(self, path):
        '''Import grade records from txt file and store them in db'''
        self.day = self.get_import_day(path)
        with open(path, 'r') as input_file:
            records = input_file.readlines()

        self._import_players(records)
        self._import_grades(records)

    def _import_players(self, records):
        for record in records:
            # input pattern:  102|AGAZZI|CAG|5.0|6.0|11
            code, name, team, f_grade, grade, value = record.split('|')
            if not self._player_exists(code):
                pl = Player(code=code, name=name, team=team)
                db.session.add(pl)
                print '+++ import player {}'.format(pl)
            else:
                print '*** player {} existing...'.format(name)
        db.session.commit()

    def _import_grades(self, records):
        for record in records:
            code, name, team, f_grade, grade, value = record.split('|')
            if not self._grade_exists(int(self.day)):
                pl = self.get_player_by_code(code)
                gr = Grade(day=self.day, f_grade=f_grade, grade=grade,
                           value=value, player=pl)
                db.session.add(gr)
                print '<<< import new grade {}'.format(gr)
            else:
                print 'this day is not new'
        db.session.commit()

    def _player_exists(self, code):
        return Player.query.filter_by(code=code).first()

    def _grade_exists(self, day):
        return Grade.query.filter_by(day=day).first()

    def _get_imported_days(self):
        return db.session.query(Grade.day).distinct().all()

    def get_player_by_code(self, code):
        return Player.query.filter_by(code=code).first()

    def get_player_by_name(self, name):
        return Player.query.filter_by(name=name.upper()).first()

    def get_player_fgrades(self, player):
        return [(pl.day, pl.f_grade) for pl in player.grades.all()]

    def get_avg_player(self, player):
        ndays = len(player.grades.filter(Grade.grade > 0.0).all())
        sum_fg = sum([f_g for day, f_g in self.get_player_fgrades(player)])
        try:
            return sum_fg / ndays
        except ZeroDivisionError:
            return 0.0

    def get_players_by_team(self, team):
        return Player.query.filter_by(team=team.upper()[:3]).all()

    def get_avg_players(self, iterable):
        return [(pl.name, pl.team, self.get_avg_player(pl)) \
             for pl in iterable]
        

    def _get_best_avg(self, iterable):
        # from operator import itemgetter  # faster
        # max(list, key=itemgetter(1))
        result= [(pl.code, self.get_avg_player(pl)) \
             for pl in iterable]
        return max(result, key=lambda item: item[1])

    def get_best_player(self):
        code, grade = self._get_best_avg(Player.query.all())
        return self.get_player_by_code(code), grade

    def get_best_team_player(self, team):
        players = Player.query.filter_by(team=team.upper()[:3])
        try:
            code, grade = self._get_best_avg(players)
            return self.get_player_by_code(code), grade
        except ValueError:
            print "Error: team '%s' does not exist!" % team
            print "try one of {}".format([t[0] for t in
                    db.session.query(Player.team).distinct().all()])

    def get_best_role_player(self, role):
        lmin, lmax = 0, 0
        if role.lower() == 'portiere':
            lmin, lmax = 100, 200
        elif role.lower() == 'difensore':
            lmin, lmax = 200, 500
        elif role.lower() == 'centrocampista':
            lmin, lmax = 500, 800
        elif role.lower() == 'attaccante':
            lmin, lmax = 800, 1000
        else:
            print "role must be: 'portiere' or 'difensore'"
            print "or 'centrocampista' or 'attaccante'"
            return None

        players = Player.query.filter(Player.code > lmin).filter(
                  Player.code < lmax).all()        
        code, grade = self._get_best_avg(players)
        return self.get_player_by_code(code), grade

    def get_players_name_starts_with(self, string):
        return Player.query.filter(
            Player.name.startswith(string.upper())).all()

in fondo al file model.py, per comodità di debug, inseriamo il seguente codice:

if __name__ == '__main__':
    m = Model()
    if m._database_exist():
        print "database exists"
    else:
        print "create new database..."
        db.create_all()

eseguito il software la prima volta, verrà creato il database, la seconda volta,
tramite il metodo _database_exist() faremo un controllo di presenza del file ‘stat.db’.
Se presente, non verrà calpestato da una nuova creazione.

con il metodo _delete_database(self), è possibile cancellare tutto.

quindi, sempre da console:

>>> m._delete_database()

Questo non si dovrebbe fare (l’underscore nel prefisso del metodo parla chiaro), ma siamo
in debug…
Quando faremo la UI nel modulo view, rimarrà tutto più nascosto.

Per proseguire, dobbiamo importare i file dei voti (metodo _import_file(path)).
Ne basta uno, ma sarebbe meglio importare più file.

ad es.

>>> m._import_file('MCC01.txt')

una volta che il database è riempito, il modo di utilizzo viene da sè.
Esempio, voglio sapere il giocatore con la media più alta del campionato?

>>> m.get_best_player()
(<Player u'VIDAL'>, 12.5)
>>> 

Voglio sapere il miglior giocatore di un determinato ruolo?

>>> m.get_best_role_player('a')
role must be: 'portiere' or 'difensore'
or 'centrocampista' or 'attaccante'
>>> m.get_best_role_player('attaccante')
(<Player u'ROSSI G.'>, 11.166666666666666)
>>> 

oppure il miglior giocatore di una squadra?

>>> m.get_best_team_player('inter')
(<Player u'ICARDI'>, 10.0)
>>> 

se volessi la rosa dell’Inter?

>>> m.get_players_by_team('inter')
[<Player u'CARRIZO'>, <Player u'CASTELLAZZI'>, <Player u'HANDANOVIC'>,
 <Player u'ANDREOLLI'>, <Player u'CAMPAGNARO'>, <Player u'CHIVU'>,
 <Player u'JONATHAN'>, <Player u'JUAN JESUS'>, <Player u'NAGATOMO'>,
 <Player u'PEREIRA A.'>, <Player u'RANOCCHIA'>, <Player u'SAMUEL'>,
 <Player u'ROLANDO'>, <Player u'WALLACE'>, <Player u'ALVAREZ R.'>,
 <Player u'CAMBIASSO'>, <Player u'GUARIN'>, <Player u'KOVACIC'>,
 <Player u'KUZMANOVIC'>, <Player u'MUDINGAYI'>, <Player u'OBI'>,
 <Player u'TAIDER'>, <Player u'ZANETTI'>, <Player u'OLSEN'>,
 <Player u'MARIGA'>, <Player u'BELFODIL'>, <Player u'ICARDI'>,
 <Player u'MILITO'>, <Player u'PALACIO'>]
>>> 

se poi vuoi anche vedere la media di costoro,mandi la lista al prossimo metodo:

>>> inter = m.get_players_by_team('inter')
>>> m.get_avg_players(inter)
[(u'CARRIZO', u'INT', 0.0), (u'CASTELLAZZI', u'INT', 0.0),
 (u'HANDANOVIC', u'INT', 6.333333333333333),
 (u'ANDREOLLI', u'INT', 0.0), (u'CAMPAGNARO', u'INT', 7.0),
 (u'CHIVU', u'INT', 0.0), (u'JONATHAN', u'INT', 7.166666666666667),
 (u'JUAN JESUS', u'INT', 6.5), (u'NAGATOMO', u'INT', 8.5),
 (u'PEREIRA A.', u'INT', 0.0), (u'RANOCCHIA', u'INT', 6.333333333333333),
 (u'SAMUEL', u'INT', 0.0), (u'ROLANDO', u'INT', 0.0), (u'WALLACE', u'INT', 0.0),
 (u'ALVAREZ R.', u'INT', 9.333333333333334), (u'CAMBIASSO', u'INT', 6.0),
 (u'GUARIN', u'INT', 5.833333333333333), (u'KOVACIC', u'INT', 5.0),
 (u'KUZMANOVIC', u'INT', 0.0), (u'MUDINGAYI', u'INT', 0.0), (u'OBI', u'INT', 0.0),
 (u'TAIDER', u'INT', 6.166666666666667), (u'ZANETTI', u'INT', 0.0),
 (u'OLSEN', u'INT', 0.0), (u'MARIGA', u'INT', 0.0), (u'BELFODIL', u'INT', 6.0),
 (u'ICARDI', u'INT', 10.0), (u'MILITO', u'INT', 0.0),
 (u'PALACIO', u'INT', 9.833333333333334)]
>>> 

se volessi analizzare un giocatore?

>>> palacio = m.get_player_by_name('palacio')
>>> palacio
<Player u'PALACIO'>
>>> palacio.grades
<sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x01A22C10>
>>> palacio.grades.all()
[<Grade day 1: <Player u'PALACIO'>>, <Grade day 2: <Player u'PALACIO'>>, <Grade day 3: <Player u'PALACIO'>>]
>>> for v in palacio.grades.all():
	print v.day, v.f_grade

	
1 11.5
2 11.5
3 6.5
>>> m.get_avg_player(palacio)
9.833333333333334
>>> 

e se non conoscessi bene il nome?
come si scrive il nome dello slavo del genoa?

>>> m.get_players_name_starts_with('vr')
[<Player u'VRSALJKO'>]

bo’, non resta che creare una UI per rendere più facili, lettura ed utilizzo di
questa app.
Alla prossima.

link utili:
flask-sqlalchemy
model.py
view.py

Categorie:Fantacalcio, FantaLega, python Tag:

Python: FantaStat 1.1 Light

15 Dicembre 2011 Commenti chiusi

Nuova versione FantaStat2.0

Si sta avvicinando il mercato di riparazione, quindi ho creato un piccolo tool con Python, per tutti i FantaAllenatori, che non vogliono arrivare impreparati all’asta invernale.

download:
eseguibile win
sorgenti python

Funziona come i fratelli maggiori FantaManager Full e
FantaManager Light, cioè si importano i file MCCxx.txt,
e si consultano le varie medie voto, gol total/bancaldo.wordpress.com/2011/08/05/lista-calciatori-gazzetta-2011-2012/”>file MCCxx.txt,
e si consultano le varie medie voto, gol totali, presenze,
di ogni singolo giocatore.

L’interfaccia è semplicissima (utilizzate le solite wx):


un bottone per importare il file MCC, un bottone per uscire,
una list-control per visualizzare i dati del database ed
un radiobox per visualizzare i calciatori, per ruolo.
Le colonne della list-control, ordinano in modo di, presenze,
di ogni singolo giocatore.

L’interfaccia è semplicissima (utilizzate le solite wx):


un bottone per importare il file MCC, un bottone per uscire,
una list-control per visualizzare i dati del database ed
un radiobox per visualizzare i calciatori, per ruolo.
Le colonne della list-control, ordinano in modo decrescente i
dati, eccetto nome e squadra (che francamente non incidono
sulla qualità della ricerca)

Il database è generato e gestito con Sqlite e SqlAlchemy.
Gli oggetti riferiti al db ed sqla (tabelle, campi ecc) sono definiti
in non-declarative perchè mi piace semplificare il porting su web con
Pylons (ho trovato difficoltà quando ho definito gli oggetti in
declarative)

Come sempre, ho cercato di utilizzare il pattern MVC,
spero riducendo al minimo gli errori di stile.

NOTA:
Ricordarsi di posizionare le dirs /images e /giornate
dove si troverà l’eseguibile o i sorgenti.

Qui i codici (sicuramente migliorabili):

Model:

# -*- coding: utf-8 -*-#
# DataLight.py
'''MODEL Module for FantaStat Application:
Database engine: provided by Sqlite
Database O.R.M: provided by SqlAlchemy (no_declarative)
All data for fantapython manage are contained here.
All the O.R.M. operation on data are defined here as
Model-Class methods.
''' 

from sqlalchemy import create_engine, Column, Integer, Float
from sqlalchemy import ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import aliased
from sqlalchemy.orm import sessionmaker
from sqlalchemy import orm, func, desc, asc
from sqlalchemy import MetaData
from sqlalchemy.types import Unicode

class Giocatore(object):
    '''class for sqla mapper'''
    pass

class Voto(object):
    '''class for sqla mapper'''
    pass

class Model(object): 
    '''Model class with common application data'''
    
    base = declarative_base()
    engine = create_engine('sqlite:///fantastat.db', echo = False)
    metadata = MetaData()
    
    giocatore_table = Table('giocatori', metadata,
        Column('id', Integer(), primary_key=True),
        Column('idgaz', Integer()),
        Column('nome', Unicode(255)),
        Column('squadra', Unicode(255)),
        Column('valore', Integer()),
        Column('ruolo', Unicode(255)),
    )
    
    voto_table = Table('voti', metadata,
        Column('id', Integer(), primary_key=True),
        Column('voto', Float()),
        Column('gg_', Integer()),
        Column('val', Integer()),
        Column('gol_fatti', Integer()),
        Column('pres', Integer()),
        Column('gol_sub', Integer()),
        Column('assist', Integer()),
        Column('ammo', Integer()),
        Column('esp', Integer()),
        Column('rig_tir', Integer()),
        Column('rig_ok', Integer()),
        Column('rig_no', Integer()),
        Column('voto_s', Float()),
    )
    
    # M2M relation
    giocatori_voti_table = Table('giocatori_voti', metadata,
        Column('giocatori_id', Integer, ForeignKey('giocatori.id')),
        Column('voti_id', Integer, ForeignKey('voti.id')),
    )

    # Map SQLAlchemy table definitions to python classes
    orm.mapper(Giocatore, giocatore_table, properties={
        'voti':orm.relation(Voto, secondary = giocatori_voti_table),})
    
    orm.mapper(Voto, voto_table, properties={
        'giocatori':orm.relation(Giocatore, secondary = giocatori_voti_table),})
    
    def __init__(self):
        
        Model.metadata.create_all(Model.engine)
        session = sessionmaker(bind = Model.engine)
        self.session = session()
        self.ruoli = ['portiere', 'difensore', 'ccampista', 'attaccante']
        self.value_state, self.val_right_state = 'asc', 'asc'
        self.name_state, self.name_right_state = 'asc', 'asc'
        self.player_to_change = None
        self.team_a, self.ruolo = '', ''
        self.cols = {0: Giocatore.nome, 1: Giocatore.squadra,
                     2: Voto.voto, 3: Voto.pres,
                     4: (Voto.gol_fatti + Voto.rig_ok),
                     5: Voto.gol_sub, 6: Voto.assist, 7: Voto.ammo,
                     8: Voto.esp, 9: Voto.rig_tir, 10: Voto.rig_no,
                     11: Giocatore.valore}
        
    def get_data_players(self, ruolo, item = Voto.voto):
        '''get the avg of vote and all total data by role'''
        query = self.session.query(Giocatore).filter(
                            Giocatore.ruolo == ruolo).subquery()
        adalias = aliased(Giocatore, query)

        query_p = self.session.query(adalias.nome, adalias.idgaz,
                          func.avg(Voto.voto),
                          func.sum(Voto.pres),
                          func.sum(Voto.gol_fatti+Voto.rig_ok),
                          func.sum(Voto.gol_sub),
                          func.sum(Voto.assist),
                          func.sum(Voto.ammo),
                          func.sum(Voto.esp),
                          func.sum(Voto.rig_tir),
                          func.sum(Voto.rig_no),
                          adalias.valore,
                          adalias.squadra).join( # &lt; JOIN
                            adalias, Voto.giocatori).filter(Voto.pres &gt; 0)
                            .group_by(adalias).order_by( # &lt; GROUP &amp; SORT
                                    asc(func.avg(item))).all()
        return query_p

    def get_players_sort_name(self, ruolo):
        '''Get all available goalkeeper'''
        if self.name_state == 'asc':
            query = self.session.query(Giocatore).filter(
                                        Giocatore.ruolo == '%s' % ruolo
                                        ).order_by(desc(Giocatore.idgaz)).all()
        else:
            query = self.session.query(Giocatore).filter(
                                          Giocatore.ruolo == '%s' % ruolo
                                          ).order_by((Giocatore.idgaz)).all()
        return query

    def get_inserted_days(self): # for checking days
        '''get inserted votes-days for import-votes checking'''
        ggs = [item[0] for item in self.session.query(func.distinct(Voto.gg_)).all()]
        return ggs

    def get_player_by_idgaz(self, idgaz):
        '''Get the player by idgaz'''
        player = self.session.query(Giocatore).filter(
                                    Giocatore.idgaz == idgaz).first()
        return player

    def do_players_exist(self): # check at the first time
        '''Return True if players are already imported'''
        if self.session.query(Giocatore).count() != 0:
            print &quot;Gazzetta players present in database&quot;
            return True
        else:
            print &quot;Gazzetta players must be imported first!&quot;
            return False 
        
    def import_new_player(self, idgaz, nome, squadra, valore, ruolo):
        '''Add new player on &quot;giocatori&quot; table'''
        player = Giocatore()
        player.idgaz = idgaz
        player.nome = nome
        player.squadra = squadra
        player.valore = valore
        player.ruolo = ruolo
        self.session.add(player)
        
    def upload_votes(self, item):
        '''Save the data-tuble of votes file into database'''
        print &quot;UPLOAD&quot;, item
        rig_ok = int(item[10]) - int(item[11])
        voto = Voto()
        voto.voto = item[1]
        voto.gg_ = item[2]
        voto.val = item[3]
        voto.gol_fatti = item[4]
        voto.pres = item[5]
        voto.gol_sub = item[6]
        voto.assist = item[7]
        voto.ammo = item[8]
        voto.esp = item[9]
        voto.rig_tir = item[10]
        voto.rig_ok = rig_ok
        voto.rig_no = item[11]
        voto.voto_s = item[12]
        self.session.add(voto)
        #m2m append
        voto.giocatori.append(self.get_player_by_idgaz(item[0])) # pylint: disable=E1101,C0301
        return True

    def total_commit(self):
        '''external commit to use outside loop'''
        self.session.commit()

    def update_player_by_idgaz(self, idgaz, nome, squadra, valore, ruolo):
        '''Get the player by idgaz'''
        player = self.session.query(Giocatore).filter(
                                    Giocatore.idgaz == idgaz).first()
        player.idgaz = idgaz
        player.nome = nome
        player.squadra = squadra
        player.valore = int(valore)
        player.ruolo = ruolo
        
    def get_data_players_by_value(self, ruolo, jolly):
        '''generic query that uses subquery, alias and join function:
        return the sum of a Giocatore.value itering the votes, by idgaz'''
        query = self.session.query(Giocatore).filter(
                            Giocatore.ruolo == ruolo).subquery()
        adalias = aliased(Giocatore, query)
        query_p = self.session.query(adalias.nome, adalias.idgaz,
                          func.avg(Voto.voto),
                          func.sum(Voto.pres),
                          func.sum(Voto.gol_fatti+Voto.rig_ok),
                          func.sum(Voto.gol_sub),
                          func.sum(Voto.assist),
                          func.sum(Voto.ammo),
                          func.sum(Voto.esp),
                          func.sum(Voto.rig_tir),
                          func.sum(Voto.rig_no),
                          adalias.valore,
                          adalias.squadra).join( # &lt; JOIN
                            adalias, Voto.giocatori).filter(Voto.pres &gt; 0)
                            .group_by(adalias).order_by( # &lt; GROUP &amp; SORT
                                    asc(func.sum(jolly))).all()
        return query_p

    def get_data_players_by_valore(self, ruolo):
        '''generic query that uses subquery, alias and join function:
        return all total data player sorted by value'''
        query = self.session.query(Giocatore).filter(
                            Giocatore.ruolo == ruolo).subquery()
        adalias = aliased(Giocatore, query)
        query_p = self.session.query(adalias.nome, adalias.idgaz,
                          func.avg(Voto.voto),
                          func.sum(Voto.pres),
                          func.sum(Voto.gol_fatti+Voto.rig_ok),
                          func.sum(Voto.gol_sub),
                          func.sum(Voto.assist),
                          func.sum(Voto.ammo),
                          func.sum(Voto.esp),
                          func.sum(Voto.rig_tir),
                          func.sum(Voto.rig_no),
                          adalias.valore,
                          adalias.squadra).join( # &lt; JOIN
                            adalias, Voto.giocatori).filter(Voto.pres &gt; 0)
                            .group_by(adalias).order_by( # &lt; GROUP &amp; SORT
                                    asc(adalias.valore)).all()
        return query_p

def main():
    '''test starter'''
    mod = Model()


if __name__ == '__main__':
    main()

Qui il codice relativo al View:

# -*- coding: utf-8 -*-#
# ViewLight.py
'''VIEW Module for FantaPython Application:
Graphic library: provided by wx
All the frames and their children widgets,
are defined here.
Binding are defined under CONTROLLER Module.
Every Frame as a panel child and every widgets
have panel as first parent.
''' 


import wx
import os

from wx import html as wxhtml
from wx.lib.buttons import GenBitmapTextButton

class FrameVotiPlayers(wx.Frame):
    '''Frame for votes consulting'''
    def __init__(self, *args, **kwargs):
        ruoli = ['portiere', 'difensore', 'ccampista', 'attaccante']
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self, -1)
        self.panel.SetBackgroundColour('Pink')
        self.btn_import = GenBitmapTextButton(self.panel, wx.ID_NEW,
                                            wx.Bitmap('images\import_tr.png'),
                                            'Import players/votes'.rjust(30),
                                            size = (350, 40), pos = (5, 425))
        self.btn_exit = GenBitmapTextButton(self.panel, wx.ID_ANY,
                                            wx.Bitmap('images\quit.png'),
                                            'Exit'.rjust(30), size = (335, 40),
                                            pos = (355, 425))
        self.rboxruoli = wx.RadioBox(self.panel, wx.ID_ANY, &quot;ruoli&quot;, (100, 100),
                                     wx.DefaultSize, ruoli, 4,
                                     wx.RA_SPECIFY_COLS)
        self.rboxruoli.Enable()
        self.listdati = wx.ListCtrl(self.panel, wx.NewId(), size = (690, 350),
                                  style = wx.LC_REPORT | wx.LC_HRULES |
                                  wx.LC_VRULES)
        self.listdati.Show(True)
        self.listdati.InsertColumn(0, &quot;nome&quot;, wx.LIST_AUTOSIZE, 140)
        self.listdati.InsertColumn(1, &quot;sq&quot;, wx.LIST_FORMAT_CENTER, 70)
        self.listdati.InsertColumn(2, &quot;mv&quot;, wx.LIST_FORMAT_CENTER, 70)
        self.listdati.InsertColumn(3, &quot;pr&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(4, &quot;gf&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(5, &quot;gs&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(6, &quot;ass&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(7, &quot;amm&quot;, wx.LIST_FORMAT_CENTER, 50)
        self.listdati.InsertColumn(8, &quot;esp&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(9, &quot;rt&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(10, &quot;rs&quot;, wx.LIST_FORMAT_CENTER, 40)
        self.listdati.InsertColumn(11, &quot;$&quot;, wx.LIST_FORMAT_CENTER, 35)

        # sizerize
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.rboxruoli, 0, wx.CENTER, 5)
        vbox.Add(wx.StaticLine(self.panel,), 0, wx.ALL|wx.EXPAND, 5)
        vbox.Add(self.listdati, 0, wx.ALL|wx.CENTER, 5)
        vbox.Add(wx.StaticLine(self.panel,), 0, wx.ALL|wx.EXPAND, 5)

        self.panel.SetSizer(vbox)
        self.Centre()
        self.Show()

class InfoMessage(wx.MessageDialog):
    '''Simple message Dialog'''
    def __init__(self, parent, message):
        wx.MessageDialog.__init__(self, parent, message, 'core info', wx.OK |
                                  wx.ICON_EXCLAMATION)
    def get_choice(self):
        '''get the state of the user choice'''
        if self.ShowModal() == wx.ID_OK:
            self.Destroy()

class ChoiceMessage(wx.MessageDialog):
    '''Simple choice message Dialog'''
    def __init__(self, parent, message):
        wx.MessageDialog.__init__(self, parent, message, 'Core question',
                                  wx.YES_NO | wx.ICON_QUESTION)
    def get_yes(self):
        '''get True if YES is clicked'''
        if self.ShowModal() == wx.ID_YES:
            return True
        else:
            self.Destroy()

class EntryDialog(wx.TextEntryDialog):            
    '''Simple Text Entry Dialog'''
    def __init__(self, parent, msg, value):
        wx.TextEntryDialog.__init__(self, parent, msg, 'Core request',
                                    defaultValue = value, style = wx.OK)
    def get_choice(self):
        '''get the state of the user choice'''
        if self.ShowModal() == wx.ID_OK:
            response = self.GetValue()
            self.Destroy()
            return response

class FileBrowser(wx.FileDialog):
    '''Class for file browser'''
    def __init__(self):
        self.fin = None
        wildcard = &quot;File Gazzetta (*.txt)|*.txt|&quot; 
            &quot;Tutti i files (*.*)|*.*&quot;
        wx.FileDialog.__init__(self, None, &quot;scegli il file&quot;, os.getcwd(),
                               &quot;&quot;, wildcard, wx.OPEN)
        if self.ShowModal() == wx.ID_OK:  
            print(self.GetPath())
            self.file = self.GetPath()
            self.fin = open(self.file, 'r')
        else:
            print &quot;operazione apertura annullata&quot;
            self.file = None
            self.Destroy()
        self.Destroy()
def main():
    '''app starter'''
    app = wx.PySimpleApp()
    FrameVotiPlayers(None, wx.ID_NEW, &quot;FantaStat 1.1 light&quot;, size = (700, 500))
    app.MainLoop()

if __name__ == '__main__':
    main()

infine il codice relativo al COntroller:

# -*- coding: utf-8 -*-#
# ControllerLight.py
'''FantaStat v 1.1 Light
Controller Module for FantaStat Application:
Application pattern:
MODEL | VIEW | CONTROLLER
MODEL Module COntains all data for database querying
VIEW Module Contains all GUI for viewing all database data
CONTROLLER Module contains app logic.
All the binding method to frame widget are defined here''' 

import wx

from DataLight import Model
from ProgressBar import BarController
from ViewLight import FrameVotiPlayers, InfoMessage, ChoiceMessage, FileBrowser

class Controller(object):
    '''Controller class for MVC-like pattern'''
    def __init__(self):
        self.model = Model()
        self.list_voti = []
        self.ctrl = FrameVotiPlayers(None, wx.ID_NEW, &quot;FantaStat 1.1 light&quot;,
                                     size = (700, 500))
        self.ctrl.btn_exit.Bind(wx.EVT_BUTTON, _frame_exit)
        self.ctrl.btn_import.Bind(wx.EVT_BUTTON, self.import_votes)
        self.query = self.model.get_data_players(u'portiere')
        self.fill_list(self.query)
        self.ctrl.rboxruoli.Bind(wx.EVT_RADIOBOX, self.on_roles)
        self.ctrl.listdati.Bind(wx.EVT_LIST_COL_CLICK, self.on_list_column)

    def upload_players(self, file_in):
        '''upload/upgrade players in db via model'''
        def get_flag_role(idg):
            &quot;&quot;&quot;get the role, by idgaz&quot;&quot;&quot;
            if idg &lt; 200:
                role = 'portiere'
            elif idg &gt; 200 and idg &lt; 500:
                role = 'difensore'
            elif idg &gt; 500 and idg &lt; 800:
                role = 'ccampista'
            else:
                role = 'attaccante'
            return role

        if file_in != None:
            record = len(file_in.readlines()) #progress bar param
            barc = BarController(record)
            file_in.seek(0)
            for line in file_in.readlines():
                val = line.split('|')
                try:
                    idr = int(val[0])
                except ValueError:
                    idr = int(val[0].replace('xefxbbxbf', ''))
                
                role = get_flag_role(idr)
                nomev = val[2].decode('utf-8').replace('&quot;', '')
                squadrav = val[3].replace('&quot;', '')
                play_in = self.model.get_player_by_idgaz(idr)
                if play_in == None:
                    print &quot; &lt;&lt;&lt;&lt; assente giocatore con idgaz = &quot;, idr
                    self.model.import_new_player(idr, nomev, squadrav,
                                                 int(val[27].rstrip()), role)
                else:
                    print &quot; ++++ aggiorno giocatore con idgaz = &quot;, idr
                    self.model.update_player_by_idgaz(idr, nomev,
                                        squadrav, int(val[27]), role)
                barc.update_progress_bar()
            self.model.session.commit()
            barc.destroy_progress_bar()
            print &quot;core&gt; dati inseriti con successo&quot;
        else:
            print &quot;core&gt; Nessun dato da salvare&quot;
    
    def import_votes(self, evt):
        '''Import Votes from Gazzetta.com'''
        parent = evt.GetEventObject().GetParent().GetParent()
        ggs = self.model.get_inserted_days()
        mes = &quot;Inserire i voti nel database?&quot;
        if ChoiceMessage(parent, mes).get_yes() == True:
            fbr = FileBrowser()
            file_in = fbr.fin
            path = fbr.file
            if file_in != None:
                choice = int(path.partition('MCC')[-1].partition('.txt')[0])
                if choice not in ggs:
                    try:
                        self.upload_players(file_in)
                    except ValueError:
                        print &quot;core&gt; Operazione annullata&quot;
                    else:
                        self.list_voti = []
                        file_in.seek(0)
                        for line in file_in:
                            self.list_voti.append((int(line.split(&quot;|&quot;)[0]),
                                                   float(line.split(&quot;|&quot;)[7]),
                                                   int(line.split(&quot;|&quot;)[1]),
                                                   int((line.split(&quot;|&quot;)[27]
                                                        ).rstrip()),
                                                   int(line.split(&quot;|&quot;)[11]),
                                                   int(line.split(&quot;|&quot;)[6]),
                                                   int(line.split(&quot;|&quot;)[12]),
                                                   int(line.split(&quot;|&quot;)[15]),
                                                   int(line.split(&quot;|&quot;)[16]),
                                                   int(line.split(&quot;|&quot;)[17]),
                                                   int(line.split(&quot;|&quot;)[18]),
                                                   int(line.split(&quot;|&quot;)[21]),
                                                   float(line.split(&quot;|&quot;)[10])))
                        file_in.close()
                        print &quot;core&gt; pre RECORD&quot;
                        record = len(self.list_voti)
                        if record &gt; 0:
                            upbar = BarController(record)
                            for item in self.list_voti: 
                                self.model.upload_votes(item)
                                if True:
                                    upbar.update_progress_bar()
                            self.model.total_commit()
                            print &quot;core&gt; Dati Salvati con successo&quot;
                            upbar.destroy_progress_bar()
                            role = self.ctrl.rboxruoli.GetStringSelection()
                            self.ctrl.listdati.DeleteAllItems()
                            self.fill_list(self.model.get_data_players(role))
                else:
                    mes = &quot;errore file. Giornata gia' inserita!&quot;
                    InfoMessage(parent, mes).get_choice()
        else:
            print &quot;core&gt; nessun dato da salvare&quot;
    
    def fill_list(self, sqla_query):
        '''Fill the ListControl with stats data from db'''
        self.ctrl.listdati.DeleteAllItems()
        for player in sqla_query:
            nome = player[0]
            row = self.ctrl.listdati.InsertStringItem(0, nome)#ROW
            sq = player[12]
            self.ctrl.listdati.SetStringItem(row, 1, str(sq)) #1st C
            m_v = player[2]
            self.ctrl.listdati.SetStringItem(row, 2, str(&quot;%.3f&quot; % m_v))
            pres = player[3]
            self.ctrl.listdati.SetStringItem(row, 3, str(pres)) 
            g_f = player[4]
            self.ctrl.listdati.SetStringItem(row, 4, str(g_f))
            g_s = player[5]
            self.ctrl.listdati.SetStringItem(row, 5, str(g_s))
            ass = player[6]
            self.ctrl.listdati.SetStringItem(row, 6, str(ass))
            ammo = player[7]
            self.ctrl.listdati.SetStringItem(row, 7, str(ammo))
            esp = player[8]
            self.ctrl.listdati.SetStringItem(row, 8, str(esp))
            r_t = player[9]
            self.ctrl.listdati.SetStringItem(row, 9, str(r_t))
            r_s = player[10]
            self.ctrl.listdati.SetStringItem(row, 10, str(r_s))
            val = player[11]
            self.ctrl.listdati.SetStringItem(row, 11, str(val))

    def on_list_column(self, evt):
        '''get the column header for sorting database data'''
        header = evt.GetColumn()
        ruolo = self.ctrl.rboxruoli.GetStringSelection()
        if 2 &lt; header &lt; 11:
            jolly = self.model.cols[header]
            self.ctrl.listdati.DeleteAllItems()
            self.fill_list(self.model.get_data_players_by_value(ruolo, jolly))
        elif header == 2:
            jolly = self.model.cols[header]
            self.ctrl.listdati.DeleteAllItems()
            self.fill_list(self.model.get_data_players(ruolo, jolly))
        elif header == 11:
            self.ctrl.listdati.DeleteAllItems()
            self.fill_list(self.model.get_data_players_by_valore(ruolo))
        
    def on_roles(self, evt):
        '''radio box &quot;Ruolo&quot; event handler'''
        role = evt.GetEventObject().GetStringSelection()
        players = self.model.get_data_players(role)
        self.ctrl.listdati.DeleteAllItems()
        self.fill_list(players)

def _frame_exit(evt):
    '''exit from frame'''
    frame = evt.GetEventObject().GetParent().GetParent()
    frame.Close()

def main():
    '''starter'''
    app = wx.PySimpleApp()
    Controller()
    app.MainLoop()
    
if __name__ == '__main__':
    main()

Ultimo oggetto estraneo al FantaStat, è la ControlBar delle importazioni:

# ProgressBar.py
'''Module containing a Progress Bar class'''

import wx

class Data(object):
    '''Data class for M-V-C pattern'''
    def __init__(self):
        self.count = 0
        self.max_v = 0
    def add_count (self):
        '''increase the progress bar count for showing its progress'''
        self.count += 1
    def get_max(self):
        '''Get the max value of the progress bar'''
        return self.max_v
    def get_count(self):
        '''Get the current count value of the progressbar in the loop'''
        return self.count
    def set_max(self, value):
        '''set the max value of the progress bar'''
        self.max_v = int(value)

class ProgressBar(wx.ProgressDialog):
    '''Progress Dialog constructor'''
    def __init__(self, max_v):
        wx.ProgressDialog.__init__(self, &quot;&quot;, &quot;Update...&quot;, max_v,
                                   style = wx.PD_AUTO_HIDE |
                                   wx.PD_ELAPSED_TIME |
                                   wx.PD_REMAINING_TIME)
    def progress(self, progress):
        '''update the progress bar during the loops'''
        wx.MicroSleep(1) #for short processes use wx.MilliSleep(1)
        self.Update(progress)
    def destroy(self):
        '''Distruttore della progress bar'''
        self.Destroy()

class BarController(object):
    '''Progress Bar Controller Constructor'''
    def __init__(self, value = 3):
        self.data = Data()
        self.data.set_max(value)
        self.max = self.data.get_max()
        self.count = self.data.get_count()
        self.progress_bar = ProgressBar(self.max)
    def update_progress_bar(self):
        '''Aggiorna la progressBar'''
        self.data.add_count()
        self.count = self.data.get_count()
        self.progress_bar.progress(self.count) # update progressbar
    def destroy_progress_bar(self):
        '''Destroy the wx.ProgressDialog instance'''
        self.progress_bar.destroy()

def main():
    '''app starter'''
    app = wx.PySimpleApp()
    bar_c = BarController(600)
    while bar_c.count &lt; bar_c.max:
        bar_c.update_progress_bar()
    bar_c.destroy_progress_bar()
    app.MainLoop()
    
if __name__ == &quot;__main__&quot;:
    main()

Nella versione 1.2 implementerò l’ordinamento decrescente per colonna E per media_voto,
in modo da ordinare i giocatori di pari valore (es presenze), mettendo prima quello
con la media voto maggiore.

…continua

Pylons: Fantamanager parte 4 – update giocatori

26 Ottobre 2011 Commenti chiusi

<part_3
Vorrei inserire nella pagina “Squadre”, la possibilità di fare l’update del database,
in modo da avere il giocatori e le squadre di appartenenza, sempre aggiornati.

Per prima cosa creo il controller che si occuperà di leggere il file di ingresso per
poi passarlo all’action che aggiornerà il database:

paster controller upload

Inserisco un form nella template ..FantaManagerfantamanagertemplatesderivedseriea.mako:

&lt;%inherit file=&quot;/base/base.mako&quot; /&gt;

&lt;%def name=&quot;title()&quot;&gt;Teams&lt;/%def&gt;
&lt;%def name=&quot;heading()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;head_tags()&quot;&gt;&lt;/%def&gt;

&lt;p&gt;Squadre Serie A&lt;/p&gt;

&lt;ul&gt;
% for squadra in c.squadre:
	&lt;li&gt;&lt;a href=&quot;/team/${squadra}&quot;&gt;${squadra}&lt;/a&gt;&lt;/li&gt;
% endfor
&lt;/ul&gt;

&lt;%def name=&quot;footer()&quot;&gt;
	&lt;h1&gt;Upgrade Players&lt;/h1&gt;

	${h.form(h.url(controller='upload', action='upload'), multipart=True)}
			MCC file: ${h.file('mccfile')} &lt;br /&gt;
	${h.submit('submit', 'Upgrade')}
	${h.end_form()}

&lt;/%def&gt;

la parte che mi interessa è:

${h.form(h.url(controller='upload', action='upload'), multipart=True)}
			MCC file: ${h.file('mccfile')} &lt;br /&gt;
	${h.submit('submit', 'Upgrade')}
	${h.end_form()}

e sopratutto il context_object h.file(‘mccfile’), che richiamerò nel controller al
momento del “submit”.
Utilizzando parecchi helpers (h.) all’interno del template, è necessario fare le
dovute importazioni, nel modulo FantaManagerfantamanagerlibhelpers:

from webhelpers.html.tags import file

from webhelpers.html.tags import stylesheet_link
from webhelpers.html.tags import link_to
from webhelpers.html import escape, HTML, literal, url_escape
from webhelpers.html.tags import *
from pylons import url

Ora occupiamoci del controller FantaManagerfantamanagercontrollersupload.py

import logging

import fantamanager.lib.helpers as h


from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from fantamanager.model.models import Giocatore
from fantamanager.lib.base import BaseController, render
from fantamanager.extra.FileBrowser import FileBrowser
import fantamanager.model.meta as meta

from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector


log = logging.getLogger(__name__)

class UploadController(BaseController):

    @ActionProtector(has_permission('admin'))
    def upload(self):
        def get_flag_role(idg):
            &quot;&quot;&quot;get the role, by idgaz&quot;&quot;&quot;
            if idg &lt; 200:
                role = 'portiere'
            elif idg &gt; 200 and idg &lt; 500:
                role = 'difensore'
            elif idg &gt; 500 and idg &lt; 800:
                role = 'ccampista'
            else:
                role = 'attaccante'
            return role
        mccfile = request.POST['mccfile']
        file_in = mccfile.file
        if file_in != None:
            file_in.seek(0)
            for line in file_in.readlines():
                val = line.split('|')
                try:
                    idr = int(val[0])
                except ValueError:
                    idr = int(val[0].replace('xefxbbxbf', ''))
                
                role = get_flag_role(idr)
                nomev = val[2].decode('utf-8').replace('&quot;', '')
                squadrav = val[3].replace('&quot;', '')
                play_in = meta.Session.query(Giocatore).filter(
                                                Giocatore.idgaz == idr).first()
                if play_in == None:
                    log.info(&quot; &lt;&lt;&lt;&lt; assente giocatore con idgaz = %s&quot; % idr)
                    valore = int(val[27].rstrip())
                    player = Giocatore()
                    player.idgaz = idr
                    player.nome = nomev
                    player.squadra = squadrav
                    player.valore = valore
                    player.ruolo = role
                    meta.Session.add(player)
                else:
                    log.info(&quot; ++++ aggiorno giocatore con idgaz = %s&quot; % idr)
                    player = meta.Session.query(Giocatore).filter(
                                                Giocatore.idgaz == idr).first()
                    player.idgaz = idr
                    player.nome = nomev
                    player.squadra = squadrav
                    player.valore = int(val[27])
                    player.ruolo = role
            meta.Session.commit()
            file_in.close()
            log.info(&quot;core&gt; dati inseriti con successo&quot;)
            session['flash'] = 'Players successfully updated.'
            session.save()
        else:
            log.info(&quot;core&gt; Nessun dato da salvare&quot;)
        redirect(url(controller = 'seriea', action = 'squadre'))

Il context_object (catturata dalla pagina con il codice della template) lo utilizziamo qui:

        mccfile = request.POST['mccfile']

poco prima del redirect, abbiamo salvato nella session (un dizionario), alla chiave ‘flash’, il messaggio
che desideriamo far apparire ad update avvenuto (come descritto nel pylons book).
Poi abbiamo salvato la session con il metodo save().

            session['flash'] = 'Players successfully updated.'
            session.save()

Perchè il flash-message funzioni, bisogna innanzitutto importare la session all’interno del controller:

from pylons import session

Poi editiamo la template base, dal quale ereditano tutte le altre template, e modifichiamo il codice:

## -*- coding: utf-8 -*-
&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot;
&quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;

&lt;html&gt;
	&lt;head&gt;
	  &lt;title&gt;${self.title()}&lt;/title&gt;
	  ${self.head()}
	&lt;/head&gt;
&lt;h1&gt;Fantacalcio Manager&lt;/h1&gt;
	&lt;body&gt;
		${self.header()}
		${self.tabs()}
		${self.menu()}
		${self.heading()}
		${self.breadcrumbs()}
		${self.flash()}
		${next.body()}
		${self.footer()}
	&lt;/body&gt;
&lt;/html&gt;

&lt;%def name=&quot;title()&quot;&gt;Fantacalcio Manager&lt;/%def&gt;
&lt;%def name=&quot;head()&quot;&gt;${h.stylesheet_link(h.url('/css/main.css'))}&lt;/%def&gt;

&lt;%def name=&quot;header()&quot;&gt;&lt;a name=&quot;top&quot;&gt;&lt;/a&gt;&lt;/%def&gt;
&lt;%def name=&quot;tabs()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;menu()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;heading()&quot;&gt;&lt;h1&gt;${c.heading or 'No Title'}&lt;/h1&gt;&lt;/%def&gt;
&lt;%def name=&quot;breadcrumbs()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;footer()&quot;&gt;&lt;p&gt;&lt;a href=&quot;#top&quot;&gt;Top ^&lt;/a&gt;&lt;/p&gt;&lt;/%def&gt;

&lt;%def name=&quot;flash()&quot;&gt;
	% if session.has_key('flash'):
	&lt;div id=&quot;flash&quot;&gt;&lt;p&gt;${session.get('flash')}&lt;/p&gt;&lt;/div&gt;
	&lt;%
	del session['flash']
	session.save()
	%&gt;
	% endif
&lt;/%def&gt;

definiamo la funzione flash all’interno della template

		${self.flash()}

, subito prima del body, con il seguente codice:

&lt;%def name=&quot;flash()&quot;&gt;
	% if session.has_key('flash'):
	&lt;div id=&quot;flash&quot;&gt;&lt;p&gt;${session.get('flash')}&lt;/p&gt;&lt;/div&gt;
	&lt;%
	del session['flash']
	session.save()
	%&gt;
	% endif
&lt;/%def&gt;

e inseriamo nel file FantaManagerfantamanagerpubliccssmain.css, lo
stile desiderato per l’evidenziazione del nostro flash-message:

#flash {
background: #ffc;
padding: 5px;
border: 1px dotted #000;
margin-bottom: 20px;
}
#flash p { margin: 0px; padding: 0px; }

Ora, quando la action upload (del controller upload), viene chiamata (premendo il pulsante upgrade),
dopo tutte le operazioni effettuate sul file, compreso l’aggiornamento del database,
il messaggio ‘Players successfully updated.’ viene salvato nella session e avviene il redirect alla
template squadre.mako

redirect(url(controller = 'seriea', action = 'squadre'))

Siccome la template squadre.mako eredita da base.mako, prima del viene eseguita la funzione flash.
Tale funzione flash, controlla che nella session sia presente la chiave flash (sì!), ne memorizza il
valore associato alla chiave (in nostro messaggio), cancella la chiave ‘flash’ e salva la
session (per pulirla).
Il messaggio viene quindi visualizzato dalla template figlia (squadre.mako).
Se effettuiamo inoltre un refresh della pagina, il flash-message sparisce, poichè la session
viene ripulita come ultima operazione.

Categorie:Fantacalcio, pylons, python Tag:

Pylons: Fantamanager parte 3 – authorization e authentication con repoze.what

14 Ottobre 2011 Commenti chiusi

<part_1<part_2
Le prime basi di pylons mi sono piaciute parecchio.
Ho testato i vari semplici esempi disponibili sulle guide e sulla documentazione
reperibile on-line, poi mi sono imbattuto sul problema dell’autenticazione e
delle autorizzazioni.
I metodi sono parecchi, ma ho scelto di appoggiarmi per questo compito
a Repoze.what (come suggerito dalla guida ufficiale).
Tale guida è mutuata da questa e, vista la scarsa documentazioni disponibile
in Italiano ho pensato di riassumere un po’ quello che ho provato e testato.
Per i programmatori più esperti, forse non ci sono stati problemi, per me, aspirante tale, invece sì.

Anticipo subito che i problemi più fastidiosi li ho avuti codificando i models di
Sqlalchemy, avvalendomi del declarative_base.
Non ho avuto modo di vedere repoze funzionante appieno, a causa di un errore riguardante il mapper di SQLA.

‘EagerLoader’ object has no attribute ‘mapper’
Per questa ragione ho dovuto ricodificare tutte le classi della mia applicazione:
Giocatore, Squadra ecc.

Comunque…

I componenti utili alla causa sono:

-Pylons v1.0
-repoze.what v1.0.9
-repoze.what-pylons v1.0
-repoze.what-quickstart v1.0.8

perchè tutto funzioni, sono necessarie le seguenti dipendenze:

-repoze.who
-repoze.who-friendlyform
-repoze.what
-repoze.what-pylons
-repoze.what-quickstart
-repoze.what-plugins-sql
-repoze.who.plugins.sa

Dovrebbero comunque installarsi tutte automaticamente via easy_install

easy_install repoze.what-pylons

easy_install repoze.what-quickstart

Ora riprendendo a mano il mio progetto di esempio, creo il file:
..FantaManagerfantamanagermodelauth.py
dove definisco i modelli utilizzati da repoze.what

from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Unicode, UnicodeText, Integer, Date, CHAR
from sqlalchemy import orm
from fantamanager.model.meta import metadata
import os
from hashlib import sha1

group_table = Table('group', metadata,
    Column('id', Integer(), primary_key=True),
    Column('name', Unicode(255), unique=True, nullable=False),
)

permission_table = Table('permission', metadata,
    Column('id', Integer(), primary_key=True),
    Column('name', Unicode(255), unique=True, nullable=False),
)

user_table = Table('user', metadata,
    Column('id', Integer(), primary_key=True),
    Column('username', Unicode(255), unique=True, nullable=False),
    Column('email', Unicode(255), unique=True, nullable=False),
    Column('password', Unicode(80), nullable=False),
    Column('fullname', Unicode(255), nullable=False),
)

# many-to-many relationship between groups and permissions.
group_permission_table = Table('group_permission', metadata,
    Column('group_id', Integer, ForeignKey('group.id')),
    Column('permission_id', Integer, ForeignKey('permission.id')),
)

# many-to-many relationship between groups and users
user_group_table = Table('user_group', metadata,
    Column('user_id', Integer, ForeignKey('user.id')),
    Column('group_id', Integer, ForeignKey('group.id')),
)

class Group(object):
    pass

class Permission(object):
    pass

class User(object):

    def _set_password(self, password):
        &quot;&quot;&quot;Hash password on the fly.&quot;&quot;&quot;
        hashed_password = password

        if isinstance(password, unicode):
            password_8bit = password.encode('UTF-8')
        else:
            password_8bit = password

        salt = sha1()
        salt.update(os.urandom(60))
        hash = sha1()
        hash.update(password_8bit + salt.hexdigest())
        hashed_password = salt.hexdigest() + hash.hexdigest()

        # Make sure the hased password is an UTF-8 object at the end of the
        # process because SQLAlchemy _wants_ a unicode object for Unicode
        # fields
        if not isinstance(hashed_password, unicode):
            hashed_password = hashed_password.decode('UTF-8')

        self.password = hashed_password

    def _get_password(self):
        &quot;&quot;&quot;Return the password hashed&quot;&quot;&quot;
        return self.password

    def validate_password(self, password):
        &quot;&quot;&quot;
        Check the password against existing credentials.

        :param password: the password that was provided by the user to
            try and authenticate. This is the clear text version that we will
            need to match against the hashed one in the database.
        :type password: unicode object.
        :return: Whether the password is valid.
        :rtype: bool

        &quot;&quot;&quot;
        hashed_pass = sha1()
        hashed_pass.update(password + self.password[:40])
        return self.password[40:] == hashed_pass.hexdigest()

# Map SQLAlchemy table definitions to python classes
orm.mapper(Group, group_table, properties={
    'permissions':orm.relation(Permission, secondary=group_permission_table),
    'users':orm.relation(User, secondary=user_group_table),
})
orm.mapper(Permission, permission_table, properties={
    'groups':orm.relation(Group, secondary=group_permission_table),
})
orm.mapper(User, user_table, properties={
    'groups':orm.relation(Group, secondary=user_group_table),
})

il file ..FantaManagerfantamanagermodelmeta.py è così composto:

&quot;&quot;&quot;SQLAlchemy Metadata and Session object&quot;&quot;&quot;
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import MetaData

__all__ = ['Base', 'Session']

Session = scoped_session(sessionmaker())

metadata = MetaData()

Ora importiamo Group, User, Permission nel file ..FantaManagerfantamanagermodel__init__.py

&quot;&quot;&quot;The application's model objects&quot;&quot;&quot;
import sqlalchemy as sa
from sqlalchemy import orm
from fantamanager.model import meta
from fantamanager.model.auth import User, Group, Permission

def init_model(engine):
    &quot;&quot;&quot;Call me before using any of the tables or classes in the model&quot;&quot;&quot;
    sm = orm.sessionmaker(autoflush=True, autocommit=False, bind=engine)
    meta.engine = engine
    meta.Session = orm.scoped_session(sm)

Una volta definite le class/table per le autorizzazioni/autenticazioni, dobbiamo settare
repoze.what in modo che le utilizzi. Per fare questo si utilizza la funzione
setup_sql_auth() all’interno della funzione add_auth(), nel file
..FantaManagerfantamanagerlibauth.py

from repoze.what.plugins.quickstart import setup_sql_auth
from fantamanager.model import meta
from fantamanager.model.auth import User, Group, Permission

def add_auth(app, config):
    &quot;&quot;&quot;
    Add authentication and authorization middleware to the ``app``.

    We're going to define post-login and post-logout pages
    to do some cool things.

    &quot;&quot;&quot;
    # we need to provide repoze.what with translations as described here:
    # http://what.repoze.org/docs/plugins/quickstart/
    return setup_sql_auth(app, User, Group, Permission, meta.Session,
                login_url='/account/login',
                post_login_url='/account/login',
                post_logout_url='/',
                login_handler='/account/login_handler',
                logout_handler='/account/logout',
                cookie_secret=config.get('cookie_secret'),
                translations={
                    'user_name': 'username',
                    'group_name': 'name',
                    'permission_name': 'name',
                })

Editiamo il file ..FantaManagerdevelopment.ini ed inseriamo il cookie_secret

[app:main]
use = egg:FantaManager
full_stack = true
static_files = true
sqlalchemy.url = sqlite:///%(here)s/FantaManager.sqlite
cache_dir = %(here)s/data
beaker.session.key = fantamanager
beaker.session.secret = somesecret
# set repoze cookie secret
cookie_secret = 'your-own-secret'

il paramtero cookie_secret, essendo config, un argomento passato alla precedente funzione add_auth(),
ci serve per generare i cookies.
Ora aggiungiamo il middleware (lo strato di cipolla Repoze).
Editiamo il file ..FantaManagerfantamanagerconfigmiddleware.py

&quot;&quot;&quot;Pylons middleware initialization&quot;&quot;&quot;
from beaker.middleware import SessionMiddleware
from paste.cascade import Cascade
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from pylons.middleware import ErrorHandler, StatusCodeRedirect
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware

from fantamanager.config.environment import load_environment
from fantamanager.lib.auth import add_auth
# from repoze.who
from repoze.who.config import make_middleware_with_config as make_who_with_config

def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
    ...
    # Configure the Pylons environment
    config = load_environment(global_conf, app_conf)

    # The Pylons WSGI app
    app = PylonsApp(config=config)

    # Routing/Session/Cache Middleware
    app = RoutesMiddleware(app, config['routes.map'], singleton=False)
    app = SessionMiddleware(app, config)

    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
    # added for repoze.what auth
    app = add_auth(app, config)

    if asbool(full_stack):
        # Handle Python exceptions
        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])

        # Display error documents for 401, 403, 404 status codes (and
        # 500 when debug is disabled)
        if asbool(config['debug']):
            app = StatusCodeRedirect(app)
        else:
            app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])

    # Establish the Registry for this application
    app = RegistryManager(app)

    if asbool(static_files):
        # Serve static files
        static_app = StaticURLParser(config['pylons.paths']['static_files'])
        app = Cascade([static_app, app])
    app.config = config
    return app

il nostro strato di cipolla è rappresentato dalla riga di codice

app = add_auth(app, config)

subito sotto

    # CUSTOM MIDDLEWARE HERE

Aggiungiamo ora un Gruppo ‘admin’, un paio di utenti e i permessi di admin.
Inseriamo il codice necessario, all’interno del file
..FantaManagerfantamanagerwebsetup.py

&quot;&quot;&quot;Setup the FantaManager application&quot;&quot;&quot;
import logging

import pylons.test

from fantamanager import model
from fantamanager.model import meta
from fantamanager.config.environment import load_environment
from fantamanager.model.meta import Session
from fantamanager.model.auth import User, Group, Permission
from fantamanager.model.models import Giocatore, Squadra

log = logging.getLogger(__name__)


def setup_app(command, conf, vars):
    &quot;&quot;&quot;Place any commands to setup fantamanager here&quot;&quot;&quot;
    # Don't reload the app if it was loaded under the testing environment
    if not pylons.test.pylonsapp:
        load_environment(conf.global_conf, conf.local_conf)

    meta.metadata.bind = meta.engine
    # Create the tables if they don't already exist
    log.info(&quot;Creating tables&quot;)
    meta.metadata.drop_all(checkfirst=True, bind=Session.bind)
    meta.metadata.create_all(bind=Session.bind)
    # Now let's create users, group and permission

    # ADMIN group
    log.info(&quot;Adding initial users, groups and permissions...&quot;)
    log.info(&quot;Adding 'Admin' Group...&quot;)
    g = Group()
    g.name = u'admin'
    meta.Session.add(g)
    meta.Session.commit()
    log.info(&quot;+++ 'Admin' Group Done!&quot;)

    # ADMIN permission
    log.info(&quot;-&gt; Adding 'Admin' permission...&quot;)
    p = Permission()
    p.name = u'admin'
    p.groups.append(g)
    meta.Session.add(p)
    meta.Session.commit()
    log.info(&quot;+++ 'Admin' permission assigned to 'Admin' group!&quot;)

    # User ADMIN
    log.info(&quot;-&gt; Adding 'Admin' user...&quot;)
    u = User()
    u.username = u'admin'
    u.fullname = u'admin'
    u._set_password('admin')
    u.email = u'[email protected]'
    u.groups.append(g)
    meta.Session.add(u)
    meta.Session.commit()
    log.info(&quot;+++ 'Admin' user created!&quot;)
    log.info(&quot;+++ 'Admin' user assigned to 'Admin' group!&quot;)

    # User TEST
    log.info(&quot;-&gt; Adding 'Test' user...&quot;)
    u = User()
    u.username = u'test'
    u.fullname = u'test'
    u._set_password('test')
    u.email = u'[email protected]'
    meta.Session.add(u)
    meta.Session.commit()
    log.info(&quot;+++ 'Test' user created!&quot;)
    log.info(&quot;+++ Done!&quot;)

alternativamente utilizzare la shell di paster e creare gli oggetti interattivamente:

paster shell

Ora non resta che definire il controller e la action di Login:

paster controller account

Edito il controller appena creato (..FantaManagerfantamanagercontrollersaccount.py)

import logging

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect

from fantamanager.lib.base import BaseController, render

from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector
from pylons.controllers.util import redirect

log = logging.getLogger(__name__)

class AccountController(BaseController):

    def login(self):
        &quot;&quot;&quot;
        This is where the login form should be rendered.
        Without the login counter, we won't be able to tell if the user has
        tried to log in with wrong credentials
        &quot;&quot;&quot;
        identity = request.environ.get('repoze.who.identity')
        came_from = str(request.GET.get('came_from', '')) or 
                    url(controller='account', action='welcome')
        if identity:
            redirect(url(came_from))
        else:
            c.came_from = came_from
            c.login_counter = request.environ['repoze.who.logins'] + 1
            return render('/derived/account/login.html')

    @ActionProtector(not_anonymous())
    def welcome(self):
        &quot;&quot;&quot;
        Greet the user if she logged in successfully or redirect back
        to the login form otherwise(using ActionProtector decorator).
        &quot;&quot;&quot;
        identity = request.environ.get('repoze.who.identity')
        return 'Welcome back %s' % identity['repoze.who.userid']

    @ActionProtector(not_anonymous())
    def test_user_access(self):
        return 'You are inside user section'

    @ActionProtector(has_permission('admin'))
    def test_admin_access(self):
        return 'You are inside admin section'

…e la relativa template ..FantaManagerfantamanagertemplatesderivedaccountlogin.html

% if c.login_counter &gt; 1:
    Incorrect Username or Password
% endif
&lt;form action=&quot;${h.url(controller='account', action='login_handler'
,came_from=c.came_from, __logins=c.login_counter)}&quot; method=&quot;POST&quot;&gt;
&lt;label for=&quot;login&quot;&gt;Username:&lt;/label&gt;
&lt;input type=&quot;text&quot; id=&quot;login&quot; name=&quot;login&quot; /&gt;&lt;br /&gt;
&lt;label for=&quot;password&quot;&gt;Password:&lt;/label&gt;
&lt;input type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot; /&gt;&lt;br /&gt;
&lt;input type=&quot;submit&quot; id=&quot;submit&quot; value=&quot;Submit&quot; /&gt;
&lt;/form&gt;

Prima di lanciare il server, ricodifico le class/table della mia applicazione
(precedentemente create utilizzando il declarative_base).
Per renderla breve utilizzo solo la classe Giocatore e Squadra:

# -*- coding: utf-8 -*-#
## ..modelmodels.py
'''model for FantaManager application powered by Pylons''' 

from fantamanager.model.meta import metadata
from sqlalchemy.types import Unicode
from sqlalchemy import create_engine, Column, Integer, String, Float
from sqlalchemy import ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, aliased
from sqlalchemy.orm import relation as relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy import orm, func, desc, asc, and_

#metadata = Base.metadata

giocatore_table = Table('giocatori', metadata,
    Column('id', Integer(), primary_key=True),
    Column('idgaz', Integer()),
    Column('nome', Unicode(255)),
    Column('squadra', Unicode(255)),
    Column('valore', Integer()),
    Column('ruolo', Unicode(255)),
)

squadra_table = Table('squadre', metadata,
    Column('id', Integer(), primary_key=True),
    Column('nome', Unicode(255)),
    Column('budget', Integer()),
    Column('cost', Integer()),
    Column('pts', Float()),
)
# M2M relation
squadre_giocatori_table = Table('squadre_giocatori', metadata,
    Column('squadre_id', Integer, ForeignKey('squadre.id')),
    Column('giocatori_id', Integer, ForeignKey('giocatori.id')),
)

class Giocatore(object):
    pass

class Squadra(object):
    pass

# Map SQLAlchemy table definitions to python classes
orm.mapper(Squadra, squadra_table, properties={
    'giocatori':orm.relation(Giocatore, secondary=squadre_giocatori_table),})
orm.mapper(Giocatore, giocatore_table, properties={
    'squadre':orm.relation(Squadra, secondary=squadre_giocatori_table),})

Lanciamo il setup dell’applicazione:

paster setup-app development.ini

e avviamo il server:

paster serve --reload development.ini

se visito la pagina:
http://127.0.0.1:5000/account/test_user_access
e inserisco username e password (‘test’, ‘test’)
vedo renderizzato correttamente: You are inside user sectionse visito la pagina
http://127.0.0.1:5000/account/test_admin_access
ottengo il FORBIDDEN default di pylons poichè mi vedo ancora assegnato il cookie
relativo al test-user

ATTENZIONE! svuotare la cache di firefox prima di testare la pagina di Admin!

se ri-visito la pagina:
http://127.0.0.1:5000/account/test_admin_access
e inserisco username e password (‘admin’, ‘admin’)
vedo renderizzato correttamente: You are inside ADMIN section
Tutto ok!

Ora se vogliamo decorare la azioni del mio controller personale (la mia app),
basta appunto anteporre il decorator desiderato, prima della action che vogliamo
proteggere, es:

import logging

from sqlalchemy import distinct

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from fantamanager.lib.base import Session, BaseController, render
from fantamanager.model.models import Giocatore

from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector

import fantamanager.lib.helpers as h

log = logging.getLogger(__name__)

class SerieaController(BaseController):
    def __before__(self):
        self.ateam_q = Session.query(distinct(Giocatore.squadra))

    @ActionProtector(not_anonymous())
    def index(self):
        return 'Test: &gt;&gt; Controller Serie_A &gt;&gt; Connection OK!'

    @ActionProtector(has_permission('admin'))
    def squadre(self):
        c.squadre = [team[0] for team in self.ateam_q.all()]
        return render('/seriea.mako')

se visito la pagina http://127.0.0.1:5000/seriea/squadre
devo fare il login: se utilizzo test mi becco il Forbidden
se svuoto la cache ed entro come admin, passo!

Questo è quanto.

Categorie:Fantacalcio, pylons, python, sqlalchemy Tag: