

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Fase 2: esame del modello di dati e dei dettagli di implementazione
<a name="TicTacToe.Phase2"></a>

**Topics**
+ [2.1: Modello di dati di base](#TicTacToe.Phase2.DataModel)
+ [2.2: Funzionamento dell'applicazione (procedura guidata per il codice)](#TicTacToe.Phase2.AppInAction)

## 2.1: Modello di dati di base
<a name="TicTacToe.Phase2.DataModel"></a>

Questa applicazione di esempio mette in evidenza i concetti seguenti relativi al modello di dati di DynamoDB:
+ ****Tabella****: in DynamoDB, una tabella è una raccolta di elementi (ovvero record) e ogni elemento è una raccolta di coppie nome-valore denominate attributi.

  In questo Tic-Tac-Toe esempio, l'applicazione memorizza tutti i dati di gioco in una tabella,. `Games` L'applicazione crea una item nella tabella per ogni partita e archivia tutti i dati di gioco come attributi. Una tic-tac-toe partita può avere fino a nove mosse. Poiché le tabelle DynamoDB non hanno uno schema nei casi in cui l'attributo richiesto è solo la chiave primaria, l'applicazione può archiviare un numero variabile di attributi per elemento di gioco.

  La tabella `Games` ha una chiave primaria semplice costituita da un attributo, `GameId`, di tipo stringa. L'applicazione assegna un ID univoco a ogni partita. Per ulteriori informazioni sulle chiavi primarie di DynamoDB, vedi [Chiave primaria](HowItWorks.CoreComponents.md#HowItWorks.CoreComponents.PrimaryKey). 

  Quando un utente inizia una tic-tac-toe partita invitando un altro utente a giocare, l'applicazione crea un nuovo elemento nella `Games` tabella con attributi che memorizzano i metadati del gioco, come i seguenti:
  + `HostId`, l'utente che ha avviato la partita.
  + `Opponent`, l'utente che è stato invitato a giocare.
  + Utente che deve giocare il turno successivo. L'utente che avvia la partita gioca per primo.
  + Utente che usa il simbolo **O** sulla griglia di gioco. L'utente che avvia la partita usa il simbolo **O**.

  L'applicazione crea anche un attributo concatenato `StatusDate`, contrassegnando lo stato iniziale della partita come `PENDING`. Lo screenshot seguente mostra un elemento di esempio come appare nella console DynamoDB:  
![Schermata della console della tabella degli attributi.](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/tic-tac-toe-10.png)

  Man mano che la partita procede, l'applicazione aggiunge un attributo alla tabella per ogni mossa nel gioco. Il nome dell'attributo è costituito dalla posizione sulla griglia, ad esempio `TopLeft` o `BottomRight`. Una mossa, ad esempio, può avere un attributo `TopLeft` con il valore `O`, un attributo `TopRight` con il valore `O` e un attributo `BottomRight` con il valore `X`. Il valore dell'attributo è `O` o `X`, a seconda dell'utente che ha fatto la mossa. Considera, ad esempio, la griglia seguente.  
![Schermata che mostra una tic-tac-toe partita finita che si è conclusa in parità.](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/tic-tac-toe-30.png)
+ ****Attributi dei valori concatenati****: l'attributo `StatusDate`illustra un attributo valore concatenato. In base a questo approccio, anziché creare attributi separati per archiviare lo stato della partita (`PENDING`, `IN_PROGRESS` e `FINISHED`) e la data (dell'ultima mossa), i valori vengono combinati in un unico attributo, ad esempio `IN_PROGRESS_2014-04-30 10:20:32`.

  L'applicazione usa quindi l'attributo `StatusDate` per la creazione di indici secondari specificando `StatusDate` come chiave di ordinamento per l'indice. Il vantaggio dell'uso dell'attributo con valori concatenati `StatusDate` è spiegato ulteriormente negli indici illustrati più avanti.
+ ****Indici secondari globali****: è possibile utilizzare la chiave primaria della tabella, `GameId`, per interrogare in modo efficiente la tabella per trovare un oggetto di gioco. Per eseguire query sulla tabella in base ad attributi diversi da quelli della chiave primaria, DynamoDB supporta la creazione di indici secondari. In questa applicazione di esempio vengono creati i due indici secondari seguenti:   
![Schermata che mostra hostStatusDate gli indici secondari oppStatusDate globali creati nell'applicazione di esempio.](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/tic-tac-toe-indexes-10.png)
  + **HostId- -indice. StatusDate** Questo indice ha `HostId` come chiave di partizione e `StatusDate` come chiave di ordinamento. Puoi usare l'indice per eseguire query su `HostId`, ad esempio per trovare le partite ospitate da un utente specifico. 
  + **OpponentId- StatusDate -indice**. Questo indice ha `OpponentId` come chiave di partizione e `StatusDate` come chiave di ordinamento. Puoi usare l'indice per eseguire query su `Opponent`, ad esempio per trovare le partite in cui un utente specifico è l'avversario.

  Questi indici vengono detti indici secondari globali perché la loro chiave di partizione non corrisponde alla chiave di partizione (`GameId`) usata nella chiave primaria della tabella. 

  Entrambi gli indici hanno `StatusDate` come chiave di ordinamento. Ciò permette quanto segue:
  + Puoi eseguire query usando l'operatore di confronto `BEGINS_WITH`. Puoi ad esempio trovare tutte le partite con l'attributo `IN_PROGRESS` ospitate da un utente specifico. In questo caso, l'operatore `BEGINS_WITH` cerca il valore `StatusDate` che inizia con `IN_PROGRESS`.
  + DynamoDB archivia gli elementi nell'indice ordinandoli in base al valore della chiave di ordinamento. Se pertanto tutti i prefissi relativi allo stato sono uguali (ad esempio, `IN_PROGRESS`), il formato ISO usato per la parte relativa alla data farà in modo che gli elementi vengano ordinati dal meno recente al più recente. Questo approccio permette di eseguire in modo efficace determinate query, come le seguenti: 
    + Recupera fino a 10 partite `IN_PROGRESS` più recenti ospitate dall'utente che ha eseguito l'accesso. Per questa query, specifica l'indice `HostId-StatusDate-index`.
    + Recupera fino a 10 partite `IN_PROGRESS` più recenti in cui l'utente che ha eseguito l'accesso è l'avversario. Per questa query, specifica l'indice `OpponentId-StatusDate-index`.

Per ulteriori informazioni sugli indici secondari, consulta [Miglioramento dell’accesso ai dati con gli indici secondari in DynamoDB](SecondaryIndexes.md).

## 2.2: Funzionamento dell'applicazione (procedura guidata per il codice)
<a name="TicTacToe.Phase2.AppInAction"></a>

Questa applicazione ha due pagine principali:
+ ****Pagina iniziale****: questa pagina fornisce all'utente un semplice accesso, un pulsante **CREA per creare** una nuova tic-tac-toe partita, un elenco delle partite in corso, la cronologia delle partite e tutti gli inviti a giocare attivi in sospeso. 

  La home page non si aggiorna automaticamente, ma deve essere aggiornata manualmente per aggiornare gli elenchi.
+ ****Pagina del gioco****: questa pagina mostra la tic-tac-toe griglia in cui giocano gli utenti. 

  L'applicazione aggiorna la pagina del gioco automaticamente ogni secondo. JavaScript Nel tuo browser richiama il server web Python ogni secondo per chiedere alla tabella Giochi se gli elementi del gioco nella tabella sono cambiati. In caso affermativo, JavaScript attiva un aggiornamento della pagina in modo che l'utente veda la bacheca aggiornata.

Analizziamo nel dettaglio il funzionamento dell'applicazione.

### Home page
<a name="TicTacToe.Phase2.AppInAction.HomePage"></a>

Dopo l'accesso dell'utente, l'applicazione visualizza i tre elenchi di informazioni seguenti.

![Screenshot che mostra la home page dell'applicazione con 3 elenchi: inviti in sospeso, partite in corso e cronologia recente.](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/tic-tac-toe-homepage-10.png)

+ ****Inviti****: in questo elenco vengono visualizzati fino a 10 inviti più recenti da altri che sono in attesa di accettazione da parte dell'utente che ha eseguito l'accesso. Nello screenshot precedente l'utente user1 ha inviti in attesa da user5 e user2.
+ ****Giochi in corso****: questo elenco mostra fino ai 10 giochi più recenti in corso. Si tratta delle partite a cui l'utente sta giocando, che hanno stato `IN_PROGRESS`. Nello screenshot, l'utente1 sta giocando attivamente con user3 e tic-tac-toe user4.
+ ****Cronologia recente****: questo elenco mostra fino ai 10 giochi più recenti che l'utente ha terminato, che hanno lo stato `FINISHED`. Nello screenshot user1 ha giocato in precedenza con user2. Per ogni partita completata viene indicato il risultato.

Nel codice la funzione `index` (in `application.py`) esegue le tre chiamate seguenti per recuperare informazioni sullo stato della partita:

```
inviteGames     = controller.getGameInvites(session["username"])
inProgressGames = controller.getGamesWithStatus(session["username"], "IN_PROGRESS")
finishedGames   = controller.getGamesWithStatus(session["username"], "FINISHED")
```

Ognuna di queste chiamate restituisce un elenco di elementi di DynamoDB inclusi in oggetti `Game`. È possibile estrarre i dati da questi oggetti nella vista in modo semplice. La funzione index passa gli elenchi di oggetti alla vista per il rendering in formato HTML.

```
return render_template("index.html",
                       user=session["username"],
                       invites=inviteGames,
                       inprogress=inProgressGames,
                       finished=finishedGames)
```

L' Tic-Tac-Toeapplicazione definisce la `Game` classe principalmente per archiviare i dati di gioco recuperati da DynamoDB. Queste funzioni restituiscono elenchi di oggetti `Game` che permettono di isolare il resto dell'applicazione dal codice correlato agli elementi Amazon DynamoDB. Queste funzioni aiutano quindi a separare il codice dell'applicazione dai dettagli del livello datastore. 

Il pattern architettonico qui descritto viene anche chiamato pattern di interfaccia utente model-view-controller (MVC). In questo caso, le istanze dell'oggetto `Game` (che rappresentano i dati) sono il modello e la pagina HTML è la vista. Il controller è diviso in due file. Il file `application.py` ha la logica del controller per il framework Flask e la logica di business è isolata nel file `gameController.py`. Ciò significa che l'applicazione archivia tutti i dati correlati all'SDK DynamoDB in un file distinto nella cartella `dynamodb`.

Esaminiamo le tre funzioni e come eseguono query sulla tabella Games usando gli indici secondari globali per recuperare i dati rilevanti.

#### Si usa getGameInvites per ottenere l'elenco degli inviti di gioco in sospeso
<a name="TicTacToe.Phase2.GameInAction.ListInvitations"></a>

La funzione `getGameInvites` restituisce l'elenco dei 10 inviti in attesa più recenti. Queste partite sono state create dagli utenti, ma gli avversari non hanno accettato l'invito. Per queste partite, lo stato rimane `PENDING` fino a quando l'avversario non accetta l'invito. Se l'avversario rifiuta l'invito, l'applicazione rimuove l'item corrispondente dalla tabella. 

La funzione specifica la query come segue:
+ Specifica l'indice `OpponentId-StatusDate-index` da usare con i valori delle chiavi di indice e gli operatori di confronto seguenti:
  + La chiave di partizione è `OpponentId` e accetta la chiave di indice `{{user ID}}`.
  + La chiave di ordinamento è `StatusDate` e accetta l'operatore di confronto e il valore della chiave di indice `beginswith="PENDING_"`.

  È possibile utilizzare l'indice `OpponentId-StatusDate-index` per recuperare i giochi a cui è invitato l'utente connesso, ovvero dove l'utente connesso è l'avversario.
+ La query prevede un limite di 10 item nel risultato.

```
gameInvitesIndex = self.cm.getGamesTable().query(
                                            Opponent__eq=user,
                                            StatusDate__beginswith="PENDING_",
                                            index="OpponentId-StatusDate-index",
                                            limit=10)
```

Nell'indice, per ogni valore `OpponentId` (la chiave di partizione) DynamoDB mantiene gli elementi ordinati in base a `StatusDate` (la chiave di ordinamento). Le partite restituite dalla query saranno quindi le 10 più recenti.

#### Utilizzo di getGamesWith Status per ottenere l'elenco dei giochi con uno stato specifico
<a name="TicTacToe.Phase2.GameInAction.ListGamesInProgressHistory"></a>

Quando un avversario accetta un invito a una partita, lo stato diventa `IN_PROGRESS`. Al termine della partita, lo stato cambia in `FINISHED`. 

Le query per trovare le partite in corso o terminate sono uguali, ad eccezione del valore di stato diverso. L'applicazione definisce quindi la funzione `getGamesWithStatus`, che accetta un valore di stato come parametro. 

```
inProgressGames = controller.getGamesWithStatus(session["username"], "IN_PROGRESS")
finishedGames   = controller.getGamesWithStatus(session["username"], "FINISHED")
```

La sezione seguente si riferisce alle partite in corso, ma la stessa descrizione si applica alle partite terminate.

Un elenco delle partite in corso per un utente specifico include entrambi i tipi seguenti: 
+ Partite in corso ospitate dall'utente 
+ Partite in corso in cui l'utente è l'avversario 

La funzione `getGamesWithStatus` esegue le due query seguenti, ogni volta usando l'indice secondario appropriato. 
+ La funzione esegue una query sulla tabella `Games` usando l'indice `HostId-StatusDate-index`. Per l'indice, la query specifica i valori della chiave primaria, sia la chiave di partizione (`HostId`) che la chiave di ordinamento (`StatusDate`), insieme agli operatori di confronto. 

  ```
  hostGamesInProgress = self.cm.getGamesTable ().query(HostId__eq=user,
                                                 StatusDate__beginswith=status,
                                                 index="HostId-StatusDate-index",
                                                 limit=10)
  ```

  Ecco la sintassi Python per gli operatori di confronto:
  + `HostId__eq=user` specifica l'operatore di confronto di uguaglianza.
  + `StatusDate__beginswith=status` specifica l'operatore di confronto `BEGINS_WITH`.
+ La funzione esegue una query sulla tabella `Games` usando l'indice `OpponentId-StatusDate-index`. 

  ```
  oppGamesInProgress = self.cm.getGamesTable().query(Opponent__eq=user,
                                               StatusDate__beginswith=status,
                                               index="OpponentId-StatusDate-index",
                                               limit=10)
  ```
+ La funzione combina quindi i due elenchi, li ordina, crea un elenco di oggetti `Game` per i primi da 0 a 10 item e restituisce l'elenco alla funzione chiamante, ovvero all'indice.

  ```
  games = self.mergeQueries(hostGamesInProgress,
                          oppGamesInProgress)
  return games
  ```

### Pagina del gioco
<a name="TicTacToe.Phase2.AppInAction.GamePage"></a>

La pagina del gioco è dove l'utente tic-tac-toe gioca. In questa pagina sono presenti la griglia di gioco e informazioni sulla partita. Lo screenshot seguente mostra una partita di esempio in corso:

![Schermata che mostra una tic-tac-toe partita in corso.](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/tic-tac-toe-example-board-10.png)


L'applicazione visualizza la pagina del gioco nelle situazioni seguenti:
+ L'utente crea una partita invitando un altro utente a giocare. 

  In questo caso, la pagina mostra l'utente come host e lo stato della partita come `PENDING`, in attesa che l'avversario accetti l'invito.
+ L'utente accetta uno degli inviti in attesa nella home page. 

  In questo caso, la pagina mostra l'utente come avversario e lo stato della partita come `IN_PROGRESS`.

Quando l'utente effettua una selezione sulla griglia, viene generata una richiesta `POST` per l'applicazione. Flask chiama la funzione `selectSquare` (in `application.py`) con i dati in formato HTML. Questa funzione, a sua volta, chiama la funzione `updateBoardAndTurn` (in `gameController.py`) per aggiornare l'item di gioco come indicato di seguito:
+ Aggiunge un nuovo attributo specifico della mossa.
+ Aggiorna il valore dell'attributo `Turn` in base all'utente che deve giocare il turno successivo.

```
controller.updateBoardAndTurn(item, value, session["username"])
```

La funzione restituisce true se l'aggiornamento dell'item è stato eseguito correttamente, altrimenti restituisce false. Tieni presente quanto segue in relazione alla funzione `updateBoardAndTurn`:
+ La funzione chiama la funzione `update_item` di SDK for Python per apportare un set specifico di aggiornamenti a un elemento esistente. La funzione è mappata all'operazione `UpdateItem` in DynamoDB. Per ulteriori informazioni, consulta [UpdateItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html). 
**Nota**  
La differenza tra le operazioni `UpdateItem` e `PutItem` è legata al fatto che `PutItem` sostituisce l'intero item. Per ulteriori informazioni, consulta [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html).

Per la chiamata di `update_item`, il codice identifica quanto segue:
+ La chiave primaria della tabella `Games`, ovvero `ItemId`.

  ```
  key = { "GameId" : { "S" : gameId } }
  ```
+ Il nuovo attributo da aggiungere, specifico della mossa dell'utente corrente, e il relativo valore (ad esempio `TopLeft="X"`).

  ```
  attributeUpdates = {
      position : {
          "Action" : "PUT",
          "Value" : { "S" : representation }
      }
  }
  ```
+ Le condizioni che devono essere soddisfatte affinché venga eseguito l'aggiornamento:
  + La partita deve essere in corso. Ciò significa che il valore dell'attributo `StatusDate` deve iniziare con `IN_PROGRESS`.
  + Il turno corrente deve essere un turno utente valido come specificato dall'attributo `Turn`. 
  + Il quadrato della griglia scelto dall'utente deve essere disponibile. Ciò significa che l'attributo corrispondente al quadrato non deve esistere.

  ```
  expectations = {"StatusDate" : {"AttributeValueList": [{"S" : "IN_PROGRESS_"}],
      "ComparisonOperator": "BEGINS_WITH"},
      "Turn" : {"Value" : {"S" : current_player}},
      position : {"Exists" : False}}
  ```

La funzione chiama ora `update_item` per aggiornare l'item.

```
self.cm.db.update_item("Games", key=key,
    attribute_updates=attributeUpdates,
    expected=expectations)
```

Dopo che la funzione restituisce il risultato, la funzione `selectSquare` chiama il metodo redirect, come illustrato nell'esempio seguente.

```
redirect("/game="+gameId) 
```

Questa chiamata causa l'aggiornamento del browser. Nel corso di questo aggiornamento, l'applicazione verifica se la partita si è conclusa con una vittoria o un pareggio. In caso affermativo, l'applicazione aggiorna l'item di gioco di conseguenza. 