#!/usr/bin/python
# -*- coding: UTF-8 -*-


"""
    Code pour le test de l'utilisation de fichiers au format CSV.
"""


###############################################################################
# Informations de versions
###############################################################################
__project__   = 'Formation Python'
__author__    = 'Simon CHOLLET (s.chollet@llr.in2p3.fr)'
__modifiers__ = ''
__date__      = '31/08/2011'
__version__   = '1.0'


###############################################################################
# Importations
###############################################################################
# Importation du module CSV
import csv

# Importation du module sys
import sys

# Importation du module operator
import operator


###############################################################################
# Definitions specifiques
###############################################################################


###############################################################################
# Declaration des classes / fonctions
###############################################################################

# Definition de la classe personnalisee de lecture
class DataCSV(list):
    """
        Cette classe permet de lire, charger et ecrire le contenu
        d'un fichier CSV.
        -----------------------------------------------------------------------
        Attributs :
            - m_csv_path   : Chemin du fichier CSV a lire/ecrire.
            - m_dialect    : Dialecte utilise.
            - m_fieldnames : Noms des champs d'en-tete.
    """

    # Attributs avec initialisation par defaut
    m_csv_path   = ""
    m_dialect    = None
    m_fieldnames = None


    def __init__(self, csv_path):
        """
            Constructeur
            -------------------------------------------------------------------
            Arguments :
                - csv_path : Chemin vers le fichier a analyser.
            -------------------------------------------------------------------
            Retour : Aucun.
        """

        # Mise a jour des attributs
        self.m_csv_path = csv_path

        # Ouverture du fichier CSV
        csv_file = open(self.m_csv_path, "r")

        # Recherche automatique du dialecte
        self.m_dialect = csv.Sniffer().sniff(csv_file.readline())

        # Retour au debut du fichier
        csv_file.seek(0)

        # Lancement de la lecture
        reader = csv.DictReader(csv_file, restval="???", dialect=self.m_dialect)

        # Les champs d'en-tete
        self.m_fieldnames = reader.fieldnames

        # Remplit le dictionnaire
        try:
            # Pour toutes les donnees extraites
            for item in reader:
                if type(item) == dict and len(item) > 0:
                    # Test sur la validite de la donnee
                    if item.has_key(''):
                        del item['']
                    if item.has_key(None):
                        del item[None]

                    # Ajout de la donnee au dictionnaire
                    self.append(item)
        except csv.Error, e:
            # Sort du programme avec affichage du message d'erreur
            sys.exit("ERREUR : Fichier %s, ligne %d : %s") % (csv_path, reader.line_num, e)

        # Fermeture du fichier CSV
        csv_file.close()

    def __repr__(self):
        """
            Methode appelee lors de la demande d'affichage de la classe
            -------------------------------------------------------------------
            Arguments : Aucun.
            -------------------------------------------------------------------
            Retour :
                Chaine de caracteres a afficher.
        """
        # Pour tous les elements de la liste
        disp = ""
        for item in self:
            disp += item.__repr__() + "\n"

        # Retourne la chaine a afficher
        return disp

    def WriteFile(self, csv_path=None, dialect=None):
        """
            Methode qui permet d'ecrire le contenu de la classe vers un fichier
            au format CSV.
            -------------------------------------------------------------------
            Arguments :
                - csv_path : Chemin vers le fichier a analyser, None par defaut
                             -> utilise alors le chemin de fichier courant.
                - dialect  : Le dialecte a utiliser, None par defaut -> utilise
                             alors le dialecte courant.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Reglage des parametres
        if csv_path is None:
            csv_path = self.m_csv_path
        if dialect is None:
            dialect = self.m_dialect

        # Ouverture du fichier en ecriture
        csv_file = open(csv_path, "w")

        # Selon le dialecte
        if dialect in ["excel", "excel-tab", self.m_dialect]:
            writer = csv.DictWriter(csv_file,
                                    self.m_fieldnames,
                                    dialect=dialect,
                                    extrasaction='ignore')
        else:
            writer = csv.DictWriter(csv_file,
                                    self.m_fieldnames,
                                    delimiter=";",
                                    quotechar="'",
                                    quoting=csv.QUOTE_MINIMAL,
                                    extrasaction='ignore')

        # Insertion de l'en-tete dans les donnees a ecrire
        self.insert(0, dict((i, i) for i in self.m_fieldnames))

        # Enregistrement des donnees
        writer.writerows(self)

        # Fermeture du fichier
        csv_file.close()

    @staticmethod
    def Write(csv_path, headers, datas, dialect=None):
        """
            Methode qui permet d'ecrire le contenu de la classe vers un fichier
            au format CSV.
            -------------------------------------------------------------------
            Arguments :
                - csv_path : Chemin vers le fichier a analyser.
                - headers  : L'en-tete de fichier.
                - datas    : Les donnees a ecrire.
                - dialect  : Le dialecte a utiliser.
            -------------------------------------------------------------------
            Retour : Aucun.
        """
        # Ouverture du fichier a ecrire
        csv_file = open(csv_path, "w")

        # Selon le dialecte
        if dialect in ["excel", "excel-tab"]:
            writer = csv.DictWriter(csv_file,
                                    headers,
                                    dialect=dialect,
                                    extrasaction='ignore')
        else:
            writer = csv.DictWriter(csv_file,
                                    headers,
                                    delimiter=";",
                                    quotechar="'",
                                    quoting=csv.QUOTE_MINIMAL,
                                    extrasaction='ignore')

        # Insertion de l'en-tete dans les donnees a ecrire
        datas.insert(0, dict((i, i) for i in headers))

        # Enregistrement des donnees
        writer.writerows(datas)

        # Fermeture du fichier
        csv_file.close()
        


#
#
# Fonction appelee lors de la demande
# d'execution de la classe/module
if __name__ == "__main__":

    ###########################################################################
    # CLASSE DICTREADER
    ###########################################################################

    # Le chemin du fichier CSV a lire
    csv_path = "../input/short_ex.csv"

    # Ouvre le fichier CSV
    csv_file = open(csv_path)

    # L'objet de lecture
    reader = csv.DictReader(csv_file,
                            delimiter=";",
                            quotechar="'",
                            quoting=csv.QUOTE_MINIMAL)

    # Affichage de tous les elements trouves
    print ">>> Utilisation DictReader 1 <<<"
    for item in reader:
        print item

    # Affiche :
    #   >>> Utilisation DictReader 1 <<<
    #   {'nom': 'CHOLLET', 'identifiant': '007', 'prenom': 'Simon'}
    #   {'nom': 'DUPONT', 'identifiant': '123456', 'prenom': None}
    #   {'nom': 'MARTIN', 'identifiant': '0123', 'prenom': 'Jean'}
    #   {None: ['En', 'Plus'], 'nom': 'DUPONG', 'identifiant': '456789', 'prenom': 'Camille'}


    # Revient au debut du fichier
    csv_file.seek(0)

    # L'objet de lecture
    reader = csv.DictReader(csv_file,
                            delimiter=";",
                            restkey="autre",
                            restval="???")

    # Affichage de tous les elements trouves
    print ">>> Utilisation DictReader 2 <<<"
    for item in reader:
        print item

    # Affiche :
    #   >>> Utilisation DictReader 2 <<<
    #   {'nom': 'CHOLLET', 'identifiant': '007', 'prenom': 'Simon'}
    #   {'nom': 'DUPONT', 'identifiant': '123456', 'prenom': '???'}
    #   {'nom': 'MARTIN', 'identifiant': '0123', 'prenom': 'Jean'}
    #   {'autre': ['En', 'Plus'], 'nom': 'DUPONG', 'identifiant': '456789', 'prenom': 'Camille'}


    # Ferme le fichier CSV
    csv_file.close()


    ###########################################################################
    # Utilisation d'un dialecte

    # Ouvre le fichier CSV
    csv_file = open(csv_path)

    # Recherche du dialecte
    dialecte = csv.Sniffer().sniff(csv_file.readline())

    # Revient au debut du fichier
    csv_file.seek(0)

    # L'objet de lecture
    reader = csv.DictReader(csv_file,
                            delimiter=";",
                            restkey="autre",
                            restval="???",
                            dialect=dialecte)

    # Affichage de tous les elements trouves
    print ">>> Utilisation DictReader 3 <<<"
    for item in reader:
        print item

    # Affiche :
    #   >>> Utilisation DictReader 3 <<<
    #   {'nom': 'CHOLLET', 'identifiant': '007', 'prenom': 'Simon'}
    #   {'nom': 'DUPONT', 'identifiant': '123456', 'prenom': '???'}
    #   {'nom': 'MARTIN', 'identifiant': '0123', 'prenom': 'Jean'}
    #   {'autre': ['En', 'Plus'], 'nom': 'DUPONG', 'identifiant': '456789', 'prenom': 'Camille'}

    # Ferme le fichier CSV
    csv_file.close()


    ###########################################################################
    # UTILISATION CLASSE PERSONNALISEE
    ###########################################################################

    ###########################################################################
    # Utilisation simple et affichage
    # Objet de lecture
    data_reader = DataCSV(csv_path)

    # Affichage du contenu de l'objet
    print ">>> Utilisation DataCSV 1 <<<"
    print data_reader

    # Affiche :
    #   >>> Utilisation DataCSV 1 <<<
    #   {'nom': 'CHOLLET', 'identifiant': '007', 'prenom': 'Simon'}
    #   {'nom': 'DUPONT', 'identifiant': '123456', 'prenom': None}
    #   {'nom': 'MARTIN', 'identifiant': '0123', 'prenom': 'Jean'}
    #   {'nom': 'DUPONG', 'identifiant': '456789', 'prenom': 'Camille'}

    ###########################################################################
    # Utilisation avec iteration
    print ">>> Utilisation DataCSV 2 <<<"
    for item in data_reader:
        print "%(nom)s %(prenom)s (%(identifiant)s)" % (item)

    # Affiche :
    #   >>> Utilisation DataCSV 2 <<<
    #   CHOLLET Simon (007)
    #   DUPONT ??? (123456)
    #   MARTIN Jean (0123)
    #   DUPONG Camille (456789)

    ###########################################################################
    # Tri des elements par numero d'identifiant
    data_reader.sort(key=operator.itemgetter("identifiant"))
    print ">>> Utilisation DataCSV 3 <<<"
    for item in data_reader:
        print "%(nom)s %(prenom)s (%(identifiant)s)" % (item)

    # Affiche :
    #   >>> Utilisation DataCSV 3 <<<
    #   CHOLLET Simon (007)
    #   MARTIN Jean (0123)
    #   DUPONT ??? (123456)
    #   DUPONG Camille (456789)

    # Tri des elements par nom
    data_reader.sort(key=operator.itemgetter("nom"))
    print ">>> Utilisation DataCSV 4 <<<"
    for item in data_reader:
        print "%(nom)s %(prenom)s (%(identifiant)s)" % (item)

    # Affiche :
    #   >>> Utilisation DataCSV 4 <<<
    #   CHOLLET Simon (007)
    #   DUPONG Camille (456789)
    #   DUPONT ??? (123456)
    #   MARTIN Jean (0123)

    ###########################################################################
    # Affichage selon un champ
    new_dict = {}
    for item in data_reader:
        new_dict[item["identifiant"]] = {"name": item["nom"], "surname": item["prenom"]}

    # Affichage personnalise
    # Affiche :
    #   >>> Utilisation DataCSV 5 <<<
    #   {'surname': 'Simon', 'name': 'CHOLLET'}
    #   {'surname': 'Camille', 'name': 'DUPONG'}
    print ">>> Utilisation DataCSV 5 <<<"
    print new_dict["007"]
    print new_dict["456789"]


    ###########################################################################
    # CLASSE DICTWRITER
    ###########################################################################

    # Le fichier a ecrire
    csv_path_w = "../output/short_ex_sorted.csv"

    # Lecture du fichier CSV non-ordonne
    datas = DataCSV(csv_path)

    # Tri des donnees
    datas.sort(key=operator.itemgetter("identifiant"))

    # Un objet pour ecrire
    writer = csv.DictWriter(open(csv_path_w, "w"),
                            ("nom", "prenom", "identifiant", ),
                            delimiter=";",
                            quotechar="'",
                            quoting=csv.QUOTE_MINIMAL)

    # Pour toutes les donnees a ecrire
    for item in datas:
        writer.writerow(item)


    ###########################################################################
    # UTILISATION CLASSE PERSONNALISEE
    ###########################################################################

    ###########################################################################
    # Utilisation de la methode 'WriteFile()'
    # Le fichier a ecrire
    csv_path_w = "../output/short_ex_2.csv"

    # L'objet d'ecriture
    writer = DataCSV(csv_path)

    # Ecriture du contenu'
    writer.WriteFile(csv_path_w)

    ###########################################################################
    # Utilisation de la methode statique 'Write()'

    # Le fichier a ecrire
    csv_path_w = "../output/short_ex_3.csv"

    # Ecriture du contenu'
    DataCSV.Write(csv_path_w,
                  ("nom", "prenom", "identifiant", ),
                  DataCSV(csv_path))
