viernes, octubre 01, 2010

Montando un Multisitio web con Drupal

Montando un Multisitio web con Drupal - séptima parte

Un multisitio es una características de algunos CMS que permiten crear varios sitios distintos utilizando código en forma compartida, y por ende, un mismo núcleo o base del CMS.[1]

Es así como podemos tener varios sitios con una administración más cómoda y sencilla. Claro que cada sitio puede tener sus propios temas y módulos, compartir unos ya existentes en el sitio por defecto... sí lo hay.

El proceso que describiremos será para un nuevo sitio, dentro de una instalación de drupal ya en funcionamiento, pero que podrá repetirse para sucesivos nuevos sitios.

Vale decir que estos pasos llegan gracias al valeroso aporte de ilo a través del irc de drupal-es en freenode ;-) quien amablemente me pidió que nombre a documentadosDOTcom[2]

Prerequesitos:
  • Linux
  • un sitrio con Drupal funcionando
La receta:
  • editar el /etc/hosts y añadir: 127.0.0.1 prueba
  • ir a: cd drupal/sites
  • crear el directorio: mkdir prueba
  • copiar: default/default-settings.php prueba/
  • copiar prueba/default-settings.php prueba/settings.php
  • en el navegador, ir a http://prueba/drupal

[1]http://cuencodigital.com/articulos/multisitios_en_drupal.html
[2]http://www.documentados.com/

viernes, julio 02, 2010

crosstab para consultas de tablas cruzadas

Esta no le he necesitado aún, pero me puse a revisarla para ver qué tal funciona y ver sí luego se le puede sacar provecho.

La documentación oficial[0] habla por sí sola, sobre lo que se quiere. hacerla funcionar no es complicado, pero tiene su engaño.

Lo que se tiene:

SELECT est_002::text, agri02_002::text, count(*)::numeric
FROM consulta_agri04 GROUP BY agri02_002, est_002 ORDER BY est_002, agri02_002;
est_002 | agri02_002 | count
----------+------------------------+-------
Mérida | CACAO | 83
Mérida | CAFE | 169
Mérida | CAÑA | 68
Mérida | CEREALES Y LEGUMINOSAS | 421
Mérida | FRUTALES | 746
Mérida | HORTALIZAS | 1277
Mérida | ORNAMENTALES | 41
Mérida | RAICES Y TUBERCULOS | 484
Táchira | CACAO | 12
Táchira | CAFE | 29
Táchira | CAÑA | 1
Táchira | CEREALES Y LEGUMINOSAS | 310
Táchira | FRUTALES | 209
Táchira | HORTALIZAS | 451
Táchira | RAICES Y TUBERCULOS | 222
Trujillo | CAFE | 26
Trujillo | CAÑA | 20
Trujillo | CEREALES Y LEGUMINOSAS | 97
Trujillo | FRUTALES | 174
Trujillo | HORTALIZAS | 361
Trujillo | RAICES Y TUBERCULOS | 127
Lo que se quiere:

est_002 |CACAO |CAFE |CAÑA
Mérida |83 |169 |68
Táchira |12 |29 |1
Trujillo | |26 |20
con ésta consulta verifico cuántas columnas tendré:
SELECT DISTINCT agri02_002 FROM consulta_agri04 ORDER BY agri02_002, ya que luego voy a necesitar esa información para definir la salida.

SELECT est_002, agri02_002, count(*)::numeric

Acá defindo, como primer campo, la columna pivote, el segunda campo, serán las columnas, y el tercer campo, los valores a rellenar en la tabla.

Acá defino as columnas de salida en la tabla:
AS lista(estado text, renglon1 text, renglon2 text, renglon3 text, renglon4 text, renglon5 text, renglon6 text, renglon7 text, renglon8 text)

primer campo, el pivot, los demás serán tantos como me devuelva la consulta con el DISTINCT.

De esta amnera, la consulta completa queda:
SELECT * FROM public.crosstab
(
'SELECT est_002, agri02_002, count(*)::numeric FROM consulta_agri04 GROUP BY agri02_002, est_002 ORDER BY est_002, agri02_002',
'SELECT DISTINCT agri02_002 FROM consulta_agri04 ORDER BY agri02_002'
) AS lista(estado text, renglon1 text, renglon2 text, renglon3 text, renglon4 text, renglon5 text, renglon6 text, renglon7 text, renglon8 text);

y devuelve:
estado | renglon1 | renglon2 | renglon3 | renglon4 | renglon5 | renglon6 | renglon7 | renglon8
----------+----------+----------+----------+----------+----------+----------+----------+----------
Mérida | 83 | 169 | 68 | 421 | 746 | 1277 | 41 | 484
Táchira | 12 | 29 | 1 | 310 | 209 | 451 | | 222
Trujillo | | 26 | 20 | 97 | 174 | 361 | | 127
(3 filas)



