sábado, 31 de octubre de 2020

Tratamiento y visualización de ficheros con Python

En esta entrada voy a dar algunas pautas de utilización de ficheros con Python y de su tratamiento para analizar los datos estadísticos que estos contienen.
Para leer un archivo basta con hacer esto: 

file_for_reading = open('prueba.txt', 'r')

Como se puede apreciar, no escribimos el directorio, la instrucción se lee del directorio raíz de la instalación de Anaconda que suele ser C:\Usuarios\usuario\
En mi caso particular es  C:\Users\Administrador

Para escribir:

file_for_writing = open('prueba.txt', 'w')

para añadir datos

file_for_appending = open('prueba.txt', 'a')

Cuando escribimos no hay que olvidarse de cerrar el fichero

file_for_writing.close()

Para evitar que se nos olvide cerrar el archivo podemos hacer una función que lo cierre siempre y llamar a la función.

#estructura de lectura de ficheros
with open(nombre_fichero,'r') as f:
datos = function_que_lee_los_datos(f)
# aquí el fichero ya está cerrado pero operamos con el objeto que contiene sus datos (data)

leemos un fichero de principio a fin de este modo  

import re

comienza_con_hash = 0
     with open('input.txt','r') as f:
         for linea in f: # iteramos cada línea del fichero                 if re.match("^#",linea): # si es un comentario no la leemos (las que empiezan con '#'
                       comienza_con_hash += 1 # si comienza con # añadimos 1 al Contador de líneas para r a la siguiente

el ejemplo completo sería de este estilo

#funcion de lectura de ficheros
def function_que_lee_los_datos(f):
    import re
    comienza_con_hash = 0
    with open('prueba.txt','r') as f:
        for linea in f: comienza_con_hash += 1
    f.close()

Del mismo modo podemos crear una función que procese los datos utilizando el objeto Datos

Process(datos)

Leer y visualizar un fichero .csv

Un archivo csv es un archivo cuyas columnas están separadas por comas “,” una forma sencilla de montar un archivo csv es crearlo desde Excel y luego lo abrimos con  notepad++ donde le añadimos las comas* “,” y lo renombramos como  .csv

*Ojo, Hay que asegurarse de que nuestro Excel genere los .csv con comas, pues puede estar configurado (sobre todo en castellano) con punto y coma ‘;‘ para no confundirlo con las comas ‘,’  decimales. Para evitar problemas, sustituiremos las comas decimales ‘,’ por puntos ‘.’ Y posteriormente sustituiremos los punto y coma ‘;’ por comas ‘,’ de este modo el interprete de Jupyter ya leerá correctamente nuestro archivo .csv.

El archivo csv debe estar en el directorio de trabajo que como hemos dicho suele ser C:\Usuarios\Usuario_principal
Una vez colocado aquí el fichero .csv podemos leerlo con 

import pandas as pd
def load_retiro():
    return pd.read_csv("retiro.csv")

En nuestro caso se trata de las temperaturas del observatorio de Madrid -Retiro podemos ver las primeras líneas del fichero con estas líneas

datos = load_retiro()
datos.head()


Tratamiento de ficheros con Python

para mostrar la información del archivo

datos.info

Y para mostrar una descripción de los datos ponemos:

datos.describe()

Visualizar datos de un archivo .csv con Python


Count nos muestra el número de filas, mean el valor medio, std muestra la desviación estándar, min el valor mínimo, 25% muestra el percentil 25% , etc y max el máximo.
Las siguientes instrucciones nos mostraran unos histogramas mostrando en número de veces que aparece un valor dentro de un rango.

%matplotlib inline
import matplotlib.pyplot as plt
datos.hist(bins=13, figsize=(20,15))
plt.show()

Visualizar histogramas de un archivo con Python

Por ejemplo enero muestra el número de veces que aparecen datos por debajo de 2 ºC , entre 2 y 2,5 ºC , entre 2,5 y 3 ºC etc.
Hay muchas más funciones de Phyton en diferente librerías con la que obtener un montón de información. Pero para obtener gráficos se utiliza mucho matplolib que es muy completa

La instrucción value_counts() nos permite examinar los valores que toma una columna del dataset y cuantas veces aparece dicho valor en todo el dataset. 

Para dividir el dataset en dos partes, una de entrenamiento y otra de pruebas

#dividimos el dataset en dos, uno de entrenamiento con el 80% de los datos y otro de testeo con el 20%.

#Random state es una semilla aleatoria

from sklearn.model_selection import train_test_split

