#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Le module DBA.py permet l'acces à la base de donnée
"""
import pgdb
import pg
import random
import ConfigParser
#import gwa.main
from traceback import format_exc 
[docs]class DBConnect:
    """
    La base de donée s'appelle 'wifi' et se trouve sur la machine
    """
    def __init__(self,main=None):
        """
        Initialisation de l'objet DBConnect
        """
	self.main_app=main
        #lecture du fichier de configuration
        config = ConfigParser.RawConfigParser()
        #si on arrive a lire le fichier
        if config.read(self.main_app.gwa_path + '/gwa.conf'):
            self.host = config.get('BDD','ip')
            self.password = config.get('BDD','password')
            self.user = config.get('BDD','user')
            self.dbname = config.get('BDD','base')
        else: #sans fichier de conf
            self.dbname = 'gwa'
            self.host    = 'localhost'
            self.user    = 'gwa_user'
            self.password = 'gwa_pass'
        self.connectionObject=None
        self.table_col = {}
        self.__create_dico()
    def __connect(self):
        """
        Cette fonction privée permet de se connecter à une base de donnée
        """
        try:
            self.connectionObject = pgdb.connect(host=self.host, database=self.dbname, user=self.user, password=self.password)
        except pg.InternalError:
            print "Impossible de se connecter sur la BDD, verifier les arguments\n", format_exc()
            exit(0)
    def __disconnect(self):
        """
        Cette fonction privée permet de se déconnecter de la base de donnée.
        """
        try:
            self.connectionObject.close()
        except pg.InternalError:
            print "Erreur lors de la déconexion de la BDD\n", format_exc()
            exit(0)
    def __create_colonne_string(self,liste_colonne):
        """
        Cette fonction privée permet de transformer une liste en chaine de caractère pour permettre
        l'injection de requêtes SQL. Cette liste représente les table de la BDD.
        """
        colonne = ''
        for nom in liste_colonne:
            colonne = colonne + nom + ','
        colonne = colonne[:-1]
        return colonne
    def __create_values_string(self,liste_value):
        """
        Cette fonction privée permet de transformer une liste en chaine de caractère pour permettre
        l'injection de requêtes SQL. Cette liste représente les valeurs contenues dans une table.
        """
        values = '('
        for val in liste_value:
            if type(val) == int:
                values = '%s %i,' % (values, val)
            elif type(val) == str:
                values = values + "'" + val + "'" + ','
            elif type(val) == unicode:
                values = values + "'" + val.encode("latin1") + "'" + ","
        values = values[:-1]
        values = values + ')'
        return values
    def __create_condition_string(self,dico_condition):
        """
        Cette fonction privée permet de mettre en forme les conditions stockées dans un dictionnaire pour qu'elles
        soient insérée dans une requête SQL.
        """
        condition = ''
        for var in dico_condition:
            condition = condition+var+'='+str(dico_condition[var])+' AND '
        condition = condition[:-5]
        return 'WHERE '+condition
    def __create_order_string(self,order_by):
        """
        Cette fonction privée permet de mettre en forme l'ordre dans lequel on souhaite recevoir les informations
        de la base de données.
        """
        order = 'ORDER BY '+order_by[0]+' '+order_by[1]
        return order
[docs]    def insert(self,table,col_value):
        """
        La fonction "insert" permet d'ajouter dans la BDD un objet par l'intermediaire d'un dictionnaire
        :arguments: 
            - table (type string)
                Choix de la table qui va recvoir l'objet
            - col_value (type dictionnaire)
                Objet à inserer
        **exemple**
 
        >>> table = 'lieux'
        >>> col_value={'nom':'Evry','largeur':13,'longueur':32,'coor_x':8,'coor_y':2,'description':'Evry'}
        DBA.DBConnect().insert(table,col_value)
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        col_names = []
        col_vals = []
        for key, value in col_value.items():
           col_names.append(key)
           col_vals.append(value)
        #print col_names, col_vals
        cursor.execute('INSERT INTO %s (%s) VALUES %s' % (table,self.__create_colonne_string(col_names),\
                            
self.__create_values_string(col_vals)))
        self.connectionObject.commit()
        cursor.close()
        self.__disconnect()
         
