Archivio

Archivio autore

Xpath: appunti

10 ottobre 2017 Commenti chiusi

Indice:
parte 1. Xpath: Appunti
parte 2. xpath: Abbreviazioni
parte 3. xpath: Funzioni generiche
scrapy
xpath syntax

Da wikipedia:
In informatica XPath è un linguaggio, parte della famiglia XML, che permette di individuare i nodi all’interno di un documento XML.
Le espressioni XPath, a differenza delle espressioni XML, non servono solo ad identificare la struttura di un documento,
bensì a localizzarne con precisione i nodi.

Per poter fare pratica, utilizzeremo la shell di scrapy, quindi passiamo all’installazione.

Installazione Scrapy

Per prima cosa creare un virtual-environment.

virtualenv venv

Abilitare il virtual-environment:

venv/Script/activate

per windows, mentre per linux, nel mio specifico caso Ubuntu:

source venv/bin/activate

Prima di procedere con l’installazione dei pacchetti necessari tramite
“pip”, per ubuntu (>12.04) è necessario installare alcuni pacchetti necessari
alle successive compilazioni, quindi da terminale:

sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev

sempre da venv, se stiamo lavorando da window, installare il pacchetto “pypiwin32”:

pip install pypiwin32

ora, per entrambi i casi, è possibile installare Scrapy:

pip install scrapy

Shell

Per lanciare la shell di scrapy utilizzare il comando:

scrapy shell

Scrapy ci mette a disposizione un oggetto Selector sul quale è possibile
utilizzare xpath.

>>> from scrapy import Selector
>>> s = Selector(text="<foo><bar></bar></foo>")
>>> s.xpath('//bar').extract()
[u'<bar></bar>']

Nota:
Le espressioni xpath, oltre con Selector, possono essere analizzate direttamente
sull’oggetto response, aprendo la shell e passando come argomento il percorso di un
file o l’indirizzo di una pagina web:

(venv) C:\...>scrapy shell "file://path_to_file"
>>> response.xpath('//*').extract()

Qualora volessimo modificare il body dell’oggetto response,
dobbiamo fare una copia di esso utilizzando il metodo replace():

>>> new_body = "<foo><bar></bar></foo>"
>>> response.body = new_body
    ...
    raise AttributeError(msg)
AttributeError: HtmlResponse.body is not modifiable, use HtmlResponse.replace() instead
>>> new_response = response.replace(body=new_body)
>>> new_response.xpath('//bar')
[<Selector xpath='//bar' data=u'<bar></bar>'>]

Diamo ora un’occhiata molto generica a xpath.

Xpath: parentele

Supponiamo di avere una struttura di questo genere:

<A-TAG class="a-class">A-text
    <B-TAG class="b-class">B-text</B-TAG>
    <B-TAG id="1">B-text
        <C-TAG class="c-class" id="3">C-text</C-TAG>
        <C-TAG class="c-class" id="4">C-text
            <D-TAG>D-text</D-TAG> 
        </C-TAG>
    </B-TAG>
    <B-TAG id="2">B-text
        <C-TAG id="5">C-text
            <D-TAG>D-text</D-TAG>
            <D-TAG id="6">D-text
                <E-TAG>E-text</E-TAG>
                <E-TAG>E-text
                    <F-TAG>F-text</F-TAG>
                </E-TAG>
            </D-TAG>
        </C-TAG>
    </B-TAG>
    <B-TAG attr="b-attr">B-text
        <C-TAG>C-text
            <D-TAG>D-text</D-TAG>
        </C-TAG>
    </B-TAG>
</A-TAG>

prepariamo il selector con il testo precedente:

>>> body="""
... <A-TAG class="a-class">A-text
...     <B-TAG class="b-class">B-text</B-TAG>
...     <B-TAG id="1">B-text
...         <C-TAG class="c-class" id="3">C-text</C-TAG>
...         <C-TAG class="c-class" id="4">C-text
...             <D-TAG>D-text</D-TAG>
...         </C-TAG>
...     </B-TAG>
...     <B-TAG id="2">B-text
...         <C-TAG id="5">C-text
...             <D-TAG>D-text</D-TAG>
...             <D-TAG id="6">D-text
...                 <E-TAG>E-text</E-TAG>
...                 <E-TAG>E-text
...                     <F-TAG>F-text</F-TAG>
...                 </E-TAG>
...             </D-TAG>
...         </C-TAG>
...     </B-TAG>
...     <B-TAG attr="b-attr">B-text
...         <C-TAG>C-text
...             <D-TAG>D-text</D-TAG>
...         </C-TAG>
...     </B-TAG>
... </A-TAG>
... """
>>> selector = Selector(text=body)

