Archivio

Posts Tagged ‘python’

scrapy: spider

12 ottobre 2017 Nessun commento

Articolo precedente: primi passi con scrapy

Spiders

Gli Spiders sono classi che definiscono come un sito o gruppi di siti, debbano essere analizzati.
In queste classi si decide come sarà il crawling/parsing del sito o dei gruppi di siti e come
saranno strutturati i dati estratti da tali pagine web.

Il progetto è stato già creato precedentemente, si tratta ora di creare lo spider adatto al nostro scopo.
Esiste un direttorio nome-progetto/nome-progetto//spiders all’interno del quale creeremo un file nome-progetto_spider.py nel mio caso, nel direttorio ansa/ansa/spiders, creerò un file ‘ansa_spider.py‘ con all’interno il codice necessario per lo scraping/crawling del sito in oggetto.

Vediamo il ciclo di funzionamento dello spider.

1. Requests iniziali

Generazione delle Requests iniziali per “raschiare” (crawl) le prime URLs e
specifica della funzione di callback da invocare con il Response ottenuto da queste prime Requests.
Le prime requests da eseguire sono ottenute con la chiamata del metodo start_requests()
che di default genera Request per le URLs specificate in una lista, chiamando per ognuna di esse
la corrispondente funzione di callback parse().

import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        urls = ["http://www.ansa.it",]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        pass

“name” è il nome univoco con il quale scrapy troverà ed utilizzerà lo spider.

“allowed_domains” è la lista dei domini autorizzati ad essere analizzati dallo spider.
Se il middleware OffsiteMiddleware sarà abilitato, i domini non presenti in questa lista,
non verranno analizzati dallo spider.

“start_requests” è il metodo che ritorna la lista con le prime Requests da analizzare. Viene invocato da Scrapy
quando apre lo spider per iniziare l’analisi.

Callback parse()

Nella funzione di callback parse(), eseguiamo appunto il parsing del response (web page) e ritorniamo:
– dizionario con i dati estratti
– Item object
– Request objects, o un iterable di tali oggetti

Con sintassi xpath, il primo abbozzo di spider sarebbe:

# ansa_spider.py
import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        xf = '//article[@class="news"]/descendant::a/@href'
        for url in response.xpath(xf).extract():
             yield {"title": url}

mentre la stessa con il metodo css():

# ansa_spider.py
import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        for url in response.css('article.news a::attr(href)').extract():
             yield {"title": url}

Lanciamo lo spider e viediamo che succede:

scrapy crawl ansa

come si nota, lo spider estrapola tutti i link presenti nella web page, ritornando un dizionario per ogni
link trovato. Se vogliamo generare un file con questi dati:

scrapy crawl ansa -o ansa.json

Nota:

Al momento scrapy esegue solo l’append dei dati e non sovrascrive il file, se già esistente.
Ricordarsi di eliminarlo prima di procedere con l’estrazione dei dati!

volendo estrarre i dati dal file, entriamo nella shell:

scrapy shell
import json


with open('ansa.json') as f:
    data = json.loads(f.read())

d = {n: v.get('link') for n, v in enumerate(data, 1) if v.get('link') != "javascript:void(0);"}

Tutte queste Requests potranno essere parsate e scaricate a loro volta da scrapy, facendo riferimento ad
una ulteriore callback (anche la stessa parse() volendo).

import scrapy


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        xf = '//article[@class="news"]/descendant::a/@href'
        for url in response.xpath(xf).extract():
            if url and url != "javascript:void(0);":
                yield response.follow(url, callback=self.parse_news)

    def parse_news(self, response):
        title = response.xpath('//title/text()').extract_first()
        x_body = '//div[@class="news-txt"]/descendant::p'
        body = ' '.join(([node.xpath('string()').extract()[0]
                          for node in response.xpath(x_body)]))
        yield {'title': title,
               'body': body,
               }