[docs]    def insertAndReturn(self,table,col_value, value_return):
        """
        La fonction "insertAndReturn" permet d'ajouter dans la BDD un objet par l'intermediaire d'un dictionnaire
        :arguments: 
            - table (type string)
                Choix de la table qui va recvoir l'objet
            - col_value (type dictionnaire)
                Objet à inserer
            - value_return (type string)
                Choix de la valeur de retour lors d'un INSERT
        
        :return:
            La fonction retourne une liste de liste
        **exemple**
        >>> value_return = 'id_lieu'
        >>> table = 'lieux'
        >>> col_value={'nom':'Evry','largeur':13,'longueur':32,'coor_x':8,'coor_y':2,'description':'Evry'}
        id = DBA.DBConnect().insert(table,col_value,value_return )
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        col_names = []
        col_vals = []
        for key, value in col_value.items():
           col_names.append(key)
           col_vals.append(value)
        cursor.execute('INSERT INTO %s (%s) VALUES %s RETURNING %s' % (table,self.__create_colonne_string(col_names),\
                            
self.__create_values_string(col_vals), value_return))
        value = cursor.fetchall()
        self.connectionObject.commit()
        cursor.close()
        self.__disconnect()
        
        return value
 
[docs]    def select(self,table,colonne='*',condition='',order_by=''):
        """
        La fonction "select" permet de faire des recherches sur la BDD.
        Ces recherches peuvent se faire avec des conditions précises et l'affichage peut être général ou restrictif. 
        
        :arguments:
            -table (type string ou liste)
                Choix de la table contenant l'objet
        :arguments optionnels:
            - colonne (type string ou liste)
                Choix des colonnes souhaité. Par defaut, elles sont toutes retournée
            - condition (type dictionnaire)
                Insertion d'une condition dans la sélection
            - order_by (type tuple)
                Choix du paramètre sur lequel l'ordre de la requête sera appliquée avec le paramètre ASC ou DESC
        :retour:
            La fonction retourne une liste de liste
        :infos:
            Dans le cas d'une condition avec comparaison de string, mettre le champs string entre quote
        **exemple**
        >>> table = 'place'
        >>> colonne = '*'
        >>> condition = { 'name':'\'Evry\'' }
        >>> order_by = ('id_place','DESC')
        "DBA.DBConnect().select(table,colonne,condition,order_by)"
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        if type(table) == list:
            table = self.__create_colonne_string(table)
        if type(colonne) == list:
            colonne = self.__create_colonne_string(colonne)
        if type(condition) == dict:
            condition =  self.__create_condition_string(condition)
        if type(order_by) == tuple:
            order_by = self.__create_order_string(order_by)
        liste=[]
        try:
            cursor.execute('SELECT %s FROM %s %s %s' %(colonne,table,condition,order_by))
            self.connectionObject.commit()
            liste = cursor.fetchall()
        except pg.DatabaseError:
            print "Erreur sur la requete de selection dans la base de donnee", format_exc()
        cursor.close()
        self.__disconnect()
        return liste
 
[docs]    def delete(self,table,row):
        """
        La fonction "delete" permet de supprimer des données dans la BDD.
        :arguments:
            - table (type string)
                Choix de la table contenant l'objet
            - row (type dico)
                Dictionnaire avec en clé le nom de la colonne et en value la valeur...
        **exemple**
        >>> table = 'place'
        >>> id_sup = 12
        DBA.DBConnect().delete(table,{'id_place':id_sup})
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        try:
            contrainte = row.popitem()
            requete = 'DELETE FROM %s WHERE %s = \'%s\'' % (table, contrainte[0], contrainte[1])
            cursor.execute( requete )
            self.connectionObject.commit()
        except pg.DatabaseError:
            print "Erreur sur la requette de suppression", format_exc()
        cursor.close()
        self.__disconnect()
 
    def __create_dico(self):
        """
        Cette fonction privée permet de récupérer les informations de la base de donnée sous forme de dictionnaire.
        On y retrouvera donc chaque colonne de la base avec ses attributs.
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        cursor.execute( 'SELECT tablename FROM pg_tables WHERE (tablename NOT LIKE \'pg_%\') AND (tablename NOT LIKE \'sql_%\')' )
        self.connectionObject.commit()
        liste = cursor.fetchall()
        self.table_col = {}
        for i in liste:
            cursor.execute('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=\'%s\'' % i[0])
            self.connectionObject.commit()
            liste_temp = []
            for col_temp in cursor.fetchall():
                liste_temp.append(col_temp[0])
            self.table_col[i[0]]=liste_temp
        cursor.close()
        self.__disconnect()
        
