"""
# Notebooks sur Kaggle
Kaggle nous offre la possibilité d'utiliser Jupyter Notebook.
Les données d'entrainement et de test sont disponible en lecture seule.
"""
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    print(dirname)
"""
# Remarques
- C'est à vous de diviser l'ensemble d'entrainement en train/valid, vous pouvez vous en inspirez de la question 1
- Ce code est à titre indicatif, vous n'êtes pas obligés de suivre la même logique pour lader les données
"""
# nous utilisons pathlib afin de parcourir les dossiers et fichiers
import pathlib

# nous utilisons aussi torchvision et pytorch pour le code de Data Loading
import torchvision
from torchvision import datasets, models, transforms
from torch.utils.data import Dataset, DataLoader

from PIL import Image # pour lire les images .jpg

# nous utilisons Pandas pour manipuler et creer le fichier de soumission assez rapidement
import pandas as pd

import numpy as np
# changer les chemins selon votre machine
# TODO 
# A vous de diviser le train en deux sous ensemble train/valid
train_path = '/kaggle/input/je-suis-ou/train'
valid_path = '' # a changer selon le chemin dans votre machine

# le chemin vers le test set
test_path = '/kaggle/input/je-suis-ou/test'
train_transform = transforms.Compose([
    # a vous de remplir
    #
    #
    #
    # ...
])

test_transform = transforms.Compose([
    # a vous de remplir
    #
    #
    #
    # ...
])
# utilisons la classe ImageFolder afin de charger le train set
train_dataset = datasets.ImageFolder(train_path, train_transform)

# la classe ImageFolder assigne automatiquement un label pour chaque nom de classe (class -> idx)
print('class -> idx : ',train_dataset.class_to_idx)

# on aura besoin d'un dictionnaire qui fait le sens inverse (idx -> class)
idx_to_class = {train_dataset.class_to_idx[class_name]: class_name for class_name in  train_dataset.class_to_idx}
print('idx -> class : ',idx_to_class)
"""
## Chargement des données de Test
Pour les données de test, nous n'avons pas d'étitquettes, donc la classe ImageFolder n'est pas utile.
- Nous avons besoin des noms des images afin de créer le fichier de soumission
"""
# on hérite de la classe Dataset de PyTorch 
class TestDataset(Dataset):
    def __init__(self, test_path, transform=None):
        
        # lister toutes les images dans le repertoire test
        # self.image_paths va etre une liste contenant des 
        # elements de type Path (pour plus d'info voir la class Path de pathlib)
        # cela fonctionne seulement pour les versions de python >3.4
        self.image_paths = list(pathlib.Path(test_path).glob('*.jpg'))
        
        # trier les noms des fichiers d'images par ordre
        self.image_paths.sort()
        
        # garder la fonctions de tranform dans self pour l'utiliser dans __getitem__
        self.transform = transform

    def __getitem__(self, index):
        # index est un nombre qui vient du dataloader (il est entre 0 et ce que retourne la methode __len__ ci-dessous)
        # c'est donc entre 0 et le nombre d'images de test
        img_path = self.image_paths[index]
        
        # retourner aussi le nom de l'image en question(i.e. '0xxxx')
        # sans l'extension '.jpg', donc on ignore les 4 dernier caracteres du nom de l'image (.jpg) avec [:-4]
        # par exemple, pour l'image '00037.jpg' on retourne '00037'
        # cela va vous etre util pour generer le fichier predictions.csv
        img_name = img_path.name[:-4] #img_path est un objet de type Path, et a donc un attribut 'name'
        
        # lire l'image avec PIL
        img = Image.open(img_path)
        
        # appliquer les transforms s'il y'en a
        if self.transform is not None:
            img = self.transform(img)
        
        return img, img_name

    def __len__(self):
        return len(self.image_paths)
# Nous allons tester la classe TestDataset
test_dataset = TestDataset(test_path, transform=transforms.ToTensor()) # ici je transforme les images en Tensor pour une utilisation rapide de PyTorch Dataloader 

# on aura besoin d'un dataloader qui enveloppe notre objet test_dataset
test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=4) # souvent le shuffle est mis a False, mais avec notre implementation, ca marche dans tous les cas
# on itere sur le dataloader pour récuperer une batch
batch, names = next(iter(test_loader))

i = 6 # choisir une image dans la batch
print(names[i]) # afficher le nom de l'image
transforms.ToPILImage()(batch[i]) # afficher l'image correspondante à partir de la batch
# affichons l'image a partir du dossier test et voir si c'est la meme
nom_image = names[i]+'.jpg' # rajouter l'extension
Image.open(test_path + '/' + nom_image)
"""
# Generation du fichier de prédiction
Pour chaque batch que l'on passe au model, nous gardons dans une liste, les noms des images et les labels prédits   
Le résultats pourrait ressembler à ça  
image_names   `['00000', '00001', '00002', ..., '09453']`   
label_predictions   `[1, 0, 4, ..., 1]`

"""
# generons une prediction aleatoire pour notre test

label_predictions = []
image_names = []
for batch,im_names in test_loader:
    # remplir avec des predictions aleatoires (entre 0 et 4)
    random_preds = [np.random.randint(0, 5) for _ in range(len(batch))]
    label_predictions.extend(random_preds) 
    
    # retenir les noms des images
    image_names.extend(im_names) 

assert len(label_predictions) == len(image_names) 
assert len(label_predictions) == len(test_dataset) # est-ce qu'on a prédits tous les exemples de tests ?
print(f'Il y a {len(label_predictions)} exemples de test')
# Utilisons Pandas afin de generer un DataFrame

predictions_df = pd.DataFrame(data=zip(image_names, label_predictions), columns=['image_name', 'class_label'])
predictions_df
# mais il faudra d'abord traduire les class_label en nom de classes
# on peut utiliser le dictionnaire idx_to_class calculé au départ
predictions_df['class'] = predictions_df['class_label'].map(idx_to_class)
predictions_df
# on drop la colonne class_label avant d'entregistrer en fichier CSV
predictions_df = predictions_df.drop(labels=['class_label'], axis=1)
# enregistrer dans fichier de format CSV
# N'OUBLIEZ PAS DE METTRE index=None
predictions_df.to_csv('./mes_predictions.csv', index=None)