Nota:
Prima di analizzare le parentele è bene fare una distinzione sui percorsi.
Se l’espressione xpath comincia con ‘/’ si vogliono ricercare percorsi assoluti,
mentre con ‘//’ si cercano TUTTi gli elementi che sposano i criteri successivi.

Iniziamo ad analizzare le parentele ricordandoci di utilizzare il filtri tag in MINUSCOLO!

>>> selector
<Selector xpath=None data=u'<html><body><a-tag class="a-class">A-tex...'
>>>> len(selector.xpath('//B-TAG'))
0
>>> len(selector.xpath('//b-tag'))
4

ancestor::

Ritorna tutti i predecessori (ancestors ovvero genitori, genitori di genitori e così via)
E’ importantissimo ricordarsi di chiamare la funzione position() che decide quale
livello di ancestor si è scelto di conoscere:

>>> selector = Selector(text=body)
>>> for pos in range(1, 4):
        selector.xpath('//c-tag[@id=4]/ancestor::*[position() = %s]' % pos)
...
[<Selector xpath='//c-tag[@id=4]/ancestor::*[position() = 1]' data=u'<b-tag id="1">B-text\n        <c-tag clas'>]
[<Selector xpath='//c-tag[@id=4]/ancestor::*[position() = 2]' data=u'<a-tag class="a-class">A-text\n    <b-tag'>]
[<Selector xpath='//c-tag[@id=4]/ancestor::*[position() = 3]' data=u'<body><a-tag class="a-class">A-text\n    '>]

All’aumentare del numero di posizione, ci allontaniamo con il predecessore: 1=b-tag, 2=a-tag, 3=”body”
‘c-tag[@id=4]’ serve a filtrare tutti i c-tag con attributo id=4, se omettessi questo filtro,
otterrei gli ancestors di tutti i nodi c-tag (o ch iper lui):

>>> for s in selector.xpath('//c-tag/ancestor::*[position() = 1]'): print s
...
<Selector xpath='//c-tag/ancestor::*[position() = 1]' data=u'<b-tag id="1">B-text\n        <c-tag clas'>
<Selector xpath='//c-tag/ancestor::*[position() = 1]' data=u'<b-tag id="2">B-text\n        <c-tag id="'>
<Selector xpath='//c-tag/ancestor::*[position() = 1]' data=u'<b-tag attr="b-attr">B-text\n        <c-t'>

ancestor-or-self::

Ritorna il nodo corrente (self) e come nel caso precedente tutti i predecessori (ancestors).
In questo caso la posizione 1 corrisponde a self (il nodo stesso)

>>> for pos in range(1, 5):
...     selector.xpath('//c-tag[@id=4]/ancestor-or-self::*[position() = %s]' % pos)
...
[<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 1]' data=u'<c-tag class="c-class" id="4">C-text\n   '>]
[<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 2]' data=u'<b-tag id="1">B-text\n        <c-tag clas'>]
[<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 3]' data=u'<a-tag class="a-class">A-text\n    <b-tag'>]
[<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 4]' data=u'<body><a-tag class="a-class">A-text\n    '>]

attribute::

Ritorna gli attributi del nodo corrente

>>> for s in selector.xpath('//c-tag[@id=4]/attribute::*'): print s
...
<Selector xpath='//c-tag[@id=4]/attribute::*' data=u'c-class'>
<Selector xpath='//c-tag[@id=4]/attribute::*' data=u'4'>

child::

Ritorna tutti i figli de nodo corrente

>>> for s in selector.xpath('//b-tag[@id=1]/child::*'): print s
...
<Selector xpath='//b-tag[@id=1]/child::*' data=u'<c-tag class="c-class" id="3">C-text</c-'>
<Selector xpath='//b-tag[@id=1]/child::*' data=u'<c-tag class="c-class" id="4">C-text\n   '>

child è sottinteso essere il nodo corrente quindi la sintassi

