Migración de Datos de Oracle a PostgreSQL con Kettle

 Bajamos el software de kettle.pentaho.com y lo instalamos , en mi caso uso la versión estable 4.2.0

 Creamos un nuevo documento de transformación de la siguiente manera: File->New->Transformation

 Configuramos nuestras dos conexiones, la de Oracle y la de Postgres: Ubicamos la pestaña View, luego Transformations, elegimos la nuestra y le damos botón derecho a Database connections, despues New y alli configuramos nuestras conexiones separadamente ingresando Host Name, Database Name, User Name, Password y demás datos, probamos nuestra conexión con el botón Test para verificar que todo marche bien.

  

Las transformaciones se basan en pasos que se comunican entre si mediante un elemento llamado Hop (salto), a continuación detallo los 3 pasos de esta migración de datos:

 PASO 1 Creamos nuestro primer paso que será una entrada de tabla, en este caso alguna tabla de Oracle, la que queremos pasar sus datos a la equivalente en Postgres: Design->Input->Table Input, una vez puesto el Table Input en nuestro espacio le damos doble clic para modificarlo, en Step Name colocamos el nombre del paso, por ejemplo el nombre de la tabla, en Connection ubicamos nuestra conexión Oracle configurada anteriormente, en el campo SQL colocamos la consulta de la(s) tabla(s) que queremos migrar, para facilitarnos el trabajo podemos darle a la opcion Get SQL Select Statements y buscamos directamente nuestra tabla, con esto terminamos este paso que arroja el resultado del query para que el siguiente paso lo pueda utilizar.

 

PASO 2 El siguiente paso consiste en pasar el resultado del paso anterior por una serie de validaciones y modificaciones, para ello seleccionamos Design->Experimental->Script, de igual manera le damos doble clic para modificarlo, en la ventana script es donde escribiremos codigo javascript como por ejemplo:

var id_status= new Array();
id_status["A"]=1;
id_status["N"]=2;
id_status["I"]=3;
id_status["C"]=4;
var idstatus;
for(var indice in id_status) {
   if(ESTATUS==null){
     idstatus=null;
                    }
   if(ESTATUS==indice){
     idstatus=id_status[indice];
                    }
                             }

En este caso cambiaremos los datos que nos trae el campo ESTATUS, en Oracle este campo originalmente es varchar, mientras que en Postgres es integer.

 

PASO 3 El siguiente paso es un Table Output, que no es mas que nuestra tabla en Postgres a la que se le ingresaran los datos filtrados en el paso anterior, para crearlo lo ubicamos en Design->Output->Table Output, le colocamos un nombre, lo asociamos a nuestra conexion en Postgres, ubicamos el esquema en donde se encuentra la tabla, ingresamos el nombre de la tabla y para configurar los campos le damos a la pestaña Database Fields, luego presionamos Get Fields, para que se carguen los campos de nuestra tabla, acto seguido nos metemos en Enter Field Mapping para determinar cuales campos del paso anterior asociamos a los campos de nuestra tabla, de mas está decir que las variables identificadas como salida en el paso del Script también se ven reflejadas en esta ventana.

  

 – Para ejecutar nuestra transformación ubicamos el botón de run o tecleamos F9, si no observamos ningún error en el log que se muestra abajo se habrá ejecutado satisfactoriamente la transformación.

Ejemplo Básico de PgCrypto

Pgcrypto es una extensión para encriptar datos en Postgresql, a partir de pg-9.1 se instala con create extension pgcrypto, para ello debemos tener instalado el paquete postgresql-contrib de la version de nuestro postgres.

Ejemplo de uso:

Creamos la siguiente tabla:

CREATE TABLE usuario
(
 id serial NOT NULL,
 usuario character varying(15) NOT NULL,
 clave bytea
)

Ingresamos datos:

insert into usuario (usuario, clave) values ('alex', encrypt('11112222', 'password','3des'))

Como vemos usamos la funcion encrypt, el primer parametro es la clave a encriptar, el segundo es la clave de encriptacion y el tercero el algoritmo utilizado, en nuestro caso 3des, mejor conocido como triple des

Si hacemos un select directo a la tabla nos mostrará algo como

usuario        clave
alex              \xefea90726dc45f756f40a9e22f66fc1a

Para tomar el valor desencriptado hacemos:

select usuario, encode(decrypt(clave,'password','3des'::text), 'escape'::text) AS clave from usuario

FUNCIÓN MONTHS_BETWEEN() DE ORACLE EN POSTGRESQL

