MT 2116, Follow-up : Fix subfield separator
[koha.git] / C4 / Record.pm
index 1e00a7b..25df9ad 100644 (file)
@@ -32,6 +32,7 @@ use XML::LibXML;
 use C4::Biblio; #marc2bibtex
 use C4::Csv; #marc2csv
 use C4::Koha; #marc2csv
+use YAML; #marcrecords2csv
 use Text::CSV::Encoded; #marc2csv
 
 use vars qw($VERSION @ISA @EXPORT);
@@ -52,7 +53,6 @@ $VERSION = 3.00;
   &marc2modsxml
   &marc2bibtex
   &marc2csv
-
   &html2marcxml
   &html2marc
   &changeEncoding
@@ -328,11 +328,58 @@ sub marc2endnote {
        
 }
 
-=head2 marc2csv - Convert from UNIMARC to CSV
+=head2 marc2csv - Convert several records from UNIMARC to CSV
+Pre and postprocessing can be done through a YAML file
 
 =over 4
 
-my ($csv) = marc2csv($record, $csvprofileid, $header);
+my ($csv) = marc2csv($biblios, $csvprofileid);
+
+Returns a CSV scalar
+
+=over 2
+
+C<$biblio> - a list of biblionumbers
+
+C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id and the GetCsvProfiles function in C4::Csv)
+
+=back
+
+=back
+
+=cut
+sub marc2csv {
+    my ($biblios, $id) = @_;
+    my $output;
+    my $csv = Text::CSV::Encoded->new();
+
+    # Getting yaml file
+    my $configfile = "../tools/csv-profiles/$id.yaml";
+    my ($preprocess, $postprocess, $fieldprocessing);
+    if (-e $configfile){
+        ($preprocess,$postprocess, $fieldprocessing) = YAML::LoadFile($configfile);
+    }
+
+    # Preprocessing
+    eval $preprocess if ($preprocess);
+
+    my $firstpass = 1;
+    foreach my $biblio (@$biblios) {
+       $output .= marcrecord2csv($biblio, $id, $firstpass, $csv, $fieldprocessing) ;
+       $firstpass = 0;
+    }
+
+    # Postprocessing
+    eval $postprocess if ($postprocess);
+
+    return $output;
+}
+
+=head2 marcrecord2csv - Convert a single record from UNIMARC to CSV
+
+=over 4
+
+my ($csv) = marcrecord2csv($biblio, $csvprofileid, $header);
 
 Returns a CSV scalar
 