[docs]    def update(self,table,colonne,value,condition):
        """
        La fonction "update" permet de mettre à jour des objets de la BDD
        :arguments:
            - table (type string ou liste)
                Choix de la table contenant l'objet à mettre à jour
            - colonne (type string ou liste)
                Choix de la colonne à modifier
            - value (type string ou integer)
                Choix de la nouvelle valeur à inserer
            - condition (type dictionnaire)
                Paramétrage d'une condition
        **exemple**
        >>> table = 'measure'
        >>> colonne = 'signal_level'
        >>> value = -91
        >>> condition = {'coor_x':'2', 'coor_y':'0'}
        DBA.DBConnect().update(table,colonne,value,condition)
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        if type(table) == list:
            table = self.__create_colonne_string(table)
        if type(colonne) == list:
            colonne = self.__create_colonne_string(colonne)
        if type(condition) == dict:
            condition =  self.__create_condition_string(condition)
        cursor.execute('UPDATE %s SET %s = %s %s' % (table,colonne,value,condition))
        self.connectionObject.commit()
        cursor.close()
        self.__disconnect()
 
[docs]    def custom(self,requete):
        """
        La fonction 'custom' permet d'envoyer une requête SQL de son choix sur la base de donnée
        :argument:
            -requete (type SQL)
        """
        self.__connect()
        cursor = self.connectionObject.cursor()
        cursor.execute( requete )
        self.connectionObject.commit()
        try:
            liste = cursor.fetchall()
        except pgdb.DatabaseError, exc:
          print "Exception dans requete custom:\n", format_exc()
          liste =[]
        cursor.close()
        self.__disconnect()
        return liste
  
[docs]class Fill:
    """
    La classe 'Remp_mes' permet de remplir la table de mesure d'une campagne avec des valeurs à -100db.
    Le remplissage des valeurs se fait par rapport à la campagne, à la taille du lieux et au step choisit.
    Avant de remplir les valeurs dans la campagne, on supprime les eventuelles données existantes.
    :argument:
        id_campagne (parametre type integer)
            ID de la campagne pour lequel les valeurs seront inserées
    """
    def __init__(self, id_camp, matrice):
        db = DBConnect()
        coorX = coorY = 0
        for x in matrice:
            coorY = 0
            for y in x:
                if y == 0:
                    col_value={'id_campagne':id_camp,'coor_x':coorX,'coor_y':coorY,'power':-100}
                    db.insert('mesure', col_value)
                coorY +=1 
            coorX += 1
 
if __name__=="__main__":
    info = raw_input("Affichage de la base -> 0\n\
    Remplissage de la base -> 1,\n\
    Lecture de la base -> 2\n\
    Suppression d'infos de la base -> 3\n\
    Mise à jour des puissances -> 4\n")
    db = DBConnect()
    if info == '0':
        print db.table_col
    elif info == '1':
        col_value={'id_area':100,'name':'Evry','width':13,'length':32,'height':5,'x_coordinate_area':8,'y_coordinate_area':34,'z_coordinate_area':5,'description':'Salle 3456 Evry','origin_info':'origine !!!'}
        db.insert('area',col_value)
        print "Objet ajouté dans la base de donnée"
    elif info == '2':
        dico_test = { 'name':'\'Evry\'' }
        print db.select('area','*',dico_test,('id_area','DESC'))
        print "Lecture de la table lieux"
    elif info == '3':
        db.delete('area',{'id_area':100})
        print "Objet supprimé"
    elif info == '4':
        dico_test={'x_coordinate_area':'8', 'y_coordinate_area':'34'}
        db.update('area','height',345678965,dico_test)
        print "Objet modifié"