Estando en un proyecto de migración de ORACLE a PostgreSQL me di cuenta que no existe una función que haga lo mismo que months_between() de Oracle, esta función calcula la diferencia de meses entre dos fecha, con decimales y negativo según sea el caso, el lenguaje utilizado es PL/PgSql, espero que les sirva:
CREATE OR REPLACE FUNCTION months_between (date, date) RETURNS NUMERIC AS
$body$
DECLARE
mes FLOAT;
mes1 NUMERIC;
ano FLOAT;
dia FLOAT;
begin
mes=extract(month from (age($1,$2)));
ano=extract(year from (age($1,$2)));
dia=extract(day from (age($1,$2)));
mes1=(ano*12) + mes + cast(dia as real)/31;
mes1= round(mes1,7);
return mes1;
end
$body$
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;

TUTORIAL YII FRAMEWORK (CAPÍTULO 18) – CAMPOS ENMASCARADOS EN FORMULARIO

Algo muy útil en los formularios es poder enmascarar los campos, esto significa colocar los separadores automáticamente y restringir que caracteres puede ingresar el usuario a medida que se va tecleando, a continuación mostramos un ejemplo de como implementarlo en Yii, no hace falta la inclusión de una extensión.

<?php $this->widget('CMaskedTextField', array(
'model' => $model,
'attribute' => 'telefono_persona',
'mask' => '(9999)-999-9999',
'htmlOptions' => array('size' => 11)
));
?>

En ejemplo model es el modelo en cuestión, attribute es el nombre del campo, mask es el tipo de máscara que le quieres colocar y en htmlOptions indicas las características HTML que quieres que tenga tu campo. En la mascara si usamos ‘9’ indicamos que solo se puede teclear números, ‘a’ es solo letras, y ‘*’ es cualquier carácter alfanumérico

TUTORIAL YII FRAMEWORK (CAPÍTULO 17) – HACER FORMULARIO PARA MULTIPLES MODELOS

Una vez realizado el CRUD de cada modelo este nos genera automáticamente un formulario con los datos del modelo, si queremos hacer un formulario que incluya los datos de dos o mas modelos debemos modificar varias lineas de código.

En el ejemplo que explico a continuación tenemos dos modelos, el modelo Personal cuyos datos son id_personal, nombre_personal y id_estado y el modelo Estado con id_estado y nombre_estado, de mas está decir que Personal es alimentado por Estado, lo primero que haremos es modificar la vista _form de Personal.