>>> for s in selector.xpath('//b-tag[@id=1]/child::c-tag'): print s
...
<Selector xpath='//b-tag[@id=1]/child::c-tag' data=u'<c-tag class="c-class" id="3">C-text</c-'>
<Selector xpath='//b-tag[@id=1]/child::c-tag' data=u'<c-tag class="c-class" id="4">C-text\n   '>
>>> for s in selector.xpath('//b-tag[@id=1]/c-tag'): print s
...
<Selector xpath='//b-tag[@id=1]/c-tag' data=u'<c-tag class="c-class" id="3">C-text</c-'>
<Selector xpath='//b-tag[@id=1]/c-tag' data=u'<c-tag class="c-class" id="4">C-text\n   '>

descendant::
Ritorna tutti i nodi discendenti del noto corrente (figli, figli dei figli ecc)

>>> for s in selector.xpath('//b-tag[@id=1]/descendant::*'): print s
...
<Selector xpath='//b-tag[@id=1]/descendant::*' data=u'<c-tag class="c-class" id="3">C-text</c-'>
<Selector xpath='//b-tag[@id=1]/descendant::*' data=u'<c-tag class="c-class" id="4">C-text\n   '>
<Selector xpath='//b-tag[@id=1]/descendant::*' data=u'<d-tag>D-text</d-tag>'>

descendant-or-self::

Ritorna tutti i discendenti del nodo corrente, partendo dal nodo stesso (position())

>>> for pos in range(1, 4):
...     r.xpath('//root/descendant-or-self::*[position()= %s]' % pos)
...
[<Selector xpath='//root/descendant-or-self::*[position()= 1]' data=u'<root><foo><bar attr="x"></bar></foo><fo'>]
[<Selector xpath='//root/descendant-or-self::*[position()= 2]' data=u'<foo><bar attr="x"></bar></foo>'>]
[<Selector xpath='//root/descendant-or-self::*[position()= 3]' data=u'<bar attr="x"></bar>'>]

following::

Ritorna tutto ciò che nel documento viene dopo il tag di chiusura del nodo corrente:

>>> for s in selector.xpath('//b-tag[@id=1]/following::*'): print s
...
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<b-tag id="2">B-text\n        <c-tag id="'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<c-tag id="5">C-text\n            <d-tag>'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<d-tag>D-text</d-tag>'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<d-tag id="6">D-text\n                <e-'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<e-tag>E-text</e-tag>'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<e-tag>E-text\n                    <f-tag'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<f-tag>F-text</f-tag>'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<b-tag attr="b-attr">B-text\n        <c-t'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<c-tag>C-text\n            <d-tag>D-text<'>
<Selector xpath='//b-tag[@id=1]/following::*' data=u'<d-tag>D-text</d-tag>'>

se vogliamo filtrare il risultato ad esempio solo i ‘c-tag’ successivi:

>>> for s in selector.xpath('//b-tag[@id=1]/following::c-tag'): print s
...
<Selector xpath='//b-tag[@id=1]/following::c-tag' data=u'<c-tag id="5">C-text\n            <d-tag>'>
<Selector xpath='//b-tag[@id=1]/following::c-tag' data=u'<c-tag>C-text\n            <d-tag>D-text<'>

following-sibling::

Ritorna tutti i fratelli (siblings, cioè i nodi sullo stesso ASSE) dopo il nodo corrente:

>>> for s in selector.xpath('//b-tag[@id=1]/following-sibling::*'): print s
...
<Selector xpath='//b-tag[@id=1]/following-sibling::*' data=u'<b-tag id="2">B-text\n        <c-tag id="'>
<Selector xpath='//b-tag[@id=1]/following-sibling::*' data=u'<b-tag attr="b-attr">B-text\n        <c-t'>

parent::

Ritorna il genitore del nodo corrente

>>> selector.xpath('//b-tag[@id=1]/parent::*')
[<Selector xpath='//b-tag[@id=1]/parent::*' data=u'<a-tag class="a-class">A-text\n    <b-tag'>]

preceding::

Ritorna tutto ciò che sta prima del tag di apertura del nodo corrente:

>>> for s in selector.xpath('//b-tag[@id=1]/preceding::*'): print s
...
<Selector xpath='//b-tag[@id=1]/preceding::*' data=u'<b-tag class="b-class">B-text</b-tag>'>

preceding-sibling::