train_set, test_set = train_test_split(datos, test_size=0.2, random_state=42)

Seleccionar y entrenar el modelo

#tomamos como entrenamiento desde el principio hasta el297 y como tets desde el 297 hasta el final

X_train, X_test, y_train, y_test = X[:297], X[297:], y[:297], y[297:], AÑO

lunes, 26 de octubre de 2020

AWS in a Nutshell 4: Criptografia y PKIs

 La criptografía existe para proteger los datos. Proporciona confidencialidad de los datos para garantizar que solo las partes autorizadas puedan ver la información confidencial. Proporciona integridad para que podamos estar seguros de que los datos no han sido manipulados. Y también autenticación de origen para que podamos estar seguros de que los datos provienen de quien dice que provienen. La integridad y la autenticación vienen en muchas formas, como el hash de archivos que se utiliza para verificar la integridad de los archivos. Firmas digitales de correo electrónico, que utilizan hash y autenticación para verificar que un mensaje proviene de quien dice que proviene.

4.1 Infraestructura de Clave Pública

La infraestructura de clave pública, o jerarquía PKI, es una jerarquía de certificados de seguridad digital. Estos certificados son emitidos y administrados por autoridades de certificación, que también se denominan CA. Hay CA privadas que se pueden configurar dentro de una organización para emitir certificados privados. Y también hay CA públicas disponibles en Internet que hacen lo mismo. La diferencia es que los dispositivos confiarán automáticamente en las CA públicas y en los certificados que emitan, donde no confiarán automáticamente en los certificados emitidos por una CA privada.

Criptografia y PKIs

Claves pública y privada

4.2 Descripción general del servicio de administración de claves

El Servicio de administración de claves de AWS, también denominado KMS, está diseñado para facilitar el uso de claves con los servicios de AWS. Las claves se pueden utilizar para cifrado y firma digital. AWS Key Management Service también admite módulos de seguridad de hardware compatibles con FIPS 140-2 o HSM. Un HSM es un dispositivo de hardware a prueba de manipulaciones que está diseñado para el almacenamiento seguro de secretos criptográficos.

4.3 Creación de una clave KMS

En esta demostración, usaremos la Consola de administración de AWS para crear una clave KMS.

Desde la consola de AWS ponemos kms

Creación de una clave KMS

4.4 Cifrado de volumen EBS

Podemos utilizar AWS para cifrar los volúmenes de EBS que utilizan las instancias EC2.

Por supuesto, dentro del sistema operativo de la instancia, también podemos habilitar el cifrado de volumen de disco completo. 

Consola → EC2 → Elastic Block Store → Volumes → Create volume 

4.5 Módulos de seguridad de hardware

Un módulo de seguridad de hardware, o HSM, como su nombre lo indica, es una pieza de hardware diseñada para almacenar claves secretas criptográficas. Ahora también ofrece soluciones a prueba de manipulaciones. Y almacena las claves de tal manera que no se pueden manipular sin registrarnos en alguna parte. Los módulos de seguridad de hardware también se pueden configurar o implementar a través de AWS Cloud HSM.

4.6 Descripción general del administrador de certificados

AWS Certificate Manager, o ACM, es una PKI o solución de infraestructura de clave pública en la nube de AWS y es un servicio administrado. Lo que significa que no tenemos que preocuparnos por la configuración subyacente. Se  utiliza para la administración de certificados PKI públicos, así como para la administración de certificados PKI privados. Lo que hay que pensar con ACM es que podemos implementar y administrar una autoridad de certificación privada, o CA, donde los certificados no son confiables de forma predeterminada.

4.7 Configuración de CA privada

En una infraestructura de clave pública, las autoridades de certificación se utilizan para emitir certificados PKI. Y podemos implementar una CA privada directamente a través de la Consola de administración de AWS.

Para acceder desde AWS entramos en la consola y ponemos:

acm → private certificate authority  y pulsamos en Get started

Configuración de CA privada

Configuración de CA privada

4.8 Solicitar un certificado privado

Para solicitar un certificado privado desde la consola escribimos acm → certificate manager

Solicitar un certificado privado AWS

Y elegimos certificado privado 

Solicitar un certificado privado AWS

4.9 Solicitudes de certificados públicos

Para solicitar un certificado público desde la consola 

escribimos acm → certificate manager

Consola → acm → certificate manager

Elegimos certificado público

Solicitudes de certificados públicos AWS

4.10 HTTPS y el balanceador de carga de aplicaciones

En esta demostración, habilitaremos un equilibrador de carga HTTPS.