<div class="form">
<?php $form = $this->beginWidget('CActiveForm', array( 'id'=>'user-form',
'enableAjaxValidation'=>true, ));
if ($a->isNewRecord==false) { $b=Estado::model()->findByPk($a->id_estado); }
echo $form->errorSummary(array($a,$b)); ?>
<div class="row">
<?php echo $form->labelEx($a,'nombre_personal'); ?>
<?php echo $form->textField($a,'nombre_personal'); ?>
<?php echo $form->error($a,'nombre_personal'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($b,'nombre_estado'); ?>
<?php echo $form->textField($b,'nombre_estado'); ?>
<?php echo $form->error($b,'nombre_estado'); ?>
</div>
<div class="row buttons">
<?php echo CHtml::submitButton($a->isNewRecord ? 'Crear' : 'Actualizar'); ?>
</div> <?php $this->endWidget(); ?> </div></p>

Con ese código incluimos el campo nombre_estado del modelo Estado en el formulario de Personal, también agregamos en el parámetro errorSummary el modelo $b, que en este caso es Estado, con esto nos valida el formulario con las restricciones que hayamos puesto en el rules() de ambos modelos, por otra parte preguntamos si se está creando un nuevo registro o no, en caso de ser una actualización cargamos la información del modelo Estado, de Personal nos la carga automáticamente.

En el caso de las vistas create.php y update.php cambiamos la siguiente linea:

<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>

por esta

<?php echo $this->renderPartial('_form', array('a'=>$a, 'b'=>$b)); ?>

Ahora modificamos la acción create:

public function actionCreate()
{
$a=new Personal;
$b=new Estado;
$this->performAjaxValidation(array($a,$b));
if(isset($_POST['Personal'],$_POST['Estado']))
{
$a->attributes=$_POST['Personal'];
$b->attributes=$_POST['Estado'];
$sql='select max(id_estado) from estado;';
$connection=Yii::app()->db;
$command=$connection->createCommand($sql);
$row=$command->queryRow();
$row["max"]++;
$b->id_estado=$row["max"];
$a->id_estado=$row["max"];
if($b->save() && $a->save())
$this->redirect(array('view','id'=>$a->id_personal));
}
$this->render('create',array('a'=>$a,'b'=>$b));
}
<p style="text-align: justify;">

Lo primero que hacemos es instanciar los modelos Personal y Estado, luego si los datos pasan la validación determinamos el mayor id de estado, se lo asignamos al dato id_estado de cada modelo y guardamos los datos.
Para update hacemos algo parecido:

public function actionUpdate($id)
{
$a=new Personal;
$b=new Estado;
$this->performAjaxValidation(array($a,$b));
$a=$this->loadModel($id);
if(isset($_POST['Personal'],$_POST['Estado']))
{
$a->attributes=$_POST['Personal'];
$b->attributes=$_POST['Estado'];

$b->id_estado=$a->id_estado;
$b->setIsNewRecord(false);
if($a->save() && $b->update())
$this->redirect(array('view','id'=>$a->id_personal));
}
$this->render('update',array('a'=>$a,'b'=>$b));
}
<p style="text-align: justify;">

De igual forma que con el create validamos los datos, guardamos y direccionamos. En definitiva hemos hecho un formulario en el que guardamos datos de dos modelos distintos, es un ejemplo sencillo pero válido, si tus modelos tienen mas datos lo incluyes en el _form y si quieres agregar más modelos al form se hace de la misma forma.

TUTORIAL YII FRAMEWORK (CAPÍTULO 16) – GUARDAR AUTOMÁTICAMENTE USUARIO Y FECHA DE CREACIÓN Y MODIFICACIÓN

Nos descargamos la librería de http://www.yiiframework.com/extension/blameable-behavior/ y la pegamos en /component/ , no olvides darle permisos. En el modelo en cuestión copiamos la siguiente función:

public function behaviors()
{
return array(
'CTimestampBehavior' => array(
'class' => 'zii.behaviors.CTimestampBehavior',
'createAttribute' => 'created_date',
'updateAttribute' => 'modified_date',
'setUpdateOnCreate' => true,
),
'BlameableBehavior' => array(
'class' => 'application.components.behaviors.BlameableBehavior',
'createdByColumn' => 'created_by',
'updatedByColumn' => 'modified_by',
),
);
}
<p style="text-align: justify;">

Donde created_date, modified_date, created_by y modified_by son los datos de nuestra tabla en donde queremos guardar los datos. Si no tenemos uno de los datos podemos comentar esa linea de código.

TUTORIAL YII FRAMEWORK (CAPÍTULO 15) – NO MOSTRAR INDEX.PHP EN LAS URL

Si deseamos que los módulos de nuestro sitio puedan ser accedidos así: /miproyecto/contact y no así:  /miproyecto/index.php&r=contact debemos hacer varios cambios en nuestro proyecto. En primer lugar ejecutamos en la consola de linux: a2enmod rewrite, abrimos /etc/apache2/SiteAvailables/default y cambiamos “Allowoverride=None” por “Allowoverride=All” (se encuentra en dos lineas distintas), luego cambiamos la dirección de nuestro archivo .htaccess de nombre_proyecto/protected/ a nombre_proyecto/, .htaccess es un archivo oculto, para mostrar los archivos ocultos usamos el atajo control + h.
Abrimos el .htaccess y pegamos los siguiente:

RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php

Finalmente vamos a /protected/config/main.php y en el arreglo component agregamos:

'urlManager'=>array(
'urlFormat'=>'path',
'showScriptName'=>false,
'caseSensitive'=>false,
),

Con esto hemos limpiado las URLs de nuestro sistema, si nuestros controladores contienen mayúsculas es necesario colocar el atributo ‘caseSensitive’ en true.

TUTORIAL YII FRAMEWORK (CAPÍTULO 14) – CAMPO CON AUTOCOMPLETAR

La idea es tener un campo de texto en el que apenas empecemos a ingresar texto se nos liste una serie de resultados que hayan coincidido con ese texto, en la vista donde va a estar el campo debemos colocar el siguiente código:

if ($model->estado0!='')
{
$value=$model->estado0->nombre_estado;
}
else {
$value='';
}
echo $form->hiddenField($model, 'estado');
$this->widget('zii.widgets.jui.CJuiAutoComplete', array(
'name'=>'estado',
'model'=>$model,
'value'=>$value,
'sourceUrl'=>$this->createUrl('ListarEstados'),
'options'=>array(
'minLength'=>'3',
'showAnim'=>'fold',
'select' => 'js:function(event, ui)
{ jQuery("#Contrato_estado").val(ui.item["id"]); }',
'search'=> 'js:function(event, ui)
{ jQuery("#Contrato_estado").val(0); }'
),
));

En el código anterior validamos que el campo no esté vacío en la base de datos, de estar vacío es que se está creando un nuevo registro o no se seleccionó ningún estado, luego creamos el campo de auto completar colocandole el nombre, el nombre del modelo, el valor por defecto, cantidad mínima para realizar la búsqueda y la función que guarda el id del campo seleccionado.

Copiamos la función ListarEstados en nuestro controlador:

public function actionListarEstados($term) {
$criteria = new CDbCriteria;
$criteria->condition = "LOWER(nombre_estado) like LOWER(:term)";</p>
$criteria->params = array(':term'=> '%'.$_GET['term'].'%');
$criteria->limit = 30;
$data = Estado::model()->findAll($criteria);
$arr = array();
foreach ($data as $item) {
$arr[] = array(
'id' => $item->id,
'value' => $item->nombre_estado,
'label' => $item->nombre_estado,
);
}
echo CJSON::encode($arr);
}

En la función de arriba definimos la condición que va a tener nuestro query, en nuestro caso buscaremos por nombre_estado indiferentemente minúsculas y mayúsculas utilizando un like ‘% %’, la máxima cantidad de resultados será de 30, los datos provienen del modelo Estado, el campo a guardar es el id y el que mostraremos es el nombre del estado.

Hay que darle permisos a los usuarios para utilizar la acción ListarEstados.

TUTORIAL YII FRAMEWORK (CAPÍTULO 13) – COMBOS DEPENDIENTES

La idea es la siguiente: Queremos dos combos, Tipo Organismo y Organismo, la idea es que al momento de escoger un tipo de organismo se despliegue en el segundo combo los distintos organismos que pertenecen a ese tipo para ello utilizamos el siguiente código en el _form:

 // Tipo de organismos
 <div class="row">
 <?php echo $form->labelEx($model,'id_tipo_org');
 // Nombre de la etiqueta a mostrar ej: Tipo Organización
 $departamento = new CDbCriteria;
 // Preparamos los parámetros de búsqueda
 $departamento->order = 'descripcion ASC';
 // ordenamos alfabéticamente
 echo $form->dropDownList($model,'id_tipo_org',
 // id_tipo_org es el nombre del campo en el modelo
 CHtml::listData(TipoOrgG::model()->findAll($departamento),
 // TipoOrgG es el modelo en el que se buscaran los datos
 'id_tipo_org','descripcion'),
 // id_tipo_org es el dato que se quiere guardar y
 // descripción lo que se quiere mostrar
 array('ajax' => array('type' => 'POST',
 'url' => CController::createUrl('Correspondencia/cargarorganismos'), //
 la acción que va a cargar el segundo div
 'update' => '#Correspondencia_id_org_g'
 // el div que se va a actualizar
 ),'prompt' => 'Seleccione un Tipo'
 // Valor por defecto
 )
 );
 echo $form->error($model,'id_tipo_org'); ?>
 </div>
 // Segundo Combo, organismos
 <div class="row">
 <?php echo $form->labelEx($model,'id_org_g');

if ($model->isNewRecord==1)
 //Si se está creando un registro nuevo
 {
 echo $form->dropDownList($model,'id_org_g',
 array('0' => 'Seleccione un Organismo'));
 // se muestra solo Seleccione un Organismo
 }
 else {
 $tipo=$model->id_tipo_org;
 // Si se está modificando un registro
 $sql="select count(id_org_g) from organismos_g where id_tipo_org='$tipo';";
 //
 $connection=Yii::app()->db;
 //
 $command=$connection->createCommand($sql);
 //
 $row=$command->queryRow();
 //
 $bandera=$row['count'];
 //
 if ($bandera==0) {
 //
 echo $form->dropDownList($model,'id_org_g',
 array('0' => 'Seleccione un Organismo')); }
 // Si el tipo de organismo no tiene ningún
 else {
 // organismo solo muestra Seleccione un Organismo
 echo $form->dropDownList($model,'id_org_g',
 CHtml::listData(OrganismosG::model()->findAllBySql(
 //Aquí van los datos de la búsqueda del segundo combo
 "select * from organismos_g where id_tipo_org
 =:keyword order by id_org_g=:clave2 asc",
 array(':keyword'=>$model->id_tipo_org,':clave2'=>$model->id_org_g)),
 'id_org_g','descripcion'));
 }
 }
 ?></div>

En el controlador colocamos la siguiente función:

 public function actionCargarorganismos()
 {
 $data=OrganismosG::model()->findAllBySql(
 "select * from organismos_g where id_tipo_org
 =:keyword or id_org_g=0 order by id_org_g=0 desc, descripcion asc",
 // Aquí buscamos los diferentes organismos que pertenecen al tipo elegido
 array(':keyword'=>$_POST['Correspondencia']['id_tipo_org']));
 $data=CHtml::listData($data,'id_org_g','descripcion');
 foreach($data as $value=>$name)
 {
 echo CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true);
 }
 }

