# 1 - Développement d'applications destinées à la population vieillissante - Choix du patient
* Auteur: Prune Pillone
* Début du projet : 02/07/2019
Ce projet nécessite :
- Java 8
- Node 10.9
- Angular 8
- Maven 3
[TOC]
Cette partie sera la plus longue car elle nécessite beaucoup de mise en place et d’initialisation.
Dans cette application, le patient est au cœur. La majeure partie des interfaces seront vues par lui, et il est nécessaire de les adapter. Nous n’allons donc pas utiliser un système de connexion avec mot de passe, mais plutôt juste un écran de sélection à la Netflix.

Lorsque le patient sélectionnera son profil, il sera redirigé vers sa page.

Pour réaliser cela nous avons besoin de plusieurs notions :
- Des patients, avec leurs noms et leurs photos
- 2 pages Web différentes
- Une communication entre la base de données et le back-end
- Un service REST permettant au front d’accéder aux données
On va donc bien traverser les couches suivantes.

Commençons par la base de données.
## Création de la base de données
Tout d’abord, il faut lancer le serveur PostgreSQL précédemment installé.
Ouvrez une invite de commande, et lancez cette ligne :
`pg_ctl -D "C:\Program Files\PostgreSQL\10\data" start`
Sous mac si installé avec homebrew :
`brew services start postgresql`
Si cette erreur apparait :
`pg_ctl : un autre serveur semble en cours d'exécution ; le démarrage du serveur va toutefois être tenté`
Cela signifie que le serveur était déjà lancé.
Connectez vous à votre utilisateur que vous avez créé précédemment lors de l’installation.
Pour créer la BDD, il suffit d’écrire cette ligne :
`CREATE DATABASE dvp;`
Connectez vous ensuite à la BDD en faisant
`\c dvp`
Pour remplir la base de données, il suffit d’exécuter des commandes SQL.
Nous allons créer la table Patient.
Ici nous allons prendre un raccourci à des fins de simplification ici et seulement sauvegarder l'adresse URL d'une photo. Dans une vraie base de données, il faudrait enregistrer le fichier original de la photo, et le stocker en binaire dans la base de données.
```sql
CREATE TABLE patient(name varchar(30), photoURL varchar(200));
ALTER TABLE patient ADD COLUMN patientID BIGSERIAL PRIMARY KEY;
```
Cette deuxième ligne permet de créer un identifiant unique pour chaque patient qui s’incrémentera à chaque nouveau patient.
Créons maintenant 5 premiers patients :
```sql
INSERT INTO patient values('Jean','https://previews.123rf.com/images/nyul/nyul1102/nyul110200262/8748110-portrait-of-happy-old-man-smiling-looking-at-camera-.jpg');
INSERT INTO patient values('Maurice','https://previews.123rf.com/images/dolgachov/dolgachov1610/dolgachov161012052/64861303-old-age-gesture-comfort-and-people-concept-smiling-senior-man-in-glasses-sitting-on-sofa-and-showing.jpg');
INSERT INTO patient values('Dominique', 'https://images.pexels.com/photos/160422/man-hat-portrait-old-man-160422.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940');
INSERT INTO patient values('Josette','https://images.pexels.com/photos/432722/pexels-photo-432722.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940');
INSERT INTO patient values ('Marie', 'https://images.pexels.com/photos/2658231/pexels-photo-2658231.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940');
```
En faisant `SELECT * FROM patient;`, on peut voir que les 5 patients ont bien été ajoutés.
## Création du Back-End
Maintenant que la base de données est bien créée, il faut créer le service REST qui exposera les données de la base au front-end.
Pour cela nous allons utiliser un back-end en Java et plusieurs outils supplémentaires :
- Jersey pour exposer une API Rest
- Jetty pour avoir un serveur web où exposer l’API
- Un JDBC PostgreSQL
Commençons par créer un projet Java avec Maven.
Si vous utilisez IntelliJ, créez un nouveau projet. Choisissez Maven et Java 1.8 comme project SDK. Ne choisissez pas d’archétype.

Vous devriez obtenir une structure comme celle-ci.