Ritorna tutti i fratelli (sibling) prima del nodo corrente

>>> for s in selector.xpath('//b-tag[@id=1]/preceding-sibling::*'): print s
...
<Selector xpath='//b-tag[@id=1]/preceding-sibling::*' data=u'<b-tag class="b-class">B-text</b-tag>'>

self::

Ritorna il nodo corrente

>>> selector.xpath('//b-tag[@id=1]/self::*')
[<Selector xpath='//b-tag[@id=1]/self::*' data=u'<b-tag id="1">B-text\n        <c-tag clas'>]

Riassumendo:

1. I percorsi possono essere ASSOLUTI:

>>> for s in selector.xpath('body/a-tag/b-tag/c-tag'): print s
...
<Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag class="c-class" id="3">C-text</c-'>
<Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag class="c-class" id="4">C-text\n   '>
<Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag id="5">C-text\n            <d-tag>'>
<Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag>C-text\n            <d-tag>D-text<'>

2. I percorsi possono essere RELATIVI:

>>> for s in selector.xpath('//c-tag/../..'): print s
...
<Selector xpath='//c-tag/../..' data=u'<a-tag class="a-class">A-text\n    <b-tag'>

3. La sintassi Xpath formale è la seguente:

axisname::nodetest[predicate]

link utili:
parte 2. xpath: Abbraviazioni
parte 3. xpath: Funzioni generiche
scrapy
xpath syntax

Categorie:scrapy, xpath Tag: ,

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).

django: standalone application

3 aprile 2017 Commenti chiusi

Supponiamo di dover creare una piccola applicazione (‘players’)
che inserisca in un database dei dati, ad esempio i nomi
dei calciatori della serie A.
Ci piace utilizzare django per la confidenza che abbiamo con il suo ORM,
ma non abbiamo voglia di sbatterci con tutto il meraviglioso e vastissimo
mondo di django, comprensivo di pattern Model-View-Template, Admin, browser…
Vogliamo invece usare le wx, ma non SQLAlchemy, o peewee.
Non resta che strutturare la nostra mini applicazione in questo modo:

C:\TMP\STANDALONEDJANGOEXAMPLE
|   main.py
|   manage.py
|   settings.py
|   __init__.py
|   
\---players
    |   controller.py
    |   model.py
    |   models.py
    |   view.py
    |   __init__.py
    |   
    \---migrations
            __init__.py

partendo dall’alto, analizziamo i singoli file.

‘main.py’ è il cuore della nostra applicazione django STAND-ALONE e contiene
i settaggi che ci permetteranno di utilizzare solo l’ORM di django:

import os

# qui importiamo il modulo settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")


import sys
# qui aggiungiamo il percorso della nostra app al path
sys.path.append('/tmp/standalonedjangoexample')
# qui aggiungiamo i virtualenv site-packages al sys.path
sys.path.append('/tmp/venv/Lib/site-packages')


from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()


if __name__ == '__main__':
    from players.controller import Controller
    c = Controller()

questo è il contenuto di ‘manage.py’, necessario per le migrations:

import os
import sys

sys.dont_write_bytecode = True

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

poi abbiamo i ‘settings.py’:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'players.db',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

INSTALLED_APPS = (
    'players',
    )

SECRET_KEY = 'mysecretkey'

Ora entriamo nel cuore della app, che prevede:
– i models che django utilizzerà con il suo ORM
– i file per uno spartano MVC

Questo spartano MVC prevede una file view.py che importa il modulo wx
per creare un’interfaccia di inserimento dati, un model.py per la gestione
dei dati e un controller che metta interagisca con essi tenendoli separati.

Il file models.py (django models)

from django.db import models


class Player(models.Model):
    name = models.CharField(max_length=32)

    def __unicode__(self):
        return self.name

il file view.py

import wx


class InfoMessage(wx.MessageDialog):
    """Simple message Dialog"""
    def __init__(self, parent, message):
        super(InfoMessage, self).__init__(parent, message, 'info', wx.OK |
                                          wx.ICON_EXCLAMATION)
        if self.ShowModal() == wx.ID_OK:
            self.Destroy()