Ora possiamo accedere ad un dizionario che al posto dei singoli link, ha i testi degli articoli.
Ma se volessimo portarci dietro dal primo parse, all’interno delle pagine figlie qualche attributo tipo ad es.
il link alla pagina figlia? Questo ha senso ovviamento per parametri che non sono reperibili nelle pagine figlie,
ma che debbano essere “trasportati” da un parse all’altro.

In questo caso ci vengono in aiuto gli items.

Dentro al file ansa/items.py definiamo il nostro personale Item:

import scrapy


class AnsaItem(scrapy.Item):
    # define the fields for your item here like:
    link = scrapy.Field()

Con l’ausilio di quest’ultimo, possiamo modificare il codice dello spider come segue:

import scrapy
from ansa.items import AnsaItem
from scrapy.exceptions import NotSupported


class AnsaSpider(scrapy.Spider):
    name = "ansa"
    allowed_domains = ["ansa.it"]

    def start_requests(self):
        start_urls = ["http://www.ansa.it",]
        for url in start_urls:
            print '[INFO] Parsing url %s...' % url
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        xf = '//article[@class="news"]/descendant::a[position()=1]/@href'
        for url in response.xpath(xf).extract():
            if url and url != "javascript:void(0);":
                short_url = '/.../%s' % url.split('/')[-1]
                print '[INFO] Parsing urls %s = %s' % (n, short_url)
                ansa_item = AnsaItem()
                # ansa_item assignments
                ansa_item['link'] = url
                yield response.follow(url, callback=self.parse_news,
                                      meta={'ansa_item': ansa_item})

    def parse_news(self, response):
        ansa_item = response.meta.get('ansa_item')
        link = ansa_item.get('link')
        try:
            title = response.xpath('//title/text()').extract_first()
            x_body = '//div[@class="news-txt"]/descendant::p'
            body = ' '.join(([node.xpath('string()').extract()[0]
                              for node in response.xpath(x_body)]))
            yield {'link': link.strip(),
                   'title': title.strip(),
                   'body': body.strip(),
                  }
        except NotSupported:
            print "[WARNING] Response %s not crawled" % response

Nell’Item, gli assegnamenti agli attributi e l’accesso ad essi si comportano
allo stesso modo dei dizionari.
Come si vede, nella prima callback parse(), viene creato l’item e gli si assegna alla chiave ‘link’ il
valore di url. Quando utilizzeremo il metodo follow dell’oggetto response, per analizzare le sottopagine,
richiameremo la callback parse_news() e passeremo al metodo follow(),
l’attributo meta, un dizionario con all’interno il nostro item.
Questo item, viene recuperato all’interno della callback parse_news() ed il valore della sua
chiave ‘link’, ritornato nel dizionario con yield, insieme agli altri valori.

Eliminiamo un eventuale file json preesistente e ricreiamo il tutto:


>>> import json
>>> with open('ansa.json') as f:
...     data = json.loads(f.read())
...
>>> news_dict = {n: value for n, value in enumerate(data, 1)}
>>> news_1 = news_dict.get(1)
>>> news_1.get('title')
u"Ultimatum..."
>>> news_1.get('link')
u'/sito/notizie/mondo/europa/...'

link utili:
scrapy: appunti
scrapy
xpath: appunti

Categorie:python, scrapy, xpath Tag: , ,

scrapy: appunti

12 ottobre 2017 Nessun commento

Installazione

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

Ora per vedere che tutto funzioni, è sufficiente eseguire il comando:

scrapy shell
...
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x04108D90>
[s]   item       {}
[s]   settings   <scrapy.settings.Settings object at 0x04108E90>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser

per uscire:

>>>exit()

Progetto

Creiamo il primo progetto, ad esempio un estrattore di ultime notizie dell’ANSA:

scrapy startproject ansa

prima però di scrivere codice, prendiamo confidenza con i principali comandi.
Lanciamo la shell di scrapy dandole in pasto la pagina web che ci interessa:

scrapy shell "www.ANSA.it"
...
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x04035DB0>
[s]   item       {}
[s]   request    <GET http://www.ANSA.it>
[s]   response   <200 http://www.ANSA.it>
[s]   settings   <scrapy.settings.Settings object at 0x04035EF0>
[s]   spider     <DefaultSpider 'default' at 0x4302fb0>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
...