Antes de que podamos hacer eso, primero necesitamos un certificado PKI que se utilizaremos para asegurar la conexión usando HTTPS en el puerto 443. Echemos un vistazo a algunos elementos existentes que ya están en su lugar. Vamos a la consola de administración de EC2, donde ya tenemos instancias de Windows en funcionamiento que admitirán la aplicación web.

Los pasos a seguir son:

Consola → Ec2 → instancias

Consola → acm → certificados

Consola → Ec2 → Load Balancers → create → aplication Load Balancer


sábado, 17 de octubre de 2020

Pantalla de mantenimiento con campos asociados



Ahora vamos a ampliar un poco más los conceptos y vamos a ver como hacer un enlace (binding) a otra tabla similar a un INNER JOIN de Oracle pero desde el código de Visual Basic. También veremos como posicionar en el DataGridView  las columnas donde deseemos independientemente de su orden de carga.

Finalmente añadiremos una sub-pantalla para rellenar dos campos de mantenimiento  desde otra pantalla con combos anidadas 

La pantalla de mantenimiento será una pantalla sencilla con un grid,  cuatro campos de filtrado y mantenimiento, los botones Alta, Baja, Modificación. Aceptar, Cancelar y finalmente un botón que nos llevará a la sub-pantalla con dos combos anidadas para la edición de los dos campos nivel 1 y nivel 2.

En el form load indicamos la asociación con los botones de Alta, Baja y Modificación.

Pantalla de mantenimiento con campos asociados
Si quieres esta foto.



Private Sub frmCamposaAsociados_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.grdResult.DataSource = bs

        txtColumna.MaxLength = 40
  

        CamposAsociados()
        btnModificar.Visible = IIf(ObjetosPermitidos.ContainsKey("btnModificar"), True, False)
        btnBaja.Visible = IIf(ObjetosPermitidos.ContainsKey("btnBaja"), True, False)
        btnAlta.Visible = IIf(ObjetosPermitidos.ContainsKey("btnAlta"), True, False)

        cboDataSource.Text = gDataSource
        btnCombosAnidadas.Enabled = False
    End Sub

Definimos un procedimiento donde leemos de la Base de Datos de Oracle y hacemos el enlace (binding) entre nuestra pantalla y la base de datos.