class ViewNewPlayer(wx.Frame):
    def __init__(self, parent, controller, title):
        self.title = title
        super(ViewNewPlayer, self).__init__(parent=parent, title=title,)
                                         #style=STYLE)
        self.controller = controller
        self._build()
        self._bind_widgets()
        self.Centre()
        self.Show()

    def _build(self):
        self.panel = PanelNewPlayer(parent=self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel, 1, wx.EXPAND)
        self.SetSizer(sizer)
        self.SetSize((250, 100))

    def _bind_widgets(self):
        self.Bind(wx.EVT_BUTTON, self.on_quit, self.panel.btn_quit)
        self.Bind(wx.EVT_BUTTON, self.on_save, self.panel.btn_save)
        self.Bind(wx.EVT_TEXT, self.on_text_entry, self.panel.name)

    def show_message(self, message):
        InfoMessage(parent=self, message=message)

    def on_text_entry(self, event):
        entry = self.panel.name.GetValue()
        if len(entry) > 2:
            self.panel.btn_save.Enable()
        else:
            self.panel.btn_save.Disable()

    def on_save(self, event):
        name = self.panel.name.GetValue()
        self.controller.save_player(name)

    def on_quit(self, event):
        self.Destroy()


class PanelNewPlayer(wx.Panel):
    def __init__(self, parent):
        super(PanelNewPlayer, self).__init__(parent)
        # Attributes
        self.name = wx.TextCtrl(self)
        text_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=5, vgap=5)
        text_sizer.Add(wx.StaticText(self, label=''),
                       0, wx.ALIGN_CENTER_VERTICAL)
        text_sizer.AddGrowableCol(1)
        button_sizer = wx.StdDialogButtonSizer()
        self.btn_save = wx.Button(self, wx.ID_SAVE)
        self.btn_quit = wx.Button(self, wx.ID_CANCEL)
        # Buttons initialization
        self.btn_quit.SetDefault()
        self.btn_save.Disable()
        # Sizers
        button_sizer.AddButton(self.btn_save)
        button_sizer.AddButton(self.btn_quit)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(text_sizer, 1, wx.EXPAND | wx.ALL, 5)
        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        button_sizer.Realize()
        self.SetBackgroundColour('LightGray')
        self.SetSizer(sizer)

il file model.py (MVC) che si interfaccerà a sua volta con i models di django:

from models import Player


class Model(object):

    @staticmethod
    def new_player(name):
        """new_player(name) -> player object"""
        player = Player.objects.create(name=name.upper())
        return player

    @staticmethod
    def get_player(name):
        """get_player(name) -> player object"""
        return Player.objects.filter(name=name.upper()).first()

    @staticmethod
    def get_players():
        """get_players() -> iterable"""
        return Player.objects.all()

il file controller.py

import wx
from model import Model
from view import ViewNewPlayer


class Controller(object):
    def __init__(self):
        app = wx.App()
        self.model = Model()
        self.view = ViewNewPlayer(parent=None, controller=self,
                                  title='New Player')
        app.MainLoop()

    def get_player(self, name):
        return self.model.get_player(name)

    def save_player(self, name):
        player_object = self.get_player(name)
        if player_object:
            self.view.show_message("Player already exist")
        else:
            new_player = self.model.new_player(name)
            self.view.show_message("New Player %s saved" % new_player.name)

Questo è quanto.
Ovviamente non esistono ancora le tabelle del database e provvediamo con i
comandi:

(venv) C:\tmp\standalonedjangoexample>python manage.py makemigrations
Migrations for 'players':
  players\migrations\0001_initial.py:
    - Create model Player

e successivamente:

(venv) C:\tmp\standalonedjangoexample>python manage.py migrate
Operations to perform:
  Apply all migrations: players
Running migrations:
  Applying players.0001_initial... OK

poi è possibile eseguire la nostra applicazione utilizzando il file
main.py.

Ovviamente la struttura dell’applicazione può essere fatta in mille modi
diversi, come pure il raggruppamento dei codici.
Lo stesso main.py può fungere da controller riducendo i livelli di
separazione, soprattutto qualora non si volesse utilizzare un
pattern simil-MVC.

Categorie:Django, python Tag: ,

django – RuntimeWarning: DateTimeField received a naive datetime

2 febbraio 2017 Commenti chiusi

Abbiamo un form

class ChangeForm(forms.Form):
    date = forms.DateField(label='date', initial=datetime.date.today)
    ...

e la view di competenza