come si nota ci vengono messi a disposizione alcuni oggetti, tra i quali ‘response’, sul quale è
possibile effettuare query di ogni tipo. Si possono usare sia il metodo css() che il metodo xpath().
Ad es. voglio ottenere il titolo della pagina:

>>> response.css('title')
[<Selector xpath=u'descendant-or-self::title' data=u'<title>ANSA.it</title>'>]
>>> response.xpath('//title')
[<Selector xpath='//title' data=u'<title>ANSA.it</title>'>]

Nel caso di utilizzo del metodo css() è previsto l’utilizzo di una sintassi dedicata.
Il metodo css() comunque ritornerà sempre un oggetto selector che si rifarà alla sintassi
di Xpath:

>>> response.css('title')
[<Selector xpath=u'descendant-or-self::title' data=u'<title>ANSA.it</title>'>]
>>> response.css('title::text')
[<Selector xpath=u'descendant-or-self::title/text()' data=u'ANSA.it'>]
>>> response.css('title::text').extract_first()
u'ANSA.it'

Selector

Un selector altro non è che un’istanza della classe Selector, alla quale viene passata una stringa
o un oggetto TextResponse e, a seconda del dato in ingresso sceglie il tipo di parsing
(XML o HTML) da usare:

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

Xpath è il linguaggio che permette di selezionare i nodi in un documento XML e può essere utilizzato
anche per l’HTML.
come si nota, si passa una stringa html al selector e con il metodo extract(),
si ottiene una lista di testi che rispondono ai nostri criteri, o con extract_first()
si ottiene il primo della lista.
Attenzione ai path:
nel primo caso :’//bar’ troviamo il tag ed il testo contenuto da esso;
nel secondo caso: ‘//bar/text()’ troviamo solo il testo contenuto da quel tag!
Stesso principio per gli oggetti TextResponse:

>>> from scrapy.http import HtmlResponse
>>> resp = HtmlResponse(url='', body=body)
>>> Selector(response=resp).xpath('//bar[text()]').extract_first()
u'<bar>Some text</bar>'