Hay que darle permisos a la acción cargarorganismos.

TUTORIAL YII FRAMEWORK (CAPÍTULO 12) – REPORTES A PARTIR DE UNA BÚSQUEDA EN EL GRID

He visto en el foro que hay muchas personas preguntando como exportar en PDF a partir de una búsqueda en la vista Admin, aquí les traigo una solución a ese problema. En el método search() del modelo en cuestión agregamos al final (antes del retorno) las siguientes lineas de código:

$_SESSION['datos_filtrados'] = new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'sort'=>$sort,
'pagination'=>false,
));

Donde $criteria son los datos de filtrado y $sort el arreglo de ordenación, eso nos guardará en una variable de sesión la ultima búsqueda realizada, si no hemos realizado ninguna búsqueda estaremos guardando todos los resultados del listado, es importante colocar en false la paginación para que pueda traernos todos los resultados. En la vista admin agregar el botón de exportar con array(‘label’⇒’Exportar a PDF’, ‘url’⇒array(‘pdf’)), en la configuración del menú. En el controlador agregamos la siguiente función:

public function actionPdf()
{
$this->render('pdf');
}

Agrega la action pdf en el accessRules del controlador, para que los usuarios del sistema puedan tener permisos de utilizarla. La librería que uso y recomiendo para exportar pdf es MPDF, la pueden descargar de su pagina oficial, una vez descargada la copian en extensions. Un ejemplo para la vista pdf es el siguiente:


<? $pdf = Yii::createComponent('application.extensions.MPDF52.mpdf');
$dataProvider = $_SESSION['datos_filtrados']->getData();
$contador=count($dataProvider);
$html.='
<table align="center"><tr>
<td align="center"><b>LISTADO DE CONTRATOS</b></td>
</tr></table>
Total Resultados: '.$contador.'
<table class="detail-view2" repeat_header="1" cellpadding="1" cellspacing="1"
width="100%" border="0">
<tr class="principal">
<td class="principal" width="7%">&nbsp;N° Control</td>
<td class="principal" width="7%">&nbsp;N° Contrato</td>
<td class="principal" width="19%">&nbsp;Empresa</td>
<td class="principal" width="10%">&nbsp;Estado</td>
<td class="principal" width="9%">&nbsp;Monto Contratado</td>
<td class="principal" width="25%">&nbsp;Objeto Contrato</td>
<td class="principal" width="14%">&nbsp;Personal Actuante</td>
<td class="principal" width="9%">&nbsp;Tipo Informe</td>
</tr>';
$i=0;
$val=count($dataProvider);
while($i<$val){
$html.='
<tr class="odd">
<td class="odd" width="7%">&nbsp;'.$dataProvider[$i]
["num_control"].'</td>
<td class="odd" width="7%">&nbsp;'.$dataProvider[$i]
["num_contrato"].'</td>
<td class="odd" width="19%">&nbsp;'.$dataProvider[$i]["empresa"].'</td>
<td class="odd" width="10%">&nbsp;'.$dataProvider[$i]["estado0"]
["nombre_estado"].'</td>
<td class="odd" width="9%">&nbsp;'.$dataProvider[$i]
["monto_contratado"].'</td>
<td class="odd" width="25%">&nbsp;'.$dataProvider[$i]
["objeto_contrato"].'</td>
<td class="odd" width="14%">&nbsp;'.$dataProvider[$i]

["personal_actuante"].'</td>
<td class="odd" width="9%">&nbsp;'.$dataProvider[$i]["informe0"]
["nombre_tipo_informe"].'</td>
';
$html.='</tr>'; $i++;
}
$html.='</table>';
$mpdf=new mPDF('win-1252','LETTER-L','','',9,9,24,10,5,5);
$mpdf->WriteHTML($html);
$mpdf->Output('Reporte_Contratos.pdf','D');
exit; ?>

Como vemos al principio del ejemplo tomamos los datos filtrado mediante la variable de sesión y disponemos de ellos como queramos en el pdf.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 28 seguidores