def edit(request, item_id):
    myitem = get_object_or_404(AItem, pk=item_id)
    if request.method == "POST":
        form = ChangeForm(request.POST)
        if form.is_valid():
            myitem.date = form.cleaned_data['date']
            myitem.save()
            return redirect(...)
    else:
        form = ChangeForm(initial={'date': myitem.date})
    return render(request, 'atemplate.html',
                  {'form': form, 'myitem': myitem})

nel momento del salvataggio dei dati (POST) ottengo il warning in oggetto.

per risolvere il problema, tramite timezone di django, trasformiamo le date
da formato ‘naive’ a formato ‘aware’.
Nel mio caso la data è di tipo datetime.date per tento prima,
la trasformerò in formato datetime.datetime.

>>> import datetime
>>> from django.utils import timezone
>>> a_date = datetime.date.today()
>>> a_datetime = datetime.datetime.combine(a_date, datetime.time())
>>> a_datetime
datetime.datetime(2017, 2, 2, 0, 0)
>>> aware_date = timezone.make_aware(a_datetime, timezone.get_default_timezone())
>>> aware_date
datetime.datetime(2017, 2, 2, 0, 0, tzinfo=<UTC>)

oppure tramite il modulo pytz (installabile tramite pip):

>>> import pytz
>>> naive = datetime.datetime(2017, 2, 2, 12, 0, 0)
>>> aware = datetime.datetime(2017, 2, 2, 12, 0, 0, 0, pytz.UTC)
>>> aware == naive
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> aware == pytz.utc.localize(naive)
True

ora basta modificare la view con la gestione corretta delle date:

def edit(request, item_id):
    myitem = get_object_or_404(AItem, pk=item_id)
    if request.method == "POST":
        form = ChangeForm(request.POST)
        if form.is_valid():
            naive_date = form.cleaned_data['date']
            naive_datetime = datetime.datetime.combine(naive_date, datetime.time())
            myitem.date = timezone.make_aware(naive_datetime, 
                                              timezone.get_default_timezone())
            myitem.save()
            return redirect(...)
    else:
        form = ChangeForm(initial={'date': myitem.date})
    return render(request, 'atemplate.html',
                  {'form': form, 'myitem': myitem})
Categorie:Django, python Tag: ,

Ubuntu 16.10: libgstreamer-0.10.so.0: cannot open shared object file: No such file

25 gennaio 2017 Commenti chiusi

install missing 64bit deb packages:

$ wget http://ftp.ca.debian.org/debian/pool/main/g/gstreamer0.10/libgstreamer0.10-0_0.10.36-1.5_amd64.deb
$ wget http://ftp.ca.debian.org/debian/pool/main/g/gst-plugins-base0.10/libgstreamer-plugins-base0.10-0_0.10.36-2_amd64.deb

or 32bit ones:

$ wget http://ftp.ca.debian.org/debian/pool/main/g/gstreamer0.10/libgstreamer0.10-0_0.10.36-1.5_i386.deb
$ wget http://ftp.ca.debian.org/debian/pool/main/g/gst-plugins-base0.10/libgstreamer-plugins-base0.10-0_0.10.36-2_i386.deb

install deb packages

sudo dpkg -i libgstreamer*.deb
Categorie:Linux, Ubuntu Tag:

Django: in_memory_zip di più in_memory_files

22 novembre 2016 Commenti chiusi

Il concetto base è di non creare file su disco,
ma utilizzare file in_memory.
Dovranno essere “volanti” quindi, sia i file
da zippare, sia il file zip stesso, che verrà
in seguito servito da django.

Semplifichiamo il tutto:

ho una funzione che genera lo zip:

from zipfile import ZipFile
from StringIO import StringIO


def zip_files():
    zip_sio = StringIO()
    zip_file = ZipFile(zip_sio, 'w')

    for line in ['a', 'b', 'c', 'd']:
        name = 'file_{}.txt'.format(line)
        outfile = StringIO()
        outfile.write(line)
        zip_file.writestr(name, outfile.getvalue())
        outfile.close()

    zip_file.close()
    zip_sio_data = zip_sio.getvalue()
    zip_sio.close()
    return zip_file, zip_sio_data

chiaramente potrebbe essere più complesso, ad esempio una
funzione che prenda in ingresso un file, lo splitti in
più parti le quali vengano restituite sotto forma di zip…

from zipfile import ZipFile
from StringIO import StringIO