>>> Selector(response=resp).xpath(‘//bar/text()’).extract_first()
u’Some text’

Xpath

Rispetto all’utilizzo dei Selector CSS, le espressioni Xpath sono più potenti
perchè oltre a navigare attraverso la struttura della pagina web, permettono di
osservare i contenuti dei nodi.
Qui è disponibile una serie di appunti su Xpath.

>>> response.xpath('//title[text()]').extract()
[u'<title>ANSA.it</title>']

Andiamo al sodo:
vogliamo ottenere una lista di tutti i selector che contengono articoli?
Per come è strutturata la pagina che stiamo analizzando, gli articoli sono racchiusi all’interno di tag “article”,
quindi:

>>> response.css('article').extract()
...
>>> response.xpath('//article').extract()
...

ovviamente per ottenere il primo della lista, basterebbe chiamare il metodo extract_first():

>>> response.css('article').extract_first()
...

Vediamo come ottenere i sunti delle singole notizie. Queste sono identificabili ad esempio dal tag

.
Quindi vogliamo filtrate tutti i tag

con attributo class=”pp-abs”:

>>> response.css('p.pp-abs').extract()
...

dove ‘p.pp-abs’ sta appunto per

Con la sintassi xpath (qui una breve guida sulla sintassi) sarebbe:

>>> response.xpath('//p[@class="pp-abs"]').extract()
...

Se volessimo solo i testi e non i tag contenitori:

>>> response.css('p.pp-abs::text').extract()
...
>>> response.xpath('//p[@class="pp-abs"]/text()').extract()
...

Oltre al sunto delle notizie, vogliamo ottenere i link delle stesse?
Per come è strutturata la pagina web (‘analizza sorgente pagina’ da browser…) potrei fare qualcosa del genere:

for r in response.css('article.news'):
    p = r.css('p::text').extract_first()
    href = r.css('a::attr(href)').extract_first()
    if p and href != "javascript:void(0);":
        print "%s\nlink: %s" % (p, href)

La condizione if è dovuta al fatto che non sempre all’interno del tag

è disponibile un un tag

e
quindi il metodo extract_first() ritornerebbe ‘None’ e inoltre molti href punterebbero al valore
“javascript:void(0);” che non vogliamo.

Con xpath invece:

for r in response.xpath('//article[@class="news"]'):
    p = r.xpath('descendant::p/text()').extract_first()
    href = r.xpath('descendant::a/@href').extract_first()
    if p and href != "javascript:void(0);":
        print "%s\nlink: %s" % (p, href)

Vediamo adesso come fare per memorizzare tutti i link delle notizie in anteprima ed aprirle tutte,
per memorizzarne il testo completo. La cosa si complica un po’, ma non troppo.
Per questo tipo di cosa utilizzeremo gli spiders.

link utili:
scrapy: spider
scrapy
xpath: appunti

Categorie:python, 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: ,

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: ,

Project Euler 87

8 settembre 2016 Commenti chiusi
The smallest number expressible as the sum of a prime square, prime cube, and prime fourth power is 28. In fact, there are exactly four numbers below fifty that can be expressed in such a way:

28 = 2^2 + 2^3 + 2^4
33 = 3^2 + 2^3 + 2^4
49 = 5^2 + 2^3 + 2^4
47 = 2^2 + 3^3 + 2^4

How many numbers below fifty million can be expressed as the sum of a prime square, prime cube, and prime fourth power?
import time


def is_prime(num):
    if num == 2:
        return True
    if num % 2 == 0 or num <= 1:
        return False
    sqr = int(num ** 0.5) + 1
    for divisor in xrange(3, sqr, 2):
        if num % divisor == 0:
            return False
    return True

def euler87(limit):
    """a**2 + b**3 + c**4"""
    results = set()
    primes = [x for x in xrange(2, int(limit ** 0.5))
              if is_prime(x)]
    for p4 in primes:
        for p3 in primes:
            partial = p4**4 + p3 ** 3
            if partial >= limit:
                break
            for p2 in primes:
                tot = p2 ** 2 + partial
                if tot >= limit:
                    break
                results.add(tot)
    return len(results)


if __name__ == "__main__":
    start_time = time.time()
    print "euler 87: {} \nelapsed time: {}s".format(
        euler87(50000000), time.time() - start_time)
Categorie:Project Euler, python Tag: ,

project euler 89

27 luglio 2016 Commenti chiusi

Qui il testo del problema 89

Si deve calcolare il totale dei caratteri risparmiati durante la semplificazione dei numeri romani.
Ci ho messo un po’ a capirlo e continuavo a calcolare il totale dei caratteri della lista corretta….

import time


def filter_romans(path):
    with open(path) as inputfile:
        romans = [num.strip() for num in inputfile]
    startchr = sum([len(r) for r in romans])

    for bad, correct in [("VIIII", "IX"), ("IIII", "IV"),
                         ("LXXXX", "XC"), ("XXXX", "XL"),
                         ("DCCCC", "CM"), ("CCCC", "CD"),]:
        for roman in romans:
            index = romans.index(roman)
            if bad in roman:
                romans[index] = roman.replace(bad, correct)
    return startchr - sum([len(r) for r in romans])


if __name__ == "__main__":
    start = time.time()
    result = filter_romans(r"p089_roman.txt")
    print "euler 89: %s\nElapsed time %ss." % (result, time.time() - start)
Categorie:Project Euler, python Tag: ,

Django: unknown command validate

1 giugno 2016 Commenti chiusi

per validare i models prima si utilizzava il comando:

python manage.py validate

nella versione attuale di django (1.9.x) questo solleva un errore:

(venv) C:\...\bancaldo\mysite>python manage.py validate
Unknown command: 'validate'
Type 'manage.py help' for usage.

il comando da utilizzare è “check”:

(venv) C:\...\bancaldo\mysite>python manage.py check
System check identified no issues (0 silenced).
Categorie:Django, python Tag: ,