# AIS-Operations &middot; [![licence](https://img.shields.io/badge/licence-MIT-blue.svg)](/LICENSE) [![python](https://img.shields.io/badge/python->=v3.7-blue.svg)](https://www.python.org/)

AIS-Operations est un programme permettant la réalisation d'opérations sur des fichiers dont les données sont issues d'[AISHub](https://www.aishub.net/).

## Dépendances

Afin de faire fonctionner correctement le programme, il vous faudra vous munir au préalable de la [version 3.7](https://www.python.org/downloads/) de [Python](https://www.python.org/) au minimum, ainsi que les dépendances suivantes :

### Obligatoires

- [Argparse](https://docs.python.org/fr/3/howto/argparse.html) (inclus avec Python)
- [Glob](https://docs.python.org/3/library/glob.html) (inclus avec Python)
- [Inspect](https://docs.python.org/3/library/inspect.html) (inclus avec Python)
- [Os](https://docs.python.org/3/library/os.html) (inclus avec Python)
- [Pathlib](https://docs.python.org/3/library/pathlib.html) (inclus avec Python)
- [Polars](https://www.pola.rs/) | [PyPi](https://pypi.org/project/polars/) | [Documentation](https://pola-rs.github.io/polars/py-polars/html/reference/index.html)

### Optionnelles

- [Gnuplot](http://www.gnuplot.info/) (pour le benchmark)

## Utilisation

Le programme possède une inteface en ligne de commande disponible dans le fichier [`cli.py`](/src/cli.py) permettant d'agir avec ce dernier. Pour l'exécuter, il suffit de se placer dans le dossier et d'exécuter la commande suivante à partir d'une console de commande :

```shell
python3 cli.py
```

 À première vue, un message d'erreur et d'aide devrait s'afficher comme suit :
 
 ```shell
 mkel@HOME:~$ python3 cli.py
----- Command-line interface for AIS-Operations -----

** Loading functions list
** Done

usage: cli.py [-h] --target TARGET [TARGET ...] --function FUNCTION
cli.py: error: the following arguments are required: --target, --function
```

Il s'agit tout simplement car un ou des fichiers doivent être fournis, ainsi qu'une fonction à exécuter. Néanmoins, il peut s'agir de fichiers, de répertoires ou encore d'expressions rationnelles. On peut également obtenir la liste des fonctions disponibles en exécutant cette commande :

```shell
python3 cli.py -h
```

Ce qui renvoie :

```shell
mkel@HOME:~$ python3 cli.py -h
----- Command-line interface for AIS-Operations -----

** Loading functions list
** Done

usage: cli.py [-h] --target TARGET [TARGET ...] --function FUNCTION

optional arguments:
  -h, --help            show this help message and exit
  --target TARGET [TARGET ...]
                        string(s) of path(s), file(s) or regexp(s) where to
                        execute the function
  --function FUNCTION   sort(column), route, filter(condition)
```

Dans la partie aide du paramètre **function**, on peut y voir la liste des fonctions disponibles ainsi que les paramètres qui doivent être fournis à la fonction.  Ainsi, si on veut par exemple exécuter la fonction [`sort(column)`](/src/utils/functions.py#L9), il suffit d'exécuter la commande suivante :

```shell
python3 cli.py --target "cible.csv" --function "sort('MMSI')"
```

Pour exécuter la fonction sur plusieurs cibles, il suffit d'ajouter les chemins des cibles les uns à la suite des autres :

```shell
python3 cli.py --target "cible.csv" "cible2.csv" "/dossier/" --function "sort('MMSI')"
```

Il est également possible de donner des expressions rationnelles :

```shell
python3 cli.py --target "cible.csv" "cible2.csv" "/dossier/*.csv" --function "sort('MMSI')"
```

## Fonctions disponibles

Pour le moment, seul ces fonctions sont disponibles :

- [`sort(column)`](/src/utils/functions.py#L9), permet de trier le(s) CSV en fonction d'une colonne donnée. Exemple : 

```shell
python3 cli.py --target "cible.csv" "cible2.csv" "/dossier/" --function "sort('MMSI')"
```

- [`route()`](/src/utils/functions.py#L18), permet d'obtenir la route des navires (uniquement dans le CSV).

Exemple :
```shell
python3 cli.py --target "cible.csv" "cible2.csv" "/dossier/" --function "route"
```

- [`filter(condition)`](/src/utils/functions.py#L42), permet de filtrer le(s) CSV en fonction d'une condition. Il faut se renseigner à propos de la documentation de Polars pour savoir quoi utiliser.

```shell
python3 cli.py --target "cible.csv" "cible2.csv" "/dossier/" --function "filter(col('MMSI') == 0)"
```

Cet exemple permet de filtrer les MMSI dont l'identifiant est 0.


## Extension de fonctions

Le programme est écrit de sorte qu'il puisse être étendu avec d'autres opérations sur les données issues d'AISHub. Python étant un langage multi-paradigme, nous avons fait le choix d'écrire la classe [`LazyFrame`](/src/LazyFrame.py) en orienté objet. Cette classe permet entre autres, la sauvegarde des données ainsi que leur nom.

Pour pouvoir ajouter d'autres opérations possibles sur les fichiers CSV, il suffit d'ajouter une fonction concernée dans le fichier [`functions.py`](/src/utils/functions.py). Attention ! Votre fonction doit toujours avoir un paramètre lazyFrame (sensible à la casse) en premier, qui concerne sur quel objet LazyFrame on applique la fonction. Par exemple :

```python
def x(lazyFrame, param1, param2, ...)
```

Pour vous aider, il existe des énumérations disponibles dans le dossier [`enums`](/src/enums/), permettant de récupérer directement des valeurs comme le nom des colonnes, le type de messages ou encore de pouvoir appliquer le programme à d'autres fichiers de données comme des TSV. Il suffit d'importer l'énumération que vous souhaitez utiliser puis d'obtenir la valeur concernée. Par exemple, si on veut avoir le nom de le la colonne Complete_Sys_Date :

```python
from enums.Column import Column

Column.COMPLETE_SYS_DATE.value
```

Autre exemple, si on souhaite récupérer le numéro du message d'un navire amarré (moored) :

```python
from enums.NavigationalStatus import NavigationalStatus

NavigationalStatus.MOORED.value
```

## Benchmark et tests

Le site [db-benchmark](https://h2oai.github.io/db-benchmark/) répertorie la vitesse de Polars face à d'autres bibliothèques similaires comme [Pandas](https://pandas.pydata.org/) ou [Dask](https://dask.org/). À noté que les données comparées concernent l'utilisation d'expression, qui sont similaires à des requêtes [SQL](https://fr.wikipedia.org/wiki/Structured_Query_Language). Chaque bibliothèque transforme une condition donnée en une expression compréhensible pour la bibliothèque concernée. Par exemple, la condition :

```python
polars.col("MMSI") == 0
```

est transformée en expression : 

```sql
SELECT COLUMN "MMSI" WHERE "MMSI" = 0
```

Afin de tester l'efficacité du programme, nous réalisons un benchmark sur la fonction [`route(LazyFrame)`](/src/utils/functions.py#L19). Attention ! Comme nous sommes en lazy, Polars applique la fonction uniquement lors de la récolte des lazyframes dans la méthode [`save()`](/src/LazyFrame.py#L67). Cette récolte ultérieure, permet à Polars d'optimiser l'expression et de réaliser la parallélisation. Le benchmark peut être (ré)généré à partir de [`benchmark.py`](/src/utils/benchmark.py). Pour obtenir des résultats plus précis, nous exécutons la fonction 10 fois sur différents fichiers CSV et réalisons une moyenne.

![benchmark](/resources/images/benchmark.png)

En abscisse, on retrouve la taille du fichier CSV en bytes et en ordonnée le temps mis pour avoir exécuter la fonction. On peut voir assez aisément que la fonction prends plus de temps sur des fichiers CSV dont la taille est plus importante.

### Spécifications

- Système d'exploitation : [Linux Mint](https://www.linuxmint.com/) (Cinnamon Edition, v20.3)
- Processeur : AMD Ryzen 7 4700U
- Carte Graphique : Radeon Graphics (intégré avec le processeur)
- Mémoire : 16 GO de RAM DDR4 3200 MHz
- SSD : Intel SSD 660p Series (1 To)

## Licence

AIS-Operations est sous [licence MIT](/LICENSE).