Ces outils vont être importés avec des dépendences Maven.
Commencez par ajouter dans votre pom.xml après la définition des groupID et des artifactId.
```xml=
<!-- Change moi ! -->
<name>Back-end-webapp</name>
<packaging>war</packaging>
<properties>
<jettyVersion>9.4.9.v20180320</jettyVersion>
<jersey.version>2.28</jersey.version>
<project.build.sourceEncoding>UTF8</project.build.sourceEncoding>
</properties>
```
Il faut ensuite ajouter ceci afin de définir les configurations de compilation et de déployement.
```xml=
<build>
<!-- Change moi aussi ! -->
<finalName>back-end</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<inherited>true</inherited>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jettyVersion}</version>
</plugin>
</plugins>
</build>
```
On peut ensuite ajouter les dépendances à proprement parler :
```xml=
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jettyVersion}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.25</version>
</dependency>
</dependencies>
```
Vous pouvez ensuite importer les dépendances.
Une fonctionnalité pratique d’IntelliJ est la possibilité de créer des configurations.

Sélectionnez +, puis Maven. Remplissez ainsi.

Vous pouvez ensuite lancer la configuration.
Vous obtiendrez alors un message d’erreur, car il manque des fichiers de configuration.

Créez cette arborescence :

Dans le fichier web.xml mettez :
```xml=
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Rest API</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<!-- Le nom de votre groupeID -->
<param-value>demo.project</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Rest API</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
```
Et dans index.jsp
```html=
<html>
<h2>Ca marche ! </h2>
</html>
```
Relancez maintenant le projet.
Ouvez ensuite un navigateur Internet et tapez dans la barre d’URL : localhost :8080.
 devrait être affiché sur la page.