Sub CamposAsociados()


        Dim tb As DataTable
        Dim srSQL As StreamReader
        Dim strConsulta As String
        Dim cmdbuilder As OleDbCommandBuilder

        tb = New DataTable

        'Repeticion si no es la primera vez es que se ha llamado desde el mantenimiento y antes de cargarse debe borrarse la grid
        If Not bolPrimeraVez Then
            ds.Tables("CamposAsociados").Clear()
        End If
        '***********************

        strStartupPath = My.Application.Info.DirectoryPath
        srSQL = New System.IO.StreamReader(strStartupPath & "\CargarCamposAsociados".sql")
        strConsulta = srSQL.ReadToEnd
        cmdPD = New OleDb.OleDbCommand(strConsulta, cnxPD)
        dA.SelectCommand = cmdPD
        cmdbuilder = New OleDbCommandBuilder(dA)
        dA.Fill(ds, "CamposAsociados")

        If Not bolPrimeraVez Then
            Exit Sub
        End If

        bolPrimeraVez = False

        'Codigo nuevo para primer campo asociado
        srSQL = New System.IO.StreamReader(strStartupPath & "\CargarCampoAso1.sql")
        strConsulta = srSQL.ReadToEnd
        cmdPD = New OleDb.OleDbCommand(strConsulta, cnxPD)
        dA.SelectCommand = cmdPD
        dA.Fill(ds, "CampoAso1")

        cboLevel1.ValueMember = "COD_CAMPO1"
        cboLevel1.DisplayMember = "DES_CAMPO1"
        cboLevel1.DataSource = ds.Tables("CampoAso1").DefaultView

        'Codigo nuevo para primer campo asociado
        srSQL = New System.IO.StreamReader(strStartupPath & "\CargarCampoAso2.sql")
        strConsulta = srSQL.ReadToEnd
        cmdPD = New OleDb.OleDbCommand(strConsulta, cnxPD)
        dA.SelectCommand = cmdPD
        dA.Fill(ds, "CampoAso2")

        cboLevel1.ValueMember = "COD_CAMPO_SEC2"
        cboLevel1.DisplayMember = "DES_CAMPO2"
        cboLevel1.DataSource = ds.Tables("CampoAso2").DefaultView

      
        'Código para definir las relaciones con las tablas de los campos asociados ojo notar 'que COD_CAMPO1 se refiere a una columna de cada tabla, es decir COD_CAMPO1 de Tabla 'Relacionada y COD_CAMPO1 de tabla principal. Esto en la simulación por código del INNER 'JOIN

        ds.Relations.Add("CampoAso1_Relacion", ds.Tables("CampoAso1").Columns("COD_CAMPO1"),
ds.Tables("CamposAsociados").Columns("COD_CAMPO1"))

     ds.Relations.Add("CampoAso2_Relacion",ds.Tables("CampoAso2").Columns("COD_CAMPO_SEC2"), ds.Tables("CamposAsociados").Columns("COD_CAMPO_PRINC2"))

    
        Dim oNivel1 As DataColumn = New DataColumn
        oNivel1.DataType = System.Type.GetType("System.String")
        oNivel1.ColumnName = "CampoAso1"
        oNivel1.Expression = "Parent(CampoAso1_Relacion).DES_CAMPO1"
        ds.Tables(Tabla).Columns.Add(oNivel1)

        Dim oNivel2 As DataColumn = New DataColumn
        oNivel2.DataType = System.Type.GetType("System.String")
        oNivel2.ColumnName = "CampoAso2"
        oNivel2.Expression = "Parent(CampoAso2_Relacion).DES_CAMPO2"
        ds.Tables(Tabla).Columns.Add(oNivel2)

      'Aquí fuerzo de nuevo la select principal para que no me de error al realizar 'operaciones con el formulario una vez he cargado el data GridView
        dA.SelectCommand = New OleDb.OleDbCommand("SELECT  COD_ID, DES_PRINCIPAL, COD_CAMPO1, COD_CAMPO_PRINC2 FROM PROPIETARIO.TABLA_PRINCIPAL", cnxPD)

        dA.UpdateCommand = cmdbuilder.GetUpdateCommand()
        dA.DeleteCommand = cmdbuilder.GetDeleteCommand()
        dA.InsertCommand = cmdbuilder.GetInsertCommand()

        Try

            bs.DataSource = ds.Tables(Tabla)

       'Aquí hacemos el binding de los campos de la base de datos con los controles del formulario

            Me.txtColumna.DataBindings.Add(New Binding("Text", bs, "COD_ID"))
            Me.txtDescripcion.DataBindings.Add(New Binding("Text", bs, "DES_PRINCIPAL"))
            Me.cboLevel1.DataBindings.Add(New Binding("SelectedValue", bs, "COD_CAMPO1"))
            Me.cboLevel2.DataBindings.Add(New Binding("SelectedValue", bs, "COD_CAMPO_PRINC2"))
          
         
            grdResult.Columns(0).MinimumWidth = 50
            grdResult.Columns(1).MinimumWidth = 50  'Descripcion
            grdResult.Columns(2).MinimumWidth = 30  'Campo1
            grdResult.Columns(3).MinimumWidth = 200 'Campo2
        
          
            grdResult.Columns(0).HeaderText = "Identificador"
            grdResult.Columns(1).HeaderText = "Descripción"
            grdResult.Columns(2).HeaderText = "Campo asociado 1"
            grdResult.Columns(3).HeaderText = "Campo Asociado 2"

            grdResult.Refresh()

        Catch ex As Exception
        End Try

    End Sub

Las SELECT principal será de este estilo:

SELECT  COD_ID,  DES_PRINCIPAL, COD_CAMPO1, COD_CAMPO_PRINC2  FROM PROPIETARIO.TABLA_PRINCIPAL

Y las de las tablas secundarias serán así:

SELECT COD_CAMPO1, DES_CAMPO1 FROM PROPIETARIO.TABLA_SEC1

SELECT COD_CAMPO_SEC2, DES_CAMPO2 FROM PROPIETARIO.TABLA_SEC2

Definición del botón para las altas

Private Sub btnAlta_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAlta.Click
   
        grbDetalle.Enabled = True
        grbBuscar.Enabled = False
        btnCombosAnidadas.Enabled = False
        drEdit = bs.AddNew()
        bs.MoveLast()
 
        grdResult.Refresh()
    End Sub

Definición del botón para las bajas

Private Sub btnBaja_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBaja.Click
        Dim drv As DataRowView
        Dim b As Integer
       
        Try

            If MsgBox("¿Esta usted seguro que desea borrar este registro?", MsgBoxStyle.YesNo + MsgBoxStyle.Question, "Confirmación de Baja") = MsgBoxResult.Yes Then
                drv = bs.Current
                b = grdResult.CurrentRow.Index
                drv.Delete()
            End If
            dA.Update(ds, Tabla)
            grbDetalle.Enabled = False
            grdResult.Refresh()
        Catch ex As Exception
            If Mid(ex.Message, 1, 9) = "ORA-00001" Then
                MsgBox("El código  COD_ID debe ser único revise los datos e inténtelo de nuevo", MsgBoxStyle.Critical, "Error al actualizar los datos")
            End If
            If Mid(ex.Message, 1, 9) = "ORA-02292" Then
                MsgBox("No está permitido el borrado en cascada, asegúrese que el COD_ID no tiene Sub ID´s antes de borrarlo", MsgBoxStyle.Critical, "Error al actualizar los datos")
            End If
            Recargar()
        End Try
    End Sub

Botón para las modificaciones

Private Sub btnModificar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnModificar.Click
     
        grbDetalle.Enabled = True
        grbBuscar.Enabled = False
        
End Sub

Botón aceptar, graba las altas, bajas o modificaciones

Private Sub btnAceptar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAceptar.Click
        Dim drv As DataRowView
        Try
            drv = bs.Current

            drv("COD_CAMPO1") = cboLevel1.SelectedValue
            drv("COD_CAMPO_SEC2") = cboLevel2.SelectedValue
         
            Traduce(drv) 'traduce las descripciones a códigos
            bs.EndEdit()
            dA.Update(ds, Tabla)

            grbBuscar.Enabled = True
            grbDetalle.Enabled = False

            grdResult.Refresh()
            CamposAsociados() 'refresca los cambios
            bolRefrescar = False
        Catch ex As Exception
            If Mid(ex.Message, 1, 9) = "ORA-00001" Then
                MsgBox("El código COD_ID debe ser único revise los datos e inténtelo de nuevo", MsgBoxStyle.Critical, "Error al actualizar los datos")
            End If
            bolRefrescar = True
        End Try

End Sub

Botón cancelar

Private Sub btnCancelar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancelar.Click
        bs.CancelEdit()
        If bolRefrescar Then
            Recargar()
        End If
        grbDetalle.Enabled = False
        grbBuscar.Enabled = True
End Sub

Botón recargar

Sub Recargar()
     bolRefrescar = False
     ds.Tables(0).Clear()
     CamposAsociados()
     grdResult.Refresh()
End Sub

Finalmente este será el código del botón que llama a la pantalla con las dos combos anidadas que cargará los dos campos asociados.

Public Sub btnPaisDom_Click(sender As Object, e As EventArgs) Handles btnPaisDom.Click
        Dim d As New frmNiveles()  'envia los datos del grid al formulario hijo


     
        d.gID = txtColumna.Text
      
        d.cboLevel1.Text = cboLevel1.Text
        d.cboLevel2.Text = cboLevel2.Text

        d.cboLevel1.SelectedItem = cboLevel1.SelectedItem
        d.cboLevel2.SelectedItem = cboLevel2.SelectedItem

        d.ShowDialog()
       
        cboLevel1.Text = d.cboLevel1.Text
        cboLevel2.Text = d.cboLevel2.Text
      
        If Not d.cboLevel1.SelectedItem Is Nothing Then
            txtNivel1.Text = d.cboLevel1.SelectedItem.clave
        End If

        If Not d.cboLevel2.SelectedItem Is Nothing Then
            txtNivel2.Text = d.cboLevel2.SelectedItem.clave
        End If

        d.Close()
    End Sub

End Class

Pasamos al código de la pantalla frmNiveles que cargará dos combos anidadas.

Public Class frmNiveles
  
    Public gID As String

    Private Sub cboLevel1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboLevel1.SelectedIndexChanged
        cboLevel2.Items.Clear()
        cboLevel2.Text = ""

        frmNiveles.Cargacombo("\CargarCampoAso2.sql", cboLevel2, DirectCast(Me.cboLevel1.SelectedItem, AddItem).Clave.ToString())
        btnSeleccionar.Enabled = True
    End Sub

    Private Sub frmNiveles_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      
        cboLevel1.Items.Clear()
        cboLevel1.Text = ""
        cboLevel2.Items.Clear()
        cboLevel2.Text = ""

        frmNiveles.Cargacombo("\CargarCampoAso1.sql", cboLevel1, gID)
        btnSeleccionar.Enabled = True
    End Sub

    Private Sub btnSeleccionar_Click(sender As Object, e As EventArgs) Handles btnSeleccionar.Click
       
       Me.Hide()
    End Sub
End Class

El código completo del formulario principal es este:

Imports System.IO
Imports System.Data.OleDb
Imports System.Windows.Forms
Imports System.Data.SqlClient

Public Class frmCamposAsociados

    Public gDataSource As String
    Dim ds As New DataSet()
    Dim bs As BindingSource = New BindingSource()
    Dim dA As New OleDb.OleDbDataAdapter
    Dim drEdit As DataRowView
    Dim bolRefrescar As Boolean
    Dim bolPrimeraVez As Boolean = True
    Const Tabla = "CamposAsociados"

     Private Sub frmCamposaAsociados_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.grdResult.DataSource = bs

        txtColumna.MaxLength = 40
  

        CamposAsociados()
        btnModificar.Visible = IIf(ObjetosPermitidos.ContainsKey("btnModificar"), True, False)
        btnBaja.Visible = IIf(ObjetosPermitidos.ContainsKey("btnBaja"), True, False)
        btnAlta.Visible = IIf(ObjetosPermitidos.ContainsKey("btnAlta"), True, False)

        cboDataSource.Text = gDataSource
        btnCombosAnidadas.Enabled = False
    End Sub

    Sub CamposAsociados()

        Dim tb As DataTable
        Dim srSQL As StreamReader
        Dim strConsulta As String
        Dim cmdbuilder As OleDbCommandBuilder

        tb = New DataTable

        'Repeticion si no es la primera vez es que se ha llamado desde el mantenimiento y antes de cargarse debe borrarse la grid
        If Not bolPrimeraVez Then
            ds.Tables("CamposAsociados").Clear()
        End If
        '***********************

        strStartupPath = My.Application.Info.DirectoryPath
        srSQL = New System.IO.StreamReader(strStartupPath & "\CargarCamposAsociados".sql")
        strConsulta = srSQL.ReadToEnd
        cmdPD = New OleDb.OleDbCommand(strConsulta, cnxPD)
        dA.SelectCommand = cmdPD
        cmdbuilder = New OleDbCommandBuilder(dA)
        dA.Fill(ds, "CamposAsociados")

        If Not bolPrimeraVez Then
            Exit Sub
        End If

        bolPrimeraVez = False

        'Codigo nuevo para primer campo asociado
        srSQL = New System.IO.StreamReader(strStartupPath & "\CargarCampoAso1.sql")
        strConsulta = srSQL.ReadToEnd
        cmdPD = New OleDb.OleDbCommand(strConsulta, cnxPD)
        dA.SelectCommand = cmdPD
        dA.Fill(ds, "CampoAso1")

        cboLevel1.ValueMember = "COD_CAMPO1"
        cboLevel1.DisplayMember = "DES_CAMPO1"
        cboLevel1.DataSource = ds.Tables("CampoAso1").DefaultView

        'Codigo nuevo para primer campo asociado
        srSQL = New System.IO.StreamReader(strStartupPath & "\CargarCampoAso2.sql")
        strConsulta = srSQL.ReadToEnd
        cmdPD = New OleDb.OleDbCommand(strConsulta, cnxPD)
        dA.SelectCommand = cmdPD
        dA.Fill(ds, "CampoAso2")

        cboLevel1.ValueMember = "COD_CAMPO_SEC2"
        cboLevel1.DisplayMember = "DES_CAMPO2"
        cboLevel1.DataSource = ds.Tables("CampoAso2").DefaultView

      
        'Código para definir las relaciones con las tablas de los campos asociados ojo notar 'que COD_CAMPO1 se refiere a una columna de cada tabla, es decir COD_CAMPO1 de Tabla 'Relacionada y COD_CAMPO1 de tabla principal. Esto en la simulación por código del INNER 'JOIN

        ds.Relations.Add("CampoAso1_Relacion", ds.Tables("CampoAso1").Columns("COD_CAMPO1"),
ds.Tables("CamposAsociados").Columns("COD_CAMPO1"))

     ds.Relations.Add("CampoAso2_Relacion",ds.Tables("CampoAso2").Columns("COD_CAMPO_SEC2"), ds.Tables("CamposAsociados").Columns("COD_CAMPO_PRINC2"))

    
        Dim oNivel1 As DataColumn = New DataColumn
        oNivel1.DataType = System.Type.GetType("System.String")
        oNivel1.ColumnName = "CampoAso1"
        oNivel1.Expression = "Parent(CampoAso1_Relacion).DES_CAMPO1"
        ds.Tables(Tabla).Columns.Add(oNivel1)

        Dim oNivel2 As DataColumn = New DataColumn
        oNivel2.DataType = System.Type.GetType("System.String")
        oNivel2.ColumnName = "CampoAso2"
        oNivel2.Expression = "Parent(CampoAso2_Relacion).DES_CAMPO2"
        ds.Tables(Tabla).Columns.Add(oNivel2)

      'Aquí fuerzo de nuevo la select principal para que no me de error al realizar 'operaciones con el formulario una vez he cargado el data GridView
        dA.SelectCommand = New OleDb.OleDbCommand("SELECT  COD_ID, DES_PRINCIPAL, COD_CAMPO1, COD_CAMPO_PRINC2 FROM PROPIETARIO.TABLA_PRINCIPAL", cnxPD)

        dA.UpdateCommand = cmdbuilder.GetUpdateCommand()
        dA.DeleteCommand = cmdbuilder.GetDeleteCommand()
        dA.InsertCommand = cmdbuilder.GetInsertCommand()

        Try

            bs.DataSource = ds.Tables(Tabla)

       'Aquí hacemos el binding de los campos de la base de datos con los controles del formulario

            Me.txtColumna.DataBindings.Add(New Binding("Text", bs, "COD_ID"))
            Me.txtDescripcion.DataBindings.Add(New Binding("Text", bs, "DES_PRINCIPAL"))
            Me.cboLevel1.DataBindings.Add(New Binding("SelectedValue", bs, "COD_CAMPO1"))
            Me.cboLevel2.DataBindings.Add(New Binding("SelectedValue", bs, "COD_CAMPO_PRINC2"))
          
         
            grdResult.Columns(0).MinimumWidth = 50
            grdResult.Columns(1).MinimumWidth = 50  'Descripcion
            grdResult.Columns(2).MinimumWidth = 30  'Campo1
            grdResult.Columns(3).MinimumWidth = 200 'Campo2
        
          
            grdResult.Columns(0).HeaderText = "Identificador"
            grdResult.Columns(1).HeaderText = "Descripción"
            grdResult.Columns(2).HeaderText = "Campo asociado 1"
            grdResult.Columns(3).HeaderText = "Campo Asociado 2"

            'coloca las columnas en la posición deseada
            grdResult.Columns(0).DisplayIndex = 2  'Identificador
            grdResult.Columns(1).DisplayIndex = 1 'Descripcion
            grdResult.Columns(2).DisplayIndex = 4 'Campo1
            grdResult.Columns(3).DisplayIndex = 3 'Campo2

            grdResult.Refresh()

        Catch ex As Exception
        End Try

    End Sub

    Function BuscarCodigoRelacion() As Integer
        Stop
    End Function


    Private Sub grdResult_RowEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles grdResult.RowEnter

    End Sub

    Private Sub btnFiltro_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFiltro.Click
        Dim strFiltro As String = ""
        Dim vwCamposAsociados As DataView


        If txtFiltroColumna.Text <> "" Then
            If Not strFiltro = "" Then
                strFiltro = strFiltro & " AND "
            End If
            strFiltro = strFiltro & "COD_ID LIKE '%" & UCase(txtFiltroColumna.Text) & "%'"  
        End If

     

        If txtFiltroDescripcion.Text <> "" Then
            If Not strFiltro = "" Then
                strFiltro = strFiltro & " AND "
            End If
            strFiltro = strFiltro & "DES_PRINCIPAL LIKE '%" & txtFiltroDescripcion.Text & "%'"
        End If

     
        If Not ds.Tables("CamposAsociados") Is Nothing Then
            vwCamposAsociados = ds.Tables("CamposAsociados").DefaultView
            vwCamposAsociados.RowFilter = strFiltro
        End If
    End Sub

    Private Sub btnAlta_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAlta.Click
      
        grbDetalle.Enabled = True
        grbBuscar.Enabled = False
        btnPaisDom.Enabled = False
        drEdit = bs.AddNew()
        bs.MoveLast()
      
        grdResult.Refresh()
    End Sub

    Private Sub btnBaja_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBaja.Click
        Dim drv As DataRowView
        Dim b As Integer
      
        Try

            If MsgBox("¿Esta usted seguro que desea borrar este registro?", MsgBoxStyle.YesNo + MsgBoxStyle.Question, "Confirmación de Baja") = MsgBoxResult.Yes Then
                drv = bs.Current
                b = grdResult.CurrentRow.Index
                drv.Delete()
            End If
            dA.Update(ds, Tabla)
            grbDetalle.Enabled = False
            grdResult.Refresh()
        Catch ex As Exception
            If Mid(ex.Message, 1, 9) = "ORA-00001" Then
                MsgBox("El código de ID debe ser único revise los datos e inténtelo de nuevo", MsgBoxStyle.Critical, "Error al actualizar los datos")
            End If
            If Mid(ex.Message, 1, 9) = "ORA-02292" Then
                MsgBox("No está permitido el borrado en cascada, asegurese que el ID no tiene Sub ID´s antes de borrarla", MsgBoxStyle.Critical, "Error al actualizar los datos")
            End If
            Recargar()
        End Try

    End Sub

    Private Sub btnModificar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnModificar.Click
     
        grbDetalle.Enabled = True
        grbBuscar.Enabled = False
        
    End Sub

    Private Sub btnAceptar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAceptar.Click
        Dim drv As DataRowView
        Try
            drv = bs.Current

         

            drv("COD_CAMPO1") = cboLevel1.SelectedValue
            drv("COD_CAMPO_SEC2") = cboLevel2.SelectedValue
          

            bs.EndEdit()
            dA.Update(ds, Tabla)

            grbBuscar.Enabled = True
            grbDetalle.Enabled = False

            grdResult.Refresh()
            CamposAsociados() 'refresca los cambios
            bolRefrescar = False
        Catch ex As Exception
            If Mid(ex.Message, 1, 9) = "ORA-00001" Then
                MsgBox("El código ID debe ser único revise los datos e inténtelo de nuevo", MsgBoxStyle.Critical, "Error al actualizar los datos")
            End If
            bolRefrescar = True
        End Try

    End Sub
   
    Private Sub btnCancelar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancelar.Click
        bs.CancelEdit()
        If bolRefrescar Then
            Recargar()
        End If
        grbDetalle.Enabled = False
        grbBuscar.Enabled = True
    End Sub

    Sub Recargar()
        bolRefrescar = False
        ds.Tables(0).Clear()
        CamposAsociados()
        grdResult.Refresh()
    End Sub

    Private Sub txtCodigo_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)

    End Sub

  
    Private Sub txtColumna_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtColumna.TextChanged
       
        txtColumna.Text = UCase(txtColumna.Text)
        txtColumna.SelectionStart = txtColumna.TextLength + 1
        txtColumna.MaxLength = 10
    End Sub

 
    Sub AplicarFiltro()
        Dim strFiltro As String = ""
        Dim vw CamposAsociados As DataView
    
        If txtFiltroColumna.Text <> "" Then
            If Not strFiltro = "" Then
                strFiltro = strFiltro & " AND "
            End If
            strFiltro = strFiltro & "COD_ID LIKE '%" & UCase(txtFiltroColumna.Text) & "%'"  
        End If

     
        If txtFiltroDescripcion.Text <> "" Then
            If Not strFiltro = "" Then
                strFiltro = strFiltro & " AND "
            End If
            strFiltro = strFiltro & "DES_PRINCIPAL LIKE '%" & txtFiltroDescripcion.Text & "%'"
        End If

     
        If Not ds.Tables("CamposAsociados") Is Nothing Then
            vwCamposAsociados = ds.Tables("CamposAsociados").DefaultView
            vwCamposAsociados.RowFilter = strFiltro
        End If

    End Sub

    Private Sub txtFiltroColumna_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtFiltroColumna.TextChanged
        AplicarFiltro()
    End Sub

    Private Sub txtFiltroDescripcion_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtFiltroFormato.TextChanged
        AplicarFiltro()
    End Sub

Public Sub btnPaisDom_Click(sender As Object, e As EventArgs) Handles btnPaisDom.Click
        Dim d As New frmNiveles()  'envia los datos del grid al formulario hijo

        d.gID = txtColumna.Text
      
        d.cboLevel1.Text = cboLevel1.Text
        d.cboLevel2.Text = cboLevel2.Text

        d.cboLevel1.SelectedItem = cboLevel1.SelectedItem
        d.cboLevel2.SelectedItem = cboLevel2.SelectedItem

        d.ShowDialog()
      
        cboLevel1.Text = d.cboLevel1.Text
        cboLevel2.Text = d.cboLevel2.Text
      
        If Not d.cboLevel1.SelectedItem Is Nothing Then
            txtNivel1.Text = d.cboLevel1.SelectedItem.clave
        End If

        If Not d.cboLevel2.SelectedItem Is Nothing Then
            txtNivel2.Text = d.cboLevel2.SelectedItem.clave
        End If

        d.Close()
    End Sub

End Class