def zip_files(file_in):
    for line in file_in.readlines():
        ...

In pratica utilizziamo degli oggetti StringIO, ovvero oggetti File-like,
all’interno dei quali possiamo scrivere delle stringhe, con il metodo ‘write’,
proprio come gli oggetti file.
Con il metodo ‘getvalue’, otteniamo tutto il contenuto dell’oggetto, che una
volta chiuso con ‘close’, non è più accessibile. Per questo motivo lo inseriamo
all’interno del file-like object zip prima di chiuderlo.
Gli stessi dati del file zip, li memorizziamo prima di chiudere lo stesso flie-like
zip, in modo da poterli avere disponibili ad esempio nella view di django
prima di costruire il ‘response’…

comunque nel nostro esempio avremo:

>>> z, data = zip_files()
>>> [o.filename for o in z.filelist]
['file_a.txt', 'file_b.txt', 'file_c.txt', 'file_d.txt']
>>> len(data)
410

Come già detto, ‘data’ tornerà utile quando con django andremo a servire il file zip
per il download, ad es.:

...
def a_view(request):
    if request.method == "POST":
        form = a_form(request.POST, request.FILES)
        if form.is_valid():
            file_in = request.FILES['a_form_file_field']
            zip_file, data = zip_file()  # or zip_file(file_in) if you process a file
            response = HttpResponse(data,
                                    content_type="application/x-zip-compressed")
            response['Content-Disposition'] = 'attachment; filename=a_file_zip.zip'
            return response
    else:
        form = a_form()
    return render(request, 'a_template.html', {'form': form})

Con questo sistema, non scriveremo nessun file sul server.

Categorie:Django, python Tag: ,

djangofantalega 1.0

15 novembre 2016 Commenti chiusi

Djangofantalega

Djangofantalega è un progetto realizzato con
python e django.

Si tratta di un gestore di fantalega, dalla A alla Z.
Si creano: stagione, lega, squadre.
Si effettua l’asta sui giocatori e composte le squadre,
si crea il calendario e si dà inizio al campionato.
Ad ogni giornata si consegnano le formazioni e, dopo
la pubblicazione dei voti ufficiali, si procede
con i conteggi.

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
8 – Asta
9 – Models: Lineup
10 – Models: Trade
11 – Asta di riparazione
12 – Classifica

Categorie:Django Tag:

djangofantalega: Classifica

15 novembre 2016 Commenti chiusi

12 – Classifica

Per visualizzare la classifica delle squadre della lega,
Colleghiamo il pulsante già presente nella template league.html
alla view league_details:

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

inserire la url:

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

Aggiungere nel file fantalega\views.py la nuove vista:

...

@login_required
def chart(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_teams'):
        return redirect('league_details', league.id)
    lineups_values = []
    for team in league_teams:
        lineups = [lineup for lineup in
                   Lineup.objects.filter(league=league, team=team).all()
                   if lineup.pts]
        won = sum([lineup.won for lineup in lineups])
        matched = sum([lineup.matched for lineup in lineups])
        lost = sum([lineup.lost for lineup in lineups])
        gm = sum([lineup.goals_made for lineup in lineups])
        gc = sum([lineup.goals_conceded for lineup in lineups])
        tot_pts = sum([lineup.pts for lineup in lineups])
        tot = won * 3 + matched
        lineups_values.append((team, tot, won, matched, lost, gm, gc, tot_pts))
    lineups_values.sort(key=lambda x: (x[1], x[6]), reverse=True)
    context = {'league': league, 'lineups_values': lineups_values}
    return render(request, 'fantalega/chart.html', context)

la template
fantalega/templates/fantalega/chart.html

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

{% block content %}
	<h1><font color="green">{{ league.name }}</font></h1>
    <form action="#" method="get">
    <input type="submit" class="btn"
           value="back to {{ league.name }} teams" name="back_to_teams">
    </form>

    <table class="table table-striped" width="100%">
      <tr>
          <th>team</th>
          <th>pts</th>
          <th>won</th>
          <th>matched</th>
          <th>lost</th>
          <th>goals made</th>
          <th>goals conceded</th>
          <th>total pts</th>
      </tr>
      {% for values in lineups_values %}
        <tr>
              <td>{{ values.0.name }}</td>
              <td>{{ values.1 }}</td>
              <td>{{ values.2 }}</td>
              <td>{{ values.3 }}</td>
              <td>{{ values.4 }}</td>
              <td>{{ values.5 }}</td>
              <td>{{ values.6 }}</td>
              <td>{{ values.7 }}</td>
      </tr>
      {% endfor %}
    </table>

{% endblock %}

Per visualizzare la classifica basterà premere il pulsante ‘view chart’

Salvare su github:

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

articoli precedenti
0 – indice
1 – Virtualenv e Git
2 – Models: Season
3 – Admin: Login e Logout
4 – Models: League
5 – Models: Team
6 – Models: Match
7 – Models: Player
8 – Asta
9 – Models: Lineup
10 – Models: Trade
11 – Asta di riparazione

Categorie:Django Tag:

djangofantalega: Asta di riparazione

15 novembre 2016 Commenti chiusi

11 – Asta di riparazione

Il 31 Gennaio scade il vero mercato di riparazione. Da lì in avanti
si organizza l’asta di riparazione.
Una squadra deve avere la possibilità di vendere un giocatore ed incrementare
il suo budget.

Nella template di ogni singola squadra è presente un pulsante ‘sell players for repair session’
Colleghiamo il pulsante alla view team_details:

...
@login_required
def team_details(request, league_id, team_id):
    ...
    if request.GET.get('sale'):
        return redirect('sale', league.id, team.id)
    return render(request, 'fantalega/team.html', context)

inseriamo la url:

...
    # player sale url
    url(r'^leagues/(?P<league_id>[0-9]+)/teams/'
        r'(?P<team_id>[0-9]+)/player_sale$', views.sale, name='sale'),
]