Maintenant que nous avons cela, nous pouvons créer les objets métiers qu’on va utiliser.
Créez dans le dossier Java un package nommé {{votreGroupID}}.core.
Dans ce package, ajoutez une classe Patient avec 3 attributs : un int id, une String nom, et une String photoURL. Ecrivez également le constructeur de cette classe.
Ajoutez dans le pom.xml la dépendence :
```xml =
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.1</version>
</dependency>
```
Maintenant, ajoutez un package nommé {{votreGroupID}}.bdd, dans lequel vous créerez une classe PostgreSQLJDBC.
Remplissez cette classe avec ce code :
```java=
private static final Logger logger = Logger.getLogger(PostgreSQLJDBC.class.getName());
/**
* Elements nécessaires
**/
private static Connection connection;
private static Statement statement;
/**
* Constructeur privé pour empêcher l'appel de cette classe autrement qu'en static
*/
private PostgreSQLJDBC() {
}
/**
* Initialisation de la connexion à la base.
*/
public static void init() {
try {
Class.forName("org.postgresql.Driver");
//Changez cette ligne avec les bons identifiants et mot de passe
connection = DriverManager
.getConnection("jdbc:postgresql://localhost:5432/dpv",
"postgres", "azerty");
connection.setAutoCommit(false);
logger.log(Level.INFO, "Connection à la base de données effectuée.");
} catch (ClassNotFoundException ex) {
logger.log(Level.SEVERE, "Class not found");
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Connection Impossible");
}
}
/**
* Méthode qui permet d'exécuter une requête SQL
*
* @param query la requête SQL sous forme de chaîne de caractères
* @return un ResultSet qui sera ensuite parsé pour obtenir les élements de la requête
*/
public static ResultSet query(String query) {
try {
statement = connection.createStatement();
try {
ResultSet rs = statement.executeQuery(query);
logger.log(Level.INFO, "Requête effectuée avec succès");
return rs;
} catch (NullPointerException e) {
logger.log(Level.SEVERE, "Erreur dans la requête");
} catch (SQLException e) {
logger.log(Level.SEVERE, "Connection Impossible");
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* Méthode qui permet de fermer la connection à la base de données
*/
public static void close() {
try {
statement.close();
connection.close();
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Erreur lors de la fermeture");
}
}
```
Libre à vous de modifier les informations loguées afin d’être plus précis.
Ajoutez maintenant une classe PatientAccess.
Nous allons tout d’abord créer une méthode permettant de récupérer tous les patients de la base de données.
```java
public static List<Patient> getAllPatients() {
String query = "SELECT * FROM patient";
PostgreSQLJDBC.init();
List<Patient> patientList;
ResultSet patients = PostgreSQLJDBC.query(query);
//patientList = convertRSToPatient(patients);
return patientList;
}
```
La méthode convertRSToPatient est commentée car elle reste à écrire. Lorsqu’on appelle PostgreSQLJDBC.query(query), un objet de type ResultSet est renvoyé. Il reste ensuite à parser cet objet et de convertir ce qu’on obtient en notre objet Patient.
```java=
private static List<Patient> convertRSToPatient(ResultSet patients) {
List<Patient> patientList = new ArrayList<>();
try {
while (patients.next()) {
int id = patients.getInt("patientID");
String name = patients.getString("name");
String url = patients.getString("photoURL");
patientList.add(new Patient(id, name, url));
}
} catch (SQLException e) {
e.printStackTrace();
}
return patientList;
}
```
Vous pouvez maintenant décommenter la ligne dans la méthode getAllPatients().
Maintenant que nous avons la conversion entre l’objet de la base de données et notre objet métier, il ne reste plus qu’à l’envoyer via l’API Rest.
Ajoutez dans le pom.xml la dépendance suivante :
```xml=
<!-- Gson: Java to Json conversion -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
```
Créez un nouveau package appelé {{votreGroupID}}.api. Ajoutez dans ce package une classe : PatientsResource.
```java=
import com.google.gson.Gson;
import org.mortbay.jetty.Request;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.util.List;
@Path("/patients")
public class PatientsResource {
@Context
UriInfo uriInfo;
@Context
Request request;
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getPatients() {
List<Patient> patientList = PatientAccess.getAllPatients();
return new Gson().toJson(patientList);
}
}
```
Lorsque le serveur REST sera appelé sur l’URL /patients, il appelera la méthode getPatients() qui renverra alors la liste de tous les patients dans la BDD.
Vous pouvez à présent lancer le projet en faisant mvn clean package jetty:run ou en utilisant la configuration définie précédemment.
Allez ensuite sur http://localhost:8080/api/patients
Vous devriez obtenir la liste en json des patients dans la base de données.
Il reste une dernière chose à ajouter avant de pouvoir passer à la suite, et c’est un filtre CORS. Ajoutez une classe CorsFilter dans le package api.
```java=
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
/**
* Source https://www.baeldung.com/cors-in-jax-rs
*/
@Provider
public class CorsFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().add(
"Access-Control-Allow-Origin", "*");
responseContext.getHeaders().add(
"Access-Control-Allow-Credentials", "true");
responseContext.getHeaders().add(
"Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
responseContext.getHeaders().add(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
```
Relancez le serveur avec mvn clean package jetty:run.
Il ne reste plus qu’à récupérer et à afficher ces données en Angular.
## Creation du Front-End
(Si vous n’avez jamais fait d’Angular, il est fortement conseillé de faire le tutoriel présent sur le site d’Angular : https://angular.io/start)
Une fois node et angular installés, ouvrez un terminal à l’endroit où vous voulez créer le projet, puis lancer cette ligne de commande :
`ng new <nomDuProjet> --defaults`
Après un certain temps, surtout si c’est la première fois que vous lancez Angular, vous devriez obtenir ceci :

L’IDE utilisé ici est WebStorm mais il est tout à fait possible de faire ceci avec Visual Studio Code, Atom et autres.
Dans le dossier src/app, créez 2 nouveaux dossiers, components et services.
Les nouveaux composants Angular iront dans le dossier components, tandis que les services dans services.
Lancez maintenant l’application
`ng serve --open`
Un navigateur devrait s’ouvrir et afficher une page web avec le nom de votre projet.
Dans un autre terminal, allez ensuite dans le dossier components que vous venez de créer, et tapez la commande
`ng g c patient-selection` ou `ng generate component patient-selection`
Cette commande permet de générer un composant nommé patient-selection.
Allez ensuite dans app-component.html, et effacez tout son contenu, que vous remplacerez par
```htmlembedded
<app-patient-selection></app-patient-selection>.
```
Sauvegardez le fichier et allez ensuite rafraichir la page du navigateur.
Allez ensuite dans services depuis le terminal et tapez
`ng g s rest` ou `ng generate service rest`
Ce fichier servira à faire le lien entre le service rest crée précédemment dans le back-end et le front-end Angular.
Créez maintenant au même niveau que components et services un dossier models qui servira pour les classes et modèles de l’application, tels que les patients, les questions etc.Créez dans ce dossier un fichier nommé patient.ts, puis remplissez le ainsi.
```typescript=
export class Patient {
id: number;
name: string;
photoURl: string;
}
```
Allez dans app.module.ts et ajoutez
```typescript=
import {HttpClientModule} from '@angular/common/http'; dans les imports, puis
HttpClientModule dans NgModule imports[]
Allez ensuite dans le fichier rest-service.ts et remplissez le ainsi :
import {Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {retry} from "rxjs/operators";
import {Patient} from "../models/patient";
@Injectable({
providedIn: 'root'
})
export class RestService {
apiURL = 'http://localhost:8080';
constructor(private httpClient: HttpClient) {
}
getPatients(): Observable<Patient> {
return this.httpClient.get<Patient>(`${this.apiURL}/api/patients`).pipe(retry(1));
}
}
```
Allez maintenant dans le fichier patient-selection.component.ts et ajoutez dans la classe PatientSelectionComponent un attribut Patients : any = [].
Cela déclare un tableau de n’importe quel type nommé Patients.
Ajoutez ensuite dans le constructeur un attribut private restService.
```typescript
export class PatientSelectionComponent implements OnInit {
Patients: any = [];
constructor(private restService: RestService) { }
```
Créez ensuite une méthode loadPatients().
Cette méthode va permettre de récupérer les Patients à partir du service Rest.
```typescript
loadPatients() {
return this.restService.getPatients().subscribe((data: {}) => {
this.Patients = data;
});
}
```
Appelez ensuite cette méthode dans ngOnInit. La liste des Patients sera donc chargée lors du chargement du composant, de manière asynchrone.
Ouvrez ensuite le fichier patient-selection.component.html. Dedans devrait se trouver une seule ligne : <p>patient-selection works!</p>
Remplacez la par :
```htmlembedded=
<ul>
<li *ngFor="let patient of Patients">{{patient.name}}</li>
</ul>
```
Sauvegardez et actualisez la page ouverte dans votre navigateur.
Une liste des patients s’affichera.
Il faut maintenant afficher cela sous forme de grille. Pour cela nous allons utiliser angular material, une bibliothèque additionnelle.
Lancez : npm install @angular/material @angular/cdk @angular/animations
Pius ajoutez import { MatGridListModule} from '@angular/material'; dans app.module.ts, et MatGridListModule dans les imports du NgModule.
Remplacez les lignes prédécentes par ceci. Vous pouvez changez la valeur de cols (changera le nombre de colonnes) comme vous le souhaitez, ainsi que le ratio rowHeight (changera la taille des lignes).
```htmlembedded=
<mat-grid-list cols="3" rowHeight="2:1">
<mat-grid-tile *ngFor="let patient of Patients">
<div class="patientTile">
<img src="{{patient.photoURL}}" alt="Photo de {{patient.name}}"/>
<p>{{patient.name}}</p>
</div>
</mat-grid-tile>
</mat-grid-list>
```
Allez ensuite dans le fichier css associé, et ajoutez ceci.
```css=
.patientTile {
/*vertical-align: central;*/
flex-direction: column;
border: 1px solid teal;
border-radius: 1em;
}
img {
margin: 0.1em;
padding: 0.1em;
object-fit: cover;
width: 200px;
height: 200px;
}
```
Libre à vous de modifier ensuite le composant pour arriver à un aspect qui vous convient.
Il faut ensuite passer à la deuxième partie, le profil personnalisé du patient, et cela va inclure du routing.
### Routing
Pour commencez dans le app.module.ts :
```typescript
Import { RouterModule, Routes } from '@angular/router';
```
et ajoutez aux imports de NgModule comme d’habitude.
Ajoutez ensuite
```typescript
const appRoutes: Routes = [
{path: 'patientselection', component: PatientSelectionComponent},
{path: 'patientprofile/:id', component: PatientProfileComponent},
{
path: '',
redirectTo: '/patientselection',
pathMatch: 'full'
},
//{path: '**', component: PageNotFoundComponent}
];
```
Puis dans les imports :
```typescript
RouterModule.forRoot(
appRoutes,
)
```
Le tableau appRoutes permet de décrire comment se déplacer entre les différentes routes.
Une fois cette étape finie, localhost :4200/patientselection redigera vers la page de sélection du patient, et patientprofile vers un patient en particulier.
Vous pouvez déjà tester que localhost :4200 redirge vers localhost :4200/patientselection, grâce au troisième élément du tableau.
La ligne commentée est celle qui gère la redirection lorsqu’une URL rentrée ne correspond à aucune des routes définies. Libre à vous de créer votre PageNotFoundComponent.
Remplacez maintenant le contenu de app-component.html par
```htmlembedded=
<nav>
<a routerLink="/patientselection" routerLinkActive="active">Sélection du profil</a>
</nav>
<router-outlet></router-outlet>
```
Modifiez le css à votre guise pour obtenir un aspect qui vous plaise.
Modifiez le code de patient-selection ainsi :
```htmlembedded=
<mat-grid-list cols="3" rowHeight="2:1">
<mat-grid-tile *ngFor="let patient of Patients">
<a routerLink="/patientprofile/{{patient.id}}">
<div class="patientTile" (click)="openPatientProfile(patient)">
<img src="{{patient.photoURL}}" alt="Photo de {{patient.name}}"/>
<p>{{patient.name}}</p>
</div>
</a>
</mat-grid-tile>
</mat-grid-list>
```
Dans patient-profile.component.ts, ajoutez :
```typescript
import { Router, ActivatedRoute, ParamMap } from '@angular/router'; et import { switchMap } from 'rxjs/operators';
```
Modifiez le constructeur de la classe.
```typescript
constructor(
private route: ActivatedRoute,
private router: Router,
private service: RestService
) {}
```
Ajoutez ceci dans le ngOnInit :
```typescript
ngOnInit() {
this.route.paramMap.pipe(
switchMap((params: ParamMap) =>
this.restService.getPatient(params.get('id')))
).subscribe(data => this.patient = data);
console.log(this.patient);
}
```
Il va donc falloir ajouter dans le restService une méthode getPatient, et modifiez le back-end en accord.
```typescript
getPatient(id: string): Observable<Patient> {
return this.httpClient.get<Patient>(`${this.apiURL}/api/patients/` + id).pipe(retry(1));
}
```
Dans patient-profile.component.html ajoutez ceci :
```htmlembedded=
<h1>Bienvenue {{patient.name}}</h1>
<img src="{{patient.photoURL}}" alt="Photo de {{patient.name}}"/>
<a mat-raised-button><h1>Jouer !</h1></a>
```
Et dans le css :
```css=
img {
margin: 0.1em;
padding: 0.1em;
object-fit: cover;
width: 300px;
height: 300px;
}
a {
color: white;
background-color: teal;
}
```
Dans le back-end, dans le package api, créez une classe PatientResource.
```java=
@Context
UriInfo uriInfo;
@Context
Request request;
int id;
public PatientResource(UriInfo uriInfo, Request request, int id) {
this.uriInfo = uriInfo;
this.request = request;
this.id = id;
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public String getPatient() {
Patient patient;
patient = PatientAccess.getPatientById(id);
if (patient == null) {
throw new RuntimeException("Get: Patient with " + id + " not found");
}
return new Gson().toJson(patient);
}
```
Ajoutez ensuite dans PatientsResource la méthode suivante :
```java=
@Path("{patient}")
public PatientResource getPatient(@PathParam("patient") int id) {
return new PatientResource(uriInfo, request, id);
}
```
Ajoutez dans PatientAccess la méthode getPatientById(id), de la même façon que vous avez construit getPatients, sachant que pour obtenir les patients ayant pour patientid « id », la requête est « SELECT * FROM patients WHERE patientId = id »
Relancez ensuite le back-end en faisant mvn clean package jetty :run et revenez sur localhost :4200 et actualisez la page. Testez ensuite si votre routing marche.
Vous pouvez après passer à la partie 2 : [Partie 2](/ylDRAyZiRpW7xADOjbNqoA)