[0]http://www.postgresql.org/docs/8.3/interactive/tablefunc.html

viernes, abril 16, 2010

Configuración y ejecución con PL / R (PLR) en PostgreSQL

Algunos de los datos aquí provistos, provienen de [0], los cuales se encuentran en Inglés.

R es tanto un lenguaje como un medio para hacer el análisis estadístico. R está disponible como software libre bajo la GPL. Para aquellos familiarizados con entornos como S, MatLab, y SAS - R tiene el mismo propósito. Tiene poderosas construcciones para la manipulación de matrices, los paquetes para importar datos de fuentes de datos diversas como bases de datos relacionales, csv, hojas de cálculo, entre otras.

PL / R es una extensión del lenguaje de PostgreSQL que le permite escribir funciones PostgreSQL y funciones de agregación en el lenguaje estadístico R.

Con el lenguaje R puede escribir cosas como funciones de agregación para calcular la mediana, que no existe de forma nativa en PostgreSQL y sólo existe en una pocas bases de datos relacionales de forma nativa (por ejemplo Oracle) se me ocurre. Incluso en la función de Oracle no aparece hasta la versión 10.

Otro uso popular de R es para hacer diagramas de Voronoi...

Cuando se combina esto con PostGIS tiene un ambiente extremadamente poderoso para hacer cosas como vecino más cercano y búsquedas planeación de instalaciones.

Instalando R

En Debian Linux, aptitude install r-cran-base para obtener R. Sí a alguien se le ocurre hacer un search podrá encontrar un montón de cosas adicionales para R, como módulos por ejemplo, que cada quien instalará según necesite o quiera.

Instalando PL/R

En la lista de postgresql-es, cuando pregunté, Alvaro Herrera me respondió: "¿Probaste apt-get install postgresql-8.3-plr ?". ciertamente lo había buscado en los repos de Debian, pensando que quizás existía, y no lo había encontrado, asumí que sólo conseguiría el tar.gz de la página oficial de PL/R [1], pero, se me ocurrió preguntarle a google y zas! Ubuntu sí que lo tiene en un repos, así que me descargue el .deb a ver qué tal me iba, y bueno... dpkg -i y listo!

Por ahí leí que había que decirle a postgresql dónde está R, y la seña era agregar R_HOME = '/usr/lib/R/' a environment de postresql (en /etc). Reiniciar postgresql

Agregando el lenguaje a la BD

Luego, me di cuenta que un simple createlang plr no funcionaba, así que volví a google y... [2] psql -d mi_bd < /usr/share/postgresql/8.3/plr.sql. Ojo, es posible que esté en otro lado

Probando PL/R

SELECT * FROM plr_environ();
SELECT load_r_typenames();
SELECT * FROM r_typenames();
SELECT plr_array_accum('{23,35}', 42);

Next try to create a helper function (this was copied from (http://www.joeconway.com/plr/doc/plr-pgsql-support-funcs.html) - and test with the following

CREATE OR REPLACE FUNCTION plr_array (text, text) RETURNS text[] AS '$libdir/plr','plr_array' LANGUAGE 'C' WITH (isstrict);
select plr_array('hello','world');

Creating Median Function in PostgreSQL using R

create or replace function r_median(_float8) returns float as 'median(arg1)' language 'plr';
CREATE AGGREGATE median (sfunc = plr_array_accum, basetype = float8, stype = _float8, finalfunc = r_median );
create table foo(f0 int, f1 text, f2 float8);
insert into foo values(1,'cat1',1.21);
insert into foo values(2,'cat1',1.24);
insert into foo values(3,'cat1',1.18);
insert into foo values(4,'cat1',1.26);
insert into foo values(5,'cat1',1.15);
insert into foo values(6,'cat2',1.15);
insert into foo values(7,'cat2',1.26);
insert into foo values(8,'cat2',1.32);
insert into foo values(9,'cat2',1.30);
select f1, median(f2) from foo group by f1 order by f1;
[0]http://www.bostongis.com/PrinterFriendly.aspx?content_name=postgresql_plr_tut01
[1]http://www.joeconway.com/web/guest/pl/r
[2]http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg589297.html

martes, marzo 02, 2010

Las universidades españolas acuerdan promover el software libre

Buenas noticias para el código libre: la Conferencia de Rectores de las Universidades Españolas o CRUE, una asociación de 74 universidades públicas y privadas, ha acordado en una reunión que las universidades impulsarán los programas de código libre para situarse “en una posición de referencia en el ámbito de nuevas tecnologías”.


Ciertamente, aún queda mucho camino que recorrer en el tema del uso de software libre como prioridad para el aprovechamiento de los recursos en la parte acedémica, pero puede ser un avance...

La noticia completa aquí

viernes, enero 15, 2010

Instalando una impresora nueva: Epson Stylus T20

Agradecimientos públicos a otro blog [0]

El primer paso es descargar el archivo desde la web:

http://www.ircdshells.com.ar/linux/epson-pips.tar.gz

Entramos al directorio donde sea que sea que hayamos descargado el archivo, en mi caso:

# cd /home/emmanuel/Desktop

Descomprimimos el archivo:

# tar zxvf epson-pips.tar.gz

# cd epson-pips

Instalamos los drivers:

# sudo dpkg -i *.deb

Copiamos el archivo "ekpd":

# sudo cp ekpd /etc/init.d/ekpd

Damos Permiso de Ejecucion:

# sudo chmod +x /etc/init.d/ekpd

Agregamos ekpd para que se ejecute al inicio:

# sudo update-rc.d ekpd defaults

Instalamos 2 librerias:

# sudo apt-get install libtiff4 libpng3

Creamos el siguiente enlace simbolico:

# sudo ln -s /usr/lib/libtiff.so.4.2.1 /usr/lib/libtiff.so.3

Copiamos el archivo de configuracion:

# sudo cp /usr/local/EPAva/printer/st20/ekpdrc_st20 /etc/ekpdrc

Apartir de este punto, la impresora debe estar conectada!

Ejecutamos la aplicacion "ekpd-tool":

# sudo ekpd-tool

En mi caso los valores que puse son los siguientes:



Ahora iniciamos el demonio ekpd:

# sudo /etc/init.d/ekpd start

Ahora abrimos el configurador de impresoras, y taran! ahí ta!

[0]http://linuxapuntes.blogspot.com/2009/05/instalar-impresora-epson-stylus.html

lunes, enero 04, 2010

FLISOL 2010

El evento Latinoamericano más importante, en su edición 2010, será el próximo sábado 24 de abril, y Mérida no escapará a esta oportunidad.

Aún se está montando el evento, pero como todos los años, es un encuentro de experiencias que dejan emociones y satisfacciones.

Festival Latinoamericano de Instalación de Software LIbre

Pendientes!

miércoles, diciembre 16, 2009

Buscar y Reemplazar texto desde la cónsola

Buscar y Reemplazar texto desde la cónsola en varios archivos y sus subdirectorios

http://www.guatewireless.org/buscar-y-reemplazar-texto-en-todos-los-archivos-dentro-de-un-directorio-y-todos-los-subdirectorios/

find ./ "*.php" | xargs perl -pi -e 's/user=yoyoyoyo/user=tambien/g'

martes, octubre 27, 2009

Sabiduría para programar

Recientemente leí en el histórica de la lista de postgresql, en palabras de Alvaro Herrera, algo así como:
No le digas como lo va a hacer, dile qué es lo que tiene que hacer
. Palabras que hoy en día me sonaron, por mucho, llenas de sabiduría... ¿a qué se refería? era parte de una respuesta a una programación del lado de la Base de Datos... mejorar la programación, el rendimiento del motor, etc...

Haré el intento de conseguir las palabras exactas...

viernes, octubre 16, 2009

Insertar varios registros sí no existe

Bueno... es posible que esta idea este extraña, pero así es como lo tenemos.

Nuestro código político está más o menos así:
\d region
Tabla «public.region»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
reg_002 | character varying(35) | not null
Índices:
«region_pkey» PRIMARY KEY, btree (reg_001)

\d estado
Tabla «public.estado»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
est_002 | character varying(35) | not null
Índices:
«estado_pkey» PRIMARY KEY, btree (est_001)
«estado_ukey» UNIQUE, btree (reg_001, est_001)
Restricciones de llave foránea:
«estado_fkey» FOREIGN KEY (reg_001) REFERENCES region(reg_001) ON UPDATE CASCADE ON DELETE RESTRICT

\d municipio
Tabla «public.municipio»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
mun_002 | character varying(35) | not null
Índices:
«municipio_ukey» UNIQUE, btree (reg_001, est_001, mun_001)
Restricciones de llave foránea:
«municipio_fkey» FOREIGN KEY (est_001) REFERENCES estado(est_001) ON UPDATE CASCADE ON DELETE RESTRICT

\d parroquia
Tabla «public.parroquia»
Columna | Tipo | Modificadores
---------+-----------------------+---------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
par_002 | character varying(35) | not null
Índices:
«parroquia_ukey» UNIQUE, btree (reg_001, est_001, mun_001, par_001)

\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)

Y para el registro de datos, existe la posibilidad de que el detalle no llegue sino hasta la parroquia, por ejemplo, por lo que habría que seleccionar un Centro Poblado del tipo 'No Aplica', pero, un 'No Aplica' por cada parroquia existente, son como... muchos! al menos para insertarlos manualmente y uno por uno, así que decidí estudiar un poquito y ver cómo lo hacía vía plpgsql, he aquí el resultado:
CREATE OR REPLACE FUNCTION leer(out a varchar) RETURNS SETOF varchar LANGUAGE plpgsql AS $$
DECLARE
r record;
p varchar := '01';
BEGIN
-- recorrer la tabla tantas veces como parroquias existan
FOR r IN SELECT par_001 FROM centro_poblado GROUP BY par_001 LOOP
-- recorrer la tabla tantas veces como parroquias existan y centros poblados con código igual a '000' existan
FOR r IN SELECT COUNT(*) AS cuenta FROM centro_poblado WHERE par_001 = p AND cpo_001 = '000' LOOP
-- inicializo 'a'
a = r.cuenta;
-- sí a leido como entero, es igual a cero, es porque no existe un centro poblado con código igual a '000'
IF (a::int = 0) THEN
-- insertar el registro
INSERT INTO centro_poblado (reg_001, est_001, mun_001, par_001, cpo_001, cpo_002) VALUES ('06', '14', '01', p, '000', 'No Aplica');
END IF;
-- incremento para pasar a la siguiente parroquia
p := '0'|| p::int + 1;
-- paso a la siguiente parroquia
RETURN next;
END LOOP;
END LOOP;
END;
$$;

Seguro, alguien consigue otra forma de hacerlo mejor, pero al menos sirve... También es posible ir lanzando algunas notificaciones para saber por dónde y cómo va, tal como RAISE NOTICE ' Parroquia tiene %',p; pero es a gusto de cada quien...

Listo! yo mismo lo hice de otra forma:

--
-- funcion para insertar no aplica para parroquias
--
CREATE OR REPLACE FUNCTION insertar_no_aplica_centro_poblado(e varchar, out a varchar) RETURNS SETOF varchar LANGUAGE plpgsql AS $$
DECLARE
l record;
r record;
p record;
BEGIN
create temp table audit_tmp (usuario integer, ip inet, ocurrencia timestamp default now());
-- listar todas las parroquias y municipios agrupadas
FOR l IN SELECT mun_001 FROM centro_poblado WHERE est_001 = e GROUP BY mun_001 ORDER BY mun_001 LOOP
FOR r IN SELECT par_001 FROM centro_poblado WHERE est_001 = e AND mun_001 = l.mun_001 GROUP BY par_001 ORDER BY par_001 LOOP
FOR p IN SELECT cpo_001 FROM centro_poblado WHERE est_001 = e AND mun_001 = l.mun_001 AND par_001 = r.par_001 GROUP BY cpo_001 ORDER BY cpo_001 LOOP
IF (p.cpo_001 = '000') THEN
RAISE NOTICE 'No Aplica para : %', r.par_001;
EXIT;
ELSE
insert into audit_tmp values (1, '127.0.0.1');
RAISE NOTICE 'Insertando para la parroquia : %', r.par_001;
INSERT INTO centro_poblado (reg_001, est_001, mun_001, par_001, cpo_001, cpo_002) VALUES ('06', e, l.mun_001, r.par_001, '000', 'No Aplica');
EXIT;
END IF;
END LOOP;
END LOOP;
END LOOP;
END;
$$;

Ya ven, es otra forma...

miércoles, octubre 14, 2009

Eliminando registros duplicados, desde SQL

Zas! Por un error de programación, de repente aparecieron un montón de registros duplicados, y cuando digo un montón, son un montón!!!

y ahora? pues, a pensar con cabeza fría... cuáles son las opciones?
  • borrar todos los duplicados a través del mismo sistema
  • pensar en un script en cualquier lenguaje que haga todo de un sólo halón
Por razones de seguridad de los datos, me inclino por la segunda opción... lo primero que pensé es en cómo se podía hacer con SQL

La tabla:
\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)

Como la tabla tiene un id de tipo serial, eso ayuda.

Verficiar cuáles son los registros repetidos:
SELECT COUNT(*), reg_001, est_001, mun_001, par_001 FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001 ORDER BY reg_001, est_001, mun_001, par_001;

Verificar los registros repetidos:
SELECT * FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05';

Contar los registros repetidos, en detalle:
SELECT count(*) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';

Verificar el id min y máx del detalle de los registros repetidos:
SELECT min(id), max(id) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';

La verdad es que luego descubrí que se podía hacer así:
SELECT * FROM centro_poblado AS a
WHERE EXISTS(
SELECT cpo_001, COUNT(cpo_001)
FROM centro_poblado
WHERE a.cpo_001= cpo_001
GROUP BY cpo_001
HAVING COUNT(cpo_001)>1)
ORDER BY cpo_001

Eliminar los registros repetidos, menos 1:
DELETE FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018' AND id > 1;

Esto también se puede resolver con una tabla temporal, y metiendo la consulta en una función y todo eso, pero aun no lo he probado...

Finalmente, y gracias a la lista de usuarios de postgres y su histórico, me encontré [1]:
DELETE FROM centro_poblado WHERE ctid NOT IN (SELECT min(ctid) FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001);

[1]http://archives.postgresql.org/pgsql-es-ayuda/2007-01/msg00353.php

jueves, septiembre 10, 2009

Jugando con los privilegios de accesos a Bases de Datos PostgreSQL

El escenario varias BDs con
  • Un usuario diferente para cada BD con privilegios para insertar/modificar
  • Un usuario capaz de leer todas las BDs
  • Un usuario admin de todas las BDs
  • Un Rol, que agrupe todos los usuarios y no tenga ningún privilegio
CREATE ROLE dba NO LOGIN;
CREATE USER user1 IN ROLE dba LOGIN ENCRYPTED PASSWORD '123456';
CREATE USER user2 LOGIN ENCRYPTED PASSWORD '123456';
CREATE USER admin1 IN ROLE dba LOGIN ENCRYPTED PASSWORD '123456';

ALTER GROUP dba ADD USER user2;

CREATE DATABASE db1 OWNER TO user1;
CREATE TABLE tabla1 (id serial, nombre character varying(10));

quitar todos los privilegios sobre la tabla: tabla1 para los usuarios user1 y user2
REVOKE ALL ON tabla1 FROM user1;
REVOKE ALL ON tabla1 FROM user2;

otorgar privilegios de consulta sobre la tabla talba1 para el usuario user1
GRANT SELECT ON tabla1 TO user1;

otorgar privilegios para insertar datos sobre la tabla tabla1 para el usuario user2 (requiere privilegios sobre la secuencia de la tabla)
GRANT INSERT ON tabla1 TO user2;
GTANT UPDATE ON tabla1_id_seq TO user2;

Para poder actualizar/eliminar un registro, se requiere tener privilegios de selección (requiere privilegios sobre la secuencias de la tabla)
GRANT UPDATE ON tabla1 TO user2;
GRANT DELETE ON tabla1 TO user2;

miércoles, septiembre 09, 2009

Migrando capas shape (.shp) a postgis

El tema de pasar capas .shp a postgis, no sé sí será una manía, pero el tema de acceso a bases de datos contra el acceso a archivos no lo creo...

Resulta que en el trabajo, hay un poco de gente haciendo capas y capas de mapas en software propietario... alguna vez pregunté (en pasillo) sí se habían puesto de acuerdo sobre algunas normas para hacer esas capas y me miraron como sí gubiera hablado en húngaro! y pensé... coño!

No me equivoqué... cuando me dieron la tarea de montar un servidor de mapas y empecé a pedir las capas, empezaron mis dolores de cabeza... ahora, me toca ver cómo hacer, y escribir (acá) para que la cosa funcione...

Vale decir que no tengo conocimientos en el tema de cartografía... por ahora...

Existen varias formas, una de ellas es con el comando shp2pgsql, otra con QGis y otra con gvSIG (esas son las que yo conozco)...

shp2pgsql: debería funcionar con algo parecido a esto
shp2pgsql -W UTF-8 .shp <schema>.<tablename> ><filename>.sql
pero yo obtuve un mensaje de error por la codificación en la que está hecha la capa, o la mala norma de los atributos

Finalmente amprendí algo sobre el estándar a seleccionar para que la cosa funcione. No significa que haya entendido, pero lo aprendí. Existe algo que se llama Sistema de Coordenadas Proyectadas y Sistema de Coordenadas Geográficas, ambas tienen como Sistema de Coordenadas de Referencia WGS84, pero la primera utiliza UTM y al segunda no, de manera que para la región Los Andes (Venezuela) sería para el primer caso SRC: EPSG 32619 que tiene WGS84 / UTM 19N y para el segundo caso SRC: EPSG 4326. De manera que es importante recordar este detalle para las propiedades de los proyectos y capas...

QGis:
  • verifico en las propiedades del proyecto que el Sistema de Referencia de Coordenadas (SRC) esté en WGS 84 (EPSG 4326 o 32619 según sea el caso)
  • Añado la capa vectorial (.shp)
  • verifico las propiedades de la capa que tenga el datum WGS 84
Importando la capa (.shp) a PostGIS:
  • Deseleccionar la opción Usar SRID por omisión o especificar aquí (que trae por defecto -1)
  • cambiar el -1 por 4326 (o el que corresponda)
  • añadir la capa
  • ok
gvSIG:
  • Agragar una vista nueva
  • Verificar en Propiedades la Proyección Actual, que por defecto viene en 23030 (España), con lo cual seleccionamos 4326 o 32619 (u otra, según sea el caso)
  • Click al botón Abrir, para añadir una capa .shp
  • Al seleccionar la opción de añadir una capa, verificamos que tenga en Proyección Actual, el mismo valor que hemos puesto a la vista. Añadios la capa
  • Seleccionada la capa a exportar, menú -> capa -> exportar -> postgis
  • llenamos los datos que nos solicitan (nombre de la tabla, datos para la conexión con la BD) y listo!
En todo esto, seguro hay algunas otras consideraciones a tomar en cuenta. Sí algiuen las sabe, paselas, sino, esperémos a descubrirlas...

Normas de cortesía para trabajar con Bases de Datos

Justo ahora que me encuentro tratando de montar un servidor de mapas, con un conjunto de capas .shp generadas por estudiantes, pasantes, tesistas y profesionales del área (no informáticos) me encuentro con algo que creo será todo un dolor de cabeza... la falta de cortesía!

Claro! ellos no tienen la culpa, como siempre, la culpa es de la vaca (para los que no saben, ese es el título de un libro, sí alguien cree que le suena interesante, pues, búsquelo, y leálo)... ¿cuál vaca? la que sea, no importa! cuando les dije que por favor, no utilizaran en los nombres de atributos (atributos en SIGs son las tablas de BD para los informáticos) carácteres acentuados, ni especiales, ni mayúsculas, me respondieron: "Cómo vamos a irrespetar la ortografía! sí es un nombre propio, claro que irá en mayúscula!"

A lo que pensé... coño!

Para los que puedan, corran la voz!!! siempre que un producto informático, tenga como futuro una base de datos, aunque sea remoto, por favor, recordar:
  • No utilizar caracteres especiales (vocales acentuadas, eñes, etc)
  • No utlizar combinaciones de mayúsculas/minúsculas
  • Utilizar nombres descriptibles
  • En caso de ser nombres muy largos, utilizar nemotécnicos y su respectivo diccionario
A medida que vaya recordando (o tropezando) agregaré a la lista... o sí algún lector se le ocurre colaborar...

martes, septiembre 08, 2009

Preparando un entorno SIG en Debian Lenny

Para la presentación de datos georeferenciados, a través de un servicio web, voy a instalar un servidor de mapas. Ahora no recuerdo los detalles porque ya lo tengo instalado, pero trataré de ir corrigiendo en caso de que haga falta.

aptitude install cgi-mapserver mapserver-bin mapserver-doc gdal-bin proj proj-bin proj-data php5-mapscript

Sí algo me falta, es probable que aptitude se los traiga. con esto ya quedaría instalado el servidor de mapas.

Cómo accederlo todavía estoy revisando la doc, pero en un navegador al teclear http://localhost/cgi-bin/mapserv te debería devolver algo como No query information to decode. QUERY_STRING is set, but empty.lo que dice que ya está funcionando. Una vez se tenga una capa para mostrar, también se puede cargar vía http://localhost/tu_path_al_template/

El qué y cómo mostrar, vienen en la próxima entrega del blog... generar el mapfile y trabajar con la plantilla, que por ahora, me tiro a OpenLayers

lunes, septiembre 07, 2009

Migrar BD de Mysql a PostGreSQL (parte 3)

Conversión de tipos

Resulta, que cuando vemos lo que nos dice el espejo que hemos creado de mysql sobre postgresql con dbi-link, los campos son creados como tipo text, con un comentario en la vista que dice qué tipo de dato aproximado debería haber ahí...

Esto es así, porque los tipos de datos entre diferentes gestores normalmente no son iguales, es decir, todos aplican los estándares, pero además, cada uno le da su toque, de manera que es posible que tenga más o menos, y que de paso, se llamen diferentes... o más bien, se le aplique aliases...

El tema es que cuando trato de leer para insertar en otra tabla, en un proceso de migración, debo tener muy presente los tipos de datos, ocasionalmente, podría recibir un mensaje del tipo:

Error de SQL:

ERROR: la columna «id» es de tipo integer pero la expresión es de tipo text
LINE 1: INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT c...
^
HINT: Necesitará reescribir la expresión o aplicarle una conversión de tipo.

En la declaración:
INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT codcat, codedi, catedi FROM sir_cultura_catedi)

Con lo cual, cualquiera se asusta a la primera... y no hace falta... con leer el mensaje y fijarme en la frase "aplicarle una conversión de tipos" será más que suficiente para tener algo de calma nuevamente...

¿qué es una conversión de tipos? digamos que tenemos un campo de un tipo de dato (p.e. text), pero queremos "interpretar" su contenido como sí fuera otro tipo de dato (p.e. int)... a eso se le conoce como conversión de tipos

INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT codcat::int, codedi::int, catedi FROM sir_cultura_catedi)

o

INSERT INTO cultu03 (id, cultu02_001, cultu03_002) (SELECT cast(codcat as int), cast (codedi as int), catedi FROM sir_cultura_catedi)

y listo!!!

Una consultica que tiene algo más:

INSERT INTO cultu04 (reg_001, est_001, mun_001, par_001, cpo_001, cultu04_001, cultu04_002, cultu04_003, cultu04_004, cultu04_005, cultu04_006, cultu04_007, cultu02_001, cultu03_001)
(SELECT codreg, codest, codmun, codpar, codcen, anno, nombre, direccion, telefono, correo, web,
CASE WHEN area <> '' THEN
CAST(area AS numeric)
ELSE
0
END,
codedi::int, codcat::int FROM sir_cultura_edif_det)

Creo que no hace falta explicar la estructura de las tablas, sólo entender que leo en una tabla e inserto lo que estoy leyendo, con algunas particularidades sencillas....

Una más

INSERT INTO acu03 (reg_001, est_001, mun_001, par_001, cpo_001, acu03_001, acu01_001, acu03_003, acu03_004, acu03_005, acu02_001, acu03_006, acu03_007, acu03_008)
(SELECT codreg, codest, codmun, CASE WHEN codpar = 'NA' THEN '00' ELSE codpar END, CASE WHEN codcen = 'NA' THEN '000' ELSE codcen END, EXTRACT(YEAR FROM agno)::int, tipo::int, nb_acueducto, fuentes, caudal, tratamiento::int, suscriptores, poblacion, produccion FROM servicios_acueducto)

jueves, septiembre 03, 2009

Migrar BD de Mysql a PostGreSQL (parte 1 Versión 2)

Diciendo que soy usuario Debian, no digo mucho, pero les cuento que habiendo reinstalado mi equipo de la oficina, en el momento en que estoy devolviendo las cosas a la normalidad, se me ocurrió hacer un aptitude search dbi_link y vaya sorpresa, existe!

Pues, se me ocurrió instalarlo, y probar luego, cómo sería el procedimiento para que funcione... de manera que está sería la versión 2 de Migrar BD de Mysql a PostGreSQL (parte 1)

instalarlo:
  • aptitude install dbi_link
buscar donde queda:
  • find /usr/ -iname dbi_link
crear la BD en postgresql:
  • createdb migracion
crear el lenguaje plperlu a la BD recien creada:
  • createlang plperlu -d migracion
lanzar dbi_link.sql a la BD recien creada:
  • psql -d migracion -f /path/completo/dbi_link.sql
crear la estructura que me permitirá _conectar_ con la otra BD, en este caso mysql (acá usamos el mysql.sql que viene en el example del dbi_link, que no viene con el aptitude install):
  • psql -d migracion -f /path/completo/mysql.sql
y listo! ya está el esquema con la estructura y datos que necesito leer...

martes, septiembre 01, 2009

Solucionando el error de llave pública en Debian

Hoy decidí reinstalar mi computador de la oficina, tenía Debian Etch, y me estoy halando Debian Lenny desde un netinstall de etch...

¿Por qué reinstalar en vez de actualizar? vale, porque había instalado varias cosillas que no tienen uninstall (no son productos nativos para debian) y algunos quedaron mal instalados, con lo cual el sistema se fue poneindo "fastidioso" con el tiempo...

Lo que hice: instalar sólo el sistema base, esto es, que cuando llegó el tasksel, le dije que nada de eso, de manera que instaló algunos 8 paquetes más y listo. Luego, edité el sources.list para colocar uno que tengo cerca, y al darle aptitude update me saltaron 2 errores relacionados con las llaves, uno para el security y otro para el mirror que yo había agregado...

La solución:
gpg --keyserver subkeys.pgp.net --recv-keys 07DC563D1F41B907 && gpg --export --armor 07DC563D1F41B907 | apt-key add -

donde el 07DC563D1F41B907 es la llave que devuelve el error, de manera que tuve que hacerlo 2 veces, primero para el mirror y luego para el security... y update!

Apache como proxy reverse

Configurando Apache como Servidor de Páginas Web para Sistemas alojados en otro server dentro de la intranet

La situación: Un servidor A que tiene Apache2 (servidor de páginas web que aloja la página web institucional) y Bind9 (servidor de nombres de dominio: DNS), un servidor B que contiene otro Apache2 (para aplicaciones a la medida de la oficina, las cuales deben ser accedidas desde la intranet pero también desde otros lugares geográficos, de manera que son accedidos desde su propio subdominio, de la forma sistemas.misitio.tal)

Siguiendo lo que dice en [1], en ele ejemplo básico, he agregado además del default modificado, una especie de vhost, que en realidad es un proxy reverse, el el servidor A:

ServerName sistemas.misitio.tal

ProxyRequests Off

Order deny,allow
Allow from all

ProxyPass / http://192.168.15.18/
ProxyPassReverse / http://192.168.15.18/

ErrorDocument 404 /

TransferLog /var/log/apache2/sistemas.access
ErrorLog /var/log/apache2/sistemas.error


En el servidor B, sólo basta que esté el site default.

Luego, buscar los módulos para instalar y activar: proxy, proxy_connect y proxy_http

reiniciar apache y listo!

[1] http://httpd.apache.org/docs/2.2/mod/mod_proxy.html

lunes, agosto 31, 2009

Configurando Apache como Servidor de Páginas Web

Configurando Apache como Servidor de Páginas Web

La situación: en la oficina tenemos nuestro propio hosting, de manera que nuestra página web y nuestras aplicaciones (de uso interno) están también alojadas por nuestros servidores... por ahora, sólo vamos con la página web institucional.

Servidor corriendo Debian Lenny, instalado por la vía tradicional (aptitude) apache2 (como la página está en drupal, pues, todo lo demás, php5, mysql etc...)

El sites activado por defecto nos sirve para la ocasión, editado, modifico el DocumentRoot y la directiva para que ambos apunten al directorio de drupal... y listo!

Ahora no recuerdo que haya sido distinto, el asunto es que dedicamos más tiempo a configurar otras cositas que espero contar mañana...

See you!

viernes, agosto 28, 2009

Montando un sitio web con Drupal (Video)

Montando un sitio web con Drupal - sexta parte

Instalar un módulo: Flashvideo

Crear un tipo de contenido exclusivo para videos institucionales

Me he guiado por [1], pero con pequeñas variaciones, que describo desde el principio. Haré una mala traducción, ya que [1] por estar publicado en drupal.org le da carácter de oficial.

Permite subir vídeos a su sitio web e incorporarlo a cualquier contenido existente en la misma

Manual para el uso del módulo de FlashVideo para Drupal.

Pasos a seguir:
  1. Descargar del sitio de Drupal el módulo (http://drupal.org/project/flashvideo)
  2. Una vez descargado, instalelo normalmente copiando el módulo en la carpeta de módulos de su sitio
  3. Luego, en el sitio debe activar el módulo
  4. El módulo, una vez instalado, se verá en la lista de módulos en: Administrar -> Configuración del sitio, c omo se muestra en la imagen 1
  5. La configuración del entorno del módulo, quedará más o menos como se ve en la imagen 2 (muestra unas notas importantes sobre los parámetros a ser fijados en php.ini, los actuales de sus sistema y los recomendados) y la imagen 3 (muestra los tipos de nodos existentes en su sistema, donde podrá ingresar a cada uno para ver el estado y opearción de cada uno. Esto es, que puede publicar videos en cualquier módulo, basta con configurar el tipo de contenido y habilitar el módulo, tal como se muestra en la imagen 4)
  6. Si se desea incrustar el vídeo en un tipo de contenido, seleccione el tipo de contenido y sus operaciones estraán marcados para este tipo de nodo. El procedimiento será el mismo para cualquier tipo de contenido. En las imágenes se muestra como la configuración de estos campos y opciones:
  7. Una vez configurado el módulo, guardar los cambios.
  8. Puede ocurrir un error semejante al mostrado en la imágen, que se resolverá descargando los controladores de S3 en [4]
  9. Descargar y extraer la carpeta. Ésta se guarda en el directorio módulos -> FlashVideo -> drivers. Luego, volver a la página de configuración y verña como el error a desaparecido.
  10. Para este módulo se pueden utilizar varios tipos reproductores de medios swf. Dos de los más utilizados, son JW Player and the Dash Media Player los cuales puedes descargar en [2]. Ahí mismo hay unas instrucciones para su instalación, las cuales también comentaré acá según mi experiencia.
  11. En mi caso, he decidido el JW Player, así que será ese el que comentaré acá cómo me ha ido... Descargado y descomprimido, hay un archivo lamado player-viral.swf el cual he renombrado a Player.swf. Éste último y el swfobject.js los he copiado en el directorio raíz de mi instalación de drupal. En [2] sugiere que se copien en el directorio files, pero mirando los logs de apache me decía que no los encontraba en el directorio padre, así que los coloqué ahí. El resto de pasos sugeridos en [2] no me han sido útiles, así que los he obviado.
  12. Finalmente, para poder verlo, he seguido lo que dice [3].
  13. El último paso, tal como dice en [1], para poder ver el vídeo insertado en el sitio, es ejecutar el cron, esto se hace de la siguiente manera, vaya a Administrar / Informe de situación y tareas de mantenimiento de cron / ejecutar cron manualmente, lo que necesita saber que cada vez cree una página para integrar un video se necesita ejecutar el cron, una vez hecho esto se puede ver el vídeo incrustado.
[1] http://drupal.org/node/496410
[2] http://www.travistidwell.com/node/177
[3] http://www.longtailvideo.com/players/jw-flv-player/
[4] http://www.travistidwell.com/files/Storage3.tar.gz