Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Isis2 Koha
Isis2 Koha
Cuestiones preliminares...................................................................................................................2
Aproximaciones...............................................................................................................................2
ISIS -> XML -> TEXT -> MARC -> KOHA.............................................................................3
ISIS -> ISO -> ISO2709 -> KOHA............................................................................................4
ISIS -> XML -> KOHA..............................................................................................................4
Apéndice..............................................................................................................................................9
Cuestiones preliminares
Para que cualquiera de los métodos descritos a continuación funcione, es imprescindible completar
algunos pasos previos. Estos pasos se refieren a la configuración del sistema e incluyen, en orden de
importancia, los siguientes parámetros:
item types (tipos de item): Son las categorías a las que pertenecen los ejemplares que maneja de
biblioteca. Por ejemplo, los videocassettes pertenecen a una categoría distinta a la de los libros y a
la de las grabaciones sonoras.
Este paso es fundamental, ya que mucha de la funcionalidad de Koha se pierde si no se
especifican correctamente los tipos de ítem.
MARC tag structure (Estructura de campos MARC): Koha permite definir qué campos MARC
serán usados y cuáles serán ignorados.
La importancia de este paso radica en la posibilidad de establecer una correlación entre los
subcampos MARC y los campos de las tablas presentes en Koha.
Como ejemplo, la posible estructura para los subcampos del campo de título, 245:
Aproximaciones
Hay más de una aproximación al problema de la migración de datos de una base existente a
las bases del sistema Koha, si bien no todas se enfocan a la migración de datos contenidos en bases
ISIS. Las siguientes secciones muestran los distintos caminos analizados y tratan de anticipar los
problemas que pueden presentar cada uno de ellos.
ISIS -> XML -> TEXT -> MARC -> KOHA
Este método consiste, tal como describe su autor en su página web, en exportar una base de
datos ISIS a un archivo XML a través de la utilidad incluida a tal efecto en WinISIS.
Nota: La utilidad mencionada se encuentra en el menú 'Utils' de WinIsis, pero sólo es accesible si
el idioma seleccionado es el inglés. Por alguna razón desconocida, si se configura el idioma de
WinIsis a español, la opción no es visible en el menú.
A partir de este archivo XML, se utiliza un script escrito en Python, el cual genera un
archivo de texto delimitado por tabulaciones ('tab delimited text file').
A continuación, se transforma la información contenida en el archivo de texto en registros
MARC mediante otro script, este escrito en lenguaje Perl.
Finalmente, el archivo conteniendo los registros en fomrato MARC se importaría a las bases
de Koha. El autor no especifica la manera en que esta importación se realiza, pero se infiere la
utilización del script bulkmarcimport.pl provisto en la ditribución de Koha (ubicado en Linux en:
DirectorioDeKoha/scripts/misc/).
Este método no ha sido probado hasta el momento, debido a que es el más complejo de los
tres analizados, en cuanto a la cantidad de pasos necesarios
ISIS -> ISO -> ISO2709 -> KOHA
Este método no está presente en una página web, ni ha sido propuesto en la lista de correo de
Koha, sino que se deriva de la posibilidad, ofrecida por el propio Koha, de realizar una importación
masiva de datos a las bases que componen el sistema a través del script bulkmarcimport.pl (ubicado
en Linux en: DirectorioDeKoha/scripts/misc/).
Este script recibe como parámetro, entre otras cosas, la ubicación y el nombre de un archivo
ISO2709 que contenga los datos MARC que se quieran importar.
La dificultad que se presenta a la hora de migrar bases ISIS a Koha, es que el archivo ISO
generado por las distintas herramientas disponibles no es interpretado correctamente, ya que no se
atendría lo suficiente al estándar ISO2709.
Poco antes de la elaboración de este informe, se realizaron pruebas con la utilidad ImpExp
disponible en http://www.unesco.org/isis/files/winisis/windows/utilities/. Mediante esta utilidad
sería posible transformar un archivo ISO generado a partir de una base ISIS en un archivo ISO
conforme al estándar ISO2709.
Las pruebas realizadas hasta el momento no han sido satisfactorias, pero es necesario
continuar con las mismas para obtener una conclusión determinante.
Instalación:
Posteriormente, el script necesitará saber dónde se encuentran los módulos de Koha. Por
ejemplo, si la instalación de Koha se encuentra en /usr/local/koha/ el comando a ejecutar sería:
$ export PERL5LIB=/usr/local/koha/intranet/modules
Con los pasos anteriores sería suficiente para que el script pueda funcionar correctamente.
Se puede probar lo anterior ubicándose en el directorio en el que se encuentra el script y ejecutando
el comando:
$./xml2koha.pl -pf data/10-records.xml
El resultado de este comando es un 'dump' en pantalla del contenido de 10 registros de
prueba (contenidos en el archivo 10-records.xml) en formato MARC.
Configuración:
xml2koha.conf
Se trata de un archivo de configuración bastante simple en el cual se debe setear las
siguientes variables:
Al abrir el archivo .map con un editor de texto, se observarán dos secciones. La primera la
compone una serie de comnetarios referidos a la cantidad de ocurrencias de las distintas
etiquetas encontradas en el archivo XML. Esto puede servir como un primer punto de
control.
Los tres números iniciales deben ser reemplazados con el campo MARC de destino, al cual
irá a parar la información contenida en cada etiqueta <TAG>
Los dos ## encontrados a continuación, deberán ser reemplazados, si es necesario, por los
valores de los indicadores que deberán incluirse en el campo/subcampo MARC de destino.
A continuación se podrá especificar el subcampo MARC al cual irá a parar el la información
contenida en cada etiqueta <TAG>, lo cual se logra colocando un signo $ seguido de la
etiqueta de subcampo.
Finalmente, se coloca el nombre del elemento/etiqueta XML del que se extraerá la
información, para el campo/subcampo MARC especificado. Este elemento puede ser
precedido por:
% : Sirve para mezclar, en un mismo campo MARC, la información proveniente de varios
campos. En la base de destino, se mezclaran los contenidos de los campos de la base ISIS
que apunten al mismo campo/subcampo MARC.
@ : Un elemento/etiqueta que comience con este símbolo será repetible. Cada elemento
enviado desde la base ISIS al mismo campo/subcampo MARC, será colocado en una nueva
ocurrencia en la base MARC de destino.
Un archivo xml2koha.map de ejemplo se ha incluido en este documento. La estructura de
este archivo debería quedar más o menos como sigue:
# Some counts of Elements found in data/vidunc.xml
# Count Element/Tag Name
# 3 Tag_110_a
# 5 Tag_245_a
# 5 Tag_245_n
# 9 Tag_653_a
110 ## $a %Tag_110_a
245 ## $a %Tag_245_a
245 ## $n %Tag_245_n
653 ## $a @Tag_653_a
Una vez que se está conforme con la configuración, se puede probar el resultado con el
comando:
$ ./xml2koha.pl -pf datos-cdsisis.xml
Para insertar los datos en la instalación de Koha, se deberá ejecutar el siguiente comando:
$ ./xml2koha.pl -cf datos-cdsisis.xml
En el caso de que se haya producido un error o se haya cometido una equivocación, se podrá
eliminar todos los datos bibliográficos de las base de Koha, antes de arreglar el problema y realizar
una nueva importación, con el siguiente comando:
$ ./xml2koha.pl -dcf datos-cdsisis.xml
import libxml2
record = []
sub_node = node.children
if sub_node.type == "element":
record[i][sub_node.name] = sub_node.get_content()
sub_node = sub_node.next
else:
sub_node = sub_node.next
root = isis_data.getRootElement()
node = root.children
i = 0
node = node.next
continue
if node is None:
break
if node.name == "RECORD":
##print node.get_content()
record.append({})
Record(i, node)
i = i + 1
node = node.next
else:
isis_data.freeDoc()
if libxml2.debugMemory(1) == 0:
print "OK"
else:
libxml2.dumpMemory()
for j in record:
if j.has_key('Tag_691'):
record_file.write(j['Tag_691'])
record_file.write('_B_L_A_N_K_')
for h in tag:
if j.has_key(h):
record_file.write('\t'+j[h])
else:
record_file.write('\t_B_L_A_N_K_')
record_file.write('\n')
use strict;
use MARC::Record;
my $input_file = "../outs/docs.txt";
my $output_file = "../outs/docs.out";
my $c = 0;
while (<INFILE>) {
$c++;
my @biblionumber_array;
my $i = 1;
while (<INF>) {
$i++;
my $biblionumber_ftf_691_090_c2, my $title_ftf_4_245_a2, my
$edition_ftf_7_250_a2, my $publisher_ftf_11_260_b2, my
$publishcountry_ftf_12_260_a2, my $publishyear_ftf_13_260_c2, my
$author1ee_ftf_18_100_a2, my $author1se_ftf_19_100_a2, my
$author2ee_ftf_20_700_a2, my $author2se_ftf_21_700_a2, my
$adiauthors_ftf_24_700_a2, my $role_ftf_38_590_a2, my $phydescr_ftf_10_300_a2,
my $classno_ftf_114_852_k2, my $classno_ftf_115_852_h2, my
$keyword_ftf_5_520_a2, my $voldetails_ftf_110_440_v2, my
$dateofentry_ftf_122_952_v2, my $notes_ftf_97_500_a2, my $pubprice_151_952_r2,
my $currency_153_590_b2, my $reccreated_123_508_a2, my $remainder2;
print "$biblionumber_ftf_691_090_c:$biblionumber_ftf_691_090_c2\t";
print "\n";
close(INF);
my $record = MARC::Record->new();
if ($author1ee_ftf_18_100_a =~ /_B_L_A_N_K_/) {
$author1ee_ftf_18_100_a = "";
if ($author1se_ftf_19_100_a =~ /_B_L_A_N_K_/) {
$author1se_ftf_19_100_a = "";
}
if ($author1_ftf ne "") {
my $author1 = MARC::Field->new(
'100','1','',
a => $author1_ftf
);
$record->add_fields($author1);
if ($title_ftf_4_245_a !~ /_B_L_A_N_K_/) {
my $title = MARC::Field->new(
'245','1','2',
a => $title_ftf_4_245_a
);
$record->add_fields($title);
if ($edition_ftf_7_250_a !~ /_B_L_A_N_K_/) {
my $edition = MARC::Field->new(
'250',,,
a => $edition_ftf_7_250_a
);
$record->add_fields($edition);
if ($publishcountry_ftf_12_260_a !~ /_B_L_A_N_K_/) {
my $publishcountry = MARC::Field->new(
'260',,,
a => $publishcountry_ftf_12_260_a
);
$record->add_fields($publishcountry);
if ($publisher_ftf_11_260_b =~ /_B_L_A_N_K_/) {
$publisher_ftf_11_260_b = "";
if ($publishyear_ftf_13_260_c =~ /_B_L_A_N_K_/) {
$publishyear_ftf_13_260_c = "";
my $publisher = MARC::Field->new(
'260',,,
b => $publisher_ftf_11_260_b,
c => $publishyear_ftf_13_260_c
);
$record->add_fields($publisher);
if ($phydescr_ftf_10_300_a =~ /_B_L_A_N_K_/) {
$phydescr_ftf_10_300_a = "";
my $phydescr = MARC::Field->new(
'300',,,
a => $phydescr_ftf_10_300_a,
f => 'BOOK'
);
$record->append_fields($phydescr);
if ($voldetails_ftf_110_440_v !~ /_B_L_A_N_K_/) {
my $voldetails = MARC::Field->new(
'440','','3',
v => $voldetails_ftf_110_440_v
);
$record->add_fields($voldetails);
if ($notes_ftf_97_500_a !~ /_B_L_A_N_K_/) {
my $notes = MARC::Field->new(
'500',,,
a => $notes_ftf_97_500_a
);
$record->add_fields($notes);
}
if ($reccreated_123_508_a !~ /_B_L_A_N_K_/) {
my $reccreated = MARC::Field->new(
'508',,,
a => $reccreated_123_508_a
);
$record->add_fields($reccreated);
if ($keyword_ftf_5_520_a !~ /_B_L_A_N_K_/) {
my $keyword = MARC::Field->new(
'520',,,
a => $keyword_ftf_5_520_a
);
$record->add_fields($keyword);
if ($role_ftf_38_590_a =~ /_B_L_A_N_K_/) {
$role_ftf_38_590_a = '';
if ($currency_153_590_b =~ /_B_L_A_N_K_/) {
$currency_153_590_b = '';
}
my $role = MARC::Field->new(
'590',,,
a => $role_ftf_38_590_a,
b => $currency_153_590_b
);
$record->add_fields($role);
if ($author2ee_ftf_20_700_a =~ /_B_L_A_N_K_/) {
$author2ee_ftf_20_700_a = "";
if ($author2se_ftf_21_700_a =~ /_B_L_A_N_K_/) {
$author2se_ftf_21_700_a = "";
if ($adiauthors_ftf_24_700_a =~ /_B_L_A_N_K_/) {
$adiauthors_ftf_24_700_a = "";
if ($adiauthors_ftf ne "") {
my $adiauthors = MARC::Field->new(
'700','1','',
a => $adiauthors_ftf
);
$record->add_fields($adiauthors);
if ($classno_ftf_114_852_k =~ /_B_L_A_N_K_/) {
if ($classno_ftf_115_852_h =~ /_B_L_A_N_K_/) {
my $classno = MARC::Field->new(
'852',,,
k => '999.9999'
);
$record->add_fields($classno);
else {
if ($classno_ftf_115_852_h =~ /^\d\d\d/) {
my $classno = MARC::Field->new(
'852',,,
k => $classno_ftf_115_852_h
);
$record->add_fields($classno);
else {
my $classno = MARC::Field->new(
'852',,,
k => '999.9998',
h => $classno_ftf_115_852_h
);
$record->add_fields($classno);
else {
if ($classno_ftf_114_852_k =~ /^\d\d\d/) {
if ($classno_ftf_115_852_h =~ /_B_L_A_N_K_/) {
$classno_ftf_115_852_h = "";
my $classno = MARC::Field->new(
'852',,,
k => $classno_ftf_114_852_k,
h => $classno_ftf_115_852_h
);
$record->add_fields($classno);
else {
if ($classno_ftf_115_852_h =~ /^\d\d\d/) {
my $classno = MARC::Field->new(
'852',,,
k => $classno_ftf_115_852_h,
h => $classno_ftf_114_852_k
);
$record->add_fields($classno);
else {
if ($classno_ftf_115_852_h =~ /_B_L_A_N_K_/) {
$classno_ftf_115_852_h = "";
my $classno = MARC::Field->new(
'852',,,
k => '999.9997',
h => $classno_ftf_115_852_h
);
$record->add_fields($classno);
if ($pubprice_151_952_r =~ /_B_L_A_N_K_/) {
$pubprice_151_952_r = '';
}
if ($dateofentry_ftf_122_952_v =~ /_B_L_A_N_K_/) {
$dateofentry_ftf_122_952_v = '';
foreach (@biblionumber_array_tmp) {
my $biblionumber = $_;
my $barcode = MARC::Field->new(
'952',,,
b => "MAIN",
d => "MAIN",
p => $biblionumber,
r => $pubprice_151_952_r,
u => $biblionumber,
v => $dateofentry_ftf_122_952_v
);
$record->add_fields($barcode);
#print "\n";
}
else {
#print $_;
use strict;
package xml2koha;
use vars qw( $opt_m $opt_v $opt_c $opt_f $opt_p $opt_d $opt_q);
use Getopt::Std;
use XML::Twig;
# use XML::Checker::Parser;
use MARC::Record;
use C4::Context;
use C4::Biblio;
use Term::Activity;
my $xml_file = $opt_f;
our %tags;
our %map;
if( $opt_v ) {
# validate the XML
# print "Validating XML file..." unless $opt_q;
# validate($xml_file);
# print "done\n" unless $opt_q;
}
if( $opt_m ) {
# make XML to MARC map file
print "Making XML to MARC map..." unless $opt_q;
make_map($xml_file);
print "done\n" unless $opt_q;
}
if ($opt_d) {
# remove any trace of previous records in koha db
print "Deleting biblios..." unless $opt_q;
$dbh->do("delete from biblio");
$dbh->do("delete from biblioitems");
$dbh->do("delete from items");
$dbh->do("delete from bibliosubject");
$dbh->do("delete from additionalauthors");
$dbh->do("delete from bibliosubtitle");
$dbh->do("delete from marc_biblio");
$dbh->do("delete from marc_subfield_table");
$dbh->do("delete from marc_word");
$dbh->do("delete from marc_blob_subfield");
print "done\n" unless $opt_q;
}
if( $opt_p ) {
# always go into quiet mode when printing MARC to STDOUT
$opt_q = 1;
if( $opt_c ) {
#convert to MARC
$xml2koha::ta = new Term::Activity({label => 'records imported'})
unless $opt_q;
convert($xml_file);
}
if(-f $xml2koha::MAPFILE) {
return(0) unless &y_or_n(
"The map file ($xml2koha::MAPFILE) already exists.
Are you sure you wish to overwrite it?");
}
open(MAPFILE,"> $xml2koha::MAPFILE") ||
die "Can't open output '$xml2koha::MAPFILE':$!\n";
# an example leader
# 1 2
# 012345678901234567890123
# *****nam##22*****#a#4500
# 012345678901234567890123
my @ldr = split(//,'************************');
$ldr[5] = 'n'; # new item
$ldr[6] = 'a'; # language material. ie book
$ldr[7] = 'm'; # it's a monograph
$ldr[8] = '#'; # No specific type
$ldr[9] = '#'; # MARC-8
$ldr[10] = '2'; # indicators are 2 chars
$ldr[11] = '2'; # subfields are 2 chars
$ldr[17] = '5'; # Partial (preliminary) level
# Look, the previous db is a mess
$ldr[18] = '#'; # Non-ISBD
$ldr[19] = '#'; # Related record not required
$ldr[20] = '4'; # spec just says put these numbers in
$ldr[21] = '5';
$ldr[22] = '0';
$ldr[23] = '0';
return join('',@ldr);
}
# 0123456789012345678901234567890123456789
my @tag_008 = split(//,'########################################');
# language of material
# once again, no attempt made
# 35..37
for(my $i = 35; $i <= 37; $i++) { $tag_008[$i] = '|'; }
# Modified record
# No attempt
$tag_008[38] = '|';
# Cataloging source
# No attempt
$tag_008[39] = '|';
return join('',@tag_008);
}
my $mfn = $record->att("MFN");
$xml2koha::marc_rec = MARC::Record->new();
my $el = $record->first_child();
if($el) {
do {
my $key = $el->gi();
my $value = $el->text();
# these symbols cause havoc in web-based systems so dump
# them.
$value =~ s/<//;
$value =~ s/>//;
$items{$key} = $value;
&add_field($key,$value);
} while( $el = $el->next_sibling());
}
# first get all the required values for this xml element out of
# map defined by the user
$tag = $xml2koha::map{$xml_element}->{'tag'};
$indicator_1 = $xml2koha::map{$xml_element}->{'indicator_1'};
$indicator_2 = $xml2koha::map{$xml_element}->{'indicator_2'};
$subfield = $xml2koha::map{$xml_element}->{'subfield'};
$merge = $xml2koha::map{$xml_element}->{'merge'};
# check whether this tag has already been inserted. If not then
# it won't be a merge.
if(!$xml2koha::marc_rec->field($tag)) {
$merge = 0;
# no matter what, we have a new tag so make the subfields hash
# straight away
%fields = ($subfield => $element_value);
}
if($merge) {
# The tag already exists so get the subfields hash and
# add or append new subfields.
if(my $text = $xml2koha::marc_rec->field($tag)->subfield($subfield)) {
# update the subfield
$text .= ' ' . $element_value;
$xml2koha::marc_rec->field($tag)->update($subfield => $text);
}
else {
# add a new subfield
$xml2koha::marc_rec->field($tag)->add_subfields($subfield,
$element_value);
}
# The tag already exists so check whether indicators should
# be updated or added or left alone
if(my $ind1 = $xml2koha::marc_rec->field($tag)->indicator(1)) {
# update an indicator
if($indicator_1 =~ m/\d/) {
if($ind1 !~ m/\d/) {
$xml2koha::marc_rec->field($tag)->
update(ind1 => $indicator_1);
};
}
}
else {
# add a new indicator
$xml2koha::marc_rec->field($tag)->
update(ind1 => $indicator_1);
}
}
else {
# always add fields that are not merged together.
# tag 650 for example.
my $field = MARC::Field->new($tag,
$indicator_1,
$indicator_2,
$subfield => $element_value);
$xml2koha::marc_rec->append_fields($field);
}
}
}
# share my rows with evriwun else
return %tags;
}
-m map
Makes a map file in the current directory called
xml2koha.map
xml2koha.map
#
# XML to MARC mapping derived from data/CAMZA.xml
#
245 ## $b %Tag_24_s
245 ## $a %Tag_24_t
100 ## $a @Tag_28_a
260 ## $b %Tag_47_e
260 ## $a %Tag_47_l
952 ## $p %Tag_77
020 ## $a @Tag_10
020 ## $a @Tag_11
040 ## $a %Tag_76
041 ## $a @Tag_50
044 ## $a @Tag_48
080 ## $a %Tag_75_c
080 ## $b %Tag_75_l
100 ## $a @Tag_28_a
100 ## $a @Tag_28_b
100 ## $d @Tag_28_d
100 ## $c @Tag_28_o
245 ## $c @Tag_28_f
245 ## $a @Tag_24_t
245 ## $b @Tag_24_s
245 ## $c @Tag_24_r
110 ## $a @Tag_29_e
110 ## $b @Tag_29_j
110 ## $c @Tag_29_l
110 ## $g @Tag_29_p
111 ## $a @Tag_40_n
111 ## $c @Tag_41_l
111 ## $d @Tag_42
111 ## $n @Tag_40_x
240 ## $a %Tag_27
250 ## $a %Tag_44
260 ## $b @Tag_47_e
260 ## $a @Tag_47_l
260 ## $c %Tag_45
300 ## $a %Tag_52_e
300 ## $b %Tag_52_i
440 ## $a @Tag_36_t
440 ## $n @Tag_36_s
440 ## $p @Tag_36_d
440 ## $v @Tag_12
440 ## $x %Tag_15
500 ## $a @Tag_59
653 ## $a @Tag_62
650 ## $a @Tag_65
900 ## $ %Tag_999
920 ## $ %Tag_77