@@ -344,6 +391,8 @@ C<$csvprofileid> - the id of the CSV profile to use for the export (see export_f
 
 C<$header> - true if the headers are to be printed (typically at first pass)
 
+C<$csv> - an already initialised Text::CSV object
+
 =back
 
 =back
@@ -351,8 +400,8 @@ C<$header> - true if the headers are to be printed (typically at first pass)
 =cut
 
 
-sub marc2csv {
-    my ($biblio, $id, $header) = @_;
+sub marcrecord2csv {
+    my ($biblio, $id, $header, $csv, $fieldprocessing) = @_;
     my $output;
 
     # Getting the record
@@ -366,7 +415,6 @@ sub marc2csv {
 
     # Getting output encoding
     my $encoding          = $profile->{encoding} || 'utf8';
-
     # Getting separators
     my $csvseparator      = $profile->{csv_separator}      || ',';
     my $fieldseparator    = $profile->{field_separator}    || '#';
@@ -377,9 +425,9 @@ sub marc2csv {
     if ($fieldseparator eq '\t') { $fieldseparator = "\t" }
     if ($subfieldseparator eq '\t') { $subfieldseparator = "\t" }
 
-    # Init CSV
-    my $csv = Text::CSV::Encoded->new({ sep_char => $csvseparator });
-    $csv = $csv->encoding_out($encoding) if ($encoding ne 'utf8');
+
+    $csv = $csv->encoding_out($encoding) ;
+    $csv->sep_char($csvseparator);
 
     # Getting the marcfields
     my $marcfieldslist = $profile->{marcfields};
@@ -437,6 +485,10 @@ sub marc2csv {
     my @fieldstab;
     foreach (@marcfields) {
        my $marcfield = $_->{field};
+
+       # Remove any blank char that might have unintentionally insered into the tag name
+       $marcfield =~ s/\s+//g; 
+
        # If it is a subfield
        if (index($marcfield, '$') > 0) {
            my ($fieldtag, $subfieldtag) = split('\$', $marcfield);
@@ -460,13 +512,37 @@ sub marc2csv {
        } else {
            my @fields = ($record->field($marcfield));
            my $authvalues = GetKohaAuthorisedValuesFromField($marcfield, undef, $frameworkcode, undef);
-           push (@fieldstab, join($fieldseparator, map((defined $authvalues->{$_->as_string}) ? $authvalues->{$_->as_string} : $_->as_string, @fields)));              
+
+           my @valuesarray;
+           foreach (@fields) {
+               my $value;
+
+               # If it is a control field
+               if ($_->is_control_field) {
+                   $value = defined $authvalues->{$_->as_string} ? $authvalues->{$_->as_string} : $_->as_string;
+               } else {
+
+                   # If it is a field, we gather all subfields, joined by the subfield separator
+                   my @subvaluesarray;
+                   my @subfields = $_->subfields;
+                   foreach my $subfield (@subfields) {
+                       push (@subvaluesarray, defined $authvalues->{$subfield->[1]} ? $authvalues->{$subfield->[1]} : $subfield->[1]);
+                   }
+                   $value = join ($subfieldseparator, @subvaluesarray);
+               }
+
+               # Field processing
+               eval $fieldprocessing if ($fieldprocessing);
+
+               push @valuesarray, $value;
+           }
+           push (@fieldstab, join($fieldseparator, @valuesarray)); 
         }
     };
 
     $csv->combine(@fieldstab);
     $output .= $csv->string() . "\n";
-   
+
     return $output;
 
 }
@@ -713,6 +789,94 @@ sub changeEncoding {
        return ($error,$newrecord);
 }
 
+=head2 marc2bibtex - Convert from MARC21 and UNIMARC to BibTex
+
+=over 4
+
+my ($bibtex) = marc2bibtex($record, $id);
+
+Returns a BibTex scalar
+
+=over 2
+
+C<$record> - a MARC::Record object
+
+C<$id> - an id for the BibTex record (might be the biblionumber)
+
+=back
+
+=back
+
+=cut
+
+
+sub marc2bibtex {
+    my ($record, $id) = @_;
+    my $tex;
+
+    # Authors
+    my $marcauthors = GetMarcAuthors($record,C4::Context->preference("marcflavour"));
+    my $author;
+    for my $authors ( map { map { @$_ } values %$_  } @$marcauthors  ) {  
+       $author .= " and " if ($author && $$authors{value});
+       $author .= $$authors{value} if ($$authors{value}); 
+    }
+
+    # Defining the conversion hash according to the marcflavour
+    my %bh;
+    if (C4::Context->preference("marcflavour") eq "UNIMARC") {
+       
+       # FIXME, TODO : handle repeatable fields
+       # TODO : handle more types of documents
+
+       # Unimarc to bibtex hash
+       %bh = (
+
+           # Mandatory
+           author    => $author,
+           title     => $record->subfield("200", "a") || "",
+           editor    => $record->subfield("210", "g") || "",
+           publisher => $record->subfield("210", "c") || "",
+           year      => $record->subfield("210", "d") || $record->subfield("210", "h") || "",
+
+           # Optional
+           volume  =>  $record->subfield("200", "v") || "",
+           series  =>  $record->subfield("225", "a") || "",
+           address =>  $record->subfield("210", "a") || "",
+           edition =>  $record->subfield("205", "a") || "",
+           note    =>  $record->subfield("300", "a") || "",
+           url     =>  $record->subfield("856", "u") || ""
+       );
+    } else {
+
+       # Marc21 to bibtex hash
+       %bh = (
+
+           # Mandatory
+           author    => $author,
+           title     => $record->subfield("245", "a") || "",
+           editor    => $record->subfield("260", "f") || "",
+           publisher => $record->subfield("260", "b") || "",
+           year      => $record->subfield("260", "c") || $record->subfield("260", "g") || "",
+
+           # Optional
+           # unimarc to marc21 specification says not to convert 200$v to marc21
+           series  =>  $record->subfield("490", "a") || "",
+           address =>  $record->subfield("260", "a") || "",
+           edition =>  $record->subfield("250", "a") || "",
+           note    =>  $record->subfield("500", "a") || "",
+           url     =>  $record->subfield("856", "u") || ""
+       );
+    }
+
+    $tex .= "\@book{";
+    $tex .= join(",\n", $id, map { $bh{$_} ? qq(\t$_ = "$bh{$_}") : () } keys %bh);
+    $tex .= "\n}\n";
+
+    return $tex;
+}
+
+
 =head1 INTERNAL FUNCTIONS
 
 =head2 _entity_encode - Entity-encode an array of strings