Aggiungere nel file fantalega\views.py la nuove vista:

...
@login_required
def sale(request, league_id, team_id):
    league = get_object_or_404(League, pk=int(league_id))
    team = get_object_or_404(Team, pk=int(team_id))
    team_players = [(p.code, "%s [%s]" % (p.name, p.role))
                    for p in team.player_set.all()]
    form = TeamSellPlayersForm(request.POST,
                               initial={'team_players': team_players,
                                        'team': team, 'league': league})
    if request.GET.get('back_to_team_details'):
        return redirect('team_details', league.id, team.id)
    if request.method == "POST":
        if form.is_valid():
            players_to_sell = [Player.get_by_code(int(code),
                                                  season=league.season)
                               for code in form.cleaned_data['team_players']]

            gain = 0
            for player in players_to_sell:
                team.player_set.remove(player)
                gain += player.cost
                team.budget += player.cost
                team.save()
            messages.success(request, "Players sold correctly! You gain: %s" %
                             gain)
            return redirect('team_details', league.id, team.id)
    return render(request, 'fantalega/sell.html',
                  {'form': form, 'team_players': team_players, 'team': team,
                   'league': league})

il form che permetterà la vendita di un giocatore sarà:

...
class TeamSellPlayersForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.dict_values = kwargs.pop('initial')
        super(TeamSellPlayersForm, self).__init__(*args, **kwargs)
        self.fields['team_players'] = forms.MultipleChoiceField(
            choices=self.dict_values['team_players'],
            widget=forms.CheckboxSelectMultiple())

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

from .forms import TeamSellPlayersForm
...

Ora la template:

templates/sell.html

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

{% block content %}
    <b>Upload lineup for <font color="green">{{ team.name }}</font></b>
    <br><br>
    <form action="#" method="get">
    <input type="submit" class="btn"
           value="back to {{ team.name }} team" name="back_to_team_details">
    </form>

    <form method="POST" class="form">
      {% csrf_token %}
      {% bootstrap_field form.team_players %}
        {% buttons %}
            <button type="submit" class="btn btn-primary">
                {% bootstrap_icon "save" %}Sell players</button>
        {% endbuttons %}
    </form>
{% endblock %}

Per riacquistare un giocatore è possibile utilizzare il form utilizzato
per la prima Asta.

Salvare ora gli avanzamenti su github:

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

articoli precedenti
0 – indice
1 – Virtualenv e Git
2 – Models: Season
3 – Admin: Login e Logout
4 – Models: League
5 – Models: Team
6 – Models: Match
7 – Models: Player
8 – Asta
9 – Models: Lineup
10 – Models: Trade

articoli successivi
12 – Classifica

Categorie:Django Tag: